10 #include <dnscrypt/plugin.h>
11 #include <ldns/ldns.h>
13 DCPLUGIN_MAIN(__FILE__
);
15 typedef struct StrList_
{
16 struct StrList_
*next
;
20 typedef struct Blocking_
{
25 static struct option getopt_long_options
[] = {
26 { "domains", 1, NULL
, 'd' },
27 { "ips", 1, NULL
, 'i' },
30 static const char *getopt_options
= "di";
33 str_list_free(StrList
* const str_list
)
36 StrList
*scanned
= str_list
;
38 while (scanned
!= NULL
) {
49 parse_str_list(const char * const file
)
54 StrList
*str_list
= NULL
;
55 StrList
*str_list_item
;
56 StrList
*str_list_last
= NULL
;
58 if ((fp
= fopen(file
, "r")) == NULL
) {
61 while (fgets(line
, (int) sizeof line
, fp
) != NULL
) {
62 while ((ptr
= strchr(line
, '\n')) != NULL
||
63 (ptr
= strchr(line
, '\r')) != NULL
) {
66 if (*line
== 0 || *line
== '#') {
69 if ((str_list_item
= calloc(1U, sizeof *str_list_item
)) == NULL
||
70 (str_list_item
->str
= strdup(line
)) == NULL
) {
73 str_list_item
->next
= NULL
;
74 *(str_list
== NULL
? &str_list
: &str_list_last
->next
) = str_list_item
;
75 str_list_last
= str_list_item
;
78 str_list_free(str_list
);
88 substr_find(const char *str
, const char * const substr
, const size_t max_len
)
91 size_t str_len
= strlen(str
);
94 assert(strlen(substr
) >= max_len
);
95 if (str_len
< max_len
) {
98 str_max
= str
+ str_len
- max_len
;
99 substr_c0
= tolower((int) (unsigned char) substr
[0]);
101 if (tolower((int) (unsigned char) *str
) == substr_c0
&&
102 strncasecmp(str
, substr
, max_len
) == 0) {
105 } while (str
++ < str_max
);
111 wildcard_match(const char * const str
, const char *pattern
)
113 size_t pattern_len
= strlen(pattern
);
114 _Bool wildcard_start
= 0;
115 _Bool wildcard_end
= 0;
117 if (pattern_len
<= (size_t) 0U) {
120 if (*pattern
== '*') {
121 if (pattern_len
<= (size_t) 1U) {
128 assert(pattern_len
> 0U);
129 if (pattern
[pattern_len
- 1U] == '*') {
130 if (pattern_len
<= (size_t) 1U) {
136 if (wildcard_start
== 0) {
137 return (wildcard_end
== 0 ?
138 strcasecmp(str
, pattern
) :
139 strncasecmp(str
, pattern
, pattern_len
)) == 0;
141 const char * const found
= substr_find(str
, pattern
, pattern_len
);
145 return wildcard_end
== 0 ? *(found
+ pattern_len
) == 0 : 1;
149 dcplugin_description(DCPlugin
* const dcplugin
)
151 return "Block specific domains and IP addresses";
155 dcplugin_long_description(DCPlugin
* const dcplugin
)
158 "This plugin returns a REFUSED response if the query name is in a\n"
159 "list of blacklisted names, or if at least one of the returned IP\n"
160 "addresses happens to be in a list of blacklisted IPs.\n"
162 "Recognized switches are:\n"
166 "A file should list one entry per line.\n"
168 "IPv4 and IPv6 addresses are supported.\n"
169 "For names, leading and trailing wildcards (*) are also supported\n"
170 "(e.g. *xxx*, *.example.com, ads.*)\n"
172 "# dnscrypt-proxy --plugin \\\n"
173 " libdcplugin_example,--ips=/etc/blk-ips,--domains=/etc/blk-names\n";
177 dcplugin_init(DCPlugin
* const dcplugin
, int argc
, char *argv
[])
181 int option_index
= 0;
183 if ((blocking
= calloc((size_t) 1U, sizeof *blocking
)) == NULL
) {
186 dcplugin_set_user_data(dcplugin
, blocking
);
187 if (blocking
== NULL
) {
190 blocking
->domains
= blocking
->ips
= NULL
;
195 while ((opt_flag
= getopt_long(argc
, argv
,
196 getopt_options
, getopt_long_options
,
197 &option_index
)) != -1) {
200 if ((blocking
->domains
= parse_str_list(optarg
)) == NULL
) {
205 if ((blocking
->ips
= parse_str_list(optarg
)) == NULL
) {
213 if (blocking
->domains
== NULL
&& blocking
->ips
== NULL
) {
220 dcplugin_destroy(DCPlugin
* const dcplugin
)
222 Blocking
*blocking
= dcplugin_get_user_data(dcplugin
);
224 if (blocking
== NULL
) {
227 str_list_free(blocking
->domains
);
228 blocking
->domains
= NULL
;
229 str_list_free(blocking
->ips
);
230 blocking
->ips
= NULL
;
236 static DCPluginSyncFilterResult
237 apply_block_domains(DCPluginDNSPacket
*dcp_packet
, Blocking
* const blocking
,
238 ldns_pkt
* const packet
)
243 size_t owner_str_len
;
245 scanned
= blocking
->domains
;
246 question
= ldns_rr_list_rr(ldns_pkt_question(packet
), 0U);
247 if ((owner_str
= ldns_rdf2str(ldns_rr_owner(question
))) == NULL
) {
248 return DCP_SYNC_FILTER_RESULT_FATAL
;
250 owner_str_len
= strlen(owner_str
);
251 if (owner_str_len
> (size_t) 1U && owner_str
[--owner_str_len
] == '.') {
252 owner_str
[owner_str_len
] = 0;
255 if (wildcard_match(owner_str
, scanned
->str
)) {
256 LDNS_RCODE_SET(dcplugin_get_wire_data(dcp_packet
),
260 } while ((scanned
= scanned
->next
) != NULL
);
263 return DCP_SYNC_FILTER_RESULT_OK
;
266 static DCPluginSyncFilterResult
267 apply_block_ips(DCPluginDNSPacket
*dcp_packet
, Blocking
* const blocking
,
268 ldns_pkt
* const packet
)
271 ldns_rr_list
*answers
;
275 size_t answers_count
;
278 answers
= ldns_pkt_answer(packet
);
279 answers_count
= ldns_rr_list_rr_count(answers
);
280 for (i
= (size_t) 0U; i
< answers_count
; i
++) {
281 answer
= ldns_rr_list_rr(answers
, i
);
282 type
= ldns_rr_get_type(answer
);
283 if (type
!= LDNS_RR_TYPE_A
&& type
!= LDNS_RR_TYPE_AAAA
) {
286 if ((answer_str
= ldns_rdf2str(ldns_rr_a_address(answer
))) == NULL
) {
287 return DCP_SYNC_FILTER_RESULT_FATAL
;
289 scanned
= blocking
->ips
;
291 if (strcasecmp(scanned
->str
, answer_str
) == 0) {
292 LDNS_RCODE_SET(dcplugin_get_wire_data(dcp_packet
),
296 } while ((scanned
= scanned
->next
) != NULL
);
299 return DCP_SYNC_FILTER_RESULT_OK
;
302 DCPluginSyncFilterResult
303 dcplugin_sync_post_filter(DCPlugin
*dcplugin
, DCPluginDNSPacket
*dcp_packet
)
305 Blocking
*blocking
= dcplugin_get_user_data(dcplugin
);
307 DCPluginSyncFilterResult result
= DCP_SYNC_FILTER_RESULT_OK
;
309 if (blocking
->domains
== NULL
&& blocking
->ips
== NULL
) {
310 return DCP_SYNC_FILTER_RESULT_OK
;
312 ldns_wire2pkt(&packet
, dcplugin_get_wire_data(dcp_packet
),
313 dcplugin_get_wire_data_len(dcp_packet
));
314 if (packet
== NULL
) {
315 return DCP_SYNC_FILTER_RESULT_ERROR
;
317 if (blocking
->domains
!= NULL
&&
318 (result
= apply_block_domains(dcp_packet
, blocking
, packet
)
319 != DCP_SYNC_FILTER_RESULT_OK
)) {
320 ldns_pkt_free(packet
);
323 if (blocking
->ips
!= NULL
&&
324 (result
= apply_block_ips(dcp_packet
, blocking
, packet
)
325 != DCP_SYNC_FILTER_RESULT_OK
)) {
326 ldns_pkt_free(packet
);
329 ldns_pkt_free(packet
);
331 return DCP_SYNC_FILTER_RESULT_OK
;