dnsmasq: follow upstream dnsmasq pre-v2.81 v2
authorKevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
Thu, 25 Oct 2018 09:20:25 +0000 (10:20 +0100)
committerKevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
Mon, 10 Dec 2018 10:38:49 +0000 (10:38 +0000)
Backport upstream commits.  Most interesting 122392e which changes how
SERVFAIL is handled especially in event of genuine server down/failure
scenarios with multiple servers.  a799ca0 also interesting in that
answered received via TCP are now cached, DNSSEC typically using TCP
meant until now answers weren't cached, hence reducing performance.

59e4703 Free config file values on parsing errors.
48d12f1 Remove the NO_FORK compile-time option, and support for uclinux.
122392e Revert 68f6312d4bae30b78daafcd6f51dc441b8685b1e
3a5a84c Fix Makefile lines generating UBUS linker config.
24b8760 Do not rely on dead code elimination, use array instead. Make options bits derived from size and count. Use size of option bits and last supported bit in computation. No new change would be required when new options are added. Just change OPT_LAST constant.
6f7812d Fix spurious AD flags in some DNS replies from local config.
cbb5b17 Fix logging in cf5984367bc6a949e3803a576512c5a7bc48ebab
cf59843 Don't forward *.bind/*.server queries upstream
ee87504 Remove ability to compile without IPv6 support.
a220545 Ensure that AD bit is reset on answers from --address=/<domain>/<address>.
a799ca0 Impove cache behaviour for TCP connections.

Along with an additional patch to fix compilation without DHCPv6, sent
upstream.

I've been running this for aaaages without obvious issue hence brave
step of opening to wider openwrt community.

Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
14 files changed:
package/network/services/dnsmasq/Makefile
package/network/services/dnsmasq/patches/0001-Impove-cache-behaviour-for-TCP-connections.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0002-Ensure-that-AD-bit-is-reset-on-answers-from-address-.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0003-Remove-ability-to-compile-without-IPv6-support.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0004-Don-t-forward-.bind-.server-queries-upstream.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0005-Fix-logging-in-cf5984367bc6a949e3803a576512c5a7bc48e.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0006-Fix-spurious-AD-flags-in-some-DNS-replies-from-local.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0007-Do-not-rely-on-dead-code-elimination-use-array-inste.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0008-Fix-Makefile-lines-generating-UBUS-linker-config.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0009-Revert-68f6312d4bae30b78daafcd6f51dc441b8685b1e.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0010-Remove-the-NO_FORK-compile-time-option-and-support-f.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/0012-option-fix-non-DHCPv6-build-error.patch [new file with mode: 0644]
package/network/services/dnsmasq/patches/110-ipset-remove-old-kernel-support.patch

index 10537d711f385c05e1d589664340b25b3fb04b0a..85c43505e0976a88c5abc7439d318b735ac37208 100644 (file)
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=dnsmasq
 PKG_VERSION:=2.80
-PKG_RELEASE:=2
+PKG_RELEASE:=3
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
 PKG_SOURCE_URL:=http://thekelleys.org.uk/dnsmasq
