* Re: [PATCH] dnsmasq: 2.76test10 with latest patches (001-004)
[not found] <1456781959.3052.8.camel@hughes.net>
@ 2016-03-02 23:49 ` Michael Tremer
2016-03-03 19:06 ` Kienker, Fred
0 siblings, 1 reply; 6+ messages in thread
From: Michael Tremer @ 2016-03-02 23:49 UTC (permalink / raw)
To: development
[-- Attachment #1: Type: text/plain, Size: 1289 bytes --]
Hi,
On Mon, 2016-02-29 at 15:39 -0600, Paul Simmons wrote:
> On Mon, 2016-02-29 at 20:22 +0100, Matthias Fischer wrote:
> > Hi,
> >
> > On 28.02.2016 21:19, Michael Tremer wrote:
> > >
> > > I merged this patch and the one after.
> > >
> > > Please give this version a good test as it is a pre-release
> > > version.
> > Its running here.
> >
> > >
> > > You can maintain a branch where you integrate all new changes,
> > > but
> > > I
> > > think it is not required to send every single one to the mailing
> > > list.
> > > It creates a bit of noise and I think that unfortunately nobody
> > > is
> > > testing every single one any ways. Which is sad.
> > Yep. ;-)
> >
> > >
> > > Can we have maybe one aggregated patch after every release of a
> > > Core
> > > Update? So we always have the latest version of dnsmasq in the
> > > updates?
> > I'll do that. If not otherwise wanted, I'll send the current -
> > patched -
> > version after every Core Update.
> >
> > Best,
> > Matthias
>
> @list:
>
> I hope to have an opportunity to test RealSoonNow via an alternate
> boot
> scheme on current hardware.
>
> Do you want me to test the i586 or the x86_64?
Ideally both :) But as we do not release the 64 bit builds at the
moment, i586 is preferred.
Best,
-Michael
>
> Paul
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* RE: [PATCH] dnsmasq: 2.76test10 with latest patches (001-004)
2016-03-02 23:49 ` [PATCH] dnsmasq: 2.76test10 with latest patches (001-004) Michael Tremer
@ 2016-03-03 19:06 ` Kienker, Fred
2016-03-04 12:00 ` Michael Tremer
0 siblings, 1 reply; 6+ messages in thread
From: Kienker, Fred @ 2016-03-03 19:06 UTC (permalink / raw)
To: development
[-- Attachment #1: Type: text/plain, Size: 1672 bytes --]
Is there any chance the x86_64 version will ever be released? If it is a
question of testing, I will volunteer to test it.
Best regards,
Fred Kienker
-----Original Message-----
From: Michael Tremer [mailto:michael.tremer(a)ipfire.org]
Sent: Wednesday, March 02, 2016 6:49 PM
To: Paul Simmons; development(a)lists.ipfire.org
Subject: Re: [PATCH] dnsmasq: 2.76test10 with latest patches (001-004)
Hi,
On Mon, 2016-02-29 at 15:39 -0600, Paul Simmons wrote:
> On Mon, 2016-02-29 at 20:22 +0100, Matthias Fischer wrote:
> > Hi,
> >
> > On 28.02.2016 21:19, Michael Tremer wrote:
> > >
> > > I merged this patch and the one after.
> > >
> > > Please give this version a good test as it is a pre-release
> > > version.
> > Its running here.
> >
> > >
> > > You can maintain a branch where you integrate all new changes, but
> > > I think it is not required to send every single one to the mailing
> > > list.
> > > It creates a bit of noise and I think that unfortunately nobody is
> > > testing every single one any ways. Which is sad.
> > Yep. ;-)
> >
> > >
> > > Can we have maybe one aggregated patch after every release of a
> > > Core Update? So we always have the latest version of dnsmasq in
> > > the updates?
> > I'll do that. If not otherwise wanted, I'll send the current -
> > patched - version after every Core Update.
> >
> > Best,
> > Matthias
>
> @list:
>
> I hope to have an opportunity to test RealSoonNow via an alternate
> boot scheme on current hardware.
>
> Do you want me to test the i586 or the x86_64?
Ideally both :) But as we do not release the 64 bit builds at the
moment, i586 is preferred.
Best,
-Michael
>
> Paul
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] dnsmasq: 2.76test10 with latest patches (001-004)
2016-03-03 19:06 ` Kienker, Fred
@ 2016-03-04 12:00 ` Michael Tremer
0 siblings, 0 replies; 6+ messages in thread
From: Michael Tremer @ 2016-03-04 12:00 UTC (permalink / raw)
To: development
[-- Attachment #1: Type: text/plain, Size: 2114 bytes --]
Hi,
it is indeed a question of testing.
We intend to release it soon, but unfortunately there is only little
man power to work on an other architecture. So contribution is
appreciated.
Best,
-Michael
On Thu, 2016-03-03 at 14:06 -0500, Kienker, Fred wrote:
> Is there any chance the x86_64 version will ever be released? If it
> is a
> question of testing, I will volunteer to test it.
>
> Best regards,
> Fred Kienker
>
> -----Original Message-----
> From: Michael Tremer [mailto:michael.tremer(a)ipfire.org]
> Sent: Wednesday, March 02, 2016 6:49 PM
> To: Paul Simmons; development(a)lists.ipfire.org
> Subject: Re: [PATCH] dnsmasq: 2.76test10 with latest patches (001-
> 004)
>
> Hi,
>
> On Mon, 2016-02-29 at 15:39 -0600, Paul Simmons wrote:
> > On Mon, 2016-02-29 at 20:22 +0100, Matthias Fischer wrote:
> > > Hi,
> > >
> > > On 28.02.2016 21:19, Michael Tremer wrote:
> > > >
> > > > I merged this patch and the one after.
> > > >
> > > > Please give this version a good test as it is a pre-release
> > > > version.
> > > Its running here.
> > >
> > > >
> > > > You can maintain a branch where you integrate all new changes,
> > > > but
>
> > > > I think it is not required to send every single one to the
> > > > mailing
>
> > > > list.
> > > > It creates a bit of noise and I think that unfortunately nobody
> > > > is
>
> > > > testing every single one any ways. Which is sad.
> > > Yep. ;-)
> > >
> > > >
> > > > Can we have maybe one aggregated patch after every release of
> > > > a
> > > > Core Update? So we always have the latest version of dnsmasq
> > > > in
> > > > the updates?
> > > I'll do that. If not otherwise wanted, I'll send the current -
> > > patched - version after every Core Update.
> > >
> > > Best,
> > > Matthias
> >
> > @list:
> >
> > I hope to have an opportunity to test RealSoonNow via an alternate
> > boot scheme on current hardware.
> >
> > Do you want me to test the i586 or the x86_64?
>
> Ideally both :) But as we do not release the 64 bit builds at the
> moment, i586 is preferred.
>
> Best,
> -Michael
>
> >
> > Paul
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] dnsmasq: 2.76test10 with latest patches (001-004)
2016-02-26 17:29 Matthias Fischer
@ 2016-02-28 20:19 ` Michael Tremer
0 siblings, 0 replies; 6+ messages in thread
From: Michael Tremer @ 2016-02-28 20:19 UTC (permalink / raw)
To: development
[-- Attachment #1: Type: text/plain, Size: 322553 bytes --]
Hi,
I merged this patch and the one after.
Please give this version a good test as it is a pre-release version.
You can maintain a branch where you integrate all new changes, but I
think it is not required to send every single one to the mailing list.
It creates a bit of noise and I think that unfortunately nobody is
testing every single one any ways. Which is sad.
Can we have maybe one aggregated patch after every release of a Core
Update? So we always have the latest version of dnsmasq in the updates?
Best,
-Michael
On Fri, 2016-02-26 at 18:29 +0100, Matthias Fischer wrote:
> This is 'dnsmasq 2.76test10', based on current 'next', containing
> latest patches.
>
> Signed-off-by: Matthias Fischer <matthias.fischer(a)ipfire.org>
> ---
> lfs/dnsmasq | 39 +-
> ...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 -
> ...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 -
> 34 files changed, 542 insertions(+), 6603 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_que
> ries_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
> 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.p
> atch
> 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
>
> diff --git a/lfs/dnsmasq b/lfs/dnsmasq
> index 8058663..29d7895 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 @@
>
> include Config
>
> -VER = 2.75
> +VER = 2.76test10
>
> THISAPP = dnsmasq-$(VER)
> DL_FILE = $(THISAPP).tar.xz
> @@ -43,7 +43,7 @@ objects = $(DL_FILE)
>
> $(DL_FILE) = $(DL_FROM)/$(DL_FILE)
>
> -$(DL_FILE)_MD5 = 887236f1ddde6eb57cdb9d01916c9f72
> +$(DL_FILE)_MD5 = 4b51474ed6081b18c61407077f254cf7
>
> install : $(TARGET)
>
> @@ -73,35 +73,10 @@ $(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_que
> ries_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.p
> atch
> - 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-Add-support-to-read-ISC-DHCP-lease-
> file.patch
>
> cd $(DIR_APP) && sed -i src/config.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(a)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>[,<repla
> cement>]
> + 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(a)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(a)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(a)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-subne
> t-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;h=6d9ba490488f80ef565f459cef3c110bdf31212c;hp=1435
> 4f2506a7fbf8360cd32c96e1d7ce1bfeb3f9;hb=e06e6e34bffd781b7cefa49b25fb8
> ae863654ca2;hpb=832e47beab95c2918b5264f0504f2fe6fe523e4c
> +
> +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_que
> ries_set.patch b/src/patches/dnsmasq/003-
> dont_answer_non_auth_queries_for_auth_zones_locally_when_localise_que
> ries_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_que
> ries_set.patch
> +++ /dev/null
> @@ -1,34 +0,0 @@
> -From 3a3965ac21b1b759eab8799b6edb09195b671306 Mon Sep 17 00:00:00
> 2001
> -From: Simon Kelley <simon(a)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(a)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(a)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-
> 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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)darbyshire-bryant.me.u
> k>
> ----
> - 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(a)etorok.ne
> t>
> -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(a)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(a)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(a)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(a)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(cr
> ecp, 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(a)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(a)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/licens
> es/>.
> -+*/
> -+
> -+/* 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(a)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(a)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(a)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(a)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.p
> atch b/src/patches/dnsmasq/024-
> Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.p
> atch
> deleted file mode 100644
> index abcae5c..0000000
> --- a/src/patches/dnsmasq/024-
> Do_a_better_job_of_determining_which_DNSSEC_sig_algos_are_supported.p
> atch
> +++ /dev/null
> @@ -1,145 +0,0 @@
> -From 14a4ae883d51130d33da7133287e8867c64bab65 Mon Sep 17 00:00:00
> 2001
> -From: Simon Kelley <simon(a)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(a)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(a)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-n
> sec3-parameters.xhtml */
> -+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(a)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(a)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(a)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
> -
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] dnsmasq: 2.76test10 with latest patches (001-004)
@ 2016-02-26 17:45 Matthias Fischer
0 siblings, 0 replies; 6+ messages in thread
From: Matthias Fischer @ 2016-02-26 17:45 UTC (permalink / raw)
To: development
[-- Attachment #1: Type: text/plain, Size: 282 bytes --]
Hi,
For explanation - I hope I didn't garble anything...
Thinking it over, I decided to create a new diff-patch for '2.75' =>
'2.76test10', completely based on current 'next' - containing the latest
patches.
This version was tested here and is running right now.
Best,
Matthias
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] dnsmasq: 2.76test10 with latest patches (001-004)
@ 2016-02-26 17:29 Matthias Fischer
2016-02-28 20:19 ` Michael Tremer
0 siblings, 1 reply; 6+ messages in thread
From: Matthias Fischer @ 2016-02-26 17:29 UTC (permalink / raw)
To: development
[-- Attachment #1: Type: text/plain, Size: 280747 bytes --]
This is 'dnsmasq 2.76test10', based on current 'next', containing latest patches.
Signed-off-by: Matthias Fischer <matthias.fischer(a)ipfire.org>
---
lfs/dnsmasq | 39 +-
...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 -
...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 -
34 files changed, 542 insertions(+), 6603 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
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
diff --git a/lfs/dnsmasq b/lfs/dnsmasq
index 8058663..29d7895 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 @@
include Config
-VER = 2.75
+VER = 2.76test10
THISAPP = dnsmasq-$(VER)
DL_FILE = $(THISAPP).tar.xz
@@ -43,7 +43,7 @@ objects = $(DL_FILE)
$(DL_FILE) = $(DL_FROM)/$(DL_FILE)
-$(DL_FILE)_MD5 = 887236f1ddde6eb57cdb9d01916c9f72
+$(DL_FILE)_MD5 = 4b51474ed6081b18c61407077f254cf7
install : $(TARGET)
@@ -73,35 +73,10 @@ $(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-Add-support-to-read-ISC-DHCP-lease-file.patch
cd $(DIR_APP) && sed -i src/config.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(a)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(a)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(a)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(a)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;h=6d9ba490488f80ef565f459cef3c110bdf31212c;hp=14354f2506a7fbf8360cd32c96e1d7ce1bfeb3f9;hb=e06e6e34bffd781b7cefa49b25fb8ae863654ca2;hpb=832e47beab95c2918b5264f0504f2fe6fe523e4c
+
+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(a)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(a)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(a)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-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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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(a)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-parameters.xhtml */
-+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(a)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(a)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(a)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
-
--
2.7.2
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2016-03-04 12:00 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <1456781959.3052.8.camel@hughes.net>
2016-03-02 23:49 ` [PATCH] dnsmasq: 2.76test10 with latest patches (001-004) Michael Tremer
2016-03-03 19:06 ` Kienker, Fred
2016-03-04 12:00 ` Michael Tremer
2016-02-26 17:45 Matthias Fischer
-- strict thread matches above, loose matches on Subject: below --
2016-02-26 17:29 Matthias Fischer
2016-02-28 20:19 ` Michael Tremer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox