Diff for /embedaddon/dnsmasq/src/network.c between versions 1.1.1.1 and 1.1.1.2

version 1.1.1.1, 2013/07/29 19:37:40 version 1.1.1.2, 2014/06/15 16:31:38
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     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     it under the terms of the GNU General Public License as published by
Line 16 Line 16
   
 #include "dnsmasq.h"  #include "dnsmasq.h"
   
   #ifndef IN6_IS_ADDR_ULA
   #define IN6_IS_ADDR_ULA(a) ((((__const uint32_t *) (a))[0] & htonl (0xfe00000)) == htonl (0xfc000000))
   #endif
   
 #ifdef HAVE_LINUX_NETWORK  #ifdef HAVE_LINUX_NETWORK
   
 int indextoname(int fd, int index, char *name)  int indextoname(int fd, int index, char *name)
Line 99  int indextoname(int fd, int index, char *name) Line 103  int indextoname(int fd, int index, char *name)
   
 int indextoname(int fd, int index, char *name)  int indextoname(int fd, int index, char *name)
 {   { 
     (void)fd;
   
   if (index == 0 || !if_indextoname(index, name))    if (index == 0 || !if_indextoname(index, name))
     return 0;      return 0;
   
Line 110  int indextoname(int fd, int index, char *name) Line 116  int indextoname(int fd, int index, char *name)
 int iface_check(int family, struct all_addr *addr, char *name, int *auth)  int iface_check(int family, struct all_addr *addr, char *name, int *auth)
 {  {
   struct iname *tmp;    struct iname *tmp;
  int ret = 1;  int ret = 1, match_addr = 0;
   
   /* Note: have to check all and not bail out early, so that we set the    /* Note: have to check all and not bail out early, so that we set the
     "used" flags. */     "used" flags.
 
      May be called with family == AF_LOCALto check interface by name only. */
       
   if (auth)    if (auth)
     *auth = 0;      *auth = 0;
Line 132  int iface_check(int family, struct all_addr *addr, cha Line 140  int iface_check(int family, struct all_addr *addr, cha
             {              {
               if (family == AF_INET &&                if (family == AF_INET &&
                   tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)                    tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
                ret = tmp->used = 1;                ret = match_addr = tmp->used = 1;
 #ifdef HAVE_IPV6  #ifdef HAVE_IPV6
               else if (family == AF_INET6 &&                else if (family == AF_INET6 &&
                        IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr,                          IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, 
                                           &addr->addr.addr6))                                            &addr->addr.addr6))
                ret = tmp->used = 1;                ret = match_addr = tmp->used = 1;
 #endif  #endif
             }                        }          
     }      }
       
  for (tmp = daemon->if_except; tmp; tmp = tmp->next)  if (!match_addr)
    if (tmp->name && wildcard_match(tmp->name, name))    for (tmp = daemon->if_except; tmp; tmp = tmp->next)
      ret = 0;      if (tmp->name && wildcard_match(tmp->name, name))
         ret = 0;
           
   
   for (tmp = daemon->authinterface; tmp; tmp = tmp->next)    for (tmp = daemon->authinterface; tmp; tmp = tmp->next)
     if (tmp->name)      if (tmp->name)
       {        {
        if (strcmp(tmp->name, name) == 0)        if (strcmp(tmp->name, name) == 0 &&
             (tmp->addr.sa.sa_family == 0 || tmp->addr.sa.sa_family == family))
           break;            break;
       }        }
     else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET &&      else if (addr && tmp->addr.sa.sa_family == AF_INET && family == AF_INET &&
Line 204  int loopback_exception(int fd, int family, struct all_ Line 214  int loopback_exception(int fd, int family, struct all_
   return 0;    return 0;
 }  }
   
static int iface_allowed(struct irec **irecp, int if_index, /* If we're configured with something like --interface=eth0:0 then we'll listen correctly
                         union mysockaddr *addr, struct in_addr netmask, int dad)    on the relevant address, but the name of the arrival interface, derived from the
    index won't match the config. Check that we found an interface address for the arrival 
    interface: daemon->interfaces must be up-to-date. */
 int label_exception(int index, int family, struct all_addr *addr)
 {  {
   struct irec *iface;    struct irec *iface;
  int fd, mtu = 0, loopback;
   /* labels only supported on IPv4 addresses. */
   if (family != AF_INET)
     return 0;
 
   for (iface = daemon->interfaces; iface; iface = iface->next)
     if (iface->index == index && iface->addr.sa.sa_family == AF_INET &&
         iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
       return 1;
 
   return 0;
 }
 
 struct iface_param {
   struct addrlist *spare;
   int fd;
 };
 
 static int iface_allowed(struct iface_param *param, int if_index, char *label,
                          union mysockaddr *addr, struct in_addr netmask, int prefixlen, int dad) 
 {
   struct irec *iface;
   int mtu = 0, loopback;
   struct ifreq ifr;    struct ifreq ifr;
   int tftp_ok = !!option_bool(OPT_TFTP);    int tftp_ok = !!option_bool(OPT_TFTP);
   int dhcp_ok = 1;    int dhcp_ok = 1;
   int auth_dns = 0;    int auth_dns = 0;
#ifdef HAVE_DHCP#if defined(HAVE_DHCP) || defined(HAVE_TFTP)
   struct iname *tmp;    struct iname *tmp;
 #endif  #endif
   
  /* check whether the interface IP has been added already   (void)prefixlen;
     we call this routine multiple times. */ 
  for (iface = *irecp; iface; iface = iface->next)  
    if (sockaddr_isequal(&iface->addr, addr)) 
      { 
        iface->dad = dad; 
        return 1; 
      } 
   
  if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 ||  if (!indextoname(param->fd, if_index, ifr.ifr_name) ||
      !indextoname(fd, if_index, ifr.ifr_name) ||      ioctl(param->fd, SIOCGIFFLAGS, &ifr) == -1)
      ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)    return 0;
    { 
      if (fd != -1) 
        { 
          int errsave = errno; 
          close(fd); 
          errno = errsave; 
        } 
      return 0; 
    } 
         
   loopback = ifr.ifr_flags & IFF_LOOPBACK;    loopback = ifr.ifr_flags & IFF_LOOPBACK;
       
   if (loopback)    if (loopback)
     dhcp_ok = 0;    dhcp_ok = 0;
  
  if (ioctl(fd, SIOCGIFMTU, &ifr) != -1)  if (ioctl(param->fd, SIOCGIFMTU, &ifr) != -1)
     mtu = ifr.ifr_mtu;      mtu = ifr.ifr_mtu;
       
  close(fd);  if (!label)
     label = ifr.ifr_name;
  
   /* maintain a list of all addresses on all interfaces for --local-service option */
   if (option_bool(OPT_LOCAL_SERVICE))
     {
       struct addrlist *al;
 
       if (param->spare)
         {
           al = param->spare;
           param->spare = al->next;
         }
       else
         al = whine_malloc(sizeof(struct addrlist));
       
       if (al)
         {
           al->next = daemon->interface_addrs;
           daemon->interface_addrs = al;
           al->prefixlen = prefixlen;
           
           if (addr->sa.sa_family == AF_INET)
             {
               al->addr.addr.addr4 = addr->in.sin_addr;
               al->flags = 0;
             }
 #ifdef HAVE_IPV6
           else
             {
               al->addr.addr.addr6 = addr->in6.sin6_addr;
               al->flags = ADDRLIST_IPV6;
             } 
 #endif
         }
     }
       
  /* If we are restricting the set of interfaces to use, make#ifdef HAVE_IPV6
   if (addr->sa.sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
 #endif
     {
       struct interface_name *int_name;
       struct addrlist *al;
 #ifdef HAVE_AUTH
       struct auth_zone *zone;
       struct auth_name_list *name;
 
       /* Find subnets in auth_zones */
       for (zone = daemon->auth_zones; zone; zone = zone->next)
         for (name = zone->interface_names; name; name = name->next)
           if (wildcard_match(name->name, label))
             {
               if (addr->sa.sa_family == AF_INET && (name->flags & AUTH4))
                 {
                   if (param->spare)
                     {
                       al = param->spare;
                       param->spare = al->next;
                     }
                   else
                     al = whine_malloc(sizeof(struct addrlist));
                   
                   if (al)
                     {
                       al->next = zone->subnet;
                       zone->subnet = al;
                       al->prefixlen = prefixlen;
                       al->addr.addr.addr4 = addr->in.sin_addr;
                       al->flags = 0;
                     }
                 }
               
 #ifdef HAVE_IPV6
               if (addr->sa.sa_family == AF_INET6 && (name->flags & AUTH6))
                 {
                   if (param->spare)
                     {
                       al = param->spare;
                       param->spare = al->next;
                     }
                   else
                     al = whine_malloc(sizeof(struct addrlist));
                   
                   if (al)
                     {
                       al->next = zone->subnet;
                       zone->subnet = al;
                       al->prefixlen = prefixlen;
                       al->addr.addr.addr6 = addr->in6.sin6_addr;
                       al->flags = ADDRLIST_IPV6;
                     }
                 } 
 #endif
               
             }
 #endif
        
       /* Update addresses from interface_names. These are a set independent
          of the set we're listening on. */  
       for (int_name = daemon->int_names; int_name; int_name = int_name->next)
         if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0 && 
             (addr->sa.sa_family == int_name->family || int_name->family == 0))
           {
             if (param->spare)
               {
                 al = param->spare;
                 param->spare = al->next;
               }
             else
               al = whine_malloc(sizeof(struct addrlist));
             
             if (al)
               {
                 al->next = int_name->addr;
                 int_name->addr = al;
                 
                 if (addr->sa.sa_family == AF_INET)
                   {
                     al->addr.addr.addr4 = addr->in.sin_addr;
                     al->flags = 0;
                   }
 #ifdef HAVE_IPV6
                 else
                  {
                     al->addr.addr.addr6 = addr->in6.sin6_addr;
                     al->flags = ADDRLIST_IPV6;
                  } 
 #endif
               }
           }
     }
  
   /* check whether the interface IP has been added already 
      we call this routine multiple times. */
   for (iface = daemon->interfaces; iface; iface = iface->next) 
     if (sockaddr_isequal(&iface->addr, addr))
       {
         iface->dad = dad;
         iface->found = 1; /* for garbage collection */
         return 1;
       }
 
  /* If we are restricting the set of interfaces to use, make
      sure that loopback interfaces are in that set. */       sure that loopback interfaces are in that set. */
   if (daemon->if_names && loopback)    if (daemon->if_names && loopback)
     {      {
Line 273  static int iface_allowed(struct irec **irecp, int if_i Line 432  static int iface_allowed(struct irec **irecp, int if_i
     }      }
       
   if (addr->sa.sa_family == AF_INET &&    if (addr->sa.sa_family == AF_INET &&
      !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, &auth_dns))      !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, label, &auth_dns))
     return 1;      return 1;
   
 #ifdef HAVE_IPV6  #ifdef HAVE_IPV6
   if (addr->sa.sa_family == AF_INET6 &&    if (addr->sa.sa_family == AF_INET6 &&
      !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, &auth_dns))      !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, label, &auth_dns))
     return 1;      return 1;
 #endif  #endif
           
Line 298  static int iface_allowed(struct irec **irecp, int if_i Line 457  static int iface_allowed(struct irec **irecp, int if_i
         }          }
 #endif  #endif
     
     
   #ifdef HAVE_TFTP
     if (daemon->tftp_interfaces)
       {
         /* dedicated tftp interface list */
         tftp_ok = 0;
         for (tmp = daemon->tftp_interfaces; tmp; tmp = tmp->next)
           if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
             tftp_ok = 1;
       }
   #endif
     
   /* add to list */    /* add to list */
   if ((iface = whine_malloc(sizeof(struct irec))))    if ((iface = whine_malloc(sizeof(struct irec))))
     {      {
Line 308  static int iface_allowed(struct irec **irecp, int if_i Line 479  static int iface_allowed(struct irec **irecp, int if_i
       iface->dns_auth = auth_dns;        iface->dns_auth = auth_dns;
       iface->mtu = mtu;        iface->mtu = mtu;
       iface->dad = dad;        iface->dad = dad;
      iface->done = iface->multicast_done = 0;      iface->found = 1;
       iface->done = iface->multicast_done = iface->warned = 0;
       iface->index = if_index;        iface->index = if_index;
       if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))        if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
         {          {
           strcpy(iface->name, ifr.ifr_name);            strcpy(iface->name, ifr.ifr_name);
          iface->next = *irecp;          iface->next = daemon->interfaces;
          *irecp = iface;          daemon->interfaces = iface;
           return 1;            return 1;
         }          }
       free(iface);        free(iface);
Line 334  static int iface_allowed_v6(struct in6_addr *local, in Line 506  static int iface_allowed_v6(struct in6_addr *local, in
   struct in_addr netmask; /* dummy */    struct in_addr netmask; /* dummy */
   netmask.s_addr = 0;    netmask.s_addr = 0;
   
   (void)prefix; /* warning */  
   (void)scope; /* warning */    (void)scope; /* warning */
   (void)preferred;    (void)preferred;
   (void)valid;    (void)valid;
Line 346  static int iface_allowed_v6(struct in6_addr *local, in Line 517  static int iface_allowed_v6(struct in6_addr *local, in
   addr.in6.sin6_family = AF_INET6;    addr.in6.sin6_family = AF_INET6;
   addr.in6.sin6_addr = *local;    addr.in6.sin6_addr = *local;
   addr.in6.sin6_port = htons(daemon->port);    addr.in6.sin6_port = htons(daemon->port);
  addr.in6.sin6_scope_id = if_index;  /* FreeBSD insists this is zero for non-linklocal addresses */
   if (IN6_IS_ADDR_LINKLOCAL(local))
     addr.in6.sin6_scope_id = if_index;
   else
     addr.in6.sin6_scope_id = 0;
       
  return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, !!(flags & IFACE_TENTATIVE));  return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, !!(flags & IFACE_TENTATIVE));
 }  }
 #endif  #endif
   
static int iface_allowed_v4(struct in_addr local, int if_index, static int iface_allowed_v4(struct in_addr local, int if_index, char *label,
                             struct in_addr netmask, struct in_addr broadcast, void *vparam)                              struct in_addr netmask, struct in_addr broadcast, void *vparam)
 {  {
   union mysockaddr addr;    union mysockaddr addr;
     int prefix, bit;
   
   memset(&addr, 0, sizeof(addr));    memset(&addr, 0, sizeof(addr));
 #ifdef HAVE_SOCKADDR_SA_LEN  #ifdef HAVE_SOCKADDR_SA_LEN
Line 366  static int iface_allowed_v4(struct in_addr local, int  Line 542  static int iface_allowed_v4(struct in_addr local, int 
   addr.in.sin_addr = local;    addr.in.sin_addr = local;
   addr.in.sin_port = htons(daemon->port);    addr.in.sin_port = htons(daemon->port);
   
  return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, 0);  /* determine prefix length from netmask */
   for (prefix = 32, bit = 1; (bit & ntohl(netmask.s_addr)) == 0 && prefix != 0; bit = bit << 1, prefix--);
 
   return iface_allowed((struct iface_param *)vparam, if_index, label, &addr, netmask, prefix, 0);
 }  }
         
int enumerate_interfaces(void)int enumerate_interfaces(int reset)
 {  {
     static struct addrlist *spare = NULL;
     static int done = 0, active = 0;
     struct iface_param param;
     int errsave, ret = 1;
     struct addrlist *addr, *tmp;
     struct interface_name *intname;
     struct irec *iface;
   #ifdef HAVE_AUTH
     struct auth_zone *zone;
   #endif
   
     /* Do this max once per select cycle  - also inhibits netlink socket use
      in TCP child processes. */
   
     if (reset)
       {
         done = 0;
         return 1;
       }
   
     if (done || active)
       return 1;
   
     done = 1;
   
     /* protect against recusive calls from iface_enumerate(); */
     active = 1;
   
     if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
       return 0;
    
     /* Mark interfaces for garbage collection */
     for (iface = daemon->interfaces; iface; iface = iface->next) 
       iface->found = 0;
   
     /* remove addresses stored against interface_names */
     for (intname = daemon->int_names; intname; intname = intname->next)
       {
         for (addr = intname->addr; addr; addr = tmp)
           {
             tmp = addr->next;
             addr->next = spare;
             spare = addr;
           }
         
         intname->addr = NULL;
       }
   
     /* Remove list of addresses of local interfaces */
     for (addr = daemon->interface_addrs; addr; addr = tmp)
       {
         tmp = addr->next;
         addr->next = spare;
         spare = addr;
       }
     daemon->interface_addrs = NULL;
     
   #ifdef HAVE_AUTH
     /* remove addresses stored against auth_zone subnets, but not 
      ones configured as address literals */
     for (zone = daemon->auth_zones; zone; zone = zone->next)
       if (zone->interface_names)
         {
           struct addrlist **up;
           for (up = &zone->subnet, addr = zone->subnet; addr; addr = tmp)
             {
               tmp = addr->next;
               if (addr->flags & ADDRLIST_LITERAL)
                 up = &addr->next;
               else
                 {
                   *up = addr->next;
                   addr->next = spare;
                   spare = addr;
                 }
             }
         }
   #endif
   
     param.spare = spare;
     
 #ifdef HAVE_IPV6  #ifdef HAVE_IPV6
  if (!iface_enumerate(AF_INET6, &daemon->interfaces, iface_allowed_v6))  ret = iface_enumerate(AF_INET6, &param, iface_allowed_v6);
    return 0;  
 #endif  #endif
   
  return iface_enumerate(AF_INET, &daemon->interfaces, iface_allowed_v4);   if (ret)
     ret = iface_enumerate(AF_INET, &param, iface_allowed_v4); 
  
   errsave = errno;
   close(param.fd);
   
   if (option_bool(OPT_CLEVERBIND))
     { 
       /* Garbage-collect listeners listening on addresses that no longer exist.
          Does nothing when not binding interfaces or for listeners on localhost, 
          since the ->iface field is NULL. Note that this needs the protections
          against re-entrancy, hence it's here.  It also means there's a possibility,
          in OPT_CLEVERBIND mode, that at listener will just disappear after
          a call to enumerate_interfaces, this is checked OK on all calls. */
       struct listener *l, *tmp, **up;
       
       for (up = &daemon->listeners, l = daemon->listeners; l; l = tmp)
         {
           tmp = l->next;
           
           if (!l->iface || l->iface->found)
             up = &l->next;
           else
             {
               *up = l->next;
               
               /* In case it ever returns */
               l->iface->done = 0;
               
               if (l->fd != -1)
                 close(l->fd);
               if (l->tcpfd != -1)
                 close(l->tcpfd);
               if (l->tftpfd != -1)
                 close(l->tftpfd);
               
               free(l);
             }
         }
     }
   
   errno = errsave;
   
   spare = param.spare;
   active = 0;
   
   return ret;
 }  }
   
 /* set NONBLOCK bit on fd: See Stevens 16.6 */  /* set NONBLOCK bit on fd: See Stevens 16.6 */
Line 398  static int make_sock(union mysockaddr *addr, int type, Line 703  static int make_sock(union mysockaddr *addr, int type,
       
   if ((fd = socket(family, type, 0)) == -1)    if ((fd = socket(family, type, 0)) == -1)
     {      {
      int port;      int port, errsav;
       char *s;        char *s;
   
       /* No error if the kernel just doesn't support this IP flavour */        /* No error if the kernel just doesn't support this IP flavour */
Line 408  static int make_sock(union mysockaddr *addr, int type, Line 713  static int make_sock(union mysockaddr *addr, int type,
         return -1;          return -1;
               
     err:      err:
         errsav = errno;
       port = prettyprint_addr(addr, daemon->addrbuff);        port = prettyprint_addr(addr, daemon->addrbuff);
       if (!option_bool(OPT_NOWILD) && !option_bool(OPT_CLEVERBIND))        if (!option_bool(OPT_NOWILD) && !option_bool(OPT_CLEVERBIND))
         sprintf(daemon->addrbuff, "port %d", port);          sprintf(daemon->addrbuff, "port %d", port);
Line 415  static int make_sock(union mysockaddr *addr, int type, Line 721  static int make_sock(union mysockaddr *addr, int type,
               
       if (fd != -1)        if (fd != -1)
         close (fd);          close (fd);
              
       errno = errsav;
 
       if (dienow)        if (dienow)
         {          {
           /* failure to bind addresses given by --listen-address at this point            /* failure to bind addresses given by --listen-address at this point
Line 445  static int make_sock(union mysockaddr *addr, int type, Line 753  static int make_sock(union mysockaddr *addr, int type,
       if (listen(fd, 5) == -1)        if (listen(fd, 5) == -1)
         goto err;          goto err;
     }      }
  else if (!option_bool(OPT_NOWILD))  else if (family == AF_INET)
     {      {
      if (family == AF_INET)      if (!option_bool(OPT_NOWILD))
         {          {
 #if defined(HAVE_LINUX_NETWORK)   #if defined(HAVE_LINUX_NETWORK) 
           if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1)            if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1)
Line 458  static int make_sock(union mysockaddr *addr, int type, Line 766  static int make_sock(union mysockaddr *addr, int type,
             goto err;              goto err;
 #endif  #endif
         }          }
       }
 #ifdef HAVE_IPV6  #ifdef HAVE_IPV6
      else if (!set_ipv6pktinfo(fd))  else if (!set_ipv6pktinfo(fd))
        goto err;    goto err;
 #endif  #endif
     }  
       
   return fd;    return fd;
 }  }
Line 572  static struct listener *create_listeners(union mysocka Line 880  static struct listener *create_listeners(union mysocka
   struct listener *l = NULL;    struct listener *l = NULL;
   int fd = -1, tcpfd = -1, tftpfd = -1;    int fd = -1, tcpfd = -1, tftpfd = -1;
   
     (void)do_tftp;
   
   if (daemon->port != 0)    if (daemon->port != 0)
     {      {
       fd = make_sock(addr, SOCK_DGRAM, dienow);        fd = make_sock(addr, SOCK_DGRAM, dienow);
Line 608  static struct listener *create_listeners(union mysocka Line 918  static struct listener *create_listeners(union mysocka
       l->family = addr->sa.sa_family;        l->family = addr->sa.sa_family;
       l->fd = fd;        l->fd = fd;
       l->tcpfd = tcpfd;        l->tcpfd = tcpfd;
      l->tftpfd = tftpfd;      l->tftpfd = tftpfd;       
       l->iface = NULL;
     }      }
   
   return l;    return l;
Line 655  void create_bound_listeners(int dienow) Line 966  void create_bound_listeners(int dienow)
   struct iname *if_tmp;    struct iname *if_tmp;
   
   for (iface = daemon->interfaces; iface; iface = iface->next)    for (iface = daemon->interfaces; iface; iface = iface->next)
    if (!iface->done && !iface->dad &&     if (!iface->done && !iface->dad && iface->found &&
         (new = create_listeners(&iface->addr, iface->tftp_ok, dienow)))          (new = create_listeners(&iface->addr, iface->tftp_ok, dienow)))
       {        {
         new->iface = iface;          new->iface = iface;
Line 679  void create_bound_listeners(int dienow) Line 990  void create_bound_listeners(int dienow)
     if (!if_tmp->used &&       if (!if_tmp->used && 
         (new = create_listeners(&if_tmp->addr, !!option_bool(OPT_TFTP), dienow)))          (new = create_listeners(&if_tmp->addr, !!option_bool(OPT_TFTP), dienow)))
       {        {
         new->iface = NULL;  
         new->next = daemon->listeners;          new->next = daemon->listeners;
         daemon->listeners = new;          daemon->listeners = new;
       }        }
 }  }
   
   /* In --bind-interfaces, the only access control is the addresses we're listening on. 
      There's nothing to avoid a query to the address of an internal interface arriving via
      an external interface where we don't want to accept queries, except that in the usual 
      case the addresses of internal interfaces are RFC1918. When bind-interfaces in use, 
      and we listen on an address that looks like it's probably globally routeable, shout.
   
      The fix is to use --bind-dynamic, which actually checks the arrival interface too.
      Tough if your platform doesn't support this.
   
      Note that checking the arrival interface is supported in the standard IPv6 API and
      always done, so we don't warn about any IPv6 addresses here.
   */
   
   void warn_bound_listeners(void)
   {
     struct irec *iface;   
     int advice = 0;
   
     for (iface = daemon->interfaces; iface; iface = iface->next)
       if (!iface->dns_auth)
         {
           if (iface->addr.sa.sa_family == AF_INET)
             {
               if (!private_net(iface->addr.in.sin_addr, 1))
                 {
                   inet_ntop(AF_INET, &iface->addr.in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
                   iface->warned = advice = 1;
                   my_syslog(LOG_WARNING, 
                             _("LOUD WARNING: listening on %s may accept requests via interfaces other than %s"),
                             daemon->addrbuff, iface->name);
                 }
             }
         }
     
     if (advice)
       my_syslog(LOG_WARNING, _("LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s)")); 
   }
   
   void warn_int_names(void)
   {
     struct interface_name *intname;
    
     for (intname = daemon->int_names; intname; intname = intname->next)
       if (!intname->addr)
         my_syslog(LOG_WARNING, _("warning: no addresses found for interface %s"), intname->intr);
   }
    
 int is_dad_listeners(void)  int is_dad_listeners(void)
 {  {
   struct irec *iface;    struct irec *iface;
Line 722  void join_multicast(int dienow)       Line 1079  void join_multicast(int dienow)      
                           
             inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);              inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
                           
            if (daemon->doing_dhcp6 &&            if ((daemon->doing_dhcp6 || daemon->relay6) &&
                 setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)                  setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
               err = 1;                err = 1;
                           
Line 939  void pre_allocate_sfds(void) Line 1296  void pre_allocate_sfds(void)
       }          }  
 }  }
   
   void mark_servers(int flag)
   {
     struct server *serv;
   
     /* mark everything with argument flag */
     for (serv = daemon->servers; serv; serv = serv->next)
       if (serv->flags & flag)
         serv->flags |= SERV_MARK;
   }
   
   void cleanup_servers(void)
   {
     struct server *serv, *tmp, **up;
   
     /* unlink and free anything still marked. */
     for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) 
       {
         tmp = serv->next;
         if (serv->flags & SERV_MARK)
          {
            server_gone(serv);
            *up = serv->next;
            if (serv->domain)
              free(serv->domain);
            free(serv);
          }
         else 
          up = &serv->next;
       }
   }
   
   void add_update_server(int flags,
                          union mysockaddr *addr,
                          union mysockaddr *source_addr,
                          const char *interface,
                          const char *domain)
   {
     struct server *serv, *next = NULL;
     char *domain_str = NULL;
     
     /* See if there is a suitable candidate, and unmark */
     for (serv = daemon->servers; serv; serv = serv->next)
       if (serv->flags & SERV_MARK)
         {
           if (domain)
             {
               if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
                 continue;
             }
           else
             {
               if (serv->flags & SERV_HAS_DOMAIN)
                 continue;
             }
           
           break;
         }
   
     if (serv)
       {
         domain_str = serv->domain;
         next = serv->next;
       }
     else if ((serv = whine_malloc(sizeof (struct server))))
       {
         /* Not found, create a new one. */
         if (domain && !(domain_str = whine_malloc(strlen(domain)+1)))
           {
             free(serv);
             serv = NULL;
           }
         else
           {
             struct server *s;
             /* Add to the end of the chain, for order */
             if (!daemon->servers)
               daemon->servers = serv;
             else
               {
                 for (s = daemon->servers; s->next; s = s->next);
                 s->next = serv;
               }
             if (domain)
               strcpy(domain_str, domain);
           }
       }
     
     if (serv)
       {
         memset(serv, 0, sizeof(struct server));
         serv->flags = flags;
         serv->domain = domain_str;
         serv->next = next;
         serv->queries = serv->failed_queries = 0;
         
         if (domain)
           serv->flags |= SERV_HAS_DOMAIN;
         
         if (interface)
           strcpy(serv->interface, interface);      
         if (addr)
           serv->addr = *addr;
         if (source_addr)
           serv->source_addr = *source_addr;
       }
   }
   
 void check_servers(void)  void check_servers(void)
 {  {
   struct irec *iface;    struct irec *iface;
  struct server *new, *tmp, *ret = NULL;  struct server *serv;
   int port = 0;    int port = 0;
   
   /* interface may be new since startup */    /* interface may be new since startup */
   if (!option_bool(OPT_NOWILD))    if (!option_bool(OPT_NOWILD))
    enumerate_interfaces();    enumerate_interfaces(0);
       
  for (new = daemon->servers; new; new = tmp)  for (serv = daemon->servers; serv; serv = serv->next)
     {      {
      tmp = new->next;       if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
       
      if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND))) 
         {          {
          port = prettyprint_addr(&new->addr, daemon->namebuff);          port = prettyprint_addr(&serv->addr, daemon->namebuff);
   
           /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */            /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
          if (new->addr.sa.sa_family == AF_INET &&          if (serv->addr.sa.sa_family == AF_INET &&
              new->addr.in.sin_addr.s_addr == 0)              serv->addr.in.sin_addr.s_addr == 0)
             {              {
              free(new);              serv->flags |= SERV_MARK;
               continue;                continue;
             }              }
   
           for (iface = daemon->interfaces; iface; iface = iface->next)            for (iface = daemon->interfaces; iface; iface = iface->next)
            if (sockaddr_isequal(&new->addr, &iface->addr))            if (sockaddr_isequal(&serv->addr, &iface->addr))
               break;                break;
           if (iface)            if (iface)
             {              {
               my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);                my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
              free(new);              serv->flags |= SERV_MARK;
               continue;                continue;
             }              }
                       
           /* Do we need a socket set? */            /* Do we need a socket set? */
          if (!new->sfd &&           if (!serv->sfd && 
              !(new->sfd = allocate_sfd(&new->source_addr, new->interface)) &&              !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) &&
               errno != 0)                errno != 0)
             {              {
               my_syslog(LOG_WARNING,                 my_syslog(LOG_WARNING, 
                         _("ignoring nameserver %s - cannot make/bind socket: %s"),                          _("ignoring nameserver %s - cannot make/bind socket: %s"),
                         daemon->namebuff, strerror(errno));                          daemon->namebuff, strerror(errno));
              free(new);              serv->flags |= SERV_MARK;
               continue;                continue;
             }              }
         }          }
               
      /* reverse order - gets it right. */      if (!(serv->flags & SERV_NO_REBIND))
      new->next = ret; 
      ret = new; 
       
      if (!(new->flags & SERV_NO_REBIND)) 
         {          {
          if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))          if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
             {              {
               char *s1, *s2;                char *s1, *s2;
              if (!(new->flags & SERV_HAS_DOMAIN))              if (!(serv->flags & SERV_HAS_DOMAIN))
                 s1 = _("unqualified"), s2 = _("names");                  s1 = _("unqualified"), s2 = _("names");
              else if (strlen(new->domain) == 0)              else if (strlen(serv->domain) == 0)
                 s1 = _("default"), s2 = "";                  s1 = _("default"), s2 = "";
               else                else
                s1 = _("domain"), s2 = new->domain;                s1 = _("domain"), s2 = serv->domain;
                               
              if (new->flags & SERV_NO_ADDR)              if (serv->flags & SERV_NO_ADDR)
                 my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);                  my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
              else if (new->flags & SERV_USE_RESOLV)              else if (serv->flags & SERV_USE_RESOLV)
                 my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);                  my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
              else if (!(new->flags & SERV_LITERAL_ADDRESS))              else if (!(serv->flags & SERV_LITERAL_ADDRESS))
                 my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);                  my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
             }              }
          else if (new->interface[0] != 0)          else if (serv->interface[0] != 0)
            my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface);             my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface); 
           else            else
             my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);               my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port); 
         }          }
     }      }
  
  daemon->servers = ret;  cleanup_servers();
 }  }
   
 /* Return zero if no servers found, in that case we keep polling.  /* Return zero if no servers found, in that case we keep polling.
Line 1028  int reload_servers(char *fname) Line 1485  int reload_servers(char *fname)
 {  {
   FILE *f;    FILE *f;
   char *line;    char *line;
   struct server *old_servers = NULL;  
   struct server *new_servers = NULL;  
   struct server *serv;  
   int gotone = 0;    int gotone = 0;
   
   /* buff happens to be MAXDNAME long... */    /* buff happens to be MAXDNAME long... */