diff --git a/package/network/services/dnsmasq/patches/0001-Impove-cache-behaviour-for-TCP-connections.patch b/package/network/services/dnsmasq/patches/0001-Impove-cache-behaviour-for-TCP-connections.patch
new file mode 100644 (file)
index 0000000..453c292
--- /dev/null
@@ -0,0 +1,495 @@
+From a799ca0c6314ad73a97bc6c89382d2712a9c0b0e Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Thu, 18 Oct 2018 19:35:29 +0100
+Subject: [PATCH 01/11] Impove cache behaviour for TCP connections.
+
+For ease of implementaion, dnsmasq has always forked a new process to
+handle each incoming TCP connection. A side-effect of this is that any
+DNS queries answered from TCP connections are not cached: when TCP
+connections were rare, this was not a problem.  With the coming of
+DNSSEC, it's now the case that some DNSSEC queries have answers which
+spill to TCP, and if, for instance, this applies to the keys for the
+root then those never get cached, and performance is very bad.  This
+fix passes cache entries back from the TCP child process to the main
+server process, and fixes the problem.
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ CHANGELOG       |  14 ++++
+ src/blockdata.c |  37 ++++++++-
+ src/cache.c     | 196 ++++++++++++++++++++++++++++++++++++++++++++++--
+ src/dnsmasq.c   |  58 ++++++++++++--
+ src/dnsmasq.h   |   5 ++
+ 5 files changed, 291 insertions(+), 19 deletions(-)
+
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -1,3 +1,17 @@
++version 2.81
++      Impove cache behaviour for TCP connections. For ease of
++      implementaion, dnsmasq has always forked a new process to handle
++      each incoming TCP connection. A side-effect of this is that
++      any DNS queries answered from TCP connections are not cached:
++      when TCP connections were rare, this was not a problem.
++      With the coming of DNSSEC, it's now the case that some
++      DNSSEC queries have answers which spill to TCP, and if,
++      for instance, this applies to the keys for the root then
++      those never get cached, and performance is very bad.
++      This fix passes cache entries back from the TCP child process to
++      the main server process, and fixes the problem.
++
++
+ version 2.80
+       Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method
+       for the initial patch and motivation.
+--- a/src/blockdata.c
++++ b/src/blockdata.c
+@@ -61,7 +61,7 @@ void blockdata_report(void)
+             blockdata_alloced * sizeof(struct blockdata));
+ } 
+-struct blockdata *blockdata_alloc(char *data, size_t len)
++static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len)
+ {
+   struct blockdata *block, *ret = NULL;
+   struct blockdata **prev = &ret;
+@@ -89,8 +89,17 @@ struct blockdata *blockdata_alloc(char *
+       blockdata_hwm = blockdata_count; 
+       
+       blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
+-      memcpy(block->key, data, blen);
+-      data += blen;
++      if (data)
++      {
++        memcpy(block->key, data, blen);
++        data += blen;
++      }
++      else if (!read_write(fd, block->key, blen, 1))
++      {
++        /* failed read free partial chain */
++        blockdata_free(ret);
++        return NULL;
++      }
+       len -= blen;
+       *prev = block;
+       prev = &block->next;
+@@ -100,6 +109,10 @@ struct blockdata *blockdata_alloc(char *
+   return ret;
+ }
++struct blockdata *blockdata_alloc(char *data, size_t len)
++{
++  return blockdata_alloc_real(0, data, len);
++}
+ void blockdata_free(struct blockdata *blocks)
+ {
+@@ -148,5 +161,21 @@ void *blockdata_retrieve(struct blockdat
+   return data;
+ }
+- 
++
++
++void blockdata_write(struct blockdata *block, size_t len, int fd)
++{
++  for (; len > 0 && block; block = block->next)
++    {
++      size_t blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
++      read_write(fd, block->key, blen, 0);
++      len -= blen;
++    }
++}
++
++struct blockdata *blockdata_read(int fd, size_t len)
++{
++  return blockdata_alloc_real(fd, NULL, len);
++}
++
+ #endif
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -26,6 +26,8 @@ static union bigname *big_free = NULL;
+ static int bignames_left, hash_size;
+ static void make_non_terminals(struct crec *source);
++static struct crec *really_insert(char *name, struct all_addr *addr, 
++                                time_t now,  unsigned long ttl, unsigned short flags);
+ /* type->string mapping: this is also used by the name-hash function as a mixing table. */
+ static const struct {
+@@ -464,16 +466,10 @@ void cache_start_insert(void)
+   new_chain = NULL;
+   insert_error = 0;
+ }
+- 
++
+ struct crec *cache_insert(char *name, struct all_addr *addr, 
+                         time_t now,  unsigned long ttl, unsigned short flags)
+ {
+-  struct crec *new, *target_crec = NULL;
+-  union bigname *big_name = NULL;
+-  int freed_all = flags & F_REVERSE;
+-  int free_avail = 0;
+-  unsigned int target_uid;
+-  
+   /* Don't log DNSSEC records here, done elsewhere */
+   if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
+     {
+@@ -484,7 +480,20 @@ struct crec *cache_insert(char *name, st
+       if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
+       ttl = daemon->min_cache_ttl;
+     }
++  
++  return really_insert(name, addr, now, ttl, flags);
++}
++
++static struct crec *really_insert(char *name, struct all_addr *addr, 
++                                time_t now,  unsigned long ttl, unsigned short flags)
++{
++  struct crec *new, *target_crec = NULL;
++  union bigname *big_name = NULL;
++  int freed_all = flags & F_REVERSE;
++  int free_avail = 0;
++  unsigned int target_uid;
++  
+   /* if previous insertion failed give up now. */
+   if (insert_error)
+     return NULL;
+@@ -645,12 +654,185 @@ void cache_end_insert(void)
+         cache_hash(new_chain);
+         cache_link(new_chain);
+         daemon->metrics[METRIC_DNS_CACHE_INSERTED]++;
++
++        /* If we're a child process, send this cache entry up the pipe to the master.
++           The marshalling process is rather nasty. */
++        if (daemon->pipe_to_parent != -1)
++          {
++            char *name = cache_get_name(new_chain);
++            ssize_t m = strlen(name);
++            unsigned short flags = new_chain->flags;
++#ifdef HAVE_DNSSEC
++            u16 class = new_chain->uid;
++#endif
++            
++            read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0);
++            read_write(daemon->pipe_to_parent, (unsigned char *)name, m, 0);
++            read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0);
++            read_write(daemon->pipe_to_parent, (unsigned  char *)&flags, sizeof(flags), 0);
++
++            if (flags & (F_IPV4 | F_IPV6))
++              read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0);
++#ifdef HAVE_DNSSEC
++            else if (flags & F_DNSKEY)
++              {
++                read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0);
++                read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr.key.algo, sizeof(new_chain->addr.key.algo), 0);
++                read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr.key.keytag, sizeof(new_chain->addr.key.keytag), 0);
++                read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr.key.flags, sizeof(new_chain->addr.key.flags), 0);
++                read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr.key.keylen, sizeof(new_chain->addr.key.keylen), 0);
++                blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent);
++              }
++            else if (flags & F_DS)
++              {
++                read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), 0);
++                /* A negative DS entry is possible and has no data, obviously. */
++                if (!(flags & F_NEG))
++                  {
++                    read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr.ds.algo, sizeof(new_chain->addr.ds.algo), 0);
++                    read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr.ds.keytag, sizeof(new_chain->addr.ds.keytag), 0);
++                    read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr.ds.digest, sizeof(new_chain->addr.ds.digest), 0);
++                    read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr.ds.keylen, sizeof(new_chain->addr.ds.keylen), 0);
++                    blockdata_write(new_chain->addr.ds.keydata, new_chain->addr.ds.keylen, daemon->pipe_to_parent);
++                  }
++              }
++#endif
++            
++          }
+       }
++      
+       new_chain = tmp;
+     }
++
++  /* signal end of cache insert in master process */
++  if (daemon->pipe_to_parent != -1)
++    {
++      ssize_t m = -1;
++      read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0);
++    }
++      
+   new_chain = NULL;
+ }
++
++/* A marshalled cache entry arrives on fd, read, unmarshall and insert into cache of master process. */
++int cache_recv_insert(time_t now, int fd)
++{
++  ssize_t m;
++  struct all_addr addr;
++  unsigned long ttl;
++  time_t ttd;
++  unsigned short flags;
++  struct crec *crecp = NULL;
++  
++  cache_start_insert();
++  
++  while(1)
++    {
++ 
++      if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1))
++      return 0;
++      
++      if (m == -1)
++      {
++        cache_end_insert();
++        return 1;
++      }
++
++      if (!read_write(fd, (unsigned char *)daemon->namebuff, m, 1) ||
++        !read_write(fd, (unsigned char *)&ttd, sizeof(ttd), 1) ||
++        !read_write(fd, (unsigned char *)&flags, sizeof(flags), 1))
++      return 0;
++
++      daemon->namebuff[m] = 0;
++
++      ttl = difftime(ttd, now);
++      
++      if (flags & (F_IPV4 | F_IPV6))
++      {
++        if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1))
++          return 0;
++        crecp = really_insert(daemon->namebuff, &addr, now, ttl, flags);
++      }
++      else if (flags & F_CNAME)
++      {
++        struct crec *newc = really_insert(daemon->namebuff, NULL, now, ttl, flags);
++        /* This relies on the fact the the target of a CNAME immediately preceeds
++           it because of the order of extraction in extract_addresses, and
++           the order reversal on the new_chain. */
++        if (newc)
++          {
++            if (!crecp)
++              {
++                newc->addr.cname.target.cache = NULL;
++                /* anything other than zero, to avoid being mistaken for CNAME to interface-name */ 
++                newc->addr.cname.uid = 1; 
++              }
++            else
++              {
++                next_uid(crecp);
++                newc->addr.cname.target.cache = crecp;
++                newc->addr.cname.uid = crecp->uid;
++              }
++          }
++      }
++#ifdef HAVE_DNSSEC
++      else if (flags & (F_DNSKEY | F_DS))
++      {
++        unsigned short class, keylen, keyflags, keytag;
++        unsigned char algo, digest;
++        struct blockdata *keydata;
++        
++        if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1))
++          return 0;
++        /* Cache needs to known class for DNSSEC stuff */
++        addr.addr.dnssec.class = class;
++
++        crecp = really_insert(daemon->namebuff, &addr, now, ttl, flags);
++          
++        if (flags & F_DNSKEY)
++          {
++            if (!read_write(fd, (unsigned char *)&algo, sizeof(algo), 1) ||
++                !read_write(fd, (unsigned char *)&keytag, sizeof(keytag), 1) ||
++                !read_write(fd, (unsigned char *)&keyflags, sizeof(keyflags), 1) ||
++                !read_write(fd, (unsigned char *)&keylen, sizeof(keylen), 1) ||
++                !(keydata = blockdata_read(fd, keylen)))
++              return 0;
++          }
++        else if (!(flags & F_NEG))
++          {
++            if (!read_write(fd, (unsigned char *)&algo, sizeof(algo), 1) ||
++                !read_write(fd, (unsigned char *)&keytag, sizeof(keytag), 1) ||
++                !read_write(fd, (unsigned char *)&digest, sizeof(digest), 1) ||
++                !read_write(fd, (unsigned char *)&keylen, sizeof(keylen), 1) ||
++                !(keydata = blockdata_read(fd, keylen)))
++              return 0;
++          }
++
++        if (crecp)
++          {
++             if (flags & F_DNSKEY)
++               {
++                 crecp->addr.key.algo = algo;
++                 crecp->addr.key.keytag = keytag;
++                 crecp->addr.key.flags = flags;
++                 crecp->addr.key.keylen = keylen;
++                 crecp->addr.key.keydata = keydata;
++               }
++             else if (!(flags & F_NEG))
++               {
++                 crecp->addr.ds.algo = algo;
++                 crecp->addr.ds.keytag = keytag;
++                 crecp->addr.ds.digest = digest;
++                 crecp->addr.ds.keylen = keylen;
++                 crecp->addr.ds.keydata = keydata;
++               }
++          }
++      }
++#endif
++    }
++}
++      
+ int cache_find_non_terminal(char *name, time_t now)
+ {
+   struct crec *crecp;
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -930,6 +930,10 @@ int main (int argc, char **argv)
+     check_servers();
+   
+   pid = getpid();
++
++  daemon->pipe_to_parent = -1;
++  for (i = 0; i < MAX_PROCS; i++)
++    daemon->tcp_pipes[i] = -1;
+   
+ #ifdef HAVE_INOTIFY
+   /* Using inotify, have to select a resolv file at startup */
+@@ -1611,7 +1615,7 @@ static int set_dns_listeners(time_t now)
+        we don't need to explicitly arrange to wake up here */
+       if  (listener->tcpfd != -1)
+       for (i = 0; i < MAX_PROCS; i++)
+-        if (daemon->tcp_pids[i] == 0)
++        if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
+           {
+             poll_listen(listener->tcpfd, POLLIN);
+             break;
+@@ -1624,6 +1628,13 @@ static int set_dns_listeners(time_t now)
+     }
+   
++#ifndef NO_FORK
++  if (!option_bool(OPT_DEBUG))
++    for (i = 0; i < MAX_PROCS; i++)
++      if (daemon->tcp_pipes[i] != -1)
++      poll_listen(daemon->tcp_pipes[i], POLLIN);
++#endif
++  
+   return wait;
+ }
+@@ -1632,7 +1643,10 @@ static void check_dns_listeners(time_t n
+   struct serverfd *serverfdp;
+   struct listener *listener;
+   int i;
+-
++#ifndef NO_FORK
++  int pipefd[2];
++#endif
++  
+   for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
+     if (poll_check(serverfdp->fd, POLLIN))
+       reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
+@@ -1642,7 +1656,26 @@ static void check_dns_listeners(time_t n
+       if (daemon->randomsocks[i].refcount != 0 && 
+         poll_check(daemon->randomsocks[i].fd, POLLIN))
+       reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
+-  
++
++#ifndef NO_FORK
++  /* Races. The child process can die before we read all of the data from the
++     pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the 
++     process, and tcp_pipes to -1 and close the FD when we read the last
++     of the data - indicated by cache_recv_insert returning zero.
++     The order of these events is indeterminate, and both are needed
++     to free the process slot. Once the child process has gone, poll()
++     returns POLLHUP, not POLLIN, so have to check for both here. */
++  if (!option_bool(OPT_DEBUG))
++    for (i = 0; i < MAX_PROCS; i++)
++      if (daemon->tcp_pipes[i] != -1 &&
++        poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP) &&
++        !cache_recv_insert(now, daemon->tcp_pipes[i]))
++      {
++        close(daemon->tcp_pipes[i]);
++        daemon->tcp_pipes[i] = -1;    
++      }
++#endif
++      
+   for (listener = daemon->listeners; listener; listener = listener->next)
+     {
+       if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
+@@ -1736,15 +1769,20 @@ static void check_dns_listeners(time_t n
+             while (retry_send(close(confd)));
+           }
+ #ifndef NO_FORK
+-        else if (!option_bool(OPT_DEBUG) && (p = fork()) != 0)
++        else if (!option_bool(OPT_DEBUG) && pipe(pipefd) == 0 && (p = fork()) != 0)
+           {
+-            if (p != -1)
++            close(pipefd[1]); /* parent needs read pipe end. */
++            if (p == -1)
++              close(pipefd[0]);
++            else
+               {
+                 int i;
++
+                 for (i = 0; i < MAX_PROCS; i++)
+-                  if (daemon->tcp_pids[i] == 0)
++                  if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
+                     {
+                       daemon->tcp_pids[i] = p;
++                      daemon->tcp_pipes[i] = pipefd[0];
+                       break;
+                     }
+               }
+@@ -1761,7 +1799,7 @@ static void check_dns_listeners(time_t n
+             int flags;
+             struct in_addr netmask;
+             int auth_dns;
+-
++         
+             if (iface)
+               {
+                 netmask = iface->netmask;
+@@ -1777,7 +1815,11 @@ static void check_dns_listeners(time_t n
+             /* Arrange for SIGALRM after CHILD_LIFETIME seconds to
+                terminate the process. */
+             if (!option_bool(OPT_DEBUG))
+-              alarm(CHILD_LIFETIME);
++              {
++                alarm(CHILD_LIFETIME);
++                close(pipefd[0]); /* close read end in child. */
++                daemon->pipe_to_parent = pipefd[1];
++              }
+ #endif
+             /* start with no upstream connections. */
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -1091,6 +1091,8 @@ extern struct daemon {
+   size_t packet_len;       /*      "        "        */
+   struct randfd *rfd_save; /*      "        "        */
+   pid_t tcp_pids[MAX_PROCS];
++  int tcp_pipes[MAX_PROCS];
++  int pipe_to_parent;
+   struct randfd randomsocks[RANDOM_SOCKS];
+   int v6pktinfo; 
+   struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
+@@ -1152,6 +1154,7 @@ struct crec *cache_find_by_name(struct c
+                               char *name, time_t now, unsigned int prot);
+ void cache_end_insert(void);
+ void cache_start_insert(void);
++int cache_recv_insert(time_t now, int fd);
+ struct crec *cache_insert(char *name, struct all_addr *addr,
+                         time_t now, unsigned long ttl, unsigned short flags);
+ void cache_reload(void);
+@@ -1174,6 +1177,8 @@ void blockdata_init(void);
+ void blockdata_report(void);
+ struct blockdata *blockdata_alloc(char *data, size_t len);
+ void *blockdata_retrieve(struct blockdata *block, size_t len, void *data);
++struct blockdata *blockdata_read(int fd, size_t len);
++void blockdata_write(struct blockdata *block, size_t len, int fd);
+ void blockdata_free(struct blockdata *blocks);
+ #endif
diff --git a/package/network/services/dnsmasq/patches/0002-Ensure-that-AD-bit-is-reset-on-answers-from-address-.patch b/package/network/services/dnsmasq/patches/0002-Ensure-that-AD-bit-is-reset-on-answers-from-address-.patch
new file mode 100644 (file)
index 0000000..e3178b8
--- /dev/null
@@ -0,0 +1,26 @@
+From a220545c4277cba534be5ef4638b5076fc7d2cf4 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 22 Oct 2018 18:21:48 +0100
+Subject: [PATCH 02/11] Ensure that AD bit is reset on answers from
+ --address=/<domain>/<address>.
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ src/rfc1035.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -938,9 +938,9 @@ size_t setup_reply(struct dns_header *he
+     return 0;
+   
+   /* clear authoritative and truncated flags, set QR flag */
+-  header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
+-  /* set RA flag */
+-  header->hb4 |= HB4_RA;
++  header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR;
++  /* clear AD flag, set RA flag */
++  header->hb4 = (header->hb4 & ~HB4_AD) | HB4_RA;
+   header->nscount = htons(0);
+   header->arcount = htons(0);
diff --git a/package/network/services/dnsmasq/patches/0003-Remove-ability-to-compile-without-IPv6-support.patch b/package/network/services/dnsmasq/patches/0003-Remove-ability-to-compile-without-IPv6-support.patch
new file mode 100644 (file)
index 0000000..be3c411
--- /dev/null
@@ -0,0 +1,2192 @@
+From ee8750451b49d27b180517a4e35b636be0fae575 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Tue, 23 Oct 2018 22:10:17 +0100
+Subject: [PATCH 03/11] Remove ability to compile without IPv6 support.
+
+This was the source of a large number of #ifdefs, originally
+included for use with old embedded libc versions. I'm
+sure no-one wants or needs IPv6-free code these days, so this
+is a move towards more maintainable code.
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ src/arp.c       | 13 ----------
+ src/auth.c      | 53 ++++++++++----------------------------
+ src/bpf.c       | 10 ++------
+ src/cache.c     | 31 ++++------------------
+ src/config.h    | 30 +++-------------------
+ src/conntrack.c |  2 --
+ src/dbus.c      |  4 ---
+ src/dnsmasq.c   |  6 ++---
+ src/dnsmasq.h   | 20 ++-------------
+ src/domain.c    | 28 +++-----------------
+ src/dump.c      |  4 ---
+ src/edns0.c     | 14 +---------
+ src/forward.c   | 68 ++++---------------------------------------------
+ src/helper.c    |  8 ------
+ src/ipset.c     |  9 +------
+ src/netlink.c   |  5 +---
+ src/network.c   | 54 +++------------------------------------
+ src/option.c    | 34 -------------------------
+ src/rfc1035.c   | 50 +++++++-----------------------------
+ src/tables.c    |  3 +--
+ src/tftp.c      | 17 ++-----------
+ src/util.c      | 13 +---------
+ 22 files changed, 57 insertions(+), 419 deletions(-)
+
+--- a/src/arp.c
++++ b/src/arp.c
+@@ -44,11 +44,6 @@ static int filter_mac(int family, char *
+   if (maclen > DHCP_CHADDR_MAX)
+     return 1;
+-#ifndef HAVE_IPV6
+-  if (family != AF_INET)
+-    return 1;
+-#endif
+-
+   /* Look for existing entry */
+   for (arp = arps; arp; arp = arp->next)
+     {
+@@ -60,13 +55,11 @@ static int filter_mac(int family, char *
+         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)
+       {
+@@ -103,10 +96,8 @@ static int filter_mac(int family, char *
+       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;
+@@ -136,11 +127,9 @@ int find_mac(union mysockaddr *addr, uns
+             arp->addr.addr.addr4.s_addr != addr->in.sin_addr.s_addr)
+           continue;
+           
+-#ifdef HAVE_IPV6
+         if (arp->family == AF_INET6 && 
+             !IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr->in6.sin6_addr))
+           continue;
+-#endif
+         
+         /* Only accept positive entries unless in lazy mode. */
+         if (arp->status != ARP_EMPTY || lazy || updated)
+@@ -203,10 +192,8 @@ int find_mac(union mysockaddr *addr, uns
+       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;
+--- a/src/auth.c
++++ b/src/auth.c
+@@ -33,10 +33,8 @@ static struct addrlist *find_addrlist(st
+       if  (is_same_net(addr, list->addr.addr.addr4, netmask))
+         return list;
+       }
+-#ifdef HAVE_IPV6
+     else if (is_same_net6(&(addr_u->addr.addr6), &list->addr.addr.addr6, list->prefixlen))
+       return list;
+-#endif
+     
+   } while ((list = list->next));
+   
+@@ -189,7 +187,6 @@ size_t answer_auth(struct dns_header *he
+                 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
+                   intr = intr->next;
+             }
+-#ifdef HAVE_IPV6
+         else if (flag == F_IPV6)
+           for (intr = daemon->int_names; intr; intr = intr->next)
+             {
+@@ -205,7 +202,6 @@ size_t answer_auth(struct dns_header *he
+                 while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
+                   intr = intr->next;
+             }
+-#endif
+         
+         if (intr)
+           {
+@@ -378,10 +374,8 @@ size_t answer_auth(struct dns_header *he
+        if (qtype == T_A)
+        flag = F_IPV4;
+        
+-#ifdef HAVE_IPV6
+        if (qtype == T_AAAA)
+        flag = F_IPV6;
+-#endif
+        
+        for (intr = daemon->int_names; intr; intr = intr->next)
+        if ((rc = hostname_issubdomain(name, intr->name)))
+@@ -395,10 +389,9 @@ size_t answer_auth(struct dns_header *he
+                if (((addrlist->flags & ADDRLIST_IPV6)  ? T_AAAA : T_A) == qtype &&
+                    (local_query || filter_zone(zone, flag, &addrlist->addr)))
+                  {
+-#ifdef HAVE_IPV6
+                    if (addrlist->flags & ADDRLIST_REVONLY)
+                      continue;
+-#endif
++
+                    found = 1;
+                    log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
+                    if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+@@ -424,13 +417,11 @@ size_t answer_auth(struct dns_header *he
+             
+             if (peer_addr->sa.sa_family == AF_INET)
+               peer_addr->in.sin_port = 0;
+-#ifdef HAVE_IPV6
+             else
+               {
+                 peer_addr->in6.sin6_port = 0; 
+                 peer_addr->in6.sin6_scope_id = 0;
+               }
+-#endif
+             
+             for (peers = daemon->auth_peers; peers; peers = peers->next)
+               if (sockaddr_isequal(peer_addr, &peers->addr))
+@@ -442,10 +433,8 @@ size_t answer_auth(struct dns_header *he
+               {
+                 if (peer_addr->sa.sa_family == AF_INET)
+                   inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
+-#ifdef HAVE_IPV6
+                 else
+                   inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN); 
+-#endif
+                 
+                 my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
+                 return 0;
+@@ -603,7 +592,6 @@ size_t answer_auth(struct dns_header *he
+             p += sprintf(p, "%u.in-addr.arpa", a & 0xff);
+             
+           }
+-#ifdef HAVE_IPV6
+         else
+           {
+             char *p = name;
+@@ -617,7 +605,6 @@ size_t answer_auth(struct dns_header *he
+             p += sprintf(p, "ip6.arpa");
+             
+           }
+-#endif
+       }
+       
+       /* handle NS and SOA in auth section or for explicit queries */
+@@ -754,14 +741,12 @@ size_t answer_auth(struct dns_header *he
+                                         daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addrlist->addr))
+                   anscount++;
+               
+-#ifdef HAVE_IPV6
+               for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) 
+                 if ((addrlist->flags & ADDRLIST_IPV6) && 
+                     (local_query || filter_zone(zone, F_IPV6, &addrlist->addr)) &&
+                     add_resource_record(header, limit, &trunc, -axfroffset, &ansp, 
+                                         daemon->auth_ttl, NULL, T_AAAA, C_IN, "6", cut ? intr->name : NULL, &addrlist->addr))
+                   anscount++;
+-#endif                    
+               
+               /* restore config data */
+               if (cut)
+@@ -798,18 +783,11 @@ size_t answer_auth(struct dns_header *he
+                   {
+                     char *cache_name = cache_get_name(crecp);
+                     if (!strchr(cache_name, '.') && 
+-                        (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
+-                      {
+-                        qtype = T_A;
+-#ifdef HAVE_IPV6
+-                        if (crecp->flags & F_IPV6)
+-                          qtype = T_AAAA;
+-#endif
+-                        if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, 
+-                                                daemon->auth_ttl, NULL, qtype, C_IN, 
+-                                                (crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
+-                          anscount++;
+-                      }
++                        (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))) &&
++                        add_resource_record(header, limit, &trunc, -axfroffset, &ansp, 
++                                            daemon->auth_ttl, NULL, (crecp->flags & F_IPV6) ? T_AAAA : T_A, C_IN, 
++                                            (crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
++                      anscount++;
+                   }
+                 
+                 if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
+@@ -818,18 +796,13 @@ size_t answer_auth(struct dns_header *he
+                     if (in_zone(zone, name, &cut) && 
+                         (local_query || filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr))))
+                       {
+-                        qtype = T_A;
+-#ifdef HAVE_IPV6
+-                        if (crecp->flags & F_IPV6)
+-                          qtype = T_AAAA;
+-#endif
+-                         if (cut)
+-                           *cut = 0;
+-
+-                         if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, 
+-                                                 daemon->auth_ttl, NULL, qtype, C_IN, 
+-                                                 (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
+-                           anscount++;
++                        if (cut)
++                          *cut = 0;
++
++                        if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, 
++                                                daemon->auth_ttl, NULL, (crecp->flags & F_IPV6) ? T_AAAA : T_A, C_IN, 
++                                                (crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
++                          anscount++;
+                       }
+                   }
+               }
+--- a/src/bpf.c
++++ b/src/bpf.c
+@@ -31,9 +31,7 @@
+ #  include <net/if_var.h> 
+ #endif
+ #include <netinet/in_var.h>
+-#ifdef HAVE_IPV6
+-#  include <netinet6/in6_var.h>
+-#endif
++#include <netinet6/in6_var.h>
+ #ifndef SA_SIZE
+ #define SA_SIZE(sa)                                             \
+@@ -121,7 +119,7 @@ int iface_enumerate(int family, void *pa
+   if (getifaddrs(&head) == -1)
+     return 0;
+-#if defined(HAVE_BSD_NETWORK) && defined(HAVE_IPV6)
++#if defined(HAVE_BSD_NETWORK)
+   if (family == AF_INET6)
+     fd = socket(PF_INET6, SOCK_DGRAM, 0);
+ #endif
+@@ -152,7 +150,6 @@ int iface_enumerate(int family, void *pa
+             if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm)))
+               goto err;
+           }
+-#ifdef HAVE_IPV6
+         else if (family == AF_INET6)
+           {
+             struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
+@@ -219,7 +216,6 @@ int iface_enumerate(int family, void *pa
+                               (int) preferred, (int)valid, parm)))
+               goto err;             
+           }
+-#endif /* HAVE_IPV6 */
+ #ifdef HAVE_DHCP6      
+         else if (family == AF_LINK)
+@@ -427,10 +423,8 @@ void route_sock(void)
+                del_family = sa->sa_family;
+                if (del_family == AF_INET)
+                  del_addr.addr.addr4 = ((struct sockaddr_in *)sa)->sin_addr;
+-#ifdef HAVE_IPV6
+                else if (del_family == AF_INET6)
+                  del_addr.addr.addr6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
+-#endif
+                else
+                  del_family = 0;
+              }
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -410,11 +410,8 @@ static struct crec *cache_scan_free(char
+   else
+     {
+       int i;
+-#ifdef HAVE_IPV6
+       int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
+-#else
+-      int addrlen = INADDRSZ;
+-#endif 
++
+       for (i = 0; i < hash_size; i++)
+       for (crecp = hash_table[i], up = &hash_table[i]; 
+            crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL));
+@@ -512,11 +509,9 @@ static struct crec *really_insert(char *
+         if ((flags & F_IPV4) && (new->flags & F_IPV4) &&
+             new->addr.addr.addr.addr4.s_addr == addr->addr.addr4.s_addr)
+           return new;
+-#ifdef HAVE_IPV6
+         else if ((flags & F_IPV6) && (new->flags & F_IPV6) &&
+                  IN6_ARE_ADDR_EQUAL(&new->addr.addr.addr.addr6, &addr->addr.addr6))
+           return new;
+-#endif
+       }
+       
+       insert_error = 1;
+@@ -938,11 +933,7 @@ struct crec *cache_find_by_addr(struct c
+                               time_t now, unsigned int prot)
+ {
+   struct crec *ans;
+-#ifdef HAVE_IPV6
+   int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ;
+-#else
+-  int addrlen = INADDRSZ;
+-#endif
+   
+   if (crecp) /* iterating */
+     ans = crecp->next;
+@@ -1171,14 +1162,12 @@ int read_hostsfile(char *filename, unsig
+         addrlen = INADDRSZ;
+         domain_suffix = get_domain(addr.addr.addr4);
+       }
+-#ifdef HAVE_IPV6
+       else if (inet_pton(AF_INET6, token, &addr) > 0)
+       {
+         flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
+         addrlen = IN6ADDRSZ;
+         domain_suffix = get_domain6(&addr.addr.addr6);
+       }
+-#endif
+       else
+       {
+         my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno); 
+@@ -1343,7 +1332,7 @@ void cache_reload(void)
+           cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
+           add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
+         }
+-#ifdef HAVE_IPV6
++
+       if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) &&
+           (cache = whine_malloc(SIZEOF_POINTER_CREC)))
+         {
+@@ -1352,7 +1341,6 @@ void cache_reload(void)
+           cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
+           add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
+         }
+-#endif
+       }
+       
+   if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
+@@ -1451,13 +1439,11 @@ void cache_add_dhcp_entry(char *host_nam
+   int in_hosts = 0;
+   size_t addrlen = sizeof(struct in_addr);
+-#ifdef HAVE_IPV6
+   if (prot == AF_INET6)
+     {
+       flags = F_IPV6;
+       addrlen = sizeof(struct in6_addr);
+     }
+-#endif
+   
+   inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
+   
+@@ -1810,10 +1796,8 @@ void dump_cache(time_t now)
+               a = daemon->addrbuff;
+               if (cache->flags & F_IPV4)
+                 inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
+-#ifdef HAVE_IPV6
+               else if (cache->flags & F_IPV6)
+                 inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
+-#endif
+             }
+           if (cache->flags & F_IPV4)
+@@ -1954,14 +1938,9 @@ void log_query(unsigned int flags, char
+            sprintf(daemon->addrbuff, "%u", rcode);
+       }
+       else
+-      {
+-#ifdef HAVE_IPV6
+-        inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
+-                  addr, daemon->addrbuff, ADDRSTRLEN);
+-#else
+-        strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);  
+-#endif
+-      }
++      inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
++                addr, daemon->addrbuff, ADDRSTRLEN);
++      
+     }
+   else
+     dest = arg;
+--- a/src/config.h
++++ b/src/config.h
+@@ -131,7 +131,6 @@ HAVE_INOTIFY
+ NO_ID
+    Don't report *.bind CHAOS info to clients, forward such requests upstream instead.
+-NO_IPV6
+ NO_TFTP
+ NO_DHCP
+ NO_DHCP6
+@@ -141,8 +140,8 @@ NO_AUTH
+ NO_DUMPFILE
+ NO_INOTIFY
+    these are available to explicitly disable compile time options which would 
+-   otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or 
+-   which are enabled  by default in the distributed source tree. Building dnsmasq
++   otherwise be enabled automatically or which are enabled  by default 
++   in the distributed source tree. Building dnsmasq
+    with something like "make COPTS=-DNO_SCRIPT" will do the trick.
+ NO_GMP
+    Don't use and link against libgmp, Useful if nettle is built with --enable-mini-gmp.
+@@ -308,29 +307,9 @@ HAVE_SOCKADDR_SA_LEN
+  
+ #endif
+-/* Decide if we're going to support IPv6 */
+-/* We assume that systems which don't have IPv6
+-   headers don't have ntop and pton either */
+-
+-#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY)
+-#  define HAVE_IPV6
+-#  define ADDRSTRLEN INET6_ADDRSTRLEN
+-#else
+-#  if !defined(INET_ADDRSTRLEN)
+-#      define INET_ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
+-#  endif
+-#  undef HAVE_IPV6
+-#  define ADDRSTRLEN INET_ADDRSTRLEN
+-#endif
+-
+-
+ /* rules to implement compile-time option dependencies and 
+    the NO_XXX flags */
+-#ifdef NO_IPV6
+-#undef HAVE_IPV6
+-#endif
+-
+ #ifdef NO_TFTP
+ #undef HAVE_TFTP
+ #endif
+@@ -340,7 +319,7 @@ HAVE_SOCKADDR_SA_LEN
+ #undef HAVE_DHCP6
+ #endif
+-#if defined(NO_DHCP6) || !defined(HAVE_IPV6)
++#if defined(NO_DHCP6)
+ #undef HAVE_DHCP6
+ #endif
+@@ -385,9 +364,6 @@ HAVE_SOCKADDR_SA_LEN
+ #ifdef DNSMASQ_COMPILE_OPTS
+ static char *compile_opts = 
+-#ifndef HAVE_IPV6
+-"no-"
+-#endif
+ "IPv6 "
+ #ifndef HAVE_GETOPT_LONG
+ "no-"
+--- a/src/conntrack.c
++++ b/src/conntrack.c
+@@ -36,7 +36,6 @@ int get_incoming_mark(union mysockaddr *
+       nfct_set_attr_u8(ct, ATTR_L4PROTO, istcp ? IPPROTO_TCP : IPPROTO_UDP);
+       nfct_set_attr_u16(ct, ATTR_PORT_DST, htons(daemon->port));
+       
+-#ifdef HAVE_IPV6
+       if (peer_addr->sa.sa_family == AF_INET6)
+       {
+         nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET6);
+@@ -45,7 +44,6 @@ int get_incoming_mark(union mysockaddr *
+         nfct_set_attr(ct, ATTR_IPV6_DST, local_addr->addr.addr6.s6_addr);
+       }
+       else
+-#endif
+       {
+         nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
+         nfct_set_attr_u32(ct, ATTR_IPV4_SRC, peer_addr->in.sin_addr.s_addr);
+--- a/src/dbus.c
++++ b/src/dbus.c
+@@ -185,9 +185,6 @@ static void dbus_read_servers(DBusMessag
+               }
+           }
+-#ifndef HAVE_IPV6
+-        my_syslog(LOG_WARNING, _("attempt to set an IPv6 server address via DBus - no IPv6 support"));
+-#else
+         if (i == sizeof(struct in6_addr))
+           {
+             memcpy(&addr.in6.sin6_addr, p, sizeof(struct in6_addr));
+@@ -202,7 +199,6 @@ static void dbus_read_servers(DBusMessag
+               source_addr.in6.sin6_port = htons(daemon->query_port);
+             skip = 0;
+           }
+-#endif
+       }
+       else
+       /* At the end */
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -1730,11 +1730,11 @@ static void check_dns_listeners(time_t n
+                 indextoname(listener->tcpfd, if_index, intr_name))
+               {
+                 struct all_addr addr;
+-                addr.addr.addr4 = tcp_addr.in.sin_addr;
+-#ifdef HAVE_IPV6
++                
+                 if (tcp_addr.sa.sa_family == AF_INET6)
+                   addr.addr.addr6 = tcp_addr.in6.sin6_addr;
+-#endif
++                else
++                  addr.addr.addr4 = tcp_addr.in.sin_addr;
+                 
+                 for (iface = daemon->interfaces; iface; iface = iface->next)
+                   if (iface->index == if_index)
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -126,9 +126,7 @@ typedef unsigned long long u64;
+ #include <net/if_arp.h>
+ #include <netinet/in_systm.h>
+ #include <netinet/ip.h>
+-#ifdef HAVE_IPV6
+ #include <netinet/ip6.h>
+-#endif
+ #include <netinet/ip_icmp.h>
+ #include <sys/uio.h>
+ #include <syslog.h>
+@@ -159,6 +157,8 @@ extern int capget(cap_user_header_t head
+ /* daemon is function in the C library.... */
+ #define daemon dnsmasq_daemon
++#define ADDRSTRLEN INET6_ADDRSTRLEN
++
+ /* Async event queue */
+ struct event_desc {
+   int event, data, msg_sz;
+@@ -273,9 +273,7 @@ struct event_desc {
+ struct all_addr {
+   union {
+     struct in_addr addr4;
+-#ifdef HAVE_IPV6
+     struct in6_addr addr6;
+-#endif
+     /* for log_query */
+     struct {
+       unsigned short keytag, algo, digest;
+@@ -383,9 +381,7 @@ struct host_record {
+     struct name_list *next;
+   } *names;
+   struct in_addr addr;
+-#ifdef HAVE_IPV6
+   struct in6_addr addr6;
+-#endif
+   struct host_record *next;
+ };
+@@ -496,9 +492,7 @@ struct crec {
+ union mysockaddr {
+   struct sockaddr sa;
+   struct sockaddr_in in;
+-#if defined(HAVE_IPV6)
+   struct sockaddr_in6 in6;
+-#endif
+ };
+ /* bits in flag param to IPv6 callbacks from iface_enumerate() */
+@@ -660,9 +654,7 @@ struct frec {
+   struct all_addr dest;
+   struct server *sentto; /* NULL means free */
+   struct randfd *rfd4;
+-#ifdef HAVE_IPV6
+   struct randfd *rfd6;
+-#endif
+   unsigned int iface;
+   unsigned short orig_id, new_id;
+   int log_id, fd, forwardall, flags;
+@@ -877,9 +869,7 @@ struct dhcp_bridge {
+ struct cond_domain {
+   char *domain, *prefix;
+   struct in_addr start, end;
+-#ifdef HAVE_IPV6
+   struct in6_addr start6, end6;
+-#endif
+   int is6, indexed;
+   struct cond_domain *next;
+ }; 
+@@ -1184,9 +1174,7 @@ void blockdata_free(struct blockdata *bl
+ /* domain.c */
+ char *get_domain(struct in_addr addr);
+-#ifdef HAVE_IPV6
+ char *get_domain6(struct in6_addr *addr);
+-#endif
+ int is_name_synthetic(int flags, char *name, struct all_addr *addr);
+ int is_rev_synth(int flag, struct all_addr *addr, char *name);
+@@ -1270,11 +1258,9 @@ int hostname_issubdomain(char *a, char *
+ time_t dnsmasq_time(void);
+ int netmask_length(struct in_addr mask);
+ int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
+-#ifdef HAVE_IPV6
+ int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
+ u64 addr6part(struct in6_addr *addr);
+ void setaddr6part(struct in6_addr *addr, u64 host);
+-#endif
+ int retry_send(ssize_t rc);
+ void prettyprint_time(char *buf, unsigned int t);
+ int prettyprint_addr(union mysockaddr *addr, char *buf);
+@@ -1353,9 +1339,7 @@ int loopback_exception(int fd, int famil
+ int label_exception(int index, int family, struct all_addr *addr);
+ int fix_fd(int fd);
+ int tcp_interface(int fd, int af);
+-#ifdef HAVE_IPV6
+ int set_ipv6pktinfo(int fd);
+-#endif
+ #ifdef HAVE_DHCP6
+ void join_multicast(int dienow);
+ #endif
+--- a/src/domain.c
++++ b/src/domain.c
+@@ -18,21 +18,14 @@
+ static struct cond_domain *search_domain(struct in_addr addr, struct cond_domain *c);
+-#ifdef HAVE_IPV6
+ static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c);
+-#endif
+ int is_name_synthetic(int flags, char *name, struct all_addr *addr)
+ {
+   char *p;
+   struct cond_domain *c = NULL;
+-  int prot = AF_INET;
+-
+-#ifdef HAVE_IPV6
+-  if (flags & F_IPV6)
+-    prot = AF_INET6;
+-#endif
++  int prot = (flags & F_IPV6) ? AF_INET6 : AF_INET;
+   for (c = daemon->synth_domains; c; c = c->next)
+     {
+@@ -83,8 +76,7 @@ int is_name_synthetic(int flags, char *n
+                     addr->addr.addr4.s_addr = htonl(ntohl(c->start.s_addr) + index);
+                     found = 1;
+                   }
+-              }
+-#ifdef HAVE_IPV6 
++              } 
+             else
+               {
+                 u64 index = atoll(tail);
+@@ -98,7 +90,6 @@ int is_name_synthetic(int flags, char *n
+                     found = 1;
+                   }
+               }
+-#endif
+           }
+       }
+       else
+@@ -111,10 +102,8 @@ int is_name_synthetic(int flags, char *n
+             if ((c >='0' && c <= '9') || c == '-')
+               continue;
+             
+-#ifdef HAVE_IPV6
+             if (prot == AF_INET6 && ((c >='A' && c <= 'F') || (c >='a' && c <= 'f'))) 
+               continue;
+-#endif
+             
+             break;
+           }
+@@ -124,7 +113,6 @@ int is_name_synthetic(int flags, char *n
+         
+         *p = 0;       
+         
+-#ifdef HAVE_IPV6
+         if (prot == AF_INET6 && strstr(tail, "--ffff-") == tail)
+           {
+             /* special hack for v4-mapped. */
+@@ -134,7 +122,6 @@ int is_name_synthetic(int flags, char *n
+                 *p = '.';
+           }
+         else
+-#endif
+           {
+             /* swap . or : for - */
+             for (p = tail; *p; p++)
+@@ -142,10 +129,8 @@ int is_name_synthetic(int flags, char *n
+                 {
+                   if (prot == AF_INET)
+                     *p = '.';
+-#ifdef HAVE_IPV6
+                   else
+                     *p = ':';
+-#endif
+                 }
+           }
+         
+@@ -158,7 +143,6 @@ int is_name_synthetic(int flags, char *n
+                     ntohl(addr->addr.addr4.s_addr) <= ntohl(c->end.s_addr))
+                   found = 1;
+               }
+-#ifdef HAVE_IPV6
+             else
+               {
+                 u64 addrpart = addr6part(&addr->addr.addr6);
+@@ -169,7 +153,6 @@ int is_name_synthetic(int flags, char *n
+                     addrpart <= addr6part(&c->end6))
+                   found = 1;
+               }
+-#endif
+           }
+       }
+@@ -221,8 +204,7 @@ int is_rev_synth(int flag, struct all_ad
+        return 1;
+      }
+-#ifdef HAVE_IPV6
+-   if (flag & F_IPV6 && (c = search_domain6(&addr->addr.addr6, daemon->synth_domains))) 
++   if ((flag & F_IPV6) && (c = search_domain6(&addr->addr.addr6, daemon->synth_domains))) 
+      {
+        char *p;
+        
+@@ -259,7 +241,6 @@ int is_rev_synth(int flag, struct all_ad
+        
+        return 1;
+      }
+-#endif
+    
+    return 0;
+ }
+@@ -286,7 +267,7 @@ char *get_domain(struct in_addr addr)
+   return daemon->domain_suffix;
+ } 
+-#ifdef HAVE_IPV6
++
+ static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c)
+ {
+   u64 addrpart = addr6part(addr);
+@@ -310,4 +291,3 @@ char *get_domain6(struct in6_addr *addr)
+   return daemon->domain_suffix;
+ } 
+-#endif
+--- a/src/dump.c
++++ b/src/dump.c
+@@ -82,10 +82,8 @@ void dump_init(void)
+ void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst)
+ {
+   struct ip ip;
+-#ifdef HAVE_IPV6
+   struct ip6_hdr ip6;
+   int family;
+-#endif
+   struct udphdr {
+     u16 uh_sport;               /* source port */
+     u16 uh_dport;               /* destination port */
+@@ -105,7 +103,6 @@ void dump_packet(int mask, void *packet,
+   /* So wireshark can Id the packet. */
+   udp.uh_sport = udp.uh_dport = htons(NAMESERVER_PORT);
+-#ifdef HAVE_IPV6
+   if (src)
+     family = src->sa.sa_family;
+   else
+@@ -139,7 +136,6 @@ void dump_packet(int mask, void *packet,
+       sum += ((u16 *)&ip6.ip6_src)[i];
+     }
+   else
+-#endif
+     {
+       iphdr = &ip;
+       ipsz = sizeof(ip);
+--- a/src/edns0.c
++++ b/src/edns0.c
+@@ -301,20 +301,14 @@ static size_t add_mac(struct dns_header
+ struct subnet_opt {
+   u16 family;
+-  u8 source_netmask, scope_netmask;
+-#ifdef HAVE_IPV6 
++  u8 source_netmask, scope_netmask; 
+   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;
+ }
+@@ -330,7 +324,6 @@ static size_t calc_subnet_opt(struct sub
+   opt->source_netmask = 0;
+   opt->scope_netmask = 0;
+-#ifdef HAVE_IPV6
+   if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6)
+     {
+       opt->source_netmask = daemon->add_subnet6->mask;
+@@ -342,7 +335,6 @@ static size_t calc_subnet_opt(struct sub
+       else 
+       addrp = &source->in6.sin6_addr;
+     }
+-#endif
+   if (source->sa.sa_family == AF_INET && daemon->add_subnet4)
+     {
+@@ -356,11 +348,7 @@ static size_t calc_subnet_opt(struct sub
+         addrp = &source->in.sin_addr;
+     }
+   
+-#ifdef HAVE_IPV6
+   opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
+-#else
+-  opt->family = htons(1);
+-#endif
+   
+   len = 0;
+   
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -38,9 +38,7 @@ int send_from(int fd, int nowild, char *
+ #elif defined(IP_SENDSRCADDR)
+     char control[CMSG_SPACE(sizeof(struct in_addr))];
+ #endif
+-#ifdef HAVE_IPV6
+     char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+-#endif
+   } control_u;
+   
+   iov[0].iov_base = packet;
+@@ -79,7 +77,6 @@ int send_from(int fd, int nowild, char *
+ #endif
+       }
+       else
+-#ifdef HAVE_IPV6
+       {
+         struct in6_pktinfo p;
+         p.ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
+@@ -89,9 +86,6 @@ int send_from(int fd, int nowild, char *
+         cmptr->cmsg_type = daemon->v6pktinfo;
+         cmptr->cmsg_level = IPPROTO_IPV6;
+       }
+-#else
+-      (void)iface; /* eliminate warning */
+-#endif
+     }
+   
+   while (retry_send(sendmsg(fd, &msg, 0)));
+@@ -144,10 +138,8 @@ static unsigned int search_servers(time_
+               flags = sflag;
+               if (serv->addr.sa.sa_family == AF_INET) 
+                 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
+-#ifdef HAVE_IPV6
+               else
+                 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
+-#endif 
+             }
+           else if (!flags || (flags & F_NXDOMAIN))
+             flags = F_NOERR;
+@@ -204,10 +196,8 @@ static unsigned int search_servers(time_
+                           flags = sflag;
+                           if (serv->addr.sa.sa_family == AF_INET) 
+                             *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
+-#ifdef HAVE_IPV6
+                           else
+                             *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
+-#endif
+                         }
+                       else if (!flags || (flags & F_NXDOMAIN))
+                         flags = F_NOERR;
+@@ -236,10 +226,8 @@ static unsigned int search_servers(time_
+          /* handle F_IPV4 and F_IPV6 set on ANY query to 0.0.0.0/:: domain. */
+          if (flags & F_IPV4)
+            log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, qdomain, *addrpp, NULL);
+-#ifdef HAVE_IPV6
+          if (flags & F_IPV6)
+            log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, qdomain, *addrpp, NULL);
+-#endif
+        }
+     }
+   else if ((*type) & SERV_USE_RESOLV)
+@@ -302,20 +290,17 @@ static int forward_query(int udpfd, unio
+         
+         if (forward->sentto->addr.sa.sa_family == AF_INET) 
+           log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
+-#ifdef HAVE_IPV6
+         else
+           log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
+-#endif
++
+   
+         if (forward->sentto->sfd)
+           fd = forward->sentto->sfd->fd;
+         else
+           {
+-#ifdef HAVE_IPV6
+             if (forward->sentto->addr.sa.sa_family == AF_INET6)
+               fd = forward->rfd6->fd;
+             else
+-#endif
+               fd = forward->rfd4->fd;
+           }
+         
+@@ -475,7 +460,6 @@ static int forward_query(int udpfd, unio
+               fd = start->sfd->fd;
+             else 
+               {
+-#ifdef HAVE_IPV6
+                 if (start->addr.sa.sa_family == AF_INET6)
+                   {
+                     if (!forward->rfd6 &&
+@@ -485,7 +469,6 @@ static int forward_query(int udpfd, unio
+                     fd = forward->rfd6->fd;
+                   }
+                 else
+-#endif
+                   {
+                     if (!forward->rfd4 &&
+                         !(forward->rfd4 = allocate_rfd(AF_INET)))
+@@ -541,11 +524,9 @@ static int forward_query(int udpfd, unio
+                 if (start->addr.sa.sa_family == AF_INET)
+                   log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
+                             (struct all_addr *)&start->addr.in.sin_addr, NULL); 
+-#ifdef HAVE_IPV6
+                 else
+                   log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
+                             (struct all_addr *)&start->addr.in6.sin6_addr, NULL);
+-#endif 
+                 start->queries++;
+                 forwarded = 1;
+                 forward->sentto = start;
+@@ -788,11 +769,8 @@ void reply_query(int fd, int family, tim
+   daemon->srv_save = NULL;
+   
+   /* Determine the address of the server replying  so that we can mark that as good */
+-  serveraddr.sa.sa_family = family;
+-#ifdef HAVE_IPV6
+-  if (serveraddr.sa.sa_family == AF_INET6)
++  if ((serveraddr.sa.sa_family = family) == AF_INET6)
+     serveraddr.in6.sin6_flowinfo = 0;
+-#endif
+   
+   header = (struct dns_header *)daemon->packet;
+@@ -878,7 +856,6 @@ void reply_query(int fd, int family, tim
+           fd = start->sfd->fd;
+         else
+           {
+-#ifdef HAVE_IPV6
+             if (start->addr.sa.sa_family == AF_INET6)
+               {
+                 /* may have changed family */
+@@ -887,7 +864,6 @@ void reply_query(int fd, int family, tim
+                 fd = forward->rfd6->fd;
+               }
+             else
+-#endif
+               {
+                 /* may have changed family */
+                 if (!forward->rfd4)
+@@ -902,10 +878,8 @@ void reply_query(int fd, int family, tim
+         
+         if (start->addr.sa.sa_family == AF_INET) 
+           log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&start->addr.in.sin_addr, "dnssec");
+-#ifdef HAVE_IPV6
+         else
+           log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&start->addr.in6.sin6_addr, "dnssec");
+-#endif
+         
+         return;
+       }
+@@ -1099,9 +1073,7 @@ void reply_query(int fd, int family, tim
+                     
+                     new->sentto = server;
+                     new->rfd4 = NULL;
+-#ifdef HAVE_IPV6
+                     new->rfd6 = NULL;
+-#endif
+                     new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA);
+                     new->forwardall = 0;
+                     
+@@ -1125,11 +1097,9 @@ void reply_query(int fd, int family, tim
+                     if (server->addr.sa.sa_family == AF_INET) 
+                       log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, daemon->keyname, (struct all_addr *)&(server->addr.in.sin_addr),
+                                 querystr("dnssec-query", querytype));
+-#ifdef HAVE_IPV6
+                     else
+                       log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, daemon->keyname, (struct all_addr *)&(server->addr.in6.sin6_addr),
+                                 querystr("dnssec-query", querytype));
+-#endif
+   
+                     if ((hash = hash_questions(header, nn, daemon->namebuff)))
+                       memcpy(new->hash, hash, HASH_SIZE);
+@@ -1147,14 +1117,12 @@ void reply_query(int fd, int family, tim
+                     else
+                       {
+                         fd = -1;
+-#ifdef HAVE_IPV6
+                         if (server->addr.sa.sa_family == AF_INET6)
+                           {
+                             if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
+                               fd = new->rfd6->fd;
+                           }
+                         else
+-#endif
+                           {
+                             if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
+                               fd = new->rfd4->fd;
+@@ -1290,9 +1258,7 @@ void receive_query(struct listener *list
+   struct cmsghdr *cmptr;
+   union {
+     struct cmsghdr align; /* this ensures alignment */
+-#ifdef HAVE_IPV6
+     char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+-#endif
+ #if defined(HAVE_LINUX_NETWORK)
+     char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
+ #elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK)
+@@ -1303,12 +1269,8 @@ void receive_query(struct listener *list
+                CMSG_SPACE(sizeof(struct sockaddr_dl))];
+ #endif
+   } control_u;
+-#ifdef HAVE_IPV6
+    /* Can always get recvd interface for IPv6 */
+   int check_dst = !option_bool(OPT_NOWILD) || listen->family == AF_INET6;
+-#else
+-  int check_dst = !option_bool(OPT_NOWILD);
+-#endif
+   /* packet buffer overwritten */
+   daemon->srv_save = NULL;
+@@ -1359,7 +1321,6 @@ void receive_query(struct listener *list
+       if (source_addr.in.sin_port == 0)
+       return;
+     }
+-#ifdef HAVE_IPV6
+   else
+     {
+       /* Source-port == 0 is an error, we can't send back to that. */
+@@ -1367,13 +1328,12 @@ void receive_query(struct listener *list
+       return;
+       source_addr.in6.sin6_flowinfo = 0;
+     }
+-#endif
+   
+   /* We can be configured to only accept queries from at-most-one-hop-away addresses. */
+   if (option_bool(OPT_LOCAL_SERVICE))
+     {
+       struct addrlist *addr;
+-#ifdef HAVE_IPV6
++
+       if (listen->family == AF_INET6) 
+       {
+         for (addr = daemon->interface_addrs; addr; addr = addr->next)
+@@ -1382,7 +1342,6 @@ void receive_query(struct listener *list
+             break;
+       }
+       else
+-#endif
+       {
+         struct in_addr netmask;
+         for (addr = daemon->interface_addrs; addr; addr = addr->next)
+@@ -1451,7 +1410,6 @@ void receive_query(struct listener *list
+       }
+ #endif
+       
+-#ifdef HAVE_IPV6
+       if (listen->family == AF_INET6)
+       {
+         for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+@@ -1467,7 +1425,6 @@ void receive_query(struct listener *list
+               if_index = p.p->ipi6_ifindex;
+             }
+       }
+-#endif
+       
+       /* enforce available interface configuration */
+       
+@@ -1531,11 +1488,9 @@ void receive_query(struct listener *list
+       if (listen->family == AF_INET) 
+       log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
+                 (struct all_addr *)&source_addr.in.sin_addr, types);
+-#ifdef HAVE_IPV6
+       else
+       log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
+                 (struct all_addr *)&source_addr.in6.sin6_addr, types);
+-#endif
+ #ifdef HAVE_AUTH
+       /* find queries for zones we're authoritative for, and answer them directly */
+@@ -1744,11 +1699,9 @@ static int tcp_key_recurse(time_t now, i
+         if (server->addr.sa.sa_family == AF_INET) 
+           log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, keyname, (struct all_addr *)&(server->addr.in.sin_addr),
+                     querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
+-#ifdef HAVE_IPV6
+         else
+           log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, keyname, (struct all_addr *)&(server->addr.in6.sin6_addr),
+                     querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
+-#endif
+         
+         server->flags |= SERV_GOT_TCP;
+         
+@@ -1813,11 +1766,10 @@ unsigned char *tcp_request(int confd, ti
+   if (option_bool(OPT_CONNTRACK))
+     {
+       struct all_addr local;
+-#ifdef HAVE_IPV6                    
++                    
+       if (local_addr->sa.sa_family == AF_INET6)
+       local.addr.addr6 = local_addr->in6.sin6_addr;
+       else
+-#endif
+       local.addr.addr4 = local_addr->in.sin_addr;
+       
+       have_mark = get_incoming_mark(&peer_addr, &local, 1, &mark);
+@@ -1828,7 +1780,7 @@ unsigned char *tcp_request(int confd, ti
+   if (option_bool(OPT_LOCAL_SERVICE))
+     {
+       struct addrlist *addr;
+-#ifdef HAVE_IPV6
++
+       if (peer_addr.sa.sa_family == AF_INET6) 
+       {
+         for (addr = daemon->interface_addrs; addr; addr = addr->next)
+@@ -1837,7 +1789,6 @@ unsigned char *tcp_request(int confd, ti
+             break;
+       }
+       else
+-#endif
+       {
+         struct in_addr netmask;
+         for (addr = daemon->interface_addrs; addr; addr = addr->next)
+@@ -1892,11 +1843,9 @@ unsigned char *tcp_request(int confd, ti
+         if (peer_addr.sa.sa_family == AF_INET) 
+           log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
+                     (struct all_addr *)&peer_addr.in.sin_addr, types);
+-#ifdef HAVE_IPV6
+         else
+           log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
+                     (struct all_addr *)&peer_addr.in6.sin6_addr, types);
+-#endif
+         
+ #ifdef HAVE_AUTH
+         /* find queries for zones we're authoritative for, and answer them directly */
+@@ -2072,11 +2021,9 @@ unsigned char *tcp_request(int confd, ti
+                     if (last_server->addr.sa.sa_family == AF_INET)
+                       log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
+                                 (struct all_addr *)&last_server->addr.in.sin_addr, NULL); 
+-#ifdef HAVE_IPV6
+                     else
+                       log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
+                                 (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
+-#endif 
+ #ifdef HAVE_DNSSEC
+                     if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC))
+@@ -2175,9 +2122,7 @@ static struct frec *allocate_frec(time_t
+       f->sentto = NULL;
+       f->rfd4 = NULL;
+       f->flags = 0;
+-#ifdef HAVE_IPV6
+       f->rfd6 = NULL;
+-#endif
+ #ifdef HAVE_DNSSEC
+       f->dependent = NULL;
+       f->blocking_query = NULL;
+@@ -2237,11 +2182,8 @@ static void free_frec(struct frec *f)
+   f->rfd4 = NULL;
+   f->sentto = NULL;
+   f->flags = 0;
+-  
+-#ifdef HAVE_IPV6
+   free_rfd(f->rfd6);
+   f->rfd6 = NULL;
+-#endif
+ #ifdef HAVE_DNSSEC
+   if (f->stash)
+--- a/src/helper.c
++++ b/src/helper.c
+@@ -64,9 +64,7 @@ struct script_data
+ #ifdef HAVE_TFTP
+   off_t file_len;
+ #endif
+-#ifdef HAVE_IPV6
+   struct in6_addr addr6;
+-#endif
+ #ifdef HAVE_DHCP6
+   int iaid, vendorclass_count;
+ #endif
+@@ -302,10 +300,8 @@ int create_helper(int event_fd, int err_
+     
+       if (!is6)
+       inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
+-#ifdef HAVE_IPV6
+       else
+       inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
+-#endif
+ #ifdef HAVE_TFTP
+       /* file length */
+@@ -826,10 +822,8 @@ void queue_tftp(off_t file_len, char *fi
+   if ((buf->flags = peer->sa.sa_family) == AF_INET)
+     buf->addr = peer->in.sin_addr;
+-#ifdef HAVE_IPV6
+   else
+     buf->addr6 = peer->in6.sin6_addr;
+-#endif
+   memcpy((unsigned char *)(buf+1), filename, filename_len);
+   
+@@ -851,10 +845,8 @@ void queue_arp(int action, unsigned char
+   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);
+   
+--- a/src/ipset.c
++++ b/src/ipset.c
+@@ -120,13 +120,8 @@ static int new_add_to_ipset(const char *
+   struct my_nfgenmsg *nfg;
+   struct my_nlattr *nested[2];
+   uint8_t proto;
+-  int addrsz = INADDRSZ;
++  int addrsz = (af == AF_INET6) ? INADDRSZ : IN6ADDRSZ;
+-#ifdef HAVE_IPV6
+-  if (af == AF_INET6)
+-    addrsz = IN6ADDRSZ;
+-#endif
+-    
+   if (strlen(setname) >= IPSET_MAXNAMELEN) 
+     {
+       errno = ENAMETOOLONG;
+@@ -213,7 +208,6 @@ int add_to_ipset(const char *setname, co
+ {
+   int ret = 0, af = AF_INET;
+-#ifdef HAVE_IPV6
+   if (flags & F_IPV6)
+     {
+       af = AF_INET6;
+@@ -224,7 +218,6 @@ int add_to_ipset(const char *setname, co
+         ret = -1;
+       }
+     }
+-#endif
+   
+   if (ret != -1) 
+     ret = old_kernel ? old_add_to_ipset(setname, ipaddr, remove) : new_add_to_ipset(setname, ipaddr, af, remove);
+--- a/src/netlink.c
++++ b/src/netlink.c
+@@ -51,11 +51,10 @@ void netlink_init(void)
+   addr.nl_groups = RTMGRP_IPV4_ROUTE;
+   if (option_bool(OPT_CLEVERBIND))
+     addr.nl_groups |= RTMGRP_IPV4_IFADDR;  
+-#ifdef HAVE_IPV6
+   addr.nl_groups |= RTMGRP_IPV6_ROUTE;
+   if (option_bool(OPT_CLEVERBIND))
+     addr.nl_groups |= RTMGRP_IPV6_IFADDR;
+-#endif
++
+ #ifdef HAVE_DHCP6
+   if (daemon->doing_ra || daemon->doing_dhcp6)
+     addr.nl_groups |= RTMGRP_IPV6_IFADDR;
+@@ -235,7 +234,6 @@ int iface_enumerate(int family, void *pa
+                     if (!((*callback)(addr, ifa->ifa_index, label,  netmask, broadcast, parm)))
+                       callback_ok = 0;
+                 }
+-#ifdef HAVE_IPV6
+               else if (ifa->ifa_family == AF_INET6)
+                 {
+                   struct in6_addr *addrp = NULL;
+@@ -270,7 +268,6 @@ int iface_enumerate(int family, void *pa
+                                       (int) preferred, (int)valid, parm)))
+                       callback_ok = 0;
+                 }
+-#endif
+             }
+         }
+       else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC)
+--- a/src/network.c
++++ b/src/network.c
+@@ -137,12 +137,10 @@ int iface_check(int family, struct all_a
+             if (family == AF_INET &&
+                 tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
+               ret = match_addr = tmp->used = 1;
+-#ifdef HAVE_IPV6
+             else if (family == AF_INET6 &&
+                      IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, 
+                                         &addr->addr.addr6))
+               ret = match_addr = tmp->used = 1;
+-#endif
+           }          
+     }
+   
+@@ -162,11 +160,9 @@ int iface_check(int family, struct all_a
+     else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET &&
+            tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
+       break;
+-#ifdef HAVE_IPV6
+     else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 &&
+            IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr.addr6))
+       break;
+-#endif      
+   if (tmp && auth) 
+     {
+@@ -200,11 +196,8 @@ int loopback_exception(int fd, int famil
+               if (iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
+                 return 1;
+             }
+-#ifdef HAVE_IPV6
+           else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr.addr6))
+             return 1;
+-#endif
+-          
+         }
+     }
+   return 0;
+@@ -292,19 +285,15 @@ static int iface_allowed(struct iface_pa
+             al->addr.addr.addr4 = addr->in.sin_addr;
+             al->flags = 0;
+           }
+-#ifdef HAVE_IPV6
+         else
+           {
+             al->addr.addr.addr6 = addr->in6.sin6_addr;
+             al->flags = ADDRLIST_IPV6;
+           } 
+-#endif
+       }
+     }
+   
+-#ifdef HAVE_IPV6
+   if (addr->sa.sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
+-#endif
+     {
+       struct interface_name *int_name;
+       struct addrlist *al;
+@@ -337,7 +326,6 @@ static int iface_allowed(struct iface_pa
+                   }
+               }
+             
+-#ifdef HAVE_IPV6
+             if (addr->sa.sa_family == AF_INET6 && (name->flags & AUTH6))
+               {
+                 if (param->spare)
+@@ -357,8 +345,6 @@ static int iface_allowed(struct iface_pa
+                     al->flags = ADDRLIST_IPV6;
+                   }
+               } 
+-#endif
+-            
+           }
+ #endif
+        
+@@ -386,7 +372,6 @@ static int iface_allowed(struct iface_pa
+                   al->addr.addr.addr4 = addr->in.sin_addr;
+                   al->flags = 0;
+                 }
+-#ifdef HAVE_IPV6
+               else
+                {
+                   al->addr.addr.addr6 = addr->in6.sin6_addr;
+@@ -396,7 +381,6 @@ static int iface_allowed(struct iface_pa
+                   if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
+                     al->flags |= ADDRLIST_REVONLY;
+                } 
+-#endif
+             }
+         }
+     }
+@@ -438,11 +422,9 @@ static int iface_allowed(struct iface_pa
+       !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, label, &auth_dns))
+     return 1;
+-#ifdef HAVE_IPV6
+   if (addr->sa.sa_family == AF_INET6 &&
+       !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, label, &auth_dns))
+     return 1;
+-#endif
+     
+ #ifdef HAVE_DHCP
+   /* No DHCP where we're doing auth DNS. */
+@@ -501,7 +483,6 @@ static int iface_allowed(struct iface_pa
+   return 0;
+ }
+-#ifdef HAVE_IPV6
+ static int iface_allowed_v6(struct in6_addr *local, int prefix, 
+                           int scope, int if_index, int flags, 
+                           int preferred, int valid, void *vparam)
+@@ -529,7 +510,6 @@ static int iface_allowed_v6(struct in6_a
+   
+   return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, flags);
+ }
+-#endif
+ static int iface_allowed_v4(struct in_addr local, int if_index, char *label,
+                           struct in_addr netmask, struct in_addr broadcast, void *vparam)
+@@ -633,9 +613,7 @@ int enumerate_interfaces(int reset)
+   param.spare = spare;
+   
+-#ifdef HAVE_IPV6
+   ret = iface_enumerate(AF_INET6, &param, iface_allowed_v6);
+-#endif
+   if (ret)
+     ret = iface_enumerate(AF_INET, &param, iface_allowed_v4); 
+@@ -740,10 +718,8 @@ static int make_sock(union mysockaddr *a
+   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd))
+     goto err;
+   
+-#ifdef HAVE_IPV6
+   if (family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
+     goto err;
+-#endif
+   
+   if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) == -1)
+     goto err;
+@@ -767,15 +743,12 @@ static int make_sock(union mysockaddr *a
+ #endif
+       }
+     }
+-#ifdef HAVE_IPV6
+   else if (!set_ipv6pktinfo(fd))
+     goto err;
+-#endif
+   
+   return fd;
+ }
+-#ifdef HAVE_IPV6  
+ int set_ipv6pktinfo(int fd)
+ {
+   int opt = 1;
+@@ -802,7 +775,6 @@ int set_ipv6pktinfo(int fd)
+   return 0;
+ }
+-#endif
+ /* Find the interface on which a TCP connection arrived, if possible, or zero otherwise. */
+@@ -842,7 +814,6 @@ int tcp_interface(int fd, int af)
+             }
+       }
+     }
+-#ifdef HAVE_IPV6
+   else
+     {
+       /* Only the RFC-2292 API has the ability to find the interface for TCP connections,
+@@ -874,7 +845,6 @@ int tcp_interface(int fd, int af)
+               }
+       }
+     }
+-#endif /* IPV6 */
+ #endif /* Linux */
+  
+   return if_index;
+@@ -904,7 +874,6 @@ static struct listener *create_listeners
+         tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
+         addr->in.sin_port = save;
+       }
+-#  ifdef HAVE_IPV6
+       else
+       {
+         short save = addr->in6.sin6_port;
+@@ -912,7 +881,6 @@ static struct listener *create_listeners
+         tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
+         addr->in6.sin6_port = save;
+       }  
+-#  endif
+     }
+ #endif
+@@ -945,11 +913,10 @@ void create_wildcard_listeners(void)
+   l = create_listeners(&addr, !!option_bool(OPT_TFTP), 1);
+-#ifdef HAVE_IPV6
+   memset(&addr, 0, sizeof(addr));
+-#  ifdef HAVE_SOCKADDR_SA_LEN
++#ifdef HAVE_SOCKADDR_SA_LEN
+   addr.in6.sin6_len = sizeof(addr.in6);
+-#  endif
++#endif
+   addr.in6.sin6_family = AF_INET6;
+   addr.in6.sin6_addr = in6addr_any;
+   addr.in6.sin6_port = htons(daemon->port);
+@@ -959,7 +926,6 @@ void create_wildcard_listeners(void)
+     l->next = l6;
+   else 
+     l = l6;
+-#endif
+   daemon->listeners = l;
+ }
+@@ -1159,7 +1125,6 @@ int random_sock(int family)
+               addr.in.sin_len = sizeof(struct sockaddr_in);
+ #endif
+             }
+-#ifdef HAVE_IPV6
+           else
+             {
+               addr.in6.sin6_addr = in6addr_any; 
+@@ -1168,7 +1133,6 @@ int random_sock(int family)
+               addr.in6.sin6_len = sizeof(struct sockaddr_in6);
+ #endif
+             }
+-#endif
+           
+           if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0)
+             return fd;
+@@ -1193,10 +1157,8 @@ int local_bind(int fd, union mysockaddr
+     {
+       if (addr_copy.sa.sa_family == AF_INET)
+       addr_copy.in.sin_port = 0;
+-#ifdef HAVE_IPV6
+       else
+       addr_copy.in6.sin6_port = 0;
+-#endif
+     }
+   
+   if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) == -1)
+@@ -1211,7 +1173,7 @@ int local_bind(int fd, union mysockaddr
+           return setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0;
+         }
+ #endif
+-#if defined(HAVE_IPV6) && defined (IPV6_UNICAST_IF)
++#if defined (IPV6_UNICAST_IF)
+       if (addr_copy.sa.sa_family == AF_INET6)
+         {
+           uint32_t ifindex_opt = htonl(ifindex);
+@@ -1247,12 +1209,10 @@ static struct serverfd *allocate_sfd(uni
+         addr->in.sin_port == htons(0)) 
+       return NULL;
+-#ifdef HAVE_IPV6
+       if (addr->sa.sa_family == AF_INET6 &&
+         memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
+         addr->in6.sin6_port == htons(0)) 
+       return NULL;
+-#endif
+     }
+   if (intname && strlen(intname) != 0)
+@@ -1315,7 +1275,7 @@ void pre_allocate_sfds(void)
+ #endif
+       if ((sfd = allocate_sfd(&addr, "")))
+       sfd->preallocated = 1;
+-#ifdef HAVE_IPV6
++
+       memset(&addr, 0, sizeof(addr));
+       addr.in6.sin6_family = AF_INET6;
+       addr.in6.sin6_addr = in6addr_any;
+@@ -1325,7 +1285,6 @@ void pre_allocate_sfds(void)
+ #endif
+       if ((sfd = allocate_sfd(&addr, "")))
+       sfd->preallocated = 1;
+-#endif
+     }
+   
+   for (srv = daemon->servers; srv; srv = srv->next)
+@@ -1658,7 +1617,6 @@ int reload_servers(char *fname)
+         source_addr.in.sin_addr.s_addr = INADDR_ANY;
+         source_addr.in.sin_port = htons(daemon->query_port);
+       }
+-#ifdef HAVE_IPV6
+       else 
+       {       
+         int scope_index = 0;
+@@ -1686,10 +1644,6 @@ int reload_servers(char *fname)
+         else
+           continue;
+       }
+-#else /* IPV6 */
+-      else
+-      continue;
+-#endif 
+       add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
+       gotone = 1;
+--- a/src/option.c
++++ b/src/option.c
+@@ -764,10 +764,8 @@ static char *parse_mysockaddr(char *arg,
+ {
+   if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
+     addr->sa.sa_family = AF_INET;
+-#ifdef HAVE_IPV6
+   else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
+     addr->sa.sa_family = AF_INET6;
+-#endif
+   else
+     return _("bad address");
+    
+@@ -779,10 +777,8 @@ char *parse_server(char *arg, union myso
+   int source_port = 0, serv_port = NAMESERVER_PORT;
+   char *portno, *source;
+   char *interface_opt = NULL;
+-#ifdef HAVE_IPV6
+   int scope_index = 0;
+   char *scope_id;
+-#endif
+   
+   if (!arg || strlen(arg) == 0)
+     {
+@@ -800,9 +796,7 @@ char *parse_server(char *arg, union myso
+       !atoi_check16(portno, &serv_port))
+     return _("bad port");
+   
+-#ifdef HAVE_IPV6
+   scope_id = split_chr(arg, '%');
+-#endif
+   
+   if (source) {
+     interface_opt = split_chr(source, '@');
+@@ -846,7 +840,6 @@ char *parse_server(char *arg, union myso
+           }
+       }
+     }
+-#ifdef HAVE_IPV6
+   else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
+     {
+       if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
+@@ -881,7 +874,6 @@ char *parse_server(char *arg, union myso
+           }
+       }
+     }
+-#endif
+   else
+     return _("bad address");
+@@ -1914,10 +1906,8 @@ static int one_opt(int option, char *arg
+         unhide_metas(arg);
+         if (inet_pton(AF_INET, arg, &new->addr.in.sin_addr) > 0)
+           new->addr.sa.sa_family = AF_INET;
+-#ifdef HAVE_IPV6
+         else if (inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
+           new->addr.sa.sa_family = AF_INET6;
+-#endif
+         else
+           {
+             char *fam = split_chr(arg, '/');
+@@ -1927,10 +1917,8 @@ static int one_opt(int option, char *arg
+               {
+                 if (strcmp(fam, "4") == 0)
+                   new->addr.sa.sa_family = AF_INET;
+-#ifdef HAVE_IPV6
+                 else if (strcmp(fam, "6") == 0)
+                   new->addr.sa.sa_family = AF_INET6;
+-#endif
+                 else
+                   ret_err(gen_err);
+               } 
+@@ -1996,14 +1984,12 @@ static int one_opt(int option, char *arg
+               subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
+               subnet->flags = ADDRLIST_LITERAL;
+             }
+-#ifdef HAVE_IPV6
+           else if (inet_pton(AF_INET6, arg, &addr.addr.addr6))
+             {
+               subnet = opt_malloc(sizeof(struct addrlist));
+               subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
+               subnet->flags = ADDRLIST_LITERAL | ADDRLIST_IPV6;
+             }
+-#endif
+           else 
+             {
+               struct auth_name_list *name =  opt_malloc(sizeof(struct auth_name_list));
+@@ -2015,10 +2001,8 @@ static int one_opt(int option, char *arg
+                 {
+                   if (prefixlen == 4)
+                     name->flags &= ~AUTH6;
+-#ifdef HAVE_IPV6
+                   else if (prefixlen == 6)
+                     name->flags &= ~AUTH4;
+-#endif
+                   else
+                     ret_err(gen_err);
+                 }
+@@ -2139,7 +2123,6 @@ static int one_opt(int option, char *arg
+                               }
+                           }
+                       }
+-#ifdef HAVE_IPV6
+                     else if (inet_pton(AF_INET6, comma, &new->start6))
+                       {
+                         u64 mask = (1LLU << (128 - msize)) - 1LLU;
+@@ -2183,7 +2166,6 @@ static int one_opt(int option, char *arg
+                               }
+                           }
+                       }
+-#endif
+                     else
+                       ret_err(gen_err);
+                   }
+@@ -2201,7 +2183,6 @@ static int one_opt(int option, char *arg
+                         else if (!inet_pton(AF_INET, arg, &new->end))
+                           ret_err(gen_err);
+                       }
+-#ifdef HAVE_IPV6
+                     else if (inet_pton(AF_INET6, comma, &new->start6))
+                       {
+                         new->is6 = 1;
+@@ -2210,7 +2191,6 @@ static int one_opt(int option, char *arg
+                         else if (!inet_pton(AF_INET6, arg, &new->end6))
+                           ret_err(gen_err);
+                       }
+-#endif
+                     else 
+                       ret_err(gen_err);
+@@ -2369,7 +2349,6 @@ static int one_opt(int option, char *arg
+           new->addr.in.sin_len = sizeof(new->addr.in);
+ #endif
+         }
+-#ifdef HAVE_IPV6
+       else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
+         {
+           new->addr.sa.sa_family = AF_INET6;
+@@ -2380,7 +2359,6 @@ static int one_opt(int option, char *arg
+           new->addr.in6.sin6_len = sizeof(new->addr.in6);
+ #endif
+         }
+-#endif
+       else
+         ret_err(gen_err);
+@@ -2493,9 +2471,7 @@ static int one_opt(int option, char *arg
+       int size;
+       struct server *serv;
+       struct in_addr addr4;
+-#ifdef HAVE_IPV6
+       struct in6_addr addr6;
+-#endif
+  
+       unhide_metas(arg);
+       if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
+@@ -2507,10 +2483,8 @@ static int one_opt(int option, char *arg
+           if (!serv)
+             ret_err(_("bad prefix"));
+         }
+-#ifdef HAVE_IPV6
+       else if (inet_pton(AF_INET6, arg, &addr6))
+         serv = add_rev6(&addr6, size);
+-#endif
+       else
+         ret_err(gen_err);
+  
+@@ -3863,10 +3837,8 @@ err:
+         {
+           if (strcmp(arg, "4") == 0)
+             new->family = AF_INET;
+-#ifdef HAVE_IPV6
+           else if (strcmp(arg, "6") == 0)
+             new->family = AF_INET6;
+-#endif
+           else
+             ret_err(gen_err);
+         } 
+@@ -4156,10 +4128,8 @@ err:
+             new->ttl = atoi(arg);
+           else if (inet_pton(AF_INET, arg, &addr))
+             new->addr = addr.addr.addr4;
+-#ifdef HAVE_IPV6
+           else if (inet_pton(AF_INET6, arg, &addr))
+             new->addr6 = addr.addr.addr6;
+-#endif
+           else
+             {
+               int nomem;
+@@ -4866,10 +4836,8 @@ void read_opts(int argc, char **argv, ch
+         {
+           if (tmp->source_addr.sa.sa_family == AF_INET)
+             tmp->source_addr.in.sin_port = htons(daemon->query_port);
+-#ifdef HAVE_IPV6
+           else if (tmp->source_addr.sa.sa_family == AF_INET6)
+             tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
+-#endif 
+         }
+     } 
+   
+@@ -4930,10 +4898,8 @@ void read_opts(int argc, char **argv, ch
+       for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
+       if (tmp->addr.sa.sa_family == AF_INET)
+         tmp->addr.in.sin_port = htons(daemon->port);
+-#ifdef HAVE_IPV6
+       else if (tmp->addr.sa.sa_family == AF_INET6)
+         tmp->addr.in6.sin6_port = htons(daemon->port);
+-#endif /* IPv6 */
+     }
+       
+   /* create default, if not specified */
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -198,7 +198,6 @@ int in_arpa_name_2_addr(char *namein, st
+       return F_IPV4;
+     }
+-#ifdef HAVE_IPV6
+   else if (hostname_isequal(penchunk, "ip6") && 
+          (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa")))
+     {
+@@ -243,7 +242,6 @@ int in_arpa_name_2_addr(char *namein, st
+         return F_IPV6;
+       }
+     }
+-#endif
+   
+   return 0;
+ }
+@@ -426,7 +424,6 @@ int private_net(struct in_addr addr, int
+     ((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF)  /* 255.255.255.255/32 (broadcast)*/ ;
+ }
+-#ifdef HAVE_IPV6
+ static int private_net6(struct in6_addr *a)
+ {
+   return 
+@@ -436,8 +433,6 @@ static int private_net6(struct in6_addr
+     ((unsigned char *)a)[0] == 0xfd ||   /* RFC 6303 4.4 */
+     ((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
+ }
+-#endif
+-
+ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
+ {
+@@ -738,13 +733,11 @@ int extract_addresses(struct dns_header
+             addrlen = INADDRSZ;
+             flags |= F_IPV4;
+           }
+-#ifdef HAVE_IPV6
+         else if (qtype == T_AAAA)
+           {
+             addrlen = IN6ADDRSZ;
+             flags |= F_IPV6;
+           }
+-#endif
+         else 
+           continue;
+           
+@@ -818,7 +811,6 @@ int extract_addresses(struct dns_header
+                             private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
+                           return 1;
+                         
+-#ifdef HAVE_IPV6
+                         if ((flags & F_IPV6) &&
+                             IN6_IS_ADDR_V4MAPPED(&addr.addr.addr6))
+                           {
+@@ -827,7 +819,6 @@ int extract_addresses(struct dns_header
+                             if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
+                               return 1;
+                           }
+-#endif
+                       }
+                     
+ #ifdef HAVE_IPSET
+@@ -966,7 +957,6 @@ size_t setup_reply(struct dns_header *he
+         add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
+       }
+       
+-#ifdef HAVE_IPV6
+       if (flags & F_IPV6)
+       {
+         SET_RCODE(header, NOERROR);
+@@ -974,7 +964,6 @@ size_t setup_reply(struct dns_header *he
+         header->hb3 |= HB3_AA;
+         add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
+       }
+-#endif
+     }
+   else /* nowhere to forward to */
+     {
+@@ -1164,14 +1153,12 @@ int add_resource_record(struct dns_heade
+   for (; *format; format++)
+     switch (*format)
+       {
+-#ifdef HAVE_IPV6
+       case '6':
+         CHECK_LIMIT(IN6ADDRSZ);
+       sval = va_arg(ap, char *); 
+       memcpy(p, sval, IN6ADDRSZ);
+       p += IN6ADDRSZ;
+       break;
+-#endif
+       
+       case '4':
+         CHECK_LIMIT(INADDRSZ);
+@@ -1413,7 +1400,6 @@ size_t answer_request(struct dns_header
+                     while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
+                       intr = intr->next;
+                 }
+-#ifdef HAVE_IPV6
+             else if (is_arpa == F_IPV6)
+               for (intr = daemon->int_names; intr; intr = intr->next)
+                 {
+@@ -1429,7 +1415,6 @@ size_t answer_request(struct dns_header
+                     while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
+                       intr = intr->next;
+                 }
+-#endif
+             
+             if (intr)
+               {
+@@ -1521,9 +1506,7 @@ size_t answer_request(struct dns_header
+                   }
+               }
+             else if (option_bool(OPT_BOGUSPRIV) && (
+-#ifdef HAVE_IPV6
+                      (is_arpa == F_IPV6 && private_net6(&addr.addr.addr6)) ||
+-#endif
+                      (is_arpa == F_IPV4 && private_net(addr.addr.addr4, 1))))
+               {
+                 struct server *serv;
+@@ -1564,16 +1547,9 @@ size_t answer_request(struct dns_header
+         
+         for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
+           {
+-            unsigned short type = T_A;
++            unsigned short type = (flag == F_IPV6) ? T_AAAA : T_A;
+             struct interface_name *intr;
+-            if (flag == F_IPV6)
+-#ifdef HAVE_IPV6
+-              type = T_AAAA;
+-#else
+-              break;
+-#endif
+-            
+             if (qtype != type && qtype != T_ANY)
+               continue;
+             
+@@ -1596,31 +1572,26 @@ size_t answer_request(struct dns_header
+                   for (intr = daemon->int_names; intr; intr = intr->next)
+                     if (hostname_isequal(name, intr->name))
+                       for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
+-#ifdef HAVE_IPV6
+-                        if (!(addrlist->flags & ADDRLIST_IPV6))
+-#endif
+-                          if (is_same_net(*((struct in_addr *)&addrlist->addr), local_addr, local_netmask))
+-                            {
+-                              localise = 1;
+-                              break;
+-                            }
++                        if (!(addrlist->flags & ADDRLIST_IPV6) && 
++                            is_same_net(*((struct in_addr *)&addrlist->addr), local_addr, local_netmask))
++                          {
++                            localise = 1;
++                            break;
++                          }
+                 
+                 for (intr = daemon->int_names; intr; intr = intr->next)
+                   if (hostname_isequal(name, intr->name))
+                     {
+                       for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
+-#ifdef HAVE_IPV6
+                         if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
+-#endif
+                           {
+                             if (localise && 
+                                 !is_same_net(*((struct in_addr *)&addrlist->addr), local_addr, local_netmask))
+                               continue;
+-#ifdef HAVE_IPV6
+                             if (addrlist->flags & ADDRLIST_REVONLY)
+                               continue;
+-#endif        
++
+                             ans = 1;  
+                             sec_data = 0;
+                             if (!dryrun)
+@@ -1904,11 +1875,8 @@ size_t answer_request(struct dns_header
+       crecp = NULL;
+       while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
+         {
+-#ifdef HAVE_IPV6
+           int type =  crecp->flags & F_IPV4 ? T_A : T_AAAA;
+-#else
+-          int type = T_A;
+-#endif
++
+           if (crecp->flags & F_NEG)
+             continue;
+--- a/src/tables.c
++++ b/src/tables.c
+@@ -108,7 +108,7 @@ int add_to_ipset(const char *setname, co
+     my_syslog(LOG_INFO, _("info: table created"));
+  
+   bzero(&addr, sizeof(addr));
+-#ifdef HAVE_IPV6
++
+   if (flags & F_IPV6) 
+     {
+       addr.pfra_af = AF_INET6;
+@@ -116,7 +116,6 @@ int add_to_ipset(const char *setname, co
+       memcpy(&(addr.pfra_ip6addr), &(ipaddr->addr), sizeof(struct in6_addr));
+     } 
+   else 
+-#endif
+     {
+       addr.pfra_af = AF_INET;
+       addr.pfra_net = 0x20;
+--- a/src/tftp.c
++++ b/src/tftp.c
+@@ -60,17 +60,11 @@ void tftp_request(struct listener *liste
+   char *prefix = daemon->tftp_prefix;
+   struct tftp_prefix *pref;
+   struct all_addr addra;
+-#ifdef HAVE_IPV6
+   /* Can always get recvd interface for IPv6 */
+   int check_dest = !option_bool(OPT_NOWILD) || listen->family == AF_INET6;
+-#else
+-  int check_dest = !option_bool(OPT_NOWILD);
+-#endif
+   union {
+     struct cmsghdr align; /* this ensures alignment */
+-#ifdef HAVE_IPV6
+     char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+-#endif
+ #if defined(HAVE_LINUX_NETWORK)
+     char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
+ #elif defined(HAVE_SOLARIS_NETWORK)
+@@ -174,7 +168,6 @@ void tftp_request(struct listener *liste
+         
+ #endif
+-#ifdef HAVE_IPV6
+       if (listen->family == AF_INET6)
+         {
+           for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+@@ -190,7 +183,6 @@ void tftp_request(struct listener *liste
+                 if_index = p.p->ipi6_ifindex;
+               }
+         }
+-#endif
+       
+       if (!indextoname(listen->tftpfd, if_index, namebuff))
+       return;
+@@ -199,10 +191,8 @@ void tftp_request(struct listener *liste
+       
+       addra.addr.addr4 = addr.in.sin_addr;
+-#ifdef HAVE_IPV6
+       if (listen->family == AF_INET6)
+       addra.addr.addr6 = addr.in6.sin6_addr;
+-#endif
+       if (daemon->tftp_interfaces)
+       {
+@@ -262,7 +252,6 @@ void tftp_request(struct listener *liste
+       addr.in.sin_len = sizeof(addr.in);
+ #endif
+     }
+-#ifdef HAVE_IPV6
+   else
+     {
+       addr.in6.sin6_port = htons(port);
+@@ -272,7 +261,6 @@ void tftp_request(struct listener *liste
+       addr.in6.sin6_len = sizeof(addr.in6);
+ #endif
+     }
+-#endif
+   if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
+     return;
+@@ -310,10 +298,9 @@ void tftp_request(struct listener *liste
+               { 
+                 if (listen->family == AF_INET)
+                   addr.in.sin_port = htons(port);
+-#ifdef HAVE_IPV6
+                 else
+-                   addr.in6.sin6_port = htons(port);
+-#endif
++                  addr.in6.sin6_port = htons(port);
++                
+                 continue;
+               }
+             my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
+--- a/src/util.c
++++ b/src/util.c
+@@ -320,13 +320,12 @@ int sockaddr_isequal(union mysockaddr *s
+         s1->in.sin_port == s2->in.sin_port &&
+         s1->in.sin_addr.s_addr == s2->in.sin_addr.s_addr)
+       return 1;
+-#ifdef HAVE_IPV6      
++      
+       if (s1->sa.sa_family == AF_INET6 &&
+         s1->in6.sin6_port == s2->in6.sin6_port &&
+         s1->in6.sin6_scope_id == s2->in6.sin6_scope_id &&
+         IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))
+       return 1;
+-#endif
+     }
+   return 0;
+ }
+@@ -336,11 +335,9 @@ int sa_len(union mysockaddr *addr)
+ #ifdef HAVE_SOCKADDR_SA_LEN
+   return addr->sa.sa_len;
+ #else
+-#ifdef HAVE_IPV6
+   if (addr->sa.sa_family == AF_INET6)
+     return sizeof(addr->in6);
+   else
+-#endif
+     return sizeof(addr->in); 
+ #endif
+ }
+@@ -437,7 +434,6 @@ int is_same_net(struct in_addr a, struct
+   return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
+ } 
+-#ifdef HAVE_IPV6
+ int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
+ {
+   int pfbytes = prefixlen >> 3;
+@@ -476,15 +472,12 @@ void setaddr6part(struct in6_addr *addr,
+     }
+ }
+-#endif
+- 
+ /* returns port number from address */
+ int prettyprint_addr(union mysockaddr *addr, char *buf)
+ {
+   int port = 0;
+   
+-#ifdef HAVE_IPV6
+   if (addr->sa.sa_family == AF_INET)
+     {
+       inet_ntop(AF_INET, &addr->in.sin_addr, buf, ADDRSTRLEN);
+@@ -503,10 +496,6 @@ int prettyprint_addr(union mysockaddr *a
+       }
+       port = ntohs(addr->in6.sin6_port);
+     }
+-#else
+-  strcpy(buf, inet_ntoa(addr->in.sin_addr));
+-  port = ntohs(addr->in.sin_port); 
+-#endif
+   
+   return port;
+ }
diff --git a/package/network/services/dnsmasq/patches/0004-Don-t-forward-.bind-.server-queries-upstream.patch b/package/network/services/dnsmasq/patches/0004-Don-t-forward-.bind-.server-queries-upstream.patch
new file mode 100644 (file)
index 0000000..497b4c3
--- /dev/null
@@ -0,0 +1,52 @@
+From cf5984367bc6a949e3803a576512c5a7bc48ebab Mon Sep 17 00:00:00 2001
+From: Vladislav Grishenko <themiron@mail.ru>
+Date: Thu, 18 Oct 2018 04:55:21 +0500
+Subject: [PATCH 04/11] Don't forward *.bind/*.server queries upstream
+
+Chaos .bind and .server (RFC4892) zones are local, therefore
+don't forward queries upstream to avoid mixing with supported
+locally and false replies with NO_ID enabled.
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ src/rfc1035.c | 15 ++++++++++++++-
+ 1 file changed, 14 insertions(+), 1 deletion(-)
+
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1276,7 +1276,7 @@ size_t answer_request(struct dns_header
+   int q, ans, anscount = 0, addncount = 0;
+   int dryrun = 0;
+   struct crec *crecp;
+-  int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
++  int nxdomain = 0, notimp = 0, auth = 1, trunc = 0, sec_data = 1;
+   struct mx_srv_record *rec;
+   size_t len;
+@@ -1355,6 +1355,17 @@ size_t answer_request(struct dns_header
+           }
+       }
++      if (qclass == C_CHAOS)
++      {
++        /* don't forward *.bind and *.server chaos queries */
++        if (hostname_issubdomain("bind", name) || hostname_issubdomain("server", name))
++          {
++            if (!ans)
++              notimp = 1, auth = 0;
++            ans = 1;
++          }
++      }
++
+       if (qclass == C_IN)
+       {
+         struct txt_record *t;
+@@ -1903,6 +1914,8 @@ size_t answer_request(struct dns_header
+   
+   if (nxdomain)
+     SET_RCODE(header, NXDOMAIN);
++  else if (notimp)
++    SET_RCODE(header, NOTIMP);
+   else
+     SET_RCODE(header, NOERROR); /* no error */
+   header->ancount = htons(anscount);
diff --git a/package/network/services/dnsmasq/patches/0005-Fix-logging-in-cf5984367bc6a949e3803a576512c5a7bc48e.patch b/package/network/services/dnsmasq/patches/0005-Fix-logging-in-cf5984367bc6a949e3803a576512c5a7bc48e.patch
new file mode 100644 (file)
index 0000000..b9be306
--- /dev/null
@@ -0,0 +1,63 @@
+From cbb5b17ad8e03e08ade62376a4f6a2066e55960d Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Tue, 23 Oct 2018 23:45:57 +0100
+Subject: [PATCH 05/11] Fix logging in cf5984367bc6a949e3803a576512c5a7bc48ebab
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ src/rfc1035.c | 27 ++++++++++++++++++---------
+ 1 file changed, 18 insertions(+), 9 deletions(-)
+
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1335,7 +1335,6 @@ size_t answer_request(struct dns_header
+                   {
+                     unsigned long ttl = daemon->local_ttl;
+                     int ok = 1;
+-                    log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
+ #ifndef NO_ID
+                     /* Dynamically generate stat record */
+                     if (t->stat != 0)
+@@ -1345,11 +1344,14 @@ size_t answer_request(struct dns_header
+                           ok = 0;
+                       }
+ #endif
+-                    if (ok && add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+-                                                  ttl, NULL,
+-                                                  T_TXT, t->class, "t", t->len, t->txt))
+-                      anscount++;
+-
++                    if (ok)
++                      {
++                        log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
++                        if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
++                                                ttl, NULL,
++                                                T_TXT, t->class, "t", t->len, t->txt))
++                          anscount++;
++                      }
+                   }
+               }
+           }
+@@ -1357,12 +1359,19 @@ size_t answer_request(struct dns_header
+       if (qclass == C_CHAOS)
+       {
+-        /* don't forward *.bind and *.server chaos queries */
++        /* don't forward *.bind and *.server chaos queries - always reply with NOTIMP */
+         if (hostname_issubdomain("bind", name) || hostname_issubdomain("server", name))
+           {
+             if (!ans)
+-              notimp = 1, auth = 0;
+-            ans = 1;
++              {
++                notimp = 1, auth = 0;
++                if (!dryrun)
++                  {
++                     addr.addr.rcode.rcode = NOTIMP;
++                     log_query(F_CONFIG | F_RCODE, name, &addr, NULL);
++                  }
++                ans = 1;
++              }
+           }
+       }
diff --git a/package/network/services/dnsmasq/patches/0006-Fix-spurious-AD-flags-in-some-DNS-replies-from-local.patch b/package/network/services/dnsmasq/patches/0006-Fix-spurious-AD-flags-in-some-DNS-replies-from-local.patch
new file mode 100644 (file)
index 0000000..3271a5f
--- /dev/null
@@ -0,0 +1,120 @@
+From 6f7812d97bc8f87004c0a5069c6c94c64af78106 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Tue, 23 Oct 2018 23:54:44 +0100
+Subject: [PATCH 06/11] Fix spurious AD flags in some DNS replies from local
+ config.
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ src/rfc1035.c | 42 ++++++++++++++++++++++++------------------
+ 1 file changed, 24 insertions(+), 18 deletions(-)
+
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1330,7 +1330,7 @@ size_t answer_request(struct dns_header
+           {
+             if (t->class == qclass && hostname_isequal(name, t->name))
+               {
+-                ans = 1;
++                ans = 1, sec_data = 0;
+                 if (!dryrun)
+                   {
+                     unsigned long ttl = daemon->local_ttl;
+@@ -1370,7 +1370,7 @@ size_t answer_request(struct dns_header
+                      addr.addr.rcode.rcode = NOTIMP;
+                      log_query(F_CONFIG | F_RCODE, name, &addr, NULL);
+                   }
+-                ans = 1;
++                ans = 1, sec_data = 0;
+               }
+           }
+       }
+@@ -1725,7 +1725,7 @@ size_t answer_request(struct dns_header
+               }
+             else if (is_name_synthetic(flag, name, &addr))
+               {
+-                ans = 1;
++                ans = 1, sec_data = 0;
+                 if (!dryrun)
+                   {
+                     log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL);
+@@ -1763,25 +1763,27 @@ size_t answer_request(struct dns_header
+             for (rec = daemon->mxnames; rec; rec = rec->next)
+               if (!rec->issrv && hostname_isequal(name, rec->name))
+                 {
+-                ans = found = 1;
+-                if (!dryrun)
+-                  {
+-                    int offset;
+-                    log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
+-                    if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
+-                                            &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
+-                      {
+-                        anscount++;
+-                        if (rec->target)
+-                          rec->offset = offset;
+-                      }
+-                  }
++                  ans = found = 1;
++                  sec_data = 0;
++                  if (!dryrun)
++                    {
++                      int offset;
++                      log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
++                      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
++                                              &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
++                        {
++                          anscount++;
++                          if (rec->target)
++                            rec->offset = offset;
++                        }
++                    }
+                 }
+             
+             if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && 
+                 cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR))
+               { 
+                 ans = 1;
++                sec_data = 0;
+                 if (!dryrun)
+                   {
+                     log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
+@@ -1802,6 +1804,7 @@ size_t answer_request(struct dns_header
+               if (rec->issrv && hostname_isequal(name, rec->name))
+                 {
+                   found = ans = 1;
++                  sec_data = 0;
+                   if (!dryrun)
+                     {
+                       int offset;
+@@ -1838,6 +1841,7 @@ size_t answer_request(struct dns_header
+             if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
+               {
+                 ans = 1;
++                sec_data = 0;
+                 if (!dryrun)
+                   log_query(F_CONFIG | F_NEG, name, NULL, NULL);
+               }
+@@ -1850,6 +1854,7 @@ size_t answer_request(struct dns_header
+               if (hostname_isequal(name, na->name))
+                 {
+                   ans = 1;
++                  sec_data = 0;
+                   if (!dryrun)
+                     {
+                       log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
+@@ -1862,11 +1867,12 @@ size_t answer_request(struct dns_header
+           }
+         
+         if (qtype == T_MAILB)
+-          ans = 1, nxdomain = 1;
++          ans = 1, nxdomain = 1, sec_data = 0;
+         if (qtype == T_SOA && option_bool(OPT_FILTER))
+           {
+-            ans = 1; 
++            ans = 1;
++            sec_data = 0;
+             if (!dryrun)
+               log_query(F_CONFIG | F_NEG, name, &addr, NULL);
+           }
diff --git a/package/network/services/dnsmasq/patches/0007-Do-not-rely-on-dead-code-elimination-use-array-inste.patch b/package/network/services/dnsmasq/patches/0007-Do-not-rely-on-dead-code-elimination-use-array-inste.patch
new file mode 100644 (file)
index 0000000..55ea917
--- /dev/null
@@ -0,0 +1,71 @@
+From 24b87607c1353e94689e8a2190571ab3f3b36f31 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
+Date: Wed, 24 Oct 2018 22:30:18 +0100
+Subject: [PATCH 07/11] Do not rely on dead code elimination, use array
+ instead. Make options bits derived from size and count. Use size of option
+ bits and last supported bit in computation. No new change would be required
+ when new options are added. Just change OPT_LAST constant.
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ src/dnsmasq.h | 11 +++++++----
+ src/option.c  | 10 ++--------
+ 2 files changed, 9 insertions(+), 12 deletions(-)
+
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -200,9 +200,6 @@ struct event_desc {
+ #define EC_MISC        5
+ #define EC_INIT_OFFSET 10
+-/* Trust the compiler dead-code eliminator.... */
+-#define option_bool(x) (((x) < 32) ? daemon->options & (1u << (x)) : daemon->options2 & (1u << ((x) - 32)))
+-
+ #define OPT_BOGUSPRIV      0
+ #define OPT_FILTER         1
+ #define OPT_LOG            2
+@@ -264,6 +261,12 @@ struct event_desc {
+ #define OPT_UBUS           58
+ #define OPT_LAST           59
++#define OPTION_BITS (sizeof(unsigned int)*8)
++#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
++#define option_var(x) (daemon->options[(x) / OPTION_BITS])
++#define option_val(x) ((1u) << ((x) % OPTION_BITS))
++#define option_bool(x) (option_var(x) & option_val(x))
++
+ /* 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. */
+ #define MS_TFTP   LOG_USER
+@@ -978,7 +981,7 @@ extern struct daemon {
+      config file arguments. All set (including defaults)
+      in option.c */
+-  unsigned int options, options2;
++  unsigned int options[OPTION_SIZE];
+   struct resolvc default_resolv, *resolv_files;
+   time_t last_resolv;
+   char *servers_file;
+--- a/src/option.c
++++ b/src/option.c
+@@ -1490,18 +1490,12 @@ static int parse_dhcp_opt(char *errstr,
+ void set_option_bool(unsigned int opt)
+ {
+-  if (opt < 32)
+-    daemon->options |= 1u << opt;
+-  else
+-    daemon->options2 |= 1u << (opt - 32);
++  option_var(opt) |= option_val(opt);
+ }
+ void reset_option_bool(unsigned int opt)
+ {
+-  if (opt < 32)
+-    daemon->options &= ~(1u << opt);
+-  else
+-    daemon->options2 &= ~(1u << (opt - 32));
++  option_var(opt) &= ~(option_val(opt));
+ }
+ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
diff --git a/package/network/services/dnsmasq/patches/0008-Fix-Makefile-lines-generating-UBUS-linker-config.patch b/package/network/services/dnsmasq/patches/0008-Fix-Makefile-lines-generating-UBUS-linker-config.patch
new file mode 100644 (file)
index 0000000..91b465f
--- /dev/null
@@ -0,0 +1,63 @@
+From 3a5a84cdd1488bad118eeac72d09a60299bca744 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Wed, 31 Oct 2018 21:30:13 +0000
+Subject: [PATCH 08/11] Fix Makefile lines generating UBUS linker config.
+
+If arg2 of pkg-wrapper is "--copy", then arg1 is NOT the name of
+the package manager (--copy doesn't invoke it) it's a secondary
+config string that inhibts the copy if found. This patch allows that
+to be the empty string, for unconditional copy, and modifies the
+ubus linker config to use it. It worked by coincidence before, because
+there was no config string called "pkg-config".
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ Makefile        |  2 +-
+ bld/pkg-wrapper | 14 ++++++++------
+ 2 files changed, 9 insertions(+), 7 deletions(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -53,7 +53,7 @@ top?=$(CURDIR)
+ dbus_cflags =   `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1` 
+ dbus_libs =     `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1` 
+-ubus_libs =     `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_UBUS $(PKG_CONFIG) --copy -lubox -lubus`
++ubus_libs =     `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_UBUS "" --copy -lubox -lubus`
+ idn_cflags =    `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn` 
+ idn_libs =      `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn` 
+ idn2_cflags =   `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFIG) --cflags libidn2`
+--- a/bld/pkg-wrapper
++++ b/bld/pkg-wrapper
+@@ -11,23 +11,25 @@ in=`cat`
+ if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \
+     echo $in | grep $search >/dev/null 2>&1; then
+-# Nasty, nasty, in --copy, arg 2 is another config to search for, use with NO_GMP
++# Nasty, nasty, in --copy, arg 2 (if non-empty) is another config to search for, used with NO_GMP
+     if [ $op = "--copy" ]; then
+-      if grep "^\#[[:space:]]*define[[:space:]]*$pkg" config.h >/dev/null 2>&1 || \
+-            echo $in | grep $pkg >/dev/null 2>&1; then
++      if [ -z "$pkg" ]; then
++          pkg="$*"
++      elif grep "^\#[[:space:]]*define[[:space:]]*$pkg" config.h >/dev/null 2>&1 || \
++               echo $in | grep $pkg >/dev/null 2>&1; then
+           pkg=""
+       else 
+           pkg="$*"
+       fi
+     elif grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \
+-            echo $in | grep ${search}_STATIC >/dev/null 2>&1; then
++           echo $in | grep ${search}_STATIC >/dev/null 2>&1; then
+       pkg=`$pkg  --static $op $*`
+     else
+       pkg=`$pkg $op $*`
+     fi
+-
++    
+     if grep "^\#[[:space:]]*define[[:space:]]*${search}_STATIC" config.h >/dev/null 2>&1 || \
+-      echo $in | grep ${search}_STATIC >/dev/null 2>&1; then
++         echo $in | grep ${search}_STATIC >/dev/null 2>&1; then
+       if [ $op = "--libs" ] || [ $op = "--copy" ]; then
+           echo "-Wl,-Bstatic $pkg -Wl,-Bdynamic"
+       else
diff --git a/package/network/services/dnsmasq/patches/0009-Revert-68f6312d4bae30b78daafcd6f51dc441b8685b1e.patch b/package/network/services/dnsmasq/patches/0009-Revert-68f6312d4bae30b78daafcd6f51dc441b8685b1e.patch
new file mode 100644 (file)
index 0000000..ff9ddd8
--- /dev/null
@@ -0,0 +1,41 @@
+From 122392e0b352507cabb9e982208d35d2e56902e0 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Wed, 31 Oct 2018 22:24:02 +0000
+Subject: [PATCH 09/11] Revert 68f6312d4bae30b78daafcd6f51dc441b8685b1e
+
+The above is intended to increase robustness, but actually does the
+opposite. The problem is that by ignoring SERVFAIL messages and hoping
+for a better answer from another of the servers we've forwarded to,
+we become vulnerable in the case that one or more of the configured
+servers is down or not responding.
+
+Consider the case that a domain is indeed BOGUS, and we've send the
+query to n servers. With 68f6312d4bae30b78daafcd6f51dc441b8685b1e
+we ignore the first n-1 SERVFAIL replies, and only return the
+final n'th answer to the client. Now, if one of the servers we are
+forwarding to is down, then we won't get all n replies, and the
+client will never get an answer! This is a far more likely scenario
+than a temporary SERVFAIL from only one of a set of notionally identical
+servers, so, on the ground of robustness, we have to believe
+any SERVFAIL answers we get, and return them to the client.
+
+The client could be using the same recursive servers we are,
+so it should, in theory, retry on SERVFAIL anyway.
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ src/forward.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -957,8 +957,7 @@ void reply_query(int fd, int family, tim
+      we get a good reply from another server. Kill it when we've
+      had replies from all to avoid filling the forwarding table when
+      everything is broken */
+-  if (forward->forwardall == 0 || --forward->forwardall == 1 ||
+-      (RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
++  if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != REFUSED)
+     {
+       int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
+       
diff --git a/package/network/services/dnsmasq/patches/0010-Remove-the-NO_FORK-compile-time-option-and-support-f.patch b/package/network/services/dnsmasq/patches/0010-Remove-the-NO_FORK-compile-time-option-and-support-f.patch
new file mode 100644 (file)
index 0000000..1110d4c
--- /dev/null
@@ -0,0 +1,199 @@
+From 48d12f14c9c0fc8cf943b52774c3892517dd72d4 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Fri, 2 Nov 2018 21:55:04 +0000
+Subject: [PATCH 10/11] Remove the NO_FORK compile-time option, and support for
+ uclinux.
+
+In an era where everything has an MMU, this looks like
+an anachronism, and it adds to (Ok, multiplies!) the
+combinatorial explosion of compile-time options.
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ CHANGELOG     |  6 ++++++
+ src/config.h  | 21 ++-------------------
+ src/dnsmasq.c | 14 --------------
+ src/option.c  |  4 +---
+ 4 files changed, 9 insertions(+), 36 deletions(-)
+
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -11,6 +11,12 @@ version 2.81
+       This fix passes cache entries back from the TCP child process to
+       the main server process, and fixes the problem.
++      Remove the NO_FORK compile-time option, and support for uclinux.
++      In an era where everything has an MMU, this looks like
++      an anachronism, and it adds to (Ok, multiplies!) the
++      combinatorial explosion of compile-time options. Thanks to
++      Kevin Darbyshire-Bryant for the patch.
++
+ version 2.80
+       Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method
+--- a/src/config.h
++++ b/src/config.h
+@@ -239,27 +239,13 @@ HAVE_SOCKADDR_SA_LEN
+    defined if struct sockaddr has sa_len field (*BSD) 
+ */
+-/* Must precede __linux__ since uClinux defines __linux__ too. */
+-#if defined(__uClinux__)
+-#define HAVE_LINUX_NETWORK
+-#define HAVE_GETOPT_LONG
+-#undef HAVE_SOCKADDR_SA_LEN
+-/* Never use fork() on uClinux. Note that this is subtly different from the
+-   --keep-in-foreground option, since it also  suppresses forking new 
+-   processes for TCP connections and disables the call-a-script on leasechange
+-   system. It's intended for use on MMU-less kernels. */
+-#define NO_FORK
+-
+-#elif defined(__UCLIBC__)
++#if defined(__UCLIBC__)
+ #define HAVE_LINUX_NETWORK
+ #if defined(__UCLIBC_HAS_GNU_GETOPT__) || \
+    ((__UCLIBC_MAJOR__==0) && (__UCLIBC_MINOR__==9) && (__UCLIBC_SUBLEVEL__<21))
+ #    define HAVE_GETOPT_LONG
+ #endif
+ #undef HAVE_SOCKADDR_SA_LEN
+-#if !defined(__ARCH_HAS_MMU__) && !defined(__UCLIBC_HAS_MMU__)
+-#  define NO_FORK
+-#endif
+ #if defined(__UCLIBC_HAS_IPV6__)
+ #  ifndef IPV6_V6ONLY
+ #    define IPV6_V6ONLY 26
+@@ -328,7 +314,7 @@ HAVE_SOCKADDR_SA_LEN
+ #define HAVE_DHCP
+ #endif
+-#if defined(NO_SCRIPT) || defined(NO_FORK)
++#if defined(NO_SCRIPT)
+ #undef HAVE_SCRIPT
+ #undef HAVE_LUASCRIPT
+ #endif
+@@ -372,9 +358,6 @@ static char *compile_opts =
+ #ifdef HAVE_BROKEN_RTC
+ "no-RTC "
+ #endif
+-#ifdef NO_FORK
+-"no-MMU "
+-#endif
+ #ifndef HAVE_DBUS
+ "no-"
+ #endif
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -485,7 +485,6 @@ int main (int argc, char **argv)
+       if (chdir("/") != 0)
+       die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC); 
+-#ifndef NO_FORK      
+       if (!option_bool(OPT_NO_FORK))
+       {
+         pid_t pid;
+@@ -525,7 +524,6 @@ int main (int argc, char **argv)
+         if (pid != 0)
+           _exit(0);
+       }
+-#endif
+             
+       /* write pidfile _after_ forking ! */
+       if (daemon->runfile)
+@@ -1628,12 +1626,10 @@ static int set_dns_listeners(time_t now)
+     }
+   
+-#ifndef NO_FORK
+   if (!option_bool(OPT_DEBUG))
+     for (i = 0; i < MAX_PROCS; i++)
+       if (daemon->tcp_pipes[i] != -1)
+       poll_listen(daemon->tcp_pipes[i], POLLIN);
+-#endif
+   
+   return wait;
+ }
+@@ -1643,9 +1639,7 @@ static void check_dns_listeners(time_t n
+   struct serverfd *serverfdp;
+   struct listener *listener;
+   int i;
+-#ifndef NO_FORK
+   int pipefd[2];
+-#endif
+   
+   for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
+     if (poll_check(serverfdp->fd, POLLIN))
+@@ -1657,7 +1651,6 @@ static void check_dns_listeners(time_t n
+         poll_check(daemon->randomsocks[i].fd, POLLIN))
+       reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
+-#ifndef NO_FORK
+   /* Races. The child process can die before we read all of the data from the
+      pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the 
+      process, and tcp_pipes to -1 and close the FD when we read the last
+@@ -1674,7 +1667,6 @@ static void check_dns_listeners(time_t n
+         close(daemon->tcp_pipes[i]);
+         daemon->tcp_pipes[i] = -1;    
+       }
+-#endif
+       
+   for (listener = daemon->listeners; listener; listener = listener->next)
+     {
+@@ -1768,7 +1760,6 @@ static void check_dns_listeners(time_t n
+             shutdown(confd, SHUT_RDWR);
+             while (retry_send(close(confd)));
+           }
+-#ifndef NO_FORK
+         else if (!option_bool(OPT_DEBUG) && pipe(pipefd) == 0 && (p = fork()) != 0)
+           {
+             close(pipefd[1]); /* parent needs read pipe end. */
+@@ -1791,7 +1782,6 @@ static void check_dns_listeners(time_t n
+             /* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
+             daemon->log_id += TCP_MAX_QUERIES;
+           }
+-#endif
+         else
+           {
+             unsigned char *buff;
+@@ -1811,7 +1801,6 @@ static void check_dns_listeners(time_t n
+                 auth_dns = 0;
+               }
+-#ifndef NO_FORK
+             /* Arrange for SIGALRM after CHILD_LIFETIME seconds to
+                terminate the process. */
+             if (!option_bool(OPT_DEBUG))
+@@ -1820,7 +1809,6 @@ static void check_dns_listeners(time_t n
+                 close(pipefd[0]); /* close read end in child. */
+                 daemon->pipe_to_parent = pipefd[1];
+               }
+-#endif
+             /* start with no upstream connections. */
+             for (s = daemon->servers; s; s = s->next)
+@@ -1846,13 +1834,11 @@ static void check_dns_listeners(time_t n
+                   shutdown(s->tcpfd, SHUT_RDWR);
+                   while (retry_send(close(s->tcpfd)));
+                 }
+-#ifndef NO_FORK                  
+             if (!option_bool(OPT_DEBUG))
+               {
+                 flush_log();
+                 _exit(0);
+               }
+-#endif
+           }
+       }
+     }
+--- a/src/option.c
++++ b/src/option.c
+@@ -1828,9 +1828,7 @@ static int one_opt(int option, char *arg
+       /* Sorry about the gross pre-processor abuse */
+     case '6':             /* --dhcp-script */
+     case LOPT_LUASCRIPT:  /* --dhcp-luascript */
+-#  if defined(NO_FORK)
+-      ret_err(_("cannot run scripts under uClinux"));
+-#  elif !defined(HAVE_SCRIPT)
++#  if !defined(HAVE_SCRIPT)
+       ret_err(_("recompile with HAVE_SCRIPT defined to enable lease-change scripts"));
+ #  else
+       if (option == LOPT_LUASCRIPT)
diff --git a/package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch b/package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch
new file mode 100644 (file)
index 0000000..3ba971f
--- /dev/null
@@ -0,0 +1,1199 @@
+From 59e470381f84f2fdf0640c7bc67827f3f0c64784 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
+Date: Fri, 2 Nov 2018 22:39:39 +0000
+Subject: [PATCH 11/11] Free config file values on parsing errors.
+
+This time I have a little bit more controversal patches. But I think
+still useful. They fixes memory leaks that might occur in some cases.
+Most dnsmasq errors is fatal, so it does not matter. But some are not.
+Some parts are reloaded on SIGHUP signal, so it might leak more than once.
+
+Some example when it changes the failures. Use dhcp-options file with
+this content:
+
+tag:error,vendor:redhat
+option:ntp-server,1.2.3.4.5
+option6:ntp-server,[:::]
+
+Is not fatal and dnsmasq will start. On each reload command, it would
+leak some memory. I validated it using valgrind --leak-check=full
+dnsmasq -d. This patch fixes it. It introduces something that might be
+considered constructor and destructor of selected structures.
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ src/option.c | 533 ++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 352 insertions(+), 181 deletions(-)
+
+--- a/src/option.c
++++ b/src/option.c
+@@ -577,14 +577,15 @@ static void *opt_malloc(size_t size)
+   return ret;
+ }
+-static char *opt_string_alloc(char *cp)
++static char *opt_string_alloc(const char *cp)
+ {
+   char *ret = NULL;
++  size_t len;
+   
+-  if (cp && strlen(cp) != 0)
++  if (cp && (len = strlen(cp)) != 0)
+     {
+-      ret = opt_malloc(strlen(cp)+1);
+-      strcpy(ret, cp); 
++      ret = opt_malloc(len+1);
++      memcpy(ret, cp, len+1); 
+       
+       /* restore hidden metachars */
+       unhide_metas(ret);
+@@ -759,6 +760,8 @@ static void do_usage(void)
+ }
+ #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
++#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
++#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
+ static char *parse_mysockaddr(char *arg, union mysockaddr *addr) 
+ {
+@@ -904,6 +907,8 @@ static struct server *add_rev4(struct in
+       p += sprintf(p, "%d.", (a >> 24) & 0xff);
+       break;
+     default:
++      free(serv->domain);
++      free(serv);
+       return NULL;
+     }
+@@ -958,6 +963,97 @@ static char *set_prefix(char *arg)
+    return arg;
+ }
++static struct dhcp_netid *
++dhcp_netid_create(const char *net, struct dhcp_netid *next)
++{
++  struct dhcp_netid *tt;
++  tt = opt_malloc(sizeof (struct dhcp_netid));
++  tt->net = opt_string_alloc(net);
++  tt->next = next;
++  return tt;
++}
++
++static void dhcp_netid_free(struct dhcp_netid *nid)
++{
++  while (nid)
++    {
++      struct dhcp_netid *tmp = nid;
++      nid = nid->next;
++      free(tmp->net);
++      free(tmp);
++    }
++}
++
++/* Parse one or more tag:s before parameters.
++ * Moves arg to the end of tags. */
++static struct dhcp_netid * dhcp_tags(char **arg)
++{
++  struct dhcp_netid *id = NULL;
++
++  while (is_tag_prefix(*arg))
++    {
++      char *comma = split(*arg);
++      id = dhcp_netid_create((*arg)+4, id);
++      *arg = comma;
++    };
++  if (!*arg)
++    {
++      dhcp_netid_free(id);
++      id = NULL;
++    }
++  return id;
++}
++
++static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
++{
++  while (netid)
++    {
++      struct dhcp_netid_list *tmplist = netid;
++      netid = netid->next;
++      dhcp_netid_free(tmplist->list);
++      free(tmplist);
++    }
++}
++
++static void dhcp_config_free(struct dhcp_config *config)
++{
++  if (config)
++    {
++      struct hwaddr_config *hwaddr = config->hwaddr;
++      while (hwaddr)
++        {
++        struct hwaddr_config *tmp = hwaddr;
++          hwaddr = hwaddr->next;
++        free(tmp);
++        }
++      dhcp_netid_list_free(config->netid);
++      if (config->flags & CONFIG_CLID)
++        free(config->clid);
++      free(config);
++    }
++}
++
++static void dhcp_context_free(struct dhcp_context *ctx)
++{
++  if (ctx)
++    {
++      dhcp_netid_free(ctx->filter);
++      free(ctx->netid.net);
++      free(ctx->template_interface);
++      free(ctx);
++    }
++}
++
++static void dhcp_opt_free(struct dhcp_opt *opt)
++{
++  if (opt->flags & DHOPT_VENDOR)
++    free(opt->u.vendor_class);
++  dhcp_netid_free(opt->netid);
++  free(opt->val);
++  free(opt);
++}
++
++
+ /* This is too insanely large to keep in-line in the switch */
+ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
+ {
+@@ -965,7 +1061,6 @@ static int parse_dhcp_opt(char *errstr,
+   char lenchar = 0, *cp;
+   int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
+   char *comma = NULL;
+-  struct dhcp_netid *np = NULL;
+   u16 opt_len = 0;
+   int is6 = 0;
+   int option_ok = 0;
+@@ -1052,14 +1147,9 @@ static int parse_dhcp_opt(char *errstr,
+       }
+       else
+       {
+-        new->netid = opt_malloc(sizeof (struct dhcp_netid));
+         /* allow optional "net:" or "tag:" for consistency */
+-        if (is_tag_prefix(arg))
+-          new->netid->net = opt_string_alloc(arg+4);
+-        else
+-          new->netid->net = opt_string_alloc(set_prefix(arg));
+-        new->netid->next = np;
+-        np = new->netid;
++        const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
++        new->netid = dhcp_netid_create(name, new->netid);
+       }
+       
+       arg = comma; 
+@@ -1069,7 +1159,7 @@ static int parse_dhcp_opt(char *errstr,
+   if (is6)
+     {
+       if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
+-      ret_err(_("unsupported encapsulation for IPv6 option"));
++      goto_err(_("unsupported encapsulation for IPv6 option"));
+       
+       if (opt_len == 0 &&
+         !(new->flags & DHOPT_RFC3925))
+@@ -1083,7 +1173,7 @@ static int parse_dhcp_opt(char *errstr,
+   
+   /* option may be missing with rfc3925 match */
+   if (!option_ok)
+-    ret_err(_("bad dhcp-option"));
++    goto_err(_("bad dhcp-option"));
+   
+   if (comma)
+     {
+@@ -1151,10 +1241,10 @@ static int parse_dhcp_opt(char *errstr,
+         is_string = is_dec = is_hex = 0;
+         
+         if (!is6 && (!is_addr || dots == 0))
+-          ret_err(_("bad IP address"));
++          goto_err(_("bad IP address"));
+          if (is6 && !is_addr6)
+-           ret_err(_("bad IPv6 address"));
++           goto_err(_("bad IPv6 address"));
+       }
+       /* or names */
+       else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
+@@ -1247,7 +1337,7 @@ static int parse_dhcp_opt(char *errstr,
+             comma = split(cp);
+             slash = split_chr(cp, '/');
+             if (!inet_pton(AF_INET, cp, &in))
+-              ret_err(_("bad IPv4 address"));
++              goto_err(_("bad IPv4 address"));
+             if (!slash)
+               {
+                 memcpy(op, &in, INADDRSZ);
+@@ -1292,8 +1382,8 @@ static int parse_dhcp_opt(char *errstr,
+                 op += IN6ADDRSZ;
+                 continue;
+               }
+-        
+-            ret_err(_("bad IPv6 address"));
++
++            goto_err(_("bad IPv6 address"));
+           } 
+         new->len = op - new->val;
+       }
+@@ -1320,7 +1410,7 @@ static int parse_dhcp_opt(char *errstr,
+                 if (strcmp (arg, ".") != 0)
+                   {
+                     if (!(dom = canonicalise_opt(arg)))
+-                      ret_err(_("bad domain in dhcp-option"));
++                      goto_err(_("bad domain in dhcp-option"));
+                       
+                     domlen = strlen(dom) + 2;
+                   }
+@@ -1414,7 +1504,7 @@ static int parse_dhcp_opt(char *errstr,
+               {
+                 char *dom = canonicalise_opt(arg);
+                 if (!dom)
+-                  ret_err(_("bad domain in dhcp-option"));
++                  goto_err(_("bad domain in dhcp-option"));
+                                 
+                 newp = opt_malloc(len + strlen(dom) + 2);
+                 
+@@ -1452,14 +1542,14 @@ static int parse_dhcp_opt(char *errstr,
+       ((new->len > 255) || 
+       (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
+        (new->len > 250 && (new->flags & DHOPT_RFC3925))))
+-    ret_err(_("dhcp-option too long"));
++    goto_err(_("dhcp-option too long"));
+   
+   if (flags == DHOPT_MATCH)
+     {
+       if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
+         !new->netid ||
+         new->netid->next)
+-      ret_err(_("illegal dhcp-match"));
++      goto_err(_("illegal dhcp-match"));
+        
+       if (is6)
+       {
+@@ -1484,6 +1574,9 @@ static int parse_dhcp_opt(char *errstr,
+     }
+     
+   return 1;
++on_error:
++  dhcp_opt_free(new);
++  return 0;
+ }
+ #endif
+@@ -1498,6 +1591,16 @@ void reset_option_bool(unsigned int opt)
+   option_var(opt) &= ~(option_val(opt));
+ }
++static void server_list_free(struct server *list)
++{
++  while (list)
++    {
++      struct server *tmp = list;
++      list = list->next;
++      free(tmp);
++    }
++}
++
+ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
+ {      
+   int i;
+@@ -1679,13 +1782,13 @@ static int one_opt(int option, char *arg
+             /* has subnet+len */
+             err = parse_mysockaddr(arg, &new->addr);
+             if (err)
+-              ret_err(err);
++              ret_err_free(err, new);
+             if (!atoi_check(end, &new->mask))
+-              ret_err(gen_err);
++              ret_err_free(gen_err, new);
+             new->addr_used = 1;
+           } 
+         else if (!atoi_check(arg, &new->mask))
+-          ret_err(gen_err);
++          ret_err_free(gen_err, new);
+           
+           daemon->add_subnet4 = new;
+@@ -1697,15 +1800,15 @@ static int one_opt(int option, char *arg
+                 /* has subnet+len */
+                   err = parse_mysockaddr(comma, &new->addr);
+                   if (err)
+-                    ret_err(err);
++                    ret_err_free(err, new);
+                   if (!atoi_check(end, &new->mask))
+-                    ret_err(gen_err);
++                    ret_err_free(gen_err, new);
+                   new->addr_used = 1;
+                 }
+               else
+                 {
+                   if (!atoi_check(comma, &new->mask))
+-                    ret_err(gen_err);
++                    ret_err_free(gen_err, new);
+                 }
+           
+             daemon->add_subnet6 = new;
+@@ -1912,7 +2015,10 @@ static int one_opt(int option, char *arg
+                 else if (strcmp(fam, "6") == 0)
+                   new->addr.sa.sa_family = AF_INET6;
+                 else
+-                  ret_err(gen_err);
++                {
++                  free(new->name);
++                  ret_err_free(gen_err, new);
++                }
+               } 
+           }
+         new->next = daemon->authinterface;
+@@ -2077,7 +2183,7 @@ static int one_opt(int option, char *arg
+                     arg = split(netpart);
+                     if (!atoi_check(netpart, &msize))
+-                      ret_err(gen_err);
++                      ret_err_free(gen_err, new);
+                     else if (inet_pton(AF_INET, comma, &new->start))
+                       {
+                         int mask = (1 << (32 - msize)) - 1;
+@@ -2090,18 +2196,18 @@ static int one_opt(int option, char *arg
+                               {
+                                 if (!(new->prefix = canonicalise_opt(arg)) ||
+                                     strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
+-                                  ret_err(_("bad prefix"));
++                                  ret_err_free(_("bad prefix"), new);
+                               }
+                             else if (strcmp(arg, "local") != 0 ||
+                                      (msize != 8 && msize != 16 && msize != 24))
+-                              ret_err(gen_err);
++                              ret_err_free(gen_err, new);
+                             else
+                               {
+                                  /* generate the equivalent of
+                                     local=/xxx.yyy.zzz.in-addr.arpa/ */
+                                 struct server *serv = add_rev4(new->start, msize);
+                                 if (!serv)
+-                                  ret_err(_("bad prefix"));
++                                  ret_err_free(_("bad prefix"), new);
+                                 serv->flags |= SERV_NO_ADDR;
+@@ -2130,17 +2236,17 @@ static int one_opt(int option, char *arg
+                         setaddr6part(&new->end6, addrpart | mask);
+                         
+                         if (msize < 64)
+-                          ret_err(gen_err);
++                          ret_err_free(gen_err, new);
+                         else if (arg)
+                           {
+                             if (option != 's')
+                               {
+                                 if (!(new->prefix = canonicalise_opt(arg)) ||
+                                     strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
+-                                  ret_err(_("bad prefix"));
++                                  ret_err_free(_("bad prefix"), new);
+                               }       
+                             else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
+-                              ret_err(gen_err);
++                              ret_err_free(gen_err, new);
+                             else 
+                               {
+                                 /* generate the equivalent of
+@@ -2159,7 +2265,7 @@ static int one_opt(int option, char *arg
+                           }
+                       }
+                     else
+-                      ret_err(gen_err);
++                      ret_err_free(gen_err, new);
+                   }
+                 else
+                   {
+@@ -2173,7 +2279,7 @@ static int one_opt(int option, char *arg
+                         if (!arg)
+                           new->end.s_addr = new->start.s_addr;
+                         else if (!inet_pton(AF_INET, arg, &new->end))
+-                          ret_err(gen_err);
++                          ret_err_free(gen_err, new);
+                       }
+                     else if (inet_pton(AF_INET6, comma, &new->start6))
+                       {
+@@ -2181,16 +2287,16 @@ static int one_opt(int option, char *arg
+                         if (!arg)
+                           memcpy(&new->end6, &new->start6, IN6ADDRSZ);
+                         else if (!inet_pton(AF_INET6, arg, &new->end6))
+-                          ret_err(gen_err);
++                          ret_err_free(gen_err, new);
+                       }
+                     else 
+-                      ret_err(gen_err);
++                      ret_err_free(gen_err, new);
+                     if (option != 's' && prefstr)
+                       {
+                         if (!(new->prefix = canonicalise_opt(prefstr)) ||
+                             strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
+-                          ret_err(_("bad prefix"));
++                          ret_err_free(_("bad prefix"), new);
+                       }
+                   }
+@@ -2352,7 +2458,7 @@ static int one_opt(int option, char *arg
+ #endif
+         }
+       else
+-        ret_err(gen_err);
++        ret_err_free(gen_err, new);
+       new->used = 0;
+       if (option == 'a')
+@@ -2423,7 +2529,10 @@ static int one_opt(int option, char *arg
+         {
+           newlist->flags |= SERV_LITERAL_ADDRESS;
+           if (!(newlist->flags & SERV_TYPE))
+-            ret_err(gen_err);
++            {
++              server_list_free(newlist);
++              ret_err(gen_err);
++            }
+         }
+       else if (option == LOPT_NO_REBIND)
+         newlist->flags |= SERV_NO_REBIND;
+@@ -2440,7 +2549,10 @@ static int one_opt(int option, char *arg
+         {
+           char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
+           if (err)
+-            ret_err(err);
++            {
++              server_list_free(newlist);
++              ret_err(err);
++            }
+         }
+       
+       serv = newlist;
+@@ -2776,21 +2888,19 @@ static int one_opt(int option, char *arg
+             {
+               if (is_tag_prefix(arg))
+                 {
+-                  struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
+-                  tt->net = opt_string_alloc(arg+4);
+-                  tt->next = new->filter;
+                   /* ignore empty tag */
+-                  if (tt->net)
+-                    new->filter = tt;
++                  if (arg[4])
++                    new->filter = dhcp_netid_create(arg+4, new->filter);
+                 }
+               else
+                 {
+                   if (new->netid.net)
+-                    ret_err(_("only one tag allowed"));
+-                  else if (strstr(arg, "set:") == arg)
+-                    new->netid.net = opt_string_alloc(arg+4);
++                    {
++                      dhcp_context_free(new);
++                      ret_err(_("only one tag allowed"));
++                    }
+                   else
+-                    new->netid.net = opt_string_alloc(arg);
++                    new->netid.net = opt_string_alloc(set_prefix(arg));
+                 }
+               arg = comma;
+             }
+@@ -2806,7 +2916,10 @@ static int one_opt(int option, char *arg
+           break;
+       
+       if (k < 2)
+-        ret_err(_("bad dhcp-range"));
++        {
++          dhcp_context_free(new);
++          ret_err(_("bad dhcp-range"));
++        }
+       
+       if (inet_pton(AF_INET, a[0], &new->start))
+         {
+@@ -2818,7 +2931,10 @@ static int one_opt(int option, char *arg
+           else if (strcmp(a[1], "proxy") == 0)
+             new->flags |= CONTEXT_PROXY;
+           else if (!inet_pton(AF_INET, a[1], &new->end))
+-            ret_err(_("bad dhcp-range"));
++            {
++              dhcp_context_free(new);
++              ret_err(_("bad dhcp-range"));
++            }
+           
+           if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
+             {
+@@ -2833,7 +2949,10 @@ static int one_opt(int option, char *arg
+               new->flags |= CONTEXT_NETMASK;
+               leasepos = 3;
+               if (!is_same_net(new->start, new->end, new->netmask))
+-                ret_err(_("inconsistent DHCP range"));
++                {
++                  dhcp_context_free(new);
++                  ret_err(_("inconsistent DHCP range"));
++                }
+               
+           
+               if (k >= 4 && strchr(a[3], '.') &&  
+@@ -2847,6 +2966,8 @@ static int one_opt(int option, char *arg
+ #ifdef HAVE_DHCP6
+       else if (inet_pton(AF_INET6, a[0], &new->start6))
+         {
++          const char *err = NULL;
++
+           new->flags |= CONTEXT_V6; 
+           new->prefix = 64; /* default */
+           new->end6 = new->start6;
+@@ -2892,19 +3013,24 @@ static int one_opt(int option, char *arg
+                 }
+             }
+           
+-          if (new->prefix != 64)
++          if (new->prefix > 64)
+             {
+               if (new->flags & CONTEXT_RA)
+-                ret_err(_("prefix length must be exactly 64 for RA subnets"));
++                err=(_("prefix length must be exactly 64 for RA subnets"));
+               else if (new->flags & CONTEXT_TEMPLATE)
+-                ret_err(_("prefix length must be exactly 64 for subnet constructors"));
++                err=(_("prefix length must be exactly 64 for subnet constructors"));
+             }
+-
+-          if (new->prefix < 64)
+-            ret_err(_("prefix length must be at least 64"));
++          else if (new->prefix < 64)
++            err=(_("prefix length must be at least 64"));
+           
+-          if (!is_same_net6(&new->start6, &new->end6, new->prefix))
+-            ret_err(_("inconsistent DHCPv6 range"));
++          if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
++            err=(_("inconsistent DHCPv6 range"));
++
++          if (err)
++            {
++              dhcp_context_free(new);
++              ret_err(err);
++            }
+           /* dhcp-range=:: enables DHCP stateless on any interface */
+           if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
+@@ -2915,7 +3041,10 @@ static int one_opt(int option, char *arg
+               struct in6_addr zero;
+               memset(&zero, 0, sizeof(zero));
+               if (!is_same_net6(&zero, &new->start6, new->prefix))
+-                ret_err(_("prefix must be zero with \"constructor:\" argument"));
++                {
++                  dhcp_context_free(new);
++                  ret_err(_("prefix must be zero with \"constructor:\" argument"));
++                }
+             }
+           
+           if (addr6part(&new->start6) > addr6part(&new->end6))
+@@ -2927,12 +3056,18 @@ static int one_opt(int option, char *arg
+         }
+ #endif
+       else
+-        ret_err(_("bad dhcp-range"));
++        {
++          dhcp_context_free(new);
++          ret_err(_("bad dhcp-range"));
++        }
+       
+       if (leasepos < k)
+         {
+           if (leasepos != k-1)
+-            ret_err(_("bad dhcp-range"));
++            {
++              dhcp_context_free(new);
++              ret_err(_("bad dhcp-range"));
++            }
+           
+           if (strcmp(a[leasepos], "infinite") == 0)
+             new->lease_time = 0xffffffff;
+@@ -2971,7 +3106,7 @@ static int one_opt(int option, char *arg
+                       break;
+                   if (*cp || (leasepos+1 < k))
+-                    ret_err(_("bad dhcp-range"));
++                    ret_err_free(_("bad dhcp-range"), new);
+                   
+                   new->lease_time = atoi(a[leasepos]) * fac;
+                   /* Leases of a minute or less confuse
+@@ -2998,6 +3133,7 @@ static int one_opt(int option, char *arg
+       new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
+       new->hwaddr = NULL;
+       new->netid = NULL;
++      new->clid = NULL;
+       if ((a[0] = arg))
+         for (k = 1; k < 7; k++)
+@@ -3028,7 +3164,10 @@ static int one_opt(int option, char *arg
+                       }
+                     if (len == -1)
+-                      ret_err(_("bad hex constant"));
++                      {
++                        dhcp_config_free(new);
++                        ret_err(_("bad hex constant"));
++                      }
+                     else if ((new->clid = opt_malloc(len)))
+                       {
+                         new->flags |= CONFIG_CLID;
+@@ -3040,17 +3179,17 @@ static int one_opt(int option, char *arg
+             /* dhcp-host has strange backwards-compat needs. */
+             else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
+               {
+-                struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
+                 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
+-                newtag->net = opt_malloc(strlen(arg + 4) + 1);
+                 newlist->next = new->netid;
+                 new->netid = newlist;
+-                newlist->list = newtag;
+-                strcpy(newtag->net, arg+4);
+-                unhide_metas(newtag->net);
++                newlist->list = dhcp_netid_create(arg+4, NULL);
+               }
+             else if (strstr(arg, "tag:") == arg)
+-              ret_err(_("cannot match tags in --dhcp-host"));
++              {
++                
++                dhcp_config_free(new);
++                ret_err(_("cannot match tags in --dhcp-host"));
++              }
+ #ifdef HAVE_DHCP6
+             else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
+               {
+@@ -3058,7 +3197,10 @@ static int one_opt(int option, char *arg
+                 arg++;
+                 
+                 if (!inet_pton(AF_INET6, arg, &new->addr6))
+-                  ret_err(_("bad IPv6 address"));
++                  {
++                    dhcp_config_free(new);
++                    ret_err(_("bad IPv6 address"));
++                  }
+                 for (i= 0; i < 8; i++)
+                   if (new->addr6.s6_addr[i] != 0)
+@@ -3076,10 +3218,13 @@ static int one_opt(int option, char *arg
+                 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
+                 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, 
+                                                    &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
+-                  ret_err(_("bad hex constant"));
++                  {
++                    free(newhw);
++                    dhcp_config_free(new);
++                    ret_err(_("bad hex constant"));
++                  }
+                 else
+                   {
+-                    
+                     newhw->next = new->hwaddr;
+                     new->hwaddr = newhw;
+                   }               
+@@ -3156,7 +3301,10 @@ static int one_opt(int option, char *arg
+                   {
+                     if (!(new->hostname = canonicalise_opt(a[j])) ||
+                         !legal_hostname(new->hostname))
+-                      ret_err(_("bad DHCP host name"));
++                      {
++                        dhcp_config_free(new);
++                        ret_err(_("bad DHCP host name"));
++                      }
+                    
+                     new->flags |= CONFIG_NAME;
+                     new->domain = strip_hostname(new->hostname);                      
+@@ -3209,10 +3357,7 @@ static int one_opt(int option, char *arg
+             }
+           else
+             {
+-              struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
+-              newtag->net = opt_malloc(len - 3);
+-              strcpy(newtag->net, arg+4);
+-              unhide_metas(newtag->net);
++              struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
+               if (strstr(arg, "set:") == arg)
+                 {
+@@ -3229,7 +3374,7 @@ static int one_opt(int option, char *arg
+               else 
+                 {
+                   new->set = NULL;
+-                  free(newtag);
++                  dhcp_netid_free(newtag);
+                   break;
+                 }
+             }
+@@ -3238,7 +3383,11 @@ static int one_opt(int option, char *arg
+         }
+       if (!new->set)
+-        ret_err(_("bad tag-if"));
++        {
++          dhcp_netid_free(new->tag);
++          dhcp_netid_list_free(new->set);
++          ret_err_free(_("bad tag-if"), new);
++        }
+         
+       break;
+       }
+@@ -3281,19 +3430,12 @@ static int one_opt(int option, char *arg
+       
+     case 'M': /* --dhcp-boot */
+       {
+-      struct dhcp_netid *id = NULL;
+-      while (is_tag_prefix(arg))
+-        {
+-          struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
+-          newid->next = id;
+-          id = newid;
+-          comma = split(arg);
+-          newid->net = opt_string_alloc(arg+4);
+-          arg = comma;
+-        };
++      struct dhcp_netid *id = dhcp_tags(&arg);
+       
+-      if (!arg)
+-        ret_err(gen_err);
++      if (!id)
++        {
++          ret_err(gen_err);
++        }
+       else 
+         {
+           char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
+@@ -3339,19 +3481,12 @@ static int one_opt(int option, char *arg
+     case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
+       {
+-      struct dhcp_netid *id = NULL;
+-      while (is_tag_prefix(arg))
+-        {
+-          struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
+-          newid->next = id;
+-          id = newid;
+-          comma = split(arg);
+-          newid->net = opt_string_alloc(arg+4);
+-          arg = comma;
+-        };
++      struct dhcp_netid *id = dhcp_tags(&arg);
+       
+-      if (!arg)
+-        ret_err(gen_err);
++      if (!id)
++        {
++          ret_err(gen_err);
++        }
+       else
+         {
+           struct delay_config *new;
+@@ -3376,19 +3511,13 @@ static int one_opt(int option, char *arg
+        
+        new->netid = NULL;
+        new->opt = 10; /* PXE_MENU_PROMPT */
+-
+-       while (is_tag_prefix(arg))
+-        {
+-           struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
+-           comma = split(arg);
+-           nn->next = new->netid;
+-           new->netid = nn;
+-           nn->net = opt_string_alloc(arg+4);
+-           arg = comma;
+-         }
++       new->netid = dhcp_tags(&arg);
+        
+-       if (!arg)
+-         ret_err(gen_err);
++       if (!new->netid)
++         {
++           dhcp_opt_free(new);
++           ret_err(gen_err);
++         }
+        else
+          {
+            comma = split(arg);
+@@ -3424,17 +3553,8 @@ static int one_opt(int option, char *arg
+        new->netid = NULL;
+        new->sname = NULL;
+        new->server.s_addr = 0;
++       new->netid = dhcp_tags(&arg);
+-       while (is_tag_prefix(arg))
+-         {
+-           struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
+-           comma = split(arg);
+-           nn->next = new->netid;
+-           new->netid = nn;
+-           nn->net = opt_string_alloc(arg+4);
+-           arg = comma;
+-         }
+-       
+        if (arg && (comma = split(arg)))
+          {
+            for (i = 0; CSA[i]; i++)
+@@ -3511,7 +3631,10 @@ static int one_opt(int option, char *arg
+           unhide_metas(comma);
+           new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
+           if (new->hwaddr_len == -1)
+-            ret_err(gen_err);
++            {
++              free(new->netid.net);
++              ret_err_free(gen_err, new);
++            }
+           else
+             {
+               new->next = daemon->dhcp_macs;
+@@ -3528,7 +3651,7 @@ static int one_opt(int option, char *arg
+       
+       if (!(comma = split(arg)) ||
+           !atoi_check16(comma, &new->class))
+-        ret_err(gen_err);
++        ret_err_free(gen_err, new);
+       
+       new->tag.net = opt_string_alloc(set_prefix(arg));
+       new->next = daemon->prefix_classes;
+@@ -3550,7 +3673,7 @@ static int one_opt(int option, char *arg
+        struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
+        
+        if (!(comma = split(arg)))
+-         ret_err(gen_err);
++         ret_err_free(gen_err, new);
+       
+        new->netid.net = opt_string_alloc(set_prefix(arg));
+        /* check for hex string - must digits may include : must not have nothing else, 
+@@ -3560,7 +3683,10 @@ static int one_opt(int option, char *arg
+        if ((comma = split(arg)))
+          {
+            if (option  != 'U' || strstr(arg, "enterprise:") != arg)
+-             ret_err(gen_err);
++             {
++               free(new->netid.net);
++               ret_err_free(gen_err, new);
++             }
+            else
+              new->enterprise = atoi(arg+11);
+          }
+@@ -3662,14 +3788,8 @@ static int one_opt(int option, char *arg
+         }
+       
+       while (arg) {
+-        struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
+         comma = split(arg);
+-        member->next = list;
+-        list = member;
+-        if (is_tag_prefix(arg))
+-          member->net = opt_string_alloc(arg+4);
+-        else
+-          member->net = opt_string_alloc(arg);
++        list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
+         arg = comma;
+       }
+       
+@@ -3683,7 +3803,7 @@ static int one_opt(int option, char *arg
+       struct addr_list *new = opt_malloc(sizeof(struct addr_list));
+       comma = split(arg);
+       if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
+-        ret_err(_("bad dhcp-proxy address"));
++        ret_err_free(_("bad dhcp-proxy address"), new);
+       new->next = daemon->override_relays;
+       daemon->override_relays = new;
+       arg = comma;
+@@ -3709,7 +3829,10 @@ static int one_opt(int option, char *arg
+         }
+ #endif
+       else
+-        ret_err(_("Bad dhcp-relay"));
++        {
++          free(new->interface);
++          ret_err_free(_("Bad dhcp-relay"), new);
++        }
+       
+       break;
+       }
+@@ -3749,8 +3872,11 @@ static int one_opt(int option, char *arg
+          arg = split(comma);
+          if (!atoi_check(comma, &new->interval) || 
+             (arg && !atoi_check(arg, &new->lifetime)))
++             {
+ err:
+-          ret_err(_("bad RA-params"));
++             free(new->name);
++             ret_err_free(_("bad RA-params"), new);
++             }
+         
+         new->next = daemon->ra_interfaces;
+         daemon->ra_interfaces = new;
+@@ -3799,7 +3925,7 @@ err:
+           (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
+            !is_same_net(new->in, new->end, new->mask) ||
+            ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
+-        ret_err(_("invalid alias range"));
++        ret_err_free(_("invalid alias range"), new);
+       
+       break;
+       }
+@@ -3832,7 +3958,7 @@ err:
+           else if (strcmp(arg, "6") == 0)
+             new->family = AF_INET6;
+           else
+-            ret_err(gen_err);
++            ret_err_free(gen_err, new);
+         } 
+       new->intr = opt_string_alloc(comma);
+       break;
+@@ -3864,11 +3990,19 @@ err:
+           alias = canonicalise_opt(arg);
+           if (!alias || !target)
+-            ret_err(_("bad CNAME"));
++            {
++              free(target);
++              free(alias);
++              ret_err(_("bad CNAME"));
++            }
+           
+           for (new = daemon->cnames; new; new = new->next)
+             if (hostname_isequal(new->alias, alias))
+-              ret_err(_("duplicate CNAME"));
++              {
++                free(target);
++                free(alias);
++                ret_err(_("duplicate CNAME"));
++              }
+           new = opt_malloc(sizeof(struct cname));
+           new->next = daemon->cnames;
+           daemon->cnames = new;
+@@ -3891,7 +4025,11 @@ err:
+       
+       if (!(dom = canonicalise_opt(arg)) ||
+           (comma && !(target = canonicalise_opt(comma))))
+-        ret_err(_("bad PTR record"));
++        {
++          free(dom);
++          free(target);
++          ret_err(_("bad PTR record"));
++        }
+       else
+         {
+           new = opt_malloc(sizeof(struct ptr_record));
+@@ -3909,7 +4047,7 @@ err:
+       int k = 0;
+       struct naptr *new;
+       int order, pref;
+-      char *name, *replace = NULL;
++      char *name=NULL, *replace = NULL;
+       if ((a[0] = arg))
+         for (k = 1; k < 7; k++)
+@@ -3922,7 +4060,11 @@ err:
+           !atoi_check16(a[1], &order) || 
+           !atoi_check16(a[2], &pref) ||
+           (k == 7 && !(replace = canonicalise_opt(a[6]))))
+-        ret_err(_("bad NAPTR record"));
++          {
++          free(name);
++          free(replace);
++          ret_err(_("bad NAPTR record"));
++          }
+       else
+         {
+           new = opt_malloc(sizeof(struct naptr));
+@@ -3944,22 +4086,26 @@ err:
+               struct txt_record *new;
+       size_t len = 0;
+       char *data;
+-      int val;
++      int class;
+       comma = split(arg);
+       data = split(comma);
+               
+       new = opt_malloc(sizeof(struct txt_record));
+-      new->next = daemon->rr;
+-      daemon->rr = new;
++      new->name = NULL;
+       
+-      if (!atoi_check(comma, &val) || 
++      if (!atoi_check(comma, &class) || 
+           !(new->name = canonicalise_opt(arg)) ||
+           (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
+-        ret_err(_("bad RR record"));
+-              
+-      new->class = val;
++          {
++            free(new->name);
++          ret_err_free(_("bad RR record"), new);
++          }
++
+       new->len = 0;
++      new->class = class;
++      new->next = daemon->rr;
++      daemon->rr = new;
+       
+       if (data)
+         {
+@@ -4011,14 +4157,14 @@ err:
+       comma = split(arg);
+               
+       new = opt_malloc(sizeof(struct txt_record));
+-      new->next = daemon->txt;
+-      daemon->txt = new;
+       new->class = C_IN;
+       new->stat = 0;
+       if (!(new->name = canonicalise_opt(arg)))
+-        ret_err(_("bad TXT record"));
++        ret_err_free(_("bad TXT record"), new);
+       
++      new->next = daemon->txt;
++      daemon->txt = new;
+       len = comma ? strlen(comma) : 0;
+       len += (len/255) + 1; /* room for extra counts */
+       new->txt = p = opt_malloc(len);
+@@ -4065,24 +4211,32 @@ err:
+           arg = comma;
+           comma = split(arg);
+           if (!(target = canonicalise_opt(arg)))
+-            ret_err(_("bad SRV target"));
++            ret_err_free(_("bad SRV target"), name);
+               
+           if (comma)
+             {
+               arg = comma;
+               comma = split(arg);
+               if (!atoi_check16(arg, &port))
+-                ret_err(_("invalid port number"));
++                  {
++                    free(name);
++                  ret_err_free(_("invalid port number"), target);
++                  }
+               
+               if (comma)
+                 {
+                   arg = comma;
+                   comma = split(arg);
+                   if (!atoi_check16(arg, &priority))
+-                    ret_err(_("invalid priority"));
+-                      
++                      {
++                        free(name);
++                      ret_err_free(_("invalid priority"), target);
++                    }
+                   if (comma && !atoi_check16(comma, &weight))
+-                    ret_err(_("invalid weight"));
++                      {
++                        free(name);
++                      ret_err_free(_("invalid weight"), target);
++                      }
+                 }
+             }
+         }
+@@ -4101,13 +4255,15 @@ err:
+       
+     case LOPT_HOST_REC: /* --host-record */
+       {
+-      struct host_record *new = opt_malloc(sizeof(struct host_record));
+-      memset(new, 0, sizeof(struct host_record));
+-      new->ttl = -1;
++      struct host_record *new;
+       if (!arg || !(comma = split(arg)))
+         ret_err(_("Bad host-record"));
+       
++      new = opt_malloc(sizeof(struct host_record));
++      memset(new, 0, sizeof(struct host_record));
++      new->ttl = -1;
++
+       while (arg)
+         {
+           struct all_addr addr;
+@@ -4126,10 +4282,19 @@ err:
+             {
+               int nomem;
+               char *canon = canonicalise(arg, &nomem);
+-              struct name_list *nl = opt_malloc(sizeof(struct name_list));
++              struct name_list *nl;
+               if (!canon)
+-                ret_err(_("Bad name in host-record"));
++                  {
++                  struct name_list *tmp = new->names, *next;
++                  for (tmp = new->names; tmp; tmp = next)
++                    {
++                      next = tmp->next;
++                      free(tmp);
++                    }
++                  ret_err_free(_("Bad name in host-record"), new);
++                  }
++              nl = opt_malloc(sizeof(struct name_list));
+               nl->name = canon;
+               /* keep order, so that PTR record goes to first name */
+               nl->next = NULL;
+@@ -4179,6 +4344,7 @@ err:
+       int len;
+       
+       new->class = C_IN;
++      new->name = NULL;
+       if ((comma = split(arg)) && (algo = split(comma)))
+         {
+@@ -4203,7 +4369,7 @@ err:
+           !atoi_check8(algo, &new->algo) ||
+           !atoi_check8(digest, &new->digest_type) ||
+           !(new->name = canonicalise_opt(arg)))
+-        ret_err(_("bad trust anchor"));
++        ret_err_free(_("bad trust anchor"), new);
+           
+       /* Upper bound on length */
+       len = (2*strlen(keyhex))+1;
+@@ -4217,7 +4383,10 @@ err:
+         else
+           cp++;
+       if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
+-        ret_err(_("bad HEX in trust anchor"));
++        {
++          free(new->name);
++          ret_err_free(_("bad HEX in trust anchor"), new);
++        }
+       
+       new->next = daemon->ds;
+       daemon->ds = new;
+@@ -4686,8 +4855,8 @@ void read_opts(int argc, char **argv, ch
+   size_t argbuf_size = MAXDNAME;
+   char *argbuf = opt_malloc(argbuf_size);
+   char *buff = opt_malloc(MAXDNAME);
+-  int option, conffile_opt = '7', testmode = 0;
+-  char *arg, *conffile = CONFFILE;
++  int option, testmode = 0;
++  char *arg, *conffile = NULL;
+       
+   opterr = 0;
+@@ -4796,7 +4965,8 @@ void read_opts(int argc, char **argv, ch
+         }
+       else if (option == 'C')
+       {
+-        conffile_opt = 0; /* file must exist */
++          if (conffile)
++            free(conffile);
+         conffile = opt_string_alloc(arg);
+       }
+       else
+@@ -4814,10 +4984,11 @@ void read_opts(int argc, char **argv, ch
+   if (conffile)
+     {
+-      one_file(conffile, conffile_opt);
+-      if (conffile_opt == 0)
+-      free(conffile);
++      one_file(conffile, 0);
++      free(conffile);
+     }
++  else
++    one_file(CONFFILE, '7');
+   /* port might not be known when the address is parsed - fill in here */
+   if (daemon->servers)
diff --git a/package/network/services/dnsmasq/patches/0012-option-fix-non-DHCPv6-build-error.patch b/package/network/services/dnsmasq/patches/0012-option-fix-non-DHCPv6-build-error.patch
new file mode 100644 (file)
index 0000000..983cb14
--- /dev/null
@@ -0,0 +1,27 @@
+From 061eb8599636bb360e0b7fa5986935b86db39497 Mon Sep 17 00:00:00 2001
+From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+Date: Mon, 10 Dec 2018 10:07:33 +0000
+Subject: [PATCH] option: fix non DHCPv6 build error
+
+option.c: In function 'dhcp_context_free':
+option.c:1042:15: error: 'struct dhcp_context' has no member named 'template_interface'
+       free(ctx->template_interface);
+               ^~
+
+Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+---
+ src/option.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/src/option.c
++++ b/src/option.c
+@@ -1039,7 +1039,9 @@ static void dhcp_context_free(struct dhc
+     {
+       dhcp_netid_free(ctx->filter);
+       free(ctx->netid.net);
++#ifdef HAVE_DHCP6
+       free(ctx->template_interface);
++#endif
+       free(ctx);
+     }
+ }
index 88e334b0fc91336d12dca11390fe78feeb239da0..f2681e399394fd17ae02c22154682e582d0d5359 100644 (file)
@@ -44,7 +44,7 @@
        (buffer = safe_malloc(BUFF_SZ)) &&
        (ipset_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER)) != -1 &&
        (bind(ipset_sock, (struct sockaddr *)&snl, sizeof(snl)) != -1))
-@@ -217,17 +198,10 @@ int add_to_ipset(const char *setname, co
+@@ -211,16 +192,9 @@ int add_to_ipset(const char *setname, co
    if (flags & F_IPV6)
      {
        af = AF_INET6;
@@ -55,7 +55,6 @@
 -        ret = -1;
 -      }
      }
- #endif
    
 -  if (ret != -1) 
 -    ret = old_kernel ? old_add_to_ipset(setname, ipaddr, remove) : new_add_to_ipset(setname, ipaddr, af, remove);