mbox

dnsmasq: 2.75 with latest patches 030-041 (2016-01-04)

Message ID 1451927214-28404-1-git-send-email-matthias.fischer@ipfire.org
State Superseded
Headers

Message

Matthias Fischer Jan. 5, 2016, 4:06 a.m. UTC
  Only for testing - this won't compile!

Hi,

On 04.01.2016 17:42, Michael Tremer wrote:
> is this still an issue?

For me: definitely YES.

> The nightly builds seem to build fine...

I think this is because the nightly builds contain patches No. 001-29.

The problems came up after I integrated patch No. 030-041, as shown below.

Even the testrelease '2.76test2' wouldn't build (I haven't tested '276test3' yet,
which came up a few hours ago).

Can anyone confirm?

Best,
Matthias

Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
---
 lfs/dnsmasq                                        |  12 +
 ...q-Add-support-to-read-ISC-DHCP-lease-file.patch |   6 +-
 ...plit_EDNS0_stuff_into_its_own_source_file.patch | 777 ++++++++++++++++
 .../031-Handle_extending_EDNS0_OPT_RR.patch        | 295 +++++++
 ..._512_bytes_that_the_client_isnt_expecting.patch |  65 ++
 ...ix_build_failure_when_DNSSEC_code_omitted.patch |  55 ++
 ...go_with_DNSKEY_and_DS_also_digest_with_DS.patch |  81 ++
 .../035-More_EDNS0_packet_size_tweaks.patch        | 138 +++
 ...036-Cache_access_to_the_kernels_ARP_table.patch | 414 +++++++++
 ...DNS-client-id_EDNS0_and_ARP_tracking_code.patch | 976 +++++++++++++++++++++
 ...38-Correct_logic_for_when_to_start_helper.patch |  25 +
 src/patches/dnsmasq/039-Trivial_code_tweak.patch   |  33 +
 .../040-Extra_check_in_NSEC3_processing.patch      |  60 ++
 ...x_linked-list_botch_in_new_ARP-cache_code.patch |  55 ++
 14 files changed, 2989 insertions(+), 3 deletions(-)
 create mode 100644 src/patches/dnsmasq/030-Split_EDNS0_stuff_into_its_own_source_file.patch
 create mode 100644 src/patches/dnsmasq/031-Handle_extending_EDNS0_OPT_RR.patch
 create mode 100644 src/patches/dnsmasq/032-Truncate_DNS_replies_bigger_512_bytes_that_the_client_isnt_expecting.patch
 create mode 100644 src/patches/dnsmasq/033-Fix_build_failure_when_DNSSEC_code_omitted.patch
 create mode 100644 src/patches/dnsmasq/034-Log_signature_algo_with_DNSKEY_and_DS_also_digest_with_DS.patch
 create mode 100644 src/patches/dnsmasq/035-More_EDNS0_packet_size_tweaks.patch
 create mode 100644 src/patches/dnsmasq/036-Cache_access_to_the_kernels_ARP_table.patch
 create mode 100644 src/patches/dnsmasq/037-First_complete_version_of_DNS-client-id_EDNS0_and_ARP_tracking_code.patch
 create mode 100644 src/patches/dnsmasq/038-Correct_logic_for_when_to_start_helper.patch
 create mode 100644 src/patches/dnsmasq/039-Trivial_code_tweak.patch
 create mode 100644 src/patches/dnsmasq/040-Extra_check_in_NSEC3_processing.patch
 create mode 100644 src/patches/dnsmasq/041-Fix_linked-list_botch_in_new_ARP-cache_code.patch
  

Comments

Michael Tremer Jan. 5, 2016, 9:53 a.m. UTC | #1
On Mon, 2016-01-04 at 18:06 +0100, Matthias Fischer wrote:
> Only for testing - this won't compile!
> 
> Hi,
> 
> On 04.01.2016 17:42, Michael Tremer wrote:
> > is this still an issue?
> 
> For me: definitely YES.
> 
> > The nightly builds seem to build fine...
> 
> I think this is because the nightly builds contain patches No. 001
> -29.
> 
> The problems came up after I integrated patch No. 030-041, as shown
> below.
> 
> Even the testrelease '2.76test2' wouldn't build (I haven't tested
> '276test3' yet,
> which came up a few hours ago).
> 
> Can anyone confirm?

Yes I can confirm. It won't build.

Best,
-Michael

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