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

version 1.1.1.3, 2016/11/02 09:57:01 version 1.1.1.4, 2021/03/17 00:56:46
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2016 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 48  int main (int argc, char **argv) Line 48  int main (int argc, char **argv)
   long i, max_fd = sysconf(_SC_OPEN_MAX);    long i, max_fd = sysconf(_SC_OPEN_MAX);
   char *baduser = NULL;    char *baduser = NULL;
   int log_err;    int log_err;
     int chown_warn = 0;
 #if defined(HAVE_LINUX_NETWORK)  #if defined(HAVE_LINUX_NETWORK)
   cap_user_header_t hdr = NULL;    cap_user_header_t hdr = NULL;
   cap_user_data_t data = NULL;    cap_user_data_t data = NULL;
     int need_cap_net_admin = 0;
     int need_cap_net_raw = 0;
     int need_cap_net_bind_service = 0;
   char *bound_device = NULL;    char *bound_device = NULL;
   int did_bind = 0;    int did_bind = 0;
     struct server *serv;
     char *netlink_warn;
 #endif   #endif 
 #if defined(HAVE_DHCP) || defined(HAVE_DHCP6)  #if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
   struct dhcp_context *context;    struct dhcp_context *context;
Line 77  int main (int argc, char **argv) Line 83  int main (int argc, char **argv)
   sigaction(SIGTERM, &sigact, NULL);    sigaction(SIGTERM, &sigact, NULL);
   sigaction(SIGALRM, &sigact, NULL);    sigaction(SIGALRM, &sigact, NULL);
   sigaction(SIGCHLD, &sigact, NULL);    sigaction(SIGCHLD, &sigact, NULL);
  sigaction(SIGINT, &sigact, NULL);
   
   /* ignore SIGPIPE */    /* ignore SIGPIPE */
   sigact.sa_handler = SIG_IGN;    sigact.sa_handler = SIG_IGN;
   sigaction(SIGPIPE, &sigact, NULL);    sigaction(SIGPIPE, &sigact, NULL);
   
   umask(022); /* known umask, create leases and pid files as 0644 */    umask(022); /* known umask, create leases and pid files as 0644 */
 
   rand_init(); /* Must precede read_opts() */    rand_init(); /* Must precede read_opts() */
       
   read_opts(argc, argv, compile_opts);    read_opts(argc, argv, compile_opts);
     
   #ifdef HAVE_LINUX_NETWORK
     daemon->kernel_version = kernel_version();
   #endif
   
   if (daemon->edns_pktsz < PACKETSZ)    if (daemon->edns_pktsz < PACKETSZ)
     daemon->edns_pktsz = PACKETSZ;      daemon->edns_pktsz = PACKETSZ;
   
Line 118  int main (int argc, char **argv) Line 129  int main (int argc, char **argv)
       daemon->namebuff = safe_malloc(MAXDNAME * 2);        daemon->namebuff = safe_malloc(MAXDNAME * 2);
       daemon->keyname = safe_malloc(MAXDNAME * 2);        daemon->keyname = safe_malloc(MAXDNAME * 2);
       daemon->workspacename = safe_malloc(MAXDNAME * 2);        daemon->workspacename = safe_malloc(MAXDNAME * 2);
         /* one char flag per possible RR in answer section (may get extended). */
         daemon->rr_status_sz = 64;
         daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
     }      }
 #endif  #endif
   
Line 129  int main (int argc, char **argv) Line 143  int main (int argc, char **argv)
     }      }
 #endif  #endif
       
  /* Close any file descriptors we inherited apart from std{in|out|err}   /* Ensure that at least stdin, stdout and stderr (fd 0, 1, 2) exist,
      
     Ensure that at least stdin, stdout and stderr (fd 0, 1, 2) exist, 
      otherwise file descriptors we create can end up being 0, 1, or 2        otherwise file descriptors we create can end up being 0, 1, or 2 
      and then get accidentally closed later when we make 0, 1, and 2        and then get accidentally closed later when we make 0, 1, and 2 
      open to /dev/null. Normally we'll be started with 0, 1 and 2 open,        open to /dev/null. Normally we'll be started with 0, 1 and 2 open, 
      but it's not guaranteed. By opening /dev/null three times, we        but it's not guaranteed. By opening /dev/null three times, we 
      ensure that we're not using those fds for real stuff. */       ensure that we're not using those fds for real stuff. */
  for (i = 0; i < max_fd; i++)  for (i = 0; i < 3; i++)
    if (i != STDOUT_FILENO && i != STDERR_FILENO && i != STDIN_FILENO)    open("/dev/null", O_RDWR); 
      close(i);  
    else  /* Close any file descriptors we inherited apart from std{in|out|err} */
      open("/dev/null", O_RDWR);   close_fds(max_fd, -1, -1, -1);
  
 #ifndef HAVE_LINUX_NETWORK  #ifndef HAVE_LINUX_NETWORK
 #  if !(defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))  #  if !(defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))
   if (!option_bool(OPT_NOWILD))    if (!option_bool(OPT_NOWILD))
Line 202  int main (int argc, char **argv) Line 214  int main (int argc, char **argv)
   
 #ifdef HAVE_SOLARIS_NETWORK  #ifdef HAVE_SOLARIS_NETWORK
   if (daemon->max_logs != 0)    if (daemon->max_logs != 0)
    die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF);    die(_("asynchronous logging is not available under Solaris"), NULL, EC_BADCONF);
 #endif  #endif
       
 #ifdef __ANDROID__  #ifdef __ANDROID__
   if (daemon->max_logs != 0)    if (daemon->max_logs != 0)
    die(_("asychronous logging is not available under Android"), NULL, EC_BADCONF);    die(_("asynchronous logging is not available under Android"), NULL, EC_BADCONF);
 #endif  #endif
   
 #ifndef HAVE_AUTH  #ifndef HAVE_AUTH
  if (daemon->authserver)  if (daemon->auth_zones)
     die(_("authoritative DNS not available: set HAVE_AUTH in src/config.h"), NULL, EC_BADCONF);      die(_("authoritative DNS not available: set HAVE_AUTH in src/config.h"), NULL, EC_BADCONF);
 #endif  #endif
   
Line 220  int main (int argc, char **argv) Line 232  int main (int argc, char **argv)
     die(_("loop detection not available: set HAVE_LOOP in src/config.h"), NULL, EC_BADCONF);      die(_("loop detection not available: set HAVE_LOOP in src/config.h"), NULL, EC_BADCONF);
 #endif  #endif
   
  if (daemon->max_port != MAX_PORT && daemon->min_port == 0)#ifndef HAVE_UBUS
    daemon->min_port = 1024u;  if (option_bool(OPT_UBUS))
    die(_("Ubus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
 #endif
   
   if (daemon->max_port < daemon->min_port)    if (daemon->max_port < daemon->min_port)
     die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);      die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);
   
   now = dnsmasq_time();    now = dnsmasq_time();
   
  /* Create a serial at startup if not configured. */  if (daemon->auth_zones)
  if (daemon->authinterface && daemon->soa_sn == 0)    {
       if (!daemon->authserver)
         die(_("--auth-server required when an auth zone is defined."), NULL, EC_BADCONF);
 
       /* Create a serial at startup if not configured. */
 #ifdef HAVE_BROKEN_RTC  #ifdef HAVE_BROKEN_RTC
    die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF);      if (daemon->soa_sn == 0)
         die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF);
 #else  #else
  daemon->soa_sn = now;      if (daemon->soa_sn == 0)
         daemon->soa_sn = now;
 #endif  #endif
       }
       
 #ifdef HAVE_DHCP6  #ifdef HAVE_DHCP6
   if (daemon->dhcp6)    if (daemon->dhcp6)
