Complete DNSSEC server-selection code and set conntrack on DNSSEC queries.
diff --git a/src/forward.c b/src/forward.c
index 95c5ef9..506d194 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -923,7 +923,7 @@
 		    status = STAT_ABANDONED;
 		  else
 		    {
-		      int fd, type;
+		      int fd, type = SERV_DO_DNSSEC;
 		      struct frec *next = new->next;
 		      char *domain;
 		      
@@ -936,7 +936,7 @@
 			 servers for domains are involved. */		      
 		      if (search_servers(now, NULL, F_QUERY, daemon->keyname, &type, &domain, NULL) == 0)
 			{
-			   struct server *start = server;
+			  struct server *start = server, *new_server = NULL;
 			   type &= ~SERV_DO_DNSSEC;
 			   
 			   while (1)
@@ -945,8 +945,12 @@
 				   (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
 				   !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
 				 {
-				   server = start;
-				   break;
+				   new_server = start;
+				   if (server == start)
+				     {
+				       new_server = NULL;
+				       break;
+				     }
 				 }
 			       
 			       if (!(start = start->next))
@@ -954,7 +958,11 @@
 			       if (start == server)
 				 break;
 			     }
+			
+			   if (new_server)
+			     server = new_server;
 			}
+
 		      new->sentto = server;
 
 		      new->rfd4 = NULL;
@@ -1010,6 +1018,15 @@
 		      
 		      if (fd != -1)
 			{
+#ifdef HAVE_CONNTRACK
+			  /* Copy connection mark of incoming query to outgoing connection. */
+			  if (option_bool(OPT_CONNTRACK))
+			    {
+			      unsigned int mark;
+			      if (get_incoming_mark(&orig->source, &orig->dest, 0, &mark))
+				setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
+			    }
+#endif
 			  while (retry_send(sendto(fd, (char *)header, nn, 0, 
 						   &server->addr.sa, 
 						   sa_len(&server->addr)))); 
@@ -1072,7 +1089,7 @@
       else
 	header->hb4 &= ~HB4_CD;
       
-      if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, 
+      if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, 
 			      forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, 
 			      forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source)))
 	{
@@ -1433,20 +1450,27 @@
 }
 
 #ifdef HAVE_DNSSEC
+/* Recurse up the key heirarchy */
 static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n, 
