blob: 7333bde1aa83fcf8857a89cb3f6369bcf22b3f99 [file] [log] [blame]
Simon Kelley3be34542004-09-11 19:12:13 +01001/* dnsmasq is Copyright (c) 2000-2004 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
Simon Kelley9e4abcb2004-01-22 19:47:41 +000013/* Author's email: simon@thekelleys.org.uk */
14
15#include "dnsmasq.h"
16
Simon Kelleyfeba5c12004-07-27 20:28:58 +010017static int sigterm, sighup, sigusr1, sigalarm, num_kids, in_child;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000018
Simon Kelley3be34542004-09-11 19:12:13 +010019static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd);
20static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now);
21static void sig_handler(int sig);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000022
23int main (int argc, char **argv)
24{
Simon Kelley3be34542004-09-11 19:12:13 +010025 struct daemon *daemon;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000026 int first_loop = 1;
Simon Kelleyde379512004-06-22 20:23:33 +010027 int bind_fallback = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000028 time_t resolv_changed = 0;
29 time_t now, last = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010030 struct irec *interfaces;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000031 struct sigaction sigact;
32 sigset_t sigmask;
Simon Kelley26128d22004-11-14 16:43:54 +000033 struct iname *if_tmp;
Simon Kelley33820b72004-04-03 21:10:00 +010034
Simon Kelley9e4abcb2004-01-22 19:47:41 +000035 sighup = 1; /* init cache the first time through */
36 sigusr1 = 0; /* but don't dump */
Simon Kelley9e4abcb2004-01-22 19:47:41 +000037 sigterm = 0; /* or die */
Simon Kelley44a2a312004-03-10 20:04:35 +000038#ifdef HAVE_BROKEN_RTC
39 sigalarm = 1; /* need regular lease dumps */
40#else
41 sigalarm = 0; /* or not */
42#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +010043 num_kids = 0;
44 in_child = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000045
46 sigact.sa_handler = sig_handler;
47 sigact.sa_flags = 0;
48 sigemptyset(&sigact.sa_mask);
49 sigaction(SIGUSR1, &sigact, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000050 sigaction(SIGHUP, &sigact, NULL);
51 sigaction(SIGTERM, &sigact, NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000052 sigaction(SIGALRM, &sigact, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +010053 sigaction(SIGCHLD, &sigact, NULL);
54
55 /* ignore SIGPIPE */
56 sigact.sa_handler = SIG_IGN;
57 sigaction(SIGPIPE, &sigact, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000058
59 /* now block all the signals, they stay that way except
60 during the call to pselect */
61 sigaddset(&sigact.sa_mask, SIGUSR1);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000062 sigaddset(&sigact.sa_mask, SIGTERM);
63 sigaddset(&sigact.sa_mask, SIGHUP);
Simon Kelley44a2a312004-03-10 20:04:35 +000064 sigaddset(&sigact.sa_mask, SIGALRM);
Simon Kelleyfeba5c12004-07-27 20:28:58 +010065 sigaddset(&sigact.sa_mask, SIGCHLD);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000066 sigprocmask(SIG_BLOCK, &sigact.sa_mask, &sigmask);
67
Simon Kelley3be34542004-09-11 19:12:13 +010068 daemon = read_opts(argc, argv);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000069
Simon Kelley3be34542004-09-11 19:12:13 +010070 if (daemon->edns_pktsz < PACKETSZ)
71 daemon->edns_pktsz = PACKETSZ;
72 daemon->packet = safe_malloc(daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
73 daemon->edns_pktsz : DNSMASQ_PACKETSZ);
74
75 if (!daemon->lease_file)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000076 {
Simon Kelley3be34542004-09-11 19:12:13 +010077 if (daemon->dhcp)
78 daemon->lease_file = LEASEFILE;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000079 }
Simon Kelley33820b72004-04-03 21:10:00 +010080#ifndef HAVE_ISC_READER
Simon Kelley3be34542004-09-11 19:12:13 +010081 else if (!daemon->dhcp)
Simon Kelley33820b72004-04-03 21:10:00 +010082 die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL);
83#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000084
Simon Kelley3be34542004-09-11 19:12:13 +010085 interfaces = enumerate_interfaces(daemon);
86
87 if (!(daemon->options & OPT_NOWILD) &&
88 !(daemon->listeners = create_wildcard_listeners(daemon->port)))
Simon Kelleyde379512004-06-22 20:23:33 +010089 {
90 bind_fallback = 1;
Simon Kelley3be34542004-09-11 19:12:13 +010091 daemon->options |= OPT_NOWILD;
Simon Kelleyde379512004-06-22 20:23:33 +010092 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +000093
Simon Kelley3be34542004-09-11 19:12:13 +010094 if (daemon->options & OPT_NOWILD)
Simon Kelleyde379512004-06-22 20:23:33 +010095 {
Simon Kelley3be34542004-09-11 19:12:13 +010096 daemon->listeners = create_bound_listeners(interfaces, daemon->port);
Simon Kelleyde379512004-06-22 20:23:33 +010097
Simon Kelley3be34542004-09-11 19:12:13 +010098 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
Simon Kelleyde379512004-06-22 20:23:33 +010099 if (if_tmp->name && !if_tmp->used)
100 die("unknown interface %s", if_tmp->name);
101
Simon Kelley3be34542004-09-11 19:12:13 +0100102 for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
Simon Kelleyde379512004-06-22 20:23:33 +0100103 if (!if_tmp->used)
104 {
Simon Kelley3be34542004-09-11 19:12:13 +0100105 char *addrbuff = daemon->namebuff;
Simon Kelleyde379512004-06-22 20:23:33 +0100106#ifdef HAVE_IPV6
107 if (if_tmp->addr.sa.sa_family == AF_INET)
108 inet_ntop(AF_INET, &if_tmp->addr.in.sin_addr,
109 addrbuff, ADDRSTRLEN);
110 else
111 inet_ntop(AF_INET6, &if_tmp->addr.in6.sin6_addr,
112 addrbuff, ADDRSTRLEN);
113#else
114 strcpy(addrbuff, inet_ntoa(if_tmp->addr.in.sin_addr));
115#endif
116 die("no interface with address %s", addrbuff);
117 }
118 }
119
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000120 forward_init(1);
Simon Kelley3be34542004-09-11 19:12:13 +0100121 cache_init(daemon->cachesize, daemon->options & OPT_LOG);
Simon Kelley44a2a312004-03-10 20:04:35 +0000122
123#ifdef HAVE_BROKEN_RTC
Simon Kelley3be34542004-09-11 19:12:13 +0100124 if ((daemon->uptime_fd = open(UPTIME, O_RDONLY)) == -1)
Simon Kelley44a2a312004-03-10 20:04:35 +0000125 die("cannot open " UPTIME ":%s", NULL);
126#endif
127
Simon Kelley3be34542004-09-11 19:12:13 +0100128 now = dnsmasq_time(daemon->uptime_fd);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000129
Simon Kelley3be34542004-09-11 19:12:13 +0100130 if (daemon->dhcp)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000131 {
Simon Kelleyde379512004-06-22 20:23:33 +0100132#if !defined(IP_PKTINFO) && !defined(IP_RECVIF)
133 int c;
134 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100135 for (c = 0, tmp = daemon->if_names; tmp; tmp = tmp->next)
Simon Kelleyde379512004-06-22 20:23:33 +0100136 if (!tmp->isloop)
137 c++;
138 if (c != 1)
139 die("must set exactly one interface on broken systems without IP_RECVIF", NULL);
140#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100141 dhcp_init(daemon);
142 lease_init(daemon, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000143 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100144
145 /* If query_port is set then create a socket now, before dumping root
146 for use to access nameservers without more specific source addresses.
147 This allows query_port to be a low port */
Simon Kelley3be34542004-09-11 19:12:13 +0100148 if (daemon->query_port)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100149 {
150 union mysockaddr addr;
151 addr.in.sin_family = AF_INET;
152 addr.in.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +0100153 addr.in.sin_port = htons(daemon->query_port);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100154#ifdef HAVE_SOCKADDR_SA_LEN
155 addr.in.sin_len = sizeof(struct sockaddr_in);
156#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100157 allocate_sfd(&addr, &daemon->sfds);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100158#ifdef HAVE_IPV6
159 addr.in6.sin6_family = AF_INET6;
160 addr.in6.sin6_addr = in6addr_any;
Simon Kelley3be34542004-09-11 19:12:13 +0100161 addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100162 addr.in6.sin6_flowinfo = htonl(0);
163#ifdef HAVE_SOCKADDR_SA_LEN
164 addr.in6.sin6_len = sizeof(struct sockaddr_in6);
165#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100166 allocate_sfd(&addr, &daemon->sfds);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100167#endif
168 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000169
170 setbuf(stdout, NULL);
171
Simon Kelley3be34542004-09-11 19:12:13 +0100172 if (!(daemon->options & OPT_DEBUG))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000173 {
174 FILE *pidfile;
Simon Kelley3be34542004-09-11 19:12:13 +0100175 struct serverfd *serverfdp;
176 struct listener *listener;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000177 struct passwd *ent_pw;
178 int i;
179
180 /* The following code "daemonizes" the process.
181 See Stevens section 12.4 */
182
183#ifndef NO_FORK
Simon Kelley3be34542004-09-11 19:12:13 +0100184 if (!(daemon->options & OPT_NO_FORK))
185 {
186 if (fork() != 0 )
187 exit(0);
188
189 setsid();
190
191 if (fork() != 0)
192 exit(0);
193 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000194#endif
195
196 chdir("/");
197 umask(022); /* make pidfile 0644 */
198
199 /* write pidfile _after_ forking ! */
Simon Kelley3be34542004-09-11 19:12:13 +0100200 if (daemon->runfile && (pidfile = fopen(daemon->runfile, "w")))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000201 {
202 fprintf(pidfile, "%d\n", (int) getpid());
203 fclose(pidfile);
204 }
205
206 umask(0);
207
208 for (i=0; i<64; i++)
209 {
Simon Kelley3be34542004-09-11 19:12:13 +0100210 for (listener = daemon->listeners; listener; listener = listener->next)
211 if (listener->fd == i || listener->tcpfd == i)
212 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000213 if (listener)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000214 continue;
215
Simon Kelley3be34542004-09-11 19:12:13 +0100216#ifdef HAVE_BROKEN_RTC
217 if (i == daemon->uptime_fd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000218 continue;
Simon Kelley3be34542004-09-11 19:12:13 +0100219#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000220
Simon Kelley3be34542004-09-11 19:12:13 +0100221 if (daemon->dhcp &&
222 (i == daemon->lease_fd ||
223 i == daemon->dhcpfd ||
224 i == daemon->dhcp_raw_fd ||
225 i == daemon->dhcp_icmp_fd))
226 continue;
227
228 for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100229 if (serverfdp->fd == i)
230 break;
231 if (serverfdp)
232 continue;
233
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000234 close(i);
235 }
236
237 /* Change uid and gid for security */
Simon Kelley3be34542004-09-11 19:12:13 +0100238 if (daemon->username && (ent_pw = getpwnam(daemon->username)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000239 {
240 gid_t dummy;
241 struct group *gp;
242 /* remove all supplimentary groups */
243 setgroups(0, &dummy);
244 /* change group for /etc/ppp/resolv.conf
245 otherwise get the group for "nobody" */
Simon Kelley3be34542004-09-11 19:12:13 +0100246 if ((daemon->groupname && (gp = getgrnam(daemon->groupname))) ||
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000247 (gp = getgrgid(ent_pw->pw_gid)))
248 setgid(gp->gr_gid);
249 /* finally drop root */
250 setuid(ent_pw->pw_uid);
251 }
252 }
253
254 openlog("dnsmasq",
Simon Kelley3be34542004-09-11 19:12:13 +0100255 DNSMASQ_LOG_OPT(daemon->options & OPT_DEBUG),
256 DNSMASQ_LOG_FAC(daemon->options & OPT_DEBUG));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000257
Simon Kelley3be34542004-09-11 19:12:13 +0100258 if (daemon->cachesize != 0)
259 syslog(LOG_INFO, "started, version %s cachesize %d", VERSION, daemon->cachesize);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000260 else
261 syslog(LOG_INFO, "started, version %s cache disabled", VERSION);
262
Simon Kelleyde379512004-06-22 20:23:33 +0100263 if (bind_fallback)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100264 syslog(LOG_WARNING, "setting --bind-interfaces option because of OS limitations");
Simon Kelleyde379512004-06-22 20:23:33 +0100265
Simon Kelley26128d22004-11-14 16:43:54 +0000266 if (!(daemon->options & OPT_NOWILD))
267 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
268 if (if_tmp->name && !if_tmp->used)
269 syslog(LOG_WARNING, "warning: interface %s does not currently exist", if_tmp->name);
270
Simon Kelley3be34542004-09-11 19:12:13 +0100271 if (daemon->dhcp)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000272 {
Simon Kelley3be34542004-09-11 19:12:13 +0100273 struct dhcp_context *dhcp_tmp;
274 for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100275 {
Simon Kelley3be34542004-09-11 19:12:13 +0100276 char *time = daemon->dhcp_buff2;
277 strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
278 if (dhcp_tmp->lease_time == 0)
279 sprintf(time, "infinite");
280 else
281 {
282 unsigned int x, p = 0, t = (unsigned int)dhcp_tmp->lease_time;
283 if ((x = t/3600))
284 p += sprintf(&time[p], "%dh", x);
285 if ((x = (t/60)%60))
286 p += sprintf(&time[p], "%dm", x);
287 if ((x = t%60))
288 p += sprintf(&time[p], "%ds", x);
289 }
290 syslog(LOG_INFO,
291 dhcp_tmp->static_only ?
292 "DHCP, static leases only on %.0s%s, lease time %s" :
293 "DHCP, IP range %s -- %s, lease time %s",
294 daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), time);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100295 }
Simon Kelley26128d22004-11-14 16:43:54 +0000296
297#ifdef HAVE_BROKEN_RTC
298 syslog(LOG_INFO, "DHCP, %s will be written every %ds", daemon->lease_file, daemon->min_leasetime/3);
299#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000300 }
301
Simon Kelley3be34542004-09-11 19:12:13 +0100302 if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100303 syslog(LOG_WARNING, "running as root");
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000304
Simon Kelley3be34542004-09-11 19:12:13 +0100305 check_servers(daemon, interfaces);
306
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000307 while (sigterm == 0)
308 {
309 fd_set rset;
310
311 if (sighup)
312 {
Simon Kelley3be34542004-09-11 19:12:13 +0100313 cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
314 if (daemon->dhcp)
Simon Kelley44a2a312004-03-10 20:04:35 +0000315 {
Simon Kelley3be34542004-09-11 19:12:13 +0100316 if (daemon->options & OPT_ETHERS)
317 dhcp_read_ethers(daemon);
318 dhcp_update_configs(daemon->dhcp_conf);
319 lease_update_from_configs(daemon->dhcp_conf, daemon->domain_suffix);
Simon Kelley44a2a312004-03-10 20:04:35 +0000320 lease_update_file(0, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100321 lease_update_dns(daemon);
Simon Kelley44a2a312004-03-10 20:04:35 +0000322 }
Simon Kelley3be34542004-09-11 19:12:13 +0100323 if (daemon->resolv_files && (daemon->options & OPT_NO_POLL))
Simon Kelleyde379512004-06-22 20:23:33 +0100324 {
Simon Kelley3be34542004-09-11 19:12:13 +0100325 reload_servers(daemon->resolv_files->name, daemon);
326 check_servers(daemon, interfaces);
Simon Kelleyde379512004-06-22 20:23:33 +0100327 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000328 sighup = 0;
329 }
330
331 if (sigusr1)
332 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100333 dump_cache(daemon);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000334 sigusr1 = 0;
335 }
336
Simon Kelley44a2a312004-03-10 20:04:35 +0000337 if (sigalarm)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000338 {
Simon Kelley3be34542004-09-11 19:12:13 +0100339 if (daemon->dhcp)
Simon Kelley44a2a312004-03-10 20:04:35 +0000340 {
341 lease_update_file(1, now);
342#ifdef HAVE_BROKEN_RTC
Simon Kelley3be34542004-09-11 19:12:13 +0100343 alarm(daemon->min_leasetime/3);
Simon Kelley44a2a312004-03-10 20:04:35 +0000344#endif
345 }
346 sigalarm = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000347 }
348
349 FD_ZERO(&rset);
350
351 if (!first_loop)
352 {
Simon Kelley3be34542004-09-11 19:12:13 +0100353 int maxfd = set_dns_listeners(daemon, &rset, 0);
354
355 if (daemon->dhcp)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000356 {
Simon Kelley3be34542004-09-11 19:12:13 +0100357 FD_SET(daemon->dhcpfd, &rset);
358 if (daemon->dhcpfd > maxfd)
359 maxfd = daemon->dhcpfd;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000360 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000361
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000362#ifdef HAVE_PSELECT
363 if (pselect(maxfd+1, &rset, NULL, NULL, NULL, &sigmask) < 0)
364 FD_ZERO(&rset); /* rset otherwise undefined after error */
365#else
366 {
367 sigset_t save_mask;
368 sigprocmask(SIG_SETMASK, &sigmask, &save_mask);
369 if (select(maxfd+1, &rset, NULL, NULL, NULL) < 0)
370 FD_ZERO(&rset); /* rset otherwise undefined after error */
371 sigprocmask(SIG_SETMASK, &save_mask, NULL);
372 }
373#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000374 }
375
376 first_loop = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100377 now = dnsmasq_time(daemon->uptime_fd);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000378
379 /* Check for changes to resolv files once per second max. */
380 if (last == 0 || difftime(now, last) > 1.0)
381 {
382 last = now;
Simon Kelley33820b72004-04-03 21:10:00 +0100383
384#ifdef HAVE_ISC_READER
Simon Kelley3be34542004-09-11 19:12:13 +0100385 if (daemon->lease_file && !daemon->dhcp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100386 load_dhcp(daemon, now);
Simon Kelley33820b72004-04-03 21:10:00 +0100387#endif
388
Simon Kelley3be34542004-09-11 19:12:13 +0100389 if (!(daemon->options & OPT_NO_POLL))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000390 {
Simon Kelley3be34542004-09-11 19:12:13 +0100391 struct resolvc *res = daemon->resolv_files, *latest = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000392 struct stat statbuf;
Simon Kelley33820b72004-04-03 21:10:00 +0100393 time_t last_change = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000394 /* There may be more than one possible file.
395 Go through and find the one which changed _last_.
396 Warn of any which can't be read. */
397 while (res)
398 {
399 if (stat(res->name, &statbuf) == -1)
400 {
401 if (!res->logged)
402 syslog(LOG_WARNING, "failed to access %s: %m", res->name);
403 res->logged = 1;
404 }
405 else
406 {
407 res->logged = 0;
Simon Kelleyde379512004-06-22 20:23:33 +0100408 if (difftime(statbuf.st_mtime, last_change) > 0.0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000409 {
410 last_change = statbuf.st_mtime;
411 latest = res;
412 }
413 }
414 res = res->next;
415 }
416
Simon Kelleyde379512004-06-22 20:23:33 +0100417 if (latest && difftime(last_change, resolv_changed) > 0.0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000418 {
419 resolv_changed = last_change;
Simon Kelley3be34542004-09-11 19:12:13 +0100420 reload_servers(latest->name, daemon);
421 check_servers(daemon, interfaces);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000422 }
423 }
424 }
425
Simon Kelley3be34542004-09-11 19:12:13 +0100426 check_dns_listeners(daemon, &rset, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000427
Simon Kelley3be34542004-09-11 19:12:13 +0100428 if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
429 dhcp_packet(daemon, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000430 }
431
432 syslog(LOG_INFO, "exiting on receipt of SIGTERM");
Simon Kelley44a2a312004-03-10 20:04:35 +0000433
Simon Kelley3be34542004-09-11 19:12:13 +0100434 if (daemon->dhcp)
435 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000436#ifdef HAVE_BROKEN_RTC
Simon Kelley3be34542004-09-11 19:12:13 +0100437 lease_update_file(1, now);
Simon Kelley44a2a312004-03-10 20:04:35 +0000438#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100439 close(daemon->lease_fd);
440 }
441
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000442 return 0;
443}
444
Simon Kelley3be34542004-09-11 19:12:13 +0100445static void sig_handler(int sig)
446{
447 if (sig == SIGTERM)
448 sigterm = 1;
449 else if (sig == SIGHUP)
450 sighup = 1;
451 else if (sig == SIGUSR1)
452 sigusr1 = 1;
453 else if (sig == SIGALRM)
454 {
455 /* alarm is used to kill children after a fixed time. */
456 if (in_child)
457 exit(0);
458 else
459 sigalarm = 1;
460 }
461 else if (sig == SIGCHLD)
462 {
463 /* See Stevens 5.10 */
464
465 while (waitpid(-1, NULL, WNOHANG) > 0)
466 num_kids--;
467 }
468}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000469
Simon Kelley3be34542004-09-11 19:12:13 +0100470static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd)
471{
472 struct serverfd *serverfdp;
473 struct listener *listener;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000474
Simon Kelley3be34542004-09-11 19:12:13 +0100475 for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
476 {
477 FD_SET(serverfdp->fd, set);
478 if (serverfdp->fd > maxfd)
479 maxfd = serverfdp->fd;
480 }
481
482 for (listener = daemon->listeners; listener; listener = listener->next)
483 {
484 FD_SET(listener->fd, set);
485 if (listener->fd > maxfd)
486 maxfd = listener->fd;
487 FD_SET(listener->tcpfd, set);
488 if (listener->tcpfd > maxfd)
489 maxfd = listener->tcpfd;
490 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000491
Simon Kelley3be34542004-09-11 19:12:13 +0100492 return maxfd;
493}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000494
Simon Kelley3be34542004-09-11 19:12:13 +0100495static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
496{
497 struct serverfd *serverfdp;
498 struct listener *listener;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000499
Simon Kelley3be34542004-09-11 19:12:13 +0100500 for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
501 if (FD_ISSET(serverfdp->fd, set))
502 reply_query(serverfdp, daemon, now);
503
504 for (listener = daemon->listeners; listener; listener = listener->next)
505 {
506 if (FD_ISSET(listener->fd, set))
507 receive_query(listener, daemon, now);
508
509 if (FD_ISSET(listener->tcpfd, set))
510 {
511 int confd;
512
513 while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
514
515 if (confd != -1)
516 {
517 int match = 1;
518 if (!(daemon->options & OPT_NOWILD))
519 {
520 /* Check for allowed interfaces when binding the wildcard address */
521 /* Don't know how to get interface of a connection, so we have to
522 check by address. This will break when interfaces change address */
523 union mysockaddr tcp_addr;
524 socklen_t tcp_len = sizeof(union mysockaddr);
525 struct iname *tmp;
526
527 if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
528 {
529#ifdef HAVE_IPV6
530 if (tcp_addr.sa.sa_family == AF_INET6)
531 tcp_addr.in6.sin6_flowinfo = htonl(0);
532#endif
533 for (match = 1, tmp = daemon->if_except; tmp; tmp = tmp->next)
534 if (sockaddr_isequal(&tmp->addr, &tcp_addr))
535 match = 0;
536
537 if (match && (daemon->if_names || daemon->if_addrs))
538 {
539 match = 0;
540 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
541 if (sockaddr_isequal(&tmp->addr, &tcp_addr))
542 match = 1;
543 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
544 if (sockaddr_isequal(&tmp->addr, &tcp_addr))
545 match = 1;
546 }
547 }
548 }
549
550 if (!match || (num_kids >= MAX_PROCS))
551 close(confd);
Simon Kelley59353a62004-11-21 19:34:28 +0000552#ifndef NO_FORK
Simon Kelley3be34542004-09-11 19:12:13 +0100553 else if (!(daemon->options & OPT_DEBUG) && fork())
554 {
555 num_kids++;
556 close(confd);
557 }
Simon Kelley59353a62004-11-21 19:34:28 +0000558#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100559 else
560 {
561 char *buff;
562 struct server *s;
563 int flags;
564
565 /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
566 terminate the process. */
567 if (!(daemon->options & OPT_DEBUG))
568 {
569 sigset_t mask;
570 sigemptyset(&mask);
571 sigaddset(&mask, SIGALRM);
572 sigprocmask(SIG_UNBLOCK, &mask, NULL);
573 alarm(CHILD_LIFETIME);
574 in_child = 1;
575 }
576
577 /* start with no upstream connections. */
578 for (s = daemon->servers; s; s = s->next)
579 s->tcpfd = -1;
580
581 /* The connected socket inherits non-blocking
582 attribute from the listening socket.
583 Reset that here. */
584 if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
585 fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
586
587 buff = tcp_request(daemon, confd, now);
588
589 if (!(daemon->options & OPT_DEBUG))
590 exit(0);
591
592 close(confd);
593 if (buff)
594 free(buff);
595 for (s = daemon->servers; s; s = s->next)
596 if (s->tcpfd != -1)
597 close(s->tcpfd);
598 }
599 }
600 }
601 }
602}
603
604int icmp_ping(struct daemon *daemon, struct in_addr addr)
605{
606 /* Try and get an ICMP echo from a machine.
607 Note that we can't create the raw socket each time
608 we do this, since that needs root. Therefore the socket has to hang
609 around all the time. Since most of the time we won't read the
610 socket, it will accumulate buffers full of ICMP messages,
611 wasting memory. To avoid that we set the receive buffer
612 length to zero except when we're actively pinging. */
613
614 /* Note that whilst in the three second wait, we check for
615 (and service) events on the DNS sockets, (so doing that
616 better not use any resources our caller has in use...)
617 but we remain deaf to signals or further DHCP packets. */
618
619 struct sockaddr_in saddr;
620 struct {
621 struct ip ip;
622 struct icmp icmp;
623 } packet;
624 unsigned short id = rand16();
625 unsigned int i, j;
626 int opt = 2000, gotreply = 0;
627 time_t start, now;
628
629 saddr.sin_family = AF_INET;
630 saddr.sin_port = 0;
631 saddr.sin_addr = addr;
632#ifdef HAVE_SOCKADDR_SA_LEN
633 saddr.sin_len = sizeof(struct sockaddr_in);
634#endif
635
636 memset(&packet.icmp, 0, sizeof(packet.icmp));
637 packet.icmp.icmp_type = ICMP_ECHO;
638 packet.icmp.icmp_id = id;
639 for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++)
640 j += ((u16 *)&packet.icmp)[i];
641 while (j>>16)
642 j = (j & 0xffff) + (j >> 16);
643 packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
644
645 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
646
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100647 while (sendto(daemon->dhcp_icmp_fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
648 (struct sockaddr *)&saddr, sizeof(saddr)) == -1 &&
649 retry_send());
650
651 for (now = start = dnsmasq_time(daemon->uptime_fd); difftime(now, start) < 3.0;)
652 {
653 struct timeval tv;
654 fd_set rset;
655 struct sockaddr_in faddr;
656 int maxfd, len = sizeof(faddr);
657
658 tv.tv_usec = 250000;
659 tv.tv_sec = 0;
660
661 FD_ZERO(&rset);
662 FD_SET(daemon->dhcp_icmp_fd, &rset);
663 maxfd = set_dns_listeners(daemon, &rset, daemon->dhcp_icmp_fd);
Simon Kelley3be34542004-09-11 19:12:13 +0100664
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100665 if (select(maxfd+1, &rset, NULL, NULL, &tv) < 0)
666 FD_ZERO(&rset);
667
668 now = dnsmasq_time(daemon->uptime_fd);
669 check_dns_listeners(daemon, &rset, now);
670
671 if (FD_ISSET(daemon->dhcp_icmp_fd, &rset) &&
672 recvfrom(daemon->dhcp_icmp_fd, &packet, sizeof(packet), 0,
673 (struct sockaddr *)&faddr, &len) == sizeof(packet) &&
674 saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
675 packet.icmp.icmp_type == ICMP_ECHOREPLY &&
676 packet.icmp.icmp_seq == 0 &&
677 packet.icmp.icmp_id == id)
678 {
679 gotreply = 1;
680 break;
681 }
682 }
683
Simon Kelley3be34542004-09-11 19:12:13 +0100684 opt = 1;
685 setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100686
Simon Kelley3be34542004-09-11 19:12:13 +0100687 return gotreply;
688}