Blame SOURCES/dnsmasq-2.85-CVE-2021-3448.patch

176ecd
From d88dc5e696f1b8b95e416890ac831eb0c26250ff Mon Sep 17 00:00:00 2001
176ecd
From: Simon Kelley <simon@thekelleys.org.uk>
176ecd
Date: Mon, 15 Mar 2021 21:59:51 +0000
176ecd
Subject: [PATCH] Use random source ports where possible if source
176ecd
 addresses/interfaces in use.
176ecd
176ecd
CVE-2021-3448 applies.
176ecd
176ecd
It's possible to specify the source address or interface to be
176ecd
used when contacting upstream nameservers: server=8.8.8.8@1.2.3.4
176ecd
or server=8.8.8.8@1.2.3.4#66 or server=8.8.8.8@eth0, and all of
176ecd
these have, until now, used a single socket, bound to a fixed
176ecd
port. This was originally done to allow an error (non-existent
176ecd
interface, or non-local address) to be detected at start-up. This
176ecd
means that any upstream servers specified in such a way don't use
176ecd
random source ports, and are more susceptible to cache-poisoning
176ecd
attacks.
176ecd
176ecd
We now use random ports where possible, even when the
176ecd
source is specified, so server=8.8.8.8@1.2.3.4 or
176ecd
server=8.8.8.8@eth0 will use random source
176ecd
ports. server=8.8.8.8@1.2.3.4#66 or any use of --query-port will
176ecd
use the explicitly configured port, and should only be done with
176ecd
understanding of the security implications.
176ecd
Note that this change changes non-existing interface, or non-local
176ecd
source address errors from fatal to run-time. The error will be
176ecd
logged and communiction with the server not possible.
176ecd
---
176ecd
 man/dnsmasq.8 |   4 +-
176ecd
 src/dnsmasq.c |  31 +++--
176ecd
 src/dnsmasq.h |  28 ++--
176ecd
 src/forward.c | 373 +++++++++++++++++++++++++++++++-------------------
176ecd
 src/loop.c    |  20 +--
176ecd
 src/network.c | 100 ++++----------
176ecd
 src/option.c  |   3 +-
176ecd
 src/tftp.c    |   6 +-
176ecd
 src/util.c    |   2 +-
176ecd
 9 files changed, 310 insertions(+), 257 deletions(-)
176ecd
176ecd
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
176ecd
index 45d2273..7f4c62e 100644
176ecd
--- a/man/dnsmasq.8
176ecd
+++ b/man/dnsmasq.8
176ecd
@@ -419,7 +419,7 @@ Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
176ecd
 or domain parts, to upstream nameservers. If the name is not known
176ecd
 from /etc/hosts or DHCP then a "not found" answer is returned.
176ecd
 .TP
