public inbox for development@lists.ipfire.org
 help / color / mirror / Atom feed
From: Matthias Fischer <matthias.fischer@ipfire.org>
To: development@lists.ipfire.org
Subject: [PATCH] dnsmasq 2.75: latest patches from upstream
Date: Fri, 08 Jan 2016 19:19:52 +0100	[thread overview]
Message-ID: <1452277192-11460-1-git-send-email-matthias.fischer@ipfire.org> (raw)

[-- Attachment #1: Type: text/plain, Size: 134351 bytes --]

(Compilation errors with '#undef HAVE_DHCP' fixed (042))

Signed-off-by: Matthias Fischer <matthias.fischer(a)ipfire.org>
---
 lfs/dnsmasq                                        |  15 +
 ...q-Add-support-to-read-ISC-DHCP-lease-file.patch |  10 +-
 ...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 +
 ...SEC_sig_timestamps_when_far_in_the_future.patch |  50 ++
 ...ist_code_resulting_in_100percent_CPU_spin.patch |  56 ++
 ..._script_support_enabled_and_DHCP_disabled.patch |  48 +
 ...3-Update_copyright_notices_Happy_new_year.patch | 473 ++++++++++
 ...when_scripts_excluded_at_compilation_time.patch |  25 +
 17 files changed, 3531 insertions(+), 5 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-Fix_datatype-sixe_botch_which_broke_DNSSEC_sig_timestamps_when_far_in_the_future.patch
 create mode 100644 src/patches/dnsmasq/041-Fix_botch_in_new_arp-cache_linked-list_code_resulting_in_100percent_CPU_spin.patch
 create mode 100644 src/patches/dnsmasq/042-Handle_building_with_script_support_enabled_and_DHCP_disabled.patch
 create mode 100644 src/patches/dnsmasq/043-Update_copyright_notices_Happy_new_year.patch
 create mode 100644 src/patches/dnsmasq/044-Fix_FTBFS_when_scripts_excluded_at_compilation_time.patch

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


             reply	other threads:[~2016-01-08 18:19 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-08 18:19 Matthias Fischer [this message]
  -- strict thread matches above, loose matches on Subject: below --
2016-01-14 17:39 Matthias Fischer
2015-12-24  9:17 Matthias Fischer
2015-12-28 14:40 ` Michael Tremer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1452277192-11460-1-git-send-email-matthias.fischer@ipfire.org \
    --to=matthias.fischer@ipfire.org \
    --cc=development@lists.ipfire.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox