import of dnsmasq-2.14.tar.gz
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 1b72e16..0d25036 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2004 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
@@ -16,75 +16,18 @@
static int sigterm, sighup, sigusr1, sigalarm, num_kids, in_child;
-static void sig_handler(int sig)
-{
- if (sig == SIGTERM)
- sigterm = 1;
- else if (sig == SIGHUP)
- sighup = 1;
- else if (sig == SIGUSR1)
- sigusr1 = 1;
- else if (sig == SIGALRM)
- {
- /* alarm is used to kill children after a fixed time. */
- if (in_child)
- exit(0);
- else
- sigalarm = 1;
- }
- else if (sig == SIGCHLD)
- {
- /* See Stevens 5.10 */
- pid_t pid;
- int stat;
-
- while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
- num_kids--;
- }
-}
+static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd);
+static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now);
+static void sig_handler(int sig);
int main (int argc, char **argv)
{
- int cachesize = CACHESIZ;
- int port = NAMESERVER_PORT;
- int maxleases = MAXLEASES;
- unsigned short edns_pktsz = EDNS_PKTSZ;
- int query_port = 0;
+ struct daemon *daemon;
int first_loop = 1;
int bind_fallback = 0;
- unsigned long local_ttl = 0;
- unsigned int options, min_leasetime;
- char *runfile = RUNFILE;
time_t resolv_changed = 0;
time_t now, last = 0;
- struct irec *interfaces = NULL;
- struct listener *listener, *listeners = NULL;
- struct doctor *doctors = NULL;
- struct mx_record *mxnames = NULL;
- char *mxtarget = NULL;
- char *lease_file = NULL;
- char *addn_hosts = NULL;
- char *domain_suffix = NULL;
- char *username = CHUSER;
- char *groupname = CHGRP;
- struct iname *if_names = NULL;
- struct iname *if_addrs = NULL;
- struct iname *if_except = NULL;
- struct server *serv_addrs = NULL;
- char *dnamebuff, *packet;
- int uptime_fd = -1;
- struct server *servers, *last_server;
- struct resolvc default_resolv = { NULL, 1, 0, RESOLVFILE };
- struct resolvc *resolv = &default_resolv;
- struct bogus_addr *bogus_addr = NULL;
- struct serverfd *serverfdp, *sfds = NULL;
- struct dhcp_context *dhcp_tmp, *dhcp = NULL;
- struct dhcp_config *dhcp_configs = NULL;
- struct dhcp_opt *dhcp_options = NULL;
- struct dhcp_vendor *dhcp_vendors = NULL;
- char *dhcp_file = NULL, *dhcp_sname = NULL;
- struct in_addr dhcp_next_server;
- int leasefd = -1, dhcpfd = -1, dhcp_raw_fd = -1;
+ struct irec *interfaces;
struct sigaction sigact;
sigset_t sigmask;
@@ -121,56 +64,45 @@
sigaddset(&sigact.sa_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &sigact.sa_mask, &sigmask);
- /* These get allocated here to avoid overflowing the small stack
- on embedded systems. dnamebuff is big enough to hold one
- maximal sixed domain name and gets passed into all the processing
- code. We manage to get away with one buffer. */
- dnamebuff = safe_malloc(MAXDNAME);
+ daemon = read_opts(argc, argv);
- dhcp_next_server.s_addr = 0;
- options = read_opts(argc, argv, dnamebuff, &resolv, &mxnames, &mxtarget, &lease_file,
- &username, &groupname, &domain_suffix, &runfile,
- &if_names, &if_addrs, &if_except, &bogus_addr,
- &serv_addrs, &cachesize, &port, &query_port, &local_ttl, &addn_hosts,
- &dhcp, &dhcp_configs, &dhcp_options, &dhcp_vendors,
- &dhcp_file, &dhcp_sname, &dhcp_next_server, &maxleases, &min_leasetime,
- &doctors, &edns_pktsz);
-
- if (edns_pktsz < PACKETSZ)
- edns_pktsz = PACKETSZ;
- packet = safe_malloc(edns_pktsz > DNSMASQ_PACKETSZ ? edns_pktsz : DNSMASQ_PACKETSZ);
-
- if (!lease_file)
+ if (daemon->edns_pktsz < PACKETSZ)
+ daemon->edns_pktsz = PACKETSZ;
+ daemon->packet = safe_malloc(daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
+ daemon->edns_pktsz : DNSMASQ_PACKETSZ);
+
+ if (!daemon->lease_file)
{
- if (dhcp)
- lease_file = LEASEFILE;
+ if (daemon->dhcp)
+ daemon->lease_file = LEASEFILE;
}
#ifndef HAVE_ISC_READER
- else if (!dhcp)
+ else if (!daemon->dhcp)
die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL);
#endif
- interfaces = enumerate_interfaces(&if_names, &if_addrs, if_except, port);
-
- if (!(options & OPT_NOWILD) && !(listeners = create_wildcard_listeners(port)))
+ interfaces = enumerate_interfaces(daemon);
+
+ if (!(daemon->options & OPT_NOWILD) &&
+ !(daemon->listeners = create_wildcard_listeners(daemon->port)))
{
bind_fallback = 1;
- options |= OPT_NOWILD;
+ daemon->options |= OPT_NOWILD;
}
- if (options & OPT_NOWILD)
+ if (daemon->options & OPT_NOWILD)
{
struct iname *if_tmp;
- listeners = create_bound_listeners(interfaces, port);
+ daemon->listeners = create_bound_listeners(interfaces, daemon->port);
- for (if_tmp = if_names; if_tmp; if_tmp = if_tmp->next)
+ for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
die("unknown interface %s", if_tmp->name);
- for (if_tmp = if_addrs; if_tmp; if_tmp = if_tmp->next)
+ for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
if (!if_tmp->used)
{
- char addrbuff[ADDRSTRLEN];
+ char *addrbuff = daemon->namebuff;
#ifdef HAVE_IPV6
if (if_tmp->addr.sa.sa_family == AF_INET)
inet_ntop(AF_INET, &if_tmp->addr.in.sin_addr,
@@ -186,60 +118,62 @@
}
forward_init(1);
- cache_init(cachesize, options & OPT_LOG);
+ cache_init(daemon->cachesize, daemon->options & OPT_LOG);
#ifdef HAVE_BROKEN_RTC
- if ((uptime_fd = open(UPTIME, O_RDONLY)) == -1)
+ if ((daemon->uptime_fd = open(UPTIME, O_RDONLY)) == -1)
die("cannot open " UPTIME ":%s", NULL);
#endif
- now = dnsmasq_time(uptime_fd);
+ now = dnsmasq_time(daemon->uptime_fd);
- if (dhcp)
+ if (daemon->dhcp)
{
#if !defined(IP_PKTINFO) && !defined(IP_RECVIF)
int c;
struct iname *tmp;
- for (c = 0, tmp = if_names; tmp; tmp = tmp->next)
+ for (c = 0, tmp = daemon->if_names; tmp; tmp = tmp->next)
if (!tmp->isloop)
c++;
if (c != 1)
die("must set exactly one interface on broken systems without IP_RECVIF", NULL);
#endif
- dhcp_init(&dhcpfd, &dhcp_raw_fd, dhcp_configs);
- leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, now, maxleases);
+ dhcp_init(daemon);
+ lease_init(daemon, now);
}
/* If query_port is set then create a socket now, before dumping root
for use to access nameservers without more specific source addresses.
This allows query_port to be a low port */
- if (query_port)
+ if (daemon->query_port)
{
union mysockaddr addr;
addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr = INADDR_ANY;
- addr.in.sin_port = htons(query_port);
+ addr.in.sin_port = htons(daemon->query_port);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(struct sockaddr_in);
#endif
- allocate_sfd(&addr, &sfds);
+ allocate_sfd(&addr, &daemon->sfds);
#ifdef HAVE_IPV6
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = in6addr_any;
- addr.in6.sin6_port = htons(query_port);
+ addr.in6.sin6_port = htons(daemon->query_port);
addr.in6.sin6_flowinfo = htonl(0);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif
- allocate_sfd(&addr, &sfds);
+ allocate_sfd(&addr, &daemon->sfds);
#endif
}
setbuf(stdout, NULL);
- if (!(options & OPT_DEBUG))
+ if (!(daemon->options & OPT_DEBUG))
{
FILE *pidfile;
+ struct serverfd *serverfdp;
+ struct listener *listener;
struct passwd *ent_pw;
int i;
@@ -247,20 +181,23 @@
See Stevens section 12.4 */
#ifndef NO_FORK
- if (fork() != 0 )
- exit(0);
-
- setsid();
-
- if (fork() != 0)
- exit(0);
+ if (!(daemon->options & OPT_NO_FORK))
+ {
+ if (fork() != 0 )
+ exit(0);
+
+ setsid();
+
+ if (fork() != 0)
+ exit(0);
+ }
#endif
chdir("/");
umask(022); /* make pidfile 0644 */
/* write pidfile _after_ forking ! */
- if (runfile && (pidfile = fopen(runfile, "w")))
+ if (daemon->runfile && (pidfile = fopen(daemon->runfile, "w")))
{
fprintf(pidfile, "%d\n", (int) getpid());
fclose(pidfile);
@@ -270,23 +207,25 @@
for (i=0; i<64; i++)
{
- for (listener = listeners; listener; listener = listener->next)
- {
- if (listener->fd == i)
- break;
- if (listener->tcpfd == i)
- break;
- }
+ for (listener = daemon->listeners; listener; listener = listener->next)
+ if (listener->fd == i || listener->tcpfd == i)
+ break;
if (listener)
continue;
- if (i == leasefd ||
- i == uptime_fd ||
- i == dhcpfd ||
- i == dhcp_raw_fd)
+#ifdef HAVE_BROKEN_RTC
+ if (i == daemon->uptime_fd)
continue;
+#endif
- for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
+ if (daemon->dhcp &&
+ (i == daemon->lease_fd ||
+ i == daemon->dhcpfd ||
+ i == daemon->dhcp_raw_fd ||
+ i == daemon->dhcp_icmp_fd))
+ continue;
+
+ for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (serverfdp->fd == i)
break;
if (serverfdp)
@@ -296,7 +235,7 @@
}
/* Change uid and gid for security */
- if (username && (ent_pw = getpwnam(username)))
+ if (daemon->username && (ent_pw = getpwnam(daemon->username)))
{
gid_t dummy;
struct group *gp;
@@ -304,7 +243,7 @@
setgroups(0, &dummy);
/* change group for /etc/ppp/resolv.conf
otherwise get the group for "nobody" */
- if ((groupname && (gp = getgrnam(groupname))) ||
+ if ((daemon->groupname && (gp = getgrnam(daemon->groupname))) ||
(gp = getgrgid(ent_pw->pw_gid)))
setgid(gp->gr_gid);
/* finally drop root */
@@ -313,88 +252,91 @@
}
openlog("dnsmasq",
- DNSMASQ_LOG_OPT(options & OPT_DEBUG),
- DNSMASQ_LOG_FAC(options & OPT_DEBUG));
+ DNSMASQ_LOG_OPT(daemon->options & OPT_DEBUG),
+ DNSMASQ_LOG_FAC(daemon->options & OPT_DEBUG));
- if (cachesize != 0)
- syslog(LOG_INFO, "started, version %s cachesize %d", VERSION, cachesize);
+ if (daemon->cachesize != 0)
+ syslog(LOG_INFO, "started, version %s cachesize %d", VERSION, daemon->cachesize);
else
syslog(LOG_INFO, "started, version %s cache disabled", VERSION);
if (bind_fallback)
syslog(LOG_WARNING, "setting --bind-interfaces option because of OS limitations");
- for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
+ if (daemon->dhcp)
{
- strcpy(dnamebuff, inet_ntoa(dhcp_tmp->start));
- if (dhcp_tmp->lease_time == 0)
- sprintf(packet, "infinite");
- else
+ struct dhcp_context *dhcp_tmp;
+ for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
{
- unsigned int x, p = 0, t = (unsigned int)dhcp_tmp->lease_time;
- if ((x = t/3600))
- p += sprintf(&packet[p], "%dh", x);
- if ((x = (t/60)%60))
- p += sprintf(&packet[p], "%dm", x);
- if ((x = t%60))
- p += sprintf(&packet[p], "%ds", x);
+ char *time = daemon->dhcp_buff2;
+ strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
+ if (dhcp_tmp->lease_time == 0)
+ sprintf(time, "infinite");
+ else
+ {
+ unsigned int x, p = 0, t = (unsigned int)dhcp_tmp->lease_time;
+ if ((x = t/3600))
+ p += sprintf(&time[p], "%dh", x);
+ if ((x = (t/60)%60))
+ p += sprintf(&time[p], "%dm", x);
+ if ((x = t%60))
+ p += sprintf(&time[p], "%ds", x);
+ }
+ syslog(LOG_INFO,
+ dhcp_tmp->static_only ?
+ "DHCP, static leases only on %.0s%s, lease time %s" :
+ "DHCP, IP range %s -- %s, lease time %s",
+ daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), time);
}
- syslog(LOG_INFO,
- dhcp_tmp->start.s_addr == dhcp_tmp->end.s_addr ?
- "DHCP, static leases only on %.0s%s, lease time %s" :
- "DHCP, IP range %s -- %s, lease time %s",
- dnamebuff, inet_ntoa(dhcp_tmp->end), packet);
}
#ifdef HAVE_BROKEN_RTC
- if (dhcp)
- syslog(LOG_INFO, "DHCP, %s will be written every %ds", lease_file, min_leasetime/3);
+ if (daemon->dhcp)
+ syslog(LOG_INFO, "DHCP, %s will be written every %ds", daemon->lease_file, daemon->min_leasetime/3);
#endif
- if (!(options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
+ if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
syslog(LOG_WARNING, "running as root");
- servers = check_servers(serv_addrs, interfaces, &sfds);
- last_server = NULL;
-
+ check_servers(daemon, interfaces);
+
while (sigterm == 0)
{
fd_set rset;
if (sighup)
{
- cache_reload(options, dnamebuff, domain_suffix, addn_hosts);
- if (dhcp)
+ cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
+ if (daemon->dhcp)
{
- if (options & OPT_ETHERS)
- dhcp_configs = dhcp_read_ethers(dhcp_configs, dnamebuff);
- dhcp_update_configs(dhcp_configs);
- lease_update_from_configs(dhcp_configs, domain_suffix);
+ if (daemon->options & OPT_ETHERS)
+ dhcp_read_ethers(daemon);
+ dhcp_update_configs(daemon->dhcp_conf);
+ lease_update_from_configs(daemon->dhcp_conf, daemon->domain_suffix);
lease_update_file(0, now);
lease_update_dns();
}
- if (resolv && (options & OPT_NO_POLL))
+ if (daemon->resolv_files && (daemon->options & OPT_NO_POLL))
{
- servers = check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port),
- interfaces, &sfds);
- last_server = NULL;
+ reload_servers(daemon->resolv_files->name, daemon);
+ check_servers(daemon, interfaces);
}
sighup = 0;
}
if (sigusr1)
{
- dump_cache(options & (OPT_DEBUG | OPT_LOG), cachesize);
+ dump_cache(daemon->options & (OPT_DEBUG | OPT_LOG), daemon->cachesize);
sigusr1 = 0;
}
if (sigalarm)
{
- if (dhcp)
+ if (daemon->dhcp)
{
lease_update_file(1, now);
#ifdef HAVE_BROKEN_RTC
- alarm(min_leasetime/3);
+ alarm(daemon->min_leasetime/3);
#endif
}
sigalarm = 0;
@@ -404,30 +346,13 @@
if (!first_loop)
{
- int maxfd = 0;
-
- for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
+ int maxfd = set_dns_listeners(daemon, &rset, 0);
+
+ if (daemon->dhcp)
{
- FD_SET(serverfdp->fd, &rset);
- if (serverfdp->fd > maxfd)
- maxfd = serverfdp->fd;
- }
-
- for (listener = listeners; listener; listener = listener->next)
- {
- FD_SET(listener->fd, &rset);
- if (listener->fd > maxfd)
- maxfd = listener->fd;
- FD_SET(listener->tcpfd, &rset);
- if (listener->tcpfd > maxfd)
- maxfd = listener->tcpfd;
- }
-
- if (dhcp)
- {
- FD_SET(dhcpfd, &rset);
- if (dhcpfd > maxfd)
- maxfd = dhcpfd;
+ FD_SET(daemon->dhcpfd, &rset);
+ if (daemon->dhcpfd > maxfd)
+ maxfd = daemon->dhcpfd;
}
#ifdef HAVE_PSELECT
@@ -442,11 +367,10 @@
sigprocmask(SIG_SETMASK, &save_mask, NULL);
}
#endif
-
}
first_loop = 0;
- now = dnsmasq_time(uptime_fd);
+ now = dnsmasq_time(daemon->uptime_fd);
/* Check for changes to resolv files once per second max. */
if (last == 0 || difftime(now, last) > 1.0)
@@ -454,13 +378,13 @@
last = now;
#ifdef HAVE_ISC_READER
- if (lease_file && !dhcp)
- load_dhcp(lease_file, domain_suffix, now, dnamebuff);
+ if (daemon->lease_file && !daemon->dhcp)
+ load_dhcp(daemon->lease_file, daemon->domain_suffix, now, daemon->namebuff);
#endif
- if (!(options & OPT_NO_POLL))
+ if (!(daemon->options & OPT_NO_POLL))
{
- struct resolvc *res = resolv, *latest = NULL;
+ struct resolvc *res = daemon->resolv_files, *latest = NULL;
struct stat statbuf;
time_t last_change = 0;
/* There may be more than one possible file.
@@ -489,141 +413,268 @@
if (latest && difftime(last_change, resolv_changed) > 0.0)
{
resolv_changed = last_change;
- servers = check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
- interfaces, &sfds);
- last_server = NULL;
+ reload_servers(latest->name, daemon);
+ check_servers(daemon, interfaces);
}
}
}
- for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
- if (FD_ISSET(serverfdp->fd, &rset))
- last_server = reply_query(serverfdp, options, packet, now,
- dnamebuff, servers, last_server,
- bogus_addr, doctors, edns_pktsz);
-
- if (dhcp && FD_ISSET(dhcpfd, &rset))
- dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs, dhcp_vendors,
- now, dnamebuff, domain_suffix, dhcp_file,
- dhcp_sname, dhcp_next_server, dhcpfd, dhcp_raw_fd,
- if_names, if_addrs, if_except);
+ check_dns_listeners(daemon, &rset, now);
- for (listener = listeners; listener; listener = listener->next)
- {
- if (FD_ISSET(listener->fd, &rset))
- last_server = receive_query(listener, packet,
- mxnames, mxtarget, options, now, local_ttl, dnamebuff,
- if_names, if_addrs, if_except, last_server, servers, edns_pktsz);
-
- if (FD_ISSET(listener->tcpfd, &rset))
- {
- int confd;
-
- while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
-
- if (confd != -1)
- {
- int match = 1;
- if (!(options & OPT_NOWILD))
- {
- /* Check for allowed interfaces when binding the wildcard address */
- /* Don't know how to get interface of a connection, so we have to
- check by address. This will break when interfaces change address */
- union mysockaddr tcp_addr;
- socklen_t tcp_len = sizeof(union mysockaddr);
- struct iname *tmp;
-
- if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
- {
-#ifdef HAVE_IPV6
- if (tcp_addr.sa.sa_family == AF_INET6)
- tcp_addr.in6.sin6_flowinfo = htonl(0);
-#endif
- for (match = 1, tmp = if_except; tmp; tmp = tmp->next)
- if (sockaddr_isequal(&tmp->addr, &tcp_addr))
- match = 0;
-
- if (match && (if_names || if_addrs))
- {
- match = 0;
- for (tmp = if_names; tmp; tmp = tmp->next)
- if (sockaddr_isequal(&tmp->addr, &tcp_addr))
- match = 1;
- for (tmp = if_addrs; tmp; tmp = tmp->next)
- if (sockaddr_isequal(&tmp->addr, &tcp_addr))
- match = 1;
- }
- }
- }
-
- if (!match || (num_kids >= MAX_PROCS))
- close(confd);
- else if (!(options & OPT_DEBUG) && fork())
- {
- num_kids++;
- close(confd);
- }
- else
- {
- char *buff;
- struct server *s;
- int flags;
-
- /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
- terminate the process. */
- if (!(options & OPT_DEBUG))
- {
- sigemptyset(&sigact.sa_mask);
- sigaddset(&sigact.sa_mask, SIGALRM);
- sigprocmask(SIG_UNBLOCK, &sigact.sa_mask, NULL);
- alarm(CHILD_LIFETIME);
- in_child = 1;
- }
-
- /* start with no upstream connections. */
- for (s = servers; s; s = s->next)
- s->tcpfd = -1;
-
- /* The connected socket inherits non-blocking
- attribute from the listening socket.
- Reset that here. */
- if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
- fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
-
- buff = tcp_request(confd, mxnames, mxtarget, options, now,
- local_ttl, dnamebuff, last_server, servers,
- bogus_addr, doctors, edns_pktsz);
-
-
- if (!(options & OPT_DEBUG))
- exit(0);
-
- close(confd);
- if (buff)
- free(buff);
- for (s = servers; s; s = s->next)
- if (s->tcpfd != -1)
- close(s->tcpfd);
- }
- }
- }
- }
+ if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
+ dhcp_packet(daemon, now);
}
syslog(LOG_INFO, "exiting on receipt of SIGTERM");
+ if (daemon->dhcp)
+ {
#ifdef HAVE_BROKEN_RTC
- if (dhcp)
- lease_update_file(1, now);
+ lease_update_file(1, now);
#endif
-
- if (leasefd != -1)
- close(leasefd);
+ close(daemon->lease_fd);
+ }
+
return 0;
}
+static void sig_handler(int sig)
+{
+ if (sig == SIGTERM)
+ sigterm = 1;
+ else if (sig == SIGHUP)
+ sighup = 1;
+ else if (sig == SIGUSR1)
+ sigusr1 = 1;
+ else if (sig == SIGALRM)
+ {
+ /* alarm is used to kill children after a fixed time. */
+ if (in_child)
+ exit(0);
+ else
+ sigalarm = 1;
+ }
+ else if (sig == SIGCHLD)
+ {
+ /* See Stevens 5.10 */
+
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ num_kids--;
+ }
+}
+static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd)
+{
+ struct serverfd *serverfdp;
+ struct listener *listener;
+ for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
+ {
+ FD_SET(serverfdp->fd, set);
+ if (serverfdp->fd > maxfd)
+ maxfd = serverfdp->fd;
+ }
+
+ for (listener = daemon->listeners; listener; listener = listener->next)
+ {
+ FD_SET(listener->fd, set);
+ if (listener->fd > maxfd)
+ maxfd = listener->fd;
+ FD_SET(listener->tcpfd, set);
+ if (listener->tcpfd > maxfd)
+ maxfd = listener->tcpfd;
+ }
+ return maxfd;
+}
+static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
+{
+ struct serverfd *serverfdp;
+ struct listener *listener;
+ for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
+ if (FD_ISSET(serverfdp->fd, set))
+ reply_query(serverfdp, daemon, now);
+
+ for (listener = daemon->listeners; listener; listener = listener->next)
+ {
+ if (FD_ISSET(listener->fd, set))
+ receive_query(listener, daemon, now);
+
+ if (FD_ISSET(listener->tcpfd, set))
+ {
+ int confd;
+
+ while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
+
+ if (confd != -1)
+ {
+ int match = 1;
+ if (!(daemon->options & OPT_NOWILD))
+ {
+ /* Check for allowed interfaces when binding the wildcard address */
+ /* Don't know how to get interface of a connection, so we have to
+ check by address. This will break when interfaces change address */
+ union mysockaddr tcp_addr;
+ socklen_t tcp_len = sizeof(union mysockaddr);
+ struct iname *tmp;
+
+ if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
+ {
+#ifdef HAVE_IPV6
+ if (tcp_addr.sa.sa_family == AF_INET6)
+ tcp_addr.in6.sin6_flowinfo = htonl(0);
+#endif
+ for (match = 1, tmp = daemon->if_except; tmp; tmp = tmp->next)
+ if (sockaddr_isequal(&tmp->addr, &tcp_addr))
+ match = 0;
+
+ if (match && (daemon->if_names || daemon->if_addrs))
+ {
+ match = 0;
+ for (tmp = daemon->if_names; tmp; tmp = tmp->next)
+ if (sockaddr_isequal(&tmp->addr, &tcp_addr))
+ match = 1;
+ for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
+ if (sockaddr_isequal(&tmp->addr, &tcp_addr))
+ match = 1;
+ }
+ }
+ }
+
+ if (!match || (num_kids >= MAX_PROCS))
+ close(confd);
+ else if (!(daemon->options & OPT_DEBUG) && fork())
+ {
+ num_kids++;
+ close(confd);
+ }
+ else
+ {
+ char *buff;
+ struct server *s;
+ int flags;
+
+ /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
+ terminate the process. */
+ if (!(daemon->options & OPT_DEBUG))
+ {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ alarm(CHILD_LIFETIME);
+ in_child = 1;
+ }
+
+ /* start with no upstream connections. */
+ for (s = daemon->servers; s; s = s->next)
+ s->tcpfd = -1;
+
+ /* The connected socket inherits non-blocking
+ attribute from the listening socket.
+ Reset that here. */
+ if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
+ fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
+
+ buff = tcp_request(daemon, confd, now);
+
+ if (!(daemon->options & OPT_DEBUG))
+ exit(0);
+
+ close(confd);
+ if (buff)
+ free(buff);
+ for (s = daemon->servers; s; s = s->next)
+ if (s->tcpfd != -1)
+ close(s->tcpfd);
+ }
+ }
+ }
+ }
+}
+
+int icmp_ping(struct daemon *daemon, struct in_addr addr)
+{
+ /* Try and get an ICMP echo from a machine.
+ Note that we can't create the raw socket each time
+ we do this, since that needs root. Therefore the socket has to hang
+ around all the time. Since most of the time we won't read the
+ socket, it will accumulate buffers full of ICMP messages,
+ wasting memory. To avoid that we set the receive buffer
+ length to zero except when we're actively pinging. */
+
+ /* Note that whilst in the three second wait, we check for
+ (and service) events on the DNS sockets, (so doing that
+ better not use any resources our caller has in use...)
+ but we remain deaf to signals or further DHCP packets. */
+
+ struct sockaddr_in saddr;
+ struct {
+ struct ip ip;
+ struct icmp icmp;
+ } packet;
+ unsigned short id = rand16();
+ unsigned int i, j;
+ int opt = 2000, gotreply = 0;
+ time_t start, now;
+
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = 0;
+ saddr.sin_addr = addr;
+#ifdef HAVE_SOCKADDR_SA_LEN
+ saddr.sin_len = sizeof(struct sockaddr_in);
+#endif
+
+ memset(&packet.icmp, 0, sizeof(packet.icmp));
+ packet.icmp.icmp_type = ICMP_ECHO;
+ packet.icmp.icmp_id = id;
+ for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++)
+ j += ((u16 *)&packet.icmp)[i];
+ while (j>>16)
+ j = (j & 0xffff) + (j >> 16);
+ packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
+
+ setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+
+ if (sendto(daemon->dhcp_icmp_fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
+ (struct sockaddr *)&saddr, sizeof(saddr)) == sizeof(struct icmp))
+ for (now = start = dnsmasq_time(daemon->uptime_fd); difftime(now, start) < 3.0;)
+ {
+ struct timeval tv;
+ fd_set rset;
+ struct sockaddr_in faddr;
+ int maxfd, len = sizeof(faddr);
+
+ tv.tv_usec = 250000;
+ tv.tv_sec = 0;
+
+ FD_ZERO(&rset);
+ FD_SET(daemon->dhcp_icmp_fd, &rset);
+ maxfd = set_dns_listeners(daemon, &rset, daemon->dhcp_icmp_fd);
+
+ if (select(maxfd+1, &rset, NULL, NULL, &tv) < 0)
+ FD_ZERO(&rset);
+
+ now = dnsmasq_time(daemon->uptime_fd);
+ check_dns_listeners(daemon, &rset, now);
+
+ if (FD_ISSET(daemon->dhcp_icmp_fd, &rset) &&
+ recvfrom(daemon->dhcp_icmp_fd, &packet, sizeof(packet), 0,
+ (struct sockaddr *)&faddr, &len) == sizeof(packet) &&
+ saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
+ packet.icmp.icmp_type == ICMP_ECHOREPLY &&
+ packet.icmp.icmp_seq == 0 &&
+ packet.icmp.icmp_id == id)
+ {
+ gotreply = 1;
+ break;
+ }
+ }
+
+ opt = 1;
+ setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+
+ return gotreply;
+}