From 07ed585c38d8f7c0a18470d2e79cf46ea92ea96a Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Fri, 4 May 2018 21:52:22 +0100
Subject: [PATCH 03/10] Add logging for DNS error returns from upstream and
 local configuration.

Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
---
 src/cache.c   | 13 +++++++++++++
 src/dnsmasq.h |  7 ++++++-
 src/forward.c | 25 +++++++++++++++++++------
 src/rfc1035.c | 19 ++++++++++++++-----
 4 files changed, 52 insertions(+), 12 deletions(-)

--- a/src/cache.c
+++ b/src/cache.c
@@ -1598,6 +1598,19 @@ void log_query(unsigned int flags, char
     {
       if (flags & F_KEYTAG)
 	sprintf(daemon->addrbuff, arg, addr->addr.log.keytag, addr->addr.log.algo, addr->addr.log.digest);
+      else if (flags & F_RCODE)
+	{
+	  unsigned int rcode = addr->addr.rcode.rcode;
+
+	   if (rcode == SERVFAIL)
+	     dest = "SERVFAIL";
+	   else if (rcode == REFUSED)
+	     dest = "REFUSED";
+	   else if (rcode == NOTIMP)
+	     dest = "not implemented";
+	   else
+	     sprintf(daemon->addrbuff, "%u", rcode);
+	}
       else
 	{
 #ifdef HAVE_IPV6
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -268,7 +268,11 @@ struct all_addr {
     /* for log_query */
     struct {
       unsigned short keytag, algo, digest;
-    } log; 
+    } log;
+    /* for log_query */
+    struct {
+      unsigned int rcode;
+    } rcode;
     /* for cache_insert of DNSKEY, DS */
     struct {
       unsigned short class, type;
@@ -459,6 +463,7 @@ struct crec {
 #define F_IPSET     (1u<<26)
 #define F_NOEXTRA   (1u<<27)
 #define F_SERVFAIL  (1u<<28)
+#define F_RCODE     (1u<<29)
 
 /* Values of uid in crecs with F_CONFIG bit set. */
 #define SRC_INTERFACE 0
--- a/src/forward.c
+++ b/src/forward.c
@@ -563,6 +563,7 @@ static size_t process_reply(struct dns_h
   unsigned char *pheader, *sizep;
   char **sets = 0;
   int munged = 0, is_sign;
+  unsigned int rcode = RCODE(header);
   size_t plen; 
   
   (void)ad_reqd;
@@ -593,6 +594,9 @@ static size_t process_reply(struct dns_h
   
   if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
     {
+      /* Get extended RCODE. */
+      rcode |= sizep[2] << 4;
+
       if (check_subnet && !check_source(header, plen, pheader, query_source))
 	{
 	  my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
@@ -641,11 +645,20 @@ static size_t process_reply(struct dns_h
   if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
      header->hb4 &= ~HB4_AD;
   
-  if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
+  if (OPCODE(header) != QUERY)
     return resize_packet(header, n, pheader, plen);
+
+  if (rcode != NOERROR && rcode != NXDOMAIN)
+    {
+      struct all_addr a;
+      a.addr.rcode.rcode = rcode;
+      log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL);
+      
+      return resize_packet(header, n, pheader, plen);
+    }
   
   /* Complain loudly if the upstream server is non-recursive. */
-  if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR &&
+  if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
       server && !(server->flags & SERV_WARNED_RECURSIVE))
     {
       prettyprint_addr(&server->addr, daemon->namebuff);
@@ -654,7 +667,7 @@ static size_t process_reply(struct dns_h
 	server->flags |= SERV_WARNED_RECURSIVE;
     }  
 
-  if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
+  if (daemon->bogus_addr && rcode != NXDOMAIN &&
       check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
     {
       munged = 1;
@@ -666,7 +679,7 @@ static size_t process_reply(struct dns_h
     {
       int doctored = 0;
       
-      if (RCODE(header) == NXDOMAIN && 
+      if (rcode == NXDOMAIN && 
 	  extract_request(header, n, daemon->namebuff, NULL) &&
 	  check_for_local_domain(daemon->namebuff, now))
 	{
@@ -1090,7 +1103,7 @@ void reply_query(int fd, int family, tim
 	      if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
 		domain = daemon->namebuff;
 	      
-	      log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
+	      log_query(F_SECSTAT, domain, NULL, result);
 	    }
 	  
 	  if (status == STAT_SECURE)
@@ -1948,7 +1961,7 @@ unsigned char *tcp_request(int confd, ti
 			  if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL))
 			    domain = daemon->namebuff;
 
-			  log_query(F_KEYTAG | F_SECSTAT, domain, NULL, result);
+			  log_query(F_SECSTAT, domain, NULL, result);
 			  
 			  if (status == STAT_BOGUS)
 			    {
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -926,12 +926,11 @@ unsigned int extract_request(struct dns_
   return F_QUERY;
 }
 
-
 size_t setup_reply(struct dns_header *header, size_t qlen,
 		struct all_addr *addrp, unsigned int flags, unsigned long ttl)
 {
   unsigned char *p;
-
+  
   if (!(p = skip_questions(header, qlen)))
     return 0;
   
@@ -948,7 +947,12 @@ size_t setup_reply(struct dns_header *he
   else if (flags == F_NXDOMAIN)
     SET_RCODE(header, NXDOMAIN);
   else if (flags == F_SERVFAIL)
-    SET_RCODE(header, SERVFAIL);
+    {
+      struct all_addr a;
+      a.addr.rcode.rcode = SERVFAIL;
+      log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
+      SET_RCODE(header, SERVFAIL);
+    }
   else if (flags == F_IPV4)
     { /* we know the address */
       SET_RCODE(header, NOERROR);
@@ -966,8 +970,13 @@ size_t setup_reply(struct dns_header *he
     }
 #endif
   else /* nowhere to forward to */
-    SET_RCODE(header, REFUSED);
- 
+    {
+      struct all_addr a;
+      a.addr.rcode.rcode = REFUSED;
+      log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
+      SET_RCODE(header, REFUSED);
+    }
+  
   return p - (unsigned char *)header;
 }
 
