blob: d7029cefd5eaed3446ee38d1b254f4f9ca14ba99 [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 Kelley5e9e0ef2006-04-17 14:24:29 +010055 int flags, piperead, pipefd[2];
56 unsigned char sig;
Simon Kelley33820b72004-04-03 21:10:00 +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 Kelley9e4abcb2004-01-22 19:47:41 +0000131 forward_init(1);
Simon Kelley3be34542004-09-11 19:12:13 +0100132 cache_init(daemon->cachesize, daemon->options & OPT_LOG);
Simon Kelley44a2a312004-03-10 20:04:35 +0000133
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100134 now = dnsmasq_time();
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000135
Simon Kelley3be34542004-09-11 19:12:13 +0100136 if (daemon->dhcp)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000137 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100138#if !defined(HAVE_LINUX_NETWORK) && !defined(IP_RECVIF)
Simon Kelleyde379512004-06-22 20:23:33 +0100139 int c;
140 struct iname *tmp;
Simon Kelley3be34542004-09-11 19:12:13 +0100141 for (c = 0, tmp = daemon->if_names; tmp; tmp = tmp->next)
Simon Kelleyde379512004-06-22 20:23:33 +0100142 if (!tmp->isloop)
143 c++;
144 if (c != 1)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000145 die(_("must set exactly one interface on broken systems without IP_RECVIF"), NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100146#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100147 dhcp_init(daemon);
148 lease_init(daemon, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000149 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100150
Simon Kelley3d8df262005-08-29 12:19:27 +0100151 if (daemon->options & OPT_DBUS)
152#ifdef HAVE_DBUS
153 {
154 char *err;
155 daemon->dbus = NULL;
156 daemon->watches = NULL;
157 if ((err = dbus_init(daemon)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000158 die(_("DBus error: %s"), err);
Simon Kelley3d8df262005-08-29 12:19:27 +0100159 }
160#else
Simon Kelleycdeda282006-03-16 20:16:06 +0000161 die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL);
Simon Kelley3d8df262005-08-29 12:19:27 +0100162#endif
163
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100164 /* If query_port is set then create a socket now, before dumping root
165 for use to access nameservers without more specific source addresses.
166 This allows query_port to be a low port */
Simon Kelley3be34542004-09-11 19:12:13 +0100167 if (daemon->query_port)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100168 {
169 union mysockaddr addr;
170 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
178 addr.in6.sin6_family = AF_INET6;
179 addr.in6.sin6_addr = in6addr_any;
Simon Kelley3be34542004-09-11 19:12:13 +0100180 addr.in6.sin6_port = htons(daemon->query_port);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100181 addr.in6.sin6_flowinfo = 0;
182 addr.in6.sin6_scope_id = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100183#ifdef HAVE_SOCKADDR_SA_LEN
184 addr.in6.sin6_len = sizeof(struct sockaddr_in6);
185#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100186 allocate_sfd(&addr, &daemon->sfds);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100187#endif
188 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000189
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100190 /* Use a pipe to carry signals back to the event loop in a race-free manner */
191 if (pipe(pipefd) == -1 ||
192 (flags = fcntl(pipefd[0], F_GETFL)) == -1 ||
193 fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK) == -1 ||
194 (flags = fcntl(pipefd[1], F_GETFL)) == -1 ||
195 fcntl(pipefd[1], F_SETFL, flags | O_NONBLOCK) == -1)
196 die(_("cannot create pipe: %s"), NULL);
197
198 piperead = pipefd[0];
199 pipewrite = pipefd[1];
200 /* prime the pipe to load stuff first time. */
201 sig = SIGHUP;
202 write(pipewrite, &sig, 1);
203
204 if (!(daemon->options & OPT_DEBUG))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000205 {
206 FILE *pidfile;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100207 struct passwd *ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
Simon Kelley3d8df262005-08-29 12:19:27 +0100208 fd_set test_set;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100209 int maxfd, i;
210
211#ifdef HAVE_LINUX_NETWORK
212 cap_user_header_t hdr = NULL;
213 cap_user_data_t data = NULL;
214
215 /* On linux, we keep CAP_NETADMIN (for ARP-injection) and
216 CAP_NET_RAW (for icmp) if we're doing dhcp */
217 if (ent_pw)
218 {
219 hdr = safe_malloc(sizeof(*hdr));
220 data = safe_malloc(sizeof(*data));
221 hdr->version = _LINUX_CAPABILITY_VERSION;
222 hdr->pid = 0; /* this process */
223 data->effective = data->permitted = data->inheritable =
224 (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
225 (1 << CAP_SETGID) | (1 << CAP_SETUID);
226
227 /* Tell kernel to not clear capabilities when dropping root */
228 if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
Simon Kelley309331f2006-04-22 15:05:01 +0100229 {
230 bad_capabilities = errno;
231 ent_pw = NULL;
232 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100233 }
234#endif
235
Simon Kelley3d8df262005-08-29 12:19:27 +0100236 FD_ZERO(&test_set);
237 maxfd = set_dns_listeners(daemon, &test_set, -1);
238#ifdef HAVE_DBUS
239 maxfd = set_dbus_listeners(daemon, maxfd, &test_set, &test_set, &test_set);
240#endif
241
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000242 /* The following code "daemonizes" the process.
243 See Stevens section 12.4 */
Simon Kelley3d8df262005-08-29 12:19:27 +0100244
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000245#ifndef NO_FORK
Simon Kelley3be34542004-09-11 19:12:13 +0100246 if (!(daemon->options & OPT_NO_FORK))
247 {
248 if (fork() != 0 )
249 exit(0);
250
251 setsid();
252
253 if (fork() != 0)
254 exit(0);
255 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000256#endif
257
258 chdir("/");
259 umask(022); /* make pidfile 0644 */
260
261 /* write pidfile _after_ forking ! */
Simon Kelley3be34542004-09-11 19:12:13 +0100262 if (daemon->runfile && (pidfile = fopen(daemon->runfile, "w")))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000263 {
264 fprintf(pidfile, "%d\n", (int) getpid());
265 fclose(pidfile);
266 }
267
268 umask(0);
269
270 for (i=0; i<64; i++)
271 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100272 if (i == piperead || i == pipewrite)
273 continue;
274
275#ifdef HAVE_LINUX_NETWORK
276 if (i == daemon->netlinkfd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000277 continue;
Simon Kelley3be34542004-09-11 19:12:13 +0100278#endif
Simon Kelley3d8df262005-08-29 12:19:27 +0100279
Simon Kelley3be34542004-09-11 19:12:13 +0100280 if (daemon->dhcp &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000281 (i == fileno(daemon->lease_stream) ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100282#ifndef HAVE_LINUX_NETWORK
Simon Kelley3be34542004-09-11 19:12:13 +0100283 i == daemon->dhcp_raw_fd ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100284 i == daemon->dhcp_icmp_fd ||
285#endif
286 i == daemon->dhcpfd))
Simon Kelley3be34542004-09-11 19:12:13 +0100287 continue;
Simon Kelley3d8df262005-08-29 12:19:27 +0100288
289 if (i <= maxfd && FD_ISSET(i, &test_set))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100290 continue;
291
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000292 close(i);
293 }
294
295 /* Change uid and gid for security */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100296 if (ent_pw)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000297 {
298 gid_t dummy;
299 struct group *gp;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100300
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000301 /* remove all supplimentary groups */
302 setgroups(0, &dummy);
303 /* change group for /etc/ppp/resolv.conf
304 otherwise get the group for "nobody" */
Simon Kelley3be34542004-09-11 19:12:13 +0100305 if ((daemon->groupname && (gp = getgrnam(daemon->groupname))) ||
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000306 (gp = getgrgid(ent_pw->pw_gid)))
307 setgid(gp->gr_gid);
308 /* finally drop root */
309 setuid(ent_pw->pw_uid);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100310
311#ifdef HAVE_LINUX_NETWORK
312 data->effective = data->permitted =
313 (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
314 data->inheritable = 0;
315
316 /* lose the setuid and setgid capbilities */
317 capset(hdr, data);
318#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000319 }
320 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100321#ifdef HAVE_LINUX_NETWORK
322 else
323 prctl(PR_SET_DUMPABLE, 1);
324#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000325
326 openlog("dnsmasq",
Simon Kelley3be34542004-09-11 19:12:13 +0100327 DNSMASQ_LOG_OPT(daemon->options & OPT_DEBUG),
328 DNSMASQ_LOG_FAC(daemon->options & OPT_DEBUG));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000329
Simon Kelley3be34542004-09-11 19:12:13 +0100330 if (daemon->cachesize != 0)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000331 syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000332 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000333 syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION);
Simon Kelley3d8df262005-08-29 12:19:27 +0100334
Simon Kelleyb8187c82005-11-26 21:46:27 +0000335 syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
Simon Kelley3d8df262005-08-29 12:19:27 +0100336
337#ifdef HAVE_DBUS
338 if (daemon->options & OPT_DBUS)
339 {
340 if (daemon->dbus)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000341 syslog(LOG_INFO, _("DBus support enabled: connected to system bus"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100342 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000343 syslog(LOG_INFO, _("DBus support enabled: bus connection pending"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100344 }
345#endif
346
Simon Kelleyde379512004-06-22 20:23:33 +0100347 if (bind_fallback)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000348 syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
Simon Kelleyde379512004-06-22 20:23:33 +0100349
Simon Kelley26128d22004-11-14 16:43:54 +0000350 if (!(daemon->options & OPT_NOWILD))
351 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
352 if (if_tmp->name && !if_tmp->used)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000353 syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100354
Simon Kelley3be34542004-09-11 19:12:13 +0100355 if (daemon->dhcp)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000356 {
Simon Kelley3be34542004-09-11 19:12:13 +0100357 struct dhcp_context *dhcp_tmp;
Simon Kelley0a852542005-03-23 20:28:59 +0000358
Simon Kelley3be34542004-09-11 19:12:13 +0100359 for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100360 {
Simon Kelley0a852542005-03-23 20:28:59 +0000361 prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
Simon Kelley3be34542004-09-11 19:12:13 +0100362 strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
Simon Kelley3be34542004-09-11 19:12:13 +0100363 syslog(LOG_INFO,
Simon Kelley0a852542005-03-23 20:28:59 +0000364 (dhcp_tmp->flags & CONTEXT_STATIC) ?
Simon Kelleyb8187c82005-11-26 21:46:27 +0000365 _("DHCP, static leases only on %.0s%s, lease time %s") :
366 _("DHCP, IP range %s -- %s, lease time %s"),
Simon Kelley0a852542005-03-23 20:28:59 +0000367 daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100368 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000369 }
370
Simon Kelley3be34542004-09-11 19:12:13 +0100371 if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
Simon Kelley309331f2006-04-22 15:05:01 +0100372 {
373 if (bad_capabilities)
374 {
375 errno = bad_capabilities;
376 syslog(LOG_WARNING, _("warning: setting capabilities failed: %m"));
377 }
378 syslog(LOG_WARNING, _("running as root"));
379 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000380
Simon Kelley3d8df262005-08-29 12:19:27 +0100381 check_servers(daemon);
Simon Kelley3be34542004-09-11 19:12:13 +0100382
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100383 pid = getpid();
384
385 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000386 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100387 int maxfd;
388 struct timeval t, *tp = NULL;
Simon Kelley3d8df262005-08-29 12:19:27 +0100389 fd_set rset, wset, eset;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100390
391 t.tv_sec = 0; /* no warning */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000392
393 FD_ZERO(&rset);
Simon Kelley3d8df262005-08-29 12:19:27 +0100394 FD_ZERO(&wset);
395 FD_ZERO(&eset);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000396
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100397 maxfd = set_dns_listeners(daemon, &rset, -1);
398
399#ifdef HAVE_DBUS
400 /* Whilst polling for the dbus, wake every quarter second */
401 if ((daemon->options & OPT_DBUS) && !daemon->dbus)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000402 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100403 tp = &t;
404 tp->tv_sec = 0;
405 tp->tv_usec = 250000;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000406 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100407
408 maxfd = set_dbus_listeners(daemon, maxfd, &rset, &wset, &eset);
409#endif
410
411 if (daemon->dhcp)
412 {
413 FD_SET(daemon->dhcpfd, &rset);
414 if (daemon->dhcpfd > maxfd)
415 maxfd = daemon->dhcpfd;
416 }
417
418#ifdef HAVE_LINUX_NETWORK
419 FD_SET(daemon->netlinkfd, &rset);
420 if (daemon->netlinkfd > maxfd)
421 maxfd = daemon->netlinkfd;
422#endif
Simon Kelley3d8df262005-08-29 12:19:27 +0100423
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100424 FD_SET(piperead, &rset);
425 if (piperead > maxfd)
426 maxfd = piperead;
427
428 if (select(maxfd+1, &rset, &wset, &eset, tp) < 0)
429 {
430 /* otherwise undefined after error */
431 FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
432 }
433
434 now = dnsmasq_time();
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000435
436 /* Check for changes to resolv files once per second max. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100437 /* Don't go silent for long periods if the clock goes backwards. */
438 if (last == 0 || difftime(now, last) > 1.0 || difftime(now, last) < 1.0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000439 {
440 last = now;
Simon Kelley33820b72004-04-03 21:10:00 +0100441
442#ifdef HAVE_ISC_READER
Simon Kelley3be34542004-09-11 19:12:13 +0100443 if (daemon->lease_file && !daemon->dhcp)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100444 load_dhcp(daemon, now);
Simon Kelley33820b72004-04-03 21:10:00 +0100445#endif
446
Simon Kelley3be34542004-09-11 19:12:13 +0100447 if (!(daemon->options & OPT_NO_POLL))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000448 {
Simon Kelley3be34542004-09-11 19:12:13 +0100449 struct resolvc *res = daemon->resolv_files, *latest = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000450 struct stat statbuf;
Simon Kelley33820b72004-04-03 21:10:00 +0100451 time_t last_change = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000452 /* There may be more than one possible file.
453 Go through and find the one which changed _last_.
454 Warn of any which can't be read. */
455 while (res)
456 {
457 if (stat(res->name, &statbuf) == -1)
458 {
459 if (!res->logged)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000460 syslog(LOG_WARNING, _("failed to access %s: %m"), res->name);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000461 res->logged = 1;
462 }
463 else
464 {
465 res->logged = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100466 if (statbuf.st_mtime != res->mtime)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000467 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100468 res->mtime = statbuf.st_mtime;
469 if (difftime(res->mtime, last_change) > 0.0)
470 {
471 last_change = res->mtime;
472 latest = res;
473 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000474 }
475 }
476 res = res->next;
477 }
478
Simon Kelley3d8df262005-08-29 12:19:27 +0100479 if (latest)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000480 {
Simon Kelley3be34542004-09-11 19:12:13 +0100481 reload_servers(latest->name, daemon);
Simon Kelley3d8df262005-08-29 12:19:27 +0100482 check_servers(daemon);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000483 }
484 }
485 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000486
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100487 if (FD_ISSET(piperead, &rset))
488 {
489 if (read(piperead, &sig, 1) == 1)
490 switch (sig)
491 {
492 case SIGHUP:
493 clear_cache_and_reload(daemon);
494 if (daemon->resolv_files && (daemon->options & OPT_NO_POLL))
495 {
496 reload_servers(daemon->resolv_files->name, daemon);
497 check_servers(daemon);
498 }
499 break;
500
501 case SIGUSR1:
502 dump_cache(daemon, now);
503 break;
504
505 case SIGALRM:
506 if (daemon->dhcp)
507 lease_update_file(daemon);
508 break;
509
510 case SIGTERM:
511 syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
512 if (daemon->dhcp)
513 fclose(daemon->lease_stream);
514 exit(0);
515
516 case SIGCHLD:
517 /* See Stevens 5.10 */
518 while (waitpid(-1, NULL, WNOHANG) > 0)
519 daemon->num_kids--;
520 break;
521
522 }
523 }
524
525#ifdef HAVE_LINUX_NETWORK
526 if (FD_ISSET(daemon->netlinkfd, &rset))
Simon Kelleycdeda282006-03-16 20:16:06 +0000527 netlink_multicast(daemon);
528#endif
Simon Kelley3d8df262005-08-29 12:19:27 +0100529
530#ifdef HAVE_DBUS
531 /* if we didn't create a DBus connection, retry now. */
532 if ((daemon->options & OPT_DBUS) && !daemon->dbus)
533 {
534 char *err;
535 if ((err = dbus_init(daemon)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000536 syslog(LOG_WARNING, _("DBus error: %s"), err);
Simon Kelley3d8df262005-08-29 12:19:27 +0100537 if (daemon->dbus)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000538 syslog(LOG_INFO, _("connected to system DBus"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100539 }
540 check_dbus_listeners(daemon, &rset, &wset, &eset);
541#endif
542
Simon Kelley3be34542004-09-11 19:12:13 +0100543 check_dns_listeners(daemon, &rset, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000544
Simon Kelley3be34542004-09-11 19:12:13 +0100545 if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
546 dhcp_packet(daemon, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000547 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000548}
549
Simon Kelley3be34542004-09-11 19:12:13 +0100550static void sig_handler(int sig)
551{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100552 if (pid == 0)
553 {
554 /* ignore anything other than TERM during startup */
555 if (sig == SIGTERM)
556 exit(0);
557 }
558 else if (pid == getpid())
559 {
560 /* master process */
561 unsigned char sigchr = sig;
562 int errsave = errno;
563 write(pipewrite, &sigchr, 1);
564 errno = errsave;
565 }
566 else
Simon Kelley3be34542004-09-11 19:12:13 +0100567 {
568 /* alarm is used to kill children after a fixed time. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100569 if (sig == SIGALRM)
Simon Kelley3be34542004-09-11 19:12:13 +0100570 exit(0);
Simon Kelley3be34542004-09-11 19:12:13 +0100571 }
572}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000573
Simon Kelley3d8df262005-08-29 12:19:27 +0100574
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100575void clear_cache_and_reload(struct daemon *daemon)
Simon Kelley3d8df262005-08-29 12:19:27 +0100576{
577 cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
578 if (daemon->dhcp)
579 {
580 if (daemon->options & OPT_ETHERS)
581 dhcp_read_ethers(daemon);
582 dhcp_update_configs(daemon->dhcp_conf);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000583 lease_update_from_configs(daemon);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100584 lease_update_file(daemon);
Simon Kelley3d8df262005-08-29 12:19:27 +0100585 lease_update_dns(daemon);
586 }
587}
588
Simon Kelley3be34542004-09-11 19:12:13 +0100589static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd)
590{
591 struct serverfd *serverfdp;
592 struct listener *listener;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000593
Simon Kelley3be34542004-09-11 19:12:13 +0100594 for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
595 {
596 FD_SET(serverfdp->fd, set);
597 if (serverfdp->fd > maxfd)
598 maxfd = serverfdp->fd;
599 }
600
601 for (listener = daemon->listeners; listener; listener = listener->next)
602 {
603 FD_SET(listener->fd, set);
604 if (listener->fd > maxfd)
605 maxfd = listener->fd;
606 FD_SET(listener->tcpfd, set);
607 if (listener->tcpfd > maxfd)
608 maxfd = listener->tcpfd;
609 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000610
Simon Kelley3be34542004-09-11 19:12:13 +0100611 return maxfd;
612}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000613
Simon Kelley3be34542004-09-11 19:12:13 +0100614static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
615{
616 struct serverfd *serverfdp;
617 struct listener *listener;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000618
Simon Kelley3be34542004-09-11 19:12:13 +0100619 for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
620 if (FD_ISSET(serverfdp->fd, set))
621 reply_query(serverfdp, daemon, now);
622
623 for (listener = daemon->listeners; listener; listener = listener->next)
624 {
625 if (FD_ISSET(listener->fd, set))
626 receive_query(listener, daemon, now);
627
628 if (FD_ISSET(listener->tcpfd, set))
629 {
630 int confd;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100631
Simon Kelley3be34542004-09-11 19:12:13 +0100632 while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
633
634 if (confd != -1)
635 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100636 struct irec *iface = NULL;
637
638 if (daemon->options & OPT_NOWILD)
639 iface = listener->iface;
640 else
641 {
642 union mysockaddr tcp_addr;
643 socklen_t tcp_len = sizeof(union mysockaddr);
644 /* Check for allowed interfaces when binding the wildcard address:
645 we do this by looking for an interface with the same address as
646 the local address of the TCP connection, then looking to see if that's
647 an allowed interface. As a side effect, we get the netmask of the
648 interface too, for localisation. */
649
650 /* interface may be new since startup */
651 if (enumerate_interfaces(daemon) &&
652 getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
653 for (iface = daemon->interfaces; iface; iface = iface->next)
654 if (sockaddr_isequal(&iface->addr, &tcp_addr))
655 break;
656 }
657
658 if ((daemon->num_kids >= MAX_PROCS) || !iface)
Simon Kelley3be34542004-09-11 19:12:13 +0100659 close(confd);
Simon Kelley59353a62004-11-21 19:34:28 +0000660#ifndef NO_FORK
Simon Kelley3be34542004-09-11 19:12:13 +0100661 else if (!(daemon->options & OPT_DEBUG) && fork())
662 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100663 daemon->num_kids++;
Simon Kelley3be34542004-09-11 19:12:13 +0100664 close(confd);
665 }
Simon Kelley59353a62004-11-21 19:34:28 +0000666#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100667 else
668 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100669 unsigned char *buff;
Simon Kelley3be34542004-09-11 19:12:13 +0100670 struct server *s;
671 int flags;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100672 struct in_addr dst_addr_4;
673
674 dst_addr_4.s_addr = 0;
675
Simon Kelley3be34542004-09-11 19:12:13 +0100676 /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
677 terminate the process. */
678 if (!(daemon->options & OPT_DEBUG))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100679 alarm(CHILD_LIFETIME);
Simon Kelley3be34542004-09-11 19:12:13 +0100680
681 /* start with no upstream connections. */
682 for (s = daemon->servers; s; s = s->next)
683 s->tcpfd = -1;
684
685 /* The connected socket inherits non-blocking
686 attribute from the listening socket.
687 Reset that here. */
688 if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
689 fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
690
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000691 if (listener->family == AF_INET)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100692 dst_addr_4 = iface->addr.in.sin_addr;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000693
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100694 buff = tcp_request(daemon, confd, now, dst_addr_4, iface->netmask);
Simon Kelley3be34542004-09-11 19:12:13 +0100695
696 if (!(daemon->options & OPT_DEBUG))
697 exit(0);
698
699 close(confd);
700 if (buff)
701 free(buff);
702 for (s = daemon->servers; s; s = s->next)
703 if (s->tcpfd != -1)
704 close(s->tcpfd);
705 }
706 }
707 }
708 }
709}
710
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100711int make_icmp_sock(void)
712{
713 int fd, flags;
714 int zeroopt = 0;
715
716 if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1)
717 {
718 if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
719 fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
720 setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
721 {
722 close(fd);
723 fd = -1;
724 }
725 }
726
727 return fd;
728}
729
Simon Kelley3be34542004-09-11 19:12:13 +0100730int icmp_ping(struct daemon *daemon, struct in_addr addr)
731{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100732 /* Try and get an ICMP echo from a machine. */
Simon Kelley3be34542004-09-11 19:12:13 +0100733
734 /* Note that whilst in the three second wait, we check for
735 (and service) events on the DNS sockets, (so doing that
736 better not use any resources our caller has in use...)
737 but we remain deaf to signals or further DHCP packets. */
738
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100739 int fd;
Simon Kelley3be34542004-09-11 19:12:13 +0100740 struct sockaddr_in saddr;
741 struct {
742 struct ip ip;
743 struct icmp icmp;
744 } packet;
745 unsigned short id = rand16();
746 unsigned int i, j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100747 int gotreply = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100748 time_t start, now;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100749
750#ifdef HAVE_LINUX_NETWORK
751 if ((fd = make_icmp_sock()) == -1)
752 return 0;
753#else
754 int opt = 2000;
755 fd = daemon->dhcp_icmp_fd;
756 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
757#endif
758
Simon Kelley3be34542004-09-11 19:12:13 +0100759 saddr.sin_family = AF_INET;
760 saddr.sin_port = 0;
761 saddr.sin_addr = addr;
762#ifdef HAVE_SOCKADDR_SA_LEN
763 saddr.sin_len = sizeof(struct sockaddr_in);
764#endif
765
766 memset(&packet.icmp, 0, sizeof(packet.icmp));
767 packet.icmp.icmp_type = ICMP_ECHO;
768 packet.icmp.icmp_id = id;
769 for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++)
770 j += ((u16 *)&packet.icmp)[i];
771 while (j>>16)
772 j = (j & 0xffff) + (j >> 16);
773 packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
774
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100775 while (sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100776 (struct sockaddr *)&saddr, sizeof(saddr)) == -1 &&
777 retry_send());
778
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100779 for (now = start = dnsmasq_time();
780 difftime(now, start) < (float)PING_WAIT;)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100781 {
782 struct timeval tv;
783 fd_set rset;
784 struct sockaddr_in faddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100785 int maxfd;
786 socklen_t len = sizeof(faddr);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100787
788 tv.tv_usec = 250000;
789 tv.tv_sec = 0;
790
791 FD_ZERO(&rset);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100792 FD_SET(fd, &rset);
793 maxfd = set_dns_listeners(daemon, &rset, fd);
Simon Kelley3be34542004-09-11 19:12:13 +0100794
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100795 if (select(maxfd+1, &rset, NULL, NULL, &tv) < 0)
796 FD_ZERO(&rset);
797
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100798 now = dnsmasq_time();
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100799 check_dns_listeners(daemon, &rset, now);
800
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100801 if (FD_ISSET(fd, &rset) &&
802 recvfrom(fd, &packet, sizeof(packet), 0,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100803 (struct sockaddr *)&faddr, &len) == sizeof(packet) &&
804 saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
805 packet.icmp.icmp_type == ICMP_ECHOREPLY &&
806 packet.icmp.icmp_seq == 0 &&
807 packet.icmp.icmp_id == id)
808 {
809 gotreply = 1;
810 break;
811 }
812 }
813
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100814#ifdef HAVE_LINUX_NETWORK
815 close(fd);
816#else
Simon Kelley3be34542004-09-11 19:12:13 +0100817 opt = 1;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100818 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
819#endif
820
Simon Kelley3be34542004-09-11 19:12:13 +0100821 return gotreply;
822}
Simon Kelley0a852542005-03-23 20:28:59 +0000823
824