]>
Commit | Line | Data |
---|---|---|
fbcc3cb7 MF |
1 | From c2bcd1e183bcc5fdd63811c045355fc57e36ecfd Mon Sep 17 00:00:00 2001 |
2 | From: Simon Kelley <simon@thekelleys.org.uk> | |
3 | Date: Tue, 15 Dec 2015 17:25:21 +0000 | |
4 | Subject: [PATCH] Generalise RR-filtering code, for use with EDNS0. | |
5 | ||
6 | --- | |
7 | Makefile | 3 +- | |
8 | bld/Android.mk | 2 +- | |
9 | src/dnsmasq.h | 5 + | |
10 | src/dnssec.c | 307 +------------------------------------------------- | |
11 | src/forward.c | 2 +- | |
12 | src/rrfilter.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
13 | 6 files changed, 349 insertions(+), 309 deletions(-) | |
14 | create mode 100644 src/rrfilter.c | |
15 | ||
16 | diff --git a/Makefile b/Makefile | |
17 | index 4c87ea9..b664160 100644 | |
18 | --- a/Makefile | |
19 | +++ b/Makefile | |
20 | @@ -73,7 +73,8 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \ | |
21 | dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \ | |
22 | helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ | |
23 | dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ | |
24 | - domain.o dnssec.o blockdata.o tables.o loop.o inotify.o poll.o | |
25 | + domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \ | |
26 | + poll.o rrfilter.o | |
27 | ||
28 | hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ | |
29 | dns-protocol.h radv-protocol.h ip6addr.h | |
30 | diff --git a/bld/Android.mk b/bld/Android.mk | |
31 | index 5364ee7..67b9c4b 100644 | |
32 | --- a/bld/Android.mk | |
33 | +++ b/bld/Android.mk | |
34 | @@ -10,7 +10,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \ | |
35 | dhcp6.c rfc3315.c dhcp-common.c outpacket.c \ | |
36 | radv.c slaac.c auth.c ipset.c domain.c \ | |
37 | dnssec.c dnssec-openssl.c blockdata.c tables.c \ | |
38 | - loop.c inotify.c poll.c | |
39 | + loop.c inotify.c poll.c rrfilter.c | |
40 | ||
41 | LOCAL_MODULE := dnsmasq | |
42 | ||
43 | diff --git a/src/dnsmasq.h b/src/dnsmasq.h | |
44 | index 4344cae..39a930c 100644 | |
45 | --- a/src/dnsmasq.h | |
46 | +++ b/src/dnsmasq.h | |
47 | @@ -1513,3 +1513,8 @@ int poll_check(int fd, short event); | |
48 | void poll_listen(int fd, short event); | |
49 | int do_poll(int timeout); | |
50 | ||
51 | +/* rrfilter.c */ | |
52 | +size_t rrfilter(struct dns_header *header, size_t plen, int mode); | |
53 | +u16 *rrfilter_desc(int type); | |
54 | +int expand_workspace(unsigned char ***wkspc, int *szp, int new); | |
55 | + | |
56 | diff --git a/src/dnssec.c b/src/dnssec.c | |
57 | index 359231f..fa3eb81 100644 | |
58 | --- a/src/dnssec.c | |
59 | +++ b/src/dnssec.c | |
60 | @@ -507,50 +507,6 @@ static int check_date_range(unsigned long date_start, unsigned long date_end) | |
61 | && serial_compare_32(curtime, date_end) == SERIAL_LT; | |
62 | } | |
63 | ||
64 | -static u16 *get_desc(int type) | |
65 | -{ | |
66 | - /* List of RRtypes which include domains in the data. | |
67 | - 0 -> domain | |
68 | - integer -> no of plain bytes | |
69 | - -1 -> end | |
70 | - | |
71 | - zero is not a valid RRtype, so the final entry is returned for | |
72 | - anything which needs no mangling. | |
73 | - */ | |
74 | - | |
75 | - static u16 rr_desc[] = | |
76 | - { | |
77 | - T_NS, 0, -1, | |
78 | - T_MD, 0, -1, | |
79 | - T_MF, 0, -1, | |
80 | - T_CNAME, 0, -1, | |
81 | - T_SOA, 0, 0, -1, | |
82 | - T_MB, 0, -1, | |
83 | - T_MG, 0, -1, | |
84 | - T_MR, 0, -1, | |
85 | - T_PTR, 0, -1, | |
86 | - T_MINFO, 0, 0, -1, | |
87 | - T_MX, 2, 0, -1, | |
88 | - T_RP, 0, 0, -1, | |
89 | - T_AFSDB, 2, 0, -1, | |
90 | - T_RT, 2, 0, -1, | |
91 | - T_SIG, 18, 0, -1, | |
92 | - T_PX, 2, 0, 0, -1, | |
93 | - T_NXT, 0, -1, | |
94 | - T_KX, 2, 0, -1, | |
95 | - T_SRV, 6, 0, -1, | |
96 | - T_DNAME, 0, -1, | |
97 | - 0, -1 /* wildcard/catchall */ | |
98 | - }; | |
99 | - | |
100 | - u16 *p = rr_desc; | |
101 | - | |
102 | - while (*p != type && *p != 0) | |
103 | - while (*p++ != (u16)-1); | |
104 | - | |
105 | - return p+1; | |
106 | -} | |
107 | - | |
108 | /* Return bytes of canonicalised rdata, when the return value is zero, the remaining | |
109 | data, pointed to by *p, should be used raw. */ | |
110 | static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen, | |
111 | @@ -594,34 +550,6 @@ static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, | |
112 | } | |
113 | } | |
114 | ||
115 | -static int expand_workspace(unsigned char ***wkspc, int *szp, int new) | |
116 | -{ | |
117 | - unsigned char **p; | |
118 | - int old = *szp; | |
119 | - | |
120 | - if (old >= new+1) | |
121 | - return 1; | |
122 | - | |
123 | - if (new >= 100) | |
124 | - return 0; | |
125 | - | |
126 | - new += 5; | |
127 | - | |
128 | - if (!(p = whine_malloc(new * sizeof(unsigned char **)))) | |
129 | - return 0; | |
130 | - | |
131 | - if (old != 0 && *wkspc) | |
132 | - { | |
133 | - memcpy(p, *wkspc, old * sizeof(unsigned char **)); | |
134 | - free(*wkspc); | |
135 | - } | |
136 | - | |
137 | - *wkspc = p; | |
138 | - *szp = new; | |
139 | - | |
140 | - return 1; | |
141 | -} | |
142 | - | |
143 | /* Bubble sort the RRset into the canonical order. | |
144 | Note that the byte-streams from two RRs may get unsynced: consider | |
145 | RRs which have two domain-names at the start and then other data. | |
146 | @@ -849,7 +777,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in | |
147 | int rdlen, j, name_labels; | |
148 | struct crec *crecp = NULL; | |
149 | int algo, labels, orig_ttl, key_tag; | |
150 | - u16 *rr_desc = get_desc(type); | |
151 | + u16 *rr_desc = rrfilter_desc(type); | |
152 | ||
153 | if (wildcard_out) | |
154 | *wildcard_out = NULL; | |
155 | @@ -2266,239 +2194,6 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i | |
156 | return ret; | |
157 | } | |
158 | ||
159 | -/* Go through a domain name, find "pointers" and fix them up based on how many bytes | |
160 | - we've chopped out of the packet, or check they don't point into an elided part. */ | |
161 | -static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count) | |
162 | -{ | |
163 | - unsigned char *ansp = *namep; | |
164 | - | |
165 | - while(1) | |
166 | - { | |
167 | - unsigned int label_type; | |
168 | - | |
169 | - if (!CHECK_LEN(header, ansp, plen, 1)) | |
170 | - return 0; | |
171 | - | |
172 | - label_type = (*ansp) & 0xc0; | |
173 | - | |
174 | - if (label_type == 0xc0) | |
175 | - { | |
176 | - /* pointer for compression. */ | |
177 | - unsigned int offset; | |
178 | - int i; | |
179 | - unsigned char *p; | |
180 | - | |
181 | - if (!CHECK_LEN(header, ansp, plen, 2)) | |
182 | - return 0; | |
183 | - | |
184 | - offset = ((*ansp++) & 0x3f) << 8; | |
185 | - offset |= *ansp++; | |
186 | - | |
187 | - p = offset + (unsigned char *)header; | |
188 | - | |
189 | - for (i = 0; i < rr_count; i++) | |
190 | - if (p < rrs[i]) | |
191 | - break; | |
192 | - else | |
193 | - if (i & 1) | |
194 | - offset -= rrs[i] - rrs[i-1]; | |
195 | - | |
196 | - /* does the pointer end up in an elided RR? */ | |
197 | - if (i & 1) | |
198 | - return 0; | |
199 | - | |
200 | - /* No, scale the pointer */ | |
201 | - if (fixup) | |
202 | - { | |
203 | - ansp -= 2; | |
204 | - *ansp++ = (offset >> 8) | 0xc0; | |
205 | - *ansp++ = offset & 0xff; | |
206 | - } | |
207 | - break; | |
208 | - } | |
209 | - else if (label_type == 0x80) | |
210 | - return 0; /* reserved */ | |
211 | - else if (label_type == 0x40) | |
212 | - { | |
213 | - /* Extended label type */ | |
214 | - unsigned int count; | |
215 | - | |
216 | - if (!CHECK_LEN(header, ansp, plen, 2)) | |
217 | - return 0; | |
218 | - | |
219 | - if (((*ansp++) & 0x3f) != 1) | |
220 | - return 0; /* we only understand bitstrings */ | |
221 | - | |
222 | - count = *(ansp++); /* Bits in bitstring */ | |
223 | - | |
224 | - if (count == 0) /* count == 0 means 256 bits */ | |
225 | - ansp += 32; | |
226 | - else | |
227 | - ansp += ((count-1)>>3)+1; | |
228 | - } | |
229 | - else | |
230 | - { /* label type == 0 Bottom six bits is length */ | |
231 | - unsigned int len = (*ansp++) & 0x3f; | |
232 | - | |
233 | - if (!ADD_RDLEN(header, ansp, plen, len)) | |
234 | - return 0; | |
235 | - | |
236 | - if (len == 0) | |
237 | - break; /* zero length label marks the end. */ | |
238 | - } | |
239 | - } | |
240 | - | |
241 | - *namep = ansp; | |
242 | - | |
243 | - return 1; | |
244 | -} | |
245 | - | |
246 | -/* Go through RRs and check or fixup the domain names contained within */ | |
247 | -static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count) | |
248 | -{ | |
249 | - int i, type, class, rdlen; | |
250 | - unsigned char *pp; | |
251 | - | |
252 | - for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++) | |
253 | - { | |
254 | - pp = p; | |
255 | - | |
256 | - if (!(p = skip_name(p, header, plen, 10))) | |
257 | - return 0; | |
258 | - | |
259 | - GETSHORT(type, p); | |
260 | - GETSHORT(class, p); | |
261 | - p += 4; /* TTL */ | |
262 | - GETSHORT(rdlen, p); | |
263 | - | |
264 | - if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG) | |
265 | - { | |
266 | - /* fixup name of RR */ | |
267 | - if (!check_name(&pp, header, plen, fixup, rrs, rr_count)) | |
268 | - return 0; | |
269 | - | |
270 | - if (class == C_IN) | |
271 | - { | |
272 | - u16 *d; | |
273 | - | |
274 | - for (pp = p, d = get_desc(type); *d != (u16)-1; d++) | |
275 | - { | |
276 | - if (*d != 0) | |
277 | - pp += *d; | |
278 | - else if (!check_name(&pp, header, plen, fixup, rrs, rr_count)) | |
279 | - return 0; | |
280 | - } | |
281 | - } | |
282 | - } | |
283 | - | |
284 | - if (!ADD_RDLEN(header, p, plen, rdlen)) | |
285 | - return 0; | |
286 | - } | |
287 | - | |
288 | - return 1; | |
289 | -} | |
290 | - | |
291 | - | |
292 | -size_t filter_rrsigs(struct dns_header *header, size_t plen) | |
293 | -{ | |
294 | - static unsigned char **rrs; | |
295 | - static int rr_sz = 0; | |
296 | - | |
297 | - unsigned char *p = (unsigned char *)(header+1); | |
298 | - int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar; | |
299 | - | |
300 | - if (ntohs(header->qdcount) != 1 || | |
301 | - !(p = skip_name(p, header, plen, 4))) | |
302 | - return plen; | |
303 | - | |
304 | - GETSHORT(qtype, p); | |
305 | - GETSHORT(qclass, p); | |
306 | - | |
307 | - /* First pass, find pointers to start and end of all the records we wish to elide: | |
308 | - records added for DNSSEC, unless explicity queried for */ | |
309 | - for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0; | |
310 | - i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); | |
311 | - i++) | |
312 | - { | |
313 | - unsigned char *pstart = p; | |
314 | - int type, class; | |
315 | - | |
316 | - if (!(p = skip_name(p, header, plen, 10))) | |
317 | - return plen; | |
318 | - | |
319 | - GETSHORT(type, p); | |
320 | - GETSHORT(class, p); | |
321 | - p += 4; /* TTL */ | |
322 | - GETSHORT(rdlen, p); | |
323 | - | |
324 | - if ((type == T_NSEC || type == T_NSEC3 || type == T_RRSIG) && | |
325 | - (type != qtype || class != qclass)) | |
326 | - { | |
327 | - if (!expand_workspace(&rrs, &rr_sz, rr_found + 1)) | |
328 | - return plen; | |
329 | - | |
330 | - rrs[rr_found++] = pstart; | |
331 | - | |
332 | - if (!ADD_RDLEN(header, p, plen, rdlen)) | |
333 | - return plen; | |
334 | - | |
335 | - rrs[rr_found++] = p; | |
336 | - | |
337 | - if (i < ntohs(header->ancount)) | |
338 | - chop_an++; | |
339 | - else if (i < (ntohs(header->nscount) + ntohs(header->ancount))) | |
340 | - chop_ns++; | |
341 | - else | |
342 | - chop_ar++; | |
343 | - } | |
344 | - else if (!ADD_RDLEN(header, p, plen, rdlen)) | |
345 | - return plen; | |
346 | - } | |
347 | - | |
348 | - /* Nothing to do. */ | |
349 | - if (rr_found == 0) | |
350 | - return plen; | |
351 | - | |
352 | - /* Second pass, look for pointers in names in the records we're keeping and make sure they don't | |
353 | - point to records we're going to elide. This is theoretically possible, but unlikely. If | |
354 | - it happens, we give up and leave the answer unchanged. */ | |
355 | - p = (unsigned char *)(header+1); | |
356 | - | |
357 | - /* question first */ | |
358 | - if (!check_name(&p, header, plen, 0, rrs, rr_found)) | |
359 | - return plen; | |
360 | - p += 4; /* qclass, qtype */ | |
361 | - | |
362 | - /* Now answers and NS */ | |
363 | - if (!check_rrs(p, header, plen, 0, rrs, rr_found)) | |
364 | - return plen; | |
365 | - | |
366 | - /* Third pass, elide records */ | |
367 | - for (p = rrs[0], i = 1; i < rr_found; i += 2) | |
368 | - { | |
369 | - unsigned char *start = rrs[i]; | |
370 | - unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen; | |
371 | - | |
372 | - memmove(p, start, end-start); | |
373 | - p += end-start; | |
374 | - } | |
375 | - | |
376 | - plen = p - (unsigned char *)header; | |
377 | - header->ancount = htons(ntohs(header->ancount) - chop_an); | |
378 | - header->nscount = htons(ntohs(header->nscount) - chop_ns); | |
379 | - header->arcount = htons(ntohs(header->arcount) - chop_ar); | |
380 | - | |
381 | - /* Fourth pass, fix up pointers in the remaining records */ | |
382 | - p = (unsigned char *)(header+1); | |
383 | - | |
384 | - check_name(&p, header, plen, 1, rrs, rr_found); | |
385 | - p += 4; /* qclass, qtype */ | |
386 | - | |
387 | - check_rrs(p, header, plen, 1, rrs, rr_found); | |
388 | - | |
389 | - return plen; | |
390 | -} | |
391 | - | |
392 | unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name) | |
393 | { | |
394 | int q; | |
395 | diff --git a/src/forward.c b/src/forward.c | |
396 | index dd22a62..3e801c8 100644 | |
397 | --- a/src/forward.c | |
398 | +++ b/src/forward.c | |
399 | @@ -662,7 +662,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server | |
400 | ||
401 | /* If the requestor didn't set the DO bit, don't return DNSSEC info. */ | |
402 | if (!do_bit) | |
403 | - n = filter_rrsigs(header, n); | |
404 | + n = rrfilter(header, n, 1); | |
405 | #endif | |
406 | ||
407 | /* do this after extract_addresses. Ensure NODATA reply and remove | |
408 | diff --git a/src/rrfilter.c b/src/rrfilter.c | |
409 | new file mode 100644 | |
410 | index 0000000..ae12261 | |
411 | --- /dev/null | |
412 | +++ b/src/rrfilter.c | |
413 | @@ -0,0 +1,339 @@ | |
414 | +/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley | |
415 | + | |
416 | + This program is free software; you can redistribute it and/or modify | |
417 | + it under the terms of the GNU General Public License as published by | |
418 | + the Free Software Foundation; version 2 dated June, 1991, or | |
419 | + (at your option) version 3 dated 29 June, 2007. | |
420 | + | |
421 | + This program is distributed in the hope that it will be useful, | |
422 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
423 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
424 | + GNU General Public License for more details. | |
425 | + | |
426 | + You should have received a copy of the GNU General Public License | |
427 | + along with this program. If not, see <http://www.gnu.org/licenses/>. | |
428 | +*/ | |
429 | + | |
430 | +/* Code to safely remove RRs from an DNS answer */ | |
431 | + | |
432 | +#include "dnsmasq.h" | |
433 | + | |
434 | +/* Go through a domain name, find "pointers" and fix them up based on how many bytes | |
435 | + we've chopped out of the packet, or check they don't point into an elided part. */ | |
436 | +static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count) | |
437 | +{ | |
438 | + unsigned char *ansp = *namep; | |
439 | + | |
440 | + while(1) | |
441 | + { | |
442 | + unsigned int label_type; | |
443 | + | |
444 | + if (!CHECK_LEN(header, ansp, plen, 1)) | |
445 | + return 0; | |
446 | + | |
447 | + label_type = (*ansp) & 0xc0; | |
448 | + | |
449 | + if (label_type == 0xc0) | |
450 | + { | |
451 | + /* pointer for compression. */ | |
452 | + unsigned int offset; | |
453 | + int i; | |
454 | + unsigned char *p; | |
455 | + | |
456 | + if (!CHECK_LEN(header, ansp, plen, 2)) | |
457 | + return 0; | |
458 | + | |
459 | + offset = ((*ansp++) & 0x3f) << 8; | |
460 | + offset |= *ansp++; | |
461 | + | |
462 | + p = offset + (unsigned char *)header; | |
463 | + | |
464 | + for (i = 0; i < rr_count; i++) | |
465 | + if (p < rrs[i]) | |
466 | + break; | |
467 | + else | |
468 | + if (i & 1) | |
469 | + offset -= rrs[i] - rrs[i-1]; | |
470 | + | |
471 | + /* does the pointer end up in an elided RR? */ | |
472 | + if (i & 1) | |
473 | + return 0; | |
474 | + | |
475 | + /* No, scale the pointer */ | |
476 | + if (fixup) | |
477 | + { | |
478 | + ansp -= 2; | |
479 | + *ansp++ = (offset >> 8) | 0xc0; | |
480 | + *ansp++ = offset & 0xff; | |
481 | + } | |
482 | + break; | |
483 | + } | |
484 | + else if (label_type == 0x80) | |
485 | + return 0; /* reserved */ | |
486 | + else if (label_type == 0x40) | |
487 | + { | |
488 | + /* Extended label type */ | |
489 | + unsigned int count; | |
490 | + | |
491 | + if (!CHECK_LEN(header, ansp, plen, 2)) | |
492 | + return 0; | |
493 | + | |
494 | + if (((*ansp++) & 0x3f) != 1) | |
495 | + return 0; /* we only understand bitstrings */ | |
496 | + | |
497 | + count = *(ansp++); /* Bits in bitstring */ | |
498 | + | |
499 | + if (count == 0) /* count == 0 means 256 bits */ | |
500 | + ansp += 32; | |
501 | + else | |
502 | + ansp += ((count-1)>>3)+1; | |
503 | + } | |
504 | + else | |
505 | + { /* label type == 0 Bottom six bits is length */ | |
506 | + unsigned int len = (*ansp++) & 0x3f; | |
507 | + | |
508 | + if (!ADD_RDLEN(header, ansp, plen, len)) | |
509 | + return 0; | |
510 | + | |
511 | + if (len == 0) | |
512 | + break; /* zero length label marks the end. */ | |
513 | + } | |
514 | + } | |
515 | + | |
516 | + *namep = ansp; | |
517 | + | |
518 | + return 1; | |
519 | +} | |
520 | + | |
521 | +/* Go through RRs and check or fixup the domain names contained within */ | |
522 | +static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count) | |
523 | +{ | |
524 | + int i, j, type, class, rdlen; | |
525 | + unsigned char *pp; | |
526 | + | |
527 | + for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++) | |
528 | + { | |
529 | + pp = p; | |
530 | + | |
531 | + if (!(p = skip_name(p, header, plen, 10))) | |
532 | + return 0; | |
533 | + | |
534 | + GETSHORT(type, p); | |
535 | + GETSHORT(class, p); | |
536 | + p += 4; /* TTL */ | |
537 | + GETSHORT(rdlen, p); | |
538 | + | |
539 | + /* If this RR is to be elided, don't fix up its contents */ | |
540 | + for (j = 0; j < rr_count; j += 2) | |
541 | + if (rrs[j] == pp) | |
542 | + break; | |
543 | + | |
544 | + if (j >= rr_count) | |
545 | + { | |
546 | + /* fixup name of RR */ | |
547 | + if (!check_name(&pp, header, plen, fixup, rrs, rr_count)) | |
548 | + return 0; | |
549 | + | |
550 | + if (class == C_IN) | |
551 | + { | |
552 | + u16 *d; | |
553 | + | |
554 | + for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++) | |
555 | + { | |
556 | + if (*d != 0) | |
557 | + pp += *d; | |
558 | + else if (!check_name(&pp, header, plen, fixup, rrs, rr_count)) | |
559 | + return 0; | |
560 | + } | |
561 | + } | |
562 | + } | |
563 | + | |
564 | + if (!ADD_RDLEN(header, p, plen, rdlen)) | |
565 | + return 0; | |
566 | + } | |
567 | + | |
568 | + return 1; | |
569 | +} | |
570 | + | |
571 | + | |
572 | +/* mode is 0 to remove EDNS0, 1 to filter DNSSEC RRs */ | |
573 | +size_t rrfilter(struct dns_header *header, size_t plen, int mode) | |
574 | +{ | |
575 | + static unsigned char **rrs; | |
576 | + static int rr_sz = 0; | |
577 | + | |
578 | + unsigned char *p = (unsigned char *)(header+1); | |
579 | + int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar; | |
580 | + | |
581 | + if (ntohs(header->qdcount) != 1 || | |
582 | + !(p = skip_name(p, header, plen, 4))) | |
583 | + return plen; | |
584 | + | |
585 | + GETSHORT(qtype, p); | |
586 | + GETSHORT(qclass, p); | |
587 | + | |
588 | + /* First pass, find pointers to start and end of all the records we wish to elide: | |
589 | + records added for DNSSEC, unless explicity queried for */ | |
590 | + for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0; | |
591 | + i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); | |
592 | + i++) | |
593 | + { | |
594 | + unsigned char *pstart = p; | |
595 | + int type, class; | |
596 | + | |
597 | + if (!(p = skip_name(p, header, plen, 10))) | |
598 | + return plen; | |
599 | + | |
600 | + GETSHORT(type, p); | |
601 | + GETSHORT(class, p); | |
602 | + p += 4; /* TTL */ | |
603 | + GETSHORT(rdlen, p); | |
604 | + | |
605 | + if (!ADD_RDLEN(header, p, plen, rdlen)) | |
606 | + return plen; | |
607 | + | |
608 | + /* Don't remove the answer. */ | |
609 | + if (i < ntohs(header->ancount) && type == qtype && class == qclass) | |
610 | + continue; | |
611 | + | |
612 | + if (mode == 0) /* EDNS */ | |
613 | + { | |
614 | + /* EDNS mode, remove T_OPT from additional section only */ | |
615 | + if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT) | |
616 | + continue; | |
617 | + } | |
618 | + else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG) | |
619 | + /* DNSSEC mode, remove SIGs and NSECs from all three sections. */ | |
620 | + continue; | |
621 | + | |
622 | + | |
623 | + if (!expand_workspace(&rrs, &rr_sz, rr_found + 1)) | |
624 | + return plen; | |
625 | + | |
626 | + rrs[rr_found++] = pstart; | |
627 | + rrs[rr_found++] = p; | |
628 | + | |
629 | + if (i < ntohs(header->ancount)) | |
630 | + chop_an++; | |
631 | + else if (i < (ntohs(header->nscount) + ntohs(header->ancount))) | |
632 | + chop_ns++; | |
633 | + else | |
634 | + chop_ar++; | |
635 | + } | |
636 | + | |
637 | + /* Nothing to do. */ | |
638 | + if (rr_found == 0) | |
639 | + return plen; | |
640 | + | |
641 | + /* Second pass, look for pointers in names in the records we're keeping and make sure they don't | |
642 | + point to records we're going to elide. This is theoretically possible, but unlikely. If | |
643 | + it happens, we give up and leave the answer unchanged. */ | |
644 | + p = (unsigned char *)(header+1); | |
645 | + | |
646 | + /* question first */ | |
647 | + if (!check_name(&p, header, plen, 0, rrs, rr_found)) | |
648 | + return plen; | |
649 | + p += 4; /* qclass, qtype */ | |
650 | + | |
651 | + /* Now answers and NS */ | |
652 | + if (!check_rrs(p, header, plen, 0, rrs, rr_found)) | |
653 | + return plen; | |
654 | + | |
655 | + /* Third pass, elide records */ | |
656 | + for (p = rrs[0], i = 1; i < rr_found; i += 2) | |
657 | + { | |
658 | + unsigned char *start = rrs[i]; | |
659 | + unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen; | |
660 | + | |
661 | + memmove(p, start, end-start); | |
662 | + p += end-start; | |
663 | + } | |
664 | + | |
665 | + plen = p - (unsigned char *)header; | |
666 | + header->ancount = htons(ntohs(header->ancount) - chop_an); | |
667 | + header->nscount = htons(ntohs(header->nscount) - chop_ns); | |
668 | + header->arcount = htons(ntohs(header->arcount) - chop_ar); | |
669 | + | |
670 | + /* Fourth pass, fix up pointers in the remaining records */ | |
671 | + p = (unsigned char *)(header+1); | |
672 | + | |
673 | + check_name(&p, header, plen, 1, rrs, rr_found); | |
674 | + p += 4; /* qclass, qtype */ | |
675 | + | |
676 | + check_rrs(p, header, plen, 1, rrs, rr_found); | |
677 | + | |
678 | + return plen; | |
679 | +} | |
680 | + | |
681 | +/* This is used in the DNSSEC code too, hence it's exported */ | |
682 | +u16 *rrfilter_desc(int type) | |
683 | +{ | |
684 | + /* List of RRtypes which include domains in the data. | |
685 | + 0 -> domain | |
686 | + integer -> no of plain bytes | |
687 | + -1 -> end | |
688 | + | |
689 | + zero is not a valid RRtype, so the final entry is returned for | |
690 | + anything which needs no mangling. | |
691 | + */ | |
692 | + | |
693 | + static u16 rr_desc[] = | |
694 | + { | |
695 | + T_NS, 0, -1, | |
696 | + T_MD, 0, -1, | |
697 | + T_MF, 0, -1, | |
698 | + T_CNAME, 0, -1, | |
699 | + T_SOA, 0, 0, -1, | |
700 | + T_MB, 0, -1, | |
701 | + T_MG, 0, -1, | |
702 | + T_MR, 0, -1, | |
703 | + T_PTR, 0, -1, | |
704 | + T_MINFO, 0, 0, -1, | |
705 | + T_MX, 2, 0, -1, | |
706 | + T_RP, 0, 0, -1, | |
707 | + T_AFSDB, 2, 0, -1, | |
708 | + T_RT, 2, 0, -1, | |
709 | + T_SIG, 18, 0, -1, | |
710 | + T_PX, 2, 0, 0, -1, | |
711 | + T_NXT, 0, -1, | |
712 | + T_KX, 2, 0, -1, | |
713 | + T_SRV, 6, 0, -1, | |
714 | + T_DNAME, 0, -1, | |
715 | + 0, -1 /* wildcard/catchall */ | |
716 | + }; | |
717 | + | |
718 | + u16 *p = rr_desc; | |
719 | + | |
720 | + while (*p != type && *p != 0) | |
721 | + while (*p++ != (u16)-1); | |
722 | + | |
723 | + return p+1; | |
724 | +} | |
725 | + | |
726 | +int expand_workspace(unsigned char ***wkspc, int *szp, int new) | |
727 | +{ | |
728 | + unsigned char **p; | |
729 | + int old = *szp; | |
730 | + | |
731 | + if (old >= new+1) | |
732 | + return 1; | |
733 | + | |
734 | + if (new >= 100) | |
735 | + return 0; | |
736 | + | |
737 | + new += 5; | |
738 | + | |
739 | + if (!(p = whine_malloc(new * sizeof(unsigned char **)))) | |
740 | + return 0; | |
741 | + | |
742 | + if (old != 0 && *wkspc) | |
743 | + { | |
744 | + memcpy(p, *wkspc, old * sizeof(unsigned char **)); | |
745 | + free(*wkspc); | |
746 | + } | |
747 | + | |
748 | + *wkspc = p; | |
749 | + *szp = new; | |
750 | + | |
751 | + return 1; | |
752 | +} | |
753 | -- | |
754 | 1.7.10.4 | |
755 |