dnscrypto-proxy: Update to release 1.3.0
[tomato.git] / release / src / router / dnscrypt / src / plugins / example-ldns-blocking / example-ldns-blocking.c
blobf7a99fd24d0b2efe6bdc37f9d6ea2e39fc47f3c8
2 #include <assert.h>
3 #include <ctype.h>
4 #include <getopt.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <strings.h>
10 #include <dnscrypt/plugin.h>
11 #include <ldns/ldns.h>
13 DCPLUGIN_MAIN(__FILE__);
15 typedef struct StrList_ {
16 struct StrList_ *next;
17 char *str;
18 } StrList;
20 typedef struct Blocking_ {
21 StrList *domains;
22 StrList *ips;
23 } Blocking;
25 static struct option getopt_long_options[] = {
26 { "domains", 1, NULL, 'd' },
27 { "ips", 1, NULL, 'i' },
28 { NULL, 0, NULL, 0 }
30 static const char *getopt_options = "di";
32 static void
33 str_list_free(StrList * const str_list)
35 StrList *next;
36 StrList *scanned = str_list;
38 while (scanned != NULL) {
39 next = scanned->next;
40 free(scanned->str);
41 scanned->next = NULL;
42 scanned->str = NULL;
43 free(scanned);
44 scanned = next;
48 static StrList *
49 parse_str_list(const char * const file)
51 char line[300U];
52 FILE *fp;
53 char *ptr;
54 StrList *str_list = NULL;
55 StrList *str_list_item;
56 StrList *str_list_last = NULL;
58 if ((fp = fopen(file, "r")) == NULL) {
59 return NULL;
61 while (fgets(line, (int) sizeof line, fp) != NULL) {
62 while ((ptr = strchr(line, '\n')) != NULL ||
63 (ptr = strchr(line, '\r')) != NULL) {
64 *ptr = 0;
66 if (*line == 0 || *line == '#') {
67 continue;
69 if ((str_list_item = calloc(1U, sizeof *str_list_item)) == NULL ||
70 (str_list_item->str = strdup(line)) == NULL) {
71 break;
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;
77 if (!feof(fp)) {
78 str_list_free(str_list);
79 str_list = NULL;
81 fclose(fp);
83 return str_list;
87 static char *
88 substr_find(const char *str, const char * const substr, const size_t max_len)
90 const char *str_max;
91 size_t str_len = strlen(str);
92 int substr_c0;
94 assert(strlen(substr) >= max_len);
95 if (str_len < max_len) {
96 return NULL;
98 str_max = str + str_len - max_len;
99 substr_c0 = tolower((int) (unsigned char) substr[0]);
100 do {
101 if (tolower((int) (unsigned char) *str) == substr_c0 &&
102 strncasecmp(str, substr, max_len) == 0) {
103 return (char *) str;
105 } while (str++ < str_max);
107 return NULL;
110 static _Bool
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) {
118 return 0;
120 if (*pattern == '*') {
121 if (pattern_len <= (size_t) 1U) {
122 return 1;
124 wildcard_start = 1;
125 pattern++;
126 pattern_len--;
128 assert(pattern_len > 0U);
129 if (pattern[pattern_len - 1U] == '*') {
130 if (pattern_len <= (size_t) 1U) {
131 return 1;
133 wildcard_end = 1;
134 pattern_len--;
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);
142 if (found == NULL) {
143 return 0;
145 return wildcard_end == 0 ? *(found + pattern_len) == 0 : 1;
148 const char *
149 dcplugin_description(DCPlugin * const dcplugin)
151 return "Block specific domains and IP addresses";
154 const char *
155 dcplugin_long_description(DCPlugin * const dcplugin)
157 return
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"
161 "\n"
162 "Recognized switches are:\n"
163 "--domains=<file>\n"
164 "--ips=<file>\n"
165 "\n"
166 "A file should list one entry per line.\n"
167 "\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"
171 "\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[])
179 Blocking *blocking;
180 int opt_flag;
181 int option_index = 0;
183 if ((blocking = calloc((size_t) 1U, sizeof *blocking)) == NULL) {
184 return -1;
186 dcplugin_set_user_data(dcplugin, blocking);
187 if (blocking == NULL) {
188 return -1;
190 blocking->domains = blocking->ips = NULL;
191 optind = 0;
192 #ifdef _OPTRESET
193 optreset = 1;
194 #endif
195 while ((opt_flag = getopt_long(argc, argv,
196 getopt_options, getopt_long_options,
197 &option_index)) != -1) {
198 switch (opt_flag) {
199 case 'd':
200 if ((blocking->domains = parse_str_list(optarg)) == NULL) {
201 return -1;
203 break;
204 case 'i':
205 if ((blocking->ips = parse_str_list(optarg)) == NULL) {
206 return -1;
208 break;
209 default:
210 return -1;
213 if (blocking->domains == NULL && blocking->ips == NULL) {
214 return -1;
216 return 0;
220 dcplugin_destroy(DCPlugin * const dcplugin)
222 Blocking *blocking = dcplugin_get_user_data(dcplugin);
224 if (blocking == NULL) {
225 return 0;
227 str_list_free(blocking->domains);
228 blocking->domains = NULL;
229 str_list_free(blocking->ips);
230 blocking->ips = NULL;
231 free(blocking);
233 return 0;
236 static DCPluginSyncFilterResult
237 apply_block_domains(DCPluginDNSPacket *dcp_packet, Blocking * const blocking,
238 ldns_pkt * const packet)
240 StrList *scanned;
241 ldns_rr *question;
242 char *owner_str;
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;
254 do {
255 if (wildcard_match(owner_str, scanned->str)) {
256 LDNS_RCODE_SET(dcplugin_get_wire_data(dcp_packet),
257 LDNS_RCODE_REFUSED);
258 break;
260 } while ((scanned = scanned->next) != NULL);
261 free(owner_str);
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)
270 StrList *scanned;
271 ldns_rr_list *answers;
272 ldns_rr *answer;
273 char *answer_str;
274 ldns_rr_type type;
275 size_t answers_count;
276 size_t i;
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) {
284 continue;
286 if ((answer_str = ldns_rdf2str(ldns_rr_a_address(answer))) == NULL) {
287 return DCP_SYNC_FILTER_RESULT_FATAL;
289 scanned = blocking->ips;
290 do {
291 if (strcasecmp(scanned->str, answer_str) == 0) {
292 LDNS_RCODE_SET(dcplugin_get_wire_data(dcp_packet),
293 LDNS_RCODE_REFUSED);
294 break;
296 } while ((scanned = scanned->next) != NULL);
297 free(answer_str);
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);
306 ldns_pkt *packet;
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);
321 return result;
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);
327 return result;
329 ldns_pkt_free(packet);
331 return DCP_SYNC_FILTER_RESULT_OK;