import of dnsmasq-2.9.tar.gz
diff --git a/src/forward.c b/src/forward.c
index fb4568c..fbf8790 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -120,22 +120,27 @@
 {
   struct frec *forward;
   char *domain = NULL;
-  int type = 0;
-  struct server *serv;
+  int forwardall = 0, type = 0;
   struct all_addr *addrp = NULL;
   unsigned short flags = 0;
   unsigned short gotname = extract_request(header, (unsigned int)plen, dnamebuff);
-
+  struct server *start = NULL;
+  
   /* may be  recursion not speced or no servers available. */
   if (!header->rd || !servers)
     forward = NULL;
   else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr)))
     {
-      /* retry on existing query, send to next server */
+      /* retry on existing query, send to all available servers  */
       domain = forward->sentto->domain;
+      if (!(options & OPT_ORDER))
+	{
+	  forwardall = 1;
+	  last_server = NULL;
+	}
       type = forward->sentto->flags & SERV_TYPE;
-      if (!(forward->sentto = forward->sentto->next))
-	forward->sentto = servers; /* at end of list, recycle */
+      if (!(start = forward->sentto->next))
+	start = servers; /* at end of list, recycle */
       header->id = htons(forward->new_id);
     }
   else 
@@ -148,29 +153,25 @@
 	  
 	  unsigned int namelen = strlen(dnamebuff);
 	  unsigned int matchlen = 0;
-	 
+	  struct server *serv;
+
 	  for (serv=servers; serv; serv=serv->next)
 	    /* domain matches take priority over NODOTS matches */
 	    if ((serv->flags & SERV_FOR_NODOTS) && type != SERV_HAS_DOMAIN && !strchr(dnamebuff, '.'))
 	      {
-		if (serv->flags & SERV_LITERAL_ADDRESS)
+		unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; 
+		type = SERV_FOR_NODOTS;
+		flags = 0;
+		if ((serv->flags & SERV_LITERAL_ADDRESS) && (sflag & gotname))
 		  {
-		    /* flags gets set if server is in fact an answer */
-		    unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; 
-		    if (sflag & gotname) /* only OK if addrfamily == query */
-		      {
-			type = SERV_FOR_NODOTS;
-			flags = sflag;
-			if (serv->addr.sa.sa_family == AF_INET) 
-			  addrp = (struct all_addr *)&serv->addr.in.sin_addr;
+		    flags = sflag;
+		    if (serv->addr.sa.sa_family == AF_INET) 
+		      addrp = (struct all_addr *)&serv->addr.in.sin_addr;
 #ifdef HAVE_IPV6
-			else
-			  addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
+		    else
+		      addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
 #endif 
-		      }
 		  }
-		else
-		  flags = 0;
 	      }
 	    else if (serv->flags & SERV_HAS_DOMAIN)
 	      {
@@ -179,29 +180,20 @@
 		    hostname_isequal(dnamebuff + namelen - domainlen, serv->domain) &&
 		    domainlen >= matchlen)
 		  {
-		    if (serv->flags & SERV_LITERAL_ADDRESS)
-		      { /* flags gets set if server is in fact an answer */
-			unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; 
-			if ((sflag | F_QUERY ) & gotname) /* only OK if addrfamily == query */
-			  {
-			    type = SERV_HAS_DOMAIN;
-			    flags = gotname;
-			    domain = serv->domain;
-			    matchlen = domainlen; 
-			    if (serv->addr.sa.sa_family == AF_INET) 
-			      addrp = (struct all_addr *)&serv->addr.in.sin_addr;
-#ifdef HAVE_IPV6
-			    else
-			      addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
-#endif
-			  }
-		      }
-		    else
+		    unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
+		    type = SERV_HAS_DOMAIN;
+		    domain = serv->domain;
+		    matchlen = domainlen;
+		    flags = 0;
+		    if ((serv->flags & SERV_LITERAL_ADDRESS) && ((sflag | F_QUERY ) & gotname))
 		      {
-			flags = 0; /* may be better match from previous literal */
-			domain = serv->domain;
-			matchlen = domainlen;
-			type = SERV_HAS_DOMAIN;
+			flags = gotname;
+			if (serv->addr.sa.sa_family == AF_INET) 
+			  addrp = (struct all_addr *)&serv->addr.in.sin_addr;
+#ifdef HAVE_IPV6
+			else
+			  addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
+#endif
 		      }
 		  } 
 	      }