Line 271  int main (int argc, char **argv) Line 292  int main (int argc, char **argv)
     }      }
       
   if (daemon->dhcp || daemon->relay4)    if (daemon->dhcp || daemon->relay4)
    dhcp_init();    {
       dhcp_init();
 #   ifdef HAVE_LINUX_NETWORK
       if (!option_bool(OPT_NO_PING))
         need_cap_net_raw = 1;
       need_cap_net_admin = 1;
 #   endif
     }
       
 #  ifdef HAVE_DHCP6  #  ifdef HAVE_DHCP6
   if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6)    if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6)
    ra_init(now);    {
       ra_init(now);
 #   ifdef HAVE_LINUX_NETWORK
       need_cap_net_raw = 1;
       need_cap_net_admin = 1;
 #   endif
     }
       
   if (daemon->doing_dhcp6 || daemon->relay6)    if (daemon->doing_dhcp6 || daemon->relay6)
     dhcp6_init();      dhcp6_init();
Line 285  int main (int argc, char **argv) Line 319  int main (int argc, char **argv)
   
 #ifdef HAVE_IPSET  #ifdef HAVE_IPSET
   if (daemon->ipsets)    if (daemon->ipsets)
    ipset_init();    {
       ipset_init();
 #  ifdef HAVE_LINUX_NETWORK
       need_cap_net_admin = 1;
 #  endif
     }
 #endif  #endif
   
 #if  defined(HAVE_LINUX_NETWORK)  #if  defined(HAVE_LINUX_NETWORK)
  netlink_init();  netlink_warn = netlink_init();
 #elif defined(HAVE_BSD_NETWORK)  #elif defined(HAVE_BSD_NETWORK)
   route_init();    route_init();
 #endif  #endif
Line 351  int main (int argc, char **argv) Line 390  int main (int argc, char **argv)
   if (daemon->port != 0)    if (daemon->port != 0)
     {      {
       cache_init();        cache_init();
   
 #ifdef HAVE_DNSSEC  
       blockdata_init();        blockdata_init();
#endif      hash_questions_init();
     }      }
   
 #ifdef HAVE_INOTIFY  #ifdef HAVE_INOTIFY
  if (daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6)  if ((daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6)
       && (!option_bool(OPT_NO_RESOLV) || daemon->dynamic_dirs))
     inotify_dnsmasq_init();      inotify_dnsmasq_init();
   else    else
     daemon->inotifyfd = -1;      daemon->inotifyfd = -1;
 #endif  #endif
       
   if (daemon->dump_file)
 #ifdef HAVE_DUMPFILE
     dump_init();
   else 
     daemon->dumpfd = -1;
 #else
   die(_("Packet dumps not available: set HAVE_DUMP in src/config.h"), NULL, EC_BADCONF);
 #endif
   
   if (option_bool(OPT_DBUS))    if (option_bool(OPT_DBUS))
 #ifdef HAVE_DBUS  #ifdef HAVE_DBUS
     {      {
Line 377  int main (int argc, char **argv) Line 424  int main (int argc, char **argv)
   die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, EC_BADCONF);    die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, EC_BADCONF);
 #endif  #endif
   
     if (option_bool(OPT_UBUS))
   #ifdef HAVE_UBUS
       {
         daemon->ubus = NULL;
         ubus_init();
       }
   #else
     die(_("UBus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
   #endif
   
   if (daemon->port != 0)    if (daemon->port != 0)
     pre_allocate_sfds();      pre_allocate_sfds();
   
Line 386  int main (int argc, char **argv) Line 443  int main (int argc, char **argv)
       daemon->scriptuser &&         daemon->scriptuser && 
       (daemon->lease_change_command || daemon->luascript))        (daemon->lease_change_command || daemon->luascript))
     {      {
      if ((ent_pw = getpwnam(daemon->scriptuser)))      struct passwd *scr_pw;
       
       if ((scr_pw = getpwnam(daemon->scriptuser)))
         {          {
          script_uid = ent_pw->pw_uid;          script_uid = scr_pw->pw_uid;
          script_gid = ent_pw->pw_gid;          script_gid = scr_pw->pw_gid;
          }           }
       else        else
         baduser = daemon->scriptuser;          baduser = daemon->scriptuser;
Line 416  int main (int argc, char **argv) Line 475  int main (int argc, char **argv)
     }      }
   
 #if defined(HAVE_LINUX_NETWORK)  #if defined(HAVE_LINUX_NETWORK)
     /* We keep CAP_NETADMIN (for ARP-injection) and
        CAP_NET_RAW (for icmp) if we're doing dhcp,
        if we have yet to bind ports because of DAD, 
        or we're doing it dynamically, we need CAP_NET_BIND_SERVICE. */
     if ((is_dad_listeners() || option_bool(OPT_CLEVERBIND)) &&
         (option_bool(OPT_TFTP) || (daemon->port != 0 && daemon->port <= 1024)))
       need_cap_net_bind_service = 1;
   
     /* usptream servers which bind to an interface call SO_BINDTODEVICE
        for each TCP connection, so need CAP_NET_RAW */
     for (serv = daemon->servers; serv; serv = serv->next)
       if (serv->interface[0] != 0)
         need_cap_net_raw = 1;
   
     /* If we're doing Dbus or UBus, the above can be set dynamically,
        (as can ports) so always (potentially) needed. */
   #ifdef HAVE_DBUS
     if (option_bool(OPT_DBUS))
       {
         need_cap_net_bind_service = 1;
         need_cap_net_raw = 1;
       }
   #endif
   
   #ifdef HAVE_UBUS
     if (option_bool(OPT_UBUS))
       {
         need_cap_net_bind_service = 1;
         need_cap_net_raw = 1;
       }
   #endif
     
   /* determine capability API version here, while we can still    /* determine capability API version here, while we can still
      call safe_malloc */       call safe_malloc */
  if (ent_pw && ent_pw->pw_uid != 0)  int capsize = 1; /* for header version 1 */
   char *fail = NULL;
   
   hdr = safe_malloc(sizeof(*hdr));
   
   /* find version supported by kernel */
   memset(hdr, 0, sizeof(*hdr));
   capget(hdr, NULL);
   
   if (hdr->version != LINUX_CAPABILITY_VERSION_1)
     {      {
      int capsize = 1; /* for header version 1 */      /* if unknown version, use largest supported version (3) */
      hdr = safe_malloc(sizeof(*hdr));      if (hdr->version != LINUX_CAPABILITY_VERSION_2)
        hdr->version = LINUX_CAPABILITY_VERSION_3;
      /* find version supported by kernel */      capsize = 2;
      memset(hdr, 0, sizeof(*hdr)); 
      capget(hdr, NULL); 
       
      if (hdr->version != LINUX_CAPABILITY_VERSION_1) 
        { 
          /* if unknown version, use largest supported version (3) */ 
          if (hdr->version != LINUX_CAPABILITY_VERSION_2) 
            hdr->version = LINUX_CAPABILITY_VERSION_3; 
          capsize = 2; 
        } 
       
      data = safe_malloc(sizeof(*data) * capsize); 
      memset(data, 0, sizeof(*data) * capsize); 
     }      }
     
     data = safe_malloc(sizeof(*data) * capsize);
     capget(hdr, data); /* Get current values, for verification */
   
     if (need_cap_net_admin && !(data->permitted & (1 << CAP_NET_ADMIN)))
       fail = "NET_ADMIN";
     else if (need_cap_net_raw && !(data->permitted & (1 << CAP_NET_RAW)))
       fail = "NET_RAW";
     else if (need_cap_net_bind_service && !(data->permitted & (1 << CAP_NET_BIND_SERVICE)))
       fail = "NET_BIND_SERVICE";
     
     if (fail)
       die(_("process is missing required capability %s"), fail, EC_MISC);
   
     /* Now set bitmaps to set caps after daemonising */
     memset(data, 0, sizeof(*data) * capsize);
     
     if (need_cap_net_admin)
       data->effective |= (1 << CAP_NET_ADMIN);
     if (need_cap_net_raw)
       data->effective |= (1 << CAP_NET_RAW);
     if (need_cap_net_bind_service)
       data->effective |= (1 << CAP_NET_BIND_SERVICE);
     
     data->permitted = data->effective;  
 #endif  #endif
   
   /* Use a pipe to carry signals and other events back to the event loop     /* Use a pipe to carry signals and other events back to the event loop 
Line 459  int main (int argc, char **argv) Line 571  int main (int argc, char **argv)
       if (chdir("/") != 0)        if (chdir("/") != 0)
         die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC);           die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC); 
   
 #ifndef NO_FORK        
       if (!option_bool(OPT_NO_FORK))        if (!option_bool(OPT_NO_FORK))
         {          {
           pid_t pid;            pid_t pid;
Line 478  int main (int argc, char **argv) Line 589  int main (int argc, char **argv)
               char *msg;                char *msg;
   
               /* close our copy of write-end */                /* close our copy of write-end */
              while (retry_send(close(err_pipe[1])));              close(err_pipe[1]);
                               
               /* check for errors after the fork */                /* check for errors after the fork */
               if (read_event(err_pipe[0], &ev, &msg))                if (read_event(err_pipe[0], &ev, &msg))
