blob: 7ace797a377ab2b60cbb43e75b62994266f0c320 [file] [log] [blame]
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
13/* See RFC1035 for details of the protocol this code talks. */
14
15/* Author's email: simon@thekelleys.org.uk */
16
17#include "dnsmasq.h"
18
Simon Kelley44a2a312004-03-10 20:04:35 +000019static int sigterm, sighup, sigusr1, sigalarm;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000020
21static void sig_handler(int sig)
22{
23 if (sig == SIGTERM)
24 sigterm = 1;
25 else if (sig == SIGHUP)
26 sighup = 1;
27 else if (sig == SIGUSR1)
28 sigusr1 = 1;
Simon Kelley44a2a312004-03-10 20:04:35 +000029 else if (sig == SIGALRM)
30 sigalarm = 1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000031}
32
33int main (int argc, char **argv)
34{
Simon Kelley9e4abcb2004-01-22 19:47:41 +000035 int cachesize = CACHESIZ;
36 int port = NAMESERVER_PORT;
Simon Kelley44a2a312004-03-10 20:04:35 +000037 int maxleases = MAXLEASES;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000038 int query_port = 0;
39 int first_loop = 1;
Simon Kelleyde379512004-06-22 20:23:33 +010040 int bind_fallback = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000041 unsigned long local_ttl = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +000042 unsigned int options, min_leasetime;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000043 char *runfile = RUNFILE;
44 time_t resolv_changed = 0;
45 time_t now, last = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +000046 struct irec *interfaces = NULL;
Simon Kelleyde379512004-06-22 20:23:33 +010047 struct listener *listener, *listeners = NULL;
Simon Kelley1cff1662004-03-12 08:12:58 +000048 struct doctor *doctors = NULL;
Simon Kelleyde379512004-06-22 20:23:33 +010049 struct mx_record *mxnames = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000050 char *mxtarget = NULL;
51 char *lease_file = NULL;
52 char *addn_hosts = NULL;
53 char *domain_suffix = NULL;
54 char *username = CHUSER;
55 char *groupname = CHGRP;
56 struct iname *if_names = NULL;
57 struct iname *if_addrs = NULL;
58 struct iname *if_except = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000059 struct server *serv_addrs = NULL;
60 char *dnamebuff, *packet;
Simon Kelley44a2a312004-03-10 20:04:35 +000061 int uptime_fd = -1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000062 struct server *servers, *last_server;
63 struct resolvc default_resolv = { NULL, 1, 0, RESOLVFILE };
64 struct resolvc *resolv = &default_resolv;
65 struct bogus_addr *bogus_addr = NULL;
66 struct serverfd *serverfdp, *sfds = NULL;
67 struct dhcp_context *dhcp_tmp, *dhcp = NULL;
68 struct dhcp_config *dhcp_configs = NULL;
69 struct dhcp_opt *dhcp_options = NULL;
Simon Kelleya84fa1d2004-04-23 22:21:21 +010070 struct dhcp_vendor *dhcp_vendors = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000071 char *dhcp_file = NULL, *dhcp_sname = NULL;
72 struct in_addr dhcp_next_server;
Simon Kelley44a2a312004-03-10 20:04:35 +000073 int leasefd = -1, dhcpfd = -1, dhcp_raw_fd = -1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000074 struct sigaction sigact;
75 sigset_t sigmask;
Simon Kelley33820b72004-04-03 21:10:00 +010076
Simon Kelley9e4abcb2004-01-22 19:47:41 +000077 sighup = 1; /* init cache the first time through */
78 sigusr1 = 0; /* but don't dump */
Simon Kelley9e4abcb2004-01-22 19:47:41 +000079 sigterm = 0; /* or die */
Simon Kelley44a2a312004-03-10 20:04:35 +000080#ifdef HAVE_BROKEN_RTC
81 sigalarm = 1; /* need regular lease dumps */
82#else
83 sigalarm = 0; /* or not */
84#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000085
86 sigact.sa_handler = sig_handler;
87 sigact.sa_flags = 0;
88 sigemptyset(&sigact.sa_mask);
89 sigaction(SIGUSR1, &sigact, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000090 sigaction(SIGHUP, &sigact, NULL);
91 sigaction(SIGTERM, &sigact, NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000092 sigaction(SIGALRM, &sigact, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000093
94 /* now block all the signals, they stay that way except
95 during the call to pselect */
96 sigaddset(&sigact.sa_mask, SIGUSR1);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000097 sigaddset(&sigact.sa_mask, SIGTERM);
98 sigaddset(&sigact.sa_mask, SIGHUP);
Simon Kelley44a2a312004-03-10 20:04:35 +000099 sigaddset(&sigact.sa_mask, SIGALRM);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000100 sigprocmask(SIG_BLOCK, &sigact.sa_mask, &sigmask);
101
102 /* These get allocated here to avoid overflowing the small stack
103 on embedded systems. dnamebuff is big enough to hold one
104 maximal sixed domain name and gets passed into all the processing
105 code. We manage to get away with one buffer. */
106 dnamebuff = safe_malloc(MAXDNAME);
Simon Kelley44a2a312004-03-10 20:04:35 +0000107 packet = safe_malloc(DNSMASQ_PACKETSZ);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000108
109 dhcp_next_server.s_addr = 0;
Simon Kelleyde379512004-06-22 20:23:33 +0100110 options = read_opts(argc, argv, dnamebuff, &resolv, &mxnames, &mxtarget, &lease_file,
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000111 &username, &groupname, &domain_suffix, &runfile,
112 &if_names, &if_addrs, &if_except, &bogus_addr,
113 &serv_addrs, &cachesize, &port, &query_port, &local_ttl, &addn_hosts,
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100114 &dhcp, &dhcp_configs, &dhcp_options, &dhcp_vendors,
Simon Kelley1cff1662004-03-12 08:12:58 +0000115 &dhcp_file, &dhcp_sname, &dhcp_next_server, &maxleases, &min_leasetime,
116 &doctors);
Simon Kelley44a2a312004-03-10 20:04:35 +0000117
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000118 if (!lease_file)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000119 {
Simon Kelley33820b72004-04-03 21:10:00 +0100120 if (dhcp)
121 lease_file = LEASEFILE;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000122 }
Simon Kelley33820b72004-04-03 21:10:00 +0100123#ifndef HAVE_ISC_READER
124 else if (!dhcp)
125 die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL);
126#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000127
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100128 interfaces = enumerate_interfaces(&if_names, &if_addrs, if_except, port);
Simon Kelleyde379512004-06-22 20:23:33 +0100129
130 if (!(options & OPT_NOWILD) && !(listeners = create_wildcard_listeners(port)))
131 {
132 bind_fallback = 1;
133 options |= OPT_NOWILD;
134 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000135
Simon Kelleyde379512004-06-22 20:23:33 +0100136 if (options & OPT_NOWILD)
137 {
138 struct iname *if_tmp;
139 listeners = create_bound_listeners(interfaces);
140
141 for (if_tmp = if_names; if_tmp; if_tmp = if_tmp->next)
142 if (if_tmp->name && !if_tmp->used)
143 die("unknown interface %s", if_tmp->name);
144
145 for (if_tmp = if_addrs; if_tmp; if_tmp = if_tmp->next)
146 if (!if_tmp->used)
147 {
148 char addrbuff[ADDRSTRLEN];
149#ifdef HAVE_IPV6
150 if (if_tmp->addr.sa.sa_family == AF_INET)
151 inet_ntop(AF_INET, &if_tmp->addr.in.sin_addr,
152 addrbuff, ADDRSTRLEN);
153 else
154 inet_ntop(AF_INET6, &if_tmp->addr.in6.sin6_addr,
155 addrbuff, ADDRSTRLEN);
156#else
157 strcpy(addrbuff, inet_ntoa(if_tmp->addr.in.sin_addr));
158#endif
159 die("no interface with address %s", addrbuff);
160 }
161 }
162
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000163 forward_init(1);
164 cache_init(cachesize, options & OPT_LOG);
Simon Kelley44a2a312004-03-10 20:04:35 +0000165
166#ifdef HAVE_BROKEN_RTC
167 if ((uptime_fd = open(UPTIME, O_RDONLY)) == -1)
168 die("cannot open " UPTIME ":%s", NULL);
169#endif
170
171 now = dnsmasq_time(uptime_fd);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000172
173 if (dhcp)
174 {
Simon Kelleyde379512004-06-22 20:23:33 +0100175#if !defined(IP_PKTINFO) && !defined(IP_RECVIF)
176 int c;
177 struct iname *tmp;
178 for (c = 0, tmp = if_names; tmp; tmp = tmp->next)
179 if (!tmp->isloop)
180 c++;
181 if (c != 1)
182 die("must set exactly one interface on broken systems without IP_RECVIF", NULL);
183#endif
Simon Kelley44a2a312004-03-10 20:04:35 +0000184 dhcp_init(&dhcpfd, &dhcp_raw_fd);
185 leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, now, maxleases);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000186 }
187
188 setbuf(stdout, NULL);
189
190 if (!(options & OPT_DEBUG))
191 {
192 FILE *pidfile;
193 struct passwd *ent_pw;
194 int i;
195
196 /* The following code "daemonizes" the process.
197 See Stevens section 12.4 */
198
199#ifndef NO_FORK
200 if (fork() != 0 )
201 exit(0);
202
203 setsid();
204
205 if (fork() != 0)
206 exit(0);
207#endif
208
209 chdir("/");
210 umask(022); /* make pidfile 0644 */
211
212 /* write pidfile _after_ forking ! */
213 if (runfile && (pidfile = fopen(runfile, "w")))
214 {
215 fprintf(pidfile, "%d\n", (int) getpid());
216 fclose(pidfile);
217 }
218
219 umask(0);
220
221 for (i=0; i<64; i++)
222 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000223 for (listener = listeners; listener; listener = listener->next)
224 if (listener->fd == i)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000225 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000226 if (listener)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000227 continue;
228
Simon Kelley44a2a312004-03-10 20:04:35 +0000229 if (i == leasefd ||
230 i == uptime_fd ||
231 i == dhcpfd ||
232 i == dhcp_raw_fd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000233 continue;
234
235 close(i);
236 }
237
238 /* Change uid and gid for security */
239 if (username && (ent_pw = getpwnam(username)))
240 {
241 gid_t dummy;
242 struct group *gp;
243 /* remove all supplimentary groups */
244 setgroups(0, &dummy);
245 /* change group for /etc/ppp/resolv.conf
246 otherwise get the group for "nobody" */
247 if ((groupname && (gp = getgrnam(groupname))) ||
248 (gp = getgrgid(ent_pw->pw_gid)))
249 setgid(gp->gr_gid);
250 /* finally drop root */
251 setuid(ent_pw->pw_uid);
252 }
253 }
254
255 openlog("dnsmasq",
256 DNSMASQ_LOG_OPT(options & OPT_DEBUG),
257 DNSMASQ_LOG_FAC(options & OPT_DEBUG));
258
259 if (cachesize)
260 syslog(LOG_INFO, "started, version %s cachesize %d", VERSION, cachesize);
261 else
262 syslog(LOG_INFO, "started, version %s cache disabled", VERSION);
263
Simon Kelleyde379512004-06-22 20:23:33 +0100264 if (bind_fallback)
265 syslog(LOG_WARNING, "setting --bind-interfaces option because if OS limitations");
266
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000267 for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
268 {
269 strcpy(dnamebuff, inet_ntoa(dhcp_tmp->start));
270 if (dhcp_tmp->lease_time == 0)
271 sprintf(packet, "infinite");
272 else
273 sprintf(packet, "%ds", (int)dhcp_tmp->lease_time);
Simon Kelley33820b72004-04-03 21:10:00 +0100274 syslog(LOG_INFO,
275 dhcp_tmp->start.s_addr == dhcp_tmp->end.s_addr ?
276 "DHCP, static leases only on %.0s%s, lease time %s" :
277 "DHCP, IP range %s -- %s, lease time %s",
Simon Kelley44a2a312004-03-10 20:04:35 +0000278 dnamebuff, inet_ntoa(dhcp_tmp->end), packet);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000279 }
280
Simon Kelley44a2a312004-03-10 20:04:35 +0000281#ifdef HAVE_BROKEN_RTC
282 if (dhcp)
283 syslog(LOG_INFO, "DHCP, %s will be written every %ds", lease_file, min_leasetime/3);
284#endif
285
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000286 if (getuid() == 0 || geteuid() == 0)
287 syslog(LOG_WARNING, "failed to drop root privs");
288
Simon Kelleyde379512004-06-22 20:23:33 +0100289 servers = check_servers(serv_addrs, interfaces, &sfds);
290 last_server = NULL;
291
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000292 while (sigterm == 0)
293 {
294 fd_set rset;
295
296 if (sighup)
297 {
298 cache_reload(options, dnamebuff, domain_suffix, addn_hosts);
Simon Kelley44a2a312004-03-10 20:04:35 +0000299 if (dhcp)
300 {
Simon Kelley33820b72004-04-03 21:10:00 +0100301 if (options & OPT_ETHERS)
302 dhcp_configs = dhcp_read_ethers(dhcp_configs, dnamebuff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000303 dhcp_update_configs(dhcp_configs);
304 lease_update_from_configs(dhcp_configs, domain_suffix);
305 lease_update_file(0, now);
306 lease_update_dns();
307 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000308 if (resolv && (options & OPT_NO_POLL))
Simon Kelleyde379512004-06-22 20:23:33 +0100309 {
310 servers = check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port),
311 interfaces, &sfds);
312 last_server = NULL;
313 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000314 sighup = 0;
315 }
316
317 if (sigusr1)
318 {
319 dump_cache(options & (OPT_DEBUG | OPT_LOG), cachesize);
320 sigusr1 = 0;
321 }
322
Simon Kelley44a2a312004-03-10 20:04:35 +0000323 if (sigalarm)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000324 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000325 if (dhcp)
326 {
327 lease_update_file(1, now);
328#ifdef HAVE_BROKEN_RTC
329 alarm(min_leasetime/3);
330#endif
331 }
332 sigalarm = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000333 }
334
335 FD_ZERO(&rset);
336
337 if (!first_loop)
338 {
339 int maxfd = 0;
340
341 for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
342 {
343 FD_SET(serverfdp->fd, &rset);
344 if (serverfdp->fd > maxfd)
345 maxfd = serverfdp->fd;
346 }
347
Simon Kelley44a2a312004-03-10 20:04:35 +0000348 for (listener = listeners; listener; listener = listener->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000349 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000350 FD_SET(listener->fd, &rset);
351 if (listener->fd > maxfd)
352 maxfd = listener->fd;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000353 }
354
Simon Kelley44a2a312004-03-10 20:04:35 +0000355 if (dhcp)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000356 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000357 FD_SET(dhcpfd, &rset);
358 if (dhcpfd > maxfd)
359 maxfd = dhcpfd;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000360 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000361
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000362#ifdef HAVE_PSELECT
363 if (pselect(maxfd+1, &rset, NULL, NULL, NULL, &sigmask) < 0)
364 FD_ZERO(&rset); /* rset otherwise undefined after error */
365#else
366 {
367 sigset_t save_mask;
368 sigprocmask(SIG_SETMASK, &sigmask, &save_mask);
369 if (select(maxfd+1, &rset, NULL, NULL, NULL) < 0)
370 FD_ZERO(&rset); /* rset otherwise undefined after error */
371 sigprocmask(SIG_SETMASK, &save_mask, NULL);
372 }
373#endif
374
375 }
376
377 first_loop = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000378 now = dnsmasq_time(uptime_fd);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000379
380 /* Check for changes to resolv files once per second max. */
381 if (last == 0 || difftime(now, last) > 1.0)
382 {
383 last = now;
Simon Kelley33820b72004-04-03 21:10:00 +0100384
385#ifdef HAVE_ISC_READER
386 if (lease_file && !dhcp)
387 load_dhcp(lease_file, domain_suffix, now, dnamebuff);
388#endif
389
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000390 if (!(options & OPT_NO_POLL))
391 {
392 struct resolvc *res = resolv, *latest = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000393 struct stat statbuf;
Simon Kelley33820b72004-04-03 21:10:00 +0100394 time_t last_change = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000395 /* There may be more than one possible file.
396 Go through and find the one which changed _last_.
397 Warn of any which can't be read. */
398 while (res)
399 {
400 if (stat(res->name, &statbuf) == -1)
401 {
402 if (!res->logged)
403 syslog(LOG_WARNING, "failed to access %s: %m", res->name);
404 res->logged = 1;
405 }
406 else
407 {
408 res->logged = 0;
Simon Kelleyde379512004-06-22 20:23:33 +0100409 if (difftime(statbuf.st_mtime, last_change) > 0.0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000410 {
411 last_change = statbuf.st_mtime;
412 latest = res;
413 }
414 }
415 res = res->next;
416 }
417
Simon Kelleyde379512004-06-22 20:23:33 +0100418 if (latest && difftime(last_change, resolv_changed) > 0.0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000419 {
420 resolv_changed = last_change;
Simon Kelleyde379512004-06-22 20:23:33 +0100421 servers = check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
422 interfaces, &sfds);
423 last_server = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000424 }
425 }
426 }
427
428 for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
429 if (FD_ISSET(serverfdp->fd, &rset))
Simon Kelleyde379512004-06-22 20:23:33 +0100430 last_server = reply_query(serverfdp, options, packet, now,
431 dnamebuff, servers, last_server, bogus_addr, doctors);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000432
Simon Kelley44a2a312004-03-10 20:04:35 +0000433 if (dhcp && FD_ISSET(dhcpfd, &rset))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100434 dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs, dhcp_vendors,
Simon Kelley44a2a312004-03-10 20:04:35 +0000435 now, dnamebuff, domain_suffix, dhcp_file,
436 dhcp_sname, dhcp_next_server, dhcpfd, dhcp_raw_fd,
437 if_names, if_addrs, if_except);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000438
Simon Kelley44a2a312004-03-10 20:04:35 +0000439 for (listener = listeners; listener; listener = listener->next)
440 if (FD_ISSET(listener->fd, &rset))
441 last_server = receive_query(listener, packet,
Simon Kelleyde379512004-06-22 20:23:33 +0100442 mxnames, mxtarget, options, now, local_ttl, dnamebuff,
Simon Kelley44a2a312004-03-10 20:04:35 +0000443 if_names, if_addrs, if_except, last_server, servers);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000444 }
445
446 syslog(LOG_INFO, "exiting on receipt of SIGTERM");
Simon Kelley44a2a312004-03-10 20:04:35 +0000447
448#ifdef HAVE_BROKEN_RTC
449 if (dhcp)
450 lease_update_file(1, now);
451#endif
452
453 if (leasefd != -1)
454 close(leasefd);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000455 return 0;
456}
457
458
459
460
461
462