@@ -231,10 +223,13 @@
 	     otherwise, use the one last known to work. */
 	  
 	  if (type != 0  || (options & OPT_ORDER))
-	    forward->sentto = servers;
-	  else
-	    forward->sentto = last_server;
-	  
+	    start = servers;
+	  else if (!(start = last_server))
+	    {
+	      start = servers;
+	      forwardall = 1;
+	    }
+	    	  
 	  forward->source = *udpaddr;
 	  forward->dest = *dst_addr;
 	  forward->new_id = get_id();
@@ -250,52 +245,52 @@
   
   if (!flags && forward)
     {
-      struct server *firstsentto = forward->sentto;
-            
+      struct server *firstsentto = start;
+      int forwarded = 0;
+
       while (1)
 	{ 
-	  int logflags = 0;
-	  
-	  if (forward->sentto->addr.sa.sa_family == AF_INET)
-	    {
-	      logflags = F_SERVER | F_IPV4 | F_FORWARD;
-	      addrp = (struct all_addr *)&forward->sentto->addr.in.sin_addr;
-	    }
-#ifdef HAVE_IPV6
-	  else
-	    { 
-	      logflags = F_SERVER | F_IPV6 | F_FORWARD;
-	      addrp = (struct all_addr *)&forward->sentto->addr.in6.sin6_addr;
-	    }
-#endif
 	  /* only send to servers dealing with our domain.
 	     domain may be NULL, in which case server->domain 
 	     must be NULL also. */
 	  
-	  if (type == (forward->sentto->flags & SERV_TYPE) &&
-	      (type != SERV_HAS_DOMAIN || hostname_isequal(domain, forward->sentto->domain)))
+	  if (type == (start->flags & SERV_TYPE) &&
+	      (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)))
 	    {
-	      if (forward->sentto->flags & SERV_NO_ADDR)
+	      if (start->flags & SERV_NO_ADDR)
 		flags = F_NOERR; /* NULL servers are OK. */
-	      else if (!(forward->sentto->flags & SERV_LITERAL_ADDRESS) &&
-		       sendto(forward->sentto->sfd->fd, (char *)header, plen, 0,
-			      &forward->sentto->addr.sa,
-			      sa_len(&forward->sentto->addr)) != -1)
+	      else if (!(start->flags & SERV_LITERAL_ADDRESS) &&
+		       sendto(start->sfd->fd, (char *)header, plen, 0,
+			      &start->addr.sa,
+			      sa_len(&start->addr)) != -1)
 		{
-		  log_query(logflags, gotname ? dnamebuff : "query", addrp); 
-		  /* for no-domain, don't update last_server */
-		  return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers);
+		  if (!gotname)
+		    strcpy(dnamebuff, "query");
+		  if (start->addr.sa.sa_family == AF_INET)
+		    log_query(F_SERVER | F_IPV4 | F_FORWARD, dnamebuff, 
+			      (struct all_addr *)&start->addr.in.sin_addr); 
+#ifdef HAVE_IPV6
+		  else
+		    log_query(F_SERVER | F_IPV6 | F_FORWARD, dnamebuff, 
+			      (struct all_addr *)&start->addr.in6.sin6_addr);
+#endif 
+		  forwarded = 1;
+		  forward->sentto = start;
+		  if (!forwardall) 
+		    break;
 		}
 	    } 
 	  
-	  if (!(forward->sentto = forward->sentto->next))
-	    forward->sentto = servers;
+	  if (!(start = start->next))
+	    start = servers;
 	  
-	  /* check if we tried all without success */
-	  if (forward->sentto == firstsentto)
+	  if (start == firstsentto)
 	    break;
 	}
       
+      if (forwarded)
+	  return last_server;
+      
       /* could not send on, prepare to return */ 
       header->id = htons(forward->orig_id);
       forward->new_id = 0; /* cancel */
@@ -312,52 +307,81 @@
 }
 
 /* returns new last_server */
