Blob Blame History Raw
From 6ff6c5003120fbb609cb2e2739679fd4583731f6 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 19 Sep 2018 22:27:11 +0100
Subject: [PATCH] Change behavior when RD bit unset in queries.

Change anti cache-snooping behaviour with queries with the
recursion-desired bit unset. Instead to returning SERVFAIL, we
now always forward, and never answer from the cache. This
allows "dig +trace" command to work.

(cherry picked from commit 4139298d287eb5c57f4aa53c459cb02fc5be2495)

Restore ability to answer non-recursive requests

Instead, check only local configured entries are answered without
rdbit set. All cached replies are still denied, but locally configured
names are available with both recursion and without it.

Fixes commit 4139298d287eb5c57f4aa53c459cb02fc5be2495 unintended
behaviour.

(cherry picked from commit 29ae3083981ea82f535f77ea54bbd538f1224a9e)
---
 src/rfc1035.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/rfc1035.c b/src/rfc1035.c
index 96acae9..ae2cc96 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -1252,7 +1252,6 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
   else
     return daemon->max_ttl;
 }
-  
 
 /* 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,  
@@ -1271,12 +1270,15 @@ 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;
+  int rd_bit;
   // Make sure we do not underflow here too.
   if (qlen > (limit - ((char *)header))) return 0;
+  rd_bit = (header->hb3 & HB3_RD);
   
+  /* never answer queries with RD unset, to avoid cache snooping. */
   if (ntohs(header->ancount) != 0 ||
       ntohs(header->nscount) != 0 ||
-      ntohs(header->qdcount) == 0 || 
+      ntohs(header->qdcount) == 0 ||
       OPCODE(header) != QUERY )
     return 0;
   
@@ -1443,9 +1445,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
 		  /* 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)) || 
-		      !do_bit || 
-		      (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
+		  if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) ||
+		      (rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))))
 		    {
 		      do 
 			{ 
@@ -1633,8 +1634,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)) ||
-		      !do_bit ||
-		      (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))
+		      (rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) ))
 		    do
 		      { 
 			/* don't answer wildcard queries with data not from /etc/hosts
@@ -1718,7 +1718,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
 	    {
 	      if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME | (dryrun ? F_NO_RR : 0))) &&
 		  (qtype == T_CNAME || (crecp->flags & F_CONFIG)) &&
-		  ((crecp->flags & F_CONFIG) || !do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
+		  ((crecp->flags & F_CONFIG) ||
+		   (rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))))
 		{
 		  if (!(crecp->flags & F_DNSSECOK))
 		    sec_data = 0;
@@ -1756,7 +1757,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
 		    }
 		  }
 	      
-	      if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && 
+	      if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) &&
 		  cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR))
 		{ 
 		  ans = 1;
-- 
2.21.1