]>
Commit | Line | Data |
---|---|---|
cdeda28f | 1 | /* dnsmasq is Copyright (c) 2000-2006 Simon Kelley |
9e4abcb5 SK |
2 | |
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 | ||
9e4abcb5 SK |
13 | #include "dnsmasq.h" |
14 | ||
3d8df260 SK |
15 | static 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 | |
1697269c SK |
27 | #ifdef NO_FORK |
28 | "no-MMU " | |
29 | #endif | |
3d8df260 SK |
30 | #ifndef HAVE_ISC_READER |
31 | "no-" | |
32 | #endif | |
33 | "ISC-leasefile " | |
34 | #ifndef HAVE_DBUS | |
35 | "no-" | |
36 | #endif | |
b8187c80 SK |
37 | "DBus " |
38 | #ifdef NO_GETTEXT | |
39 | "no-" | |
40 | #endif | |
e17fb629 | 41 | "I18N "; |
3d8df260 | 42 | |
5e9e0efb SK |
43 | static pid_t pid; |
44 | static int pipewrite; | |
9e4abcb5 | 45 | |
1697269c | 46 | static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int *maxfdp); |
3be34541 SK |
47 | static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now); |
48 | static void sig_handler(int sig); | |
9e4abcb5 SK |
49 | |
50 | int main (int argc, char **argv) | |
51 | { | |
3be34541 | 52 | struct daemon *daemon; |
de37951c | 53 | int bind_fallback = 0; |
309331f5 | 54 | int bad_capabilities = 0; |
9e4abcb5 | 55 | time_t now, last = 0; |
9e4abcb5 | 56 | struct sigaction sigact; |
26128d27 | 57 | struct iname *if_tmp; |
7cebd20f | 58 | int piperead, pipefd[2]; |
5e9e0efb | 59 | unsigned char sig; |
7cebd20f | 60 | |
b8187c80 SK |
61 | #ifndef NO_GETTEXT |
62 | setlocale(LC_ALL, ""); | |
63 | bindtextdomain("dnsmasq", LOCALEDIR); | |
64 | textdomain("dnsmasq"); | |
65 | #endif | |
66 | ||
5e9e0efb | 67 | pid = 0; |
9e4abcb5 SK |
68 | |
69 | sigact.sa_handler = sig_handler; | |
70 | sigact.sa_flags = 0; | |
71 | sigemptyset(&sigact.sa_mask); | |
72 | sigaction(SIGUSR1, &sigact, NULL); | |
9e4abcb5 SK |
73 | sigaction(SIGHUP, &sigact, NULL); |
74 | sigaction(SIGTERM, &sigact, NULL); | |
44a2a316 | 75 | sigaction(SIGALRM, &sigact, NULL); |
feba5c1d SK |
76 | sigaction(SIGCHLD, &sigact, NULL); |
77 | ||
78 | /* ignore SIGPIPE */ | |
79 | sigact.sa_handler = SIG_IGN; | |
80 | sigaction(SIGPIPE, &sigact, NULL); | |
9e4abcb5 | 81 | |
3d8df260 | 82 | daemon = read_opts(argc, argv, compile_opts); |
3be34541 SK |
83 | |
84 | if (daemon->edns_pktsz < PACKETSZ) | |
85 | daemon->edns_pktsz = PACKETSZ; | |
0a852541 SK |
86 | daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? |
87 | daemon->edns_pktsz : DNSMASQ_PACKETSZ; | |
88 | daemon->packet = safe_malloc(daemon->packet_buff_sz); | |
9e4abcb5 | 89 | |
3be34541 | 90 | if (!daemon->lease_file) |
9e4abcb5 | 91 | { |
3be34541 SK |
92 | if (daemon->dhcp) |
93 | daemon->lease_file = LEASEFILE; | |
9e4abcb5 | 94 | } |
33820b7e | 95 | #ifndef HAVE_ISC_READER |
3be34541 | 96 | else if (!daemon->dhcp) |
b8187c80 | 97 | die(_("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h"), NULL); |
33820b7e | 98 | #endif |
9e4abcb5 | 99 | |
5e9e0efb SK |
100 | #ifdef HAVE_LINUX_NETWORK |
101 | netlink_init(daemon); | |
309331f5 SK |
102 | #elif !(defined(IP_RECVDSTADDR) && \ |
103 | defined(IP_RECVIF) && \ | |
104 | defined(IP_SENDSRCADDR)) | |
105 | if (!(daemon->options & OPT_NOWILD)) | |
de37951c SK |
106 | { |
107 | bind_fallback = 1; | |
3be34541 | 108 | daemon->options |= OPT_NOWILD; |
de37951c | 109 | } |
309331f5 SK |
110 | #endif |
111 | ||
832af0ba SK |
112 | #ifndef HAVE_TFTP |
113 | if (daemon->options & OPT_TFTP) | |
114 | die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL); | |
115 | #endif | |
116 | ||
309331f5 SK |
117 | daemon->interfaces = NULL; |
118 | if (!enumerate_interfaces(daemon)) | |
119 | die(_("failed to find list of interfaces: %s"), NULL); | |
9e4abcb5 | 120 | |
3be34541 | 121 | if (daemon->options & OPT_NOWILD) |
de37951c | 122 | { |
5e9e0efb | 123 | daemon->listeners = create_bound_listeners(daemon); |
de37951c | 124 | |
3be34541 | 125 | for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) |
de37951c | 126 | if (if_tmp->name && !if_tmp->used) |
b8187c80 | 127 | die(_("unknown interface %s"), if_tmp->name); |
de37951c | 128 | |
3be34541 | 129 | for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next) |
de37951c SK |
130 | if (!if_tmp->used) |
131 | { | |
3d8df260 | 132 | prettyprint_addr(&if_tmp->addr, daemon->namebuff); |
b8187c80 | 133 | die(_("no interface with address %s"), daemon->namebuff); |
de37951c SK |
134 | } |
135 | } | |
832af0ba | 136 | else if (!(daemon->listeners = create_wildcard_listeners(daemon->port, daemon->options & OPT_TFTP))) |
309331f5 | 137 | die(_("failed to create listening socket: %s"), NULL); |
de37951c | 138 | |
3be34541 | 139 | cache_init(daemon->cachesize, daemon->options & OPT_LOG); |
44a2a316 | 140 | |
5e9e0efb | 141 | now = dnsmasq_time(); |
9e4abcb5 | 142 | |
3be34541 | 143 | if (daemon->dhcp) |
9e4abcb5 | 144 | { |
5e9e0efb | 145 | #if !defined(HAVE_LINUX_NETWORK) && !defined(IP_RECVIF) |
de37951c SK |
146 | int c; |
147 | struct iname *tmp; | |
3be34541 | 148 | for (c = 0, tmp = daemon->if_names; tmp; tmp = tmp->next) |
de37951c SK |
149 | if (!tmp->isloop) |
150 | c++; | |
151 | if (c != 1) | |
b8187c80 | 152 | die(_("must set exactly one interface on broken systems without IP_RECVIF"), NULL); |
de37951c | 153 | #endif |
3be34541 SK |
154 | dhcp_init(daemon); |
155 | lease_init(daemon, now); | |
9e4abcb5 | 156 | } |
feba5c1d | 157 | |
3d8df260 SK |
158 | if (daemon->options & OPT_DBUS) |
159 | #ifdef HAVE_DBUS | |
160 | { | |
161 | char *err; | |
162 | daemon->dbus = NULL; | |
163 | daemon->watches = NULL; | |
164 | if ((err = dbus_init(daemon))) | |
b8187c80 | 165 | die(_("DBus error: %s"), err); |
3d8df260 SK |
166 | } |
167 | #else | |
cdeda28f | 168 | die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL); |
3d8df260 SK |
169 | #endif |
170 | ||
feba5c1d SK |
171 | /* If query_port is set then create a socket now, before dumping root |
172 | for use to access nameservers without more specific source addresses. | |
173 | This allows query_port to be a low port */ | |
3be34541 | 174 | if (daemon->query_port) |
feba5c1d SK |
175 | { |
176 | union mysockaddr addr; | |
849a8357 | 177 | memset(&addr, 0, sizeof(addr)); |
feba5c1d SK |
178 | addr.in.sin_family = AF_INET; |
179 | addr.in.sin_addr.s_addr = INADDR_ANY; | |
3be34541 | 180 | addr.in.sin_port = htons(daemon->query_port); |
feba5c1d SK |
181 | #ifdef HAVE_SOCKADDR_SA_LEN |
182 | addr.in.sin_len = sizeof(struct sockaddr_in); | |
183 | #endif | |
3be34541 | 184 | allocate_sfd(&addr, &daemon->sfds); |
feba5c1d | 185 | #ifdef HAVE_IPV6 |
849a8357 | 186 | memset(&addr, 0, sizeof(addr)); |
feba5c1d SK |
187 | addr.in6.sin6_family = AF_INET6; |
188 | addr.in6.sin6_addr = in6addr_any; | |
3be34541 | 189 | addr.in6.sin6_port = htons(daemon->query_port); |
feba5c1d SK |
190 | #ifdef HAVE_SOCKADDR_SA_LEN |
191 | addr.in6.sin6_len = sizeof(struct sockaddr_in6); | |
192 | #endif | |
3be34541 | 193 | allocate_sfd(&addr, &daemon->sfds); |
feba5c1d SK |
194 | #endif |
195 | } | |
9e4abcb5 | 196 | |
5e9e0efb | 197 | /* Use a pipe to carry signals back to the event loop in a race-free manner */ |
7cebd20f | 198 | if (pipe(pipefd) == -1 || !fix_fd(pipefd[0]) || !fix_fd(pipefd[1])) |
5e9e0efb SK |
199 | die(_("cannot create pipe: %s"), NULL); |
200 | ||
201 | piperead = pipefd[0]; | |
202 | pipewrite = pipefd[1]; | |
203 | /* prime the pipe to load stuff first time. */ | |
204 | sig = SIGHUP; | |
205 | write(pipewrite, &sig, 1); | |
1697269c SK |
206 | |
207 | if (!(daemon->options & OPT_DEBUG)) | |
9e4abcb5 SK |
208 | { |
209 | FILE *pidfile; | |
3d8df260 | 210 | fd_set test_set; |
1697269c | 211 | int maxfd = -1, i; |
7cebd20f | 212 | int nullfd = open("/dev/null", O_RDWR); |
5e9e0efb | 213 | |
9e4abcb5 SK |
214 | /* The following code "daemonizes" the process. |
215 | See Stevens section 12.4 */ | |
1697269c SK |
216 | |
217 | #ifndef NO_FORK | |
3be34541 SK |
218 | if (!(daemon->options & OPT_NO_FORK)) |
219 | { | |
220 | if (fork() != 0 ) | |
7cebd20f | 221 | _exit(0); |
3be34541 SK |
222 | |
223 | setsid(); | |
224 | ||
225 | if (fork() != 0) | |
7cebd20f | 226 | _exit(0); |
3be34541 | 227 | } |
9e4abcb5 SK |
228 | #endif |
229 | ||
230 | chdir("/"); | |
231 | umask(022); /* make pidfile 0644 */ | |
232 | ||
233 | /* write pidfile _after_ forking ! */ | |
3be34541 | 234 | if (daemon->runfile && (pidfile = fopen(daemon->runfile, "w"))) |
9e4abcb5 SK |
235 | { |
236 | fprintf(pidfile, "%d\n", (int) getpid()); | |
237 | fclose(pidfile); | |
238 | } | |
239 | ||
240 | umask(0); | |
1697269c SK |
241 | |
242 | FD_ZERO(&test_set); | |
243 | set_dns_listeners(daemon, now, &test_set, &maxfd); | |
244 | #ifdef HAVE_DBUS | |
245 | set_dbus_listeners(daemon, &maxfd, &test_set, &test_set, &test_set); | |
246 | #endif | |
9e4abcb5 SK |
247 | for (i=0; i<64; i++) |
248 | { | |
5e9e0efb SK |
249 | if (i == piperead || i == pipewrite) |
250 | continue; | |
251 | ||
252 | #ifdef HAVE_LINUX_NETWORK | |
253 | if (i == daemon->netlinkfd) | |
9e4abcb5 | 254 | continue; |
3be34541 | 255 | #endif |
3d8df260 | 256 | |
3be34541 | 257 | if (daemon->dhcp && |
208b65c5 | 258 | ((daemon->lease_stream && i == fileno(daemon->lease_stream)) || |
5e9e0efb | 259 | #ifndef HAVE_LINUX_NETWORK |
3be34541 | 260 | i == daemon->dhcp_raw_fd || |
5e9e0efb SK |
261 | i == daemon->dhcp_icmp_fd || |
262 | #endif | |
263 | i == daemon->dhcpfd)) | |
3be34541 | 264 | continue; |
3d8df260 SK |
265 | |
266 | if (i <= maxfd && FD_ISSET(i, &test_set)) | |
feba5c1d SK |
267 | continue; |
268 | ||
7cebd20f SK |
269 | /* open stdout etc to /dev/null */ |
270 | if (i == STDOUT_FILENO || i == STDERR_FILENO || i == STDIN_FILENO) | |
271 | dup2(nullfd, i); | |
272 | else | |
273 | close(i); | |
9e4abcb5 | 274 | } |
1697269c SK |
275 | } |
276 | ||
277 | /* if we are to run scripts, we need to fork a helper before dropping root. */ | |
832af0ba | 278 | daemon->helperfd = create_helper(daemon); |
1697269c SK |
279 | |
280 | if (!(daemon->options & OPT_DEBUG)) | |
281 | { | |
282 | /* UID changing, etc */ | |
283 | struct passwd *ent_pw = daemon->username ? getpwnam(daemon->username) : NULL; | |
284 | ||
7cebd20f | 285 | if (daemon->groupname || ent_pw) |
9e4abcb5 SK |
286 | { |
287 | gid_t dummy; | |
288 | struct group *gp; | |
7cebd20f SK |
289 | |
290 | /* change group for /etc/ppp/resolv.conf otherwise get the group for "nobody" */ | |
3be34541 | 291 | if ((daemon->groupname && (gp = getgrnam(daemon->groupname))) || |
7cebd20f SK |
292 | (ent_pw && (gp = getgrgid(ent_pw->pw_gid)))) |
293 | { | |
294 | /* remove all supplimentary groups */ | |
295 | setgroups(0, &dummy); | |
296 | setgid(gp->gr_gid); | |
297 | } | |
298 | } | |
1697269c | 299 | |
7cebd20f | 300 | if (ent_pw && ent_pw->pw_uid != 0) |
1697269c | 301 | { |
5e9e0efb | 302 | #ifdef HAVE_LINUX_NETWORK |
1697269c SK |
303 | /* On linux, we keep CAP_NETADMIN (for ARP-injection) and |
304 | CAP_NET_RAW (for icmp) if we're doing dhcp */ | |
305 | cap_user_header_t hdr = safe_malloc(sizeof(*hdr)); | |
306 | cap_user_data_t data = safe_malloc(sizeof(*data)); | |
307 | hdr->version = _LINUX_CAPABILITY_VERSION; | |
308 | hdr->pid = 0; /* this process */ | |
309 | data->effective = data->permitted = data->inheritable = | |
310 | (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | | |
311 | (1 << CAP_SETGID) | (1 << CAP_SETUID); | |
5e9e0efb | 312 | |
1697269c SK |
313 | /* Tell kernel to not clear capabilities when dropping root */ |
314 | if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1) | |
315 | bad_capabilities = errno; | |
316 | else | |
5e9e0efb | 317 | #endif |
1697269c SK |
318 | { |
319 | /* finally drop root */ | |
320 | setuid(ent_pw->pw_uid); | |
321 | ||
322 | #ifdef HAVE_LINUX_NETWORK | |
323 | data->effective = data->permitted = | |
324 | (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW); | |
325 | data->inheritable = 0; | |
326 | ||
327 | /* lose the setuid and setgid capbilities */ | |
328 | capset(hdr, data); | |
329 | #endif | |
330 | } | |
9e4abcb5 SK |
331 | } |
332 | } | |
1697269c SK |
333 | |
334 | log_start(daemon); | |
335 | ||
336 | #ifdef HAVE_LINUX_NETWORK | |
337 | if (daemon->options & OPT_DEBUG) | |
338 | prctl(PR_SET_DUMPABLE, 1); | |
339 | #endif | |
9e4abcb5 | 340 | |
3be34541 | 341 | if (daemon->cachesize != 0) |
b8187c80 | 342 | syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize); |
9e4abcb5 | 343 | else |
b8187c80 | 344 | syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION); |
1697269c | 345 | |
b8187c80 | 346 | syslog(LOG_INFO, _("compile time options: %s"), compile_opts); |
1697269c | 347 | |
3d8df260 SK |
348 | #ifdef HAVE_DBUS |
349 | if (daemon->options & OPT_DBUS) | |
350 | { | |
351 | if (daemon->dbus) | |
b8187c80 | 352 | syslog(LOG_INFO, _("DBus support enabled: connected to system bus")); |
3d8df260 | 353 | else |
b8187c80 | 354 | syslog(LOG_INFO, _("DBus support enabled: bus connection pending")); |
3d8df260 SK |
355 | } |
356 | #endif | |
357 | ||
de37951c | 358 | if (bind_fallback) |
b8187c80 | 359 | syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations")); |
de37951c | 360 | |
26128d27 SK |
361 | if (!(daemon->options & OPT_NOWILD)) |
362 | for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) | |
363 | if (if_tmp->name && !if_tmp->used) | |
b8187c80 | 364 | syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name); |
5e9e0efb | 365 | |
208b65c5 SK |
366 | if (daemon->options & OPT_NO_RESOLV) |
367 | { | |
368 | if (daemon->resolv_files && !daemon->resolv_files->is_default) | |
369 | syslog(LOG_WARNING, _("warning: ignoring resolv-file flag because no-resolv is set")); | |
370 | daemon->resolv_files = NULL; | |
371 | } | |
372 | ||
3be34541 | 373 | if (daemon->dhcp) |
9e4abcb5 | 374 | { |
3be34541 | 375 | struct dhcp_context *dhcp_tmp; |
0a852541 | 376 | |
3be34541 | 377 | for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) |
feba5c1d | 378 | { |
0a852541 | 379 | prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time); |
3be34541 | 380 | strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start)); |
3be34541 | 381 | syslog(LOG_INFO, |
0a852541 | 382 | (dhcp_tmp->flags & CONTEXT_STATIC) ? |
b8187c80 SK |
383 | _("DHCP, static leases only on %.0s%s, lease time %s") : |
384 | _("DHCP, IP range %s -- %s, lease time %s"), | |
0a852541 | 385 | daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2); |
feba5c1d | 386 | } |
26128d27 SK |
387 | } |
388 | ||
832af0ba SK |
389 | #ifdef HAVE_TFTP |
390 | if (daemon->options & OPT_TFTP) | |
391 | { | |
392 | long max_fd = sysconf(_SC_OPEN_MAX); | |
393 | ||
394 | #ifdef FD_SETSIZE | |
395 | if (FD_SETSIZE < max_fd) | |
396 | max_fd = FD_SETSIZE; | |
397 | #endif | |
398 | ||
399 | syslog(LOG_INFO, "TFTP %s%s %s", | |
400 | daemon->tftp_prefix ? _("root is ") : _("enabled"), | |
401 | daemon->tftp_prefix ? daemon->tftp_prefix: "", | |
402 | daemon->options & OPT_TFTP_SECURE ? _("secure mode") : ""); | |
403 | ||
404 | /* This is a guess, it assumes that for small limits, | |
405 | disjoint files might be servered, but for large limits, | |
406 | a single file will be sent to may clients (the file only needs | |
407 | one fd). */ | |
408 | ||
409 | max_fd -= 30; /* use other than TFTP */ | |
410 | ||
411 | if (max_fd < 0) | |
412 | max_fd = 5; | |
413 | else if (max_fd < 100) | |
414 | max_fd = max_fd/2; | |
415 | else | |
416 | max_fd = max_fd - 20; | |
417 | ||
418 | if (daemon->tftp_max > max_fd) | |
419 | { | |
420 | daemon->tftp_max = max_fd; | |
421 | syslog(LOG_WARNING, | |
422 | _("restricting maximum simultaneous TFTP transfers to %d"), | |
423 | daemon->tftp_max); | |
424 | } | |
425 | } | |
426 | #endif | |
427 | ||
3be34541 | 428 | if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0)) |
309331f5 SK |
429 | { |
430 | if (bad_capabilities) | |
431 | { | |
432 | errno = bad_capabilities; | |
433 | syslog(LOG_WARNING, _("warning: setting capabilities failed: %m")); | |
434 | } | |
435 | syslog(LOG_WARNING, _("running as root")); | |
436 | } | |
9e4abcb5 | 437 | |
3d8df260 | 438 | check_servers(daemon); |
5e9e0efb | 439 | |
7cebd20f SK |
440 | pid = getpid(); |
441 | ||
5e9e0efb | 442 | while (1) |
9e4abcb5 | 443 | { |
1697269c | 444 | int maxfd = -1; |
5e9e0efb | 445 | struct timeval t, *tp = NULL; |
3d8df260 | 446 | fd_set rset, wset, eset; |
9e4abcb5 SK |
447 | |
448 | FD_ZERO(&rset); | |
3d8df260 SK |
449 | FD_ZERO(&wset); |
450 | FD_ZERO(&eset); | |
9e4abcb5 | 451 | |
1697269c SK |
452 | /* if we are out of resources, find how long we have to wait |
453 | for some to come free, we'll loop around then and restart | |
454 | listening for queries */ | |
455 | if ((t.tv_sec = set_dns_listeners(daemon, now, &rset, &maxfd)) != 0) | |
456 | { | |
457 | t.tv_usec = 0; | |
458 | tp = &t; | |
459 | } | |
460 | ||
832af0ba SK |
461 | /* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */ |
462 | if (daemon->tftp_trans || | |
463 | ((daemon->options & OPT_DBUS) && !daemon->dbus)) | |
5e9e0efb | 464 | { |
1697269c SK |
465 | t.tv_sec = 0; |
466 | t.tv_usec = 250000; | |
5e9e0efb | 467 | tp = &t; |
5e9e0efb | 468 | } |
44a2a316 | 469 | |
832af0ba | 470 | #ifdef HAVE_DBUS |
1697269c | 471 | set_dbus_listeners(daemon, &maxfd, &rset, &wset, &eset); |
5e9e0efb SK |
472 | #endif |
473 | ||
474 | if (daemon->dhcp) | |
475 | { | |
476 | FD_SET(daemon->dhcpfd, &rset); | |
1697269c | 477 | bump_maxfd(daemon->dhcpfd, &maxfd); |
5e9e0efb | 478 | } |
cdeda28f | 479 | |
5e9e0efb SK |
480 | #ifdef HAVE_LINUX_NETWORK |
481 | FD_SET(daemon->netlinkfd, &rset); | |
1697269c | 482 | bump_maxfd(daemon->netlinkfd, &maxfd); |
3d8df260 | 483 | #endif |
3d8df260 | 484 | |
5e9e0efb | 485 | FD_SET(piperead, &rset); |
1697269c SK |
486 | bump_maxfd(piperead, &maxfd); |
487 | ||
488 | while (helper_buf_empty() && do_script_run(daemon)); | |
489 | ||
490 | if (!helper_buf_empty()) | |
491 | { | |
492 | FD_SET(daemon->helperfd, &wset); | |
493 | bump_maxfd(daemon->helperfd, &maxfd); | |
494 | } | |
495 | ||
5e9e0efb SK |
496 | if (select(maxfd+1, &rset, &wset, &eset, tp) < 0) |
497 | { | |
498 | /* otherwise undefined after error */ | |
499 | FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset); | |
500 | } | |
501 | ||
502 | now = dnsmasq_time(); | |
9e4abcb5 SK |
503 | |
504 | /* Check for changes to resolv files once per second max. */ | |
3d8df260 | 505 | /* Don't go silent for long periods if the clock goes backwards. */ |
849a8357 | 506 | if (last == 0 || difftime(now, last) > 1.0 || difftime(now, last) < -1.0) |
9e4abcb5 SK |
507 | { |
508 | last = now; | |
33820b7e SK |
509 | |
510 | #ifdef HAVE_ISC_READER | |
3be34541 | 511 | if (daemon->lease_file && !daemon->dhcp) |
fd9fa481 | 512 | load_dhcp(daemon, now); |
33820b7e SK |
513 | #endif |
514 | ||
3be34541 | 515 | if (!(daemon->options & OPT_NO_POLL)) |
9e4abcb5 | 516 | { |
208b65c5 | 517 | struct resolvc *res, *latest; |
9e4abcb5 | 518 | struct stat statbuf; |
33820b7e | 519 | time_t last_change = 0; |
9e4abcb5 SK |
520 | /* There may be more than one possible file. |
521 | Go through and find the one which changed _last_. | |
522 | Warn of any which can't be read. */ | |
208b65c5 SK |
523 | for (latest = NULL, res = daemon->resolv_files; res; res = res->next) |
524 | if (stat(res->name, &statbuf) == -1) | |
525 | { | |
526 | if (!res->logged) | |
527 | syslog(LOG_WARNING, _("failed to access %s: %m"), res->name); | |
528 | res->logged = 1; | |
529 | } | |
530 | else | |
531 | { | |
532 | res->logged = 0; | |
533 | if (statbuf.st_mtime != res->mtime) | |
534 | { | |
535 | res->mtime = statbuf.st_mtime; | |
536 | if (difftime(statbuf.st_mtime, last_change) > 0.0) | |
537 | { | |
538 | last_change = statbuf.st_mtime; | |
539 | latest = res; | |
540 | } | |
541 | } | |
542 | } | |
543 | ||
3d8df260 | 544 | if (latest) |
9e4abcb5 | 545 | { |
849a8357 SK |
546 | static int warned = 0; |
547 | if (reload_servers(latest->name, daemon)) | |
548 | { | |
549 | syslog(LOG_INFO, _("reading %s"), latest->name); | |
849a8357 SK |
550 | warned = 0; |
551 | check_servers(daemon); | |
1697269c SK |
552 | if (daemon->options & OPT_RELOAD) |
553 | cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts); | |
849a8357 | 554 | } |
208b65c5 | 555 | else |
849a8357 | 556 | { |
208b65c5 SK |
557 | latest->mtime = 0; |
558 | if (!warned) | |
559 | { | |
560 | syslog(LOG_WARNING, _("no servers found in %s, will retry"), latest->name); | |
561 | warned = 1; | |
562 | } | |
849a8357 | 563 | } |
9e4abcb5 SK |
564 | } |
565 | } | |
566 | } | |
cdeda28f | 567 | |
5e9e0efb SK |
568 | if (FD_ISSET(piperead, &rset)) |
569 | { | |
7cebd20f SK |
570 | pid_t p; |
571 | ||
5e9e0efb SK |
572 | if (read(piperead, &sig, 1) == 1) |
573 | switch (sig) | |
574 | { | |
575 | case SIGHUP: | |
7cebd20f | 576 | clear_cache_and_reload(daemon, now); |
5e9e0efb SK |
577 | if (daemon->resolv_files && (daemon->options & OPT_NO_POLL)) |
578 | { | |
579 | reload_servers(daemon->resolv_files->name, daemon); | |
580 | check_servers(daemon); | |
581 | } | |
582 | break; | |
583 | ||
584 | case SIGUSR1: | |
585 | dump_cache(daemon, now); | |
586 | break; | |
587 | ||
588 | case SIGALRM: | |
589 | if (daemon->dhcp) | |
7cebd20f SK |
590 | { |
591 | lease_prune(NULL, now); | |
592 | lease_update_file(daemon, now); | |
7cebd20f | 593 | } |
5e9e0efb SK |
594 | break; |
595 | ||
596 | case SIGTERM: | |
7cebd20f SK |
597 | { |
598 | int i; | |
7cebd20f SK |
599 | /* Knock all our children on the head. */ |
600 | for (i = 0; i < MAX_PROCS; i++) | |
601 | if (daemon->tcp_pids[i] != 0) | |
849a8357 | 602 | kill(daemon->tcp_pids[i], SIGALRM); |
7cebd20f | 603 | |
1697269c SK |
604 | /* handle pending lease transitions */ |
605 | if (daemon->helperfd != -1) | |
606 | { | |
607 | /* block in writes until all done */ | |
608 | if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1) | |
609 | fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK); | |
610 | do { | |
611 | helper_write(daemon); | |
612 | } while (!helper_buf_empty() || do_script_run(daemon)); | |
613 | close(daemon->helperfd); | |
614 | } | |
615 | ||
208b65c5 | 616 | if (daemon->lease_stream) |
849a8357 | 617 | fclose(daemon->lease_stream); |
1697269c SK |
618 | |
619 | syslog(LOG_INFO, _("exiting on receipt of SIGTERM")); | |
7cebd20f SK |
620 | exit(0); |
621 | } | |
5e9e0efb SK |
622 | |
623 | case SIGCHLD: | |
624 | /* See Stevens 5.10 */ | |
7cebd20f SK |
625 | /* Note that if a script process forks and then exits |
626 | without waiting for its child, we will reap that child. | |
627 | It is not therefore safe to assume that any dieing children | |
628 | whose pid != script_pid are TCP server threads. */ | |
629 | while ((p = waitpid(-1, NULL, WNOHANG)) > 0) | |
630 | { | |
1697269c SK |
631 | int i; |
632 | for (i = 0 ; i < MAX_PROCS; i++) | |
633 | if (daemon->tcp_pids[i] == p) | |
634 | { | |
635 | daemon->tcp_pids[i] = 0; | |
636 | break; | |
637 | } | |
7cebd20f | 638 | } |
5e9e0efb | 639 | break; |
5e9e0efb SK |
640 | } |
641 | } | |
7cebd20f | 642 | |
5e9e0efb SK |
643 | #ifdef HAVE_LINUX_NETWORK |
644 | if (FD_ISSET(daemon->netlinkfd, &rset)) | |
cdeda28f SK |
645 | netlink_multicast(daemon); |
646 | #endif | |
3d8df260 SK |
647 | |
648 | #ifdef HAVE_DBUS | |
649 | /* if we didn't create a DBus connection, retry now. */ | |
7cebd20f | 650 | if ((daemon->options & OPT_DBUS) && !daemon->dbus) |
3d8df260 SK |
651 | { |
652 | char *err; | |
653 | if ((err = dbus_init(daemon))) | |
b8187c80 | 654 | syslog(LOG_WARNING, _("DBus error: %s"), err); |
3d8df260 | 655 | if (daemon->dbus) |
b8187c80 | 656 | syslog(LOG_INFO, _("connected to system DBus")); |
3d8df260 SK |
657 | } |
658 | check_dbus_listeners(daemon, &rset, &wset, &eset); | |
659 | #endif | |
660 | ||
3be34541 | 661 | check_dns_listeners(daemon, &rset, now); |
832af0ba SK |
662 | |
663 | #ifdef HAVE_TFTP | |
664 | check_tftp_listeners(daemon, &rset, now); | |
665 | #endif | |
666 | ||
3be34541 SK |
667 | if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset)) |
668 | dhcp_packet(daemon, now); | |
1697269c SK |
669 | |
670 | if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset)) | |
671 | helper_write(daemon); | |
9e4abcb5 | 672 | } |
9e4abcb5 SK |
673 | } |
674 | ||
3be34541 SK |
675 | static void sig_handler(int sig) |
676 | { | |
5e9e0efb | 677 | if (pid == 0) |
3be34541 | 678 | { |
1697269c SK |
679 | /* ignore anything other than TERM during startup |
680 | and in helper proc. (helper ignore TERM too) */ | |
5e9e0efb | 681 | if (sig == SIGTERM) |
3be34541 | 682 | exit(0); |
3be34541 | 683 | } |
5e9e0efb | 684 | else if (pid == getpid()) |
3be34541 | 685 | { |
5e9e0efb SK |
686 | /* master process */ |
687 | unsigned char sigchr = sig; | |
688 | int errsave = errno; | |
689 | write(pipewrite, &sigchr, 1); | |
690 | errno = errsave; | |
691 | } | |
692 | else | |
693 | { | |
1697269c | 694 | /* alarm is used to kill TCP children after a fixed time. */ |
5e9e0efb | 695 | if (sig == SIGALRM) |
7cebd20f | 696 | _exit(0); |
3be34541 SK |
697 | } |
698 | } | |
699 | ||
3d8df260 | 700 | |
7cebd20f | 701 | void clear_cache_and_reload(struct daemon *daemon, time_t now) |
3d8df260 SK |
702 | { |
703 | cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts); | |
704 | if (daemon->dhcp) | |
705 | { | |
706 | if (daemon->options & OPT_ETHERS) | |
707 | dhcp_read_ethers(daemon); | |
708 | dhcp_update_configs(daemon->dhcp_conf); | |
b8187c80 | 709 | lease_update_from_configs(daemon); |
7cebd20f | 710 | lease_update_file(daemon, now); |
3d8df260 SK |
711 | lease_update_dns(daemon); |
712 | } | |
713 | } | |
714 | ||
1697269c | 715 | static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int *maxfdp) |
3be34541 SK |
716 | { |
717 | struct serverfd *serverfdp; | |
718 | struct listener *listener; | |
1697269c | 719 | int wait, i; |
832af0ba SK |
720 | |
721 | #ifdef HAVE_TFTP | |
722 | int tftp = 0; | |
723 | struct tftp_transfer *transfer; | |
724 | for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) | |
725 | { | |
726 | tftp++; | |
727 | FD_SET(transfer->sockfd, set); | |
728 | bump_maxfd(transfer->sockfd, maxfdp); | |
729 | } | |
730 | #endif | |
731 | ||
1697269c SK |
732 | /* will we be able to get memory? */ |
733 | get_new_frec(daemon, now, &wait); | |
734 | ||
3be34541 SK |
735 | for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) |
736 | { | |
737 | FD_SET(serverfdp->fd, set); | |
1697269c | 738 | bump_maxfd(serverfdp->fd, maxfdp); |
3be34541 SK |
739 | } |
740 | ||
741 | for (listener = daemon->listeners; listener; listener = listener->next) | |
742 | { | |
1697269c SK |
743 | /* only listen for queries if we have resources */ |
744 | if (wait == 0) | |
745 | { | |
746 | FD_SET(listener->fd, set); | |
747 | bump_maxfd(listener->fd, maxfdp); | |
748 | } | |
749 | ||
750 | /* death of a child goes through the select loop, so | |
751 | we don't need to explicitly arrange to wake up here */ | |
752 | for (i = 0; i < MAX_PROCS; i++) | |
753 | if (daemon->tcp_pids[i] == 0) | |
754 | { | |
755 | FD_SET(listener->tcpfd, set); | |
756 | bump_maxfd(listener->tcpfd, maxfdp); | |
757 | break; | |
758 | } | |
9e4abcb5 | 759 | |
832af0ba SK |
760 | #ifdef HAVE_TFTP |
761 | if (tftp <= daemon->tftp_max && listener->tftpfd != -1) | |
762 | { | |
763 | FD_SET(listener->tftpfd, set); | |
764 | bump_maxfd(listener->tftpfd, maxfdp); | |
765 | } | |
766 | #endif | |
767 | ||
768 | } | |
769 | ||
1697269c | 770 | return wait; |
3be34541 | 771 | } |
9e4abcb5 | 772 | |
3be34541 SK |
773 | static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now) |
774 | { | |
775 | struct serverfd *serverfdp; | |
776 | struct listener *listener; | |
832af0ba SK |
777 | |
778 | for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) | |
779 | if (FD_ISSET(serverfdp->fd, set)) | |
780 | reply_query(serverfdp, daemon, now); | |
781 | ||
782 | for (listener = daemon->listeners; listener; listener = listener->next) | |
783 | { | |
784 | if (FD_ISSET(listener->fd, set)) | |
785 | receive_query(listener, daemon, now); | |
786 | ||
787 | #ifdef HAVE_TFTP | |
788 | if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set)) | |
789 | tftp_request(listener, daemon, now); | |
790 | #endif | |
3be34541 | 791 | |
832af0ba SK |
792 | if (FD_ISSET(listener->tcpfd, set)) |
793 | { | |
794 | int confd; | |
795 | struct irec *iface = NULL; | |
796 | pid_t p; | |
797 | ||
798 | while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR); | |
799 | ||
800 | if (confd == -1) | |
801 | continue; | |
802 | ||
803 | if (daemon->options & OPT_NOWILD) | |
804 | iface = listener->iface; | |
805 | else | |
806 | { | |
807 | union mysockaddr tcp_addr; | |
808 | socklen_t tcp_len = sizeof(union mysockaddr); | |
809 | /* Check for allowed interfaces when binding the wildcard address: | |
810 | we do this by looking for an interface with the same address as | |
811 | the local address of the TCP connection, then looking to see if that's | |
812 | an allowed interface. As a side effect, we get the netmask of the | |
813 | interface too, for localisation. */ | |
3be34541 | 814 | |
832af0ba SK |
815 | /* interface may be new since startup */ |
816 | if (enumerate_interfaces(daemon) && | |
817 | getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1) | |
818 | for (iface = daemon->interfaces; iface; iface = iface->next) | |
819 | if (sockaddr_isequal(&iface->addr, &tcp_addr)) | |
820 | break; | |
821 | } | |
822 | ||
823 | if (!iface) | |
824 | { | |
825 | shutdown(confd, SHUT_RDWR); | |
826 | close(confd); | |
827 | } | |
59353a6b | 828 | #ifndef NO_FORK |
832af0ba SK |
829 | else if (!(daemon->options & OPT_DEBUG) && (p = fork()) != 0) |
830 | { | |
831 | if (p != -1) | |
832 | { | |
833 | int i; | |
834 | for (i = 0; i < MAX_PROCS; i++) | |
835 | if (daemon->tcp_pids[i] == 0) | |
836 | { | |
837 | daemon->tcp_pids[i] = p; | |
838 | break; | |
839 | } | |
840 | } | |
841 | close(confd); | |
842 | } | |
843 | #endif | |
844 | else | |
845 | { | |
846 | unsigned char *buff; | |
847 | struct server *s; | |
848 | int flags; | |
849 | struct in_addr dst_addr_4; | |
850 | ||
851 | dst_addr_4.s_addr = 0; | |
852 | ||
7cebd20f SK |
853 | /* Arrange for SIGALARM after CHILD_LIFETIME seconds to |
854 | terminate the process. */ | |
832af0ba SK |
855 | if (!(daemon->options & OPT_DEBUG)) |
856 | alarm(CHILD_LIFETIME); | |
857 | ||
858 | /* start with no upstream connections. */ | |
859 | for (s = daemon->servers; s; s = s->next) | |
7cebd20f | 860 | s->tcpfd = -1; |
832af0ba SK |
861 | |
862 | /* The connected socket inherits non-blocking | |
863 | attribute from the listening socket. | |
864 | Reset that here. */ | |
865 | if ((flags = fcntl(confd, F_GETFL, 0)) != -1) | |
866 | fcntl(confd, F_SETFL, flags & ~O_NONBLOCK); | |
867 | ||
868 | if (listener->family == AF_INET) | |
869 | dst_addr_4 = iface->addr.in.sin_addr; | |
870 | ||
871 | buff = tcp_request(daemon, confd, now, dst_addr_4, iface->netmask); | |
7cebd20f | 872 | |
832af0ba SK |
873 | shutdown(confd, SHUT_RDWR); |
874 | close(confd); | |
875 | ||
876 | if (buff) | |
877 | free(buff); | |
878 | ||
879 | for (s = daemon->servers; s; s = s->next) | |
880 | if (s->tcpfd != -1) | |
881 | { | |
882 | shutdown(s->tcpfd, SHUT_RDWR); | |
883 | close(s->tcpfd); | |
884 | } | |
7cebd20f | 885 | #ifndef NO_FORK |
832af0ba SK |
886 | if (!(daemon->options & OPT_DEBUG)) |
887 | _exit(0); | |
59353a6b | 888 | #endif |
832af0ba SK |
889 | } |
890 | } | |
891 | } | |
3be34541 SK |
892 | } |
893 | ||
7cebd20f | 894 | |
5e9e0efb SK |
895 | int make_icmp_sock(void) |
896 | { | |
7cebd20f | 897 | int fd; |
5e9e0efb SK |
898 | int zeroopt = 0; |
899 | ||
900 | if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1) | |
901 | { | |
7cebd20f | 902 | if (!fix_fd(fd) || |
5e9e0efb SK |
903 | setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1) |
904 | { | |
905 | close(fd); | |
906 | fd = -1; | |
907 | } | |
908 | } | |
909 | ||
910 | return fd; | |
911 | } | |
912 | ||
3be34541 SK |
913 | int icmp_ping(struct daemon *daemon, struct in_addr addr) |
914 | { | |
5e9e0efb | 915 | /* Try and get an ICMP echo from a machine. */ |
3be34541 SK |
916 | |
917 | /* Note that whilst in the three second wait, we check for | |
832af0ba | 918 | (and service) events on the DNS and TFTP sockets, (so doing that |
3be34541 SK |
919 | better not use any resources our caller has in use...) |
920 | but we remain deaf to signals or further DHCP packets. */ | |
921 | ||
5e9e0efb | 922 | int fd; |
3be34541 SK |
923 | struct sockaddr_in saddr; |
924 | struct { | |
925 | struct ip ip; | |
926 | struct icmp icmp; | |
927 | } packet; | |
928 | unsigned short id = rand16(); | |
929 | unsigned int i, j; | |
5e9e0efb | 930 | int gotreply = 0; |
3be34541 | 931 | time_t start, now; |
5e9e0efb SK |
932 | |
933 | #ifdef HAVE_LINUX_NETWORK | |
934 | if ((fd = make_icmp_sock()) == -1) | |
935 | return 0; | |
936 | #else | |
937 | int opt = 2000; | |
938 | fd = daemon->dhcp_icmp_fd; | |
939 | setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)); | |
940 | #endif | |
941 | ||
3be34541 SK |
942 | saddr.sin_family = AF_INET; |
943 | saddr.sin_port = 0; | |
944 | saddr.sin_addr = addr; | |
945 | #ifdef HAVE_SOCKADDR_SA_LEN | |
946 | saddr.sin_len = sizeof(struct sockaddr_in); | |
947 | #endif | |
948 | ||
949 | memset(&packet.icmp, 0, sizeof(packet.icmp)); | |
950 | packet.icmp.icmp_type = ICMP_ECHO; | |
951 | packet.icmp.icmp_id = id; | |
952 | for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++) | |
953 | j += ((u16 *)&packet.icmp)[i]; | |
954 | while (j>>16) | |
955 | j = (j & 0xffff) + (j >> 16); | |
956 | packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j; | |
957 | ||
5e9e0efb | 958 | while (sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0, |
fd9fa481 SK |
959 | (struct sockaddr *)&saddr, sizeof(saddr)) == -1 && |
960 | retry_send()); | |
961 | ||
5e9e0efb SK |
962 | for (now = start = dnsmasq_time(); |
963 | difftime(now, start) < (float)PING_WAIT;) | |
fd9fa481 SK |
964 | { |
965 | struct timeval tv; | |
966 | fd_set rset; | |
967 | struct sockaddr_in faddr; | |
1697269c | 968 | int maxfd = fd; |
3d8df260 | 969 | socklen_t len = sizeof(faddr); |
fd9fa481 SK |
970 | |
971 | tv.tv_usec = 250000; | |
972 | tv.tv_sec = 0; | |
973 | ||
974 | FD_ZERO(&rset); | |
5e9e0efb | 975 | FD_SET(fd, &rset); |
1697269c | 976 | set_dns_listeners(daemon, now, &rset, &maxfd); |
832af0ba | 977 | |
fd9fa481 SK |
978 | if (select(maxfd+1, &rset, NULL, NULL, &tv) < 0) |
979 | FD_ZERO(&rset); | |
980 | ||
5e9e0efb | 981 | now = dnsmasq_time(); |
fd9fa481 | 982 | check_dns_listeners(daemon, &rset, now); |
832af0ba SK |
983 | |
984 | #ifdef HAVE_TFTP | |
985 | check_tftp_listeners(daemon, &rset, now); | |
986 | #endif | |
987 | ||
5e9e0efb SK |
988 | if (FD_ISSET(fd, &rset) && |
989 | recvfrom(fd, &packet, sizeof(packet), 0, | |
fd9fa481 SK |
990 | (struct sockaddr *)&faddr, &len) == sizeof(packet) && |
991 | saddr.sin_addr.s_addr == faddr.sin_addr.s_addr && | |
992 | packet.icmp.icmp_type == ICMP_ECHOREPLY && | |
993 | packet.icmp.icmp_seq == 0 && | |
994 | packet.icmp.icmp_id == id) | |
995 | { | |
996 | gotreply = 1; | |
997 | break; | |
998 | } | |
999 | } | |
1000 | ||
5e9e0efb SK |
1001 | #ifdef HAVE_LINUX_NETWORK |
1002 | close(fd); | |
1003 | #else | |
3be34541 | 1004 | opt = 1; |
5e9e0efb SK |
1005 | setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)); |
1006 | #endif | |
1007 | ||
3be34541 SK |
1008 | return gotreply; |
1009 | } | |
0a852541 SK |
1010 | |
1011 |