-struct server *reply_query(int fd, int options, char *packet, time_t now,
-			   char *dnamebuff, struct server *last_server, 
+struct server *reply_query(struct serverfd *sfd, int options, char *packet, time_t now,
+			   char *dnamebuff, struct server *servers, struct server *last_server,
 			   struct bogus_addr *bogus_nxdomain, struct doctor *doctors)
 {
   /* packet from peer server, extract data for cache, and send to
      original requester */
   struct frec *forward;
   HEADER *header;
-  int n = recv(fd, packet, PACKETSZ, 0);
+  union mysockaddr serveraddr;
+  socklen_t addrlen = sizeof(serveraddr);
+  int n = recvfrom(sfd->fd, packet, PACKETSZ, 0, &serveraddr.sa, &addrlen);
+  
+  /* Determine the address of the server replying  so that we can mark that as good */
+  serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
+#ifdef HAVE_IPV6
+  if (serveraddr.sa.sa_family == AF_INET6)
+    serveraddr.in6.sin6_flowinfo = htonl(0);
+#endif
   
   header = (HEADER *)packet;
-  if (n >= (int)sizeof(HEADER) && header->qr)
+  if (n >= (int)sizeof(HEADER) && header->qr && (forward = lookup_frec(ntohs(header->id))))
     {
-      if ((forward = lookup_frec(ntohs(header->id))))
+      /* find good server by address if possible, otherwise assume the last one we sent to */ 
+      if ((forward->sentto->flags & SERV_TYPE) == 0)
 	{
-	  if (header->rcode == NOERROR || header->rcode == NXDOMAIN)
-	    {
-	      if (!forward->sentto->domain)
-		last_server = forward->sentto; /* known good */
-	      if (header->opcode == QUERY)
-		{
-		  if (!(bogus_nxdomain && 
-			header->rcode == NOERROR && 
-			check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
-		    {
-		      if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
-			extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors);
-		      else if (!(options & OPT_NO_NEG))
-			extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
-		    }
-		}
-	    }
-	  header->id = htons(forward->orig_id);
-	  /* There's no point returning an upstream reply marked as truncated,
-	     since that will prod the resolver into moving to TCP - which we
-	     don't support. */
-	  header->tc = 0; /* goodbye truncate */
-	  send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest);
-	  forward->new_id = 0; /* cancel */
+	  for (last_server = servers; last_server; last_server = last_server->next)
+	    if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
+		sockaddr_isequal(&last_server->addr, &serveraddr))
+	      break;
+	  if (!last_server)
+	    last_server = forward->sentto;
 	}
+ 
+      /* Complain loudly if the upstream server is non-recursive. */
+      if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0)
+	{
+	  char addrbuff[ADDRSTRLEN];
+#ifdef HAVE_IPV6
+          if (serveraddr.sa.sa_family == AF_INET)
+	    inet_ntop(AF_INET, &serveraddr.in.sin_addr, addrbuff, ADDRSTRLEN);
+	  else if (serveraddr.sa.sa_family == AF_INET6)
+	    inet_ntop(AF_INET6, &serveraddr.in6.sin6_addr, addrbuff, ADDRSTRLEN);
+#else
+          strcpy(addrbuff, inet_ntoa(serveraddr.in.sin_addr));
+#endif
+	  syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff);
+	  return NULL;
+	}
+      
+      if ((header->rcode == NOERROR || header->rcode == NXDOMAIN) && header->opcode == QUERY)
+	{
+	  if (!(bogus_nxdomain && 
+		header->rcode == NOERROR && 
+		check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
+	    {
+	      if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
+		extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors);
+	      else if (!(options & OPT_NO_NEG))
+		extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
+	    }
+	}
+  
+      header->id = htons(forward->orig_id);
+      /* There's no point returning an upstream reply marked as truncated,
+	 since that will prod the resolver into moving to TCP - which we
+	 don't support. */
+      header->tc = 0; /* goodbye truncate */
+      send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest);
+      forward->new_id = 0; /* cancel */
     }
-
+  
   return last_server;
 }
 
-struct server *receive_query(struct listener *listen, char *packet, char *mxname, 
+struct server *receive_query(struct listener *listen, char *packet, struct mx_record *mxnames, 
 			     char *mxtarget, unsigned int options, time_t now, 
 			     unsigned long local_ttl, char *namebuff,
 			     struct iname *names, struct iname *addrs, struct iname *except,
@@ -395,7 +419,8 @@
   msg.msg_iov = iov;
   msg.msg_iovlen = 1;
   
-  n = recvmsg(listen->fd, &msg, 0);
+  if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
+    return last_server;
   
   source_addr.sa.sa_family = listen->family;
 #ifdef HAVE_IPV6
@@ -502,7 +527,7 @@
     }
 
   m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, 
-		      mxname, mxtarget, options, now, local_ttl, namebuff);
+		      mxnames, mxtarget, options, now, local_ttl, namebuff);
   if (m >= 1)
     send_from(listen->fd, options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr);
   else