]> git.ipfire.org Git - people/ms/dnsmasq.git/blobdiff - src/network.c
import of dnsmasq-2.20.tar.gz
[people/ms/dnsmasq.git] / src / network.c
index 52503712824e4736345aff3e30b1a2de257e0727..216d70f0265471923aba83fd62ca11144fbc147f 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
+/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include "dnsmasq.h"
 
-static struct irec *add_iface(struct daemon *daemon, struct irec *list
-                             char *name, int is_loopback, union mysockaddr *addr) 
+static int iface_allowed(struct daemon *daemon, struct irec *iface
+                        char *name, int is_loopback, union mysockaddr *addr) 
 {
-  struct irec *iface;
   struct iname *tmp;
   
   /* If we are restricting the set of interfaces to use, make
@@ -45,12 +44,8 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list,
   if (daemon->if_except)
     for (tmp = daemon->if_except; tmp; tmp = tmp->next)
       if (tmp->name && strcmp(tmp->name, name) == 0)
-       {
-         /* record address of named interfaces, for TCP access control */
-         tmp->addr = *addr;
-         return list;
-       }
-
+       return 0;
+       
   /* we may need to check the whitelist */
   if (daemon->if_names || daemon->if_addrs)
     { 
@@ -58,36 +53,39 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list,
 
       for (tmp = daemon->if_names; tmp; tmp = tmp->next)
        if (tmp->name && (strcmp(tmp->name, name) == 0))
-         {
-           tmp->addr = *addr;
-           found = tmp->used = 1;
-         }
-
+         found = tmp->used = 1;
+         
       for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
        if (sockaddr_isequal(&tmp->addr, addr))
          found = tmp->used = 1;
       
       if (!found) 
-       return list;
+       return 0;
     }
   
   /* check whether the interface IP has been added already 
      it is possible to have multiple interfaces with the same address */
-  for (iface = list; iface; iface = iface->next) 
+  for (; iface; iface = iface->next) 
     if (sockaddr_isequal(&iface->addr, addr))
       break;
   if (iface)
-    return list;
+    return 0;
   
-  /* If OK, add it to the head of the list */
-  iface = safe_malloc(sizeof(struct irec));
-  iface->addr = *addr;
-  iface->next = list;
-  return iface;
+  return 1;
 }
 
+/* This does two different jobs: if chainp is non-NULL, it puts
+   a list of all the interfaces allowed by config into *chainp.
+   If chainp is NULL, it returns 1 if addr is  an address of an interface
+   allowed by config and if that address is IPv4, it fills in the
+   netmask of the interface. 
+   
+   If chainp is non-NULL, a zero return indicates a fatal error.
 
-struct irec *enumerate_interfaces(struct daemon *daemon)
+   If chainp is NULL, errors result in a match failure and zero return.
+*/
+int enumerate_interfaces(struct daemon *daemon, struct irec **chainp,
+                        union mysockaddr *test_addrp, struct in_addr *netmaskp)
 {
 #if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
   FILE *f;
@@ -100,9 +98,16 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
   int lastlen = 0;
   int len = 20 * sizeof(struct ifreq);
   int fd = socket(PF_INET, SOCK_DGRAM, 0);
+  struct in_addr netmask;
+  int ret = 0;
 
   if (fd == -1)
-    die ("cannot create socket to enumerate interfaces: %s", NULL);
+    return 0;
+
+#ifdef HAVE_IPV6
+  if (test_addrp && test_addrp->sa.sa_family == AF_INET6)
+    test_addrp->in6.sin6_flowinfo = htonl(0);
+#endif
         
   while (1)
      {
@@ -113,7 +118,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
        if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
         {
           if (errno != EINVAL || lastlen != 0)
-            die ("ioctl error while enumerating interfaces: %s", NULL);
+            goto exit;
         }
        else
         {
@@ -133,7 +138,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
         unaligned accesses. */
       int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
       if (!(ifr = realloc(ifr, ifr_len)))
-       die("cannot allocate buffer", NULL);
+       goto exit;
       
       memcpy(ifr, ptr, ifr_len);
       ptr += ifr_len;
@@ -147,6 +152,9 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
        {
          addr.in = *((struct sockaddr_in *) &ifr->ifr_addr);
          addr.in.sin_port = htons(daemon->port);
+         if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
+           goto exit;
+         netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
        }
 #ifdef HAVE_IPV6
       else if (ifr->ifr_addr.sa_family == AF_INET6)
@@ -164,9 +172,25 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
        continue; /* unknown address family */
       
       if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0)
-       die("ioctl error getting interface flags: %m", NULL);
+       goto exit;
 
-      iface = add_iface(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr);
+      if (iface_allowed(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr))
+       {
+         if (chainp)
+           {
+             struct irec *new = safe_malloc(sizeof(struct irec));
+             new->addr = addr;
+             new->netmask = netmask;
+             new->next = iface;
+             iface = new;
+           }
+         else if (sockaddr_isequal(&addr, test_addrp))
+           {
+             *netmaskp = netmask;
+             ret = 1;
+             goto exit;
+           }
+       }
     }
 
 #if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
@@ -198,13 +222,35 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
          
          strncpy(sifr.ifr_name, devname, IF_NAMESIZE);
          if (ioctl(fd, SIOCGIFFLAGS, &sifr) < 0)
-           die("ioctl error getting interface flags: %m", NULL);
-         iface = add_iface(daemon, iface, sifr.ifr_name, sifr.ifr_flags & IFF_LOOPBACK, &addr);
+           goto exit;
+         
+         if (iface_allowed(daemon, iface, sifr.ifr_name, sifr.ifr_flags & IFF_LOOPBACK, &addr))
+           {
+             if (chainp)
+               {
+                 struct irec *new = safe_malloc(sizeof(struct irec));
+                 new->addr = addr;
+                 new->next = iface;
+                 iface = new;
+               }
+             else if (sockaddr_isequal(&addr, test_addrp))
+               {
+                 ret = 1;
+                 goto exit;
+               }
+           }
        }           
       fclose(f);
     }
 #endif /* LINUX */
-   
+  
+  if (chainp)
+    {
+      *chainp = iface;
+      ret = 1;
+    }
+ exit:  
   if (buf)
     free(buf);
 #ifdef HAVE_SOCKADDR_SA_LEN
@@ -213,7 +259,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
 #endif
   close(fd);
 
-  return iface;
+  return ret;
 }
 
 #ifdef HAVE_IPV6
@@ -354,6 +400,7 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port)
     {
       struct listener *new = safe_malloc(sizeof(struct listener));
       new->family = iface->addr.sa.sa_family;
+      new->iface = iface;
       new->next = listeners;
       if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 ||
          (new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||