--- embedaddon/dnsmasq/src/dnsmasq.c 2014/06/15 16:31:38 1.1.1.2 +++ embedaddon/dnsmasq/src/dnsmasq.c 2016/11/02 09:57:01 1.1.1.3 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,12 +24,13 @@ struct daemon *daemon; static volatile pid_t pid = 0; static volatile int pipewrite; -static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp); -static void check_dns_listeners(fd_set *set, time_t now); +static int set_dns_listeners(time_t now); +static void check_dns_listeners(time_t now); static void sig_handler(int sig); static void async_event(int pipe, time_t now); static void fatal_event(struct event_desc *ev, char *msg); static int read_event(int fd, struct event_desc *evp, char **msg); +static void poll_resolv(int force, int do_reload, time_t now); int main (int argc, char **argv) { @@ -57,6 +58,9 @@ int main (int argc, char **argv) struct dhcp_context *context; struct dhcp_relay *relay; #endif +#ifdef HAVE_TFTP + int tftp_prefix_missing = 0; +#endif #ifdef LOCALEDIR setlocale(LC_ALL, ""); @@ -79,28 +83,41 @@ int main (int argc, char **argv) sigaction(SIGPIPE, &sigact, NULL); umask(022); /* known umask, create leases and pid files as 0644 */ - + + rand_init(); /* Must precede read_opts() */ + read_opts(argc, argv, compile_opts); if (daemon->edns_pktsz < PACKETSZ) daemon->edns_pktsz = PACKETSZ; -#ifdef HAVE_DNSSEC - /* Enforce min packet big enough for DNSSEC */ - if (option_bool(OPT_DNSSEC_VALID) && daemon->edns_pktsz < EDNS_PKTSZ) - daemon->edns_pktsz = EDNS_PKTSZ; -#endif - daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? - daemon->edns_pktsz : DNSMASQ_PACKETSZ; + /* Min buffer size: we check after adding each record, so there must be + memory for the largest packet, and the largest record so the + min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000. + This might be increased is EDNS packet size if greater than the minimum. */ + daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ; daemon->packet = safe_malloc(daemon->packet_buff_sz); daemon->addrbuff = safe_malloc(ADDRSTRLEN); + if (option_bool(OPT_EXTRALOG)) + daemon->addrbuff2 = safe_malloc(ADDRSTRLEN); #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID)) { - daemon->keyname = safe_malloc(MAXDNAME); - daemon->workspacename = safe_malloc(MAXDNAME); + /* Note that both /000 and '.' are allowed within labels. These get + represented in presentation format using NAME_ESCAPE as an escape + character when in DNSSEC mode. + In theory, if all the characters in a name were /000 or + '.' or NAME_ESCAPE then all would have to be escaped, so the + presentation format would be twice as long as the spec. + + daemon->namebuff was previously allocated by the option-reading + code before we knew if we're in DNSSEC mode, so reallocate here. */ + free(daemon->namebuff); + daemon->namebuff = safe_malloc(MAXDNAME * 2); + daemon->keyname = safe_malloc(MAXDNAME * 2); + daemon->workspacename = safe_malloc(MAXDNAME * 2); } #endif @@ -143,15 +160,28 @@ int main (int argc, char **argv) reset_option_bool(OPT_CLEVERBIND); } #endif + +#ifndef HAVE_INOTIFY + if (daemon->dynamic_dirs) + die(_("dhcp-hostsdir, dhcp-optsdir and hostsdir are not supported on this platform"), NULL, EC_BADCONF); +#endif if (option_bool(OPT_DNSSEC_VALID)) { #ifdef HAVE_DNSSEC - if (!daemon->ds) - die(_("No trust anchors provided for DNSSEC"), NULL, EC_BADCONF); + struct ds_config *ds; + + /* Must have at least a root trust anchor, or the DNSSEC code + can loop forever. */ + for (ds = daemon->ds; ds; ds = ds->next) + if (ds->name[0] == 0) + break; + + if (!ds) + die(_("no root trust anchor provided for DNSSEC"), NULL, EC_BADCONF); if (daemon->cachesize < CACHESIZ) - die(_("Cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF); + die(_("cannot reduce cache size from default when DNSSEC enabled"), NULL, EC_BADCONF); #else die(_("DNSSEC not available: set HAVE_DNSSEC in src/config.h"), NULL, EC_BADCONF); #endif @@ -164,10 +194,10 @@ int main (int argc, char **argv) #ifdef HAVE_CONNTRACK if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport)) - die (_("Cannot use --conntrack AND --query-port"), NULL, EC_BADCONF); + die (_("cannot use --conntrack AND --query-port"), NULL, EC_BADCONF); #else if (option_bool(OPT_CONNTRACK)) - die(_("Conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF); + die(_("conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF); #endif #ifdef HAVE_SOLARIS_NETWORK @@ -185,8 +215,17 @@ int main (int argc, char **argv) die(_("authoritative DNS not available: set HAVE_AUTH in src/config.h"), NULL, EC_BADCONF); #endif - rand_init(); - +#ifndef HAVE_LOOP + if (option_bool(OPT_LOOP_DETECT)) + die(_("loop detection not available: set HAVE_LOOP in src/config.h"), NULL, EC_BADCONF); +#endif + + if (daemon->max_port != MAX_PORT && daemon->min_port == 0) + daemon->min_port = 1024u; + + if (daemon->max_port < daemon->min_port) + die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF); + now = dnsmasq_time(); /* Create a serial at startup if not configured. */ @@ -220,8 +259,11 @@ int main (int argc, char **argv) /* Note that order matters here, we must call lease_init before creating any file descriptors which shouldn't be leaked to the lease-script init process. We need to call common_init - before lease_init to allocate buffers it uses.*/ - if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6) + before lease_init to allocate buffers it uses. + The script subsystem relies on DHCP buffers, hence the last two + conditions below. */ + if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || + daemon->relay6 || option_bool(OPT_TFTP) || option_bool(OPT_SCRIPT_ARP)) { dhcp_common_init(); if (daemon->dhcp || daemon->doing_dhcp6) @@ -309,11 +351,19 @@ int main (int argc, char **argv) if (daemon->port != 0) { cache_init(); + #ifdef HAVE_DNSSEC blockdata_init(); #endif } - + +#ifdef HAVE_INOTIFY + if (daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6) + inotify_dnsmasq_init(); + else + daemon->inotifyfd = -1; +#endif + if (option_bool(OPT_DBUS)) #ifdef HAVE_DBUS { @@ -326,7 +376,7 @@ int main (int argc, char **argv) #else die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, EC_BADCONF); #endif - + if (daemon->port != 0) pre_allocate_sfds(); @@ -353,7 +403,7 @@ int main (int argc, char **argv) if (baduser) die(_("unknown user or group: %s"), baduser, EC_BADCONF); - + /* implement group defaults, "dip" if available, or group associated with uid */ if (!daemon->group_set && !gp) { @@ -428,7 +478,7 @@ int main (int argc, char **argv) char *msg; /* close our copy of write-end */ - close(err_pipe[1]); + while (retry_send(close(err_pipe[1]))); /* check for errors after the fork */ if (read_event(err_pipe[0], &ev, &msg)) @@ -437,7 +487,7 @@ int main (int argc, char **argv) _exit(EC_GOOD); } - close(err_pipe[0]); + while (retry_send(close(err_pipe[0]))); /* NO calls to die() from here on. */ @@ -489,10 +539,12 @@ int main (int argc, char **argv) { if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0)) err = 1; - - while (!err && close(fd) == -1) - if (!retry_send()) - err = 1; + else + { + while (retry_send(close(fd))); + if (errno != 0) + err = 1; + } } if (err) @@ -509,17 +561,21 @@ int main (int argc, char **argv) { /* open stdout etc to /dev/null */ int nullfd = open("/dev/null", O_RDWR); - dup2(nullfd, STDOUT_FILENO); - dup2(nullfd, STDERR_FILENO); - dup2(nullfd, STDIN_FILENO); - close(nullfd); + if (nullfd != -1) + { + dup2(nullfd, STDOUT_FILENO); + dup2(nullfd, STDERR_FILENO); + dup2(nullfd, STDIN_FILENO); + close(nullfd); + } } /* if we are to run scripts, we need to fork a helper before dropping root. */ daemon->helperfd = -1; #ifdef HAVE_SCRIPT - if ((daemon->dhcp || daemon->dhcp6) && (daemon->lease_change_command || daemon->luascript)) - daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); + if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_SCRIPT_ARP)) && + (daemon->lease_change_command || daemon->luascript)) + daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); #endif if (!option_bool(OPT_DEBUG) && getuid() == 0) @@ -611,12 +667,14 @@ int main (int argc, char **argv) } #ifdef HAVE_LINUX_NETWORK + free(hdr); + free(data); if (option_bool(OPT_DEBUG)) prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); #endif #ifdef HAVE_TFTP - if (option_bool(OPT_TFTP)) + if (option_bool(OPT_TFTP)) { DIR *dir; struct tftp_prefix *p; @@ -625,20 +683,31 @@ int main (int argc, char **argv) { if (!((dir = opendir(daemon->tftp_prefix)))) { - send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix); - _exit(0); + tftp_prefix_missing = 1; + if (!option_bool(OPT_TFTP_NO_FAIL)) + { + send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix); + _exit(0); + } } - closedir(dir); + else + closedir(dir); } for (p = daemon->if_prefix; p; p = p->next) { + p->missing = 0; if (!((dir = opendir(p->prefix)))) - { - send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix); - _exit(0); - } - closedir(dir); + { + p->missing = 1; + if (!option_bool(OPT_TFTP_NO_FAIL)) + { + send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix); + _exit(0); + } + } + else + closedir(dir); } } #endif @@ -668,9 +737,24 @@ int main (int argc, char **argv) #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID)) { + int rc; + + /* 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. + This means we have to report fatal errors via the pipe. */ + if ((rc = setup_timestamp()) == -1) + { + send_event(err_pipe[1], EVENT_TIME_ERR, errno, daemon->timestamp_file); + _exit(0); + } + my_syslog(LOG_INFO, _("DNSSEC validation enabled")); + if (option_bool(OPT_DNSSEC_TIME)) my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until first cache reload")); + + if (rc == 1) + my_syslog(LOG_INFO, _("DNSSEC signature timestamps not checked until system time valid")); } #endif @@ -736,18 +820,22 @@ int main (int argc, char **argv) #endif #ifdef HAVE_TFTP - if (option_bool(OPT_TFTP)) + if (option_bool(OPT_TFTP)) { -#ifdef FD_SETSIZE - if (FD_SETSIZE < (unsigned)max_fd) - max_fd = FD_SETSIZE; -#endif + struct tftp_prefix *p; my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s", daemon->tftp_prefix ? _("root is ") : _("enabled"), daemon->tftp_prefix ? daemon->tftp_prefix: "", option_bool(OPT_TFTP_SECURE) ? _("secure mode") : ""); - + + if (tftp_prefix_missing) + my_syslog(MS_TFTP | LOG_WARNING, _("warning: %s inaccessible"), daemon->tftp_prefix); + + for (p = daemon->if_prefix; p; p = p->next) + if (p->missing) + my_syslog(MS_TFTP | LOG_WARNING, _("warning: TFTP directory %s inaccessible"), p->prefix); + /* This is a guess, it assumes that for small limits, disjoint files might be served, but for large limits, a single file will be sent to may clients (the file only needs @@ -780,127 +868,114 @@ int main (int argc, char **argv) /* finished start-up - release original process */ if (err_pipe[1] != -1) - close(err_pipe[1]); + while (retry_send(close(err_pipe[1]))); if (daemon->port != 0) check_servers(); pid = getpid(); +#ifdef HAVE_INOTIFY + /* Using inotify, have to select a resolv file at startup */ + poll_resolv(1, 0, now); +#endif + while (1) { - int maxfd = -1; - struct timeval t, *tp = NULL; - fd_set rset, wset, eset; + int t, timeout = -1; - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_ZERO(&eset); + poll_reset(); /* if we are out of resources, find how long we have to wait for some to come free, we'll loop around then and restart listening for queries */ - if ((t.tv_sec = set_dns_listeners(now, &rset, &maxfd)) != 0) - { - t.tv_usec = 0; - tp = &t; - } + if ((t = set_dns_listeners(now)) != 0) + timeout = t * 1000; /* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */ if (daemon->tftp_trans || (option_bool(OPT_DBUS) && !daemon->dbus)) - { - t.tv_sec = 0; - t.tv_usec = 250000; - tp = &t; - } + timeout = 250; + /* Wake every second whilst waiting for DAD to complete */ else if (is_dad_listeners()) - { - t.tv_sec = 1; - t.tv_usec = 0; - tp = &t; - } + timeout = 1000; #ifdef HAVE_DBUS - set_dbus_listeners(&maxfd, &rset, &wset, &eset); + set_dbus_listeners(); #endif #ifdef HAVE_DHCP if (daemon->dhcp || daemon->relay4) { - FD_SET(daemon->dhcpfd, &rset); - bump_maxfd(daemon->dhcpfd, &maxfd); + poll_listen(daemon->dhcpfd, POLLIN); if (daemon->pxefd != -1) - { - FD_SET(daemon->pxefd, &rset); - bump_maxfd(daemon->pxefd, &maxfd); - } + poll_listen(daemon->pxefd, POLLIN); } #endif #ifdef HAVE_DHCP6 if (daemon->doing_dhcp6 || daemon->relay6) - { - FD_SET(daemon->dhcp6fd, &rset); - bump_maxfd(daemon->dhcp6fd, &maxfd); - } - + poll_listen(daemon->dhcp6fd, POLLIN); + if (daemon->doing_ra) - { - FD_SET(daemon->icmp6fd, &rset); - bump_maxfd(daemon->icmp6fd, &maxfd); - } + poll_listen(daemon->icmp6fd, POLLIN); #endif + +#ifdef HAVE_INOTIFY + if (daemon->inotifyfd != -1) + poll_listen(daemon->inotifyfd, POLLIN); +#endif #if defined(HAVE_LINUX_NETWORK) - FD_SET(daemon->netlinkfd, &rset); - bump_maxfd(daemon->netlinkfd, &maxfd); + poll_listen(daemon->netlinkfd, POLLIN); #elif defined(HAVE_BSD_NETWORK) - FD_SET(daemon->routefd, &rset); - bump_maxfd(daemon->routefd, &maxfd); + poll_listen(daemon->routefd, POLLIN); #endif + + poll_listen(piperead, POLLIN); - FD_SET(piperead, &rset); - bump_maxfd(piperead, &maxfd); +#ifdef HAVE_SCRIPT +# ifdef HAVE_DHCP + while (helper_buf_empty() && do_script_run(now)); +# endif -#ifdef HAVE_DHCP -# ifdef HAVE_SCRIPT - while (helper_buf_empty() && do_script_run(now)); + /* Refresh cache */ + if (option_bool(OPT_SCRIPT_ARP)) + find_mac(NULL, NULL, 0, now); + while (helper_buf_empty() && do_arp_script_run()); # ifdef HAVE_TFTP while (helper_buf_empty() && do_tftp_script_run()); # endif if (!helper_buf_empty()) - { - FD_SET(daemon->helperfd, &wset); - bump_maxfd(daemon->helperfd, &maxfd); - } -# else + poll_listen(daemon->helperfd, POLLOUT); +#else /* need this for other side-effects */ +# ifdef HAVE_DHCP while (do_script_run(now)); +# endif + while (do_arp_script_run()); + # ifdef HAVE_TFTP while (do_tftp_script_run()); # endif -# endif #endif + /* must do this just before select(), when we know no more calls to my_syslog() can occur */ - set_log_writer(&wset, &maxfd); + set_log_writer(); - if (select(maxfd+1, &rset, &wset, &eset, tp) < 0) - { - /* otherwise undefined after error */ - FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset); - } - + if (do_poll(timeout) < 0) + continue; + now = dnsmasq_time(); - check_log_writer(&wset); + check_log_writer(0); /* prime. */ enumerate_interfaces(1); @@ -916,13 +991,20 @@ int main (int argc, char **argv) } #if defined(HAVE_LINUX_NETWORK) - if (FD_ISSET(daemon->netlinkfd, &rset)) - netlink_multicast(now); + if (poll_check(daemon->netlinkfd, POLLIN)) + netlink_multicast(); #elif defined(HAVE_BSD_NETWORK) - if (FD_ISSET(daemon->routefd, &rset)) - route_sock(now); + if (poll_check(daemon->routefd, POLLIN)) + route_sock(); #endif +#ifdef HAVE_INOTIFY + if (daemon->inotifyfd != -1 && poll_check(daemon->inotifyfd, POLLIN) && inotify_check(now)) + { + if (daemon->port != 0 && !option_bool(OPT_NO_POLL)) + poll_resolv(1, 1, now); + } +#else /* Check for changes to resolv files once per second max. */ /* Don't go silent for long periods if the clock goes backwards. */ if (daemon->last_resolv == 0 || @@ -935,8 +1017,9 @@ int main (int argc, char **argv) poll_resolv(0, daemon->last_resolv != 0, now); daemon->last_resolv = now; } - - if (FD_ISSET(piperead, &rset)) +#endif + + if (poll_check(piperead, POLLIN)) async_event(piperead, now); #ifdef HAVE_DBUS @@ -949,34 +1032,34 @@ int main (int argc, char **argv) if (daemon->dbus) my_syslog(LOG_INFO, _("connected to system DBus")); } - check_dbus_listeners(&rset, &wset, &eset); + check_dbus_listeners(); #endif - check_dns_listeners(&rset, now); + check_dns_listeners(now); #ifdef HAVE_TFTP - check_tftp_listeners(&rset, now); + check_tftp_listeners(now); #endif #ifdef HAVE_DHCP if (daemon->dhcp || daemon->relay4) { - if (FD_ISSET(daemon->dhcpfd, &rset)) + if (poll_check(daemon->dhcpfd, POLLIN)) dhcp_packet(now, 0); - if (daemon->pxefd != -1 && FD_ISSET(daemon->pxefd, &rset)) + if (daemon->pxefd != -1 && poll_check(daemon->pxefd, POLLIN)) dhcp_packet(now, 1); } #ifdef HAVE_DHCP6 - if ((daemon->doing_dhcp6 || daemon->relay6) && FD_ISSET(daemon->dhcp6fd, &rset)) + if ((daemon->doing_dhcp6 || daemon->relay6) && poll_check(daemon->dhcp6fd, POLLIN)) dhcp6_packet(now); - if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset)) + if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN)) icmp6_packet(now); #endif # ifdef HAVE_SCRIPT - if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset)) + if (daemon->helperfd != -1 && poll_check(daemon->helperfd, POLLOUT)) helper_write(); # endif #endif @@ -1037,6 +1120,11 @@ void send_alarm(time_t event, time_t now) } } +void queue_event(int event) +{ + send_event(pipewrite, event, 0, NULL); +} + void send_event(int fd, int event, int data, char *msg) { struct event_desc ev; @@ -1117,6 +1205,9 @@ static void fatal_event(struct event_desc *ev, char *m case EVENT_TFTP_ERR: die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE); + + case EVENT_TIME_ERR: + die(_("cannot create timestamp file %s: %s" ), msg, EC_BADCONF); } } @@ -1230,14 +1321,24 @@ static void async_event(int pipe, time_t now) if (daemon->log_file != NULL) log_reopen(daemon->log_file); break; - + + case EVENT_NEWADDR: + newaddress(now); + break; + + case EVENT_NEWROUTE: + resend_query(); + /* Force re-reading resolv file right now, for luck. */ + poll_resolv(0, 1, now); + break; + case EVENT_TERM: /* Knock all our children on the head. */ for (i = 0; i < MAX_PROCS; i++) if (daemon->tcp_pids[i] != 0) kill(daemon->tcp_pids[i], SIGALRM); -#if defined(HAVE_SCRIPT) +#if defined(HAVE_SCRIPT) && defined(HAVE_DHCP) /* handle pending lease transitions */ if (daemon->helperfd != -1) { @@ -1247,13 +1348,22 @@ static void async_event(int pipe, time_t now) do { helper_write(); } while (!helper_buf_empty() || do_script_run(now)); - close(daemon->helperfd); + while (retry_send(close(daemon->helperfd))); } #endif if (daemon->lease_stream) fclose(daemon->lease_stream); +#ifdef HAVE_DNSSEC + /* update timestamp file on TERM if time is considered valid */ + if (daemon->back_to_the_future) + { + if (utime(daemon->timestamp_file, NULL) == -1) + my_syslog(LOG_ERR, _("failed to update mtime on %s: %s"), daemon->timestamp_file, strerror(errno)); + } +#endif + if (daemon->runfile) unlink(daemon->runfile); @@ -1263,7 +1373,7 @@ static void async_event(int pipe, time_t now) } } -void poll_resolv(int force, int do_reload, time_t now) +static void poll_resolv(int force, int do_reload, time_t now) { struct resolvc *res, *latest; struct stat statbuf; @@ -1346,6 +1456,9 @@ void clear_cache_and_reload(time_t now) if (option_bool(OPT_ETHERS)) dhcp_read_ethers(); reread_dhcp(); +#ifdef HAVE_INOTIFY + set_dynamic_inotify(AH_DHCP_HST | AH_DHCP_OPT, 0, NULL, 0); +#endif dhcp_update_configs(daemon->dhcp_conf); lease_update_from_configs(); lease_update_file(now); @@ -1360,7 +1473,7 @@ void clear_cache_and_reload(time_t now) #endif } -static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp) +static int set_dns_listeners(time_t now) { struct serverfd *serverfdp; struct listener *listener; @@ -1372,8 +1485,7 @@ static int set_dns_listeners(time_t now, fd_set *set, for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) { tftp++; - FD_SET(transfer->sockfd, set); - bump_maxfd(transfer->sockfd, maxfdp); + poll_listen(transfer->sockfd, POLLIN); } #endif @@ -1382,45 +1494,32 @@ static int set_dns_listeners(time_t now, fd_set *set, get_new_frec(now, &wait, 0); for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) - { - FD_SET(serverfdp->fd, set); - bump_maxfd(serverfdp->fd, maxfdp); - } - + poll_listen(serverfdp->fd, POLLIN); + if (daemon->port != 0 && !daemon->osport) for (i = 0; i < RANDOM_SOCKS; i++) if (daemon->randomsocks[i].refcount != 0) - { - FD_SET(daemon->randomsocks[i].fd, set); - bump_maxfd(daemon->randomsocks[i].fd, maxfdp); - } - + poll_listen(daemon->randomsocks[i].fd, POLLIN); + for (listener = daemon->listeners; listener; listener = listener->next) { /* only listen for queries if we have resources */ if (listener->fd != -1 && wait == 0) - { - FD_SET(listener->fd, set); - bump_maxfd(listener->fd, maxfdp); - } - + poll_listen(listener->fd, POLLIN); + /* death of a child goes through the select loop, so we don't need to explicitly arrange to wake up here */ if (listener->tcpfd != -1) for (i = 0; i < MAX_PROCS; i++) if (daemon->tcp_pids[i] == 0) { - FD_SET(listener->tcpfd, set); - bump_maxfd(listener->tcpfd, maxfdp); + poll_listen(listener->tcpfd, POLLIN); break; } #ifdef HAVE_TFTP if (tftp <= daemon->tftp_max && listener->tftpfd != -1) - { - FD_SET(listener->tftpfd, set); - bump_maxfd(listener->tftpfd, maxfdp); - } + poll_listen(listener->tftpfd, POLLIN); #endif } @@ -1428,33 +1527,33 @@ static int set_dns_listeners(time_t now, fd_set *set, return wait; } -static void check_dns_listeners(fd_set *set, time_t now) +static void check_dns_listeners(time_t now) { struct serverfd *serverfdp; struct listener *listener; int i; for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) - if (FD_ISSET(serverfdp->fd, set)) + if (poll_check(serverfdp->fd, POLLIN)) reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now); if (daemon->port != 0 && !daemon->osport) for (i = 0; i < RANDOM_SOCKS; i++) if (daemon->randomsocks[i].refcount != 0 && - FD_ISSET(daemon->randomsocks[i].fd, set)) + poll_check(daemon->randomsocks[i].fd, POLLIN)) reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now); for (listener = daemon->listeners; listener; listener = listener->next) { - if (listener->fd != -1 && FD_ISSET(listener->fd, set)) + if (listener->fd != -1 && poll_check(listener->fd, POLLIN)) receive_query(listener, now); #ifdef HAVE_TFTP - if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set)) + if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN)) tftp_request(listener, now); #endif - if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set)) + if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN)) { int confd, client_ok = 1; struct irec *iface = NULL; @@ -1469,7 +1568,7 @@ static void check_dns_listeners(fd_set *set, time_t no if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1) { - close(confd); + while (retry_send(close(confd))); continue; } @@ -1534,7 +1633,7 @@ static void check_dns_listeners(fd_set *set, time_t no if (!client_ok) { shutdown(confd, SHUT_RDWR); - close(confd); + while (retry_send(close(confd))); } #ifndef NO_FORK else if (!option_bool(OPT_DEBUG) && (p = fork()) != 0) @@ -1549,7 +1648,10 @@ static void check_dns_listeners(fd_set *set, time_t no break; } } - close(confd); + while (retry_send(close(confd))); + + /* The child can use up to TCP_MAX_QUERIES ids, so skip that many. */ + daemon->log_id += TCP_MAX_QUERIES; } #endif else @@ -1591,7 +1693,7 @@ static void check_dns_listeners(fd_set *set, time_t no buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns); shutdown(confd, SHUT_RDWR); - close(confd); + while (retry_send(close(confd))); if (buff) free(buff); @@ -1600,7 +1702,7 @@ static void check_dns_listeners(fd_set *set, time_t no if (s->tcpfd != -1) { shutdown(s->tcpfd, SHUT_RDWR); - close(s->tcpfd); + while (retry_send(close(s->tcpfd))); } #ifndef NO_FORK if (!option_bool(OPT_DEBUG)) @@ -1642,14 +1744,22 @@ int icmp_ping(struct in_addr addr) better not use any resources our caller has in use...) but we remain deaf to signals or further DHCP packets. */ - int fd; + /* 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 { struct ip ip; struct icmp icmp; } packet; unsigned short id = rand16(); - unsigned int i, j; + unsigned int i, j, timeout_count; int gotreply = 0; time_t start, now; @@ -1678,57 +1788,47 @@ int icmp_ping(struct in_addr addr) j = (j & 0xffff) + (j >> 16); packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j; - while (sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0, - (struct sockaddr *)&saddr, sizeof(saddr)) == -1 && - retry_send()); + while (retry_send(sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0, + (struct sockaddr *)&saddr, sizeof(saddr)))); - for (now = start = dnsmasq_time(); - difftime(now, start) < (float)PING_WAIT;) + for (now = start = dnsmasq_time(), timeout_count = 0; + (difftime(now, start) < (float)PING_WAIT) && (timeout_count < PING_WAIT * 4);) { - struct timeval tv; - fd_set rset, wset; struct sockaddr_in faddr; - int maxfd = fd; socklen_t len = sizeof(faddr); - tv.tv_usec = 250000; - tv.tv_sec = 0; + poll_reset(); + poll_listen(fd, POLLIN); + set_dns_listeners(now); + set_log_writer(); - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_SET(fd, &rset); - set_dns_listeners(now, &rset, &maxfd); - set_log_writer(&wset, &maxfd); - #ifdef HAVE_DHCP6 if (daemon->doing_ra) - { - FD_SET(daemon->icmp6fd, &rset); - bump_maxfd(daemon->icmp6fd, &maxfd); - } + poll_listen(daemon->icmp6fd, POLLIN); #endif - if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0) - { - FD_ZERO(&rset); - FD_ZERO(&wset); - } + rc = do_poll(250); + + if (rc < 0) + continue; + else if (rc == 0) + timeout_count++; now = dnsmasq_time(); - check_log_writer(&wset); - check_dns_listeners(&rset, now); + check_log_writer(0); + check_dns_listeners(now); #ifdef HAVE_DHCP6 - if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset)) + if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN)) icmp6_packet(now); #endif #ifdef HAVE_TFTP - check_tftp_listeners(&rset, now); + check_tftp_listeners(now); #endif - if (FD_ISSET(fd, &rset) && + if (poll_check(fd, POLLIN) && recvfrom(fd, &packet, sizeof(packet), 0, (struct sockaddr *)&faddr, &len) == sizeof(packet) && saddr.sin_addr.s_addr == faddr.sin_addr.s_addr && @@ -1742,7 +1842,7 @@ int icmp_ping(struct in_addr addr) } #if defined(HAVE_LINUX_NETWORK) || defined(HAVE_SOLARIS_NETWORK) - close(fd); + while (retry_send(close(fd))); #else opt = 1; setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));