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