blob: c626ed9d345b7ca24ab1be3a0f09c6c70d0f49dd [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;
40 unsigned long local_ttl = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +000041 unsigned int options, min_leasetime;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000042 char *runfile = RUNFILE;
43 time_t resolv_changed = 0;
44 time_t now, last = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +000045 struct irec *interfaces = NULL;
46 struct listener *listener, *listeners;
Simon Kelley1cff1662004-03-12 08:12:58 +000047 struct doctor *doctors = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000048 char *mxname = NULL;
49 char *mxtarget = NULL;
50 char *lease_file = NULL;
51 char *addn_hosts = NULL;
52 char *domain_suffix = NULL;
53 char *username = CHUSER;
54 char *groupname = CHGRP;
55 struct iname *if_names = NULL;
56 struct iname *if_addrs = NULL;
57 struct iname *if_except = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000058 struct server *serv_addrs = NULL;
59 char *dnamebuff, *packet;
Simon Kelley44a2a312004-03-10 20:04:35 +000060 int uptime_fd = -1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000061 struct server *servers, *last_server;
62 struct resolvc default_resolv = { NULL, 1, 0, RESOLVFILE };
63 struct resolvc *resolv = &default_resolv;
64 struct bogus_addr *bogus_addr = NULL;
65 struct serverfd *serverfdp, *sfds = NULL;
66 struct dhcp_context *dhcp_tmp, *dhcp = NULL;
67 struct dhcp_config *dhcp_configs = NULL;
68 struct dhcp_opt *dhcp_options = NULL;
69 char *dhcp_file = NULL, *dhcp_sname = NULL;
70 struct in_addr dhcp_next_server;
Simon Kelley44a2a312004-03-10 20:04:35 +000071 int leasefd = -1, dhcpfd = -1, dhcp_raw_fd = -1;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000072 struct sigaction sigact;
73 sigset_t sigmask;
74
75 sighup = 1; /* init cache the first time through */
76 sigusr1 = 0; /* but don't dump */
Simon Kelley9e4abcb2004-01-22 19:47:41 +000077 sigterm = 0; /* or die */
Simon Kelley44a2a312004-03-10 20:04:35 +000078#ifdef HAVE_BROKEN_RTC
79 sigalarm = 1; /* need regular lease dumps */
80#else
81 sigalarm = 0; /* or not */
82#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +000083
84 sigact.sa_handler = sig_handler;
85 sigact.sa_flags = 0;
86 sigemptyset(&sigact.sa_mask);
87 sigaction(SIGUSR1, &sigact, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000088 sigaction(SIGHUP, &sigact, NULL);
89 sigaction(SIGTERM, &sigact, NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +000090 sigaction(SIGALRM, &sigact, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000091
92 /* now block all the signals, they stay that way except
93 during the call to pselect */
94 sigaddset(&sigact.sa_mask, SIGUSR1);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000095 sigaddset(&sigact.sa_mask, SIGTERM);
96 sigaddset(&sigact.sa_mask, SIGHUP);
Simon Kelley44a2a312004-03-10 20:04:35 +000097 sigaddset(&sigact.sa_mask, SIGALRM);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000098 sigprocmask(SIG_BLOCK, &sigact.sa_mask, &sigmask);
99
100 /* These get allocated here to avoid overflowing the small stack
101 on embedded systems. dnamebuff is big enough to hold one
102 maximal sixed domain name and gets passed into all the processing
103 code. We manage to get away with one buffer. */
104 dnamebuff = safe_malloc(MAXDNAME);
Simon Kelley44a2a312004-03-10 20:04:35 +0000105 packet = safe_malloc(DNSMASQ_PACKETSZ);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000106
107 dhcp_next_server.s_addr = 0;
108 options = read_opts(argc, argv, dnamebuff, &resolv, &mxname, &mxtarget, &lease_file,
109 &username, &groupname, &domain_suffix, &runfile,
110 &if_names, &if_addrs, &if_except, &bogus_addr,
111 &serv_addrs, &cachesize, &port, &query_port, &local_ttl, &addn_hosts,
Simon Kelley44a2a312004-03-10 20:04:35 +0000112 &dhcp, &dhcp_configs, &dhcp_options,
Simon Kelley1cff1662004-03-12 08:12:58 +0000113 &dhcp_file, &dhcp_sname, &dhcp_next_server, &maxleases, &min_leasetime,
114 &doctors);
Simon Kelley44a2a312004-03-10 20:04:35 +0000115
116 /* if we cannot support binding the wildcard address, set the "bind only
117 interfaces in use" option */
118#ifndef HAVE_UDP_SRC_DST
119 options |= OPT_NOWILD;
120#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000121
122 if (!lease_file)
123 lease_file = LEASEFILE;
124 else
125 {
126 if (!dhcp)
127 {
128 complain("********* dhcp-lease option set, but not dhcp-range.", NULL);
129 complain("********* Are you trying to use the obsolete ISC dhcpd integration?", NULL);
130 complain("********* Please configure the dnsmasq integrated DHCP server by using", NULL);
131 complain("********* the \"dhcp-range\" option, and remove any other DHCP server.", NULL);
132 }
133 }
134
Simon Kelley44a2a312004-03-10 20:04:35 +0000135 interfaces = enumerate_interfaces(if_names, if_addrs, if_except, port);
136 if (options & OPT_NOWILD)
137 listeners = create_bound_listeners(interfaces);
138 else
139 listeners = create_wildcard_listeners(port);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000140
141 forward_init(1);
142 cache_init(cachesize, options & OPT_LOG);
Simon Kelley44a2a312004-03-10 20:04:35 +0000143
144#ifdef HAVE_BROKEN_RTC
145 if ((uptime_fd = open(UPTIME, O_RDONLY)) == -1)
146 die("cannot open " UPTIME ":%s", NULL);
147#endif
148
149 now = dnsmasq_time(uptime_fd);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000150
151 if (dhcp)
152 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000153 dhcp_init(&dhcpfd, &dhcp_raw_fd);
154 leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, now, maxleases);
155 if (options & OPT_ETHERS)
156 dhcp_configs = dhcp_read_ethers(dhcp_configs, dnamebuff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000157 lease_update_from_configs(dhcp_configs, domain_suffix); /* must follow cache_init and lease_init */
158 lease_update_file(0, now);
159 lease_update_dns();
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000160 }
161
162 setbuf(stdout, NULL);
163
164 if (!(options & OPT_DEBUG))
165 {
166 FILE *pidfile;
167 struct passwd *ent_pw;
168 int i;
169
170 /* The following code "daemonizes" the process.
171 See Stevens section 12.4 */
172
173#ifndef NO_FORK
174 if (fork() != 0 )
175 exit(0);
176
177 setsid();
178
179 if (fork() != 0)
180 exit(0);
181#endif
182
183 chdir("/");
184 umask(022); /* make pidfile 0644 */
185
186 /* write pidfile _after_ forking ! */
187 if (runfile && (pidfile = fopen(runfile, "w")))
188 {
189 fprintf(pidfile, "%d\n", (int) getpid());
190 fclose(pidfile);
191 }
192
193 umask(0);
194
195 for (i=0; i<64; i++)
196 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000197 for (listener = listeners; listener; listener = listener->next)
198 if (listener->fd == i)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000199 break;
Simon Kelley44a2a312004-03-10 20:04:35 +0000200 if (listener)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000201 continue;
202
Simon Kelley44a2a312004-03-10 20:04:35 +0000203 if (i == leasefd ||
204 i == uptime_fd ||
205 i == dhcpfd ||
206 i == dhcp_raw_fd)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000207 continue;
208
209 close(i);
210 }
211
212 /* Change uid and gid for security */
213 if (username && (ent_pw = getpwnam(username)))
214 {
215 gid_t dummy;
216 struct group *gp;
217 /* remove all supplimentary groups */
218 setgroups(0, &dummy);
219 /* change group for /etc/ppp/resolv.conf
220 otherwise get the group for "nobody" */
221 if ((groupname && (gp = getgrnam(groupname))) ||
222 (gp = getgrgid(ent_pw->pw_gid)))
223 setgid(gp->gr_gid);
224 /* finally drop root */
225 setuid(ent_pw->pw_uid);
226 }
227 }
228
229 openlog("dnsmasq",
230 DNSMASQ_LOG_OPT(options & OPT_DEBUG),
231 DNSMASQ_LOG_FAC(options & OPT_DEBUG));
232
233 if (cachesize)
234 syslog(LOG_INFO, "started, version %s cachesize %d", VERSION, cachesize);
235 else
236 syslog(LOG_INFO, "started, version %s cache disabled", VERSION);
237
238 if (options & OPT_LOCALMX)
239 syslog(LOG_INFO, "serving MX record for local hosts target %s", mxtarget);
240 else if (mxname)
241 syslog(LOG_INFO, "serving MX record for mailhost %s target %s",
242 mxname, mxtarget);
243
244 for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
245 {
246 strcpy(dnamebuff, inet_ntoa(dhcp_tmp->start));
247 if (dhcp_tmp->lease_time == 0)
248 sprintf(packet, "infinite");
249 else
250 sprintf(packet, "%ds", (int)dhcp_tmp->lease_time);
Simon Kelley44a2a312004-03-10 20:04:35 +0000251 syslog(LOG_INFO, "DHCP, IP range %s -- %s, lease time %s",
252 dnamebuff, inet_ntoa(dhcp_tmp->end), packet);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000253 }
254
Simon Kelley44a2a312004-03-10 20:04:35 +0000255#ifdef HAVE_BROKEN_RTC
256 if (dhcp)
257 syslog(LOG_INFO, "DHCP, %s will be written every %ds", lease_file, min_leasetime/3);
258#endif
259
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000260 if (getuid() == 0 || geteuid() == 0)
261 syslog(LOG_WARNING, "failed to drop root privs");
262
263 servers = last_server = check_servers(serv_addrs, interfaces, &sfds);
264
265 while (sigterm == 0)
266 {
267 fd_set rset;
268
269 if (sighup)
270 {
271 cache_reload(options, dnamebuff, domain_suffix, addn_hosts);
Simon Kelley44a2a312004-03-10 20:04:35 +0000272 if (dhcp)
273 {
274 dhcp_update_configs(dhcp_configs);
275 lease_update_from_configs(dhcp_configs, domain_suffix);
276 lease_update_file(0, now);
277 lease_update_dns();
278 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000279 if (resolv && (options & OPT_NO_POLL))
280 servers = last_server =
281 check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port),
282 interfaces, &sfds);
283 sighup = 0;
284 }
285
286 if (sigusr1)
287 {
288 dump_cache(options & (OPT_DEBUG | OPT_LOG), cachesize);
289 sigusr1 = 0;
290 }
291
Simon Kelley44a2a312004-03-10 20:04:35 +0000292 if (sigalarm)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000293 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000294 if (dhcp)
295 {
296 lease_update_file(1, now);
297#ifdef HAVE_BROKEN_RTC
298 alarm(min_leasetime/3);
299#endif
300 }
301 sigalarm = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000302 }
303
304 FD_ZERO(&rset);
305
306 if (!first_loop)
307 {
308 int maxfd = 0;
309
310 for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
311 {
312 FD_SET(serverfdp->fd, &rset);
313 if (serverfdp->fd > maxfd)
314 maxfd = serverfdp->fd;
315 }
316
Simon Kelley44a2a312004-03-10 20:04:35 +0000317 for (listener = listeners; listener; listener = listener->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000318 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000319 FD_SET(listener->fd, &rset);
320 if (listener->fd > maxfd)
321 maxfd = listener->fd;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000322 }
323
Simon Kelley44a2a312004-03-10 20:04:35 +0000324 if (dhcp)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000325 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000326 FD_SET(dhcpfd, &rset);
327 if (dhcpfd > maxfd)
328 maxfd = dhcpfd;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000329 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000330
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000331#ifdef HAVE_PSELECT
332 if (pselect(maxfd+1, &rset, NULL, NULL, NULL, &sigmask) < 0)
333 FD_ZERO(&rset); /* rset otherwise undefined after error */
334#else
335 {
336 sigset_t save_mask;
337 sigprocmask(SIG_SETMASK, &sigmask, &save_mask);
338 if (select(maxfd+1, &rset, NULL, NULL, NULL) < 0)
339 FD_ZERO(&rset); /* rset otherwise undefined after error */
340 sigprocmask(SIG_SETMASK, &save_mask, NULL);
341 }
342#endif
343
344 }
345
346 first_loop = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000347 now = dnsmasq_time(uptime_fd);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000348
349 /* Check for changes to resolv files once per second max. */
350 if (last == 0 || difftime(now, last) > 1.0)
351 {
352 last = now;
353 if (!(options & OPT_NO_POLL))
354 {
355 struct resolvc *res = resolv, *latest = NULL;
356 time_t last_change = 0;
357 struct stat statbuf;
358 /* There may be more than one possible file.
359 Go through and find the one which changed _last_.
360 Warn of any which can't be read. */
361 while (res)
362 {
363 if (stat(res->name, &statbuf) == -1)
364 {
365 if (!res->logged)
366 syslog(LOG_WARNING, "failed to access %s: %m", res->name);
367 res->logged = 1;
368 }
369 else
370 {
371 res->logged = 0;
372 if (statbuf.st_mtime > last_change)
373 {
374 last_change = statbuf.st_mtime;
375 latest = res;
376 }
377 }
378 res = res->next;
379 }
380
381 if (latest && last_change > resolv_changed)
382 {
383 resolv_changed = last_change;
384 servers = last_server =
385 check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
386 interfaces, &sfds);
387 }
388 }
389 }
390
391 for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
392 if (FD_ISSET(serverfdp->fd, &rset))
393 last_server = reply_query(serverfdp->fd, options, packet, now,
Simon Kelley1cff1662004-03-12 08:12:58 +0000394 dnamebuff, last_server, bogus_addr, doctors);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000395
Simon Kelley44a2a312004-03-10 20:04:35 +0000396 if (dhcp && FD_ISSET(dhcpfd, &rset))
397 dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs,
398 now, dnamebuff, domain_suffix, dhcp_file,
399 dhcp_sname, dhcp_next_server, dhcpfd, dhcp_raw_fd,
400 if_names, if_addrs, if_except);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000401
Simon Kelley44a2a312004-03-10 20:04:35 +0000402 for (listener = listeners; listener; listener = listener->next)
403 if (FD_ISSET(listener->fd, &rset))
404 last_server = receive_query(listener, packet,
405 mxname, mxtarget, options, now, local_ttl, dnamebuff,
406 if_names, if_addrs, if_except, last_server, servers);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000407 }
408
409 syslog(LOG_INFO, "exiting on receipt of SIGTERM");
Simon Kelley44a2a312004-03-10 20:04:35 +0000410
411#ifdef HAVE_BROKEN_RTC
412 if (dhcp)
413 lease_update_file(1, now);
414#endif
415
416 if (leasefd != -1)
417 close(leasefd);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000418 return 0;
419}
420
421
422
423
424
425