Line 487  int main (int argc, char **argv) Line 598  int main (int argc, char **argv)
               _exit(EC_GOOD);                _exit(EC_GOOD);
             }               } 
                       
          while (retry_send(close(err_pipe[0])));          close(err_pipe[0]);
   
           /* NO calls to die() from here on. */            /* NO calls to die() from here on. */
                       
Line 499  int main (int argc, char **argv) Line 610  int main (int argc, char **argv)
           if (pid != 0)            if (pid != 0)
             _exit(0);              _exit(0);
         }          }
 #endif  
                           
       /* write pidfile _after_ forking ! */        /* write pidfile _after_ forking ! */
       if (daemon->runfile)        if (daemon->runfile)
Line 514  int main (int argc, char **argv) Line 624  int main (int argc, char **argv)
              extent that an attacker running as the unprivileged  user could replace the pidfile with a                extent that an attacker running as the unprivileged  user could replace the pidfile with a 
              symlink, and have the target of that symlink overwritten as root next time dnsmasq starts.                symlink, and have the target of that symlink overwritten as root next time dnsmasq starts. 
   
             The folowing code first deletes any existing file, and then opens it with the O_EXCL flag,             The following code first deletes any existing file, and then opens it with the O_EXCL flag,
              ensuring that the open() fails should there be any existing file (because the unlink() failed,                ensuring that the open() fails should there be any existing file (because the unlink() failed, 
              or an attacker exploited the race between unlink() and open()). This ensures that no symlink               or an attacker exploited the race between unlink() and open()). This ensures that no symlink
              attack can succeed.                attack can succeed. 
Line 537  int main (int argc, char **argv) Line 647  int main (int argc, char **argv)
             }              }
           else            else
             {              {
                 /* We're still running as root here. Change the ownership of the PID file
                    to the user we will be running as. Note that this is not to allow
                    us to delete the file, since that depends on the permissions 
                    of the directory containing the file. That directory will
                    need to by owned by the dnsmasq user, and the ownership of the
                    file has to match, to keep systemd >273 happy. */
                 if (getuid() == 0 && ent_pw && ent_pw->pw_uid != 0 && fchown(fd, ent_pw->pw_uid, ent_pw->pw_gid) == -1)
                   chown_warn = errno;
   
               if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0))                if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0))
                 err = 1;                  err = 1;
              else               else
                 {                  {
                  while (retry_send(close(fd)));                  if (close(fd) == -1)
                  if (errno != 0) 
                     err = 1;                      err = 1;
                 }                  }
             }              }
Line 583  int main (int argc, char **argv) Line 701  int main (int argc, char **argv)
       int bad_capabilities = 0;        int bad_capabilities = 0;
       gid_t dummy;        gid_t dummy;
               
      /* remove all supplimentary groups */      /* remove all supplementary groups */
       if (gp &&         if (gp && 
           (setgroups(0, &dummy) == -1 ||            (setgroups(0, &dummy) == -1 ||
            setgid(gp->gr_gid) == -1))             setgid(gp->gr_gid) == -1))
