From: Michael Tremer Date: Wed, 4 Mar 2015 22:27:27 +0000 (+0100) Subject: dnsmasq: Import latest git version of dnsmasq X-Git-Url: http://git.ipfire.org/?p=people%2Fdweismueller%2Fipfire-2.x.git;a=commitdiff_plain;h=6644c1c7f24262b2040d05cbb877ba36658d70e7 dnsmasq: Import latest git version of dnsmasq --- diff --git a/lfs/dnsmasq b/lfs/dnsmasq index d4eb9d4ce..c256f75b4 100644 --- a/lfs/dnsmasq +++ b/lfs/dnsmasq @@ -24,10 +24,10 @@ 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 index 000000000..adcb44fff --- /dev/null +++ b/src/patches/dnsmasq/0001-Add-newline-at-the-end-of-example-config-file.patch @@ -0,0 +1,23 @@ +From f2658275b25ebfe691cdcb9fede85a3088cca168 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..b84440baf --- /dev/null +++ b/src/patches/dnsmasq/0002-crash-at-startup-when-an-empty-suffix-is-supplied-to.patch @@ -0,0 +1,86 @@ +From 00cd9d551998307225312fd21f761cfa8868bd2c Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..fe73acd64 --- /dev/null +++ b/src/patches/dnsmasq/0003-Debian-build-fixes-for-kFreeBSD.patch @@ -0,0 +1,36 @@ +From 6ac3bc0452a74e16e3d620a0757b0f8caab182ec Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 ++#endif ++ + #include + #include + +@@ -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 index 000000000..5d7c3c4e8 --- /dev/null +++ b/src/patches/dnsmasq/0004-Set-conntrack-mark-before-connect-call.patch @@ -0,0 +1,68 @@ +From e9828b6f66b22ce8873f8d30a773137d1aef1b92 Mon Sep 17 00:00:00 2001 +From: Karl Vogel +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 index 000000000..80c55f684 --- /dev/null +++ b/src/patches/dnsmasq/0005-Fix-typo-in-new-Dbus-code.patch @@ -0,0 +1,26 @@ +From 17b475912f6a4e72797a543dad59d4d5dde6bb1b Mon Sep 17 00:00:00 2001 +From: Daniel Collins +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 index 000000000..5f9f57276 --- /dev/null +++ b/src/patches/dnsmasq/0006-Fit-example-conf-file-typo.patch @@ -0,0 +1,22 @@ +From 3d9d2dd0018603a2ae4b9cd65ac6ff959f4fd8c7 Mon Sep 17 00:00:00 2001 +From: Tomas Hozza +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 index 000000000..370d1065c --- /dev/null +++ b/src/patches/dnsmasq/0007-Improve-RFC-compliance-when-unable-to-supply-address.patch @@ -0,0 +1,107 @@ +From b9ff5c8f435173cfa616e3c398bdc089ef690a07 Mon Sep 17 00:00:00 2001 +From: Vladislav Grishenko +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 index 000000000..3f8bad331 --- /dev/null +++ b/src/patches/dnsmasq/0008-Fix-conntrack-with-bind-interfaces.patch @@ -0,0 +1,39 @@ +From 98906275a02ae260fe3f82133bd79054f8315f06 Mon Sep 17 00:00:00 2001 +From: Hans Dedecker +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 +--- + 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 index 000000000..81939202b --- /dev/null +++ b/src/patches/dnsmasq/0009-Use-inotify-instead-of-polling-on-Linux.patch @@ -0,0 +1,257 @@ +From 193de4abf59e49c6b70d54cfe9720fcb95ca2f71 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 . ++*/ ++ ++#include "dnsmasq.h" ++#include ++ ++#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 index 000000000..70fc5c3f8 --- /dev/null +++ b/src/patches/dnsmasq/0010-Teach-the-new-inotify-code-about-symlinks.patch @@ -0,0 +1,73 @@ +From 857973e6f7e0a3d03535a9df7f9373fd7a0b65cc Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..095253b18 --- /dev/null +++ b/src/patches/dnsmasq/0011-Remove-floor-on-EDNS0-packet-size-with-DNSSEC.patch @@ -0,0 +1,46 @@ +From 800c5cc1e7438818fd80f08c2d472df249a6942d Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..b71e58b75 --- /dev/null +++ b/src/patches/dnsmasq/0012-CHANGELOG-re.-inotify.patch @@ -0,0 +1,27 @@ +From ad946d555dce44eb690c7699933b6ff40ab85bb6 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..2ff5acd14 --- /dev/null +++ b/src/patches/dnsmasq/0013-Fix-breakage-of-domain-domain-subnet-local.patch @@ -0,0 +1,70 @@ +From 3ad3f3bbd4ee716a7d2fb1e115cf89bd1b1a5de9 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Tue, 16 Dec 2014 18:25:17 +0000 +Subject: [PATCH 13/55] Fix breakage of --domain=,,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=,,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=// + local=/xxx.yyy.zzz.in-addr.arpa/ */ + struct server *serv = add_rev4(new->start, msize); + serv->flags |= SERV_NO_ADDR; ++ ++ /* local=// */ ++ 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=// + local=/xxx.yyy.zzz.ip6.arpa/ */ + struct server *serv = add_rev6(&new->start6, msize); + serv->flags |= SERV_NO_ADDR; ++ ++ /* local=// */ ++ 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 index 000000000..a0e647f8a --- /dev/null +++ b/src/patches/dnsmasq/0014-Remove-redundant-IN6_IS_ADDR_ULA-a-macro-defn.patch @@ -0,0 +1,27 @@ +From bd9520b7ade7098ee423acc38965376aa57feb07 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..114a4f371 --- /dev/null +++ b/src/patches/dnsmasq/0015-Eliminate-IPv6-privacy-addresses-from-interface-name.patch @@ -0,0 +1,148 @@ +From 476693678e778886b64d0b56e27eb7695cbcca99 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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=,,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 index 000000000..d072c0379 --- /dev/null +++ b/src/patches/dnsmasq/0016-Tweak-field-width-in-cache-dump-to-avoid-truncating-.patch @@ -0,0 +1,35 @@ +From 3267804598047bd1781cab91508d1bc516e5ddbb Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 = ""; +- 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 index 000000000..944afaf15 --- /dev/null +++ b/src/patches/dnsmasq/0017-Fix-crash-in-DNSSEC-code-when-attempting-to-verify-l.patch @@ -0,0 +1,100 @@ +From 094b5c3d904bae9aeb3206d9f3b8348926b84975 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..de66adb9b --- /dev/null +++ b/src/patches/dnsmasq/0018-Make-caching-work-for-CNAMEs-pointing-to-A-AAAA-reco.patch @@ -0,0 +1,99 @@ +From cbc652423403e3cef00e00240f6beef713142246 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 ,, + 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 index 000000000..0ee2e657b --- /dev/null +++ b/src/patches/dnsmasq/0019-Fix-problems-validating-NSEC3-and-wildcards.patch @@ -0,0 +1,365 @@ +From fbc5205702c7f6f431d9f1043c553d7fb62ddfdb Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..2f70ee587 --- /dev/null +++ b/src/patches/dnsmasq/0020-Initialise-return-value.patch @@ -0,0 +1,32 @@ +From 83d2ed09fc0216b567d7fb2197e4ff3eae150b0d Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..6c88f2d18 --- /dev/null +++ b/src/patches/dnsmasq/0021-Add-ignore-address-option.patch @@ -0,0 +1,192 @@ +From 32fc6dbe03569d70dd394420ceb73532cf303c33 Mon Sep 17 00:00:00 2001 +From: Glen Huang +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= ++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, "", 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 index 000000000..1b37202c4 --- /dev/null +++ b/src/patches/dnsmasq/0022-Bad-packet-protection.patch @@ -0,0 +1,25 @@ +From 0b1008d367d44e77352134a4c5178f896f0db3e7 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..3bc3f7926 --- /dev/null +++ b/src/patches/dnsmasq/0023-Fix-build-failure-in-new-inotify-code-on-BSD.patch @@ -0,0 +1,29 @@ +From d310ab7ecbffce79d3d90debba621e0222f9bced Mon Sep 17 00:00:00 2001 +From: Matthias Andree +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 +- + #ifdef HAVE_LINUX_NETWORK + ++#include ++ + /* 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 index 000000000..e3074fa3e --- /dev/null +++ b/src/patches/dnsmasq/0024-Implement-makefile-dependencies-on-COPTS-variable.patch @@ -0,0 +1,68 @@ +From 81c538efcebfce2ce4a1d3a420b6c885b8f08df9 Mon Sep 17 00:00:00 2001 +From: Yousong Zhou +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 index 000000000..b1c42aef4 --- /dev/null +++ b/src/patches/dnsmasq/0025-Fix-race-condition-issue-in-makefile.patch @@ -0,0 +1,30 @@ +From d8dbd903d024f84a149dac2f8a674a68dfed47a3 Mon Sep 17 00:00:00 2001 +From: Yousong Zhou +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 index 000000000..7f01ee73e --- /dev/null +++ b/src/patches/dnsmasq/0026-DNSSEC-do-top-down-search-for-limit-of-secure-delega.patch @@ -0,0 +1,792 @@ +From 97e618a0e3f29465acc689d87288596b006f197e Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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 index 000000000..e258d56da --- /dev/null +++ b/src/patches/dnsmasq/0027-Add-log-queries-extra-option-for-more-complete-loggi.patch @@ -0,0 +1,346 @@ +From 25cf5e373eb41c088d4ee5e625209c4cf6a5659e Mon Sep 17 00:00:00 2001 +From: Simon Kelley +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= + 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, "", gettext_noop("DHCP option sent even if the client does not request it."), NULL}, + { 'p', ARG_ONE, "", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL }, + { 'P', ARG_ONE, "", 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, "", 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, "", 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 index 000000000..e8a61b7c1 --- /dev/null +++ b/src/patches/dnsmasq/0028-Add-min-cache-ttl-option.patch @@ -0,0 +1,144 @@ +From 28de38768e2c7d763b9aa5b7a4d251d5e56bab0b Mon Sep 17 00:00:00 2001 +From: RinSatsuki +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=