dnsmasq: Import latest git version of dnsmasq
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 4 Mar 2015 22:27:27 +0000 (23:27 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 4 Mar 2015 22:27:27 +0000 (23:27 +0100)
56 files changed:
lfs/dnsmasq
src/patches/dnsmasq/0001-Add-newline-at-the-end-of-example-config-file.patch [new file with mode: 0644]
src/patches/dnsmasq/0002-crash-at-startup-when-an-empty-suffix-is-supplied-to.patch [new file with mode: 0644]
src/patches/dnsmasq/0003-Debian-build-fixes-for-kFreeBSD.patch [new file with mode: 0644]
src/patches/dnsmasq/0004-Set-conntrack-mark-before-connect-call.patch [new file with mode: 0644]
src/patches/dnsmasq/0005-Fix-typo-in-new-Dbus-code.patch [new file with mode: 0644]
src/patches/dnsmasq/0006-Fit-example-conf-file-typo.patch [new file with mode: 0644]
src/patches/dnsmasq/0007-Improve-RFC-compliance-when-unable-to-supply-address.patch [new file with mode: 0644]
src/patches/dnsmasq/0008-Fix-conntrack-with-bind-interfaces.patch [new file with mode: 0644]
src/patches/dnsmasq/0009-Use-inotify-instead-of-polling-on-Linux.patch [new file with mode: 0644]
src/patches/dnsmasq/0010-Teach-the-new-inotify-code-about-symlinks.patch [new file with mode: 0644]
src/patches/dnsmasq/0011-Remove-floor-on-EDNS0-packet-size-with-DNSSEC.patch [new file with mode: 0644]
src/patches/dnsmasq/0012-CHANGELOG-re.-inotify.patch [new file with mode: 0644]
src/patches/dnsmasq/0013-Fix-breakage-of-domain-domain-subnet-local.patch [new file with mode: 0644]
src/patches/dnsmasq/0014-Remove-redundant-IN6_IS_ADDR_ULA-a-macro-defn.patch [new file with mode: 0644]
src/patches/dnsmasq/0015-Eliminate-IPv6-privacy-addresses-from-interface-name.patch [new file with mode: 0644]
src/patches/dnsmasq/0016-Tweak-field-width-in-cache-dump-to-avoid-truncating-.patch [new file with mode: 0644]
src/patches/dnsmasq/0017-Fix-crash-in-DNSSEC-code-when-attempting-to-verify-l.patch [new file with mode: 0644]
src/patches/dnsmasq/0018-Make-caching-work-for-CNAMEs-pointing-to-A-AAAA-reco.patch [new file with mode: 0644]
src/patches/dnsmasq/0019-Fix-problems-validating-NSEC3-and-wildcards.patch [new file with mode: 0644]
src/patches/dnsmasq/0020-Initialise-return-value.patch [new file with mode: 0644]
src/patches/dnsmasq/0021-Add-ignore-address-option.patch [new file with mode: 0644]
src/patches/dnsmasq/0022-Bad-packet-protection.patch [new file with mode: 0644]
src/patches/dnsmasq/0023-Fix-build-failure-in-new-inotify-code-on-BSD.patch [new file with mode: 0644]
src/patches/dnsmasq/0024-Implement-makefile-dependencies-on-COPTS-variable.patch [new file with mode: 0644]
src/patches/dnsmasq/0025-Fix-race-condition-issue-in-makefile.patch [new file with mode: 0644]
src/patches/dnsmasq/0026-DNSSEC-do-top-down-search-for-limit-of-secure-delega.patch [new file with mode: 0644]
src/patches/dnsmasq/0027-Add-log-queries-extra-option-for-more-complete-loggi.patch [new file with mode: 0644]
src/patches/dnsmasq/0028-Add-min-cache-ttl-option.patch [new file with mode: 0644]
src/patches/dnsmasq/0029-Log-port-of-requestor-when-doing-extra-logging.patch [new file with mode: 0644]
src/patches/dnsmasq/0030-Don-t-answer-from-cache-RRsets-from-wildcards-as-we-.patch [new file with mode: 0644]
src/patches/dnsmasq/0031-Logs-for-DS-records-consistent.patch [new file with mode: 0644]
src/patches/dnsmasq/0032-Cope-with-multiple-interfaces-with-the-same-LL-addre.patch [new file with mode: 0644]
src/patches/dnsmasq/0033-Don-t-treat-SERVFAIL-as-a-recoverable-error.patch [new file with mode: 0644]
src/patches/dnsmasq/0034-Add-dhcp-hostsdir-config-option.patch [new file with mode: 0644]
src/patches/dnsmasq/0035-Update-German-translation.patch [new file with mode: 0644]
src/patches/dnsmasq/0036-Don-t-reply-to-DHCPv6-SOLICIT-messages-when-not-conf.patch [new file with mode: 0644]
src/patches/dnsmasq/0037-Allow-inotify-to-be-disabled-at-compile-time-on-Linu.patch [new file with mode: 0644]
src/patches/dnsmasq/0038-Expand-inotify-code-to-dhcp-hostsdir-dhcp-optsdir-an.patch [new file with mode: 0644]
src/patches/dnsmasq/0039-Update-copyrights-for-dawn-of-2015.patch [new file with mode: 0644]
src/patches/dnsmasq/0040-inotify-documentation-updates.patch [new file with mode: 0644]
src/patches/dnsmasq/0041-Fix-broken-ECDSA-DNSSEC-signatures.patch [new file with mode: 0644]
src/patches/dnsmasq/0042-BSD-make-support.patch [new file with mode: 0644]
src/patches/dnsmasq/0043-Fix-build-failure-on-openBSD.patch [new file with mode: 0644]
src/patches/dnsmasq/0044-Manpage-typo-fix.patch [new file with mode: 0644]
src/patches/dnsmasq/0045-Fixup-dhcp-configs-after-reading-extra-hostfiles-wit.patch [new file with mode: 0644]
src/patches/dnsmasq/0046-Extra-logging-for-inotify-code.patch [new file with mode: 0644]
src/patches/dnsmasq/0047-man-page-typo.patch [new file with mode: 0644]
src/patches/dnsmasq/0048-Fix-get-version-script-which-returned-wrong-tag-in-s.patch [new file with mode: 0644]
src/patches/dnsmasq/0049-Typos.patch [new file with mode: 0644]
src/patches/dnsmasq/0050-Make-dynamic-hosts-files-work-when-no-hosts-set.patch [new file with mode: 0644]
src/patches/dnsmasq/0051-Fix-trivial-memory-leaks-to-quieten-valgrind.patch [new file with mode: 0644]
src/patches/dnsmasq/0052-Fix-uninitialized-value-used-in-get_client_mac.patch [new file with mode: 0644]
src/patches/dnsmasq/0053-Log-parsing-utils-in-contrib-reverse-dns.patch [new file with mode: 0644]
src/patches/dnsmasq/0054-Add-dnssec-timestamp-option-and-facility.patch [new file with mode: 0644]
src/patches/dnsmasq/0055-Fix-last-commit-to-not-crash-if-uid-changing-not-con.patch [new file with mode: 0644]

index d4eb9d4..c256f75 100644 (file)
 
 include Config
 
-VER        = 1062667
+VER        = 2.72
 
 THISAPP    = dnsmasq-$(VER)
-DL_FILE    = $(THISAPP)-20150201.tar.gz
+DL_FILE    = $(THISAPP).tar.xz
 DL_FROM    = $(URL_IPFIRE)
 DIR_APP    = $(DIR_SRC)/$(THISAPP)
 TARGET     = $(DIR_INFO)/$(THISAPP)
@@ -43,7 +43,7 @@ objects = $(DL_FILE)
 
 $(DL_FILE) = $(DL_FROM)/$(DL_FILE)
 
-$(DL_FILE)_MD5 = ee58d033a892faa69b099ed598f500c2
+$(DL_FILE)_MD5 = 0256e0a71e27c8d8a5c89a0d18f3cfe2
 
 install : $(TARGET)
 
@@ -73,6 +73,61 @@ $(subst %,%_MD5,$(objects)) :
 $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
        @$(PREBUILD)
        @rm -rf $(DIR_APP) && cd $(DIR_SRC) && tar axf $(DIR_DL)/$(DL_FILE)
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0001-Add-newline-at-the-end-of-example-config-file.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0002-crash-at-startup-when-an-empty-suffix-is-supplied-to.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0003-Debian-build-fixes-for-kFreeBSD.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0004-Set-conntrack-mark-before-connect-call.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0005-Fix-typo-in-new-Dbus-code.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0006-Fit-example-conf-file-typo.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0007-Improve-RFC-compliance-when-unable-to-supply-address.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0008-Fix-conntrack-with-bind-interfaces.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0009-Use-inotify-instead-of-polling-on-Linux.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0010-Teach-the-new-inotify-code-about-symlinks.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0011-Remove-floor-on-EDNS0-packet-size-with-DNSSEC.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0012-CHANGELOG-re.-inotify.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0013-Fix-breakage-of-domain-domain-subnet-local.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0014-Remove-redundant-IN6_IS_ADDR_ULA-a-macro-defn.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0015-Eliminate-IPv6-privacy-addresses-from-interface-name.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0016-Tweak-field-width-in-cache-dump-to-avoid-truncating-.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0017-Fix-crash-in-DNSSEC-code-when-attempting-to-verify-l.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0018-Make-caching-work-for-CNAMEs-pointing-to-A-AAAA-reco.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0019-Fix-problems-validating-NSEC3-and-wildcards.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0020-Initialise-return-value.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0021-Add-ignore-address-option.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0022-Bad-packet-protection.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0023-Fix-build-failure-in-new-inotify-code-on-BSD.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0024-Implement-makefile-dependencies-on-COPTS-variable.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0025-Fix-race-condition-issue-in-makefile.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0026-DNSSEC-do-top-down-search-for-limit-of-secure-delega.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0027-Add-log-queries-extra-option-for-more-complete-loggi.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0028-Add-min-cache-ttl-option.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0029-Log-port-of-requestor-when-doing-extra-logging.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0030-Don-t-answer-from-cache-RRsets-from-wildcards-as-we-.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0031-Logs-for-DS-records-consistent.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0032-Cope-with-multiple-interfaces-with-the-same-LL-addre.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0033-Don-t-treat-SERVFAIL-as-a-recoverable-error.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0034-Add-dhcp-hostsdir-config-option.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0035-Update-German-translation.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0036-Don-t-reply-to-DHCPv6-SOLICIT-messages-when-not-conf.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0037-Allow-inotify-to-be-disabled-at-compile-time-on-Linu.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0038-Expand-inotify-code-to-dhcp-hostsdir-dhcp-optsdir-an.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0039-Update-copyrights-for-dawn-of-2015.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0040-inotify-documentation-updates.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0041-Fix-broken-ECDSA-DNSSEC-signatures.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0042-BSD-make-support.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0043-Fix-build-failure-on-openBSD.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0044-Manpage-typo-fix.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0045-Fixup-dhcp-configs-after-reading-extra-hostfiles-wit.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0046-Extra-logging-for-inotify-code.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0047-man-page-typo.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0048-Fix-get-version-script-which-returned-wrong-tag-in-s.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0049-Typos.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0050-Make-dynamic-hosts-files-work-when-no-hosts-set.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0051-Fix-trivial-memory-leaks-to-quieten-valgrind.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0052-Fix-uninitialized-value-used-in-get_client_mac.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0053-Log-parsing-utils-in-contrib-reverse-dns.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0054-Add-dnssec-timestamp-option-and-facility.patch
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/0055-Fix-last-commit-to-not-crash-if-uid-changing-not-con.patch
        cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch
        cd $(DIR_APP) && sed -i src/config.h \
                -e 's|/\* #define HAVE_IDN \*/|#define HAVE_IDN|g' \
diff --git a/src/patches/dnsmasq/0001-Add-newline-at-the-end-of-example-config-file.patch b/src/patches/dnsmasq/0001-Add-newline-at-the-end-of-example-config-file.patch
new file mode 100644 (file)
index 0000000..adcb44f
--- /dev/null
@@ -0,0 +1,23 @@
+From f2658275b25ebfe691cdcb9fede85a3088cca168 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Thu, 25 Sep 2014 21:51:25 +0100
+Subject: [PATCH 01/55] Add newline at the end of example config file.
+
+---
+ dnsmasq.conf.example | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dnsmasq.conf.example b/dnsmasq.conf.example
+index 83924fc4a9b4..6b00bd34fbb5 100644
+--- a/dnsmasq.conf.example
++++ b/dnsmasq.conf.example
+@@ -645,4 +645,4 @@
+ #conf-dir=/etc/dnsmasq.d,.bak
+ # Include all files in a directory which end in .conf
+-#conf-dir=/etc/dnsmasq.d/*.conf
+\ No newline at end of file
++#conf-dir=/etc/dnsmasq.d/*.conf
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0002-crash-at-startup-when-an-empty-suffix-is-supplied-to.patch b/src/patches/dnsmasq/0002-crash-at-startup-when-an-empty-suffix-is-supplied-to.patch
new file mode 100644 (file)
index 0000000..b84440b
--- /dev/null
@@ -0,0 +1,86 @@
+From 00cd9d551998307225312fd21f761cfa8868bd2c Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Thu, 2 Oct 2014 21:44:21 +0100
+Subject: [PATCH 02/55] crash at startup when an empty suffix is supplied to
+ --conf-dir
+
+---
+ CHANGELOG    |  6 ++++++
+ src/option.c | 38 +++++++++++++++++++++++---------------
+ 2 files changed, 29 insertions(+), 15 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 768e2aaca42a..13ab41c05fc3 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -1,3 +1,9 @@
++version 2.73
++            Fix crash at startup when an empty suffix is supplied to
++          --conf-dir, also trivial memory leak. Thanks to 
++          Tomas Hozza for spotting this.
++      
++
+ version 2.72
+             Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
+diff --git a/src/option.c b/src/option.c
+index 45d8875fb7f9..b08e98e16f84 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -1474,22 +1474,25 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+       for (arg = comma; arg; arg = comma) 
+         {
+           comma = split(arg);
+-          li = opt_malloc(sizeof(struct list));
+-          if (*arg == '*')
++          if (strlen(arg) != 0)
+             {
+-              li->next = match_suffix;
+-              match_suffix = li;
+-              /* Have to copy: buffer is overwritten */
+-              li->suffix = opt_string_alloc(arg+1);
+-            }
+-          else
+-            {
+-              li->next = ignore_suffix;
+-              ignore_suffix = li;
+-              /* Have to copy: buffer is overwritten */
+-              li->suffix = opt_string_alloc(arg);
++              li = opt_malloc(sizeof(struct list));
++              if (*arg == '*')
++                {
++                  li->next = match_suffix;
++                  match_suffix = li;
++                  /* Have to copy: buffer is overwritten */
++                  li->suffix = opt_string_alloc(arg+1);
++                }
++              else
++                {
++                  li->next = ignore_suffix;
++                  ignore_suffix = li;
++                  /* Have to copy: buffer is overwritten */
++                  li->suffix = opt_string_alloc(arg);
++                }
+             }
+-        };
++        }
+       
+       if (!(dir_stream = opendir(directory)))
+         die(_("cannot access directory %s: %s"), directory, EC_FILE);
+@@ -1555,7 +1558,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+           free(ignore_suffix->suffix);
+           free(ignore_suffix);
+         }
+-            
++      for(; match_suffix; match_suffix = li)
++        {
++          li = match_suffix->next;
++          free(match_suffix->suffix);
++          free(match_suffix);
++        }    
+       break;
+       }
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0003-Debian-build-fixes-for-kFreeBSD.patch b/src/patches/dnsmasq/0003-Debian-build-fixes-for-kFreeBSD.patch
new file mode 100644 (file)
index 0000000..fe73acd
--- /dev/null
@@ -0,0 +1,36 @@
+From 6ac3bc0452a74e16e3d620a0757b0f8caab182ec Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Fri, 3 Oct 2014 08:48:11 +0100
+Subject: [PATCH 03/55] Debian build fixes for kFreeBSD
+
+---
+ src/tables.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/tables.c b/src/tables.c
+index 834f11944cd0..dcdef794c4d2 100644
+--- a/src/tables.c
++++ b/src/tables.c
+@@ -20,6 +20,10 @@
+ #if defined(HAVE_IPSET) && defined(HAVE_BSD_NETWORK)
++#ifndef __FreeBSD__
++#include <bsd/string.h>
++#endif
++
+ #include <sys/types.h>
+ #include <sys/ioctl.h>
+@@ -136,7 +140,7 @@ int add_to_ipset(const char *setname, const struct all_addr *ipaddr,
+       return -1;
+     }
+   
+-  if (rc = pfr_add_tables(&table, 1, &n, 0)) 
++  if ((rc = pfr_add_tables(&table, 1, &n, 0))) 
+     {
+       my_syslog(LOG_WARNING, _("warning: pfr_add_tables: %s(%d)"),
+               pfr_strerror(errno),rc);
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0004-Set-conntrack-mark-before-connect-call.patch b/src/patches/dnsmasq/0004-Set-conntrack-mark-before-connect-call.patch
new file mode 100644 (file)
index 0000000..5d7c3c4
--- /dev/null
@@ -0,0 +1,68 @@
+From e9828b6f66b22ce8873f8d30a773137d1aef1b92 Mon Sep 17 00:00:00 2001
+From: Karl Vogel <karl.vogel@gmail.com>
+Date: Fri, 3 Oct 2014 21:45:15 +0100
+Subject: [PATCH 04/55] Set conntrack mark before connect() call.
+
+SO_MARK has to be done before issuing the connect() call on the
+TCP socket.
+---
+ src/forward.c | 36 ++++++++++++++++++------------------
+ 1 file changed, 18 insertions(+), 18 deletions(-)
+
+diff --git a/src/forward.c b/src/forward.c
+index 4895efeba89a..2cf29eba6e26 100644
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -1796,6 +1796,24 @@ unsigned char *tcp_request(int confd, time_t now,
+                         if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
+                           continue;
+                         
++#ifdef HAVE_CONNTRACK
++                        /* Copy connection mark of incoming query to outgoing connection. */
++                        if (option_bool(OPT_CONNTRACK))
++                          {
++                            unsigned int mark;
++                            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;
++                            
++                            if (get_incoming_mark(&peer_addr, &local, 1, &mark))
++                              setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
++                          }
++#endif        
++                    
+                         if ((!local_bind(last_server->tcpfd,  &last_server->source_addr, last_server->interface, 1) ||
+                              connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
+                           {
+@@ -1820,24 +1838,6 @@ unsigned char *tcp_request(int confd, time_t now,
+                             size = new_size;
+                           }
+ #endif
+-                        
+-#ifdef HAVE_CONNTRACK
+-                        /* Copy connection mark of incoming query to outgoing connection. */
+-                        if (option_bool(OPT_CONNTRACK))
+-                          {
+-                            unsigned int mark;
+-                            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;
+-                            
+-                            if (get_incoming_mark(&peer_addr, &local, 1, &mark))
+-                              setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
+-                          }
+-#endif        
+                       }
+                     
+                     *length = htons(size);
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0005-Fix-typo-in-new-Dbus-code.patch b/src/patches/dnsmasq/0005-Fix-typo-in-new-Dbus-code.patch
new file mode 100644 (file)
index 0000000..80c55f6
--- /dev/null
@@ -0,0 +1,26 @@
+From 17b475912f6a4e72797a543dad59d4d5dde6bb1b Mon Sep 17 00:00:00 2001
+From: Daniel Collins <daniel.collins@smoothwall.net>
+Date: Fri, 3 Oct 2014 21:58:43 +0100
+Subject: [PATCH 05/55] Fix typo in new Dbus code.
+
+Simon's fault.
+---
+ src/dbus.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/dbus.c b/src/dbus.c
+index 15fed906bd90..a2a94dc85dac 100644
+--- a/src/dbus.c
++++ b/src/dbus.c
+@@ -426,7 +426,7 @@ static DBusMessage *dbus_set_bool(DBusMessage *message, int flag, char *name)
+     }
+   else
+     {
+-      my_syslog(LOG_INFO, "Disabling --$s option from D-Bus", name);
++      my_syslog(LOG_INFO, "Disabling --%s option from D-Bus", name);
+       reset_option_bool(flag);
+     }
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0006-Fit-example-conf-file-typo.patch b/src/patches/dnsmasq/0006-Fit-example-conf-file-typo.patch
new file mode 100644 (file)
index 0000000..5f9f572
--- /dev/null
@@ -0,0 +1,22 @@
+From 3d9d2dd0018603a2ae4b9cd65ac6ff959f4fd8c7 Mon Sep 17 00:00:00 2001
+From: Tomas Hozza <thozza@redhat.com>
+Date: Mon, 6 Oct 2014 10:46:48 +0100
+Subject: [PATCH 06/55] Fit example conf file typo.
+
+---
+ dnsmasq.conf.example | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dnsmasq.conf.example b/dnsmasq.conf.example
+index 6b00bd34fbb5..1bd305dbdbad 100644
+--- a/dnsmasq.conf.example
++++ b/dnsmasq.conf.example
+@@ -645,4 +645,4 @@
+ #conf-dir=/etc/dnsmasq.d,.bak
+ # Include all files in a directory which end in .conf
+-#conf-dir=/etc/dnsmasq.d/*.conf
++#conf-dir=/etc/dnsmasq.d/,*.conf
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0007-Improve-RFC-compliance-when-unable-to-supply-address.patch b/src/patches/dnsmasq/0007-Improve-RFC-compliance-when-unable-to-supply-address.patch
new file mode 100644 (file)
index 0000000..370d106
--- /dev/null
@@ -0,0 +1,107 @@
+From b9ff5c8f435173cfa616e3c398bdc089ef690a07 Mon Sep 17 00:00:00 2001
+From: Vladislav Grishenko <themiron@mail.ru>
+Date: Mon, 6 Oct 2014 14:34:24 +0100
+Subject: [PATCH 07/55] Improve RFC-compliance when unable to supply addresses
+ in DHCPv6
+
+While testing https://github.com/sbyx/odhcp6c client I have noticed it
+permanently crashes after startup.
+
+The reason was it (odhcp6c) doesn't expect empty IA options in ADVERTISE
+message without any suboptions.
+
+Despite this validation bug of odhcp6c, dnsmasq should not generate
+ADVERTISE messages with IA if there's nothing to advert per  RFC 3315
+17.2.2:
+
+   If the server will not assign any addresses to any IAs in a
+
+   subsequent Request from the client, the server MUST send an Advertise
+
+   message to the client that includes only a Status Code option with
+
+   code NoAddrsAvail and a status message for the user, a Server
+
+   Identifier option with the server's DUID, and a Client Identifier
+
+   option with the client's DUID.
+
+Meanwhile it's need to add status code for every IA in REPLY message per
+RFC3315 18.2.1:
+
+   If the server cannot assign any addresses to an IA in the message
+   from the client, the server MUST include the IA in the Reply message
+   with no addresses in the IA and a Status Code option in the IA
+   containing status code NoAddrsAvail.
+
+So, I've changed the logic to skip IA completely from ADVERTISE messages and
+to add NoAddrsAvail subcode into IA of REPLY messages.
+
+As for overhead, yes, I believe it's ok to return NoAddrsAvail twice in IA
+and in global section for compatibility with all old and new clients.
+---
+ src/rfc3315.c | 27 +++++++++++++++++++++++++--
+ 1 file changed, 25 insertions(+), 2 deletions(-)
+
+diff --git a/src/rfc3315.c b/src/rfc3315.c
+index 5ebf09d50ac1..ddb390bf1136 100644
+--- a/src/rfc3315.c
++++ b/src/rfc3315.c
+@@ -691,6 +691,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
+ #endif
+           o = build_ia(state, &t1cntr);
++          if (address_assigned)
++              address_assigned = 2;
+           for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
+             {
+@@ -781,6 +783,27 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
+               address_assigned = 1;
+             }
+           
++          if (address_assigned != 1)
++            {
++              /* If the server will not assign any addresses to any IAs in a
++                 subsequent Request from the client, the server MUST send an Advertise
++                 message to the client that doesn't include any IA options. */
++              if (!state->lease_allocate)
++                {
++                  save_counter(o);
++                  continue;
++                }
++              
++              /* If the server cannot assign any addresses to an IA in the message
++                 from the client, the server MUST include the IA in the Reply message
++                 with no addresses in the IA and a Status Code option in the IA
++                 containing status code NoAddrsAvail. */
++              o1 = new_opt6(OPTION6_STATUS_CODE);
++              put_opt6_short(DHCP6NOADDRS);
++              put_opt6_string(_("address unavailable"));
++              end_opt6(o1);
++            }
++          
+           end_ia(t1cntr, min_time, 0);
+           end_opt6(o);        
+         }
+@@ -806,7 +829,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
+           put_opt6_short(DHCP6NOADDRS);
+           put_opt6_string(_("no addresses available"));
+           end_opt6(o1);
+-          log6_packet(state, "DHCPADVERTISE", NULL, _("no addresses available"));
++          log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available"));
+         }
+       break;
+@@ -862,7 +885,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
+                     {
+                       /* Static range, not configured. */
+                       o1 = new_opt6(OPTION6_STATUS_CODE);
+-                      put_opt6_short(DHCP6UNSPEC);
++                      put_opt6_short(DHCP6NOADDRS);
+                       put_opt6_string(_("address unavailable"));
+                       end_opt6(o1);
+                     }
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0008-Fix-conntrack-with-bind-interfaces.patch b/src/patches/dnsmasq/0008-Fix-conntrack-with-bind-interfaces.patch
new file mode 100644 (file)
index 0000000..3f8bad3
--- /dev/null
@@ -0,0 +1,39 @@
+From 98906275a02ae260fe3f82133bd79054f8315f06 Mon Sep 17 00:00:00 2001
+From: Hans Dedecker <dedeckeh@gmail.com>
+Date: Tue, 9 Dec 2014 22:22:53 +0000
+Subject: [PATCH 08/55] Fix conntrack with --bind-interfaces
+
+Make sure dst_addr is assigned the correct address in receive_query when OPTNOWILD is
+enabled so the assigned mark can be correctly retrieved and set in forward_query when
+conntrack is enabled.
+
+Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
+---
+ src/forward.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/forward.c b/src/forward.c
+index 2cf29eba6e26..408a179a20f4 100644
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -1048,7 +1048,7 @@ void receive_query(struct listener *listen, time_t now)
+   /* packet buffer overwritten */
+   daemon->srv_save = NULL;
+   
+-  dst_addr_4.s_addr = 0;
++  dst_addr_4.s_addr = dst_addr.addr.addr4.s_addr = 0;
+   netmask.s_addr = 0;
+   
+   if (option_bool(OPT_NOWILD) && listen->iface)
+@@ -1057,7 +1057,7 @@ void receive_query(struct listener *listen, time_t now)
+      
+       if (listen->family == AF_INET)
+       {
+-        dst_addr_4 = listen->iface->addr.in.sin_addr;
++        dst_addr_4 = dst_addr.addr.addr4 = listen->iface->addr.in.sin_addr;
+         netmask = listen->iface->netmask;
+       }
+     }
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0009-Use-inotify-instead-of-polling-on-Linux.patch b/src/patches/dnsmasq/0009-Use-inotify-instead-of-polling-on-Linux.patch
new file mode 100644 (file)
index 0000000..8193920
--- /dev/null
@@ -0,0 +1,257 @@
+From 193de4abf59e49c6b70d54cfe9720fcb95ca2f71 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Wed, 10 Dec 2014 17:32:16 +0000
+Subject: [PATCH 09/55] Use inotify instead of polling on Linux.
+
+This should solve problems people are seeing when a file changes
+twice within a second and thus is missed for polling.
+---
+ Makefile       |   2 +-
+ bld/Android.mk |   2 +-
+ src/dnsmasq.c  |  25 ++++++++++++--
+ src/dnsmasq.h  |  11 ++++++-
+ src/inotify.c  | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 137 insertions(+), 5 deletions(-)
+ create mode 100644 src/inotify.c
+
+diff --git a/Makefile b/Makefile
+index 58a7975f60b5..c340f1c7b59a 100644
+--- a/Makefile
++++ b/Makefile
+@@ -69,7 +69,7 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
+        dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
+        helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
+        dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
+-       domain.o dnssec.o blockdata.o tables.o loop.o
++       domain.o dnssec.o blockdata.o tables.o loop.o inotify.o
+ hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
+        dns-protocol.h radv-protocol.h ip6addr.h
+diff --git a/bld/Android.mk b/bld/Android.mk
+index d855094eb264..d627796e8edc 100644
+--- a/bld/Android.mk
++++ b/bld/Android.mk
+@@ -10,7 +10,7 @@ LOCAL_SRC_FILES :=  bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
+                   dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
+                   radv.c slaac.c auth.c ipset.c domain.c \
+                   dnssec.c dnssec-openssl.c blockdata.c tables.c \
+-                  loop.c
++                  loop.c inotify.c
+ LOCAL_MODULE := dnsmasq
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index f4a89fc38183..bf2e25a55780 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -315,9 +315,15 @@ int main (int argc, char **argv)
+   if (daemon->port != 0)
+     {
+       cache_init();
++
+ #ifdef HAVE_DNSSEC
+       blockdata_init();
+ #endif
++
++#ifdef HAVE_LINUX_NETWORK
++      if (!option_bool(OPT_NO_POLL))
++      inotify_dnsmasq_init();
++#endif
+     }
+     
+   if (option_bool(OPT_DBUS))
+@@ -793,6 +799,11 @@ int main (int argc, char **argv)
+   
+   pid = getpid();
+   
++#ifdef HAVE_LINUX_NETWORK
++  /* Using inotify, have to select a resolv file at startup */
++  poll_resolv(1, 0, now);
++#endif
++  
+   while (1)
+     {
+       int maxfd = -1;
+@@ -862,11 +873,16 @@ int main (int argc, char **argv)
+ #if defined(HAVE_LINUX_NETWORK)
+       FD_SET(daemon->netlinkfd, &rset);
+       bump_maxfd(daemon->netlinkfd, &maxfd);
++      if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
++      {
++        FD_SET(daemon->inotifyfd, &rset);
++        bump_maxfd(daemon->inotifyfd, &maxfd);
++      }
+ #elif defined(HAVE_BSD_NETWORK)
+       FD_SET(daemon->routefd, &rset);
+       bump_maxfd(daemon->routefd, &maxfd);
+ #endif
+-
++      
+       FD_SET(piperead, &rset);
+       bump_maxfd(piperead, &maxfd);
+@@ -929,6 +945,10 @@ int main (int argc, char **argv)
+       route_sock();
+ #endif
++#ifdef HAVE_LINUX_NETWORK
++      if (daemon->port != 0 && !option_bool(OPT_NO_POLL) && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check())
++      poll_resolv(1, 1, now);           
++#else
+       /* Check for changes to resolv files once per second max. */
+       /* Don't go silent for long periods if the clock goes backwards. */
+       if (daemon->last_resolv == 0 || 
+@@ -941,7 +961,8 @@ int main (int argc, char **argv)
+         poll_resolv(0, daemon->last_resolv != 0, now);          
+         daemon->last_resolv = now;
+       }
+-      
++#endif
++
+       if (FD_ISSET(piperead, &rset))
+       async_event(piperead, now);
+       
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index e74b15a5459a..ebb6b957812f 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -541,6 +541,10 @@ struct resolvc {
+   int is_default, logged;
+   time_t mtime;
+   char *name;
++#ifdef HAVE_LINUX_NETWORK
++  int wd; /* inotify watch descriptor */
++  char *file; /* pointer to file part if path */
++#endif
+ };
+ /* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */
+@@ -998,7 +1002,7 @@ extern struct daemon {
+   /* DHCP state */
+   int dhcpfd, helperfd, pxefd; 
+ #if defined(HAVE_LINUX_NETWORK)
+-  int netlinkfd;
++  int netlinkfd, inotifyfd;
+ #elif defined(HAVE_BSD_NETWORK)
+   int dhcp_raw_fd, dhcp_icmp_fd, routefd;
+ #endif
+@@ -1469,3 +1473,8 @@ void loop_send_probes();
+ int detect_loop(char *query, int type);
+ #endif
++/* inotify.c */
++#ifdef HAVE_LINUX_NETWORK
++void inotify_dnsmasq_init();
++int inotify_check(void);
++#endif
+diff --git a/src/inotify.c b/src/inotify.c
+new file mode 100644
+index 000000000000..a0223443d6b6
+--- /dev/null
++++ b/src/inotify.c
+@@ -0,0 +1,102 @@
++/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++ 
++   This program is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License as published by
++   the Free Software Foundation; version 2 dated June, 1991, or
++   (at your option) version 3 dated 29 June, 2007.
++ 
++   This program is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++     
++   You should have received a copy of the GNU General Public License
++   along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include "dnsmasq.h"
++#include <sys/inotify.h>
++
++#ifdef HAVE_LINUX_NETWORK
++
++/* the strategy is to set a inotify on the directories containing
++   resolv files, for any files in the directory which are close-write 
++   or moved into the directory.
++   
++   When either of those happen, we look to see if the file involved
++   is actually a resolv-file, and if so, call poll-resolv with
++   the "force" argument, to ensure it's read.
++
++   This adds one new error condition: the directories containing
++   all specified resolv-files must exist at start-up, even if the actual
++   files don't. 
++*/
++
++static char *inotify_buffer;
++#define INOTIFY_SZ (sizeof(struct inotify_event) + NAME_MAX + 1)
++
++void inotify_dnsmasq_init()
++{
++  struct resolvc *res;
++
++  inotify_buffer = safe_malloc(INOTIFY_SZ);
++
++  daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
++
++  if (daemon->inotifyfd == -1)
++    die(_("failed to create inotify: %s"), NULL, EC_MISC);
++
++  for (res = daemon->resolv_files; res; res = res->next)
++    {
++      char *d = strrchr(res->name, '/');
++      
++      if (!d)
++      die(_("resolv-file %s not an absolute path"), res->name, EC_MISC);
++       
++      *d = 0; /* make ->name just directory */
++      res->wd = inotify_add_watch(daemon->inotifyfd, res->name, IN_CLOSE_WRITE | IN_MOVED_TO);
++      res->file = d+1; /* pointer to filename */
++      
++      if (res->wd == -1 && errno == ENOENT)
++      die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
++      
++      *d = '/'; /* restore name */
++      
++      if (res->wd == -1)
++      die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
++    }
++}
++
++int inotify_check(void)
++{
++  int hit = 0;
++  
++  while (1)
++    {
++      int rc;
++      char *p;
++      struct resolvc *res;
++      struct inotify_event *in;
++
++      while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
++      
++      if (rc <= 0)
++      break;
++      
++      for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len) 
++      {
++        in = (struct inotify_event*)p;
++        
++        for (res = daemon->resolv_files; res; res = res->next)
++          if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
++            hit = 1;
++      }
++    }
++
++  return hit;
++}
++
++#endif
++
++  
++  
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0010-Teach-the-new-inotify-code-about-symlinks.patch b/src/patches/dnsmasq/0010-Teach-the-new-inotify-code-about-symlinks.patch
new file mode 100644 (file)
index 0000000..70fc5c3
--- /dev/null
@@ -0,0 +1,73 @@
+From 857973e6f7e0a3d03535a9df7f9373fd7a0b65cc Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 15 Dec 2014 15:58:13 +0000
+Subject: [PATCH 10/55] Teach the new inotify code about symlinks.
+
+---
+ src/inotify.c | 43 +++++++++++++++++++++++++++----------------
+ 1 file changed, 27 insertions(+), 16 deletions(-)
+
+diff --git a/src/inotify.c b/src/inotify.c
+index a0223443d6b6..960bf5efb41f 100644
+--- a/src/inotify.c
++++ b/src/inotify.c
+@@ -41,29 +41,40 @@ void inotify_dnsmasq_init()
+   inotify_buffer = safe_malloc(INOTIFY_SZ);
+-  daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
++  daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
++  
+   if (daemon->inotifyfd == -1)
+     die(_("failed to create inotify: %s"), NULL, EC_MISC);
+-
++  
+   for (res = daemon->resolv_files; res; res = res->next)
+     {
+-      char *d = strrchr(res->name, '/');
+-      
+-      if (!d)
+-      die(_("resolv-file %s not an absolute path"), res->name, EC_MISC);
+-       
+-      *d = 0; /* make ->name just directory */
+-      res->wd = inotify_add_watch(daemon->inotifyfd, res->name, IN_CLOSE_WRITE | IN_MOVED_TO);
+-      res->file = d+1; /* pointer to filename */
+-      
+-      if (res->wd == -1 && errno == ENOENT)
+-      die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
++      char *d = NULL, *path;
+       
+-      *d = '/'; /* restore name */
++      if (!(path = realpath(res->name, NULL)))
++      {
++        /* realpath will fail if the file doesn't exist, but
++           dnsmasq copes with missing files, so fall back 
++           and assume that symlinks are not in use in that case. */
++        if (errno == ENOENT)
++          path = res->name;
++        else
++          die(_("cannot cannonicalise resolv-file %s: %s"), res->name, EC_MISC); 
++      }
+       
+-      if (res->wd == -1)
+-      die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
++      if ((d = strrchr(path, '/')))
++      {
++        *d = 0; /* make path just directory */
++        res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO);
++        res->file = d+1; /* pointer to filename */
++        *d = '/';
++        
++        if (res->wd == -1 && errno == ENOENT)
++          die(_("directory %s for resolv-file is missing, cannot poll"), res->name, EC_MISC);
++        
++        if (res->wd == -1)
++          die(_("failed to create inotify for %s: %s"), res->name, EC_MISC);
++      }
+     }
+ }
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0011-Remove-floor-on-EDNS0-packet-size-with-DNSSEC.patch b/src/patches/dnsmasq/0011-Remove-floor-on-EDNS0-packet-size-with-DNSSEC.patch
new file mode 100644 (file)
index 0000000..095253b
--- /dev/null
@@ -0,0 +1,46 @@
+From 800c5cc1e7438818fd80f08c2d472df249a6942d Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 15 Dec 2014 17:50:15 +0000
+Subject: [PATCH 11/55] Remove floor on EDNS0 packet size with DNSSEC.
+
+---
+ CHANGELOG     | 6 +++++-
+ src/dnsmasq.c | 5 -----
+ 2 files changed, 5 insertions(+), 6 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 13ab41c05fc3..68252924e743 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -2,7 +2,11 @@ version 2.73
+             Fix crash at startup when an empty suffix is supplied to
+           --conf-dir, also trivial memory leak. Thanks to 
+           Tomas Hozza for spotting this.
+-      
++
++          Remove floor of 4096 on advertised EDNS0 packet size when 
++          DNSSEC in use, the original rationale for this has long gone.
++          Thanks to Anders Kaseorg for spotting this.
++
+ version 2.72
+             Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index bf2e25a55780..5c7750d365fa 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -87,11 +87,6 @@ int main (int argc, char **argv)
+  
+   if (daemon->edns_pktsz < PACKETSZ)
+     daemon->edns_pktsz = PACKETSZ;
+-#ifdef HAVE_DNSSEC
+-  /* Enforce min packet big enough for DNSSEC */
+-  if (option_bool(OPT_DNSSEC_VALID) && daemon->edns_pktsz < EDNS_PKTSZ)
+-    daemon->edns_pktsz = EDNS_PKTSZ;
+-#endif
+   daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? 
+     daemon->edns_pktsz : DNSMASQ_PACKETSZ;
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0012-CHANGELOG-re.-inotify.patch b/src/patches/dnsmasq/0012-CHANGELOG-re.-inotify.patch
new file mode 100644 (file)
index 0000000..b71e58b
--- /dev/null
@@ -0,0 +1,27 @@
+From ad946d555dce44eb690c7699933b6ff40ab85bb6 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 15 Dec 2014 17:52:22 +0000
+Subject: [PATCH 12/55] CHANGELOG re. inotify.
+
+---
+ CHANGELOG | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 68252924e743..9174b0bd75dc 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -7,6 +7,10 @@ version 2.73
+           DNSSEC in use, the original rationale for this has long gone.
+           Thanks to Anders Kaseorg for spotting this.
++          Use inotify for checking on updates to /etc/resolv.conf and
++          friends under Linux. This fixes race conditions when the files are 
++          updated rapidly and saves CPU by noy polling.
++      
+ version 2.72
+             Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0013-Fix-breakage-of-domain-domain-subnet-local.patch b/src/patches/dnsmasq/0013-Fix-breakage-of-domain-domain-subnet-local.patch
new file mode 100644 (file)
index 0000000..2ff5acd
--- /dev/null
@@ -0,0 +1,70 @@
+From 3ad3f3bbd4ee716a7d2fb1e115cf89bd1b1a5de9 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Tue, 16 Dec 2014 18:25:17 +0000
+Subject: [PATCH 13/55] Fix breakage of --domain=<domain>,<subnet>,local
+
+---
+ CHANGELOG    |  4 ++++
+ src/option.c | 18 ++++++++++++++++--
+ 2 files changed, 20 insertions(+), 2 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 9174b0bd75dc..9e6c7aa4fd68 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -10,6 +10,10 @@ version 2.73
+           Use inotify for checking on updates to /etc/resolv.conf and
+           friends under Linux. This fixes race conditions when the files are 
+           updated rapidly and saves CPU by noy polling.
++
++          Fix breakage of --domain=<domain>,<subnet>,local - only reverse
++          queries were intercepted. THis appears to have been broken 
++          since 2.69. Thanks to Josh Stone for finding the bug.
+       
+ version 2.72
+diff --git a/src/option.c b/src/option.c
+index b08e98e16f84..209fa6976609 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -1941,10 +1941,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+                             else
+                               {
+                                  /* generate the equivalent of
+-                                    local=/<domain>/
+                                     local=/xxx.yyy.zzz.in-addr.arpa/ */
+                                 struct server *serv = add_rev4(new->start, msize);
+                                 serv->flags |= SERV_NO_ADDR;
++
++                                /* local=/<domain>/ */
++                                serv = opt_malloc(sizeof(struct server));
++                                memset(serv, 0, sizeof(struct server));
++                                serv->domain = d;
++                                serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
++                                serv->next = daemon->servers;
++                                daemon->servers = serv;
+                               }
+                           }
+                       }
+@@ -1978,10 +1985,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+                             else 
+                               {
+                                 /* generate the equivalent of
+-                                   local=/<domain>/
+                                    local=/xxx.yyy.zzz.ip6.arpa/ */
+                                 struct server *serv = add_rev6(&new->start6, msize);
+                                 serv->flags |= SERV_NO_ADDR;
++                                
++                                /* local=/<domain>/ */
++                                serv = opt_malloc(sizeof(struct server));
++                                memset(serv, 0, sizeof(struct server));
++                                serv->domain = d;
++                                serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
++                                serv->next = daemon->servers;
++                                daemon->servers = serv;
+                               }
+                           }
+                       }
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0014-Remove-redundant-IN6_IS_ADDR_ULA-a-macro-defn.patch b/src/patches/dnsmasq/0014-Remove-redundant-IN6_IS_ADDR_ULA-a-macro-defn.patch
new file mode 100644 (file)
index 0000000..a0e647f
--- /dev/null
@@ -0,0 +1,27 @@
+From bd9520b7ade7098ee423acc38965376aa57feb07 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Tue, 16 Dec 2014 20:41:29 +0000
+Subject: [PATCH 14/55] Remove redundant IN6_IS_ADDR_ULA(a) macro defn.
+
+---
+ src/network.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/src/network.c b/src/network.c
+index 5067007c5cbc..99419f57951e 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -16,10 +16,6 @@
+ #include "dnsmasq.h"
+-#ifndef IN6_IS_ADDR_ULA
+-#define IN6_IS_ADDR_ULA(a) ((((__const uint32_t *) (a))[0] & htonl (0xfe00000)) == htonl (0xfc000000))
+-#endif
+-
+ #ifdef HAVE_LINUX_NETWORK
+ int indextoname(int fd, int index, char *name)
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0015-Eliminate-IPv6-privacy-addresses-from-interface-name.patch b/src/patches/dnsmasq/0015-Eliminate-IPv6-privacy-addresses-from-interface-name.patch
new file mode 100644 (file)
index 0000000..114a4f3
--- /dev/null
@@ -0,0 +1,148 @@
+From 476693678e778886b64d0b56e27eb7695cbcca99 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Wed, 17 Dec 2014 12:41:56 +0000
+Subject: [PATCH 15/55] Eliminate IPv6 privacy addresses from --interface-name
+ answers.
+
+---
+ CHANGELOG     |  5 +++++
+ src/auth.c    |  4 ++++
+ src/dnsmasq.h |  1 +
+ src/network.c | 12 ++++++++----
+ src/rfc1035.c | 17 ++++++++++-------
+ 5 files changed, 28 insertions(+), 11 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 9e6c7aa4fd68..01f5208ec006 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -14,6 +14,11 @@ version 2.73
+           Fix breakage of --domain=<domain>,<subnet>,local - only reverse
+           queries were intercepted. THis appears to have been broken 
+           since 2.69. Thanks to Josh Stone for finding the bug.
++
++          Eliminate IPv6 privacy addresses and deprecated addresses from
++          the answers given by --interface-name. Note that reverse queries
++          (ie looking for names, given addresses) are not affected. 
++          Thanks to Michael Gorbach for the suggestion.
+       
+ version 2.72
+diff --git a/src/auth.c b/src/auth.c
+index dd46566ec2cc..a327f16d8c0b 100644
+--- a/src/auth.c
++++ b/src/auth.c
+@@ -363,6 +363,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
+                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, 
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index ebb6b957812f..1dd61c5edba3 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -318,6 +318,7 @@ struct ds_config {
+ #define ADDRLIST_LITERAL 1
+ #define ADDRLIST_IPV6    2
++#define ADDRLIST_REVONLY 4
+ struct addrlist {
+   struct all_addr addr;
+diff --git a/src/network.c b/src/network.c
+index 99419f57951e..14d2af2ce313 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -236,7 +236,7 @@ struct iface_param {
+ };
+ static int iface_allowed(struct iface_param *param, int if_index, char *label,
+-                       union mysockaddr *addr, struct in_addr netmask, int prefixlen, int dad) 
++                       union mysockaddr *addr, struct in_addr netmask, int prefixlen, int iface_flags) 
+ {
+   struct irec *iface;
+   int mtu = 0, loopback;
+@@ -388,6 +388,10 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
+                {
+                   al->addr.addr.addr6 = addr->in6.sin6_addr;
+                   al->flags = ADDRLIST_IPV6;
++                  /* Privacy addresses and addresses still undergoing DAD and deprecated addresses
++                     don't appear in forward queries, but will in reverse ones. */
++                  if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
++                    al->flags |= ADDRLIST_REVONLY;
+                } 
+ #endif
+             }
+@@ -399,7 +403,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
+   for (iface = daemon->interfaces; iface; iface = iface->next) 
+     if (sockaddr_isequal(&iface->addr, addr))
+       {
+-      iface->dad = dad;
++      iface->dad = !!(iface_flags & IFACE_TENTATIVE);
+       iface->found = 1; /* for garbage collection */
+       return 1;
+       }
+@@ -474,7 +478,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
+       iface->dhcp_ok = dhcp_ok;
+       iface->dns_auth = auth_dns;
+       iface->mtu = mtu;
+-      iface->dad = dad;
++      iface->dad = !!(iface_flags & IFACE_TENTATIVE);
+       iface->found = 1;
+       iface->done = iface->multicast_done = iface->warned = 0;
+       iface->index = if_index;
+@@ -519,7 +523,7 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix,
+   else
+     addr.in6.sin6_scope_id = 0;
+   
+-  return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, !!(flags & IFACE_TENTATIVE));
++  return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, flags);
+ }
+ #endif
+diff --git a/src/rfc1035.c b/src/rfc1035.c
+index 8a7d2608dac5..bdeb3fb10e68 100644
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1923,14 +1923,17 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
+                 for (intr = daemon->int_names; intr; intr = intr->next)
+                   if (hostname_isequal(name, intr->name))
+                     {
+-                      ans = 1;
+-                      if (!dryrun)
+-                        {
+-                          
+-                          for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
++                      for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
+ #ifdef HAVE_IPV6
+-                            if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
++                        if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type)
+ #endif
++                          {
++#ifdef HAVE_IPV6
++                            if (addrlist->flags & ADDRLIST_REVONLY)
++                              continue;
++#endif        
++                            ans = 1;  
++                            if (!dryrun)
+                               {
+                                 gotit = 1;
+                                 log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL);
+@@ -1939,7 +1942,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
+                                                         type == T_A ? "4" : "6", &addrlist->addr))
+                                   anscount++;
+                               }
+-                        }
++                          }
+                     }
+                 
+                 if (!dryrun && !gotit)
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0016-Tweak-field-width-in-cache-dump-to-avoid-truncating-.patch b/src/patches/dnsmasq/0016-Tweak-field-width-in-cache-dump-to-avoid-truncating-.patch
new file mode 100644 (file)
index 0000000..d072c03
--- /dev/null
@@ -0,0 +1,35 @@
+From 3267804598047bd1781cab91508d1bc516e5ddbb Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Wed, 17 Dec 2014 20:38:20 +0000
+Subject: [PATCH 16/55] Tweak field width in cache dump to avoid truncating
+ IPv6 addresses.
+
+---
+ src/cache.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/cache.c b/src/cache.c
+index 2c3a49887053..f9e1d31e8c99 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -1411,7 +1411,7 @@ void dump_cache(time_t now)
+           *a = 0;
+           if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
+             n = "<Root>";
+-          p += sprintf(p, "%-40.40s ", n);
++          p += sprintf(p, "%-30.30s ", n);
+           if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
+             a = cache_get_cname_target(cache);
+ #ifdef HAVE_DNSSEC
+@@ -1454,7 +1454,7 @@ void dump_cache(time_t now)
+           else if (cache->flags & F_DNSKEY)
+             t = "K";
+ #endif
+-          p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s  ", a, t,
++          p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s  ", a, t,
+                        cache->flags & F_FORWARD ? "F" : " ",
+                        cache->flags & F_REVERSE ? "R" : " ",
+                        cache->flags & F_IMMORTAL ? "I" : " ",
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0017-Fix-crash-in-DNSSEC-code-when-attempting-to-verify-l.patch b/src/patches/dnsmasq/0017-Fix-crash-in-DNSSEC-code-when-attempting-to-verify-l.patch
new file mode 100644 (file)
index 0000000..944afaf
--- /dev/null
@@ -0,0 +1,100 @@
+From 094b5c3d904bae9aeb3206d9f3b8348926b84975 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sun, 21 Dec 2014 16:11:52 +0000
+Subject: [PATCH 17/55] Fix crash in DNSSEC code when attempting to verify
+ large RRs.
+
+---
+ CHANGELOG    |  3 +++
+ src/dnssec.c | 27 +++++++++++++++++++--------
+ 2 files changed, 22 insertions(+), 8 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 01f5208ec006..956b71a151db 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -19,6 +19,9 @@ version 2.73
+           the answers given by --interface-name. Note that reverse queries
+           (ie looking for names, given addresses) are not affected. 
+           Thanks to Michael Gorbach for the suggestion.
++
++          Fix crash in DNSSEC code with long RRs. Thanks to Marco Davids
++          for the bug report.
+       
+ version 2.72
+diff --git a/src/dnssec.c b/src/dnssec.c
+index 69bfc29e355f..3208ac701149 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -456,16 +456,27 @@ static u16 *get_desc(int type)
+ /* Return bytes of canonicalised rdata, when the return value is zero, the remaining 
+    data, pointed to by *p, should be used raw. */
+-static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, 
++static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end, char *buff, int bufflen,
+                    unsigned char **p, u16 **desc)
+ {
+   int d = **desc;
+   
+-  (*desc)++;
+-  
+   /* No more data needs mangling */
+   if (d == (u16)-1)
+-    return 0;
++    {
++      /* If there's more data than we have space for, just return what fits,
++       we'll get called again for more chunks */
++      if (end - *p > bufflen)
++      {
++        memcpy(buff, *p, bufflen);
++        *p += bufflen;
++        return bufflen;
++      }
++      
++      return 0;
++    }
++ 
++  (*desc)++;
+   
+   if (d == 0 && extract_name(header, plen, p, buff, 1, 0))
+     /* domain-name, canonicalise */
+@@ -560,7 +571,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
+             if (left1 != 0)
+               memmove(buff1, buff1 + len1 - left1, left1);
+             
+-            if ((len1 = get_rdata(header, plen, end1, buff1 + left1, &p1, &dp1)) == 0)
++            if ((len1 = get_rdata(header, plen, end1, buff1 + left1, MAXDNAME - left1, &p1, &dp1)) == 0)
+               {
+                 quit = 1;
+                 len1 = end1 - p1;
+@@ -571,7 +582,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
+             if (left2 != 0)
+               memmove(buff2, buff2 + len2 - left2, left2);
+             
+-            if ((len2 = get_rdata(header, plen, end2, buff2 + left2, &p2, &dp2)) == 0)
++            if ((len2 = get_rdata(header, plen, end2, buff2 + left2, MAXDNAME - left2, &p2, &dp2)) == 0)
+               {
+                 quit = 1;
+                 len2 = end2 - p2;
+@@ -808,7 +819,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
+         /* canonicalise rdata and calculate length of same, use name buffer as workspace */
+         cp = p;
+         dp = rr_desc;
+-        for (len = 0; (seg = get_rdata(header, plen, end, name, &cp, &dp)) != 0; len += seg);
++        for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME, &cp, &dp)) != 0; len += seg);
+         len += end - cp;
+         len = htons(len);
+         hash->update(ctx, 2, (unsigned char *)&len); 
+@@ -816,7 +827,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
+         /* Now canonicalise again and digest. */
+         cp = p;
+         dp = rr_desc;
+-        while ((seg = get_rdata(header, plen, end, name, &cp, &dp)))
++        while ((seg = get_rdata(header, plen, end, name, MAXDNAME, &cp, &dp)))
+           hash->update(ctx, seg, (unsigned char *)name);
+         if (cp != end)
+           hash->update(ctx, end - cp, cp);
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0018-Make-caching-work-for-CNAMEs-pointing-to-A-AAAA-reco.patch b/src/patches/dnsmasq/0018-Make-caching-work-for-CNAMEs-pointing-to-A-AAAA-reco.patch
new file mode 100644 (file)
index 0000000..de66adb
--- /dev/null
@@ -0,0 +1,99 @@
+From cbc652423403e3cef00e00240f6beef713142246 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sun, 21 Dec 2014 21:21:53 +0000
+Subject: [PATCH 18/55] Make caching work for CNAMEs pointing to A/AAAA records
+ shadowed in /etc/hosts
+
+If the answer to an upstream query is a CNAME which points to an
+A/AAAA record which also exists in /etc/hosts and friends, then
+caching is suppressed, to avoid inconsistent answers. This is
+now modified to allow caching when the upstream and local A/AAAA
+records have the same value.
+---
+ src/cache.c | 34 +++++++++++++++++++++++++---------
+ 1 file changed, 25 insertions(+), 9 deletions(-)
+
+diff --git a/src/cache.c b/src/cache.c
+index f9e1d31e8c99..ff1ca6f1c352 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -322,7 +322,7 @@ static int is_expired(time_t now, struct crec *crecp)
+   return 1;
+ }
+-static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
++static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
+ {
+   /* Scan and remove old entries.
+      If (flags & F_FORWARD) then remove any forward entries for name and any expired
+@@ -331,8 +331,8 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
+      entries in the whole cache.
+      If (flags == 0) remove any expired entries in the whole cache. 
+-     In the flags & F_FORWARD case, the return code is valid, and returns zero if the
+-     name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
++     In the flags & F_FORWARD case, the return code is valid, and returns a non-NULL pointer
++     to a cache entry if the name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
+      We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
+      so that when we hit an entry which isn't reverse and is immortal, we're done. */
+@@ -361,7 +361,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
+                 (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
+               {
+                 if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
+-                  return 0;
++                  return crecp;
+                 *up = crecp->hash_next;
+                 cache_unlink(crecp);
+                 cache_free(crecp);
+@@ -378,7 +378,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
+                  crecp->addr.sig.type_covered == addr->addr.dnssec.type))
+               {
+                 if (crecp->flags & F_CONFIG)
+-                  return 0;
++                  return crecp;
+                 *up = crecp->hash_next;
+                 cache_unlink(crecp);
+                 cache_free(crecp);
+@@ -423,7 +423,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
+           up = &crecp->hash_next;
+     }
+   
+-  return 1;
++  return NULL;
+ }
+ /* Note: The normal calling sequence is
+@@ -471,10 +471,26 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
+     return NULL;
+   
+   /* First remove any expired entries and entries for the name/address we
+-     are currently inserting. Fail if we attempt to delete a name from
+-     /etc/hosts or DHCP. */
+-  if (!cache_scan_free(name, addr, now, flags))
++     are currently inserting. */
++  if ((new = cache_scan_free(name, addr, now, flags)))
+     {
++      /* We're trying to insert a record over one from 
++       /etc/hosts or DHCP, or other config. If the 
++       existing record is for an A or AAAA and
++       the record we're trying to insert is the same, 
++       just drop the insert, but don't error the whole process. */
++      if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD))
++      {
++        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;
+       return NULL;
+     }
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0019-Fix-problems-validating-NSEC3-and-wildcards.patch b/src/patches/dnsmasq/0019-Fix-problems-validating-NSEC3-and-wildcards.patch
new file mode 100644 (file)
index 0000000..0ee2e65
--- /dev/null
@@ -0,0 +1,365 @@
+From fbc5205702c7f6f431d9f1043c553d7fb62ddfdb Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Tue, 23 Dec 2014 15:46:08 +0000
+Subject: [PATCH 19/55] Fix problems validating NSEC3 and wildcards.
+
+---
+ src/dnssec.c | 253 ++++++++++++++++++++++++++++++-----------------------------
+ 1 file changed, 128 insertions(+), 125 deletions(-)
+
+diff --git a/src/dnssec.c b/src/dnssec.c
+index 3208ac701149..9350d3e8c963 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -615,6 +615,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
+    Return code:
+    STAT_SECURE   if it validates.
+    STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
++   (In this case *wildcard_out points to the "body" of the wildcard within name.) 
+    STAT_NO_SIG no RRsigs found.
+    STAT_INSECURE RRset empty.
+    STAT_BOGUS    signature is wrong, bad packet.
+@@ -625,8 +626,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
+    name is unchanged on exit. keyname is used as workspace and trashed.
+ */
+-static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, 
+-                        int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in)
++static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type, 
++                        char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in)
+ {
+   static unsigned char **rrset = NULL, **sigs = NULL;
+   static int rrset_sz = 0, sig_sz = 0;
+@@ -798,8 +799,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
+           {
+             int k;
+             for (k = name_labels - labels; k != 0; k--)
+-              while (*name_start != '.' && *name_start != 0)
+-                name_start++;
++              {
++                while (*name_start != '.' && *name_start != 0)
++                  name_start++;
++                if (k != 1)
++                  name_start++;
++              }
++            
++            if (wildcard_out)
++              *wildcard_out = name_start+1;
++
+             name_start--;
+             *name_start = '*';
+           }
+@@ -974,7 +983,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
+             if (recp1->addr.ds.keylen == (int)hash->digest_size &&
+                 (ds_digest = blockdata_retrieve(recp1->addr.key.keydata, recp1->addr.ds.keylen, NULL)) &&
+                 memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
+-                validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, key, rdlen - 4, algo, keytag) == STAT_SECURE)
++                validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, NULL, key, rdlen - 4, algo, keytag) == STAT_SECURE)
+               {
+                 valid = 1;
+                 break;
+@@ -1443,11 +1452,88 @@ static int base32_decode(char *in, unsigned char *out)
+   return p - out;
+ }
++static int check_nsec3_coverage(struct dns_header *header, size_t plen, int digest_len, unsigned char *digest, int type,
++                              char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count)
++{
++  int i, hash_len, salt_len, base32_len, rdlen;
++  unsigned char *p, *psave;
++
++  for (i = 0; i < nsec_count; i++)
++    if ((p = nsecs[i]))
++      {
++              if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
++          !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
++        return 0;
++      
++      p += 8; /* class, type, TTL */
++      GETSHORT(rdlen, p);
++      psave = p;
++      p += 4; /* algo, flags, iterations */
++      salt_len = *p++; /* salt_len */
++      p += salt_len; /* salt */
++      hash_len = *p++; /* p now points to next hashed name */
++      
++      if (!CHECK_LEN(header, p, plen, hash_len))
++        return 0;
++      
++      if (digest_len == base32_len && hash_len == base32_len)
++        {
++          int rc = memcmp(workspace2, digest, digest_len);
++
++          if (rc == 0)
++            {
++              /* We found an NSEC3 whose hashed name exactly matches the query, so
++                 we just need to check the type map. p points to the RR data for the record. */
++              
++              int offset = (type & 0xff) >> 3;
++              int mask = 0x80 >> (type & 0x07);
++              
++              p += hash_len; /* skip next-domain hash */
++              rdlen -= p - psave;
++
++              if (!CHECK_LEN(header, p, plen, rdlen))
++                return 0;
++              
++              while (rdlen >= 2)
++                {
++                  if (p[0] == type >> 8)
++                    {
++                      /* Does the NSEC3 say our type exists? */
++                      if (offset < p[1] && (p[offset+2] & mask) != 0)
++                        return STAT_BOGUS;
++                      
++                      break; /* finshed checking */
++                    }
++                  
++                  rdlen -= p[1];
++                  p +=  p[1];
++                }
++
++              return 1;
++            }
++          else if (rc <= 0)
++            {
++              /* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
++                 wrap around case, name-hash falls between NSEC3 name-hash and end */
++              if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
++                return 1;
++            }
++          else 
++            {
++              /* wrap around case, name falls between start and next domain name */
++              if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
++                return 1;
++            }
++        }
++      }
++  return 0;
++}
++
+ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
+-                                   char *workspace1, char *workspace2, char *name, int type)
++                                   char *workspace1, char *workspace2, char *name, int type, char *wildname)
+ {
+   unsigned char *salt, *p, *digest;
+-  int digest_len, i, iterations, salt_len, hash_len, base32_len, algo = 0;
++  int digest_len, i, iterations, salt_len, base32_len, algo = 0;
+   struct nettle_hash const *hash;
+   char *closest_encloser, *next_closest, *wildcard;
+  
+@@ -1520,7 +1606,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+   if (!(hash = hash_find("sha1")))
+     return STAT_BOGUS;
+-  /* Now, we need the "closest encloser NSEC3" */
++  if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
++    return STAT_BOGUS;
++  
++  if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
++    return STAT_SECURE;
++
++  /* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3" 
++     or an answer inferred from a wildcard record. */
+   closest_encloser = name;
+   next_closest = NULL;
+@@ -1529,6 +1622,9 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+       if (*closest_encloser == '.')
+       closest_encloser++;
++      if (wildname && hostname_isequal(closest_encloser, wildname))
++      break;
++
+       if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
+       return STAT_BOGUS;
+       
+@@ -1551,127 +1647,33 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+     }
+   while ((closest_encloser = strchr(closest_encloser, '.')));
+   
+-  /* No usable NSEC3s */
+-  if (i == nsec_count)
++  if (!closest_encloser)
+     return STAT_BOGUS;
+   
+-  if (!next_closest)
+-    {
+-      /* We found an NSEC3 whose hashed name exactly matches the query, so
+-       Now we just need to check the type map. p points to the RR data for the record. */
+-      int rdlen;
+-      unsigned char *psave;
+-      int offset = (type & 0xff) >> 3;
+-      int mask = 0x80 >> (type & 0x07);
+-      
+-      p += 8; /* class, type, TTL */
+-      GETSHORT(rdlen, p);
+-      psave = p;
+-      p += 5 + salt_len; /* algo, flags, iterations, salt_len, salt */
+-      hash_len = *p++;
+-      if (!CHECK_LEN(header, p, plen, hash_len))
+-      return STAT_BOGUS; /* bad packet */
+-      p += hash_len;
+-      rdlen -= p - psave;
+-      
+-      while (rdlen >= 2)
+-      {
+-        if (!CHECK_LEN(header, p, plen, rdlen))
+-          return STAT_BOGUS;
+-        
+-        if (p[0] == type >> 8)
+-          {
+-            /* Does the NSEC3 say our type exists? */
+-            if (offset < p[1] && (p[offset+2] & mask) != 0)
+-              return STAT_BOGUS;
+-            
+-            break; /* finshed checking */
+-          }
+-        
+-        rdlen -= p[1];
+-        p +=  p[1];
+-      }
+-      
+-      return STAT_SECURE;
+-    }
+-
+   /* Look for NSEC3 that proves the non-existence of the next-closest encloser */
+   if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
+     return STAT_BOGUS;
+-  for (i = 0; i < nsec_count; i++)
+-    if ((p = nsecs[i]))
+-      {
+-              if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
+-          !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
+-        return STAT_BOGUS;
+-         
+-      p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
+-      hash_len = *p++; /* p now points to next hashed name */
+- 
+-      if (!CHECK_LEN(header, p, plen, hash_len))
+-        return STAT_BOGUS;
+-      
+-      if (digest_len == base32_len && hash_len == base32_len)
+-        {
+-          if (memcmp(workspace2, digest, digest_len) <= 0)
+-            {
+-              /* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
+-                 wrap around case, name-hash falls between NSEC3 name-hash and end */
+-              if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
+-                return STAT_SECURE;
+-            }
+-          else 
+-            {
+-              /* wrap around case, name falls between start and next domain name */
+-              if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
+-                return STAT_SECURE;
+-            }
+-        }
+-      }
+-  
+-  /* Finally, check that there's no seat of wildcard synthesis */
+-  if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
+-    return STAT_BOGUS;
+-  
+-  wildcard--;
+-  *wildcard = '*';
+-  
+-  if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
++  if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
+     return STAT_BOGUS;
+   
+-  for (i = 0; i < nsec_count; i++)
+-    if ((p = nsecs[i]))
+-      {
+-      if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
+-          !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
+-        return STAT_BOGUS;
+-         
+-      p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
+-      hash_len = *p++; /* p now points to next hashed name */
+- 
+-      if (!CHECK_LEN(header, p, plen, hash_len))
+-        return STAT_BOGUS;
+-      
+-      if (digest_len == base32_len && hash_len == base32_len)
+-        {
+-          if (memcmp(workspace2, digest, digest_len) <= 0)
+-            {
+-              /* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
+-                 wrap around case, name-hash falls between NSEC3 name-hash and end */
+-              if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
+-                return STAT_SECURE;
+-            }
+-          else 
+-            {
+-              /* wrap around case, name falls between start and next domain name */
+-              if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
+-                return STAT_SECURE;
+-            }
+-        }
+-      }
++  /* Finally, check that there's no seat of wildcard synthesis */
++  if (!wildname)
++    {
++      if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
++      return STAT_BOGUS;
++      
++      wildcard--;
++      *wildcard = '*';
++      
++      if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
++      return STAT_BOGUS;
++      
++      if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
++      return STAT_BOGUS;
++    }
+   
+-  return STAT_BOGUS;
++  return STAT_SECURE;
+ }
+     
+ /* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
+@@ -1792,8 +1794,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+             struct all_addr a;
+             struct blockdata *key;
+             struct crec *crecp;
+-            
+-            rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0);
++            char *wildname;
++
++            rc = validate_rrset(now, header, plen, class1, type1, name, keyname, &wildname, NULL, 0, 0, 0);
+             
+             if (rc == STAT_SECURE_WILDCARD)
+               {
+@@ -1807,7 +1810,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+                 if (nsec_type == T_NSEC)
+                   rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
+                 else
+-                  rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
++                  rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, wildname);
+                 if (rc != STAT_SECURE)
+                   return rc;
+@@ -1933,7 +1936,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+   if (nsec_type == T_NSEC)
+     return prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype);
+   else
+-    return prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype);
++    return prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL);
+ }
+ /* Chase the CNAME chain in the packet until the first record which _doesn't validate.
+@@ -1980,7 +1983,7 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
+           return STAT_INSECURE;
+         
+         /* validate CNAME chain, return if insecure or need more data */
+-        rc = validate_rrset(now, header, plen, class, type, name, keyname, NULL, 0, 0, 0);
++        rc = validate_rrset(now, header, plen, class, type, name, keyname, NULL, NULL, 0, 0, 0);
+         if (rc != STAT_SECURE)
+           {
+             if (rc == STAT_NO_SIG)
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0020-Initialise-return-value.patch b/src/patches/dnsmasq/0020-Initialise-return-value.patch
new file mode 100644 (file)
index 0000000..2f70ee5
--- /dev/null
@@ -0,0 +1,32 @@
+From 83d2ed09fc0216b567d7fb2197e4ff3eae150b0d Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Tue, 23 Dec 2014 18:42:38 +0000
+Subject: [PATCH 20/55] Initialise return value.
+
+---
+ src/dnssec.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/src/dnssec.c b/src/dnssec.c
+index 9350d3e8c963..ed8cf893bad2 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -637,10 +637,13 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
+   struct crec *crecp = NULL;
+   int type_covered, algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag;
+   u16 *rr_desc = get_desc(type);
+-
++ 
++  if (wildcard_out)
++    *wildcard_out = NULL;
++  
+   if (!(p = skip_questions(header, plen)))
+     return STAT_BOGUS;
+-
++  
+   name_labels = count_labels(name); /* For 4035 5.3.2 check */
+   /* look for RRSIGs for this RRset and get pointers to each RR in the set. */
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0021-Add-ignore-address-option.patch b/src/patches/dnsmasq/0021-Add-ignore-address-option.patch
new file mode 100644 (file)
index 0000000..6c88f2d
--- /dev/null
@@ -0,0 +1,192 @@
+From 32fc6dbe03569d70dd394420ceb73532cf303c33 Mon Sep 17 00:00:00 2001
+From: Glen Huang <curvedmark@gmail.com>
+Date: Sat, 27 Dec 2014 15:28:12 +0000
+Subject: [PATCH 21/55] Add --ignore-address option.
+
+---
+ CHANGELOG     |  8 ++++++++
+ man/dnsmasq.8 |  6 ++++++
+ src/dnsmasq.h |  3 ++-
+ src/forward.c |  4 ++++
+ src/option.c  | 18 +++++++++++++++---
+ src/rfc1035.c | 37 +++++++++++++++++++++++++++++++++++++
+ 6 files changed, 72 insertions(+), 4 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 956b71a151db..2b6356bcfb02 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -22,6 +22,14 @@ version 2.73
+           Fix crash in DNSSEC code with long RRs. Thanks to Marco Davids
+           for the bug report.
++          
++          Add --ignore-address option. Ignore replies to A-record 
++          queries which include the specified address. No error is
++          generated, dnsmasq simply continues to listen for another 
++          reply. This is useful to defeat blocking strategies which
++          rely on quickly supplying a forged answer to a DNS 
++          request for certain domains, before the correct answer can
++            arrive. Thanks to Glen Huang for the patch.
+       
+ version 2.72
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index 0b8e04f0a897..4236ba307df3 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -293,6 +293,12 @@ an advertising web page in response to queries for unregistered names,
+ instead of the correct NXDOMAIN response. This option tells dnsmasq to
+ fake the correct response when it sees this behaviour. As at Sept 2003
+ the IP address being returned by Verisign is 64.94.110.11
++.TP 
++.B \-B, --ignore-address=<ipaddr>
++Ignore replies to A-record queries which include the specified address. 
++No error is generated, dnsmasq simply continues to listen for another reply. 
++This is useful to defeat blocking strategies which rely on quickly supplying a
++forged answer to a DNS request for certain domain, before the correct answer can arrive.
+ .TP
+ .B \-f, --filterwin2k
+ Later versions of windows make periodic DNS requests which don't get sensible answers from
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 1dd61c5edba3..7bc982ddf73c 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -930,7 +930,7 @@ extern struct daemon {
+   char *runfile; 
+   char *lease_change_command;
+   struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
+-  struct bogus_addr *bogus_addr;
++  struct bogus_addr *bogus_addr, *ignore_addr;
+   struct server *servers;
+   struct ipsets *ipsets;
+   int log_fac; /* log facility */
+@@ -1093,6 +1093,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
+                     time_t now, int *ad_reqd, int *do_bit);
+ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, 
+                            struct bogus_addr *addr, time_t now);
++int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr);
+ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
+                                size_t *len, unsigned char **p, int *is_sign);
+ int check_for_local_domain(char *name, time_t now);
+diff --git a/src/forward.c b/src/forward.c
+index 408a179a20f4..f28c7d51f708 100644
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -724,6 +724,10 @@ void reply_query(int fd, int family, time_t now)
+   if (!(forward = lookup_frec(ntohs(header->id), hash)))
+     return;
+   
++  if (daemon->ignore_addr && RCODE(header) == NOERROR &&
++      check_for_ignored_address(header, n, daemon->ignore_addr))
++    return;
++
+   if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
+       !option_bool(OPT_ORDER) &&
+       forward->forwardall == 0)
+diff --git a/src/option.c b/src/option.c
+index 209fa6976609..907d0cf88de9 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -147,6 +147,7 @@ struct myoption {
+ #define LOPT_LOCAL_SERVICE 335
+ #define LOPT_DNSSEC_TIME   336
+ #define LOPT_LOOP_DETECT   337
++#define LOPT_IGNORE_ADDR   338
+ #ifdef HAVE_GETOPT_LONG
+ static const struct option opts[] =  
+@@ -181,6 +182,7 @@ static const struct myoption opts[] =
+     { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
+     { "bogus-priv", 0, 0, 'b' },
+     { "bogus-nxdomain", 1, 0, 'B' },
++    { "ignore-address", 1, 0, LOPT_IGNORE_ADDR },
+     { "selfmx", 0, 0, 'e' },
+     { "filterwin2k", 0, 0, 'f' },
+     { "pid-file", 2, 0, 'x' },
+@@ -457,6 +459,7 @@ static struct {
+   { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
+   { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks"), NULL },
+   { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops"), NULL },
++  { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, 
+   { 0, 0, NULL, NULL, NULL }
+ }; 
+@@ -2119,14 +2122,23 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+       break;
+       
+     case 'B':  /* --bogus-nxdomain */
+-      {
++    case LOPT_IGNORE_ADDR: /* --ignore-address */
++     {
+       struct in_addr addr;
+       unhide_metas(arg);
+       if (arg && (inet_pton(AF_INET, arg, &addr) > 0))
+         {
+           struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
+-          baddr->next = daemon->bogus_addr;
+-          daemon->bogus_addr = baddr;
++          if (option == 'B')
++            {
++              baddr->next = daemon->bogus_addr;
++              daemon->bogus_addr = baddr;
++            }
++          else
++            {
++              baddr->next = daemon->ignore_addr;
++              daemon->ignore_addr = baddr;
++            }
+           baddr->addr = addr;
+         }
+       else
+diff --git a/src/rfc1035.c b/src/rfc1035.c
+index bdeb3fb10e68..75c4266b47dd 100644
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1328,6 +1328,43 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
+   return 0;
+ }
++int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr)
++{
++  unsigned char *p;
++  int i, qtype, qclass, rdlen;
++  struct bogus_addr *baddrp;
++
++  /* skip over questions */
++  if (!(p = skip_questions(header, qlen)))
++    return 0; /* bad packet */
++
++  for (i = ntohs(header->ancount); i != 0; i--)
++    {
++      if (!(p = skip_name(p, header, qlen, 10)))
++      return 0; /* bad packet */
++      
++      GETSHORT(qtype, p); 
++      GETSHORT(qclass, p);
++      p += 4; /* TTL */
++      GETSHORT(rdlen, p);
++      
++      if (qclass == C_IN && qtype == T_A)
++      {
++        if (!CHECK_LEN(header, p, qlen, INADDRSZ))
++          return 0;
++        
++        for (baddrp = baddr; baddrp; baddrp = baddrp->next)
++          if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
++            return 1;
++      }
++      
++      if (!ADD_RDLEN(header, p, qlen, rdlen))
++      return 0;
++    }
++  
++  return 0;
++}
++
+ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp, 
+                       unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
+ {
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0022-Bad-packet-protection.patch b/src/patches/dnsmasq/0022-Bad-packet-protection.patch
new file mode 100644 (file)
index 0000000..1b37202
--- /dev/null
@@ -0,0 +1,25 @@
+From 0b1008d367d44e77352134a4c5178f896f0db3e7 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sat, 27 Dec 2014 15:33:32 +0000
+Subject: [PATCH 22/55] Bad packet protection.
+
+---
+ src/dnssec.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/dnssec.c b/src/dnssec.c
+index ed8cf893bad2..026794b077e5 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -805,7 +805,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
+               {
+                 while (*name_start != '.' && *name_start != 0)
+                   name_start++;
+-                if (k != 1)
++                if (k != 1 && *name_start == '.')
+                   name_start++;
+               }
+             
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0023-Fix-build-failure-in-new-inotify-code-on-BSD.patch b/src/patches/dnsmasq/0023-Fix-build-failure-in-new-inotify-code-on-BSD.patch
new file mode 100644 (file)
index 0000000..3bc3f79
--- /dev/null
@@ -0,0 +1,29 @@
+From d310ab7ecbffce79d3d90debba621e0222f9bced Mon Sep 17 00:00:00 2001
+From: Matthias Andree <matthias.andree@gmx.de>
+Date: Sat, 27 Dec 2014 15:36:38 +0000
+Subject: [PATCH 23/55] Fix build failure in new inotify code on BSD.
+
+---
+ src/inotify.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/inotify.c b/src/inotify.c
+index 960bf5efb41f..83730008c11b 100644
+--- a/src/inotify.c
++++ b/src/inotify.c
+@@ -15,10 +15,10 @@
+ */
+ #include "dnsmasq.h"
+-#include <sys/inotify.h>
+-
+ #ifdef HAVE_LINUX_NETWORK
++#include <sys/inotify.h>
++
+ /* the strategy is to set a inotify on the directories containing
+    resolv files, for any files in the directory which are close-write 
+    or moved into the directory.
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0024-Implement-makefile-dependencies-on-COPTS-variable.patch b/src/patches/dnsmasq/0024-Implement-makefile-dependencies-on-COPTS-variable.patch
new file mode 100644 (file)
index 0000000..e3074fa
--- /dev/null
@@ -0,0 +1,68 @@
+From 81c538efcebfce2ce4a1d3a420b6c885b8f08df9 Mon Sep 17 00:00:00 2001
+From: Yousong Zhou <yszhou4tech@gmail.com>
+Date: Sat, 3 Jan 2015 16:36:14 +0000
+Subject: [PATCH 24/55] Implement makefile dependencies on COPTS variable.
+
+---
+ .gitignore |  2 +-
+ Makefile   | 10 ++++++----
+ 2 files changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/.gitignore b/.gitignore
+index fcdbcbd135ae..23f11488ab4c 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -3,7 +3,7 @@ src/*.mo
+ src/dnsmasq.pot
+ src/dnsmasq
+ src/dnsmasq_baseline
+-src/.configured
++src/.copts_*
+ contrib/wrt/dhcp_lease_time
+ contrib/wrt/dhcp_release
+ debian/base/
+diff --git a/Makefile b/Makefile
+index c340f1c7b59a..5675f60c2036 100644
+--- a/Makefile
++++ b/Makefile
+@@ -64,6 +64,8 @@ nettle_libs =   `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG
+ gmp_libs =      `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp`
+ sunos_libs =    `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
+ version =     -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
++copts_conf = .copts_$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | \
++                      ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
+ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
+        dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
+@@ -83,7 +85,7 @@ all : $(BUILDDIR)
+ mostly_clean :
+       rm -f $(BUILDDIR)/*.mo $(BUILDDIR)/*.pot 
+-      rm -f $(BUILDDIR)/.configured $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq 
++      rm -f $(BUILDDIR)/.copts_* $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq
+ clean : mostly_clean
+       rm -f $(BUILDDIR)/dnsmasq_baseline
+@@ -139,8 +141,8 @@ bloatcheck : $(BUILDDIR)/dnsmasq_baseline mostly_clean all
+ # rules below are targets in recusive makes with cwd=$(BUILDDIR)
+-.configured: $(hdrs)
+-      @rm -f *.o
++$(copts_conf): $(hdrs)
++      @rm -f *.o .copts_*
+       @touch $@
+ $(objs:.o=.c) $(hdrs):
+@@ -149,7 +151,7 @@ $(objs:.o=.c) $(hdrs):
+ .c.o:
+       $(CC) $(CFLAGS) $(COPTS) $(i18n) $(build_cflags) $(RPM_OPT_FLAGS) -c $< 
+-dnsmasq : .configured $(hdrs) $(objs)
++dnsmasq : $(copts_conf) $(hdrs) $(objs)
+       $(CC) $(LDFLAGS) -o $@ $(objs) $(build_libs) $(LIBS) 
+ dnsmasq.pot : $(objs:.o=.c) $(hdrs)
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0025-Fix-race-condition-issue-in-makefile.patch b/src/patches/dnsmasq/0025-Fix-race-condition-issue-in-makefile.patch
new file mode 100644 (file)
index 0000000..b1c42ae
--- /dev/null
@@ -0,0 +1,30 @@
+From d8dbd903d024f84a149dac2f8a674a68dfed47a3 Mon Sep 17 00:00:00 2001
+From: Yousong Zhou <yszhou4tech@gmail.com>
+Date: Mon, 5 Jan 2015 17:03:35 +0000
+Subject: [PATCH 25/55] Fix race condition issue in makefile.
+
+---
+ Makefile | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/Makefile b/Makefile
+index 5675f60c2036..bcbd5571671d 100644
+--- a/Makefile
++++ b/Makefile
+@@ -148,10 +148,12 @@ $(copts_conf): $(hdrs)
+ $(objs:.o=.c) $(hdrs):
+       ln -s $(top)/$(SRC)/$@ .
++$(objs): $(copts_conf) $(hdrs)
++
+ .c.o:
+       $(CC) $(CFLAGS) $(COPTS) $(i18n) $(build_cflags) $(RPM_OPT_FLAGS) -c $< 
+-dnsmasq : $(copts_conf) $(hdrs) $(objs)
++dnsmasq : $(objs)
+       $(CC) $(LDFLAGS) -o $@ $(objs) $(build_libs) $(LIBS) 
+ dnsmasq.pot : $(objs:.o=.c) $(hdrs)
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0026-DNSSEC-do-top-down-search-for-limit-of-secure-delega.patch b/src/patches/dnsmasq/0026-DNSSEC-do-top-down-search-for-limit-of-secure-delega.patch
new file mode 100644 (file)
index 0000000..7f01ee7
--- /dev/null
@@ -0,0 +1,792 @@
+From 97e618a0e3f29465acc689d87288596b006f197e Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Wed, 7 Jan 2015 21:55:43 +0000
+Subject: [PATCH 26/55] DNSSEC: do top-down search for limit of secure
+ delegation.
+
+---
+ CHANGELOG     |   9 ++
+ src/dnsmasq.h |  11 +-
+ src/dnssec.c  |  91 +++++++++-------
+ src/forward.c | 327 +++++++++++++++++++++++++++++++++-------------------------
+ 4 files changed, 260 insertions(+), 178 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 2b6356bcfb02..e8bf80f81baa 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -31,7 +31,16 @@ version 2.73
+           request for certain domains, before the correct answer can
+             arrive. Thanks to Glen Huang for the patch.
+       
++          Revisit the part of DNSSEC validation which determines if an 
++          unsigned answer is legit, or is in some part of the DNS 
++          tree which should be signed. Dnsmasq now works from the 
++          DNS root downward looking for the limit of signed 
++          delegations, rather than working bottom up. This is 
++          both more correct, and less likely to trip over broken 
++          nameservers in the unsigned parts of the DNS tree 
++          which don't respond well to DNSSEC queries.
++      
+ version 2.72
+             Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 7bc982ddf73c..2f4597294a56 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -569,8 +569,9 @@ struct hostsfile {
+ #define STAT_SECURE_WILDCARD    7
+ #define STAT_NO_SIG             8
+ #define STAT_NO_DS              9
+-#define STAT_NEED_DS_NEG       10
+-#define STAT_CHASE_CNAME       11
++#define STAT_NO_NS             10
++#define STAT_NEED_DS_NEG       11
++#define STAT_CHASE_CNAME       12
+ #define FREC_NOREBIND           1
+ #define FREC_CHECKING_DISABLED  2
+@@ -604,7 +605,9 @@ struct frec {
+ #ifdef HAVE_DNSSEC 
+   int class, work_counter;
+   struct blockdata *stash; /* Saved reply, whilst we validate */
+-  size_t stash_len;
++  struct blockdata *orig_domain; /* domain of original query, whilst
++                                  we're seeing is if in unsigned domain */
++  size_t stash_len, name_start, name_len;
+   struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
+   struct frec *blocking_query; /* Query which is blocking us. */
+ #endif
+@@ -1126,7 +1129,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
+ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr);
+ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
+ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
+-int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer);
++int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer, int *nons);
+ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
+ int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
+ size_t filter_rrsigs(struct dns_header *header, size_t plen);
+diff --git a/src/dnssec.c b/src/dnssec.c
+index 026794b077e5..8f27677628b2 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -875,8 +875,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
+ /* The DNS packet is expected to contain the answer to a DNSKEY query.
+    Put all DNSKEYs in the answer which are valid into the cache.
+    return codes:
+-         STAT_INSECURE No DNSKEYs in reply.
+-       STAT_SECURE   At least one valid DNSKEY found and in cache.
++         STAT_SECURE   At least one valid DNSKEY found and in cache.
+        STAT_BOGUS    No DNSKEYs found, which  can be validated with DS,
+                      or self-sign for DNSKEY RRset is not valid, bad packet.
+        STAT_NEED_DS  DS records to validate a key not found, name in keyname 
+@@ -896,11 +895,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
+   GETSHORT(qtype, p);
+   GETSHORT(qclass, p);
+   
+-  if (qtype != T_DNSKEY || qclass != class)
++  if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
+     return STAT_BOGUS;
+-  
+-  if (ntohs(header->ancount) == 0)
+-    return STAT_INSECURE;
+   /* See if we have cached a DS record which validates this key */
+   if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
+@@ -1103,17 +1099,17 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
+ /* The DNS packet is expected to contain the answer to a DS query
+    Put all DSs in the answer which are valid into the cache.
+    return codes:
+-   STAT_INSECURE    no DS in reply or not signed.
+    STAT_SECURE      At least one valid DS found and in cache.
+    STAT_NO_DS       It's proved there's no DS here.
+-   STAT_BOGUS       At least one DS found, which fails validation, bad packet.
++   STAT_NO_NS       It's proved there's no DS _or_ NS here.
++   STAT_BOGUS       no DS in reply or not signed, fails validation, bad packet.
+    STAT_NEED_DNSKEY DNSKEY records to validate a DS not found, name in keyname
+ */
+ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
+ {
+   unsigned char *p = (unsigned char *)(header+1);
+-  int qtype, qclass, val, i, neganswer;
++  int qtype, qclass, val, i, neganswer, nons;
+   if (ntohs(header->qdcount) != 1 ||
+       !(p = skip_name(p, header, plen, 4)))
+@@ -1125,32 +1121,39 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
+   if (qtype != T_DS || qclass != class)
+     val = STAT_BOGUS;
+   else
+-    val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, &neganswer);
+-
+-  if (val == STAT_NO_SIG)
+-    val = STAT_INSECURE;
++    val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, &neganswer, &nons);
++  /* Note dnssec_validate_reply() will have cached positive answers */
++  
++  if (val == STAT_NO_SIG || val == STAT_INSECURE)
++    val = STAT_BOGUS;
+   
+   p = (unsigned char *)(header+1);
+   extract_name(header, plen, &p, name, 1, 4);
+   p += 4; /* qtype, qclass */
+   
+   if (!(p = skip_section(p, ntohs(header->ancount), header, plen)))
+-    return STAT_BOGUS;
++    val = STAT_BOGUS;
+   
+   if (val == STAT_BOGUS)
+-    log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
+-  
+-  if ((val == STAT_SECURE || val == STAT_INSECURE) && neganswer)
+     {
+-      int rdlen, flags = F_FORWARD | F_DS | F_NEG;
++      log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
++      return STAT_BOGUS;
++    }
++
++  /* By here, the answer is proved secure, and a positive answer has been cached. */
++  if (val == STAT_SECURE && neganswer)
++    {
++      int rdlen, flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
+       unsigned long ttl, minttl = ULONG_MAX;
+       struct all_addr a;
+       if (RCODE(header) == NXDOMAIN)
+       flags |= F_NXDOMAIN;
+       
+-      if (val == STAT_SECURE)
+-      flags |= F_DNSSECOK;
++      /* We only cache validated DS records, DNSSECOK flag hijacked 
++       to store presence/absence of NS. */
++      if (nons)
++      flags &= ~F_DNSSECOK;
+       
+       for (i = ntohs(header->nscount); i != 0; i--)
+       {
+@@ -1196,10 +1199,12 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
+         a.addr.dnssec.class = class;
+         cache_insert(name, &a, now, ttl, flags);
+         
+-        cache_end_insert(); 
++        cache_end_insert();  
++        
++        log_query(F_UPSTREAM, name, NULL, nons ? "no delegation" : "no DS");
+       }
+-      return (val == STAT_SECURE) ? STAT_NO_DS : STAT_INSECURE; 
++      return nons ? STAT_NO_NS : STAT_NO_DS; 
+     }
+   return val;
+@@ -1323,12 +1328,15 @@ static int find_nsec_records(struct dns_header *header, size_t plen, unsigned ch
+ }
+ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
+-                                  char *workspace1, char *workspace2, char *name, int type)
++                                  char *workspace1, char *workspace2, char *name, int type, int *nons)
+ {
+   int i, rc, rdlen;
+   unsigned char *p, *psave;
+   int offset = (type & 0xff) >> 3;
+   int mask = 0x80 >> (type & 0x07);
++
++  if (nons)
++    *nons = 0;
+   
+   /* Find NSEC record that proves name doesn't exist */
+   for (i = 0; i < nsec_count; i++)
+@@ -1355,6 +1363,10 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
+         rdlen -= p - psave;
+         /* rdlen is now length of type map, and p points to it */
+         
++        /* If we can prove that there's no NS record, return that information. */
++        if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) == 0)
++          *nons = 1;
++        
+         while (rdlen >= 2)
+           {
+             if (!CHECK_LEN(header, p, plen, rdlen))
+@@ -1456,7 +1468,7 @@ static int base32_decode(char *in, unsigned char *out)
+ }
+ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int digest_len, unsigned char *digest, int type,
+-                              char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count)
++                              char *workspace1, char *workspace2, unsigned char **nsecs, int nsec_count, int *nons)
+ {
+   int i, hash_len, salt_len, base32_len, rdlen;
+   unsigned char *p, *psave;
+@@ -1497,6 +1509,10 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
+               if (!CHECK_LEN(header, p, plen, rdlen))
+                 return 0;
+               
++              /* If we can prove that there's no NS record, return that information. */
++              if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) == 0)
++                *nons = 1;
++              
+               while (rdlen >= 2)
+                 {
+                   if (p[0] == type >> 8)
+@@ -1533,13 +1549,16 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
+ }
+ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
+-                                   char *workspace1, char *workspace2, char *name, int type, char *wildname)
++                                   char *workspace1, char *workspace2, char *name, int type, char *wildname, int *nons)
+ {
+   unsigned char *salt, *p, *digest;
+   int digest_len, i, iterations, salt_len, base32_len, algo = 0;
+   struct nettle_hash const *hash;
+   char *closest_encloser, *next_closest, *wildcard;
+- 
++  
++  if (nons)
++    *nons = 0;
++  
+   /* Look though the NSEC3 records to find the first one with 
+      an algorithm we support (currently only algo == 1).
+@@ -1612,7 +1631,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+   if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
+     return STAT_BOGUS;
+   
+-  if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
++  if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons))
+     return STAT_SECURE;
+   /* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3" 
+@@ -1657,7 +1676,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+   if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
+     return STAT_BOGUS;
+-  if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
++  if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
+     return STAT_BOGUS;
+   
+   /* Finally, check that there's no seat of wildcard synthesis */
+@@ -1672,7 +1691,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+       if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
+       return STAT_BOGUS;
+       
+-      if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count))
++      if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
+       return STAT_BOGUS;
+     }
+   
+@@ -1681,7 +1700,8 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
+     
+ /* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
+ /* Returns are the same as validate_rrset, plus the class if the missing key is in *class */
+-int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer)
++int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, 
++                        int *class, int *neganswer, int *nons)
+ {
+   unsigned char *ans_start, *qname, *p1, *p2, **nsecs;
+   int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype;
+@@ -1811,10 +1831,11 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+                   return STAT_BOGUS; /* No NSECs or bad packet */
+                 
+                 if (nsec_type == T_NSEC)
+-                  rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
++                  rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, NULL);
+                 else
+-                  rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1, wildname);
+-
++                  rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, 
++                                                 keyname, name, type1, wildname, NULL);
++                
+                 if (rc != STAT_SECURE)
+                   return rc;
+               } 
+@@ -1937,9 +1958,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+     return STAT_BOGUS;
+   
+   if (nsec_type == T_NSEC)
+-    return prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype);
++    return prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons);
+   else
+-    return prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL);
++    return prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons);
+ }
+ /* Chase the CNAME chain in the packet until the first record which _doesn't validate.
+diff --git a/src/forward.c b/src/forward.c
+index f28c7d51f708..ee8d7b52d5e5 100644
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -26,8 +26,9 @@ static void free_frec(struct frec *f);
+ #ifdef HAVE_DNSSEC
+ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n, 
+                          int class, char *name, char *keyname, struct server *server, int *keycount);
+-static int do_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
+-static int send_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
++static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname);
++static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen, 
++                         char *name, char *keyname);
+ #endif
+@@ -815,18 +816,22 @@ void reply_query(int fd, int family, time_t now)
+         else if (forward->flags & FREC_DS_QUERY)
+           {
+             status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
+-            if (status == STAT_NO_DS)
+-              status = STAT_INSECURE;
++            if (status == STAT_NO_DS || status == STAT_NO_NS)
++              status = STAT_BOGUS;
+           }
+         else if (forward->flags & FREC_CHECK_NOSIGN)
+-          status = do_check_sign(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
++          {
++            status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
++            if (status != STAT_NEED_KEY)
++              status = do_check_sign(forward, status, now, daemon->namebuff, daemon->keyname);
++          }
+         else
+           {
+-            status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL);
++            status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL);
+             if (status == STAT_NO_SIG)
+               {
+                 if (option_bool(OPT_DNSSEC_NO_SIGN))
+-                  status = send_check_sign(now, header, n, daemon->namebuff, daemon->keyname);
++                  status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
+                 else
+                   status = STAT_INSECURE;
+               }
+@@ -861,6 +866,7 @@ void reply_query(int fd, int family, time_t now)
+                 new->blocking_query = NULL;
+                 new->sentto = server;
+                 new->rfd4 = NULL;
++                new->orig_domain = NULL;
+ #ifdef HAVE_IPV6
+                 new->rfd6 = NULL;
+ #endif
+@@ -889,7 +895,9 @@ void reply_query(int fd, int family, time_t now)
+                 new->new_id = get_id();
+                 header->id = htons(new->new_id);
+                 /* Save query for retransmission */
+-                new->stash = blockdata_alloc((char *)header, nn);
++                if (!(new->stash = blockdata_alloc((char *)header, nn)))
++                  return;
++                    
+                 new->stash_len = nn;
+                 
+                 /* Don't resend this. */
+@@ -946,18 +954,22 @@ void reply_query(int fd, int family, time_t now)
+                 else if (forward->flags & FREC_DS_QUERY)
+                   {
+                     status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
+-                    if (status == STAT_NO_DS)
+-                      status = STAT_INSECURE;
++                    if (status == STAT_NO_DS || status == STAT_NO_NS)
++                      status = STAT_BOGUS;
+                   }
+                 else if (forward->flags & FREC_CHECK_NOSIGN)
+-                  status = do_check_sign(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
++                  {
++                    status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
++                    if (status != STAT_NEED_KEY)
++                      status = do_check_sign(forward, status, now, daemon->namebuff, daemon->keyname);
++                  }
+                 else
+                   {
+-                    status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL); 
++                    status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL);   
+                     if (status == STAT_NO_SIG)
+                       {
+                         if (option_bool(OPT_DNSSEC_NO_SIGN))
+-                          status = send_check_sign(now, header, n, daemon->namebuff, daemon->keyname);
++                          status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
+                         else
+                           status = STAT_INSECURE;
+                       }
+@@ -1319,70 +1331,80 @@ void receive_query(struct listener *listen, time_t now)
+ /* UDP: we've got an unsigned answer, return STAT_INSECURE if we can prove there's no DS
+    and therefore the answer shouldn't be signed, or STAT_BOGUS if it should be, or 
+    STAT_NEED_DS_NEG and keyname if we need to do the query. */
+-static int send_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname)
++static int send_check_sign(struct frec *forward, time_t now, struct dns_header *header, size_t plen, 
++                         char *name, char *keyname)
+ {
+-  struct crec *crecp;
+-  char *name_start = name;
+   int status = dnssec_chase_cname(now, header, plen, name, keyname);
+   
+   if (status != STAT_INSECURE)
+     return status;
++  /* Store the domain we're trying to check. */
++  forward->name_start = strlen(name);
++  forward->name_len = forward->name_start + 1;
++  if (!(forward->orig_domain = blockdata_alloc(name, forward->name_len)))
++    return STAT_BOGUS;
++  
++  return do_check_sign(forward, 0, now, name, keyname);
++}
++ 
++/* We either have a a reply (header non-NULL, or we need to start by looking in the cache */ 
++static int do_check_sign(struct frec *forward, int status, time_t now, char *name, char *keyname)
++{
++  /* get domain we're checking back from blockdata store, it's stored on the original query. */
++  while (forward->dependent)
++    forward = forward->dependent;
++
++  blockdata_retrieve(forward->orig_domain, forward->name_len, name);
++  
+   while (1)
+     {
+-      crecp = cache_find_by_name(NULL, name_start, now, F_DS);
+-      
+-      if (crecp && (crecp->flags & F_DNSSECOK))
+-      return (crecp->flags & F_NEG) ? STAT_INSECURE : STAT_BOGUS;
+-       
+-      if (crecp && (crecp->flags & F_NEG) && (name_start = strchr(name_start, '.')))
++      char *p; 
++
++      if (status == 0)
+       {
+-        name_start++; /* chop a label off and try again */
+-        continue;
++        struct crec *crecp;
++
++        /* Haven't received answer, see if in cache */
++        if (!(crecp = cache_find_by_name(NULL, &name[forward->name_start], now, F_DS)))
++          {
++            /* put name of DS record we're missing into keyname */
++            strcpy(keyname, &name[forward->name_start]);
++            /* and wait for reply to arrive */
++            return STAT_NEED_DS_NEG;
++          }
++
++        /* F_DNSSECOK misused in DS cache records to non-existance of NS record */ 
++        if (!(crecp->flags & F_NEG))
++          status = STAT_SECURE;
++        else if (crecp->flags & F_DNSSECOK)
++          status = STAT_NO_DS;
++        else
++          status = STAT_NO_NS;
+       }
++      
++      /* Have entered non-signed part of DNS tree. */ 
++      if (status == STAT_NO_DS)
++      return STAT_INSECURE;
+-      /* Reached the root */
+-      if (!name_start)
++      if (status == STAT_BOGUS)
+       return STAT_BOGUS;
+-      strcpy(keyname, name_start);
+-      return STAT_NEED_DS_NEG;
+-    }
+-}
+-
+-/* Got answer to DS query from send_check_sign, check for proven non-existence, or make the next DS query to try. */
+-static int do_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
+-  
+-{ 
+-  char *name_start;
+-  unsigned char *p;
+-  int status;
++      /* There's a proven DS record, or we're within a zone, where there doesn't need
++       to be a DS record. Add a name and try again. 
++       If we've already tried the whole name, then fail */
+-  /* In this case only, a SERVFAIL reply allows us to continue up the tree, looking for a 
+-     suitable NSEC reply to DS queries. */
+-  if (RCODE(header) != SERVFAIL)
+-    { 
+-      status = dnssec_validate_ds(now, header, plen, name, keyname, class);
++      if (forward->name_start == 0)
++      return STAT_BOGUS;
+       
+-      if (status != STAT_INSECURE)
+-      {
+-        if (status == STAT_NO_DS)
+-          status = STAT_INSECURE;
+-        return status;
+-      }
+-    }
+-  
+-  p = (unsigned char *)(header+1);
+-  
+-  if (extract_name(header, plen, &p, name, 1, 4) &&
+-      (name_start = strchr(name, '.')))
+-    {
+-      name_start++; /* chop a label off and try again */
+-      strcpy(keyname, name_start);
+-      return STAT_NEED_DS_NEG;
++      for (p = &name[forward->name_start-2]; (*p != '.') && (p != name); p--);
++      
++      if (p != name)
++      p++;
++      
++      forward->name_start = p - name;
++      status = 0; /* force to cache when we iterate. */
+     }
+-  
+-  return STAT_BOGUS;
+ }
+ /* Move toward the root, until we find a signed non-existance of a DS, in which case
+@@ -1395,8 +1417,10 @@ static int  tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, s
+   unsigned char *packet, *payload;
+   u16 *length;
+   unsigned char *p = (unsigned char *)(header+1);
+-  int status;
+-  char *name_start = name;
++  int status, name_len;
++  struct blockdata *block;
++
++  char *name_start;
+   /* Get first insecure entry in CNAME chain */
+   status = tcp_key_recurse(now, STAT_CHASE_CNAME, header, plen, class, name, keyname, server, keycount);
+@@ -1409,95 +1433,113 @@ static int  tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, s
+   payload = &packet[2];
+   header = (struct dns_header *)payload;
+   length = (u16 *)packet;
++
++  /* Stash the name away, since the buffer will be trashed when we recurse */
++  name_len = strlen(name) + 1;
++  name_start = name + name_len - 1;
+   
++  if (!(block = blockdata_alloc(name, name_len)))
++    {
++      free(packet);
++      return STAT_BOGUS;
++    }
++
+   while (1)
+     {
+-      unsigned char *newhash, hash[HASH_SIZE];
+       unsigned char c1, c2;
+-      struct crec *crecp = cache_find_by_name(NULL, name_start, now, F_DS);
+- 
++      struct crec *crecp;
++
+       if (--(*keycount) == 0)
+       {
+         free(packet);
++        blockdata_free(block);
+         return STAT_BOGUS;    
+       }
+-
+-      if (crecp && (crecp->flags & F_DNSSECOK))
+-      {
+-        free(packet);
+-        return (crecp->flags & F_NEG) ? STAT_INSECURE : STAT_BOGUS;
+-      }
+       
+-      /* If we have cached insecurely that a DS doesn't exist, 
+-       ise that is a hit for where to start looking for the secure one */
+-      if (crecp && (crecp->flags & F_NEG) && (name_start = strchr(name_start, '.')))
+-      {
+-        name_start++; /* chop a label off and try again */
+-        continue;
+-      }
+-
+-      /* reached the root */
+-      if (!name_start)
+-      {
+-        free(packet);
+-        return STAT_BOGUS;
++      while (crecp = cache_find_by_name(NULL, name_start, now, F_DS))
++      {      
++        if ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK))
++          {
++            /* Found a secure denial of DS - delegation is indeed insecure */
++            free(packet);
++            blockdata_free(block);
++            return STAT_INSECURE;
++          }
++      
++        /* Here, either there's a secure DS, or no NS and no DS, and therefore no delegation.
++           Add another label and continue. */
++ 
++        if (name_start == name)
++          {
++            free(packet);
++            blockdata_free(block);
++            return STAT_BOGUS; /* run out of labels */
++          }
++        
++        name_start -= 2;
++        while (*name_start != '.' && name_start != name) 
++          name_start--;
++        if (name_start != name)
++          name_start++;
+       }
++      
++      /* Can't find it in the cache, have to send a query */
+       m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr);
+       
+-      /* We rely on the question section coming back unchanged, ensure it is with the hash. */
+-      if ((newhash = hash_questions(header, (unsigned int)m, name)))
+-      {
+-        memcpy(hash, newhash, HASH_SIZE);
++      *length = htons(m);
+       
+-        *length = htons(m);
++      if (read_write(server->tcpfd, packet, m + sizeof(u16), 0) &&
++        read_write(server->tcpfd, &c1, 1, 1) &&
++        read_write(server->tcpfd, &c2, 1, 1) &&
++        read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
++      {
++        m = (c1 << 8) | c2;
++        
++        /* Note this trashes all three name workspaces */
++        status = tcp_key_recurse(now, STAT_NEED_DS_NEG, header, m, class, name, keyname, server, keycount);
+         
+-        if (read_write(server->tcpfd, packet, m + sizeof(u16), 0) &&
+-            read_write(server->tcpfd, &c1, 1, 1) &&
+-            read_write(server->tcpfd, &c2, 1, 1) &&
+-            read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
++        if (status == STAT_NO_DS)
+           {
+-            m = (c1 << 8) | c2;
+-            
+-            newhash = hash_questions(header, (unsigned int)m, name);
+-            if (newhash && memcmp(hash, newhash, HASH_SIZE) == 0)
+-              {
+-                 /* In this case only, a SERVFAIL reply allows us to continue up the tree, looking for a 
+-                    suitable NSEC reply to DS queries. */
+-                if (RCODE(header) == SERVFAIL)
+-                  status = STAT_INSECURE;
+-                else
+-                  /* Note this trashes all three name workspaces */
+-                  status = tcp_key_recurse(now, STAT_NEED_DS_NEG, header, m, class, name, keyname, server, keycount);
+-                
+-                /* We've found a DS which proves the bit of the DNS where the
+-                   original query is, is unsigned, so the answer is OK, 
+-                   if unvalidated. */
+-                if (status == STAT_NO_DS)
+-                  {
+-                    free(packet);
+-                    return STAT_INSECURE;
+-                  }
+-            
+-                /* No DS, not got to DNSSEC-land yet, go up. */
+-                if (status == STAT_INSECURE)
+-                  {
+-                    p = (unsigned char *)(header+1);
+-                    
+-                    if (extract_name(header, plen, &p, name, 1, 4) &&
+-                        (name_start = strchr(name, '.')))
+-                      {
+-                        name_start++; /* chop a label off and try again */
+-                        continue;
+-                      }
+-                  }
+-              }
++            /* Found a secure denial of DS - delegation is indeed insecure */
++            free(packet);
++            blockdata_free(block);
++            return STAT_INSECURE;
++          }
++        
++        if (status == STAT_BOGUS)
++          {
++            free(packet);
++            blockdata_free(block);
++            return STAT_BOGUS;
++          }
++        
++        /* Here, either there's a secure DS, or no NS and no DS, and therefore no delegation.
++           Add another label and continue. */
++        
++        /* Get name we're checking back. */
++        blockdata_retrieve(block, name_len, name);
++        
++        if (name_start == name)
++          {
++            free(packet);
++            blockdata_free(block);
++            return STAT_BOGUS; /* run out of labels */
+           }
++        
++        name_start -= 2;
++        while (*name_start != '.' && name_start != name) 
++          name_start--;
++        if (name_start != name)
++          name_start++;
++      }
++      else
++      {
++        /* IO failure */
++        free(packet);
++        blockdata_free(block);
++        return STAT_BOGUS; /* run out of labels */
+       }
+-      
+-      free(packet);
+-
+-      return STAT_BOGUS;
+     }
+ }
+@@ -1516,14 +1558,14 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
+   else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
+     {
+       new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
+-      if (status == STAT_NEED_DS  && new_status == STAT_NO_DS)
+-      new_status = STAT_INSECURE;
++      if (status == STAT_NEED_DS && (new_status == STAT_NO_DS || new_status == STAT_NO_NS))
++      new_status = STAT_BOGUS;
+     }
+   else if (status == STAT_CHASE_CNAME)
+     new_status = dnssec_chase_cname(now, header, n, name, keyname);
+   else 
+     {
+-      new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL);
++      new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL, NULL);
+       
+       if (new_status == STAT_NO_SIG)
+       {
+@@ -1576,14 +1618,14 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
+             else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
+               {
+                 new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
+-                if (status == STAT_NEED_DS && new_status == STAT_NO_DS)
+-                  new_status = STAT_INSECURE; /* Validated no DS */
++                if (status == STAT_NEED_DS  && (new_status == STAT_NO_DS || new_status == STAT_NO_NS))
++                  new_status = STAT_BOGUS; /* Validated no DS */
+               }
+             else if (status == STAT_CHASE_CNAME)
+               new_status = dnssec_chase_cname(now, header, n, name, keyname);
+             else 
+               {
+-                new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL);
++                new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL, NULL);
+                 
+                 if (new_status == STAT_NO_SIG)
+                   {
+@@ -1961,6 +2003,7 @@ static struct frec *allocate_frec(time_t now)
+       f->dependent = NULL;
+       f->blocking_query = NULL;
+       f->stash = NULL;
++      f->orig_domain = NULL;
+ #endif
+       daemon->frec_list = f;
+     }
+@@ -2029,6 +2072,12 @@ static void free_frec(struct frec *f)
+       f->stash = NULL;
+     }
++  if (f->orig_domain)
++    {
++      blockdata_free(f->orig_domain);
++      f->orig_domain = NULL;
++    }
++
+   /* Anything we're waiting on is pointless now, too */
+   if (f->blocking_query)
+     free_frec(f->blocking_query);
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0027-Add-log-queries-extra-option-for-more-complete-loggi.patch b/src/patches/dnsmasq/0027-Add-log-queries-extra-option-for-more-complete-loggi.patch
new file mode 100644 (file)
index 0000000..e258d56
--- /dev/null
@@ -0,0 +1,346 @@
+From 25cf5e373eb41c088d4ee5e625209c4cf6a5659e Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Fri, 9 Jan 2015 15:53:03 +0000
+Subject: [PATCH 27/55] Add --log-queries=extra option for more complete
+ logging.
+
+---
+ CHANGELOG     |  3 +++
+ man/dnsmasq.8 |  5 ++++-
+ src/cache.c   | 11 ++++++++++-
+ src/config.h  |  1 +
+ src/dnsmasq.c |  5 +++++
+ src/dnsmasq.h |  9 +++++++--
+ src/dnssec.c  | 14 +++++++-------
+ src/forward.c | 30 ++++++++++++++++++++++++++----
+ src/option.c  | 11 +++++++++--
+ 9 files changed, 72 insertions(+), 17 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index e8bf80f81baa..0bbb7835df4f 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -40,6 +40,9 @@ version 2.73
+           nameservers in the unsigned parts of the DNS tree 
+           which don't respond well to DNSSEC queries.
++          Add --log-queries=extra option, which makes logs easier
++          to search automatically.
++
+       
+ version 2.72
+             Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index 4236ba307df3..227d74bd80e7 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -98,7 +98,10 @@ only, to stop dnsmasq daemonising in production, use
+ .B -k.
+ .TP
+ .B \-q, --log-queries
+-Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1.
++Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1. If the argument "extra" is supplied, ie
++.B --log-queries=extra
++then the log has extra information at the start of each line.
++This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor.
+ .TP
+ .B \-8, --log-facility=<facility>
+ Set the facility to which dnsmasq will send syslog entries, this
+diff --git a/src/cache.c b/src/cache.c
+index ff1ca6f1c352..960bb7938778 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -1638,7 +1638,16 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
+   if (strlen(name) == 0)
+     name = ".";
+-  my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
++  if (option_bool(OPT_EXTRALOG))
++    {
++      prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
++      if (flags & F_NOEXTRA)
++      my_syslog(LOG_INFO, "* %s %s %s %s %s", daemon->addrbuff2, source, name, verb, dest);
++      else
++      my_syslog(LOG_INFO, "%u %s %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, source, name, verb, dest);
++    }
++  else
++    my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
+ }
+  
+diff --git a/src/config.h b/src/config.h
+index 145820ad2510..3b88d8193dca 100644
+--- a/src/config.h
++++ b/src/config.h
+@@ -17,6 +17,7 @@
+ #define FTABSIZ 150 /* max number of outstanding requests (default) */
+ #define MAX_PROCS 20 /* max no children for TCP requests */
+ #define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
++#define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */
+ #define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
+ #define KEYBLOCK_LEN 40 /* choose to mininise fragmentation when storing DNSSEC keys */
+ #define DNSSEC_WORK 50 /* Max number of queries to validate one question */
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index 5c7750d365fa..c0c0589d4ce1 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -93,6 +93,8 @@ int main (int argc, char **argv)
+   daemon->packet = safe_malloc(daemon->packet_buff_sz);
+   
+   daemon->addrbuff = safe_malloc(ADDRSTRLEN);
++  if (option_bool(OPT_EXTRALOG))
++    daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
+   
+ #ifdef HAVE_DNSSEC
+   if (option_bool(OPT_DNSSEC_VALID))
+@@ -1587,6 +1589,9 @@ static void check_dns_listeners(fd_set *set, time_t now)
+                     }
+               }
+             close(confd);
++
++            /* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
++            daemon->log_id += TCP_MAX_QUERIES;
+           }
+ #endif
+         else
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 2f4597294a56..4e9aea401b75 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -238,7 +238,8 @@ struct event_desc {
+ #define OPT_DNSSEC_NO_SIGN 48 
+ #define OPT_LOCAL_SERVICE  49
+ #define OPT_LOOP_DETECT    50
+-#define OPT_LAST           51
++#define OPT_EXTRALOG       51
++#define OPT_LAST           52
+ /* 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. */
+@@ -442,6 +443,7 @@ struct crec {
+ #define F_NO_RR     (1u<<25)
+ #define F_IPSET     (1u<<26)
+ #define F_NSIGMATCH (1u<<27)
++#define F_NOEXTRA   (1u<<28)
+ /* Values of uid in crecs with F_CONFIG bit set. */
+ #define SRC_INTERFACE 0
+@@ -599,7 +601,7 @@ struct frec {
+ #endif
+   unsigned int iface;
+   unsigned short orig_id, new_id;
+-  int fd, forwardall, flags;
++  int log_id, fd, forwardall, flags;
+   time_t time;
+   unsigned char *hash[HASH_SIZE];
+ #ifdef HAVE_DNSSEC 
+@@ -1002,6 +1004,8 @@ extern struct daemon {
+   struct randfd randomsocks[RANDOM_SOCKS];
+   int v6pktinfo; 
+   struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
++  int log_id, log_display_id; /* ids of transactions for logging */
++  union mysockaddr *log_source_addr;
+   /* DHCP state */
+   int dhcpfd, helperfd, pxefd; 
+@@ -1033,6 +1037,7 @@ extern struct daemon {
+   /* utility string buffer, hold max sized IP address as string */
+   char *addrbuff;
++  char *addrbuff2; /* only allocated when OPT_EXTRALOG */
+ } *daemon;
+diff --git a/src/dnssec.c b/src/dnssec.c
+index 8f27677628b2..afb3dca38cb1 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -1038,7 +1038,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
+                     else
+                       {
+                         a.addr.keytag = keytag;
+-                        log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
++                        log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
+                         
+                         recp1->addr.key.keylen = rdlen - 4;
+                         recp1->addr.key.keydata = key;
+@@ -1092,7 +1092,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
+       return STAT_SECURE;
+     }
+-  log_query(F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
++  log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
+   return STAT_BOGUS;
+ }
+@@ -1136,7 +1136,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
+   
+   if (val == STAT_BOGUS)
+     {
+-      log_query(F_UPSTREAM, name, NULL, "BOGUS DS");
++      log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
+       return STAT_BOGUS;
+     }
+@@ -1201,7 +1201,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
+         
+         cache_end_insert();  
+         
+-        log_query(F_UPSTREAM, name, NULL, nons ? "no delegation" : "no DS");
++        log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no delegation" : "no DS");
+       }
+       return nons ? STAT_NO_NS : STAT_NO_DS; 
+@@ -1885,7 +1885,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+                             else
+                               {
+                                 a.addr.keytag = keytag;
+-                                log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
++                                log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
+                                 crecp->addr.ds.digest = digest;
+                                 crecp->addr.ds.keydata = key;
+                                 crecp->addr.ds.algo = algo;
+@@ -2058,10 +2058,10 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
+   char *types = querystr("dnssec-query", type);
+   if (addr->sa.sa_family == AF_INET) 
+-    log_query(F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types);
++    log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types);
+ #ifdef HAVE_IPV6
+   else
+-    log_query(F_DNSSEC | F_IPV6, name, (struct all_addr *)&addr->in6.sin6_addr, types);
++    log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, name, (struct all_addr *)&addr->in6.sin6_addr, types);
+ #endif
+   
+   header->qdcount = htons(1);
+diff --git a/src/forward.c b/src/forward.c
+index 55f583383bc6..713a64c0fa58 100644
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -279,10 +279,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
+         plen = forward->stash_len;
+         
+         if (forward->sentto->addr.sa.sa_family == AF_INET) 
+-          log_query(F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
++          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_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
++          log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
+ #endif
+   
+         if (forward->sentto->sfd)
+@@ -389,6 +389,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
+       struct server *firstsentto = start;
+       int forwarded = 0;
+       
++      /* If a query is retried, use the log_id for the retry when logging the answer. */
++      forward->log_id = daemon->log_id;
++      
+       if (option_bool(OPT_ADD_MAC))
+       plen = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
+       
+@@ -725,6 +728,11 @@ void reply_query(int fd, int family, time_t now)
+   if (!(forward = lookup_frec(ntohs(header->id), hash)))
+     return;
+   
++  /* log_query gets called indirectly all over the place, so 
++     pass these in global variables - sorry. */
++  daemon->log_display_id = forward->log_id;
++  daemon->log_source_addr = &forward->source;
++  
+   if (daemon->ignore_addr && RCODE(header) == NOERROR &&
+       check_for_ignored_address(header, n, daemon->ignore_addr))
+     return;
+@@ -1258,6 +1266,11 @@ void receive_query(struct listener *listen, time_t now)
+           dst_addr_4.s_addr = 0;
+       }
+     }
++   
++  /* log_query gets called indirectly all over the place, so 
++     pass these in global variables - sorry. */
++  daemon->log_display_id = ++daemon->log_id;
++  daemon->log_source_addr = &source_addr;
+   
+   if (extract_request(header, (size_t)n, daemon->namebuff, &type))
+     {
+@@ -1675,7 +1688,8 @@ unsigned char *tcp_request(int confd, time_t now,
+   struct in_addr dst_addr_4;
+   union mysockaddr peer_addr;
+   socklen_t peer_len = sizeof(union mysockaddr);
+-  
++  int query_count = 0;
++
+   if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
+     return packet;
+   
+@@ -1712,7 +1726,8 @@ unsigned char *tcp_request(int confd, time_t now,
+   while (1)
+     {
+-      if (!packet ||
++      if (query_count == TCP_MAX_QUERIES ||
++        !packet ||
+         !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
+         !(size = c1 << 8 | c2) ||
+         !read_write(confd, payload, size, 1))
+@@ -1721,6 +1736,13 @@ unsigned char *tcp_request(int confd, time_t now,
+       if (size < (int)sizeof(struct dns_header))
+       continue;
+       
++      query_count++;
++
++      /* log_query gets called indirectly all over the place, so 
++       pass these in global variables - sorry. */
++      daemon->log_display_id = ++daemon->log_id;
++      daemon->log_source_addr = &peer_addr;
++      
+       check_subnet = 0;
+       /* save state of "cd" flag in query */
+diff --git a/src/option.c b/src/option.c
+index 907d0cf88de9..b7372be0a090 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -149,6 +149,7 @@ struct myoption {
+ #define LOPT_LOOP_DETECT   337
+ #define LOPT_IGNORE_ADDR   338
++
+ #ifdef HAVE_GETOPT_LONG
+ static const struct option opts[] =  
+ #else
+@@ -160,7 +161,7 @@ static const struct myoption opts[] =
+     { "no-poll", 0, 0, 'n' },
+     { "help", 0, 0, 'w' },
+     { "no-daemon", 0, 0, 'd' },
+-    { "log-queries", 0, 0, 'q' },
++    { "log-queries", 2, 0, 'q' },
+     { "user", 2, 0, 'u' },
+     { "group", 2, 0, 'g' },
+     { "resolv-file", 2, 0, 'r' },
+@@ -357,7 +358,7 @@ static struct {
+   { LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
+   { 'p', ARG_ONE, "<integer>", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
+   { 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
+-  { 'q', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL },
++  { 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
+   { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
+   { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
+   { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE }, 
+@@ -2421,6 +2422,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+       ret_err(gen_err);
+       break;  
+     
++    case 'q': /* --log-queries */
++      set_option_bool(OPT_LOG);
++      if (arg && strcmp(arg, "extra") == 0)
++      set_option_bool(OPT_EXTRALOG);
++      break;
++
+     case LOPT_MAX_LOGS:  /* --log-async */
+       daemon->max_logs = LOG_MAX; /* default */
+       if (arg && !atoi_check(arg, &daemon->max_logs))
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0028-Add-min-cache-ttl-option.patch b/src/patches/dnsmasq/0028-Add-min-cache-ttl-option.patch
new file mode 100644 (file)
index 0000000..e8a61b7
--- /dev/null
@@ -0,0 +1,144 @@
+From 28de38768e2c7d763b9aa5b7a4d251d5e56bab0b Mon Sep 17 00:00:00 2001
+From: RinSatsuki <aa65535@live.com>
+Date: Sat, 10 Jan 2015 15:22:21 +0000
+Subject: [PATCH 28/55] Add --min-cache-ttl option.
+
+---
+ CHANGELOG     |  7 +++++++
+ man/dnsmasq.8 |  6 ++++++
+ src/cache.c   |  4 +++-
+ src/config.h  |  1 +
+ src/dnsmasq.h |  2 +-
+ src/option.c  | 11 +++++++++++
+ 6 files changed, 29 insertions(+), 2 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 0bbb7835df4f..23fc6d0530cf 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -43,6 +43,13 @@ version 2.73
+           Add --log-queries=extra option, which makes logs easier
+           to search automatically.
++          Add --min-cache-ttl option. I've resisted this for a long 
++          time, on the grounds that disbelieving TTLs is never a 
++          good idea, but I've been persuaded that there are 
++          sometimes reasons to do it. (Step forward, GFW).
++          To avoid misuse, there's a hard limit on the TTL 
++          floor of one hour. Thansk to RinSatsuki for the patch.
++      
+       
+ version 2.72
+             Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index 227d74bd80e7..5cfa355dea4a 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -81,6 +81,12 @@ the upstream DNS servers.
+ .B --max-cache-ttl=<time>
+ Set a maximum TTL value for entries in the cache.
+ .TP
++.B --min-cache-ttl=<time>
++Extend short TTL values to the time given when caching them. Note that
++artificially extending TTL values is in general a bad idea, do not do it 
++unless you have a good reason, and understand what you are doing. 
++Dnsmasq limits the value of this option to one hour, unless recompiled.
++.TP
+ .B --auth-ttl=<time>
+ Set the TTL value returned in answers from the authoritative server.
+ .TP
+diff --git a/src/cache.c b/src/cache.c
+index 960bb7938778..945be071a0b6 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -461,9 +461,11 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
+   if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
+     {
+       log_query(flags | F_UPSTREAM, name, addr, NULL);
+-      /* Don;t mess with TTL for DNSSEC records. */
++      /* Don't mess with TTL for DNSSEC records. */
+       if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
+       ttl = daemon->max_cache_ttl;
++      if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
++      ttl = daemon->min_cache_ttl;
+     }
+   /* if previous insertion failed give up now. */
+diff --git a/src/config.h b/src/config.h
+index 3b88d8193dca..cdca231b4079 100644
+--- a/src/config.h
++++ b/src/config.h
+@@ -27,6 +27,7 @@
+ #define RANDOM_SOCKS 64 /* max simultaneous random ports */
+ #define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
+ #define CACHESIZ 150 /* default cache size */
++#define TTL_FLOOR_LIMIT 3600 /* don't allow --min-cache-ttl to raise TTL above this under any circumstances */
+ #define MAXLEASES 1000 /* maximum number of DHCP leases */
+ #define PING_WAIT 3 /* wait for ping address-in-use test */
+ #define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 4e9aea401b75..f8275e3ac479 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -943,7 +943,7 @@ extern struct daemon {
+   int max_logs;  /* queue limit */
+   int cachesize, ftabsize;
+   int port, query_port, min_port;
+-  unsigned long local_ttl, neg_ttl, max_ttl, max_cache_ttl, auth_ttl;
++  unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl;
+   struct hostsfile *addn_hosts;
+   struct dhcp_context *dhcp, *dhcp6;
+   struct ra_interface *ra_interfaces;
+diff --git a/src/option.c b/src/option.c
+index b7372be0a090..8b994098cc9f 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -148,6 +148,7 @@ struct myoption {
+ #define LOPT_DNSSEC_TIME   336
+ #define LOPT_LOOP_DETECT   337
+ #define LOPT_IGNORE_ADDR   338
++#define LOPT_MINCTTL       339
+ #ifdef HAVE_GETOPT_LONG
+@@ -256,6 +257,7 @@ static const struct myoption opts[] =
+     { "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
+     { "neg-ttl", 1, 0, LOPT_NEGTTL },
+     { "max-ttl", 1, 0, LOPT_MAXTTL },
++    { "min-cache-ttl", 1, 0, LOPT_MINCTTL },
+     { "max-cache-ttl", 1, 0, LOPT_MAXCTTL },
+     { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
+     { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
+@@ -371,6 +373,8 @@ static struct {
+   { 'T', ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
+   { LOPT_NEGTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
+   { LOPT_MAXTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
++  { LOPT_MAXCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live ceiling for cache."), NULL },
++  { LOPT_MINCTTL, ARG_ONE, "<integer>", gettext_noop("Specify time-to-live floor for cache."), NULL },
+   { 'u', ARG_ONE, "<username>", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER }, 
+   { 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
+   { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
+@@ -2457,6 +2461,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+     case 'T':         /* --local-ttl */
+     case LOPT_NEGTTL: /* --neg-ttl */
+     case LOPT_MAXTTL: /* --max-ttl */
++    case LOPT_MINCTTL: /* --min-cache-ttl */
+     case LOPT_MAXCTTL: /* --max-cache-ttl */
+     case LOPT_AUTHTTL: /* --auth-ttl */
+       {
+@@ -2467,6 +2472,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+         daemon->neg_ttl = (unsigned long)ttl;
+       else if (option == LOPT_MAXTTL)
+         daemon->max_ttl = (unsigned long)ttl;
++      else if (option == LOPT_MINCTTL)
++        {
++          if (ttl > TTL_FLOOR_LIMIT)
++            ttl = TTL_FLOOR_LIMIT;
++          daemon->min_cache_ttl = (unsigned long)ttl;
++        }
+       else if (option == LOPT_MAXCTTL)
+         daemon->max_cache_ttl = (unsigned long)ttl;
+       else if (option == LOPT_AUTHTTL)
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0029-Log-port-of-requestor-when-doing-extra-logging.patch b/src/patches/dnsmasq/0029-Log-port-of-requestor-when-doing-extra-logging.patch
new file mode 100644 (file)
index 0000000..d3c4847
--- /dev/null
@@ -0,0 +1,31 @@
+From 9f79ee4ae34886c0319f06d8f162b81ef79d62fb Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 12 Jan 2015 20:18:18 +0000
+Subject: [PATCH 29/55] Log port of requestor when doing extra logging.
+
+---
+ src/cache.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/cache.c b/src/cache.c
+index 945be071a0b6..09b6dbf8087a 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -1642,11 +1642,11 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
+   if (option_bool(OPT_EXTRALOG))
+     {
+-      prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
++      int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
+       if (flags & F_NOEXTRA)
+-      my_syslog(LOG_INFO, "* %s %s %s %s %s", daemon->addrbuff2, source, name, verb, dest);
++      my_syslog(LOG_INFO, "* %s/%u %s %s %s %s", daemon->addrbuff2, port, source, name, verb, dest);
+       else
+-      my_syslog(LOG_INFO, "%u %s %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, source, name, verb, dest);
++      my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
+     }
+   else
+     my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0030-Don-t-answer-from-cache-RRsets-from-wildcards-as-we-.patch b/src/patches/dnsmasq/0030-Don-t-answer-from-cache-RRsets-from-wildcards-as-we-.patch
new file mode 100644 (file)
index 0000000..5f489e2
--- /dev/null
@@ -0,0 +1,45 @@
+From 5e321739db381a1d7b5964d76e9c81471d2564c9 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 12 Jan 2015 23:16:56 +0000
+Subject: [PATCH 30/55] Don't answer from cache RRsets from wildcards, as we
+ don't have NSECs.
+
+---
+ src/dnssec.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/src/dnssec.c b/src/dnssec.c
+index afb3dca38cb1..d39ab85ed966 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -1818,11 +1818,14 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+             struct blockdata *key;
+             struct crec *crecp;
+             char *wildname;
++            int have_wildcard = 0;
+             rc = validate_rrset(now, header, plen, class1, type1, name, keyname, &wildname, NULL, 0, 0, 0);
+             
+             if (rc == STAT_SECURE_WILDCARD)
+               {
++                have_wildcard = 1;
++
+                 /* An attacker replay a wildcard answer with a different
+                    answer and overlay a genuine RR. To prove this
+                    hasn't happened, the answer must prove that
+@@ -1913,7 +1916,11 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+                             p2 += 13; /* labels, orig_ttl, expiration, inception */
+                             GETSHORT(keytag, p2);
+                             
+-                            if ((key = blockdata_alloc((char*)psave, rdlen2)))
++                            /* We don't cache sigs for wildcard answers, because to reproduce the
++                               answer from the cache will require one or more NSEC/NSEC3 records 
++                               which we don't cache. The lack of the RRSIG ensures that a query for
++                               this RRset asking for a secure answer will always be forwarded. */
++                            if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2)))
+                               {
+                                 if (!(crecp = cache_insert(name, &a, now, ttl,  F_FORWARD | F_DNSKEY | F_DS)))
+                                   blockdata_free(key);
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0031-Logs-for-DS-records-consistent.patch b/src/patches/dnsmasq/0031-Logs-for-DS-records-consistent.patch
new file mode 100644 (file)
index 0000000..1a2d3e4
--- /dev/null
@@ -0,0 +1,25 @@
+From ae4624bf46b5e37ff1a9a2ba3c927e0dede95adb Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 12 Jan 2015 23:22:08 +0000
+Subject: [PATCH 31/55] Logs for DS records consistent.
+
+---
+ src/rfc1035.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/rfc1035.c b/src/rfc1035.c
+index 75c4266b47dd..262274fc5b80 100644
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1643,7 +1643,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
+                             {
+                               if (crecp->flags & F_NXDOMAIN)
+                                 nxdomain = 1;
+-                              log_query(F_UPSTREAM, name, NULL, "secure no DS");      
++                              log_query(F_UPSTREAM, name, NULL, "no DS");     
+                             }
+                           else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
+                             {                                               
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0032-Cope-with-multiple-interfaces-with-the-same-LL-addre.patch b/src/patches/dnsmasq/0032-Cope-with-multiple-interfaces-with-the-same-LL-addre.patch
new file mode 100644 (file)
index 0000000..81b02cc
--- /dev/null
@@ -0,0 +1,57 @@
+From 393415597c8b5b09558b789ab9ac238dbe3db65d Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sun, 18 Jan 2015 22:11:10 +0000
+Subject: [PATCH 32/55] Cope with multiple interfaces with the same LL address.
+
+---
+ CHANGELOG  | 4 ++++
+ src/auth.c | 5 ++++-
+ src/util.c | 1 +
+ 3 files changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 23fc6d0530cf..bbd7e6619689 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -49,6 +49,10 @@ version 2.73
+           sometimes reasons to do it. (Step forward, GFW).
+           To avoid misuse, there's a hard limit on the TTL 
+           floor of one hour. Thansk to RinSatsuki for the patch.
++
++          Cope with multiple interfaces with the same link-local 
++          address. (IPv6 addresses are scoped, so this is allowed.)
++          Thanks to Cory Benfield for help with this.
+       
+       
+ version 2.72
+diff --git a/src/auth.c b/src/auth.c
+index a327f16d8c0b..59e05d3da38e 100644
+--- a/src/auth.c
++++ b/src/auth.c
+@@ -413,7 +413,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
+               peer_addr->in.sin_port = 0;
+ #ifdef HAVE_IPV6
+             else
+-              peer_addr->in6.sin6_port = 0; 
++              {
++                peer_addr->in6.sin6_port = 0; 
++                peer_addr->in6.sin6_scope_id = 0;
++              }
+ #endif
+             
+             for (peers = daemon->auth_peers; peers; peers = peers->next)
+diff --git a/src/util.c b/src/util.c
+index a729f339e219..d532444da207 100644
+--- a/src/util.c
++++ b/src/util.c
+@@ -274,6 +274,7 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
+ #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
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0033-Don-t-treat-SERVFAIL-as-a-recoverable-error.patch b/src/patches/dnsmasq/0033-Don-t-treat-SERVFAIL-as-a-recoverable-error.patch
new file mode 100644 (file)
index 0000000..e88a94b
--- /dev/null
@@ -0,0 +1,25 @@
+From 2ae195f5a71f7c5a75717845de1bd72fc7dd67f3 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sun, 18 Jan 2015 22:20:48 +0000
+Subject: [PATCH 33/55] Don't treat SERVFAIL as a recoverable error.....
+
+---
+ src/forward.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/forward.c b/src/forward.c
+index 713a64c0fa58..b17bc34f865f 100644
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -737,7 +737,7 @@ void reply_query(int fd, int family, time_t now)
+       check_for_ignored_address(header, n, daemon->ignore_addr))
+     return;
+-  if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
++  if (RCODE(header) == REFUSED &&
+       !option_bool(OPT_ORDER) &&
+       forward->forwardall == 0)
+     /* for broken servers, attempt to send to another one. */
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0034-Add-dhcp-hostsdir-config-option.patch b/src/patches/dnsmasq/0034-Add-dhcp-hostsdir-config-option.patch
new file mode 100644 (file)
index 0000000..c6b6703
--- /dev/null
@@ -0,0 +1,419 @@
+From 5f4dc5c6ca50655ab14f572c7e30815ed74cd51a Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Tue, 20 Jan 2015 20:51:02 +0000
+Subject: [PATCH 34/55] Add --dhcp-hostsdir config option.
+
+---
+ CHANGELOG     |   5 +++
+ man/dnsmasq.8 |   9 +++++
+ src/dnsmasq.c |  28 ++++++++++----
+ src/dnsmasq.h |  15 ++++++--
+ src/inotify.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
+ src/option.c  |  22 +++++++++--
+ 6 files changed, 177 insertions(+), 21 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index bbd7e6619689..0076b557e95e 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -53,6 +53,11 @@ version 2.73
+           Cope with multiple interfaces with the same link-local 
+           address. (IPv6 addresses are scoped, so this is allowed.)
+           Thanks to Cory Benfield for help with this.
++
++          Add --dhcp-hostsdir. This allows addition of new host
++          configurations to a running dnsmasq instance much more 
++          cheaply than having dnsmasq re-read all its existing
++          configuration each time. 
+       
+       
+ version 2.72
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index 5cfa355dea4a..005b5cca8d1f 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -977,6 +977,15 @@ is given, then read all the files contained in that directory. The advantage of
+ using this option is the same as for --dhcp-hostsfile: the
+ dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that
+ it is possible to encode the information in a
++.TP
++.B --dhcp-hostsdir=<path>
++This is exactly equivalent to dhcp-hostfile, except for the following. The path MUST be a
++directory, and not an individual file. Changed or new files within
++the directory are read automatically, without the need to send SIGHUP.
++If a file is deleted for changed after it has been read by dnsmasq, then the
++host record it contained will remain until dnsmasq recieves a SIGHUP, or 
++is restarted; ie host records are only added dynamically.
++.TP
+ .B --dhcp-boot
+ flag as DHCP options, using the options names bootfile-name,
+ server-ip-address and tftp-server. This allows these to be included
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index c0c0589d4ce1..04cc98278f62 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -142,6 +142,9 @@ int main (int argc, char **argv)
+       set_option_bool(OPT_NOWILD);
+       reset_option_bool(OPT_CLEVERBIND);
+     }
++
++  if (daemon->inotify_hosts)
++    die(_("dhcp-hostsdir not supported on this platform"), NULL, EC_BADCONF);
+ #endif
+   
+   if (option_bool(OPT_DNSSEC_VALID))
+@@ -316,13 +319,16 @@ int main (int argc, char **argv)
+ #ifdef HAVE_DNSSEC
+       blockdata_init();
+ #endif
++    }
+ #ifdef HAVE_LINUX_NETWORK
+-      if (!option_bool(OPT_NO_POLL))
+-      inotify_dnsmasq_init();
++  if ((!option_bool(OPT_NO_POLL) && daemon->port != 0) ||
++      daemon->dhcp || daemon->doing_dhcp6)
++    inotify_dnsmasq_init();
++  else
++    daemon->inotifyfd = -1;
+ #endif
+-    }
+-    
++       
+   if (option_bool(OPT_DBUS))
+ #ifdef HAVE_DBUS
+     {
+@@ -745,7 +751,7 @@ int main (int argc, char **argv)
+ #endif
+ #ifdef HAVE_TFTP
+-      if (option_bool(OPT_TFTP))
++  if (option_bool(OPT_TFTP))
+     {
+ #ifdef FD_SETSIZE
+       if (FD_SETSIZE < (unsigned)max_fd)
+@@ -870,7 +876,7 @@ int main (int argc, char **argv)
+ #if defined(HAVE_LINUX_NETWORK)
+       FD_SET(daemon->netlinkfd, &rset);
+       bump_maxfd(daemon->netlinkfd, &maxfd);
+-      if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
++      if (daemon->inotifyfd != -1)
+       {
+         FD_SET(daemon->inotifyfd, &rset);
+         bump_maxfd(daemon->inotifyfd, &maxfd);
+@@ -943,8 +949,11 @@ int main (int argc, char **argv)
+ #endif
+ #ifdef HAVE_LINUX_NETWORK
+-      if (daemon->port != 0 && !option_bool(OPT_NO_POLL) && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check())
+-      poll_resolv(1, 1, now);           
++      if  (daemon->inotifyfd != -1 && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check(now))
++      {
++        if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
++          poll_resolv(1, 1, now);
++      }         
+ #else
+       /* Check for changes to resolv files once per second max. */
+       /* Don't go silent for long periods if the clock goes backwards. */
+@@ -1385,6 +1394,9 @@ void clear_cache_and_reload(time_t now)
+       if (option_bool(OPT_ETHERS))
+       dhcp_read_ethers();
+       reread_dhcp();
++#ifdef HAVE_LINUX_NETWORK
++      set_dhcp_inotify();
++#endif
+       dhcp_update_configs(daemon->dhcp_conf);
+       lease_update_from_configs(); 
+       lease_update_file(now); 
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index f8275e3ac479..d841fdc064ad 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -550,13 +550,17 @@ struct resolvc {
+ #endif
+ };
+-/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */
++/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile and dhcp-hostsdir*/
+ #define AH_DIR      1
+ #define AH_INACTIVE 2
++#define AH_WD_DONE  4
+ struct hostsfile {
+   struct hostsfile *next;
+   int flags;
+   char *fname;
++#ifdef HAVE_LINUX_NETWORK
++  int wd; /* inotify watch descriptor */
++#endif
+   unsigned int index; /* matches to cache entries for logging */
+ };
+@@ -961,7 +965,7 @@ extern struct daemon {
+   int doing_ra, doing_dhcp6;
+   struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names; 
+   struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
+-  struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
++  struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *inotify_hosts;
+   int dhcp_max, tftp_max;
+   int dhcp_server_port, dhcp_client_port;
+   int start_tftp_port, end_tftp_port; 
+@@ -1197,7 +1201,7 @@ void reset_option_bool(unsigned int opt);
+ struct hostsfile *expand_filelist(struct hostsfile *list);
+ char *parse_server(char *arg, union mysockaddr *addr, 
+                  union mysockaddr *source_addr, char *interface, int *flags);
+-
++int option_read_hostsfile(char *file);
+ /* forward.c */
+ void reply_query(int fd, int family, time_t now);
+ void receive_query(struct listener *listen, time_t now);
+@@ -1486,5 +1490,8 @@ int detect_loop(char *query, int type);
+ /* inotify.c */
+ #ifdef HAVE_LINUX_NETWORK
+ void inotify_dnsmasq_init();
+-int inotify_check(void);
++int inotify_check(time_t now);
++#  ifdef HAVE_DHCP
++void set_dhcp_inotify(void);
++#  endif
+ #endif
+diff --git a/src/inotify.c b/src/inotify.c
+index 83730008c11b..52a30d7f44db 100644
+--- a/src/inotify.c
++++ b/src/inotify.c
+@@ -19,6 +19,11 @@
+ #include <sys/inotify.h>
++#ifdef HAVE_DHCP
++static void check_for_dhcp_inotify(struct inotify_event *in, time_t now);
++#endif
++
++
+ /* the strategy is to set a inotify on the directories containing
+    resolv files, for any files in the directory which are close-write 
+    or moved into the directory.
+@@ -40,8 +45,6 @@ void inotify_dnsmasq_init()
+   struct resolvc *res;
+   inotify_buffer = safe_malloc(INOTIFY_SZ);
+-
+-
+   daemon->inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+   
+   if (daemon->inotifyfd == -1)
+@@ -66,6 +69,7 @@ void inotify_dnsmasq_init()
+       {
+         *d = 0; /* make path just directory */
+         res->wd = inotify_add_watch(daemon->inotifyfd, path, IN_CLOSE_WRITE | IN_MOVED_TO);
++
+         res->file = d+1; /* pointer to filename */
+         *d = '/';
+         
+@@ -78,7 +82,7 @@ void inotify_dnsmasq_init()
+     }
+ }
+-int inotify_check(void)
++int inotify_check(time_t now)
+ {
+   int hit = 0;
+   
+@@ -101,13 +105,116 @@ int inotify_check(void)
+         for (res = daemon->resolv_files; res; res = res->next)
+           if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
+             hit = 1;
++
++#ifdef HAVE_DHCP
++        if (daemon->dhcp || daemon->doing_dhcp6)
++          check_for_dhcp_inotify(in, now);
++#endif
+       }
+     }
+-
+   return hit;
+ }
+-#endif
++#ifdef HAVE_DHCP 
++/* initialisation for dhcp-hostdir. Set inotify watch for each directory, and read pre-existing files */
++void set_dhcp_inotify(void)
++{
++  struct hostsfile *ah;
+-  
++  for (ah = daemon->inotify_hosts; ah; ah = ah->next)
++    {
++       DIR *dir_stream = NULL;
++       struct dirent *ent;
++       struct stat buf;
++
++       if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
++       {
++         my_syslog(LOG_ERR, _("bad directory in dhcp-hostsdir %s"), ah->fname);
++         continue;
++       }
++
++       if (!(ah->flags & AH_WD_DONE))
++       {
++         ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
++         ah->flags |= AH_WD_DONE;
++       }
++       /* Read contents of dir _after_ calling add_watch, in the ho[e of avoiding
++        a race which misses files being added as we start */
++       if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
++       {
++         my_syslog(LOG_ERR, _("failed to create inotify for %s"), ah->fname);
++         continue;
++       }
++
++       while ((ent = readdir(dir_stream)))
++       {
++         size_t lendir = strlen(ah->fname);
++         size_t lenfile = strlen(ent->d_name);
++         char *path;
++         
++         /* ignore emacs backups and dotfiles */
++         if (lenfile == 0 || 
++             ent->d_name[lenfile - 1] == '~' ||
++             (ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
++             ent->d_name[0] == '.')
++           continue;
++         
++         if ((path = whine_malloc(lendir + lenfile + 2)))
++           {
++             strcpy(path, ah->fname);
++             strcat(path, "/");
++             strcat(path, ent->d_name);
++             
++             /* ignore non-regular files */
++             if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
++               option_read_hostsfile(path);
++             
++             free(path);
++           }
++       }
++    }
++}
++
++static void check_for_dhcp_inotify(struct inotify_event *in, time_t now)
++{
++  struct hostsfile *ah;
++
++  /* ignore emacs backups and dotfiles */
++  if (in->len == 0 || 
++      in->name[in->len - 1] == '~' ||
++      (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
++      in->name[0] == '.')
++    return;
++
++  for (ah = daemon->inotify_hosts; ah; ah = ah->next)
++    if (ah->wd == in->wd)
++      {
++      size_t lendir = strlen(ah->fname);
++      char *path;
++         
++      if ((path = whine_malloc(lendir + in->len + 2)))
++        {
++          strcpy(path, ah->fname);
++          strcat(path, "/");
++          strcat(path, in->name);
++          
++          if (option_read_hostsfile(path))
++            {
++              /* Propogate the consequences of loading a new dhcp-host */
++              dhcp_update_configs(daemon->dhcp_conf);
++              lease_update_from_configs(); 
++              lease_update_file(now); 
++              lease_update_dns(1);
++            }
++          
++          free(path);
++        }
++      
++      return;
++      }
++}
++
++#endif /* DHCP */
++
++#endif  /* LINUX_NETWORK */
+   
+diff --git a/src/option.c b/src/option.c
+index 8b994098cc9f..22e11c37d374 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -149,7 +149,7 @@ struct myoption {
+ #define LOPT_LOOP_DETECT   337
+ #define LOPT_IGNORE_ADDR   338
+ #define LOPT_MINCTTL       339
+-
++#define LOPT_DHCP_INOTIFY  340
+ #ifdef HAVE_GETOPT_LONG
+ static const struct option opts[] =  
+@@ -248,6 +248,7 @@ static const struct myoption opts[] =
+     { "interface-name", 1, 0, LOPT_INTNAME },
+     { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
+     { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
++    { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
+     { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
+     { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
+     { "stop-dns-rebind", 0, 0, LOPT_REBIND },
+@@ -336,6 +337,7 @@ static struct {
+   { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
+   { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
+   { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
++  { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL }, 
+   { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
+   { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
+   { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
+@@ -1710,8 +1712,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+       break;
+ #endif /* HAVE_DHCP */
+-    case LOPT_DHCP_HOST: /* --dhcp-hostfile */
++    case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
+     case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
++    case LOPT_DHCP_INOTIFY: /* dhcp-hostsdir */
+     case 'H': /* --addn-hosts */
+       {
+       struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
+@@ -1734,6 +1737,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+           new->next = daemon->dhcp_opts_file;
+           daemon->dhcp_opts_file = new;
+         }       
++      else if (option == LOPT_DHCP_INOTIFY)
++        {
++          new->next = daemon->inotify_hosts;
++          daemon->inotify_hosts = new;
++        }
++      
+       break;
+       }
+       
+@@ -4042,6 +4051,13 @@ static void read_file(char *file, FILE *f, int hard_opt)
+   fclose(f);
+ }
++#ifdef HAVE_DHCP
++int option_read_hostsfile(char *file)
++{
++  return one_file(file, LOPT_BANK);
++}
++#endif
++
+ static int one_file(char *file, int hard_opt)
+ {
+   FILE *f;
+@@ -4139,7 +4155,7 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
+           
+           /* don't read this as a file */
+           ah->flags |= AH_INACTIVE;
+-
++          
+           if (!(dir_stream = opendir(ah->fname)))
+             my_syslog(LOG_ERR, _("cannot access directory %s: %s"), 
+                       ah->fname, strerror(errno));
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0035-Update-German-translation.patch b/src/patches/dnsmasq/0035-Update-German-translation.patch
new file mode 100644 (file)
index 0000000..af45a1c
--- /dev/null
@@ -0,0 +1,327 @@
+From fbf01f7046e75f9aa73fd4aab2a94e43386d9052 Mon Sep 17 00:00:00 2001
+From: Conrad Kostecki <ck@conrad-kostecki.de>
+Date: Tue, 20 Jan 2015 21:07:56 +0000
+Subject: [PATCH 35/55] Update German translation.
+
+---
+ po/de.po | 101 +++++++++++++++++++++++++++++----------------------------------
+ 1 file changed, 47 insertions(+), 54 deletions(-)
+
+diff --git a/po/de.po b/po/de.po
+index e2317376d8a9..4c93c5b28ef2 100644
+--- a/po/de.po
++++ b/po/de.po
+@@ -9,10 +9,10 @@
+ # Simon Kelley <simon@thekelleys.org.uk>, 2005.
+ msgid ""
+ msgstr ""
+-"Project-Id-Version: dnsmasq 2.70\n"
++"Project-Id-Version: dnsmasq 2.73\n"
+ "Report-Msgid-Bugs-To: \n"
+ "POT-Creation-Date: 2009-06-18 12:24+0100\n"
+-"PO-Revision-Date: 2014-05-01 22:51+0100\n"
++"PO-Revision-Date: 2015-01-19 15:43+0100\n"
+ "Last-Translator: Conrad Kostecki <ck@conrad-kostecki.de>\n"
+ "Language-Team: German <de@li.org>\n"
+ "Language: de\n"
+@@ -20,12 +20,12 @@ msgstr ""
+ "Content-Type: text/plain; charset=UTF-8\n"
+ "Content-Transfer-Encoding: 8bit\n"
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
+-"X-Generator: Poedit 1.6.5\n"
++"X-Generator: Poedit 1.7.3\n"
+ "X-Poedit-SourceCharset: UTF-8\n"
+ #: cache.c:505
+ msgid "Internal error in cache."
+-msgstr ""
++msgstr "Interner Fehler im Cache."
+ #: cache.c:908
+ #, c-format
+@@ -126,7 +126,7 @@ msgstr "Lokale abzuhörende Adresse(n) angeben."
+ #: option.c:319
+ msgid "Return ipaddr for all hosts in specified domains."
+-msgstr "IP-Adresse für alle Hosts in angebenen Domänen festlegen."
++msgstr "IP-Adresse für alle Hosts in angegebenen Domänen festlegen."
+ # FIXME: the English test is not to the point. Just use a shortened description
+ # from the manpage instead. -- MA
+@@ -310,18 +310,16 @@ msgid "Specify path to resolv.conf (defaults to %s)."
+ msgstr "Pfad zu resolv.conf festlegen (%s voreingestellt)."
+ #: option.c:362
+-#, fuzzy
+ msgid "Specify path to file with server= options"
+-msgstr "Dateipfad für Prozesskennung (PID) festlegen (Voreinstellung: %s)."
++msgstr " Dateipfad mit der Option server= angeben"
+ #: option.c:363
+ msgid "Specify address(es) of upstream servers with optional domains."
+ msgstr "Adresse(n) vorgelagerter Server festlegen, optional mit Domänen."
+ #: option.c:364
+-#, fuzzy
+ msgid "Specify address of upstream servers for reverse address queries"
+-msgstr "Adresse(n) vorgelagerter Server festlegen, optional mit Domänen."
++msgstr "Adresse(n) vorgelagerter Server festlegen, für reverse Adressanfragen"
+ #: option.c:365
+ msgid "Never forward queries to specified domains."
+@@ -657,23 +655,23 @@ msgstr "Spezifiziere eine Domain und Adressbereich für synthetisierte Namen"
+ #: option.c:446
+ msgid "Activate DNSSEC validation"
+-msgstr ""
++msgstr "Aktiviere DNSSEC-Validierung"
+ #: option.c:447
+ msgid "Specify trust anchor key digest."
+-msgstr ""
++msgstr "Spezifiziere Vertrauensursprung (Trust Anchor) der Schlüssel-Prüfdaten (Key Digest)."
+ #: option.c:448
+ msgid "Disable upstream checking for DNSSEC debugging."
+-msgstr ""
++msgstr "Deaktiviere die Überprüfung vorgelagerter Server für DNSSEC-Debugging"
+ #: option.c:449
+ msgid "Ensure answers without DNSSEC are in unsigned zones."
+-msgstr ""
++msgstr "Stellt sicher, dass Antworten ohne DNSSEC sich in einer unsignierten Zone befinden."
+ #: option.c:450
+ msgid "Don't check DNSSEC signature timestamps until first cache-reload"
+-msgstr ""
++msgstr "DNSSEC Signatur-Zeitstempel nicht prüfen, bis erstmalig der Cache neugeladen wird"
+ #: option.c:452
+ msgid "Specify DHCPv6 prefix class"
+@@ -697,11 +695,11 @@ msgstr "RA nicht protokollieren."
+ #: option.c:458
+ msgid "Accept queries only from directly-connected networks"
+-msgstr ""
++msgstr "Akzeptiere nur Anfragen von direkt verbundenen Netzwerken"
+ #: option.c:459
+ msgid "Detect and remove DNS forwarding loops"
+-msgstr ""
++msgstr "Erkennen und Entfernen von DNS-Weiterleitungsschleifen"
+ #: option.c:661
+ #, c-format
+@@ -958,18 +956,16 @@ msgid "Bad name in host-record"
+ msgstr "Unzulässiger Name in host-record"
+ #: option.c:3826
+-#, fuzzy
+ msgid "bad trust anchor"
+-msgstr "unzulässiger Portbereich"
++msgstr "unzulässiger Vertrauensursprung (Trust Anchor)"
+ #: option.c:3840
+ msgid "bad HEX in trust anchor"
+-msgstr ""
++msgstr "unzulässiger Hexwert in Vertrauensursprung (Trust Anchor)"
+ #: option.c:3850
+-#, fuzzy
+ msgid "unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)"
+-msgstr "unzulässige Option (prüfen Sie, ob dnsmasq mit DHCP/TFTP/DBus-Unterstützt übersetzt wurde)"
++msgstr "Nicht unterstützte Option (prüfen Sie, ob DNSMasq mit DHCP/TFTP/DNSSEC/DBus-Unterstützung übersetzt wurde)"
+ #: option.c:3909
+ msgid "missing \""
+@@ -988,7 +984,6 @@ msgid "missing parameter"
+ msgstr "fehler Parameter"
+ #: option.c:3972
+-#, fuzzy
+ msgid "illegal option"
+ msgstr "unzulässige Option"
+@@ -1110,7 +1105,7 @@ msgstr "möglichen DNS-Rebind-Angriff entdeckt: %s"
+ #: forward.c:1132 forward.c:1663
+ msgid "Ignoring query from non-local network"
+-msgstr ""
++msgstr "Ignoriere Anfragen vom nicht lokalen Netzwerk"
+ #: forward.c:2101
+ #, c-format
+@@ -1189,9 +1184,9 @@ msgid "using nameserver %s#%d for %s %s"
+ msgstr "Benutze Namensserver %s#%d für %s %s"
+ #: network.c:1483
+-#, fuzzy, c-format
++#, c-format
+ msgid "NOT using nameserver %s#%d - query loop detected"
+-msgstr "Benutze Namensserver %s#%d für %s %s"
++msgstr "Benutze Namensserver %s#%d NICHT - Anfragenschleife festgetellt"
+ #: network.c:1486
+ #, c-format
+@@ -1205,16 +1200,15 @@ msgstr "Benutze Namensserver %s#%d"
+ #: dnsmasq.c:154
+ msgid "No trust anchors provided for DNSSEC"
+-msgstr ""
++msgstr "Keine Vertrauensursprünge (Trust Anchor) für DNSSEC verfügbar"
+ #: dnsmasq.c:157
+ msgid "Cannot reduce cache size from default when DNSSEC enabled"
+-msgstr ""
++msgstr "Kann die Standard Cachegröße nicht verkleinern, wenn DNSSEC aktiviert ist"
+ #: dnsmasq.c:159
+-#, fuzzy
+ msgid "DNSSEC not available: set HAVE_DNSSEC in src/config.h"
+-msgstr "DBus nicht verfügbar: setzen Sie HAVE_DBUS in src/config.h"
++msgstr "DNSSEC nicht verfügbar: setzen Sie HAVE_DNSSEC in src/config.h"
+ #: dnsmasq.c:165
+ msgid "TFTP server not available: set HAVE_TFTP in src/config.h"
+@@ -1241,9 +1235,8 @@ msgid "authoritative DNS not available: set HAVE_AUTH in src/config.h"
+ msgstr "Authoritatives DNS nicht verfügbar: Es muss HAVE_AUTH in src/config.h gesetzt sein"
+ #: dnsmasq.c:193
+-#, fuzzy
+ msgid "Loop detection not available: set HAVE_LOOP in src/config.h"
+-msgstr "TFTP-Server nicht verfügbar, setzen Sie HAVE_TFTP in src/config.h"
++msgstr "Loop-Erkennung nicht verfügbar, setzen Sie HAVE_LOOP in src/config.h"
+ #: dnsmasq.c:201
+ msgid "zone serial must be configured in --auth-soa"
+@@ -1317,15 +1310,15 @@ msgstr "DBus-Unterstützung eingeschaltet: warte auf Systembus-Verbindung"
+ #: dnsmasq.c:672
+ msgid "DNS service limited to local subnets"
+-msgstr ""
++msgstr "DNS-Dienst auf lokale Subnetze eingeschränkt"
+ #: dnsmasq.c:677
+ msgid "DNSSEC validation enabled"
+-msgstr ""
++msgstr "DNSSEC-Validierung aktiviert"
+ #: dnsmasq.c:679
+ msgid "DNSSEC signature timestamps not checked until first cache reload"
+-msgstr ""
++msgstr "DNSSEC Signatur-Zeitstempel werden erst ab dem ersten Neuladen des Caches überprüft"
+ #: dnsmasq.c:684
+ #, c-format
+@@ -1366,7 +1359,7 @@ msgstr "DHCP, Sockets exklusiv an das Interface %s gebunden"
+ # FIXME: this and the next few must be full strings to be translatable - do not assemble in code"
+ #: dnsmasq.c:753
+ msgid "root is "
+-msgstr "Wurzel ist"
++msgstr "Wurzel ist "
+ #: dnsmasq.c:753
+ msgid "enabled"
+@@ -1432,7 +1425,7 @@ msgstr "Das TFTP-Verzeichnis %s ist nicht zugreifbar: %s"
+ #: dnsmasq.c:1151
+ msgid "now checking DNSSEC signature timestamps"
+-msgstr ""
++msgstr "Prüfe jetzt DNSSEC Signatur-Zeitstempel"
+ #: dnsmasq.c:1218
+ #, c-format
+@@ -1506,7 +1499,7 @@ msgstr "DHCP-Paket ohne Adresse an Schnittstelle %s empfangen"
+ #: dhcp.c:408
+ #, c-format
+ msgid "ARP-cache injection failed: %s"
+-msgstr ""
++msgstr "APR-Cache Injektion fehlgeschlagen: %s"
+ #: dhcp.c:506
+ #, c-format
+@@ -1763,13 +1756,13 @@ msgid "DHCP request for unsupported hardware type (%d) received on %s"
+ msgstr "DHCP-Anfrage für nicht unterstützen Hardwaretyp (%d) auf %s empfangen"
+ #: bpf.c:376
+-#, fuzzy, c-format
++#, c-format
+ msgid "cannot create PF_ROUTE socket: %s"
+-msgstr "kann DHCP-Socket nicht erzeugen: %s"
++msgstr "Kann PF_ROUTE socket nicht erzeugen: %s"
+ #: bpf.c:397
+ msgid "Unknown protocol version from route socket"
+-msgstr ""
++msgstr "Unbekannte Protokollversion vom Route Socket"
+ #: helper.c:153
+ msgid "lease() function missing in Lua script"
+@@ -2020,50 +2013,50 @@ msgstr "konnte IPset-Kontroll-Socket nicht erzeugen: %s"
+ #: blockdata.c:58
+ #, c-format
+ msgid "DNSSEC memory in use %u, max %u, allocated %u"
+-msgstr ""
++msgstr "DNSSEC Speicher in Benutzung %u, Max %u, zugewiesen %u"
+ #: tables.c:76
+ msgid "error: fill_addr missused"
+-msgstr ""
++msgstr "Fehler: fill_addr falsch verwendet"
+ #: tables.c:105
+-#, fuzzy, c-format
++#, c-format
+ msgid "failed to access pf devices: %s"
+-msgstr "konnte auf %s nicht zugreifen: %s"
++msgstr "konnte auf pf Geräte nicht zugreifen: %s"
+ #: tables.c:119
+-#, fuzzy, c-format
++#, c-format
+ msgid "warning: no opened pf devices %s"
+-msgstr "Warnung: Keine Adresse für die Schnittstelle %s gefunden"
++msgstr "Warnung: Keine geöffneten pf Geräte %s"
+ #: tables.c:127
+-#, fuzzy, c-format
++#, c-format
+ msgid "error: cannot use table name %s"
+-msgstr "kann Hostnamen nicht ermitteln: %s"
++msgstr "Fehler: Kann Tabellenname %s nicht benutzen"
+ #: tables.c:135
+ #, c-format
+ msgid "error: cannot strlcpy table name %s"
+-msgstr ""
++msgstr "Fehler: Kann den Tabellennamen %s nicht strlcpy"
+ #: tables.c:141
+ #, c-format
+ msgid "warning: pfr_add_tables: %s(%d)"
+-msgstr ""
++msgstr "Warnung: pfr_add_tables: %s(%d)"
+ #: tables.c:147
+ msgid "info: table created"
+-msgstr ""
++msgstr "Info: Tabelle erstellt"
+ #: tables.c:158
+ #, c-format
+ msgid "warning: DIOCR%sADDRS: %s"
+-msgstr ""
++msgstr "Warnung: DIOCR%sADDRS: %s"
+ #: tables.c:162
+-#, fuzzy, c-format
++#, c-format
+ msgid "%d addresses %s"
+-msgstr "Fehlerhafte Adresse"
++msgstr "%d Adressen %s"
+ #~ msgid "no interface with address %s"
+ #~ msgstr "keine Schnittstelle mit Adresse %s"
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0036-Don-t-reply-to-DHCPv6-SOLICIT-messages-when-not-conf.patch b/src/patches/dnsmasq/0036-Don-t-reply-to-DHCPv6-SOLICIT-messages-when-not-conf.patch
new file mode 100644 (file)
index 0000000..25007de
--- /dev/null
@@ -0,0 +1,53 @@
+From 61b838dd574c51d96fef100285a0d225824534f9 Mon Sep 17 00:00:00 2001
+From: Win King Wan <pinwing+dnsmasq@gmail.com>
+Date: Wed, 21 Jan 2015 20:41:48 +0000
+Subject: [PATCH 36/55] Don't reply to DHCPv6 SOLICIT messages when not
+ configured for statefull DHCPv6.
+
+---
+ CHANGELOG     |  4 ++++
+ src/rfc3315.c | 13 +++++++++++++
+ 2 files changed, 17 insertions(+)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 0076b557e95e..a4cb901e83ae 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -59,6 +59,10 @@ version 2.73
+           cheaply than having dnsmasq re-read all its existing
+           configuration each time. 
+       
++          Don't reply to DHCPv6 SOLICIT messages if we're not 
++          configured to do stateful DHCPv6. Thanks to Win King Wan 
++          for the patch.
++
+       
+ version 2.72
+             Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
+diff --git a/src/rfc3315.c b/src/rfc3315.c
+index ddb390bf1136..e593ec9c362c 100644
+--- a/src/rfc3315.c
++++ b/src/rfc3315.c
+@@ -824,6 +824,19 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
+         }
+       else
+         { 
++          /* Windows 8 always requests an address even if the Managed bit
++             in RA is 0 and it keeps retrying if it receives a reply
++             stating that no addresses are available. We solve this 
++             by not replying at all if we're not configured to give any 
++             addresses by DHCPv6. RFC 3315 17.2.1. appears to allow this. */
++          
++          for (c = state->context; c; c = c->current)
++            if (!(c->flags & CONTEXT_RA_STATELESS))
++              break;
++          
++          if (!c)
++            return 0;
++          
+           /* no address, return error */
+           o1 = new_opt6(OPTION6_STATUS_CODE);
+           put_opt6_short(DHCP6NOADDRS);
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0037-Allow-inotify-to-be-disabled-at-compile-time-on-Linu.patch b/src/patches/dnsmasq/0037-Allow-inotify-to-be-disabled-at-compile-time-on-Linu.patch
new file mode 100644 (file)
index 0000000..7318688
--- /dev/null
@@ -0,0 +1,213 @@
+From 0491805d2ff6e7727f0272c94fd97d9897d1e22c Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 26 Jan 2015 11:23:43 +0000
+Subject: [PATCH 37/55] Allow inotify to be disabled at compile time on Linux.
+
+---
+ CHANGELOG     |  4 +++-
+ src/config.h  | 13 ++++++++++++-
+ src/dnsmasq.c | 21 +++++++++++++--------
+ src/dnsmasq.h | 11 +++++++----
+ src/inotify.c |  4 ++--
+ 5 files changed, 37 insertions(+), 16 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index a4cb901e83ae..c05dec63c587 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -9,7 +9,9 @@ version 2.73
+           Use inotify for checking on updates to /etc/resolv.conf and
+           friends under Linux. This fixes race conditions when the files are 
+-          updated rapidly and saves CPU by noy polling.
++          updated rapidly and saves CPU by noy polling. To build
++          a binary that runs on old Linux kernels without inotify,
++          use make COPTS=-DNO_INOTIFY
+           Fix breakage of --domain=<domain>,<subnet>,local - only reverse
+           queries were intercepted. THis appears to have been broken 
+diff --git a/src/config.h b/src/config.h
+index cdca231b4079..5e5009271eba 100644
+--- a/src/config.h
++++ b/src/config.h
+@@ -115,6 +115,8 @@ HAVE_DNSSEC
+ HAVE_LOOP
+    include functionality to probe for and remove DNS forwarding loops.
++HAVE_INOTIFY
++   use the Linux inotify facility to efficiently re-read configuration files.
+ NO_IPV6
+ NO_TFTP
+@@ -123,6 +125,7 @@ NO_DHCP6
+ NO_SCRIPT
+ NO_LARGEFILE
+ NO_AUTH
++NO_INOTIFY
+    these are avilable to explictly 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
+@@ -355,6 +358,10 @@ HAVE_SOCKADDR_SA_LEN
+ #undef HAVE_LOOP
+ #endif
++#if defined (HAVE_LINUX_NETWORK) && !defined(NO_INOTIFY)
++#define HAVE_INOTIFY
++#endif
++
+ /* Define a string indicating which options are in use.
+    DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
+@@ -428,7 +435,11 @@ static char *compile_opts =
+ #ifndef HAVE_LOOP
+ "no-"
+ #endif
+-"loop-detect";
++"loop-detect "
++#ifndef HAVE_INOTIFY
++"no-"
++#endif
++"inotify";
+ #endif
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index 04cc98278f62..bc4f47170705 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -142,7 +142,9 @@ int main (int argc, char **argv)
+       set_option_bool(OPT_NOWILD);
+       reset_option_bool(OPT_CLEVERBIND);
+     }
++#endif
++#ifndef HAVE_INOTIFY
+   if (daemon->inotify_hosts)
+     die(_("dhcp-hostsdir not supported on this platform"), NULL, EC_BADCONF);
+ #endif
+@@ -321,7 +323,7 @@ int main (int argc, char **argv)
+ #endif
+     }
+-#ifdef HAVE_LINUX_NETWORK
++#ifdef HAVE_INOTIFY
+   if ((!option_bool(OPT_NO_POLL) && daemon->port != 0) ||
+       daemon->dhcp || daemon->doing_dhcp6)
+     inotify_dnsmasq_init();
+@@ -802,7 +804,7 @@ int main (int argc, char **argv)
+   
+   pid = getpid();
+   
+-#ifdef HAVE_LINUX_NETWORK
++#ifdef HAVE_INOTIFY
+   /* Using inotify, have to select a resolv file at startup */
+   poll_resolv(1, 0, now);
+ #endif
+@@ -872,15 +874,18 @@ int main (int argc, char **argv)
+         bump_maxfd(daemon->icmp6fd, &maxfd); 
+       }
+ #endif
+-
+-#if defined(HAVE_LINUX_NETWORK)
+-      FD_SET(daemon->netlinkfd, &rset);
+-      bump_maxfd(daemon->netlinkfd, &maxfd);
++    
++#ifdef HAVE_INOTIFY
+       if (daemon->inotifyfd != -1)
+       {
+         FD_SET(daemon->inotifyfd, &rset);
+         bump_maxfd(daemon->inotifyfd, &maxfd);
+       }
++#endif
++
++#if defined(HAVE_LINUX_NETWORK)
++      FD_SET(daemon->netlinkfd, &rset);
++      bump_maxfd(daemon->netlinkfd, &maxfd);
+ #elif defined(HAVE_BSD_NETWORK)
+       FD_SET(daemon->routefd, &rset);
+       bump_maxfd(daemon->routefd, &maxfd);
+@@ -948,7 +953,7 @@ int main (int argc, char **argv)
+       route_sock();
+ #endif
+-#ifdef HAVE_LINUX_NETWORK
++#ifdef HAVE_INOTIFY
+       if  (daemon->inotifyfd != -1 && FD_ISSET(daemon->inotifyfd, &rset) && inotify_check(now))
+       {
+         if (daemon->port != 0 && !option_bool(OPT_NO_POLL))
+@@ -1394,7 +1399,7 @@ void clear_cache_and_reload(time_t now)
+       if (option_bool(OPT_ETHERS))
+       dhcp_read_ethers();
+       reread_dhcp();
+-#ifdef HAVE_LINUX_NETWORK
++#ifdef HAVE_INOTIFY
+       set_dhcp_inotify();
+ #endif
+       dhcp_update_configs(daemon->dhcp_conf);
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index d841fdc064ad..8091634f69db 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -544,7 +544,7 @@ struct resolvc {
+   int is_default, logged;
+   time_t mtime;
+   char *name;
+-#ifdef HAVE_LINUX_NETWORK
++#ifdef HAVE_INOTIFY
+   int wd; /* inotify watch descriptor */
+   char *file; /* pointer to file part if path */
+ #endif
+@@ -558,7 +558,7 @@ struct hostsfile {
+   struct hostsfile *next;
+   int flags;
+   char *fname;
+-#ifdef HAVE_LINUX_NETWORK
++#ifdef HAVE_INOTIFY
+   int wd; /* inotify watch descriptor */
+ #endif
+   unsigned int index; /* matches to cache entries for logging */
+@@ -1013,8 +1013,11 @@ extern struct daemon {
+   /* DHCP state */
+   int dhcpfd, helperfd, pxefd; 
++#ifdef HAVE_INOTIFY
++  int inotifyfd;
++#endif
+ #if defined(HAVE_LINUX_NETWORK)
+-  int netlinkfd, inotifyfd;
++  int netlinkfd;
+ #elif defined(HAVE_BSD_NETWORK)
+   int dhcp_raw_fd, dhcp_icmp_fd, routefd;
+ #endif
+@@ -1488,7 +1491,7 @@ int detect_loop(char *query, int type);
+ #endif
+ /* inotify.c */
+-#ifdef HAVE_LINUX_NETWORK
++#ifdef HAVE_INOTIFY
+ void inotify_dnsmasq_init();
+ int inotify_check(time_t now);
+ #  ifdef HAVE_DHCP
+diff --git a/src/inotify.c b/src/inotify.c
+index 52a30d7f44db..818fe8eddda4 100644
+--- a/src/inotify.c
++++ b/src/inotify.c
+@@ -15,7 +15,7 @@
+ */
+ #include "dnsmasq.h"
+-#ifdef HAVE_LINUX_NETWORK
++#ifdef HAVE_INOTIFY
+ #include <sys/inotify.h>
+@@ -216,5 +216,5 @@ static void check_for_dhcp_inotify(struct inotify_event *in, time_t now)
+ #endif /* DHCP */
+-#endif  /* LINUX_NETWORK */
++#endif  /* INOTIFY */
+   
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0038-Expand-inotify-code-to-dhcp-hostsdir-dhcp-optsdir-an.patch b/src/patches/dnsmasq/0038-Expand-inotify-code-to-dhcp-hostsdir-dhcp-optsdir-an.patch
new file mode 100644 (file)
index 0000000..7514034
--- /dev/null
@@ -0,0 +1,562 @@
+From 70d1873dd9e70041ed4bb88c69d5b886b7cc634c Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sat, 31 Jan 2015 19:59:29 +0000
+Subject: [PATCH 38/55] Expand inotify code to dhcp-hostsdir, dhcp-optsdir and
+ hostsdir.
+
+---
+ src/cache.c   |  81 +++++++++++++++++---------
+ src/dnsmasq.c |   9 ++-
+ src/dnsmasq.h |  14 +++--
+ src/inotify.c | 179 +++++++++++++++++++++++++++++-----------------------------
+ src/option.c  |  37 +++++++++---
+ 5 files changed, 187 insertions(+), 133 deletions(-)
+
+diff --git a/src/cache.c b/src/cache.c
+index 09b6dbf8087a..abaf25ec0f18 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -835,27 +835,42 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
+      Only insert each unique address once into this hashing structure.
+      This complexity avoids O(n^2) divergent CPU use whilst reading
+-     large (10000 entry) hosts files. */
+-  
+-  /* hash address */
+-  for (j = 0, i = 0; i < addrlen; i++)
+-    j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
+-  
+-  for (lookup = rhash[j]; lookup; lookup = lookup->next)
+-    if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
+-      memcmp(&lookup->addr.addr, addr, addrlen) == 0)
+-      {
+-      cache->flags &= ~F_REVERSE;
+-      break;
+-      }
++     large (10000 entry) hosts files. 
++
++     Note that we only do this process when bulk-reading hosts files, 
++     for incremental reads, rhash is NULL, and we use cache lookups
++     instead.
++  */
+   
+-  /* maintain address hash chain, insert new unique address */
+-  if (!lookup)
++  if (rhash)
+     {
+-      cache->next = rhash[j];
+-      rhash[j] = cache;
++      /* hash address */
++      for (j = 0, i = 0; i < addrlen; i++)
++      j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
++      
++      for (lookup = rhash[j]; lookup; lookup = lookup->next)
++      if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
++          memcmp(&lookup->addr.addr, addr, addrlen) == 0)
++        {
++          cache->flags &= ~F_REVERSE;
++          break;
++        }
++      
++      /* maintain address hash chain, insert new unique address */
++      if (!lookup)
++      {
++        cache->next = rhash[j];
++        rhash[j] = cache;
++      }
+     }
+-  
++  else
++    {
++      /* incremental read, lookup in cache */
++      lookup = cache_find_by_addr(NULL, addr, 0, cache->flags & (F_IPV4 | F_IPV6));
++      if (lookup && lookup->flags & F_HOSTS)
++      cache->flags &= ~F_REVERSE;
++    }
++
+   cache->uid = index;
+   memcpy(&cache->addr.addr, addr, addrlen);  
+   cache_hash(cache);
+@@ -912,7 +927,7 @@ static int gettok(FILE *f, char *token)
+     }
+ }
+-static int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
++int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
+ {  
+   FILE *f = fopen(filename, "r");
+   char *token = daemon->namebuff, *domain_suffix = NULL;
+@@ -958,7 +973,7 @@ static int read_hostsfile(char *filename, unsigned int index, int cache_size, st
+       addr_count++;
+       
+       /* rehash every 1000 names. */
+-      if ((name_count - cache_size) > 1000)
++      if (rhash && ((name_count - cache_size) > 1000))
+       {
+         rehash(name_count);
+         cache_size = name_count;
+@@ -1005,10 +1020,13 @@ static int read_hostsfile(char *filename, unsigned int index, int cache_size, st
+     } 
+   fclose(f);
+-  rehash(name_count);
+-  
+-  my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
+   
++  if (rhash)
++    {
++      rehash(name_count); 
++      my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
++    }
++
+   return name_count;
+ }
+           
+@@ -1118,14 +1136,19 @@ void cache_reload(void)
+       my_syslog(LOG_INFO, _("cleared cache"));
+       return;
+     }
+-    
++  
+   if (!option_bool(OPT_NO_HOSTS))
+     total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
+-         
++  
+   daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
+   for (ah = daemon->addn_hosts; ah; ah = ah->next)
+     if (!(ah->flags & AH_INACTIVE))
+       total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
++  
++#ifdef HAVE_INOTIFY
++  set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
++#endif
++  
+ } 
+ #ifdef HAVE_DHCP
+@@ -1505,7 +1528,13 @@ char *record_source(unsigned int index)
+   for (ah = daemon->addn_hosts; ah; ah = ah->next)
+     if (ah->index == index)
+       return ah->fname;
+-  
++
++#ifdef HAVE_INOTIFY
++  for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
++     if (ah->index == index)
++       return ah->fname;
++#endif
++
+   return "<unknown>";
+ }
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index bc4f47170705..2c629fe422aa 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -145,8 +145,8 @@ int main (int argc, char **argv)
+ #endif
+ #ifndef HAVE_INOTIFY
+-  if (daemon->inotify_hosts)
+-    die(_("dhcp-hostsdir not supported on this platform"), NULL, EC_BADCONF);
++  if (daemon->dynamic_dirs)
++    die(_("dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"), NULL, EC_BADCONF);
+ #endif
+   
+   if (option_bool(OPT_DNSSEC_VALID))
+@@ -324,8 +324,7 @@ int main (int argc, char **argv)
+     }
+ #ifdef HAVE_INOTIFY
+-  if ((!option_bool(OPT_NO_POLL) && daemon->port != 0) ||
+-      daemon->dhcp || daemon->doing_dhcp6)
++  if (daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6)
+     inotify_dnsmasq_init();
+   else
+     daemon->inotifyfd = -1;
+@@ -1400,7 +1399,7 @@ void clear_cache_and_reload(time_t now)
+       dhcp_read_ethers();
+       reread_dhcp();
+ #ifdef HAVE_INOTIFY
+-      set_dhcp_inotify();
++      set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);
+ #endif
+       dhcp_update_configs(daemon->dhcp_conf);
+       lease_update_from_configs(); 
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 8091634f69db..0c322a93993e 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -554,6 +554,9 @@ struct resolvc {
+ #define AH_DIR      1
+ #define AH_INACTIVE 2
+ #define AH_WD_DONE  4
++#define AH_HOSTS    8
++#define AH_DHCP_HST 16
++#define AH_DHCP_OPT 32
+ struct hostsfile {
+   struct hostsfile *next;
+   int flags;
+@@ -965,7 +968,7 @@ extern struct daemon {
+   int doing_ra, doing_dhcp6;
+   struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names; 
+   struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
+-  struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *inotify_hosts;
++  struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
+   int dhcp_max, tftp_max;
+   int dhcp_server_port, dhcp_client_port;
+   int start_tftp_port, end_tftp_port; 
+@@ -1071,6 +1074,8 @@ int cache_make_stat(struct txt_record *t);
+ char *cache_get_name(struct crec *crecp);
+ char *cache_get_cname_target(struct crec *crecp);
+ struct crec *cache_enumerate(int init);
++int read_hostsfile(char *filename, unsigned int index, int cache_size, 
++                 struct crec **rhash, int hashsz);
+ /* blockdata.c */
+ #ifdef HAVE_DNSSEC
+@@ -1204,7 +1209,8 @@ void reset_option_bool(unsigned int opt);
+ struct hostsfile *expand_filelist(struct hostsfile *list);
+ char *parse_server(char *arg, union mysockaddr *addr, 
+                  union mysockaddr *source_addr, char *interface, int *flags);
+-int option_read_hostsfile(char *file);
++int option_read_dynfile(char *file, int flags);
++
+ /* forward.c */
+ void reply_query(int fd, int family, time_t now);
+ void receive_query(struct listener *listen, time_t now);
+@@ -1494,7 +1500,5 @@ int detect_loop(char *query, int type);
+ #ifdef HAVE_INOTIFY
+ void inotify_dnsmasq_init();
+ int inotify_check(time_t now);
+-#  ifdef HAVE_DHCP
+-void set_dhcp_inotify(void);
+-#  endif
++void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz);
+ #endif
+diff --git a/src/inotify.c b/src/inotify.c
+index 818fe8eddda4..c537f4c1562a 100644
+--- a/src/inotify.c
++++ b/src/inotify.c
+@@ -19,11 +19,6 @@
+ #include <sys/inotify.h>
+-#ifdef HAVE_DHCP
+-static void check_for_dhcp_inotify(struct inotify_event *in, time_t now);
+-#endif
+-
+-
+ /* the strategy is to set a inotify on the directories containing
+    resolv files, for any files in the directory which are close-write 
+    or moved into the directory.
+@@ -82,57 +77,28 @@ void inotify_dnsmasq_init()
+     }
+ }
+-int inotify_check(time_t now)
++
++/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
++void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
+ {
+-  int hit = 0;
++  struct hostsfile *ah;
+   
+-  while (1)
++  for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
+     {
+-      int rc;
+-      char *p;
+-      struct resolvc *res;
+-      struct inotify_event *in;
+-
+-      while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
+-      
+-      if (rc <= 0)
+-      break;
+-      
+-      for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len) 
++      DIR *dir_stream = NULL;
++      struct dirent *ent;
++      struct stat buf;
++     
++      if (!(ah->flags & flag))
++      continue;
++ 
++      if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
+       {
+-        in = (struct inotify_event*)p;
+-        
+-        for (res = daemon->resolv_files; res; res = res->next)
+-          if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
+-            hit = 1;
+-
+-#ifdef HAVE_DHCP
+-        if (daemon->dhcp || daemon->doing_dhcp6)
+-          check_for_dhcp_inotify(in, now);
+-#endif
++        my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"), 
++                  ah->fname, strerror(errno));
++        continue;
+       }
+-    }
+-  return hit;
+-}
+-
+-#ifdef HAVE_DHCP 
+-/* initialisation for dhcp-hostdir. Set inotify watch for each directory, and read pre-existing files */
+-void set_dhcp_inotify(void)
+-{
+-  struct hostsfile *ah;
+-
+-  for (ah = daemon->inotify_hosts; ah; ah = ah->next)
+-    {
+-       DIR *dir_stream = NULL;
+-       struct dirent *ent;
+-       struct stat buf;
+-
+-       if (stat(ah->fname, &buf) == -1 || !(S_ISDIR(buf.st_mode)))
+-       {
+-         my_syslog(LOG_ERR, _("bad directory in dhcp-hostsdir %s"), ah->fname);
+-         continue;
+-       }
+-
++      
+        if (!(ah->flags & AH_WD_DONE))
+        {
+          ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
+@@ -142,7 +108,8 @@ void set_dhcp_inotify(void)
+         a race which misses files being added as we start */
+        if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
+        {
+-         my_syslog(LOG_ERR, _("failed to create inotify for %s"), ah->fname);
++         my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
++                   ah->fname, strerror(errno));
+          continue;
+        }
+@@ -167,54 +134,90 @@ void set_dhcp_inotify(void)
+              
+              /* ignore non-regular files */
+              if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
+-               option_read_hostsfile(path);
+-             
++               {
++                 if (ah->flags & AH_HOSTS)
++                   total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
++#ifdef HAVE_DHCP
++                 else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
++                   option_read_dynfile(path, ah->flags);
++#endif                   
++               }
++
+              free(path);
+            }
+        }
+     }
+ }
+-static void check_for_dhcp_inotify(struct inotify_event *in, time_t now)
++int inotify_check(time_t now)
+ {
++  int hit = 0;
+   struct hostsfile *ah;
+-  /* ignore emacs backups and dotfiles */
+-  if (in->len == 0 || 
+-      in->name[in->len - 1] == '~' ||
+-      (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
+-      in->name[0] == '.')
+-    return;
+-
+-  for (ah = daemon->inotify_hosts; ah; ah = ah->next)
+-    if (ah->wd == in->wd)
+-      {
+-      size_t lendir = strlen(ah->fname);
+-      char *path;
+-         
+-      if ((path = whine_malloc(lendir + in->len + 2)))
+-        {
+-          strcpy(path, ah->fname);
+-          strcat(path, "/");
+-          strcat(path, in->name);
+-          
+-          if (option_read_hostsfile(path))
++  while (1)
++    {
++      int rc;
++      char *p;
++      struct resolvc *res;
++      struct inotify_event *in;
++
++      while ((rc = read(daemon->inotifyfd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
++      
++      if (rc <= 0)
++      break;
++      
++      for (p = inotify_buffer; rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event); p += sizeof(struct inotify_event) + in->len) 
++      {
++        in = (struct inotify_event*)p;
++        
++        for (res = daemon->resolv_files; res; res = res->next)
++          if (res->wd == in->wd && in->len != 0 && strcmp(res->file, in->name) == 0)
++            hit = 1;
++
++        /* ignore emacs backups and dotfiles */
++        if (in->len == 0 || 
++            in->name[in->len - 1] == '~' ||
++            (in->name[0] == '#' && in->name[in->len - 1] == '#') ||
++            in->name[0] == '.')
++          continue;
++        
++        for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
++          if (ah->wd == in->wd)
+             {
+-              /* Propogate the consequences of loading a new dhcp-host */
+-              dhcp_update_configs(daemon->dhcp_conf);
+-              lease_update_from_configs(); 
+-              lease_update_file(now); 
+-              lease_update_dns(1);
++              size_t lendir = strlen(ah->fname);
++              char *path;
++              
++              if ((path = whine_malloc(lendir + in->len + 2)))
++                {
++                  strcpy(path, ah->fname);
++                  strcat(path, "/");
++                  strcat(path, in->name);
++                  
++                  if (ah->flags & AH_HOSTS)
++                    read_hostsfile(path, ah->index, 0, NULL, 0);
++#ifdef HAVE_DHCP
++                  else if (ah->flags & AH_DHCP_HST)
++                    {
++                      if (option_read_dynfile(path, AH_DHCP_HST))
++                        {
++                          /* Propogate the consequences of loading a new dhcp-host */
++                          dhcp_update_configs(daemon->dhcp_conf);
++                          lease_update_from_configs(); 
++                          lease_update_file(now); 
++                          lease_update_dns(1);
++                        }
++                    }
++                  else if (ah->flags & AH_DHCP_OPT)
++                    option_read_dynfile(path, AH_DHCP_OPT);
++#endif
++                  
++                  free(path);
++                }
+             }
+-          
+-          free(path);
+-        }
+-      
+-      return;
+-      }
++      }
++    }
++  return hit;
+ }
+-#endif /* DHCP */
+-
+ #endif  /* INOTIFY */
+   
+diff --git a/src/option.c b/src/option.c
+index 22e11c37d374..6ef80117cc8c 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -150,6 +150,8 @@ struct myoption {
+ #define LOPT_IGNORE_ADDR   338
+ #define LOPT_MINCTTL       339
+ #define LOPT_DHCP_INOTIFY  340
++#define LOPT_DHOPT_INOTIFY 341
++#define LOPT_HOST_INOTIFY  342
+ #ifdef HAVE_GETOPT_LONG
+ static const struct option opts[] =  
+@@ -200,6 +202,7 @@ static const struct myoption opts[] =
+     { "local-ttl", 1, 0, 'T' },
+     { "no-negcache", 0, 0, 'N' },
+     { "addn-hosts", 1, 0, 'H' },
++    { "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
+     { "query-port", 1, 0, 'Q' },
+     { "except-interface", 1, 0, 'I' },
+     { "no-dhcp-interface", 1, 0, '2' },
+@@ -249,6 +252,7 @@ static const struct myoption opts[] =
+     { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
+     { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
+     { "dhcp-hostsdir", 1, 0, LOPT_DHCP_INOTIFY },
++    { "dhcp-optsdir", 1, 0, LOPT_DHOPT_INOTIFY },
+     { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
+     { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
+     { "stop-dns-rebind", 0, 0, LOPT_REBIND },
+@@ -338,9 +342,11 @@ static struct {
+   { LOPT_DHCP_HOST, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from file."), NULL },
+   { LOPT_DHCP_OPTS, ARG_DUP, "<path>", gettext_noop("Read DHCP option specs from file."), NULL },
+   { LOPT_DHCP_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP host specs from a directory."), NULL }, 
++  { LOPT_DHOPT_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read DHCP options from a directory."), NULL }, 
+   { LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
+   { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
+   { 'H', ARG_DUP, "<path>", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
++  { LOPT_HOST_INOTIFY, ARG_DUP, "<path>", gettext_noop("Read hosts files from a directory."), NULL },
+   { 'i', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) to listen on."), NULL },
+   { 'I', ARG_DUP, "<interface>", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
+   { 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
+@@ -1712,10 +1718,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+       break;
+ #endif /* HAVE_DHCP */
+-    case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
+-    case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
+-    case LOPT_DHCP_INOTIFY: /* dhcp-hostsdir */
+-    case 'H': /* --addn-hosts */
++    case LOPT_DHCP_HOST:     /* --dhcp-hostsfile */
++    case LOPT_DHCP_OPTS:     /* --dhcp-optsfile */
++    case LOPT_DHCP_INOTIFY:  /* --dhcp-hostsdir */
++    case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
++    case LOPT_HOST_INOTIFY:  /* --hostsdir */
++    case 'H':                /* --addn-hosts */
+       {
+       struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
+       static unsigned int hosts_index = SRC_AH;
+@@ -1737,10 +1745,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+           new->next = daemon->dhcp_opts_file;
+           daemon->dhcp_opts_file = new;
+         }       
+-      else if (option == LOPT_DHCP_INOTIFY)
++      else 
+         {
+-          new->next = daemon->inotify_hosts;
+-          daemon->inotify_hosts = new;
++          new->next = daemon->dynamic_dirs;
++          daemon->dynamic_dirs = new; 
++          if (option == LOPT_DHCP_INOTIFY)
++            new->flags |= AH_DHCP_HST;
++          else if (option == LOPT_DHOPT_INOTIFY)
++            new->flags |= AH_DHCP_OPT;
++          else if (option == LOPT_HOST_INOTIFY)
++            new->flags |= AH_HOSTS;
+         }
+       
+       break;
+@@ -4052,9 +4066,14 @@ static void read_file(char *file, FILE *f, int hard_opt)
+ }
+ #ifdef HAVE_DHCP
+-int option_read_hostsfile(char *file)
++int option_read_dynfile(char *file, int flags)
+ {
+-  return one_file(file, LOPT_BANK);
++  if (flags & AH_DHCP_HST)
++    return one_file(file, LOPT_BANK);
++  else if (flags & AH_DHCP_OPT)
++    return one_file(file, LOPT_OPTS);
++
++  return 0;
+ }
+ #endif
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0039-Update-copyrights-for-dawn-of-2015.patch b/src/patches/dnsmasq/0039-Update-copyrights-for-dawn-of-2015.patch
new file mode 100644 (file)
index 0000000..11bde00
--- /dev/null
@@ -0,0 +1,428 @@
+From aff3396280e944833f0e23d834aa6acd5fe2605a Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sat, 31 Jan 2015 20:13:40 +0000
+Subject: [PATCH 39/55] Update copyrights for dawn of 2015.
+
+---
+ Makefile             | 2 +-
+ src/auth.c           | 2 +-
+ src/blockdata.c      | 2 +-
+ src/bpf.c            | 2 +-
+ src/cache.c          | 2 +-
+ src/config.h         | 2 +-
+ src/conntrack.c      | 2 +-
+ src/dbus.c           | 2 +-
+ src/dhcp-common.c    | 2 +-
+ src/dhcp-protocol.h  | 2 +-
+ src/dhcp.c           | 2 +-
+ src/dhcp6-protocol.h | 2 +-
+ src/dhcp6.c          | 2 +-
+ src/dns-protocol.h   | 2 +-
+ src/dnsmasq.c        | 2 +-
+ src/dnsmasq.h        | 4 ++--
+ src/dnssec.c         | 2 +-
+ src/domain.c         | 2 +-
+ src/forward.c        | 2 +-
+ src/helper.c         | 2 +-
+ src/inotify.c        | 2 +-
+ src/ip6addr.h        | 2 +-
+ src/lease.c          | 2 +-
+ src/log.c            | 2 +-
+ src/loop.c           | 2 +-
+ src/netlink.c        | 2 +-
+ src/network.c        | 2 +-
+ src/option.c         | 2 +-
+ src/outpacket.c      | 2 +-
+ src/radv-protocol.h  | 2 +-
+ src/radv.c           | 2 +-
+ src/rfc1035.c        | 2 +-
+ src/rfc2131.c        | 2 +-
+ src/rfc3315.c        | 2 +-
+ src/slaac.c          | 2 +-
+ src/tftp.c           | 2 +-
+ src/util.c           | 2 +-
+ 37 files changed, 38 insertions(+), 38 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index bcbd5571671d..21e4a5c4101c 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,4 +1,4 @@
+-# dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++# dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+ #
+ #  This program is free software; you can redistribute it and/or modify
+ #  it under the terms of the GNU General Public License as published by
+diff --git a/src/auth.c b/src/auth.c
+index 59e05d3da38e..15721e52793f 100644
+--- a/src/auth.c
++++ b/src/auth.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/blockdata.c b/src/blockdata.c
+index 5a70a7967fa3..c8f5eae811eb 100644
+--- a/src/blockdata.c
++++ b/src/blockdata.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/bpf.c b/src/bpf.c
+index 4416b1c07287..997d87421bed 100644
+--- a/src/bpf.c
++++ b/src/bpf.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/cache.c b/src/cache.c
+index abaf25ec0f18..117ae279fd4e 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/config.h b/src/config.h
+index 5e5009271eba..8def6f200461 100644
+--- a/src/config.h
++++ b/src/config.h
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/conntrack.c b/src/conntrack.c
+index 6a5133ab93af..0fa2da903b03 100644
+--- a/src/conntrack.c
++++ b/src/conntrack.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/dbus.c b/src/dbus.c
+index a2a94dc85dac..5b69de518beb 100644
+--- a/src/dbus.c
++++ b/src/dbus.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/dhcp-common.c b/src/dhcp-common.c
+index 9d13ac8df6f1..ce115202a646 100644
+--- a/src/dhcp-common.c
++++ b/src/dhcp-common.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/dhcp-protocol.h b/src/dhcp-protocol.h
+index 4c0961472482..701b6cb3346e 100644
+--- a/src/dhcp-protocol.h
++++ b/src/dhcp-protocol.h
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/dhcp.c b/src/dhcp.c
+index 7acf2c4311a9..f29be9b489a7 100644
+--- a/src/dhcp.c
++++ b/src/dhcp.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/dhcp6-protocol.h b/src/dhcp6-protocol.h
+index 5927dc32f6af..928a2fa162ed 100644
+--- a/src/dhcp6-protocol.h
++++ b/src/dhcp6-protocol.h
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/dhcp6.c b/src/dhcp6.c
+index bc48fdddd3de..3c56e77c6085 100644
+--- a/src/dhcp6.c
++++ b/src/dhcp6.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/dns-protocol.h b/src/dns-protocol.h
+index 0aced3ce6952..16fade33d98c 100644
+--- a/src/dns-protocol.h
++++ b/src/dns-protocol.h
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index 2c629fe422aa..e903a24c8105 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 0c322a93993e..89e758b56a0a 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+  
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+@@ -14,7 +14,7 @@
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+-#define COPYRIGHT "Copyright (c) 2000-2014 Simon Kelley" 
++#define COPYRIGHT "Copyright (c) 2000-2015 Simon Kelley" 
+ #ifndef NO_LARGEFILE
+ /* Ensure we can use files >2GB (log files may grow this big) */
+diff --git a/src/dnssec.c b/src/dnssec.c
+index d39ab85ed966..a8dfe3871c85 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -1,5 +1,5 @@
+ /* dnssec.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
+-           and Copyright (c) 2012-2014 Simon Kelley
++           and Copyright (c) 2012-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/domain.c b/src/domain.c
+index fdd5e4f0838f..278698ca04b3 100644
+--- a/src/domain.c
++++ b/src/domain.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/forward.c b/src/forward.c
+index b17bc34f865f..438e9fa490b8 100644
+--- a/src/forward.c
++++ b/src/forward.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/helper.c b/src/helper.c
+index 4be53c361ee1..1fee72dead8c 100644
+--- a/src/helper.c
++++ b/src/helper.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/inotify.c b/src/inotify.c
+index c537f4c1562a..470d6ed99213 100644
+--- a/src/inotify.c
++++ b/src/inotify.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+  
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/ip6addr.h b/src/ip6addr.h
+index c7dcb39c33fa..f0b7e820e227 100644
+--- a/src/ip6addr.h
++++ b/src/ip6addr.h
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/lease.c b/src/lease.c
+index 5d56b1b9147e..545bbb7fd09c 100644
+--- a/src/lease.c
++++ b/src/lease.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/log.c b/src/log.c
+index 8083a8697323..a5ac605c7c5d 100644
+--- a/src/log.c
++++ b/src/log.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/loop.c b/src/loop.c
+index bb377ad12644..565f7d8e58e0 100644
+--- a/src/loop.c
++++ b/src/loop.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/netlink.c b/src/netlink.c
+index b39328376389..10f94db25a14 100644
+--- a/src/netlink.c
++++ b/src/netlink.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/network.c b/src/network.c
+index 14d2af2ce313..7045253d467b 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/option.c b/src/option.c
+index 6ef80117cc8c..cb4e76ba0aa2 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/outpacket.c b/src/outpacket.c
+index dce68f7cb09f..5b1ff9318bbd 100644
+--- a/src/outpacket.c
++++ b/src/outpacket.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/radv-protocol.h b/src/radv-protocol.h
+index 72ccda49d7fe..4cc1ea426e69 100644
+--- a/src/radv-protocol.h
++++ b/src/radv-protocol.h
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/radv.c b/src/radv.c
+index 3c8b162dbd8e..6da125b864ae 100644
+--- a/src/radv.c
++++ b/src/radv.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/rfc1035.c b/src/rfc1035.c
+index 262274fc5b80..5ef5ddb7485e 100644
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/rfc2131.c b/src/rfc2131.c
+index 5c9040892f1d..55526443dc84 100644
+--- a/src/rfc2131.c
++++ b/src/rfc2131.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/rfc3315.c b/src/rfc3315.c
+index e593ec9c362c..c1ddc805988d 100644
+--- a/src/rfc3315.c
++++ b/src/rfc3315.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/slaac.c b/src/slaac.c
+index 351d6802f85e..abaad537e25a 100644
+--- a/src/slaac.c
++++ b/src/slaac.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/tftp.c b/src/tftp.c
+index a527911fc0df..a57a31514f44 100644
+--- a/src/tftp.c
++++ b/src/tftp.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+diff --git a/src/util.c b/src/util.c
+index d532444da207..91d02410b13f 100644
+--- a/src/util.c
++++ b/src/util.c
+@@ -1,4 +1,4 @@
+-/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
++/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0040-inotify-documentation-updates.patch b/src/patches/dnsmasq/0040-inotify-documentation-updates.patch
new file mode 100644 (file)
index 0000000..981e553
--- /dev/null
@@ -0,0 +1,53 @@
+From 3d04f46334d0e345f589eda1372e638b946fe637 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sat, 31 Jan 2015 21:59:13 +0000
+Subject: [PATCH 40/55] inotify documentation updates.
+
+---
+ man/dnsmasq.8 | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index 005b5cca8d1f..c858323d78bd 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -50,6 +50,10 @@ Additional hosts file. Read the specified file as well as /etc/hosts. If -h is g
+ only the specified file. This option may be repeated for more than one
+ additional hosts file. If a directory is given, then read all the files contained in that directory. 
+ .TP
++.B --hostsdir=<path>
++Read all the hosts files contained in the directory. New or changed files
++are read automatically. See --dhcp-hostsdir for details.
++.TP
+ .B \-E, --expand-hosts
+ Add the domain to simple names (without a period) in /etc/hosts
+ in the same way as for DHCP-derived names. Note that this does not
+@@ -979,12 +983,14 @@ dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that
+ it is possible to encode the information in a
+ .TP
+ .B --dhcp-hostsdir=<path>
+-This is exactly equivalent to dhcp-hostfile, except for the following. The path MUST be a
++This is equivalent to dhcp-hostsfile, except for the following. The path MUST be a
+ directory, and not an individual file. Changed or new files within
+ the directory are read automatically, without the need to send SIGHUP.
+ If a file is deleted for changed after it has been read by dnsmasq, then the
+ host record it contained will remain until dnsmasq recieves a SIGHUP, or 
+ is restarted; ie host records are only added dynamically.
++.B --dhcp-optsdir=<path>
++This is equivalent to dhcp-optsfile, with the differences noted for --dhcp-hostsdir.
+ .TP
+ .B --dhcp-boot
+ flag as DHCP options, using the options names bootfile-name,
+@@ -1791,7 +1797,8 @@ clears its cache and then re-loads
+ .I /etc/hosts
+ and 
+ .I /etc/ethers 
+-and any file given by --dhcp-hostsfile, --dhcp-optsfile or --addn-hosts.
++and any file given by --dhcp-hostsfile, --dhcp-hostsdir, --dhcp-optsfile, 
++--dhcp-optsdir, --addn-hosts or --hostsdir.
+ The dhcp lease change script is called for all
+ existing DHCP leases. If 
+ .B
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0041-Fix-broken-ECDSA-DNSSEC-signatures.patch b/src/patches/dnsmasq/0041-Fix-broken-ECDSA-DNSSEC-signatures.patch
new file mode 100644 (file)
index 0000000..0c8aa9b
--- /dev/null
@@ -0,0 +1,39 @@
+From 6ef15b34ca83c62a939f69356d5c3f7a6bfef3d0 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sat, 31 Jan 2015 22:44:26 +0000
+Subject: [PATCH 41/55] Fix broken ECDSA DNSSEC signatures.
+
+---
+ CHANGELOG    | 2 ++
+ src/dnssec.c | 2 +-
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index c05dec63c587..c80dc0fdbe9e 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -65,6 +65,8 @@ version 2.73
+           configured to do stateful DHCPv6. Thanks to Win King Wan 
+           for the patch.
++          Fix broken DNSSEC validation of ECDSA signatures.
++      
+       
+ version 2.72
+             Add ra-advrouter mode, for RFC-3775 mobile IPv6 support.
+diff --git a/src/dnssec.c b/src/dnssec.c
+index a8dfe3871c85..26932373cd3e 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -275,7 +275,7 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len
+     }
+   
+   if (sig_len != 2*t || key_len != 2*t ||
+-      (p = blockdata_retrieve(key_data, key_len, NULL)))
++      !(p = blockdata_retrieve(key_data, key_len, NULL)))
+     return 0;
+   
+   mpz_import(x, t , 1, 1, 0, 0, p);
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0042-BSD-make-support.patch b/src/patches/dnsmasq/0042-BSD-make-support.patch
new file mode 100644 (file)
index 0000000..76f76ef
--- /dev/null
@@ -0,0 +1,29 @@
+From 106266761828a0acb006346ae47bf031dee46a5d Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sun, 1 Feb 2015 00:15:16 +0000
+Subject: [PATCH 42/55] BSD make support
+
+---
+ Makefile | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 21e4a5c4101c..2910320b6452 100644
+--- a/Makefile
++++ b/Makefile
+@@ -64,8 +64,10 @@ nettle_libs =   `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG
+ gmp_libs =      `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC NO_GMP --copy -lgmp`
+ sunos_libs =    `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
+ version =     -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
+-copts_conf = .copts_$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | \
+-                      ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
++
++sum?=$(shell $(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' ')
++sum!=$(CC) -DDNSMASQ_COMPILE_OPTS $(COPTS) -E $(top)/$(SRC)/dnsmasq.h | ( md5sum 2>/dev/null || md5 ) | cut -f 1 -d ' '
++copts_conf = .copts_$(sum)
+ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
+        dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0043-Fix-build-failure-on-openBSD.patch b/src/patches/dnsmasq/0043-Fix-build-failure-on-openBSD.patch
new file mode 100644 (file)
index 0000000..a3df61b
--- /dev/null
@@ -0,0 +1,25 @@
+From 8d8a54ec79d9f96979fabbd97b1dd2ddebc7d78f Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sun, 1 Feb 2015 21:48:46 +0000
+Subject: [PATCH 43/55] Fix build failure on openBSD.
+
+---
+ src/tables.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/tables.c b/src/tables.c
+index dcdef794c4d2..aae1252708db 100644
+--- a/src/tables.c
++++ b/src/tables.c
+@@ -21,7 +21,7 @@
+ #if defined(HAVE_IPSET) && defined(HAVE_BSD_NETWORK)
+ #ifndef __FreeBSD__
+-#include <bsd/string.h>
++#include <string.h>
+ #endif
+ #include <sys/types.h>
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0044-Manpage-typo-fix.patch b/src/patches/dnsmasq/0044-Manpage-typo-fix.patch
new file mode 100644 (file)
index 0000000..e0e384f
--- /dev/null
@@ -0,0 +1,25 @@
+From d36b732c4cfa91ea09af64b5dc0f3a85a075e5bc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thi=C3=A9baud=20Weksteen?= <thiebaud@weksteen.fr>
+Date: Mon, 2 Feb 2015 21:37:27 +0000
+Subject: [PATCH 44/55] Manpage typo fix.
+
+---
+ man/dnsmasq.8 | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index c858323d78bd..27f85d40fbbb 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -516,7 +516,7 @@ zone files: the port, weight and priority numbers are in a different
+ order. More than one SRV record for a given service/domain is allowed,
+ all that match are returned.
+ .TP
+-.B --host-record=<name>[,<name>....][<IPv4-address>],[<IPv6-address>]
++.B --host-record=<name>[,<name>....],[<IPv4-address>],[<IPv6-address>]
+ Add A, AAAA and PTR records to the DNS. This adds one or more names to
+ the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may
+ appear in more than one 
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0045-Fixup-dhcp-configs-after-reading-extra-hostfiles-wit.patch b/src/patches/dnsmasq/0045-Fixup-dhcp-configs-after-reading-extra-hostfiles-wit.patch
new file mode 100644 (file)
index 0000000..fc1b98a
--- /dev/null
@@ -0,0 +1,38 @@
+From 2941d3ac898cf84b544e47c9735c5e4111711db1 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 2 Feb 2015 22:36:42 +0000
+Subject: [PATCH 45/55] Fixup dhcp-configs after reading extra hostfiles with
+ inotify.
+
+---
+ src/inotify.c | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/src/inotify.c b/src/inotify.c
+index 470d6ed99213..6f4cd79e0030 100644
+--- a/src/inotify.c
++++ b/src/inotify.c
+@@ -194,7 +194,19 @@ int inotify_check(time_t now)
+                   strcat(path, in->name);
+                   
+                   if (ah->flags & AH_HOSTS)
+-                    read_hostsfile(path, ah->index, 0, NULL, 0);
++                    {
++                      read_hostsfile(path, ah->index, 0, NULL, 0);
++#ifdef HAVE_DHCP
++                      if (daemon->dhcp || daemon->doing_dhcp6) 
++                        {
++                          /* Propogate the consequences of loading a new dhcp-host */
++                          dhcp_update_configs(daemon->dhcp_conf);
++                          lease_update_from_configs(); 
++                          lease_update_file(now); 
++                          lease_update_dns(1);
++                        }
++#endif
++                    }
+ #ifdef HAVE_DHCP
+                   else if (ah->flags & AH_DHCP_HST)
+                     {
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0046-Extra-logging-for-inotify-code.patch b/src/patches/dnsmasq/0046-Extra-logging-for-inotify-code.patch
new file mode 100644 (file)
index 0000000..803174a
--- /dev/null
@@ -0,0 +1,68 @@
+From f9c863708c6b0aea31ff7a466647685dc739de50 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Tue, 3 Feb 2015 21:52:48 +0000
+Subject: [PATCH 46/55] Extra logging for inotify code.
+
+---
+ src/cache.c   | 9 ++++-----
+ src/inotify.c | 4 +++-
+ src/option.c  | 4 +++-
+ 3 files changed, 10 insertions(+), 7 deletions(-)
+
+diff --git a/src/cache.c b/src/cache.c
+index 117ae279fd4e..43245b771b53 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -1022,11 +1022,10 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
+   fclose(f);
+   
+   if (rhash)
+-    {
+-      rehash(name_count); 
+-      my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
+-    }
+-
++    rehash(name_count); 
++  
++  my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
++  
+   return name_count;
+ }
+           
+diff --git a/src/inotify.c b/src/inotify.c
+index 6f4cd79e0030..44ce0c9af051 100644
+--- a/src/inotify.c
++++ b/src/inotify.c
+@@ -192,7 +192,9 @@ int inotify_check(time_t now)
+                   strcpy(path, ah->fname);
+                   strcat(path, "/");
+                   strcat(path, in->name);
+-                  
++                   
++                  my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
++
+                   if (ah->flags & AH_HOSTS)
+                     {
+                       read_hostsfile(path, ah->index, 0, NULL, 0);
+diff --git a/src/option.c b/src/option.c
+index cb4e76ba0aa2..e4b4865d07a5 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -4068,11 +4068,13 @@ static void read_file(char *file, FILE *f, int hard_opt)
+ #ifdef HAVE_DHCP
+ int option_read_dynfile(char *file, int flags)
+ {
++  my_syslog(MS_DHCP | LOG_INFO, _("read %s"), file);
++  
+   if (flags & AH_DHCP_HST)
+     return one_file(file, LOPT_BANK);
+   else if (flags & AH_DHCP_OPT)
+     return one_file(file, LOPT_OPTS);
+-
++  
+   return 0;
+ }
+ #endif
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0047-man-page-typo.patch b/src/patches/dnsmasq/0047-man-page-typo.patch
new file mode 100644 (file)
index 0000000..6606a7a
--- /dev/null
@@ -0,0 +1,24 @@
+From efb8b5566aafc1f3ce18514a2df93af5a2e4998c Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sat, 7 Feb 2015 22:36:34 +0000
+Subject: [PATCH 47/55] man page typo.
+
+---
+ man/dnsmasq.8 | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index 27f85d40fbbb..5cdd186afaa0 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -989,6 +989,7 @@ the directory are read automatically, without the need to send SIGHUP.
+ If a file is deleted for changed after it has been read by dnsmasq, then the
+ host record it contained will remain until dnsmasq recieves a SIGHUP, or 
+ is restarted; ie host records are only added dynamically.
++.TP
+ .B --dhcp-optsdir=<path>
+ This is equivalent to dhcp-optsfile, with the differences noted for --dhcp-hostsdir.
+ .TP
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0048-Fix-get-version-script-which-returned-wrong-tag-in-s.patch b/src/patches/dnsmasq/0048-Fix-get-version-script-which-returned-wrong-tag-in-s.patch
new file mode 100644 (file)
index 0000000..5633d9d
--- /dev/null
@@ -0,0 +1,26 @@
+From f4f400776b3c1aa303d1a0fcd500f0ab5bc970f2 Mon Sep 17 00:00:00 2001
+From: Shantanu Gadgil <shantanugadgil@yahoo.com>
+Date: Wed, 11 Feb 2015 20:16:59 +0000
+Subject: [PATCH 48/55] Fix get-version script which returned wrong tag in some
+ situations.
+
+---
+ bld/get-version | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/bld/get-version b/bld/get-version
+index c246a3cc6a47..7ab75db729ac 100755
+--- a/bld/get-version
++++ b/bld/get-version
+@@ -20,7 +20,7 @@ else
+      vers=`cat $1/VERSION | sed 's/[(), ]/,/ g' | tr ',' '\n' | grep ^v[0-9]`
+      if [ $? -eq 0 ]; then
+-         echo "${vers}" | sort | head -n 1 | sed 's/^v//'
++         echo "${vers}" | sort -r | head -n 1 | sed 's/^v//'
+      else
+          cat $1/VERSION
+      fi
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0049-Typos.patch b/src/patches/dnsmasq/0049-Typos.patch
new file mode 100644 (file)
index 0000000..8f64b39
--- /dev/null
@@ -0,0 +1,26 @@
+From 8ff70de618eb7de9147dbfbd4deca4a2dd62f0cb Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sat, 14 Feb 2015 20:02:37 +0000
+Subject: [PATCH 49/55] Typos.
+
+---
+ src/inotify.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/inotify.c b/src/inotify.c
+index 44ce0c9af051..9422066257f5 100644
+--- a/src/inotify.c
++++ b/src/inotify.c
+@@ -104,7 +104,8 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
+          ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
+          ah->flags |= AH_WD_DONE;
+        }
+-       /* Read contents of dir _after_ calling add_watch, in the ho[e of avoiding
++
++       /* Read contents of dir _after_ calling add_watch, in the hope of avoiding
+         a race which misses files being added as we start */
+        if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
+        {
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0050-Make-dynamic-hosts-files-work-when-no-hosts-set.patch b/src/patches/dnsmasq/0050-Make-dynamic-hosts-files-work-when-no-hosts-set.patch
new file mode 100644 (file)
index 0000000..af79802
--- /dev/null
@@ -0,0 +1,45 @@
+From caeea190f12efd20139f694aac4942d1ac00019f Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sat, 14 Feb 2015 20:08:56 +0000
+Subject: [PATCH 50/55] Make dynamic hosts files work when --no-hosts set.
+
+---
+ src/cache.c | 21 +++++++++++----------
+ 1 file changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/src/cache.c b/src/cache.c
+index 43245b771b53..c95624c42b1c 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -1133,17 +1133,18 @@ void cache_reload(void)
+     {
+       if (daemon->cachesize > 0)
+       my_syslog(LOG_INFO, _("cleared cache"));
+-      return;
+     }
+-  
+-  if (!option_bool(OPT_NO_HOSTS))
+-    total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
+-  
+-  daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
+-  for (ah = daemon->addn_hosts; ah; ah = ah->next)
+-    if (!(ah->flags & AH_INACTIVE))
+-      total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
+-  
++  else
++    {
++      if (!option_bool(OPT_NO_HOSTS))
++      total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
++      
++      daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
++      for (ah = daemon->addn_hosts; ah; ah = ah->next)
++      if (!(ah->flags & AH_INACTIVE))
++        total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
++    }
++
+ #ifdef HAVE_INOTIFY
+   set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
+ #endif
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0051-Fix-trivial-memory-leaks-to-quieten-valgrind.patch b/src/patches/dnsmasq/0051-Fix-trivial-memory-leaks-to-quieten-valgrind.patch
new file mode 100644 (file)
index 0000000..2c38cfc
--- /dev/null
@@ -0,0 +1,55 @@
+From 28b879ac47b872af6e8c5e86d76806c69338434d Mon Sep 17 00:00:00 2001
+From: Chen Wei <weichen302@icloud.com>
+Date: Tue, 17 Feb 2015 22:07:35 +0000
+Subject: [PATCH 51/55] Fix trivial memory leaks to quieten valgrind.
+
+---
+ src/dnsmasq.c |  2 ++
+ src/option.c  | 11 +++++++++--
+ 2 files changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index e903a24c8105..e6dabbf556f7 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -627,6 +627,8 @@ int main (int argc, char **argv)
+     }
+   
+ #ifdef HAVE_LINUX_NETWORK
++  free(hdr);
++  free(data);
+   if (option_bool(OPT_DEBUG)) 
+     prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ #endif
+diff --git a/src/option.c b/src/option.c
+index e4b4865d07a5..ae0ad002d8b8 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -4361,7 +4361,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
+ {
+   char *buff = opt_malloc(MAXDNAME);
+   int option, conffile_opt = '7', testmode = 0;
+-  char *arg, *conffile = CONFFILE;
++  char *arg, *conffile = NULL;
+       
+   opterr = 0;
+@@ -4476,7 +4476,14 @@ void read_opts(int argc, char **argv, char *compile_opts)
+     }
+   if (conffile)
+-    one_file(conffile, conffile_opt);
++    {
++      one_file(conffile, conffile_opt);
++      free(conffile);
++    }
++  else
++    {
++      one_file(CONFFILE, conffile_opt);
++    }
+   /* port might not be known when the address is parsed - fill in here */
+   if (daemon->servers)
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0052-Fix-uninitialized-value-used-in-get_client_mac.patch b/src/patches/dnsmasq/0052-Fix-uninitialized-value-used-in-get_client_mac.patch
new file mode 100644 (file)
index 0000000..46277fb
--- /dev/null
@@ -0,0 +1,27 @@
+From 0705a7e2d57654b27c7e14f35ca77241c1821f4d Mon Sep 17 00:00:00 2001
+From: Tomas Hozza <thozza@redhat.com>
+Date: Mon, 23 Feb 2015 21:26:26 +0000
+Subject: [PATCH 52/55] Fix uninitialized value used in get_client_mac()
+
+---
+ src/dhcp6.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/dhcp6.c b/src/dhcp6.c
+index 3c56e77c6085..c7144f5fee7c 100644
+--- a/src/dhcp6.c
++++ b/src/dhcp6.c
+@@ -246,7 +246,9 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
+   neigh.code = 0;
+   neigh.reserved = 0;
+   neigh.target = *client;
+-  
++  /* RFC4443 section-2.3: checksum has to be zero to be calculated */
++  neigh.checksum = 0;
++   
+   memset(&addr, 0, sizeof(addr));
+ #ifdef HAVE_SOCKADDR_SA_LEN
+   addr.sin6_len = sizeof(struct sockaddr_in6);
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0053-Log-parsing-utils-in-contrib-reverse-dns.patch b/src/patches/dnsmasq/0053-Log-parsing-utils-in-contrib-reverse-dns.patch
new file mode 100644 (file)
index 0000000..9527030
--- /dev/null
@@ -0,0 +1,110 @@
+From 47b9ac59c715827252ae6e6732903c3dabb697fb Mon Sep 17 00:00:00 2001
+From: Joachim Zobel <jz-2014@heute-morgen.de>
+Date: Mon, 23 Feb 2015 21:38:11 +0000
+Subject: [PATCH 53/55] Log parsing utils in contrib/reverse-dns
+
+---
+ contrib/reverse-dns/README             | 18 ++++++++++++++++++
+ contrib/reverse-dns/reverse_dns.sh     | 29 +++++++++++++++++++++++++++++
+ contrib/reverse-dns/reverse_replace.sh | 28 ++++++++++++++++++++++++++++
+ 3 files changed, 75 insertions(+)
+ create mode 100644 contrib/reverse-dns/README
+ create mode 100644 contrib/reverse-dns/reverse_dns.sh
+ create mode 100644 contrib/reverse-dns/reverse_replace.sh
+
+diff --git a/contrib/reverse-dns/README b/contrib/reverse-dns/README
+new file mode 100644
+index 000000000000..f87eb77c4c22
+--- /dev/null
++++ b/contrib/reverse-dns/README
+@@ -0,0 +1,18 @@
++Hi.\r
++\r
++To translate my routers netstat-nat output into names that actually talk\r
++to me I have started writing to simple shell scripts. They require \r
++\r
++log-queries\r
++log-facility=/var/log/dnsmasq.log\r
++\r
++to be set. With\r
++\r
++netstat-nat -n -4 | reverse_replace.sh \r
++\r
++I get retranslated output.\r
++\r
++Sincerely,\r
++Joachim\r
++\r
++\r
+diff --git a/contrib/reverse-dns/reverse_dns.sh b/contrib/reverse-dns/reverse_dns.sh
+new file mode 100644
+index 000000000000..c0fff300a947
+--- /dev/null
++++ b/contrib/reverse-dns/reverse_dns.sh
+@@ -0,0 +1,29 @@
++#!/bin/bash
++# $Id: reverse_dns.sh 4 2015-02-17 20:14:59Z jo $
++#
++# Usage: reverse_dns.sh IP
++# Uses the dnsmasq query log to lookup the name 
++# that was last queried to return the given IP.
++#
++
++IP=$1
++qmIP=`echo $IP | sed 's#\.#\\.#g'`
++LOG=/var/log/dnsmasq.log
++
++IP_regex='^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'
++
++if ! [[ $IP =~ $IP_regex ]]; then
++  echo -n $IP
++  exit
++fi
++
++NAME=`tac $LOG | \
++  grep " is $IP" | head -1 | \
++  sed "s#.* \([^ ]*\) is $qmIP.*#\1#" `
++
++if [ -z "$NAME" ]; then
++  echo -n $IP
++else
++  echo -n $NAME
++fi
++
+diff --git a/contrib/reverse-dns/reverse_replace.sh b/contrib/reverse-dns/reverse_replace.sh
+new file mode 100644
+index 000000000000..a11c164b7f19
+--- /dev/null
++++ b/contrib/reverse-dns/reverse_replace.sh
+@@ -0,0 +1,28 @@
++#!/bin/bash
++# $Id: reverse_replace.sh 4 2015-02-17 20:14:59Z jo $
++#
++# Usage e.g.: netstat -n -4 | reverse_replace.sh 
++# Parses stdin for IP4 addresses and replaces them 
++# with names retrieved by reverse_dns.sh
++#
++
++DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
++DNS=$DIR/reverse_dns.sh
++
++# sed regex
++IP_regex='[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'
++
++while read LINE; do
++  if grep --quiet $IP_regex <<< "$LINE"; then
++    IPs=`sed "s#.*\b\($IP_regex\)\b.*#\1 #g" <<< "$LINE"`
++    IPs=($IPs)
++    for IP in "${IPs[@]}"
++    do
++      NAME=`$DNS $IP`
++      # echo "$NAME is $IP";
++      LINE="${LINE/$IP/$NAME}" 
++    done
++  fi
++  echo $LINE
++done < /dev/stdin
++
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0054-Add-dnssec-timestamp-option-and-facility.patch b/src/patches/dnsmasq/0054-Add-dnssec-timestamp-option-and-facility.patch
new file mode 100644 (file)
index 0000000..2ff79d5
--- /dev/null
@@ -0,0 +1,252 @@
+From f6e62e2af96f5fa0d1e3d93167a93a8f09bf6e61 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Sun, 1 Mar 2015 18:17:54 +0000
+Subject: [PATCH 54/55] Add --dnssec-timestamp option and facility.
+
+---
+ CHANGELOG     |  6 +++++
+ man/dnsmasq.8 |  6 +++++
+ src/dnsmasq.c | 11 +++++++-
+ src/dnsmasq.h |  2 ++
+ src/dnssec.c  | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
+ src/option.c  |  7 +++++
+ 6 files changed, 108 insertions(+), 6 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index c80dc0fdbe9e..4f4fa305deaa 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -66,6 +66,12 @@ version 2.73
+           for the patch.
+           Fix broken DNSSEC validation of ECDSA signatures.
++
++          Add --dnssec-timestamp option, which provides an automatic
++          way to detect when the system time becomes valid after boot
++          on systems without an RTC, whilst allowing DNS queries before the
++          clock is valid so that NTP can run. Thanks to
++          Kevin Darbyshire-Bryant for developing this idea.
+       
+       
+ version 2.72
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index 5cdd186afaa0..097e7d75145c 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -674,6 +674,12 @@ that dnsmasq should be started with this flag when the platform determines that
+ reliable time is established, a SIGHUP should be sent to dnsmasq, which enables time checking, and purges the cache of DNS records
+ which have not been throughly checked.
+ .TP
++.B --dnssec-timestamp=<path>
++Enables an alternative way of checking the validity of the system time for DNSSEC (see --dnssec-no-timecheck). In this case, the 
++system time is considered to be valid once it becomes later than the timestamp on the specified file. The file is created and 
++its timestamp set automatically by dnsmasq. The file must be stored on a persistent filesystem, so that it and its mtime are carried 
++over system restarts. 
++.TP
+ .B --proxy-dnssec
+ Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it.  This is an 
+ alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between 
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index e6dabbf556f7..769a19afe6c5 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -58,6 +58,9 @@ int main (int argc, char **argv)
+   struct dhcp_context *context;
+   struct dhcp_relay *relay;
+ #endif
++#ifdef HAVE_DNSSEC
++  int badtime;
++#endif
+ #ifdef LOCALEDIR
+   setlocale(LC_ALL, "");
+@@ -369,7 +372,11 @@ int main (int argc, char **argv)
+   if (baduser)
+     die(_("unknown user or group: %s"), baduser, EC_BADCONF);
+-   
++
++#ifdef HAVE_DNSSEC  
++  badtime = setup_timestamp(ent_pw->pw_uid);
++#endif
++
+   /* implement group defaults, "dip" if available, or group associated with uid */
+   if (!daemon->group_set && !gp)
+     {
+@@ -689,6 +696,8 @@ int main (int argc, char **argv)
+       my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
+       if (option_bool(OPT_DNSSEC_TIME))
+       my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until first cache reload"));
++      if (badtime)
++      my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));
+     }
+ #endif
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 89e758b56a0a..b2f02dda63f0 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -986,6 +986,7 @@ extern struct daemon {
+ #endif
+ #ifdef HAVE_DNSSEC
+   struct ds_config *ds;
++  char *timestamp_file;
+ #endif
+   /* globally used stuff for DNS */
+@@ -1151,6 +1152,7 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
+ int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
+ size_t filter_rrsigs(struct dns_header *header, size_t plen);
+ unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
++int setup_timestamp(uid_t uid);
+ /* util.c */
+ void rand_init(void);
+diff --git a/src/dnssec.c b/src/dnssec.c
+index 26932373cd3e..bf4406469de0 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -34,6 +34,7 @@
+ #include <nettle/dsa-compat.h>
+ #endif
++#include <utime.h>
+ #define SERIAL_UNDEF  -100
+ #define SERIAL_EQ        0
+@@ -394,17 +395,88 @@ static int serial_compare_32(unsigned long s1, unsigned long s2)
+   return SERIAL_UNDEF;
+ }
++/* Called at startup. If the timestamp file is configured and exists, put its mtime on
++   timestamp_time. If it doesn't exist, create it, and set the mtime to 1-1-2015.
++   Change the ownership to the user we'll be running as, so that we can update the mtime.
++*/
++static time_t timestamp_time;
++static int back_to_the_future;
++
++int setup_timestamp(uid_t uid)
++{
++  struct stat statbuf;
++  
++  back_to_the_future = 0;
++  
++  if (!option_bool(OPT_DNSSEC_VALID) || !daemon->timestamp_file)
++    return 0;
++  
++  if (stat(daemon->timestamp_file, &statbuf) != -1)
++    {
++      timestamp_time = statbuf.st_mtime;
++    check_and_exit:
++      if (difftime(timestamp_time, time(0)) <=  0)
++      {
++        /* time already OK, update timestamp, and do key checking from the start. */
++        if (utime(daemon->timestamp_file, NULL) == -1)
++          my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
++        back_to_the_future = 1;
++        return 0;
++      }
++      return 1;
++    }
++  
++  if (errno == ENOENT)
++    {
++      int fd = open(daemon->timestamp_file, O_WRONLY | O_CREAT | O_NONBLOCK, 0666);
++      if (fd != -1)
++      {
++        struct utimbuf timbuf;
++
++        close(fd);
++        
++        timestamp_time = timbuf.actime = timbuf.modtime = 1420070400; /* 1-1-2015 */
++        if (utime(daemon->timestamp_file, &timbuf) == 0 &&
++            (getuid() != 0 || chown(daemon->timestamp_file, uid, -1) == 0))
++          goto check_and_exit;
++      }
++    }
++
++  die(_("Cannot create timestamp file %s: %s" ), daemon->timestamp_file, EC_BADCONF);
++  return 0;
++}
++
+ /* Check whether today/now is between date_start and date_end */
+ static int check_date_range(unsigned long date_start, unsigned long date_end)
+ {
+-  unsigned long curtime;
+-
++  unsigned long curtime = time(0);
++ 
+   /* Checking timestamps may be temporarily disabled */
+-  if (option_bool(OPT_DNSSEC_TIME))
++    
++  /* If the current time if _before_ the timestamp
++     on our persistent timestamp file, then assume the
++     time if not yet correct, and don't check the
++     key timestamps. As soon as the current time is
++     later then the timestamp, update the timestamp
++     and start checking keys */
++  if (daemon->timestamp_file)
++    {
++      if (back_to_the_future == 0 && difftime(timestamp_time, curtime) <= 0)
++      {
++        if (utime(daemon->timestamp_file, NULL) != 0)
++          my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
++        
++        back_to_the_future = 1;       
++        set_option_bool(OPT_DNSSEC_TIME);
++        queue_event(EVENT_RELOAD); /* purge cache */
++      } 
++
++      if (back_to_the_future == 0)
++      return 1;
++    }
++  else if (option_bool(OPT_DNSSEC_TIME))
+     return 1;
+   
+-  curtime = time(0);
+-  
+   /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
+   return serial_compare_32(curtime, date_start) == SERIAL_GT
+     && serial_compare_32(curtime, date_end) == SERIAL_LT;
+diff --git a/src/option.c b/src/option.c
+index ae0ad002d8b8..eace40bb566c 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -152,6 +152,7 @@ struct myoption {
+ #define LOPT_DHCP_INOTIFY  340
+ #define LOPT_DHOPT_INOTIFY 341
+ #define LOPT_HOST_INOTIFY  342
++#define LOPT_DNSSEC_STAMP  343
+ #ifdef HAVE_GETOPT_LONG
+ static const struct option opts[] =  
+@@ -300,6 +301,7 @@ static const struct myoption opts[] =
+     { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
+     { "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
+     { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME },
++    { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
+ #ifdef OPTION6_PREFIX_CLASS 
+     { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
+ #endif
+@@ -463,6 +465,7 @@ static struct {
+   { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
+   { LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
+   { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL },
++  { LOPT_DNSSEC_STAMP, ARG_ONE, "<path>", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL },
+ #ifdef OPTION6_PREFIX_CLASS 
+   { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
+ #endif
+@@ -3867,6 +3870,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+       }
+ #ifdef HAVE_DNSSEC
++    case LOPT_DNSSEC_STAMP:
++      daemon->timestamp_file = opt_string_alloc(arg); 
++      break;
++
+     case LOPT_TRUST_ANCHOR:
+       {
+       struct ds_config *new = opt_malloc(sizeof(struct ds_config));
+-- 
+2.1.0
+
diff --git a/src/patches/dnsmasq/0055-Fix-last-commit-to-not-crash-if-uid-changing-not-con.patch b/src/patches/dnsmasq/0055-Fix-last-commit-to-not-crash-if-uid-changing-not-con.patch
new file mode 100644 (file)
index 0000000..54bb5d5
--- /dev/null
@@ -0,0 +1,63 @@
+From 9003b50b13da624ca45f3e0cf99abb623b8d026b Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Mon, 2 Mar 2015 22:47:23 +0000
+Subject: [PATCH 55/55] Fix last commit to not crash if uid changing not
+ configured.
+
+---
+ src/dnsmasq.c | 2 +-
+ src/dnsmasq.h | 2 +-
+ src/dnssec.c  | 4 ++--
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index 769a19afe6c5..9e05c0e31569 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -374,7 +374,7 @@ int main (int argc, char **argv)
+     die(_("unknown user or group: %s"), baduser, EC_BADCONF);
+ #ifdef HAVE_DNSSEC  
+-  badtime = setup_timestamp(ent_pw->pw_uid);
++  badtime = setup_timestamp(ent_pw);
+ #endif
+   /* implement group defaults, "dip" if available, or group associated with uid */
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index b2f02dda63f0..a451cb4dd03c 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -1152,7 +1152,7 @@ int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char
+ int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
+ size_t filter_rrsigs(struct dns_header *header, size_t plen);
+ unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
+-int setup_timestamp(uid_t uid);
++int setup_timestamp(struct passwd *ent_pw);
+ /* util.c */
+ void rand_init(void);
+diff --git a/src/dnssec.c b/src/dnssec.c
+index bf4406469de0..c60eacf73c6b 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -402,7 +402,7 @@ static int serial_compare_32(unsigned long s1, unsigned long s2)
+ static time_t timestamp_time;
+ static int back_to_the_future;
+-int setup_timestamp(uid_t uid)
++int setup_timestamp(struct passwd *ent_pw)
+ {
+   struct stat statbuf;
+   
+@@ -437,7 +437,7 @@ int setup_timestamp(uid_t uid)
+         
+         timestamp_time = timbuf.actime = timbuf.modtime = 1420070400; /* 1-1-2015 */
+         if (utime(daemon->timestamp_file, &timbuf) == 0 &&
+-            (getuid() != 0 || chown(daemon->timestamp_file, uid, -1) == 0))
++            (!ent_pw || getuid() != 0 || chown(daemon->timestamp_file, ent_pw->pw_uid, -1) == 0))
+           goto check_and_exit;
+       }
+     }
+-- 
+2.1.0
+