Line 595  int main (int argc, char **argv) Line 713  int main (int argc, char **argv)
       if (ent_pw && ent_pw->pw_uid != 0)        if (ent_pw && ent_pw->pw_uid != 0)
         {               {     
 #if defined(HAVE_LINUX_NETWORK)     #if defined(HAVE_LINUX_NETWORK)   
          /* On linux, we keep CAP_NETADMIN (for ARP-injection) and          /* Need to be able to drop root. */
             CAP_NET_RAW (for icmp) if we're doing dhcp. If we have yet to bind           data->effective |= (1 << CAP_SETUID);
             ports because of DAD, or we're doing it dynamically,          data->permitted |= (1 << CAP_SETUID);
             we need CAP_NET_BIND_SERVICE too. */ 
          if (is_dad_listeners() || option_bool(OPT_CLEVERBIND)) 
            data->effective = data->permitted = data->inheritable = 
              (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |  
              (1 << CAP_SETUID) | (1 << CAP_NET_BIND_SERVICE); 
          else 
            data->effective = data->permitted = data->inheritable = 
              (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID); 
           
           /* Tell kernel to not clear capabilities when dropping root */            /* Tell kernel to not clear capabilities when dropping root */
           if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)            if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
             bad_capabilities = errno;              bad_capabilities = errno;
Line 647  int main (int argc, char **argv) Line 756  int main (int argc, char **argv)
             }                   }     
   
 #ifdef HAVE_LINUX_NETWORK  #ifdef HAVE_LINUX_NETWORK
          if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))          data->effective &= ~(1 << CAP_SETUID);
           data->effective = data->permitted =          data->permitted &= ~(1 << CAP_SETUID);
             (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE); 
         else 
           data->effective = data->permitted =  
             (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW); 
          data->inheritable = 0; 
                       
          /* lose the setuid and setgid capbilities */          /* lose the setuid capability */
           if (capset(hdr, data) == -1)            if (capset(hdr, data) == -1)
             {              {
               send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);                send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);
Line 714  int main (int argc, char **argv) Line 818  int main (int argc, char **argv)
   
   if (daemon->port == 0)    if (daemon->port == 0)
     my_syslog(LOG_INFO, _("started, version %s DNS disabled"), VERSION);      my_syslog(LOG_INFO, _("started, version %s DNS disabled"), VERSION);
  else if (daemon->cachesize != 0)  else 
    my_syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);    {
  else      if (daemon->cachesize != 0)
    my_syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION);        {
           my_syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);
           if (daemon->cachesize > 10000)
             my_syslog(LOG_WARNING, _("cache size greater than 10000 may cause performance issues, and is unlikely to be useful."));
         }
       else
         my_syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION);
 
       if (option_bool(OPT_LOCAL_SERVICE))
         my_syslog(LOG_INFO, _("DNS service limited to local subnets"));
     }
       
   my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts);    my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
   
     if (chown_warn != 0)
       my_syslog(LOG_WARNING, "chown of PID file %s failed: %s", daemon->runfile, strerror(chown_warn));
       
 #ifdef HAVE_DBUS  #ifdef HAVE_DBUS
   if (option_bool(OPT_DBUS))    if (option_bool(OPT_DBUS))
Line 731  int main (int argc, char **argv) Line 848  int main (int argc, char **argv)
     }      }
 #endif  #endif
   
  if (option_bool(OPT_LOCAL_SERVICE))#ifdef HAVE_UBUS
    my_syslog(LOG_INFO, _("DNS service limited to local subnets"));  if (option_bool(OPT_UBUS))
      {
       if (daemon->ubus)
         my_syslog(LOG_INFO, _("UBus support enabled: connected to system bus"));
       else
         my_syslog(LOG_INFO, _("UBus support enabled: bus connection pending"));
     }
 #endif
 
 #ifdef HAVE_DNSSEC  #ifdef HAVE_DNSSEC
   if (option_bool(OPT_DNSSEC_VALID))    if (option_bool(OPT_DNSSEC_VALID))
     {      {
       int rc;        int rc;
      struct ds_config *ds;
       
       /* Delay creating the timestamp file until here, after we've changed user, so that        /* Delay creating the timestamp file until here, after we've changed user, so that
          it has the correct owner to allow updating the mtime later.            it has the correct owner to allow updating the mtime later. 
          This means we have to report fatal errors via the pipe. */           This means we have to report fatal errors via the pipe. */
Line 748  int main (int argc, char **argv) Line 873  int main (int argc, char **argv)
           _exit(0);            _exit(0);
         }          }
               
      my_syslog(LOG_INFO, _("DNSSEC validation enabled"));      if (option_bool(OPT_DNSSEC_IGN_NS))
         my_syslog(LOG_INFO, _("DNSSEC validation enabled but all unsigned answers are trusted"));
       else
         my_syslog(LOG_INFO, _("DNSSEC validation enabled"));
               
      if (option_bool(OPT_DNSSEC_TIME))      daemon->dnssec_no_time_check = option_bool(OPT_DNSSEC_TIME);
        my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until first cache reload"));      if (option_bool(OPT_DNSSEC_TIME) && !daemon->back_to_the_future)
         my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until receipt of SIGINT"));
               
       if (rc == 1)        if (rc == 1)
         my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));          my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid"));
   
         for (ds = daemon->ds; ds; ds = ds->next)
           my_syslog(LOG_INFO, _("configured with trust anchor for %s keytag %u"),
                     ds->name[0] == 0 ? "<root>" : ds->name, ds->keytag);
     }      }
 #endif  #endif
   
Line 767  int main (int argc, char **argv) Line 900  int main (int argc, char **argv)
   
   if (option_bool(OPT_NOWILD))    if (option_bool(OPT_NOWILD))
     warn_bound_listeners();      warn_bound_listeners();
     else if (!option_bool(OPT_CLEVERBIND))
       warn_wild_labels();
   
   warn_int_names();    warn_int_names();
       
Line 812  int main (int argc, char **argv) Line 947  int main (int argc, char **argv)
 #  ifdef HAVE_LINUX_NETWORK  #  ifdef HAVE_LINUX_NETWORK
   if (did_bind)    if (did_bind)
     my_syslog(MS_DHCP | LOG_INFO, _("DHCP, sockets bound exclusively to interface %s"), bound_device);      my_syslog(MS_DHCP | LOG_INFO, _("DHCP, sockets bound exclusively to interface %s"), bound_device);
   
     if (netlink_warn)
       my_syslog(LOG_WARNING, netlink_warn);
 #  endif  #  endif
   
  /* after dhcp_contruct_contexts */  /* after dhcp_construct_contexts */
   if (daemon->dhcp || daemon->doing_dhcp6)    if (daemon->dhcp || daemon->doing_dhcp6)
     lease_find_interfaces(now);      lease_find_interfaces(now);
 #endif  #endif
Line 824  int main (int argc, char **argv) Line 962  int main (int argc, char **argv)
     {      {
       struct tftp_prefix *p;        struct tftp_prefix *p;
   
      my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s",       my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s %s", 
                 daemon->tftp_prefix ? _("root is ") : _("enabled"),                  daemon->tftp_prefix ? _("root is ") : _("enabled"),
                daemon->tftp_prefix ? daemon->tftp_prefix: "",                daemon->tftp_prefix ? daemon->tftp_prefix : "",
                option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "");                option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "",
                 option_bool(OPT_SINGLE_PORT) ? _("single port mode") : "");
   
       if (tftp_prefix_missing)        if (tftp_prefix_missing)
         my_syslog(MS_TFTP | LOG_WARNING, _("warning: %s inaccessible"), daemon->tftp_prefix);          my_syslog(MS_TFTP | LOG_WARNING, _("warning: %s inaccessible"), daemon->tftp_prefix);
