blob: a3a4930e4d45c04e960cb2c4d22c9020d6775c9d [file] [log] [blame]
Simon Kelleycdeda282006-03-16 20:16:06 +00001/* dnsmasq is Copyright (c) 2000-2006 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#include "dnsmasq.h"
14
Simon Kelley3d8df262005-08-29 12:19:27 +010015static char *compile_opts =
16#ifndef HAVE_IPV6
17"no-"
18#endif
19"IPv6 "
20#ifndef HAVE_GETOPT_LONG
21"no-"
22#endif
23"GNU-getopt "
24#ifdef HAVE_BROKEN_RTC
25"no-RTC "
26#endif
27#ifndef HAVE_ISC_READER
28"no-"
29#endif
30"ISC-leasefile "
31#ifndef HAVE_DBUS
32"no-"
33#endif
Simon Kelleyb8187c82005-11-26 21:46:27 +000034"DBus "
35#ifdef NO_GETTEXT
36"no-"
37#endif
Simon Kelleye17fb622006-01-14 20:33:46 +000038"I18N ";
Simon Kelley3d8df262005-08-29 12:19:27 +010039
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010040static pid_t pid;
41static int pipewrite;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000042
Simon Kelley3be34542004-09-11 19:12:13 +010043static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd);
44static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now);
45static void sig_handler(int sig);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000046
47int main (int argc, char **argv)
48{
Simon Kelley3be34542004-09-11 19:12:13 +010049 struct daemon *daemon;
Simon Kelleyde379512004-06-22 20:23:33 +010050 int bind_fallback = 0;
Simon Kelley309331f2006-04-22 15:05:01 +010051 int bad_capabilities = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000052 time_t now, last = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000053 struct sigaction sigact;
Simon Kelley26128d22004-11-14 16:43:54 +000054 struct iname *if_tmp;
Simon Kelley7cebd202006-05-06 14:13:33 +010055 int piperead, pipefd[2];
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010056 unsigned char sig;
Simon Kelley7cebd202006-05-06 14:13:33 +010057
Simon Kelleyb8187c82005-11-26 21:46:27 +000058#ifndef NO_GETTEXT
59 setlocale(LC_ALL, "");
60 bindtextdomain("dnsmasq", LOCALEDIR);
61 textdomain("dnsmasq");
62#endif
63
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010064 pid = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000065
66 sigact.sa_handler = sig_handler;
67 sigact.sa_flags = 0;
68 sigemptyset(&sigact.sa_mask);
69 sigaction(SIGUSR1, &sigact, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000070 sigaction(SIGHUP, &sigact, NULL);
71 sigaction(SIGTERM, &sigact, NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000072 sigaction(SIGALRM, &sigact, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +010073 sigaction(SIGCHLD, &sigact, NULL);
74
75 /* ignore SIGPIPE */
76 sigact.sa_handler = SIG_IGN;
77 sigaction(SIGPIPE, &sigact, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000078
Simon Kelley3d8df262005-08-29 12:19:27 +010079 daemon = read_opts(argc, argv, compile_opts);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000080
Simon Kelley3be34542004-09-11 19:12:13 +010081 if (daemon->edns_pktsz < PACKETSZ)
82 daemon->edns_pktsz = PACKETSZ;
Simon Kelley0a852542005-03-23 20:28:59 +000083 daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
84 daemon->edns_pktsz : DNSMASQ_PACKETSZ;
85 daemon->packet = safe_malloc(daemon->packet_buff_sz);
Simon Kelley3be34542004-09-11 19:12:13 +010086
87 if (!daemon->lease_file)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000088 {
Simon Kelley3be34542004-09-11 19:12:13 +010089 if (daemon->dhcp)
90 daemon->lease_file = LEASEFILE;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000091 }
Simon Kelley33820b72004-04-03 21:10:00 +010092#ifndef HAVE_ISC_READER
Simon Kelley3be34542004-09-11 19:12:13 +010093 else if (!daemon->dhcp)
Simon Kelleyb8187c82005-11-26 21:46:27 +000094 die(_("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h"), NULL);
Simon Kelley33820b72004-04-03 21:10:00 +010095#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000096
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010097#ifdef HAVE_LINUX_NETWORK
98 netlink_init(daemon);
Simon Kelley309331f2006-04-22 15:05:01 +010099#elif !(defined(IP_RECVDSTADDR) && \
100 defined(IP_RECVIF) && \
101 defined(IP_SENDSRCADDR))
102 if (!(daemon->options & OPT_NOWILD))
Simon Kelleyde379512004-06-22 20:23:33 +0100103 {
104 bind_fallback = 1;
Simon Kelley3be34542004-09-11 19:12:13 +0100105 daemon->options |= OPT_NOWILD;
Simon Kelleyde379512004-06-22 20:23:33 +0100106 }
Simon Kelley309331f2006-04-22 15:05:01 +0100107#endif
108
109 daemon->interfaces = NULL;
110 if (!enumerate_interfaces(daemon))
111 die(_("failed to find list of interfaces: %s"), NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000112
Simon Kelley3be34542004-09-11 19:12:13 +0100113 if (daemon->options & OPT_NOWILD)
Simon Kelleyde379512004-06-22 20:23:33 +0100114 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100115 daemon->listeners = create_bound_listeners(daemon);
Simon Kelleyde379512004-06-22 20:23:33 +0100116
Simon Kelley3be34542004-09-11 19:12:13 +0100117 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
Simon Kelleyde379512004-06-22 20:23:33 +0100118 if (if_tmp->name && !if_tmp->used)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000119 die(_("unknown interface %s"), if_tmp->name);
Simon Kelleyde379512004-06-22 20:23:33 +0100120
Simon Kelley3be34542004-09-11 19:12:13 +0100121 for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
Simon Kelleyde379512004-06-22 20:23:33 +0100122 if (!if_tmp->used)
123 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100124 prettyprint_addr(&if_tmp->addr, daemon->namebuff);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000125 die(_("no interface with address %s"), daemon->namebuff);
Simon Kelleyde379512004-06-22 20:23:33 +0100126 }
127 }
Simon Kelley309331f2006-04-22 15:05:01 +0100128 else if (!(daemon->listeners = create_wildcard_listeners(daemon->port)))
129 die(_("failed to create listening socket: %s"), NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100130
Simon Kelley3be34542004-09-11 19:12:13 +0100131 cache_init(daemon->cachesize, daemon->options & OPT_LOG);
Simon Kelley44a2a312004-03-10 20:04:35 +0000132
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100133 now = dnsmasq_time();
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000134
Simon Kelley3be34542004-09-11 19:12:13 +0100135 if (daemon->dhcp)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000136 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100137#if !defined(HAVE_LINUX_NETWORK) && !defined(IP_RECVIF)
Simon Kelleyde379512004-06-22 20:23:33 +0100138 int c;
139 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100140 for (c = 0, tmp = daemon->if_names; tmp; tmp = tmp->next)
Simon Kelleyde379512004-06-22 20:23:33 +0100141 if (!tmp->isloop)
142 c++;
143 if (c != 1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000144 die(_("must set exactly one interface on broken systems without IP_RECVIF"), NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100145#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100146 dhcp_init(daemon);
147 lease_init(daemon, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000148 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100149
Simon Kelley3d8df262005-08-29 12:19:27 +0100150 if (daemon->options & OPT_DBUS)
151#ifdef HAVE_DBUS
152 {
153 char *err;
154 daemon->dbus = NULL;
155 daemon->watches = NULL;
156 if ((err = dbus_init(daemon)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000157 die(_("DBus error: %s"), err);
Simon Kelley3d8df262005-08-29 12:19:27 +0100158 }
159#else
Simon Kelleycdeda282006-03-16 20:16:06 +0000160 die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL);
Simon Kelley3d8df262005-08-29 12:19:27 +0100161#endif
162
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100163 /* If query_port is set then create a socket now, before dumping root
164 for use to access nameservers without more specific source addresses.
165 This allows query_port to be a low port */
Simon Kelley3be34542004-09-11 19:12:13 +0100166 if (daemon->query_port)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100167 {
168 union mysockaddr addr;
Simon Kelley849a8352006-06-09 21:02:31 +0100169 memset(&addr, 0, sizeof(addr));
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100170 addr.in.sin_family = AF_INET;
171 addr.in.sin_addr.s_addr = INADDR_ANY;
Simon Kelley3be34542004-09-11 19:12:13 +0100172 addr.in.sin_port = htons(daemon->query_port);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100173#ifdef HAVE_SOCKADDR_SA_LEN
174 addr.in.sin_len = sizeof(struct sockaddr_in);
175#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100176 allocate_sfd(&addr, &daemon->sfds);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100177#ifdef HAVE_IPV6
Simon Kelley849a8352006-06-09 21:02:31 +0100178 memset(&addr, 0, sizeof(addr));
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100179 addr.in6.sin6_family = AF_INET6;
180 addr.in6.sin6_addr = in6addr_any;
Simon Kelley3be34542004-09-11 19:12:13 +0100181 addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100182#ifdef HAVE_SOCKADDR_SA_LEN
183 addr.in6.sin6_len = sizeof(struct sockaddr_in6);
184#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100185 allocate_sfd(&addr, &daemon->sfds);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100186#endif
187 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000188
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100189 /* Use a pipe to carry signals back to the event loop in a race-free manner */
Simon Kelley7cebd202006-05-06 14:13:33 +0100190 if (pipe(pipefd) == -1 || !fix_fd(pipefd[0]) || !fix_fd(pipefd[1]))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100191 die(_("cannot create pipe: %s"), NULL);
192
193 piperead = pipefd[0];
194 pipewrite = pipefd[1];
195 /* prime the pipe to load stuff first time. */
196 sig = SIGHUP;
197 write(pipewrite, &sig, 1);
198
Simon Kelley849a8352006-06-09 21:02:31 +0100199 if (daemon->options & OPT_DEBUG)
200 {
201#ifdef LOG_PERROR
202 openlog("dnsmasq", LOG_PERROR, daemon->log_fac);
203#else
204 openlog("dnsmasq", 0, daemon->log_fac);
205#endif
206
207#ifdef HAVE_LINUX_NETWORK
208 prctl(PR_SET_DUMPABLE, 1);
209#endif
210 }
211 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000212 {
213 FILE *pidfile;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100214 struct passwd *ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
Simon Kelley3d8df262005-08-29 12:19:27 +0100215 fd_set test_set;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100216 int maxfd, i;
Simon Kelley7cebd202006-05-06 14:13:33 +0100217 int nullfd = open("/dev/null", O_RDWR);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100218
219#ifdef HAVE_LINUX_NETWORK
220 cap_user_header_t hdr = NULL;
221 cap_user_data_t data = NULL;
222
223 /* On linux, we keep CAP_NETADMIN (for ARP-injection) and
224 CAP_NET_RAW (for icmp) if we're doing dhcp */
Simon Kelley7cebd202006-05-06 14:13:33 +0100225 if (ent_pw && ent_pw->pw_uid != 0)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100226 {
227 hdr = safe_malloc(sizeof(*hdr));
228 data = safe_malloc(sizeof(*data));
229 hdr->version = _LINUX_CAPABILITY_VERSION;
230 hdr->pid = 0; /* this process */
231 data->effective = data->permitted = data->inheritable =
232 (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
233 (1 << CAP_SETGID) | (1 << CAP_SETUID);
234
235 /* Tell kernel to not clear capabilities when dropping root */
236 if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
Simon Kelley309331f2006-04-22 15:05:01 +0100237 {
238 bad_capabilities = errno;
239 ent_pw = NULL;
240 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100241 }
242#endif
243
Simon Kelley3d8df262005-08-29 12:19:27 +0100244 FD_ZERO(&test_set);
245 maxfd = set_dns_listeners(daemon, &test_set, -1);
246#ifdef HAVE_DBUS
247 maxfd = set_dbus_listeners(daemon, maxfd, &test_set, &test_set, &test_set);
248#endif
249
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000250 /* The following code "daemonizes" the process.
251 See Stevens section 12.4 */
Simon Kelley3d8df262005-08-29 12:19:27 +0100252
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000253#ifndef NO_FORK
Simon Kelley3be34542004-09-11 19:12:13 +0100254 if (!(daemon->options & OPT_NO_FORK))
255 {
256 if (fork() != 0 )
Simon Kelley7cebd202006-05-06 14:13:33 +0100257 _exit(0);
Simon Kelley3be34542004-09-11 19:12:13 +0100258
259 setsid();
260
261 if (fork() != 0)
Simon Kelley7cebd202006-05-06 14:13:33 +0100262 _exit(0);
Simon Kelley3be34542004-09-11 19:12:13 +0100263 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000264#endif
265
266 chdir("/");
267 umask(022); /* make pidfile 0644 */
268
269 /* write pidfile _after_ forking ! */
Simon Kelley3be34542004-09-11 19:12:13 +0100270 if (daemon->runfile && (pidfile = fopen(daemon->runfile, "w")))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000271 {
272 fprintf(pidfile, "%d\n", (int) getpid());
273 fclose(pidfile);
274 }
275
276 umask(0);
277
278 for (i=0; i<64; i++)
279 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100280 if (i == piperead || i == pipewrite)
281 continue;
282
283#ifdef HAVE_LINUX_NETWORK
284 if (i == daemon->netlinkfd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000285 continue;
Simon Kelley3be34542004-09-11 19:12:13 +0100286#endif
Simon Kelley3d8df262005-08-29 12:19:27 +0100287
Simon Kelley3be34542004-09-11 19:12:13 +0100288 if (daemon->dhcp &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000289 (i == fileno(daemon->lease_stream) ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100290#ifndef HAVE_LINUX_NETWORK
Simon Kelley3be34542004-09-11 19:12:13 +0100291 i == daemon->dhcp_raw_fd ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100292 i == daemon->dhcp_icmp_fd ||
293#endif
294 i == daemon->dhcpfd))
Simon Kelley3be34542004-09-11 19:12:13 +0100295 continue;
Simon Kelley3d8df262005-08-29 12:19:27 +0100296
297 if (i <= maxfd && FD_ISSET(i, &test_set))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100298 continue;
299
Simon Kelley7cebd202006-05-06 14:13:33 +0100300 /* open stdout etc to /dev/null */
301 if (i == STDOUT_FILENO || i == STDERR_FILENO || i == STDIN_FILENO)
302 dup2(nullfd, i);
303 else
304 close(i);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000305 }
306
Simon Kelley7cebd202006-05-06 14:13:33 +0100307 if (daemon->groupname || ent_pw)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000308 {
309 gid_t dummy;
310 struct group *gp;
Simon Kelley7cebd202006-05-06 14:13:33 +0100311
312 /* change group for /etc/ppp/resolv.conf otherwise get the group for "nobody" */
Simon Kelley3be34542004-09-11 19:12:13 +0100313 if ((daemon->groupname && (gp = getgrnam(daemon->groupname))) ||
Simon Kelley7cebd202006-05-06 14:13:33 +0100314 (ent_pw && (gp = getgrgid(ent_pw->pw_gid))))
315 {
316 /* remove all supplimentary groups */
317 setgroups(0, &dummy);
318 setgid(gp->gr_gid);
319 }
320 }
321
322 if (ent_pw && ent_pw->pw_uid != 0)
323 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000324 /* finally drop root */
325 setuid(ent_pw->pw_uid);
Simon Kelley7cebd202006-05-06 14:13:33 +0100326
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100327#ifdef HAVE_LINUX_NETWORK
328 data->effective = data->permitted =
329 (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
330 data->inheritable = 0;
331
332 /* lose the setuid and setgid capbilities */
333 capset(hdr, data);
334#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000335 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000336
Simon Kelley849a8352006-06-09 21:02:31 +0100337 openlog("dnsmasq", LOG_PID, daemon->log_fac);
338 }
339
Simon Kelley3be34542004-09-11 19:12:13 +0100340 if (daemon->cachesize != 0)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000341 syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000342 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000343 syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION);
Simon Kelley3d8df262005-08-29 12:19:27 +0100344
Simon Kelleyb8187c82005-11-26 21:46:27 +0000345 syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
Simon Kelley3d8df262005-08-29 12:19:27 +0100346
347#ifdef HAVE_DBUS
348 if (daemon->options & OPT_DBUS)
349 {
350 if (daemon->dbus)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000351 syslog(LOG_INFO, _("DBus support enabled: connected to system bus"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100352 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000353 syslog(LOG_INFO, _("DBus support enabled: bus connection pending"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100354 }
355#endif
356
Simon Kelleyde379512004-06-22 20:23:33 +0100357 if (bind_fallback)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000358 syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
Simon Kelleyde379512004-06-22 20:23:33 +0100359
Simon Kelley26128d22004-11-14 16:43:54 +0000360 if (!(daemon->options & OPT_NOWILD))
361 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
362 if (if_tmp->name && !if_tmp->used)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000363 syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100364
Simon Kelley3be34542004-09-11 19:12:13 +0100365 if (daemon->dhcp)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000366 {
Simon Kelley3be34542004-09-11 19:12:13 +0100367 struct dhcp_context *dhcp_tmp;
Simon Kelley0a852542005-03-23 20:28:59 +0000368
Simon Kelley3be34542004-09-11 19:12:13 +0100369 for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100370 {
Simon Kelley0a852542005-03-23 20:28:59 +0000371 prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
Simon Kelley3be34542004-09-11 19:12:13 +0100372 strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
Simon Kelley3be34542004-09-11 19:12:13 +0100373 syslog(LOG_INFO,
Simon Kelley0a852542005-03-23 20:28:59 +0000374 (dhcp_tmp->flags & CONTEXT_STATIC) ?
Simon Kelleyb8187c82005-11-26 21:46:27 +0000375 _("DHCP, static leases only on %.0s%s, lease time %s") :
376 _("DHCP, IP range %s -- %s, lease time %s"),
Simon Kelley0a852542005-03-23 20:28:59 +0000377 daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100378 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000379 }
380
Simon Kelley3be34542004-09-11 19:12:13 +0100381 if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
Simon Kelley309331f2006-04-22 15:05:01 +0100382 {
383 if (bad_capabilities)
384 {
385 errno = bad_capabilities;
386 syslog(LOG_WARNING, _("warning: setting capabilities failed: %m"));
387 }
388 syslog(LOG_WARNING, _("running as root"));
389 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000390
Simon Kelley3d8df262005-08-29 12:19:27 +0100391 check_servers(daemon);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100392
Simon Kelley7cebd202006-05-06 14:13:33 +0100393 pid = getpid();
394
395 /* Start lease-change script */
396 if (daemon->dhcp)
397 lease_collect(daemon);
398
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100399 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000400 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100401 int maxfd;
402 struct timeval t, *tp = NULL;
Simon Kelley3d8df262005-08-29 12:19:27 +0100403 fd_set rset, wset, eset;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100404
405 t.tv_sec = 0; /* no warning */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000406
407 FD_ZERO(&rset);
Simon Kelley3d8df262005-08-29 12:19:27 +0100408 FD_ZERO(&wset);
409 FD_ZERO(&eset);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000410
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100411 maxfd = set_dns_listeners(daemon, &rset, -1);
412
413#ifdef HAVE_DBUS
414 /* Whilst polling for the dbus, wake every quarter second */
415 if ((daemon->options & OPT_DBUS) && !daemon->dbus)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000416 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100417 tp = &t;
418 tp->tv_sec = 0;
419 tp->tv_usec = 250000;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000420 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100421
422 maxfd = set_dbus_listeners(daemon, maxfd, &rset, &wset, &eset);
423#endif
424
425 if (daemon->dhcp)
426 {
427 FD_SET(daemon->dhcpfd, &rset);
428 if (daemon->dhcpfd > maxfd)
429 maxfd = daemon->dhcpfd;
430 }
431
432#ifdef HAVE_LINUX_NETWORK
433 FD_SET(daemon->netlinkfd, &rset);
434 if (daemon->netlinkfd > maxfd)
435 maxfd = daemon->netlinkfd;
436#endif
Simon Kelley3d8df262005-08-29 12:19:27 +0100437
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100438 FD_SET(piperead, &rset);
439 if (piperead > maxfd)
440 maxfd = piperead;
441
442 if (select(maxfd+1, &rset, &wset, &eset, tp) < 0)
443 {
444 /* otherwise undefined after error */
445 FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
446 }
447
448 now = dnsmasq_time();
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000449
450 /* Check for changes to resolv files once per second max. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100451 /* Don't go silent for long periods if the clock goes backwards. */
Simon Kelley849a8352006-06-09 21:02:31 +0100452 if (last == 0 || difftime(now, last) > 1.0 || difftime(now, last) < -1.0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000453 {
454 last = now;
Simon Kelley33820b72004-04-03 21:10:00 +0100455
456#ifdef HAVE_ISC_READER
Simon Kelley3be34542004-09-11 19:12:13 +0100457 if (daemon->lease_file && !daemon->dhcp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100458 load_dhcp(daemon, now);
Simon Kelley33820b72004-04-03 21:10:00 +0100459#endif
460
Simon Kelley3be34542004-09-11 19:12:13 +0100461 if (!(daemon->options & OPT_NO_POLL))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000462 {
Simon Kelley3be34542004-09-11 19:12:13 +0100463 struct resolvc *res = daemon->resolv_files, *latest = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000464 struct stat statbuf;
Simon Kelley33820b72004-04-03 21:10:00 +0100465 time_t last_change = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000466 /* There may be more than one possible file.
467 Go through and find the one which changed _last_.
468 Warn of any which can't be read. */
469 while (res)
470 {
471 if (stat(res->name, &statbuf) == -1)
472 {
473 if (!res->logged)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000474 syslog(LOG_WARNING, _("failed to access %s: %m"), res->name);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000475 res->logged = 1;
476 }
477 else
478 {
479 res->logged = 0;
Simon Kelley849a8352006-06-09 21:02:31 +0100480 if (statbuf.st_mtime != res->mtime &&
481 difftime(statbuf.st_mtime, last_change) > 0.0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000482 {
Simon Kelley849a8352006-06-09 21:02:31 +0100483 last_change = statbuf.st_mtime;
484 latest = res;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000485 }
486 }
487 res = res->next;
488 }
489
Simon Kelley3d8df262005-08-29 12:19:27 +0100490 if (latest)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000491 {
Simon Kelley849a8352006-06-09 21:02:31 +0100492 static int warned = 0;
493 if (reload_servers(latest->name, daemon))
494 {
495 syslog(LOG_INFO, _("reading %s"), latest->name);
496 latest->mtime = last_change;
497 warned = 0;
498 check_servers(daemon);
499 }
500 else if (!warned)
501 {
502 syslog(LOG_WARNING, _("no servers found in %s, will retry"), latest->name);
503 warned = 1;
504 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000505 }
506 }
507 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000508
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100509 if (FD_ISSET(piperead, &rset))
510 {
Simon Kelley7cebd202006-05-06 14:13:33 +0100511 pid_t p;
512
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100513 if (read(piperead, &sig, 1) == 1)
514 switch (sig)
515 {
516 case SIGHUP:
Simon Kelley7cebd202006-05-06 14:13:33 +0100517 clear_cache_and_reload(daemon, now);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100518 if (daemon->resolv_files && (daemon->options & OPT_NO_POLL))
519 {
520 reload_servers(daemon->resolv_files->name, daemon);
521 check_servers(daemon);
522 }
523 break;
524
525 case SIGUSR1:
526 dump_cache(daemon, now);
527 break;
528
529 case SIGALRM:
530 if (daemon->dhcp)
Simon Kelley7cebd202006-05-06 14:13:33 +0100531 {
532 lease_prune(NULL, now);
533 lease_update_file(daemon, now);
534 lease_collect(daemon);
535 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100536 break;
537
538 case SIGTERM:
Simon Kelley7cebd202006-05-06 14:13:33 +0100539 {
540 int i;
541 syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
542 /* Knock all our children on the head. */
543 for (i = 0; i < MAX_PROCS; i++)
544 if (daemon->tcp_pids[i] != 0)
Simon Kelley849a8352006-06-09 21:02:31 +0100545 kill(daemon->tcp_pids[i], SIGALRM);
Simon Kelley7cebd202006-05-06 14:13:33 +0100546
547 if (daemon->dhcp)
Simon Kelley849a8352006-06-09 21:02:31 +0100548 fclose(daemon->lease_stream);
549
Simon Kelley7cebd202006-05-06 14:13:33 +0100550 exit(0);
551 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100552
553 case SIGCHLD:
554 /* See Stevens 5.10 */
Simon Kelley7cebd202006-05-06 14:13:33 +0100555 /* Note that if a script process forks and then exits
556 without waiting for its child, we will reap that child.
557 It is not therefore safe to assume that any dieing children
558 whose pid != script_pid are TCP server threads. */
559 while ((p = waitpid(-1, NULL, WNOHANG)) > 0)
560 {
561 if (p == daemon->script_pid)
562 {
563 daemon->script_pid = 0;
564 lease_collect(daemon);
565 }
566 else
567 {
568 int i;
569 for (i = 0 ; i < MAX_PROCS; i++)
570 if (daemon->tcp_pids[i] == p)
571 {
572 daemon->tcp_pids[i] = 0;
573 daemon->num_kids--;
574 break;
575 }
576 }
577 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100578 break;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100579 }
580 }
Simon Kelley7cebd202006-05-06 14:13:33 +0100581
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100582#ifdef HAVE_LINUX_NETWORK
583 if (FD_ISSET(daemon->netlinkfd, &rset))
Simon Kelleycdeda282006-03-16 20:16:06 +0000584 netlink_multicast(daemon);
585#endif
Simon Kelley3d8df262005-08-29 12:19:27 +0100586
587#ifdef HAVE_DBUS
588 /* if we didn't create a DBus connection, retry now. */
Simon Kelley7cebd202006-05-06 14:13:33 +0100589 if ((daemon->options & OPT_DBUS) && !daemon->dbus)
Simon Kelley3d8df262005-08-29 12:19:27 +0100590 {
591 char *err;
592 if ((err = dbus_init(daemon)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000593 syslog(LOG_WARNING, _("DBus error: %s"), err);
Simon Kelley3d8df262005-08-29 12:19:27 +0100594 if (daemon->dbus)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000595 syslog(LOG_INFO, _("connected to system DBus"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100596 }
597 check_dbus_listeners(daemon, &rset, &wset, &eset);
598#endif
599
Simon Kelley3be34542004-09-11 19:12:13 +0100600 check_dns_listeners(daemon, &rset, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000601
Simon Kelley3be34542004-09-11 19:12:13 +0100602 if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
603 dhcp_packet(daemon, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000604 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000605}
606
Simon Kelley3be34542004-09-11 19:12:13 +0100607static void sig_handler(int sig)
608{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100609 if (pid == 0)
610 {
611 /* ignore anything other than TERM during startup */
612 if (sig == SIGTERM)
613 exit(0);
614 }
615 else if (pid == getpid())
616 {
617 /* master process */
618 unsigned char sigchr = sig;
619 int errsave = errno;
620 write(pipewrite, &sigchr, 1);
621 errno = errsave;
622 }
623 else
Simon Kelley3be34542004-09-11 19:12:13 +0100624 {
625 /* alarm is used to kill children after a fixed time. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100626 if (sig == SIGALRM)
Simon Kelley7cebd202006-05-06 14:13:33 +0100627 _exit(0);
Simon Kelley3be34542004-09-11 19:12:13 +0100628 }
629}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000630
Simon Kelley3d8df262005-08-29 12:19:27 +0100631
Simon Kelley7cebd202006-05-06 14:13:33 +0100632void clear_cache_and_reload(struct daemon *daemon, time_t now)
Simon Kelley3d8df262005-08-29 12:19:27 +0100633{
634 cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
635 if (daemon->dhcp)
636 {
637 if (daemon->options & OPT_ETHERS)
638 dhcp_read_ethers(daemon);
639 dhcp_update_configs(daemon->dhcp_conf);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000640 lease_update_from_configs(daemon);
Simon Kelley7cebd202006-05-06 14:13:33 +0100641 lease_update_file(daemon, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100642 lease_update_dns(daemon);
643 }
644}
645
Simon Kelley3be34542004-09-11 19:12:13 +0100646static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd)
647{
648 struct serverfd *serverfdp;
649 struct listener *listener;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000650
Simon Kelley3be34542004-09-11 19:12:13 +0100651 for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
652 {
653 FD_SET(serverfdp->fd, set);
654 if (serverfdp->fd > maxfd)
655 maxfd = serverfdp->fd;
656 }
657
658 for (listener = daemon->listeners; listener; listener = listener->next)
659 {
660 FD_SET(listener->fd, set);
661 if (listener->fd > maxfd)
662 maxfd = listener->fd;
663 FD_SET(listener->tcpfd, set);
664 if (listener->tcpfd > maxfd)
665 maxfd = listener->tcpfd;
666 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000667
Simon Kelley3be34542004-09-11 19:12:13 +0100668 return maxfd;
669}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000670
Simon Kelley3be34542004-09-11 19:12:13 +0100671static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
672{
673 struct serverfd *serverfdp;
674 struct listener *listener;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000675
Simon Kelley3be34542004-09-11 19:12:13 +0100676 for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
677 if (FD_ISSET(serverfdp->fd, set))
678 reply_query(serverfdp, daemon, now);
679
680 for (listener = daemon->listeners; listener; listener = listener->next)
681 {
682 if (FD_ISSET(listener->fd, set))
683 receive_query(listener, daemon, now);
684
685 if (FD_ISSET(listener->tcpfd, set))
686 {
687 int confd;
Simon Kelley7cebd202006-05-06 14:13:33 +0100688 struct irec *iface = NULL;
689 pid_t p;
690
Simon Kelley3be34542004-09-11 19:12:13 +0100691 while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
692
Simon Kelley7cebd202006-05-06 14:13:33 +0100693 if (confd == -1)
694 continue;
695
696 if (daemon->options & OPT_NOWILD)
697 iface = listener->iface;
698 else
Simon Kelley3be34542004-09-11 19:12:13 +0100699 {
Simon Kelley7cebd202006-05-06 14:13:33 +0100700 union mysockaddr tcp_addr;
701 socklen_t tcp_len = sizeof(union mysockaddr);
702 /* Check for allowed interfaces when binding the wildcard address:
703 we do this by looking for an interface with the same address as
704 the local address of the TCP connection, then looking to see if that's
705 an allowed interface. As a side effect, we get the netmask of the
706 interface too, for localisation. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100707
Simon Kelley7cebd202006-05-06 14:13:33 +0100708 /* interface may be new since startup */
709 if (enumerate_interfaces(daemon) &&
710 getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
711 for (iface = daemon->interfaces; iface; iface = iface->next)
712 if (sockaddr_isequal(&iface->addr, &tcp_addr))
713 break;
714 }
715
716 if ((daemon->num_kids >= MAX_PROCS) || !iface)
717 {
718 shutdown(confd, SHUT_RDWR);
719 close(confd);
720 }
Simon Kelley59353a62004-11-21 19:34:28 +0000721#ifndef NO_FORK
Simon Kelley7cebd202006-05-06 14:13:33 +0100722 else if (!(daemon->options & OPT_DEBUG) && (p = fork()) != 0)
723 {
724 if (p != -1)
Simon Kelley3be34542004-09-11 19:12:13 +0100725 {
Simon Kelley7cebd202006-05-06 14:13:33 +0100726 int i;
727 for (i = 0; i < MAX_PROCS; i++)
728 if (daemon->tcp_pids[i] == 0)
729 {
730 daemon->tcp_pids[i] = p;
731 break;
732 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100733 daemon->num_kids++;
Simon Kelley3be34542004-09-11 19:12:13 +0100734 }
Simon Kelley7cebd202006-05-06 14:13:33 +0100735 close(confd);
736 }
Simon Kelley59353a62004-11-21 19:34:28 +0000737#endif
Simon Kelley7cebd202006-05-06 14:13:33 +0100738 else
739 {
740 unsigned char *buff;
741 struct server *s;
742 int flags;
743 struct in_addr dst_addr_4;
744
745 dst_addr_4.s_addr = 0;
746
747 /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
748 terminate the process. */
749 if (!(daemon->options & OPT_DEBUG))
750 alarm(CHILD_LIFETIME);
751
752 /* start with no upstream connections. */
753 for (s = daemon->servers; s; s = s->next)
754 s->tcpfd = -1;
755
756 /* The connected socket inherits non-blocking
757 attribute from the listening socket.
758 Reset that here. */
759 if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
760 fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
761
762 if (listener->family == AF_INET)
763 dst_addr_4 = iface->addr.in.sin_addr;
764
765 buff = tcp_request(daemon, confd, now, dst_addr_4, iface->netmask);
766
767 shutdown(confd, SHUT_RDWR);
768 close(confd);
769
770 if (buff)
771 free(buff);
772
773 for (s = daemon->servers; s; s = s->next)
774 if (s->tcpfd != -1)
775 {
776 shutdown(s->tcpfd, SHUT_RDWR);
777 close(s->tcpfd);
778 }
779#ifndef NO_FORK
780 if (!(daemon->options & OPT_DEBUG))
781 _exit(0);
782#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100783 }
784 }
785 }
786}
787
Simon Kelley7cebd202006-05-06 14:13:33 +0100788
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100789int make_icmp_sock(void)
790{
Simon Kelley7cebd202006-05-06 14:13:33 +0100791 int fd;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100792 int zeroopt = 0;
793
794 if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1)
795 {
Simon Kelley7cebd202006-05-06 14:13:33 +0100796 if (!fix_fd(fd) ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100797 setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
798 {
799 close(fd);
800 fd = -1;
801 }
802 }
803
804 return fd;
805}
806
Simon Kelley3be34542004-09-11 19:12:13 +0100807int icmp_ping(struct daemon *daemon, struct in_addr addr)
808{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100809 /* Try and get an ICMP echo from a machine. */
Simon Kelley3be34542004-09-11 19:12:13 +0100810
811 /* Note that whilst in the three second wait, we check for
812 (and service) events on the DNS sockets, (so doing that
813 better not use any resources our caller has in use...)
814 but we remain deaf to signals or further DHCP packets. */
815
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100816 int fd;
Simon Kelley3be34542004-09-11 19:12:13 +0100817 struct sockaddr_in saddr;
818 struct {
819 struct ip ip;
820 struct icmp icmp;
821 } packet;
822 unsigned short id = rand16();
823 unsigned int i, j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100824 int gotreply = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100825 time_t start, now;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100826
827#ifdef HAVE_LINUX_NETWORK
828 if ((fd = make_icmp_sock()) == -1)
829 return 0;
830#else
831 int opt = 2000;
832 fd = daemon->dhcp_icmp_fd;
833 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
834#endif
835
Simon Kelley3be34542004-09-11 19:12:13 +0100836 saddr.sin_family = AF_INET;
837 saddr.sin_port = 0;
838 saddr.sin_addr = addr;
839#ifdef HAVE_SOCKADDR_SA_LEN
840 saddr.sin_len = sizeof(struct sockaddr_in);
841#endif
842
843 memset(&packet.icmp, 0, sizeof(packet.icmp));
844 packet.icmp.icmp_type = ICMP_ECHO;
845 packet.icmp.icmp_id = id;
846 for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++)
847 j += ((u16 *)&packet.icmp)[i];
848 while (j>>16)
849 j = (j & 0xffff) + (j >> 16);
850 packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
851
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100852 while (sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100853 (struct sockaddr *)&saddr, sizeof(saddr)) == -1 &&
854 retry_send());
855
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100856 for (now = start = dnsmasq_time();
857 difftime(now, start) < (float)PING_WAIT;)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100858 {
859 struct timeval tv;
860 fd_set rset;
861 struct sockaddr_in faddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100862 int maxfd;
863 socklen_t len = sizeof(faddr);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100864
865 tv.tv_usec = 250000;
866 tv.tv_sec = 0;
867
868 FD_ZERO(&rset);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100869 FD_SET(fd, &rset);
870 maxfd = set_dns_listeners(daemon, &rset, fd);
Simon Kelley3be34542004-09-11 19:12:13 +0100871
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100872 if (select(maxfd+1, &rset, NULL, NULL, &tv) < 0)
873 FD_ZERO(&rset);
874
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100875 now = dnsmasq_time();
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100876 check_dns_listeners(daemon, &rset, now);
877
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100878 if (FD_ISSET(fd, &rset) &&
879 recvfrom(fd, &packet, sizeof(packet), 0,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100880 (struct sockaddr *)&faddr, &len) == sizeof(packet) &&
881 saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
882 packet.icmp.icmp_type == ICMP_ECHOREPLY &&
883 packet.icmp.icmp_seq == 0 &&
884 packet.icmp.icmp_id == id)
885 {
886 gotreply = 1;
887 break;
888 }
889 }
890
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100891#ifdef HAVE_LINUX_NETWORK
892 close(fd);
893#else
Simon Kelley3be34542004-09-11 19:12:13 +0100894 opt = 1;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100895 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
896#endif
897
Simon Kelley3be34542004-09-11 19:12:13 +0100898 return gotreply;
899}
Simon Kelley0a852542005-03-23 20:28:59 +0000900
901