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