176ecd
-.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]]
176ecd
+.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<interface>][@<source-ip>[#<port>]]
176ecd
 Specify IP address of upstream servers directly. Setting this flag does
176ecd
 not suppress reading of /etc/resolv.conf, use -R to do that. If one or
176ecd
 more 
176ecd
@@ -481,7 +481,7 @@ source address specified but the port may be specified directly as
176ecd
 part of the source address. Forcing queries to an interface is not
176ecd
 implemented on all platforms supported by dnsmasq.
176ecd
 .TP
176ecd
-.B --rev-server=<ip-address>/<prefix-len>,<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]]
176ecd
+.B --rev-server=<ip-address>/<prefix-len>,<ipaddr>[#<port>][@<source-ip>|@<interface>[#<port>]]
176ecd
 This is functionally the same as 
176ecd
 .B --server, 
176ecd
 but provides some syntactic sugar to make specifying address-to-name queries easier. For example
176ecd
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
176ecd
index b7f0a29..3a1f65e 100644
176ecd
--- a/src/dnsmasq.c
176ecd
+++ b/src/dnsmasq.c
176ecd
@@ -1538,6 +1538,7 @@ static int set_dns_listeners(time_t now)
176ecd
 {
176ecd
   struct serverfd *serverfdp;
176ecd
   struct listener *listener;
176ecd
+  struct randfd_list *rfl;
176ecd
   int wait = 0, i;
176ecd
   
176ecd
 #ifdef HAVE_TFTP
176ecd
@@ -1557,11 +1558,14 @@ static int set_dns_listeners(time_t now)
176ecd
   for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
176ecd
     poll_listen(serverfdp->fd, POLLIN);
176ecd
     
176ecd
-  if (daemon->port != 0 && !daemon->osport)
176ecd
-    for (i = 0; i < RANDOM_SOCKS; i++)
176ecd
-      if (daemon->randomsocks[i].refcount != 0)
176ecd
-	poll_listen(daemon->randomsocks[i].fd, POLLIN);
176ecd
-	  
176ecd
+  for (i = 0; i < RANDOM_SOCKS; i++)
176ecd
+    if (daemon->randomsocks[i].refcount != 0)
176ecd
+      poll_listen(daemon->randomsocks[i].fd, POLLIN);
176ecd
+
176ecd
+  /* Check overflow random sockets too. */
176ecd
+  for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next)
176ecd
+    poll_listen(rfl->rfd->fd, POLLIN);
176ecd
+  
176ecd
   for (listener = daemon->listeners; listener; listener = listener->next)
176ecd
     {
176ecd
       /* only listen for queries if we have resources */
176ecd
@@ -1592,17 +1596,22 @@ static void check_dns_listeners(time_t now)
176ecd
 {
176ecd
   struct serverfd *serverfdp;
176ecd
   struct listener *listener;
176ecd
+  struct randfd_list *rfl;
176ecd
   int i;
176ecd
 
176ecd
   for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
176ecd
     if (poll_check(serverfdp->fd, POLLIN))
176ecd
-      reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
176ecd
+      reply_query(serverfdp->fd, now);
176ecd
   
176ecd
-  if (daemon->port != 0 && !daemon->osport)
176ecd
-    for (i = 0; i < RANDOM_SOCKS; i++)
176ecd
-      if (daemon->randomsocks[i].refcount != 0 && 
176ecd
-	  poll_check(daemon->randomsocks[i].fd, POLLIN))
176ecd
-	reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
176ecd
+  for (i = 0; i < RANDOM_SOCKS; i++)
176ecd
+    if (daemon->randomsocks[i].refcount != 0 && 
176ecd
+	poll_check(daemon->randomsocks[i].fd, POLLIN))
176ecd
+      reply_query(daemon->randomsocks[i].fd, now);
176ecd
+
176ecd
+  /* Check overflow random sockets too. */
176ecd
+  for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next)
176ecd
+    if (poll_check(rfl->rfd->fd, POLLIN))
176ecd
+      reply_query(rfl->rfd->fd, now);
176ecd
   
176ecd
   for (listener = daemon->listeners; listener; listener = listener->next)
176ecd
     {
176ecd
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
176ecd
index 221f788..4beef35 100644
176ecd
--- a/src/dnsmasq.h
176ecd
+++ b/src/dnsmasq.h
176ecd
@@ -521,13 +521,20 @@ struct serverfd {
176ecd
 };
176ecd
 
176ecd
 struct randfd {
176ecd
+  struct server *serv;
176ecd
   int fd;
176ecd
-  unsigned short refcount, family;
176ecd
+  unsigned short refcount; /* refcount == 0xffff means overflow record. */
176ecd
 };
176ecd
-  
176ecd
+
176ecd
+struct randfd_list {
176ecd
+  struct randfd *rfd;
176ecd
+  struct randfd_list *next;
176ecd
+};
176ecd
+
176ecd
 struct server {
176ecd
   union mysockaddr addr, source_addr;
176ecd
   char interface[IF_NAMESIZE+1];
176ecd
+  unsigned int ifindex; /* corresponding to interface, above */
176ecd
   struct serverfd *sfd; 
176ecd
   char *domain; /* set if this server only handles a domain. */ 
176ecd
   int flags, tcpfd, edns_pktsz;
176ecd
@@ -640,10 +647,7 @@ struct frec {
176ecd
     struct frec_src *next;
176ecd
   } frec_src;
176ecd
   struct server *sentto; /* NULL means free */
176ecd
-  struct randfd *rfd4;
176ecd
-#ifdef HAVE_IPV6
176ecd
-  struct randfd *rfd6;
176ecd
-#endif
176ecd
+  struct randfd_list *rfds;
176ecd
   unsigned short new_id;
176ecd
   int forwardall, flags;
176ecd
   time_t time;
176ecd
@@ -1062,9 +1066,10 @@ extern struct daemon {
176ecd
   int forwardcount;
176ecd
   struct server *srv_save; /* Used for resend on DoD */
176ecd
   size_t packet_len;       /*      "        "        */
176ecd
-  struct randfd *rfd_save; /*      "        "        */
176ecd
+  int    fd_save;          /*      "        "        */
176ecd
   pid_t tcp_pids[MAX_PROCS];
176ecd
   struct randfd randomsocks[RANDOM_SOCKS];
176ecd
+  struct randfd_list *rfl_spare, *rfl_poll;
176ecd
   int v6pktinfo; 
176ecd
   struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
176ecd
   int log_id, log_display_id; /* ids of transactions for logging */
176ecd
@@ -1227,7 +1232,7 @@ void safe_strncpy(char *dest, const char *src, size_t size);
176ecd
 void safe_pipe(int *fd, int read_noblock);
176ecd
 void *whine_malloc(size_t size);
176ecd
 int sa_len(union mysockaddr *addr);
176ecd
-int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
176ecd
+int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
176ecd
 int hostname_isequal(const char *a, const char *b);
176ecd
 time_t dnsmasq_time(void);
176ecd
 int netmask_length(struct in_addr mask);
176ecd
@@ -1276,7 +1281,7 @@ char *parse_server(char *arg, union mysockaddr *addr,
176ecd
 int option_read_dynfile(char *file, int flags);
176ecd
 
176ecd
 /* forward.c */
176ecd
-void reply_query(int fd, int family, time_t now);
176ecd
+void reply_query(int fd, time_t now);
176ecd
 void receive_query(struct listener *listen, time_t now);
176ecd
 unsigned char *tcp_request(int confd, time_t now,
176ecd
 			   union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
176ecd
@@ -1286,13 +1291,12 @@ int send_from(int fd, int nowild, char *packet, size_t len,
176ecd
 	       union mysockaddr *to, struct all_addr *source,
176ecd
 	       unsigned int iface);
176ecd
 void resend_query(void);
176ecd
-struct randfd *allocate_rfd(int family);
176ecd
-void free_rfd(struct randfd *rfd);
176ecd
+int allocate_rfd(struct randfd_list **fdlp, struct server *serv);
176ecd
+void free_rfds(struct randfd_list **fdlp);
176ecd
 
176ecd
 /* network.c */
176ecd
 int indextoname(int fd, int index, char *name);
176ecd
 int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp);
176ecd
-int random_sock(int family);
176ecd
 void pre_allocate_sfds(void);
176ecd
 int reload_servers(char *fname);
176ecd
 void mark_servers(int flag);
176ecd
diff --git a/src/forward.c b/src/forward.c
176ecd
index 82dd850..11e0310 100644
176ecd
--- a/src/forward.c
176ecd
+++ b/src/forward.c
176ecd
@@ -16,7 +16,7 @@
176ecd
 
176ecd
 #include "dnsmasq.h"
176ecd
 
176ecd
-static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash);
176ecd
+static struct frec *lookup_frec(unsigned short id, int fd, void *hash);
176ecd
 static struct frec *lookup_frec_by_sender(unsigned short id,
176ecd
 					  union mysockaddr *addr,
176ecd
 					  void *hash);
176ecd
@@ -291,29 +291,19 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
176ecd
 	  if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
176ecd
 	    PUTSHORT(SAFE_PKTSZ, pheader);
176ecd
 	  
176ecd
-	  if (forward->sentto->addr.sa.sa_family == AF_INET) 
176ecd
-	    log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
176ecd
-#ifdef HAVE_IPV6
176ecd
-	  else
176ecd
-	    log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
176ecd
-#endif
176ecd
-  
176ecd
-	  if (forward->sentto->sfd)
176ecd
-	    fd = forward->sentto->sfd->fd;
176ecd
-	  else
176ecd
+	  if ((fd = allocate_rfd(&forward->rfds, forward->sentto)) != -1)
176ecd
 	    {
176ecd
+	      if (forward->sentto->addr.sa.sa_family == AF_INET) 
176ecd
+		log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec");
176ecd
 #ifdef HAVE_IPV6
176ecd
-	      if (forward->sentto->addr.sa.sa_family == AF_INET6)
176ecd
-		fd = forward->rfd6->fd;
176ecd
 	      else
176ecd
+		log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec");
176ecd
 #endif
176ecd
-		fd = forward->rfd4->fd;
176ecd
+	      while (retry_send(sendto(fd, (char *)header, plen, 0,
176ecd
+				       &forward->sentto->addr.sa,
176ecd
+				       sa_len(&forward->sentto->addr))));
176ecd
 	    }
176ecd
-	  
176ecd
-	  while (retry_send( sendto(fd, (char *)header, plen, 0,
176ecd
-				    &forward->sentto->addr.sa,
176ecd
-				    sa_len(&forward->sentto->addr))));
176ecd
-	  
176ecd
+
176ecd
 	  return 1;
176ecd
 	}
176ecd
 #endif
176ecd
@@ -490,50 +480,26 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
176ecd
       
176ecd
       while (1)
176ecd
 	{ 
176ecd
+	  int fd;
176ecd
+
176ecd
 	  /* only send to servers dealing with our domain.
176ecd
 	     domain may be NULL, in which case server->domain 
176ecd
 	     must be NULL also. */
176ecd
 	  
176ecd
 	  if (type == (start->flags & SERV_TYPE) &&
176ecd
 	      (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
176ecd
-	      !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
176ecd
+	      !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)) &&
176ecd
+	      ((fd = allocate_rfd(&forward->rfds, start)) != -1))
176ecd
 	    {
176ecd
-	      int fd;
176ecd
-
176ecd
-	      /* find server socket to use, may need to get random one. */
176ecd
-	      if (start->sfd)
176ecd
-		fd = start->sfd->fd;
176ecd
-	      else 
176ecd
-		{
176ecd
-#ifdef HAVE_IPV6
176ecd
-		  if (start->addr.sa.sa_family == AF_INET6)
176ecd
-		    {
176ecd
-		      if (!forward->rfd6 &&
176ecd
-			  !(forward->rfd6 = allocate_rfd(AF_INET6)))
176ecd
-			break;
176ecd
-		      daemon->rfd_save = forward->rfd6;
176ecd
-		      fd = forward->rfd6->fd;
176ecd
-		    }
176ecd
-		  else
176ecd
-#endif
176ecd
-		    {
176ecd
-		      if (!forward->rfd4 &&
176ecd
-			  !(forward->rfd4 = allocate_rfd(AF_INET)))
176ecd
-			break;
176ecd
-		      daemon->rfd_save = forward->rfd4;
176ecd
-		      fd = forward->rfd4->fd;
176ecd
-		    }
176ecd
-
176ecd
 #ifdef HAVE_CONNTRACK
176ecd
-		  /* Copy connection mark of incoming query to outgoing connection. */
176ecd
-		  if (option_bool(OPT_CONNTRACK))
176ecd
-		    {
176ecd
-		      unsigned int mark;
176ecd
-		      if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark))
176ecd
-			setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
176ecd
-		    }
176ecd
-#endif
176ecd
+	      /* Copy connection mark of incoming query to outgoing connection. */
176ecd
+	      if (option_bool(OPT_CONNTRACK))
176ecd
+		{
176ecd
+		  unsigned int mark;
176ecd
+		  if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark))
176ecd
+		    setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
176ecd
 		}
176ecd
+#endif
176ecd
 	      
176ecd
 #ifdef HAVE_DNSSEC
176ecd
 	      if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER))
