From: Simon Kelley Date: Mon, 21 Dec 2015 14:17:06 +0000 (+0000) Subject: Split EDNS0 stuff into its own source file. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1d03016bbcb78962305cca20cbf7441423ff897d;p=people%2Fms%2Fdnsmasq.git Split EDNS0 stuff into its own source file. --- diff --git a/Makefile b/Makefile index b664160..dfb0347 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.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 rrfilter.o + poll.o rrfilter.o edns0.o hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ dns-protocol.h radv-protocol.h ip6addr.h diff --git a/bld/Android.mk b/bld/Android.mk index 67b9c4b..87966d2 100644 --- a/bld/Android.mk +++ b/bld/Android.mk @@ -10,7 +10,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \ 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 rrfilter.c + loop.c inotify.c poll.c rrfilter.c edns0.c LOCAL_MODULE := dnsmasq diff --git a/src/dnsmasq.h b/src/dnsmasq.h index abb34c5..a41c8cc 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1123,14 +1123,6 @@ int check_for_local_domain(char *name, time_t now); unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff); size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen); -size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, - unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do); -size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3); -size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source); -#ifdef HAVE_DNSSEC -size_t add_do_bit(struct dns_header *header, size_t plen, char *limit); -#endif -int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer); int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp, unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...); @@ -1521,3 +1513,12 @@ 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); +/* edns0.c */ +size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, + unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do); +size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3); +size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source); +#ifdef HAVE_DNSSEC +size_t add_do_bit(struct dns_header *header, size_t plen, char *limit); +#endif +int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer); diff --git a/src/edns0.c b/src/edns0.c new file mode 100644 index 0000000..f348b01 --- /dev/null +++ b/src/edns0.c @@ -0,0 +1,351 @@ +/* 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 . +*/ + +#include "dnsmasq.h" + +unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign) +{ + /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. + also return length of pseudoheader in *len and pointer to the UDP size in *p + Finally, check to see if a packet is signed. If it is we cannot change a single bit before + forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */ + + int i, arcount = ntohs(header->arcount); + unsigned char *ansp = (unsigned char *)(header+1); + unsigned short rdlen, type, class; + unsigned char *ret = NULL; + + if (is_sign) + { + *is_sign = 0; + + if (OPCODE(header) == QUERY) + { + for (i = ntohs(header->qdcount); i != 0; i--) + { + if (!(ansp = skip_name(ansp, header, plen, 4))) + return NULL; + + GETSHORT(type, ansp); + GETSHORT(class, ansp); + + if (class == C_IN && type == T_TKEY) + *is_sign = 1; + } + } + } + else + { + if (!(ansp = skip_questions(header, plen))) + return NULL; + } + + if (arcount == 0) + return NULL; + + if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen))) + return NULL; + + for (i = 0; i < arcount; i++) + { + unsigned char *save, *start = ansp; + if (!(ansp = skip_name(ansp, header, plen, 10))) + return NULL; + + GETSHORT(type, ansp); + save = ansp; + GETSHORT(class, ansp); + ansp += 4; /* TTL */ + GETSHORT(rdlen, ansp); + if (!ADD_RDLEN(header, ansp, plen, rdlen)) + return NULL; + if (type == T_OPT) + { + if (len) + *len = ansp - start; + if (p) + *p = save; + ret = start; + } + else if (is_sign && + i == arcount - 1 && + class == C_ANY && + type == T_TSIG) + *is_sign = 1; + } + + return ret; +} + +struct macparm { + unsigned char *limit; + struct dns_header *header; + size_t plen; + union mysockaddr *l3; +}; + +size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, + unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do) +{ + unsigned char *lenp, *datap, *p; + int rdlen, is_sign; + + if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign))) + { + if (is_sign) + return plen; + + /* We are adding the pseudoheader */ + if (!(p = skip_questions(header, plen)) || + !(p = skip_section(p, + ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), + header, plen))) + return plen; + *p++ = 0; /* empty name */ + PUTSHORT(T_OPT, p); + PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */ + PUTSHORT(0, p); /* extended RCODE and version */ + PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */ + lenp = p; + PUTSHORT(0, p); /* RDLEN */ + rdlen = 0; + if (((ssize_t)optlen) > (limit - (p + 4))) + return plen; /* Too big */ + header->arcount = htons(ntohs(header->arcount) + 1); + datap = p; + } + else + { + int i; + unsigned short code, len, flags; + + /* Must be at the end, if exists */ + if (ntohs(header->arcount) != 1 || + is_sign || + (!(p = skip_name(p, header, plen, 10)))) + return plen; + + p += 6; /* skip UDP length and RCODE */ + GETSHORT(flags, p); + if (set_do) + { + p -=2; + PUTSHORT(flags | 0x8000, p); + } + + lenp = p; + GETSHORT(rdlen, p); + if (!CHECK_LEN(header, p, plen, rdlen)) + return plen; /* bad packet */ + datap = p; + + /* no option to add */ + if (optno == 0) + return plen; + + /* check if option already there */ + for (i = 0; i + 4 < rdlen; i += len + 4) + { + GETSHORT(code, p); + GETSHORT(len, p); + if (code == optno) + return plen; + p += len; + } + + if (((ssize_t)optlen) > (limit - (p + 4))) + return plen; /* Too big */ + } + + if (optno != 0) + { + PUTSHORT(optno, p); + PUTSHORT(optlen, p); + memcpy(p, opt, optlen); + p += optlen; + } + + PUTSHORT(p - datap, lenp); + return p - (unsigned char *)header; + +} + +static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) +{ + struct macparm *parm = parmv; + int match = 0; + + if (family == parm->l3->sa.sa_family) + { + if (family == AF_INET && memcmp(&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0) + match = 1; +#ifdef HAVE_IPV6 + else + if (family == AF_INET6 && memcmp(&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0) + match = 1; +#endif + } + + if (!match) + return 1; /* continue */ + + parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, PACKETSZ, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0); + + return 0; /* done */ +} + +size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3) +{ + struct macparm parm; + + parm.header = header; + parm.limit = (unsigned char *)limit; + parm.plen = plen; + parm.l3 = l3; + + iface_enumerate(AF_UNSPEC, &parm, filter_mac); + + return parm.plen; +} + +struct subnet_opt { + u16 family; + u8 source_netmask, scope_netmask; +#ifdef HAVE_IPV6 + u8 addr[IN6ADDRSZ]; +#else + u8 addr[INADDRSZ]; +#endif +}; + +static void *get_addrp(union mysockaddr *addr, const short family) +{ +#ifdef HAVE_IPV6 + if (family == AF_INET6) + return &addr->in6.sin6_addr; +#endif + + return &addr->in.sin_addr; +} + +static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) +{ + /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ + + int len; + void *addrp; + int sa_family = source->sa.sa_family; + +#ifdef HAVE_IPV6 + if (source->sa.sa_family == AF_INET6) + { + opt->source_netmask = daemon->add_subnet6->mask; + if (daemon->add_subnet6->addr_used) + { + sa_family = daemon->add_subnet6->addr.sa.sa_family; + addrp = get_addrp(&daemon->add_subnet6->addr, sa_family); + } + else + addrp = &source->in6.sin6_addr; + } + else +#endif + { + opt->source_netmask = daemon->add_subnet4->mask; + if (daemon->add_subnet4->addr_used) + { + sa_family = daemon->add_subnet4->addr.sa.sa_family; + addrp = get_addrp(&daemon->add_subnet4->addr, sa_family); + } + else + addrp = &source->in.sin_addr; + } + + opt->scope_netmask = 0; + len = 0; + + if (opt->source_netmask != 0) + { +#ifdef HAVE_IPV6 + opt->family = htons(sa_family == AF_INET6 ? 2 : 1); +#else + opt->family = htons(1); +#endif + len = ((opt->source_netmask - 1) >> 3) + 1; + memcpy(opt->addr, addrp, len); + if (opt->source_netmask & 7) + opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7)); + } + + return len + 4; +} + +size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source) +{ + /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ + + int len; + struct subnet_opt opt; + + len = calc_subnet_opt(&opt, source); + return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0); +} + +#ifdef HAVE_DNSSEC +size_t add_do_bit(struct dns_header *header, size_t plen, char *limit) +{ + return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1); +} +#endif + +int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer) +{ + /* Section 9.2, Check that subnet option in reply matches. */ + + + int len, calc_len; + struct subnet_opt opt; + unsigned char *p; + int code, i, rdlen; + + calc_len = calc_subnet_opt(&opt, peer); + + if (!(p = skip_name(pseudoheader, header, plen, 10))) + return 1; + + p += 8; /* skip UDP length and RCODE */ + + GETSHORT(rdlen, p); + if (!CHECK_LEN(header, p, plen, rdlen)) + return 1; /* bad packet */ + + /* check if option there */ + for (i = 0; i + 4 < rdlen; i += len + 4) + { + GETSHORT(code, p); + GETSHORT(len, p); + if (code == EDNS0_OPTION_CLIENT_SUBNET) + { + /* make sure this doesn't mismatch. */ + opt.scope_netmask = p[3]; + if (len != calc_len || memcmp(p, &opt, len) != 0) + return 0; + } + p += len; + } + + return 1; +} diff --git a/src/rfc1035.c b/src/rfc1035.c index 18858a8..5d89287 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -408,340 +408,6 @@ size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *phea return ansp - (unsigned char *)header; } -unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign) -{ - /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. - also return length of pseudoheader in *len and pointer to the UDP size in *p - Finally, check to see if a packet is signed. If it is we cannot change a single bit before - forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */ - - int i, arcount = ntohs(header->arcount); - unsigned char *ansp = (unsigned char *)(header+1); - unsigned short rdlen, type, class; - unsigned char *ret = NULL; - - if (is_sign) - { - *is_sign = 0; - - if (OPCODE(header) == QUERY) - { - for (i = ntohs(header->qdcount); i != 0; i--) - { - if (!(ansp = skip_name(ansp, header, plen, 4))) - return NULL; - - GETSHORT(type, ansp); - GETSHORT(class, ansp); - - if (class == C_IN && type == T_TKEY) - *is_sign = 1; - } - } - } - else - { - if (!(ansp = skip_questions(header, plen))) - return NULL; - } - - if (arcount == 0) - return NULL; - - if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen))) - return NULL; - - for (i = 0; i < arcount; i++) - { - unsigned char *save, *start = ansp; - if (!(ansp = skip_name(ansp, header, plen, 10))) - return NULL; - - GETSHORT(type, ansp); - save = ansp; - GETSHORT(class, ansp); - ansp += 4; /* TTL */ - GETSHORT(rdlen, ansp); - if (!ADD_RDLEN(header, ansp, plen, rdlen)) - return NULL; - if (type == T_OPT) - { - if (len) - *len = ansp - start; - if (p) - *p = save; - ret = start; - } - else if (is_sign && - i == arcount - 1 && - class == C_ANY && - type == T_TSIG) - *is_sign = 1; - } - - return ret; -} - -struct macparm { - unsigned char *limit; - struct dns_header *header; - size_t plen; - union mysockaddr *l3; -}; - -size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, - unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do) -{ - unsigned char *lenp, *datap, *p; - int rdlen, is_sign; - - if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign))) - { - if (is_sign) - return plen; - - /* We are adding the pseudoheader */ - if (!(p = skip_questions(header, plen)) || - !(p = skip_section(p, - ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), - header, plen))) - return plen; - *p++ = 0; /* empty name */ - PUTSHORT(T_OPT, p); - PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */ - PUTSHORT(0, p); /* extended RCODE and version */ - PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */ - lenp = p; - PUTSHORT(0, p); /* RDLEN */ - rdlen = 0; - if (((ssize_t)optlen) > (limit - (p + 4))) - return plen; /* Too big */ - header->arcount = htons(ntohs(header->arcount) + 1); - datap = p; - } - else - { - int i; - unsigned short code, len, flags; - - /* Must be at the end, if exists */ - if (ntohs(header->arcount) != 1 || - is_sign || - (!(p = skip_name(p, header, plen, 10)))) - return plen; - - p += 6; /* skip UDP length and RCODE */ - GETSHORT(flags, p); - if (set_do) - { - p -=2; - PUTSHORT(flags | 0x8000, p); - } - - lenp = p; - GETSHORT(rdlen, p); - if (!CHECK_LEN(header, p, plen, rdlen)) - return plen; /* bad packet */ - datap = p; - - /* no option to add */ - if (optno == 0) - return plen; - - /* check if option already there */ - for (i = 0; i + 4 < rdlen; i += len + 4) - { - GETSHORT(code, p); - GETSHORT(len, p); - if (code == optno) - return plen; - p += len; - } - - if (((ssize_t)optlen) > (limit - (p + 4))) - return plen; /* Too big */ - } - - if (optno != 0) - { - PUTSHORT(optno, p); - PUTSHORT(optlen, p); - memcpy(p, opt, optlen); - p += optlen; - } - - PUTSHORT(p - datap, lenp); - return p - (unsigned char *)header; - -} - -static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) -{ - struct macparm *parm = parmv; - int match = 0; - - if (family == parm->l3->sa.sa_family) - { - if (family == AF_INET && memcmp(&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0) - match = 1; -#ifdef HAVE_IPV6 - else - if (family == AF_INET6 && memcmp(&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0) - match = 1; -#endif - } - - if (!match) - return 1; /* continue */ - - parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, PACKETSZ, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0); - - return 0; /* done */ -} - -size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3) -{ - struct macparm parm; - - parm.header = header; - parm.limit = (unsigned char *)limit; - parm.plen = plen; - parm.l3 = l3; - - iface_enumerate(AF_UNSPEC, &parm, filter_mac); - - return parm.plen; -} - -struct subnet_opt { - u16 family; - u8 source_netmask, scope_netmask; -#ifdef HAVE_IPV6 - u8 addr[IN6ADDRSZ]; -#else - u8 addr[INADDRSZ]; -#endif -}; - -static void *get_addrp(union mysockaddr *addr, const short family) -{ -#ifdef HAVE_IPV6 - if (family == AF_INET6) - return &addr->in6.sin6_addr; -#endif - - return &addr->in.sin_addr; -} - -static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) -{ - /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ - - int len; - void *addrp; - int sa_family = source->sa.sa_family; - -#ifdef HAVE_IPV6 - if (source->sa.sa_family == AF_INET6) - { - opt->source_netmask = daemon->add_subnet6->mask; - if (daemon->add_subnet6->addr_used) - { - sa_family = daemon->add_subnet6->addr.sa.sa_family; - addrp = get_addrp(&daemon->add_subnet6->addr, sa_family); - } - else - addrp = &source->in6.sin6_addr; - } - else -#endif - { - opt->source_netmask = daemon->add_subnet4->mask; - if (daemon->add_subnet4->addr_used) - { - sa_family = daemon->add_subnet4->addr.sa.sa_family; - addrp = get_addrp(&daemon->add_subnet4->addr, sa_family); - } - else - addrp = &source->in.sin_addr; - } - - opt->scope_netmask = 0; - len = 0; - - if (opt->source_netmask != 0) - { -#ifdef HAVE_IPV6 - opt->family = htons(sa_family == AF_INET6 ? 2 : 1); -#else - opt->family = htons(1); -#endif - len = ((opt->source_netmask - 1) >> 3) + 1; - memcpy(opt->addr, addrp, len); - if (opt->source_netmask & 7) - opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7)); - } - - return len + 4; -} - -size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source) -{ - /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ - - int len; - struct subnet_opt opt; - - len = calc_subnet_opt(&opt, source); - return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0); -} - -#ifdef HAVE_DNSSEC -size_t add_do_bit(struct dns_header *header, size_t plen, char *limit) -{ - return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1); -} -#endif - -int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer) -{ - /* Section 9.2, Check that subnet option in reply matches. */ - - - int len, calc_len; - struct subnet_opt opt; - unsigned char *p; - int code, i, rdlen; - - calc_len = calc_subnet_opt(&opt, peer); - - if (!(p = skip_name(pseudoheader, header, plen, 10))) - return 1; - - p += 8; /* skip UDP length and RCODE */ - - GETSHORT(rdlen, p); - if (!CHECK_LEN(header, p, plen, rdlen)) - return 1; /* bad packet */ - - /* check if option there */ - for (i = 0; i + 4 < rdlen; i += len + 4) - { - GETSHORT(code, p); - GETSHORT(len, p); - if (code == EDNS0_OPTION_CLIENT_SUBNET) - { - /* make sure this doesn't mismatch. */ - opt.scope_netmask = p[3]; - if (len != calc_len || memcmp(p, &opt, len) != 0) - return 0; - } - p += len; - } - - return 1; -} - /* is addr in the non-globally-routed IP space? */ int private_net(struct in_addr addr, int ban_localhost) {