1
2 #include "compat.h"
3
4 #include <assert.h>
5 #include <ctype.h>
6 #include <getopt.h>
7 #include <stdio.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <time.h>
12
13 #include "dnscrypt.h"
14 #include "fpst.h"
15
16 #define MAX_QNAME_LENGTH 255U
17
18 typedef enum BlockType {
19 BLOCKTYPE_UNDEFINED,
20 BLOCKTYPE_EXACT,
21 BLOCKTYPE_PREFIX,
22 BLOCKTYPE_SUFFIX,
23 BLOCKTYPE_SUBSTRING
24 } BlockType;
25
26 typedef struct Blocking_ {
27 FPST *domains;
28 FPST *domains_rev;
29 FPST *domains_substr;
30 } Blocking;
31
32 static char *
skip_spaces(char * str)33 skip_spaces(char *str)
34 {
35 while (*str != 0 && isspace((int) (unsigned char) *str)) {
36 str++;
37 }
38 return str;
39 }
40
41 static char *
skip_chars(char * str)42 skip_chars(char *str)
43 {
44 while (*str != 0 && !isspace((int) (unsigned char) *str)) {
45 str++;
46 }
47 return str;
48 }
49
50 static void
str_tolower(char * str)51 str_tolower(char *str)
52 {
53 while (*str != 0) {
54 *str = (char) tolower((unsigned char) *str);
55 str++;
56 }
57 }
58
59 static void
str_reverse(char * str)60 str_reverse(char *str)
61 {
62 size_t i = 0;
63 size_t j = strlen(str);
64 char t;
65
66 while (i < j) {
67 t = str[i];
68 str[i++] = str[--j];
69 str[j] = t;
70 }
71 }
72
73 static char *
untab(char * line)74 untab(char *line)
75 {
76 char *ptr;
77
78 while ((ptr = strchr(line, '\t')) != NULL) {
79 *ptr = ' ';
80 }
81 return line;
82 }
83
84 static char *
trim_comments(char * line)85 trim_comments(char *line)
86 {
87 char *ptr;
88 char *s1;
89 char *s2;
90
91 while ((ptr = strchr(line, '\n')) != NULL ||
92 (ptr = strchr(line, '\r')) != NULL) {
93 *ptr = 0;
94 }
95 line = skip_spaces(line);
96 if (*line == 0 || *line == '#') {
97 return NULL;
98 }
99 s1 = skip_chars(line);
100 if (*(s2 = skip_spaces(s1)) == 0) {
101 *s1 = 0;
102 return line;
103 }
104 if (*s2 == '#') {
105 return NULL;
106 }
107 *skip_chars(s2) = 0;
108
109 return s2;
110 }
111
112 static void
free_list(const char * key,uint32_t val)113 free_list(const char *key, uint32_t val)
114 {
115 (void) val;
116 free((void *) key);
117 }
118
119 static int
parse_domain_list(FPST ** const domain_list_p,FPST ** const domain_rev_list_p,FPST ** const domain_substr_list_p,const char * const file)120 parse_domain_list(FPST ** const domain_list_p,
121 FPST ** const domain_rev_list_p,
122 FPST ** const domain_substr_list_p,
123 const char * const file)
124 {
125 char buf[MAX_QNAME_LENGTH + 1U];
126 char *line;
127 FILE *fp;
128 FPST *domain_list;
129 FPST *domain_list_tmp;
130 FPST *domain_rev_list;
131 FPST *domain_rev_list_tmp;
132 FPST *domain_substr_list;
133 FPST *domain_substr_list_tmp;
134 size_t line_len;
135 BlockType block_type = BLOCKTYPE_UNDEFINED;
136 int ret = -1;
137
138 assert(domain_list_p != NULL);
139 assert(domain_rev_list_p != NULL);
140 assert(domain_substr_list_p != NULL);
141 *domain_list_p = NULL;
142 *domain_rev_list_p = NULL;
143 *domain_substr_list_p = NULL;
144 domain_list = fpst_new();
145 domain_rev_list = fpst_new();
146 domain_substr_list = fpst_new();
147 if ((fp = fopen(file, "r")) == NULL) {
148 return -1;
149 }
150 while (fgets(buf, (int) sizeof buf, fp) != NULL) {
151 if ((line = trim_comments(untab(buf))) == NULL || *line == 0) {
152 continue;
153 }
154 line_len = strlen(line);
155 if (line[0] == '*' && line[line_len - 1] == '*') {
156 line[line_len - 1] = 0;
157 line++;
158 block_type = BLOCKTYPE_SUBSTRING;
159 } else if (line[line_len - 1] == '*') {
160 line[line_len - 1] = 0;
161 block_type = BLOCKTYPE_PREFIX;
162 } else {
163 if (line[0] == '*') {
164 line++;
165 }
166 if (line[0] == '.') {
167 line++;
168 }
169 str_reverse(line);
170 block_type = BLOCKTYPE_SUFFIX;
171 }
172 if (*line == 0) {
173 continue;
174 }
175 str_tolower(line);
176 if ((line = strdup(line)) == NULL) {
177 break;
178 }
179 if (block_type == BLOCKTYPE_SUFFIX) {
180 if ((domain_rev_list_tmp = fpst_insert_str(domain_rev_list, line,
181 (uint32_t) block_type)) == NULL) {
182 free(line);
183 break;
184 }
185 domain_rev_list = domain_rev_list_tmp;
186 } else if (block_type == BLOCKTYPE_PREFIX) {
187 if ((domain_list_tmp = fpst_insert_str(domain_list, line,
188 (uint32_t) block_type)) == NULL) {
189 free(line);
190 break;
191 }
192 domain_list = domain_list_tmp;
193 } else if (block_type == BLOCKTYPE_SUBSTRING) {
194 if ((domain_substr_list_tmp = fpst_insert_str(domain_substr_list, line,
195 (uint32_t) block_type)) == NULL) {
196 free(line);
197 break;
198 }
199 domain_substr_list = domain_substr_list_tmp;
200 } else {
201 free(line);
202 }
203 }
204 if (!feof(fp)) {
205 fpst_free(domain_list, free_list);
206 fpst_free(domain_rev_list, free_list);
207 fpst_free(domain_substr_list, free_list);
208 } else {
209 *domain_list_p = domain_list;
210 *domain_rev_list_p = domain_rev_list;
211 *domain_substr_list_p = domain_substr_list;
212 ret = 0;
213 }
214 fclose(fp);
215 logger(LOG_INFO, "Blacklist [%s] loaded", file);
216
217 return ret;
218 }
219
220 static _Bool
substr_match(FPST * list,const char * str,const char ** found_key_p,uint32_t * found_block_type_p)221 substr_match(FPST *list, const char *str,
222 const char **found_key_p, uint32_t *found_block_type_p)
223 {
224 while (*str != 0) {
225 if (fpst_str_starts_with_existing_key(list, str, found_key_p,
226 found_block_type_p)) {
227 return 1;
228 }
229 str++;
230 }
231 return 0;
232 }
233
234 int
blocking_init(struct context * c,const char * file)235 blocking_init(struct context *c, const char *file)
236 {
237 Blocking *blocking;
238
239 if ((blocking = calloc((size_t) 1U, sizeof *blocking)) == NULL) {
240 return -1;
241 }
242 c->blocking = blocking;
243 blocking->domains = NULL;
244 blocking->domains_rev = NULL;
245 blocking->domains_substr = NULL;
246
247 return parse_domain_list(&blocking->domains, &blocking->domains_rev,
248 &blocking->domains_substr, file);
249 }
250
251 void
blocking_free(struct context * c)252 blocking_free(struct context *c)
253 {
254 Blocking *blocking = c->blocking;
255
256 if (blocking == NULL) {
257 return;
258 }
259 fpst_free(blocking->domains, free_list);
260 blocking->domains = NULL;
261 fpst_free(blocking->domains_rev, free_list);
262 blocking->domains_rev = NULL;
263 fpst_free(blocking->domains_substr, free_list);
264 blocking->domains_substr = NULL;
265 free(blocking);
266 }
267
268 void
str_lcpy(char * dst,const char * src,size_t dsize)269 str_lcpy(char *dst, const char *src, size_t dsize)
270 {
271 size_t nleft = dsize;
272
273 if (nleft != 0) {
274 while (--nleft != 0) {
275 if ((*dst++ = *src++) == 0) {
276 break;
277 }
278 }
279 }
280 if (nleft == 0 && dsize != 0) {
281 *dst = 0;
282 }
283 }
284
285 static int
name_matches_blacklist(const Blocking * const blocking,char * const name)286 name_matches_blacklist(const Blocking * const blocking, char * const name)
287 {
288 char rev[MAX_QNAME_LENGTH + 1U];
289 const char *found_key;
290 size_t name_len;
291 uint32_t found_block_type;
292 _Bool block = 0;
293
294 rev[MAX_QNAME_LENGTH] = 0;
295 name_len = strlen(name);
296 if (name_len >= sizeof rev) {
297 return -1;
298 }
299 if (name_len > (size_t) 1U && name[name_len - 1U] == '.') {
300 name[--name_len] = 0;
301 }
302 if (name_len <= 0) {
303 return 0;
304 }
305 str_tolower(name);
306 do {
307 str_lcpy(rev, name, sizeof rev);
308 str_reverse(rev);
309 if (fpst_starts_with_existing_key(blocking->domains_rev,
310 rev, name_len,
311 &found_key, &found_block_type)) {
312 const size_t found_key_len = strlen(found_key);
313
314 assert(found_block_type == BLOCKTYPE_SUFFIX);
315 if (found_key_len <= name_len &&
316 (rev[found_key_len] == 0 || rev[found_key_len] == '.')) {
317 block = 1;
318 break;
319 }
320 if (found_key_len < name_len) {
321 size_t owner_part_len = name_len;
322
323 while (owner_part_len > 0U && rev[owner_part_len] != '.') {
324 owner_part_len--;
325 }
326 rev[owner_part_len] = 0;
327 if (owner_part_len > 0U && fpst_starts_with_existing_key
328 (blocking->domains_rev, rev, owner_part_len,
329 &found_key, &found_block_type)) {
330 const size_t found_key_len = strlen(found_key);
331 if (found_key_len <= owner_part_len &&
332 (rev[found_key_len] == 0 || rev[found_key_len] == '.')) {
333 block = 1;
334 break;
335 }
336 }
337 }
338 }
339 if (fpst_starts_with_existing_key(blocking->domains,
340 name, name_len,
341 &found_key, &found_block_type)) {
342 assert(found_block_type == BLOCKTYPE_PREFIX);
343 block = 1;
344 break;
345 }
346 if (blocking->domains_substr != NULL &&
347 substr_match(blocking->domains_substr, name,
348 &found_key, &found_block_type)) {
349 assert(found_block_type == BLOCKTYPE_SUBSTRING);
350 block = 1;
351 break;
352 }
353 } while (0);
354
355 return (int) block;
356 }
357
358 int
is_blocked(struct context * c,struct dns_header * header,size_t dns_query_len)359 is_blocked(struct context *c, struct dns_header *header, size_t dns_query_len)
360 {
361 unsigned char *ansp;
362 unsigned char *p;
363 char *name;
364
365 if (c->blocking == NULL) {
366 return 0;
367 }
368 if (ntohs(header->qdcount) != 1) {
369 return -1;
370 }
371 if (!(ansp = skip_questions(header, dns_query_len))) {
372 return -1;
373 }
374 p = (unsigned char *)(header + 1);
375 if (!extract_name(header, dns_query_len, &p, c->namebuff, 1, 4)) {
376 return -1;
377 }
378 name = c->namebuff;
379
380 return name_matches_blacklist(c->blocking, name);
381 }
382