Line 1039  int reload_servers(char *fname) Line 1493  int reload_servers(char *fname)
       my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));        my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
       return 0;        return 0;
     }      }
     
  /* move old servers to free list - we can reuse the memory   mark_servers(SERV_FROM_RESOLV);
     and not risk malloc if there are the same or fewer new servers.     
     Servers which were specced on the command line go to the new list. */ 
  for (serv = daemon->servers; serv;) 
    { 
      struct server *tmp = serv->next; 
      if (serv->flags & SERV_FROM_RESOLV) 
        { 
          serv->next = old_servers; 
          old_servers = serv;  
          /* forward table rules reference servers, so have to blow them away */ 
          server_gone(serv); 
        } 
      else 
        { 
          serv->next = new_servers; 
          new_servers = serv; 
        } 
      serv = tmp; 
    } 
   
   while ((line = fgets(daemon->namebuff, MAXDNAME, f)))    while ((line = fgets(daemon->namebuff, MAXDNAME, f)))
     {      {
       union mysockaddr addr, source_addr;        union mysockaddr addr, source_addr;
Line 1119  int reload_servers(char *fname) Line 1554  int reload_servers(char *fname)
         continue;          continue;
 #endif   #endif 
   
      if (old_servers)      add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
        { 
          serv = old_servers; 
          old_servers = old_servers->next; 
        } 
      else if (!(serv = whine_malloc(sizeof (struct server)))) 
        continue; 
       
      /* this list is reverse ordered:  
         it gets reversed again in check_servers */ 
      serv->next = new_servers; 
      new_servers = serv; 
      serv->addr = addr; 
      serv->source_addr = source_addr; 
      serv->domain = NULL; 
      serv->interface[0] = 0; 
      serv->sfd = NULL; 
      serv->flags = SERV_FROM_RESOLV; 
      serv->queries = serv->failed_queries = 0; 
       gotone = 1;        gotone = 1;
     }      }
       
   /* Free any memory not used. */  
   while (old_servers)  
     {  
       struct server *tmp = old_servers->next;  
       free(old_servers);  
       old_servers = tmp;  
     }  
   
   daemon->servers = new_servers;  
   fclose(f);    fclose(f);
     cleanup_servers();
   
   return gotone;    return gotone;
 }  }
   