-			   int class, char *name, char *keyname, struct server *server, int *keycount)
+			   int class, char *name, char *keyname, struct server *server, 
+			   int have_mark, unsigned int mark, int *keycount)
 {
-  /* Recurse up the key heirarchy */
   int new_status;
   unsigned char *packet = NULL;
-  size_t m; 
   unsigned char *payload = NULL;
   struct dns_header *new_header = NULL;
   u16 *length = NULL;
-  unsigned char c1, c2;
+
+  (void)have_mark;
+  (void)mark;
 
   while (1)
     {
+      int type = SERV_DO_DNSSEC;
+      char *domain;
+      size_t m; 
+      unsigned char c1, c2;
+            
       /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
       if (--(*keycount) == 0)
 	new_status = STAT_ABANDONED;
@@ -1480,6 +1504,67 @@
 				new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz);
       
       *length = htons(m);
+
+      /* Find server to forward to. This will normally be the 
+	 same as for the original query, but may be another if
+	 servers for domains are involved. */		      
+      if (search_servers(now, NULL, F_QUERY, keyname, &type, &domain, NULL) == 0)
+	{
+	  struct server *start = server, *new_server = NULL;
+	  type &= ~SERV_DO_DNSSEC;
+			   
+	  while (1)
+	    {
+	      if (type == (start->flags & SERV_TYPE) &&
+		  (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
+		  !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
+		{
+		  new_server = start;
+		  if (server == start)
+		    {
+		      new_server = NULL;
+		      break;
+		    }
+		}
+			       
+	      if (!(start = start->next))
+		start = daemon->servers;
+	      if (start == server)
+		break;
+	    }
+       
+
+	  if (new_server)
+	    {
+	      server = new_server;
+	      /* may need to make new connection. */
+	      if (server->tcpfd == -1)
+		{
+		  if ((server->tcpfd = socket(server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
+		    {
+		      new_status = STAT_ABANDONED;
+		      break;
+		    }
+
+#ifdef HAVE_CONNTRACK
+		  /* Copy connection mark of incoming query to outgoing connection. */
+		  if (have_mark)
+		    setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
+#endif	
+			  
+		  if (!local_bind(server->tcpfd,  &server->source_addr, server->interface, 1) ||
+		      connect(server->tcpfd, &server->addr.sa, sa_len(&server->addr)) == -1)
+		    {
+		      close(server->tcpfd);
+		      server->tcpfd = -1;
+		      new_status = STAT_ABANDONED;
+		      break;
+		    }
+
+		}
+	    }
+	}
+
       
       if (!read_write(server->tcpfd, packet, m + sizeof(u16), 0) ||
 	  !read_write(server->tcpfd, &c1, 1, 1) ||
@@ -1492,7 +1577,7 @@
 
       m = (c1 << 8) | c2;
       
-      new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount);
+      new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount);
       
       if (new_status != STAT_OK)
 	break;
@@ -1536,10 +1621,30 @@
   socklen_t peer_len = sizeof(union mysockaddr);
   int query_count = 0;
   unsigned char *pheader;
+#ifdef HAVE_CONNTRACK
+  unsigned int mark = 0;
+  int have_mark = 0;
+#endif
 
   if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
     return packet;
-  
+
+#ifdef HAVE_CONNTRACK
+  /* Get connection mark of incoming query to set on outgoing connections. */
+  if (option_bool(OPT_CONNTRACK))
+    {
+      struct all_addr local;
+#ifdef HAVE_IPV6		      
+      if (local_addr->sa.sa_family == AF_INET6)
+	local.addr.addr6 = local_addr->in6.sin6_addr;
+      else
+#endif
+	local.addr.addr4 = local_addr->in.sin_addr;
+      
+      have_mark = get_incoming_mark(&peer_addr, &local, 1, &mark);
+    }
+#endif	
+
   /* We can be configured to only accept queries from at-most-one-hop-away addresses. */
   if (option_bool(OPT_LOCAL_SERVICE))
     {
@@ -1665,7 +1770,7 @@
 	    {
 	      unsigned int flags = 0;
 	      struct all_addr *addrp = NULL;
-	      int type = 0;
+	      int type = SERV_DO_DNSSEC;
 	      char *domain = NULL;
 	      size_t new_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet);
 
@@ -1728,20 +1833,8 @@
 			  
 #ifdef HAVE_CONNTRACK
 			  /* Copy connection mark of incoming query to outgoing connection. */
-			  if (option_bool(OPT_CONNTRACK))
-			    {
-			      unsigned int mark;
-			      struct all_addr local;
-#ifdef HAVE_IPV6		      
-			      if (local_addr->sa.sa_family == AF_INET6)
-				local.addr.addr6 = local_addr->in6.sin6_addr;
-			      else
-#endif
-				local.addr.addr4 = local_addr->in.sin_addr;
-			      
-			      if (get_incoming_mark(&peer_addr, &local, 1, &mark))
-				setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
-			    }
+			  if (have_mark)
+			    setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
 #endif	
 		      
 			  if ((!local_bind(last_server->tcpfd,  &last_server->source_addr, last_server->interface, 1) ||
@@ -1802,7 +1895,8 @@
 		      if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (last_server->flags & SERV_DO_DNSSEC))
 			{
 			  int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
-			  int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
+			  int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, 
+						       last_server, have_mark, mark, &keycount);
 			  char *result, *domain = "result";
 			  
 			  if (status == STAT_ABANDONED)