Only for testing - this won't compile!
Hi,
On 04.01.2016 17:42, Michael Tremer wrote:
is this still an issue?
For me: definitely YES.
The nightly builds seem to build fine...
I think this is because the nightly builds contain patches No. 001-29.
The problems came up after I integrated patch No. 030-041, as shown below.
Even the testrelease '2.76test2' wouldn't build (I haven't tested '276test3' yet, which came up a few hours ago).
Can anyone confirm?
Best, Matthias
Signed-off-by: Matthias Fischer matthias.fischer@ipfire.org --- lfs/dnsmasq | 12 + ...q-Add-support-to-read-ISC-DHCP-lease-file.patch | 6 +- ...plit_EDNS0_stuff_into_its_own_source_file.patch | 777 ++++++++++++++++ .../031-Handle_extending_EDNS0_OPT_RR.patch | 295 +++++++ ..._512_bytes_that_the_client_isnt_expecting.patch | 65 ++ ...ix_build_failure_when_DNSSEC_code_omitted.patch | 55 ++ ...go_with_DNSKEY_and_DS_also_digest_with_DS.patch | 81 ++ .../035-More_EDNS0_packet_size_tweaks.patch | 138 +++ ...036-Cache_access_to_the_kernels_ARP_table.patch | 414 +++++++++ ...DNS-client-id_EDNS0_and_ARP_tracking_code.patch | 976 +++++++++++++++++++++ ...38-Correct_logic_for_when_to_start_helper.patch | 25 + src/patches/dnsmasq/039-Trivial_code_tweak.patch | 33 + .../040-Extra_check_in_NSEC3_processing.patch | 60 ++ ...x_linked-list_botch_in_new_ARP-cache_code.patch | 55 ++ 14 files changed, 2989 insertions(+), 3 deletions(-) create mode 100644 src/patches/dnsmasq/030-Split_EDNS0_stuff_into_its_own_source_file.patch create mode 100644 src/patches/dnsmasq/031-Handle_extending_EDNS0_OPT_RR.patch create mode 100644 src/patches/dnsmasq/032-Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting.patch create mode 100644 src/patches/dnsmasq/033-Fix_build_failure_when_DNSSEC_code_omitted.patch create mode 100644 src/patches/dnsmasq/034-Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch create mode 100644 src/patches/dnsmasq/035-More_EDNS0_packet_size_tweaks.patch create mode 100644 src/patches/dnsmasq/036-Cache_access_to_the_kernels_ARP_table.patch create mode 100644 src/patches/dnsmasq/037-First_complete_version_of_DNS-client-id_EDNS0_and_ARP_tracking_code.patch create mode 100644 src/patches/dnsmasq/038-Correct_logic_for_when_to_start_helper.patch create mode 100644 src/patches/dnsmasq/039-Trivial_code_tweak.patch create mode 100644 src/patches/dnsmasq/040-Extra_check_in_NSEC3_processing.patch create mode 100644 src/patches/dnsmasq/041-Fix_linked-list_botch_in_new_ARP-cache_code.patch
diff --git a/lfs/dnsmasq b/lfs/dnsmasq index 8058663..d12a54a 100644 --- a/lfs/dnsmasq +++ b/lfs/dnsmasq @@ -102,6 +102,18 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects)) cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/030-Split_EDNS0_stuff_into_its_own_source_file.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/031-Handle_extending_EDNS0_OPT_RR.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/032-Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/033-Fix_build_failure_when_DNSSEC_code_omitted.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/034-Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/035-More_EDNS0_packet_size_tweaks.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/036-Cache_access_to_the_kernels_ARP_table.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/037-First_complete_version_of_DNS-client-id_EDNS0_and_ARP_tracking_code.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/038-Correct_logic_for_when_to_start_helper.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/039-Trivial_code_tweak.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/040-Extra_check_in_NSEC3_processing.patch + cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/041-Fix_linked-list_botch_in_new_ARP-cache_code.patch cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch
cd $(DIR_APP) && sed -i src/config.h \ diff --git a/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch b/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch index f55ebe8..60514f1 100644 --- a/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch +++ b/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch @@ -56,7 +56,7 @@
--- a/src/dnsmasq.h Wed Dec 16 19:24:12 2015 +++ b/src/dnsmasq.h Wed Dec 16 19:40:11 2015 -@@ -1513,8 +1513,12 @@ +@@ -1509,6 +1509,11 @@ void poll_listen(int fd, short event); int do_poll(int timeout);
@@ -341,8 +341,8 @@ 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 isc.o +- poll.o rrfilter.o edns0.o arp.o ++ poll.o rrfilter.o edns0.o arp.o isc.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ dns-protocol.h radv-protocol.h ip6addr.h diff --git a/src/patches/dnsmasq/030-Split_EDNS0_stuff_into_its_own_source_file.patch b/src/patches/dnsmasq/030-Split_EDNS0_stuff_into_its_own_source_file.patch new file mode 100644 index 0000000..0cef987 --- /dev/null +++ b/src/patches/dnsmasq/030-Split_EDNS0_stuff_into_its_own_source_file.patch @@ -0,0 +1,777 @@ +From 1d03016bbcb78962305cca20cbf7441423ff897d Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 21 Dec 2015 14:17:06 +0000 +Subject: [PATCH] Split EDNS0 stuff into its own source file. + +--- + Makefile | 2 +- + bld/Android.mk | 2 +- + src/dnsmasq.h | 17 +-- + src/edns0.c | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + src/rfc1035.c | 334 ----------------------------------------------------- + 5 files changed, 362 insertions(+), 344 deletions(-) + create mode 100644 src/edns0.c + +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 http://www.gnu.org/licenses/. ++*/ ++ ++#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) + { +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/031-Handle_extending_EDNS0_OPT_RR.patch b/src/patches/dnsmasq/031-Handle_extending_EDNS0_OPT_RR.patch new file mode 100644 index 0000000..386ff69 --- /dev/null +++ b/src/patches/dnsmasq/031-Handle_extending_EDNS0_OPT_RR.patch @@ -0,0 +1,295 @@ +From 5bb88f096363e66ac08e31761f850a1d5aa22244 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 21 Dec 2015 16:23:47 +0000 +Subject: [PATCH] Handle extending EDNS0 OPT RR. + +--- + src/dnsmasq.h | 4 +- + src/dnssec.c | 2 +- + src/edns0.c | 113 +++++++++++++++++++++++++++++++++++---------------------- + src/forward.c | 16 ++++---- + 4 files changed, 80 insertions(+), 55 deletions(-) + +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index a41c8cc..9828819 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -1117,8 +1117,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, + struct bogus_addr *addr, time_t now); + int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr); +-unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, +- size_t *len, unsigned char **p, int *is_sign); + 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, +@@ -1514,6 +1512,8 @@ u16 *rrfilter_desc(int type); + int expand_workspace(unsigned char ***wkspc, int *szp, int new); + + /* edns0.c */ ++unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, ++ size_t *len, unsigned char **p, int *is_sign, int *is_last); + 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); +diff --git a/src/dnssec.c b/src/dnssec.c +index 486e422..e0b7f39 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -2206,7 +2206,7 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i + + ret = add_do_bit(header, p - (unsigned char *)header, end); + +- if (find_pseudoheader(header, ret, NULL, &p, NULL)) ++ if (find_pseudoheader(header, ret, NULL, &p, NULL, NULL)) + PUTSHORT(edns_pktsz, p); + + return ret; +diff --git a/src/edns0.c b/src/edns0.c +index f348b01..d1a11e7 100644 +--- a/src/edns0.c ++++ b/src/edns0.c +@@ -16,12 +16,12 @@ + + #include "dnsmasq.h" + +-unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign) ++unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last) + { + /* 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) */ ++ forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */ + + int i, arcount = ntohs(header->arcount); + unsigned char *ansp = (unsigned char *)(header+1); +@@ -76,8 +76,13 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t + { + if (len) + *len = ansp - start; ++ + if (p) + *p = save; ++ ++ if (is_last) ++ *is_last = (i == arcount-1); ++ + ret = start; + } + else if (is_sign && +@@ -100,50 +105,31 @@ struct macparm { + 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; ++ unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL; ++ int rdlen = 0, is_sign, is_last; ++ unsigned short flags = set_do ? 0x8000 : 0, rcode = 0; ++ ++ p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last); + +- if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign))) +- { +- if (is_sign) +- return plen; ++ 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 ++ if (p) + { ++ /* Existing header */ + 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 */ ++ unsigned short code, len; ++ ++ p = udp_len; ++ GETSHORT(udp_sz, p); ++ GETSHORT(rcode, p); + GETSHORT(flags, p); ++ + if (set_do) + { + p -=2; +- PUTSHORT(flags | 0x8000, p); ++ flags |= 0x8000; ++ PUTSHORT(flags, p); + } + + lenp = p; +@@ -165,22 +151,61 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l + return plen; + p += len; + } +- +- if (((ssize_t)optlen) > (limit - (p + 4))) +- return plen; /* Too big */ ++ ++ /* If we're going to extend the RR, it has to be the last RR in the packet */ ++ if (!is_last) ++ { ++ /* First, take a copy of the options. */ ++ if (rdlen != 0 && (buff = whine_malloc(rdlen))) ++ memcpy(buff, datap, rdlen); ++ ++ /* now, delete OPT RR */ ++ plen = rrfilter(header, plen, 0); ++ ++ /* Now, force addition of a new one */ ++ p = NULL; ++ } ++ } ++ ++ if (!p) ++ { ++ /* We are (re)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(rcode, p); /* extended RCODE and version */ ++ PUTSHORT(flags, p); /* DO flag */ ++ lenp = p; ++ PUTSHORT(rdlen, p); /* RDLEN */ ++ datap = p; ++ /* Copy back any options */ ++ if (buff) ++ { ++ memcpy(p, buff, rdlen); ++ free(buff); ++ p += rdlen; ++ } ++ header->arcount = htons(ntohs(header->arcount) + 1); + } + ++ if (((ssize_t)optlen) > (limit - (p + 4))) ++ return plen; /* Too big */ ++ ++ /* Add new option */ + if (optno != 0) + { + PUTSHORT(optno, p); + PUTSHORT(optlen, p); + memcpy(p, opt, optlen); + p += optlen; ++ PUTSHORT(p - datap, lenp); + } +- +- PUTSHORT(p - datap, lenp); + return p - (unsigned char *)header; +- + } + + static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) +diff --git a/src/forward.c b/src/forward.c +index 041353c..2ca3c86 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -276,7 +276,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + blockdata_retrieve(forward->stash, forward->stash_len, (void *)header); + plen = forward->stash_len; + +- if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign) && !is_sign) ++ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign) + PUTSHORT(SAFE_PKTSZ, pheader); + + if (forward->sentto->addr.sa.sa_family == AF_INET) +@@ -479,7 +479,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + } + + #ifdef HAVE_DNSSEC +- if (option_bool(OPT_DNSSEC_VALID) && !do_bit) ++ if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER)) + { + /* Difficult one here. If our client didn't send EDNS0, we will have set the UDP + packet size to 512. But that won't provide space for the RRSIGS in many cases. +@@ -489,7 +489,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + the truncated bit? */ + unsigned char *pheader; + int is_sign; +- if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign)) ++ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign) + PUTSHORT(start->edns_pktsz, pheader); + } + #endif +@@ -584,7 +584,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server + } + #endif + +- if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign))) ++ if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL))) + { + if (check_subnet && !check_source(header, plen, pheader, query_source)) + { +@@ -779,7 +779,7 @@ void reply_query(int fd, int family, time_t now) + int is_sign; + + /* recreate query from reply */ +- pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign); ++ pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign, NULL); + if (!is_sign) + { + header->ancount = htons(0); +@@ -1313,7 +1313,7 @@ void receive_query(struct listener *listen, time_t now) + #endif + } + +- if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL)) ++ if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL)) + { + unsigned short flags; + +@@ -1569,7 +1569,7 @@ unsigned char *tcp_request(int confd, time_t now, + + do_bit = 0; + +- if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL)) ++ if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL)) + { + unsigned short flags; + +@@ -1578,7 +1578,7 @@ unsigned char *tcp_request(int confd, time_t now, + GETSHORT(flags, pheader); + + if (flags & 0x8000) +- do_bit = 1;/* do bit */ ++ do_bit = 1; /* do bit */ + } + + #ifdef HAVE_AUTH +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/032-Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting.patch b/src/patches/dnsmasq/032-Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting.patch new file mode 100644 index 0000000..df90a4d --- /dev/null +++ b/src/patches/dnsmasq/032-Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting.patch @@ -0,0 +1,65 @@ +From 5aa5f0ff2f8227ed743feb089dee421f1ca69943 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 21 Dec 2015 17:20:35 +0000 +Subject: [PATCH] Truncate DNS replies >512 bytes that the client isn't + expecting. + +--- + src/edns0.c | 5 ++--- + src/forward.c | 17 +++++++++++++++-- + 2 files changed, 17 insertions(+), 5 deletions(-) + +diff --git a/src/edns0.c b/src/edns0.c +index d1a11e7..e137992 100644 +--- a/src/edns0.c ++++ b/src/edns0.c +@@ -339,9 +339,8 @@ size_t add_do_bit(struct dns_header *header, size_t plen, char *limit) + 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; ++ ++ int len, calc_len; + struct subnet_opt opt; + unsigned char *p; + int code, i, rdlen; +diff --git a/src/forward.c b/src/forward.c +index 2ca3c86..e1766b9 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -485,8 +485,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + packet size to 512. But that won't provide space for the RRSIGS in many cases. + The RRSIGS will be stripped out before the answer goes back, so the packet should + shrink again. So, if we added a do-bit, bump the udp packet size to the value +- known to be OK for this server. Maybe check returned size after stripping and set +- the truncated bit? */ ++ known to be OK for this server. We check returned size after stripping and set ++ the truncated bit if it's still too big. */ + unsigned char *pheader; + int is_sign; + if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign) +@@ -1028,6 +1028,19 @@ void reply_query(int fd, int family, time_t now) + { + header->id = htons(forward->orig_id); + header->hb4 |= HB4_RA; /* recursion if available */ ++#ifdef HAVE_DNSSEC ++ /* We added an EDNSO header for the purpose of getting DNSSEC RRs, and set the value of the UDP payload size ++ greater than the no-EDNS0-implied 512 to have if space for the RRSIGS. If, having stripped them and the EDNS0 ++ header, the answer is still bigger than 512, truncate it and mark it so. The client then retries with TCP. */ ++ if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER) && (nn > PACKETSZ)) ++ { ++ header->ancount = htons(0); ++ header->nscount = htons(0); ++ header->arcount = htons(0); ++ header->hb3 |= HB3_TC; ++ nn = resize_packet(header, nn, NULL, 0); ++ } ++#endif + send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, + &forward->source, &forward->dest, forward->iface); + } +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/033-Fix_build_failure_when_DNSSEC_code_omitted.patch b/src/patches/dnsmasq/033-Fix_build_failure_when_DNSSEC_code_omitted.patch new file mode 100644 index 0000000..eed613b --- /dev/null +++ b/src/patches/dnsmasq/033-Fix_build_failure_when_DNSSEC_code_omitted.patch @@ -0,0 +1,55 @@ +From efef497b890231ba9232d02e7bfaf8273f044622 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 21 Dec 2015 17:30:44 +0000 +Subject: [PATCH] Fix build failure when DNSSEC code omitted. + +--- + src/dnsmasq.h | 2 -- + src/edns0.c | 12 +++++------- + 2 files changed, 5 insertions(+), 9 deletions(-) + +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 9828819..1286807 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -1518,7 +1518,5 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l + 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 +index e137992..f82ba1b 100644 +--- a/src/edns0.c ++++ b/src/edns0.c +@@ -208,6 +208,11 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l + return p - (unsigned char *)header; + } + ++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); ++} ++ + static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) + { + struct macparm *parm = parmv; +@@ -329,13 +334,6 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio + 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. */ +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/034-Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch b/src/patches/dnsmasq/034-Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch new file mode 100644 index 0000000..5742d4b --- /dev/null +++ b/src/patches/dnsmasq/034-Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch @@ -0,0 +1,81 @@ +From 15379ea1f252d1f53c5d93ae970b22dedb233642 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 21 Dec 2015 18:31:55 +0000 +Subject: [PATCH] Log signature algo with DNSKEY and DS, also digest with DS. + +--- + src/cache.c | 2 +- + src/dnsmasq.h | 6 ++++-- + src/dnssec.c | 15 +++++++++------ + 3 files changed, 14 insertions(+), 9 deletions(-) + +diff --git a/src/cache.c b/src/cache.c +index 51ba7cc..4da380a 100644 +--- a/src/cache.c ++++ b/src/cache.c +@@ -1580,7 +1580,7 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg) + if (addr) + { + if (flags & F_KEYTAG) +- sprintf(daemon->addrbuff, arg, addr->addr.keytag); ++ sprintf(daemon->addrbuff, arg, addr->addr.log.keytag, addr->addr.log.algo, addr->addr.log.digest); + else + { + #ifdef HAVE_IPV6 +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 1286807..4503a2d 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -256,8 +256,10 @@ struct all_addr { + struct in6_addr addr6; + #endif + /* for log_query */ +- unsigned int keytag; +- /* for cache_insert if RRSIG, DNSKEY, DS */ ++ struct { ++ unsigned short keytag, algo, digest; ++ } log; ++ /* for cache_insert of DNSKEY, DS */ + struct { + unsigned short class, type; + } dnssec; +diff --git a/src/dnssec.c b/src/dnssec.c +index e0b7f39..ed2d3fe 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1115,11 +1115,12 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch + } + else + { +- a.addr.keytag = keytag; ++ a.addr.log.keytag = keytag; ++ a.addr.log.algo = algo; + if (verify_func(algo)) +- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u"); ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu"); + else +- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u (not supported)"); ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)"); + + recp1->addr.key.keylen = rdlen - 4; + recp1->addr.key.keydata = key; +@@ -1241,11 +1242,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char + } + else + { +- a.addr.keytag = keytag; ++ a.addr.log.keytag = keytag; ++ a.addr.log.algo = algo; ++ a.addr.log.digest = digest; + if (hash_find(ds_digest_name(digest)) && verify_func(algo)) +- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u"); ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu"); + else +- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u (not supported)"); ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)"); + + crecp->addr.ds.digest = digest; + crecp->addr.ds.keydata = key; +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/035-More_EDNS0_packet_size_tweaks.patch b/src/patches/dnsmasq/035-More_EDNS0_packet_size_tweaks.patch new file mode 100644 index 0000000..5c8ebd7 --- /dev/null +++ b/src/patches/dnsmasq/035-More_EDNS0_packet_size_tweaks.patch @@ -0,0 +1,138 @@ +From d3a8b39c7df2f0debf3b5f274a1c37a9e261f94e Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Wed, 23 Dec 2015 12:27:37 +0000 +Subject: [PATCH] More EDNS0 packet-size tweaks. + +--- + src/dnsmasq.c | 7 +++++-- + src/dnsmasq.h | 8 +------- + src/forward.c | 22 +++++++++++++++------- + 3 files changed, 21 insertions(+), 16 deletions(-) + +diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index 81254f6..45761cc 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -91,8 +91,11 @@ int main (int argc, char **argv) + if (daemon->edns_pktsz < PACKETSZ) + daemon->edns_pktsz = PACKETSZ; + +- daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? +- daemon->edns_pktsz : DNSMASQ_PACKETSZ; ++ /* Min buffer size: we check after adding each record, so there must be ++ memory for the largest packet, and the largest record so the ++ min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000. ++ This might be increased is EDNS packet size if greater than the minimum. */ ++ daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ; + daemon->packet = safe_malloc(daemon->packet_buff_sz); + + daemon->addrbuff = safe_malloc(ADDRSTRLEN); +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 4503a2d..1c94f2a 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -179,13 +179,6 @@ struct event_desc { + #define EC_MISC 5 + #define EC_INIT_OFFSET 10 + +-/* Min buffer size: we check after adding each record, so there must be +- memory for the largest packet, and the largest record so the +- min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000. +- This might be increased is EDNS packet size if greater than the minimum. +-*/ +-#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ +- + /* Trust the compiler dead-code eliminator.... */ + #define option_bool(x) (((x) < 32) ? daemon->options & (1u << (x)) : daemon->options2 & (1u << ((x) - 32))) + +@@ -594,6 +587,7 @@ struct hostsfile { + #define FREC_DO_QUESTION 64 + #define FREC_ADDED_PHEADER 128 + #define FREC_TEST_PKTSZ 256 ++#define FREC_HAS_EXTRADATA 512 + + #ifdef HAVE_DNSSEC + #define HASH_SIZE 20 /* SHA-1 digest size */ +diff --git a/src/forward.c b/src/forward.c +index e1766b9..c0e4d9a 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -389,13 +389,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + { + struct server *firstsentto = start; + int forwarded = 0; +- ++ size_t edns0_len; ++ + /* If a query is retried, use the log_id for the retry when logging the answer. */ + forward->log_id = daemon->log_id; + + if (option_bool(OPT_ADD_MAC)) + { +- size_t new = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); ++ size_t new = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source); + if (new != plen) + { + plen = new; +@@ -405,7 +406,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + + if (option_bool(OPT_CLIENT_SUBNET)) + { +- size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); ++ size_t new = add_source_addr(header, plen, ((char *) header) + PACKETSZ, &forward->source); + if (new != plen) + { + plen = new; +@@ -416,7 +417,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + #ifdef HAVE_DNSSEC + if (option_bool(OPT_DNSSEC_VALID)) + { +- size_t new = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz); ++ size_t new = add_do_bit(header, plen, ((char *) header) + PACKETSZ); + + if (new != plen) + forward->flags |= FREC_ADDED_PHEADER; +@@ -430,6 +431,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + + } + #endif ++ ++ /* If we're sending an EDNS0 with any options, we can't recreate the query from a reply. */ ++ if (find_pseudoheader(header, plen, &edns0_len, NULL, NULL, NULL) && edns0_len > 11) ++ forward->flags |= FREC_HAS_EXTRADATA; + + while (1) + { +@@ -769,9 +774,12 @@ void reply_query(int fd, int family, time_t now) + check_for_ignored_address(header, n, daemon->ignore_addr)) + return; + ++ /* Note: if we send extra options in the EDNS0 header, we can't recreate ++ the query from the reply. */ + if (RCODE(header) == REFUSED && + !option_bool(OPT_ORDER) && +- forward->forwardall == 0) ++ forward->forwardall == 0 && ++ !(forward->flags & FREC_HAS_EXTRADATA)) + /* for broken servers, attempt to send to another one. */ + { + unsigned char *pheader; +@@ -919,13 +927,13 @@ void reply_query(int fd, int family, time_t now) + if (status == STAT_NEED_KEY) + { + new->flags |= FREC_DNSKEY_QUERY; +- nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz, ++ nn = dnssec_generate_query(header, ((char *) header) + server->edns_pktsz, + daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz); + } + else + { + new->flags |= FREC_DS_QUERY; +- nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz, ++ nn = dnssec_generate_query(header,((char *) header) + server->edns_pktsz, + daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz); + } + if ((hash = hash_questions(header, nn, daemon->namebuff))) +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/036-Cache_access_to_the_kernels_ARP_table.patch b/src/patches/dnsmasq/036-Cache_access_to_the_kernels_ARP_table.patch new file mode 100644 index 0000000..ec70419 --- /dev/null +++ b/src/patches/dnsmasq/036-Cache_access_to_the_kernels_ARP_table.patch @@ -0,0 +1,414 @@ +From 11867dc28c7bd7c8a509ee7c8c7438cd2bcc1770 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Wed, 23 Dec 2015 16:15:58 +0000 +Subject: [PATCH] Cache access to the kernel's ARP table. + +--- + Makefile | 2 +- + bld/Android.mk | 2 +- + src/arp.c | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + src/dhcp6.c | 54 ++++----------- + src/dnsmasq.h | 4 ++ + src/edns0.c | 37 ++--------- + 6 files changed, 223 insertions(+), 77 deletions(-) + create mode 100644 src/arp.c + +diff --git a/Makefile b/Makefile +index dfb0347..41e368f 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 edns0.o ++ poll.o rrfilter.o edns0.o arp.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 87966d2..eafef35 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 edns0.c ++ loop.c inotify.c poll.c rrfilter.c edns0.c arp.c + + LOCAL_MODULE := dnsmasq + +diff --git a/src/arp.c b/src/arp.c +new file mode 100644 +index 0000000..b624dac +--- /dev/null ++++ b/src/arp.c +@@ -0,0 +1,201 @@ ++/* 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/. ++*/ ++ ++#include "dnsmasq.h" ++ ++#define ARP_FREE 0 ++#define ARP_FOUND 1 ++#define ARP_NEW 2 ++#define ARP_EMPTY 3 ++ ++struct arp_record { ++ short hwlen, status; ++ int family; ++ unsigned char hwaddr[DHCP_CHADDR_MAX]; ++ struct all_addr addr; ++ struct arp_record *next; ++}; ++ ++static struct arp_record *arps = NULL, *old = NULL; ++ ++static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) ++{ ++ int match = 0; ++ struct arp_record *arp; ++ ++ if (maclen > DHCP_CHADDR_MAX) ++ return 1; ++ ++ /* Look for existing entry */ ++ for (arp = arps; arp; arp = arp->next) ++ { ++ if (family != arp->family || arp->status == ARP_NEW) ++ continue; ++ ++ if (family == AF_INET) ++ { ++ if (arp->addr.addr.addr4.s_addr != ((struct in_addr *)addrp)->s_addr) ++ continue; ++ } ++#ifdef HAVE_IPV6 ++ else ++ { ++ if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, (struct in6_addr *)addrp)) ++ continue; ++ } ++#endif ++ ++ if (arp->status != ARP_EMPTY && arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0) ++ arp->status = ARP_FOUND; ++ else ++ { ++ /* existing address, MAC changed or arrived new. */ ++ arp->status = ARP_NEW; ++ arp->hwlen = maclen; ++ arp->family = family; ++ memcpy(arp->hwaddr, mac, maclen); ++ } ++ ++ break; ++ } ++ ++ if (!arp) ++ { ++ /* New entry */ ++ if (old) ++ { ++ arp = old; ++ old = old->next; ++ } ++ else if (!(arp = whine_malloc(sizeof(struct arp_record)))) ++ return 1; ++ ++ arp->next = arps; ++ arps = arp; ++ arp->status = ARP_NEW; ++ arp->hwlen = maclen; ++ arp->family = family; ++ memcpy(arp->hwaddr, mac, maclen); ++ if (family == AF_INET) ++ arp->addr.addr.addr4.s_addr = ((struct in_addr *)addrp)->s_addr; ++#ifdef HAVE_IPV6 ++ else ++ memcpy(&arp->addr.addr.addr6, addrp, IN6ADDRSZ); ++#endif ++ } ++ ++ return 1; ++} ++ ++/* If in lazy mode, we cache absence of ARP entries. */ ++int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy) ++{ ++ struct arp_record *arp, **up; ++ int updated = 0; ++ ++ again: ++ ++ for (arp = arps; arp; arp = arp->next) ++ { ++ if (addr->sa.sa_family == arp->family) ++ { ++ if (arp->addr.addr.addr4.s_addr != addr->in.sin_addr.s_addr) ++ continue; ++ } ++#ifdef HAVE_IPV6 ++ else ++ { ++ if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr->in6.sin6_addr)) ++ continue; ++ } ++#endif ++ ++ /* Only accept poitive entries unless in lazy mode. */ ++ if (arp->status != ARP_EMPTY || lazy || updated) ++ { ++ if (mac && arp->hwlen != 0) ++ memcpy(mac, arp->hwaddr, arp->hwlen); ++ return arp->hwlen; ++ } ++ } ++ ++ /* Not found, try the kernel */ ++ if (!updated) ++ { ++ updated = 1; ++ ++ /* Mark all non-negative entries */ ++ for (arp = arps, up = &arps; arp; arp = arp->next) ++ if (arp->status != ARP_EMPTY) ++ arp->status = ARP_FREE; ++ ++ iface_enumerate(AF_UNSPEC, NULL, filter_mac); ++ ++ /* Remove all unconfirmed entries to old list, announce new ones. */ ++ for (arp = arps, up = &arps; arp; arp = arp->next) ++ if (arp->status == ARP_FREE) ++ { ++ *up = arp->next; ++ arp->next = old; ++ old = arp; ++ } ++ else ++ { ++ up = &arp->next; ++ if (arp->status == ARP_NEW) ++ { ++ char a[ADDRSTRLEN], m[ADDRSTRLEN]; ++ union mysockaddr pa; ++ pa.sa.sa_family = arp->family; ++ pa.in.sin_addr.s_addr = arp->addr.addr.addr4.s_addr; ++ prettyprint_addr(&pa, a); ++ print_mac(m, arp->hwaddr, arp->hwlen); ++ my_syslog(LOG_INFO, _("new arp: %s %s"), a, m); ++ } ++ } ++ ++ goto again; ++ } ++ ++ /* record failure, so we don't consult the kernel each time ++ we're asked for this address */ ++ if (old) ++ { ++ arp = old; ++ old = old->next; ++ } ++ else ++ arp = whine_malloc(sizeof(struct arp_record)); ++ ++ if (arp) ++ { ++ arp->next = arps; ++ arps = arp; ++ arp->status = ARP_EMPTY; ++ arp->family = addr->sa.sa_family; ++ ++ if (addr->sa.sa_family == AF_INET) ++ arp->addr.addr.addr4.s_addr = addr->in.sin_addr.s_addr; ++#ifdef HAVE_IPV6 ++ else ++ memcpy(&arp->addr.addr.addr6, &addr->in6.sin6_addr, IN6ADDRSZ); ++#endif ++ } ++ ++ return 0; ++} ++ ++ +diff --git a/src/dhcp6.c b/src/dhcp6.c +index 8286ff4..7b1a7c7 100644 +--- a/src/dhcp6.c ++++ b/src/dhcp6.c +@@ -27,17 +27,10 @@ struct iface_param { + int ind, addr_match; + }; + +-struct mac_param { +- struct in6_addr *target; +- unsigned char *mac; +- unsigned int maclen; +-}; +- + + static int complete_context6(struct in6_addr *local, int prefix, + int scope, int if_index, int flags, + unsigned int preferred, unsigned int valid, void *vparam); +-static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv); + static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm); + + void dhcp6_init(void) +@@ -264,9 +257,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi + find the sender. Repeat a few times in case of packet loss. */ + + struct neigh_packet neigh; +- struct sockaddr_in6 addr; +- struct mac_param mac_param; +- int i; ++ union mysockaddr addr; ++ int i, maclen; + + neigh.type = ND_NEIGHBOR_SOLICIT; + neigh.code = 0; +@@ -277,55 +269,31 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi + + memset(&addr, 0, sizeof(addr)); + #ifdef HAVE_SOCKADDR_SA_LEN +- addr.sin6_len = sizeof(struct sockaddr_in6); ++ addr.in6.sin6_len = sizeof(struct sockaddr_in6); + #endif +- addr.sin6_family = AF_INET6; +- addr.sin6_port = htons(IPPROTO_ICMPV6); +- addr.sin6_addr = *client; +- addr.sin6_scope_id = iface; +- +- mac_param.target = client; +- mac_param.maclen = 0; +- mac_param.mac = mac; ++ addr.in6.sin6_family = AF_INET6; ++ addr.in6.sin6_port = htons(IPPROTO_ICMPV6); ++ addr.in6.sin6_addr = *client; ++ addr.in6.sin6_scope_id = iface; + + for (i = 0; i < 5; i++) + { + struct timespec ts; + +- iface_enumerate(AF_UNSPEC, &mac_param, find_mac); +- +- if (mac_param.maclen != 0) ++ if ((maclen = find_mac(&addr, mac, 0)) != 0) + break; +- +- sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, (struct sockaddr *)&addr, sizeof(addr)); ++ ++ sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr)); + + ts.tv_sec = 0; + ts.tv_nsec = 100000000; /* 100ms */ + nanosleep(&ts, NULL); + } + +- *maclenp = mac_param.maclen; ++ *maclenp = maclen; + *mactypep = ARPHRD_ETHER; + } + +-static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) +-{ +- struct mac_param *parm = parmv; +- +- if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, (struct in6_addr *)addrp)) +- { +- if (maclen <= DHCP_CHADDR_MAX) +- { +- parm->maclen = maclen; +- memcpy(parm->mac, mac, maclen); +- } +- +- return 0; /* found, abort */ +- } +- +- return 1; +-} +- + static int complete_context6(struct in6_addr *local, int prefix, + int scope, int if_index, int flags, unsigned int preferred, + unsigned int valid, void *vparam) +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 1c94f2a..4459594 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -1516,3 +1516,7 @@ size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysock + size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source); + size_t add_do_bit(struct dns_header *header, size_t plen, char *limit); + int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer); ++ ++/* arp.c */ ++int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy); ++ +diff --git a/src/edns0.c b/src/edns0.c +index f82ba1b..9d8c0b9 100644 +--- a/src/edns0.c ++++ b/src/edns0.c +@@ -213,42 +213,15 @@ 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); + } + +-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; ++ int maclen; ++ unsigned char mac[DHCP_CHADDR_MAX]; + +- iface_enumerate(AF_UNSPEC, &parm, filter_mac); ++ if ((maclen = find_mac(l3, mac, 1)) != 0) ++ plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0); + +- return parm.plen; ++ return plen; + } + + struct subnet_opt { +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/037-First_complete_version_of_DNS-client-id_EDNS0_and_ARP_tracking_code.patch b/src/patches/dnsmasq/037-First_complete_version_of_DNS-client-id_EDNS0_and_ARP_tracking_code.patch new file mode 100644 index 0000000..c89984a --- /dev/null +++ b/src/patches/dnsmasq/037-First_complete_version_of_DNS-client-id_EDNS0_and_ARP_tracking_code.patch @@ -0,0 +1,976 @@ +From 33702ab1f829789183cbaf6b1c39eee7ff15d744 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 28 Dec 2015 23:17:15 +0000 +Subject: [PATCH] First complete version of DNS-client-id EDNS0 and ARP + tracking code. + +--- + src/arp.c | 148 +++++++++++++++++++++++++++++++--------------------- + src/config.h | 2 +- + src/dhcp6.c | 6 +-- + src/dns-protocol.h | 2 + + src/dnsmasq.c | 23 ++++---- + src/dnsmasq.h | 25 +++++---- + src/dnssec.c | 2 +- + src/edns0.c | 72 ++++++++++++++++++++----- + src/forward.c | 107 ++++++++++++++++++------------------- + src/helper.c | 66 ++++++++++++++++++++--- + src/option.c | 9 ++++ + src/rfc3315.c | 7 +-- + 12 files changed, 308 insertions(+), 161 deletions(-) + +diff --git a/src/arp.c b/src/arp.c +index b624dac..f41cdec 100644 +--- a/src/arp.c ++++ b/src/arp.c +@@ -16,26 +16,31 @@ + + #include "dnsmasq.h" + +-#define ARP_FREE 0 +-#define ARP_FOUND 1 +-#define ARP_NEW 2 +-#define ARP_EMPTY 3 ++/* Time between forced re-loads from kernel. */ ++#define INTERVAL 90 ++ ++#define ARP_MARK 0 ++#define ARP_FOUND 1 /* Confirmed */ ++#define ARP_NEW 2 /* Newly created */ ++#define ARP_EMPTY 3 /* No MAC addr */ + + struct arp_record { +- short hwlen, status; ++ unsigned short hwlen, status; + int family; + unsigned char hwaddr[DHCP_CHADDR_MAX]; + struct all_addr addr; + struct arp_record *next; + }; + +-static struct arp_record *arps = NULL, *old = NULL; ++static struct arp_record *arps = NULL, *old = NULL, *freelist = NULL; ++static time_t last = 0; + + static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) + { +- int match = 0; + struct arp_record *arp; + ++ (void)parmv; ++ + if (maclen > DHCP_CHADDR_MAX) + return 1; + +@@ -58,16 +63,18 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p + } + #endif + +- if (arp->status != ARP_EMPTY && arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0) +- arp->status = ARP_FOUND; +- else ++ if (arp->status == ARP_EMPTY) + { +- /* existing address, MAC changed or arrived new. */ ++ /* existing address, was negative. */ + arp->status = ARP_NEW; + arp->hwlen = maclen; +- arp->family = family; + memcpy(arp->hwaddr, mac, maclen); + } ++ else if (arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0) ++ /* Existing entry matches - confirm. */ ++ arp->status = ARP_FOUND; ++ else ++ continue; + + break; + } +@@ -75,10 +82,10 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p + if (!arp) + { + /* New entry */ +- if (old) ++ if (freelist) + { +- arp = old; +- old = old->next; ++ arp = freelist; ++ freelist = freelist->next; + } + else if (!(arp = whine_malloc(sizeof(struct arp_record)))) + return 1; +@@ -101,81 +108,72 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p + } + + /* If in lazy mode, we cache absence of ARP entries. */ +-int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy) ++int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now) + { + struct arp_record *arp, **up; + int updated = 0; + + again: + +- for (arp = arps; arp; arp = arp->next) +- { +- if (addr->sa.sa_family == arp->family) +- { +- if (arp->addr.addr.addr4.s_addr != addr->in.sin_addr.s_addr) +- continue; +- } ++ /* If the database is less then INTERVAL old, look in there */ ++ if (difftime(now, last) < INTERVAL) ++ for (arp = arps; arp; arp = arp->next) ++ { ++ if (addr->sa.sa_family == arp->family) ++ { ++ if (arp->addr.addr.addr4.s_addr != addr->in.sin_addr.s_addr) ++ continue; ++ } + #ifdef HAVE_IPV6 +- else +- { +- if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr->in6.sin6_addr)) +- continue; +- } ++ else ++ { ++ if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr->in6.sin6_addr)) ++ continue; ++ } + #endif +- +- /* Only accept poitive entries unless in lazy mode. */ +- if (arp->status != ARP_EMPTY || lazy || updated) +- { +- if (mac && arp->hwlen != 0) +- memcpy(mac, arp->hwaddr, arp->hwlen); +- return arp->hwlen; +- } +- } +- ++ ++ /* Only accept poitive entries unless in lazy mode. */ ++ if (arp->status != ARP_EMPTY || lazy || updated) ++ { ++ if (mac && arp->hwlen != 0) ++ memcpy(mac, arp->hwaddr, arp->hwlen); ++ return arp->hwlen; ++ } ++ } ++ + /* Not found, try the kernel */ + if (!updated) + { + updated = 1; +- ++ last = now; ++ + /* Mark all non-negative entries */ + for (arp = arps, up = &arps; arp; arp = arp->next) + if (arp->status != ARP_EMPTY) +- arp->status = ARP_FREE; ++ arp->status = ARP_MARK; + + iface_enumerate(AF_UNSPEC, NULL, filter_mac); + +- /* Remove all unconfirmed entries to old list, announce new ones. */ ++ /* Remove all unconfirmed entries to old list. */ + for (arp = arps, up = &arps; arp; arp = arp->next) +- if (arp->status == ARP_FREE) ++ if (arp->status == ARP_MARK) + { + *up = arp->next; + arp->next = old; + old = arp; + } + else +- { +- up = &arp->next; +- if (arp->status == ARP_NEW) +- { +- char a[ADDRSTRLEN], m[ADDRSTRLEN]; +- union mysockaddr pa; +- pa.sa.sa_family = arp->family; +- pa.in.sin_addr.s_addr = arp->addr.addr.addr4.s_addr; +- prettyprint_addr(&pa, a); +- print_mac(m, arp->hwaddr, arp->hwlen); +- my_syslog(LOG_INFO, _("new arp: %s %s"), a, m); +- } +- } +- ++ up = &arp->next; ++ + goto again; + } + + /* record failure, so we don't consult the kernel each time + we're asked for this address */ +- if (old) ++ if (freelist) + { +- arp = old; +- old = old->next; ++ arp = freelist; ++ freelist = freelist->next; + } + else + arp = whine_malloc(sizeof(struct arp_record)); +@@ -198,4 +196,36 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy) + return 0; + } + ++int do_arp_script_run(void) ++{ ++ struct arp_record *arp; ++ ++ /* Notify any which went, then move to free list */ ++ if (old) ++ { ++#ifdef HAVE_SCRIPT ++ if (option_bool(OPT_DNS_CLIENT)) ++ queue_arp(ACTION_ARP_OLD, old->hwaddr, old->hwlen, old->family, &old->addr); ++#endif ++ arp = old; ++ old = arp->next; ++ arp->next = freelist; ++ freelist = arp; ++ return 1; ++ } ++ ++ for (arp = arps; arp; arp = arp->next) ++ if (arp->status == ARP_NEW) ++ { ++#ifdef HAVE_SCRIPT ++ if (option_bool(OPT_DNS_CLIENT)) ++ queue_arp(ACTION_ARP, arp->hwaddr, arp->hwlen, arp->family, &arp->addr); ++#endif ++ arp->status = ARP_FOUND; ++ return 1; ++ } ++ ++ return 0; ++} ++ + +diff --git a/src/config.h b/src/config.h +index f75fe9d..309be6b 100644 +--- a/src/config.h ++++ b/src/config.h +@@ -337,7 +337,7 @@ HAVE_SOCKADDR_SA_LEN + #define HAVE_DHCP + #endif + +-#if defined(NO_SCRIPT) || !defined(HAVE_DHCP) || defined(NO_FORK) ++#if defined(NO_SCRIPT) || defined(NO_FORK) + #undef HAVE_SCRIPT + #undef HAVE_LUASCRIPT + #endif +diff --git a/src/dhcp6.c b/src/dhcp6.c +index 7b1a7c7..0e2e171 100644 +--- a/src/dhcp6.c ++++ b/src/dhcp6.c +@@ -220,7 +220,7 @@ void dhcp6_packet(time_t now) + inet_pton(AF_INET6, ALL_SERVERS, &all_servers); + + if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers)) +- relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id); ++ relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now); + return; + } + +@@ -250,7 +250,7 @@ void dhcp6_packet(time_t now) + } + } + +-void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep) ++void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now) + { + /* Recieving a packet from a host does not populate the neighbour + cache, so we send a neighbour discovery request if we can't +@@ -280,7 +280,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi + { + struct timespec ts; + +- if ((maclen = find_mac(&addr, mac, 0)) != 0) ++ if ((maclen = find_mac(&addr, mac, 0, now)) != 0) + break; + + sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr)); +diff --git a/src/dns-protocol.h b/src/dns-protocol.h +index 6cf5158..addfa9e 100644 +--- a/src/dns-protocol.h ++++ b/src/dns-protocol.h +@@ -77,6 +77,8 @@ + + #define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */ + #define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */ ++#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */ ++#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */ + + struct dns_header { + u16 id; +diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index 45761cc..229693f 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -245,8 +245,11 @@ int main (int argc, char **argv) + /* Note that order matters here, we must call lease_init before + creating any file descriptors which shouldn't be leaked + to the lease-script init process. We need to call common_init +- before lease_init to allocate buffers it uses.*/ +- if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6) ++ before lease_init to allocate buffers it uses. ++ The script subsystrm relies on DHCP buffers, hence the last two ++ conditions below. */ ++ if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || ++ daemon->relay6 || option_bool(OPT_TFTP) || option_bool(OPT_ADD_MAC)) + { + dhcp_common_init(); + if (daemon->dhcp || daemon->doing_dhcp6) +@@ -553,8 +556,9 @@ int main (int argc, char **argv) + /* if we are to run scripts, we need to fork a helper before dropping root. */ + daemon->helperfd = -1; + #ifdef HAVE_SCRIPT +- if ((daemon->dhcp || daemon->dhcp6) && (daemon->lease_change_command || daemon->luascript)) +- daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); ++ if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_ADD_MAC)) && ++ (daemon->lease_change_command || daemon->luascript)) ++ daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); + #endif + + if (!option_bool(OPT_DEBUG) && getuid() == 0) +@@ -914,9 +918,9 @@ int main (int argc, char **argv) + + poll_listen(piperead, POLLIN); + +-#ifdef HAVE_DHCP +-# ifdef HAVE_SCRIPT +- while (helper_buf_empty() && do_script_run(now)); ++#ifdef HAVE_SCRIPT ++ while (helper_buf_empty() && do_script_run(now)); ++ while (helper_buf_empty() && do_arp_script_run()); + + # ifdef HAVE_TFTP + while (helper_buf_empty() && do_tftp_script_run()); +@@ -924,16 +928,17 @@ int main (int argc, char **argv) + + if (!helper_buf_empty()) + poll_listen(daemon->helperfd, POLLOUT); +-# else ++#else + /* need this for other side-effects */ + while (do_script_run(now)); ++ while (do_arp_script_run(now)); + + # ifdef HAVE_TFTP + while (do_tftp_script_run()); + # endif + +-# endif + #endif ++ + + /* must do this just before select(), when we know no + more calls to my_syslog() can occur */ +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 4459594..fec0f8d 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -235,7 +235,8 @@ struct event_desc { + #define OPT_LOOP_DETECT 50 + #define OPT_EXTRALOG 51 + #define OPT_TFTP_NO_FAIL 52 +-#define OPT_LAST 53 ++#define OPT_DNS_CLIENT 53 ++#define OPT_LAST 54 + + /* extra flags for my_syslog, we use a couple of facilities since they are known + not to occupy the same bits as priorities, no matter how syslog.h is set up. */ +@@ -633,6 +634,8 @@ struct frec { + #define ACTION_OLD 3 + #define ACTION_ADD 4 + #define ACTION_TFTP 5 ++#define ACTION_ARP 6 ++#define ACTION_ARP_OLD 7 + + #define LEASE_NEW 1 /* newly created */ + #define LEASE_CHANGED 2 /* modified */ +@@ -948,6 +951,7 @@ extern struct daemon { + int cachesize, ftabsize; + int port, query_port, min_port; + unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl; ++ char *dns_client_id; + struct hostsfile *addn_hosts; + struct dhcp_context *dhcp, *dhcp6; + struct ra_interface *ra_interfaces; +@@ -1135,7 +1139,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut); + #endif + + /* dnssec.c */ +-size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz); ++size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz); + int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class); + int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); + int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, +@@ -1372,6 +1376,8 @@ void queue_script(int action, struct dhcp_lease *lease, + #ifdef HAVE_TFTP + void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer); + #endif ++void queue_arp(int action, unsigned char *mac, int maclen, ++ int family, struct all_addr *addr); + int helper_buf_empty(void); + #endif + +@@ -1408,7 +1414,7 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct + void make_duid(time_t now); + void dhcp_construct_contexts(time_t now); + void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, +- unsigned int *maclenp, unsigned int *mactypep); ++ unsigned int *maclenp, unsigned int *mactypep, time_t now); + #endif + + /* rfc3315.c */ +@@ -1416,7 +1422,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, + unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, + struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr, + size_t sz, struct in6_addr *client_addr, time_t now); +-void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id); ++void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, ++ u32 scope_id, time_t now); + + unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface); + #endif +@@ -1512,11 +1519,11 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, + size_t *len, unsigned char **p, int *is_sign, int *is_last); + 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); +-size_t add_do_bit(struct dns_header *header, size_t plen, char *limit); ++size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit); ++size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, ++ union mysockaddr *source, time_t now, int *check_subnet); + int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer); + + /* arp.c */ +-int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy); +- ++int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now); ++int do_arp_script_run(void); +diff --git a/src/dnssec.c b/src/dnssec.c +index ed2d3fe..918a2dc 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -2173,7 +2173,7 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen) + } + } + +-size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, ++size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, + int type, union mysockaddr *addr, int edns_pktsz) + { + unsigned char *p; +diff --git a/src/edns0.c b/src/edns0.c +index 9d8c0b9..12e0210 100644 +--- a/src/edns0.c ++++ b/src/edns0.c +@@ -94,13 +94,6 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t + + 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) +@@ -208,19 +201,54 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l + return p - (unsigned char *)header; + } + +-size_t add_do_bit(struct dns_header *header, size_t plen, char *limit) ++size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit) + { + return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1); + } + +-size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3) ++static unsigned char char64(unsigned char c) ++{ ++ return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f]; ++} ++ ++static void encoder(unsigned char *in, char *out) ++{ ++ out[0] = char64(in[0]>>2); ++ out[1] = char64((in[0]<<4) | (in[1]>>4)); ++ out[2] = char64((in[1]<<2) | (in[2]>>6)); ++ out[3] = char64(in[2]); ++} ++ ++static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now) + { + int maclen; + unsigned char mac[DHCP_CHADDR_MAX]; ++ char encode[8]; /* handle 6 byte MACs */ ++ ++ if ((maclen = find_mac(l3, mac, 1, now)) == 6) ++ { ++ encoder(mac, encode); ++ encoder(mac+3, encode+4); ++ ++ plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, 8, 0); ++ } ++ ++ if (daemon->dns_client_id) ++ plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, ++ (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0); ++ ++ return plen; ++} ++ + +- if ((maclen = find_mac(l3, mac, 1)) != 0) ++static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now) ++{ ++ int maclen; ++ unsigned char mac[DHCP_CHADDR_MAX]; ++ ++ if ((maclen = find_mac(l3, mac, 1, now)) != 0) + plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0); +- ++ + return plen; + } + +@@ -296,7 +324,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source) + return len + 4; + } + +-size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source) ++static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source) + { + /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */ + +@@ -344,3 +372,23 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe + + return 1; + } ++ ++size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, ++ union mysockaddr *source, time_t now, int *check_subnet) ++{ ++ *check_subnet = 0; ++ ++ if (option_bool(OPT_ADD_MAC)) ++ plen = add_mac(header, plen, limit, source, now); ++ ++ if (option_bool(OPT_DNS_CLIENT)) ++ plen = add_dns_client(header, plen, limit, source, now); ++ ++ if (option_bool(OPT_CLIENT_SUBNET)) ++ { ++ plen = add_source_addr(header, plen, limit, source); ++ *check_subnet = 1; ++ } ++ ++ return plen; ++} +diff --git a/src/forward.c b/src/forward.c +index c0e4d9a..911f46e 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -388,36 +388,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + if (!flags && forward) + { + struct server *firstsentto = start; +- int forwarded = 0; ++ int subnet, forwarded = 0; + size_t edns0_len; + + /* If a query is retried, use the log_id for the retry when logging the answer. */ + forward->log_id = daemon->log_id; + +- if (option_bool(OPT_ADD_MAC)) +- { +- size_t new = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source); +- if (new != plen) +- { +- plen = new; +- forward->flags |= FREC_ADDED_PHEADER; +- } +- } +- +- if (option_bool(OPT_CLIENT_SUBNET)) ++ edns0_len = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet); ++ ++ if (edns0_len != plen) + { +- size_t new = add_source_addr(header, plen, ((char *) header) + PACKETSZ, &forward->source); +- if (new != plen) +- { +- plen = new; +- forward->flags |= FREC_HAS_SUBNET | FREC_ADDED_PHEADER; +- } ++ plen = edns0_len; ++ forward->flags |= FREC_ADDED_PHEADER; ++ ++ if (subnet) ++ forward->flags |= FREC_HAS_SUBNET; + } +- ++ + #ifdef HAVE_DNSSEC + if (option_bool(OPT_DNSSEC_VALID)) + { +- size_t new = add_do_bit(header, plen, ((char *) header) + PACKETSZ); ++ size_t new = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ); + + if (new != plen) + forward->flags |= FREC_ADDED_PHEADER; +@@ -607,15 +598,30 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server + } + else + { ++ unsigned short udpsz; ++ + /* If upstream is advertising a larger UDP packet size + than we allow, trim it so that we don't get overlarge + requests for the client. We can't do this for signed packets. */ +- unsigned short udpsz; +- unsigned char *psave = sizep; +- + GETSHORT(udpsz, sizep); + if (udpsz > daemon->edns_pktsz) +- PUTSHORT(daemon->edns_pktsz, psave); ++ { ++ sizep -= 2; ++ PUTSHORT(daemon->edns_pktsz, sizep); ++ } ++ ++#ifdef HAVE_DNSSEC ++ /* If the client didn't set the do bit, but we did, reset it. */ ++ if (option_bool(OPT_DNSSEC_VALID) && !do_bit) ++ { ++ unsigned short flags; ++ sizep += 2; /* skip RCODE */ ++ GETSHORT(flags, sizep); ++ flags &= ~0x8000; ++ sizep -= 2; ++ PUTSHORT(flags, sizep); ++ } ++#endif + } + } + } +@@ -674,14 +680,11 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server + } + + #ifdef HAVE_DNSSEC +- if (bogusanswer && !(header->hb4 & HB4_CD)) ++ if (bogusanswer && !(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG)) + { +- if (!option_bool(OPT_DNSSEC_DEBUG)) +- { +- /* Bogus reply, turn into SERVFAIL */ +- SET_RCODE(header, SERVFAIL); +- munged = 1; +- } ++ /* Bogus reply, turn into SERVFAIL */ ++ SET_RCODE(header, SERVFAIL); ++ munged = 1; + } + + if (option_bool(OPT_DNSSEC_VALID)) +@@ -802,7 +805,7 @@ void reply_query(int fd, int family, time_t now) + if (forward->flags |= FREC_AD_QUESTION) + header->hb4 |= HB4_AD; + if (forward->flags & FREC_DO_QUESTION) +- add_do_bit(header, nn, (char *)pheader + plen); ++ add_do_bit(header, nn, (unsigned char *)pheader + plen); + forward_query(-1, NULL, NULL, 0, header, nn, now, forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION); + return; + } +@@ -927,13 +930,13 @@ void reply_query(int fd, int family, time_t now) + if (status == STAT_NEED_KEY) + { + new->flags |= FREC_DNSKEY_QUERY; +- nn = dnssec_generate_query(header, ((char *) header) + server->edns_pktsz, ++ nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz, + daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz); + } + else + { + new->flags |= FREC_DS_QUERY; +- nn = dnssec_generate_query(header,((char *) header) + server->edns_pktsz, ++ nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz, + daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz); + } + if ((hash = hash_questions(header, nn, daemon->namebuff))) +@@ -1434,7 +1437,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si + break; + } + +- m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class, ++ m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, + new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz); + + *length = htons(m); +@@ -1548,8 +1551,6 @@ unsigned char *tcp_request(int confd, time_t now, + daemon->log_display_id = ++daemon->log_id; + daemon->log_source_addr = &peer_addr; + +- check_subnet = 0; +- + /* save state of "cd" flag in query */ + if ((checking_disabled = header->hb4 & HB4_CD)) + no_cache_dnssec = 1; +@@ -1627,20 +1628,14 @@ unsigned char *tcp_request(int confd, time_t now, + struct all_addr *addrp = NULL; + int type = 0; + char *domain = NULL; +- +- if (option_bool(OPT_ADD_MAC)) +- size = add_mac(header, size, ((char *) header) + 65536, &peer_addr); +- +- if (option_bool(OPT_CLIENT_SUBNET)) ++ size_t new_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet); ++ ++ if (size != new_size) + { +- size_t new = add_source_addr(header, size, ((char *) header) + 65536, &peer_addr); +- if (size != new) +- { +- size = new; +- check_subnet = 1; +- } ++ added_pheader = 1; ++ size = new_size; + } +- ++ + if (gotname) + flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); + +@@ -1715,20 +1710,20 @@ unsigned char *tcp_request(int confd, time_t now, + } + + #ifdef HAVE_DNSSEC +- added_pheader = 0; + if (option_bool(OPT_DNSSEC_VALID)) + { +- size_t new_size = add_do_bit(header, size, ((char *) header) + 65536); ++ new_size = add_do_bit(header, size, ((unsigned char *) header) + 65536); ++ ++ if (size != new_size) ++ { ++ added_pheader = 1; ++ size = new_size; ++ } + + /* For debugging, set Checking Disabled, otherwise, have the upstream check too, + this allows it to select auth servers when one is returning bad data. */ + if (option_bool(OPT_DNSSEC_DEBUG)) + header->hb4 |= HB4_CD; +- +- if (size != new_size) +- added_pheader = 1; +- +- size = new_size; + } + #endif + } +diff --git a/src/helper.c b/src/helper.c +index 1fee72d..517cfd9 100644 +--- a/src/helper.c ++++ b/src/helper.c +@@ -219,7 +219,18 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) + action_str = "tftp"; + is6 = (data.flags != AF_INET); + } +- else ++ else if (data.action == ACTION_ARP) ++ { ++ action_str = "arp"; ++ is6 = (data.flags != AF_INET); ++ } ++ else if (data.action == ACTION_ARP_OLD) ++ { ++ action_str = "arp-old"; ++ is6 = (data.flags != AF_INET); ++ data.action = ACTION_ARP; ++ } ++ else + continue; + + +@@ -321,6 +332,22 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) + lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ + } + } ++ else if (data.action == ACTION_ARP) ++ { ++ lua_getglobal(lua, "arp"); ++ if (lua_type(lua, -1) != LUA_TFUNCTION) ++ lua_pop(lua, 1); /* arp function optional */ ++ else ++ { ++ lua_pushstring(lua, action_str); /* arg1 - action */ ++ lua_newtable(lua); /* arg2 - data table */ ++ lua_pushstring(lua, daemon->addrbuff); ++ lua_setfield(lua, -2, "client_address"); ++ lua_pushstring(lua, daemon->dhcp_buff); ++ lua_setfield(lua, -2, "mac_address"); ++ lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ ++ } ++ } + else + { + lua_getglobal(lua, "lease"); /* function to call */ +@@ -478,7 +505,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) + continue; + } + +- if (data.action != ACTION_TFTP) ++ if (data.action != ACTION_TFTP && data.action != ACTION_ARP) + { + #ifdef HAVE_DHCP6 + my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err); +@@ -550,10 +577,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) + my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err); + if (data.action == ACTION_OLD_HOSTNAME) + hostname = NULL; +- } +- +- my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err); +- ++ ++ my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err); ++ } + /* we need to have the event_fd around if exec fails */ + if ((i = fcntl(event_fd, F_GETFD)) != -1) + fcntl(event_fd, F_SETFD, i | FD_CLOEXEC); +@@ -563,8 +589,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) + if (err == 0) + { + execl(daemon->lease_change_command, +- p ? p+1 : daemon->lease_change_command, +- action_str, is6 ? daemon->packet : daemon->dhcp_buff, ++ p ? p+1 : daemon->lease_change_command, action_str, ++ (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff, + daemon->addrbuff, hostname, (char*)NULL); + err = errno; + } +@@ -760,6 +786,30 @@ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer) + } + #endif + ++void queue_arp(int action, unsigned char *mac, int maclen, int family, struct all_addr *addr) ++{ ++ /* no script */ ++ if (daemon->helperfd == -1) ++ return; ++ ++ buff_alloc(sizeof(struct script_data)); ++ memset(buf, 0, sizeof(struct script_data)); ++ ++ buf->action = action; ++ buf->hwaddr_len = maclen; ++ buf->hwaddr_type = ARPHRD_ETHER; ++ if ((buf->flags = family) == AF_INET) ++ buf->addr = addr->addr.addr4; ++#ifdef HAVE_IPV6 ++ else ++ buf->addr6 = addr->addr.addr6; ++#endif ++ ++ memcpy(buf->hwaddr, mac, maclen); ++ ++ bytes_in_buf = sizeof(struct script_data); ++} ++ + int helper_buf_empty(void) + { + return bytes_in_buf == 0; +diff --git a/src/option.c b/src/option.c +index 71beb98..f359bc5 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -154,6 +154,7 @@ struct myoption { + #define LOPT_HOST_INOTIFY 342 + #define LOPT_DNSSEC_STAMP 343 + #define LOPT_TFTP_NO_FAIL 344 ++#define LOPT_DNS_CLIENT_ID 355 + + #ifdef HAVE_GETOPT_LONG + static const struct option opts[] = +@@ -281,6 +282,7 @@ static const struct myoption opts[] = + { "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND }, + { "add-mac", 0, 0, LOPT_ADD_MAC }, + { "add-subnet", 2, 0, LOPT_ADD_SBNET }, ++ { "add-dns-client", 2, 0 , LOPT_DNS_CLIENT_ID }, + { "proxy-dnssec", 0, 0, LOPT_DNSSEC }, + { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR }, + { "conntrack", 0, 0, LOPT_CONNTRACK }, +@@ -446,6 +448,7 @@ static struct { + { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL }, + { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL }, + { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL }, ++ { LOPT_DNS_CLIENT_ID, ARG_ONE, "<proxyname>", gettext_noop("Add client identification to forwarded DNS queries."), NULL }, + { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL }, + { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL }, + { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL }, +@@ -2150,6 +2153,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + break; + ++ case LOPT_DNS_CLIENT_ID: /* --add-dns-client */ ++ set_option_bool(OPT_DNS_CLIENT); ++ if (arg) ++ daemon->dns_client_id = opt_string_alloc(arg); ++ break; ++ + case 'u': /* --user */ + daemon->username = opt_string_alloc(arg); + break; +diff --git a/src/rfc3315.c b/src/rfc3315.c +index 3ed8623..31bb41b 100644 +--- a/src/rfc3315.c ++++ b/src/rfc3315.c +@@ -130,7 +130,7 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, + MAC address from the local ND cache. */ + + if (!state->link_address) +- get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type); ++ get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now); + else + { + struct dhcp_context *c; +@@ -2054,7 +2054,8 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size) + return ret; + } + +-void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id) ++void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, ++ struct in6_addr *peer_address, u32 scope_id, time_t now) + { + /* ->local is same value for all relays on ->current chain */ + +@@ -2068,7 +2069,7 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer + unsigned char mac[DHCP_CHADDR_MAX]; + + inet_pton(AF_INET6, ALL_SERVERS, &multicast); +- get_client_mac(peer_address, scope_id, mac, &maclen, &mactype); ++ get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now); + + /* source address == relay address */ + from.addr.addr6 = relay->local.addr.addr6; +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/038-Correct_logic_for_when_to_start_helper.patch b/src/patches/dnsmasq/038-Correct_logic_for_when_to_start_helper.patch new file mode 100644 index 0000000..2c25d30 --- /dev/null +++ b/src/patches/dnsmasq/038-Correct_logic_for_when_to_start_helper.patch @@ -0,0 +1,25 @@ +From 8e39c34077cdad5b8e7cc799443bf8d1f22a1e80 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Thu, 31 Dec 2015 16:18:11 +0000 +Subject: [PATCH] Correct logic for when to start helper. + +--- + src/dnsmasq.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index 229693f..009d357 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -556,7 +556,7 @@ int main (int argc, char **argv) + /* if we are to run scripts, we need to fork a helper before dropping root. */ + daemon->helperfd = -1; + #ifdef HAVE_SCRIPT +- if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_ADD_MAC)) && ++ if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_DNS_CLIENT)) && + (daemon->lease_change_command || daemon->luascript)) + daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); + #endif +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/039-Trivial_code_tweak.patch b/src/patches/dnsmasq/039-Trivial_code_tweak.patch new file mode 100644 index 0000000..ce0d23b --- /dev/null +++ b/src/patches/dnsmasq/039-Trivial_code_tweak.patch @@ -0,0 +1,33 @@ +From ec0628c4b2a06e1fc21216091bb040d61a43b271 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Thu, 31 Dec 2015 20:55:39 +0000 +Subject: [PATCH] Trivial code tweak. + +--- + src/dnssec.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/dnssec.c b/src/dnssec.c +index 918a2dc..0e5cbe8 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1599,12 +1599,12 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige + if (!CHECK_LEN(header, p, plen, rdlen)) + return 0; + +- /* If we can prove that there's no NS record, return that information. */ +- if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0) +- *nons = 0; +- + if (rdlen >= 2 && p[0] == 0) + { ++ /* If we can prove that there's no NS record, return that information. */ ++ if (nons && (p[2] & (0x80 >> T_NS)) != 0) ++ *nons = 0; ++ + /* A CNAME answer would also be valid, so if there's a CNAME is should + have been returned. */ + if ((p[2] & (0x80 >> T_CNAME)) != 0) +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/040-Extra_check_in_NSEC3_processing.patch b/src/patches/dnsmasq/040-Extra_check_in_NSEC3_processing.patch new file mode 100644 index 0000000..d0daa23 --- /dev/null +++ b/src/patches/dnsmasq/040-Extra_check_in_NSEC3_processing.patch @@ -0,0 +1,60 @@ +From f89391d09844837b7ec4394f1e3008c6f339b4bd Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Fri, 1 Jan 2016 19:44:11 +0000 +Subject: [PATCH] Extra check in NSEC3 processing. + +--- + src/dnssec.c | 30 +++++++++++++++++++++++++++--- + 1 file changed, 27 insertions(+), 3 deletions(-) + +diff --git a/src/dnssec.c b/src/dnssec.c +index 0e5cbe8..cd29d88 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1665,8 +1665,8 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige + static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count, + char *workspace1, char *workspace2, char *name, int type, char *wildname, int *nons) + { +- unsigned char *salt, *p, *digest; +- int digest_len, i, iterations, salt_len, base32_len, algo = 0; ++ unsigned char *salt, *p, *digest, *psave; ++ int digest_len, i, rdlen, iterations, salt_len, hash_len, base32_len, algo = 0; + struct nettle_hash const *hash; + char *closest_encloser, *next_closest, *wildcard; + +@@ -1785,7 +1785,31 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns + + if (!closest_encloser) + return 0; +- ++ ++ p += 8; /* class, type, TTL */ ++ GETSHORT(rdlen, p); ++ psave = p; ++ p += 4; /* algo, flags, iterations */ ++ salt_len = *p++; /* salt_len */ ++ p += salt_len; /* salt */ ++ hash_len = *p++; /* p now points to next hashed name */ ++ p += hash_len; /* skip next-domain hash */ ++ rdlen -= p - psave; ++ ++ if (!CHECK_LEN(header, p, plen, rdlen)) ++ return 0; ++ ++ if (rdlen >= 2 && p[0] == 0) ++ { ++ /* 5155 8.3: the NS type bit may only be set if the SOA type bit is set. */ ++ if ((p[2] & (0x80 >> T_NS)) != 0 && (p[2] & (0x80 >> T_SOA)) == 0) ++ return 0; ++ ++ /* 5155 8.3: The DNAME type bit must not be set. */ ++ if (rdlen >= 2 + (T_DNAME/8) && (p[2 + (T_DNAME/8)] & (0x80 >> (T_DNAME%8))) != 0) ++ return 0; ++ } ++ + /* Look for NSEC3 that proves the non-existence of the next-closest encloser */ + if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0) + return 0; +-- +1.7.10.4 + diff --git a/src/patches/dnsmasq/041-Fix_linked-list_botch_in_new_ARP-cache_code.patch b/src/patches/dnsmasq/041-Fix_linked-list_botch_in_new_ARP-cache_code.patch new file mode 100644 index 0000000..396171d --- /dev/null +++ b/src/patches/dnsmasq/041-Fix_linked-list_botch_in_new_ARP-cache_code.patch @@ -0,0 +1,55 @@ +From 19eeed3b32086ec3bd18cb2841d310ff5953b6d7 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Fri, 1 Jan 2016 20:24:15 +0000 +Subject: [PATCH] Fix linked-list botch in new ARP-cache code. + +--- + src/arp.c | 25 ++++++++++++++----------- + 1 file changed, 14 insertions(+), 11 deletions(-) + +diff --git a/src/arp.c b/src/arp.c +index f41cdec..374446c 100644 +--- a/src/arp.c ++++ b/src/arp.c +@@ -110,7 +110,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p + /* If in lazy mode, we cache absence of ARP entries. */ + int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now) + { +- struct arp_record *arp, **up; ++ struct arp_record *arp, *tmp, **up; + int updated = 0; + + again: +@@ -155,16 +155,19 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now) + iface_enumerate(AF_UNSPEC, NULL, filter_mac); + + /* Remove all unconfirmed entries to old list. */ +- for (arp = arps, up = &arps; arp; arp = arp->next) +- if (arp->status == ARP_MARK) +- { +- *up = arp->next; +- arp->next = old; +- old = arp; +- } +- else +- up = &arp->next; +- ++ for (arp = arps, up = &arps; arp; arp = tmp) ++ { ++ tmp = arp->next; ++ if (arp->status == ARP_MARK) ++ { ++ *up = arp->next; ++ arp->next = old; ++ old = arp; ++ } ++ else ++ up = &arp->next; ++ } ++ + goto again; + } + +-- +1.7.10.4 +
On Mon, 2016-01-04 at 18:06 +0100, Matthias Fischer wrote:
Only for testing - this won't compile!
Hi,
On 04.01.2016 17:42, Michael Tremer wrote:
is this still an issue?
For me: definitely YES.
The nightly builds seem to build fine...
I think this is because the nightly builds contain patches No. 001 -29.
The problems came up after I integrated patch No. 030-041, as shown below.
Even the testrelease '2.76test2' wouldn't build (I haven't tested '276test3' yet, which came up a few hours ago).
Can anyone confirm?
Yes I can confirm. It won't build.
Best, -Michael
Best, Matthias
Signed-off-by: Matthias Fischer matthias.fischer@ipfire.org
lfs/dnsmasq | 12 + ...q-Add-support-to-read-ISC-DHCP-lease-file.patch | 6 +- ...plit_EDNS0_stuff_into_its_own_source_file.patch | 777 ++++++++++++++++ .../031-Handle_extending_EDNS0_OPT_RR.patch | 295 +++++++ ..._512_bytes_that_the_client_isnt_expecting.patch | 65 ++ ...ix_build_failure_when_DNSSEC_code_omitted.patch | 55 ++ ...go_with_DNSKEY_and_DS_also_digest_with_DS.patch | 81 ++ .../035-More_EDNS0_packet_size_tweaks.patch | 138 +++ ...036-Cache_access_to_the_kernels_ARP_table.patch | 414 +++++++++ ...DNS-client-id_EDNS0_and_ARP_tracking_code.patch | 976 +++++++++++++++++++++ ...38-Correct_logic_for_when_to_start_helper.patch | 25 + src/patches/dnsmasq/039-Trivial_code_tweak.patch | 33 + .../040-Extra_check_in_NSEC3_processing.patch | 60 ++ ...x_linked-list_botch_in_new_ARP-cache_code.patch | 55 ++ 14 files changed, 2989 insertions(+), 3 deletions(-) create mode 100644 src/patches/dnsmasq/030 -Split_EDNS0_stuff_into_its_own_source_file.patch create mode 100644 src/patches/dnsmasq/031 -Handle_extending_EDNS0_OPT_RR.patch create mode 100644 src/patches/dnsmasq/032 -Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting .patch create mode 100644 src/patches/dnsmasq/033 -Fix_build_failure_when_DNSSEC_code_omitted.patch create mode 100644 src/patches/dnsmasq/034 -Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch create mode 100644 src/patches/dnsmasq/035 -More_EDNS0_packet_size_tweaks.patch create mode 100644 src/patches/dnsmasq/036 -Cache_access_to_the_kernels_ARP_table.patch create mode 100644 src/patches/dnsmasq/037 -First_complete_version_of_DNS-client -id_EDNS0_and_ARP_tracking_code.patch create mode 100644 src/patches/dnsmasq/038 -Correct_logic_for_when_to_start_helper.patch create mode 100644 src/patches/dnsmasq/039-Trivial_code_tweak.patch create mode 100644 src/patches/dnsmasq/040 -Extra_check_in_NSEC3_processing.patch create mode 100644 src/patches/dnsmasq/041-Fix_linked -list_botch_in_new_ARP-cache_code.patch
diff --git a/lfs/dnsmasq b/lfs/dnsmasq index 8058663..d12a54a 100644 --- a/lfs/dnsmasq +++ b/lfs/dnsmasq @@ -102,6 +102,18 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects)) cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by -one_in_DNSSEC_hostname_cmp.patch cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/028 -Minor_tweak_to_previous_commit.patch cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/030 -Split_EDNS0_stuff_into_its_own_source_file.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/031 -Handle_extending_EDNS0_OPT_RR.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/032 -Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting .patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/033 -Fix_build_failure_when_DNSSEC_code_omitted.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/034 -Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/035 -More_EDNS0_packet_size_tweaks.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/036 -Cache_access_to_the_kernels_ARP_table.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/037-First_complete_version_of_DNS -client-id_EDNS0_and_ARP_tracking_code.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/038 -Correct_logic_for_when_to_start_helper.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/039-Trivial_code_tweak.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/040 -Extra_check_in_NSEC3_processing.patch
- cd $(DIR_APP) && patch -Np1 -i
$(DIR_SRC)/src/patches/dnsmasq/041-Fix_linked-list_botch_in_new_ARP -cache_code.patch cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease -file.patch
cd $(DIR_APP) && sed -i src/config.h \ diff --git a/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease -file.patch b/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease -file.patch index f55ebe8..60514f1 100644 --- a/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease -file.patch +++ b/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease -file.patch @@ -56,7 +56,7 @@
--- a/src/dnsmasq.h Wed Dec 16 19:24:12 2015 +++ b/src/dnsmasq.h Wed Dec 16 19:40:11 2015 -@@ -1513,8 +1513,12 @@ +@@ -1509,6 +1509,11 @@ void poll_listen(int fd, short event); int do_poll(int timeout);
@@ -341,8 +341,8 @@ 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 isc.o +- poll.o rrfilter.o edns0.o arp.o ++ poll.o rrfilter.o edns0.o arp.o isc.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ dns-protocol.h radv-protocol.h ip6addr.h diff --git a/src/patches/dnsmasq/030 -Split_EDNS0_stuff_into_its_own_source_file.patch b/src/patches/dnsmasq/030 -Split_EDNS0_stuff_into_its_own_source_file.patch new file mode 100644 index 0000000..0cef987 --- /dev/null +++ b/src/patches/dnsmasq/030 -Split_EDNS0_stuff_into_its_own_source_file.patch @@ -0,0 +1,777 @@ +From 1d03016bbcb78962305cca20cbf7441423ff897d Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 21 Dec 2015 14:17:06 +0000 +Subject: [PATCH] Split EDNS0 stuff into its own source file.
+---
- Makefile | 2 +-
- bld/Android.mk | 2 +-
- src/dnsmasq.h | 17 +--
- src/edns0.c | 351
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/rfc1035.c | 334 ---------------------------------------------
- 5 files changed, 362 insertions(+), 344 deletions(-)
- create mode 100644 src/edns0.c
+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 < http://www.gnu.org/licenses/%3E. ++*/ ++ ++#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)
- {
+-- +1.7.10.4
diff --git a/src/patches/dnsmasq/031 -Handle_extending_EDNS0_OPT_RR.patch b/src/patches/dnsmasq/031 -Handle_extending_EDNS0_OPT_RR.patch new file mode 100644 index 0000000..386ff69 --- /dev/null +++ b/src/patches/dnsmasq/031-Handle_extending_EDNS0_OPT_RR.patch @@ -0,0 +1,295 @@ +From 5bb88f096363e66ac08e31761f850a1d5aa22244 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 21 Dec 2015 16:23:47 +0000 +Subject: [PATCH] Handle extending EDNS0 OPT RR.
+---
- src/dnsmasq.h | 4 +-
- src/dnssec.c | 2 +-
- src/edns0.c | 113 +++++++++++++++++++++++++++++++++++-----------
- src/forward.c | 16 ++++----
- 4 files changed, 80 insertions(+), 55 deletions(-)
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index a41c8cc..9828819 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -1117,8 +1117,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- int check_for_bogus_wildcard(struct dns_header *header, size_t
qlen, char *name,
struct bogus_addr *addr, time_t now);
- int check_for_ignored_address(struct dns_header *header, size_t
qlen, struct bogus_addr *baddr); +-unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, +- size_t *len, unsigned char **p, int *is_sign);
- 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,
+@@ -1514,6 +1512,8 @@ u16 *rrfilter_desc(int type);
- int expand_workspace(unsigned char ***wkspc, int *szp, int new);
- /* edns0.c */
++unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, ++ size_t *len, unsigned char **p, int *is_sign, int *is_last);
- 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); +diff --git a/src/dnssec.c b/src/dnssec.c +index 486e422..e0b7f39 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -2206,7 +2206,7 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
- ret = add_do_bit(header, p - (unsigned char *)header, end);
+- if (find_pseudoheader(header, ret, NULL, &p, NULL)) ++ if (find_pseudoheader(header, ret, NULL, &p, NULL, NULL))
PUTSHORT(edns_pktsz, p);
- return ret;
+diff --git a/src/edns0.c b/src/edns0.c +index f348b01..d1a11e7 100644 +--- a/src/edns0.c ++++ b/src/edns0.c +@@ -16,12 +16,12 @@
- #include "dnsmasq.h"
+-unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign) ++unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last)
- {
- /* 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) */ ++ forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
- int i, arcount = ntohs(header->arcount);
- unsigned char *ansp = (unsigned char *)(header+1);
+@@ -76,8 +76,13 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
- {
if (len)
*len = ansp - start;
++
if (p)
*p = save;
++ ++ if (is_last) ++ *is_last = (i == arcount-1); ++
ret = start;
- }
else if (is_sign &&
+@@ -100,50 +105,31 @@ struct macparm {
- 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; ++ unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL; ++ int rdlen = 0, is_sign, is_last; ++ unsigned short flags = set_do ? 0x8000 : 0, rcode = 0; ++ ++ p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
+- if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign))) +- { +- if (is_sign) +- return plen; ++ 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 ++ if (p)
{
++ /* Existing header */
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 */ ++ unsigned short code, len; ++ ++ p = udp_len; ++ GETSHORT(udp_sz, p); ++ GETSHORT(rcode, p);
GETSHORT(flags, p);
++
if (set_do)
- {
p -=2;
+- PUTSHORT(flags | 0x8000, p); ++ flags |= 0x8000; ++ PUTSHORT(flags, p);
- }
lenp = p;
+@@ -165,22 +151,61 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
return plen;
p += len;
- }
+- +- if (((ssize_t)optlen) > (limit - (p + 4))) +- return plen; /* Too big */ ++ ++ /* If we're going to extend the RR, it has to be the last RR in the packet */ ++ if (!is_last) ++ { ++ /* First, take a copy of the options. */ ++ if (rdlen != 0 && (buff = whine_malloc(rdlen))) ++ memcpy(buff, datap, rdlen); ++ ++ /* now, delete OPT RR */ ++ plen = rrfilter(header, plen, 0); ++ ++ /* Now, force addition of a new one */ ++ p = NULL; ++ } ++ } ++ ++ if (!p) ++ { ++ /* We are (re)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(rcode, p); /* extended RCODE and version */ ++ PUTSHORT(flags, p); /* DO flag */ ++ lenp = p; ++ PUTSHORT(rdlen, p); /* RDLEN */ ++ datap = p; ++ /* Copy back any options */ ++ if (buff) ++ { ++ memcpy(p, buff, rdlen); ++ free(buff); ++ p += rdlen; ++ } ++ header->arcount = htons(ntohs(header->arcount) + 1);
}
++ if (((ssize_t)optlen) > (limit - (p + 4))) ++ return plen; /* Too big */ ++ ++ /* Add new option */
- if (optno != 0)
{
PUTSHORT(optno, p);
PUTSHORT(optlen, p);
memcpy(p, opt, optlen);
p += optlen;
++ PUTSHORT(p - datap, lenp);
}
+- +- PUTSHORT(p - datap, lenp);
- return p - (unsigned char *)header;
+-
- }
- static int filter_mac(int family, char *addrp, char *mac, size_t
maclen, void *parmv) +diff --git a/src/forward.c b/src/forward.c +index 041353c..2ca3c86 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -276,7 +276,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
blockdata_retrieve(forward->stash, forward->stash_len,
(void *)header);
plen = forward->stash_len;
+- if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign) && !is_sign) ++ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
PUTSHORT(SAFE_PKTSZ, pheader);
if (forward->sentto->addr.sa.sa_family == AF_INET)
+@@ -479,7 +479,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
- #ifdef HAVE_DNSSEC
+- if (option_bool(OPT_DNSSEC_VALID) && !do_bit) ++ if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER))
{
/* Difficult one here. If our client didn't send
EDNS0, we will have set the UDP
packet size to 512. But that won't provide
space for the RRSIGS in many cases. +@@ -489,7 +489,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
the truncated bit? */
unsigned char *pheader;
int is_sign;
+- if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign)) ++ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
PUTSHORT(start->edns_pktsz, pheader);
}
- #endif
+@@ -584,7 +584,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
- #endif
+- if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign))) ++ if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
{
if (check_subnet && !check_source(header, plen, pheader,
query_source))
- {
+@@ -779,7 +779,7 @@ void reply_query(int fd, int family, time_t now)
int is_sign;
/* recreate query from reply */
+- pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign); ++ pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign, NULL);
if (!is_sign)
- {
header->ancount = htons(0);
+@@ -1313,7 +1313,7 @@ void receive_query(struct listener *listen, time_t now)
- #endif
}
+- if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL)) ++ if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL))
{
unsigned short flags;
+@@ -1569,7 +1569,7 @@ unsigned char *tcp_request(int confd, time_t now,
do_bit = 0;
+- if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL)) ++ if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
- {
unsigned short flags;
+@@ -1578,7 +1578,7 @@ unsigned char *tcp_request(int confd, time_t now,
GETSHORT(flags, pheader);
if (flags & 0x8000)
+- do_bit = 1;/* do bit */ ++ do_bit = 1; /* do bit */
- }
- #ifdef HAVE_AUTH
+-- +1.7.10.4
diff --git a/src/patches/dnsmasq/032 -Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting .patch b/src/patches/dnsmasq/032 -Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting .patch new file mode 100644 index 0000000..df90a4d --- /dev/null +++ b/src/patches/dnsmasq/032 -Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting .patch @@ -0,0 +1,65 @@ +From 5aa5f0ff2f8227ed743feb089dee421f1ca69943 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 21 Dec 2015 17:20:35 +0000 +Subject: [PATCH] Truncate DNS replies >512 bytes that the client isn't
- expecting.
+---
- src/edns0.c | 5 ++---
- src/forward.c | 17 +++++++++++++++--
- 2 files changed, 17 insertions(+), 5 deletions(-)
+diff --git a/src/edns0.c b/src/edns0.c +index d1a11e7..e137992 100644 +--- a/src/edns0.c ++++ b/src/edns0.c +@@ -339,9 +339,8 @@ size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)
- 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; ++ ++ int len, calc_len;
- struct subnet_opt opt;
- unsigned char *p;
- int code, i, rdlen;
+diff --git a/src/forward.c b/src/forward.c +index 2ca3c86..e1766b9 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -485,8 +485,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
packet size to 512. But that won't provide
space for the RRSIGS in many cases.
The RRSIGS will be stripped out before the
answer goes back, so the packet should
shrink again. So, if we added a do-bit, bump
the udp packet size to the value +- known to be OK for this server. Maybe check returned size after stripping and set +- the truncated bit? */ ++ known to be OK for this server. We check returned size after stripping and set ++ the truncated bit if it's still too big. */
unsigned char *pheader;
int is_sign;
if (find_pseudoheader(header, plen, NULL,
&pheader, &is_sign, NULL) && !is_sign) +@@ -1028,6 +1028,19 @@ void reply_query(int fd, int family, time_t now)
- {
header->id = htons(forward->orig_id);
header->hb4 |= HB4_RA; /* recursion if available */
++#ifdef HAVE_DNSSEC ++ /* We added an EDNSO header for the purpose of getting DNSSEC RRs, and set the value of the UDP payload size ++ greater than the no-EDNS0-implied 512 to have if space for the RRSIGS. If, having stripped them and the EDNS0 ++ header, the answer is still bigger than 512, truncate it and mark it so. The client then retries with TCP. */ ++ if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER) && (nn > PACKETSZ)) ++ { ++ header->ancount = htons(0); ++ header->nscount = htons(0); ++ header->arcount = htons(0); ++ header->hb3 |= HB3_TC; ++ nn = resize_packet(header, nn, NULL, 0); ++ } ++#endif
send_from(forward->fd, option_bool(OPT_NOWILD) ||
option_bool (OPT_CLEVERBIND), daemon->packet, nn,
&forward->source, &forward->dest, forward
->iface);
- }
+-- +1.7.10.4
diff --git a/src/patches/dnsmasq/033 -Fix_build_failure_when_DNSSEC_code_omitted.patch b/src/patches/dnsmasq/033 -Fix_build_failure_when_DNSSEC_code_omitted.patch new file mode 100644 index 0000000..eed613b --- /dev/null +++ b/src/patches/dnsmasq/033 -Fix_build_failure_when_DNSSEC_code_omitted.patch @@ -0,0 +1,55 @@ +From efef497b890231ba9232d02e7bfaf8273f044622 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 21 Dec 2015 17:30:44 +0000 +Subject: [PATCH] Fix build failure when DNSSEC code omitted.
+---
- src/dnsmasq.h | 2 --
- src/edns0.c | 12 +++++-------
- 2 files changed, 5 insertions(+), 9 deletions(-)
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 9828819..1286807 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -1518,7 +1518,5 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
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 +index e137992..f82ba1b 100644 +--- a/src/edns0.c ++++ b/src/edns0.c +@@ -208,6 +208,11 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
- return p - (unsigned char *)header;
- }
++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); ++} ++
- static int filter_mac(int family, char *addrp, char *mac, size_t
maclen, void *parmv)
- {
- struct macparm *parm = parmv;
+@@ -329,13 +334,6 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio
- 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. */
+-- +1.7.10.4
diff --git a/src/patches/dnsmasq/034 -Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch b/src/patches/dnsmasq/034 -Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch new file mode 100644 index 0000000..5742d4b --- /dev/null +++ b/src/patches/dnsmasq/034 -Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch @@ -0,0 +1,81 @@ +From 15379ea1f252d1f53c5d93ae970b22dedb233642 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 21 Dec 2015 18:31:55 +0000 +Subject: [PATCH] Log signature algo with DNSKEY and DS, also digest with DS.
+---
- src/cache.c | 2 +-
- src/dnsmasq.h | 6 ++++--
- src/dnssec.c | 15 +++++++++------
- 3 files changed, 14 insertions(+), 9 deletions(-)
+diff --git a/src/cache.c b/src/cache.c +index 51ba7cc..4da380a 100644 +--- a/src/cache.c ++++ b/src/cache.c +@@ -1580,7 +1580,7 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
- if (addr)
{
if (flags & F_KEYTAG)
+- sprintf(daemon->addrbuff, arg, addr->addr.keytag); ++ sprintf(daemon->addrbuff, arg, addr->addr.log.keytag, addr ->addr.log.algo, addr->addr.log.digest);
else
- {
- #ifdef HAVE_IPV6
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 1286807..4503a2d 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -256,8 +256,10 @@ struct all_addr {
struct in6_addr addr6;
- #endif
/* for log_query */
+- unsigned int keytag; +- /* for cache_insert if RRSIG, DNSKEY, DS */ ++ struct { ++ unsigned short keytag, algo, digest; ++ } log; ++ /* for cache_insert of DNSKEY, DS */
struct {
unsigned short class, type;
} dnssec;
+diff --git a/src/dnssec.c b/src/dnssec.c +index e0b7f39..ed2d3fe 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1115,11 +1115,12 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
}
else
{
+- a.addr.keytag = keytag; ++ a.addr.log.keytag = keytag; ++ a.addr.log.algo = algo;
if (verify_func(algo))
+- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u"); ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu");
else
+- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u (not supported)"); ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)");
recp1->addr.key.keylen = rdlen - 4;
recp1->addr.key.keydata = key;
+@@ -1241,11 +1242,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
}
else
{
+- a.addr.keytag = keytag; ++ a.addr.log.keytag = keytag; ++ a.addr.log.algo = algo; ++ a.addr.log.digest = digest;
if (hash_find(ds_digest_name(digest)) &&
verify_func(algo)) +- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u"); ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu");
else
+- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u (not supported)"); ++ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)");
crecp->addr.ds.digest = digest;
crecp->addr.ds.keydata = key;
+-- +1.7.10.4
diff --git a/src/patches/dnsmasq/035 -More_EDNS0_packet_size_tweaks.patch b/src/patches/dnsmasq/035 -More_EDNS0_packet_size_tweaks.patch new file mode 100644 index 0000000..5c8ebd7 --- /dev/null +++ b/src/patches/dnsmasq/035-More_EDNS0_packet_size_tweaks.patch @@ -0,0 +1,138 @@ +From d3a8b39c7df2f0debf3b5f274a1c37a9e261f94e Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Wed, 23 Dec 2015 12:27:37 +0000 +Subject: [PATCH] More EDNS0 packet-size tweaks.
+---
- src/dnsmasq.c | 7 +++++--
- src/dnsmasq.h | 8 +-------
- src/forward.c | 22 +++++++++++++++-------
- 3 files changed, 21 insertions(+), 16 deletions(-)
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index 81254f6..45761cc 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -91,8 +91,11 @@ int main (int argc, char **argv)
- if (daemon->edns_pktsz < PACKETSZ)
daemon->edns_pktsz = PACKETSZ;
+- daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? +- daemon->edns_pktsz : DNSMASQ_PACKETSZ; ++ /* Min buffer size: we check after adding each record, so there must be ++ memory for the largest packet, and the largest record so the ++ min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000. ++ This might be increased is EDNS packet size if greater than the minimum. */ ++ daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ;
- daemon->packet = safe_malloc(daemon->packet_buff_sz);
- daemon->addrbuff = safe_malloc(ADDRSTRLEN);
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 4503a2d..1c94f2a 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -179,13 +179,6 @@ struct event_desc {
- #define EC_MISC 5
- #define EC_INIT_OFFSET 10
+-/* Min buffer size: we check after adding each record, so there must be +- memory for the largest packet, and the largest record so the +- min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000. +- This might be increased is EDNS packet size if greater than the minimum. +-*/ +-#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ +-
- /* Trust the compiler dead-code eliminator.... */
- #define option_bool(x) (((x) < 32) ? daemon->options & (1u << (x))
: daemon->options2 & (1u << ((x) - 32)))
+@@ -594,6 +587,7 @@ struct hostsfile {
- #define FREC_DO_QUESTION 64
- #define FREC_ADDED_PHEADER 128
- #define FREC_TEST_PKTSZ 256
++#define FREC_HAS_EXTRADATA 512
- #ifdef HAVE_DNSSEC
- #define HASH_SIZE 20 /* SHA-1 digest size */
+diff --git a/src/forward.c b/src/forward.c +index e1766b9..c0e4d9a 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -389,13 +389,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
{
struct server *firstsentto = start;
int forwarded = 0;
+- ++ size_t edns0_len; ++
/* If a query is retried, use the log_id for the retry when
logging the answer. */
forward->log_id = daemon->log_id;
if (option_bool(OPT_ADD_MAC))
- {
+- size_t new = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); ++ size_t new = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source);
if (new != plen)
{
plen = new;
+@@ -405,7 +406,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (option_bool(OPT_CLIENT_SUBNET))
- {
+- size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source); ++ size_t new = add_source_addr(header, plen, ((char *) header) + PACKETSZ, &forward->source);
if (new != plen)
{
plen = new;
+@@ -416,7 +417,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
- #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
- {
+- size_t new = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz); ++ size_t new = add_do_bit(header, plen, ((char *) header) + PACKETSZ);
if (new != plen)
forward->flags |= FREC_ADDED_PHEADER;
+@@ -430,6 +431,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
- }
- #endif
++ ++ /* If we're sending an EDNS0 with any options, we can't recreate the query from a reply. */ ++ if (find_pseudoheader(header, plen, &edns0_len, NULL, NULL, NULL) && edns0_len > 11) ++ forward->flags |= FREC_HAS_EXTRADATA;
while (1)
- {
+@@ -769,9 +774,12 @@ void reply_query(int fd, int family, time_t now)
check_for_ignored_address(header, n, daemon->ignore_addr))
return;
++ /* Note: if we send extra options in the EDNS0 header, we can't recreate ++ the query from the reply. */
- if (RCODE(header) == REFUSED &&
!option_bool(OPT_ORDER) &&
+- forward->forwardall == 0) ++ forward->forwardall == 0 && ++ !(forward->flags & FREC_HAS_EXTRADATA))
/* for broken servers, attempt to send to another one. */
{
unsigned char *pheader;
+@@ -919,13 +927,13 @@ void reply_query(int fd, int family, time_t now)
if (status == STAT_NEED_KEY)
{
new->flags |= FREC_DNSKEY_QUERY;
+- nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz, ++ nn = dnssec_generate_query(header, ((char *) header) + server->edns_pktsz,
daemon
->keyname, forward->class, T_DNSKEY, &server->addr, server ->edns_pktsz);
}
else
{
new->flags |= FREC_DS_QUERY;
+- nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz, ++ nn = dnssec_generate_query(header,((char *) header) + server->edns_pktsz,
daemon
->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
}
if ((hash = hash_questions(header, nn, daemon
->namebuff))) +-- +1.7.10.4
diff --git a/src/patches/dnsmasq/036 -Cache_access_to_the_kernels_ARP_table.patch b/src/patches/dnsmasq/036-Cache_access_to_the_kernels_ARP_table.patch new file mode 100644 index 0000000..ec70419 --- /dev/null +++ b/src/patches/dnsmasq/036 -Cache_access_to_the_kernels_ARP_table.patch @@ -0,0 +1,414 @@ +From 11867dc28c7bd7c8a509ee7c8c7438cd2bcc1770 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Wed, 23 Dec 2015 16:15:58 +0000 +Subject: [PATCH] Cache access to the kernel's ARP table.
+---
- Makefile | 2 +-
- bld/Android.mk | 2 +-
- src/arp.c | 201
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- src/dhcp6.c | 54 ++++-----------
- src/dnsmasq.h | 4 ++
- src/edns0.c | 37 ++---------
- 6 files changed, 223 insertions(+), 77 deletions(-)
- create mode 100644 src/arp.c
+diff --git a/Makefile b/Makefile +index dfb0347..41e368f 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 edns0.o ++ poll.o rrfilter.o edns0.o arp.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 87966d2..eafef35 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 edns0.c ++ loop.c inotify.c poll.c rrfilter.c edns0.c arp.c
- LOCAL_MODULE := dnsmasq
+diff --git a/src/arp.c b/src/arp.c +new file mode 100644 +index 0000000..b624dac +--- /dev/null ++++ b/src/arp.c +@@ -0,0 +1,201 @@ ++/* 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/%3E. ++*/ ++ ++#include "dnsmasq.h" ++ ++#define ARP_FREE 0 ++#define ARP_FOUND 1 ++#define ARP_NEW 2 ++#define ARP_EMPTY 3 ++ ++struct arp_record { ++ short hwlen, status; ++ int family; ++ unsigned char hwaddr[DHCP_CHADDR_MAX]; ++ struct all_addr addr; ++ struct arp_record *next; ++}; ++ ++static struct arp_record *arps = NULL, *old = NULL; ++ ++static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) ++{ ++ int match = 0; ++ struct arp_record *arp; ++ ++ if (maclen > DHCP_CHADDR_MAX) ++ return 1; ++ ++ /* Look for existing entry */ ++ for (arp = arps; arp; arp = arp->next) ++ { ++ if (family != arp->family || arp->status == ARP_NEW) ++ continue; ++ ++ if (family == AF_INET) ++ { ++ if (arp->addr.addr.addr4.s_addr != ((struct in_addr *)addrp)->s_addr) ++ continue; ++ } ++#ifdef HAVE_IPV6 ++ else ++ { ++ if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, (struct in6_addr *)addrp)) ++ continue; ++ } ++#endif ++ ++ if (arp->status != ARP_EMPTY && arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0) ++ arp->status = ARP_FOUND; ++ else ++ { ++ /* existing address, MAC changed or arrived new. */ ++ arp->status = ARP_NEW; ++ arp->hwlen = maclen; ++ arp->family = family; ++ memcpy(arp->hwaddr, mac, maclen); ++ } ++ ++ break; ++ } ++ ++ if (!arp) ++ { ++ /* New entry */ ++ if (old) ++ { ++ arp = old; ++ old = old->next; ++ } ++ else if (!(arp = whine_malloc(sizeof(struct arp_record)))) ++ return 1; ++ ++ arp->next = arps; ++ arps = arp; ++ arp->status = ARP_NEW; ++ arp->hwlen = maclen; ++ arp->family = family; ++ memcpy(arp->hwaddr, mac, maclen); ++ if (family == AF_INET) ++ arp->addr.addr.addr4.s_addr = ((struct in_addr *)addrp) ->s_addr; ++#ifdef HAVE_IPV6 ++ else ++ memcpy(&arp->addr.addr.addr6, addrp, IN6ADDRSZ); ++#endif ++ } ++ ++ return 1; ++} ++ ++/* If in lazy mode, we cache absence of ARP entries. */ ++int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy) ++{ ++ struct arp_record *arp, **up; ++ int updated = 0; ++ ++ again: ++ ++ for (arp = arps; arp; arp = arp->next) ++ { ++ if (addr->sa.sa_family == arp->family) ++ { ++ if (arp->addr.addr.addr4.s_addr != addr ->in.sin_addr.s_addr) ++ continue; ++ } ++#ifdef HAVE_IPV6 ++ else ++ { ++ if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr ->in6.sin6_addr)) ++ continue; ++ } ++#endif ++ ++ /* Only accept poitive entries unless in lazy mode. */ ++ if (arp->status != ARP_EMPTY || lazy || updated) ++ { ++ if (mac && arp->hwlen != 0) ++ memcpy(mac, arp->hwaddr, arp->hwlen); ++ return arp->hwlen; ++ } ++ } ++ ++ /* Not found, try the kernel */ ++ if (!updated) ++ { ++ updated = 1; ++ ++ /* Mark all non-negative entries */ ++ for (arp = arps, up = &arps; arp; arp = arp->next) ++ if (arp->status != ARP_EMPTY) ++ arp->status = ARP_FREE; ++ ++ iface_enumerate(AF_UNSPEC, NULL, filter_mac); ++ ++ /* Remove all unconfirmed entries to old list, announce new ones. */ ++ for (arp = arps, up = &arps; arp; arp = arp->next) ++ if (arp->status == ARP_FREE) ++ { ++ *up = arp->next; ++ arp->next = old; ++ old = arp; ++ } ++ else ++ { ++ up = &arp->next; ++ if (arp->status == ARP_NEW) ++ { ++ char a[ADDRSTRLEN], m[ADDRSTRLEN]; ++ union mysockaddr pa; ++ pa.sa.sa_family = arp->family; ++ pa.in.sin_addr.s_addr = arp ->addr.addr.addr4.s_addr; ++ prettyprint_addr(&pa, a); ++ print_mac(m, arp->hwaddr, arp->hwlen); ++ my_syslog(LOG_INFO, _("new arp: %s %s"), a, m); ++ } ++ } ++ ++ goto again; ++ } ++ ++ /* record failure, so we don't consult the kernel each time ++ we're asked for this address */ ++ if (old) ++ { ++ arp = old; ++ old = old->next; ++ } ++ else ++ arp = whine_malloc(sizeof(struct arp_record)); ++ ++ if (arp) ++ { ++ arp->next = arps; ++ arps = arp; ++ arp->status = ARP_EMPTY; ++ arp->family = addr->sa.sa_family; ++ ++ if (addr->sa.sa_family == AF_INET) ++ arp->addr.addr.addr4.s_addr = addr->in.sin_addr.s_addr; ++#ifdef HAVE_IPV6 ++ else ++ memcpy(&arp->addr.addr.addr6, &addr->in6.sin6_addr, IN6ADDRSZ); ++#endif ++ } ++ ++ return 0; ++} ++ ++ +diff --git a/src/dhcp6.c b/src/dhcp6.c +index 8286ff4..7b1a7c7 100644 +--- a/src/dhcp6.c ++++ b/src/dhcp6.c +@@ -27,17 +27,10 @@ struct iface_param {
- int ind, addr_match;
- };
+-struct mac_param { +- struct in6_addr *target; +- unsigned char *mac; +- unsigned int maclen; +-}; +-
- static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
unsigned int preferred, unsigned int
valid, void *vparam); +-static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv);
- static int make_duid1(int index, unsigned int type, char *mac,
size_t maclen, void *parm);
- void dhcp6_init(void)
+@@ -264,9 +257,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
find the sender. Repeat a few times in case of packet loss. */
- struct neigh_packet neigh;
+- struct sockaddr_in6 addr; +- struct mac_param mac_param; +- int i; ++ union mysockaddr addr; ++ int i, maclen;
- neigh.type = ND_NEIGHBOR_SOLICIT;
- neigh.code = 0;
+@@ -277,55 +269,31 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
- memset(&addr, 0, sizeof(addr));
- #ifdef HAVE_SOCKADDR_SA_LEN
+- addr.sin6_len = sizeof(struct sockaddr_in6); ++ addr.in6.sin6_len = sizeof(struct sockaddr_in6);
- #endif
+- addr.sin6_family = AF_INET6; +- addr.sin6_port = htons(IPPROTO_ICMPV6); +- addr.sin6_addr = *client; +- addr.sin6_scope_id = iface; +- +- mac_param.target = client; +- mac_param.maclen = 0; +- mac_param.mac = mac; ++ addr.in6.sin6_family = AF_INET6; ++ addr.in6.sin6_port = htons(IPPROTO_ICMPV6); ++ addr.in6.sin6_addr = *client; ++ addr.in6.sin6_scope_id = iface;
- for (i = 0; i < 5; i++)
{
struct timespec ts;
+- iface_enumerate(AF_UNSPEC, &mac_param, find_mac); +- +- if (mac_param.maclen != 0) ++ if ((maclen = find_mac(&addr, mac, 0)) != 0)
- break;
+- +- sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, (struct sockaddr *)&addr, sizeof(addr)); ++ ++ sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
ts.tv_sec = 0;
ts.tv_nsec = 100000000; /* 100ms */
nanosleep(&ts, NULL);
}
+- *maclenp = mac_param.maclen; ++ *maclenp = maclen;
- *mactypep = ARPHRD_ETHER;
- }
+-static int find_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv) +-{ +- struct mac_param *parm = parmv; +- +- if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(parm->target, (struct in6_addr *)addrp)) +- { +- if (maclen <= DHCP_CHADDR_MAX) +- { +- parm->maclen = maclen; +- memcpy(parm->mac, mac, maclen); +- } +- +- return 0; /* found, abort */ +- } +- +- return 1; +-} +-
- static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int flags,
unsigned int preferred,
unsigned int valid, void *vparam)
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 1c94f2a..4459594 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -1516,3 +1516,7 @@ size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysock
- size_t add_source_addr(struct dns_header *header, size_t plen, char
*limit, union mysockaddr *source);
- size_t add_do_bit(struct dns_header *header, size_t plen, char
*limit);
- int check_source(struct dns_header *header, size_t plen, unsigned
char *pseudoheader, union mysockaddr *peer); ++ ++/* arp.c */ ++int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy); ++ +diff --git a/src/edns0.c b/src/edns0.c +index f82ba1b..9d8c0b9 100644 +--- a/src/edns0.c ++++ b/src/edns0.c +@@ -213,42 +213,15 @@ 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);
- }
+-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; ++ int maclen; ++ unsigned char mac[DHCP_CHADDR_MAX];
+- iface_enumerate(AF_UNSPEC, &parm, filter_mac); ++ if ((maclen = find_mac(l3, mac, 1)) != 0) ++ plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0);
+- return parm.plen; ++ return plen;
- }
- struct subnet_opt {
+-- +1.7.10.4
diff --git a/src/patches/dnsmasq/037-First_complete_version_of_DNS -client-id_EDNS0_and_ARP_tracking_code.patch b/src/patches/dnsmasq/037-First_complete_version_of_DNS-client -id_EDNS0_and_ARP_tracking_code.patch new file mode 100644 index 0000000..c89984a --- /dev/null +++ b/src/patches/dnsmasq/037-First_complete_version_of_DNS-client -id_EDNS0_and_ARP_tracking_code.patch @@ -0,0 +1,976 @@ +From 33702ab1f829789183cbaf6b1c39eee7ff15d744 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Mon, 28 Dec 2015 23:17:15 +0000 +Subject: [PATCH] First complete version of DNS-client-id EDNS0 and ARP
- tracking code.
+---
- src/arp.c | 148 +++++++++++++++++++++++++++++++----------
- src/config.h | 2 +-
- src/dhcp6.c | 6 +--
- src/dns-protocol.h | 2 +
- src/dnsmasq.c | 23 ++++----
- src/dnsmasq.h | 25 +++++----
- src/dnssec.c | 2 +-
- src/edns0.c | 72 ++++++++++++++++++++-----
- src/forward.c | 107 ++++++++++++++++++-------------------
- src/helper.c | 66 ++++++++++++++++++++---
- src/option.c | 9 ++++
- src/rfc3315.c | 7 +--
- 12 files changed, 308 insertions(+), 161 deletions(-)
+diff --git a/src/arp.c b/src/arp.c +index b624dac..f41cdec 100644 +--- a/src/arp.c ++++ b/src/arp.c +@@ -16,26 +16,31 @@
- #include "dnsmasq.h"
+-#define ARP_FREE 0 +-#define ARP_FOUND 1 +-#define ARP_NEW 2 +-#define ARP_EMPTY 3 ++/* Time between forced re-loads from kernel. */ ++#define INTERVAL 90 ++ ++#define ARP_MARK 0 ++#define ARP_FOUND 1 /* Confirmed */ ++#define ARP_NEW 2 /* Newly created */ ++#define ARP_EMPTY 3 /* No MAC addr */
- struct arp_record {
+- short hwlen, status; ++ unsigned short hwlen, status;
- int family;
- unsigned char hwaddr[DHCP_CHADDR_MAX];
- struct all_addr addr;
- struct arp_record *next;
- };
+-static struct arp_record *arps = NULL, *old = NULL; ++static struct arp_record *arps = NULL, *old = NULL, *freelist = NULL; ++static time_t last = 0;
- static int filter_mac(int family, char *addrp, char *mac, size_t
maclen, void *parmv)
- {
+- int match = 0;
- struct arp_record *arp;
++ (void)parmv; ++
- if (maclen > DHCP_CHADDR_MAX)
return 1;
+@@ -58,16 +63,18 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
- }
- #endif
+- if (arp->status != ARP_EMPTY && arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0) +- arp->status = ARP_FOUND; +- else ++ if (arp->status == ARP_EMPTY)
- {
+- /* existing address, MAC changed or arrived new. */ ++ /* existing address, was negative. */
arp->status = ARP_NEW;
arp->hwlen = maclen;
+- arp->family = family;
memcpy(arp->hwaddr, mac, maclen);
- }
++ else if (arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0) ++ /* Existing entry matches - confirm. */ ++ arp->status = ARP_FOUND; ++ else ++ continue;
break;
}
+@@ -75,10 +82,10 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
- if (!arp)
{
/* New entry */
+- if (old) ++ if (freelist)
- {
+- arp = old; +- old = old->next; ++ arp = freelist; ++ freelist = freelist->next;
- }
else if (!(arp = whine_malloc(sizeof(struct arp_record))))
- return 1;
+@@ -101,81 +108,72 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
- }
- /* If in lazy mode, we cache absence of ARP entries. */
+-int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy) ++int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
- {
- struct arp_record *arp, **up;
- int updated = 0;
- again:
+- for (arp = arps; arp; arp = arp->next) +- { +- if (addr->sa.sa_family == arp->family) +- { +- if (arp->addr.addr.addr4.s_addr != addr ->in.sin_addr.s_addr) +- continue; +- } ++ /* If the database is less then INTERVAL old, look in there */ ++ if (difftime(now, last) < INTERVAL) ++ for (arp = arps; arp; arp = arp->next) ++ { ++ if (addr->sa.sa_family == arp->family) ++ { ++ if (arp->addr.addr.addr4.s_addr != addr ->in.sin_addr.s_addr) ++ continue; ++ }
- #ifdef HAVE_IPV6
+- else +- { +- if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr ->in6.sin6_addr)) +- continue; +- } ++ else ++ { ++ if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr ->in6.sin6_addr)) ++ continue; ++ }
- #endif
+- +- /* Only accept poitive entries unless in lazy mode. */ +- if (arp->status != ARP_EMPTY || lazy || updated) +- { +- if (mac && arp->hwlen != 0) +- memcpy(mac, arp->hwaddr, arp->hwlen); +- return arp->hwlen; +- } +- } +- ++ ++ /* Only accept poitive entries unless in lazy mode. */ ++ if (arp->status != ARP_EMPTY || lazy || updated) ++ { ++ if (mac && arp->hwlen != 0) ++ memcpy(mac, arp->hwaddr, arp->hwlen); ++ return arp->hwlen; ++ } ++ } ++
- /* Not found, try the kernel */
- if (!updated)
{
updated = 1;
+- ++ last = now; ++
/* Mark all non-negative entries */
for (arp = arps, up = &arps; arp; arp = arp->next)
if (arp->status != ARP_EMPTY)
+- arp->status = ARP_FREE; ++ arp->status = ARP_MARK;
iface_enumerate(AF_UNSPEC, NULL, filter_mac);
+- /* Remove all unconfirmed entries to old list, announce new ones. */ ++ /* Remove all unconfirmed entries to old list. */
for (arp = arps, up = &arps; arp; arp = arp->next)
+- if (arp->status == ARP_FREE) ++ if (arp->status == ARP_MARK)
{
*up = arp->next;
arp->next = old;
old = arp;
}
else
+- { +- up = &arp->next; +- if (arp->status == ARP_NEW) +- { +- char a[ADDRSTRLEN], m[ADDRSTRLEN]; +- union mysockaddr pa; +- pa.sa.sa_family = arp->family; +- pa.in.sin_addr.s_addr = arp ->addr.addr.addr4.s_addr; +- prettyprint_addr(&pa, a); +- print_mac(m, arp->hwaddr, arp->hwlen); +- my_syslog(LOG_INFO, _("new arp: %s %s"), a, m); +- } +- } +- ++ up = &arp->next; ++
goto again;
}
- /* record failure, so we don't consult the kernel each time
we're asked for this address */
+- if (old) ++ if (freelist)
{
+- arp = old; +- old = old->next; ++ arp = freelist; ++ freelist = freelist->next;
}
- else
arp = whine_malloc(sizeof(struct arp_record));
+@@ -198,4 +196,36 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy)
- return 0;
- }
++int do_arp_script_run(void) ++{ ++ struct arp_record *arp; ++ ++ /* Notify any which went, then move to free list */ ++ if (old) ++ { ++#ifdef HAVE_SCRIPT ++ if (option_bool(OPT_DNS_CLIENT)) ++ queue_arp(ACTION_ARP_OLD, old->hwaddr, old->hwlen, old ->family, &old->addr); ++#endif ++ arp = old; ++ old = arp->next; ++ arp->next = freelist; ++ freelist = arp; ++ return 1; ++ } ++ ++ for (arp = arps; arp; arp = arp->next) ++ if (arp->status == ARP_NEW) ++ { ++#ifdef HAVE_SCRIPT ++ if (option_bool(OPT_DNS_CLIENT)) ++ queue_arp(ACTION_ARP, arp->hwaddr, arp->hwlen, arp ->family, &arp->addr); ++#endif ++ arp->status = ARP_FOUND; ++ return 1; ++ } ++ ++ return 0; ++} ++
+diff --git a/src/config.h b/src/config.h +index f75fe9d..309be6b 100644 +--- a/src/config.h ++++ b/src/config.h +@@ -337,7 +337,7 @@ HAVE_SOCKADDR_SA_LEN
- #define HAVE_DHCP
- #endif
+-#if defined(NO_SCRIPT) || !defined(HAVE_DHCP) || defined(NO_FORK) ++#if defined(NO_SCRIPT) || defined(NO_FORK)
- #undef HAVE_SCRIPT
- #undef HAVE_LUASCRIPT
- #endif
+diff --git a/src/dhcp6.c b/src/dhcp6.c +index 7b1a7c7..0e2e171 100644 +--- a/src/dhcp6.c ++++ b/src/dhcp6.c +@@ -220,7 +220,7 @@ void dhcp6_packet(time_t now)
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
+- relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id); ++ relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now);
return;
- }
+@@ -250,7 +250,7 @@ void dhcp6_packet(time_t now)
}
- }
+-void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep) ++void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)
- {
- /* Recieving a packet from a host does not populate the neighbour
cache, so we send a neighbour discovery request if we can't
+@@ -280,7 +280,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
{
struct timespec ts;
+- if ((maclen = find_mac(&addr, mac, 0)) != 0) ++ if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
- break;
sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa,
sizeof(addr)); +diff --git a/src/dns-protocol.h b/src/dns-protocol.h +index 6cf5158..addfa9e 100644 +--- a/src/dns-protocol.h ++++ b/src/dns-protocol.h +@@ -77,6 +77,8 @@
- #define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary
assignment */
- #define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
++#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */ ++#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
- struct dns_header {
- u16 id;
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index 45761cc..229693f 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -245,8 +245,11 @@ int main (int argc, char **argv)
- /* Note that order matters here, we must call lease_init before
creating any file descriptors which shouldn't be leaked
to the lease-script init process. We need to call common_init
+- before lease_init to allocate buffers it uses.*/ +- if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6) ++ before lease_init to allocate buffers it uses. ++ The script subsystrm relies on DHCP buffers, hence the last two ++ conditions below. */ ++ if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || ++ daemon->relay6 || option_bool(OPT_TFTP) || option_bool(OPT_ADD_MAC))
{
dhcp_common_init();
if (daemon->dhcp || daemon->doing_dhcp6)
+@@ -553,8 +556,9 @@ int main (int argc, char **argv)
- /* if we are to run scripts, we need to fork a helper before
dropping root. */
- daemon->helperfd = -1;
- #ifdef HAVE_SCRIPT
+- if ((daemon->dhcp || daemon->dhcp6) && (daemon ->lease_change_command || daemon->luascript)) +- daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); ++ if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_ADD_MAC)) && ++ (daemon->lease_change_command || daemon->luascript)) ++ daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
- #endif
- if (!option_bool(OPT_DEBUG) && getuid() == 0)
+@@ -914,9 +918,9 @@ int main (int argc, char **argv)
poll_listen(piperead, POLLIN);
+-#ifdef HAVE_DHCP +-# ifdef HAVE_SCRIPT +- while (helper_buf_empty() && do_script_run(now)); ++#ifdef HAVE_SCRIPT ++ while (helper_buf_empty() && do_script_run(now)); ++ while (helper_buf_empty() && do_arp_script_run());
- # ifdef HAVE_TFTP
while (helper_buf_empty() && do_tftp_script_run());
+@@ -924,16 +928,17 @@ int main (int argc, char **argv)
if (!helper_buf_empty())
- poll_listen(daemon->helperfd, POLLOUT);
+-# else ++#else
/* need this for other side-effects */
while (do_script_run(now));
++ while (do_arp_script_run(now));
- # ifdef HAVE_TFTP
while (do_tftp_script_run());
- # endif
+-# endif
- #endif
++
/* must do this just before select(), when we know no
more calls to my_syslog() can occur */
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 4459594..fec0f8d 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -235,7 +235,8 @@ struct event_desc {
- #define OPT_LOOP_DETECT 50
- #define OPT_EXTRALOG 51
- #define OPT_TFTP_NO_FAIL 52
+-#define OPT_LAST 53 ++#define OPT_DNS_CLIENT 53 ++#define OPT_LAST 54
- /* extra flags for my_syslog, we use a couple of facilities since
they are known
- not to occupy the same bits as priorities, no matter how
syslog.h is set up. */ +@@ -633,6 +634,8 @@ struct frec {
- #define ACTION_OLD 3
- #define ACTION_ADD 4
- #define ACTION_TFTP 5
++#define ACTION_ARP 6 ++#define ACTION_ARP_OLD 7
- #define LEASE_NEW 1 /* newly created */
- #define LEASE_CHANGED 2 /* modified */
+@@ -948,6 +951,7 @@ extern struct daemon {
- int cachesize, ftabsize;
- int port, query_port, min_port;
- unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl,
max_cache_ttl, auth_ttl; ++ char *dns_client_id;
- struct hostsfile *addn_hosts;
- struct dhcp_context *dhcp, *dhcp6;
- struct ra_interface *ra_interfaces;
+@@ -1135,7 +1139,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
- #endif
- /* dnssec.c */
+-size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz); ++size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz);
- int dnssec_validate_by_ds(time_t now, struct dns_header *header,
size_t n, char *name, char *keyname, int class);
- int dnssec_validate_ds(time_t now, struct dns_header *header,
size_t plen, char *name, char *keyname, int class);
- int dnssec_validate_reply(time_t now, struct dns_header *header,
size_t plen, char *name, char *keyname, int *class, +@@ -1372,6 +1376,8 @@ void queue_script(int action, struct dhcp_lease *lease,
- #ifdef HAVE_TFTP
- void queue_tftp(off_t file_len, char *filename, union mysockaddr
*peer);
- #endif
++void queue_arp(int action, unsigned char *mac, int maclen, ++ int family, struct all_addr *addr);
- int helper_buf_empty(void);
- #endif
+@@ -1408,7 +1414,7 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct
- void make_duid(time_t now);
- void dhcp_construct_contexts(time_t now);
- void get_client_mac(struct in6_addr *client, int iface, unsigned
char *mac, +- unsigned int *maclenp, unsigned int *mactypep); ++ unsigned int *maclenp, unsigned int *mactypep, time_t now);
- #endif
- /* rfc3315.c */
+@@ -1416,7 +1422,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
- unsigned short dhcp6_reply(struct dhcp_context *context, int
interface, char *iface_name,
struct in6_addr *fallback, struct
in6_addr *ll_addr, struct in6_addr *ula_addr,
size_t sz, struct in6_addr *client_addr,
time_t now); +-void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id); ++void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, ++ u32 scope_id, time_t now);
- unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz,
char *arrival_interface);
- #endif
+@@ -1512,11 +1519,11 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p,
int *is_sign, int *is_last);
- 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); +-size_t add_do_bit(struct dns_header *header, size_t plen, char *limit); ++size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit); ++size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, ++ union mysockaddr *source, time_t now, int *check_subnet);
- int check_source(struct dns_header *header, size_t plen, unsigned
char *pseudoheader, union mysockaddr *peer);
- /* arp.c */
+-int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy); +- ++int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now); ++int do_arp_script_run(void); +diff --git a/src/dnssec.c b/src/dnssec.c +index ed2d3fe..918a2dc 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -2173,7 +2173,7 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
}
- }
+-size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, ++size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class,
int type, union mysockaddr *addr, int
edns_pktsz)
- {
- unsigned char *p;
+diff --git a/src/edns0.c b/src/edns0.c +index 9d8c0b9..12e0210 100644 +--- a/src/edns0.c ++++ b/src/edns0.c +@@ -94,13 +94,6 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
- 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) +@@ -208,19 +201,54 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
- return p - (unsigned char *)header;
- }
+-size_t add_do_bit(struct dns_header *header, size_t plen, char *limit) ++size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
- {
- return add_pseudoheader(header, plen, (unsigned char *)limit,
PACKETSZ, 0, NULL, 0, 1);
- }
+-size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3) ++static unsigned char char64(unsigned char c) ++{ ++ return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f]; ++} ++ ++static void encoder(unsigned char *in, char *out) ++{ ++ out[0] = char64(in[0]>>2); ++ out[1] = char64((in[0]<<4) | (in[1]>>4)); ++ out[2] = char64((in[1]<<2) | (in[2]>>6)); ++ out[3] = char64(in[2]); ++} ++ ++static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
- {
- int maclen;
- unsigned char mac[DHCP_CHADDR_MAX];
++ char encode[8]; /* handle 6 byte MACs */ ++ ++ if ((maclen = find_mac(l3, mac, 1, now)) == 6) ++ { ++ encoder(mac, encode); ++ encoder(mac+3, encode+4); ++ ++ plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, 8, 0); ++ } ++ ++ if (daemon->dns_client_id) ++ plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, ++ (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0); ++ ++ return plen; ++} ++
+- if ((maclen = find_mac(l3, mac, 1)) != 0) ++static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now) ++{ ++ int maclen; ++ unsigned char mac[DHCP_CHADDR_MAX]; ++ ++ if ((maclen = find_mac(l3, mac, 1, now)) != 0)
plen = add_pseudoheader(header, plen, limit, PACKETSZ,
EDNS0_OPTION_MAC, mac, maclen, 0); +- ++
- return plen;
- }
+@@ -296,7 +324,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
- return len + 4;
- }
+-size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source) ++static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source)
- {
- /*
http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
+@@ -344,3 +372,23 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
- return 1;
- }
++ ++size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, ++ union mysockaddr *source, time_t now, int *check_subnet) ++{ ++ *check_subnet = 0; ++ ++ if (option_bool(OPT_ADD_MAC)) ++ plen = add_mac(header, plen, limit, source, now); ++ ++ if (option_bool(OPT_DNS_CLIENT)) ++ plen = add_dns_client(header, plen, limit, source, now); ++ ++ if (option_bool(OPT_CLIENT_SUBNET)) ++ { ++ plen = add_source_addr(header, plen, limit, source); ++ *check_subnet = 1; ++ } ++ ++ return plen; ++} +diff --git a/src/forward.c b/src/forward.c +index c0e4d9a..911f46e 100644 +--- a/src/forward.c ++++ b/src/forward.c +@@ -388,36 +388,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
- if (!flags && forward)
{
struct server *firstsentto = start;
+- int forwarded = 0; ++ int subnet, forwarded = 0;
size_t edns0_len;
/* If a query is retried, use the log_id for the retry when
logging the answer. */
forward->log_id = daemon->log_id;
+- if (option_bool(OPT_ADD_MAC)) +- { +- size_t new = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source); +- if (new != plen) +- { +- plen = new; +- forward->flags |= FREC_ADDED_PHEADER; +- } +- } +- +- if (option_bool(OPT_CLIENT_SUBNET)) ++ edns0_len = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet); ++ ++ if (edns0_len != plen)
- {
+- size_t new = add_source_addr(header, plen, ((char *) header) + PACKETSZ, &forward->source); +- if (new != plen) +- { +- plen = new; +- forward->flags |= FREC_HAS_SUBNET | FREC_ADDED_PHEADER; +- } ++ plen = edns0_len; ++ forward->flags |= FREC_ADDED_PHEADER; ++ ++ if (subnet) ++ forward->flags |= FREC_HAS_SUBNET;
- }
+- ++
- #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
- {
+- size_t new = add_do_bit(header, plen, ((char *) header) + PACKETSZ); ++ size_t new = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ);
if (new != plen)
forward->flags |= FREC_ADDED_PHEADER;
+@@ -607,15 +598,30 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
else
{
++ unsigned short udpsz; ++
/* If upstream is advertising a larger UDP packet
size
than we allow, trim it so that we don't get
overlarge
requests for the client. We can't do this for
signed packets. */ +- unsigned short udpsz; +- unsigned char *psave = sizep; +-
GETSHORT(udpsz, sizep);
if (udpsz > daemon->edns_pktsz)
+- PUTSHORT(daemon->edns_pktsz, psave); ++ { ++ sizep -= 2; ++ PUTSHORT(daemon->edns_pktsz, sizep); ++ } ++ ++#ifdef HAVE_DNSSEC ++ /* If the client didn't set the do bit, but we did, reset it. */ ++ if (option_bool(OPT_DNSSEC_VALID) && !do_bit) ++ { ++ unsigned short flags; ++ sizep += 2; /* skip RCODE */ ++ GETSHORT(flags, sizep); ++ flags &= ~0x8000; ++ sizep -= 2; ++ PUTSHORT(flags, sizep); ++ } ++#endif
}
- }
}
+@@ -674,14 +680,11 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
- #ifdef HAVE_DNSSEC
+- if (bogusanswer && !(header->hb4 & HB4_CD)) ++ if (bogusanswer && !(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG))
{
+- if (!option_bool(OPT_DNSSEC_DEBUG)) +- { +- /* Bogus reply, turn into SERVFAIL */ +- SET_RCODE(header, SERVFAIL); +- munged = 1; +- } ++ /* Bogus reply, turn into SERVFAIL */ ++ SET_RCODE(header, SERVFAIL); ++ munged = 1;
}
- if (option_bool(OPT_DNSSEC_VALID))
+@@ -802,7 +805,7 @@ void reply_query(int fd, int family, time_t now)
if (forward->flags |= FREC_AD_QUESTION)
header->hb4 |= HB4_AD;
if (forward->flags & FREC_DO_QUESTION)
+- add_do_bit(header, nn, (char *)pheader + plen); ++ add_do_bit(header, nn, (unsigned char *)pheader + plen);
forward_query(-1, NULL, NULL, 0, header, nn, now,
forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION);
return;
}
+@@ -927,13 +930,13 @@ void reply_query(int fd, int family, time_t now)
if (status == STAT_NEED_KEY)
{
new->flags |= FREC_DNSKEY_QUERY;
+- nn = dnssec_generate_query(header, ((char *) header) + server->edns_pktsz, ++ nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
daemon
->keyname, forward->class, T_DNSKEY, &server->addr, server ->edns_pktsz);
}
else
{
new->flags |= FREC_DS_QUERY;
+- nn = dnssec_generate_query(header,((char *) header) + server->edns_pktsz, ++ nn = dnssec_generate_query(header,((unsigned char *) header) + server ->edns_pktsz,
daemon
->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
}
if ((hash = hash_questions(header, nn, daemon
->namebuff))) +@@ -1434,7 +1437,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
break;
- }
+- m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class, ++ m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class,
new_status == STAT_NEED_KEY ?
T_DNSKEY : T_DS, &server->addr, server->edns_pktsz);
*length = htons(m);
+@@ -1548,8 +1551,6 @@ unsigned char *tcp_request(int confd, time_t now,
daemon->log_display_id = ++daemon->log_id;
daemon->log_source_addr = &peer_addr;
+- check_subnet = 0; +-
/* save state of "cd" flag in query */
if ((checking_disabled = header->hb4 & HB4_CD))
- no_cache_dnssec = 1;
+@@ -1627,20 +1628,14 @@ unsigned char *tcp_request(int confd, time_t now,
struct all_addr *addrp = NULL;
int type = 0;
char *domain = NULL;
+- +- if (option_bool(OPT_ADD_MAC)) +- size = add_mac(header, size, ((char *) header) + 65536, &peer_addr); +- +- if (option_bool(OPT_CLIENT_SUBNET)) ++ size_t new_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet); ++ ++ if (size != new_size)
{
+- size_t new = add_source_addr(header, size, ((char *) header) + 65536, &peer_addr); +- if (size != new) +- { +- size = new; +- check_subnet = 1; +- } ++ added_pheader = 1; ++ size = new_size;
}
+- ++
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon
->namebuff, &type, &domain, &norebind);
+@@ -1715,20 +1710,20 @@ unsigned char *tcp_request(int confd, time_t now,
}
- #ifdef HAVE_DNSSEC
+- added_pheader = 0;
if (option_bool(OPT_DNSSEC_VALID))
{
+- size_t new_size = add_do_bit(header, size, ((char *) header) + 65536); ++ new_size = add_do_bit(header, size, ((unsigned char *) header) + 65536); ++ ++ if (size != new_size) ++ { ++ added_pheader = 1; ++ size = new_size; ++ }
/* For debugging, set Checking
Disabled, otherwise, have the upstream check too,
this allows it to select auth
servers when one is returning bad data. */
if (option_bool(OPT_DNSSEC_DEBUG))
header->hb4 |= HB4_CD;
+- +- if (size != new_size) +- added_pheader = 1; +- +- size = new_size;
}
- #endif
}
+diff --git a/src/helper.c b/src/helper.c +index 1fee72d..517cfd9 100644 +--- a/src/helper.c ++++ b/src/helper.c +@@ -219,7 +219,18 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
action_str = "tftp";
is6 = (data.flags != AF_INET);
- }
+- else ++ else if (data.action == ACTION_ARP) ++ { ++ action_str = "arp"; ++ is6 = (data.flags != AF_INET); ++ } ++ else if (data.action == ACTION_ARP_OLD) ++ { ++ action_str = "arp-old"; ++ is6 = (data.flags != AF_INET); ++ data.action = ACTION_ARP; ++ } ++ else
- continue;
+@@ -321,6 +332,22 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
lua_call(lua, 2, 0); /* pass 2 values,
expect 0 */
}
}
++ else if (data.action == ACTION_ARP) ++ { ++ lua_getglobal(lua, "arp"); ++ if (lua_type(lua, -1) != LUA_TFUNCTION) ++ lua_pop(lua, 1); /* arp function optional */ ++ else ++ { ++ lua_pushstring(lua, action_str); /* arg1 - action */ ++ lua_newtable(lua); /* arg2 - data table */ ++ lua_pushstring(lua, daemon->addrbuff); ++ lua_setfield(lua, -2, "client_address"); ++ lua_pushstring(lua, daemon->dhcp_buff); ++ lua_setfield(lua, -2, "mac_address"); ++ lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ ++ } ++ }
else
{
lua_getglobal(lua, "lease"); /* function to call
*/ +@@ -478,7 +505,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
continue;
- }
+- if (data.action != ACTION_TFTP) ++ if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
- {
- #ifdef HAVE_DHCP6
my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 :
NULL, &err); +@@ -550,10 +577,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
my_setenv("DNSMASQ_OLD_HOSTNAME", data.action ==
ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
if (data.action == ACTION_OLD_HOSTNAME)
hostname = NULL;
+- } +- +- my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err); +- ++ ++ my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err); ++ }
/* we need to have the event_fd around if exec fails */
if ((i = fcntl(event_fd, F_GETFD)) != -1)
- fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
+@@ -563,8 +589,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
if (err == 0)
- {
execl(daemon->lease_change_command,
+- p ? p+1 : daemon->lease_change_command, +- action_str, is6 ? daemon->packet : daemon ->dhcp_buff, ++ p ? p+1 : daemon->lease_change_command, action_str, ++ (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
daemon->addrbuff, hostname, (char*)NULL);
err = errno;
- }
+@@ -760,6 +786,30 @@ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
- }
- #endif
++void queue_arp(int action, unsigned char *mac, int maclen, int family, struct all_addr *addr) ++{ ++ /* no script */ ++ if (daemon->helperfd == -1) ++ return; ++ ++ buff_alloc(sizeof(struct script_data)); ++ memset(buf, 0, sizeof(struct script_data)); ++ ++ buf->action = action; ++ buf->hwaddr_len = maclen; ++ buf->hwaddr_type = ARPHRD_ETHER; ++ if ((buf->flags = family) == AF_INET) ++ buf->addr = addr->addr.addr4; ++#ifdef HAVE_IPV6 ++ else ++ buf->addr6 = addr->addr.addr6; ++#endif ++ ++ memcpy(buf->hwaddr, mac, maclen); ++ ++ bytes_in_buf = sizeof(struct script_data); ++} ++
- int helper_buf_empty(void)
- {
- return bytes_in_buf == 0;
+diff --git a/src/option.c b/src/option.c +index 71beb98..f359bc5 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -154,6 +154,7 @@ struct myoption {
- #define LOPT_HOST_INOTIFY 342
- #define LOPT_DNSSEC_STAMP 343
- #define LOPT_TFTP_NO_FAIL 344
++#define LOPT_DNS_CLIENT_ID 355
- #ifdef HAVE_GETOPT_LONG
- static const struct option opts[] =
+@@ -281,6 +282,7 @@ static const struct myoption opts[] =
{ "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
{ "add-mac", 0, 0, LOPT_ADD_MAC },
{ "add-subnet", 2, 0, LOPT_ADD_SBNET },
++ { "add-dns-client", 2, 0 , LOPT_DNS_CLIENT_ID },
{ "proxy-dnssec", 0, 0, LOPT_DNSSEC },
{ "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
{ "conntrack", 0, 0, LOPT_CONNTRACK },
+@@ -446,6 +448,7 @@ static struct {
- { LOPT_TEST, 0, NULL, gettext_noop("Check configuration
syntax."), NULL },
- { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's
MAC address to forwarded DNS queries."), NULL },
- { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]",
gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL }, ++ { LOPT_DNS_CLIENT_ID, ARG_ONE, "<proxyname>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
- { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC
validation results from upstream nameservers."), NULL },
- { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to
allocate sequential IP addresses to DHCP clients."), NULL },
- { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy
connection-track mark from queries to upstream connections."), NULL }, +@@ -2150,6 +2153,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
- }
break;
++ case LOPT_DNS_CLIENT_ID: /* --add-dns-client */ ++ set_option_bool(OPT_DNS_CLIENT); ++ if (arg) ++ daemon->dns_client_id = opt_string_alloc(arg); ++ break; ++
case 'u': /* --user */
daemon->username = opt_string_alloc(arg);
break;
+diff --git a/src/rfc3315.c b/src/rfc3315.c +index 3ed8623..31bb41b 100644 +--- a/src/rfc3315.c ++++ b/src/rfc3315.c +@@ -130,7 +130,7 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
MAC address from the local ND cache. */
if (!state->link_address)
+- get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type); ++ get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now);
else
- {
struct dhcp_context *c;
+@@ -2054,7 +2054,8 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
- return ret;
- }
+-void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id) ++void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, ++ struct in6_addr *peer_address, u32 scope_id, time_t now)
- {
- /* ->local is same value for all relays on ->current chain */
+@@ -2068,7 +2069,7 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer
- unsigned char mac[DHCP_CHADDR_MAX];
- inet_pton(AF_INET6, ALL_SERVERS, &multicast);
+- get_client_mac(peer_address, scope_id, mac, &maclen, &mactype); ++ get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
- /* source address == relay address */
- from.addr.addr6 = relay->local.addr.addr6;
+-- +1.7.10.4
diff --git a/src/patches/dnsmasq/038 -Correct_logic_for_when_to_start_helper.patch b/src/patches/dnsmasq/038 -Correct_logic_for_when_to_start_helper.patch new file mode 100644 index 0000000..2c25d30 --- /dev/null +++ b/src/patches/dnsmasq/038 -Correct_logic_for_when_to_start_helper.patch @@ -0,0 +1,25 @@ +From 8e39c34077cdad5b8e7cc799443bf8d1f22a1e80 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Thu, 31 Dec 2015 16:18:11 +0000 +Subject: [PATCH] Correct logic for when to start helper.
+---
- src/dnsmasq.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index 229693f..009d357 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -556,7 +556,7 @@ int main (int argc, char **argv)
- /* if we are to run scripts, we need to fork a helper before
dropping root. */
- daemon->helperfd = -1;
- #ifdef HAVE_SCRIPT
+- if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_ADD_MAC)) && ++ if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_DNS_CLIENT)) &&
(daemon->lease_change_command || daemon->luascript))
daemon->helperfd = create_helper(pipewrite, err_pipe[1],
script_uid, script_gid, max_fd);
- #endif
+-- +1.7.10.4
diff --git a/src/patches/dnsmasq/039-Trivial_code_tweak.patch b/src/patches/dnsmasq/039-Trivial_code_tweak.patch new file mode 100644 index 0000000..ce0d23b --- /dev/null +++ b/src/patches/dnsmasq/039-Trivial_code_tweak.patch @@ -0,0 +1,33 @@ +From ec0628c4b2a06e1fc21216091bb040d61a43b271 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Thu, 31 Dec 2015 20:55:39 +0000 +Subject: [PATCH] Trivial code tweak.
+---
- src/dnssec.c | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
+diff --git a/src/dnssec.c b/src/dnssec.c +index 918a2dc..0e5cbe8 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1599,12 +1599,12 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
if (!CHECK_LEN(header, p, plen, rdlen))
return 0;
+- /* If we can prove that there's no NS record, return that information. */ +- if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0) +- *nons = 0; +-
if (rdlen >= 2 && p[0] == 0)
{
++ /* If we can prove that there's no NS record, return that information. */ ++ if (nons && (p[2] & (0x80 >> T_NS)) != 0) ++ *nons = 0; ++
/* A CNAME answer would also be valid, so if
there's a CNAME is should
have been returned. */
if ((p[2] & (0x80 >> T_CNAME)) != 0)
+-- +1.7.10.4
diff --git a/src/patches/dnsmasq/040 -Extra_check_in_NSEC3_processing.patch b/src/patches/dnsmasq/040 -Extra_check_in_NSEC3_processing.patch new file mode 100644 index 0000000..d0daa23 --- /dev/null +++ b/src/patches/dnsmasq/040-Extra_check_in_NSEC3_processing.patch @@ -0,0 +1,60 @@ +From f89391d09844837b7ec4394f1e3008c6f339b4bd Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Fri, 1 Jan 2016 19:44:11 +0000 +Subject: [PATCH] Extra check in NSEC3 processing.
+---
- src/dnssec.c | 30 +++++++++++++++++++++++++++---
- 1 file changed, 27 insertions(+), 3 deletions(-)
+diff --git a/src/dnssec.c b/src/dnssec.c +index 0e5cbe8..cd29d88 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1665,8 +1665,8 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
- static int prove_non_existence_nsec3(struct dns_header *header,
size_t plen, unsigned char **nsecs, int nsec_count,
char *workspace1, char
*workspace2, char *name, int type, char *wildname, int *nons)
- {
+- unsigned char *salt, *p, *digest; +- int digest_len, i, iterations, salt_len, base32_len, algo = 0; ++ unsigned char *salt, *p, *digest, *psave; ++ int digest_len, i, rdlen, iterations, salt_len, hash_len, base32_len, algo = 0;
- struct nettle_hash const *hash;
- char *closest_encloser, *next_closest, *wildcard;
+@@ -1785,7 +1785,31 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- if (!closest_encloser)
return 0;
+- ++ ++ p += 8; /* class, type, TTL */ ++ GETSHORT(rdlen, p); ++ psave = p; ++ p += 4; /* algo, flags, iterations */ ++ salt_len = *p++; /* salt_len */ ++ p += salt_len; /* salt */ ++ hash_len = *p++; /* p now points to next hashed name */ ++ p += hash_len; /* skip next-domain hash */ ++ rdlen -= p - psave; ++ ++ if (!CHECK_LEN(header, p, plen, rdlen)) ++ return 0; ++ ++ if (rdlen >= 2 && p[0] == 0) ++ { ++ /* 5155 8.3: the NS type bit may only be set if the SOA type bit is set. */ ++ if ((p[2] & (0x80 >> T_NS)) != 0 && (p[2] & (0x80 >> T_SOA)) == 0) ++ return 0; ++ ++ /* 5155 8.3: The DNAME type bit must not be set. */ ++ if (rdlen >= 2 + (T_DNAME/8) && (p[2 + (T_DNAME/8)] & (0x80
(T_DNAME%8))) != 0)
++ return 0; ++ } ++
- /* Look for NSEC3 that proves the non-existence of the next
-closest encloser */
- if ((digest_len = hash_name(next_closest, &digest, hash, salt,
salt_len, iterations)) == 0)
return 0;
+-- +1.7.10.4
diff --git a/src/patches/dnsmasq/041-Fix_linked-list_botch_in_new_ARP -cache_code.patch b/src/patches/dnsmasq/041-Fix_linked -list_botch_in_new_ARP-cache_code.patch new file mode 100644 index 0000000..396171d --- /dev/null +++ b/src/patches/dnsmasq/041-Fix_linked-list_botch_in_new_ARP -cache_code.patch @@ -0,0 +1,55 @@ +From 19eeed3b32086ec3bd18cb2841d310ff5953b6d7 Mon Sep 17 00:00:00 2001 +From: Simon Kelley simon@thekelleys.org.uk +Date: Fri, 1 Jan 2016 20:24:15 +0000 +Subject: [PATCH] Fix linked-list botch in new ARP-cache code.
+---
- src/arp.c | 25 ++++++++++++++-----------
- 1 file changed, 14 insertions(+), 11 deletions(-)
+diff --git a/src/arp.c b/src/arp.c +index f41cdec..374446c 100644 +--- a/src/arp.c ++++ b/src/arp.c +@@ -110,7 +110,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
- /* If in lazy mode, we cache absence of ARP entries. */
- int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy,
time_t now)
- {
+- struct arp_record *arp, **up; ++ struct arp_record *arp, *tmp, **up;
- int updated = 0;
- again:
+@@ -155,16 +155,19 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
iface_enumerate(AF_UNSPEC, NULL, filter_mac);
/* Remove all unconfirmed entries to old list. */
+- for (arp = arps, up = &arps; arp; arp = arp->next) +- if (arp->status == ARP_MARK) +- { +- *up = arp->next; +- arp->next = old; +- old = arp; +- } +- else +- up = &arp->next; +- ++ for (arp = arps, up = &arps; arp; arp = tmp) ++ { ++ tmp = arp->next; ++ if (arp->status == ARP_MARK) ++ { ++ *up = arp->next; ++ arp->next = old; ++ old = arp; ++ } ++ else ++ up = &arp->next; ++ } ++
goto again;
}
+-- +1.7.10.4