176ecd
@@ -561,6 +527,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
176ecd
 		  /* Keep info in case we want to re-send this packet */
176ecd
 		  daemon->srv_save = start;
176ecd
 		  daemon->packet_len = plen;
176ecd
+		  daemon->fd_save = fd;
176ecd
 		  
176ecd
 		  if (!gotname)
176ecd
 		    strcpy(daemon->namebuff, "query");
176ecd
@@ -579,7 +546,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
176ecd
 		    break;
176ecd
 		  forward->forwardall++;
176ecd
 		}
176ecd
-	    } 
176ecd
+	    }
176ecd
 	  
176ecd
 	  if (!(start = start->next))
176ecd
  	    start = daemon->servers;
176ecd
@@ -779,7 +746,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
176ecd
 }
176ecd
 
176ecd
 /* sets new last_server */
176ecd
-void reply_query(int fd, int family, time_t now)
176ecd
+void reply_query(int fd, time_t now)
176ecd
 {
176ecd
   /* packet from peer server, extract data for cache, and send to
176ecd
      original requester */
176ecd
@@ -794,9 +761,8 @@ void reply_query(int fd, int family, time_t now)
176ecd
 
176ecd
   /* packet buffer overwritten */
176ecd
   daemon->srv_save = NULL;
176ecd
-  
176ecd
+
176ecd
   /* Determine the address of the server replying  so that we can mark that as good */
176ecd
-  serveraddr.sa.sa_family = family;
176ecd
 #ifdef HAVE_IPV6
176ecd
   if (serveraddr.sa.sa_family == AF_INET6)
176ecd
     serveraddr.in6.sin6_flowinfo = 0;
176ecd
@@ -822,7 +788,7 @@ void reply_query(int fd, int family, time_t now)
176ecd
 
176ecd
   hash = hash_questions(header, n, daemon->namebuff);
176ecd
   
176ecd
-  if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash)))
176ecd
+  if (!(forward = lookup_frec(ntohs(header->id), fd, hash)))
176ecd
     return;
176ecd
   
176ecd
   /* log_query gets called indirectly all over the place, so 
176ecd
@@ -1027,9 +993,8 @@ void reply_query(int fd, int family, time_t now)
176ecd
 			}
176ecd
 		      
176ecd
 		      new->sentto = server;
176ecd
-		      new->rfd4 = NULL;
176ecd
+		      new->rfds = NULL;
176ecd
 #ifdef HAVE_IPV6
176ecd
-		      new->rfd6 = NULL;
176ecd
 #endif
176ecd
 		      new->frec_src.next = NULL;
176ecd
 		      new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
176ecd
@@ -1059,26 +1024,7 @@ void reply_query(int fd, int family, time_t now)
176ecd
 		      /* Don't resend this. */
