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

version 1.1.1.1, 2013/07/29 19:37:40 version 1.1.1.4, 2021/03/17 00:56:46
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley/* dnsmasq is Copyright (c) 2000-2021 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 29  int indextoname(int fd, int index, char *name) Line 29  int indextoname(int fd, int index, char *name)
   if (ioctl(fd, SIOCGIFNAME, &ifr) == -1)    if (ioctl(fd, SIOCGIFNAME, &ifr) == -1)
     return 0;      return 0;
   
  strncpy(name, ifr.ifr_name, IF_NAMESIZE);  safe_strncpy(name, ifr.ifr_name, IF_NAMESIZE);
   
   return 1;    return 1;
 }  }
Line 82  int indextoname(int fd, int index, char *name) Line 82  int indextoname(int fd, int index, char *name)
   for (i = lifc.lifc_len / sizeof(struct lifreq); i; i--, lifrp++)     for (i = lifc.lifc_len / sizeof(struct lifreq); i; i--, lifrp++) 
     {      {
       struct lifreq lifr;        struct lifreq lifr;
      strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE);      safe_strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE);
       if (ioctl(fd, SIOCGLIFINDEX, &lifr) < 0)         if (ioctl(fd, SIOCGLIFINDEX, &lifr) < 0) 
         return 0;          return 0;
               
       if (lifr.lifr_index == index) {        if (lifr.lifr_index == index) {
        strncpy(name, lifr.lifr_name, IF_NAMESIZE);        safe_strncpy(name, lifr.lifr_name, IF_NAMESIZE);
         return 1;          return 1;
       }        }
     }      }
Line 99  int indextoname(int fd, int index, char *name) Line 99  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 107  int indextoname(int fd, int index, char *name) Line 109  int indextoname(int fd, int index, char *name)
   
 #endif  #endif
   
int iface_check(int family, struct all_addr *addr, char *name, int *auth)int iface_check(int family, union 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 131  int iface_check(int family, struct all_addr *addr, cha Line 135  int iface_check(int family, struct all_addr *addr, cha
           if (tmp->addr.sa.sa_family == family)            if (tmp->addr.sa.sa_family == family)
             {              {
               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->addr4.s_addr)
                ret = tmp->used = 1;                ret = match_addr = tmp->used = 1;
#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->addr6))
                ret = tmp->used = 1;                ret = match_addr = tmp->used = 1;
#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 &&
             tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)             tmp->addr.in.sin_addr.s_addr == addr->addr4.s_addr)
       break;        break;
 #ifdef HAVE_IPV6  
     else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 &&      else if (addr && tmp->addr.sa.sa_family == AF_INET6 && family == AF_INET6 &&
             IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr.addr6))             IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr6))
       break;        break;
 #endif        
   
   if (tmp && auth)     if (tmp && auth) 
     {      {
Line 172  int iface_check(int family, struct all_addr *addr, cha Line 174  int iface_check(int family, struct all_addr *addr, cha
 }  }
   
   
/* Fix for problem that the kernel sometimes reports the loopback inerface as the/* Fix for problem that the kernel sometimes reports the loopback interface as the
    arrival interface when a packet originates locally, even when sent to address of      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      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     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. */     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)    int loopback_exception(int fd, int family, union all_addr *addr, char *name)    
 {  {
   struct ifreq ifr;    struct ifreq ifr;
   struct irec *iface;    struct irec *iface;
   
  strncpy(ifr.ifr_name, name, IF_NAMESIZE);  safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
   if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 &&    if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 &&
       ifr.ifr_flags & IFF_LOOPBACK)        ifr.ifr_flags & IFF_LOOPBACK)
     {      {
Line 191  int loopback_exception(int fd, int family, struct all_ Line 193  int loopback_exception(int fd, int family, struct all_
           {            {
             if (family == AF_INET)              if (family == AF_INET)
               {                {
                if (iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)                if (iface->addr.in.sin_addr.s_addr == addr->addr4.s_addr)
                   return 1;                    return 1;
               }                }
#ifdef HAVE_IPV6            else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr6))
            else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr.addr6)) 
               return 1;                return 1;
 #endif  
               
           }            }
     }      }
   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, union 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->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 iface_flags) 
 {
   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  int is_label = 0;
 #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;
   else
     is_label = strcmp(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.addr4 = addr->in.sin_addr;
               al->flags = 0;
             }
           else
             {
               al->addr.addr6 = addr->in6.sin6_addr;
               al->flags = ADDRLIST_IPV6;
             } 
         }
     }
       
  /* If we are restricting the set of interfaces to use, make  if (addr->sa.sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
     {
       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.addr4 = addr->in.sin_addr;
                       al->flags = 0;
                     }
                 }
               
               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.addr6 = addr->in6.sin6_addr;
                       al->flags = ADDRLIST_IPV6;
                     }
                 } 
             }
 #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.addr4 = addr->in.sin_addr;
                     al->flags = 0;
                   }
                 else
                  {
                     al->addr.addr6 = addr->in6.sin6_addr;
                     al->flags = ADDRLIST_IPV6;
                     /* Privacy addresses and addresses still undergoing DAD and deprecated addresses
                        don't appear in forward queries, but will in reverse ones. */
                     if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
                       al->flags |= ADDRLIST_REVONLY;
                  } 
               }
           }
     }
  
   /* 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->index == if_index)
       {
         iface->dad = !!(iface_flags & IFACE_TENTATIVE);
         iface->found = 1; /* for garbage collection */
         iface->netmask = netmask;
         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 420  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, (union all_addr *)&addr->in.sin_addr, label, &auth_dns))
     return 1;      return 1;
   
 #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, (union all_addr *)&addr->in6.sin6_addr, label, &auth_dns))
     return 1;      return 1;
 #endif  
           
 #ifdef HAVE_DHCP  #ifdef HAVE_DHCP
   /* No DHCP where we're doing auth DNS. */    /* No DHCP where we're doing auth DNS. */