Line 845  int main (int argc, char **argv) Line 984  int main (int argc, char **argv)
               
       if (max_fd < 0)        if (max_fd < 0)
         max_fd = 5;          max_fd = 5;
      else if (max_fd < 100)      else if (max_fd < 100 && !option_bool(OPT_SINGLE_PORT))
         max_fd = max_fd/2;          max_fd = max_fd/2;
       else        else
         max_fd = max_fd - 20;          max_fd = max_fd - 20;
Line 868  int main (int argc, char **argv) Line 1007  int main (int argc, char **argv)
   
   /* finished start-up - release original process */    /* finished start-up - release original process */
   if (err_pipe[1] != -1)    if (err_pipe[1] != -1)
    while (retry_send(close(err_pipe[1])));    close(err_pipe[1]);
       
   if (daemon->port != 0)    if (daemon->port != 0)
     check_servers();      check_servers();
       
   pid = getpid();    pid = getpid();
   
     daemon->pipe_to_parent = -1;
     for (i = 0; i < MAX_PROCS; i++)
       daemon->tcp_pipes[i] = -1;
       
 #ifdef HAVE_INOTIFY  #ifdef HAVE_INOTIFY
   /* Using inotify, have to select a resolv file at startup */    /* Using inotify, have to select a resolv file at startup */
Line 903  int main (int argc, char **argv) Line 1046  int main (int argc, char **argv)
   
 #ifdef HAVE_DBUS  #ifdef HAVE_DBUS
       set_dbus_listeners();        set_dbus_listeners();
#endif  #endif
  
 #ifdef HAVE_UBUS
       if (option_bool(OPT_UBUS))
         set_ubus_listeners();
 #endif
           
 #ifdef HAVE_DHCP  #ifdef HAVE_DHCP
       if (daemon->dhcp || daemon->relay4)        if (daemon->dhcp || daemon->relay4)
         {          {
Line 966  int main (int argc, char **argv) Line 1114  int main (int argc, char **argv)
 #endif  #endif
   
         
      /* must do this just before select(), when we know no      /* must do this just before do_poll(), when we know no
          more calls to my_syslog() can occur */           more calls to my_syslog() can occur */
       set_log_writer();        set_log_writer();
               
Line 1034  int main (int argc, char **argv) Line 1182  int main (int argc, char **argv)
         }          }
       check_dbus_listeners();        check_dbus_listeners();
 #endif  #endif
      
 #ifdef HAVE_UBUS
       if (option_bool(OPT_UBUS))
         {
           /* if we didn't create a UBus connection, retry now. */
           if (!daemon->ubus)
             {
               ubus_init();
             }
 
           check_ubus_listeners();
         }
 #endif
 
       check_dns_listeners(now);        check_dns_listeners(now);
   
 #ifdef HAVE_TFTP  #ifdef HAVE_TFTP
Line 1073  static void sig_handler(int sig) Line 1234  static void sig_handler(int sig)
     {      {
       /* ignore anything other than TERM during startup        /* ignore anything other than TERM during startup
          and in helper proc. (helper ignore TERM too) */           and in helper proc. (helper ignore TERM too) */
      if (sig == SIGTERM)      if (sig == SIGTERM || sig == SIGINT)
         exit(EC_MISC);          exit(EC_MISC);
     }      }
   else if (pid != getpid())    else if (pid != getpid())
Line 1099  static void sig_handler(int sig) Line 1260  static void sig_handler(int sig)
         event = EVENT_DUMP;          event = EVENT_DUMP;
       else if (sig == SIGUSR2)        else if (sig == SIGUSR2)
         event = EVENT_REOPEN;          event = EVENT_REOPEN;
         else if (sig == SIGINT)
           {
             /* Handle SIGINT normally in debug mode, so
                ctrl-c continues to operate. */
             if (option_bool(OPT_DEBUG))
               exit(EC_MISC);
             else
               event = EVENT_TIME;
           }
       else        else
         return;          return;
   
Line 1181  static void fatal_event(struct event_desc *ev, char *m Line 1351  static void fatal_event(struct event_desc *ev, char *m
   
     case EVENT_FORK_ERR:      case EVENT_FORK_ERR:
       die(_("cannot fork into background: %s"), NULL, EC_MISC);        die(_("cannot fork into background: %s"), NULL, EC_MISC);
  
       /* fall through */
     case EVENT_PIPE_ERR:      case EVENT_PIPE_ERR:
       die(_("failed to create helper: %s"), NULL, EC_MISC);        die(_("failed to create helper: %s"), NULL, EC_MISC);
  
       /* fall through */
     case EVENT_CAP_ERR:      case EVENT_CAP_ERR:
       die(_("setting capabilities failed: %s"), NULL, EC_MISC);        die(_("setting capabilities failed: %s"), NULL, EC_MISC);
   
         /* fall through */
     case EVENT_USER_ERR:      case EVENT_USER_ERR:
       die(_("failed to change user-id to %s: %s"), msg, EC_MISC);        die(_("failed to change user-id to %s: %s"), msg, EC_MISC);
   
         /* fall through */
     case EVENT_GROUP_ERR:      case EVENT_GROUP_ERR:
       die(_("failed to change group-id to %s: %s"), msg, EC_MISC);        die(_("failed to change group-id to %s: %s"), msg, EC_MISC);
      
       /* fall through */
     case EVENT_PIDFILE:      case EVENT_PIDFILE:
       die(_("failed to open pidfile %s: %s"), msg, EC_FILE);        die(_("failed to open pidfile %s: %s"), msg, EC_FILE);
   
         /* fall through */
     case EVENT_LOG_ERR:      case EVENT_LOG_ERR:
       die(_("cannot open log %s: %s"), msg, EC_FILE);        die(_("cannot open log %s: %s"), msg, EC_FILE);
    
       /* fall through */
     case EVENT_LUA_ERR:      case EVENT_LUA_ERR:
       die(_("failed to load Lua script: %s"), msg, EC_MISC);        die(_("failed to load Lua script: %s"), msg, EC_MISC);
   
         /* fall through */
     case EVENT_TFTP_ERR:      case EVENT_TFTP_ERR:
       die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE);        die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE);
    
       /* fall through */
     case EVENT_TIME_ERR:      case EVENT_TIME_ERR:
       die(_("cannot create timestamp file %s: %s" ), msg, EC_BADCONF);        die(_("cannot create timestamp file %s: %s" ), msg, EC_BADCONF);
     }      }