176ecd
 		      daemon->srv_save = NULL;
176ecd
 		      
176ecd
-		      if (server->sfd)
176ecd
-			fd = server->sfd->fd;
176ecd
-		      else
176ecd
-			{
176ecd
-			  fd = -1;
176ecd
-#ifdef HAVE_IPV6
176ecd
-			  if (server->addr.sa.sa_family == AF_INET6)
176ecd
-			    {
176ecd
-			      if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
176ecd
-				fd = new->rfd6->fd;
176ecd
-			    }
176ecd
-			  else
176ecd
-#endif
176ecd
-			    {
176ecd
-			      if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
176ecd
-				fd = new->rfd4->fd;
176ecd
-			    }
176ecd
-			}
176ecd
-		      
176ecd
-		      if (fd != -1)
176ecd
+		      if ((fd = allocate_rfd(&new->rfds, server)) != -1)
176ecd
 			{
176ecd
 #ifdef HAVE_CONNTRACK
176ecd
 			  /* Copy connection mark of incoming query to outgoing connection. */
176ecd
@@ -1234,7 +1180,7 @@ void receive_query(struct listener *listen, time_t now)
176ecd
 
176ecd
   /* packet buffer overwritten */
176ecd
   daemon->srv_save = NULL;
176ecd
-  
176ecd
+
176ecd
   dst_addr_4.s_addr = dst_addr.addr.addr4.s_addr = 0;
176ecd
   netmask.s_addr = 0;
176ecd
   
176ecd
@@ -2066,10 +2012,9 @@ static struct frec *allocate_frec(time_t now)
176ecd
       f->next = daemon->frec_list;
176ecd
       f->time = now;
176ecd
       f->sentto = NULL;
176ecd
-      f->rfd4 = NULL;
176ecd
+      f->rfds = NULL;
176ecd
       f->flags = 0;
176ecd
 #ifdef HAVE_IPV6
176ecd
-      f->rfd6 = NULL;
176ecd
 #endif
176ecd
 #ifdef HAVE_DNSSEC
176ecd
       f->dependent = NULL;
176ecd
@@ -2082,46 +2027,192 @@ static struct frec *allocate_frec(time_t now)
176ecd
   return f;
176ecd
 }
176ecd
 
