--- /dev/null
+From dd4ad9ac7ea6d51dcc34a1f2cd2da14efbb87714 Mon Sep 17 00:00:00 2001
+From: Simon Kelley <simon@thekelleys.org.uk>
+Date: Thu, 17 Dec 2015 10:44:58 +0000
+Subject: [PATCH] Tweaks to EDNS0 handling in DNS replies.
+
+---
+ src/dnssec.c | 20 +++++++++-----------
+ src/rfc1035.c | 57 +++++++++++++++++++++++++++++++++------------------------
+ 2 files changed, 42 insertions(+), 35 deletions(-)
+
+diff --git a/src/dnssec.c b/src/dnssec.c
+index dc563e0..012b2a6 100644
+--- a/src/dnssec.c
++++ b/src/dnssec.c
+@@ -2129,18 +2129,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
+ /* Empty DS without NSECS */
+ if (qtype == T_DS)
+ return STAT_BOGUS;
+- else
++
++ rc = zone_status(name, qclass, keyname, now);
++ if (rc != STAT_SECURE)
+ {
+- rc = zone_status(name, qclass, keyname, now);
+- if (rc != STAT_SECURE)
+- {
+- if (class)
+- *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
+- return rc;
+- }
+-
+- return STAT_BOGUS; /* signed zone, no NSECs */
+- }
++ if (class)
++ *class = qclass; /* Class for NEED_DS or NEED_DNSKEY */
++ return rc;
++ }
++
++ return STAT_BOGUS; /* signed zone, no NSECs */
+ }
+
+ if (nsec_type == T_NSEC)
+diff --git a/src/rfc1035.c b/src/rfc1035.c
+index def8fa0..188d05f 100644
+--- a/src/rfc1035.c
++++ b/src/rfc1035.c
+@@ -1539,7 +1539,13 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
+ int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
+ struct mx_srv_record *rec;
+ size_t len;
+-
++
++ if (ntohs(header->ancount) != 0 ||
++ ntohs(header->nscount) != 0 ||
++ ntohs(header->qdcount) == 0 ||
++ OPCODE(header) != QUERY )
++ return 0;
++
+ /* Don't return AD set if checking disabled. */
+ if (header->hb4 & HB4_CD)
+ sec_data = 0;
+@@ -1548,33 +1554,32 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
+ *ad_reqd = header->hb4 & HB4_AD;
+ *do_bit = 0;
+
+- /* If there is an RFC2671 pseudoheader then it will be overwritten by
++ /* If there is an additional data section then it will be overwritten by
+ partial replies, so we have to do a dry run to see if we can answer
+- the query. We check to see if the do bit is set, if so we always
+- forward rather than answering from the cache, which doesn't include
+- security information, unless we're in DNSSEC validation mode. */
++ the query. */
+
+- if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
+- {
+- unsigned short flags;
+-
+- have_pseudoheader = 1;
++ if (ntohs(header->arcount) != 0)
++ {
++ dryrun = 1;
+
+- pheader += 4; /* udp size, ext_rcode */
+- GETSHORT(flags, pheader);
+-
+- if ((sec_reqd = flags & 0x8000))
+- {
+- *do_bit = 1;/* do bit */
+- *ad_reqd = 1;
++ /* If there's an additional section, there might be an EDNS(0) pseudoheader */
++ if (find_pseudoheader(header, qlen, NULL, &pheader, NULL))
++ {
++ unsigned short flags;
++
++ have_pseudoheader = 1;
++
++ pheader += 4; /* udp size, ext_rcode */
++ GETSHORT(flags, pheader);
++
++ if ((sec_reqd = flags & 0x8000))
++ {
++ *do_bit = 1;/* do bit */
++ *ad_reqd = 1;
++ }
+ }
+-
+- dryrun = 1;
+ }
+
+- if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
+- return 0;
+-
+ for (rec = daemon->mxnames; rec; rec = rec->next)
+ rec->offset = 0;
+
+@@ -1730,8 +1735,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
+ }
+ else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
+ {
+- /* Don't use cache when DNSSEC data required. */
+- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !sec_reqd || !(crecp->flags & F_DNSSECOK))
++ /* Don't use cache when DNSSEC data required, unless we know that
++ the zone is unsigned, which implies that we're doing
++ validation. */
++ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
++ !sec_reqd ||
++ (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
+ {
+ do
+ {
+--
+1.7.10.4
+