Line 1225  static void async_event(int pipe, time_t now) Line 1404  static void async_event(int pipe, time_t now)
     switch (ev.event)      switch (ev.event)
       {        {
       case EVENT_RELOAD:        case EVENT_RELOAD:
#ifdef HAVE_DNSSEC        daemon->soa_sn++; /* Bump zone serial, as it may have changed. */
        if (option_bool(OPT_DNSSEC_VALID) && option_bool(OPT_DNSSEC_TIME))        
          { 
            my_syslog(LOG_INFO, _("now checking DNSSEC signature timestamps")); 
            reset_option_bool(OPT_DNSSEC_TIME); 
          }  
#endif 
         /* fall through */          /* fall through */
                   
       case EVENT_INIT:        case EVENT_INIT:
Line 1294  static void async_event(int pipe, time_t now) Line 1468  static void async_event(int pipe, time_t now)
                 daemon->tcp_pids[i] = 0;                  daemon->tcp_pids[i] = 0;
         break;          break;
                   
   #if defined(HAVE_SCRIPT)        
       case EVENT_KILLED:        case EVENT_KILLED:
         my_syslog(LOG_WARNING, _("script process killed by signal %d"), ev.data);          my_syslog(LOG_WARNING, _("script process killed by signal %d"), ev.data);
         break;          break;
Line 1307  static void async_event(int pipe, time_t now) Line 1482  static void async_event(int pipe, time_t now)
                   daemon->lease_change_command, strerror(ev.data));                    daemon->lease_change_command, strerror(ev.data));
         break;          break;
   
         case EVENT_SCRIPT_LOG:
           my_syslog(MS_SCRIPT | LOG_DEBUG, "%s", msg ? msg : "");
           free(msg);
           msg = NULL;
           break;
   
         /* necessary for fatal errors in helper */          /* necessary for fatal errors in helper */
       case EVENT_USER_ERR:        case EVENT_USER_ERR:
       case EVENT_DIE:        case EVENT_DIE:
       case EVENT_LUA_ERR:        case EVENT_LUA_ERR:
         fatal_event(&ev, msg);          fatal_event(&ev, msg);
         break;          break;
   #endif
   
       case EVENT_REOPEN:        case EVENT_REOPEN:
         /* Note: this may leave TCP-handling processes with the old file still open.          /* Note: this may leave TCP-handling processes with the old file still open.
Line 1332  static void async_event(int pipe, time_t now) Line 1514  static void async_event(int pipe, time_t now)
         poll_resolv(0, 1, now);          poll_resolv(0, 1, now);
         break;          break;
   
         case EVENT_TIME:
   #ifdef HAVE_DNSSEC
           if (daemon->dnssec_no_time_check && option_bool(OPT_DNSSEC_VALID) && option_bool(OPT_DNSSEC_TIME))
             {
               my_syslog(LOG_INFO, _("now checking DNSSEC signature timestamps"));
               daemon->dnssec_no_time_check = 0;
               clear_cache_and_reload(now);
             }
   #endif
           break;
           
       case EVENT_TERM:        case EVENT_TERM:
         /* Knock all our children on the head. */          /* Knock all our children on the head. */
         for (i = 0; i < MAX_PROCS; i++)          for (i = 0; i < MAX_PROCS; i++)
Line 1348  static void async_event(int pipe, time_t now) Line 1541  static void async_event(int pipe, time_t now)
             do {              do {
               helper_write();                helper_write();
             } while (!helper_buf_empty() || do_script_run(now));              } while (!helper_buf_empty() || do_script_run(now));
            while (retry_send(close(daemon->helperfd)));            close(daemon->helperfd);
           }            }
 #endif  #endif
                   
Line 1359  static void async_event(int pipe, time_t now) Line 1552  static void async_event(int pipe, time_t now)
         /* update timestamp file on TERM if time is considered valid */          /* update timestamp file on TERM if time is considered valid */
         if (daemon->back_to_the_future)          if (daemon->back_to_the_future)
           {            {
             if (utime(daemon->timestamp_file, NULL) == -1)             if (utimes(daemon->timestamp_file, NULL) == -1)
                 my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));                  my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno));
           }            }
 #endif  #endif
   
         if (daemon->runfile)          if (daemon->runfile)
           unlink(daemon->runfile);            unlink(daemon->runfile);
   
   #ifdef HAVE_DUMPFILE
           if (daemon->dumpfd != -1)
             close(daemon->dumpfd);
   #endif
                   
         my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));          my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
         flush_log();          flush_log();
Line 1456  void clear_cache_and_reload(time_t now) Line 1654  void clear_cache_and_reload(time_t now)
       if (option_bool(OPT_ETHERS))        if (option_bool(OPT_ETHERS))
         dhcp_read_ethers();          dhcp_read_ethers();
       reread_dhcp();        reread_dhcp();
 #ifdef HAVE_INOTIFY  
       set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0);  
 #endif  
       dhcp_update_configs(daemon->dhcp_conf);        dhcp_update_configs(daemon->dhcp_conf);
       lease_update_from_configs();         lease_update_from_configs(); 
       lease_update_file(now);         lease_update_file(now); 
Line 1482  static int set_dns_listeners(time_t now) Line 1677  static int set_dns_listeners(time_t now)
 #ifdef HAVE_TFTP  #ifdef HAVE_TFTP
   int  tftp = 0;    int  tftp = 0;
   struct tftp_transfer *transfer;    struct tftp_transfer *transfer;
  for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)  if (!option_bool(OPT_SINGLE_PORT))
    {    for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
      tftp++;      {
      poll_listen(transfer->sockfd, POLLIN);        tftp++;
    }        poll_listen(transfer->sockfd, POLLIN);
       }
 #endif  #endif
       
   /* will we be able to get memory? */    /* will we be able to get memory? */
   if (daemon->port != 0)    if (daemon->port != 0)
    get_new_frec(now, &wait, 0);    get_new_frec(now, &wait, NULL);
       
   for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)    for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
     poll_listen(serverfdp->fd, POLLIN);      poll_listen(serverfdp->fd, POLLIN);
Line 1511  static int set_dns_listeners(time_t now) Line 1707  static int set_dns_listeners(time_t now)
          we don't need to explicitly arrange to wake up here */           we don't need to explicitly arrange to wake up here */
       if  (listener->tcpfd != -1)        if  (listener->tcpfd != -1)
         for (i = 0; i < MAX_PROCS; i++)          for (i = 0; i < MAX_PROCS; i++)
          if (daemon->tcp_pids[i] == 0)          if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
             {              {
               poll_listen(listener->tcpfd, POLLIN);                poll_listen(listener->tcpfd, POLLIN);
               break;                break;
             }              }
   
 #ifdef HAVE_TFTP  #ifdef HAVE_TFTP
         /* tftp == 0 in single-port mode. */
       if (tftp <= daemon->tftp_max && listener->tftpfd != -1)        if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
         poll_listen(listener->tftpfd, POLLIN);          poll_listen(listener->tftpfd, POLLIN);
 #endif  #endif
   
     }      }
       
     if (!option_bool(OPT_DEBUG))
       for (i = 0; i < MAX_PROCS; i++)
         if (daemon->tcp_pipes[i] != -1)
           poll_listen(daemon->tcp_pipes[i], POLLIN);
     
   return wait;    return wait;
 }  }
   
Line 1532  static void check_dns_listeners(time_t now) Line 1734  static void check_dns_listeners(time_t now)
   struct serverfd *serverfdp;    struct serverfd *serverfdp;
   struct listener *listener;    struct listener *listener;
   int i;    int i;
  int pipefd[2];
   
   for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)    for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
     if (poll_check(serverfdp->fd, POLLIN))      if (poll_check(serverfdp->fd, POLLIN))
       reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);        reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