176ecd
-struct randfd *allocate_rfd(int family)
176ecd
+/* return a UDP socket bound to a random port, have to cope with straying into
176ecd
+   occupied port nos and reserved ones. */
176ecd
+static int random_sock(struct server *s)
176ecd
 {
176ecd
-  static int finger = 0;
176ecd
-  int i;
176ecd
+  int fd;
176ecd
+
176ecd
+  if ((fd = socket(s->source_addr.sa.sa_family, SOCK_DGRAM, 0)) != -1)
176ecd
+    {
176ecd
+      if (local_bind(fd, &s->source_addr, s->interface, s->ifindex, 0))
176ecd
+	return fd;
176ecd
 
176ecd
+      if (s->interface[0] == 0)
176ecd
+	(void)prettyprint_addr(&s->source_addr, daemon->namebuff);
176ecd
+      else
176ecd
+	strcpy(daemon->namebuff, s->interface);
176ecd
+
176ecd
+      my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"),
176ecd
+		daemon->namebuff, strerror(errno));
176ecd
+      close(fd);
176ecd
+    }
176ecd
+  
176ecd
+  return -1;
176ecd
+}
176ecd
+
176ecd
+/* compare source addresses and interface, serv2 can be null. */
176ecd
+static int server_isequal(const struct server *serv1,
176ecd
+			 const struct server *serv2)
176ecd
+{
176ecd
+  return (serv2 &&
176ecd
+    serv2->ifindex == serv1->ifindex &&
176ecd
+    sockaddr_isequal(&serv2->source_addr, &serv1->source_addr) &&
176ecd
+    strncmp(serv2->interface, serv1->interface, IF_NAMESIZE) == 0);
176ecd
+}
176ecd
+
176ecd
+/* fdlp points to chain of randomfds already in use by transaction.
176ecd
+   If there's already a suitable one, return it, else allocate a 
176ecd
+   new one and add it to the list. 
176ecd
+
176ecd
+   Not leaking any resources in the face of allocation failures
176ecd
+   is rather convoluted here.
176ecd
+   
176ecd
+   Note that rfd->serv may be NULL, when a server goes away.
176ecd
+*/
176ecd
+int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
176ecd
+{
176ecd
+  static int finger = 0;
176ecd
+  int i, j = 0;
176ecd
+  struct randfd_list *rfl;
176ecd
+  struct randfd *rfd = NULL;
176ecd
+  int fd = 0;
176ecd
+  
176ecd
+  /* If server has a pre-allocated fd, use that. */
176ecd
+  if (serv->sfd)
176ecd
+    return serv->sfd->fd;
176ecd
+  
176ecd
+  /* existing suitable random port socket linked to this transaction? */
176ecd
+  for (rfl = *fdlp; rfl; rfl = rfl->next)
176ecd
+    if (server_isequal(serv, rfl->rfd->serv))
176ecd
+      return rfl->rfd->fd;
176ecd
+
176ecd
+  /* No. need new link. */
176ecd
+  if ((rfl = daemon->rfl_spare))
176ecd
+    daemon->rfl_spare = rfl->next;
176ecd
+  else if (!(rfl = whine_malloc(sizeof(struct randfd_list))))
176ecd
+    return -1;
176ecd
+   
176ecd
   /* limit the number of sockets we have open to avoid starvation of 
176ecd
      (eg) TFTP. Once we have a reasonable number, randomness should be OK */
176ecd
-
176ecd
   for (i = 0; i < RANDOM_SOCKS; i++)
176ecd
     if (daemon->randomsocks[i].refcount == 0)
176ecd
       {
176ecd
-	if ((daemon->randomsocks[i].fd = random_sock(family)) == -1)
176ecd
-	  break;
176ecd
-      
176ecd
-	daemon->randomsocks[i].refcount = 1;
176ecd
-	daemon->randomsocks[i].family = family;
176ecd
-	return &daemon->randomsocks[i];
176ecd
+	if ((fd = random_sock(serv)) != -1)
176ecd
+    	  {
176ecd
+	    rfd = &daemon->randomsocks[i];
176ecd
+	    rfd->serv = serv;
176ecd
+	    rfd->fd = fd;
176ecd
+	    rfd->refcount = 1;
176ecd
+	  }
176ecd
+	break;
176ecd
       }
176ecd
-
176ecd
+  
176ecd
   /* No free ones or cannot get new socket, grab an existing one */
176ecd
-  for (i = 0; i < RANDOM_SOCKS; i++)
176ecd
+  if (!rfd)
176ecd
+    for (j = 0; j < RANDOM_SOCKS; j++)
176ecd
+      {
176ecd
+	i = (j + finger) % RANDOM_SOCKS;
176ecd
+	if (daemon->randomsocks[i].refcount != 0 &&
176ecd
+	    server_isequal(serv, daemon->randomsocks[i].serv) &&
176ecd
+	    daemon->randomsocks[i].refcount != 0xfffe)
176ecd
+	  {
176ecd
+	    finger = i + 1;
176ecd
+	    rfd = &daemon->randomsocks[i];
176ecd
+	    rfd->refcount++;
176ecd
+	    break;
176ecd
+	  }
176ecd
+      }
176ecd
+
176ecd
+  if (j == RANDOM_SOCKS)
176ecd
     {
176ecd
-      int j = (i+finger) % RANDOM_SOCKS;
176ecd
-      if (daemon->randomsocks[j].refcount != 0 &&
176ecd
-	  daemon->randomsocks[j].family == family && 
176ecd
-	  daemon->randomsocks[j].refcount != 0xffff)
176ecd
+      struct randfd_list *rfl_poll;
176ecd
+
176ecd
+      /* there are no free slots, and non with the same parameters we can piggy-back on. 
176ecd
+	 We're going to have to allocate a new temporary record, distinguished by
176ecd
+	 refcount == 0xffff. This will exist in the frec randfd list, never be shared,
176ecd
+	 and be freed when no longer in use. It will also be held on 
176ecd
+	 the daemon->rfl_poll list so the poll system can find it. */
176ecd
+
176ecd
+      if ((rfl_poll = daemon->rfl_spare))
176ecd
+	daemon->rfl_spare = rfl_poll->next;
176ecd
+      else
176ecd
+	rfl_poll = whine_malloc(sizeof(struct randfd_list));
176ecd
+      
176ecd
+      if (!rfl_poll ||
176ecd
+	  !(rfd = whine_malloc(sizeof(struct randfd))) ||
176ecd
+	  (fd = random_sock(serv)) == -1)
176ecd
 	{
176ecd
-	  finger = j;
176ecd
-	  daemon->randomsocks[j].refcount++;
176ecd
-	  return &daemon->randomsocks[j];
176ecd
+	  
176ecd
+	  /* Don't leak anything we may already have */
176ecd
+	  rfl->next = daemon->rfl_spare;
176ecd
+	  daemon->rfl_spare = rfl;
176ecd
+
176ecd
+	  if (rfl_poll)
176ecd
+	    {
176ecd
+	      rfl_poll->next = daemon->rfl_spare;
176ecd
+	      daemon->rfl_spare = rfl_poll;
176ecd
+	    }
176ecd
+	  
176ecd
+	  if (rfd)
176ecd
+	    free(rfd);
176ecd
+	  
176ecd
+	  return -1; /* doom */
176ecd
 	}
176ecd
-    }
176ecd
 
176ecd
-  return NULL; /* doom */
176ecd
+      /* Note rfd->serv not set here, since it's not reused */
176ecd
+      rfd->fd = fd;
176ecd
+      rfd->refcount = 0xffff; /* marker for temp record */
176ecd
+
176ecd
+      rfl_poll->rfd = rfd;
176ecd
+      rfl_poll->next = daemon->rfl_poll;
176ecd
+      daemon->rfl_poll = rfl_poll;
176ecd
+    }
176ecd
+  
176ecd
+  rfl->rfd = rfd;
176ecd
+  rfl->next = *fdlp;
176ecd
+  *fdlp = rfl;
176ecd
+  
176ecd
+  return rfl->rfd->fd;
176ecd
 }
176ecd
 
176ecd
-void free_rfd(struct randfd *rfd)
176ecd
+void free_rfds(struct randfd_list **fdlp)
176ecd
 {
176ecd
-  if (rfd && --(rfd->refcount) == 0)
176ecd
-    close(rfd->fd);
176ecd
+  struct randfd_list *tmp, *rfl, *poll, *next, **up;
176ecd
+  
176ecd
+  for (rfl = *fdlp; rfl; rfl = tmp)
176ecd
+    {
176ecd
+      if (rfl->rfd->refcount == 0xffff || --(rfl->rfd->refcount) == 0)
176ecd
+	close(rfl->rfd->fd);
176ecd
+
176ecd
+      /* temporary overflow record */
176ecd
+      if (rfl->rfd->refcount == 0xffff)
176ecd
+	{
176ecd
+	  free(rfl->rfd);
176ecd
+	  
176ecd
+	  /* go through the link of all these by steam to delete.
176ecd
+	     This list is expected to be almost always empty. */
176ecd
+	  for (poll = daemon->rfl_poll, up = &daemon->rfl_poll; poll; poll = next)
176ecd
+	    {
176ecd
+	      next = poll->next;
176ecd
+	      
176ecd
+	      if (poll->rfd == rfl->rfd)
176ecd
+		{
176ecd
+		  *up = poll->next;
176ecd
+		  poll->next = daemon->rfl_spare;
176ecd
+		  daemon->rfl_spare = poll;
176ecd
+		}
176ecd
+	      else
176ecd
+		up = &poll->next;
176ecd
+	    }
176ecd
+	}
176ecd
+
176ecd
+      tmp = rfl->next;
176ecd
+      rfl->next = daemon->rfl_spare;
176ecd
+      daemon->rfl_spare = rfl;
176ecd
+    }
176ecd
+
176ecd
+  *fdlp = NULL;
176ecd
 }
