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.
10 src/dnssec.c | 307 +-------------------------------------------------
12 src/rrfilter.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 6 files changed, 349 insertions(+), 309 deletions(-)
14 create mode 100644 src/rrfilter.c
16 diff --git a/Makefile b/Makefile
17 index 4c87ea9..b664160 100644
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 \
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
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
41 LOCAL_MODULE := dnsmasq
43 diff --git a/src/dnsmasq.h b/src/dnsmasq.h
44 index 4344cae..39a930c 100644
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);
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);
56 diff --git a/src/dnssec.c b/src/dnssec.c
57 index 359231f..fa3eb81 100644
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;
64 -static u16 *get_desc(int type)
66 - /* List of RRtypes which include domains in the data.
68 - integer -> no of plain bytes
71 - zero is not a valid RRtype, so the final entry is returned for
72 - anything which needs no mangling.
75 - static u16 rr_desc[] =
97 - 0, -1 /* wildcard/catchall */
102 - while (*p != type && *p != 0)
103 - while (*p++ != (u16)-1);
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,
115 -static int expand_workspace(unsigned char ***wkspc, int *szp, int new)
128 - if (!(p = whine_malloc(new * sizeof(unsigned char **))))
131 - if (old != 0 && *wkspc)
133 - memcpy(p, *wkspc, old * sizeof(unsigned char **));
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);
154 *wildcard_out = NULL;
155 @@ -2266,239 +2194,6 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
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)
163 - unsigned char *ansp = *namep;
167 - unsigned int label_type;
169 - if (!CHECK_LEN(header, ansp, plen, 1))
172 - label_type = (*ansp) & 0xc0;
174 - if (label_type == 0xc0)
176 - /* pointer for compression. */
177 - unsigned int offset;
181 - if (!CHECK_LEN(header, ansp, plen, 2))
184 - offset = ((*ansp++) & 0x3f) << 8;
187 - p = offset + (unsigned char *)header;
189 - for (i = 0; i < rr_count; i++)
194 - offset -= rrs[i] - rrs[i-1];
196 - /* does the pointer end up in an elided RR? */
200 - /* No, scale the pointer */
204 - *ansp++ = (offset >> 8) | 0xc0;
205 - *ansp++ = offset & 0xff;
209 - else if (label_type == 0x80)
210 - return 0; /* reserved */
211 - else if (label_type == 0x40)
213 - /* Extended label type */
214 - unsigned int count;
216 - if (!CHECK_LEN(header, ansp, plen, 2))
219 - if (((*ansp++) & 0x3f) != 1)
220 - return 0; /* we only understand bitstrings */
222 - count = *(ansp++); /* Bits in bitstring */
224 - if (count == 0) /* count == 0 means 256 bits */
227 - ansp += ((count-1)>>3)+1;
230 - { /* label type == 0 Bottom six bits is length */
231 - unsigned int len = (*ansp++) & 0x3f;
233 - if (!ADD_RDLEN(header, ansp, plen, len))
237 - break; /* zero length label marks the end. */
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)
249 - int i, type, class, rdlen;
252 - for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
256 - if (!(p = skip_name(p, header, plen, 10)))
260 - GETSHORT(class, p);
262 - GETSHORT(rdlen, p);
264 - if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
266 - /* fixup name of RR */
267 - if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
274 - for (pp = p, d = get_desc(type); *d != (u16)-1; d++)
278 - else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
284 - if (!ADD_RDLEN(header, p, plen, rdlen))
292 -size_t filter_rrsigs(struct dns_header *header, size_t plen)
294 - static unsigned char **rrs;
295 - static int rr_sz = 0;
297 - unsigned char *p = (unsigned char *)(header+1);
298 - int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
300 - if (ntohs(header->qdcount) != 1 ||
301 - !(p = skip_name(p, header, plen, 4)))
304 - GETSHORT(qtype, p);
305 - GETSHORT(qclass, p);
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);
313 - unsigned char *pstart = p;
316 - if (!(p = skip_name(p, header, plen, 10)))
320 - GETSHORT(class, p);
322 - GETSHORT(rdlen, p);
324 - if ((type == T_NSEC || type == T_NSEC3 || type == T_RRSIG) &&
325 - (type != qtype || class != qclass))
327 - if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
330 - rrs[rr_found++] = pstart;
332 - if (!ADD_RDLEN(header, p, plen, rdlen))
335 - rrs[rr_found++] = p;
337 - if (i < ntohs(header->ancount))
339 - else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
344 - else if (!ADD_RDLEN(header, p, plen, rdlen))
348 - /* Nothing to do. */
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);
357 - /* question first */
358 - if (!check_name(&p, header, plen, 0, rrs, rr_found))
360 - p += 4; /* qclass, qtype */
362 - /* Now answers and NS */
363 - if (!check_rrs(p, header, plen, 0, rrs, rr_found))
366 - /* Third pass, elide records */
367 - for (p = rrs[0], i = 1; i < rr_found; i += 2)
369 - unsigned char *start = rrs[i];
370 - unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen;
372 - memmove(p, start, end-start);
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);
381 - /* Fourth pass, fix up pointers in the remaining records */
382 - p = (unsigned char *)(header+1);
384 - check_name(&p, header, plen, 1, rrs, rr_found);
385 - p += 4; /* qclass, qtype */
387 - check_rrs(p, header, plen, 1, rrs, rr_found);
392 unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name)
395 diff --git a/src/forward.c b/src/forward.c
396 index dd22a62..3e801c8 100644
399 @@ -662,7 +662,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
401 /* If the requestor didn't set the DO bit, don't return DNSSEC info. */
403 - n = filter_rrsigs(header, n);
404 + n = rrfilter(header, n, 1);
407 /* do this after extract_addresses. Ensure NODATA reply and remove
408 diff --git a/src/rrfilter.c b/src/rrfilter.c
410 index 0000000..ae12261
414 +/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
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.
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.
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/>.
430 +/* Code to safely remove RRs from an DNS answer */
432 +#include "dnsmasq.h"
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)
438 + unsigned char *ansp = *namep;
442 + unsigned int label_type;
444 + if (!CHECK_LEN(header, ansp, plen, 1))
447 + label_type = (*ansp) & 0xc0;
449 + if (label_type == 0xc0)
451 + /* pointer for compression. */
452 + unsigned int offset;
456 + if (!CHECK_LEN(header, ansp, plen, 2))
459 + offset = ((*ansp++) & 0x3f) << 8;
462 + p = offset + (unsigned char *)header;
464 + for (i = 0; i < rr_count; i++)
469 + offset -= rrs[i] - rrs[i-1];
471 + /* does the pointer end up in an elided RR? */
475 + /* No, scale the pointer */
479 + *ansp++ = (offset >> 8) | 0xc0;
480 + *ansp++ = offset & 0xff;
484 + else if (label_type == 0x80)
485 + return 0; /* reserved */
486 + else if (label_type == 0x40)
488 + /* Extended label type */
489 + unsigned int count;
491 + if (!CHECK_LEN(header, ansp, plen, 2))
494 + if (((*ansp++) & 0x3f) != 1)
495 + return 0; /* we only understand bitstrings */
497 + count = *(ansp++); /* Bits in bitstring */
499 + if (count == 0) /* count == 0 means 256 bits */
502 + ansp += ((count-1)>>3)+1;
505 + { /* label type == 0 Bottom six bits is length */
506 + unsigned int len = (*ansp++) & 0x3f;
508 + if (!ADD_RDLEN(header, ansp, plen, len))
512 + break; /* zero length label marks the end. */
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)
524 + int i, j, type, class, rdlen;
527 + for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
531 + if (!(p = skip_name(p, header, plen, 10)))
535 + GETSHORT(class, p);
537 + GETSHORT(rdlen, p);
539 + /* If this RR is to be elided, don't fix up its contents */
540 + for (j = 0; j < rr_count; j += 2)
546 + /* fixup name of RR */
547 + if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
554 + for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++)
558 + else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
564 + if (!ADD_RDLEN(header, p, plen, rdlen))
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)
575 + static unsigned char **rrs;
576 + static int rr_sz = 0;
578 + unsigned char *p = (unsigned char *)(header+1);
579 + int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
581 + if (ntohs(header->qdcount) != 1 ||
582 + !(p = skip_name(p, header, plen, 4)))
585 + GETSHORT(qtype, p);
586 + GETSHORT(qclass, p);
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);
594 + unsigned char *pstart = p;
597 + if (!(p = skip_name(p, header, plen, 10)))
601 + GETSHORT(class, p);
603 + GETSHORT(rdlen, p);
605 + if (!ADD_RDLEN(header, p, plen, rdlen))
608 + /* Don't remove the answer. */
609 + if (i < ntohs(header->ancount) && type == qtype && class == qclass)
612 + if (mode == 0) /* EDNS */
614 + /* EDNS mode, remove T_OPT from additional section only */
615 + if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
618 + else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
619 + /* DNSSEC mode, remove SIGs and NSECs from all three sections. */
623 + if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
626 + rrs[rr_found++] = pstart;
627 + rrs[rr_found++] = p;
629 + if (i < ntohs(header->ancount))
631 + else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
637 + /* Nothing to do. */
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);
646 + /* question first */
647 + if (!check_name(&p, header, plen, 0, rrs, rr_found))
649 + p += 4; /* qclass, qtype */
651 + /* Now answers and NS */
652 + if (!check_rrs(p, header, plen, 0, rrs, rr_found))
655 + /* Third pass, elide records */
656 + for (p = rrs[0], i = 1; i < rr_found; i += 2)
658 + unsigned char *start = rrs[i];
659 + unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen;
661 + memmove(p, start, end-start);
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);
670 + /* Fourth pass, fix up pointers in the remaining records */
671 + p = (unsigned char *)(header+1);
673 + check_name(&p, header, plen, 1, rrs, rr_found);
674 + p += 4; /* qclass, qtype */
676 + check_rrs(p, header, plen, 1, rrs, rr_found);
681 +/* This is used in the DNSSEC code too, hence it's exported */
682 +u16 *rrfilter_desc(int type)
684 + /* List of RRtypes which include domains in the data.
686 + integer -> no of plain bytes
689 + zero is not a valid RRtype, so the final entry is returned for
690 + anything which needs no mangling.
693 + static u16 rr_desc[] =
715 + 0, -1 /* wildcard/catchall */
720 + while (*p != type && *p != 0)
721 + while (*p++ != (u16)-1);
726 +int expand_workspace(unsigned char ***wkspc, int *szp, int new)
739 + if (!(p = whine_malloc(new * sizeof(unsigned char **))))
742 + if (old != 0 && *wkspc)
744 + memcpy(p, *wkspc, old * sizeof(unsigned char **));