Line 1542  static void check_dns_listeners(time_t now) Line 1745  static void check_dns_listeners(time_t now)
       if (daemon->randomsocks[i].refcount != 0 &&         if (daemon->randomsocks[i].refcount != 0 && 
           poll_check(daemon->randomsocks[i].fd, POLLIN))            poll_check(daemon->randomsocks[i].fd, POLLIN))
         reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);          reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
  
   /* Races. The child process can die before we read all of the data from the
      pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the 
      process, and tcp_pipes to -1 and close the FD when we read the last
      of the data - indicated by cache_recv_insert returning zero.
      The order of these events is indeterminate, and both are needed
      to free the process slot. Once the child process has gone, poll()
      returns POLLHUP, not POLLIN, so have to check for both here. */
   if (!option_bool(OPT_DEBUG))
     for (i = 0; i < MAX_PROCS; i++)
       if (daemon->tcp_pipes[i] != -1 &&
           poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP) &&
           !cache_recv_insert(now, daemon->tcp_pipes[i]))
         {
           close(daemon->tcp_pipes[i]);
           daemon->tcp_pipes[i] = -1;    
         }
         
   for (listener = daemon->listeners; listener; listener = listener->next)    for (listener = daemon->listeners; listener; listener = listener->next)
     {      {
       if (listener->fd != -1 && poll_check(listener->fd, POLLIN))        if (listener->fd != -1 && poll_check(listener->fd, POLLIN))
Line 1568  static void check_dns_listeners(time_t now) Line 1788  static void check_dns_listeners(time_t now)
                       
           if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)            if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
             {              {
              while (retry_send(close(confd)));              close(confd);
               continue;                continue;
             }              }
                       
Line 1596  static void check_dns_listeners(time_t now) Line 1816  static void check_dns_listeners(time_t now)
               if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&                if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
                   indextoname(listener->tcpfd, if_index, intr_name))                    indextoname(listener->tcpfd, if_index, intr_name))
                 {                  {
                  struct all_addr addr;                  union all_addr addr;
                  addr.addr.addr4 = tcp_addr.in.sin_addr;                  
#ifdef HAVE_IPV6 
                   if (tcp_addr.sa.sa_family == AF_INET6)                    if (tcp_addr.sa.sa_family == AF_INET6)
                    addr.addr.addr6 = tcp_addr.in6.sin6_addr;                    addr.addr6 = tcp_addr.in6.sin6_addr;
#endif                  else
                     addr.addr4 = tcp_addr.in.sin_addr;
                                       
                   for (iface = daemon->interfaces; iface; iface = iface->next)                    for (iface = daemon->interfaces; iface; iface = iface->next)
                    if (iface->index == if_index)                    if (iface->index == if_index &&
                         iface->addr.sa.sa_family == tcp_addr.sa.sa_family)
                       break;                        break;
                                       
                   if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))                    if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
Line 1633  static void check_dns_listeners(time_t now) Line 1854  static void check_dns_listeners(time_t now)
           if (!client_ok)            if (!client_ok)
             {              {
               shutdown(confd, SHUT_RDWR);                shutdown(confd, SHUT_RDWR);
              while (retry_send(close(confd)));              close(confd);
             }              }
#ifndef NO_FORK          else if (!option_bool(OPT_DEBUG) && pipe(pipefd) == 0 && (p = fork()) != 0)
          else if (!option_bool(OPT_DEBUG) && (p = fork()) != 0) 
             {              {
              if (p != -1)              close(pipefd[1]); /* parent needs read pipe end. */
               if (p == -1)
                 close(pipefd[0]);
               else
                 {                  {
                   int i;                    int i;
   #ifdef HAVE_LINUX_NETWORK
                     /* The child process inherits the netlink socket, 
                        which it never uses, but when the parent (us) 
                        uses it in the future, the answer may go to the 
                        child, resulting in the parent blocking
                        forever awaiting the result. To avoid this
                        the child closes the netlink socket, but there's
                        a nasty race, since the parent may use netlink
                        before the child has done the close.
                        
                        To avoid this, the parent blocks here until a 
                        single byte comes back up the pipe, which
                        is sent by the child after it has closed the
                        netlink socket. */
                     
                     unsigned char a;
                     read_write(pipefd[0], &a, 1, 1);
   #endif
   
                   for (i = 0; i < MAX_PROCS; i++)                    for (i = 0; i < MAX_PROCS; i++)
                    if (daemon->tcp_pids[i] == 0)                    if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
                       {                        {
                         daemon->tcp_pids[i] = p;                          daemon->tcp_pids[i] = p;
                           daemon->tcp_pipes[i] = pipefd[0];
                         break;                          break;
                       }                        }
                 }                  }
              while (retry_send(close(confd)));              close(confd);
   
               /* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */                /* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */
               daemon->log_id += TCP_MAX_QUERIES;                daemon->log_id += TCP_MAX_QUERIES;
             }              }
 #endif  
           else            else
             {              {
               unsigned char *buff;                unsigned char *buff;
Line 1661  static void check_dns_listeners(time_t now) Line 1903  static void check_dns_listeners(time_t now)
               int flags;                int flags;
               struct in_addr netmask;                struct in_addr netmask;
               int auth_dns;                int auth_dns;
           
               if (iface)                if (iface)
                 {                  {
                   netmask = iface->netmask;                    netmask = iface->netmask;
Line 1673  static void check_dns_listeners(time_t now) Line 1915  static void check_dns_listeners(time_t now)
                   auth_dns = 0;                    auth_dns = 0;
                 }                  }
   
#ifndef NO_FORK              /* Arrange for SIGALRM after CHILD_LIFETIME seconds to
              /* Arrange for SIGALARM after CHILD_LIFETIME seconds to 
                  terminate the process. */                   terminate the process. */
               if (!option_bool(OPT_DEBUG))                if (!option_bool(OPT_DEBUG))
                alarm(CHILD_LIFETIME);                {
#endif#ifdef HAVE_LINUX_NETWORK
                   /* See comment above re: netlink socket. */
                   unsigned char a = 0;
   
                     close(daemon->netlinkfd);
                     read_write(pipefd[1], &a, 1, 0);
   #endif            
                     alarm(CHILD_LIFETIME);
                     close(pipefd[0]); /* close read end in child. */
                     daemon->pipe_to_parent = pipefd[1];
                   }
   
               /* start with no upstream connections. */                /* start with no upstream connections. */
               for (s = daemon->servers; s; s = s->next)                for (s = daemon->servers; s; s = s->next)
                  s->tcpfd = -1;                    s->tcpfd = -1; 
Line 1693  static void check_dns_listeners(time_t now) Line 1944  static void check_dns_listeners(time_t now)
               buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);                buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
                                 
               shutdown(confd, SHUT_RDWR);                shutdown(confd, SHUT_RDWR);
              while (retry_send(close(confd)));              close(confd);
                               
               if (buff)                if (buff)
                 free(buff);                  free(buff);
Line 1702  static void check_dns_listeners(time_t now) Line 1953  static void check_dns_listeners(time_t now)
                 if (s->tcpfd != -1)                  if (s->tcpfd != -1)
                   {                    {
                     shutdown(s->tcpfd, SHUT_RDWR);                      shutdown(s->tcpfd, SHUT_RDWR);
                    while (retry_send(close(s->tcpfd)));                    close(s->tcpfd);
                   }                    }
#ifndef NO_FORK                          
               if (!option_bool(OPT_DEBUG))                if (!option_bool(OPT_DEBUG))
                 {                  {
                     close(daemon->pipe_to_parent);
                   flush_log();                    flush_log();
                   _exit(0);                    _exit(0);
                 }                  }
 #endif  
             }              }
         }          }
     }      }