176ecd
 
176ecd
 static void free_frec(struct frec *f)
176ecd
@@ -2137,14 +2228,11 @@ static void free_frec(struct frec *f)
176ecd
     }
176ecd
     
176ecd
   f->frec_src.next = NULL;    
176ecd
-  free_rfd(f->rfd4);
176ecd
-  f->rfd4 = NULL;
176ecd
+  free_rfds(&f->rfds);
176ecd
   f->sentto = NULL;
176ecd
   f->flags = 0;
176ecd
   
176ecd
 #ifdef HAVE_IPV6
176ecd
-  free_rfd(f->rfd6);
176ecd
-  f->rfd6 = NULL;
176ecd
 #endif
176ecd
 
176ecd
 #ifdef HAVE_DNSSEC
176ecd
@@ -2252,26 +2340,39 @@ struct frec *get_new_frec(time_t now, int *wait, int force)
176ecd
 }
176ecd
 
176ecd
 /* crc is all-ones if not known. */
176ecd
-static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash)
176ecd
+static struct frec *lookup_frec(unsigned short id, int fd, void *hash)
176ecd
 {
176ecd
   struct frec *f;
176ecd
-
176ecd
+  struct server *s;
176ecd
+  int type;
176ecd
+  struct randfd_list *fdl;
176ecd
+  
176ecd
   for(f = daemon->frec_list; f; f = f->next)
176ecd
     if (f->sentto && f->new_id == id && 
176ecd
 	(memcmp(hash, f->hash, HASH_SIZE) == 0))
176ecd
       {
176ecd
 	/* sent from random port */
176ecd
-	if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd)
176ecd
-	  return f;
176ecd
-
176ecd
-	if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd)
176ecd
-	  return f;
176ecd
-
176ecd
-	/* sent to upstream from bound socket. */
176ecd
-	if (f->sentto->sfd && f->sentto->sfd->fd == fd)
176ecd
+	for (fdl = f->rfds; fdl; fdl = fdl->next)
176ecd
+	  if (fdl->rfd->fd == fd)
176ecd
 	  return f;
176ecd
+	
176ecd
+	/* Sent to upstream from socket associated with a server. 
176ecd
+	   Note we have to iterate over all the possible servers, since they may
176ecd
+	   have different bound sockets. */
176ecd
+	type = f->sentto->flags & SERV_TYPE;
176ecd
+	s = f->sentto;
176ecd
+	do {
176ecd
+	  if ((type == (s->flags & SERV_TYPE)) &&
176ecd
+	      (type != SERV_HAS_DOMAIN ||
176ecd
+	       (s->domain && hostname_isequal(f->sentto->domain, s->domain))) &&
176ecd
+	      !(s->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)) &&
176ecd
+	      s->sfd && s->sfd->fd == fd)
176ecd
+	    return f;
176ecd
+	  
176ecd
+	  s = s->next ? s->next : daemon->servers;
176ecd
+	} while (s != f->sentto);
176ecd
       }
176ecd
-      
176ecd
+
176ecd
   return NULL;
176ecd
 }
176ecd
 
176ecd
@@ -2317,31 +2418,27 @@ static struct frec *lookup_frec_by_query(void *hash, unsigned int flags)
176ecd
 void resend_query()
176ecd
 {
176ecd
   if (daemon->srv_save)
176ecd
-    {
176ecd
-      int fd;
176ecd
-      
176ecd
-      if (daemon->srv_save->sfd)
176ecd
-	fd = daemon->srv_save->sfd->fd;
176ecd
-      else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
176ecd
-	fd = daemon->rfd_save->fd;
176ecd
-      else
176ecd
-	return;
176ecd
-      
176ecd
-      while(retry_send(sendto(fd, daemon->packet, daemon->packet_len, 0,
176ecd
-			      &daemon->srv_save->addr.sa, 
176ecd
-			      sa_len(&daemon->srv_save->addr)))); 
176ecd
-    }
176ecd
+    while(retry_send(sendto(daemon->fd_save, daemon->packet, daemon->packet_len, 0,
176ecd
+			    &daemon->srv_save->addr.sa, 
176ecd
+			    sa_len(&daemon->srv_save->addr)))); 
176ecd
 }
176ecd
 
176ecd
 /* A server record is going away, remove references to it */
176ecd
 void server_gone(struct server *server)