#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_BSD_NETWORK)
/* Use an IPv4 listener socket for ioctling *//* Called when addresses are added or deleted from an interface */
struct in_addr get_ifaddr(char *intr)void newaddress(time_t now)
 {  {
  struct listener *l;  (void)now;
  struct ifreq ifr; 
  struct sockaddr_in ret; 
       
  ret.sin_addr.s_addr = -1;  if (option_bool(OPT_CLEVERBIND) || option_bool(OPT_LOCAL_SERVICE) ||
      daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
  for (l = daemon->listeners;     enumerate_interfaces(0);
       l && (l->family != AF_INET || l->fd == -1); 
       l = l->next); 
       
  strncpy(ifr.ifr_name, intr, IF_NAMESIZE);  if (option_bool(OPT_CLEVERBIND))
  ifr.ifr_addr.sa_family = AF_INET;    create_bound_listeners(0);
       
  if (l &&  ioctl(l->fd, SIOCGIFADDR, &ifr) != -1)#ifdef HAVE_DHCP6
    memcpy(&ret, &ifr.ifr_addr, sizeof(ret));   if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
     join_multicast(0);
       
  return ret.sin_addr;  if (daemon->doing_dhcp6 || daemon->doing_ra)
     dhcp_construct_contexts(now);
   
   if (daemon->doing_dhcp6)
     lease_find_interfaces(now);
 #endif
 }  }
   
   #endif
   
   
   
   

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.2


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>