Line 1739  int icmp_ping(struct in_addr addr) Line 1990  int icmp_ping(struct in_addr addr)
 {  {
   /* Try and get an ICMP echo from a machine. */    /* Try and get an ICMP echo from a machine. */
   
  /* Note that whilst in the three second wait, we check for   int fd;
     (and service) events on the DNS and TFTP  sockets, (so doing that 
     better not use any resources our caller has in use...) 
     but we remain deaf to signals or further DHCP packets. */ 
 
  /* There can be a problem using dnsmasq_time() to end the loop, since 
     it's not monotonic, and can go backwards if the system clock is 
     tweaked, leading to the code getting stuck in this loop and 
     ignoring DHCP requests. To fix this, we check to see if select returned 
     as a result of a timeout rather than a socket becoming available. We 
     only allow this to happen as many times as it takes to get to the wait time 
     in quarter-second chunks. This provides a fallback way to end loop. */  
 
  int fd, rc; 
   struct sockaddr_in saddr;    struct sockaddr_in saddr;
   struct {     struct { 
     struct ip ip;      struct ip ip;
     struct icmp icmp;      struct icmp icmp;
   } packet;    } packet;
   unsigned short id = rand16();    unsigned short id = rand16();
  unsigned int i, j, timeout_count;  unsigned int i, j;
   int gotreply = 0;    int gotreply = 0;
   time_t start, now;  
   
 #if defined(HAVE_LINUX_NETWORK) || defined (HAVE_SOLARIS_NETWORK)  #if defined(HAVE_LINUX_NETWORK) || defined (HAVE_SOLARIS_NETWORK)
   if ((fd = make_icmp_sock()) == -1)    if ((fd = make_icmp_sock()) == -1)
Line 1791  int icmp_ping(struct in_addr addr) Line 2028  int icmp_ping(struct in_addr addr)
   while (retry_send(sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,     while (retry_send(sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0, 
                            (struct sockaddr *)&saddr, sizeof(saddr))));                             (struct sockaddr *)&saddr, sizeof(saddr))));
       
  for (now = start = dnsmasq_time(), timeout_count = 0;   gotreply = delay_dhcp(dnsmasq_time(), PING_WAIT, fd, addr.s_addr, id);
       (difftime(now, start) < (float)PING_WAIT) && (timeout_count < PING_WAIT * 4);)
 #if defined(HAVE_LINUX_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
   close(fd);
 #else
   opt = 1;
   setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
 #endif
 
   return gotreply;
 }
 
 int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
 {
   /* Delay processing DHCP packets for "sec" seconds counting from "start".
      If "fd" is not -1 it will stop waiting if an ICMP echo reply is received
      from "addr" with ICMP ID "id" and return 1 */
 
   /* Note that whilst waiting, we check for
      (and service) events on the DNS and TFTP  sockets, (so doing that
      better not use any resources our caller has in use...)
      but we remain deaf to signals or further DHCP packets. */
 
   /* There can be a problem using dnsmasq_time() to end the loop, since
      it's not monotonic, and can go backwards if the system clock is
      tweaked, leading to the code getting stuck in this loop and
      ignoring DHCP requests. To fix this, we check to see if select returned
      as a result of a timeout rather than a socket becoming available. We
      only allow this to happen as many times as it takes to get to the wait time
      in quarter-second chunks. This provides a fallback way to end loop. */
 
   int rc, timeout_count;
   time_t now;
 
   for (now = dnsmasq_time(), timeout_count = 0;
        (difftime(now, start) <= (float)sec) && (timeout_count < sec * 4);)
     {      {
       struct sockaddr_in faddr;  
       socklen_t len = sizeof(faddr);  
         
       poll_reset();        poll_reset();
      poll_listen(fd, POLLIN);      if (fd != -1)
         poll_listen(fd, POLLIN);
       set_dns_listeners(now);        set_dns_listeners(now);
       set_log_writer();        set_log_writer();
               
Line 1815  int icmp_ping(struct in_addr addr) Line 2084  int icmp_ping(struct in_addr addr)
         timeout_count++;          timeout_count++;
   
       now = dnsmasq_time();        now = dnsmasq_time();
      
       check_log_writer(0);        check_log_writer(0);
       check_dns_listeners(now);        check_dns_listeners(now);
      
 #ifdef HAVE_DHCP6  #ifdef HAVE_DHCP6
       if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN))        if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN))
         icmp6_packet(now);          icmp6_packet(now);
Line 1828  int icmp_ping(struct in_addr addr) Line 2097  int icmp_ping(struct in_addr addr)
       check_tftp_listeners(now);        check_tftp_listeners(now);
 #endif  #endif
   
      if (poll_check(fd, POLLIN) &&      if (fd != -1)
          recvfrom(fd, &packet, sizeof(packet), 0,        {
                   (struct sockaddr *)&faddr, &len) == sizeof(packet) &&          struct {
          saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&            struct ip ip;
          packet.icmp.icmp_type == ICMP_ECHOREPLY &&            struct icmp icmp;
          packet.icmp.icmp_seq == 0 &&          } packet;
          packet.icmp.icmp_id == id)          struct sockaddr_in faddr;
        {          socklen_t len = sizeof(faddr);
          gotreply = 1;          
          break;          if (poll_check(fd, POLLIN) &&
               recvfrom(fd, &packet, sizeof(packet), 0, (struct sockaddr *)&faddr, &len) == sizeof(packet) &&
               addr == faddr.sin_addr.s_addr &&
               packet.icmp.icmp_type == ICMP_ECHOREPLY &&
               packet.icmp.icmp_seq == 0 &&
               packet.icmp.icmp_id == id)
             return 1;
         }          }
     }      }
     
 #if defined(HAVE_LINUX_NETWORK) || defined(HAVE_SOLARIS_NETWORK)  
   while (retry_send(close(fd)));  
 #else  
   opt = 1;  
   setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));  
 #endif  
   
  return gotreply;  return 0;
 }  }
#endif#endif /* HAVE_DHCP */
 
  

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


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