176ecd
 {
176ecd
   struct frec *f;
176ecd
+  int i;
176ecd
   
176ecd
   for (f = daemon->frec_list; f; f = f->next)
176ecd
     if (f->sentto && f->sentto == server)
176ecd
       free_frec(f);
176ecd
-  
176ecd
+
176ecd
+  /* If any random socket refers to this server, NULL the reference.
176ecd
+     No more references to the socket will be created in the future. */
176ecd
+  for (i = 0; i < RANDOM_SOCKS; i++)
176ecd
+    if (daemon->randomsocks[i].refcount != 0 && daemon->randomsocks[i].serv == server)
176ecd
+      daemon->randomsocks[i].serv = NULL;
176ecd
+
176ecd
   if (daemon->last_server == server)
176ecd
     daemon->last_server = NULL;
176ecd
 
176ecd
diff --git a/src/loop.c b/src/loop.c
176ecd
index 0b47a2f..98d0b9e 100644
176ecd
--- a/src/loop.c
176ecd
+++ b/src/loop.c
176ecd
@@ -22,6 +22,7 @@ static ssize_t loop_make_probe(u32 uid);
176ecd
 void loop_send_probes()
176ecd
 {
176ecd
    struct server *serv;
176ecd
+   struct randfd_list *rfds = NULL;
176ecd
    
176ecd
    if (!option_bool(OPT_LOOP_DETECT))
176ecd
      return;
176ecd
@@ -34,29 +35,22 @@ void loop_send_probes()
176ecd
        {
176ecd
 	 ssize_t len = loop_make_probe(serv->uid);
176ecd
 	 int fd;
176ecd
-	 struct randfd *rfd = NULL;
176ecd
 	 
176ecd
-	 if (serv->sfd)
176ecd
-	   fd = serv->sfd->fd;
176ecd
-	 else 
176ecd
-	   {
176ecd
-	     if (!(rfd = allocate_rfd(serv->addr.sa.sa_family)))
176ecd
-	       continue;
176ecd
-	     fd = rfd->fd;
176ecd
-	   }
176ecd
-
176ecd
+	 if ((fd = allocate_rfd(&rfds, serv)) == -1)
176ecd
+	   continue;
176ecd
+	 
176ecd
 	 while (retry_send(sendto(fd, daemon->packet, len, 0, 
176ecd
 				  &serv->addr.sa, sa_len(&serv->addr))));
176ecd
-	 
176ecd
-	 free_rfd(rfd);
176ecd
        }
176ecd
+
176ecd
+   free_rfds(&rfds);
176ecd
 }
176ecd
   
176ecd
 static ssize_t loop_make_probe(u32 uid)
176ecd
 {
176ecd
   struct dns_header *header = (struct dns_header *)daemon->packet;
176ecd
   unsigned char *p = (unsigned char *)(header+1);
176ecd
-
176ecd
+  
176ecd
   /* packet buffer overwritten */
176ecd
   daemon->srv_save = NULL;
176ecd
   
176ecd
diff --git a/src/network.c b/src/network.c
176ecd
index 47caf38..4eda1fd 100644
176ecd
--- a/src/network.c
176ecd
+++ b/src/network.c
176ecd
@@ -639,7 +639,8 @@ int enumerate_interfaces(int reset)
176ecd
 #ifdef HAVE_AUTH
176ecd
   struct auth_zone *zone;
176ecd
 #endif
176ecd
-
176ecd
+  struct server *serv;
176ecd
+  
176ecd
   /* Do this max once per select cycle  - also inhibits netlink socket use
176ecd
    in TCP child processes. */
176ecd
 
176ecd
@@ -657,6 +658,13 @@ int enumerate_interfaces(int reset)
176ecd
   if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
176ecd
     return 0;
176ecd
 
176ecd
+  /* iface indexes can change when interfaces are created/destroyed.
176ecd
+     We use them in the main forwarding control path, when the path
176ecd
+     to a server is specified by an interface, so cache them.
176ecd
+     Update the cache here. */
176ecd
+  for (serv = daemon->servers; serv; serv = serv->next)
176ecd
+    serv->ifindex = if_nametoindex(serv->interface);
176ecd
+
176ecd
 again:
176ecd
   /* Mark interfaces for garbage collection */
176ecd
   for (iface = daemon->interfaces; iface; iface = iface->next) 
176ecd
@@ -754,7 +762,7 @@ again:
176ecd
 
176ecd
   errno = errsave;
176ecd
   spare = param.spare;
176ecd
-    
176ecd
+  
176ecd
   return ret;
176ecd
 }
176ecd
 
176ecd
@@ -893,10 +901,10 @@ int tcp_interface(int fd, int af)
176ecd
   /* use mshdr so that the CMSDG_* macros are available */
176ecd
   msg.msg_control = daemon->packet;
176ecd
   msg.msg_controllen = len = daemon->packet_buff_sz;
176ecd
-  
176ecd
+
176ecd
   /* we overwrote the buffer... */
176ecd
-  daemon->srv_save = NULL;
176ecd
-  
176ecd
+  daemon->srv_save = NULL; 
176ecd
+
176ecd
   if (af == AF_INET)
176ecd
     {
176ecd
       if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&
176ecd
@@ -1228,61 +1236,6 @@ void join_multicast(int dienow)
176ecd
 }
176ecd
 #endif
176ecd
 
176ecd
-/* return a UDP socket bound to a random port, have to cope with straying into
176ecd
-   occupied port nos and reserved ones. */
176ecd
-int random_sock(int family)
176ecd
-{
176ecd
-  int fd;
176ecd
-
176ecd
-  if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)
176ecd
-    {
176ecd
-      union mysockaddr addr;
176ecd
-      unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
176ecd
-      int tries = ports_avail < 30 ? 3 * ports_avail : 100;
176ecd
-
176ecd
-      memset(&addr, 0, sizeof(addr));
176ecd
-      addr.sa.sa_family = family;
176ecd
-
176ecd
-      /* don't loop forever if all ports in use. */
176ecd
-
176ecd
-      if (fix_fd(fd))
176ecd
-	while(tries--)
176ecd
-	  {
176ecd
-	    unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
176ecd
-	    
176ecd
-	    if (family == AF_INET) 
176ecd
-	      {
176ecd
-		addr.in.sin_addr.s_addr = INADDR_ANY;
176ecd
-		addr.in.sin_port = port;
176ecd
-#ifdef HAVE_SOCKADDR_SA_LEN
176ecd
-		addr.in.sin_len = sizeof(struct sockaddr_in);
176ecd
-#endif
176ecd
-	      }
176ecd
-#ifdef HAVE_IPV6
176ecd
-	    else
176ecd
-	      {
176ecd
-		addr.in6.sin6_addr = in6addr_any; 
176ecd
-		addr.in6.sin6_port = port;
176ecd
-#ifdef HAVE_SOCKADDR_SA_LEN
176ecd
-		addr.in6.sin6_len = sizeof(struct sockaddr_in6);
176ecd
-#endif
176ecd
-	      }
176ecd
-#endif
176ecd
-	    
176ecd
-	    if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0)
176ecd
-	      return fd;
176ecd
-	    
176ecd
-	    if (errno != EADDRINUSE && errno != EACCES)
176ecd
-	      break;
176ecd
-	  }
176ecd
-
176ecd
-      close(fd);
176ecd
-    }
176ecd
-
176ecd
-  return -1; 
176ecd
-}
176ecd
-  
176ecd
-
176ecd
 int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp)
176ecd
 {
176ecd
   union mysockaddr addr_copy = *addr;
176ecd
@@ -1328,39 +1281,34 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind
176ecd
   return 1;
176ecd
 }
176ecd
 
