mbox

dnsmasq 2.75: latest patches from upstream

Message ID 1450948636-31650-1-git-send-email-matthias.fischer@ipfire.org
State Accepted
Commit c3b4c861f5531712b6579cae839ea621f5336950
Headers

Message

Matthias Fischer Dec. 24, 2015, 8:17 p.m. UTC
  Same procedure as... :-)

Best to all for xmas and 2016!

Matthias

Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
---
 lfs/dnsmasq                                        |   5 +
 ...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 ++
 6 files changed, 1015 insertions(+)
 create mode 100644 src/patches/dnsmasq/025-Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch
 create mode 100644 src/patches/dnsmasq/026-More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch
 create mode 100644 src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch
 create mode 100644 src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch
 create mode 100644 src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch
  

Comments

Michael Tremer Dec. 29, 2015, 1:40 a.m. UTC | #1
Merged.

Can we have these updates like once per Core Update?

Best,
-Michael

On Thu, 2015-12-24 at 10:17 +0100, Matthias Fischer wrote:
> Same procedure as... :-)
> 
> Best to all for xmas and 2016!
> 
> Matthias
> 
> Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
> ---
>  lfs/dnsmasq                                        |   5 +
>  ...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 ++
>  6 files changed, 1015 insertions(+)
>  create mode 100644 src/patches/dnsmasq/025-
> Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch
>  create mode 100644 src/patches/dnsmasq/026-
> More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch
>  create mode 100644 src/patches/dnsmasq/027-
> Nasty_rare_and_obscure_off-by-one_in_DNSSEC_hostname_cmp.patch
>  create mode 100644 src/patches/dnsmasq/028-
> Minor_tweak_to_previous_commit.patch
>  create mode 100644 src/patches/dnsmasq/029-
> NSEC3_check_RFC5155_para_8_2.patch
> 
> diff --git a/lfs/dnsmasq b/lfs/dnsmasq
> index c8fd7db..8058663 100644
> --- a/lfs/dnsmasq
> +++ b/lfs/dnsmasq
> @@ -97,6 +97,11 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
>  	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-Add-support-to-read-ISC-DHCP-lease-
> file.patch
>  
>  	cd $(DIR_APP) && sed -i src/config.h \
> 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
> new file mode 100644
> index 0000000..c016e73
> --- /dev/null
> +++ b/src/patches/dnsmasq/025-
> Major_tidy_up_of_EDNS0_handling_and_computation_use_of_udp.patch
> @@ -0,0 +1,643 @@
> +From fa14bec83b2db010fd076910fddab56957b9375d Mon Sep 17 00:00:00
> 2001
> +From: Simon Kelley <simon@thekelleys.org.uk>
> +Date: Sun, 20 Dec 2015 17:12:16 +0000
> +Subject: [PATCH] Major tidy up of EDNS0 handling and computation/use
> of udp
> + packet size.
> +
> +---
> + src/auth.c     |    8 ++-
> + src/dnsmasq.h  |    7 ++-
> + src/dnssec.c   |    1 -
> + src/forward.c  |  184 ++++++++++++++++++++++++++++++++++++++++-----
> -----------
> + src/netlink.c  |    3 +-
> + src/rfc1035.c  |   81 +++++++------------------
> + src/rrfilter.c |    2 +-
> + 7 files changed, 168 insertions(+), 118 deletions(-)
> +
> +diff --git a/src/auth.c b/src/auth.c
> +index 2b0b7d6..85bd5e7 100644
> +--- a/src/auth.c
> ++++ b/src/auth.c
> +@@ -81,7 +81,8 @@ int in_zone(struct auth_zone *zone, char *name,
> char **cut)
> + }
> + 
> + 
> +-size_t answer_auth(struct dns_header *header, char *limit, size_t
> qlen, time_t now, union mysockaddr *peer_addr, int local_query) 
> ++size_t answer_auth(struct dns_header *header, char *limit, size_t
> qlen, time_t now, union mysockaddr *peer_addr, 
> ++		   int local_query, int do_bit, int
> have_pseudoheader) 
> + {
> +   char *name = daemon->namebuff;
> +   unsigned char *p, *ansp;
> +@@ -820,6 +821,11 @@ size_t answer_auth(struct dns_header *header,
> char *limit, size_t qlen, time_t n
> +   header->ancount = htons(anscount);
> +   header->nscount = htons(authcount);
> +   header->arcount = htons(0);
> ++
> ++  /* Advertise our packet size limit in our reply */
> ++  if (have_pseudoheader)
> ++    return add_pseudoheader(header,  ansp - (unsigned char
> *)header, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0,
> do_bit);
> ++
> +   return ansp - (unsigned char *)header;
> + }
> +   
> +diff --git a/src/dnsmasq.h b/src/dnsmasq.h
> +index 39a930c..abb34c5 100644
> +--- a/src/dnsmasq.h
> ++++ b/src/dnsmasq.h
> +@@ -1113,7 +1113,7 @@ int extract_addresses(struct dns_header
> *header, size_t qlen, char *namebuff,
> + 		      int no_cache, int secure, int *doctored);
> + size_t answer_request(struct dns_header *header, char *limit,
> size_t qlen,  
> + 		      struct in_addr local_addr, struct in_addr
> local_netmask, 
> +-		      time_t now, int *ad_reqd, int *do_bit);
> ++		      time_t now, int ad_reqd, int do_bit, int
> have_pseudoheader);
> + int check_for_bogus_wildcard(struct dns_header *header, size_t
> qlen, char *name, 
> + 			     struct bogus_addr *addr, time_t now);
> + int check_for_ignored_address(struct dns_header *header, size_t
> qlen, struct bogus_addr *baddr);
> +@@ -1123,6 +1123,8 @@ int check_for_local_domain(char *name, time_t
> now);
> + unsigned int questions_crc(struct dns_header *header, size_t plen,
> char *buff);
> + size_t resize_packet(struct dns_header *header, size_t plen, 
> + 		  unsigned char *pheader, size_t hlen);
> ++size_t add_pseudoheader(struct dns_header *header, size_t plen,
> unsigned char *limit, 
> ++			unsigned short udp_sz, int optno, unsigned
> char *opt, size_t optlen, int set_do);
> + size_t add_mac(struct dns_header *header, size_t plen, char *limit,
> union mysockaddr *l3);
> + size_t add_source_addr(struct dns_header *header, size_t plen, char
> *limit, union mysockaddr *source);
> + #ifdef HAVE_DNSSEC
> +@@ -1141,7 +1143,8 @@ int private_net(struct in_addr addr, int
> ban_localhost);
> + /* auth.c */
> + #ifdef HAVE_AUTH
> + size_t answer_auth(struct dns_header *header, char *limit, size_t
> qlen, 
> +-		   time_t now, union mysockaddr *peer_addr, int
> local_query);
> ++		   time_t now, union mysockaddr *peer_addr, int
> local_query,
> ++		   int do_bit, int have_pseudoheader);
> + int in_zone(struct auth_zone *zone, char *name, char **cut);
> + #endif
> + 
> +diff --git a/src/dnssec.c b/src/dnssec.c
> +index 82394ee..299ca64 100644
> +--- a/src/dnssec.c
> ++++ b/src/dnssec.c
> +@@ -67,7 +67,6 @@ static char *algo_digest_name(int algo)
> +     case 12: return "gosthash94";
> +     case 13: return "sha256";
> +     case 14: return "sha384";
> +-
> +     default: return NULL;
> +     }
> + }
> +diff --git a/src/forward.c b/src/forward.c
> +index 3e801c8..041353c 100644
> +--- a/src/forward.c
> ++++ b/src/forward.c
> +@@ -244,7 +244,6 @@ static int forward_query(int udpfd, union
> mysockaddr *udpaddr,
> +   void *hash = &crc;
> + #endif
> +  unsigned int gotname = extract_request(header, plen, daemon-
> >namebuff, NULL);
> +- unsigned char *pheader;
> + 
> +  (void)do_bit;
> + 
> +@@ -264,7 +263,8 @@ static int forward_query(int udpfd, union
> mysockaddr *udpaddr,
> + 	 there's no point retrying the query, retry the key query
> instead...... */
> +       if (forward->blocking_query)
> + 	{
> +-	  int fd;
> ++	  int fd, is_sign;
> ++	  unsigned char *pheader;
> + 	  
> + 	  forward->flags &= ~FREC_TEST_PKTSZ;
> + 	  
> +@@ -276,8 +276,8 @@ static int forward_query(int udpfd, union
> mysockaddr *udpaddr,
> + 	  blockdata_retrieve(forward->stash, forward->stash_len,
> (void *)header);
> + 	  plen = forward->stash_len;
> + 	  
> +-	  if (find_pseudoheader(header, plen, NULL, &pheader,
> NULL))
> +-	    PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ?
> SAFE_PKTSZ : forward->sentto->edns_pktsz, pheader);
> ++	  if (find_pseudoheader(header, plen, NULL, &pheader,
> &is_sign) && !is_sign)
> ++	    PUTSHORT(SAFE_PKTSZ, pheader);
> + 
> + 	  if (forward->sentto->addr.sa.sa_family == AF_INET) 
> + 	    log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry",
> (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
> +@@ -394,32 +394,40 @@ static int forward_query(int udpfd, union
> mysockaddr *udpaddr,
> +       forward->log_id = daemon->log_id;
> +       
> +       if (option_bool(OPT_ADD_MAC))
> +-	plen = add_mac(header, plen, ((char *) header) + daemon-
> >packet_buff_sz, &forward->source);
> +-      
> ++	{
> ++	  size_t new = add_mac(header, plen, ((char *) header) +
> daemon->packet_buff_sz, &forward->source);
> ++	  if (new != plen)
> ++	    {
> ++	      plen = new;
> ++	      forward->flags |= FREC_ADDED_PHEADER;
> ++	    }
> ++	}
> ++
> +       if (option_bool(OPT_CLIENT_SUBNET))
> + 	{
> + 	  size_t new = add_source_addr(header, plen, ((char *)
> header) + daemon->packet_buff_sz, &forward->source); 
> + 	  if (new != plen)
> + 	    {
> + 	      plen = new;
> +-	      forward->flags |= FREC_HAS_SUBNET;
> ++	      forward->flags |= FREC_HAS_SUBNET |
> FREC_ADDED_PHEADER;
> + 	    }
> + 	}
> + 
> + #ifdef HAVE_DNSSEC
> +       if (option_bool(OPT_DNSSEC_VALID))
> + 	{
> +-	  size_t new_plen = add_do_bit(header, plen, ((char *)
> header) + daemon->packet_buff_sz);
> ++	  size_t new = add_do_bit(header, plen, ((char *) header) +
> daemon->packet_buff_sz);
> + 	 
> ++	  if (new != plen)
> ++	    forward->flags |= FREC_ADDED_PHEADER;
> ++
> ++	  plen = new;
> ++	      
> + 	  /* For debugging, set Checking Disabled, otherwise, have
> the upstream check too,
> + 	     this allows it to select auth servers when one is
> returning bad data. */
> + 	  if (option_bool(OPT_DNSSEC_DEBUG))
> + 	    header->hb4 |= HB4_CD;
> + 
> +-	  if (new_plen != plen)
> +-	    forward->flags |= FREC_ADDED_PHEADER;
> +-
> +-	  plen = new_plen;
> + 	}
> + #endif
> +       
> +@@ -469,10 +477,23 @@ static int forward_query(int udpfd, union
> mysockaddr *udpaddr,
> + 		    }
> + #endif
> + 		}
> +-
> +-	      if (find_pseudoheader(header, plen, NULL, &pheader,
> NULL))
> +-		PUTSHORT((forward->flags & FREC_TEST_PKTSZ) ?
> SAFE_PKTSZ : start->edns_pktsz, pheader);
> + 	      
> ++#ifdef HAVE_DNSSEC
> ++	      if (option_bool(OPT_DNSSEC_VALID) && !do_bit)
> ++		{
> ++		  /* Difficult one here. If our client didn't send
> EDNS0, we will have set the UDP
> ++		     packet size to 512. But that won't provide
> space for the RRSIGS in many cases.
> ++		     The RRSIGS will be stripped out before the
> answer goes back, so the packet should
> ++		     shrink again. So, if we added a do-bit, bump
> the udp packet size to the value
> ++		     known to be OK for this server. Maybe check
> returned size after stripping and set
> ++		     the truncated bit? */		  
> ++		  unsigned char *pheader;
> ++		  int is_sign;
> ++		  if (find_pseudoheader(header, plen, NULL,
> &pheader, &is_sign))
> ++		    PUTSHORT(start->edns_pktsz, pheader);
> ++		}
> ++#endif
> ++
> + 	      if (retry_send(sendto(fd, (char *)header, plen, 0,
> + 				    &start->addr.sa,
> + 				    sa_len(&start->addr))))
> +@@ -563,30 +584,34 @@ static size_t process_reply(struct dns_header
> *header, time_t now, struct server
> +     }
> + #endif
> +   
> +-  /* If upstream is advertising a larger UDP packet size
> +-     than we allow, trim it so that we don't get overlarge
> +-     requests for the client. We can't do this for signed packets.
> */
> +-
> +   if ((pheader = find_pseudoheader(header, n, &plen, &sizep,
> &is_sign)))
> +     {
> +-      unsigned short udpsz;
> +-      unsigned char *psave = sizep;
> +-      
> +-      GETSHORT(udpsz, sizep);
> +-
> +-      if (!is_sign && udpsz > daemon->edns_pktsz)
> +-	PUTSHORT(daemon->edns_pktsz, psave);
> +-      
> +       if (check_subnet && !check_source(header, plen, pheader,
> query_source))
> + 	{
> + 	  my_syslog(LOG_WARNING, _("discarding DNS reply: subnet
> option mismatch"));
> + 	  return 0;
> + 	}
> +       
> +-      if (added_pheader)
> ++      if (!is_sign)
> + 	{
> +-	  pheader = 0; 
> +-	  header->arcount = htons(0);
> ++	  if (added_pheader)
> ++	    {
> ++	      /* client didn't send EDNS0, we added one, strip it
> off before returning answer. */
> ++	      n = rrfilter(header, n, 0);
> ++	      pheader = NULL;
> ++	    }
> ++	  else
> ++	    {
> ++	      /* If upstream is advertising a larger UDP packet
> size
> ++		 than we allow, trim it so that we don't get
> overlarge
> ++		 requests for the client. We can't do this for
> signed packets. */
> ++	      unsigned short udpsz;
> ++	      unsigned char *psave = sizep;
> ++	      
> ++	      GETSHORT(udpsz, sizep);
> ++	      if (udpsz > daemon->edns_pktsz)
> ++		PUTSHORT(daemon->edns_pktsz, psave);
> ++	    }
> + 	}
> +     }
> +   
> +@@ -655,14 +680,16 @@ static size_t process_reply(struct dns_header
> *header, time_t now, struct server
> +     }
> + 
> +   if (option_bool(OPT_DNSSEC_VALID))
> +-    header->hb4 &= ~HB4_AD;
> +-  
> +-  if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
> +-    header->hb4 |= HB4_AD;
> +-
> +-  /* If the requestor didn't set the DO bit, don't return DNSSEC
> info. */
> +-  if (!do_bit)
> +-    n = rrfilter(header, n, 1);
> ++    {
> ++      header->hb4 &= ~HB4_AD;
> ++      
> ++      if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
> ++	header->hb4 |= HB4_AD;
> ++      
> ++      /* If the requestor didn't set the DO bit, don't return
> DNSSEC info. */
> ++      if (!do_bit)
> ++	n = rrfilter(header, n, 1);
> ++    }
> + #endif
> + 
> +   /* do this after extract_addresses. Ensure NODATA reply and
> remove
> +@@ -761,8 +788,14 @@ void reply_query(int fd, int family, time_t
> now)
> + 	  if ((nn = resize_packet(header, (size_t)n, pheader,
> plen)))
> + 	    {
> + 	      header->hb3 &= ~(HB3_QR | HB3_AA | HB3_TC);
> +-	      header->hb4 &= ~(HB4_RA | HB4_RCODE);
> +-	      forward_query(-1, NULL, NULL, 0, header, nn, now,
> forward, 0, 0);
> ++	      header->hb4 &= ~(HB4_RA | HB4_RCODE | HB4_CD |
> HB4_AD);
> ++	      if (forward->flags |= FREC_CHECKING_DISABLED)
> ++		header->hb4 |= HB4_CD;
> ++	      if (forward->flags |= FREC_AD_QUESTION)
> ++		header->hb4 |= HB4_AD;
> ++	      if (forward->flags & FREC_DO_QUESTION)
> ++		add_do_bit(header, nn,  (char *)pheader + plen);
> ++	      forward_query(-1, NULL, NULL, 0, header, nn, now,
> forward, forward->flags & FREC_AD_QUESTION, forward->flags &
> FREC_DO_QUESTION);
> + 	      return;
> + 	    }
> + 	}
> +@@ -1007,12 +1040,13 @@ void receive_query(struct listener *listen,
> time_t now)
> + {
> +   struct dns_header *header = (struct dns_header *)daemon->packet;
> +   union mysockaddr source_addr;
> +-  unsigned short type;
> ++  unsigned char *pheader;
> ++  unsigned short type, udp_size = PACKETSZ; /* default if no EDNS0
> */
> +   struct all_addr dst_addr;
> +   struct in_addr netmask, dst_addr_4;
> +   size_t m;
> +   ssize_t n;
> +-  int if_index = 0, auth_dns = 0;
> ++  int if_index = 0, auth_dns = 0, do_bit = 0, have_pseudoheader =
> 0;
> + #ifdef HAVE_AUTH
> +   int local_auth = 0;
> + #endif
> +@@ -1279,10 +1313,30 @@ void receive_query(struct listener *listen,
> time_t now)
> + #endif
> +     }
> +   
> ++  if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL))
> ++    { 
> ++      unsigned short flags;
> ++      
> ++      have_pseudoheader = 1;
> ++      GETSHORT(udp_size, pheader);
> ++      pheader += 2; /* ext_rcode */
> ++      GETSHORT(flags, pheader);
> ++      
> ++      if (flags & 0x8000)
> ++	do_bit = 1;/* do bit */ 
> ++	
> ++      /* If the client provides an EDNS0 UDP size, use that to
> limit our reply.
> ++	 (bounded by the maximum configured). If no EDNS0, then it
> ++	 defaults to 512 */
> ++      if (udp_size > daemon->edns_pktsz)
> ++	udp_size = daemon->edns_pktsz;
> ++    }
> ++
> + #ifdef HAVE_AUTH
> +   if (auth_dns)
> +     {
> +-      m = answer_auth(header, ((char *) header) + daemon-
> >packet_buff_sz, (size_t)n, now, &source_addr, local_auth);
> ++      m = answer_auth(header, ((char *) header) + udp_size,
> (size_t)n, now, &source_addr, 
> ++		      local_auth, do_bit, have_pseudoheader);
> +       if (m >= 1)
> + 	{
> + 	  send_from(listen->fd, option_bool(OPT_NOWILD) ||
> option_bool(OPT_CLEVERBIND),
> +@@ -1293,9 +1347,13 @@ void receive_query(struct listener *listen,
> time_t now)
> +   else
> + #endif
> +     {
> +-      int ad_reqd, do_bit;
> +-      m = answer_request(header, ((char *) header) + daemon-
> >packet_buff_sz, (size_t)n, 
> +-			 dst_addr_4, netmask, now, &ad_reqd,
> &do_bit);
> ++      int ad_reqd = do_bit;
> ++       /* RFC 6840 5.7 */
> ++      if (header->hb4 & HB4_AD)
> ++	ad_reqd = 1;
> ++
> ++      m = answer_request(header, ((char *) header) + udp_size,
> (size_t)n, 
> ++			 dst_addr_4, netmask, now, ad_reqd, do_bit,
> have_pseudoheader);
> +       
> +       if (m >= 1)
> + 	{
> +@@ -1397,7 +1455,7 @@ unsigned char *tcp_request(int confd, time_t
> now,
> + #ifdef HAVE_AUTH
> +   int local_auth = 0;
> + #endif
> +-  int checking_disabled, ad_question, do_bit, added_pheader = 0;
> ++  int checking_disabled, do_bit, added_pheader = 0,
> have_pseudoheader = 0;
> +   int check_subnet, no_cache_dnssec = 0, cache_secure = 0,
> bogusanswer = 0;
> +   size_t m;
> +   unsigned short qtype;
> +@@ -1414,6 +1472,7 @@ unsigned char *tcp_request(int confd, time_t
> now,
> +   union mysockaddr peer_addr;
> +   socklen_t peer_len = sizeof(union mysockaddr);
> +   int query_count = 0;
> ++  unsigned char *pheader;
> + 
> +   if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len)
> == -1)
> +     return packet;
> +@@ -1508,15 +1567,35 @@ unsigned char *tcp_request(int confd, time_t
> now,
> +       else
> + 	dst_addr_4.s_addr = 0;
> +       
> ++      do_bit = 0;
> ++
> ++      if (find_pseudoheader(header, (size_t)size, NULL, &pheader,
> NULL))
> ++	{ 
> ++	  unsigned short flags;
> ++	  
> ++	  have_pseudoheader = 1;
> ++	  pheader += 4; /* udp_size, ext_rcode */
> ++	  GETSHORT(flags, pheader);
> ++      
> ++	  if (flags & 0x8000)
> ++	    do_bit = 1;/* do bit */ 
> ++	}
> ++
> + #ifdef HAVE_AUTH
> +       if (auth_dns)
> +-	m = answer_auth(header, ((char *) header) + 65536,
> (size_t)size, now, &peer_addr, local_auth);
> ++	m = answer_auth(header, ((char *) header) + 65536,
> (size_t)size, now, &peer_addr, 
> ++			local_auth, do_bit, have_pseudoheader);
> +       else
> + #endif
> + 	{
> +-	  /* m > 0 if answered from cache */
> +-	  m = answer_request(header, ((char *) header) + 65536,
> (size_t)size, 
> +-			     dst_addr_4, netmask, now,
> &ad_question, &do_bit);
> ++	   int ad_reqd = do_bit;
> ++	   /* RFC 6840 5.7 */
> ++	   if (header->hb4 & HB4_AD)
> ++	     ad_reqd = 1;
> ++	   
> ++	   /* m > 0 if answered from cache */
> ++	   m = answer_request(header, ((char *) header) + 65536,
> (size_t)size, 
> ++			      dst_addr_4, netmask, now, ad_reqd,
> do_bit, have_pseudoheader);
> + 	  
> + 	  /* Do this by steam now we're not in the select() loop */
> + 	  check_log_writer(1); 
> +@@ -1615,6 +1694,7 @@ unsigned char *tcp_request(int confd, time_t
> now,
> + 			    }
> + 			  
> + #ifdef HAVE_DNSSEC
> ++			  added_pheader = 0;			
>   
> + 			  if (option_bool(OPT_DNSSEC_VALID))
> + 			    {
> + 			      size_t new_size = add_do_bit(header,
> size, ((char *) header) + 65536);
> +@@ -1719,7 +1799,7 @@ unsigned char *tcp_request(int confd, time_t
> now,
> + 
> + 		      m = process_reply(header, now, last_server,
> (unsigned int)m, 
> + 					option_bool(OPT_NO_REBIND)
> && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
> +-					ad_question, do_bit,
> added_pheader, check_subnet, &peer_addr); 
> ++					ad_reqd, do_bit,
> added_pheader, check_subnet, &peer_addr); 
> + 		      
> + 		      break;
> + 		    }
> +diff --git a/src/netlink.c b/src/netlink.c
> +index 753784d..3376d68 100644
> +--- a/src/netlink.c
> ++++ b/src/netlink.c
> +@@ -288,7 +288,8 @@ int iface_enumerate(int family, void *parm, int
> (*callback)())
> + 		rta = RTA_NEXT(rta, len1);
> + 	      }
> + 
> +-	    if (inaddr && mac && callback_ok)
> ++	    if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE |
> NUD_FAILED)) &&
> ++		inaddr && mac && callback_ok)
> + 	      if (!((*callback)(neigh->ndm_family, inaddr, mac,
> maclen, parm)))
> + 		callback_ok = 0;
> + 	  }
> +diff --git a/src/rfc1035.c b/src/rfc1035.c
> +index 188d05f..18858a8 100644
> +--- a/src/rfc1035.c
> ++++ b/src/rfc1035.c
> +@@ -489,8 +489,8 @@ struct macparm {
> +   union mysockaddr *l3;
> + };
> +  
> +-static size_t add_pseudoheader(struct dns_header *header, size_t
> plen, unsigned char *limit, 
> +-			       int optno, unsigned char *opt,
> size_t optlen, int set_do)
> ++size_t add_pseudoheader(struct dns_header *header, size_t plen,
> unsigned char *limit, 
> ++			unsigned short udp_sz, int optno, unsigned
> char *opt, size_t optlen, int set_do)
> + { 
> +   unsigned char *lenp, *datap, *p;
> +   int rdlen, is_sign;
> +@@ -508,7 +508,7 @@ static size_t add_pseudoheader(struct dns_header
> *header, size_t plen, unsigned
> + 	return plen;
> +       *p++ = 0; /* empty name */
> +       PUTSHORT(T_OPT, p);
> +-      PUTSHORT(SAFE_PKTSZ, p); /* max packet length, this will be
> overwritten */
> ++      PUTSHORT(udp_sz, p); /* max packet length, 512 if not given
> in EDNS0 header */
> +       PUTSHORT(0, p);    /* extended RCODE and version */
> +       PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
> +       lenp = p;
> +@@ -594,7 +594,7 @@ static int filter_mac(int family, char *addrp,
> char *mac, size_t maclen, void *p
> +   if (!match)
> +     return 1; /* continue */
> + 
> +-  parm->plen = add_pseudoheader(parm->header, parm->plen, parm-
> >limit,  EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
> ++  parm->plen = add_pseudoheader(parm->header, parm->plen, parm-
> >limit, PACKETSZ, EDNS0_OPTION_MAC, (unsigned char *)mac, maclen, 0);
> +   
> +   return 0; /* done */
> + }	      
> +@@ -603,12 +603,6 @@ size_t add_mac(struct dns_header *header,
> size_t plen, char *limit, union mysock
> + {
> +   struct macparm parm;
> +      
> +-/* Must have an existing pseudoheader as the only ar-record, 
> +-   or have no ar-records. Must also not be signed */
> +-   
> +-  if (ntohs(header->arcount) > 1)
> +-    return plen;
> +-
> +   parm.header = header;
> +   parm.limit = (unsigned char *)limit;
> +   parm.plen = plen;
> +@@ -699,13 +693,13 @@ size_t add_source_addr(struct dns_header
> *header, size_t plen, char *limit, unio
> +   struct subnet_opt opt;
> +   
> +   len = calc_subnet_opt(&opt, source);
> +-  return add_pseudoheader(header, plen, (unsigned char *)limit,
> EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
> ++  return add_pseudoheader(header, plen, (unsigned char *)limit,
> PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0);
> + }
> + 
> + #ifdef HAVE_DNSSEC
> + size_t add_do_bit(struct dns_header *header, size_t plen, char
> *limit)
> + {
> +-  return add_pseudoheader(header, plen, (unsigned char *)limit, 0,
> NULL, 0, 1);
> ++  return add_pseudoheader(header, plen, (unsigned char *)limit,
> PACKETSZ, 0, NULL, 0, 1);
> + }
> + #endif
> + 
> +@@ -1525,16 +1519,16 @@ static unsigned long crec_ttl(struct crec
> *crecp, time_t now)
> + /* return zero if we can't answer from cache, or packet size if we
> can */
> + size_t answer_request(struct dns_header *header, char *limit,
> size_t qlen,  
> + 		      struct in_addr local_addr, struct in_addr
> local_netmask, 
> +-		      time_t now, int *ad_reqd, int *do_bit) 
> ++		      time_t now, int ad_reqd, int do_bit, int
> have_pseudoheader) 
> + {
> +   char *name = daemon->namebuff;
> +-  unsigned char *p, *ansp, *pheader;
> ++  unsigned char *p, *ansp;
> +   unsigned int qtype, qclass;
> +   struct all_addr addr;
> +   int nameoffset;
> +   unsigned short flag;
> +   int q, ans, anscount = 0, addncount = 0;
> +-  int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
> ++  int dryrun = 0;
> +   struct crec *crecp;
> +   int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
> +   struct mx_srv_record *rec;
> +@@ -1550,35 +1544,11 @@ size_t answer_request(struct dns_header
> *header, char *limit, size_t qlen,
> +   if (header->hb4 & HB4_CD)
> +     sec_data = 0;
> +   
> +-  /* RFC 6840 5.7 */
> +-  *ad_reqd = header->hb4 & HB4_AD;
> +-  *do_bit = 0;
> +-
> +   /* If there is an  additional data section then it will be
> overwritten by
> +      partial replies, so we have to do a dry run to see if we can
> answer
> +      the query. */
> +-
> +   if (ntohs(header->arcount) != 0)
> +-    {
> +-      dryrun = 1;
> +-
> +-      /* If there's an additional section, there might be an
> EDNS(0) pseudoheader */
> +-      if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
> +-	{ 
> +-	  unsigned short flags;
> +-	  
> +-	  have_pseudoheader = 1;
> +-	  
> +-	  pheader += 4; /* udp size, ext_rcode */
> +-	  GETSHORT(flags, pheader);
> +-	  
> +-	  if ((sec_reqd = flags & 0x8000))
> +-	    {
> +-	      *do_bit = 1;/* do bit */ 
> +-	      *ad_reqd = 1;
> +-	    }
> +-	}
> +-    }
> ++    dryrun = 1;
> + 
> +   for (rec = daemon->mxnames; rec; rec = rec->next)
> +     rec->offset = 0;
> +@@ -1603,11 +1573,6 @@ size_t answer_request(struct dns_header
> *header, char *limit, size_t qlen,
> +       GETSHORT(qtype, p); 
> +       GETSHORT(qclass, p);
> + 
> +-      /* Don't filter RRSIGS from answers to ANY queries, even if
> do-bit
> +-	 not set. */
> +-      if (qtype == T_ANY)
> +-	*do_bit = 1;
> +-
> +       ans = 0; /* have we answered this question */
> +       
> +       if (qtype == T_TXT || qtype == T_ANY)
> +@@ -1739,7 +1704,7 @@ size_t answer_request(struct dns_header
> *header, char *limit, size_t qlen,
> + 		     the zone is unsigned, which implies that we're
> doing
> + 		     validation. */
> + 		  if ((crecp->flags & (F_HOSTS | F_DHCP |
> F_CONFIG)) || 
> +-		      !sec_reqd || 
> ++		      !do_bit || 
> + 		      (option_bool(OPT_DNSSEC_VALID) && !(crecp-
> >flags & F_DNSSECOK)))
> + 		    {
> + 		      do 
> +@@ -1927,7 +1892,7 @@ size_t answer_request(struct dns_header
> *header, char *limit, size_t qlen,
> + 		    }
> + 
> + 		  /* If the client asked for DNSSEC  don't use
> cached data. */
> +-		  if ((crecp->flags & (F_HOSTS | F_DHCP |
> F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
> ++		  if ((crecp->flags & (F_HOSTS | F_DHCP |
> F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK))
> + 		    do
> + 		      { 
> + 			/* don't answer wildcard queries with data
> not from /etc/hosts
> +@@ -1961,17 +1926,12 @@ size_t answer_request(struct dns_header
> *header, char *limit, size_t qlen,
> + 			
> + 			if (crecp->flags & F_NEG)
> + 			  {
> +-			    /* We don't cache NSEC records, so if a
> DNSSEC-validated negative answer
> +-			       is cached and the client wants
> DNSSEC, forward rather than answering from the cache */
> +-			    if (!sec_reqd || !(crecp->flags &
> F_DNSSECOK))
> +-			      {
> +-				ans = 1;
> +-				auth = 0;
> +-				if (crecp->flags & F_NXDOMAIN)
> +-				  nxdomain = 1;
> +-				if (!dryrun)
> +-				  log_query(crecp->flags, name,
> NULL, NULL);
> +-			      }
> ++			    ans = 1;
> ++			    auth = 0;
> ++			    if (crecp->flags & F_NXDOMAIN)
> ++			      nxdomain = 1;
> ++			    if (!dryrun)
> ++			      log_query(crecp->flags, name, NULL,
> NULL);
> + 			  }
> + 			else 
> + 			  {
> +@@ -2209,10 +2169,11 @@ size_t answer_request(struct dns_header
> *header, char *limit, size_t qlen,
> + 
> +   len = ansp - (unsigned char *)header;
> +   
> ++  /* Advertise our packet size limit in our reply */
> +   if (have_pseudoheader)
> +-    len = add_pseudoheader(header, len, (unsigned char *)limit, 0,
> NULL, 0, sec_reqd);
> ++    len = add_pseudoheader(header, len, (unsigned char *)limit,
> daemon->edns_pktsz, 0, NULL, 0, do_bit);
> +   
> +-  if (*ad_reqd && sec_data)
> ++  if (ad_reqd && sec_data)
> +     header->hb4 |= HB4_AD;
> +   else
> +     header->hb4 &= ~HB4_AD;
> +diff --git a/src/rrfilter.c b/src/rrfilter.c
> +index ae12261..b26b39f 100644
> +--- a/src/rrfilter.c
> ++++ b/src/rrfilter.c
> +@@ -243,7 +243,7 @@ size_t rrfilter(struct dns_header *header,
> size_t plen, int mode)
> +   for (p = rrs[0], i = 1; i < rr_found; i += 2)
> +     {
> +       unsigned char *start = rrs[i];
> +-      unsigned char *end = (i != rr_found - 1) ? rrs[i+1] :
> ((unsigned char *)(header+1)) + plen;
> ++      unsigned char *end = (i != rr_found - 1) ? rrs[i+1] :
> ((unsigned char *)header) + plen;
> +       
> +       memmove(p, start, end-start);
> +       p += end-start;
> +-- 
> +1.7.10.4
> +
> diff --git a/src/patches/dnsmasq/026-
> More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch
> b/src/patches/dnsmasq/026-
> More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch
> new file mode 100644
> index 0000000..910921b
> --- /dev/null
> +++ b/src/patches/dnsmasq/026-
> More_tweaks_in_handling_unknown_DNSSEC_algorithms.patch
> @@ -0,0 +1,262 @@
> +From d67ecac59d58f249707d26e38d49c29b552af4d8 Mon Sep 17 00:00:00
> 2001
> +From: Simon Kelley <simon@thekelleys.org.uk>
> +Date: Sun, 20 Dec 2015 20:44:23 +0000
> +Subject: [PATCH] More tweaks in handling unknown DNSSEC algorithms.
> +
> +---
> + src/dnssec.c |  128 +++++++++++++++++++++++++++++----------------
> -------------
> + 1 file changed, 63 insertions(+), 65 deletions(-)
> +
> +diff --git a/src/dnssec.c b/src/dnssec.c
> +index 299ca64..e09f304 100644
> +--- a/src/dnssec.c
> ++++ b/src/dnssec.c
> +@@ -70,7 +70,17 @@ static char *algo_digest_name(int algo)
> +     default: return NULL;
> +     }
> + }
> +-      
> ++  
> ++/* http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-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
> new file mode 100644
> index 0000000..031339e
> --- /dev/null
> +++ b/src/patches/dnsmasq/027-Nasty_rare_and_obscure_off-by-
> one_in_DNSSEC_hostname_cmp.patch
> @@ -0,0 +1,27 @@
> +From 3e86d316c4bb406ed813aa5256615c8a95cac6d8 Mon Sep 17 00:00:00
> 2001
> +From: Simon Kelley <simon@thekelleys.org.uk>
> +Date: Sun, 20 Dec 2015 20:50:05 +0000
> +Subject: [PATCH] Nasty, rare and obscure off-by-one in DNSSEC
> hostname_cmp().
> +
> +---
> + src/dnssec.c |    4 ++--
> + 1 file changed, 2 insertions(+), 2 deletions(-)
> +
> +diff --git a/src/dnssec.c b/src/dnssec.c
> +index e09f304..29848e1 100644
> +--- a/src/dnssec.c
> ++++ b/src/dnssec.c
> +@@ -1394,8 +1394,8 @@ static int hostname_cmp(const char *a, const
> char *b)
> +       if (sb == b)
> + 	return 1;
> +       
> +-      ea = sa--;
> +-      eb = sb--;
> ++      ea = --sa;
> ++      eb = --sb;
> +     }
> + }
> + 
> +-- 
> +1.7.10.4
> +
> diff --git a/src/patches/dnsmasq/028-
> Minor_tweak_to_previous_commit.patch b/src/patches/dnsmasq/028-
> Minor_tweak_to_previous_commit.patch
> new file mode 100644
> index 0000000..f3758fc
> --- /dev/null
> +++ b/src/patches/dnsmasq/028-Minor_tweak_to_previous_commit.patch
> @@ -0,0 +1,39 @@
> +From a86fdf437ecc29398f9715ceb5240442a17ac014 Mon Sep 17 00:00:00
> 2001
> +From: Simon Kelley <simon@thekelleys.org.uk>
> +Date: Sun, 20 Dec 2015 21:19:20 +0000
> +Subject: [PATCH] Minor tweak to previous commit.
> +
> +---
> + src/dnssec.c |    6 ++----
> + 1 file changed, 2 insertions(+), 4 deletions(-)
> +
> +diff --git a/src/dnssec.c b/src/dnssec.c
> +index 29848e1..9fa64b6 100644
> +--- a/src/dnssec.c
> ++++ b/src/dnssec.c
> +@@ -1889,8 +1889,6 @@ static int zone_status(char *name, int class,
> char *keyname, time_t now)
> + 	}
> +       else
> + 	{
> +-	  int gotone = 0;
> +-
> + 	  /* If all the DS records have digest and/or sig algos we
> don't support,
> + 	     then the zone is insecure. Note that if an algo
> + 	     appears in the DS, then RRSIGs for that algo MUST
> +@@ -1904,11 +1902,11 @@ static int zone_status(char *name, int
> class, char *keyname, time_t now)
> + 	      if (crecp->uid == (unsigned int)class &&
> + 		  hash_find(ds_digest_name(crecp->addr.ds.digest))
> &&
> + 		  verify_func(crecp->addr.ds.algo))
> +-		gotone = 1;
> ++		break;
> + 	    }
> + 	  while ((crecp = cache_find_by_name(crecp, keyname, now,
> F_DS)));
> + 
> +-	  if (!gotone)
> ++	  if (!crecp)
> + 	    return STAT_INSECURE;
> + 	}
> + 
> +-- 
> +1.7.10.4
> +
> diff --git a/src/patches/dnsmasq/029-
> NSEC3_check_RFC5155_para_8_2.patch b/src/patches/dnsmasq/029-
> NSEC3_check_RFC5155_para_8_2.patch
> new file mode 100644
> index 0000000..33219d2
> --- /dev/null
> +++ b/src/patches/dnsmasq/029-NSEC3_check_RFC5155_para_8_2.patch
> @@ -0,0 +1,39 @@
> +From ce5732e84fc46d7f99c152f736cfb4ef5ec98a01 Mon Sep 17 00:00:00
> 2001
> +From: Simon Kelley <simon@thekelleys.org.uk>
> +Date: Sun, 20 Dec 2015 21:39:19 +0000
> +Subject: [PATCH] NSEC3 check: RFC5155 para 8.2
> +
> +---
> + src/dnssec.c |    8 ++++++--
> + 1 file changed, 6 insertions(+), 2 deletions(-)
> +
> +diff --git a/src/dnssec.c b/src/dnssec.c
> +index 9fa64b6..486e422 100644
> +--- a/src/dnssec.c
> ++++ b/src/dnssec.c
> +@@ -1704,7 +1704,7 @@ static int prove_non_existence_nsec3(struct
> dns_header *header, size_t plen, uns
> +   for (i = 0; i < nsec_count; i++)
> +     {
> +       unsigned char *nsec3p = nsecs[i];
> +-      int this_iter;
> ++      int this_iter, flags;
> + 
> +       nsecs[i] = NULL; /* Speculative, will be restored if OK. */
> +       
> +@@ -1716,8 +1716,12 @@ static int prove_non_existence_nsec3(struct
> dns_header *header, size_t plen, uns
> +       if (*p++ != algo)
> + 	continue;
> +  
> +-      p++; /* flags */
> ++      flags = *p++; /* flags */
> +       
> ++      /* 5155 8.2 */
> ++      if (flags != 0 && flags != 1)
> ++	continue;
> ++
> +       GETSHORT(this_iter, p);
> +       if (this_iter != iterations)
> + 	continue;
> +-- 
> +1.7.10.4
> +