Blob Blame History Raw
diff -up dnsmasq-2.65/src/dnsmasq.c.local_queries dnsmasq-2.65/src/dnsmasq.c
--- dnsmasq-2.65/src/dnsmasq.c.local_queries	2013-01-31 09:07:45.603092125 +0100
+++ dnsmasq-2.65/src/dnsmasq.c	2013-01-31 09:07:45.606092127 +0100
@@ -1401,20 +1401,29 @@ static void check_dns_listeners(fd_set *
 	   else 
 	     {
 	       int if_index;
-
+	       char intr_name[IF_NAMESIZE];
+ 
 	       /* In full wildcard mode, need to refresh interface list.
 		  This happens automagically in CLEVERBIND */
-	        if (!option_bool(OPT_CLEVERBIND))
-		  enumerate_interfaces();
-
-		/* if we can find the arrival interface, check it's one that's allowed */
-		if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0)
+	       if (!option_bool(OPT_CLEVERBIND))
+		 enumerate_interfaces();
+	       
+	       /* if we can find the arrival interface, check it's one that's allowed */
+	       if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
+		   indextoname(listener->tcpfd, if_index, intr_name))
 		 {
+		   struct all_addr addr;
+		   addr.addr.addr4 = tcp_addr.in.sin_addr;
+#ifdef HAVE_IPV6
+		   if (tcp_addr.sa.sa_family == AF_INET6)
+		     addr.addr.addr6 = tcp_addr.in6.sin6_addr;
+#endif
+		   
 		   for (iface = daemon->interfaces; iface; iface = iface->next)
 		     if (iface->index == if_index)
 		       break;
 		   
-		   if (!iface)
+		   if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
 		     client_ok = 0;
 		 }
 	       
@@ -1422,10 +1431,10 @@ static void check_dns_listeners(fd_set *
 		 iface = listener->iface; /* May be NULL */
 	       else
 		 {
-		   /* Check for allowed interfaces when binding the wildcard address:
-		      we do this by looking for an interface with the same address as 
-		      the local address of the TCP connection, then looking to see if that's
-		      an allowed interface. As a side effect, we get the netmask of the
+		    /* Check for allowed interfaces when binding the wildcard address:
+		       we do this by looking for an interface with the same address as 
+		       the local address of the TCP connection, then looking to see if that's
+		       an allowed interface. As a side effect, we get the netmask of the
 		      interface too, for localisation. */
 		   
 		   for (iface = daemon->interfaces; iface; iface = iface->next)
diff -up dnsmasq-2.65/src/dnsmasq.h.local_queries dnsmasq-2.65/src/dnsmasq.h
--- dnsmasq-2.65/src/dnsmasq.h.local_queries	2013-01-31 09:07:45.000000000 +0100
+++ dnsmasq-2.65/src/dnsmasq.h	2013-01-31 09:10:36.091202196 +0100
@@ -954,6 +954,7 @@ void create_wildcard_listeners(void);
 void create_bound_listeners(int die);
 int is_dad_listeners(void);
 int iface_check(int family, struct all_addr *addr, char *name);
+int loopback_exception(int fd, int family, struct all_addr *addr, char *name);
 int fix_fd(int fd);
 int tcp_interface(int fd, int af);
 struct in_addr get_ifaddr(char *intr);
diff -up dnsmasq-2.65/src/forward.c.local_queries dnsmasq-2.65/src/forward.c
--- dnsmasq-2.65/src/forward.c.local_queries	2012-12-14 12:48:26.000000000 +0100
+++ dnsmasq-2.65/src/forward.c	2013-01-31 09:19:58.087573008 +0100
@@ -759,10 +759,17 @@ void receive_query(struct listener *list
       
       /* enforce available interface configuration */
       
-      if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
-	  !iface_check(listen->family, &dst_addr, ifr.ifr_name))
+      if (!indextoname(listen->fd, if_index, ifr.ifr_name))
 	return;
       
+	   if (!iface_check(listen->family, &dst_addr, ifr.ifr_name))
+	{
+	   if (!option_bool(OPT_CLEVERBIND))
+	     enumerate_interfaces(); 
+	   if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name))
+	     return;
+	}
+
       if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
 	{
 	  struct irec *iface;
@@ -776,7 +783,7 @@ void receive_query(struct listener *list
 	      break;
 	  
 	  /* interface may be new */
-	  if (!iface)
+	  if (!iface && !option_bool(OPT_CLEVERBIND))
 	    enumerate_interfaces(); 
 	  
 	  for (iface = daemon->interfaces; iface; iface = iface->next)
diff -up dnsmasq-2.65/src/network.c.local_queries dnsmasq-2.65/src/network.c
--- dnsmasq-2.65/src/network.c.local_queries	2013-01-31 09:07:45.000000000 +0100
+++ dnsmasq-2.65/src/network.c	2013-01-31 09:25:28.669822969 +0100
@@ -144,7 +144,39 @@ int iface_check(int family, struct all_a
     
   return ret; 
 }
-      
+
+/* Fix for problem that the kernel sometimes reports the loopback inerface as the
+   arrival interface when a packet originates locally, even when sent to address of 
+   an interface other than the loopback. Accept packet if it arrived via a loopback 
+   interface, even when we're not accepting packets that way, as long as the destination
+   address is one we're believing. Interface list must be up-to-date before calling. */
+int loopback_exception(int fd, int family, struct all_addr *addr, char *name)    
+{
+  struct ifreq ifr;
+  struct irec *iface;
+
+  strncpy(ifr.ifr_name, name, IF_NAMESIZE);
+  if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 &&
+      ifr.ifr_flags & IFF_LOOPBACK)
+    {
+      for (iface = daemon->interfaces; iface; iface = iface->next)
+	if (iface->addr.sa.sa_family == family)
+	  {
+	    if (family == AF_INET)
+	      {
+		if (iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
+		  return 1;
+	      }
+#ifdef HAVE_IPV6
+	    else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr.addr6))
+	      return 1;
+#endif
+	    
+	  }
+    }
+  return 0;
+}
+
 static int iface_allowed(struct irec **irecp, int if_index, 
 			 union mysockaddr *addr, struct in_addr netmask, int dad) 
 {
diff -up dnsmasq-2.65/src/tftp.c.local_queries dnsmasq-2.65/src/tftp.c
--- dnsmasq-2.65/src/tftp.c.local_queries	2012-12-14 12:48:26.000000000 +0100
+++ dnsmasq-2.65/src/tftp.c	2013-01-31 09:49:44.478008214 +0100
@@ -61,6 +61,7 @@ void tftp_request(struct listener *liste
   char *name = NULL;
   char *prefix = daemon->tftp_prefix;
   struct tftp_prefix *pref;
+  struct all_addr addra;
 
   union {
     struct cmsghdr align; /* this ensures alignment */
@@ -190,16 +191,19 @@ void tftp_request(struct listener *liste
 
       name = namebuff;
 
+      addra.addr.addr4 = addr.in.sin_addr;
+
 #ifdef HAVE_IPV6
       if (listen->family == AF_INET6)
+        addra.addr.addr6 = addr.in6.sin6_addr;
+#endif
+    if (!iface_check(listen->family, &addra, name))
 	{
-	  if (!iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name))
+	  if (!option_bool(OPT_CLEVERBIND))
+	    enumerate_interfaces(); 
+	  if (!loopback_exception(listen->tftpfd, listen->family, &addra, name))
 	    return;
 	}
-      else
-#endif
-        if (!iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name))
-	  return;
 
 #ifdef HAVE_DHCP      
       /* allowed interfaces are the same as for DHCP */