176ecd
-static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
176ecd
+static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, unsigned int ifindex)
176ecd
 {
176ecd
   struct serverfd *sfd;
176ecd
-  unsigned int ifindex = 0;
176ecd
   int errsave;
176ecd
 
176ecd
   /* when using random ports, servers which would otherwise use
176ecd
-     the INADDR_ANY/port0 socket have sfd set to NULL */
176ecd
-  if (!daemon->osport && intname[0] == 0)
176ecd
+     the INADDR_ANY/port0 socket have sfd set to NULL, this is 
176ecd
+     anything without an explictly set source port. */
176ecd
+  if (!daemon->osport)
176ecd
     {
176ecd
       errno = 0;
176ecd
       
176ecd
       if (addr->sa.sa_family == AF_INET &&
176ecd
-	  addr->in.sin_addr.s_addr == INADDR_ANY &&
176ecd
 	  addr->in.sin_port == htons(0)) 
176ecd
 	return NULL;
176ecd
 
176ecd
 #ifdef HAVE_IPV6
176ecd
       if (addr->sa.sa_family == AF_INET6 &&
176ecd
-	  memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
176ecd
 	  addr->in6.sin6_port == htons(0)) 
176ecd
 	return NULL;
176ecd
 #endif
176ecd
     }
176ecd
 
176ecd
-  if (intname && strlen(intname) != 0)
176ecd
-    ifindex = if_nametoindex(intname); /* index == 0 when not binding to an interface */
176ecd
-      
176ecd
   /* may have a suitable one already */
176ecd
   for (sfd = daemon->sfds; sfd; sfd = sfd->next )
176ecd
-    if (sockaddr_isequal(&sfd->source_addr, addr) &&
176ecd
-	strcmp(intname, sfd->interface) == 0 &&
176ecd
-	ifindex == sfd->ifindex) 
176ecd
+    if (ifindex == sfd->ifindex &&
176ecd
+	sockaddr_isequal(&sfd->source_addr, addr) &&
176ecd
+	strcmp(intname, sfd->interface) == 0)
176ecd
       return sfd;
176ecd
   
176ecd
   /* need to make a new one. */
176ecd
@@ -1408,7 +1356,7 @@ void pre_allocate_sfds(void)
176ecd
 #ifdef HAVE_SOCKADDR_SA_LEN
176ecd
       addr.in.sin_len = sizeof(struct sockaddr_in);
176ecd
 #endif
176ecd
-      allocate_sfd(&addr, "");
176ecd
+      allocate_sfd(&addr, "", 0);
176ecd
 #ifdef HAVE_IPV6
176ecd
       memset(&addr, 0, sizeof(addr));
176ecd
       addr.in6.sin6_family = AF_INET6;
176ecd
@@ -1417,13 +1365,13 @@ void pre_allocate_sfds(void)
176ecd
 #ifdef HAVE_SOCKADDR_SA_LEN
176ecd
       addr.in6.sin6_len = sizeof(struct sockaddr_in6);
176ecd
 #endif
176ecd
-      allocate_sfd(&addr, "");
176ecd
+      allocate_sfd(&addr, "", 0);
176ecd
 #endif
176ecd
     }
176ecd
   
176ecd
   for (srv = daemon->servers; srv; srv = srv->next)
176ecd
     if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
176ecd
-	!allocate_sfd(&srv->source_addr, srv->interface) &&
176ecd
+	!allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) &&
176ecd
 	errno != 0 &&
176ecd
 	option_bool(OPT_NOWILD))
176ecd
       {
176ecd
@@ -1631,7 +1579,7 @@ void check_servers(void)
176ecd
 	  
176ecd
 	  /* Do we need a socket set? */
176ecd
 	  if (!serv->sfd && 
176ecd
-	      !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) &&
176ecd
+	      !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) &&
176ecd
 	      errno != 0)
176ecd
 	    {
176ecd
 	      my_syslog(LOG_WARNING, 
176ecd
diff --git a/src/option.c b/src/option.c
176ecd
index 79122df..abc5a48 100644
176ecd
--- a/src/option.c
176ecd
+++ b/src/option.c
176ecd
@@ -795,7 +795,8 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
176ecd
     if (interface_opt)
176ecd
       {
176ecd
 #if defined(SO_BINDTODEVICE)
176ecd
-	safe_strncpy(interface, interface_opt, IF_NAMESIZE);
176ecd
+	safe_strncpy(interface, source, IF_NAMESIZE);
176ecd
+	source = interface_opt;
176ecd
 #else
176ecd
 	return _("interface binding not supported");
176ecd
 #endif
176ecd
diff --git a/src/tftp.c b/src/tftp.c
176ecd
index f2eccbc..ba9833e 100644
176ecd
--- a/src/tftp.c
176ecd
+++ b/src/tftp.c
176ecd
@@ -96,7 +96,7 @@ void tftp_request(struct listener *listen, time_t now)
176ecd
 
176ecd
   if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
176ecd
     return;
176ecd
-
176ecd
+  
176ecd
   /* Can always get recvd interface for IPv6 */
176ecd
   if (!check_dest)
176ecd
     {
176ecd
@@ -566,7 +566,7 @@ void check_tftp_listeners(time_t now)
176ecd
 	{
176ecd
 	  /* we overwrote the buffer... */
176ecd
 	  daemon->srv_save = NULL;
176ecd
-	  
176ecd
+
176ecd
 	  if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
176ecd
 	    {
176ecd
 	      if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block) 
176ecd
@@ -609,7 +609,7 @@ void check_tftp_listeners(time_t now)
176ecd
 	  	  
176ecd
 	  /* we overwrote the buffer... */
176ecd
 	  daemon->srv_save = NULL;
176ecd
-	 
176ecd
+
176ecd
 	  if ((len = get_block(daemon->packet, transfer)) == -1)
176ecd
 	    {
176ecd
 	      len = tftp_err_oops(daemon->packet, transfer->file->filename);
176ecd
diff --git a/src/util.c b/src/util.c
176ecd
index 6287529..d016db6 100644
176ecd
--- a/src/util.c
176ecd
+++ b/src/util.c
176ecd
@@ -311,7 +311,7 @@ void *whine_malloc(size_t size)
176ecd
   return ret;
176ecd
 }
176ecd
 
176ecd
-int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
176ecd
+int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
176ecd
 {
176ecd
   if (s1->sa.sa_family == s2->sa.sa_family)
176ecd
     { 
176ecd
-- 
176ecd
2.26.2
176ecd