From 1f60a18ea1c64beb8b6cffa0650a2bfad95ac352 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Fri, 11 May 2018 16:44:16 +0100
Subject: [PATCH 07/10] Retry SERVFAIL DNSSEC queries to a different server, if
 possible.

Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
---
 src/forward.c | 53 ++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 42 insertions(+), 11 deletions(-)

--- a/src/forward.c
+++ b/src/forward.c
@@ -825,9 +825,12 @@ void reply_query(int fd, int family, tim
       size_t plen;
       int is_sign;
 
+#ifdef HAVE_DNSSEC
       /* For DNSSEC originated queries, just retry the query to the same server. */
       if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY))
 	{
+	  struct server *start;
+	  
 	  blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
 	  plen = forward->stash_len;
 
@@ -839,26 +842,54 @@ void reply_query(int fd, int family, tim
 	  else
 	    log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
 #endif
-  
-	  if (forward->sentto->sfd)
-	    fd = forward->sentto->sfd->fd;
+
+	  start = forward->sentto;
+
+	  /* for non-domain specific servers, see if we can find another to try. */
+	  if ((forward->sentto->flags & SERV_TYPE) == 0)
+	    while (1)
+	      {
+		if (!(start = start->next))
+		  start = daemon->servers;
+		if (start == forward->sentto)
+		  break;
+		
+		if ((start->flags & SERV_TYPE) == 0 &&
+		    (start->flags & SERV_DO_DNSSEC))
+		  break;
+	      }
+	    
+	  
+	  if (start->sfd)
+	    fd = start->sfd->fd;
 	  else
 	    {
 #ifdef HAVE_IPV6
-	      if (forward->sentto->addr.sa.sa_family == AF_INET6)
-		fd = forward->rfd6->fd;
+	      if (start->addr.sa.sa_family == AF_INET6)
+		{
+		  /* may have changed family */
+		  if (!forward->rfd6)
+		    forward->rfd6 = allocate_rfd(AF_INET6);
+		  fd = forward->rfd6->fd;
+		}
 	      else
 #endif
-		fd = forward->rfd4->fd;
+		{
+		  /* may have changed family */
+		  if (!forward->rfd4)
+		    forward->rfd4 = allocate_rfd(AF_INET);
+		  fd = forward->rfd4->fd;
+		}
 	    }
-	  
+	
 	  while (retry_send(sendto(fd, (char *)header, plen, 0,
-				   &forward->sentto->addr.sa,
-				   sa_len(&forward->sentto->addr))));
+				   &start->addr.sa,
+				   sa_len(&start->addr))));
 	  
 	  return;
 	}
-	  
+#endif
+      
       /* In strict order mode, there must be a server later in the chain
 	 left to send to, otherwise without the forwardall mechanism,
 	 code further on will cycle around the list forwever if they
@@ -1024,7 +1055,7 @@ void reply_query(int fd, int family, tim
 			  while (1)
 			    {
 			      if (type == (start->flags & (SERV_TYPE | SERV_DO_DNSSEC)) &&
-				  (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
+				  ((type & SERV_TYPE) != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
 				  !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
 				{
 				  new_server = start;
