* [PATCH] dnsmasq: 2.75 with latest patches 030-041 (2016-01-04)
@ 2016-01-04 17:06 Matthias Fischer
2016-01-04 22:53 ` Michael Tremer
0 siblings, 1 reply; 2+ messages in thread
From: Matthias Fischer @ 2016-01-04 17:06 UTC (permalink / raw)
To: development
[-- Attachment #1: Type: text/plain, Size: 111920 bytes --]
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(a)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 +
| 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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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
+
--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(a)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(a)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
+
--
2.6.4
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] dnsmasq: 2.75 with latest patches 030-041 (2016-01-04)
2016-01-04 17:06 [PATCH] dnsmasq: 2.75 with latest patches 030-041 (2016-01-04) Matthias Fischer
@ 2016-01-04 22:53 ` Michael Tremer
0 siblings, 0 replies; 2+ messages in thread
From: Michael Tremer @ 2016-01-04 22:53 UTC (permalink / raw)
To: development
[-- Attachment #1: Type: text/plain, Size: 116385 bytes --]
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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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
> +
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2016-01-04 22:53 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-04 17:06 [PATCH] dnsmasq: 2.75 with latest patches 030-041 (2016-01-04) Matthias Fischer
2016-01-04 22:53 ` Michael Tremer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox