aboutsummaryrefslogtreecommitdiffstats
path: root/package
diff options
context:
space:
mode:
Diffstat (limited to 'package')
-rw-r--r--package/network/services/dnsmasq/Makefile2
-rw-r--r--package/network/services/dnsmasq/patches/0001-Impove-cache-behaviour-for-TCP-connections.patch495
-rw-r--r--package/network/services/dnsmasq/patches/0002-Ensure-that-AD-bit-is-reset-on-answers-from-address-.patch26
-rw-r--r--package/network/services/dnsmasq/patches/0003-Remove-ability-to-compile-without-IPv6-support.patch2192
-rw-r--r--package/network/services/dnsmasq/patches/0004-Don-t-forward-.bind-.server-queries-upstream.patch52
-rw-r--r--package/network/services/dnsmasq/patches/0005-Fix-logging-in-cf5984367bc6a949e3803a576512c5a7bc48e.patch63
-rw-r--r--package/network/services/dnsmasq/patches/0006-Fix-spurious-AD-flags-in-some-DNS-replies-from-local.patch120
-rw-r--r--package/network/services/dnsmasq/patches/0007-Do-not-rely-on-dead-code-elimination-use-array-inste.patch71
-rw-r--r--package/network/services/dnsmasq/patches/0008-Fix-Makefile-lines-generating-UBUS-linker-config.patch63
-rw-r--r--package/network/services/dnsmasq/patches/0009-Revert-68f6312d4bae30b78daafcd6f51dc441b8685b1e.patch41
-rw-r--r--package/network/services/dnsmasq/patches/0010-Remove-the-NO_FORK-compile-time-option-and-support-f.patch199
-rw-r--r--package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch1199
-rw-r--r--package/network/services/dnsmasq/patches/0012-option-fix-non-DHCPv6-build-error.patch27
-rw-r--r--package/network/services/dnsmasq/patches/110-ipset-remove-old-kernel-support.patch3
14 files changed, 4550 insertions, 3 deletions
diff --git a/package/network/services/dnsmasq/Makefile b/package/network/services/dnsmasq/Makefile
index 10537d711f..85c43505e0 100644
--- a/package/network/services/dnsmasq/Makefile
+++ b/package/network/services/dnsmasq/Makefile
@@ -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
index 0000000000..453c2924b2
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0001-Impove-cache-behaviour-for-TCP-connections.patch
@@ -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
index 0000000000..e3178b8dfd
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0002-Ensure-that-AD-bit-is-reset-on-answers-from-address-.patch
@@ -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
index 0000000000..be3c4116b6
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0003-Remove-ability-to-compile-without-IPv6-support.patch
@@ -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
index 0000000000..497b4c3df2
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0004-Don-t-forward-.bind-.server-queries-upstream.patch
@@ -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
index 0000000000..b9be306118
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0005-Fix-logging-in-cf5984367bc6a949e3803a576512c5a7bc48e.patch
@@ -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
index 0000000000..3271a5f3af
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0006-Fix-spurious-AD-flags-in-some-DNS-replies-from-local.patch
@@ -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
index 0000000000..55ea917c39
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0007-Do-not-rely-on-dead-code-elimination-use-array-inste.patch
@@ -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
index 0000000000..91b465fd1c
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0008-Fix-Makefile-lines-generating-UBUS-linker-config.patch
@@ -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
index 0000000000..ff9ddd842c
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0009-Revert-68f6312d4bae30b78daafcd6f51dc441b8685b1e.patch
@@ -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
index 0000000000..1110d4c5fa
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0010-Remove-the-NO_FORK-compile-time-option-and-support-f.patch
@@ -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
index 0000000000..3ba971fe72
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch
@@ -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
index 0000000000..983cb1402e
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0012-option-fix-non-DHCPv6-build-error.patch
@@ -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);
+ }
+ }
diff --git a/package/network/services/dnsmasq/patches/110-ipset-remove-old-kernel-support.patch b/package/network/services/dnsmasq/patches/110-ipset-remove-old-kernel-support.patch
index 88e334b0fc..f2681e3993 100644
--- a/package/network/services/dnsmasq/patches/110-ipset-remove-old-kernel-support.patch
+++ b/package/network/services/dnsmasq/patches/110-ipset-remove-old-kernel-support.patch
@@ -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);