blob: 4c8cf8a53dd7b7b87676a07b05b7c6701edcb1bc [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 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
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelleyc72daea2012-01-05 21:33:27 +000017/* Declare static char *compiler_opts in config.h */
18#define DNSMASQ_COMPILE_OPTS
19
Simon Kelley9e4abcb2004-01-22 19:47:41 +000020#include "dnsmasq.h"
21
Simon Kelley5aabfc72007-08-29 11:24:47 +010022struct daemon *daemon;
23
Simon Kelley5aabfc72007-08-29 11:24:47 +010024static volatile pid_t pid = 0;
25static volatile int pipewrite;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000026
Simon Kelley5aabfc72007-08-29 11:24:47 +010027static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
28static void check_dns_listeners(fd_set *set, time_t now);
Simon Kelley3be34542004-09-11 19:12:13 +010029static void sig_handler(int sig);
Simon Kelley5aabfc72007-08-29 11:24:47 +010030static void async_event(int pipe, time_t now);
Simon Kelleyc72daea2012-01-05 21:33:27 +000031static void fatal_event(struct event_desc *ev, char *msg);
32static int read_event(int fd, struct event_desc *evp, char **msg);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000033
34int main (int argc, char **argv)
35{
Simon Kelleyde379512004-06-22 20:23:33 +010036 int bind_fallback = 0;
Simon Kelley9009d742008-11-14 20:04:27 +000037 time_t now;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000038 struct sigaction sigact;
Simon Kelley26128d22004-11-14 16:43:54 +000039 struct iname *if_tmp;
Simon Kelley1a6bca82008-07-11 11:11:42 +010040 int piperead, pipefd[2], err_pipe[2];
41 struct passwd *ent_pw = NULL;
Simon Kelleyc72daea2012-01-05 21:33:27 +000042#if defined(HAVE_SCRIPT)
Simon Kelley1a6bca82008-07-11 11:11:42 +010043 uid_t script_uid = 0;
44 gid_t script_gid = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +010045#endif
46 struct group *gp = NULL;
Simon Kelley5aabfc72007-08-29 11:24:47 +010047 long i, max_fd = sysconf(_SC_OPEN_MAX);
Simon Kelley1a6bca82008-07-11 11:11:42 +010048 char *baduser = NULL;
49 int log_err;
50#if defined(HAVE_LINUX_NETWORK)
51 cap_user_header_t hdr = NULL;
52 cap_user_data_t data = NULL;
Simon Kelley3b3f4412013-10-11 16:33:28 +010053 char *bound_device = NULL;
54 int did_bind = 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +010055#endif
Vladislav Grishenko408c3682013-09-24 16:18:49 +010056#if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
Simon Kelley1f776932012-12-16 19:46:08 +000057 struct dhcp_context *context;
Simon Kelleyff7eea22013-09-04 18:01:38 +010058 struct dhcp_relay *relay;
Vladislav Grishenko408c3682013-09-24 16:18:49 +010059#endif
Simon Kelley5aabfc72007-08-29 11:24:47 +010060
Simon Kelley824af852008-02-12 20:43:05 +000061#ifdef LOCALEDIR
Simon Kelleyb8187c82005-11-26 21:46:27 +000062 setlocale(LC_ALL, "");
63 bindtextdomain("dnsmasq", LOCALEDIR);
64 textdomain("dnsmasq");
65#endif
66
Simon Kelley9e4abcb2004-01-22 19:47:41 +000067 sigact.sa_handler = sig_handler;
68 sigact.sa_flags = 0;
69 sigemptyset(&sigact.sa_mask);
70 sigaction(SIGUSR1, &sigact, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +010071 sigaction(SIGUSR2, &sigact, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000072 sigaction(SIGHUP, &sigact, NULL);
73 sigaction(SIGTERM, &sigact, NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000074 sigaction(SIGALRM, &sigact, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +010075 sigaction(SIGCHLD, &sigact, NULL);
76
77 /* ignore SIGPIPE */
78 sigact.sa_handler = SIG_IGN;
79 sigaction(SIGPIPE, &sigact, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000080
Simon Kelley5aabfc72007-08-29 11:24:47 +010081 umask(022); /* known umask, create leases and pid files as 0644 */
82
83 read_opts(argc, argv, compile_opts);
84
Simon Kelley3be34542004-09-11 19:12:13 +010085 if (daemon->edns_pktsz < PACKETSZ)
86 daemon->edns_pktsz = PACKETSZ;
Simon Kelley0a852542005-03-23 20:28:59 +000087 daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
88 daemon->edns_pktsz : DNSMASQ_PACKETSZ;
89 daemon->packet = safe_malloc(daemon->packet_buff_sz);
Simon Kelley1a6bca82008-07-11 11:11:42 +010090
Simon Kelleyc72daea2012-01-05 21:33:27 +000091 daemon->addrbuff = safe_malloc(ADDRSTRLEN);
92
Simon Kelley4f7b3042012-11-28 21:27:02 +000093
Simon Kelley7622fc02009-06-04 20:32:05 +010094#ifdef HAVE_DHCP
Simon Kelley3be34542004-09-11 19:12:13 +010095 if (!daemon->lease_file)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000096 {
Simon Kelley52b92f42012-01-22 16:05:15 +000097 if (daemon->dhcp || daemon->dhcp6)
Simon Kelley3be34542004-09-11 19:12:13 +010098 daemon->lease_file = LEASEFILE;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000099 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100100#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000101
Simon Kelleya2761752012-01-18 16:07:21 +0000102 /* Close any file descriptors we inherited apart from std{in|out|err}
103
104 Ensure that at least stdin, stdout and stderr (fd 0, 1, 2) exist,
105 otherwise file descriptors we create can end up being 0, 1, or 2
106 and then get accidentally closed later when we make 0, 1, and 2
107 open to /dev/null. Normally we'll be started with 0, 1 and 2 open,
108 but it's not guaranteed. By opening /dev/null three times, we
109 ensure that we're not using those fds for real stuff. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100110 for (i = 0; i < max_fd; i++)
111 if (i != STDOUT_FILENO && i != STDERR_FILENO && i != STDIN_FILENO)
112 close(i);
Simon Kelleya2761752012-01-18 16:07:21 +0000113 else
114 open("/dev/null", O_RDWR);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100115
Simon Kelley801ca9a2012-03-06 19:30:17 +0000116#ifndef HAVE_LINUX_NETWORK
117# if !(defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))
Simon Kelley28866e92011-02-14 20:19:14 +0000118 if (!option_bool(OPT_NOWILD))
Simon Kelleyde379512004-06-22 20:23:33 +0100119 {
120 bind_fallback = 1;
Simon Kelley28866e92011-02-14 20:19:14 +0000121 set_option_bool(OPT_NOWILD);
Simon Kelleyde379512004-06-22 20:23:33 +0100122 }
Simon Kelley801ca9a2012-03-06 19:30:17 +0000123# endif
Simon Kelley2b5bae92012-06-26 16:55:23 +0100124
125 /* -- bind-dynamic not supported on !Linux, fall back to --bind-interfaces */
Simon Kelley54dd3932012-06-20 11:23:38 +0100126 if (option_bool(OPT_CLEVERBIND))
Simon Kelley2b5bae92012-06-26 16:55:23 +0100127 {
128 bind_fallback = 1;
129 set_option_bool(OPT_NOWILD);
Simon Kelley236e0722012-06-26 21:33:01 +0100130 reset_option_bool(OPT_CLEVERBIND);
Simon Kelley2b5bae92012-06-26 16:55:23 +0100131 }
Simon Kelley309331f2006-04-22 15:05:01 +0100132#endif
Simon Kelley2b5bae92012-06-26 16:55:23 +0100133
Simon Kelley832af0b2007-01-21 20:01:28 +0000134#ifndef HAVE_TFTP
Simon Kelley9b40cbf2012-07-13 19:58:26 +0100135 if (option_bool(OPT_TFTP))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100136 die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
Simon Kelley832af0b2007-01-21 20:01:28 +0000137#endif
138
Simon Kelley7de060b2011-08-26 17:24:52 +0100139#ifdef HAVE_CONNTRACK
140 if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
141 die (_("Cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
142#else
143 if (option_bool(OPT_CONNTRACK))
144 die(_("Conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
145#endif
146
Simon Kelley824af852008-02-12 20:43:05 +0000147#ifdef HAVE_SOLARIS_NETWORK
148 if (daemon->max_logs != 0)
149 die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF);
150#endif
151
Simon Kelley572b41e2011-02-18 18:11:18 +0000152#ifdef __ANDROID__
153 if (daemon->max_logs != 0)
154 die(_("asychronous logging is not available under Android"), NULL, EC_BADCONF);
155#endif
156
Simon Kelley4820dce2012-12-18 18:30:30 +0000157#ifndef HAVE_AUTH
158 if (daemon->authserver)
159 die(_("authoritative DNS not available: set HAVE_AUTH in src/config.h"), NULL, EC_BADCONF);
160#endif
161
Simon Kelley1a6bca82008-07-11 11:11:42 +0100162 rand_init();
163
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100164 now = dnsmasq_time();
Simon Kelley4f7b3042012-11-28 21:27:02 +0000165
Simon Kelleyb0ff8582013-02-06 09:57:47 +0000166 /* Create a serial at startup if not configured. */
Simon Kelley4f7b3042012-11-28 21:27:02 +0000167 if (daemon->authinterface && daemon->soa_sn == 0)
168#ifdef HAVE_BROKEN_RTC
Simon Kelleyb0ff8582013-02-06 09:57:47 +0000169 die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000170#else
171 daemon->soa_sn = now;
172#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000173
Simon Kelleyff7eea22013-09-04 18:01:38 +0100174#ifdef HAVE_DHCP6
175 if (daemon->dhcp6)
176 {
177 daemon->doing_ra = option_bool(OPT_RA);
Simon Kelley1f776932012-12-16 19:46:08 +0000178
Simon Kelleyff7eea22013-09-04 18:01:38 +0100179 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelley1f776932012-12-16 19:46:08 +0000180 {
Simon Kelleyff7eea22013-09-04 18:01:38 +0100181 if (context->flags & CONTEXT_DHCP)
182 daemon->doing_dhcp6 = 1;
183 if (context->flags & CONTEXT_RA)
184 daemon->doing_ra = 1;
Simon Kelleybb86e852012-12-17 22:00:53 +0000185#ifndef HAVE_LINUX_NETWORK
Simon Kelleyff7eea22013-09-04 18:01:38 +0100186 if (context->flags & CONTEXT_TEMPLATE)
187 die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF);
Simon Kelleybb86e852012-12-17 22:00:53 +0000188#endif
Simon Kelley1f776932012-12-16 19:46:08 +0000189 }
Simon Kelley1f776932012-12-16 19:46:08 +0000190 }
Simon Kelleyff7eea22013-09-04 18:01:38 +0100191#endif
192
193#ifdef HAVE_DHCP
194 /* Note that order matters here, we must call lease_init before
195 creating any file descriptors which shouldn't be leaked
196 to the lease-script init process. We need to call common_init
197 before lease_init to allocate buffers it uses.*/
198 if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6)
199 {
200 dhcp_common_init();
201 if (daemon->dhcp || daemon->doing_dhcp6)
202 lease_init(now);
203 }
204
205 if (daemon->dhcp || daemon->relay4)
206 dhcp_init();
207
208# ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100209 if (daemon->doing_ra || daemon->doing_dhcp6 || daemon->relay6)
Simon Kelleyff7eea22013-09-04 18:01:38 +0100210 ra_init(now);
211
212 if (daemon->doing_dhcp6 || daemon->relay6)
213 dhcp6_init();
214# endif
Simon Kelley843c96b2012-02-27 17:42:38 +0000215
Simon Kelley7622fc02009-06-04 20:32:05 +0100216#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100217
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000218#ifdef HAVE_IPSET
219 if (daemon->ipsets)
220 ipset_init();
221#endif
222
Simon Kelley801ca9a2012-03-06 19:30:17 +0000223#ifdef HAVE_LINUX_NETWORK
Simon Kelley801ca9a2012-03-06 19:30:17 +0000224 netlink_init();
Simon Kelley8445f5d2012-12-17 21:54:08 +0000225
Simon Kelley54dd3932012-06-20 11:23:38 +0100226 if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
227 die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
Simon Kelley801ca9a2012-03-06 19:30:17 +0000228#endif
229
Simon Kelley115ac3e2013-05-20 11:28:32 +0100230 if (!enumerate_interfaces(1) || !enumerate_interfaces(0))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100231 die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
Simon Kelley843c96b2012-02-27 17:42:38 +0000232
Simon Kelley54dd3932012-06-20 11:23:38 +0100233 if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100234 {
Simon Kelley74c95c22011-10-19 09:33:39 +0100235 create_bound_listeners(1);
Simon Kelley54dd3932012-06-20 11:23:38 +0100236
237 if (!option_bool(OPT_CLEVERBIND))
238 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
239 if (if_tmp->name && !if_tmp->used)
240 die(_("unknown interface %s"), if_tmp->name, EC_BADNET);
Simon Kelley9380ba72012-04-16 14:41:56 +0100241
242#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP)
243 /* after enumerate_interfaces() */
Simon Kelley3b3f4412013-10-11 16:33:28 +0100244 bound_device = whichdevice();
245
Simon Kelley10ae7b52013-09-05 20:08:01 +0100246 if (daemon->dhcp)
Simon Kelley9380ba72012-04-16 14:41:56 +0100247 {
Simon Kelley3b3f4412013-10-11 16:33:28 +0100248 if (!daemon->relay4 && bound_device)
249 {
250 bindtodevice(bound_device, daemon->dhcpfd);
251 did_bind = 1;
252 }
253 if (daemon->enable_pxe && bound_device)
254 {
255 bindtodevice(bound_device, daemon->pxefd);
256 did_bind = 1;
257 }
Simon Kelley9380ba72012-04-16 14:41:56 +0100258 }
259#endif
260
261#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6)
Simon Kelley3b3f4412013-10-11 16:33:28 +0100262 if (daemon->doing_dhcp6 && !daemon->relay6 && bound_device)
263 {
264 bindtodevice(bound_device, daemon->dhcp6fd);
265 did_bind = 1;
266 }
Simon Kelley9380ba72012-04-16 14:41:56 +0100267#endif
Simon Kelley5aabfc72007-08-29 11:24:47 +0100268 }
Simon Kelley28866e92011-02-14 20:19:14 +0000269 else
Simon Kelley74c95c22011-10-19 09:33:39 +0100270 create_wildcard_listeners();
Simon Kelley5d162f22012-12-20 14:55:46 +0000271
272#ifdef HAVE_DHCP6
273 /* after enumerate_interfaces() */
Simon Kelleyff7eea22013-09-04 18:01:38 +0100274 if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
Simon Kelley5d162f22012-12-20 14:55:46 +0000275 join_multicast(1);
Simon Kelley3511a922013-11-07 10:28:11 +0000276
277 /* After netlink_init() and before create_helper() */
278 lease_make_duid(now);
Simon Kelley5d162f22012-12-20 14:55:46 +0000279#endif
Simon Kelley5aabfc72007-08-29 11:24:47 +0100280
Simon Kelley824af852008-02-12 20:43:05 +0000281 if (daemon->port != 0)
282 cache_init();
Simon Kelley1a6bca82008-07-11 11:11:42 +0100283
Simon Kelley28866e92011-02-14 20:19:14 +0000284 if (option_bool(OPT_DBUS))
Simon Kelley3d8df262005-08-29 12:19:27 +0100285#ifdef HAVE_DBUS
286 {
287 char *err;
288 daemon->dbus = NULL;
289 daemon->watches = NULL;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100290 if ((err = dbus_init()))
291 die(_("DBus error: %s"), err, EC_MISC);
Simon Kelley3d8df262005-08-29 12:19:27 +0100292 }
293#else
Simon Kelley5aabfc72007-08-29 11:24:47 +0100294 die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, EC_BADCONF);
Simon Kelley3d8df262005-08-29 12:19:27 +0100295#endif
296
Simon Kelley824af852008-02-12 20:43:05 +0000297 if (daemon->port != 0)
298 pre_allocate_sfds();
Simon Kelley1a6bca82008-07-11 11:11:42 +0100299
Simon Kelleyc72daea2012-01-05 21:33:27 +0000300#if defined(HAVE_SCRIPT)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100301 /* Note getpwnam returns static storage */
Simon Kelley843c96b2012-02-27 17:42:38 +0000302 if ((daemon->dhcp || daemon->dhcp6) &&
303 daemon->scriptuser &&
Simon Kelleyc72daea2012-01-05 21:33:27 +0000304 (daemon->lease_change_command || daemon->luascript))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100305 {
306 if ((ent_pw = getpwnam(daemon->scriptuser)))
307 {
308 script_uid = ent_pw->pw_uid;
309 script_gid = ent_pw->pw_gid;
310 }
311 else
312 baduser = daemon->scriptuser;
313 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100314#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000315
Simon Kelley1a6bca82008-07-11 11:11:42 +0100316 if (daemon->username && !(ent_pw = getpwnam(daemon->username)))
317 baduser = daemon->username;
318 else if (daemon->groupname && !(gp = getgrnam(daemon->groupname)))
319 baduser = daemon->groupname;
320
321 if (baduser)
322 die(_("unknown user or group: %s"), baduser, EC_BADCONF);
323
324 /* implement group defaults, "dip" if available, or group associated with uid */
325 if (!daemon->group_set && !gp)
326 {
327 if (!(gp = getgrnam(CHGRP)) && ent_pw)
328 gp = getgrgid(ent_pw->pw_gid);
329
330 /* for error message */
331 if (gp)
332 daemon->groupname = gp->gr_name;
333 }
334
335#if defined(HAVE_LINUX_NETWORK)
336 /* determine capability API version here, while we can still
337 call safe_malloc */
338 if (ent_pw && ent_pw->pw_uid != 0)
339 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100340 int capsize = 1; /* for header version 1 */
Simon Kelley3927da42008-07-20 15:10:39 +0100341 hdr = safe_malloc(sizeof(*hdr));
342
Simon Kelley1a6bca82008-07-11 11:11:42 +0100343 /* find version supported by kernel */
344 memset(hdr, 0, sizeof(*hdr));
345 capget(hdr, NULL);
346
347 if (hdr->version != LINUX_CAPABILITY_VERSION_1)
348 {
349 /* if unknown version, use largest supported version (3) */
350 if (hdr->version != LINUX_CAPABILITY_VERSION_2)
351 hdr->version = LINUX_CAPABILITY_VERSION_3;
352 capsize = 2;
353 }
354
355 data = safe_malloc(sizeof(*data) * capsize);
356 memset(data, 0, sizeof(*data) * capsize);
357 }
358#endif
359
Simon Kelley5aabfc72007-08-29 11:24:47 +0100360 /* Use a pipe to carry signals and other events back to the event loop
Simon Kelley1a6bca82008-07-11 11:11:42 +0100361 in a race-free manner and another to carry errors to daemon-invoking process */
362 safe_pipe(pipefd, 1);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100363
364 piperead = pipefd[0];
365 pipewrite = pipefd[1];
366 /* prime the pipe to load stuff first time. */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000367 send_event(pipewrite, EVENT_RELOAD, 0, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100368
369 err_pipe[1] = -1;
Simon Kelley16972692006-10-16 20:04:18 +0100370
Simon Kelley28866e92011-02-14 20:19:14 +0000371 if (!option_bool(OPT_DEBUG))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000372 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000373 /* The following code "daemonizes" the process.
374 See Stevens section 12.4 */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100375
Simon Kelley9e038942008-05-30 20:06:34 +0100376 if (chdir("/") != 0)
377 die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC);
378
Simon Kelley16972692006-10-16 20:04:18 +0100379#ifndef NO_FORK
Simon Kelley28866e92011-02-14 20:19:14 +0000380 if (!option_bool(OPT_NO_FORK))
Simon Kelley3be34542004-09-11 19:12:13 +0100381 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100382 pid_t pid;
383
Simon Kelley1a6bca82008-07-11 11:11:42 +0100384 /* pipe to carry errors back to original process.
385 When startup is complete we close this and the process terminates. */
386 safe_pipe(err_pipe, 0);
387
Simon Kelley7622fc02009-06-04 20:32:05 +0100388 if ((pid = fork()) == -1)
389 /* fd == -1 since we've not forked, never returns. */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000390 send_event(-1, EVENT_FORK_ERR, errno, NULL);
Simon Kelley9e038942008-05-30 20:06:34 +0100391
Simon Kelley5aabfc72007-08-29 11:24:47 +0100392 if (pid != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100393 {
394 struct event_desc ev;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000395 char *msg;
396
Simon Kelley1a6bca82008-07-11 11:11:42 +0100397 /* close our copy of write-end */
398 close(err_pipe[1]);
399
400 /* check for errors after the fork */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000401 if (read_event(err_pipe[0], &ev, &msg))
402 fatal_event(&ev, msg);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100403
404 _exit(EC_GOOD);
405 }
406
407 close(err_pipe[0]);
408
409 /* NO calls to die() from here on. */
Simon Kelley3be34542004-09-11 19:12:13 +0100410
411 setsid();
Simon Kelley7622fc02009-06-04 20:32:05 +0100412
413 if ((pid = fork()) == -1)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000414 send_event(err_pipe[1], EVENT_FORK_ERR, errno, NULL);
Simon Kelley7622fc02009-06-04 20:32:05 +0100415
416 if (pid != 0)
Simon Kelley7cebd202006-05-06 14:13:33 +0100417 _exit(0);
Simon Kelley3be34542004-09-11 19:12:13 +0100418 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000419#endif
Simon Kelley9e038942008-05-30 20:06:34 +0100420
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000421 /* write pidfile _after_ forking ! */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100422 if (daemon->runfile)
423 {
Simon Kelley79cfefd2012-09-02 13:29:51 +0100424 int fd, err = 0;
425
426 sprintf(daemon->namebuff, "%d\n", (int) getpid());
427
428 /* Explanation: Some installations of dnsmasq (eg Debian/Ubuntu) locate the pid-file
429 in a directory which is writable by the non-privileged user that dnsmasq runs as. This
430 allows the daemon to delete the file as part of its shutdown. This is a security hole to the
431 extent that an attacker running as the unprivileged user could replace the pidfile with a
432 symlink, and have the target of that symlink overwritten as root next time dnsmasq starts.
433
434 The folowing code first deletes any existing file, and then opens it with the O_EXCL flag,
435 ensuring that the open() fails should there be any existing file (because the unlink() failed,
436 or an attacker exploited the race between unlink() and open()). This ensures that no symlink
437 attack can succeed.
438
439 Any compromise of the non-privileged user still theoretically allows the pid-file to be
440 replaced whilst dnsmasq is running. The worst that could allow is that the usual
441 "shutdown dnsmasq" shell command could be tricked into stopping any other process.
442
443 Note that if dnsmasq is started as non-root (eg for testing) it silently ignores
444 failure to write the pid-file.
445 */
446
447 unlink(daemon->runfile);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100448
Simon Kelley79cfefd2012-09-02 13:29:51 +0100449 if ((fd = open(daemon->runfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)) == -1)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100450 {
Simon Kelley79cfefd2012-09-02 13:29:51 +0100451 /* only complain if started as root */
452 if (getuid() == 0)
453 err = 1;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100454 }
Simon Kelley79cfefd2012-09-02 13:29:51 +0100455 else
456 {
457 if (!read_write(fd, (unsigned char *)daemon->namebuff, strlen(daemon->namebuff), 0))
458 err = 1;
459
460 while (!err && close(fd) == -1)
461 if (!retry_send())
462 err = 1;
463 }
464
465 if (err)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100466 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000467 send_event(err_pipe[1], EVENT_PIDFILE, errno, daemon->runfile);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100468 _exit(0);
469 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000470 }
Simon Kelley16972692006-10-16 20:04:18 +0100471 }
472
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100473 log_err = log_start(ent_pw, err_pipe[1]);
474
Simon Kelley28866e92011-02-14 20:19:14 +0000475 if (!option_bool(OPT_DEBUG))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100476 {
477 /* open stdout etc to /dev/null */
478 int nullfd = open("/dev/null", O_RDWR);
479 dup2(nullfd, STDOUT_FILENO);
480 dup2(nullfd, STDERR_FILENO);
481 dup2(nullfd, STDIN_FILENO);
482 close(nullfd);
483 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100484
Simon Kelley1a6bca82008-07-11 11:11:42 +0100485 /* if we are to run scripts, we need to fork a helper before dropping root. */
486 daemon->helperfd = -1;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000487#ifdef HAVE_SCRIPT
Simon Kelley52b92f42012-01-22 16:05:15 +0000488 if ((daemon->dhcp || daemon->dhcp6) && (daemon->lease_change_command || daemon->luascript))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100489 daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
490#endif
Simon Kelley5aabfc72007-08-29 11:24:47 +0100491
Simon Kelley28866e92011-02-14 20:19:14 +0000492 if (!option_bool(OPT_DEBUG) && getuid() == 0)
Simon Kelley16972692006-10-16 20:04:18 +0100493 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100494 int bad_capabilities = 0;
495 gid_t dummy;
Simon Kelley16972692006-10-16 20:04:18 +0100496
Simon Kelley1a6bca82008-07-11 11:11:42 +0100497 /* remove all supplimentary groups */
498 if (gp &&
499 (setgroups(0, &dummy) == -1 ||
500 setgid(gp->gr_gid) == -1))
501 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000502 send_event(err_pipe[1], EVENT_GROUP_ERR, errno, daemon->groupname);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100503 _exit(0);
504 }
505
Simon Kelley7cebd202006-05-06 14:13:33 +0100506 if (ent_pw && ent_pw->pw_uid != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100507 {
Simon Kelley74c95c22011-10-19 09:33:39 +0100508#if defined(HAVE_LINUX_NETWORK)
Simon Kelley16972692006-10-16 20:04:18 +0100509 /* On linux, we keep CAP_NETADMIN (for ARP-injection) and
Simon Kelley74c95c22011-10-19 09:33:39 +0100510 CAP_NET_RAW (for icmp) if we're doing dhcp. If we have yet to bind
Simon Kelley54dd3932012-06-20 11:23:38 +0100511 ports because of DAD, or we're doing it dynamically,
512 we need CAP_NET_BIND_SERVICE too. */
513 if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
Simon Kelley74c95c22011-10-19 09:33:39 +0100514 data->effective = data->permitted = data->inheritable =
515 (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
516 (1 << CAP_SETUID) | (1 << CAP_NET_BIND_SERVICE);
517 else
518 data->effective = data->permitted = data->inheritable =
519 (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100520
Simon Kelley16972692006-10-16 20:04:18 +0100521 /* Tell kernel to not clear capabilities when dropping root */
Simon Kelley572b41e2011-02-18 18:11:18 +0000522 if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
Simon Kelley16972692006-10-16 20:04:18 +0100523 bad_capabilities = errno;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100524
Simon Kelley7622fc02009-06-04 20:32:05 +0100525#elif defined(HAVE_SOLARIS_NETWORK)
Simon Kelley824af852008-02-12 20:43:05 +0000526 /* http://developers.sun.com/solaris/articles/program_privileges.html */
527 priv_set_t *priv_set;
528
529 if (!(priv_set = priv_str_to_set("basic", ",", NULL)) ||
530 priv_addset(priv_set, PRIV_NET_ICMPACCESS) == -1 ||
531 priv_addset(priv_set, PRIV_SYS_NET_CONFIG) == -1)
532 bad_capabilities = errno;
533
534 if (priv_set && bad_capabilities == 0)
535 {
536 priv_inverse(priv_set);
537
538 if (setppriv(PRIV_OFF, PRIV_LIMIT, priv_set) == -1)
539 bad_capabilities = errno;
540 }
541
542 if (priv_set)
543 priv_freeset(priv_set);
544
Simon Kelley824af852008-02-12 20:43:05 +0000545#endif
546
Simon Kelley1a6bca82008-07-11 11:11:42 +0100547 if (bad_capabilities != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100548 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000549 send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100550 _exit(0);
Simon Kelley16972692006-10-16 20:04:18 +0100551 }
Simon Kelley1a6bca82008-07-11 11:11:42 +0100552
553 /* finally drop root */
554 if (setuid(ent_pw->pw_uid) == -1)
555 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000556 send_event(err_pipe[1], EVENT_USER_ERR, errno, daemon->username);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100557 _exit(0);
558 }
559
560#ifdef HAVE_LINUX_NETWORK
Simon Kelley54dd3932012-06-20 11:23:38 +0100561 if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
Simon Kelley74c95c22011-10-19 09:33:39 +0100562 data->effective = data->permitted =
563 (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE);
564 else
565 data->effective = data->permitted =
566 (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100567 data->inheritable = 0;
568
569 /* lose the setuid and setgid capbilities */
570 if (capset(hdr, data) == -1)
571 {
Simon Kelleyc72daea2012-01-05 21:33:27 +0000572 send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100573 _exit(0);
574 }
575#endif
576
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000577 }
Simon Kelley849a8352006-06-09 21:02:31 +0100578 }
Simon Kelley16972692006-10-16 20:04:18 +0100579
Simon Kelley16972692006-10-16 20:04:18 +0100580#ifdef HAVE_LINUX_NETWORK
Simon Kelley28866e92011-02-14 20:19:14 +0000581 if (option_bool(OPT_DEBUG))
Simon Kelley572b41e2011-02-18 18:11:18 +0000582 prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
Simon Kelley16972692006-10-16 20:04:18 +0100583#endif
Simon Kelley849a8352006-06-09 21:02:31 +0100584
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100585#ifdef HAVE_TFTP
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100586 if (option_bool(OPT_TFTP))
Simon Kelley8b3ae2f2012-06-13 13:43:49 +0100587 {
588 DIR *dir;
589 struct tftp_prefix *p;
590
591 if (daemon->tftp_prefix)
592 {
593 if (!((dir = opendir(daemon->tftp_prefix))))
594 {
595 send_event(err_pipe[1], EVENT_TFTP_ERR, errno, daemon->tftp_prefix);
596 _exit(0);
597 }
598 closedir(dir);
599 }
600
601 for (p = daemon->if_prefix; p; p = p->next)
602 {
603 if (!((dir = opendir(p->prefix))))
604 {
605 send_event(err_pipe[1], EVENT_TFTP_ERR, errno, p->prefix);
606 _exit(0);
607 }
608 closedir(dir);
609 }
610 }
611#endif
612
Simon Kelley824af852008-02-12 20:43:05 +0000613 if (daemon->port == 0)
614 my_syslog(LOG_INFO, _("started, version %s DNS disabled"), VERSION);
615 else if (daemon->cachesize != 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100616 my_syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000617 else
Simon Kelleyf2621c72007-04-29 19:47:21 +0100618 my_syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION);
Simon Kelley16972692006-10-16 20:04:18 +0100619
Simon Kelleyf2621c72007-04-29 19:47:21 +0100620 my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
Simon Kelley16972692006-10-16 20:04:18 +0100621
Simon Kelley3d8df262005-08-29 12:19:27 +0100622#ifdef HAVE_DBUS
Simon Kelley28866e92011-02-14 20:19:14 +0000623 if (option_bool(OPT_DBUS))
Simon Kelley3d8df262005-08-29 12:19:27 +0100624 {
625 if (daemon->dbus)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100626 my_syslog(LOG_INFO, _("DBus support enabled: connected to system bus"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100627 else
Simon Kelleyf2621c72007-04-29 19:47:21 +0100628 my_syslog(LOG_INFO, _("DBus support enabled: bus connection pending"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100629 }
630#endif
631
Simon Kelley1a6bca82008-07-11 11:11:42 +0100632 if (log_err != 0)
633 my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"),
634 daemon->log_file, strerror(log_err));
635
Simon Kelleyde379512004-06-22 20:23:33 +0100636 if (bind_fallback)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100637 my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
Simon Kelleydc27e142013-10-16 13:09:53 +0100638
Simon Kelleyf7029f52013-11-21 15:09:09 +0000639 if (option_bool(OPT_NOWILD))
640 warn_bound_listeners();
641
642 warn_int_names();
Simon Kelleyde379512004-06-22 20:23:33 +0100643
Simon Kelley28866e92011-02-14 20:19:14 +0000644 if (!option_bool(OPT_NOWILD))
Simon Kelley26128d22004-11-14 16:43:54 +0000645 for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
646 if (if_tmp->name && !if_tmp->used)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100647 my_syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100648
Simon Kelley28866e92011-02-14 20:19:14 +0000649 if (daemon->port != 0 && option_bool(OPT_NO_RESOLV))
Simon Kelley208b65c2006-08-05 21:41:37 +0100650 {
651 if (daemon->resolv_files && !daemon->resolv_files->is_default)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100652 my_syslog(LOG_WARNING, _("warning: ignoring resolv-file flag because no-resolv is set"));
Simon Kelley208b65c2006-08-05 21:41:37 +0100653 daemon->resolv_files = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000654 if (!daemon->servers)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100655 my_syslog(LOG_WARNING, _("warning: no upstream servers configured"));
Simon Kelley208b65c2006-08-05 21:41:37 +0100656 }
657
Simon Kelleyf2621c72007-04-29 19:47:21 +0100658 if (daemon->max_logs != 0)
659 my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs);
Simon Kelley1f776932012-12-16 19:46:08 +0000660
Simon Kelleyf2621c72007-04-29 19:47:21 +0100661
Simon Kelley7622fc02009-06-04 20:32:05 +0100662#ifdef HAVE_DHCP
Simon Kelley1f776932012-12-16 19:46:08 +0000663 for (context = daemon->dhcp; context; context = context->next)
664 log_context(AF_INET, context);
Simon Kelleyc8257542012-03-28 21:15:41 +0100665
Simon Kelleyff7eea22013-09-04 18:01:38 +0100666 for (relay = daemon->relay4; relay; relay = relay->next)
667 log_relay(AF_INET, relay);
668
Simon Kelley1f776932012-12-16 19:46:08 +0000669# ifdef HAVE_DHCP6
670 for (context = daemon->dhcp6; context; context = context->next)
671 log_context(AF_INET6, context);
Simon Kelley52b92f42012-01-22 16:05:15 +0000672
Simon Kelleyff7eea22013-09-04 18:01:38 +0100673 for (relay = daemon->relay6; relay; relay = relay->next)
674 log_relay(AF_INET6, relay);
675
Simon Kelley1f776932012-12-16 19:46:08 +0000676 if (daemon->doing_dhcp6 || daemon->doing_ra)
677 dhcp_construct_contexts(now);
678
679 if (option_bool(OPT_RA))
680 my_syslog(MS_DHCP | LOG_INFO, _("IPv6 router advertisement enabled"));
681# endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000682
Simon Kelley3b3f4412013-10-11 16:33:28 +0100683# ifdef HAVE_LINUX_NETWORK
684 if (did_bind)
685 my_syslog(MS_DHCP | LOG_INFO, _("DHCP, sockets bound exclusively to interface %s"), bound_device);
686# endif
687
Simon Kelley8445f5d2012-12-17 21:54:08 +0000688 /* after dhcp_contruct_contexts */
689 if (daemon->dhcp || daemon->doing_dhcp6)
690 lease_find_interfaces(now);
Simon Kelley1f776932012-12-16 19:46:08 +0000691#endif
Simon Kelley52b92f42012-01-22 16:05:15 +0000692
Simon Kelley832af0b2007-01-21 20:01:28 +0000693#ifdef HAVE_TFTP
Simon Kelley8bc4cec2012-07-03 21:04:11 +0100694 if (option_bool(OPT_TFTP))
Simon Kelley832af0b2007-01-21 20:01:28 +0000695 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000696#ifdef FD_SETSIZE
Simon Kelley5aabfc72007-08-29 11:24:47 +0100697 if (FD_SETSIZE < (unsigned)max_fd)
Simon Kelley832af0b2007-01-21 20:01:28 +0000698 max_fd = FD_SETSIZE;
699#endif
700
Simon Kelley7622fc02009-06-04 20:32:05 +0100701 my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s",
Simon Kelleyf2621c72007-04-29 19:47:21 +0100702 daemon->tftp_prefix ? _("root is ") : _("enabled"),
703 daemon->tftp_prefix ? daemon->tftp_prefix: "",
Simon Kelley28866e92011-02-14 20:19:14 +0000704 option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +0100705
Simon Kelley832af0b2007-01-21 20:01:28 +0000706 /* This is a guess, it assumes that for small limits,
Simon Kelleyf2621c72007-04-29 19:47:21 +0100707 disjoint files might be served, but for large limits,
Simon Kelley832af0b2007-01-21 20:01:28 +0000708 a single file will be sent to may clients (the file only needs
709 one fd). */
710
711 max_fd -= 30; /* use other than TFTP */
712
713 if (max_fd < 0)
714 max_fd = 5;
715 else if (max_fd < 100)
716 max_fd = max_fd/2;
717 else
718 max_fd = max_fd - 20;
Simon Kelley824af852008-02-12 20:43:05 +0000719
720 /* if we have to use a limited range of ports,
721 that will limit the number of transfers */
722 if (daemon->start_tftp_port != 0 &&
723 daemon->end_tftp_port - daemon->start_tftp_port + 1 < max_fd)
724 max_fd = daemon->end_tftp_port - daemon->start_tftp_port + 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000725
726 if (daemon->tftp_max > max_fd)
727 {
728 daemon->tftp_max = max_fd;
Simon Kelley7622fc02009-06-04 20:32:05 +0100729 my_syslog(MS_TFTP | LOG_WARNING,
Simon Kelleyf2621c72007-04-29 19:47:21 +0100730 _("restricting maximum simultaneous TFTP transfers to %d"),
731 daemon->tftp_max);
Simon Kelley832af0b2007-01-21 20:01:28 +0000732 }
733 }
734#endif
735
Simon Kelley1a6bca82008-07-11 11:11:42 +0100736 /* finished start-up - release original process */
737 if (err_pipe[1] != -1)
738 close(err_pipe[1]);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000739
Simon Kelley824af852008-02-12 20:43:05 +0000740 if (daemon->port != 0)
741 check_servers();
742
Simon Kelley7cebd202006-05-06 14:13:33 +0100743 pid = getpid();
744
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100745 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000746 {
Simon Kelley16972692006-10-16 20:04:18 +0100747 int maxfd = -1;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100748 struct timeval t, *tp = NULL;
Simon Kelley3d8df262005-08-29 12:19:27 +0100749 fd_set rset, wset, eset;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000750
751 FD_ZERO(&rset);
Simon Kelley3d8df262005-08-29 12:19:27 +0100752 FD_ZERO(&wset);
753 FD_ZERO(&eset);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000754
Simon Kelley16972692006-10-16 20:04:18 +0100755 /* if we are out of resources, find how long we have to wait
756 for some to come free, we'll loop around then and restart
757 listening for queries */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100758 if ((t.tv_sec = set_dns_listeners(now, &rset, &maxfd)) != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100759 {
760 t.tv_usec = 0;
761 tp = &t;
762 }
763
Simon Kelley832af0b2007-01-21 20:01:28 +0000764 /* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
765 if (daemon->tftp_trans ||
Simon Kelley28866e92011-02-14 20:19:14 +0000766 (option_bool(OPT_DBUS) && !daemon->dbus))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000767 {
Simon Kelley16972692006-10-16 20:04:18 +0100768 t.tv_sec = 0;
769 t.tv_usec = 250000;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100770 tp = &t;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000771 }
Simon Kelley74c95c22011-10-19 09:33:39 +0100772 /* Wake every second whilst waiting for DAD to complete */
773 else if (is_dad_listeners())
774 {
775 t.tv_sec = 1;
776 t.tv_usec = 0;
777 tp = &t;
778 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100779
Simon Kelley832af0b2007-01-21 20:01:28 +0000780#ifdef HAVE_DBUS
Simon Kelley5aabfc72007-08-29 11:24:47 +0100781 set_dbus_listeners(&maxfd, &rset, &wset, &eset);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100782#endif
783
Simon Kelley7622fc02009-06-04 20:32:05 +0100784#ifdef HAVE_DHCP
Simon Kelleyff7eea22013-09-04 18:01:38 +0100785 if (daemon->dhcp || daemon->relay4)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100786 {
787 FD_SET(daemon->dhcpfd, &rset);
Simon Kelley16972692006-10-16 20:04:18 +0100788 bump_maxfd(daemon->dhcpfd, &maxfd);
Simon Kelley316e2732010-01-22 20:16:09 +0000789 if (daemon->pxefd != -1)
790 {
791 FD_SET(daemon->pxefd, &rset);
792 bump_maxfd(daemon->pxefd, &maxfd);
793 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100794 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100795#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100796
Simon Kelley52b92f42012-01-22 16:05:15 +0000797#ifdef HAVE_DHCP6
Simon Kelleyff7eea22013-09-04 18:01:38 +0100798 if (daemon->doing_dhcp6 || daemon->relay6)
Simon Kelley52b92f42012-01-22 16:05:15 +0000799 {
800 FD_SET(daemon->dhcp6fd, &rset);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000801 bump_maxfd(daemon->dhcp6fd, &maxfd);
Simon Kelley5d71d832012-03-24 14:40:42 +0000802 }
803
Simon Kelley1f776932012-12-16 19:46:08 +0000804 if (daemon->doing_ra)
Simon Kelley5d71d832012-03-24 14:40:42 +0000805 {
806 FD_SET(daemon->icmp6fd, &rset);
807 bump_maxfd(daemon->icmp6fd, &maxfd);
Simon Kelley52b92f42012-01-22 16:05:15 +0000808 }
809#endif
810
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100811#ifdef HAVE_LINUX_NETWORK
812 FD_SET(daemon->netlinkfd, &rset);
Simon Kelley16972692006-10-16 20:04:18 +0100813 bump_maxfd(daemon->netlinkfd, &maxfd);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100814#endif
Simon Kelley3d8df262005-08-29 12:19:27 +0100815
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100816 FD_SET(piperead, &rset);
Simon Kelley16972692006-10-16 20:04:18 +0100817 bump_maxfd(piperead, &maxfd);
818
Simon Kelley7622fc02009-06-04 20:32:05 +0100819#ifdef HAVE_DHCP
Simon Kelley1f15b812009-10-13 17:49:32 +0100820# ifdef HAVE_SCRIPT
Simon Kelley5aabfc72007-08-29 11:24:47 +0100821 while (helper_buf_empty() && do_script_run(now));
Simon Kelley16972692006-10-16 20:04:18 +0100822
Simon Kelleya9530962012-03-20 22:07:35 +0000823# ifdef HAVE_TFTP
824 while (helper_buf_empty() && do_tftp_script_run());
825# endif
826
Simon Kelley16972692006-10-16 20:04:18 +0100827 if (!helper_buf_empty())
828 {
829 FD_SET(daemon->helperfd, &wset);
830 bump_maxfd(daemon->helperfd, &maxfd);
831 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100832# else
Simon Kelley5aabfc72007-08-29 11:24:47 +0100833 /* need this for other side-effects */
834 while (do_script_run(now));
Simon Kelleya9530962012-03-20 22:07:35 +0000835
836# ifdef HAVE_TFTP
837 while (do_tftp_script_run());
838# endif
839
Simon Kelley7622fc02009-06-04 20:32:05 +0100840# endif
Simon Kelley5aabfc72007-08-29 11:24:47 +0100841#endif
Simon Kelley7622fc02009-06-04 20:32:05 +0100842
Simon Kelleyf2621c72007-04-29 19:47:21 +0100843 /* must do this just before select(), when we know no
844 more calls to my_syslog() can occur */
845 set_log_writer(&wset, &maxfd);
846
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100847 if (select(maxfd+1, &rset, &wset, &eset, tp) < 0)
848 {
849 /* otherwise undefined after error */
850 FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
851 }
852
853 now = dnsmasq_time();
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000854
Simon Kelleyf2621c72007-04-29 19:47:21 +0100855 check_log_writer(&wset);
Simon Kelley115ac3e2013-05-20 11:28:32 +0100856
857 /* prime. */
858 enumerate_interfaces(1);
859
Simon Kelley74c95c22011-10-19 09:33:39 +0100860 /* Check the interfaces to see if any have exited DAD state
861 and if so, bind the address. */
862 if (is_dad_listeners())
863 {
Simon Kelley115ac3e2013-05-20 11:28:32 +0100864 enumerate_interfaces(0);
Simon Kelley74c95c22011-10-19 09:33:39 +0100865 /* NB, is_dad_listeners() == 1 --> we're binding interfaces */
866 create_bound_listeners(0);
Simon Kelleydc27e142013-10-16 13:09:53 +0100867 warn_bound_listeners();
Simon Kelley74c95c22011-10-19 09:33:39 +0100868 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100869
Simon Kelleyc52e1892010-06-07 22:01:39 +0100870#ifdef HAVE_LINUX_NETWORK
871 if (FD_ISSET(daemon->netlinkfd, &rset))
Simon Kelley1f776932012-12-16 19:46:08 +0000872 netlink_multicast(now);
Simon Kelleyc52e1892010-06-07 22:01:39 +0100873#endif
874
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000875 /* Check for changes to resolv files once per second max. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100876 /* Don't go silent for long periods if the clock goes backwards. */
Simon Kelley9009d742008-11-14 20:04:27 +0000877 if (daemon->last_resolv == 0 ||
878 difftime(now, daemon->last_resolv) > 1.0 ||
879 difftime(now, daemon->last_resolv) < -1.0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000880 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100881 /* poll_resolv doesn't need to reload first time through, since
882 that's queued anyway. */
Simon Kelley33820b72004-04-03 21:10:00 +0100883
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100884 poll_resolv(0, daemon->last_resolv != 0, now);
885 daemon->last_resolv = now;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000886 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100887
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100888 if (FD_ISSET(piperead, &rset))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100889 async_event(piperead, now);
Simon Kelley7cebd202006-05-06 14:13:33 +0100890
Simon Kelley3d8df262005-08-29 12:19:27 +0100891#ifdef HAVE_DBUS
892 /* if we didn't create a DBus connection, retry now. */
Simon Kelley28866e92011-02-14 20:19:14 +0000893 if (option_bool(OPT_DBUS) && !daemon->dbus)
Simon Kelley3d8df262005-08-29 12:19:27 +0100894 {
895 char *err;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100896 if ((err = dbus_init()))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100897 my_syslog(LOG_WARNING, _("DBus error: %s"), err);
Simon Kelley3d8df262005-08-29 12:19:27 +0100898 if (daemon->dbus)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100899 my_syslog(LOG_INFO, _("connected to system DBus"));
Simon Kelley3d8df262005-08-29 12:19:27 +0100900 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100901 check_dbus_listeners(&rset, &wset, &eset);
Simon Kelley3d8df262005-08-29 12:19:27 +0100902#endif
Simon Kelley824af852008-02-12 20:43:05 +0000903
Simon Kelley5aabfc72007-08-29 11:24:47 +0100904 check_dns_listeners(&rset, now);
Simon Kelley832af0b2007-01-21 20:01:28 +0000905
906#ifdef HAVE_TFTP
Simon Kelley5aabfc72007-08-29 11:24:47 +0100907 check_tftp_listeners(&rset, now);
Simon Kelley832af0b2007-01-21 20:01:28 +0000908#endif
909
Simon Kelley7622fc02009-06-04 20:32:05 +0100910#ifdef HAVE_DHCP
Simon Kelleyff7eea22013-09-04 18:01:38 +0100911 if (daemon->dhcp || daemon->relay4)
Simon Kelley316e2732010-01-22 20:16:09 +0000912 {
913 if (FD_ISSET(daemon->dhcpfd, &rset))
914 dhcp_packet(now, 0);
915 if (daemon->pxefd != -1 && FD_ISSET(daemon->pxefd, &rset))
916 dhcp_packet(now, 1);
917 }
Simon Kelley16972692006-10-16 20:04:18 +0100918
Simon Kelley52b92f42012-01-22 16:05:15 +0000919#ifdef HAVE_DHCP6
Simon Kelleyff7eea22013-09-04 18:01:38 +0100920 if ((daemon->doing_dhcp6 || daemon->relay6) && FD_ISSET(daemon->dhcp6fd, &rset))
Simon Kelley18c63ef2012-05-21 14:34:15 +0100921 dhcp6_packet(now);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +0000922
Simon Kelley1f776932012-12-16 19:46:08 +0000923 if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset))
924 icmp6_packet(now);
Simon Kelley52b92f42012-01-22 16:05:15 +0000925#endif
926
Simon Kelley1f15b812009-10-13 17:49:32 +0100927# ifdef HAVE_SCRIPT
Simon Kelley16972692006-10-16 20:04:18 +0100928 if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
Simon Kelley5aabfc72007-08-29 11:24:47 +0100929 helper_write();
Simon Kelley7622fc02009-06-04 20:32:05 +0100930# endif
Simon Kelley5aabfc72007-08-29 11:24:47 +0100931#endif
932
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000933 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000934}
935
Simon Kelley3be34542004-09-11 19:12:13 +0100936static void sig_handler(int sig)
937{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100938 if (pid == 0)
939 {
Simon Kelley16972692006-10-16 20:04:18 +0100940 /* ignore anything other than TERM during startup
941 and in helper proc. (helper ignore TERM too) */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100942 if (sig == SIGTERM)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100943 exit(EC_MISC);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100944 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100945 else if (pid != getpid())
Simon Kelley3be34542004-09-11 19:12:13 +0100946 {
Simon Kelley16972692006-10-16 20:04:18 +0100947 /* alarm is used to kill TCP children after a fixed time. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100948 if (sig == SIGALRM)
Simon Kelley7cebd202006-05-06 14:13:33 +0100949 _exit(0);
Simon Kelley3be34542004-09-11 19:12:13 +0100950 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100951 else
952 {
953 /* master process */
954 int event, errsave = errno;
955
956 if (sig == SIGHUP)
957 event = EVENT_RELOAD;
958 else if (sig == SIGCHLD)
959 event = EVENT_CHILD;
960 else if (sig == SIGALRM)
961 event = EVENT_ALARM;
962 else if (sig == SIGTERM)
963 event = EVENT_TERM;
964 else if (sig == SIGUSR1)
965 event = EVENT_DUMP;
966 else if (sig == SIGUSR2)
967 event = EVENT_REOPEN;
968 else
969 return;
970
Simon Kelleyc72daea2012-01-05 21:33:27 +0000971 send_event(pipewrite, event, 0, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100972 errno = errsave;
973 }
Simon Kelley3be34542004-09-11 19:12:13 +0100974}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000975
Simon Kelley353ae4d2012-03-19 20:07:51 +0000976/* now == 0 -> queue immediate callback */
977void send_alarm(time_t event, time_t now)
Simon Kelley741c2952012-02-25 13:09:18 +0000978{
Simon Kelley884a6df2012-03-20 16:20:22 +0000979 if (now == 0 || event != 0)
Simon Kelley353ae4d2012-03-19 20:07:51 +0000980 {
Simon Kelley884a6df2012-03-20 16:20:22 +0000981 /* alarm(0) or alarm(-ve) doesn't do what we want.... */
982 if ((now == 0 || difftime(event, now) <= 0.0))
983 send_event(pipewrite, EVENT_ALARM, 0, NULL);
984 else
985 alarm((unsigned)difftime(event, now));
Simon Kelley353ae4d2012-03-19 20:07:51 +0000986 }
Simon Kelley741c2952012-02-25 13:09:18 +0000987}
988
Simon Kelleyc72daea2012-01-05 21:33:27 +0000989void send_event(int fd, int event, int data, char *msg)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100990{
991 struct event_desc ev;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000992 struct iovec iov[2];
993
Simon Kelley5aabfc72007-08-29 11:24:47 +0100994 ev.event = event;
995 ev.data = data;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000996 ev.msg_sz = msg ? strlen(msg) : 0;
997
998 iov[0].iov_base = &ev;
999 iov[0].iov_len = sizeof(ev);
1000 iov[1].iov_base = msg;
1001 iov[1].iov_len = ev.msg_sz;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001002
1003 /* error pipe, debug mode. */
1004 if (fd == -1)
Simon Kelleyc72daea2012-01-05 21:33:27 +00001005 fatal_event(&ev, msg);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001006 else
1007 /* pipe is non-blocking and struct event_desc is smaller than
1008 PIPE_BUF, so this either fails or writes everything */
Simon Kelleyc72daea2012-01-05 21:33:27 +00001009 while (writev(fd, iov, msg ? 2 : 1) == -1 && errno == EINTR);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001010}
Simon Kelley3d8df262005-08-29 12:19:27 +01001011
Simon Kelleyc72daea2012-01-05 21:33:27 +00001012/* NOTE: the memory used to return msg is leaked: use msgs in events only
1013 to describe fatal errors. */
1014static int read_event(int fd, struct event_desc *evp, char **msg)
1015{
1016 char *buf;
1017
1018 if (!read_write(fd, (unsigned char *)evp, sizeof(struct event_desc), 1))
1019 return 0;
1020
1021 *msg = NULL;
1022
1023 if (evp->msg_sz != 0 &&
1024 (buf = malloc(evp->msg_sz + 1)) &&
1025 read_write(fd, (unsigned char *)buf, evp->msg_sz, 1))
1026 {
1027 buf[evp->msg_sz] = 0;
1028 *msg = buf;
1029 }
1030
1031 return 1;
1032}
1033
1034static void fatal_event(struct event_desc *ev, char *msg)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001035{
1036 errno = ev->data;
1037
1038 switch (ev->event)
1039 {
1040 case EVENT_DIE:
1041 exit(0);
Simon Kelley7622fc02009-06-04 20:32:05 +01001042
1043 case EVENT_FORK_ERR:
1044 die(_("cannot fork into background: %s"), NULL, EC_MISC);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001045
1046 case EVENT_PIPE_ERR:
1047 die(_("failed to create helper: %s"), NULL, EC_MISC);
1048
1049 case EVENT_CAP_ERR:
1050 die(_("setting capabilities failed: %s"), NULL, EC_MISC);
1051
1052 case EVENT_USER_ERR:
Simon Kelleyc72daea2012-01-05 21:33:27 +00001053 die(_("failed to change user-id to %s: %s"), msg, EC_MISC);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001054
1055 case EVENT_GROUP_ERR:
Simon Kelleyc72daea2012-01-05 21:33:27 +00001056 die(_("failed to change group-id to %s: %s"), msg, EC_MISC);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001057
1058 case EVENT_PIDFILE:
Simon Kelleyc72daea2012-01-05 21:33:27 +00001059 die(_("failed to open pidfile %s: %s"), msg, EC_FILE);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001060
1061 case EVENT_LOG_ERR:
Simon Kelleyc72daea2012-01-05 21:33:27 +00001062 die(_("cannot open log %s: %s"), msg, EC_FILE);
1063
1064 case EVENT_LUA_ERR:
1065 die(_("failed to load Lua script: %s"), msg, EC_MISC);
Simon Kelley8b3ae2f2012-06-13 13:43:49 +01001066
1067 case EVENT_TFTP_ERR:
1068 die(_("TFTP directory %s inaccessible: %s"), msg, EC_FILE);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001069 }
1070}
1071
Simon Kelley5aabfc72007-08-29 11:24:47 +01001072static void async_event(int pipe, time_t now)
1073{
1074 pid_t p;
1075 struct event_desc ev;
1076 int i;
Simon Kelleyc72daea2012-01-05 21:33:27 +00001077 char *msg;
1078
1079 /* NOTE: the memory used to return msg is leaked: use msgs in events only
1080 to describe fatal errors. */
1081
1082 if (read_event(pipe, &ev, &msg))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001083 switch (ev.event)
1084 {
1085 case EVENT_RELOAD:
1086 clear_cache_and_reload(now);
Simon Kelley28866e92011-02-14 20:19:14 +00001087 if (daemon->port != 0 && daemon->resolv_files && option_bool(OPT_NO_POLL))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001088 {
1089 reload_servers(daemon->resolv_files->name);
1090 check_servers();
1091 }
Simon Kelley7622fc02009-06-04 20:32:05 +01001092#ifdef HAVE_DHCP
Simon Kelley5aabfc72007-08-29 11:24:47 +01001093 rerun_scripts();
Simon Kelley7622fc02009-06-04 20:32:05 +01001094#endif
Simon Kelley5aabfc72007-08-29 11:24:47 +01001095 break;
1096
1097 case EVENT_DUMP:
Simon Kelley824af852008-02-12 20:43:05 +00001098 if (daemon->port != 0)
1099 dump_cache(now);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001100 break;
1101
1102 case EVENT_ALARM:
Simon Kelley7622fc02009-06-04 20:32:05 +01001103#ifdef HAVE_DHCP
Simon Kelley1f776932012-12-16 19:46:08 +00001104 if (daemon->dhcp || daemon->doing_dhcp6)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001105 {
1106 lease_prune(NULL, now);
1107 lease_update_file(now);
1108 }
Simon Kelley843c96b2012-02-27 17:42:38 +00001109#ifdef HAVE_DHCP6
Simon Kelley1f776932012-12-16 19:46:08 +00001110 else if (daemon->doing_ra)
Simon Kelley353ae4d2012-03-19 20:07:51 +00001111 /* Not doing DHCP, so no lease system, manage alarms for ra only */
1112 send_alarm(periodic_ra(now), now);
Simon Kelley843c96b2012-02-27 17:42:38 +00001113#endif
Simon Kelley7622fc02009-06-04 20:32:05 +01001114#endif
Simon Kelley5aabfc72007-08-29 11:24:47 +01001115 break;
1116
1117 case EVENT_CHILD:
1118 /* See Stevens 5.10 */
1119 while ((p = waitpid(-1, NULL, WNOHANG)) != 0)
1120 if (p == -1)
1121 {
1122 if (errno != EINTR)
1123 break;
1124 }
1125 else
1126 for (i = 0 ; i < MAX_PROCS; i++)
1127 if (daemon->tcp_pids[i] == p)
1128 daemon->tcp_pids[i] = 0;
1129 break;
1130
1131 case EVENT_KILLED:
Simon Kelleyc72daea2012-01-05 21:33:27 +00001132 my_syslog(LOG_WARNING, _("script process killed by signal %d"), ev.data);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001133 break;
1134
1135 case EVENT_EXITED:
Simon Kelleyc72daea2012-01-05 21:33:27 +00001136 my_syslog(LOG_WARNING, _("script process exited with status %d"), ev.data);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001137 break;
1138
1139 case EVENT_EXEC_ERR:
Simon Kelley9e038942008-05-30 20:06:34 +01001140 my_syslog(LOG_ERR, _("failed to execute %s: %s"),
1141 daemon->lease_change_command, strerror(ev.data));
Simon Kelley5aabfc72007-08-29 11:24:47 +01001142 break;
1143
Simon Kelley1a6bca82008-07-11 11:11:42 +01001144 /* necessary for fatal errors in helper */
Simon Kelleyc72daea2012-01-05 21:33:27 +00001145 case EVENT_USER_ERR:
Simon Kelley1a6bca82008-07-11 11:11:42 +01001146 case EVENT_DIE:
Simon Kelleyc72daea2012-01-05 21:33:27 +00001147 case EVENT_LUA_ERR:
1148 fatal_event(&ev, msg);
Simon Kelley9e038942008-05-30 20:06:34 +01001149 break;
1150
Simon Kelley5aabfc72007-08-29 11:24:47 +01001151 case EVENT_REOPEN:
1152 /* Note: this may leave TCP-handling processes with the old file still open.
1153 Since any such process will die in CHILD_LIFETIME or probably much sooner,
1154 we leave them logging to the old file. */
1155 if (daemon->log_file != NULL)
1156 log_reopen(daemon->log_file);
1157 break;
1158
1159 case EVENT_TERM:
1160 /* Knock all our children on the head. */
1161 for (i = 0; i < MAX_PROCS; i++)
1162 if (daemon->tcp_pids[i] != 0)
1163 kill(daemon->tcp_pids[i], SIGALRM);
1164
Simon Kelleyc72daea2012-01-05 21:33:27 +00001165#if defined(HAVE_SCRIPT)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001166 /* handle pending lease transitions */
1167 if (daemon->helperfd != -1)
1168 {
1169 /* block in writes until all done */
1170 if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1)
1171 fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK);
1172 do {
1173 helper_write();
1174 } while (!helper_buf_empty() || do_script_run(now));
1175 close(daemon->helperfd);
1176 }
1177#endif
1178
1179 if (daemon->lease_stream)
1180 fclose(daemon->lease_stream);
Simon Kelley73a08a22009-02-05 20:28:08 +00001181
1182 if (daemon->runfile)
1183 unlink(daemon->runfile);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001184
1185 my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
1186 flush_log();
1187 exit(EC_GOOD);
1188 }
1189}
1190
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001191void poll_resolv(int force, int do_reload, time_t now)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001192{
1193 struct resolvc *res, *latest;
1194 struct stat statbuf;
1195 time_t last_change = 0;
1196 /* There may be more than one possible file.
1197 Go through and find the one which changed _last_.
1198 Warn of any which can't be read. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001199
Simon Kelley28866e92011-02-14 20:19:14 +00001200 if (daemon->port == 0 || option_bool(OPT_NO_POLL))
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001201 return;
1202
Simon Kelley5aabfc72007-08-29 11:24:47 +01001203 for (latest = NULL, res = daemon->resolv_files; res; res = res->next)
1204 if (stat(res->name, &statbuf) == -1)
1205 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001206 if (force)
1207 {
1208 res->mtime = 0;
1209 continue;
1210 }
1211
Simon Kelley5aabfc72007-08-29 11:24:47 +01001212 if (!res->logged)
1213 my_syslog(LOG_WARNING, _("failed to access %s: %s"), res->name, strerror(errno));
1214 res->logged = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001215
1216 if (res->mtime != 0)
1217 {
1218 /* existing file evaporated, force selection of the latest
1219 file even if its mtime hasn't changed since we last looked */
1220 poll_resolv(1, do_reload, now);
1221 return;
1222 }
Simon Kelley5aabfc72007-08-29 11:24:47 +01001223 }
1224 else
1225 {
1226 res->logged = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001227 if (force || (statbuf.st_mtime != res->mtime))
1228 {
1229 res->mtime = statbuf.st_mtime;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001230 if (difftime(statbuf.st_mtime, last_change) > 0.0)
1231 {
1232 last_change = statbuf.st_mtime;
1233 latest = res;
1234 }
1235 }
1236 }
1237
1238 if (latest)
1239 {
1240 static int warned = 0;
1241 if (reload_servers(latest->name))
1242 {
1243 my_syslog(LOG_INFO, _("reading %s"), latest->name);
1244 warned = 0;
1245 check_servers();
Simon Kelley28866e92011-02-14 20:19:14 +00001246 if (option_bool(OPT_RELOAD) && do_reload)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001247 clear_cache_and_reload(now);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001248 }
1249 else
1250 {
1251 latest->mtime = 0;
1252 if (!warned)
1253 {
1254 my_syslog(LOG_WARNING, _("no servers found in %s, will retry"), latest->name);
1255 warned = 1;
1256 }
1257 }
1258 }
1259}
1260
1261void clear_cache_and_reload(time_t now)
Simon Kelley3d8df262005-08-29 12:19:27 +01001262{
Vladislav Grishenko408c3682013-09-24 16:18:49 +01001263 (void)now;
1264
Simon Kelley824af852008-02-12 20:43:05 +00001265 if (daemon->port != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001266 cache_reload();
Simon Kelley824af852008-02-12 20:43:05 +00001267
Simon Kelley7622fc02009-06-04 20:32:05 +01001268#ifdef HAVE_DHCP
Simon Kelley1f776932012-12-16 19:46:08 +00001269 if (daemon->dhcp || daemon->doing_dhcp6)
Simon Kelley3d8df262005-08-29 12:19:27 +01001270 {
Simon Kelley28866e92011-02-14 20:19:14 +00001271 if (option_bool(OPT_ETHERS))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001272 dhcp_read_ethers();
Simon Kelley824af852008-02-12 20:43:05 +00001273 reread_dhcp();
Simon Kelley3d8df262005-08-29 12:19:27 +01001274 dhcp_update_configs(daemon->dhcp_conf);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001275 lease_update_from_configs();
1276 lease_update_file(now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001277 lease_update_dns(1);
Simon Kelley3d8df262005-08-29 12:19:27 +01001278 }
Simon Kelley843c96b2012-02-27 17:42:38 +00001279#ifdef HAVE_DHCP6
Simon Kelley1f776932012-12-16 19:46:08 +00001280 else if (daemon->doing_ra)
Simon Kelley2021c662012-05-07 16:43:21 +01001281 /* Not doing DHCP, so no lease system, manage
1282 alarms for ra only */
1283 send_alarm(periodic_ra(now), now);
Simon Kelley843c96b2012-02-27 17:42:38 +00001284#endif
Simon Kelley7622fc02009-06-04 20:32:05 +01001285#endif
Simon Kelley3d8df262005-08-29 12:19:27 +01001286}
1287
Simon Kelley5aabfc72007-08-29 11:24:47 +01001288static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
Simon Kelley3be34542004-09-11 19:12:13 +01001289{
1290 struct serverfd *serverfdp;
1291 struct listener *listener;
Simon Kelley824af852008-02-12 20:43:05 +00001292 int wait = 0, i;
Simon Kelley832af0b2007-01-21 20:01:28 +00001293
1294#ifdef HAVE_TFTP
1295 int tftp = 0;
1296 struct tftp_transfer *transfer;
1297 for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
1298 {
1299 tftp++;
1300 FD_SET(transfer->sockfd, set);
1301 bump_maxfd(transfer->sockfd, maxfdp);
1302 }
1303#endif
1304
Simon Kelley16972692006-10-16 20:04:18 +01001305 /* will we be able to get memory? */
Simon Kelley824af852008-02-12 20:43:05 +00001306 if (daemon->port != 0)
1307 get_new_frec(now, &wait);
Simon Kelley16972692006-10-16 20:04:18 +01001308
Simon Kelley3be34542004-09-11 19:12:13 +01001309 for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
1310 {
1311 FD_SET(serverfdp->fd, set);
Simon Kelley16972692006-10-16 20:04:18 +01001312 bump_maxfd(serverfdp->fd, maxfdp);
Simon Kelley3be34542004-09-11 19:12:13 +01001313 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001314
1315 if (daemon->port != 0 && !daemon->osport)
1316 for (i = 0; i < RANDOM_SOCKS; i++)
1317 if (daemon->randomsocks[i].refcount != 0)
1318 {
1319 FD_SET(daemon->randomsocks[i].fd, set);
1320 bump_maxfd(daemon->randomsocks[i].fd, maxfdp);
1321 }
1322
Simon Kelley3be34542004-09-11 19:12:13 +01001323 for (listener = daemon->listeners; listener; listener = listener->next)
1324 {
Simon Kelley16972692006-10-16 20:04:18 +01001325 /* only listen for queries if we have resources */
Simon Kelley824af852008-02-12 20:43:05 +00001326 if (listener->fd != -1 && wait == 0)
Simon Kelley16972692006-10-16 20:04:18 +01001327 {
1328 FD_SET(listener->fd, set);
1329 bump_maxfd(listener->fd, maxfdp);
1330 }
1331
1332 /* death of a child goes through the select loop, so
1333 we don't need to explicitly arrange to wake up here */
Simon Kelley824af852008-02-12 20:43:05 +00001334 if (listener->tcpfd != -1)
1335 for (i = 0; i < MAX_PROCS; i++)
1336 if (daemon->tcp_pids[i] == 0)
1337 {
1338 FD_SET(listener->tcpfd, set);
1339 bump_maxfd(listener->tcpfd, maxfdp);
1340 break;
1341 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001342
Simon Kelley832af0b2007-01-21 20:01:28 +00001343#ifdef HAVE_TFTP
1344 if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
1345 {
1346 FD_SET(listener->tftpfd, set);
1347 bump_maxfd(listener->tftpfd, maxfdp);
1348 }
1349#endif
1350
1351 }
1352
Simon Kelley16972692006-10-16 20:04:18 +01001353 return wait;
Simon Kelley3be34542004-09-11 19:12:13 +01001354}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001355
Simon Kelley5aabfc72007-08-29 11:24:47 +01001356static void check_dns_listeners(fd_set *set, time_t now)
Simon Kelley3be34542004-09-11 19:12:13 +01001357{
1358 struct serverfd *serverfdp;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001359 struct listener *listener;
1360 int i;
1361
Simon Kelley832af0b2007-01-21 20:01:28 +00001362 for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
1363 if (FD_ISSET(serverfdp->fd, set))
Simon Kelley1a6bca82008-07-11 11:11:42 +01001364 reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
1365
1366 if (daemon->port != 0 && !daemon->osport)
1367 for (i = 0; i < RANDOM_SOCKS; i++)
1368 if (daemon->randomsocks[i].refcount != 0 &&
1369 FD_ISSET(daemon->randomsocks[i].fd, set))
1370 reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
Simon Kelley832af0b2007-01-21 20:01:28 +00001371
1372 for (listener = daemon->listeners; listener; listener = listener->next)
1373 {
Simon Kelley824af852008-02-12 20:43:05 +00001374 if (listener->fd != -1 && FD_ISSET(listener->fd, set))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001375 receive_query(listener, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001376
Simon Kelley832af0b2007-01-21 20:01:28 +00001377#ifdef HAVE_TFTP
1378 if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001379 tftp_request(listener, now);
Simon Kelley59353a62004-11-21 19:34:28 +00001380#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001381
Simon Kelley824af852008-02-12 20:43:05 +00001382 if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set))
Simon Kelley832af0b2007-01-21 20:01:28 +00001383 {
Simon Kelley22ce5502013-01-22 13:53:04 +00001384 int confd, client_ok = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001385 struct irec *iface = NULL;
1386 pid_t p;
Simon Kelley52d4abf2012-03-21 21:39:48 +00001387 union mysockaddr tcp_addr;
1388 socklen_t tcp_len = sizeof(union mysockaddr);
1389
1390 while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
Simon Kelley832af0b2007-01-21 20:01:28 +00001391
Simon Kelley46b06652013-02-04 21:47:59 +00001392 if (confd == -1)
Simon Kelley832af0b2007-01-21 20:01:28 +00001393 continue;
Simon Kelley76dd75d2013-05-23 10:04:25 +01001394
Simon Kelley46b06652013-02-04 21:47:59 +00001395 if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
1396 {
1397 close(confd);
1398 continue;
1399 }
Simon Kelley76dd75d2013-05-23 10:04:25 +01001400
1401 /* Make sure that the interface list is up-to-date.
1402
1403 We do this here as we may need the results below, and
1404 the DNS code needs them for --interface-name stuff.
Simon Kelley46b06652013-02-04 21:47:59 +00001405
Simon Kelley76dd75d2013-05-23 10:04:25 +01001406 Multiple calls to enumerate_interfaces() per select loop are
1407 inhibited, so calls to it in the child process (which doesn't select())
1408 have no effect. This avoids two processes reading from the same
1409 netlink fd and screwing the pooch entirely.
1410 */
Simon Kelleye25db1f2013-01-29 22:10:26 +00001411
Simon Kelley76dd75d2013-05-23 10:04:25 +01001412 enumerate_interfaces(0);
1413
1414 if (option_bool(OPT_NOWILD))
1415 iface = listener->iface; /* May be NULL */
1416 else
1417 {
1418 int if_index;
1419 char intr_name[IF_NAMESIZE];
1420
1421 /* if we can find the arrival interface, check it's one that's allowed */
1422 if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
1423 indextoname(listener->tcpfd, if_index, intr_name))
1424 {
1425 struct all_addr addr;
1426 addr.addr.addr4 = tcp_addr.in.sin_addr;
Simon Kelleye25db1f2013-01-29 22:10:26 +00001427#ifdef HAVE_IPV6
Simon Kelley76dd75d2013-05-23 10:04:25 +01001428 if (tcp_addr.sa.sa_family == AF_INET6)
1429 addr.addr.addr6 = tcp_addr.in6.sin6_addr;
Simon Kelleye25db1f2013-01-29 22:10:26 +00001430#endif
Simon Kelley76dd75d2013-05-23 10:04:25 +01001431
1432 for (iface = daemon->interfaces; iface; iface = iface->next)
1433 if (iface->index == if_index)
1434 break;
1435
1436 if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
1437 client_ok = 0;
1438 }
1439
1440 if (option_bool(OPT_CLEVERBIND))
1441 iface = listener->iface; /* May be NULL */
1442 else
1443 {
1444 /* Check for allowed interfaces when binding the wildcard address:
1445 we do this by looking for an interface with the same address as
1446 the local address of the TCP connection, then looking to see if that's
1447 an allowed interface. As a side effect, we get the netmask of the
1448 interface too, for localisation. */
1449
1450 for (iface = daemon->interfaces; iface; iface = iface->next)
1451 if (sockaddr_isequal(&iface->addr, &tcp_addr))
1452 break;
1453
1454 if (!iface)
1455 client_ok = 0;
1456 }
1457 }
1458
Simon Kelley22ce5502013-01-22 13:53:04 +00001459 if (!client_ok)
Simon Kelley832af0b2007-01-21 20:01:28 +00001460 {
1461 shutdown(confd, SHUT_RDWR);
1462 close(confd);
1463 }
1464#ifndef NO_FORK
Simon Kelley28866e92011-02-14 20:19:14 +00001465 else if (!option_bool(OPT_DEBUG) && (p = fork()) != 0)
Simon Kelley832af0b2007-01-21 20:01:28 +00001466 {
1467 if (p != -1)
1468 {
1469 int i;
1470 for (i = 0; i < MAX_PROCS; i++)
1471 if (daemon->tcp_pids[i] == 0)
1472 {
1473 daemon->tcp_pids[i] = p;
1474 break;
1475 }
1476 }
1477 close(confd);
1478 }
1479#endif
1480 else
1481 {
1482 unsigned char *buff;
1483 struct server *s;
1484 int flags;
Simon Kelley52d4abf2012-03-21 21:39:48 +00001485 struct in_addr netmask;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001486 int auth_dns;
Simon Kelley52d4abf2012-03-21 21:39:48 +00001487
1488 if (iface)
Simon Kelley4f7b3042012-11-28 21:27:02 +00001489 {
1490 netmask = iface->netmask;
1491 auth_dns = iface->dns_auth;
1492 }
Simon Kelley52d4abf2012-03-21 21:39:48 +00001493 else
Simon Kelley4f7b3042012-11-28 21:27:02 +00001494 {
1495 netmask.s_addr = 0;
1496 auth_dns = 0;
1497 }
Simon Kelley52d4abf2012-03-21 21:39:48 +00001498
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001499#ifndef NO_FORK
1500 /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
1501 terminate the process. */
Simon Kelley28866e92011-02-14 20:19:14 +00001502 if (!option_bool(OPT_DEBUG))
Simon Kelley832af0b2007-01-21 20:01:28 +00001503 alarm(CHILD_LIFETIME);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001504#endif
1505
Simon Kelley832af0b2007-01-21 20:01:28 +00001506 /* start with no upstream connections. */
1507 for (s = daemon->servers; s; s = s->next)
Simon Kelley7cebd202006-05-06 14:13:33 +01001508 s->tcpfd = -1;
Simon Kelley832af0b2007-01-21 20:01:28 +00001509
1510 /* The connected socket inherits non-blocking
1511 attribute from the listening socket.
1512 Reset that here. */
1513 if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
1514 fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
1515
Simon Kelley4f7b3042012-11-28 21:27:02 +00001516 buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
Simon Kelley7cebd202006-05-06 14:13:33 +01001517
Simon Kelley832af0b2007-01-21 20:01:28 +00001518 shutdown(confd, SHUT_RDWR);
1519 close(confd);
1520
1521 if (buff)
1522 free(buff);
1523
1524 for (s = daemon->servers; s; s = s->next)
1525 if (s->tcpfd != -1)
1526 {
1527 shutdown(s->tcpfd, SHUT_RDWR);
1528 close(s->tcpfd);
1529 }
Simon Kelley7cebd202006-05-06 14:13:33 +01001530#ifndef NO_FORK
Simon Kelley28866e92011-02-14 20:19:14 +00001531 if (!option_bool(OPT_DEBUG))
Simon Kelley5aabfc72007-08-29 11:24:47 +01001532 {
1533 flush_log();
1534 _exit(0);
1535 }
Simon Kelley7cebd202006-05-06 14:13:33 +01001536#endif
Simon Kelley832af0b2007-01-21 20:01:28 +00001537 }
1538 }
1539 }
Simon Kelley3be34542004-09-11 19:12:13 +01001540}
1541
Simon Kelley7622fc02009-06-04 20:32:05 +01001542#ifdef HAVE_DHCP
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001543int make_icmp_sock(void)
1544{
Simon Kelley7cebd202006-05-06 14:13:33 +01001545 int fd;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001546 int zeroopt = 0;
1547
1548 if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1)
1549 {
Simon Kelley7cebd202006-05-06 14:13:33 +01001550 if (!fix_fd(fd) ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001551 setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
1552 {
1553 close(fd);
1554 fd = -1;
1555 }
1556 }
1557
1558 return fd;
1559}
1560
Simon Kelley5aabfc72007-08-29 11:24:47 +01001561int icmp_ping(struct in_addr addr)
Simon Kelley3be34542004-09-11 19:12:13 +01001562{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001563 /* Try and get an ICMP echo from a machine. */
Simon Kelley3be34542004-09-11 19:12:13 +01001564
1565 /* Note that whilst in the three second wait, we check for
Simon Kelley832af0b2007-01-21 20:01:28 +00001566 (and service) events on the DNS and TFTP sockets, (so doing that
Simon Kelley3be34542004-09-11 19:12:13 +01001567 better not use any resources our caller has in use...)
1568 but we remain deaf to signals or further DHCP packets. */
1569
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001570 int fd;
Simon Kelley3be34542004-09-11 19:12:13 +01001571 struct sockaddr_in saddr;
1572 struct {
1573 struct ip ip;
1574 struct icmp icmp;
1575 } packet;
1576 unsigned short id = rand16();
1577 unsigned int i, j;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001578 int gotreply = 0;
Simon Kelley3be34542004-09-11 19:12:13 +01001579 time_t start, now;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001580
Simon Kelley824af852008-02-12 20:43:05 +00001581#if defined(HAVE_LINUX_NETWORK) || defined (HAVE_SOLARIS_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001582 if ((fd = make_icmp_sock()) == -1)
1583 return 0;
1584#else
1585 int opt = 2000;
1586 fd = daemon->dhcp_icmp_fd;
1587 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
1588#endif
1589
Simon Kelley3be34542004-09-11 19:12:13 +01001590 saddr.sin_family = AF_INET;
1591 saddr.sin_port = 0;
1592 saddr.sin_addr = addr;
1593#ifdef HAVE_SOCKADDR_SA_LEN
1594 saddr.sin_len = sizeof(struct sockaddr_in);
1595#endif
1596
1597 memset(&packet.icmp, 0, sizeof(packet.icmp));
1598 packet.icmp.icmp_type = ICMP_ECHO;
1599 packet.icmp.icmp_id = id;
1600 for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++)
1601 j += ((u16 *)&packet.icmp)[i];
1602 while (j>>16)
1603 j = (j & 0xffff) + (j >> 16);
1604 packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
1605
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001606 while (sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001607 (struct sockaddr *)&saddr, sizeof(saddr)) == -1 &&
1608 retry_send());
1609
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001610 for (now = start = dnsmasq_time();
1611 difftime(now, start) < (float)PING_WAIT;)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001612 {
1613 struct timeval tv;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001614 fd_set rset, wset;
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001615 struct sockaddr_in faddr;
Simon Kelley16972692006-10-16 20:04:18 +01001616 int maxfd = fd;
Simon Kelley3d8df262005-08-29 12:19:27 +01001617 socklen_t len = sizeof(faddr);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001618
1619 tv.tv_usec = 250000;
1620 tv.tv_sec = 0;
1621
1622 FD_ZERO(&rset);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001623 FD_ZERO(&wset);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001624 FD_SET(fd, &rset);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001625 set_dns_listeners(now, &rset, &maxfd);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001626 set_log_writer(&wset, &maxfd);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00001627
1628#ifdef HAVE_DHCP6
Simon Kelley1f776932012-12-16 19:46:08 +00001629 if (daemon->doing_ra)
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00001630 {
1631 FD_SET(daemon->icmp6fd, &rset);
1632 bump_maxfd(daemon->icmp6fd, &maxfd);
1633 }
1634#endif
1635
Simon Kelleyf2621c72007-04-29 19:47:21 +01001636 if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0)
1637 {
1638 FD_ZERO(&rset);
1639 FD_ZERO(&wset);
1640 }
1641
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001642 now = dnsmasq_time();
Simon Kelleyf2621c72007-04-29 19:47:21 +01001643
1644 check_log_writer(&wset);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001645 check_dns_listeners(&rset, now);
Simon Kelley832af0b2007-01-21 20:01:28 +00001646
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00001647#ifdef HAVE_DHCP6
Simon Kelley1f776932012-12-16 19:46:08 +00001648 if (daemon->doing_ra && FD_ISSET(daemon->icmp6fd, &rset))
1649 icmp6_packet(now);
Simon Kelleyc5ad4e72012-02-24 16:06:20 +00001650#endif
1651
Simon Kelley832af0b2007-01-21 20:01:28 +00001652#ifdef HAVE_TFTP
Simon Kelley5aabfc72007-08-29 11:24:47 +01001653 check_tftp_listeners(&rset, now);
Simon Kelley832af0b2007-01-21 20:01:28 +00001654#endif
1655
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001656 if (FD_ISSET(fd, &rset) &&
1657 recvfrom(fd, &packet, sizeof(packet), 0,
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001658 (struct sockaddr *)&faddr, &len) == sizeof(packet) &&
1659 saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
1660 packet.icmp.icmp_type == ICMP_ECHOREPLY &&
1661 packet.icmp.icmp_seq == 0 &&
1662 packet.icmp.icmp_id == id)
1663 {
1664 gotreply = 1;
1665 break;
1666 }
1667 }
1668
Simon Kelley824af852008-02-12 20:43:05 +00001669#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001670 close(fd);
1671#else
Simon Kelley3be34542004-09-11 19:12:13 +01001672 opt = 1;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001673 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
1674#endif
1675
Simon Kelley3be34542004-09-11 19:12:13 +01001676 return gotreply;
1677}
Simon Kelley7622fc02009-06-04 20:32:05 +01001678#endif
Simon Kelley0a852542005-03-23 20:28:59 +00001679
1680