This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "IPFire 2.x development tree".
The branch, next has been updated
via 64835e022c52d24432d201bd4d57f1aa962c1f90 (commit)
via a6cd8b9b5d903da27fef106329c892e372753e5f (commit)
via 3b9815eb87e83a1b24e85ce0eab14a962b62ccd0 (commit)
from 43747fae333273890aadb66b72d911efaa4c2086 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 64835e022c52d24432d201bd4d57f1aa962c1f90
Author: Michael Tremer michael.tremer@ipfire.org
Date: Sun Feb 28 12:27:06 2016 -0800
The VLAN hotplugging script was called with a wrong parameter that
should just be used to rename devices. Hence the script was not
correctly executed and did not create the virtual devices.
1. Added patch 005 because of the discussion on the dnsmasq-list:
"I've noticed that replies which get their TTL from the dhcp-ttl
option always get the TTL specified in dhcp-ttl. I'd prefer
something like max(0, min(<dhcp-ttl>, <lease-expire-time> -
<now>)). Otherwise, dns might hand out a high TTL for a dhcp-lease
which expires one second later.
...
Seems a sensible addition.
2. Fixed several line numbers and patch lines in
'dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch'. On the last build
I got some "Hunk failed" messages. Patches are now applied exactly at the
given lines.
3. Nevertheless, I still get some warnings:
...
dnsmasq.c: In function 'main':
dnsmasq.c:55:7: warning: unused variable 'did_bind' [-Wunused-variable]
int did_bind = 0;
^
dnsmasq.c:54:9: warning: unused variable 'bound_device' [-Wunused-variable]
char *bound_device = NULL;
^
...
isc.c: In function 'dhcp_lease_new':
isc.c:40:3: warning: ignoring return value of 'asprintf', declared with attribute warn_unused_result [-Wunused-result]
asprintf(&lease->fqdn, "%s.%s", hostname, daemon->domain_suffix);
^
Asking about these warnings in the dnsmasq-list showed no reaction - no one answered.
This is 'dnsmasq 2.76test10', based on current 'next', containing latest patches.
Summary of changes:
config/rootfiles/core/100/filelists/files | 1 +
config/udev/60-net.rules | 2 +-
lfs/dnsmasq | 40 +-
...q-Add-support-to-read-ISC-DHCP-lease-file.patch | 14 +-
...TL_parameter_to_--host-record_and_--cname.patch | 265 +++
...01-include_0_0_0_0_8_in_DNS_rebind_checks.patch | 41 -
.../dnsmasq/002-Add_--dhcp-ttl_option.patch | 117 ++
...subnet_to_allow_arbitary_subnet_addresses.patch | 271 ---
src/patches/dnsmasq/003-Update_CHANGELOG.patch | 17 +
...h_zones_locally_when_localise_queries_set.patch | 34 -
.../dnsmasq/004-Add_--tftp-mtu_option.patch | 136 ++
.../004-fix_behaviour_of_empty_dhcp-option.patch | 38 -
...ease_length_to_TTL_when_--dhcp-ttl_in_use.patch | 37 +
...ution_to_ENOMEM_error_with_IPv6_multicast.patch | 50 -
...page_on_RDNSS_set_in_router_advertisement.patch | 35 -
...gned_dangling_CNAME_replies_to_DS_queries.patch | 30 -
...6_option_56_does_not_hold_an_address_list.patch | 25 -
...pect_the_--no_resolv_flag_in_inotify_code.patch | 47 -
..._5e3e464ac4022ee0b3794513abe510817e2cf3ca.patch | 26 -
...11-Catch_errors_from_sendmsg_in_DHCP_code.patch | 32 -
...12-Update_list_of_subnet_for_--bogus-priv.patch | 48 -
...y_address_from_DNS_overlays_A_record_from.patch | 43 -
...14-Handle_unknown_DS_hash_algos_correctly.patch | 39 -
.../015-Fix_crash_at_start_up_with_conf-dir.patch | 38 -
...ajor_rationalisation_of_DNSSEC_validation.patch | 2209 --------------------
...hing_RRSIGs_and_returning_them_from_cache.patch | 612 ------
...caches_DS_records_to_a_more_logical_place.patch | 269 ---
...lise_RR-filtering_code_for_use_with_EDNS0.patch | 755 -------
.../dnsmasq/020-DNSSEC_validation_tweak.patch | 134 --
...1-Tweaks_to_EDNS0_handling_in_DNS_replies.patch | 133 --
..._code_Check_zone_status_is_NSEC_proof_bad.patch | 409 ----
...023-Fix_brace_botch_in_dnssec_validate_ds.patch | 98 -
...ning_which_DNSSEC_sig_algos_are_supported.patch | 145 --
...EDNS0_handling_and_computation_use_of_udp.patch | 643 ------
...aks_in_handling_unknown_DNSSEC_algorithms.patch | 262 ---
...obscure_off-by-one_in_DNSSEC_hostname_cmp.patch | 27 -
.../028-Minor_tweak_to_previous_commit.patch | 39 -
.../dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch | 39 -
38 files changed, 589 insertions(+), 6611 deletions(-)
create mode 100644 src/patches/dnsmasq/001-Add_TTL_parameter_to_--host-record_and_--cname.patch
delete mode 100644 src/patches/dnsmasq/001-include_0_0_0_0_8_in_DNS_rebind_checks.patch
create mode 100644 src/patches/dnsmasq/002-Add_--dhcp-ttl_option.patch
delete mode 100644 src/patches/dnsmasq/002-enhance_add_subnet_to_allow_arbitary_subnet_addresses.patch
create mode 100644 src/patches/dnsmasq/003-Update_CHANGELOG.patch
delete mode 100644 src/patches/dnsmasq/003-dont_answer_non_auth_queries_for_auth_zones_locally_when_localise_queries_set.patch
create mode 100644 src/patches/dnsmasq/004-Add_--tftp-mtu_option.patch
delete mode 100644 src/patches/dnsmasq/004-fix_behaviour_of_empty_dhcp-option.patch
create mode 100644 src/patches/dnsmasq/005-Apply_ceiling_of_lease_length_to_TTL_when_--dhcp-ttl_in_use.patch
delete mode 100644 src/patches/dnsmasq/005-suggest_solution_to_ENOMEM_error_with_IPv6_multicast.patch
delete mode 100644 src/patches/dnsmasq/006-clarify_man_page_on_RDNSS_set_in_router_advertisement.patch
delete mode 100644 src/patches/dnsmasq/007-handle_signed_dangling_CNAME_replies_to_DS_queries.patch
delete mode 100644 src/patches/dnsmasq/008-DHCPv6_option_56_does_not_hold_an_address_list.patch
delete mode 100644 src/patches/dnsmasq/009-Respect_the_--no_resolv_flag_in_inotify_code.patch
delete mode 100644 src/patches/dnsmasq/010-Rationalise_5e3e464ac4022ee0b3794513abe510817e2cf3ca.patch
delete mode 100644 src/patches/dnsmasq/011-Catch_errors_from_sendmsg_in_DHCP_code.patch
delete mode 100644 src/patches/dnsmasq/012-Update_list_of_subnet_for_--bogus-priv.patch
delete mode 100644 src/patches/dnsmasq/013-Fix_crash_when_empty_address_from_DNS_overlays_A_record_from.patch
delete mode 100644 src/patches/dnsmasq/014-Handle_unknown_DS_hash_algos_correctly.patch
delete mode 100644 src/patches/dnsmasq/015-Fix_crash_at_start_up_with_conf-dir.patch
delete mode 100644 src/patches/dnsmasq/016-Major_rationalisation_of_DNSSEC_validation.patch
delete mode 100644 src/patches/dnsmasq/017-Abandon_caching_RRSIGs_and_returning_them_from_cache.patch
delete mode 100644 src/patches/dnsmasq/018-Move_code_which_caches_DS_records_to_a_more_logical_place.patch
delete mode 100644 src/patches/dnsmasq/019-Generalise_RR-filtering_code_for_use_with_EDNS0.patch
delete mode 100644 src/patches/dnsmasq/020-DNSSEC_validation_tweak.patch
delete mode 100644 src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch
delete mode 100644 src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch
delete mode 100644 src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch
delete mode 100644 src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch
delete mode 100644 src/patches/dnsmasq/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch
delete mode 100644 src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch
delete mode 100644 src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch
delete mode 100644 src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch
delete mode 100644 src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch
Difference in files:
diff --git a/config/rootfiles/core/100/filelists/files b/config/rootfiles/core/100/filelists/files
index 1202ea8..b3cd418 100644
--- a/config/rootfiles/core/100/filelists/files
+++ b/config/rootfiles/core/100/filelists/files
@@ -6,6 +6,7 @@ etc/rc.d/init.d/firewall
etc/rc.d/init.d/functions
etc/rc.d/init.d/networking/dhcpcd.exe
etc/modprobe.d/nf_conntrack.conf
+lib/udev/rules.d/60-net.rules
srv/web/ipfire/cgi-bin/logs.cgi/firewalllog.dat
srv/web/ipfire/cgi-bin/logs.cgi/firewalllogcountry.dat
srv/web/ipfire/cgi-bin/logs.cgi/firewalllogip.dat
diff --git a/config/udev/60-net.rules b/config/udev/60-net.rules
index dc39ff0..e82320c 100644
--- a/config/udev/60-net.rules
+++ b/config/udev/60-net.rules
@@ -4,4 +4,4 @@ ACTION=="add", SUBSYSTEM=="net", PROGRAM="/lib/udev/network-hotplug-rename", RES
# Call a script that will create all virtual devices for a parent device
# that has just come up.
-ACTION=="add", SUBSYSTEM=="net", PROGRAM="/lib/udev/network-hotplug-vlan"
+ACTION=="add", SUBSYSTEM=="net", RUN+="/lib/udev/network-hotplug-vlan"
diff --git a/lfs/dnsmasq b/lfs/dnsmasq
index 8058663..84585c1 100644
--- a/lfs/dnsmasq
+++ b/lfs/dnsmasq
@@ -1,7 +1,7 @@
###############################################################################
# #
# IPFire.org - A linux based firewall #
-# Copyright (C) 2015 Michael Tremer & Christian Schmidt #
+# Copyright (C) 2016 Michael Tremer & Christian Schmidt #
# #
# 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 #
@@ -24,7 +24,7 @@
THISAPP = dnsmasq-$(VER)
DL_FILE = $(THISAPP).tar.xz
@@ -43,7 +43,7 @@ objects = $(DL_FILE)
-$(DL_FILE)_MD5 = 887236f1ddde6eb57cdb9d01916c9f72
+$(DL_FILE)_MD5 = 4b51474ed6081b18c61407077f254cf7
@@ -73,35 +73,11 @@ $(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/001-include_0_0_0_0_8_in_DNS_rebind_checks.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/002-enhance_add_subnet_to_allow_arbitary_subnet_addresses.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/003-dont_answer_non_auth_queries_for_auth_zones_locally_when_localise_queries_set.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/004-fix_behaviour_of_empty_dhcp-option.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/005-suggest_solution_to_ENOMEM_error_with_IPv6_multicast.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/006-clarify_man_page_on_RDNSS_set_in_router_advertisement.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/007-handle_signed_dangling_CNAME_replies_to_DS_queries.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/008-DHCPv6_option_56_does_not_hold_an_address_list.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/009-Respect_the_--no_resolv_flag_in_inotify_code.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/010-Rationalise_5e3e464ac4022ee0b3794513abe510817e2cf3ca.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/011-Catch_errors_from_sendmsg_in_DHCP_code.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/012-Update_list_of_subnet_for_--bogus-priv.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/013-Fix_crash_when_empty_address_from_DNS_overlays_A_record_from.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/014-Handle_unknown_DS_hash_algos_correctly.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/015-Fix_crash_at_start_up_with_conf-dir.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/016-Major_rationalisation_of_DNSSEC_validation.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/017-Abandon_caching_RRSIGs_and_returning_them_from_cache.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/018-Move_code_which_caches_DS_records_to_a_more_logical_place.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/019-Generalise_RR-filtering_code_for_use_with_EDNS0.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/020-DNSSEC_validation_tweak.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch
- cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch
+ cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/001-Add_TTL_parameter_to_--host-record_and_--cname.patch
+ cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/002-Add_--dhcp-ttl_option.patch
+ cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/003-Update_CHANGELOG.patch
+ cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/004-Add_--tftp-mtu_option.patch
+ cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq/005-Apply_ceiling_of_lease_length_to_TTL_when_--dhcp-ttl_in_use.patch
cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch
cd $(DIR_APP) && sed -i src/config.h \
diff --git a/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch b/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch
index f55ebe8..703e94f 100644
--- a/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch
+++ b/src/patches/dnsmasq-Add-support-to-read-ISC-DHCP-lease-file.patch
@@ -19,7 +19,7 @@
#ifdef HAVE_DNSSEC
cache_blockdata_free(crecp);
#endif
-@@ -1131,7 +1134,7 @@
+@@ -1138,7 +1141,7 @@
@@ -28,7 +28,7 @@
struct in_addr a_record_from_hosts(char *name, time_t now)
{
struct crec *crecp = NULL;
-@@ -1274,7 +1277,11 @@
+@@ -1281,7 +1284,11 @@
else
crec->ttd = ttd;
crec->addr.addr = *host_address;
@@ -42,7 +42,7 @@
--- a/src/dnsmasq.c Thu Jul 30 20:59:06 2015
+++ b/src/dnsmasq.c Wed Dec 16 19:38:32 2015
-@@ -982,6 +982,11 @@
+@@ -1013,6 +1013,11 @@
poll_resolv(0, daemon->last_resolv != 0, now);
daemon->last_resolv = now;
@@ -56,7 +56,7 @@
--- a/src/dnsmasq.h Wed Dec 16 19:24:12 2015
+++ b/src/dnsmasq.h Wed Dec 16 19:40:11 2015
-@@ -1513,8 +1513,12 @@
+@@ -1514,6 +1514,11 @@
void poll_listen(int fd, short event);
int do_poll(int timeout);
@@ -326,7 +326,7 @@
+#endif
--- a/src/option.c Wed Dec 16 19:24:12 2015
+++ b/src/option.c Wed Dec 16 19:42:48 2015
-@@ -1754,7 +1754,7 @@
+@@ -1769,7 +1769,7 @@
ret_err(_("bad MX target"));
break;
@@ -341,8 +341,8 @@
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
-- poll.o rrfilter.o
-+ poll.o rrfilter.o isc.o
+- poll.o rrfilter.o edns0.o arp.o
++ poll.o rrfilter.o edns0.o arp.o isc.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h ip6addr.h
diff --git a/src/patches/dnsmasq/001-Add_TTL_parameter_to_--host-record_and_--cname.patch b/src/patches/dnsmasq/001-Add_TTL_parameter_to_--host-record_and_--cname.patch
new file mode 100644
index 0000000..86fbc9c
--- /dev/null
+++ b/src/patches/dnsmasq/001-Add_TTL_parameter_to_--host-record_and_--cname.patch
@@ -0,0 +1,265 @@
+From df3d54f776a3c9b60735b45c0b7fd88b66a2d5c4 Mon Sep 17 00:00:00 2001
+From: Simon Kelley simon@thekelleys.org.uk
+Date: Wed, 24 Feb 2016 21:03:38 +0000
+Subject: [PATCH] Add TTL parameter to --host-record and --cname.
+
+---
+ man/dnsmasq.8 | 12 ++++++++++--
+ src/cache.c | 7 +++++++
+ src/dnsmasq.h | 2 ++
+ src/option.c | 46 ++++++++++++++++++++++++++++++++++++++--------
+ src/rfc1035.c | 6 +++++-
+ 5 files changed, 62 insertions(+), 11 deletions(-)
+
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index b782eaf..7bc1394 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -529,7 +529,7 @@ zone files: the port, weight and priority numbers are in a different
+ order. More than one SRV record for a given service/domain is allowed,
+ all that match are returned.
+ .TP
+-.B --host-record=<name>[,<name>....],[<IPv4-address>],[<IPv6-address>]
++.B --host-record=<name>[,<name>....],[<IPv4-address>],[<IPv6-address>][,<TTL>]
+ Add A, AAAA and PTR records to the DNS. This adds one or more names to
+ the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may
+ appear in more than one
+@@ -546,6 +546,10 @@ is in effect. Short and long names may appear in the same
+ .B host-record,
+ eg.
+ .B --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100
++
++If the time-to-live is given, it overrides the default, which is zero
++or the value of --local-ttl. The value is a positive integer and gives
++the time-to-live in seconds.
+ .TP
+ .B -Y, --txt-record=<name>[[,<text>],<text>]
+ Return a TXT DNS record. The value of TXT record is a set of strings,
+@@ -559,7 +563,7 @@ Return a PTR DNS record.
+ .B --naptr-record=<name>,<order>,<preference>,<flags>,<service>,<regexp>[,<replacement>]
+ Return an NAPTR DNS record, as specified in RFC3403.
+ .TP
+-.B --cname=<cname>,<target>
++.B --cname=<cname>,<target>[,<TTL>]
+ Return a CNAME record which indicates that <cname> is really
+ <target>. There are significant limitations on the target; it must be a
+ DNS name which is known to dnsmasq from /etc/hosts (or additional
+@@ -568,6 +572,10 @@ hosts files), from DHCP, from --interface-name or from another
+ If the target does not satisfy this
+ criteria, the whole cname is ignored. The cname must be unique, but it
+ is permissable to have more than one cname pointing to the same target.
++
++If the time-to-live is given, it overrides the default, which is zero
++or the value of -local-ttl. The value is a positive integer and gives
++the time-to-live in seconds.
+ .TP
+ .B --dns-rr=<name>,<RR-number>,[<hex data>]
+ Return an arbitrary DNS Resource Record. The number is the type of the
+diff --git a/src/cache.c b/src/cache.c
+index a9eaa65..4ecd535 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -778,6 +778,7 @@ static void add_hosts_cname(struct crec *target)
+ (crec = whine_malloc(sizeof(struct crec))))
+ {
+ crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME;
++ crec->ttd = a->ttl;
+ crec->name.namep = a->alias;
+ crec->addr.cname.target.cache = target;
+ crec->addr.cname.uid = target->uid;
+@@ -981,6 +982,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
+ strcat(cache->name.sname, ".");
+ strcat(cache->name.sname, domain_suffix);
+ cache->flags = flags;
++ cache->ttd = daemon->local_ttl;
+ add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
+ name_count++;
+ }
+@@ -988,6 +990,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
+ {
+ strcpy(cache->name.sname, canon);
+ cache->flags = flags;
++ cache->ttd = daemon->local_ttl;
+ add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
+ name_count++;
+ }
+@@ -1057,6 +1060,7 @@ void cache_reload(void)
+ ((cache = whine_malloc(sizeof(struct crec)))))
+ {
+ cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
++ cache->ttd = a->ttl;
+ cache->name.namep = a->alias;
+ cache->addr.cname.target.int_name = intr;
+ cache->addr.cname.uid = SRC_INTERFACE;
+@@ -1071,6 +1075,7 @@ void cache_reload(void)
+ (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
+ {
+ cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
++ cache->ttd = daemon->local_ttl;
+ cache->name.namep = ds->name;
+ cache->addr.ds.keylen = ds->digestlen;
+ cache->addr.ds.algo = ds->algo;
+@@ -1095,6 +1100,7 @@ void cache_reload(void)
+ (cache = whine_malloc(sizeof(struct crec))))
+ {
+ cache->name.namep = nl->name;
++ cache->ttd = hr->ttl;
+ cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
+ add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
+ }
+@@ -1103,6 +1109,7 @@ void cache_reload(void)
+ (cache = whine_malloc(sizeof(struct crec))))
+ {
+ cache->name.namep = nl->name;
++ cache->ttd = hr->ttl;
+ cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
+ add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
+ }
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 6d1c5ae..6344df5 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -308,6 +308,7 @@ struct ptr_record {
+ };
+
+ struct cname {
++ int ttl;
+ char *alias, *target;
+ struct cname *next;
+ };
+@@ -344,6 +345,7 @@ struct auth_zone {
+
+
+ struct host_record {
++ int ttl;
+ struct name_list {
+ char *name;
+ struct name_list *next;
+diff --git a/src/option.c b/src/option.c
+index c98bdc9..7c5e6bc 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -448,20 +448,20 @@ static struct {
+ { LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
+ { LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
+ { LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<interface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
+- { LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
++ { LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
+ { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
+ { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
+ { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
+ { LOPT_ADD_MAC, ARG_DUP, "[=base64|text]", gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
+ { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
+- { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
++ { LOPT_CPE_ID, ARG_ONE, "<text>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
+ { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
+ { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
+ { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
+ { LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
+ { LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
+ { LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
+- { LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
++ { LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
+ { LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
+ { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
+ { LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
+@@ -3692,12 +3692,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+ case LOPT_CNAME: /* --cname */
+ {
+ struct cname *new;
+- char *alias;
+- char *target;
++ char *alias, *target, *ttls;
++ int ttl = -1;
+
+ if (!(comma = split(arg)))
+ ret_err(gen_err);
+
++ if ((ttls = split(comma)) && !atoi_check(ttls, &ttl))
++ ret_err(_("bad TTL"));
++
+ alias = canonicalise_opt(arg);
+ target = canonicalise_opt(comma);
+
+@@ -3713,6 +3716,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+ daemon->cnames = new;
+ new->alias = alias;
+ new->target = target;
++ new->ttl = ttl;
+ }
+
+ break;
+@@ -3913,14 +3917,22 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+ {
+ struct host_record *new = opt_malloc(sizeof(struct host_record));
+ memset(new, 0, sizeof(struct host_record));
+-
++ new->ttl = -1;
++
+ if (!arg || !(comma = split(arg)))
+ ret_err(_("Bad host-record"));
+
+ while (arg)
+ {
+ struct all_addr addr;
+- if (inet_pton(AF_INET, arg, &addr))
++ char *dig;
++
++ for (dig = arg; *dig != 0; dig++)
++ if (*dig < '0' || *dig > '9')
++ break;
++ if (*dig == 0)
++ new->ttl = atoi(arg);
++ else if (inet_pton(AF_INET, arg, &addr))
+ new->addr = addr.addr.addr4;
+ #ifdef HAVE_IPV6
+ else if (inet_pton(AF_INET6, arg, &addr))
+@@ -4601,7 +4613,25 @@ void read_opts(int argc, char **argv, char *compile_opts)
+ }
+ }
+ }
+-
++
++ if (daemon->host_records)
++ {
++ struct host_record *hr;
++
++ for (hr = daemon->host_records; hr; hr = hr->next)
++ if (hr->ttl == -1)
++ hr->ttl = daemon->local_ttl;
++ }
++
++ if (daemon->cnames)
++ {
++ struct cname *cn;
++
++ for (cn = daemon->cnames; cn; cn = cn->next)
++ if (cn->ttl == -1)
++ cn->ttl = daemon->local_ttl;
++ }
++
+ if (daemon->if_addrs)
+ {
+ struct iname *tmp;
+diff --git a/src/rfc1035.c b/src/rfc1035.c
+index 9c0ddb5..3535a71 100644
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1169,9 +1169,13 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
+ /* Return 0 ttl for DHCP entries, which might change
+ before the lease expires. */
+
+- if (crecp->flags & (F_IMMORTAL | F_DHCP))
++ if (crecp->flags & F_DHCP)
+ return daemon->local_ttl;
+
++ /* Immortal entries other than DHCP are local, and hold TTL in TTD field. */
++ if (crecp->flags & F_IMMORTAL)
++ return crecp->ttd;
++
+ /* Return the Max TTL value if it is lower then the actual TTL */
+ if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
+ return crecp->ttd - now;
+--
+1.7.10.4
+
diff --git a/src/patches/dnsmasq/001-include_0_0_0_0_8_in_DNS_rebind_checks.patch b/src/patches/dnsmasq/001-include_0_0_0_0_8_in_DNS_rebind_checks.patch
deleted file mode 100644
index 8a2557a..0000000
--- a/src/patches/dnsmasq/001-include_0_0_0_0_8_in_DNS_rebind_checks.patch
+++ /dev/null
@@ -1,41 +0,0 @@
-From d2aa7dfbb6d1088dcbea9fecc61b9293b320eb95 Mon Sep 17 00:00:00 2001
-From: Simon Kelley simon@thekelleys.org.uk
-Date: Mon, 3 Aug 2015 21:52:12 +0100
-Subject: [PATCH] Include 0.0.0.0/8 in DNS rebind checks.
-
----
- CHANGELOG | 7 +++++++
- src/rfc1035.c | 3 ++-
- 2 files changed, 9 insertions(+), 1 deletion(-)
-
-diff --git a/CHANGELOG b/CHANGELOG
-index 901da47..3f4026d 100644
---- a/CHANGELOG
-+++ b/CHANGELOG
-@@ -1,3 +1,10 @@
-+version 2.76
-+ Include 0.0.0.0/8 in DNS rebind checks. This range
-+ translates to hosts on the local network, or, at
-+ least, 0.0.0.0 accesses the local host, so could
-+ be targets for DNS rebinding. See RFC 5735 section 3
-+ for details. Thanks to Stephen Röttger for the bug report.
-+
- version 2.75
- Fix reversion on 2.74 which caused 100% CPU use when a
- dhcp-script is configured. Thanks to Adrian Davey for
-diff --git a/src/rfc1035.c b/src/rfc1035.c
-index 56647b0..29e9e65 100644
---- a/src/rfc1035.c
-+++ b/src/rfc1035.c
-@@ -728,7 +728,8 @@ int private_net(struct in_addr addr, int ban_localhost)
- in_addr_t ip_addr = ntohl(addr.s_addr);
-
- return
-- (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
-+ (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
-+ ((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ ||
- ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
- ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
- ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
---
-1.7.10.4
diff --git a/src/patches/dnsmasq/002-Add_--dhcp-ttl_option.patch b/src/patches/dnsmasq/002-Add_--dhcp-ttl_option.patch
new file mode 100644
index 0000000..45e3b9b
--- /dev/null
+++ b/src/patches/dnsmasq/002-Add_--dhcp-ttl_option.patch
@@ -0,0 +1,117 @@
+From 832e47beab95c2918b5264f0504f2fe6fe523e4c Mon Sep 17 00:00:00 2001
+From: Simon Kelley simon@thekelleys.org.uk
+Date: Wed, 24 Feb 2016 21:24:45 +0000
+Subject: [PATCH] Add --dhcp-ttl option.
+
+---
+ man/dnsmasq.8 | 5 ++++-
+ src/dnsmasq.h | 2 +-
+ src/option.c | 13 +++++++++++--
+ src/rfc1035.c | 2 +-
+ 4 files changed, 17 insertions(+), 5 deletions(-)
+
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index 7bc1394..2bcce20 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -60,7 +60,7 @@ in the same way as for DHCP-derived names. Note that this does not
+ apply to domain names in cnames, PTR records, TXT records etc.
+ .TP
+ .B -T, --local-ttl=<time>
+-When replying with information from /etc/hosts or the DHCP leases
++When replying with information from /etc/hosts or configuration or the DHCP leases
+ file dnsmasq by default sets the time-to-live field to zero, meaning
+ that the requester should not itself cache the information. This is
+ the correct thing to do in almost all situations. This option allows a
+@@ -68,6 +68,9 @@ time-to-live (in seconds) to be given for these replies. This will
+ reduce the load on the server at the expense of clients using stale
+ data under some circumstances.
+ .TP
++.B --dhcp-ttl=<time>
++As for --local-ttl, but affects only replies with information from DHCP leases. If both are given, --dhcp-ttl applies for DHCP information, and --local-ttl for others. Setting this to zero eliminates the effect of --local-ttl for DHCP.
++.TP
+ .B --neg-ttl=<time>
+ Negative replies from upstream servers normally contain time-to-live
+ information in SOA records which dnsmasq uses for caching. If the
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 6344df5..9f73c3b 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -955,7 +955,7 @@ extern struct daemon {
+ int max_logs; /* queue limit */
+ int cachesize, ftabsize;
+ int port, query_port, min_port, max_port;
+- unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl;
++ unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;
+ char *dns_client_id;
+ struct hostsfile *addn_hosts;
+ struct dhcp_context *dhcp, *dhcp6;
+diff --git a/src/option.c b/src/option.c
+index 7c5e6bc..3f6d162 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -157,6 +157,7 @@ struct myoption {
+ #define LOPT_MAXPORT 345
+ #define LOPT_CPE_ID 346
+ #define LOPT_SCRIPT_ARP 347
++#define LOPT_DHCPTTL 348
+
+ #ifdef HAVE_GETOPT_LONG
+ static const struct option opts[] =
+@@ -319,6 +320,7 @@ static const struct myoption opts[] =
+ { "quiet-ra", 0, 0, LOPT_QUIET_RA },
+ { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
+ { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
++ { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
+ { NULL, 0, 0, 0 }
+ };
+
+@@ -485,9 +487,10 @@ static struct {
+ { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
+ { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
+ { 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_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL },
++ { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL },
+ { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL },
++ { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL },
+ { 0, 0, NULL, NULL, NULL }
+ };
+
+@@ -2580,6 +2583,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+ case LOPT_MINCTTL: /* --min-cache-ttl */
+ case LOPT_MAXCTTL: /* --max-cache-ttl */
+ case LOPT_AUTHTTL: /* --auth-ttl */
++ case LOPT_DHCPTTL: /* --dhcp-ttl */
+ {
+ int ttl;
+ if (!atoi_check(arg, &ttl))
+@@ -2598,6 +2602,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+ daemon->max_cache_ttl = (unsigned long)ttl;
+ else if (option == LOPT_AUTHTTL)
+ daemon->auth_ttl = (unsigned long)ttl;
++ else if (option == LOPT_DHCPTTL)
++ {
++ daemon->dhcp_ttl = (unsigned long)ttl;
++ daemon->use_dhcp_ttl = 1;
++ }
+ else
+ daemon->local_ttl = (unsigned long)ttl;
+ break;
+diff --git a/src/rfc1035.c b/src/rfc1035.c
+index 3535a71..8f1e3b4 100644
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1170,7 +1170,7 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
+ before the lease expires. */
+
+ if (crecp->flags & F_DHCP)
+- return daemon->local_ttl;
++ return daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl;
+
+ /* Immortal entries other than DHCP are local, and hold TTL in TTD field. */
+ if (crecp->flags & F_IMMORTAL)
+--
+1.7.10.4
+
diff --git a/src/patches/dnsmasq/002-enhance_add_subnet_to_allow_arbitary_subnet_addresses.patch b/src/patches/dnsmasq/002-enhance_add_subnet_to_allow_arbitary_subnet_addresses.patch
deleted file mode 100644
index 2d3d6e4..0000000
--- a/src/patches/dnsmasq/002-enhance_add_subnet_to_allow_arbitary_subnet_addresses.patch
+++ /dev/null
@@ -1,271 +0,0 @@
-From a7369bef8abd241c3d85633fa9c870943f091e76 Mon Sep 17 00:00:00 2001
-From: Ed Bardsley ebardsley@google.com
-Date: Wed, 5 Aug 2015 21:17:18 +0100
-Subject: [PATCH] Enhance --add-subnet to allow arbitary subnet addresses.
-
----
- CHANGELOG | 4 ++++
- man/dnsmasq.8 | 32 ++++++++++++++++++++-----------
- src/dnsmasq.h | 13 ++++++++++---
- src/option.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
- src/rfc1035.c | 39 +++++++++++++++++++++++++++++++-------
- 5 files changed, 121 insertions(+), 26 deletions(-)
-
-diff --git a/CHANGELOG b/CHANGELOG
-index 3f4026d..bbc2834 100644
---- a/CHANGELOG
-+++ b/CHANGELOG
-@@ -4,6 +4,10 @@ version 2.76
- least, 0.0.0.0 accesses the local host, so could
- be targets for DNS rebinding. See RFC 5735 section 3
- for details. Thanks to Stephen Röttger for the bug report.
-+
-+ Enhance --add-subnet to allow arbitrary subnet addresses.
-+ Thanks to Ed Barsley for the patch.
-+
-
- version 2.75
- Fix reversion on 2.74 which caused 100% CPU use when a
-diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
-index c8913b5..a23c898 100644
---- a/man/dnsmasq.8
-+++ b/man/dnsmasq.8
-@@ -604,17 +604,27 @@ experimental. Also note that exposing MAC addresses in this way may
- have security and privacy implications. The warning about caching
- given for --add-subnet applies to --add-mac too.
- .TP
--.B --add-subnet[[=<IPv4 prefix length>],<IPv6 prefix length>]
--Add the subnet address of the requestor to the DNS queries which are
--forwarded upstream. The amount of the address forwarded depends on the
--prefix length parameter: 32 (128 for IPv6) forwards the whole address,
--zero forwards none of it but still marks the request so that no
--upstream nameserver will add client address information either. The
--default is zero for both IPv4 and IPv6. Note that upstream nameservers
--may be configured to return different results based on this
--information, but the dnsmasq cache does not take account. If a dnsmasq
--instance is configured such that different results may be encountered,
--caching should be disabled.
-+.B --add-subnet[[=[<IPv4 address>/]<IPv4 prefix length>][,[<IPv6 address>/]<IPv6 prefix length>]]
-+Add a subnet address to the DNS queries which are forwarded
-+upstream. If an address is specified in the flag, it will be used,
-+otherwise, the address of the requestor will be used. The amount of
-+the address forwarded depends on the prefix length parameter: 32 (128
-+for IPv6) forwards the whole address, zero forwards none of it but
-+still marks the request so that no upstream nameserver will add client
-+address information either. The default is zero for both IPv4 and
-+IPv6. Note that upstream nameservers may be configured to return
-+different results based on this information, but the dnsmasq cache
-+does not take account. If a dnsmasq instance is configured such that
-+different results may be encountered, caching should be disabled.
-+
-+For example,
-+.B --add-subnet=24,96
-+will add the /24 and /96 subnets of the requestor for IPv4 and IPv6 requestors, respectively.
-+.B --add-subnet=1.2.3.4/24
-+will add 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors.
-+.B --add-subnet=1.2.3.4/24,1.2.3.4/24
-+will add 1.2.3.0/24 for both IPv4 and IPv6 requestors.
-+
- .TP
- .B -c, --cache-size=<cachesize>
- Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
-diff --git a/src/dnsmasq.h b/src/dnsmasq.h
-index cf1a782..f42acdb 100644
---- a/src/dnsmasq.h
-+++ b/src/dnsmasq.h
-@@ -541,6 +541,13 @@ struct iname {
- struct iname *next;
- };
-
-+/* subnet parameters from command line */
-+struct mysubnet {
-+ union mysockaddr addr;
-+ int addr_used;
-+ int mask;
-+};
-+
- /* resolv-file parms from command-line */
- struct resolvc {
- struct resolvc *next;
-@@ -935,9 +942,9 @@ extern struct daemon {
- struct auth_zone *auth_zones;
- struct interface_name *int_names;
- char *mxtarget;
-- int addr4_netmask;
-- int addr6_netmask;
-- char *lease_file;
-+ struct mysubnet *add_subnet4;
-+ struct mysubnet *add_subnet6;
-+ char *lease_file;
- char *username, *groupname, *scriptuser;
- char *luascript;
- char *authserver, *hostmaster;
-diff --git a/src/option.c b/src/option.c
-index ecc2619..746cd11 100644
---- a/src/option.c
-+++ b/src/option.c
-@@ -445,7 +445,7 @@ static struct {
- { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
- { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
- { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
-- { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add requestor's IP subnet to forwarded DNS queries."), NULL },
-+ { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
- { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
- { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
- { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
-@@ -722,6 +722,20 @@ static void do_usage(void)
-
- #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
-
-+static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
-+{
-+ if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
-+ addr->sa.sa_family = AF_INET;
-+#ifdef HAVE_IPV6
-+ else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
-+ addr->sa.sa_family = AF_INET6;
-+#endif
-+ else
-+ return _("bad address");
-+
-+ return NULL;
-+}
-+
- char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
- {
- int source_port = 0, serv_port = NAMESERVER_PORT;
-@@ -1585,7 +1599,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
- li = match_suffix->next;
- free(match_suffix->suffix);
- free(match_suffix);
-- }
-+ }
- break;
- }
-
-@@ -1593,10 +1607,45 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
- set_option_bool(OPT_CLIENT_SUBNET);
- if (arg)
- {
-+ char *err, *end;
- comma = split(arg);
-- if (!atoi_check(arg, &daemon->addr4_netmask) ||
-- (comma && !atoi_check(comma, &daemon->addr6_netmask)))
-- ret_err(gen_err);
-+
-+ struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
-+ if ((end = split_chr(arg, '/')))
-+ {
-+ /* has subnet+len */
-+ err = parse_mysockaddr(arg, &new->addr);
-+ if (err)
-+ ret_err(err);
-+ if (!atoi_check(end, &new->mask))
-+ ret_err(gen_err);
-+ new->addr_used = 1;
-+ }
-+ else if (!atoi_check(arg, &new->mask))
-+ ret_err(gen_err);
-+
-+ daemon->add_subnet4 = new;
-+
-+ new = opt_malloc(sizeof(struct mysubnet));
-+ if (comma)
-+ {
-+ if ((end = split_chr(comma, '/')))
-+ {
-+ /* has subnet+len */
-+ err = parse_mysockaddr(comma, &new->addr);
-+ if (err)
-+ ret_err(err);
-+ if (!atoi_check(end, &new->mask))
-+ ret_err(gen_err);
-+ new->addr_used = 1;
-+ }
-+ else
-+ {
-+ if (!atoi_check(comma, &new->mask))
-+ ret_err(gen_err);
-+ }
-+ }
-+ daemon->add_subnet6 = new;
- }
- break;
-
-diff --git a/src/rfc1035.c b/src/rfc1035.c
-index 29e9e65..6a51b30 100644
---- a/src/rfc1035.c
-+++ b/src/rfc1035.c
-@@ -629,26 +629,47 @@ struct subnet_opt {
- #endif
- };
-
-+static void *get_addrp(union mysockaddr *addr, const short family)
-+{
-+#ifdef HAVE_IPV6
-+ if (family == AF_INET6)
-+ return &addr->in6.sin6_addr;
-+#endif
-+
-+ return &addr->in.sin_addr;
-+}
-+
- static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
- {
- /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
-
- int len;
- void *addrp;
-+ int sa_family = source->sa.sa_family;
-
- #ifdef HAVE_IPV6
- if (source->sa.sa_family == AF_INET6)
- {
-- opt->family = htons(2);
-- opt->source_netmask = daemon->addr6_netmask;
-- addrp = &source->in6.sin6_addr;
-+ opt->source_netmask = daemon->add_subnet6->mask;
-+ if (daemon->add_subnet6->addr_used)
-+ {
-+ sa_family = daemon->add_subnet6->addr.sa.sa_family;
-+ addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
-+ }
-+ else
-+ addrp = &source->in6.sin6_addr;
- }
- else
- #endif
- {
-- opt->family = htons(1);
-- opt->source_netmask = daemon->addr4_netmask;
-- addrp = &source->in.sin_addr;
-+ opt->source_netmask = daemon->add_subnet4->mask;
-+ if (daemon->add_subnet4->addr_used)
-+ {
-+ sa_family = daemon->add_subnet4->addr.sa.sa_family;
-+ addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
-+ }
-+ else
-+ addrp = &source->in.sin_addr;
- }
-
- opt->scope_netmask = 0;
-@@ -656,6 +677,11 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
-
- if (opt->source_netmask != 0)
- {
-+#ifdef HAVE_IPV6
-+ opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
-+#else
-+ opt->family = htons(1);
-+#endif
- len = ((opt->source_netmask - 1) >> 3) + 1;
- memcpy(opt->addr, addrp, len);
- if (opt->source_netmask & 7)
-@@ -2335,4 +2361,3 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
-
- return len;
- }
--
---
-1.7.10.4
diff --git a/src/patches/dnsmasq/003-Update_CHANGELOG.patch b/src/patches/dnsmasq/003-Update_CHANGELOG.patch
new file mode 100644
index 0000000..f04f943
--- /dev/null
+++ b/src/patches/dnsmasq/003-Update_CHANGELOG.patch
@@ -0,0 +1,17 @@
+X-Git-Url: http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=blobdiff_plain;f=CHANGELOG;...
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 14354f2..6d9ba49 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -48,6 +48,10 @@ version 2.76
+ (ie xx::0 to xx::ffff:ffff:ffff:ffff)
+ Thanks to Laurent Bendel for spotting this problem.
+
++ Add support for a TTL parameter in --host-record and
++ --cname.
++
++ Add --dhcp-ttl option.
+
+ version 2.75
+ Fix reversion on 2.74 which caused 100% CPU use when a
diff --git a/src/patches/dnsmasq/003-dont_answer_non_auth_queries_for_auth_zones_locally_when_localise_queries_set.patch b/src/patches/dnsmasq/003-dont_answer_non_auth_queries_for_auth_zones_locally_when_localise_queries_set.patch
deleted file mode 100644
index cfbcdfb..0000000
--- a/src/patches/dnsmasq/003-dont_answer_non_auth_queries_for_auth_zones_locally_when_localise_queries_set.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 3a3965ac21b1b759eab8799b6edb09195b671306 Mon Sep 17 00:00:00 2001
-From: Simon Kelley simon@thekelleys.org.uk
-Date: Sun, 9 Aug 2015 17:45:06 +0100
-Subject: [PATCH] Don't answer non-auth queries for auth zones locally when
- --localise-queries set.
-
----
- src/forward.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/forward.c b/src/forward.c
-index 2731b90..b76a974 100644
---- a/src/forward.c
-+++ b/src/forward.c
-@@ -1365,7 +1365,7 @@ void receive_query(struct listener *listen, time_t now)
-
- #ifdef HAVE_AUTH
- /* find queries for zones we're authoritative for, and answer them directly */
-- if (!auth_dns)
-+ if (!auth_dns && !option_bool(OPT_LOCALISE))
- for (zone = daemon->auth_zones; zone; zone = zone->next)
- if (in_zone(zone, daemon->namebuff, NULL))
- {
-@@ -1904,7 +1904,7 @@ unsigned char *tcp_request(int confd, time_t now,
-
- #ifdef HAVE_AUTH
- /* find queries for zones we're authoritative for, and answer them directly */
-- if (!auth_dns)
-+ if (!auth_dns && !option_bool(OPT_LOCALISE))
- for (zone = daemon->auth_zones; zone; zone = zone->next)
- if (in_zone(zone, daemon->namebuff, NULL))
- {
---
-1.7.10.4
diff --git a/src/patches/dnsmasq/004-Add_--tftp-mtu_option.patch b/src/patches/dnsmasq/004-Add_--tftp-mtu_option.patch
new file mode 100644
index 0000000..c06705a
--- /dev/null
+++ b/src/patches/dnsmasq/004-Add_--tftp-mtu_option.patch
@@ -0,0 +1,136 @@
+From bec366b4041df72b559e713f1c924177676e6eb0 Mon Sep 17 00:00:00 2001
+From: Simon Kelley simon@thekelleys.org.uk
+Date: Wed, 24 Feb 2016 22:03:26 +0000
+Subject: [PATCH] Add --tftp-mtu option.
+
+---
+ CHANGELOG | 4 ++++
+ man/dnsmasq.8 | 4 ++++
+ src/dnsmasq.h | 2 +-
+ src/option.c | 10 +++++++++-
+ src/tftp.c | 14 ++++++++++++--
+ 5 files changed, 30 insertions(+), 4 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 6d9ba49..9218b8c 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -53,6 +53,10 @@ version 2.76
+
+ Add --dhcp-ttl option.
+
++ Add --tftp-mtu option. Thanks to Patrick McLean for the
++ initial patch.
++
++
+ version 2.75
+ Fix reversion on 2.74 which caused 100% CPU use when a
+ dhcp-script is configured. Thanks to Adrian Davey for
+diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
+index 2bcce20..3cf48cd 100644
+--- a/man/dnsmasq.8
++++ b/man/dnsmasq.8
+@@ -1810,6 +1810,10 @@ require about (2*n) + 10 descriptors. If
+ .B --tftp-port-range
+ is given, that can affect the number of concurrent connections.
+ .TP
++.B --tftp-mtu=<mtu size>
++Use size as the ceiling of the MTU supported by the intervening network when
++negotiating TFTP blocksize, overriding the MTU setting of the local interface if it is larger.
++.TP
+ .B --tftp-no-blocksize
+ Stop the TFTP server from negotiating the "blocksize" option with a
+ client. Some buggy clients request this option but then behave badly
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 9f73c3b..280ad9d 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -975,7 +975,7 @@ extern struct daemon {
+ struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
+ struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
+ struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
+- int dhcp_max, tftp_max;
++ int dhcp_max, tftp_max, tftp_mtu;
+ int dhcp_server_port, dhcp_client_port;
+ int start_tftp_port, end_tftp_port;
+ unsigned int min_leasetime;
+diff --git a/src/option.c b/src/option.c
+index 3f6d162..765965f 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -158,7 +158,8 @@ struct myoption {
+ #define LOPT_CPE_ID 346
+ #define LOPT_SCRIPT_ARP 347
+ #define LOPT_DHCPTTL 348
+-
++#define LOPT_TFTP_MTU 349
++
+ #ifdef HAVE_GETOPT_LONG
+ static const struct option opts[] =
+ #else
+@@ -244,6 +245,7 @@ static const struct myoption opts[] =
+ { "tftp-unique-root", 0, 0, LOPT_APREF },
+ { "tftp-root", 1, 0, LOPT_PREFIX },
+ { "tftp-max", 1, 0, LOPT_TFTP_MAX },
++ { "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
+ { "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
+ { "ptr-record", 1, 0, LOPT_PTR },
+ { "naptr-record", 1, 0, LOPT_NAPTR },
+@@ -432,6 +434,7 @@ static struct {
+ { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
+ { LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL },
+ { LOPT_TFTP_MAX, ARG_ONE, "<integer>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
++ { LOPT_TFTP_MTU, ARG_ONE, "<integer>", gettext_noop("Maximum MTU to use for TFTP transfers."), NULL },
+ { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
+ { LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
+ { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
+@@ -2625,6 +2628,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+ ret_err(gen_err);
+ break;
+
++ case LOPT_TFTP_MTU: /* --tftp-mtu */
++ if (!atoi_check(arg, &daemon->tftp_mtu))
++ ret_err(gen_err);
++ break;
++
+ case LOPT_PREFIX: /* --tftp-prefix */
+ comma = split(arg);
+ if (comma)
+diff --git a/src/tftp.c b/src/tftp.c
+index 00ed2fc..dc4aa85 100644
+--- a/src/tftp.c
++++ b/src/tftp.c
+@@ -103,8 +103,10 @@ void tftp_request(struct listener *listen, time_t now)
+ if (listen->iface)
+ {
+ addr = listen->iface->addr;
+- mtu = listen->iface->mtu;
+ name = listen->iface->name;
++ mtu = listen->iface->mtu;
++ if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
++ mtu = daemon->tftp_mtu;
+ }
+ else
+ {
+@@ -234,9 +236,17 @@ void tftp_request(struct listener *listen, time_t now)
+
+ strncpy(ifr.ifr_name, name, IF_NAMESIZE);
+ if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
+- mtu = ifr.ifr_mtu;
++ {
++ mtu = ifr.ifr_mtu;
++ if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu)
++ mtu = daemon->tftp_mtu;
++ }
+ }
+
++ /* Failed to get interface mtu - can use configured value. */
++ if (mtu == 0)
++ mtu = daemon->tftp_mtu;
++
+ if (name)
+ {
+ /* check for per-interface prefix */
+--
+1.7.10.4
+
diff --git a/src/patches/dnsmasq/004-fix_behaviour_of_empty_dhcp-option.patch b/src/patches/dnsmasq/004-fix_behaviour_of_empty_dhcp-option.patch
deleted file mode 100644
index 492ada9..0000000
--- a/src/patches/dnsmasq/004-fix_behaviour_of_empty_dhcp-option.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 5e3e464ac4022ee0b3794513abe510817e2cf3ca Mon Sep 17 00:00:00 2001
-From: Simon Kelley simon@thekelleys.org.uk
-Date: Tue, 25 Aug 2015 23:08:39 +0100
-Subject: [PATCH] Fix behaviour of empty dhcp-option=option6:dns-server, which
- should inhibit sending option.
-
----
- src/rfc3315.c | 9 +++++----
- 1 file changed, 5 insertions(+), 4 deletions(-)
-
-diff --git a/src/rfc3315.c b/src/rfc3315.c
-index 2665d0d..3f1f9ee 100644
---- a/src/rfc3315.c
-+++ b/src/rfc3315.c
-@@ -1320,15 +1320,16 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
-
- if (opt_cfg->opt == OPTION6_REFRESH_TIME)
- done_refresh = 1;
-+
-+ if (opt_cfg->opt == OPTION6_DNS_SERVER)
-+ done_dns = 1;
-
-- if (opt_cfg->flags & DHOPT_ADDR6)
-+ /* Empty DNS_SERVER option will not set DHOPT_ADDR6 */
-+ if ((opt_cfg->flags & DHOPT_ADDR6) || opt_cfg->opt == OPTION6_DNS_SERVER)
- {
- int len, j;
- struct in6_addr *a;
-
-- if (opt_cfg->opt == OPTION6_DNS_SERVER)
-- done_dns = 1;
--
- for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0;
- j < opt_cfg->len; j += IN6ADDRSZ, a++)
- if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ||
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/005-Apply_ceiling_of_lease_length_to_TTL_when_--dhcp-ttl_in_use.patch b/src/patches/dnsmasq/005-Apply_ceiling_of_lease_length_to_TTL_when_--dhcp-ttl_in_use.patch
new file mode 100644
index 0000000..2875d2c
--- /dev/null
+++ b/src/patches/dnsmasq/005-Apply_ceiling_of_lease_length_to_TTL_when_--dhcp-ttl_in_use.patch
@@ -0,0 +1,37 @@
+From 7480aeffc8ad195e9fd8bcf424bae0fab3839d55 Mon Sep 17 00:00:00 2001
+From: Simon Kelley simon@thekelleys.org.uk
+Date: Fri, 26 Feb 2016 21:58:20 +0000
+Subject: [PATCH] Apply ceiling of lease length to TTL when --dhcp-ttl in use.
+
+---
+ src/rfc1035.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/src/rfc1035.c b/src/rfc1035.c
+index 8f1e3b4..bed5312 100644
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1167,10 +1167,18 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp, int
+ static unsigned long crec_ttl(struct crec *crecp, time_t now)
+ {
+ /* Return 0 ttl for DHCP entries, which might change
+- before the lease expires. */
++ before the lease expires, unless configured otherwise. */
+
+ if (crecp->flags & F_DHCP)
+- return daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl;
++ {
++ int conf_ttl = daemon->use_dhcp_ttl ? daemon->dhcp_ttl : daemon->local_ttl;
++
++ /* Apply ceiling of actual lease length to configured TTL. */
++ if (!(crecp->flags & F_IMMORTAL) && (crecp->ttd - now) < conf_ttl)
++ return crecp->ttd - now;
++
++ return conf_ttl;
++ }
+
+ /* Immortal entries other than DHCP are local, and hold TTL in TTD field. */
+ if (crecp->flags & F_IMMORTAL)
+--
+1.7.10.4
+
diff --git a/src/patches/dnsmasq/005-suggest_solution_to_ENOMEM_error_with_IPv6_multicast.patch b/src/patches/dnsmasq/005-suggest_solution_to_ENOMEM_error_with_IPv6_multicast.patch
deleted file mode 100644
index c7cee60..0000000
--- a/src/patches/dnsmasq/005-suggest_solution_to_ENOMEM_error_with_IPv6_multicast.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From 9cdcfe9f19ffd45bac4e5b459879bf7c50a287ed Mon Sep 17 00:00:00 2001
-From: Simon Kelley simon@thekelleys.org.uk
-Date: Wed, 26 Aug 2015 22:38:08 +0100
-Subject: [PATCH] Suggest solution to ENOMEM error with IPv6 multicast.
-
----
- src/network.c | 13 ++++++++++---
- 1 file changed, 10 insertions(+), 3 deletions(-)
-
-diff --git a/src/network.c b/src/network.c
-index a1d90c8..819302f 100644
---- a/src/network.c
-+++ b/src/network.c
-@@ -1076,23 +1076,30 @@ void join_multicast(int dienow)
-
- if ((daemon->doing_dhcp6 || daemon->relay6) &&
- setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
-- err = 1;
-+ err = errno;
-
- inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
-
- if (daemon->doing_dhcp6 &&
- setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
-- err = 1;
-+ err = errno;
-
- inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
-
- if (daemon->doing_ra &&
- setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
-- err = 1;
-+ err = errno;
-
- if (err)
- {
- char *s = _("interface %s failed to join DHCPv6 multicast group: %s");
-+ errno = err;
-+
-+#ifdef HAVE_LINUX_NETWORK
-+ if (errno == ENOMEM)
-+ my_syslog(LOG_ERR, _("try increasing /proc/sys/net/core/optmem_max"));
-+#endif
-+
- if (dienow)
- die(s, iface->name, EC_BADNET);
- else
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/006-clarify_man_page_on_RDNSS_set_in_router_advertisement.patch b/src/patches/dnsmasq/006-clarify_man_page_on_RDNSS_set_in_router_advertisement.patch
deleted file mode 100644
index 19c76e6..0000000
--- a/src/patches/dnsmasq/006-clarify_man_page_on_RDNSS_set_in_router_advertisement.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From 20fd11e11a9d09edcea94de135396ae1541fbbab Mon Sep 17 00:00:00 2001
-From: Simon Kelley simon@thekelleys.org.uk
-Date: Wed, 26 Aug 2015 22:48:13 +0100
-Subject: [PATCH] Clarify man page on RDNSS set in router advertisement.
-
----
- man/dnsmasq.8 | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
-index a23c898..d51b10f 100644
---- a/man/dnsmasq.8
-+++ b/man/dnsmasq.8
-@@ -1687,15 +1687,15 @@ creation are handled by a different protocol. When DHCP is in use,
- only a subset of this is needed, and dnsmasq can handle it, using
- existing DHCP configuration to provide most data. When RA is enabled,
- dnsmasq will advertise a prefix for each dhcp-range, with default
--router and recursive DNS server as the relevant link-local address on
--the machine running dnsmasq. By default, he "managed address" bits are set, and
-+router as the relevant link-local address on
-+the machine running dnsmasq. By default, the "managed address" bits are set, and
- the "use SLAAC" bit is reset. This can be changed for individual
- subnets with the mode keywords described in
- .B --dhcp-range.
- RFC6106 DNS parameters are included in the advertisements. By default,
- the relevant link-local address of the machine running dnsmasq is sent
- as recursive DNS server. If provided, the DHCPv6 options dns-server and
--domain-search are used for RDNSS and DNSSL.
-+domain-search are used for the DNS server (RDNSS) and the domain serach list (DNSSL).
- .TP
- .B --ra-param=<interface>,[high|low],[[<ra-interval>],<router lifetime>]
- Set non-default values for router advertisements sent via an
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/007-handle_signed_dangling_CNAME_replies_to_DS_queries.patch b/src/patches/dnsmasq/007-handle_signed_dangling_CNAME_replies_to_DS_queries.patch
deleted file mode 100644
index 832a22e..0000000
--- a/src/patches/dnsmasq/007-handle_signed_dangling_CNAME_replies_to_DS_queries.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 6de81f1250fd323c9155de065d5a9dc200a6f20b Mon Sep 17 00:00:00 2001
-From: Simon Kelley simon@thekelleys.org.uk
-Date: Wed, 9 Sep 2015 22:51:13 +0100
-Subject: [PATCH] Handle signed dangling CNAME replies to DS queries.
-
----
- src/dnssec.c | 7 ++-----
- 1 file changed, 2 insertions(+), 5 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index 4deda24..67ce486 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -1232,11 +1232,8 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
-
- /* If we return STAT_NO_SIG, name contains the name of the DS query */
- if (val == STAT_NO_SIG)
-- {
-- *keyname = 0;
-- return val;
-- }
--
-+ return val;
-+
- /* If the key needed to validate the DS is on the same domain as the DS, we'll
- loop getting nowhere. Stop that now. This can happen of the DS answer comes
- from the DS's zone, and not the parent zone. */
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/008-DHCPv6_option_56_does_not_hold_an_address_list.patch b/src/patches/dnsmasq/008-DHCPv6_option_56_does_not_hold_an_address_list.patch
deleted file mode 100644
index fdccd0e..0000000
--- a/src/patches/dnsmasq/008-DHCPv6_option_56_does_not_hold_an_address_list.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 102208df695e886a3086754d32bf7f8c541fbe46 Mon Sep 17 00:00:00 2001
-From: Simon Kelley simon@thekelleys.org.uk
-Date: Thu, 10 Sep 2015 21:50:00 +0100
-Subject: [PATCH] DHCPv6 option 56 does not hold an address list. (RFC 5908).
-
----
- src/dhcp-common.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/dhcp-common.c b/src/dhcp-common.c
-index bc48f41..8fc171a 100644
---- a/src/dhcp-common.c
-+++ b/src/dhcp-common.c
-@@ -599,7 +599,7 @@ static const struct opttab_t opttab6[] = {
- { "sntp-server", 31, OT_ADDR_LIST },
- { "information-refresh-time", 32, OT_TIME },
- { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
-- { "ntp-server", 56, OT_ADDR_LIST },
-+ { "ntp-server", 56, 0 },
- { "bootfile-url", 59, OT_NAME },
- { "bootfile-param", 60, OT_CSTRING },
- { NULL, 0, 0 }
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/009-Respect_the_--no_resolv_flag_in_inotify_code.patch b/src/patches/dnsmasq/009-Respect_the_--no_resolv_flag_in_inotify_code.patch
deleted file mode 100644
index 2014fdb..0000000
--- a/src/patches/dnsmasq/009-Respect_the_--no_resolv_flag_in_inotify_code.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 77607cbea0ad0f876dfb79c8b2c121ee400d57d0 Mon Sep 17 00:00:00 2001
-From: Simon Kelley simon@thekelleys.org.uk
-Date: Thu, 10 Sep 2015 23:08:43 +0100
-Subject: [PATCH] Respect the --no-resolv flag in inotify code.
-
----
- CHANGELOG | 7 ++++++-
- debian/changelog | 6 ++++++
- src/inotify.c | 3 +++
- 3 files changed, 15 insertions(+), 1 deletion(-)
-
-diff --git a/CHANGELOG b/CHANGELOG
-index bbc2834..d6e309f 100644
---- a/CHANGELOG
-+++ b/CHANGELOG
-@@ -7,8 +7,13 @@ version 2.76
-
- Enhance --add-subnet to allow arbitrary subnet addresses.
- Thanks to Ed Barsley for the patch.
-+
-+ Respect the --no-resolv flag in inotify code. Fixes bug
-+ which caused dnsmasq to fail to start if a resolv-file
-+ was a dangling symbolic link, even of --no-resolv set.
-+ Thanks to Alexander Kurtz for spotting the problem.
-+
-
--
- version 2.75
- Fix reversion on 2.74 which caused 100% CPU use when a
- dhcp-script is configured. Thanks to Adrian Davey for
-diff --git a/src/inotify.c b/src/inotify.c
-index 52d412f..ef05c58 100644
---- a/src/inotify.c
-+++ b/src/inotify.c
-@@ -90,6 +90,9 @@ void inotify_dnsmasq_init()
-
- if (daemon->inotifyfd == -1)
- die(_("failed to create inotify: %s"), NULL, EC_MISC);
-+
-+ if (option_bool(OPT_NO_RESOLV))
-+ return;
-
- for (res = daemon->resolv_files; res; res = res->next)
- {
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/010-Rationalise_5e3e464ac4022ee0b3794513abe510817e2cf3ca.patch b/src/patches/dnsmasq/010-Rationalise_5e3e464ac4022ee0b3794513abe510817e2cf3ca.patch
deleted file mode 100644
index 281697f..0000000
--- a/src/patches/dnsmasq/010-Rationalise_5e3e464ac4022ee0b3794513abe510817e2cf3ca.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 27b78d990b7cd901866ad6f1a17b9d633a95fdce Mon Sep 17 00:00:00 2001
-From: Simon Kelley simon@thekelleys.org.uk
-Date: Sat, 26 Sep 2015 21:40:45 +0100
-Subject: [PATCH] Rationalise 5e3e464ac4022ee0b3794513abe510817e2cf3ca
-
----
- src/rfc3315.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
-diff --git a/src/rfc3315.c b/src/rfc3315.c
-index 3f1f9ee..3ed8623 100644
---- a/src/rfc3315.c
-+++ b/src/rfc3315.c
-@@ -1324,8 +1324,7 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
- if (opt_cfg->opt == OPTION6_DNS_SERVER)
- done_dns = 1;
-
-- /* Empty DNS_SERVER option will not set DHOPT_ADDR6 */
-- if ((opt_cfg->flags & DHOPT_ADDR6) || opt_cfg->opt == OPTION6_DNS_SERVER)
-+ if (opt_cfg->flags & DHOPT_ADDR6)
- {
- int len, j;
- struct in6_addr *a;
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/011-Catch_errors_from_sendmsg_in_DHCP_code.patch b/src/patches/dnsmasq/011-Catch_errors_from_sendmsg_in_DHCP_code.patch
deleted file mode 100644
index 631495f..0000000
--- a/src/patches/dnsmasq/011-Catch_errors_from_sendmsg_in_DHCP_code.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 98079ea89851da1df4966dfdfa1852a98da02912 Mon Sep 17 00:00:00 2001
-From: Simon Kelley simon@thekelleys.org.uk
-Date: Tue, 13 Oct 2015 20:30:32 +0100
-Subject: [PATCH] Catch errors from sendmsg in DHCP code. Logs, eg, iptables
- DROPS of dest 255.255.255.255
-
----
- src/dhcp.c | 7 ++++++-
- 1 file changed, 6 insertions(+), 1 deletion(-)
-
-diff --git a/src/dhcp.c b/src/dhcp.c
-index e6fceb1..1c85e42 100644
---- a/src/dhcp.c
-+++ b/src/dhcp.c
-@@ -452,8 +452,13 @@ void dhcp_packet(time_t now, int pxe_fd)
- #endif
-
- while(retry_send(sendmsg(fd, &msg, 0)));
-+
-+ /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
-+ if (errno != 0)
-+ my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
-+ inet_ntoa(dest.sin_addr), strerror(errno));
- }
--
-+
- /* check against secondary interface addresses */
- static int check_listen_addrs(struct in_addr local, int if_index, char *label,
- struct in_addr netmask, struct in_addr broadcast, void *vparam)
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/012-Update_list_of_subnet_for_--bogus-priv.patch b/src/patches/dnsmasq/012-Update_list_of_subnet_for_--bogus-priv.patch
deleted file mode 100644
index 3ba98fc..0000000
--- a/src/patches/dnsmasq/012-Update_list_of_subnet_for_--bogus-priv.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 90477fb79420a34124b66ebd808c578817a30e4c Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Tue, 20 Oct 2015 21:21:32 +0100
-Subject: [PATCH] Update list of subnet for --bogus-priv
-
-RFC6303 specifies & recommends following zones not be forwarded
-to globally facing servers.
-+------------------------------+-----------------------+
-| Zone | Description |
-+------------------------------+-----------------------+
-| 0.IN-ADDR.ARPA | IPv4 "THIS" NETWORK |
-| 127.IN-ADDR.ARPA | IPv4 Loopback NETWORK |
-| 254.169.IN-ADDR.ARPA | IPv4 LINK LOCAL |
-| 2.0.192.IN-ADDR.ARPA | IPv4 TEST-NET-1 |
-| 100.51.198.IN-ADDR.ARPA | IPv4 TEST-NET-2 |
-| 113.0.203.IN-ADDR.ARPA | IPv4 TEST-NET-3 |
-| 255.255.255.255.IN-ADDR.ARPA | IPv4 BROADCAST |
-+------------------------------+-----------------------+
-
-Signed-off-by: Kevin Darbyshire-Bryant
kevin@darbyshire-bryant.me.uk
----
- src/rfc1035.c | 8 ++++++--
- 1 file changed, 6 insertions(+), 2 deletions(-)
-
-diff --git a/src/rfc1035.c b/src/rfc1035.c
-index 6a51b30..4eb1772 100644
---- a/src/rfc1035.c
-+++ b/src/rfc1035.c
-@@ -756,10 +756,14 @@ int private_net(struct in_addr addr, int ban_localhost)
- return
- (((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
- ((ip_addr & 0xFF000000) == 0x00000000) /* RFC 5735 section 3. "here" network */ ||
-- ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
- ((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
- ((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
-- ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
-+ ((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
-+ ((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ||
-+ ((ip_addr & 0xFFFFFF00) == 0xC0000200) /* 192.0.2.0/24 (test-net) */ ||
-+ ((ip_addr & 0xFFFFFF00) == 0xC6336400) /* 198.51.100.0/24(test-net) */ ||
-+ ((ip_addr & 0xFFFFFF00) == 0xCB007100) /* 203.0.113.0/24 (test-net) */ ||
-+ ((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
- }
-
- static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/013-Fix_crash_when_empty_address_from_DNS_overlays_A_record_from.patch b/src/patches/dnsmasq/013-Fix_crash_when_empty_address_from_DNS_overlays_A_record_from.patch
deleted file mode 100644
index 736cf38..0000000
--- a/src/patches/dnsmasq/013-Fix_crash_when_empty_address_from_DNS_overlays_A_record_from.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 41a8d9e99be9f2cc8b02051dd322cb45e0faac87 Mon Sep 17 00:00:00 2001
-From: =?utf8?q?Edwin=20T=C3=B6r=C3=B6k?=
edwin+ml-cerowrt@etorok.net
-Date: Sat, 14 Nov 2015 17:45:48 +0000
-Subject: [PATCH] Fix crash when empty address from DNS overlays A record from
- hosts.
-
----
- CHANGELOG | 5 +++++
- src/cache.c | 2 +-
- 2 files changed, 6 insertions(+), 1 deletion(-)
-
-diff --git a/CHANGELOG b/CHANGELOG
-index d6e309f..93c73d0 100644
---- a/CHANGELOG
-+++ b/CHANGELOG
-@@ -13,6 +13,11 @@ version 2.76
- was a dangling symbolic link, even of --no-resolv set.
- Thanks to Alexander Kurtz for spotting the problem.
-
-+ Fix crash when an A or AAAA record is defined locally,
-+ in a hosts file, and an upstream server sends a reply
-+ that the same name is empty. Thanks to Edwin Török for
-+ the patch.
-+
-
- version 2.75
- Fix reversion on 2.74 which caused 100% CPU use when a
-diff --git a/src/cache.c b/src/cache.c
-index 178d654..1b76b67 100644
---- a/src/cache.c
-+++ b/src/cache.c
-@@ -481,7 +481,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
- 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 | F_IPV6)) && (flags & F_FORWARD) && addr)
- {
- if ((flags & F_IPV4) && (new->flags & F_IPV4) &&
- new->addr.addr.addr.addr4.s_addr == addr->addr.addr4.s_addr)
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/014-Handle_unknown_DS_hash_algos_correctly.patch b/src/patches/dnsmasq/014-Handle_unknown_DS_hash_algos_correctly.patch
deleted file mode 100644
index 8b17431..0000000
--- a/src/patches/dnsmasq/014-Handle_unknown_DS_hash_algos_correctly.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 67ab3285b5d9a1b1e20e034cf272867fdab8a0f9 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Fri, 20 Nov 2015 23:20:47 +0000
-Subject: [PATCH] Handle unknown DS hash algos correctly.
-
-When we can validate a DS RRset, but don't speak the hash algo it
-contains, treat that the same as an NSEC/3 proving that the DS
-doesn't exist. 4025 5.2
----
- src/dnssec.c | 13 +++++++++++++
- 1 file changed, 13 insertions(+)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index 67ce486..b4dc14e 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -1005,6 +1005,19 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
- if (crecp->flags & F_NEG)
- return STAT_INSECURE_DS;
-
-+ /* 4035 5.2
-+ If the validator does not support any of the algorithms listed in an
-+ authenticated DS RRset, then the resolver has no supported
-+ authentication path leading from the parent to the child. The
-+ resolver should treat this case as it would the case of an
-+ authenticated NSEC RRset proving that no DS RRset exists, */
-+ for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS))
-+ if (hash_find(ds_digest_name(recp1->addr.ds.digest)))
-+ break;
-+
-+ if (!recp1)
-+ return STAT_INSECURE_DS;
-+
- /* NOTE, we need to find ONE DNSKEY which matches the DS */
- for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--)
- {
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/015-Fix_crash_at_start_up_with_conf-dir.patch b/src/patches/dnsmasq/015-Fix_crash_at_start_up_with_conf-dir.patch
deleted file mode 100644
index a9102c1..0000000
--- a/src/patches/dnsmasq/015-Fix_crash_at_start_up_with_conf-dir.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 0007ee90646a5a78a96ee729932e89d31c69513a Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Sat, 21 Nov 2015 21:47:41 +0000
-Subject: [PATCH] Fix crash at start up with conf-dir=/path,*
-
-Thanks to Brian Carpenter and American Fuzzy Lop for finding the bug.
----
- src/option.c | 14 ++++++++++----
- 1 file changed, 10 insertions(+), 4 deletions(-)
-
-diff --git a/src/option.c b/src/option.c
-index 746cd11..71beb98 100644
---- a/src/option.c
-+++ b/src/option.c
-@@ -1515,10 +1515,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
- 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);
-+ /* "*" with no suffix is a no-op */
-+ if (arg[1] == 0)
-+ free(li);
-+ else
-+ {
-+ li->next = match_suffix;
-+ match_suffix = li;
-+ /* Have to copy: buffer is overwritten */
-+ li->suffix = opt_string_alloc(arg+1);
-+ }
- }
- else
- {
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/016-Major_rationalisation_of_DNSSEC_validation.patch b/src/patches/dnsmasq/016-Major_rationalisation_of_DNSSEC_validation.patch
deleted file mode 100644
index 7f25066..0000000
--- a/src/patches/dnsmasq/016-Major_rationalisation_of_DNSSEC_validation.patch
+++ /dev/null
@@ -1,2209 +0,0 @@
-From 9a31b68b59adcac01016d4026d906b69c4216c01 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Tue, 15 Dec 2015 10:20:39 +0000
-Subject: [PATCH] Major rationalisation of DNSSEC validation.
-
-Much gnarly special-case code removed and replaced with correct
-general implementaion. Checking of zone-status moved to DNSSEC code,
-where it should be, vastly simplifying query-forwarding code.
----
- src/dnsmasq.h | 19 +-
- src/dnssec.c | 926 ++++++++++++++++++++++++++++++---------------------------
- src/forward.c | 741 ++++++++++-----------------------------------
- 3 files changed, 653 insertions(+), 1033 deletions(-)
-
-diff --git a/src/dnsmasq.h b/src/dnsmasq.h
-index f42acdb..023a1cf 100644
---- a/src/dnsmasq.h
-+++ b/src/dnsmasq.h
-@@ -586,12 +586,8 @@ struct hostsfile {
- #define STAT_NEED_KEY 5
- #define STAT_TRUNCATED 6
- #define STAT_SECURE_WILDCARD 7
--#define STAT_NO_SIG 8
--#define STAT_NO_DS 9
--#define STAT_NO_NS 10
--#define STAT_NEED_DS_NEG 11
--#define STAT_CHASE_CNAME 12
--#define STAT_INSECURE_DS 13
-+#define STAT_OK 8
-+#define STAT_ABANDONED 9
-
- #define FREC_NOREBIND 1
- #define FREC_CHECKING_DISABLED 2
-@@ -601,8 +597,7 @@ struct hostsfile {
- #define FREC_AD_QUESTION 32
- #define FREC_DO_QUESTION 64
- #define FREC_ADDED_PHEADER 128
--#define FREC_CHECK_NOSIGN 256
--#define FREC_TEST_PKTSZ 512
-+#define FREC_TEST_PKTSZ 256
-
- #ifdef HAVE_DNSSEC
- #define HASH_SIZE 20 /* SHA-1 digest size */
-@@ -626,9 +621,7 @@ struct frec {
- #ifdef HAVE_DNSSEC
- int class, work_counter;
- struct blockdata *stash; /* Saved reply, whilst we validate */
-- 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;
-+ size_t stash_len;
- struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
- struct frec *blocking_query; /* Query which is blocking us. */
- #endif
-@@ -1162,8 +1155,8 @@ 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 edns_pktsz);
- int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
- int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
--int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer, int *nons);
--int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
-+int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class,
-+ int check_unsigned, int *neganswer, int *nons);
- int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
- size_t filter_rrsigs(struct dns_header *header, size_t plen);
- unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
-diff --git a/src/dnssec.c b/src/dnssec.c
-index b4dc14e..de7b335 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -65,8 +65,10 @@ static char *algo_digest_name(int algo)
- case 8: return "sha256";
- case 10: return "sha512";
- case 12: return "gosthash94";
-+#ifndef NO_NETTLE_ECC
- case 13: return "sha256";
- case 14: return "sha384";
-+#endif
- default: return NULL;
- }
- }
-@@ -592,30 +594,30 @@ static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end,
- }
- }
-
--static int expand_workspace(unsigned char ***wkspc, int *sz, int new)
-+static int expand_workspace(unsigned char ***wkspc, int *szp, int new)
- {
- unsigned char **p;
-- int new_sz = *sz;
--
-- if (new_sz > new)
-+ int old = *szp;
-+
-+ if (old >= new+1)
- return 1;
-
- if (new >= 100)
- return 0;
-
-- new_sz += 5;
-+ new += 5;
-
-- if (!(p = whine_malloc((new_sz) * sizeof(unsigned char **))))
-+ if (!(p = whine_malloc(new * sizeof(unsigned char **))))
- return 0;
-
-- if (*wkspc)
-+ if (old != 0 && *wkspc)
- {
-- memcpy(p, *wkspc, *sz * sizeof(unsigned char **));
-+ memcpy(p, *wkspc, old * sizeof(unsigned char **));
- free(*wkspc);
- }
-
- *wkspc = p;
-- *sz = new_sz;
-+ *szp = new;
-
- return 1;
- }
-@@ -706,47 +708,28 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
- } while (swap);
- }
-
--/* Validate a single RRset (class, type, name) in the supplied DNS reply
-- 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.
-- STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
--
-- if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
-- otherwise find the key in the cache.
-+static unsigned char **rrset = NULL, **sigs = NULL;
-
-- 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, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in)
-+/* Get pointers to RRset menbers and signature(s) for same.
-+ Check signatures, and return keyname associated in keyname. */
-+static int explore_rrset(struct dns_header *header, size_t plen, int class, int type,
-+ char *name, char *keyname, int *sigcnt, int *rrcnt)
- {
-- static unsigned char **rrset = NULL, **sigs = NULL;
-- static int rrset_sz = 0, sig_sz = 0;
--
-+ static int rrset_sz = 0, sig_sz = 0;
- unsigned char *p;
-- int rrsetidx, sigidx, res, rdlen, j, name_labels;
-- 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;
--
-+ int rrsetidx, sigidx, j, rdlen, res;
-+ int name_labels = count_labels(name); /* For 4035 5.3.2 check */
-+ int gotkey = 0;
-+
- 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. */
-+ /* look for RRSIGs for this RRset and get pointers to each RR in the set. */
- for (rrsetidx = 0, sigidx = 0, j = ntohs(header->ancount) + ntohs(header->nscount);
- j != 0; j--)
- {
- unsigned char *pstart, *pdata;
-- int stype, sclass;
-+ int stype, sclass, algo, type_covered, labels, sig_expiration, sig_inception;
-
- pstart = p;
-
-@@ -762,14 +745,14 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
- GETSHORT(rdlen, p);
-
- if (!CHECK_LEN(header, p, plen, rdlen))
-- return STAT_BOGUS;
-+ return 0;
-
- if (res == 1 && sclass == class)
- {
- if (stype == type)
- {
- if (!expand_workspace(&rrset, &rrset_sz, rrsetidx))
-- return STAT_BOGUS;
-+ return 0;
-
- rrset[rrsetidx++] = pstart;
- }
-@@ -777,14 +760,54 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
- if (stype == T_RRSIG)
- {
- if (rdlen < 18)
-- return STAT_BOGUS; /* bad packet */
-+ return 0; /* bad packet */
-
- GETSHORT(type_covered, p);
-+ algo = *p++;
-+ labels = *p++;
-+ p += 4; /* orig_ttl */
-+ GETLONG(sig_expiration, p);
-+ GETLONG(sig_inception, p);
-+ p += 2; /* key_tag */
-
-- if (type_covered == type)
-+ if (gotkey)
-+ {
-+ /* If there's more than one SIG, ensure they all have same keyname */
-+ if (extract_name(header, plen, &p, keyname, 0, 0) != 1)
-+ return 0;
-+ }
-+ else
-+ {
-+ gotkey = 1;
-+
-+ if (!extract_name(header, plen, &p, keyname, 1, 0))
-+ return 0;
-+
-+ /* RFC 4035 5.3.1 says that the Signer's Name field MUST equal
-+ the name of the zone containing the RRset. We can't tell that
-+ for certain, but we can check that the RRset name is equal to
-+ or encloses the signers name, which should be enough to stop
-+ an attacker using signatures made with the key of an unrelated
-+ zone he controls. Note that the root key is always allowed. */
-+ if (*keyname != 0)
-+ {
-+ char *name_start;
-+ for (name_start = name; !hostname_isequal(name_start, keyname); )
-+ if ((name_start = strchr(name_start, '.')))
-+ name_start++; /* chop a label off and try again */
-+ else
-+ return 0;
-+ }
-+ }
-+
-+ /* Don't count signatures for algos we don't support */
-+ if (check_date_range(sig_inception, sig_expiration) &&
-+ labels <= name_labels &&
-+ type_covered == type &&
-+ algo_digest_name(algo))
- {
- if (!expand_workspace(&sigs, &sig_sz, sigidx))
-- return STAT_BOGUS;
-+ return 0;
-
- sigs[sigidx++] = pdata;
- }
-@@ -794,17 +817,45 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
- }
-
- if (!ADD_RDLEN(header, p, plen, rdlen))
-- return STAT_BOGUS;
-+ return 0;
- }
-
-- /* RRset empty */
-- if (rrsetidx == 0)
-- return STAT_INSECURE;
-+ *sigcnt = sigidx;
-+ *rrcnt = rrsetidx;
-+
-+ return 1;
-+}
-+
-+/* Validate a single RRset (class, type, name) in the supplied DNS reply
-+ 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_BOGUS signature is wrong, bad packet.
-+ STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
-+ STAT_NEED_DS need DS to complete validation (name is returned in keyname)
-+
-+ if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
-+ otherwise find the key in the cache.
-
-- /* no RRSIGs */
-- if (sigidx == 0)
-- return STAT_NO_SIG;
-+ name is unchanged on exit. keyname is used as workspace and trashed.
-+
-+ Call explore_rrset first to find and count RRs and sigs.
-+*/
-+static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type, int sigidx, int rrsetidx,
-+ char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in)
-+{
-+ unsigned char *p;
-+ int rdlen, j, name_labels;
-+ struct crec *crecp = NULL;
-+ int algo, labels, orig_ttl, key_tag;
-+ u16 *rr_desc = get_desc(type);
-+
-+ if (wildcard_out)
-+ *wildcard_out = NULL;
-
-+ name_labels = count_labels(name); /* For 4035 5.3.2 check */
-+
- /* Sort RRset records into canonical order.
- Note that at this point keyname and daemon->workspacename buffs are
- unused, and used as workspace by the sort. */
-@@ -828,44 +879,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
- algo = *p++;
- labels = *p++;
- GETLONG(orig_ttl, p);
-- GETLONG(sig_expiration, p);
-- GETLONG(sig_inception, p);
-+ p += 8; /* sig_expiration, sig_inception already checked */
- GETSHORT(key_tag, p);
-
- if (!extract_name(header, plen, &p, keyname, 1, 0))
- return STAT_BOGUS;
-
-- /* RFC 4035 5.3.1 says that the Signer's Name field MUST equal
-- the name of the zone containing the RRset. We can't tell that
-- for certain, but we can check that the RRset name is equal to
-- or encloses the signers name, which should be enough to stop
-- an attacker using signatures made with the key of an unrelated
-- zone he controls. Note that the root key is always allowed. */
-- if (*keyname != 0)
-- {
-- int failed = 0;
--
-- for (name_start = name; !hostname_isequal(name_start, keyname); )
-- if ((name_start = strchr(name_start, '.')))
-- name_start++; /* chop a label off and try again */
-- else
-- {
-- failed = 1;
-- break;
-- }
--
-- /* Bad sig, try another */
-- if (failed)
-- continue;
-- }
--
-- /* Other 5.3.1 checks */
-- if (!check_date_range(sig_inception, sig_expiration) ||
-- labels > name_labels ||
-- !(hash = hash_find(algo_digest_name(algo))) ||
-+ if (!(hash = hash_find(algo_digest_name(algo))) ||
- !hash_init(hash, &ctx, &digest))
- continue;
--
-+
- /* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
- if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
- return STAT_NEED_KEY;
-@@ -971,10 +994,11 @@ 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_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
-+ STAT_OK Done, key(s) 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
-+ STAT_NEED_DNSKEY DNSKEY records to validate a key not found, name in keyname
- */
- int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
- {
-@@ -1001,23 +1025,6 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
- return STAT_NEED_DS;
- }
-
-- /* If we've cached that DS provably doesn't exist, result must be INSECURE */
-- if (crecp->flags & F_NEG)
-- return STAT_INSECURE_DS;
--
-- /* 4035 5.2
-- If the validator does not support any of the algorithms listed in an
-- authenticated DS RRset, then the resolver has no supported
-- authentication path leading from the parent to the child. The
-- resolver should treat this case as it would the case of an
-- authenticated NSEC RRset proving that no DS RRset exists, */
-- for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS))
-- if (hash_find(ds_digest_name(recp1->addr.ds.digest)))
-- break;
--
-- if (!recp1)
-- return STAT_INSECURE_DS;
--
- /* NOTE, we need to find ONE DNSKEY which matches the DS */
- for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--)
- {
-@@ -1070,7 +1077,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
- void *ctx;
- unsigned char *digest, *ds_digest;
- const struct nettle_hash *hash;
--
-+ int sigcnt, rrcnt;
-+
- if (recp1->addr.ds.algo == algo &&
- recp1->addr.ds.keytag == keytag &&
- recp1->uid == (unsigned int)class &&
-@@ -1088,10 +1096,14 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
-
- from_wire(name);
-
-- if (recp1->addr.ds.keylen == (int)hash->digest_size &&
-+ if (!(recp1->flags & F_NEG) &&
-+ 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, NULL, key, rdlen - 4, algo, keytag) == STAT_SECURE)
-+ explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&
-+ sigcnt != 0 && rrcnt != 0 &&
-+ validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
-+ NULL, key, rdlen - 4, algo, keytag) == STAT_SECURE)
- {
- valid = 1;
- break;
-@@ -1112,7 +1124,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
- {
- /* Ensure we have type, class TTL and length */
- if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
-- return STAT_INSECURE; /* bad packet */
-+ return STAT_BOGUS; /* bad packet */
-
- GETSHORT(qtype, p);
- GETSHORT(qclass, p);
-@@ -1198,7 +1210,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
-
- /* commit cache insert. */
- cache_end_insert();
-- return STAT_SECURE;
-+ return STAT_OK;
- }
-
- log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
-@@ -1207,12 +1219,14 @@ 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.
-+ Also handles replies which prove that there's no DS at this location,
-+ either because the zone is unsigned or this isn't a zone cut. These are
-+ cached too.
- return codes:
-- STAT_SECURE At least one valid DS found and in cache.
-- STAT_NO_DS It's proved there's no DS here.
-- STAT_NO_NS It's proved there's no DS _or_ NS here.
-+ STAT_OK At least one valid DS found and in cache.
- STAT_BOGUS no DS in reply or not signed, fails validation, bad packet.
- STAT_NEED_KEY DNSKEY records to validate a DS not found, name in keyname
-+ STAT_NEED_DS DS record needed.
- */
-
- int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
-@@ -1230,7 +1244,7 @@ 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, &nons);
-+ val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons);
- /* Note dnssec_validate_reply() will have cached positive answers */
-
- if (val == STAT_INSECURE)
-@@ -1242,22 +1256,21 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
-
- if (!(p = skip_section(p, ntohs(header->ancount), header, plen)))
- val = STAT_BOGUS;
--
-- /* If we return STAT_NO_SIG, name contains the name of the DS query */
-- if (val == STAT_NO_SIG)
-- return val;
-
- /* If the key needed to validate the DS is on the same domain as the DS, we'll
- loop getting nowhere. Stop that now. This can happen of the DS answer comes
- from the DS's zone, and not the parent zone. */
-- if (val == STAT_BOGUS || (val == STAT_NEED_KEY && hostname_isequal(name, keyname)))
-+ if (val == STAT_BOGUS || (val == STAT_NEED_KEY && hostname_isequal(name, keyname)))
- {
- log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
- return STAT_BOGUS;
- }
-+
-+ if (val != STAT_SECURE)
-+ return val;
-
- /* By here, the answer is proved secure, and a positive answer has been cached. */
-- if (val == STAT_SECURE && neganswer)
-+ if (neganswer)
- {
- int rdlen, flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
- unsigned long ttl, minttl = ULONG_MAX;
-@@ -1317,15 +1330,14 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
-
- cache_end_insert();
-
-- log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no delegation" : "no DS");
-+ log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "no DS");
- }
--
-- return nons ? STAT_NO_NS : STAT_NO_DS;
- }
-
-- return val;
-+ return STAT_OK;
- }
-
-+
- /* 4034 6.1 */
- static int hostname_cmp(const char *a, const char *b)
- {
-@@ -1452,7 +1464,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
- int mask = 0x80 >> (type & 0x07);
-
- if (nons)
-- *nons = 0;
-+ *nons = 1;
-
- /* Find NSEC record that proves name doesn't exist */
- for (i = 0; i < nsec_count; i++)
-@@ -1480,9 +1492,22 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
- /* 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;
-+ if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0)
-+ *nons = 0;
-
-+ if (rdlen >= 2 && p[0] == 0)
-+ {
-+ /* A CNAME answer would also be valid, so if there's a CNAME is should
-+ have been returned. */
-+ if ((p[2] & (0x80 >> T_CNAME)) != 0)
-+ return STAT_BOGUS;
-+
-+ /* If the SOA bit is set for a DS record, then we have the
-+ DS from the wrong side of the delegation. */
-+ if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
-+ return STAT_BOGUS;
-+ }
-+
- while (rdlen >= 2)
- {
- if (!CHECK_LEN(header, p, plen, rdlen))
-@@ -1586,7 +1611,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, int *nons)
- {
-- int i, hash_len, salt_len, base32_len, rdlen;
-+ int i, hash_len, salt_len, base32_len, rdlen, flags;
- unsigned char *p, *psave;
-
- for (i = 0; i < nsec_count; i++)
-@@ -1599,7 +1624,9 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
- p += 8; /* class, type, TTL */
- GETSHORT(rdlen, p);
- psave = p;
-- p += 4; /* algo, flags, iterations */
-+ p++; /* algo */
-+ flags = *p++; /* flags */
-+ p += 2; /* iterations */
- salt_len = *p++; /* salt_len */
- p += salt_len; /* salt */
- hash_len = *p++; /* p now points to next hashed name */
-@@ -1626,16 +1653,29 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
- 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;
-+ if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0)
-+ *nons = 0;
-
-+ if (rdlen >= 2 && p[0] == 0)
-+ {
-+ /* A CNAME answer would also be valid, so if there's a CNAME is should
-+ have been returned. */
-+ if ((p[2] & (0x80 >> T_CNAME)) != 0)
-+ return 0;
-+
-+ /* If the SOA bit is set for a DS record, then we have the
-+ DS from the wrong side of the delegation. */
-+ if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
-+ 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;
-+ return 0;
-
- break; /* finshed checking */
- }
-@@ -1643,7 +1683,7 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
- rdlen -= p[1];
- p += p[1];
- }
--
-+
- return 1;
- }
- else if (rc < 0)
-@@ -1651,16 +1691,27 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
- /* 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;
-+ {
-+ if ((flags & 0x01) && nons) /* opt out */
-+ *nons = 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;
-+ {
-+ if ((flags & 0x01) && nons) /* opt out */
-+ *nons = 0;
-+
-+ return 1;
-+ }
- }
- }
- }
-+
- return 0;
- }
-
-@@ -1673,7 +1724,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- char *closest_encloser, *next_closest, *wildcard;
-
- if (nons)
-- *nons = 0;
-+ *nons = 1;
-
- /* Look though the NSEC3 records to find the first one with
- an algorithm we support (currently only algo == 1).
-@@ -1813,16 +1864,81 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
-
- return STAT_SECURE;
- }
--
--/* 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 */
-+
-+/* Check signing status of name.
-+ returns:
-+ STAT_SECURE zone is signed.
-+ STAT_INSECURE zone proved unsigned.
-+ STAT_NEED_DS require DS record of name returned in keyname.
-+
-+ name returned unaltered.
-+*/
-+static int zone_status(char *name, int class, char *keyname, time_t now)
-+{
-+ int name_start = strlen(name);
-+ struct crec *crecp;
-+ char *p;
-+
-+ while (1)
-+ {
-+ strcpy(keyname, &name[name_start]);
-+
-+ if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS)))
-+ return STAT_NEED_DS;
-+ else
-+ do
-+ {
-+ if (crecp->uid == (unsigned int)class)
-+ {
-+ /* F_DNSSECOK misused in DS cache records to non-existance of NS record.
-+ F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here,
-+ but that's because there's no NS record either, ie this isn't the start
-+ of a zone. We only prove that the DNS tree below a node is unsigned when
-+ we prove that we're at a zone cut AND there's no DS record.
-+ */
-+ if (crecp->flags & F_NEG)
-+ {
-+ if (crecp->flags & F_DNSSECOK)
-+ return STAT_INSECURE; /* proved no DS here */
-+ }
-+ else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo))
-+ return STAT_INSECURE; /* algo we can't use - insecure */
-+ }
-+ }
-+ while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
-+
-+ if (name_start == 0)
-+ break;
-+
-+ for (p = &name[name_start-2]; (*p != '.') && (p != name); p--);
-+
-+ if (p != name)
-+ p++;
-+
-+ name_start = p - name;
-+ }
-+
-+ return STAT_SECURE;
-+}
-+
-+/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3)
-+ Return code:
-+ STAT_SECURE if it validates.
-+ STAT_INSECURE at least one RRset not validated, because in unsigned zone.
-+ STAT_BOGUS signature is wrong, bad packet, no validation where there should be.
-+ STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname, class in *class)
-+ STAT_NEED_DS need DS to complete validation (name is returned in keyname)
-+*/
- 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 *class, int check_unsigned, int *neganswer, int *nons)
- {
-- unsigned char *ans_start, *qname, *p1, *p2, **nsecs;
-- int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype;
-- int i, j, rc, nsec_count, cname_count = CNAME_CHAIN;
-- int nsec_type = 0, have_answer = 0;
-+ static unsigned char **targets = NULL;
-+ static int target_sz = 0;
-+
-+ unsigned char *ans_start, *p1, *p2, **nsecs;
-+ int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype, targetidx;
-+ int i, j, rc, nsec_count;
-+ int nsec_type;
-
- if (neganswer)
- *neganswer = 0;
-@@ -1833,70 +1949,51 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- if (RCODE(header) != NXDOMAIN && RCODE(header) != NOERROR)
- return STAT_INSECURE;
-
-- qname = p1 = (unsigned char *)(header+1);
-+ p1 = (unsigned char *)(header+1);
-
-+ /* Find all the targets we're looking for answers to.
-+ The zeroth array element is for the query, subsequent ones
-+ for CNAME targets, unless the query is for a CNAME. */
-+
-+ if (!expand_workspace(&targets, &target_sz, 0))
-+ return STAT_BOGUS;
-+
-+ targets[0] = p1;
-+ targetidx = 1;
-+
- if (!extract_name(header, plen, &p1, name, 1, 4))
- return STAT_BOGUS;
--
-+
- GETSHORT(qtype, p1);
- GETSHORT(qclass, p1);
- ans_start = p1;
--
-- if (qtype == T_ANY)
-- have_answer = 1;
-
-- /* Can't validate an RRISG query */
-+ /* Can't validate an RRSIG query */
- if (qtype == T_RRSIG)
- return STAT_INSECURE;
--
-- cname_loop:
-- for (j = ntohs(header->ancount); j != 0; j--)
-- {
-- /* leave pointer to missing name in qname */
--
-- if (!(rc = extract_name(header, plen, &p1, name, 0, 10)))
-- return STAT_BOGUS; /* bad packet */
--
-- GETSHORT(type2, p1);
-- GETSHORT(class2, p1);
-- p1 += 4; /* TTL */
-- GETSHORT(rdlen2, p1);
--
-- if (rc == 1 && qclass == class2)
-- {
-- /* Do we have an answer for the question? */
-- if (type2 == qtype)
-- {
-- have_answer = 1;
-- break;
-- }
-- else if (type2 == T_CNAME)
-- {
-- qname = p1;
--
-- /* looped CNAMES */
-- if (!cname_count-- || !extract_name(header, plen, &p1, name, 1, 0))
-- return STAT_BOGUS;
--
-- p1 = ans_start;
-- goto cname_loop;
-- }
-- }
--
-- if (!ADD_RDLEN(header, p1, plen, rdlen2))
-- return STAT_BOGUS;
-- }
--
-- if (neganswer && !have_answer)
-- *neganswer = 1;
-
-- /* No data, therefore no sigs */
-- if (ntohs(header->ancount) + ntohs(header->nscount) == 0)
-- {
-- *keyname = 0;
-- return STAT_NO_SIG;
-- }
--
-+ if (qtype != T_CNAME)
-+ for (j = ntohs(header->ancount); j != 0; j--)
-+ {
-+ if (!(p1 = skip_name(p1, header, plen, 10)))
-+ return STAT_BOGUS; /* bad packet */
-+
-+ GETSHORT(type2, p1);
-+ p1 += 6; /* class, TTL */
-+ GETSHORT(rdlen2, p1);
-+
-+ if (type2 == T_CNAME)
-+ {
-+ if (!expand_workspace(&targets, &target_sz, targetidx))
-+ return STAT_BOGUS;
-+
-+ targets[targetidx++] = p1; /* pointer to target name */
-+ }
-+
-+ if (!ADD_RDLEN(header, p1, plen, rdlen2))
-+ return STAT_BOGUS;
-+ }
-+
- for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++)
- {
- if (!extract_name(header, plen, &p1, name, 1, 10))
-@@ -1931,7 +2028,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- /* Not done, validate now */
- if (j == i)
- {
-- int ttl, keytag, algo, digest, type_covered;
-+ int ttl, keytag, algo, digest, type_covered, sigcnt, rrcnt;
- unsigned char *psave;
- struct all_addr a;
- struct blockdata *key;
-@@ -1939,143 +2036,186 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- char *wildname;
- int have_wildcard = 0;
-
-- rc = validate_rrset(now, header, plen, class1, type1, name, keyname, &wildname, NULL, 0, 0, 0);
--
-- if (rc == STAT_SECURE_WILDCARD)
-- {
-- have_wildcard = 1;
--
-- /* An attacker replay a wildcard answer with a different
-- answer and overlay a genuine RR. To prove this
-- hasn't happened, the answer must prove that
-- the gennuine record doesn't exist. Check that here. */
-- if (!nsec_type && !(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1)))
-- 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, NULL);
-- else
-- rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename,
-- keyname, name, type1, wildname, NULL);
--
-- if (rc != STAT_SECURE)
-- return rc;
-- }
-- else if (rc != STAT_SECURE)
-- {
-- if (class)
-- *class = class1; /* Class for DS or DNSKEY */
-+ if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
-+ return STAT_BOGUS;
-
-- if (rc == STAT_NO_SIG)
-+ /* No signatures for RRset. We can be configured to assume this is OK and return a INSECURE result. */
-+ if (sigcnt == 0)
-+ {
-+ if (check_unsigned)
- {
-- /* If we dropped off the end of a CNAME chain, return
-- STAT_NO_SIG and the last name is keyname. This is used for proving non-existence
-- if DS records in CNAME chains. */
-- if (cname_count == CNAME_CHAIN || i < ntohs(header->ancount))
-- /* No CNAME chain, or no sig in answer section, return empty name. */
-- *keyname = 0;
-- else if (!extract_name(header, plen, &qname, keyname, 1, 0))
-- return STAT_BOGUS;
-+ rc = zone_status(name, class1, keyname, now);
-+ if (rc == STAT_SECURE)
-+ rc = STAT_BOGUS;
-+ if (class)
-+ *class = class1; /* Class for NEED_DS or NEED_DNSKEY */
- }
--
-+ else
-+ rc = STAT_INSECURE;
-+
- return rc;
- }
-
-- /* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */
-- cache_start_insert();
-+ /* explore_rrset() gives us key name from sigs in keyname.
-+ Can't overwrite name here. */
-+ strcpy(daemon->workspacename, keyname);
-+ rc = zone_status(daemon->workspacename, class1, keyname, now);
-+ if (rc != STAT_SECURE)
-+ {
-+ /* Zone is insecure, don't need to validate RRset */
-+ if (class)
-+ *class = class1; /* Class for NEED_DS or NEED_DNSKEY */
-+ return rc;
-+ }
-+
-+ rc = validate_rrset(now, header, plen, class1, type1, sigcnt, rrcnt, name, keyname, &wildname, NULL, 0, 0, 0);
-
-- for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++)
-+ if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
- {
-- if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
-- return STAT_BOGUS; /* bad packet */
-+ if (class)
-+ *class = class1; /* Class for DS or DNSKEY */
-+ return rc;
-+ }
-+ else
-+ {
-+ /* rc is now STAT_SECURE or STAT_SECURE_WILDCARD */
-+
-+ /* Note if we've validated either the answer to the question
-+ or the target of a CNAME. Any not noted will need NSEC or
-+ to be in unsigned space. */
-+
-+ for (j = 0; j <targetidx; j++)
-+ if ((p2 = targets[j]))
-+ {
-+ if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
-+ return STAT_BOGUS; /* bad packet */
-+
-+ if (class1 == qclass && rc == 1 && (type1 == T_CNAME || type1 == qtype || qtype == T_ANY ))
-+ targets[j] = NULL;
-+ }
-+
-+ if (rc == STAT_SECURE_WILDCARD)
-+ {
-+ have_wildcard = 1;
-
-- GETSHORT(type2, p2);
-- GETSHORT(class2, p2);
-- GETLONG(ttl, p2);
-- GETSHORT(rdlen2, p2);
--
-- if (!CHECK_LEN(header, p2, plen, rdlen2))
-- return STAT_BOGUS; /* bad packet */
--
-- if (class2 == class1 && rc == 1)
-- {
-- psave = p2;
-+ /* An attacker replay a wildcard answer with a different
-+ answer and overlay a genuine RR. To prove this
-+ hasn't happened, the answer must prove that
-+ the gennuine record doesn't exist. Check that here. */
-+ if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1)))
-+ return STAT_BOGUS; /* No NSECs or bad packet */
-+
-+ /* Note that we may not yet have validated the NSEC/NSEC3 RRsets. Since the check
-+ below returns either SECURE or BOGUS, that's not a problem. If the RRsets later fail
-+ we'll return BOGUS then. */
-
-- if (type1 == T_DS && type2 == T_DS)
-- {
-- if (rdlen2 < 4)
-- return STAT_BOGUS; /* bad packet */
--
-- GETSHORT(keytag, p2);
-- algo = *p2++;
-- digest = *p2++;
--
-- /* Cache needs to known class for DNSSEC stuff */
-- a.addr.dnssec.class = class2;
--
-- if ((key = blockdata_alloc((char*)p2, rdlen2 - 4)))
-- {
-- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)))
-- blockdata_free(key);
-- else
-- {
-- a.addr.keytag = keytag;
-- 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;
-- crecp->addr.ds.keytag = keytag;
-- crecp->addr.ds.keylen = rdlen2 - 4;
-- }
-- }
-- }
-- else if (type2 == T_RRSIG)
-- {
-- if (rdlen2 < 18)
-- return STAT_BOGUS; /* bad packet */
-+ if (nsec_type == T_NSEC)
-+ 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, NULL);
-+
-+ if (rc == STAT_BOGUS)
-+ return rc;
-+ }
-+
-+ /* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */
-+ /* Also note if the RRset is the answer to the question, or the target of a CNAME */
-+ cache_start_insert();
-+
-+ for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++)
-+ {
-+ if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
-+ return STAT_BOGUS; /* bad packet */
-+
-+ GETSHORT(type2, p2);
-+ GETSHORT(class2, p2);
-+ GETLONG(ttl, p2);
-+ GETSHORT(rdlen2, p2);
-+
-+ if (!CHECK_LEN(header, p2, plen, rdlen2))
-+ return STAT_BOGUS; /* bad packet */
-+
-+ if (class2 == class1 && rc == 1)
-+ {
-+ psave = p2;
-
-- GETSHORT(type_covered, p2);
--
-- if (type_covered == type1 &&
-- (type_covered == T_A || type_covered == T_AAAA ||
-- type_covered == T_CNAME || type_covered == T_DS ||
-- type_covered == T_DNSKEY || type_covered == T_PTR))
-+ if (type1 == T_DS && type2 == T_DS)
- {
-- a.addr.dnssec.type = type_covered;
-- a.addr.dnssec.class = class1;
-+ if (rdlen2 < 4)
-+ return STAT_BOGUS; /* bad packet */
-
-- algo = *p2++;
-- p2 += 13; /* labels, orig_ttl, expiration, inception */
- GETSHORT(keytag, p2);
-+ algo = *p2++;
-+ digest = *p2++;
-+
-+ /* Cache needs to known class for DNSSEC stuff */
-+ a.addr.dnssec.class = class2;
-
-- /* We don't cache sigs for wildcard answers, because to reproduce the
-- answer from the cache will require one or more NSEC/NSEC3 records
-- which we don't cache. The lack of the RRSIG ensures that a query for
-- this RRset asking for a secure answer will always be forwarded. */
-- if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2)))
-+ if ((key = blockdata_alloc((char*)p2, rdlen2 - 4)))
- {
-- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS)))
-+ if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)))
- blockdata_free(key);
- else
- {
-- crecp->addr.sig.keydata = key;
-- crecp->addr.sig.keylen = rdlen2;
-- crecp->addr.sig.keytag = keytag;
-- crecp->addr.sig.type_covered = type_covered;
-- crecp->addr.sig.algo = algo;
-+ a.addr.keytag = keytag;
-+ 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;
-+ crecp->addr.ds.keytag = keytag;
-+ crecp->addr.ds.keylen = rdlen2 - 4;
-+ }
-+ }
-+ }
-+ else if (type2 == T_RRSIG)
-+ {
-+ if (rdlen2 < 18)
-+ return STAT_BOGUS; /* bad packet */
-+
-+ GETSHORT(type_covered, p2);
-+
-+ if (type_covered == type1 &&
-+ (type_covered == T_A || type_covered == T_AAAA ||
-+ type_covered == T_CNAME || type_covered == T_DS ||
-+ type_covered == T_DNSKEY || type_covered == T_PTR))
-+ {
-+ a.addr.dnssec.type = type_covered;
-+ a.addr.dnssec.class = class1;
-+
-+ algo = *p2++;
-+ p2 += 13; /* labels, orig_ttl, expiration, inception */
-+ GETSHORT(keytag, p2);
-+
-+ /* We don't cache sigs for wildcard answers, because to reproduce the
-+ answer from the cache will require one or more NSEC/NSEC3 records
-+ which we don't cache. The lack of the RRSIG ensures that a query for
-+ this RRset asking for a secure answer will always be forwarded. */
-+ if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2)))
-+ {
-+ if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS)))
-+ blockdata_free(key);
-+ else
-+ {
-+ crecp->addr.sig.keydata = key;
-+ crecp->addr.sig.keylen = rdlen2;
-+ crecp->addr.sig.keytag = keytag;
-+ crecp->addr.sig.type_covered = type_covered;
-+ crecp->addr.sig.algo = algo;
-+ }
- }
- }
- }
-+
-+ p2 = psave;
- }
-
-- p2 = psave;
-+ if (!ADD_RDLEN(header, p2, plen, rdlen2))
-+ return STAT_BOGUS; /* bad packet */
- }
-
-- if (!ADD_RDLEN(header, p2, plen, rdlen2))
-- return STAT_BOGUS; /* bad packet */
-+ cache_end_insert();
- }
--
-- cache_end_insert();
- }
- }
-
-@@ -2083,143 +2223,49 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- return STAT_BOGUS;
- }
-
-- /* OK, all the RRsets validate, now see if we have a NODATA or NXDOMAIN reply */
-- if (have_answer)
-- return STAT_SECURE;
--
-- /* NXDOMAIN or NODATA reply, prove that (name, class1, type1) can't exist */
-- /* First marshall the NSEC records, if we've not done it previously */
-- if (!nsec_type && !(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass)))
-- {
-- /* No NSEC records. If we dropped off the end of a CNAME chain, return
-- STAT_NO_SIG and the last name is keyname. This is used for proving non-existence
-- if DS records in CNAME chains. */
-- if (cname_count == CNAME_CHAIN) /* No CNAME chain, return empty name. */
-- *keyname = 0;
-- else if (!extract_name(header, plen, &qname, keyname, 1, 0))
-- return STAT_BOGUS;
-- return STAT_NO_SIG; /* No NSECs, this is probably a dangling CNAME pointing into
-- an unsigned zone. Return STAT_NO_SIG to cause this to be proved. */
-- }
--
-- /* Get name of missing answer */
-- if (!extract_name(header, plen, &qname, name, 1, 0))
-- return STAT_BOGUS;
--
-- if (nsec_type == T_NSEC)
-- 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, nons);
--}
--
--/* Chase the CNAME chain in the packet until the first record which _doesn't validate.
-- Needed for proving answer in unsigned space.
-- Return STAT_NEED_*
-- STAT_BOGUS - error
-- STAT_INSECURE - name of first non-secure record in name
--*/
--int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname)
--{
-- unsigned char *p = (unsigned char *)(header+1);
-- int type, class, qclass, rdlen, j, rc;
-- int cname_count = CNAME_CHAIN;
-- char *wildname;
--
-- /* Get question */
-- if (!extract_name(header, plen, &p, name, 1, 4))
-- return STAT_BOGUS;
--
-- p +=2; /* type */
-- GETSHORT(qclass, p);
--
-- while (1)
-- {
-- for (j = ntohs(header->ancount); j != 0; j--)
-- {
-- if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
-- return STAT_BOGUS; /* bad packet */
--
-- GETSHORT(type, p);
-- GETSHORT(class, p);
-- p += 4; /* TTL */
-- GETSHORT(rdlen, p);
--
-- /* Not target, loop */
-- if (rc == 2 || qclass != class)
-- {
-- if (!ADD_RDLEN(header, p, plen, rdlen))
-- return STAT_BOGUS;
-- continue;
-- }
--
-- /* Got to end of CNAME chain. */
-- if (type != T_CNAME)
-- return STAT_INSECURE;
--
-- /* validate CNAME chain, return if insecure or need more data */
-- rc = validate_rrset(now, header, plen, class, type, name, keyname, &wildname, NULL, 0, 0, 0);
--
-- if (rc == STAT_SECURE_WILDCARD)
-- {
-- int nsec_type, nsec_count, i;
-- unsigned char **nsecs;
--
-- /* An attacker can replay a wildcard answer with a different
-- answer and overlay a genuine RR. To prove this
-- hasn't happened, the answer must prove that
-- the genuine record doesn't exist. Check that here. */
-- if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class)))
-- return STAT_BOGUS; /* No NSECs or bad packet */
--
-- /* Note that we're called here because something didn't validate in validate_reply,
-- so we can't assume that any NSEC records have been validated. We do them by steam here */
--
-- for (i = 0; i < nsec_count; i++)
-- {
-- unsigned char *p1 = nsecs[i];
--
-- if (!extract_name(header, plen, &p1, daemon->workspacename, 1, 0))
-- return STAT_BOGUS;
--
-- rc = validate_rrset(now, header, plen, class, nsec_type, daemon->workspacename, keyname, NULL, NULL, 0, 0, 0);
-+ /* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
-+ for (j = 0; j <targetidx; j++)
-+ if ((p2 = targets[j]))
-+ {
-+ if (neganswer)
-+ *neganswer = 1;
-
-- /* NSECs can't be wildcards. */
-- if (rc == STAT_SECURE_WILDCARD)
-- rc = STAT_BOGUS;
-+ if (!extract_name(header, plen, &p2, name, 1, 10))
-+ return STAT_BOGUS; /* bad packet */
-+
-+ /* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
-
-- if (rc != STAT_SECURE)
-+ /* For anything other than a DS record, this situation is OK if either
-+ the answer is in an unsigned zone, or there's a NSEC records. */
-+ if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass)))
-+ {
-+ /* Empty DS without NSECS */
-+ if (qtype == T_DS)
-+ return STAT_BOGUS;
-+ else
-+ {
-+ rc = zone_status(name, qclass, keyname, now);
-+ if (rc != STAT_SECURE)
-+ {
-+ if (class)
-+ *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
- return rc;
-- }
--
-- if (nsec_type == T_NSEC)
-- rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type, NULL);
-- else
-- rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename,
-- keyname, name, type, wildname, NULL);
--
-- if (rc != STAT_SECURE)
-- return rc;
-- }
--
-- if (rc != STAT_SECURE)
-- {
-- if (rc == STAT_NO_SIG)
-- rc = STAT_INSECURE;
-- return rc;
-- }
-+ }
-+
-+ return STAT_BOGUS; /* signed zone, no NSECs */
-+ }
-+ }
-
-- /* Loop down CNAME chain/ */
-- if (!cname_count-- ||
-- !extract_name(header, plen, &p, name, 1, 0) ||
-- !(p = skip_questions(header, plen)))
-- return STAT_BOGUS;
--
-- break;
-- }
-+ if (nsec_type == T_NSEC)
-+ rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons);
-+ else
-+ rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons);
-
-- /* End of CNAME chain */
-- return STAT_INSECURE;
-- }
-+ if (rc != STAT_SECURE)
-+ return rc;
-+ }
-+
-+ return STAT_SECURE;
- }
-
-
-diff --git a/src/forward.c b/src/forward.c
-index b76a974..dd22a62 100644
---- a/src/forward.c
-+++ b/src/forward.c
-@@ -23,15 +23,6 @@ static struct frec *lookup_frec_by_sender(unsigned short id,
- static unsigned short get_id(void);
- 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(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
--
--
- /* Send a UDP packet with its source address set as "source"
- unless nowild is true, when we just send it with the kernel default */
- int send_from(int fd, int nowild, char *packet, size_t len,
-@@ -825,236 +816,142 @@ void reply_query(int fd, int family, time_t now)
- #ifdef HAVE_DNSSEC
- if (server && option_bool(OPT_DNSSEC_VALID) && !(forward->flags & FREC_CHECKING_DISABLED))
- {
-- int status;
-+ int status = 0;
-
- /* We've had a reply already, which we're validating. Ignore this duplicate */
- if (forward->blocking_query)
- return;
--
-- if (header->hb3 & HB3_TC)
-- {
-- /* Truncated answer can't be validated.
-+
-+ /* Truncated answer can't be validated.
- If this is an answer to a DNSSEC-generated query, we still
- need to get the client to retry over TCP, so return
- an answer with the TC bit set, even if the actual answer fits.
- */
-- status = STAT_TRUNCATED;
-- }
-- else if (forward->flags & FREC_DNSKEY_QUERY)
-- status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
-- else if (forward->flags & FREC_DS_QUERY)
-- {
-- status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
-- /* Provably no DS, everything below is insecure, even if signatures are offered */
-- if (status == STAT_NO_DS)
-- /* We only cache sigs when we've validated a reply.
-- Avoid caching a reply with sigs if there's a vaildated break in the
-- DS chain, so we don't return replies from cache missing sigs. */
-- status = STAT_INSECURE_DS;
-- else if (status == STAT_NO_SIG)
-- {
-- if (option_bool(OPT_DNSSEC_NO_SIGN))
-- {
-- status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
-- if (status == STAT_INSECURE)
-- status = STAT_INSECURE_DS;
-- }
-- else
-- status = STAT_INSECURE_DS;
-- }
-- else if (status == STAT_NO_NS)
-- status = STAT_BOGUS;
-- }
-- else if (forward->flags & FREC_CHECK_NOSIGN)
-- {
-- 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
-+ if (header->hb3 & HB3_TC)
-+ status = STAT_TRUNCATED;
-+
-+ while (1)
- {
-- status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL, NULL);
-- if (status == STAT_NO_SIG)
-+ /* As soon as anything returns BOGUS, we stop and unwind, to do otherwise
-+ would invite infinite loops, since the answers to DNSKEY and DS queries
-+ will not be cached, so they'll be repeated. */
-+ if (status != STAT_BOGUS && status != STAT_TRUNCATED && status != STAT_ABANDONED)
- {
-- if (option_bool(OPT_DNSSEC_NO_SIGN))
-- status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
-+ if (forward->flags & FREC_DNSKEY_QUERY)
-+ status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
-+ else if (forward->flags & FREC_DS_QUERY)
-+ status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
- else
-- status = STAT_INSECURE;
-+ status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class,
-+ option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL);
- }
-- }
-- /* Can't validate, as we're missing key data. Put this
-- answer aside, whilst we get that. */
-- if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
-- {
-- struct frec *new, *orig;
--
-- /* Free any saved query */
-- if (forward->stash)
-- blockdata_free(forward->stash);
--
-- /* Now save reply pending receipt of key data */
-- if (!(forward->stash = blockdata_alloc((char *)header, n)))
-- return;
-- forward->stash_len = n;
-
-- anotherkey:
-- /* Find the original query that started it all.... */
-- for (orig = forward; orig->dependent; orig = orig->dependent);
--
-- if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1)))
-- status = STAT_INSECURE;
-- else
-+ /* Can't validate, as we're missing key data. Put this
-+ answer aside, whilst we get that. */
-+ if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
- {
-- int fd;
-- struct frec *next = new->next;
-- *new = *forward; /* copy everything, then overwrite */
-- new->next = next;
-- new->blocking_query = NULL;
-- new->sentto = server;
-- new->rfd4 = NULL;
-- new->orig_domain = NULL;
--#ifdef HAVE_IPV6
-- new->rfd6 = NULL;
--#endif
-- new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_CHECK_NOSIGN);
-+ struct frec *new, *orig;
-
-- new->dependent = forward; /* to find query awaiting new one. */
-- forward->blocking_query = new; /* for garbage cleaning */
-- /* validate routines leave name of required record in daemon->keyname */
-- if (status == STAT_NEED_KEY)
-- {
-- new->flags |= FREC_DNSKEY_QUERY;
-- nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
-- daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
-- }
-- else
-- {
-- if (status == STAT_NEED_DS_NEG)
-- new->flags |= FREC_CHECK_NOSIGN;
-- else
-- new->flags |= FREC_DS_QUERY;
-- nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
-- daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
-- }
-- if ((hash = hash_questions(header, nn, daemon->namebuff)))
-- memcpy(new->hash, hash, HASH_SIZE);
-- new->new_id = get_id();
-- header->id = htons(new->new_id);
-- /* Save query for retransmission */
-- if (!(new->stash = blockdata_alloc((char *)header, nn)))
-+ /* Free any saved query */
-+ if (forward->stash)
-+ blockdata_free(forward->stash);
-+
-+ /* Now save reply pending receipt of key data */
-+ if (!(forward->stash = blockdata_alloc((char *)header, n)))
- return;
--
-- new->stash_len = nn;
-+ forward->stash_len = n;
-
-- /* Don't resend this. */
-- daemon->srv_save = NULL;
-+ /* Find the original query that started it all.... */
-+ for (orig = forward; orig->dependent; orig = orig->dependent);
-
-- if (server->sfd)
-- fd = server->sfd->fd;
-+ if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1)))
-+ status = STAT_ABANDONED;
- else
- {
-- fd = -1;
-+ int fd;
-+ struct frec *next = new->next;
-+ *new = *forward; /* copy everything, then overwrite */
-+ new->next = next;
-+ new->blocking_query = NULL;
-+ new->sentto = server;
-+ new->rfd4 = NULL;
- #ifdef HAVE_IPV6
-- if (server->addr.sa.sa_family == AF_INET6)
-+ new->rfd6 = NULL;
-+#endif
-+ new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
-+
-+ new->dependent = forward; /* to find query awaiting new one. */
-+ forward->blocking_query = new; /* for garbage cleaning */
-+ /* validate routines leave name of required record in daemon->keyname */
-+ if (status == STAT_NEED_KEY)
-+ {
-+ new->flags |= FREC_DNSKEY_QUERY;
-+ nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
-+ daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
-+ }
-+ else
- {
-- if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
-- fd = new->rfd6->fd;
-+ new->flags |= FREC_DS_QUERY;
-+ nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
-+ daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
- }
-+ if ((hash = hash_questions(header, nn, daemon->namebuff)))
-+ memcpy(new->hash, hash, HASH_SIZE);
-+ new->new_id = get_id();
-+ header->id = htons(new->new_id);
-+ /* Save query for retransmission */
-+ new->stash = blockdata_alloc((char *)header, nn);
-+ new->stash_len = nn;
-+
-+ /* Don't resend this. */
-+ daemon->srv_save = NULL;
-+
-+ if (server->sfd)
-+ fd = server->sfd->fd;
- else
-+ {
-+ fd = -1;
-+#ifdef HAVE_IPV6
-+ if (server->addr.sa.sa_family == AF_INET6)
-+ {
-+ if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
-+ fd = new->rfd6->fd;
-+ }
-+ else
- #endif
-+ {
-+ if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
-+ fd = new->rfd4->fd;
-+ }
-+ }
-+
-+ if (fd != -1)
- {
-- if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
-- fd = new->rfd4->fd;
-+ while (retry_send(sendto(fd, (char *)header, nn, 0,
-+ &server->addr.sa,
-+ sa_len(&server->addr))));
-+ server->queries++;
- }
-- }
--
-- if (fd != -1)
-- {
-- while (retry_send(sendto(fd, (char *)header, nn, 0,
-- &server->addr.sa,
-- sa_len(&server->addr))));
-- server->queries++;
-- }
--
-+ }
- return;
- }
-- }
-
-- /* Ok, we reached far enough up the chain-of-trust that we can validate something.
-- Now wind back down, pulling back answers which wouldn't previously validate
-- and validate them with the new data. Note that if an answer needs multiple
-- keys to validate, we may find another key is needed, in which case we set off
-- down another branch of the tree. Once we get to the original answer
-- (FREC_DNSSEC_QUERY not set) and it validates, return it to the original requestor. */
-- while (forward->dependent)
-- {
-+ /* Validated original answer, all done. */
-+ if (!forward->dependent)
-+ break;
-+
-+ /* validated subsdiary query, (and cached result)
-+ pop that and return to the previous query we were working on. */
- struct frec *prev = forward->dependent;
- free_frec(forward);
- forward = prev;
- forward->blocking_query = NULL; /* already gone */
- blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
- n = forward->stash_len;
--
-- if (status == STAT_SECURE)
-- {
-- if (forward->flags & FREC_DNSKEY_QUERY)
-- status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
-- else if (forward->flags & FREC_DS_QUERY)
-- {
-- status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
-- /* Provably no DS, everything below is insecure, even if signatures are offered */
-- if (status == STAT_NO_DS)
-- /* We only cache sigs when we've validated a reply.
-- Avoid caching a reply with sigs if there's a vaildated break in the
-- DS chain, so we don't return replies from cache missing sigs. */
-- status = STAT_INSECURE_DS;
-- else if (status == STAT_NO_SIG)
-- {
-- if (option_bool(OPT_DNSSEC_NO_SIGN))
-- {
-- status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
-- if (status == STAT_INSECURE)
-- status = STAT_INSECURE_DS;
-- }
-- else
-- status = STAT_INSECURE_DS;
-- }
-- else if (status == STAT_NO_NS)
-- status = STAT_BOGUS;
-- }
-- else if (forward->flags & FREC_CHECK_NOSIGN)
-- {
-- 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, NULL);
-- if (status == STAT_NO_SIG)
-- {
-- if (option_bool(OPT_DNSSEC_NO_SIGN))
-- status = send_check_sign(forward, now, header, n, daemon->namebuff, daemon->keyname);
-- else
-- status = STAT_INSECURE;
-- }
-- }
--
-- if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
-- goto anotherkey;
-- }
- }
-+
-
- no_cache_dnssec = 0;
--
-- if (status == STAT_INSECURE_DS)
-- {
-- /* We only cache sigs when we've validated a reply.
-- Avoid caching a reply with sigs if there's a vaildated break in the
-- DS chain, so we don't return replies from cache missing sigs. */
-- status = STAT_INSECURE;
-- no_cache_dnssec = 1;
-- }
-
- if (status == STAT_TRUNCATED)
- header->hb3 |= HB3_TC;
-@@ -1062,7 +959,7 @@ void reply_query(int fd, int family, time_t now)
- {
- char *result, *domain = "result";
-
-- if (forward->work_counter == 0)
-+ if (status == STAT_ABANDONED)
- {
- result = "ABANDONED";
- status = STAT_BOGUS;
-@@ -1072,7 +969,7 @@ void reply_query(int fd, int family, time_t now)
-
- if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
- domain = daemon->namebuff;
--
-+
- log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
- }
-
-@@ -1415,315 +1312,49 @@ void receive_query(struct listener *listen, time_t now)
- }
-
- #ifdef HAVE_DNSSEC
--
--/* 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(struct frec *forward, time_t now, struct dns_header *header, size_t plen,
-- char *name, char *keyname)
--{
-- 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->orig_domain)
-- forward = forward->dependent;
--
-- blockdata_retrieve(forward->orig_domain, forward->name_len, name);
--
-- while (1)
-- {
-- char *p;
--
-- if (status == 0)
-- {
-- 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 forward->dependent ? STAT_INSECURE_DS : STAT_INSECURE;
--
-- if (status == STAT_BOGUS)
-- return STAT_BOGUS;
--
-- if (status == STAT_NO_SIG && *keyname != 0)
-- {
-- /* There is a validated CNAME chain that doesn't end in a DS record. Start
-- the search again in that domain. */
-- blockdata_free(forward->orig_domain);
-- forward->name_start = strlen(keyname);
-- forward->name_len = forward->name_start + 1;
-- if (!(forward->orig_domain = blockdata_alloc(keyname, forward->name_len)))
-- return STAT_BOGUS;
--
-- strcpy(name, keyname);
-- status = 0; /* force to cache when we iterate. */
-- continue;
-- }
--
-- /* 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 */
--
-- if (forward->name_start == 0)
-- return STAT_BOGUS;
--
-- 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. */
-- }
--}
--
--/* Move down from the root, until we find a signed non-existance of a DS, in which case
-- an unsigned answer is OK, or we find a signed DS, in which case there should be
-- a signature, and the answer is BOGUS */
--static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, size_t plen, int class, char *name,
-- char *keyname, struct server *server, int *keycount)
--{
-- size_t m;
-- unsigned char *packet, *payload;
-- u16 *length;
-- 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);
-- if (status == STAT_BOGUS)
-- return STAT_BOGUS;
--
-- if (!(packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16))))
-- return STAT_BOGUS;
--
-- 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 c1, c2;
-- struct crec *crecp;
--
-- if (--(*keycount) == 0)
-- {
-- free(packet);
-- blockdata_free(block);
-- 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, server->edns_pktsz);
--
-- *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 (status == STAT_NO_DS)
-- {
-- /* Found a secure denial of DS - delegation is indeed insecure */
-- free(packet);
-- blockdata_free(block);
-- return STAT_INSECURE;
-- }
--
-- if (status == STAT_NO_SIG && *keyname != 0)
-- {
-- /* There is a validated CNAME chain that doesn't end in a DS record. Start
-- the search again in that domain. */
-- blockdata_free(block);
-- name_len = strlen(keyname) + 1;
-- name_start = name + name_len - 1;
--
-- if (!(block = blockdata_alloc(keyname, name_len)))
-- return STAT_BOGUS;
--
-- strcpy(name, keyname);
-- continue;
-- }
--
-- 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 */
-- }
-- }
--}
--
- 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)
- {
- /* Recurse up the key heirarchy */
- int new_status;
-+ unsigned char *packet = NULL;
-+ size_t m;
-+ unsigned char *payload = NULL;
-+ struct dns_header *new_header = NULL;
-+ u16 *length = NULL;
-+ unsigned char c1, c2;
-
-- /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
-- if (--(*keycount) == 0)
-- return STAT_INSECURE;
--
-- if (status == STAT_NEED_KEY)
-- new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
-- else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
-+ while (1)
- {
-- new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
-- if (status == STAT_NEED_DS)
-+ /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
-+ if (--(*keycount) == 0)
-+ new_status = STAT_ABANDONED;
-+ else if (status == STAT_NEED_KEY)
-+ new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
-+ else if (status == STAT_NEED_DS)
-+ new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
-+ else
-+ new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL);
-+
-+ if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY)
-+ break;
-+
-+ /* Can't validate because we need a key/DS whose name now in keyname.
-+ Make query for same, and recurse to validate */
-+ if (!packet)
- {
-- if (new_status == STAT_NO_DS)
-- new_status = STAT_INSECURE_DS;
-- if (new_status == STAT_NO_SIG)
-- {
-- if (option_bool(OPT_DNSSEC_NO_SIGN))
-- {
-- new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
-- if (new_status == STAT_INSECURE)
-- new_status = STAT_INSECURE_DS;
-- }
-- else
-- new_status = STAT_INSECURE_DS;
-- }
-- else if (new_status == STAT_NO_NS)
-- new_status = STAT_BOGUS;
-+ packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
-+ payload = &packet[2];
-+ new_header = (struct dns_header *)payload;
-+ length = (u16 *)packet;
- }
-- }
-- 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, NULL);
-
-- if (new_status == STAT_NO_SIG)
-+ if (!packet)
- {
-- if (option_bool(OPT_DNSSEC_NO_SIGN))
-- new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
-- else
-- new_status = STAT_INSECURE;
-+ new_status = STAT_ABANDONED;
-+ break;
- }
-- }
--
-- /* Can't validate because we need a key/DS whose name now in keyname.
-- Make query for same, and recurse to validate */
-- if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
-- {
-- size_t m;
-- unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
-- unsigned char *payload = &packet[2];
-- struct dns_header *new_header = (struct dns_header *)payload;
-- u16 *length = (u16 *)packet;
-- unsigned char c1, c2;
--
-- if (!packet)
-- return STAT_INSECURE;
--
-- another_tcp_key:
-+
- m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class,
- new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz);
-
-@@ -1733,65 +1364,22 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
- !read_write(server->tcpfd, &c1, 1, 1) ||
- !read_write(server->tcpfd, &c2, 1, 1) ||
- !read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
-- new_status = STAT_INSECURE;
-- else
- {
-- m = (c1 << 8) | c2;
--
-- new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount);
--
-- if (new_status == STAT_SECURE)
-- {
-- /* Reached a validated record, now try again at this level.
-- Note that we may get ANOTHER NEED_* if an answer needs more than one key.
-- If so, go round again. */
--
-- if (status == STAT_NEED_KEY)
-- new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
-- 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)
-- {
-- if (new_status == STAT_NO_DS)
-- new_status = STAT_INSECURE_DS;
-- else if (new_status == STAT_NO_SIG)
-- {
-- if (option_bool(OPT_DNSSEC_NO_SIGN))
-- {
-- new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
-- if (new_status == STAT_INSECURE)
-- new_status = STAT_INSECURE_DS;
-- }
-- else
-- new_status = STAT_INSECURE_DS;
-- }
-- else if (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, NULL);
--
-- if (new_status == STAT_NO_SIG)
-- {
-- if (option_bool(OPT_DNSSEC_NO_SIGN))
-- new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
-- else
-- new_status = STAT_INSECURE;
-- }
-- }
--
-- if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
-- goto another_tcp_key;
-- }
-+ new_status = STAT_ABANDONED;
-+ break;
- }
-+
-+ m = (c1 << 8) | c2;
-
-- free(packet);
-+ new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount);
-+
-+ if (new_status != STAT_OK)
-+ break;
- }
-+
-+ if (packet)
-+ free(packet);
-+
- return new_status;
- }
- #endif
-@@ -2075,19 +1663,10 @@ unsigned char *tcp_request(int confd, time_t now,
- if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
- {
- int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
-- int status = tcp_key_recurse(now, STAT_TRUNCATED, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
-+ int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
- char *result, *domain = "result";
--
-- if (status == STAT_INSECURE_DS)
-- {
-- /* We only cache sigs when we've validated a reply.
-- Avoid caching a reply with sigs if there's a vaildated break in the
-- DS chain, so we don't return replies from cache missing sigs. */
-- status = STAT_INSECURE;
-- no_cache_dnssec = 1;
-- }
-
-- if (keycount == 0)
-+ if (status == STAT_ABANDONED)
- {
- result = "ABANDONED";
- status = STAT_BOGUS;
-@@ -2179,7 +1758,6 @@ 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;
- }
-@@ -2248,12 +1826,6 @@ 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);
-@@ -2281,14 +1853,23 @@ struct frec *get_new_frec(time_t now, int *wait, int force)
- target = f;
- else
- {
-- if (difftime(now, f->time) >= 4*TIMEOUT)
-- {
-- free_frec(f);
-- target = f;
-- }
--
-- if (!oldest || difftime(f->time, oldest->time) <= 0)
-- oldest = f;
-+#ifdef HAVE_DNSSEC
-+ /* Don't free DNSSEC sub-queries here, as we may end up with
-+ dangling references to them. They'll go when their "real" query
-+ is freed. */
-+ if (!f->dependent)
-+#endif
-+ {
-+ if (difftime(now, f->time) >= 4*TIMEOUT)
-+ {
-+ free_frec(f);
-+ target = f;
-+ }
-+
-+
-+ if (!oldest || difftime(f->time, oldest->time) <= 0)
-+ oldest = f;
-+ }
- }
-
- if (target)
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/017-Abandon_caching_RRSIGs_and_returning_them_from_cache.patch b/src/patches/dnsmasq/017-Abandon_caching_RRSIGs_and_returning_them_from_cache.patch
deleted file mode 100644
index 5ffaf97..0000000
--- a/src/patches/dnsmasq/017-Abandon_caching_RRSIGs_and_returning_them_from_cache.patch
+++ /dev/null
@@ -1,612 +0,0 @@
-From 93be5b1e023b0c661e1ec2cd6d811a8ec9055c49 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Tue, 15 Dec 2015 12:04:40 +0000
-Subject: [PATCH] Abandon caching RRSIGs and returning them from cache.
-
-The list of exceptions to being able to locally answer
-cached data for validated records when DNSSEC data is requested
-was getting too long, so don't ever do that. This means
-that the cache no longer has to hold RRSIGS and allows
-us to lose lots of code. Note that cached validated
-answers are still returned as long as do=0
----
- src/cache.c | 38 ++---------
- src/dnsmasq.h | 10 +--
- src/dnssec.c | 94 ++++-----------------------
- src/rfc1035.c | 197 ++++++---------------------------------------------------
- 4 files changed, 42 insertions(+), 297 deletions(-)
-
-diff --git a/src/cache.c b/src/cache.c
-index 1b76b67..51ba7cc 100644
---- a/src/cache.c
-+++ b/src/cache.c
-@@ -189,12 +189,7 @@ static void cache_hash(struct crec *crecp)
- static void cache_blockdata_free(struct crec *crecp)
- {
- if (crecp->flags & F_DNSKEY)
-- {
-- if (crecp->flags & F_DS)
-- blockdata_free(crecp->addr.sig.keydata);
-- else
-- blockdata_free(crecp->addr.key.keydata);
-- }
-+ blockdata_free(crecp->addr.key.keydata);
- else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
- blockdata_free(crecp->addr.ds.keydata);
- }
-@@ -369,13 +364,8 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
- }
-
- #ifdef HAVE_DNSSEC
-- /* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also
-- type-covered sensitive for RRSIG */
-- if ((flags & (F_DNSKEY | F_DS)) &&
-- (flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) &&
-- crecp->uid == addr->addr.dnssec.class &&
-- (!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) ||
-- crecp->addr.sig.type_covered == addr->addr.dnssec.type))
-+ /* Deletion has to be class-sensitive for DS and DNSKEY */
-+ if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == addr->addr.dnssec.class)
- {
- if (crecp->flags & F_CONFIG)
- return crecp;
-@@ -532,13 +522,9 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
- struct all_addr free_addr = new->addr.addr;;
-
- #ifdef HAVE_DNSSEC
-- /* For DNSSEC records, addr holds class and type_covered for RRSIG */
-+ /* For DNSSEC records, addr holds class. */
- if (new->flags & (F_DS | F_DNSKEY))
-- {
-- free_addr.addr.dnssec.class = new->uid;
-- if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
-- free_addr.addr.dnssec.type = new->addr.sig.type_covered;
-- }
-+ free_addr.addr.dnssec.class = new->uid;
- #endif
-
- free_avail = 1; /* Must be free space now. */
-@@ -653,9 +639,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
- if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
- {
- if ((crecp->flags & F_FORWARD) &&
--#ifdef HAVE_DNSSEC
-- (((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
--#endif
- (crecp->flags & prot) &&
- hostname_isequal(cache_get_name(crecp), name))
- {
-@@ -713,9 +696,6 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
-
- if (ans &&
- (ans->flags & F_FORWARD) &&
--#ifdef HAVE_DNSSEC
-- (((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
--#endif
- (ans->flags & prot) &&
- hostname_isequal(cache_get_name(ans), name))
- return ans;
-@@ -1472,11 +1452,7 @@ void dump_cache(time_t now)
- #ifdef HAVE_DNSSEC
- else if (cache->flags & F_DS)
- {
-- if (cache->flags & F_DNSKEY)
-- /* RRSIG */
-- sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
-- cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
-- else if (!(cache->flags & F_NEG))
-+ if (!(cache->flags & F_NEG))
- sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
- cache->addr.ds.algo, cache->addr.ds.digest);
- }
-@@ -1502,8 +1478,6 @@ void dump_cache(time_t now)
- else if (cache->flags & F_CNAME)
- t = "C";
- #ifdef HAVE_DNSSEC
-- else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
-- t = "G"; /* DNSKEY and DS set -> RRISG */
- else if (cache->flags & F_DS)
- t = "S";
- else if (cache->flags & F_DNSKEY)
-diff --git a/src/dnsmasq.h b/src/dnsmasq.h
-index 023a1cf..4344cae 100644
---- a/src/dnsmasq.h
-+++ b/src/dnsmasq.h
-@@ -398,14 +398,9 @@ struct crec {
- unsigned char algo;
- unsigned char digest;
- } ds;
-- struct {
-- struct blockdata *keydata;
-- unsigned short keylen, type_covered, keytag;
-- char algo;
-- } sig;
- } addr;
- time_t ttd; /* time to die */
-- /* used as class if DNSKEY/DS/RRSIG, index to source for F_HOSTS */
-+ /* used as class if DNSKEY/DS, index to source for F_HOSTS */
- unsigned int uid;
- unsigned short flags;
- union {
-@@ -445,8 +440,7 @@ struct crec {
- #define F_SECSTAT (1u<<24)
- #define F_NO_RR (1u<<25)
- #define F_IPSET (1u<<26)
--#define F_NSIGMATCH (1u<<27)
--#define F_NOEXTRA (1u<<28)
-+#define F_NOEXTRA (1u<<27)
-
- /* Values of uid in crecs with F_CONFIG bit set. */
- #define SRC_INTERFACE 0
-diff --git a/src/dnssec.c b/src/dnssec.c
-index de7b335..1ae03a6 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -1004,7 +1004,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
- {
- unsigned char *psave, *p = (unsigned char *)(header+1);
- struct crec *crecp, *recp1;
-- int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag, type_covered;
-+ int rc, j, qtype, qclass, ttl, rdlen, flags, algo, valid, keytag;
- struct blockdata *key;
- struct all_addr a;
-
-@@ -1115,7 +1115,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
-
- if (valid)
- {
-- /* DNSKEY RRset determined to be OK, now cache it and the RRsigs that sign it. */
-+ /* DNSKEY RRset determined to be OK, now cache it. */
- cache_start_insert();
-
- p = skip_questions(header, plen);
-@@ -1155,7 +1155,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
- if ((key = blockdata_alloc((char*)p, rdlen - 4)))
- {
- if (!(recp1 = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK)))
-- blockdata_free(key);
-+ {
-+ blockdata_free(key);
-+ return STAT_BOGUS;
-+ }
- else
- {
- a.addr.keytag = keytag;
-@@ -1169,38 +1172,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
- }
- }
- }
-- else if (qtype == T_RRSIG)
-- {
-- /* RRSIG, cache if covers DNSKEY RRset */
-- if (rdlen < 18)
-- return STAT_BOGUS; /* bad packet */
--
-- GETSHORT(type_covered, p);
--
-- if (type_covered == T_DNSKEY)
-- {
-- a.addr.dnssec.class = class;
-- a.addr.dnssec.type = type_covered;
--
-- algo = *p++;
-- p += 13; /* labels, orig_ttl, expiration, inception */
-- GETSHORT(keytag, p);
-- if ((key = blockdata_alloc((char*)psave, rdlen)))
-- {
-- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS)))
-- blockdata_free(key);
-- else
-- {
-- crecp->addr.sig.keydata = key;
-- crecp->addr.sig.keylen = rdlen;
-- crecp->addr.sig.keytag = keytag;
-- crecp->addr.sig.type_covered = type_covered;
-- crecp->addr.sig.algo = algo;
-- }
-- }
-- }
-- }
--
-+
- p = psave;
- }
-
-@@ -1326,7 +1298,8 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
- cache_start_insert();
-
- a.addr.dnssec.class = class;
-- cache_insert(name, &a, now, ttl, flags);
-+ if (!cache_insert(name, &a, now, ttl, flags))
-+ return STAT_BOGUS;
-
- cache_end_insert();
-
-@@ -2028,14 +2001,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- /* Not done, validate now */
- if (j == i)
- {
-- int ttl, keytag, algo, digest, type_covered, sigcnt, rrcnt;
-+ int ttl, keytag, algo, digest, sigcnt, rrcnt;
- unsigned char *psave;
- struct all_addr a;
- struct blockdata *key;
- struct crec *crecp;
- char *wildname;
-- int have_wildcard = 0;
--
-+
- if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
- return STAT_BOGUS;
-
-@@ -2096,8 +2068,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
-
- if (rc == STAT_SECURE_WILDCARD)
- {
-- have_wildcard = 1;
--
- /* An attacker replay a wildcard answer with a different
- answer and overlay a genuine RR. To prove this
- hasn't happened, the answer must prove that
-@@ -2119,7 +2089,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- return rc;
- }
-
-- /* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */
-+ /* If we just validated a DS RRset, cache it */
- /* Also note if the RRset is the answer to the question, or the target of a CNAME */
- cache_start_insert();
-
-@@ -2168,45 +2138,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- }
- }
- }
-- else if (type2 == T_RRSIG)
-- {
-- if (rdlen2 < 18)
-- return STAT_BOGUS; /* bad packet */
--
-- GETSHORT(type_covered, p2);
--
-- if (type_covered == type1 &&
-- (type_covered == T_A || type_covered == T_AAAA ||
-- type_covered == T_CNAME || type_covered == T_DS ||
-- type_covered == T_DNSKEY || type_covered == T_PTR))
-- {
-- a.addr.dnssec.type = type_covered;
-- a.addr.dnssec.class = class1;
--
-- algo = *p2++;
-- p2 += 13; /* labels, orig_ttl, expiration, inception */
-- GETSHORT(keytag, p2);
--
-- /* We don't cache sigs for wildcard answers, because to reproduce the
-- answer from the cache will require one or more NSEC/NSEC3 records
-- which we don't cache. The lack of the RRSIG ensures that a query for
-- this RRset asking for a secure answer will always be forwarded. */
-- if (!have_wildcard && (key = blockdata_alloc((char*)psave, rdlen2)))
-- {
-- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DNSKEY | F_DS)))
-- blockdata_free(key);
-- else
-- {
-- crecp->addr.sig.keydata = key;
-- crecp->addr.sig.keylen = rdlen2;
-- crecp->addr.sig.keytag = keytag;
-- crecp->addr.sig.type_covered = type_covered;
-- crecp->addr.sig.algo = algo;
-- }
-- }
-- }
-- }
--
-+
- p2 = psave;
- }
-
-diff --git a/src/rfc1035.c b/src/rfc1035.c
-index 4eb1772..def8fa0 100644
---- a/src/rfc1035.c
-+++ b/src/rfc1035.c
-@@ -1275,11 +1275,9 @@ int check_for_local_domain(char *name, time_t now)
- struct naptr *naptr;
-
- /* Note: the call to cache_find_by_name is intended to find any record which matches
-- ie A, AAAA, CNAME, DS. Because RRSIG records are marked by setting both F_DS and F_DNSKEY,
-- cache_find_by name ordinarily only returns records with an exact match on those bits (ie
-- for the call below, only DS records). The F_NSIGMATCH bit changes this behaviour */
-+ ie A, AAAA, CNAME. */
-
-- if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_DS | F_NO_RR | F_NSIGMATCH)) &&
-+ if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME |F_NO_RR)) &&
- (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
- return 1;
-
-@@ -1566,9 +1564,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- GETSHORT(flags, pheader);
-
- if ((sec_reqd = flags & 0x8000))
-- *do_bit = 1;/* do bit */
-+ {
-+ *do_bit = 1;/* do bit */
-+ *ad_reqd = 1;
-+ }
-
-- *ad_reqd = 1;
- dryrun = 1;
- }
-
-@@ -1636,98 +1636,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- }
- }
-
--#ifdef HAVE_DNSSEC
-- if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS))
-- {
-- int gotone = 0;
-- struct blockdata *keydata;
--
-- /* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
-- if (sec_reqd)
-- {
-- crecp = NULL;
-- while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
-- if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype)
-- break;
-- }
--
-- if (!sec_reqd || crecp)
-- {
-- if (qtype == T_DS)
-- {
-- crecp = NULL;
-- while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
-- if (crecp->uid == qclass)
-- {
-- gotone = 1;
-- if (!dryrun)
-- {
-- if (crecp->flags & F_NEG)
-- {
-- if (crecp->flags & F_NXDOMAIN)
-- nxdomain = 1;
-- log_query(F_UPSTREAM, name, NULL, "no DS");
-- }
-- else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
-- {
-- struct all_addr a;
-- a.addr.keytag = crecp->addr.ds.keytag;
-- log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
-- if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
-- crec_ttl(crecp, now), &nameoffset,
-- T_DS, qclass, "sbbt",
-- crecp->addr.ds.keytag, crecp->addr.ds.algo,
-- crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
-- anscount++;
--
-- }
-- }
-- }
-- }
-- else /* DNSKEY */
-- {
-- crecp = NULL;
-- while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
-- if (crecp->uid == qclass)
-- {
-- gotone = 1;
-- if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
-- {
-- struct all_addr a;
-- a.addr.keytag = crecp->addr.key.keytag;
-- log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
-- if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
-- crec_ttl(crecp, now), &nameoffset,
-- T_DNSKEY, qclass, "sbbt",
-- crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
-- anscount++;
-- }
-- }
-- }
-- }
--
-- /* Now do RRSIGs */
-- if (gotone)
-- {
-- ans = 1;
-- auth = 0;
-- if (!dryrun && sec_reqd)
-- {
-- crecp = NULL;
-- while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
-- if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype &&
-- (keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
-- {
-- add_resource_record(header, limit, &trunc, nameoffset, &ansp,
-- crec_ttl(crecp, now), &nameoffset,
-- T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata);
-- anscount++;
-- }
-- }
-- }
-- }
--#endif
--
- if (qclass == C_IN)
- {
- struct txt_record *t;
-@@ -1736,6 +1644,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
- {
- ans = 1;
-+ sec_data = 0;
- if (!dryrun)
- {
- log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
-@@ -1792,6 +1701,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
-
- if (intr)
- {
-+ sec_data = 0;
- ans = 1;
- if (!dryrun)
- {
-@@ -1805,6 +1715,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- else if (ptr)
- {
- ans = 1;
-+ sec_data = 0;
- if (!dryrun)
- {
- log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
-@@ -1819,38 +1730,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- }
- else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
- {
-- if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
-- {
-- if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
-- crecp = NULL;
--#ifdef HAVE_DNSSEC
-- else if (crecp->flags & F_DNSSECOK)
-- {
-- int gotsig = 0;
-- struct crec *rr_crec = NULL;
--
-- while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
-- {
-- if (rr_crec->addr.sig.type_covered == T_PTR && rr_crec->uid == C_IN)
-- {
-- char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
-- gotsig = 1;
--
-- if (!dryrun &&
-- add_resource_record(header, limit, &trunc, nameoffset, &ansp,
-- rr_crec->ttd - now, &nameoffset,
-- T_RRSIG, C_IN, "t", crecp->addr.sig.keylen, sigdata))
-- anscount++;
-- }
-- }
--
-- if (!gotsig)
-- crecp = NULL;
-- }
--#endif
-- }
--
-- if (crecp)
-+ /* Don't use cache when DNSSEC data required. */
-+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
- {
- do
- {
-@@ -1860,19 +1741,19 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
-
- if (!(crecp->flags & F_DNSSECOK))
- sec_data = 0;
--
-+
-+ ans = 1;
-+
- if (crecp->flags & F_NEG)
- {
-- ans = 1;
- auth = 0;
- if (crecp->flags & F_NXDOMAIN)
- nxdomain = 1;
- if (!dryrun)
- log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
- }
-- else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
-+ else
- {
-- ans = 1;
- if (!(crecp->flags & (F_HOSTS | F_DHCP)))
- auth = 0;
- if (!dryrun)
-@@ -1892,6 +1773,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- else if (is_rev_synth(is_arpa, &addr, name))
- {
- ans = 1;
-+ sec_data = 0;
- if (!dryrun)
- {
- log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL);
-@@ -1908,6 +1790,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- {
- /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
- ans = 1;
-+ sec_data = 0;
- nxdomain = 1;
- if (!dryrun)
- log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
-@@ -1955,6 +1838,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- if (i == 4)
- {
- ans = 1;
-+ sec_data = 0;
- if (!dryrun)
- {
- addr.addr.addr4.s_addr = htonl(a);
-@@ -1993,6 +1877,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- continue;
- #endif
- ans = 1;
-+ sec_data = 0;
- if (!dryrun)
- {
- gotit = 1;
-@@ -2032,48 +1917,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- crecp = save;
- }
-
-- /* If the client asked for DNSSEC and we can't provide RRSIGs, either
-- because we've not doing DNSSEC or the cached answer is signed by negative,
-- don't answer from the cache, forward instead. */
-- if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && sec_reqd)
-- {
-- if (!option_bool(OPT_DNSSEC_VALID) || ((crecp->flags & F_NEG) && (crecp->flags & F_DNSSECOK)))
-- crecp = NULL;
--#ifdef HAVE_DNSSEC
-- else if (crecp->flags & F_DNSSECOK)
-- {
-- /* We're returning validated data, need to return the RRSIG too. */
-- struct crec *rr_crec = NULL;
-- int sigtype = type;
-- /* The signature may have expired even though the data is still in cache,
-- forward instead of answering from cache if so. */
-- int gotsig = 0;
--
-- if (crecp->flags & F_CNAME)
-- sigtype = T_CNAME;
--
-- while ((rr_crec = cache_find_by_name(rr_crec, name, now, F_DS | F_DNSKEY)))
-- {
-- if (rr_crec->addr.sig.type_covered == sigtype && rr_crec->uid == C_IN)
-- {
-- char *sigdata = blockdata_retrieve(rr_crec->addr.sig.keydata, rr_crec->addr.sig.keylen, NULL);
-- gotsig = 1;
--
-- if (!dryrun &&
-- add_resource_record(header, limit, &trunc, nameoffset, &ansp,
-- rr_crec->ttd - now, &nameoffset,
-- T_RRSIG, C_IN, "t", rr_crec->addr.sig.keylen, sigdata))
-- anscount++;
-- }
-- }
--
-- if (!gotsig)
-- crecp = NULL;
-- }
--#endif
-- }
--
-- if (crecp)
-+ /* If the client asked for DNSSEC don't use cached data. */
-+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
- do
- {
- /* don't answer wildcard queries with data not from /etc/hosts
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/018-Move_code_which_caches_DS_records_to_a_more_logical_place.patch b/src/patches/dnsmasq/018-Move_code_which_caches_DS_records_to_a_more_logical_place.patch
deleted file mode 100644
index ff055f7..0000000
--- a/src/patches/dnsmasq/018-Move_code_which_caches_DS_records_to_a_more_logical_place.patch
+++ /dev/null
@@ -1,269 +0,0 @@
-From d64c81fff7faf4392b688223ef3a617c5c07e7dc Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Tue, 15 Dec 2015 16:11:06 +0000
-Subject: [PATCH] Move code which caches DS records to a more logical place.
-
----
- src/dnssec.c | 179 +++++++++++++++++++++++++++++-----------------------------
- 1 file changed, 90 insertions(+), 89 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index 1ae03a6..359231f 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -1204,7 +1204,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
- 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, nons;
-+ int qtype, qclass, rc, i, neganswer, nons;
-+ int aclass, atype, rdlen;
-+ unsigned long ttl;
-+ struct all_addr a;
-
- if (ntohs(header->qdcount) != 1 ||
- !(p = skip_name(p, header, plen, 4)))
-@@ -1214,40 +1217,100 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
- GETSHORT(qclass, p);
-
- if (qtype != T_DS || qclass != class)
-- val = STAT_BOGUS;
-+ rc = STAT_BOGUS;
- else
-- val = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons);
-+ rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons);
- /* Note dnssec_validate_reply() will have cached positive answers */
-
-- if (val == STAT_INSECURE)
-- val = STAT_BOGUS;
--
-+ if (rc == STAT_INSECURE)
-+ rc = 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)))
-- val = STAT_BOGUS;
--
- /* If the key needed to validate the DS is on the same domain as the DS, we'll
- loop getting nowhere. Stop that now. This can happen of the DS answer comes
- from the DS's zone, and not the parent zone. */
-- if (val == STAT_BOGUS || (val == STAT_NEED_KEY && hostname_isequal(name, keyname)))
-+ if (rc == STAT_BOGUS || (rc == STAT_NEED_KEY && hostname_isequal(name, keyname)))
- {
- log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
- return STAT_BOGUS;
- }
-
-- if (val != STAT_SECURE)
-- return val;
--
-- /* By here, the answer is proved secure, and a positive answer has been cached. */
-- if (neganswer)
-+ if (rc != STAT_SECURE)
-+ return rc;
-+
-+ if (!neganswer)
- {
-- int rdlen, flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
-- unsigned long ttl, minttl = ULONG_MAX;
-- struct all_addr a;
-+ cache_start_insert();
-+
-+ for (i = 0; i < ntohs(header->ancount); i++)
-+ {
-+ if (!(rc = extract_name(header, plen, &p, name, 0, 10)))
-+ return STAT_BOGUS; /* bad packet */
-+
-+ GETSHORT(atype, p);
-+ GETSHORT(aclass, p);
-+ GETLONG(ttl, p);
-+ GETSHORT(rdlen, p);
-+
-+ if (!CHECK_LEN(header, p, plen, rdlen))
-+ return STAT_BOGUS; /* bad packet */
-+
-+ if (aclass == class && atype == T_DS && rc == 1)
-+ {
-+ int algo, digest, keytag;
-+ unsigned char *psave = p;
-+ struct blockdata *key;
-+ struct crec *crecp;
-
-+ if (rdlen < 4)
-+ return STAT_BOGUS; /* bad packet */
-+
-+ GETSHORT(keytag, p);
-+ algo = *p++;
-+ digest = *p++;
-+
-+ /* Cache needs to known class for DNSSEC stuff */
-+ a.addr.dnssec.class = class;
-+
-+ if ((key = blockdata_alloc((char*)p, rdlen - 4)))
-+ {
-+ if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)))
-+ {
-+ blockdata_free(key);
-+ return STAT_BOGUS;
-+ }
-+ else
-+ {
-+ a.addr.keytag = keytag;
-+ 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;
-+ crecp->addr.ds.keytag = keytag;
-+ crecp->addr.ds.keylen = rdlen - 4;
-+ }
-+ }
-+
-+ p = psave;
-+
-+ if (!ADD_RDLEN(header, p, plen, rdlen))
-+ return STAT_BOGUS; /* bad packet */
-+ }
-+
-+ cache_end_insert();
-+ }
-+ }
-+ else
-+ {
-+ int flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
-+ unsigned long minttl = ULONG_MAX;
-+
-+ if (!(p = skip_section(p, ntohs(header->ancount), header, plen)))
-+ return STAT_BOGUS;
-+
- if (RCODE(header) == NXDOMAIN)
- flags |= F_NXDOMAIN;
-
-@@ -1261,20 +1324,20 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
- if (!(p = skip_name(p, header, plen, 0)))
- return STAT_BOGUS;
-
-- GETSHORT(qtype, p);
-- GETSHORT(qclass, p);
-+ GETSHORT(atype, p);
-+ GETSHORT(aclass, p);
- GETLONG(ttl, p);
- GETSHORT(rdlen, p);
--
-+
- if (!CHECK_LEN(header, p, plen, rdlen))
- return STAT_BOGUS; /* bad packet */
--
-- if (qclass != class || qtype != T_SOA)
-+
-+ if (aclass != class || atype != T_SOA)
- {
- p += rdlen;
- continue;
- }
--
-+
- if (ttl < minttl)
- minttl = ttl;
-
-@@ -1306,7 +1369,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
- log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "no DS");
- }
- }
--
-+
- return STAT_OK;
- }
-
-@@ -2001,11 +2064,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- /* Not done, validate now */
- if (j == i)
- {
-- int ttl, keytag, algo, digest, sigcnt, rrcnt;
-- unsigned char *psave;
-- struct all_addr a;
-- struct blockdata *key;
-- struct crec *crecp;
-+ int sigcnt, rrcnt;
- char *wildname;
-
- if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
-@@ -2032,6 +2091,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- Can't overwrite name here. */
- strcpy(daemon->workspacename, keyname);
- rc = zone_status(daemon->workspacename, class1, keyname, now);
-+
- if (rc != STAT_SECURE)
- {
- /* Zone is insecure, don't need to validate RRset */
-@@ -2088,65 +2148,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- if (rc == STAT_BOGUS)
- return rc;
- }
--
-- /* If we just validated a DS RRset, cache it */
-- /* Also note if the RRset is the answer to the question, or the target of a CNAME */
-- cache_start_insert();
--
-- for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++)
-- {
-- if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
-- return STAT_BOGUS; /* bad packet */
--
-- GETSHORT(type2, p2);
-- GETSHORT(class2, p2);
-- GETLONG(ttl, p2);
-- GETSHORT(rdlen2, p2);
--
-- if (!CHECK_LEN(header, p2, plen, rdlen2))
-- return STAT_BOGUS; /* bad packet */
--
-- if (class2 == class1 && rc == 1)
-- {
-- psave = p2;
--
-- if (type1 == T_DS && type2 == T_DS)
-- {
-- if (rdlen2 < 4)
-- return STAT_BOGUS; /* bad packet */
--
-- GETSHORT(keytag, p2);
-- algo = *p2++;
-- digest = *p2++;
--
-- /* Cache needs to known class for DNSSEC stuff */
-- a.addr.dnssec.class = class2;
--
-- if ((key = blockdata_alloc((char*)p2, rdlen2 - 4)))
-- {
-- if (!(crecp = cache_insert(name, &a, now, ttl, F_FORWARD | F_DS | F_DNSSECOK)))
-- blockdata_free(key);
-- else
-- {
-- a.addr.keytag = keytag;
-- 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;
-- crecp->addr.ds.keytag = keytag;
-- crecp->addr.ds.keylen = rdlen2 - 4;
-- }
-- }
-- }
--
-- p2 = psave;
-- }
--
-- if (!ADD_RDLEN(header, p2, plen, rdlen2))
-- return STAT_BOGUS; /* bad packet */
-- }
--
-- cache_end_insert();
- }
- }
- }
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/019-Generalise_RR-filtering_code_for_use_with_EDNS0.patch b/src/patches/dnsmasq/019-Generalise_RR-filtering_code_for_use_with_EDNS0.patch
deleted file mode 100644
index 0a4942a..0000000
--- a/src/patches/dnsmasq/019-Generalise_RR-filtering_code_for_use_with_EDNS0.patch
+++ /dev/null
@@ -1,755 +0,0 @@
-From c2bcd1e183bcc5fdd63811c045355fc57e36ecfd Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Tue, 15 Dec 2015 17:25:21 +0000
-Subject: [PATCH] Generalise RR-filtering code, for use with EDNS0.
-
----
- Makefile | 3 +-
- bld/Android.mk | 2 +-
- src/dnsmasq.h | 5 +
- src/dnssec.c | 307 +-------------------------------------------------
- src/forward.c | 2 +-
- src/rrfilter.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 6 files changed, 349 insertions(+), 309 deletions(-)
- create mode 100644 src/rrfilter.c
-
-diff --git a/Makefile b/Makefile
-index 4c87ea9..b664160 100644
---- a/Makefile
-+++ b/Makefile
-@@ -73,7 +73,8 @@ 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 inotify.o poll.o
-+ domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
-+ poll.o rrfilter.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 5364ee7..67b9c4b 100644
---- a/bld/Android.mk
-+++ b/bld/Android.mk
-@@ -10,7 +10,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
- dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
- radv.c slaac.c auth.c ipset.c domain.c \
- dnssec.c dnssec-openssl.c blockdata.c tables.c \
-- loop.c inotify.c poll.c
-+ loop.c inotify.c poll.c rrfilter.c
-
- LOCAL_MODULE := dnsmasq
-
-diff --git a/src/dnsmasq.h b/src/dnsmasq.h
-index 4344cae..39a930c 100644
---- a/src/dnsmasq.h
-+++ b/src/dnsmasq.h
-@@ -1513,3 +1513,8 @@ int poll_check(int fd, short event);
- void poll_listen(int fd, short event);
- int do_poll(int timeout);
-
-+/* rrfilter.c */
-+size_t rrfilter(struct dns_header *header, size_t plen, int mode);
-+u16 *rrfilter_desc(int type);
-+int expand_workspace(unsigned char ***wkspc, int *szp, int new);
-+
-diff --git a/src/dnssec.c b/src/dnssec.c
-index 359231f..fa3eb81 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -507,50 +507,6 @@ static int check_date_range(unsigned long date_start, unsigned long date_end)
- && serial_compare_32(curtime, date_end) == SERIAL_LT;
- }
-
--static u16 *get_desc(int type)
--{
-- /* List of RRtypes which include domains in the data.
-- 0 -> domain
-- integer -> no of plain bytes
-- -1 -> end
--
-- zero is not a valid RRtype, so the final entry is returned for
-- anything which needs no mangling.
-- */
--
-- static u16 rr_desc[] =
-- {
-- T_NS, 0, -1,
-- T_MD, 0, -1,
-- T_MF, 0, -1,
-- T_CNAME, 0, -1,
-- T_SOA, 0, 0, -1,
-- T_MB, 0, -1,
-- T_MG, 0, -1,
-- T_MR, 0, -1,
-- T_PTR, 0, -1,
-- T_MINFO, 0, 0, -1,
-- T_MX, 2, 0, -1,
-- T_RP, 0, 0, -1,
-- T_AFSDB, 2, 0, -1,
-- T_RT, 2, 0, -1,
-- T_SIG, 18, 0, -1,
-- T_PX, 2, 0, 0, -1,
-- T_NXT, 0, -1,
-- T_KX, 2, 0, -1,
-- T_SRV, 6, 0, -1,
-- T_DNAME, 0, -1,
-- 0, -1 /* wildcard/catchall */
-- };
--
-- u16 *p = rr_desc;
--
-- while (*p != type && *p != 0)
-- while (*p++ != (u16)-1);
--
-- return p+1;
--}
--
- /* 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, int bufflen,
-@@ -594,34 +550,6 @@ static int get_rdata(struct dns_header *header, size_t plen, unsigned char *end,
- }
- }
-
--static int expand_workspace(unsigned char ***wkspc, int *szp, int new)
--{
-- unsigned char **p;
-- int old = *szp;
--
-- if (old >= new+1)
-- return 1;
--
-- if (new >= 100)
-- return 0;
--
-- new += 5;
--
-- if (!(p = whine_malloc(new * sizeof(unsigned char **))))
-- return 0;
--
-- if (old != 0 && *wkspc)
-- {
-- memcpy(p, *wkspc, old * sizeof(unsigned char **));
-- free(*wkspc);
-- }
--
-- *wkspc = p;
-- *szp = new;
--
-- return 1;
--}
--
- /* Bubble sort the RRset into the canonical order.
- Note that the byte-streams from two RRs may get unsynced: consider
- RRs which have two domain-names at the start and then other data.
-@@ -849,7 +777,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
- int rdlen, j, name_labels;
- struct crec *crecp = NULL;
- int algo, labels, orig_ttl, key_tag;
-- u16 *rr_desc = get_desc(type);
-+ u16 *rr_desc = rrfilter_desc(type);
-
- if (wildcard_out)
- *wildcard_out = NULL;
-@@ -2266,239 +2194,6 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
- return ret;
- }
-
--/* Go through a domain name, find "pointers" and fix them up based on how many bytes
-- we've chopped out of the packet, or check they don't point into an elided part. */
--static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
--{
-- unsigned char *ansp = *namep;
--
-- while(1)
-- {
-- unsigned int label_type;
--
-- if (!CHECK_LEN(header, ansp, plen, 1))
-- return 0;
--
-- label_type = (*ansp) & 0xc0;
--
-- if (label_type == 0xc0)
-- {
-- /* pointer for compression. */
-- unsigned int offset;
-- int i;
-- unsigned char *p;
--
-- if (!CHECK_LEN(header, ansp, plen, 2))
-- return 0;
--
-- offset = ((*ansp++) & 0x3f) << 8;
-- offset |= *ansp++;
--
-- p = offset + (unsigned char *)header;
--
-- for (i = 0; i < rr_count; i++)
-- if (p < rrs[i])
-- break;
-- else
-- if (i & 1)
-- offset -= rrs[i] - rrs[i-1];
--
-- /* does the pointer end up in an elided RR? */
-- if (i & 1)
-- return 0;
--
-- /* No, scale the pointer */
-- if (fixup)
-- {
-- ansp -= 2;
-- *ansp++ = (offset >> 8) | 0xc0;
-- *ansp++ = offset & 0xff;
-- }
-- break;
-- }
-- else if (label_type == 0x80)
-- return 0; /* reserved */
-- else if (label_type == 0x40)
-- {
-- /* Extended label type */
-- unsigned int count;
--
-- if (!CHECK_LEN(header, ansp, plen, 2))
-- return 0;
--
-- if (((*ansp++) & 0x3f) != 1)
-- return 0; /* we only understand bitstrings */
--
-- count = *(ansp++); /* Bits in bitstring */
--
-- if (count == 0) /* count == 0 means 256 bits */
-- ansp += 32;
-- else
-- ansp += ((count-1)>>3)+1;
-- }
-- else
-- { /* label type == 0 Bottom six bits is length */
-- unsigned int len = (*ansp++) & 0x3f;
--
-- if (!ADD_RDLEN(header, ansp, plen, len))
-- return 0;
--
-- if (len == 0)
-- break; /* zero length label marks the end. */
-- }
-- }
--
-- *namep = ansp;
--
-- return 1;
--}
--
--/* Go through RRs and check or fixup the domain names contained within */
--static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
--{
-- int i, type, class, rdlen;
-- unsigned char *pp;
--
-- for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
-- {
-- pp = p;
--
-- if (!(p = skip_name(p, header, plen, 10)))
-- return 0;
--
-- GETSHORT(type, p);
-- GETSHORT(class, p);
-- p += 4; /* TTL */
-- GETSHORT(rdlen, p);
--
-- if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
-- {
-- /* fixup name of RR */
-- if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
-- return 0;
--
-- if (class == C_IN)
-- {
-- u16 *d;
--
-- for (pp = p, d = get_desc(type); *d != (u16)-1; d++)
-- {
-- if (*d != 0)
-- pp += *d;
-- else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
-- return 0;
-- }
-- }
-- }
--
-- if (!ADD_RDLEN(header, p, plen, rdlen))
-- return 0;
-- }
--
-- return 1;
--}
--
--
--size_t filter_rrsigs(struct dns_header *header, size_t plen)
--{
-- static unsigned char **rrs;
-- static int rr_sz = 0;
--
-- unsigned char *p = (unsigned char *)(header+1);
-- int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
--
-- if (ntohs(header->qdcount) != 1 ||
-- !(p = skip_name(p, header, plen, 4)))
-- return plen;
--
-- GETSHORT(qtype, p);
-- GETSHORT(qclass, p);
--
-- /* First pass, find pointers to start and end of all the records we wish to elide:
-- records added for DNSSEC, unless explicity queried for */
-- for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
-- i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
-- i++)
-- {
-- unsigned char *pstart = p;
-- int type, class;
--
-- if (!(p = skip_name(p, header, plen, 10)))
-- return plen;
--
-- GETSHORT(type, p);
-- GETSHORT(class, p);
-- p += 4; /* TTL */
-- GETSHORT(rdlen, p);
--
-- if ((type == T_NSEC || type == T_NSEC3 || type == T_RRSIG) &&
-- (type != qtype || class != qclass))
-- {
-- if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
-- return plen;
--
-- rrs[rr_found++] = pstart;
--
-- if (!ADD_RDLEN(header, p, plen, rdlen))
-- return plen;
--
-- rrs[rr_found++] = p;
--
-- if (i < ntohs(header->ancount))
-- chop_an++;
-- else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
-- chop_ns++;
-- else
-- chop_ar++;
-- }
-- else if (!ADD_RDLEN(header, p, plen, rdlen))
-- return plen;
-- }
--
-- /* Nothing to do. */
-- if (rr_found == 0)
-- return plen;
--
-- /* Second pass, look for pointers in names in the records we're keeping and make sure they don't
-- point to records we're going to elide. This is theoretically possible, but unlikely. If
-- it happens, we give up and leave the answer unchanged. */
-- p = (unsigned char *)(header+1);
--
-- /* question first */
-- if (!check_name(&p, header, plen, 0, rrs, rr_found))
-- return plen;
-- p += 4; /* qclass, qtype */
--
-- /* Now answers and NS */
-- if (!check_rrs(p, header, plen, 0, rrs, rr_found))
-- return plen;
--
-- /* Third pass, elide records */
-- for (p = rrs[0], i = 1; i < rr_found; i += 2)
-- {
-- unsigned char *start = rrs[i];
-- unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen;
--
-- memmove(p, start, end-start);
-- p += end-start;
-- }
--
-- plen = p - (unsigned char *)header;
-- header->ancount = htons(ntohs(header->ancount) - chop_an);
-- header->nscount = htons(ntohs(header->nscount) - chop_ns);
-- header->arcount = htons(ntohs(header->arcount) - chop_ar);
--
-- /* Fourth pass, fix up pointers in the remaining records */
-- p = (unsigned char *)(header+1);
--
-- check_name(&p, header, plen, 1, rrs, rr_found);
-- p += 4; /* qclass, qtype */
--
-- check_rrs(p, header, plen, 1, rrs, rr_found);
--
-- return plen;
--}
--
- unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name)
- {
- int q;
-diff --git a/src/forward.c b/src/forward.c
-index dd22a62..3e801c8 100644
---- a/src/forward.c
-+++ b/src/forward.c
-@@ -662,7 +662,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
-
- /* If the requestor didn't set the DO bit, don't return DNSSEC info. */
- if (!do_bit)
-- n = filter_rrsigs(header, n);
-+ n = rrfilter(header, n, 1);
- #endif
-
- /* do this after extract_addresses. Ensure NODATA reply and remove
-diff --git a/src/rrfilter.c b/src/rrfilter.c
-new file mode 100644
-index 0000000..ae12261
---- /dev/null
-+++ b/src/rrfilter.c
-@@ -0,0 +1,339 @@
-+/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
-+
-+ This program is free software; you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published by
-+ the Free Software Foundation; version 2 dated June, 1991, or
-+ (at your option) version 3 dated 29 June, 2007.
-+
-+ This program is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU General Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License
-+ along with this program. If not, see
http://www.gnu.org/licenses/.
-+*/
-+
-+/* Code to safely remove RRs from an DNS answer */
-+
-+#include "dnsmasq.h"
-+
-+/* Go through a domain name, find "pointers" and fix them up based on how many bytes
-+ we've chopped out of the packet, or check they don't point into an elided part. */
-+static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
-+{
-+ unsigned char *ansp = *namep;
-+
-+ while(1)
-+ {
-+ unsigned int label_type;
-+
-+ if (!CHECK_LEN(header, ansp, plen, 1))
-+ return 0;
-+
-+ label_type = (*ansp) & 0xc0;
-+
-+ if (label_type == 0xc0)
-+ {
-+ /* pointer for compression. */
-+ unsigned int offset;
-+ int i;
-+ unsigned char *p;
-+
-+ if (!CHECK_LEN(header, ansp, plen, 2))
-+ return 0;
-+
-+ offset = ((*ansp++) & 0x3f) << 8;
-+ offset |= *ansp++;
-+
-+ p = offset + (unsigned char *)header;
-+
-+ for (i = 0; i < rr_count; i++)
-+ if (p < rrs[i])
-+ break;
-+ else
-+ if (i & 1)
-+ offset -= rrs[i] - rrs[i-1];
-+
-+ /* does the pointer end up in an elided RR? */
-+ if (i & 1)
-+ return 0;
-+
-+ /* No, scale the pointer */
-+ if (fixup)
-+ {
-+ ansp -= 2;
-+ *ansp++ = (offset >> 8) | 0xc0;
-+ *ansp++ = offset & 0xff;
-+ }
-+ break;
-+ }
-+ else if (label_type == 0x80)
-+ return 0; /* reserved */
-+ else if (label_type == 0x40)
-+ {
-+ /* Extended label type */
-+ unsigned int count;
-+
-+ if (!CHECK_LEN(header, ansp, plen, 2))
-+ return 0;
-+
-+ if (((*ansp++) & 0x3f) != 1)
-+ return 0; /* we only understand bitstrings */
-+
-+ count = *(ansp++); /* Bits in bitstring */
-+
-+ if (count == 0) /* count == 0 means 256 bits */
-+ ansp += 32;
-+ else
-+ ansp += ((count-1)>>3)+1;
-+ }
-+ else
-+ { /* label type == 0 Bottom six bits is length */
-+ unsigned int len = (*ansp++) & 0x3f;
-+
-+ if (!ADD_RDLEN(header, ansp, plen, len))
-+ return 0;
-+
-+ if (len == 0)
-+ break; /* zero length label marks the end. */
-+ }
-+ }
-+
-+ *namep = ansp;
-+
-+ return 1;
-+}
-+
-+/* Go through RRs and check or fixup the domain names contained within */
-+static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
-+{
-+ int i, j, type, class, rdlen;
-+ unsigned char *pp;
-+
-+ for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
-+ {
-+ pp = p;
-+
-+ if (!(p = skip_name(p, header, plen, 10)))
-+ return 0;
-+
-+ GETSHORT(type, p);
-+ GETSHORT(class, p);
-+ p += 4; /* TTL */
-+ GETSHORT(rdlen, p);
-+
-+ /* If this RR is to be elided, don't fix up its contents */
-+ for (j = 0; j < rr_count; j += 2)
-+ if (rrs[j] == pp)
-+ break;
-+
-+ if (j >= rr_count)
-+ {
-+ /* fixup name of RR */
-+ if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
-+ return 0;
-+
-+ if (class == C_IN)
-+ {
-+ u16 *d;
-+
-+ for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++)
-+ {
-+ if (*d != 0)
-+ pp += *d;
-+ else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
-+ return 0;
-+ }
-+ }
-+ }
-+
-+ if (!ADD_RDLEN(header, p, plen, rdlen))
-+ return 0;
-+ }
-+
-+ return 1;
-+}
-+
-+
-+/* mode is 0 to remove EDNS0, 1 to filter DNSSEC RRs */
-+size_t rrfilter(struct dns_header *header, size_t plen, int mode)
-+{
-+ static unsigned char **rrs;
-+ static int rr_sz = 0;
-+
-+ unsigned char *p = (unsigned char *)(header+1);
-+ int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
-+
-+ if (ntohs(header->qdcount) != 1 ||
-+ !(p = skip_name(p, header, plen, 4)))
-+ return plen;
-+
-+ GETSHORT(qtype, p);
-+ GETSHORT(qclass, p);
-+
-+ /* First pass, find pointers to start and end of all the records we wish to elide:
-+ records added for DNSSEC, unless explicity queried for */
-+ for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0;
-+ i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
-+ i++)
-+ {
-+ unsigned char *pstart = p;
-+ int type, class;
-+
-+ if (!(p = skip_name(p, header, plen, 10)))
-+ return plen;
-+
-+ GETSHORT(type, p);
-+ GETSHORT(class, p);
-+ p += 4; /* TTL */
-+ GETSHORT(rdlen, p);
-+
-+ if (!ADD_RDLEN(header, p, plen, rdlen))
-+ return plen;
-+
-+ /* Don't remove the answer. */
-+ if (i < ntohs(header->ancount) && type == qtype && class == qclass)
-+ continue;
-+
-+ if (mode == 0) /* EDNS */
-+ {
-+ /* EDNS mode, remove T_OPT from additional section only */
-+ if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
-+ continue;
-+ }
-+ else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
-+ /* DNSSEC mode, remove SIGs and NSECs from all three sections. */
-+ continue;
-+
-+
-+ if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
-+ return plen;
-+
-+ rrs[rr_found++] = pstart;
-+ rrs[rr_found++] = p;
-+
-+ if (i < ntohs(header->ancount))
-+ chop_an++;
-+ else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
-+ chop_ns++;
-+ else
-+ chop_ar++;
-+ }
-+
-+ /* Nothing to do. */
-+ if (rr_found == 0)
-+ return plen;
-+
-+ /* Second pass, look for pointers in names in the records we're keeping and make sure they don't
-+ point to records we're going to elide. This is theoretically possible, but unlikely. If
-+ it happens, we give up and leave the answer unchanged. */
-+ p = (unsigned char *)(header+1);
-+
-+ /* question first */
-+ if (!check_name(&p, header, plen, 0, rrs, rr_found))
-+ return plen;
-+ p += 4; /* qclass, qtype */
-+
-+ /* Now answers and NS */
-+ if (!check_rrs(p, header, plen, 0, rrs, rr_found))
-+ return plen;
-+
-+ /* Third pass, elide records */
-+ for (p = rrs[0], i = 1; i < rr_found; i += 2)
-+ {
-+ unsigned char *start = rrs[i];
-+ unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen;
-+
-+ memmove(p, start, end-start);
-+ p += end-start;
-+ }
-+
-+ plen = p - (unsigned char *)header;
-+ header->ancount = htons(ntohs(header->ancount) - chop_an);
-+ header->nscount = htons(ntohs(header->nscount) - chop_ns);
-+ header->arcount = htons(ntohs(header->arcount) - chop_ar);
-+
-+ /* Fourth pass, fix up pointers in the remaining records */
-+ p = (unsigned char *)(header+1);
-+
-+ check_name(&p, header, plen, 1, rrs, rr_found);
-+ p += 4; /* qclass, qtype */
-+
-+ check_rrs(p, header, plen, 1, rrs, rr_found);
-+
-+ return plen;
-+}
-+
-+/* This is used in the DNSSEC code too, hence it's exported */
-+u16 *rrfilter_desc(int type)
-+{
-+ /* List of RRtypes which include domains in the data.
-+ 0 -> domain
-+ integer -> no of plain bytes
-+ -1 -> end
-+
-+ zero is not a valid RRtype, so the final entry is returned for
-+ anything which needs no mangling.
-+ */
-+
-+ static u16 rr_desc[] =
-+ {
-+ T_NS, 0, -1,
-+ T_MD, 0, -1,
-+ T_MF, 0, -1,
-+ T_CNAME, 0, -1,
-+ T_SOA, 0, 0, -1,
-+ T_MB, 0, -1,
-+ T_MG, 0, -1,
-+ T_MR, 0, -1,
-+ T_PTR, 0, -1,
-+ T_MINFO, 0, 0, -1,
-+ T_MX, 2, 0, -1,
-+ T_RP, 0, 0, -1,
-+ T_AFSDB, 2, 0, -1,
-+ T_RT, 2, 0, -1,
-+ T_SIG, 18, 0, -1,
-+ T_PX, 2, 0, 0, -1,
-+ T_NXT, 0, -1,
-+ T_KX, 2, 0, -1,
-+ T_SRV, 6, 0, -1,
-+ T_DNAME, 0, -1,
-+ 0, -1 /* wildcard/catchall */
-+ };
-+
-+ u16 *p = rr_desc;
-+
-+ while (*p != type && *p != 0)
-+ while (*p++ != (u16)-1);
-+
-+ return p+1;
-+}
-+
-+int expand_workspace(unsigned char ***wkspc, int *szp, int new)
-+{
-+ unsigned char **p;
-+ int old = *szp;
-+
-+ if (old >= new+1)
-+ return 1;
-+
-+ if (new >= 100)
-+ return 0;
-+
-+ new += 5;
-+
-+ if (!(p = whine_malloc(new * sizeof(unsigned char **))))
-+ return 0;
-+
-+ if (old != 0 && *wkspc)
-+ {
-+ memcpy(p, *wkspc, old * sizeof(unsigned char **));
-+ free(*wkspc);
-+ }
-+
-+ *wkspc = p;
-+ *szp = new;
-+
-+ return 1;
-+}
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/020-DNSSEC_validation_tweak.patch b/src/patches/dnsmasq/020-DNSSEC_validation_tweak.patch
deleted file mode 100644
index ffb412b..0000000
--- a/src/patches/dnsmasq/020-DNSSEC_validation_tweak.patch
+++ /dev/null
@@ -1,134 +0,0 @@
-From 2dbba34b2c1289a108f876c78b84889f2a93115d Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Wed, 16 Dec 2015 13:41:58 +0000
-Subject: [PATCH] DNSSEC validation tweak.
-
-A zone which has at least one key with an algorithm we don't
-support should be considered as insecure.
----
- src/dnssec.c | 82 ++++++++++++++++++++++++++++++++++++++--------------------
- 1 file changed, 54 insertions(+), 28 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index fa3eb81..dc563e0 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -763,10 +763,10 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
- STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
- STAT_NEED_DS need DS to complete validation (name is returned in keyname)
-
-- if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
-+ If key is non-NULL, use that key, which has the algo and tag given in the params of those names,
- otherwise find the key in the cache.
-
-- name is unchanged on exit. keyname is used as workspace and trashed.
-+ Name is unchanged on exit. keyname is used as workspace and trashed.
-
- Call explore_rrset first to find and count RRs and sigs.
- */
-@@ -919,6 +919,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
- return STAT_BOGUS;
- }
-
-+
- /* 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:
-@@ -1831,15 +1832,15 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
-
- /* Check signing status of name.
- returns:
-- STAT_SECURE zone is signed.
-- STAT_INSECURE zone proved unsigned.
-- STAT_NEED_DS require DS record of name returned in keyname.
--
-+ STAT_SECURE zone is signed.
-+ STAT_INSECURE zone proved unsigned.
-+ STAT_NEED_DS require DS record of name returned in keyname.
-+ STAT_NEED_DNSKEY require DNSKEY record of name returned in keyname.
- name returned unaltered.
- */
- static int zone_status(char *name, int class, char *keyname, time_t now)
- {
-- int name_start = strlen(name);
-+ int secure_ds, name_start = strlen(name);
- struct crec *crecp;
- char *p;
-
-@@ -1850,27 +1851,52 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
- if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS)))
- return STAT_NEED_DS;
- else
-- do
-- {
-- if (crecp->uid == (unsigned int)class)
-- {
-- /* F_DNSSECOK misused in DS cache records to non-existance of NS record.
-- F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here,
-- but that's because there's no NS record either, ie this isn't the start
-- of a zone. We only prove that the DNS tree below a node is unsigned when
-- we prove that we're at a zone cut AND there's no DS record.
-- */
-- if (crecp->flags & F_NEG)
-- {
-- if (crecp->flags & F_DNSSECOK)
-- return STAT_INSECURE; /* proved no DS here */
-- }
-- else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo))
-- return STAT_INSECURE; /* algo we can't use - insecure */
-- }
-- }
-- while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
--
-+ {
-+ secure_ds = 0;
-+
-+ do
-+ {
-+ if (crecp->uid == (unsigned int)class)
-+ {
-+ /* F_DNSSECOK misused in DS cache records to non-existance of NS record.
-+ F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here,
-+ but that's because there's no NS record either, ie this isn't the start
-+ of a zone. We only prove that the DNS tree below a node is unsigned when
-+ we prove that we're at a zone cut AND there's no DS record.
-+ */
-+ if (crecp->flags & F_NEG)
-+ {
-+ if (crecp->flags & F_DNSSECOK)
-+ return STAT_INSECURE; /* proved no DS here */
-+ }
-+ else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo))
-+ return STAT_INSECURE; /* algo we can't use - insecure */
-+ else
-+ secure_ds = 1;
-+ }
-+ }
-+ while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
-+ }
-+
-+ if (secure_ds)
-+ {
-+ /* We've found only DS records that attest to the DNSKEY RRset in the zone, so we believe
-+ that RRset is good. Furthermore the DNSKEY whose hash is proved by the DS record is
-+ one we can use. However the DNSKEY RRset may contain more than one key and
-+ one of the other keys may use an algorithm we don't support. If that's
-+ the case the zone is insecure for us. */
-+
-+ if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
-+ return STAT_NEED_KEY;
-+
-+ do
-+ {
-+ if (crecp->uid == (unsigned int)class && !algo_digest_name(crecp->addr.key.algo))
-+ return STAT_INSECURE;
-+ }
-+ while ((crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY)));
-+ }
-+
- if (name_start == 0)
- break;
-
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch b/src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch
deleted file mode 100644
index c3c74cc..0000000
--- a/src/patches/dnsmasq/021-Tweaks_to_EDNS0_handling_in_DNS_replies.patch
+++ /dev/null
@@ -1,133 +0,0 @@
-From dd4ad9ac7ea6d51dcc34a1f2cd2da14efbb87714 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Thu, 17 Dec 2015 10:44:58 +0000
-Subject: [PATCH] Tweaks to EDNS0 handling in DNS replies.
-
----
- src/dnssec.c | 20 +++++++++-----------
- src/rfc1035.c | 57 +++++++++++++++++++++++++++++++++------------------------
- 2 files changed, 42 insertions(+), 35 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index dc563e0..012b2a6 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -2129,18 +2129,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- /* Empty DS without NSECS */
- if (qtype == T_DS)
- return STAT_BOGUS;
-- else
-+
-+ rc = zone_status(name, qclass, keyname, now);
-+ if (rc != STAT_SECURE)
- {
-- rc = zone_status(name, qclass, keyname, now);
-- if (rc != STAT_SECURE)
-- {
-- if (class)
-- *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
-- return rc;
-- }
--
-- return STAT_BOGUS; /* signed zone, no NSECs */
-- }
-+ if (class)
-+ *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
-+ return rc;
-+ }
-+
-+ return STAT_BOGUS; /* signed zone, no NSECs */
- }
-
- if (nsec_type == T_NSEC)
-diff --git a/src/rfc1035.c b/src/rfc1035.c
-index def8fa0..188d05f 100644
---- a/src/rfc1035.c
-+++ b/src/rfc1035.c
-@@ -1539,7 +1539,13 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
- struct mx_srv_record *rec;
- size_t len;
--
-+
-+ if (ntohs(header->ancount) != 0 ||
-+ ntohs(header->nscount) != 0 ||
-+ ntohs(header->qdcount) == 0 ||
-+ OPCODE(header) != QUERY )
-+ return 0;
-+
- /* Don't return AD set if checking disabled. */
- if (header->hb4 & HB4_CD)
- sec_data = 0;
-@@ -1548,33 +1554,32 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- *ad_reqd = header->hb4 & HB4_AD;
- *do_bit = 0;
-
-- /* If there is an RFC2671 pseudoheader then it will be overwritten by
-+ /* If there is an additional data section then it will be overwritten by
- partial replies, so we have to do a dry run to see if we can answer
-- the query. We check to see if the do bit is set, if so we always
-- forward rather than answering from the cache, which doesn't include
-- security information, unless we're in DNSSEC validation mode. */
-+ the query. */
-
-- if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
-- {
-- unsigned short flags;
--
-- have_pseudoheader = 1;
-+ if (ntohs(header->arcount) != 0)
-+ {
-+ dryrun = 1;
-
-- pheader += 4; /* udp size, ext_rcode */
-- GETSHORT(flags, pheader);
--
-- if ((sec_reqd = flags & 0x8000))
-- {
-- *do_bit = 1;/* do bit */
-- *ad_reqd = 1;
-+ /* If there's an additional section, there might be an EDNS(0) pseudoheader */
-+ if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
-+ {
-+ unsigned short flags;
-+
-+ have_pseudoheader = 1;
-+
-+ pheader += 4; /* udp size, ext_rcode */
-+ GETSHORT(flags, pheader);
-+
-+ if ((sec_reqd = flags & 0x8000))
-+ {
-+ *do_bit = 1;/* do bit */
-+ *ad_reqd = 1;
-+ }
- }
--
-- dryrun = 1;
- }
-
-- if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
-- return 0;
--
- for (rec = daemon->mxnames; rec; rec = rec->next)
- rec->offset = 0;
-
-@@ -1730,8 +1735,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- }
- else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
- {
-- /* Don't use cache when DNSSEC data required. */
-- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
-+ /* Don't use cache when DNSSEC data required, unless we know that
-+ the zone is unsigned, which implies that we're doing
-+ validation. */
-+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
-+ !sec_reqd ||
-+ (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
- {
- do
- {
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch b/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch
deleted file mode 100644
index 60503e9..0000000
--- a/src/patches/dnsmasq/022-Tidy_up_DNSSEC_non-existence_code_Check_zone_status_is_NSEC_proof_bad.patch
+++ /dev/null
@@ -1,409 +0,0 @@
-From b40f26c0199235073abc37e1e1d6ed93bed372f5 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Thu, 17 Dec 2015 11:57:26 +0000
-Subject: [PATCH] Tidy up DNSSEC non-existence code. Check zone status is NSEC
- proof bad.
-
----
- src/dnssec.c | 207 +++++++++++++++++++++++++---------------------------------
- 1 file changed, 90 insertions(+), 117 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index 012b2a6..ddae497 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -1367,59 +1367,6 @@ static int hostname_cmp(const char *a, const char *b)
- }
- }
-
--/* Find all the NSEC or NSEC3 records in a reply.
-- return an array of pointers to them. */
--static int find_nsec_records(struct dns_header *header, size_t plen, unsigned char ***nsecsetp, int *nsecsetl, int class_reqd)
--{
-- static unsigned char **nsecset = NULL;
-- static int nsecset_sz = 0;
--
-- int type_found = 0;
-- unsigned char *p = skip_questions(header, plen);
-- int type, class, rdlen, i, nsecs_found;
--
-- /* Move to NS section */
-- if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
-- return 0;
--
-- for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
-- {
-- unsigned char *pstart = p;
--
-- if (!(p = skip_name(p, header, plen, 10)))
-- return 0;
--
-- GETSHORT(type, p);
-- GETSHORT(class, p);
-- p += 4; /* TTL */
-- GETSHORT(rdlen, p);
--
-- if (class == class_reqd && (type == T_NSEC || type == T_NSEC3))
-- {
-- /* No mixed NSECing 'round here, thankyouverymuch */
-- if (type_found == T_NSEC && type == T_NSEC3)
-- return 0;
-- if (type_found == T_NSEC3 && type == T_NSEC)
-- return 0;
--
-- type_found = type;
--
-- if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
-- return 0;
--
-- nsecset[nsecs_found++] = pstart;
-- }
--
-- if (!ADD_RDLEN(header, p, plen, rdlen))
-- return 0;
-- }
--
-- *nsecsetp = nsecset;
-- *nsecsetl = nsecs_found;
--
-- return type_found;
--}
--
- 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, int *nons)
- {
-@@ -1436,12 +1383,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
- {
- p = nsecs[i];
- if (!extract_name(header, plen, &p, workspace1, 1, 10))
-- return STAT_BOGUS;
-+ return 0;
- p += 8; /* class, type, TTL */
- GETSHORT(rdlen, p);
- psave = p;
- if (!extract_name(header, plen, &p, workspace2, 1, 10))
-- return STAT_BOGUS;
-+ return 0;
-
- rc = hostname_cmp(workspace1, name);
-
-@@ -1449,7 +1396,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
- {
- /* 4035 para 5.4. Last sentence */
- if (type == T_NSEC || type == T_RRSIG)
-- return STAT_SECURE;
-+ return 1;
-
- /* NSEC with the same name as the RR we're testing, check
- that the type in question doesn't appear in the type map */
-@@ -1465,24 +1412,24 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
- /* A CNAME answer would also be valid, so if there's a CNAME is should
- have been returned. */
- if ((p[2] & (0x80 >> T_CNAME)) != 0)
-- return STAT_BOGUS;
-+ return 0;
-
- /* If the SOA bit is set for a DS record, then we have the
- DS from the wrong side of the delegation. */
- if (type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
-- return STAT_BOGUS;
-+ return 0;
- }
-
- while (rdlen >= 2)
- {
- if (!CHECK_LEN(header, p, plen, rdlen))
-- return STAT_BOGUS;
-+ return 0;
-
- if (p[0] == type >> 8)
- {
- /* Does the NSEC say our type exists? */
- if (offset < p[1] && (p[offset+2] & mask) != 0)
-- return STAT_BOGUS;
-+ return 0;
-
- break; /* finshed checking */
- }
-@@ -1491,24 +1438,24 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
- p += p[1];
- }
-
-- return STAT_SECURE;
-+ return 1;
- }
- else if (rc == -1)
- {
- /* Normal case, name falls between NSEC name and next domain name,
- wrap around case, name falls between NSEC name (rc == -1) and end */
- if (hostname_cmp(workspace2, name) >= 0 || hostname_cmp(workspace1, workspace2) >= 0)
-- return STAT_SECURE;
-+ return 1;
- }
- else
- {
- /* wrap around case, name falls between start and next domain name */
- if (hostname_cmp(workspace1, workspace2) >= 0 && hostname_cmp(workspace2, name) >=0 )
-- return STAT_SECURE;
-+ return 1;
- }
- }
-
-- return STAT_BOGUS;
-+ return 0;
- }
-
- /* return digest length, or zero on error */
-@@ -1701,7 +1648,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- for (i = 0; i < nsec_count; i++)
- {
- if (!(p = skip_name(nsecs[i], header, plen, 15)))
-- return STAT_BOGUS; /* bad packet */
-+ return 0; /* bad packet */
-
- p += 10; /* type, class, TTL, rdlen */
- algo = *p++;
-@@ -1712,14 +1659,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
-
- /* No usable NSEC3s */
- if (i == nsec_count)
-- return STAT_BOGUS;
-+ return 0;
-
- p++; /* flags */
- GETSHORT (iterations, p);
- salt_len = *p++;
- salt = p;
- if (!CHECK_LEN(header, salt, plen, salt_len))
-- return STAT_BOGUS; /* bad packet */
-+ return 0; /* bad packet */
-
- /* Now prune so we only have NSEC3 records with same iterations, salt and algo */
- for (i = 0; i < nsec_count; i++)
-@@ -1730,7 +1677,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- nsecs[i] = NULL; /* Speculative, will be restored if OK. */
-
- if (!(p = skip_name(nsec3p, header, plen, 15)))
-- return STAT_BOGUS; /* bad packet */
-+ return 0; /* bad packet */
-
- p += 10; /* type, class, TTL, rdlen */
-
-@@ -1747,7 +1694,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- continue;
-
- if (!CHECK_LEN(header, p, plen, salt_len))
-- return STAT_BOGUS; /* bad packet */
-+ return 0; /* bad packet */
-
- if (memcmp(p, salt, salt_len) != 0)
- continue;
-@@ -1758,13 +1705,13 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
-
- /* Algo is checked as 1 above */
- if (!(hash = hash_find("sha1")))
-- return STAT_BOGUS;
-+ return 0;
-
- if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
-- return STAT_BOGUS;
-+ return 0;
-
- if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons))
-- return STAT_SECURE;
-+ return 1;
-
- /* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3"
- or an answer inferred from a wildcard record. */
-@@ -1780,14 +1727,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- break;
-
- if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
-- return STAT_BOGUS;
-+ return 0;
-
- 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;
-+ return 0;
-
- if (digest_len == base32_len &&
- memcmp(digest, workspace2, digest_len) == 0)
-@@ -1802,32 +1749,81 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- while ((closest_encloser = strchr(closest_encloser, '.')));
-
- if (!closest_encloser)
-- return STAT_BOGUS;
-+ return 0;
-
- /* 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;
-+ return 0;
-
- if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
-- return STAT_BOGUS;
-+ return 0;
-
- /* Finally, check that there's no seat of wildcard synthesis */
- if (!wildname)
- {
- if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
-- return STAT_BOGUS;
-+ return 0;
-
- wildcard--;
- *wildcard = '*';
-
- if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
-- return STAT_BOGUS;
-+ return 0;
-
- if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL))
-- return STAT_BOGUS;
-+ return 0;
- }
-
-- return STAT_SECURE;
-+ return 1;
-+}
-+
-+static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass, char *wildname, int *nons)
-+{
-+ static unsigned char **nsecset = NULL;
-+ static int nsecset_sz = 0;
-+
-+ int type_found = 0;
-+ unsigned char *p = skip_questions(header, plen);
-+ int type, class, rdlen, i, nsecs_found;
-+
-+ /* Move to NS section */
-+ if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
-+ return 0;
-+
-+ for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
-+ {
-+ unsigned char *pstart = p;
-+
-+ if (!(p = skip_name(p, header, plen, 10)))
-+ return 0;
-+
-+ GETSHORT(type, p);
-+ GETSHORT(class, p);
-+ p += 4; /* TTL */
-+ GETSHORT(rdlen, p);
-+
-+ if (class == qclass && (type == T_NSEC || type == T_NSEC3))
-+ {
-+ /* No mixed NSECing 'round here, thankyouverymuch */
-+ if (type_found != 0 && type_found != type)
-+ return 0;
-+
-+ type_found = type;
-+
-+ if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
-+ return 0;
-+
-+ nsecset[nsecs_found++] = pstart;
-+ }
-+
-+ if (!ADD_RDLEN(header, p, plen, rdlen))
-+ return 0;
-+ }
-+
-+ if (type_found == T_NSEC)
-+ return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
-+ else
-+ return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons);
- }
-
- /* Check signing status of name.
-@@ -1925,10 +1921,9 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- static unsigned char **targets = NULL;
- static int target_sz = 0;
-
-- unsigned char *ans_start, *p1, *p2, **nsecs;
-+ unsigned char *ans_start, *p1, *p2;
- int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype, targetidx;
-- int i, j, rc, nsec_count;
-- int nsec_type;
-+ int i, j, rc;
-
- if (neganswer)
- *neganswer = 0;
-@@ -2080,28 +2075,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- targets[j] = NULL;
- }
-
-- if (rc == STAT_SECURE_WILDCARD)
-- {
-- /* An attacker replay a wildcard answer with a different
-- answer and overlay a genuine RR. To prove this
-- hasn't happened, the answer must prove that
-- the gennuine record doesn't exist. Check that here. */
-- if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1)))
-- return STAT_BOGUS; /* No NSECs or bad packet */
--
-- /* Note that we may not yet have validated the NSEC/NSEC3 RRsets. Since the check
-- below returns either SECURE or BOGUS, that's not a problem. If the RRsets later fail
-- we'll return BOGUS then. */
--
-- if (nsec_type == T_NSEC)
-- 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, NULL);
--
-- if (rc == STAT_BOGUS)
-- return rc;
-- }
-+ /* An attacker replay a wildcard answer with a different
-+ answer and overlay a genuine RR. To prove this
-+ hasn't happened, the answer must prove that
-+ the gennuine record doesn't exist. Check that here.
-+ Note that we may not yet have validated the NSEC/NSEC3 RRsets.
-+ That's not a problem since if the RRsets later fail
-+ we'll return BOGUS then. */
-+ if (rc == STAT_SECURE_WILDCARD && !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL))
-+ return STAT_BOGUS;
- }
- }
- }
-@@ -2124,14 +2106,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
-
- /* For anything other than a DS record, this situation is OK if either
- the answer is in an unsigned zone, or there's a NSEC records. */
-- if (!(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass)))
-+ if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons))
- {
- /* Empty DS without NSECS */
- if (qtype == T_DS)
- return STAT_BOGUS;
-
-- rc = zone_status(name, qclass, keyname, now);
-- if (rc != STAT_SECURE)
-+ if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
- {
- if (class)
- *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
-@@ -2140,14 +2121,6 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
-
- return STAT_BOGUS; /* signed zone, no NSECs */
- }
--
-- if (nsec_type == T_NSEC)
-- rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, nons);
-- else
-- rc = prove_non_existence_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype, NULL, nons);
--
-- if (rc != STAT_SECURE)
-- return rc;
- }
-
- return STAT_SECURE;
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch b/src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch
deleted file mode 100644
index eda6fbd..0000000
--- a/src/patches/dnsmasq/023-Fix_brace_botch_in_dnssec_validate_ds.patch
+++ /dev/null
@@ -1,98 +0,0 @@
-From 3b799c826db05fc2da1c6d15cbe372e394209d27 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Thu, 17 Dec 2015 16:58:04 +0000
-Subject: [PATCH] Fix brace botch in dnssec_validate_ds()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=utf8
-Content-Transfer-Encoding: 8bit
-
-Thanks to MichaÅ KÄpieÅ for spotting this.
----
- src/dnssec.c | 34 +++++++++++++++++-----------------
- 1 file changed, 17 insertions(+), 17 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index ddae497..1f8c954 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -923,11 +923,11 @@ 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_OK Done, key(s) 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
-- STAT_NEED_DNSKEY DNSKEY records to validate a key not found, name in keyname
-+ STAT_OK Done, key(s) 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
-+ STAT_NEED_KEY DNSKEY records to validate a key not found, name in keyname
- */
- int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
- {
-@@ -1224,13 +1224,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
- }
-
- p = psave;
--
-- if (!ADD_RDLEN(header, p, plen, rdlen))
-- return STAT_BOGUS; /* bad packet */
- }
--
-- cache_end_insert();
-+ if (!ADD_RDLEN(header, p, plen, rdlen))
-+ return STAT_BOGUS; /* bad packet */
- }
-+
-+ cache_end_insert();
-+
- }
- else
- {
-@@ -1828,10 +1828,10 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
-
- /* Check signing status of name.
- returns:
-- STAT_SECURE zone is signed.
-- STAT_INSECURE zone proved unsigned.
-- STAT_NEED_DS require DS record of name returned in keyname.
-- STAT_NEED_DNSKEY require DNSKEY record of name returned in keyname.
-+ STAT_SECURE zone is signed.
-+ STAT_INSECURE zone proved unsigned.
-+ STAT_NEED_DS require DS record of name returned in keyname.
-+ STAT_NEED_KEY require DNSKEY record of name returned in keyname.
- name returned unaltered.
- */
- static int zone_status(char *name, int class, char *keyname, time_t now)
-@@ -2028,7 +2028,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- if (rc == STAT_SECURE)
- rc = STAT_BOGUS;
- if (class)
-- *class = class1; /* Class for NEED_DS or NEED_DNSKEY */
-+ *class = class1; /* Class for NEED_DS or NEED_KEY */
- }
- else
- rc = STAT_INSECURE;
-@@ -2045,7 +2045,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- {
- /* Zone is insecure, don't need to validate RRset */
- if (class)
-- *class = class1; /* Class for NEED_DS or NEED_DNSKEY */
-+ *class = class1; /* Class for NEED_DS or NEED_KEY */
- return rc;
- }
-
-@@ -2115,7 +2115,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
- if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
- {
- if (class)
-- *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
-+ *class = qclass; /* Class for NEED_DS or NEED_KEY */
- return rc;
- }
-
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch b/src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch
deleted file mode 100644
index abcae5c..0000000
--- a/src/patches/dnsmasq/024-Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.patch
+++ /dev/null
@@ -1,145 +0,0 @@
-From 14a4ae883d51130d33da7133287e8867c64bab65 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Thu, 17 Dec 2015 17:23:03 +0000
-Subject: [PATCH] Do a better job of determining which DNSSEC sig algos are
- supported.
-
----
- src/dnssec.c | 52 +++++++++++++++++++++++++++++++++++++---------------
- 1 file changed, 37 insertions(+), 15 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index 1f8c954..82394ee 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -65,10 +65,9 @@ static char *algo_digest_name(int algo)
- case 8: return "sha256";
- case 10: return "sha512";
- case 12: return "gosthash94";
--#ifndef NO_NETTLE_ECC
- case 13: return "sha256";
- case 14: return "sha384";
--#endif
-+
- default: return NULL;
- }
- }
-@@ -129,13 +128,15 @@ static int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char
- }
-
- static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
-- unsigned char *digest, int algo)
-+ unsigned char *digest, size_t digest_len, int algo)
- {
- unsigned char *p;
- size_t exp_len;
-
- static struct rsa_public_key *key = NULL;
- static mpz_t sig_mpz;
-+
-+ (void)digest_len;
-
- if (key == NULL)
- {
-@@ -181,7 +182,7 @@ static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len,
- }
-
- static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
-- unsigned char *digest, int algo)
-+ unsigned char *digest, size_t digest_len, int algo)
- {
- unsigned char *p;
- unsigned int t;
-@@ -189,6 +190,8 @@ static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned int key_len,
- static struct dsa_public_key *key = NULL;
- static struct dsa_signature *sig_struct;
-
-+ (void)digest_len;
-+
- if (key == NULL)
- {
- if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))) ||
-@@ -292,26 +295,45 @@ static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len
- }
- #endif
-
--static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
-- unsigned char *digest, size_t digest_len, int algo)
-+static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
-+ unsigned char *digest, size_t digest_len, int algo)
- {
-- (void)digest_len;
--
-+
-+ /* Enure at runtime that we have support for this digest */
-+ if (!hash_find(algo_digest_name(algo)))
-+ return NULL;
-+
-+ /* This switch defines which sig algorithms we support, can't introspect Nettle for that. */
- switch (algo)
- {
- case 1: case 5: case 7: case 8: case 10:
-- return dnsmasq_rsa_verify(key_data, key_len, sig, sig_len, digest, algo);
-+ return dnsmasq_rsa_verify;
-
- case 3: case 6:
-- return dnsmasq_dsa_verify(key_data, key_len, sig, sig_len, digest, algo);
-+ return dnsmasq_dsa_verify;
-
- #ifndef NO_NETTLE_ECC
- case 13: case 14:
-- return dnsmasq_ecdsa_verify(key_data, key_len, sig, sig_len, digest, digest_len, algo);
-+ return dnsmasq_ecdsa_verify;
- #endif
- }
-
-- return 0;
-+ return NULL;
-+}
-+
-+static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
-+ unsigned char *digest, size_t digest_len, int algo)
-+{
-+
-+ int (*func)(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
-+ unsigned char *digest, size_t digest_len, int algo);
-+
-+ func = verify_func(algo);
-+
-+ if (!func)
-+ return 0;
-+
-+ return (*func)(key_data, key_len, sig, sig_len, digest, digest_len, algo);
- }
-
- /* Convert from presentation format to wire format, in place.
-@@ -732,7 +754,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
- if (check_date_range(sig_inception, sig_expiration) &&
- labels <= name_labels &&
- type_covered == type &&
-- algo_digest_name(algo))
-+ verify_func(algo))
- {
- if (!expand_workspace(&sigs, &sig_sz, sigidx))
- return 0;
-@@ -1865,7 +1887,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
- if (crecp->flags & F_DNSSECOK)
- return STAT_INSECURE; /* proved no DS here */
- }
-- else if (!ds_digest_name(crecp->addr.ds.digest) || !algo_digest_name(crecp->addr.ds.algo))
-+ else if (!hash_find(ds_digest_name(crecp->addr.ds.digest)) || !verify_func(crecp->addr.ds.algo))
- return STAT_INSECURE; /* algo we can't use - insecure */
- else
- secure_ds = 1;
-@@ -1887,7 +1909,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
-
- do
- {
-- if (crecp->uid == (unsigned int)class && !algo_digest_name(crecp->addr.key.algo))
-+ if (crecp->uid == (unsigned int)class && !verify_func(crecp->addr.key.algo))
- return STAT_INSECURE;
- }
- while ((crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY)));
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch b/src/patches/dnsmasq/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch
deleted file mode 100644
index c016e73..0000000
--- a/src/patches/dnsmasq/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch
+++ /dev/null
@@ -1,643 +0,0 @@
-From fa14bec83b2db010fd076910fddab56957b9375d Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Sun, 20 Dec 2015 17:12:16 +0000
-Subject: [PATCH] Major tidy up of EDNS0 handling and computation/use of udp
- packet size.
-
----
- src/auth.c | 8 ++-
- src/dnsmasq.h | 7 ++-
- src/dnssec.c | 1 -
- src/forward.c | 184 ++++++++++++++++++++++++++++++++++++++++----------------
- src/netlink.c | 3 +-
- src/rfc1035.c | 81 +++++++------------------
- src/rrfilter.c | 2 +-
- 7 files changed, 168 insertions(+), 118 deletions(-)
-
-diff --git a/src/auth.c b/src/auth.c
-index 2b0b7d6..85bd5e7 100644
---- a/src/auth.c
-+++ b/src/auth.c
-@@ -81,7 +81,8 @@ int in_zone(struct auth_zone *zone, char *name, char **cut)
- }
-
-
--size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr, int local_query)
-+size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr,
-+ int local_query, int do_bit, int have_pseudoheader)
- {
- char *name = daemon->namebuff;
- unsigned char *p, *ansp;
-@@ -820,6 +821,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
- header->ancount = htons(anscount);
- header->nscount = htons(authcount);
- header->arcount = htons(0);
-+
-+ /* Advertise our packet size limit in our reply */
-+ if (have_pseudoheader)
-+ return add_pseudoheader(header, ansp - (unsigned char *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit);
-+
- return ansp - (unsigned char *)header;
- }
-
-diff --git a/src/dnsmasq.h b/src/dnsmasq.h
-index 39a930c..abb34c5 100644
---- a/src/dnsmasq.h
-+++ b/src/dnsmasq.h
-@@ -1113,7 +1113,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
- int no_cache, int secure, int *doctored);
- size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- struct in_addr local_addr, struct in_addr local_netmask,
-- time_t now, int *ad_reqd, int *do_bit);
-+ time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
- 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);
-@@ -1123,6 +1123,8 @@ int check_for_local_domain(char *name, time_t now);
- unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff);
- size_t resize_packet(struct dns_header *header, size_t plen,
- unsigned char *pheader, size_t hlen);
-+size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
-+ unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do);
- size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
- size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source);
- #ifdef HAVE_DNSSEC
-@@ -1141,7 +1143,8 @@ int private_net(struct in_addr addr, int ban_localhost);
- /* auth.c */
- #ifdef HAVE_AUTH
- size_t answer_auth(struct dns_header *header, char *limit, size_t qlen,
-- time_t now, union mysockaddr *peer_addr, int local_query);
-+ time_t now, union mysockaddr *peer_addr, int local_query,
-+ int do_bit, int have_pseudoheader);
- int in_zone(struct auth_zone *zone, char *name, char **cut);
- #endif
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index 82394ee..299ca64 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -67,7 +67,6 @@ static char *algo_digest_name(int algo)
- case 12: return "gosthash94";
- case 13: return "sha256";
- case 14: return "sha384";
--
- default: return NULL;
- }
- }
-diff --git a/src/forward.c b/src/forward.c
-index 3e801c8..041353c 100644
---- a/src/forward.c
-+++ b/src/forward.c
-@@ -244,7 +244,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
- void *hash = &crc;
- #endif
- unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
-- unsigned char *pheader;
-
- (void)do_bit;
-
-@@ -264,7 +263,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
- there's no point retrying the query, retry the key query instead...... */
- if (forward->blocking_query)
- {
-- int fd;
-+ int fd, is_sign;
-+ unsigned char *pheader;
-
- forward->flags &= ~FREC_TEST_PKTSZ;
-
-@@ -276,8 +276,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
- blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
- plen = forward->stash_len;
-
-- if (find_pseudoheader(header, plen, NULL, &pheader, NULL))
-- PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : forward->sentto->edns_pktsz, pheader);
-+ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign) && !is_sign)
-+ PUTSHORT(SAFE_PKTSZ, pheader);
-
- if (forward->sentto->addr.sa.sa_family == AF_INET)
- log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
-@@ -394,32 +394,40 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
- 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);
--
-+ {
-+ size_t new = add_mac(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
-+ if (new != plen)
-+ {
-+ plen = new;
-+ forward->flags |= FREC_ADDED_PHEADER;
-+ }
-+ }
-+
- if (option_bool(OPT_CLIENT_SUBNET))
- {
- size_t new = add_source_addr(header, plen, ((char *) header) + daemon->packet_buff_sz, &forward->source);
- if (new != plen)
- {
- plen = new;
-- forward->flags |= FREC_HAS_SUBNET;
-+ forward->flags |= FREC_HAS_SUBNET | FREC_ADDED_PHEADER;
- }
- }
-
- #ifdef HAVE_DNSSEC
- if (option_bool(OPT_DNSSEC_VALID))
- {
-- size_t new_plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
-+ size_t new = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
-
-+ if (new != plen)
-+ forward->flags |= FREC_ADDED_PHEADER;
-+
-+ plen = new;
-+
- /* For debugging, set Checking Disabled, otherwise, have the upstream check too,
- this allows it to select auth servers when one is returning bad data. */
- if (option_bool(OPT_DNSSEC_DEBUG))
- header->hb4 |= HB4_CD;
-
-- if (new_plen != plen)
-- forward->flags |= FREC_ADDED_PHEADER;
--
-- plen = new_plen;
- }
- #endif
-
-@@ -469,10 +477,23 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
- }
- #endif
- }
--
-- if (find_pseudoheader(header, plen, NULL, &pheader, NULL))
-- PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ? SAFE_PKTSZ : start->edns_pktsz, pheader);
-
-+#ifdef HAVE_DNSSEC
-+ if (option_bool(OPT_DNSSEC_VALID) && !do_bit)
-+ {
-+ /* Difficult one here. If our client didn't send EDNS0, we will have set the UDP
-+ packet size to 512. But that won't provide space for the RRSIGS in many cases.
-+ The RRSIGS will be stripped out before the answer goes back, so the packet should
-+ shrink again. So, if we added a do-bit, bump the udp packet size to the value
-+ known to be OK for this server. Maybe check returned size after stripping and set
-+ the truncated bit? */
-+ unsigned char *pheader;
-+ int is_sign;
-+ if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign))
-+ PUTSHORT(start->edns_pktsz, pheader);
-+ }
-+#endif
-+
- if (retry_send(sendto(fd, (char *)header, plen, 0,
- &start->addr.sa,
- sa_len(&start->addr))))
-@@ -563,30 +584,34 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
- }
- #endif
-
-- /* If upstream is advertising a larger UDP packet size
-- than we allow, trim it so that we don't get overlarge
-- requests for the client. We can't do this for signed packets. */
--
- if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)))
- {
-- unsigned short udpsz;
-- unsigned char *psave = sizep;
--
-- GETSHORT(udpsz, sizep);
--
-- if (!is_sign && udpsz > daemon->edns_pktsz)
-- PUTSHORT(daemon->edns_pktsz, psave);
--
- if (check_subnet && !check_source(header, plen, pheader, query_source))
- {
- my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
- return 0;
- }
-
-- if (added_pheader)
-+ if (!is_sign)
- {
-- pheader = 0;
-- header->arcount = htons(0);
-+ if (added_pheader)
-+ {
-+ /* client didn't send EDNS0, we added one, strip it off before returning answer. */
-+ n = rrfilter(header, n, 0);
-+ pheader = NULL;
-+ }
-+ else
-+ {
-+ /* If upstream is advertising a larger UDP packet size
-+ than we allow, trim it so that we don't get overlarge
-+ requests for the client. We can't do this for signed packets. */
-+ unsigned short udpsz;
-+ unsigned char *psave = sizep;
-+
-+ GETSHORT(udpsz, sizep);
-+ if (udpsz > daemon->edns_pktsz)
-+ PUTSHORT(daemon->edns_pktsz, psave);
-+ }
- }
- }
-
-@@ -655,14 +680,16 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
- }
-
- if (option_bool(OPT_DNSSEC_VALID))
-- header->hb4 &= ~HB4_AD;
--
-- if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
-- header->hb4 |= HB4_AD;
--
-- /* If the requestor didn't set the DO bit, don't return DNSSEC info. */
-- if (!do_bit)
-- n = rrfilter(header, n, 1);
-+ {
-+ header->hb4 &= ~HB4_AD;
-+
-+ if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
-+ header->hb4 |= HB4_AD;
-+
-+ /* If the requestor didn't set the DO bit, don't return DNSSEC info. */
-+ if (!do_bit)
-+ n = rrfilter(header, n, 1);
-+ }
- #endif
-
- /* do this after extract_addresses. Ensure NODATA reply and remove
-@@ -761,8 +788,14 @@ void reply_query(int fd, int family, time_t now)
- if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
- {
- header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
-- header->hb4 &= ~(HB4_RA | HB4_RCODE);
-- forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0);
-+ header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD | HB4_AD);
-+ if (forward->flags |= FREC_CHECKING_DISABLED)
-+ header->hb4 |= HB4_CD;
-+ if (forward->flags |= FREC_AD_QUESTION)
-+ header->hb4 |= HB4_AD;
-+ if (forward->flags & FREC_DO_QUESTION)
-+ add_do_bit(header, nn, (char *)pheader + plen);
-+ forward_query(-1, NULL, NULL, 0, header, nn, now, forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION);
- return;
- }
- }
-@@ -1007,12 +1040,13 @@ void receive_query(struct listener *listen, time_t now)
- {
- struct dns_header *header = (struct dns_header *)daemon->packet;
- union mysockaddr source_addr;
-- unsigned short type;
-+ unsigned char *pheader;
-+ unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0 */
- struct all_addr dst_addr;
- struct in_addr netmask, dst_addr_4;
- size_t m;
- ssize_t n;
-- int if_index = 0, auth_dns = 0;
-+ int if_index = 0, auth_dns = 0, do_bit = 0, have_pseudoheader = 0;
- #ifdef HAVE_AUTH
- int local_auth = 0;
- #endif
-@@ -1279,10 +1313,30 @@ void receive_query(struct listener *listen, time_t now)
- #endif
- }
-
-+ if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL))
-+ {
-+ unsigned short flags;
-+
-+ have_pseudoheader = 1;
-+ GETSHORT(udp_size, pheader);
-+ pheader += 2; /* ext_rcode */
-+ GETSHORT(flags, pheader);
-+
-+ if (flags & 0x8000)
-+ do_bit = 1;/* do bit */
-+
-+ /* If the client provides an EDNS0 UDP size, use that to limit our reply.
-+ (bounded by the maximum configured). If no EDNS0, then it
-+ defaults to 512 */
-+ if (udp_size > daemon->edns_pktsz)
-+ udp_size = daemon->edns_pktsz;
-+ }
-+
- #ifdef HAVE_AUTH
- if (auth_dns)
- {
-- m = answer_auth(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n, now, &source_addr, local_auth);
-+ m = answer_auth(header, ((char *) header) + udp_size, (size_t)n, now, &source_addr,
-+ local_auth, do_bit, have_pseudoheader);
- if (m >= 1)
- {
- send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
-@@ -1293,9 +1347,13 @@ void receive_query(struct listener *listen, time_t now)
- else
- #endif
- {
-- int ad_reqd, do_bit;
-- m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n,
-- dst_addr_4, netmask, now, &ad_reqd, &do_bit);
-+ int ad_reqd = do_bit;
-+ /* RFC 6840 5.7 */
-+ if (header->hb4 & HB4_AD)
-+ ad_reqd = 1;
-+
-+ m = answer_request(header, ((char *) header) + udp_size, (size_t)n,
-+ dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
-
- if (m >= 1)
- {
-@@ -1397,7 +1455,7 @@ unsigned char *tcp_request(int confd, time_t now,
- #ifdef HAVE_AUTH
- int local_auth = 0;
- #endif
-- int checking_disabled, ad_question, do_bit, added_pheader = 0;
-+ int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0;
- int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
- size_t m;
- unsigned short qtype;
-@@ -1414,6 +1472,7 @@ unsigned char *tcp_request(int confd, time_t now,
- union mysockaddr peer_addr;
- socklen_t peer_len = sizeof(union mysockaddr);
- int query_count = 0;
-+ unsigned char *pheader;
-
- if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
- return packet;
-@@ -1508,15 +1567,35 @@ unsigned char *tcp_request(int confd, time_t now,
- else
- dst_addr_4.s_addr = 0;
-
-+ do_bit = 0;
-+
-+ if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL))
-+ {
-+ unsigned short flags;
-+
-+ have_pseudoheader = 1;
-+ pheader += 4; /* udp_size, ext_rcode */
-+ GETSHORT(flags, pheader);
-+
-+ if (flags & 0x8000)
-+ do_bit = 1;/* do bit */
-+ }
-+
- #ifdef HAVE_AUTH
- if (auth_dns)
-- m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, local_auth);
-+ m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr,
-+ local_auth, do_bit, have_pseudoheader);
- else
- #endif
- {
-- /* m > 0 if answered from cache */
-- m = answer_request(header, ((char *) header) + 65536, (size_t)size,
-- dst_addr_4, netmask, now, &ad_question, &do_bit);
-+ int ad_reqd = do_bit;
-+ /* RFC 6840 5.7 */
-+ if (header->hb4 & HB4_AD)
-+ ad_reqd = 1;
-+
-+ /* m > 0 if answered from cache */
-+ m = answer_request(header, ((char *) header) + 65536, (size_t)size,
-+ dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader);
-
- /* Do this by steam now we're not in the select() loop */
- check_log_writer(1);
-@@ -1615,6 +1694,7 @@ unsigned char *tcp_request(int confd, time_t now,
- }
-
- #ifdef HAVE_DNSSEC
-+ added_pheader = 0;
- if (option_bool(OPT_DNSSEC_VALID))
- {
- size_t new_size = add_do_bit(header, size, ((char *) header) + 65536);
-@@ -1719,7 +1799,7 @@ unsigned char *tcp_request(int confd, time_t now,
-
- m = process_reply(header, now, last_server, (unsigned int)m,
- option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
-- ad_question, do_bit, added_pheader, check_subnet, &peer_addr);
-+ ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr);
-
- break;
- }
-diff --git a/src/netlink.c b/src/netlink.c
-index 753784d..3376d68 100644
---- a/src/netlink.c
-+++ b/src/netlink.c
-@@ -288,7 +288,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
- rta = RTA_NEXT(rta, len1);
- }
-
-- if (inaddr && mac && callback_ok)
-+ if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE | NUD_FAILED)) &&
-+ inaddr && mac && callback_ok)
- if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
- callback_ok = 0;
- }
-diff --git a/src/rfc1035.c b/src/rfc1035.c
-index 188d05f..18858a8 100644
---- a/src/rfc1035.c
-+++ b/src/rfc1035.c
-@@ -489,8 +489,8 @@ struct macparm {
- union mysockaddr *l3;
- };
-
--static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
-- int optno, unsigned char *opt, size_t optlen, int set_do)
-+size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
-+ unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do)
- {
- unsigned char *lenp, *datap, *p;
- int rdlen, is_sign;
-@@ -508,7 +508,7 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
- return plen;
- *p++ = 0; /* empty name */
- PUTSHORT(T_OPT, p);
-- PUTSHORT(SAFE_PKTSZ, p); /* max packet length, this will be overwritten */
-+ PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
- PUTSHORT(0, p); /* extended RCODE and version */
- PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
- lenp = p;
-@@ -594,7 +594,7 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
- if (!match)
- return 1; /* continue */
-
-- parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
-+ parm->plen = add_pseudoheader(parm->header, parm->plen, parm->limit, PACKETSZ, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
-
- return 0; /* done */
- }
-@@ -603,12 +603,6 @@ size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysock
- {
- struct macparm parm;
-
--/* Must have an existing pseudoheader as the only ar-record,
-- or have no ar-records. Must also not be signed */
--
-- if (ntohs(header->arcount) > 1)
-- return plen;
--
- parm.header = header;
- parm.limit = (unsigned char *)limit;
- parm.plen = plen;
-@@ -699,13 +693,13 @@ size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, unio
- struct subnet_opt opt;
-
- len = calc_subnet_opt(&opt, source);
-- return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
-+ return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
- }
-
- #ifdef HAVE_DNSSEC
- size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)
- {
-- return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1);
-+ return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1);
- }
- #endif
-
-@@ -1525,16 +1519,16 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
- /* return zero if we can't answer from cache, or packet size if we can */
- size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- struct in_addr local_addr, struct in_addr local_netmask,
-- time_t now, int *ad_reqd, int *do_bit)
-+ time_t now, int ad_reqd, int do_bit, int have_pseudoheader)
- {
- char *name = daemon->namebuff;
-- unsigned char *p, *ansp, *pheader;
-+ unsigned char *p, *ansp;
- unsigned int qtype, qclass;
- struct all_addr addr;
- int nameoffset;
- unsigned short flag;
- int q, ans, anscount = 0, addncount = 0;
-- int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
-+ int dryrun = 0;
- struct crec *crecp;
- int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
- struct mx_srv_record *rec;
-@@ -1550,35 +1544,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- if (header->hb4 & HB4_CD)
- sec_data = 0;
-
-- /* RFC 6840 5.7 */
-- *ad_reqd = header->hb4 & HB4_AD;
-- *do_bit = 0;
--
- /* If there is an additional data section then it will be overwritten by
- partial replies, so we have to do a dry run to see if we can answer
- the query. */
--
- if (ntohs(header->arcount) != 0)
-- {
-- dryrun = 1;
--
-- /* If there's an additional section, there might be an EDNS(0) pseudoheader */
-- if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
-- {
-- unsigned short flags;
--
-- have_pseudoheader = 1;
--
-- pheader += 4; /* udp size, ext_rcode */
-- GETSHORT(flags, pheader);
--
-- if ((sec_reqd = flags & 0x8000))
-- {
-- *do_bit = 1;/* do bit */
-- *ad_reqd = 1;
-- }
-- }
-- }
-+ dryrun = 1;
-
- for (rec = daemon->mxnames; rec; rec = rec->next)
- rec->offset = 0;
-@@ -1603,11 +1573,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- GETSHORT(qtype, p);
- GETSHORT(qclass, p);
-
-- /* Don't filter RRSIGS from answers to ANY queries, even if do-bit
-- not set. */
-- if (qtype == T_ANY)
-- *do_bit = 1;
--
- ans = 0; /* have we answered this question */
-
- if (qtype == T_TXT || qtype == T_ANY)
-@@ -1739,7 +1704,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- the zone is unsigned, which implies that we're doing
- validation. */
- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
-- !sec_reqd ||
-+ !do_bit ||
- (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
- {
- do
-@@ -1927,7 +1892,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
- }
-
- /* If the client asked for DNSSEC don't use cached data. */
-- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
-+ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK))
- do
- {
- /* don't answer wildcard queries with data not from /etc/hosts
-@@ -1961,17 +1926,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
-
- if (crecp->flags & F_NEG)
- {
-- /* We don't cache NSEC records, so if a DNSSEC-validated negative answer
-- is cached and the client wants DNSSEC, forward rather than answering from the cache */
-- if (!sec_reqd || !(crecp->flags & F_DNSSECOK))
-- {
-- ans = 1;
-- auth = 0;
-- if (crecp->flags & F_NXDOMAIN)
-- nxdomain = 1;
-- if (!dryrun)
-- log_query(crecp->flags, name, NULL, NULL);
-- }
-+ ans = 1;
-+ auth = 0;
-+ if (crecp->flags & F_NXDOMAIN)
-+ nxdomain = 1;
-+ if (!dryrun)
-+ log_query(crecp->flags, name, NULL, NULL);
- }
- else
- {
-@@ -2209,10 +2169,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
-
- len = ansp - (unsigned char *)header;
-
-+ /* Advertise our packet size limit in our reply */
- if (have_pseudoheader)
-- len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
-+ len = add_pseudoheader(header, len, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit);
-
-- if (*ad_reqd && sec_data)
-+ if (ad_reqd && sec_data)
- header->hb4 |= HB4_AD;
- else
- header->hb4 &= ~HB4_AD;
-diff --git a/src/rrfilter.c b/src/rrfilter.c
-index ae12261..b26b39f 100644
---- a/src/rrfilter.c
-+++ b/src/rrfilter.c
-@@ -243,7 +243,7 @@ size_t rrfilter(struct dns_header *header, size_t plen, int mode)
- for (p = rrs[0], i = 1; i < rr_found; i += 2)
- {
- unsigned char *start = rrs[i];
-- unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)(header+1)) + plen;
-+ unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen;
-
- memmove(p, start, end-start);
- p += end-start;
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch b/src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch
deleted file mode 100644
index 910921b..0000000
--- a/src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch
+++ /dev/null
@@ -1,262 +0,0 @@
-From d67ecac59d58f249707d26e38d49c29b552af4d8 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Sun, 20 Dec 2015 20:44:23 +0000
-Subject: [PATCH] More tweaks in handling unknown DNSSEC algorithms.
-
----
- src/dnssec.c | 128 +++++++++++++++++++++++++++++-----------------------------
- 1 file changed, 63 insertions(+), 65 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index 299ca64..e09f304 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -70,7 +70,17 @@ static char *algo_digest_name(int algo)
- default: return NULL;
- }
- }
--
-+
-+/*
http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-paramet... */
-+static char *nsec3_digest_name(int digest)
-+{
-+ switch (digest)
-+ {
-+ case 1: return "sha1";
-+ default: return NULL;
-+ }
-+}
-+
- /* Find pointer to correct hash function in nettle library */
- static const struct nettle_hash *hash_find(char *name)
- {
-@@ -667,7 +677,6 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
- static int rrset_sz = 0, sig_sz = 0;
- unsigned char *p;
- int rrsetidx, sigidx, j, rdlen, res;
-- int name_labels = count_labels(name); /* For 4035 5.3.2 check */
- int gotkey = 0;
-
- if (!(p = skip_questions(header, plen)))
-@@ -678,7 +687,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
- j != 0; j--)
- {
- unsigned char *pstart, *pdata;
-- int stype, sclass, algo, type_covered, labels, sig_expiration, sig_inception;
-+ int stype, sclass, type_covered;
-
- pstart = p;
-
-@@ -712,12 +721,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
- return 0; /* bad packet */
-
- GETSHORT(type_covered, p);
-- algo = *p++;
-- labels = *p++;
-- p += 4; /* orig_ttl */
-- GETLONG(sig_expiration, p);
-- GETLONG(sig_inception, p);
-- p += 2; /* key_tag */
-+ p += 16; /* algo, labels, orig_ttl, sig_expiration, sig_inception, key_tag */
-
- if (gotkey)
- {
-@@ -749,11 +753,8 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
- }
- }
-
-- /* Don't count signatures for algos we don't support */
-- if (check_date_range(sig_inception, sig_expiration) &&
-- labels <= name_labels &&
-- type_covered == type &&
-- verify_func(algo))
-+
-+ if (type_covered == type)
- {
- if (!expand_workspace(&sigs, &sig_sz, sigidx))
- return 0;
-@@ -795,7 +796,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
- char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, int algo_in, int keytag_in)
- {
- unsigned char *p;
-- int rdlen, j, name_labels;
-+ int rdlen, j, name_labels, sig_expiration, sig_inception;
- struct crec *crecp = NULL;
- int algo, labels, orig_ttl, key_tag;
- u16 *rr_desc = rrfilter_desc(type);
-@@ -828,13 +829,16 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
- algo = *p++;
- labels = *p++;
- GETLONG(orig_ttl, p);
-- p += 8; /* sig_expiration, sig_inception already checked */
-+ GETLONG(sig_expiration, p);
-+ GETLONG(sig_inception, p);
- GETSHORT(key_tag, p);
-
- if (!extract_name(header, plen, &p, keyname, 1, 0))
- return STAT_BOGUS;
-
-- if (!(hash = hash_find(algo_digest_name(algo))) ||
-+ if (!check_date_range(sig_inception, sig_expiration) ||
-+ labels > name_labels ||
-+ !(hash = hash_find(algo_digest_name(algo))) ||
- !hash_init(hash, &ctx, &digest))
- continue;
-
-@@ -1112,7 +1116,10 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
- else
- {
- a.addr.keytag = keytag;
-- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
-+ if (verify_func(algo))
-+ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
-+ else
-+ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u (not supported)");
-
- recp1->addr.key.keylen = rdlen - 4;
- recp1->addr.key.keydata = key;
-@@ -1235,7 +1242,11 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
- else
- {
- a.addr.keytag = keytag;
-- log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
-+ if (hash_find(ds_digest_name(digest)) && verify_func(algo))
-+ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u");
-+ else
-+ log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %u (not supported)");
-+
- crecp->addr.ds.digest = digest;
- crecp->addr.ds.keydata = key;
- crecp->addr.ds.algo = algo;
-@@ -1660,7 +1671,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- *nons = 1;
-
- /* Look though the NSEC3 records to find the first one with
-- an algorithm we support (currently only algo == 1).
-+ an algorithm we support.
-
- Take the algo, iterations, and salt of that record
- as the ones we're going to use, and prune any
-@@ -1674,7 +1685,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- p += 10; /* type, class, TTL, rdlen */
- algo = *p++;
-
-- if (algo == 1)
-+ if ((hash = hash_find(nsec3_digest_name(algo))))
- break; /* known algo */
- }
-
-@@ -1724,10 +1735,6 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- nsecs[i] = nsec3p;
- }
-
-- /* Algo is checked as 1 above */
-- if (!(hash = hash_find("sha1")))
-- return 0;
--
- if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
- return 0;
-
-@@ -1843,8 +1850,10 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
-
- if (type_found == T_NSEC)
- return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
-- else
-+ else if (type_found == T_NSEC3)
- return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons);
-+ else
-+ return 0;
- }
-
- /* Check signing status of name.
-@@ -1857,7 +1866,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
- */
- static int zone_status(char *name, int class, char *keyname, time_t now)
- {
-- int secure_ds, name_start = strlen(name);
-+ int name_start = strlen(name);
- struct crec *crecp;
- char *p;
-
-@@ -1867,51 +1876,40 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
-
- if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DS)))
- return STAT_NEED_DS;
-+
-+ /* F_DNSSECOK misused in DS cache records to non-existance of NS record.
-+ F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here,
-+ but that's because there's no NS record either, ie this isn't the start
-+ of a zone. We only prove that the DNS tree below a node is unsigned when
-+ we prove that we're at a zone cut AND there's no DS record. */
-+ if (crecp->flags & F_NEG)
-+ {
-+ if (crecp->flags & F_DNSSECOK)
-+ return STAT_INSECURE; /* proved no DS here */
-+ }
- else
- {
-- secure_ds = 0;
--
-+ int gotone = 0;
-+
-+ /* If all the DS records have digest and/or sig algos we don't support,
-+ then the zone is insecure. Note that if an algo
-+ appears in the DS, then RRSIGs for that algo MUST
-+ exist for each RRset: 4035 para 2.2 So if we find
-+ a DS here with digest and sig we can do, we're entitled
-+ to assume we can validate the zone and if we can't later,
-+ because an RRSIG is missing we return BOGUS.
-+ */
- do
- {
-- if (crecp->uid == (unsigned int)class)
-- {
-- /* F_DNSSECOK misused in DS cache records to non-existance of NS record.
-- F_NEG && !F_DNSSECOK implies that we've proved there's no DS record here,
-- but that's because there's no NS record either, ie this isn't the start
-- of a zone. We only prove that the DNS tree below a node is unsigned when
-- we prove that we're at a zone cut AND there's no DS record.
-- */
-- if (crecp->flags & F_NEG)
-- {
-- if (crecp->flags & F_DNSSECOK)
-- return STAT_INSECURE; /* proved no DS here */
-- }
-- else if (!hash_find(ds_digest_name(crecp->addr.ds.digest)) || !verify_func(crecp->addr.ds.algo))
-- return STAT_INSECURE; /* algo we can't use - insecure */
-- else
-- secure_ds = 1;
-- }
-+ if (crecp->uid == (unsigned int)class &&
-+ hash_find(ds_digest_name(crecp->addr.ds.digest)) &&
-+ verify_func(crecp->addr.ds.algo))
-+ gotone = 1;
- }
- while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
-- }
--
-- if (secure_ds)
-- {
-- /* We've found only DS records that attest to the DNSKEY RRset in the zone, so we believe
-- that RRset is good. Furthermore the DNSKEY whose hash is proved by the DS record is
-- one we can use. However the DNSKEY RRset may contain more than one key and
-- one of the other keys may use an algorithm we don't support. If that's
-- the case the zone is insecure for us. */
--
-- if (!(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
-- return STAT_NEED_KEY;
-
-- do
-- {
-- if (crecp->uid == (unsigned int)class && !verify_func(crecp->addr.key.algo))
-- return STAT_INSECURE;
-- }
-- while ((crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY)));
-+ if (!gotone)
-+ return STAT_INSECURE;
- }
-
- if (name_start == 0)
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch b/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch
deleted file mode 100644
index 031339e..0000000
--- a/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 3e86d316c4bb406ed813aa5256615c8a95cac6d8 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Sun, 20 Dec 2015 20:50:05 +0000
-Subject: [PATCH] Nasty, rare and obscure off-by-one in DNSSEC hostname_cmp().
-
----
- src/dnssec.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index e09f304..29848e1 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -1394,8 +1394,8 @@ static int hostname_cmp(const char *a, const char *b)
- if (sb == b)
- return 1;
-
-- ea = sa--;
-- eb = sb--;
-+ ea = --sa;
-+ eb = --sb;
- }
- }
-
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch b/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch
deleted file mode 100644
index f3758fc..0000000
--- a/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From a86fdf437ecc29398f9715ceb5240442a17ac014 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Sun, 20 Dec 2015 21:19:20 +0000
-Subject: [PATCH] Minor tweak to previous commit.
-
----
- src/dnssec.c | 6 ++----
- 1 file changed, 2 insertions(+), 4 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index 29848e1..9fa64b6 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -1889,8 +1889,6 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
- }
- else
- {
-- int gotone = 0;
--
- /* If all the DS records have digest and/or sig algos we don't support,
- then the zone is insecure. Note that if an algo
- appears in the DS, then RRSIGs for that algo MUST
-@@ -1904,11 +1902,11 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
- if (crecp->uid == (unsigned int)class &&
- hash_find(ds_digest_name(crecp->addr.ds.digest)) &&
- verify_func(crecp->addr.ds.algo))
-- gotone = 1;
-+ break;
- }
- while ((crecp = cache_find_by_name(crecp, keyname, now, F_DS)));
-
-- if (!gotone)
-+ if (!crecp)
- return STAT_INSECURE;
- }
-
---
-1.7.10.4
-
diff --git a/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch b/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch
deleted file mode 100644
index 33219d2..0000000
--- a/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From ce5732e84fc46d7f99c152f736cfb4ef5ec98a01 Mon Sep 17 00:00:00 2001
-From: Simon Kelley
simon@thekelleys.org.uk
-Date: Sun, 20 Dec 2015 21:39:19 +0000
-Subject: [PATCH] NSEC3 check: RFC5155 para 8.2
-
----
- src/dnssec.c | 8 ++++++--
- 1 file changed, 6 insertions(+), 2 deletions(-)
-
-diff --git a/src/dnssec.c b/src/dnssec.c
-index 9fa64b6..486e422 100644
---- a/src/dnssec.c
-+++ b/src/dnssec.c
-@@ -1704,7 +1704,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- for (i = 0; i < nsec_count; i++)
- {
- unsigned char *nsec3p = nsecs[i];
-- int this_iter;
-+ int this_iter, flags;
-
- nsecs[i] = NULL; /* Speculative, will be restored if OK. */
-
-@@ -1716,8 +1716,12 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
- if (*p++ != algo)
- continue;
-
-- p++; /* flags */
-+ flags = *p++; /* flags */
-
-+ /* 5155 8.2 */
-+ if (flags != 0 && flags != 1)
-+ continue;
-+
- GETSHORT(this_iter, p);
- if (this_iter != iterations)
- continue;
---
-1.7.10.4
-
hooks/post-receive
--
IPFire 2.x development tree