dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
- domain.o dnssec.o blockdata.o tables.o loop.o inotify.o poll.o
+ domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
+ poll.o rrfilter.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c slaac.c auth.c ipset.c domain.c \
dnssec.c dnssec-openssl.c blockdata.c tables.c \
- loop.c inotify.c poll.c
+ loop.c inotify.c poll.c rrfilter.c
LOCAL_MODULE := dnsmasq
void poll_listen(int fd, short event);
int do_poll(int timeout);
+/* rrfilter.c */
+size_t rrfilter(struct dns_header *header, size_t plen, int mode);
+u16 *rrfilter_desc(int type);
+int expand_workspace(unsigned char ***wkspc, int *szp, int new);
+
&& serial_compare_32(curtime, date_end) == SERIAL_LT;
}
-static u16 *get_desc(int type)
-{
- /* List of RRtypes which include domains in the data.
- 0 -> domain
- integer -> no of plain bytes
- -1 -> end
-
- zero is not a valid RRtype, so the final entry is returned for
- anything which needs no mangling.
- */
-
- static u16 rr_desc[] =
- {
- T_NS, 0, -1,
- T_MD, 0, -1,
- T_MF, 0, -1,
- T_CNAME, 0, -1,
- T_SOA, 0, 0, -1,
- T_MB, 0, -1,
- T_MG, 0, -1,
- T_MR, 0, -1,
- T_PTR, 0, -1,
- T_MINFO, 0, 0, -1,
- T_MX, 2, 0, -1,
- T_RP, 0, 0, -1,
- T_AFSDB, 2, 0, -1,
- T_RT, 2, 0, -1,
- T_SIG, 18, 0, -1,
- T_PX, 2, 0, 0, -1,
- T_NXT, 0, -1,
- T_KX, 2, 0, -1,
- T_SRV, 6, 0, -1,
- T_DNAME, 0, -1,
- 0, -1 /* wildcard/catchall */
- };
-
- u16 *p = rr_desc;
-
- while (*p != type && *p != 0)
- while (*p++ != (u16)-1);
-
- return p+1;
-}
-
/* Return bytes of canonicalised rdata, when the return value is zero, the remaining
data, pointed to by *p, should be used raw. */
static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen,
}
}
-static int expand_workspace(unsigned char ***wkspc, int *szp, int new)
-{
- unsigned char **p;
- int old = *szp;
-
- if (old >= new+1)
- return 1;
-
- if (new >= 100)
- return 0;
-
- new += 5;
-
- if (!(p = whine_malloc(new * sizeof(unsigned char **))))
- return 0;
-
- if (old != 0 && *wkspc)
- {
- memcpy(p, *wkspc, old * sizeof(unsigned char **));
- free(*wkspc);
- }
-
- *wkspc = p;
- *szp = new;
-
- return 1;
-}
-
/* Bubble sort the RRset into the canonical order.
Note that the byte-streams from two RRs may get unsynced: consider
RRs which have two domain-names at the start and then other data.
int rdlen, j, name_labels;
struct crec *crecp = NULL;
int algo, labels, orig_ttl, key_tag;
- u16 *rr_desc = get_desc(type);
+ u16 *rr_desc = rrfilter_desc(type);
if (wildcard_out)
*wildcard_out = NULL;
return ret;
}
-/* Go through a domain name, find "pointers" and fix them up based on how many bytes
- we've chopped out of the packet, or check they don't point into an elided part. */
-static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
-{
- unsigned char *ansp = *namep;
-
- while(1)
- {
- unsigned int label_type;
-
- if (!CHECK_LEN(header, ansp, plen, 1))
- return 0;
-
- label_type = (*ansp) & 0xc0;
-
- if (label_type == 0xc0)
- {
- /* pointer for compression. */
- unsigned int offset;
- int i;
- unsigned char *p;
-
- if (!CHECK_LEN(header, ansp, plen, 2))
- return 0;
-
- offset = ((*ansp++) & 0x3f) << 8;
- offset |= *ansp++;
-
- p = offset + (unsigned char *)header;
-
- for (i = 0; i < rr_count; i++)
- if (p < rrs[i])
- break;
- else
- if (i & 1)
- offset -= rrs[i] - rrs[i-1];
-
- /* does the pointer end up in an elided RR? */
- if (i & 1)
- return 0;
-
- /* No, scale the pointer */
- if (fixup)
- {
- ansp -= 2;
- *ansp++ = (offset >> 8) | 0xc0;
- *ansp++ = offset & 0xff;
- }
- break;
- }
- else if (label_type == 0x80)
- return 0; /* reserved */
- else if (label_type == 0x40)
- {
- /* Extended label type */
- unsigned int count;
-
- if (!CHECK_LEN(header, ansp, plen, 2))
- return 0;
-
- if (((*ansp++) & 0x3f) != 1)
- return 0; /* we only understand bitstrings */
-
- count = *(ansp++); /* Bits in bitstring */
-
- if (count == 0) /* count == 0 means 256 bits */
- ansp += 32;
- else
- ansp += ((count-1)>>3)+1;
- }
- else
- { /* label type == 0 Bottom six bits is length */
- unsigned int len = (*ansp++) & 0x3f;
-
- if (!ADD_RDLEN(header, ansp, plen, len))
- return 0;
-
- if (len == 0)
- break; /* zero length label marks the end. */
- }
- }
-
- *namep = ansp;
-
- return 1;
-}
-
-/* Go through RRs and check or fixup the domain names contained within */
-static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
-{
- int i, type, class, rdlen;
- unsigned char *pp;
-
- for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
- {
- pp = p;
-
- if (!(p = skip_name(p, header, plen, 10)))
- return 0;
-
- GETSHORT(type, p);
- GETSHORT(class, p);
- p += 4; /* TTL */
- GETSHORT(rdlen, p);
-
- if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
- {
- /* fixup name of RR */
- if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
- return 0;
-
- if (class == C_IN)
- {
- u16 *d;
-
- for (pp = p, d = get_desc(type); *d != (u16)-1; d++)
- {
- if (*d != 0)
- pp += *d;
- else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
- return 0;
- }
- }
- }
-
- if (!ADD_RDLEN(header, p, plen, rdlen))
- return 0;
- }
-
- return 1;
-}
-
-
-size_t filter_rrsigs(struct dns_header *header, size_t plen)
-{
- static unsigned char **rrs;
- static int rr_sz = 0;
-
- unsigned char *p = (unsigned char *)(header+1);
- int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
-
- if (ntohs(header->qdcount) != 1 ||
- !(p = skip_name(p, header, plen, 4)))
- return plen;
-
- GETSHORT(qtype, p);
- GETSHORT(qclass, p);
-
- /* First pass, find pointers to start and end of all the records we wish to elide:
- records added for DNSSEC, unless explicity queried for */
- for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
- i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
- i++)
- {
- unsigned char *pstart = p;
- int type, class;
-
- if (!(p = skip_name(p, header, plen, 10)))
- return plen;
-
- GETSHORT(type, p);
- GETSHORT(class, p);
- p += 4; /* TTL */
- GETSHORT(rdlen, p);
-
- if ((type == T_NSEC || type == T_NSEC3 || type == T_RRSIG) &&
- (type != qtype || class != qclass))
- {
- if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
- return plen;
-
- rrs[rr_found++] = pstart;
-
- if (!ADD_RDLEN(header, p, plen, rdlen))
- return plen;
-
- rrs[rr_found++] = p;
-
- if (i < ntohs(header->ancount))
- chop_an++;
- else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
- chop_ns++;
- else
- chop_ar++;
- }
- else if (!ADD_RDLEN(header, p, plen, rdlen))
- return plen;
- }
-
- /* Nothing to do. */
- if (rr_found == 0)
- return plen;
-
- /* Second pass, look for pointers in names in the records we're keeping and make sure they don't
- point to records we're going to elide. This is theoretically possible, but unlikely. If
- it happens, we give up and leave the answer unchanged. */
- p = (unsigned char *)(header+1);
-
- /* question first */
- if (!check_name(&p, header, plen, 0, rrs, rr_found))
- return plen;
- p += 4; /* qclass, qtype */
-
- /* Now answers and NS */
- if (!check_rrs(p, header, plen, 0, rrs, rr_found))
- return plen;
-
- /* Third pass, elide records */
- for (p = rrs[0], i = 1; i < rr_found; i += 2)
- {
- unsigned char *start = rrs[i];
- unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen;
-
- memmove(p, start, end-start);
- p += end-start;
- }
-
- plen = p - (unsigned char *)header;
- header->ancount = htons(ntohs(header->ancount) - chop_an);
- header->nscount = htons(ntohs(header->nscount) - chop_ns);
- header->arcount = htons(ntohs(header->arcount) - chop_ar);
-
- /* Fourth pass, fix up pointers in the remaining records */
- p = (unsigned char *)(header+1);
-
- check_name(&p, header, plen, 1, rrs, rr_found);
- p += 4; /* qclass, qtype */
-
- check_rrs(p, header, plen, 1, rrs, rr_found);
-
- return plen;
-}
-
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name)
{
int q;
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
if (!do_bit)
- n = filter_rrsigs(header, n);
+ n = rrfilter(header, n, 1);
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove
--- /dev/null
+/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Code to safely remove RRs from an DNS answer */
+
+#include "dnsmasq.h"
+
+/* Go through a domain name, find "pointers" and fix them up based on how many bytes
+ we've chopped out of the packet, or check they don't point into an elided part. */
+static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
+{
+ unsigned char *ansp = *namep;
+
+ while(1)
+ {
+ unsigned int label_type;
+
+ if (!CHECK_LEN(header, ansp, plen, 1))
+ return 0;
+
+ label_type = (*ansp) & 0xc0;
+
+ if (label_type == 0xc0)
+ {
+ /* pointer for compression. */
+ unsigned int offset;
+ int i;
+ unsigned char *p;
+
+ if (!CHECK_LEN(header, ansp, plen, 2))
+ return 0;
+
+ offset = ((*ansp++) & 0x3f) << 8;
+ offset |= *ansp++;
+
+ p = offset + (unsigned char *)header;
+
+ for (i = 0; i < rr_count; i++)
+ if (p < rrs[i])
+ break;
+ else
+ if (i & 1)
+ offset -= rrs[i] - rrs[i-1];
+
+ /* does the pointer end up in an elided RR? */
+ if (i & 1)
+ return 0;
+
+ /* No, scale the pointer */
+ if (fixup)
+ {
+ ansp -= 2;
+ *ansp++ = (offset >> 8) | 0xc0;
+ *ansp++ = offset & 0xff;
+ }
+ break;
+ }
+ else if (label_type == 0x80)
+ return 0; /* reserved */
+ else if (label_type == 0x40)
+ {
+ /* Extended label type */
+ unsigned int count;
+
+ if (!CHECK_LEN(header, ansp, plen, 2))
+ return 0;
+
+ if (((*ansp++) & 0x3f) != 1)
+ return 0; /* we only understand bitstrings */
+
+ count = *(ansp++); /* Bits in bitstring */
+
+ if (count == 0) /* count == 0 means 256 bits */
+ ansp += 32;
+ else
+ ansp += ((count-1)>>3)+1;
+ }
+ else
+ { /* label type == 0 Bottom six bits is length */
+ unsigned int len = (*ansp++) & 0x3f;
+
+ if (!ADD_RDLEN(header, ansp, plen, len))
+ return 0;
+
+ if (len == 0)
+ break; /* zero length label marks the end. */
+ }
+ }
+
+ *namep = ansp;
+
+ return 1;
+}
+
+/* Go through RRs and check or fixup the domain names contained within */
+static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
+{
+ int i, j, type, class, rdlen;
+ unsigned char *pp;
+
+ for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
+ {
+ pp = p;
+
+ if (!(p = skip_name(p, header, plen, 10)))
+ return 0;
+
+ GETSHORT(type, p);
+ GETSHORT(class, p);
+ p += 4; /* TTL */
+ GETSHORT(rdlen, p);
+
+ /* If this RR is to be elided, don't fix up its contents */
+ for (j = 0; j < rr_count; j += 2)
+ if (rrs[j] == pp)
+ break;
+
+ if (j >= rr_count)
+ {
+ /* fixup name of RR */
+ if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
+ return 0;
+
+ if (class == C_IN)
+ {
+ u16 *d;
+
+ for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++)
+ {
+ if (*d != 0)
+ pp += *d;
+ else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
+ return 0;
+ }
+ }
+ }
+
+ if (!ADD_RDLEN(header, p, plen, rdlen))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* mode is 0 to remove EDNS0, 1 to filter DNSSEC RRs */
+size_t rrfilter(struct dns_header *header, size_t plen, int mode)
+{
+ static unsigned char **rrs;
+ static int rr_sz = 0;
+
+ unsigned char *p = (unsigned char *)(header+1);
+ int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
+
+ if (ntohs(header->qdcount) != 1 ||
+ !(p = skip_name(p, header, plen, 4)))
+ return plen;
+
+ GETSHORT(qtype, p);
+ GETSHORT(qclass, p);
+
+ /* First pass, find pointers to start and end of all the records we wish to elide:
+ records added for DNSSEC, unless explicity queried for */
+ for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
+ i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
+ i++)
+ {
+ unsigned char *pstart = p;
+ int type, class;
+
+ if (!(p = skip_name(p, header, plen, 10)))
+ return plen;
+
+ GETSHORT(type, p);
+ GETSHORT(class, p);
+ p += 4; /* TTL */
+ GETSHORT(rdlen, p);
+
+ if (!ADD_RDLEN(header, p, plen, rdlen))
+ return plen;
+
+ /* Don't remove the answer. */
+ if (i < ntohs(header->ancount) && type == qtype && class == qclass)
+ continue;
+
+ if (mode == 0) /* EDNS */
+ {
+ /* EDNS mode, remove T_OPT from additional section only */
+ if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
+ continue;
+ }
+ else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
+ /* DNSSEC mode, remove SIGs and NSECs from all three sections. */
+ continue;
+
+
+ if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
+ return plen;
+
+ rrs[rr_found++] = pstart;
+ rrs[rr_found++] = p;
+
+ if (i < ntohs(header->ancount))
+ chop_an++;
+ else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
+ chop_ns++;
+ else
+ chop_ar++;
+ }
+
+ /* Nothing to do. */
+ if (rr_found == 0)
+ return plen;
+
+ /* Second pass, look for pointers in names in the records we're keeping and make sure they don't
+ point to records we're going to elide. This is theoretically possible, but unlikely. If
+ it happens, we give up and leave the answer unchanged. */
+ p = (unsigned char *)(header+1);
+
+ /* question first */
+ if (!check_name(&p, header, plen, 0, rrs, rr_found))
+ return plen;
+ p += 4; /* qclass, qtype */
+
+ /* Now answers and NS */
+ if (!check_rrs(p, header, plen, 0, rrs, rr_found))
+ return plen;
+
+ /* Third pass, elide records */
+ for (p = rrs[0], i = 1; i < rr_found; i += 2)
+ {
+ unsigned char *start = rrs[i];
+ unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen;
+
+ memmove(p, start, end-start);
+ p += end-start;
+ }
+
+ plen = p - (unsigned char *)header;
+ header->ancount = htons(ntohs(header->ancount) - chop_an);
+ header->nscount = htons(ntohs(header->nscount) - chop_ns);
+ header->arcount = htons(ntohs(header->arcount) - chop_ar);
+
+ /* Fourth pass, fix up pointers in the remaining records */
+ p = (unsigned char *)(header+1);
+
+ check_name(&p, header, plen, 1, rrs, rr_found);
+ p += 4; /* qclass, qtype */
+
+ check_rrs(p, header, plen, 1, rrs, rr_found);
+
+ return plen;
+}
+
+/* This is used in the DNSSEC code too, hence it's exported */
+u16 *rrfilter_desc(int type)
+{
+ /* List of RRtypes which include domains in the data.
+ 0 -> domain
+ integer -> no of plain bytes
+ -1 -> end
+
+ zero is not a valid RRtype, so the final entry is returned for
+ anything which needs no mangling.
+ */
+
+ static u16 rr_desc[] =
+ {
+ T_NS, 0, -1,
+ T_MD, 0, -1,
+ T_MF, 0, -1,
+ T_CNAME, 0, -1,
+ T_SOA, 0, 0, -1,
+ T_MB, 0, -1,
+ T_MG, 0, -1,
+ T_MR, 0, -1,
+ T_PTR, 0, -1,
+ T_MINFO, 0, 0, -1,
+ T_MX, 2, 0, -1,
+ T_RP, 0, 0, -1,
+ T_AFSDB, 2, 0, -1,
+ T_RT, 2, 0, -1,
+ T_SIG, 18, 0, -1,
+ T_PX, 2, 0, 0, -1,
+ T_NXT, 0, -1,
+ T_KX, 2, 0, -1,
+ T_SRV, 6, 0, -1,
+ T_DNAME, 0, -1,
+ 0, -1 /* wildcard/catchall */
+ };
+
+ u16 *p = rr_desc;
+
+ while (*p != type && *p != 0)
+ while (*p++ != (u16)-1);
+
+ return p+1;
+}
+
+int expand_workspace(unsigned char ***wkspc, int *szp, int new)
+{
+ unsigned char **p;
+ int old = *szp;
+
+ if (old >= new+1)
+ return 1;
+
+ if (new >= 100)
+ return 0;
+
+ new += 5;
+
+ if (!(p = whine_malloc(new * sizeof(unsigned char **))))
+ return 0;
+
+ if (old != 0 && *wkspc)
+ {
+ memcpy(p, *wkspc, old * sizeof(unsigned char **));
+ free(*wkspc);
+ }
+
+ *wkspc = p;
+ *szp = new;
+
+ return 1;
+}