Line 298  static int iface_allowed(struct irec **irecp, int if_i Line 443  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 307  static int iface_allowed(struct irec **irecp, int if_i Line 464  static int iface_allowed(struct irec **irecp, int if_i
       iface->dhcp_ok = dhcp_ok;        iface->dhcp_ok = dhcp_ok;
       iface->dns_auth = auth_dns;        iface->dns_auth = auth_dns;
       iface->mtu = mtu;        iface->mtu = mtu;
      iface->dad = dad;      iface->dad = !!(iface_flags & IFACE_TENTATIVE);
      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;
         iface->label = is_label;
       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 325  static int iface_allowed(struct irec **irecp, int if_i Line 484  static int iface_allowed(struct irec **irecp, int if_i
   return 0;    return 0;
 }  }
   
 #ifdef HAVE_IPV6  
 static int iface_allowed_v6(struct in6_addr *local, int prefix,   static int iface_allowed_v6(struct in6_addr *local, int prefix, 
                             int scope, int if_index, int flags,                               int scope, int if_index, int flags, 
                             int preferred, int valid, void *vparam)                              int preferred, int valid, void *vparam)
Line 334  static int iface_allowed_v6(struct in6_addr *local, in Line 492  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 503  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);
 }  }
 #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;
    
     (void)broadcast; /* warning */
   
   memset(&addr, 0, sizeof(addr));    memset(&addr, 0, sizeof(addr));
 #ifdef HAVE_SOCKADDR_SA_LEN  #ifdef HAVE_SOCKADDR_SA_LEN
   addr.in.sin_len = sizeof(addr.in);    addr.in.sin_len = sizeof(addr.in);
 #endif  #endif
   addr.in.sin_family = AF_INET;    addr.in.sin_family = AF_INET;
   addr.in.sin_addr = broadcast; /* warning */  
   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)/*
  * Clean old interfaces no longer found.
  */
 static void clean_interfaces()
 {  {
#ifdef HAVE_IPV6  struct irec *iface;
  if (!iface_enumerate(AF_INET6, &daemon->interfaces, iface_allowed_v6))  struct irec **up = &daemon->interfaces;
    return 0; 
   for (iface = *up; iface; iface = *up)
   {
     if (!iface->found && !iface->done)
       {
         *up = iface->next;
         free(iface->name);
         free(iface);
       }
     else
       {
         up = &iface->next;
       }
   }
 }
 
 /** Release listener if no other interface needs it.
  *
  * @return 1 if released, 0 if still required
  */
 static int release_listener(struct listener *l)
 {
   if (l->used > 1)
     {
       struct irec *iface;
       for (iface = daemon->interfaces; iface; iface = iface->next)
         if (iface->done && sockaddr_isequal(&l->addr, &iface->addr))
           {
             if (iface->found)
               {
                 /* update listener to point to active interface instead */
                 if (!l->iface->found)
                   l->iface = iface;
               }
             else
               {
                 l->used--;
                 iface->done = 0;
               }
           }
 
       /* Someone is still using this listener, skip its deletion */
       if (l->used > 0)
         return 0;
     }
 
   if (l->iface->done)
     {
       int port;
 
       port = prettyprint_addr(&l->iface->addr, daemon->addrbuff);
       my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s port %d"),
                 l->iface->name, l->iface->index, daemon->addrbuff, port);
       /* 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);
   return 1;
 }
 
 int enumerate_interfaces(int reset)
 {
   static struct addrlist *spare = NULL;
   static int done = 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  #endif
   
  return iface_enumerate(AF_INET, &daemon->interfaces, iface_allowed_v4);   /* Do this max once per select cycle  - also inhibits netlink socket use
    in TCP child processes. */
 
   if (reset)
     {
       done = 0;
       return 1;
     }
 
   if (done)
     return 1;
 
   done = 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;
   
   ret = iface_enumerate(AF_INET6, &param, iface_allowed_v6);
 
   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 reentrancy, 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;
       int freed = 0;
       
       for (up = &daemon->listeners, l = daemon->listeners; l; l = tmp)
         {
           tmp = l->next;
           
           if (!l->iface || l->iface->found)
             up = &l->next;
           else if (release_listener(l))
             {
               *up = tmp;
               freed = 1;
             }
         }
 
       if (freed)
         clean_interfaces();
     }
 
   errno = errsave;
   spare = param.spare;
     
   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 750  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, errsave;
       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 760  static int make_sock(union mysockaddr *addr, int type,
         return -1;          return -1;
               
     err:      err:
         errsave = 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 768  static int make_sock(union mysockaddr *addr, int type,
               
       if (fd != -1)        if (fd != -1)
         close (fd);          close (fd);
              
       errno = errsave;
 
       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 432  static int make_sock(union mysockaddr *addr, int type, Line 787  static int make_sock(union mysockaddr *addr, int type,
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd))    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd))
     goto err;      goto err;
       
 #ifdef HAVE_IPV6  
   if (family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)    if (family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
     goto err;      goto err;
 #endif  
       
   if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) == -1)    if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) == -1)
     goto err;      goto err;
       
   if (type == SOCK_STREAM)    if (type == SOCK_STREAM)
     {      {
      if (listen(fd, 5) == -1)#ifdef TCP_FASTOPEN
       int qlen = 5;                           
       setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen));
 #endif
       
       if (listen(fd, TCP_BACKLOG) == -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 816  static int make_sock(union mysockaddr *addr, int type,
             goto err;              goto err;
 #endif  #endif
         }          }
 #ifdef HAVE_IPV6  
       else if (!set_ipv6pktinfo(fd))  
         goto err;  
 #endif  
     }      }
     else if (!set_ipv6pktinfo(fd))
       goto err;
       
   return fd;    return fd;
 }  }
   
 #ifdef HAVE_IPV6    
 int set_ipv6pktinfo(int fd)  int set_ipv6pktinfo(int fd)
 {  {
   int opt = 1;    int opt = 1;
Line 494  int set_ipv6pktinfo(int fd) Line 849  int set_ipv6pktinfo(int fd)
   
   return 0;    return 0;
 }  }
 #endif  
   
   
 /* Find the interface on which a TCP connection arrived, if possible, or zero otherwise. */  /* Find the interface on which a TCP connection arrived, if possible, or zero otherwise. */
 int tcp_interface(int fd, int af)  int tcp_interface(int fd, int af)
 {   { 
     (void)fd; /* suppress potential unused warning */
     (void)af; /* suppress potential unused warning */
   int if_index = 0;    int if_index = 0;
   
 #ifdef HAVE_LINUX_NETWORK  #ifdef HAVE_LINUX_NETWORK
   int opt = 1;    int opt = 1;
   struct cmsghdr *cmptr;    struct cmsghdr *cmptr;
   struct msghdr msg;    struct msghdr msg;
     socklen_t len;
       
  /* use mshdr do that the CMSDG_* macros are available */  /* use mshdr so that the CMSDG_* macros are available */
   msg.msg_control = daemon->packet;    msg.msg_control = daemon->packet;
  msg.msg_controllen = daemon->packet_buff_sz;  msg.msg_controllen = len = daemon->packet_buff_sz;
       
   /* we overwrote the buffer... */    /* we overwrote the buffer... */
   daemon->srv_save = NULL;    daemon->srv_save = NULL;
Line 517  int tcp_interface(int fd, int af) Line 874  int tcp_interface(int fd, int af)
   if (af == AF_INET)    if (af == AF_INET)
     {      {
       if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&        if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 &&
          getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)          getsockopt(fd, IPPROTO_IP, IP_PKTOPTIONS, msg.msg_control, &len) != -1)
        for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))        {
          if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)          msg.msg_controllen = len;
            {          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
              union {            if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
                unsigned char *c;              {
                struct in_pktinfo *p;                union {
              } p;                  unsigned char *c;
                                struct in_pktinfo *p;
              p.c = CMSG_DATA(cmptr);                } p;
              if_index = p.p->ipi_ifindex;                
            }                p.c = CMSG_DATA(cmptr);
                 if_index = p.p->ipi_ifindex;
               }
         }
     }      }
 #ifdef HAVE_IPV6  
   else    else
     {      {
       /* Only the RFC-2292 API has the ability to find the interface for TCP connections,        /* Only the RFC-2292 API has the ability to find the interface for TCP connections,
Line 546  int tcp_interface(int fd, int af) Line 905  int tcp_interface(int fd, int af)
 #endif  #endif
   
       if (set_ipv6pktinfo(fd) &&        if (set_ipv6pktinfo(fd) &&
          getsockopt(fd, IPPROTO_IPV6, PKTOPTIONS, msg.msg_control, (socklen_t *)&msg.msg_controllen) != -1)          getsockopt(fd, IPPROTO_IPV6, PKTOPTIONS, msg.msg_control, &len) != -1)
         {          {
          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))          msg.msg_controllen = len;
           for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
             if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)              if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
               {                {
                 union {                  union {
Line 561  int tcp_interface(int fd, int af) Line 921  int tcp_interface(int fd, int af)
               }                }
         }          }
     }      }
 #endif /* IPV6 */  
 #endif /* Linux */  #endif /* Linux */
     
   return if_index;    return if_index;
Line 572  static struct listener *create_listeners(union mysocka Line 931  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 589  static struct listener *create_listeners(union mysocka Line 950  static struct listener *create_listeners(union mysocka
           tftpfd = make_sock(addr, SOCK_DGRAM, dienow);            tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
           addr->in.sin_port = save;            addr->in.sin_port = save;
         }          }
 #  ifdef HAVE_IPV6  
       else        else
         {          {
           short save = addr->in6.sin6_port;            short save = addr->in6.sin6_port;
Line 597  static struct listener *create_listeners(union mysocka Line 957  static struct listener *create_listeners(union mysocka
           tftpfd = make_sock(addr, SOCK_DGRAM, dienow);            tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
           addr->in6.sin6_port = save;            addr->in6.sin6_port = save;
         }            }  
 #  endif  
     }      }
 #endif  #endif
   
Line 605  static struct listener *create_listeners(union mysocka Line 964  static struct listener *create_listeners(union mysocka
     {      {
       l = safe_malloc(sizeof(struct listener));        l = safe_malloc(sizeof(struct listener));
       l->next = NULL;        l->next = NULL;
       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->addr = *addr;
         l->used = 1;
         l->iface = NULL;
     }      }
   
   return l;    return l;
Line 629  void create_wildcard_listeners(void) Line 990  void create_wildcard_listeners(void)
   
   l = create_listeners(&addr, !!option_bool(OPT_TFTP), 1);    l = create_listeners(&addr, !!option_bool(OPT_TFTP), 1);
   
 #ifdef HAVE_IPV6  
   memset(&addr, 0, sizeof(addr));    memset(&addr, 0, sizeof(addr));
#  ifdef HAVE_SOCKADDR_SA_LEN#ifdef HAVE_SOCKADDR_SA_LEN
   addr.in6.sin6_len = sizeof(addr.in6);    addr.in6.sin6_len = sizeof(addr.in6);
#  endif#endif
   addr.in6.sin6_family = AF_INET6;    addr.in6.sin6_family = AF_INET6;
   addr.in6.sin6_addr = in6addr_any;    addr.in6.sin6_addr = in6addr_any;
   addr.in6.sin6_port = htons(daemon->port);    addr.in6.sin6_port = htons(daemon->port);
Line 643  void create_wildcard_listeners(void) Line 1003  void create_wildcard_listeners(void)
     l->next = l6;      l->next = l6;
   else     else 
     l = l6;      l = l6;
 #endif  
   
   daemon->listeners = l;    daemon->listeners = l;
 }  }
   
   static struct listener *find_listener(union mysockaddr *addr)
   {
     struct listener *l;
     for (l = daemon->listeners; l; l = l->next)
       if (sockaddr_isequal(&l->addr, addr))
         return l;
     return NULL;
   }
   
 void create_bound_listeners(int dienow)  void create_bound_listeners(int dienow)
 {  {
   struct listener *new;    struct listener *new;
   struct irec *iface;    struct irec *iface;
   struct iname *if_tmp;    struct iname *if_tmp;
     struct listener *existing;
   
   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->iface = iface;        existing = find_listener(&iface->addr);
        new->next = daemon->listeners;        if (existing)
        daemon->listeners = new;          {
        iface->done = 1;            iface->done = 1;
             existing->used++; /* increase usage counter */
           }
         else if ((new = create_listeners(&iface->addr, iface->tftp_ok, dienow)))
           {
             new->iface = iface;
             new->next = daemon->listeners;
             daemon->listeners = new;
             iface->done = 1;
 
             /* Don't log the initial set of listen addresses created
                at startup, since this is happening before the logging
                system is initialised and the sign-on printed. */
             if (!dienow)
               {
                 int port = prettyprint_addr(&iface->addr, daemon->addrbuff);
                 my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s port %d"),
                           iface->name, iface->index, daemon->addrbuff, port);
               }
           }
       }        }
   
   /* Check for --listen-address options that haven't been used because there's    /* Check for --listen-address options that haven't been used because there's
Line 679  void create_bound_listeners(int dienow) Line 1066  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;
   
           if (!dienow)
             {
               int port = prettyprint_addr(&if_tmp->addr, daemon->addrbuff);
               my_syslog(LOG_DEBUG, _("listening on %s port %d"), daemon->addrbuff, port);
             }
       }        }
 }  }
   
   /* 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_wild_labels(void)
   {
     struct irec *iface;
   
     for (iface = daemon->interfaces; iface; iface = iface->next)
       if (iface->found && iface->name && iface->label)
         my_syslog(LOG_WARNING, _("warning: using interface %s instead"), iface->name);
   }
   
   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 1170  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 = errno;
                           
             inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);              inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
                           
             if (daemon->doing_dhcp6 &&               if (daemon->doing_dhcp6 && 
                 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 = errno;
                           
             inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);              inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
                           
             if (daemon->doing_ra &&              if (daemon->doing_ra &&
                 setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)                  setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
              err = 1;              err = errno;
                           
             if (err)              if (err)
               {                {
                 char *s = _("interface %s failed to join DHCPv6 multicast group: %s");                  char *s = _("interface %s failed to join DHCPv6 multicast group: %s");
                   errno = err;
   
   #ifdef HAVE_LINUX_NETWORK
                   if (errno == ENOMEM)
                     my_syslog(LOG_ERR, _("try increasing /proc/sys/net/core/optmem_max"));
   #endif
   
                 if (dienow)                  if (dienow)
                   die(s, iface->name, EC_BADNET);                    die(s, iface->name, EC_BADNET);
                 else                  else
Line 760  int random_sock(int family) Line 1215  int random_sock(int family)
   if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)    if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)
     {      {
       union mysockaddr addr;        union mysockaddr addr;
      unsigned int ports_avail = 65536u - (unsigned short)daemon->min_port;      unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
       int tries = ports_avail < 30 ? 3 * ports_avail : 100;        int tries = ports_avail < 30 ? 3 * ports_avail : 100;
   
       memset(&addr, 0, sizeof(addr));        memset(&addr, 0, sizeof(addr));
Line 771  int random_sock(int family) Line 1226  int random_sock(int family)
       if (fix_fd(fd))        if (fix_fd(fd))
         while(tries--)          while(tries--)
           {            {
            unsigned short port = rand16();            unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
                           
             if (daemon->min_port != 0)  
               port = htons(daemon->min_port + (port % ((unsigned short)ports_avail)));  
               
             if (family == AF_INET)               if (family == AF_INET) 
               {                {
                 addr.in.sin_addr.s_addr = INADDR_ANY;                  addr.in.sin_addr.s_addr = INADDR_ANY;
Line 784  int random_sock(int family) Line 1236  int random_sock(int family)
                 addr.in.sin_len = sizeof(struct sockaddr_in);                  addr.in.sin_len = sizeof(struct sockaddr_in);
 #endif  #endif
               }                }
 #ifdef HAVE_IPV6  
             else              else
               {                {
                 addr.in6.sin6_addr = in6addr_any;                   addr.in6.sin6_addr = in6addr_any; 
Line 793  int random_sock(int family) Line 1244  int random_sock(int family)
                 addr.in6.sin6_len = sizeof(struct sockaddr_in6);                  addr.in6.sin6_len = sizeof(struct sockaddr_in6);
 #endif  #endif
               }                }
 #endif  
                           
             if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0)              if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0)
               return fd;                return fd;
Line 809  int random_sock(int family) Line 1259  int random_sock(int family)
 }  }
       
   
int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp)
 {  {
   union mysockaddr addr_copy = *addr;    union mysockaddr addr_copy = *addr;
     unsigned short port;
     int tries = 1, done = 0;
     unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
    
     if (addr_copy.sa.sa_family == AF_INET)
       port = addr_copy.in.sin_port;
     else
       port = addr_copy.in6.sin6_port;
   
   /* cannot set source _port_ for TCP connections. */    /* cannot set source _port_ for TCP connections. */
   if (is_tcp)    if (is_tcp)
       port = 0;
   
     /* Bind a random port within the range given by min-port and max-port */
     if (port == 0)
     {      {
         tries = ports_avail < 30 ? 3 * ports_avail : 100;
         port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
       }
     
     while (tries--)
       {
       if (addr_copy.sa.sa_family == AF_INET)        if (addr_copy.sa.sa_family == AF_INET)
        addr_copy.in.sin_port = 0;        addr_copy.in.sin_port = port;
#ifdef HAVE_IPV6 
       else        else
        addr_copy.in6.sin6_port = 0;        addr_copy.in6.sin6_port = port;
#endif
       if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1)
         {
           done = 1;
           break;
         }
       
       if (errno != EADDRINUSE && errno != EACCES)
         return 0;
       
       port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
     }      }
  
  if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) == -1)  if (!done)
     return 0;      return 0;
    
   if (!is_tcp && ifindex > 0)
     {
 #if defined(IP_UNICAST_IF)
       if (addr_copy.sa.sa_family == AF_INET)
         {
           uint32_t ifindex_opt = htonl(ifindex);
           return setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0;
         }
 #endif
 #if defined (IPV6_UNICAST_IF)
       if (addr_copy.sa.sa_family == AF_INET6)
         {
           uint32_t ifindex_opt = htonl(ifindex);
           return setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0;
         }
 #endif
     }
 
   (void)intname; /* suppress potential unused warning */
 #if defined(SO_BINDTODEVICE)  #if defined(SO_BINDTODEVICE)
   if (intname[0] != 0 &&    if (intname[0] != 0 &&
       setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE) == -1)        setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE) == -1)
Line 839  int local_bind(int fd, union mysockaddr *addr, char *i Line 1335  int local_bind(int fd, union mysockaddr *addr, char *i
 static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)  static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
 {  {
   struct serverfd *sfd;    struct serverfd *sfd;
     unsigned int ifindex = 0;
   int errsave;    int errsave;
  int opt = 1;
   
   /* when using random ports, servers which would otherwise use    /* when using random ports, servers which would otherwise use
      the INADDR_ANY/port0 socket have sfd set to NULL */       the INADDR_ANY/port0 socket have sfd set to NULL */
   if (!daemon->osport && intname[0] == 0)    if (!daemon->osport && intname[0] == 0)
Line 852  static struct serverfd *allocate_sfd(union mysockaddr  Line 1350  static struct serverfd *allocate_sfd(union mysockaddr 
           addr->in.sin_port == htons(0)            addr->in.sin_port == htons(0)
         return NULL;          return NULL;
   
 #ifdef HAVE_IPV6  
       if (addr->sa.sa_family == AF_INET6 &&        if (addr->sa.sa_family == AF_INET6 &&
           memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&            memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
           addr->in6.sin6_port == htons(0)            addr->in6.sin6_port == htons(0)
         return NULL;          return NULL;
 #endif  
     }      }
   
     if (intname && strlen(intname) != 0)
       ifindex = if_nametoindex(intname); /* index == 0 when not binding to an interface */
               
   /* may have a suitable one already */    /* may have a suitable one already */
   for (sfd = daemon->sfds; sfd; sfd = sfd->next )    for (sfd = daemon->sfds; sfd; sfd = sfd->next )
     if (sockaddr_isequal(&sfd->source_addr, addr) &&      if (sockaddr_isequal(&sfd->source_addr, addr) &&
        strcmp(intname, sfd->interface) == 0)        strcmp(intname, sfd->interface) == 0 &&
         ifindex == sfd->ifindex) 
       return sfd;        return sfd;
       
   /* need to make a new one. */    /* need to make a new one. */
Line 876  static struct serverfd *allocate_sfd(union mysockaddr  Line 1376  static struct serverfd *allocate_sfd(union mysockaddr 
       free(sfd);        free(sfd);
       return NULL;        return NULL;
     }      }
  
  if (!local_bind(sfd->fd, addr, intname, 0) || !fix_fd(sfd->fd))  if ((addr->sa.sa_family == AF_INET6 && setsockopt(sfd->fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1) ||
       !local_bind(sfd->fd, addr, intname, ifindex, 0) || !fix_fd(sfd->fd))
     {       { 
      errsave = errno; /* save error from bind. */      errsave = errno; /* save error from bind/setsockopt. */
       close(sfd->fd);        close(sfd->fd);
       free(sfd);        free(sfd);
       errno = errsave;        errno = errsave;
       return NULL;        return NULL;
     }      }
    
  strcpy(sfd->interface, intname);   safe_strncpy(sfd->interface, intname, sizeof(sfd->interface)); 
   sfd->source_addr = *addr;    sfd->source_addr = *addr;
   sfd->next = daemon->sfds;    sfd->next = daemon->sfds;
     sfd->ifindex = ifindex;
     sfd->preallocated = 0;
   daemon->sfds = sfd;    daemon->sfds = sfd;
   
   return sfd;     return sfd; 
 }  }
   
Line 898  static struct serverfd *allocate_sfd(union mysockaddr  Line 1402  static struct serverfd *allocate_sfd(union mysockaddr 
 void pre_allocate_sfds(void)  void pre_allocate_sfds(void)
 {  {
   struct server *srv;    struct server *srv;
     struct serverfd *sfd;
       
   if (daemon->query_port != 0)    if (daemon->query_port != 0)
     {      {
Line 909  void pre_allocate_sfds(void) Line 1414  void pre_allocate_sfds(void)
 #ifdef HAVE_SOCKADDR_SA_LEN  #ifdef HAVE_SOCKADDR_SA_LEN
       addr.in.sin_len = sizeof(struct sockaddr_in);        addr.in.sin_len = sizeof(struct sockaddr_in);
 #endif  #endif
      allocate_sfd(&addr, "");      if ((sfd = allocate_sfd(&addr, "")))
#ifdef HAVE_IPV6        sfd->preallocated = 1;
 
       memset(&addr, 0, sizeof(addr));        memset(&addr, 0, sizeof(addr));
       addr.in6.sin6_family = AF_INET6;        addr.in6.sin6_family = AF_INET6;
       addr.in6.sin6_addr = in6addr_any;        addr.in6.sin6_addr = in6addr_any;
Line 918  void pre_allocate_sfds(void) Line 1424  void pre_allocate_sfds(void)
 #ifdef HAVE_SOCKADDR_SA_LEN  #ifdef HAVE_SOCKADDR_SA_LEN
       addr.in6.sin6_len = sizeof(struct sockaddr_in6);        addr.in6.sin6_len = sizeof(struct sockaddr_in6);
 #endif  #endif
      allocate_sfd(&addr, "");      if ((sfd = allocate_sfd(&addr, "")))
#endif        sfd->preallocated = 1;
     }      }
       
   for (srv = daemon->servers; srv; srv = srv->next)    for (srv = daemon->servers; srv; srv = srv->next)
Line 928  void pre_allocate_sfds(void) Line 1434  void pre_allocate_sfds(void)
         errno != 0 &&          errno != 0 &&
         option_bool(OPT_NOWILD))          option_bool(OPT_NOWILD))
       {        {
        prettyprint_addr(&srv->source_addr, daemon->namebuff);        (void)prettyprint_addr(&srv->source_addr, daemon->namebuff);
         if (srv->interface[0] != 0)          if (srv->interface[0] != 0)
           {            {
             strcat(daemon->namebuff, " ");              strcat(daemon->namebuff, " ");
Line 939  void pre_allocate_sfds(void) Line 1445  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;
   #ifdef HAVE_LOOP
         /* Give looped servers another chance */
         serv->flags &= ~SERV_LOOP;
   #endif
       }
   }
   
   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;
       }
   
   #ifdef HAVE_LOOP
     /* Now we have a new set of servers, test for loops. */
     loop_send_probes();
   #endif
   }
   
   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;
   #ifdef HAVE_LOOP
         serv->uid = rand32();
   #endif      
   
         if (domain)
           serv->flags |= SERV_HAS_DOMAIN;
         
         if (interface)
           safe_strncpy(serv->interface, interface, sizeof(serv->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;  struct serverfd *sfd, *tmp, **up;
   int port = 0, count;
   int locals = 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)  /* don't garbage collect pre-allocated sfds. */
   for (sfd = daemon->sfds; sfd; sfd = sfd->next)
     sfd->used = sfd->preallocated;
 
   for (count = 0, 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);          /* Init edns_pktsz for newly created server records. */
           if (serv->edns_pktsz == 0)
             serv->edns_pktsz = daemon->edns_pktsz;
           
 #ifdef HAVE_DNSSEC
           if (option_bool(OPT_DNSSEC_VALID))
             { 
               if (!(serv->flags & SERV_FOR_NODOTS))
                 serv->flags |= SERV_DO_DNSSEC;
               
               /* Disable DNSSEC validation when using server=/domain/.... servers
                  unless there's a configured trust anchor. */
               if (serv->flags & SERV_HAS_DOMAIN)
                 {
                   struct ds_config *ds;
                   char *domain = serv->domain;
                   
                   /* .example.com is valid */
                   while (*domain == '.')
                     domain++;
                   
                   for (ds = daemon->ds; ds; ds = ds->next)
                     if (ds->name[0] != 0 && hostname_isequal(domain, ds->name))
                       break;
                   
                   if (!ds)
                     serv->flags &= ~SERV_DO_DNSSEC;
                 }
             }
 #endif
   
             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;
             }              }
             
             if (serv->sfd)
               serv->sfd->used = 1;
         }          }
               
      /* reverse order - gets it right. */      if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS))
      new->next = ret; 
      ret = new; 
       
      if (!(new->flags & SERV_NO_REBIND)) 
         {          {
          if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))          if (++count > SERVERS_LOGGED)
             continue;
           
           if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
             {              {
              char *s1, *s2;              char *s1, *s2, *s3 = "";
              if (!(new->flags & SERV_HAS_DOMAIN))#ifdef HAVE_DNSSEC
               if (option_bool(OPT_DNSSEC_VALID) && !(serv->flags & SERV_DO_DNSSEC))
                 s3 = _("(no DNSSEC)");
 #endif
               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);                {
              else if (new->flags & SERV_USE_RESOLV)                  count--;
                   if (++locals <= LOCALS_LOGGED)
                         my_syslog(LOG_INFO, _("using only locally-known addresses for %s %s"), s1, s2);
                 }
               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 
                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 %s"), daemon->namebuff, port, s1, s2, s3);
             }              }
          else if (new->interface[0] != 0)#ifdef HAVE_LOOP
            my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface);           else if (serv->flags & SERV_LOOP)
             my_syslog(LOG_INFO, _("NOT using nameserver %s#%d - query loop detected"), daemon->namebuff, port); 
 #endif
           else if (serv->interface[0] != 0)
             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;  if (locals > LOCALS_LOGGED)
     my_syslog(LOG_INFO, _("using %d more local addresses"), locals - LOCALS_LOGGED);
   if (count - 1 > SERVERS_LOGGED)
     my_syslog(LOG_INFO, _("using %d more nameservers"), count - SERVERS_LOGGED - 1);
 
   /* Remove unused sfds */
   for (sfd = daemon->sfds, up = &daemon->sfds; sfd; sfd = tmp)
     {
        tmp = sfd->next;
        if (!sfd->used) 
         {
           *up = sfd->next;
           close(sfd->fd);
           free(sfd);
         } 
       else
         up = &sfd->next;
     }
   
   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 1722  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 1730  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 1086  int reload_servers(char *fname) Line 1758  int reload_servers(char *fname)
           source_addr.in.sin_addr.s_addr = INADDR_ANY;            source_addr.in.sin_addr.s_addr = INADDR_ANY;
           source_addr.in.sin_port = htons(daemon->query_port);            source_addr.in.sin_port = htons(daemon->query_port);
         }          }
 #ifdef HAVE_IPV6  
       else         else 
         {                 {       
           int scope_index = 0;            int scope_index = 0;
Line 1114  int reload_servers(char *fname) Line 1785  int reload_servers(char *fname)
           else            else
             continue;              continue;
         }          }
 #else /* IPV6 */  
       else  
         continue;  
 #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;
 }  }
   
/* Called when addresses are added or deleted from an interface */
/* Use an IPv4 listener socket for ioctling */void newaddress(time_t now)
struct in_addr get_ifaddr(char *intr) 
 {  {
  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
 }  }
   
   
   
   
   

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


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