]>
Commit | Line | Data |
---|---|---|
9e4abcb5 SK |
1 | /* 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 | ||
44a2a316 | 19 | static int sigterm, sighup, sigusr1, sigalarm; |
9e4abcb5 SK |
20 | |
21 | static 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; | |
44a2a316 SK |
29 | else if (sig == SIGALRM) |
30 | sigalarm = 1; | |
9e4abcb5 SK |
31 | } |
32 | ||
33 | int main (int argc, char **argv) | |
34 | { | |
9e4abcb5 SK |
35 | int cachesize = CACHESIZ; |
36 | int port = NAMESERVER_PORT; | |
44a2a316 | 37 | int maxleases = MAXLEASES; |
9e4abcb5 SK |
38 | int query_port = 0; |
39 | int first_loop = 1; | |
de37951c | 40 | int bind_fallback = 0; |
9e4abcb5 | 41 | unsigned long local_ttl = 0; |
44a2a316 | 42 | unsigned int options, min_leasetime; |
9e4abcb5 SK |
43 | char *runfile = RUNFILE; |
44 | time_t resolv_changed = 0; | |
45 | time_t now, last = 0; | |
44a2a316 | 46 | struct irec *interfaces = NULL; |
de37951c | 47 | struct listener *listener, *listeners = NULL; |
1cff166d | 48 | struct doctor *doctors = NULL; |
de37951c | 49 | struct mx_record *mxnames = NULL; |
9e4abcb5 SK |
50 | 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; | |
9e4abcb5 SK |
59 | struct server *serv_addrs = NULL; |
60 | char *dnamebuff, *packet; | |
44a2a316 | 61 | int uptime_fd = -1; |
9e4abcb5 SK |
62 | 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; | |
a84fa1d0 | 70 | struct dhcp_vendor *dhcp_vendors = NULL; |
9e4abcb5 SK |
71 | char *dhcp_file = NULL, *dhcp_sname = NULL; |
72 | struct in_addr dhcp_next_server; | |
44a2a316 | 73 | int leasefd = -1, dhcpfd = -1, dhcp_raw_fd = -1; |
9e4abcb5 SK |
74 | struct sigaction sigact; |
75 | sigset_t sigmask; | |
33820b7e | 76 | |
9e4abcb5 SK |
77 | sighup = 1; /* init cache the first time through */ |
78 | sigusr1 = 0; /* but don't dump */ | |
9e4abcb5 | 79 | sigterm = 0; /* or die */ |
44a2a316 SK |
80 | #ifdef HAVE_BROKEN_RTC |
81 | sigalarm = 1; /* need regular lease dumps */ | |
82 | #else | |
83 | sigalarm = 0; /* or not */ | |
84 | #endif | |
9e4abcb5 SK |
85 | |
86 | sigact.sa_handler = sig_handler; | |
87 | sigact.sa_flags = 0; | |
88 | sigemptyset(&sigact.sa_mask); | |
89 | sigaction(SIGUSR1, &sigact, NULL); | |
9e4abcb5 SK |
90 | sigaction(SIGHUP, &sigact, NULL); |
91 | sigaction(SIGTERM, &sigact, NULL); | |
44a2a316 | 92 | sigaction(SIGALRM, &sigact, NULL); |
9e4abcb5 SK |
93 | |
94 | /* now block all the signals, they stay that way except | |
95 | during the call to pselect */ | |
96 | sigaddset(&sigact.sa_mask, SIGUSR1); | |
9e4abcb5 SK |
97 | sigaddset(&sigact.sa_mask, SIGTERM); |
98 | sigaddset(&sigact.sa_mask, SIGHUP); | |
44a2a316 | 99 | sigaddset(&sigact.sa_mask, SIGALRM); |
9e4abcb5 SK |
100 | 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); | |
44a2a316 | 107 | packet = safe_malloc(DNSMASQ_PACKETSZ); |
9e4abcb5 SK |
108 | |
109 | dhcp_next_server.s_addr = 0; | |
de37951c | 110 | options = read_opts(argc, argv, dnamebuff, &resolv, &mxnames, &mxtarget, &lease_file, |
9e4abcb5 SK |
111 | &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, | |
a84fa1d0 | 114 | &dhcp, &dhcp_configs, &dhcp_options, &dhcp_vendors, |
1cff166d SK |
115 | &dhcp_file, &dhcp_sname, &dhcp_next_server, &maxleases, &min_leasetime, |
116 | &doctors); | |
44a2a316 | 117 | |
9e4abcb5 | 118 | if (!lease_file) |
9e4abcb5 | 119 | { |
33820b7e SK |
120 | if (dhcp) |
121 | lease_file = LEASEFILE; | |
9e4abcb5 | 122 | } |
33820b7e SK |
123 | #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 | |
9e4abcb5 | 127 | |
a84fa1d0 | 128 | interfaces = enumerate_interfaces(&if_names, &if_addrs, if_except, port); |
de37951c SK |
129 | |
130 | if (!(options & OPT_NOWILD) && !(listeners = create_wildcard_listeners(port))) | |
131 | { | |
132 | bind_fallback = 1; | |
133 | options |= OPT_NOWILD; | |
134 | } | |
9e4abcb5 | 135 | |
de37951c SK |
136 | 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 | ||
9e4abcb5 SK |
163 | forward_init(1); |
164 | cache_init(cachesize, options & OPT_LOG); | |
44a2a316 SK |
165 | |
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); | |
9e4abcb5 SK |
172 | |
173 | if (dhcp) | |
174 | { | |
de37951c SK |
175 | #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 | |
44a2a316 SK |
184 | dhcp_init(&dhcpfd, &dhcp_raw_fd); |
185 | leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, now, maxleases); | |
9e4abcb5 SK |
186 | } |
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 | { | |
44a2a316 SK |
223 | for (listener = listeners; listener; listener = listener->next) |
224 | if (listener->fd == i) | |
9e4abcb5 | 225 | break; |
44a2a316 | 226 | if (listener) |
9e4abcb5 SK |
227 | continue; |
228 | ||
44a2a316 SK |
229 | if (i == leasefd || |
230 | i == uptime_fd || | |
231 | i == dhcpfd || | |
232 | i == dhcp_raw_fd) | |
9e4abcb5 SK |
233 | 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 | ||
de37951c SK |
264 | if (bind_fallback) |
265 | syslog(LOG_WARNING, "setting --bind-interfaces option because if OS limitations"); | |
266 | ||
9e4abcb5 SK |
267 | 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); | |
33820b7e SK |
274 | 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", | |
44a2a316 | 278 | dnamebuff, inet_ntoa(dhcp_tmp->end), packet); |
9e4abcb5 SK |
279 | } |
280 | ||
44a2a316 SK |
281 | #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 | ||
9e4abcb5 SK |
286 | if (getuid() == 0 || geteuid() == 0) |
287 | syslog(LOG_WARNING, "failed to drop root privs"); | |
288 | ||
de37951c SK |
289 | servers = check_servers(serv_addrs, interfaces, &sfds); |
290 | last_server = NULL; | |
291 | ||
9e4abcb5 SK |
292 | while (sigterm == 0) |
293 | { | |
294 | fd_set rset; | |
295 | ||
296 | if (sighup) | |
297 | { | |
298 | cache_reload(options, dnamebuff, domain_suffix, addn_hosts); | |
44a2a316 SK |
299 | if (dhcp) |
300 | { | |
33820b7e SK |
301 | if (options & OPT_ETHERS) |
302 | dhcp_configs = dhcp_read_ethers(dhcp_configs, dnamebuff); | |
44a2a316 SK |
303 | 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 | } | |
9e4abcb5 | 308 | if (resolv && (options & OPT_NO_POLL)) |
de37951c SK |
309 | { |
310 | servers = check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port), | |
311 | interfaces, &sfds); | |
312 | last_server = NULL; | |
313 | } | |
9e4abcb5 SK |
314 | sighup = 0; |
315 | } | |
316 | ||
317 | if (sigusr1) | |
318 | { | |
319 | dump_cache(options & (OPT_DEBUG | OPT_LOG), cachesize); | |
320 | sigusr1 = 0; | |
321 | } | |
322 | ||
44a2a316 | 323 | if (sigalarm) |
9e4abcb5 | 324 | { |
44a2a316 SK |
325 | 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; | |
9e4abcb5 SK |
333 | } |
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 | ||
44a2a316 | 348 | for (listener = listeners; listener; listener = listener->next) |
9e4abcb5 | 349 | { |
44a2a316 SK |
350 | FD_SET(listener->fd, &rset); |
351 | if (listener->fd > maxfd) | |
352 | maxfd = listener->fd; | |
9e4abcb5 SK |
353 | } |
354 | ||
44a2a316 | 355 | if (dhcp) |
9e4abcb5 | 356 | { |
44a2a316 SK |
357 | FD_SET(dhcpfd, &rset); |
358 | if (dhcpfd > maxfd) | |
359 | maxfd = dhcpfd; | |
9e4abcb5 | 360 | } |
44a2a316 | 361 | |
9e4abcb5 SK |
362 | #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; | |
44a2a316 | 378 | now = dnsmasq_time(uptime_fd); |
9e4abcb5 SK |
379 | |
380 | /* Check for changes to resolv files once per second max. */ | |
381 | if (last == 0 || difftime(now, last) > 1.0) | |
382 | { | |
383 | last = now; | |
33820b7e SK |
384 | |
385 | #ifdef HAVE_ISC_READER | |
386 | if (lease_file && !dhcp) | |
387 | load_dhcp(lease_file, domain_suffix, now, dnamebuff); | |
388 | #endif | |
389 | ||
9e4abcb5 SK |
390 | if (!(options & OPT_NO_POLL)) |
391 | { | |
392 | struct resolvc *res = resolv, *latest = NULL; | |
9e4abcb5 | 393 | struct stat statbuf; |
33820b7e | 394 | time_t last_change = 0; |
9e4abcb5 SK |
395 | /* 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; | |
de37951c | 409 | if (difftime(statbuf.st_mtime, last_change) > 0.0) |
9e4abcb5 SK |
410 | { |
411 | last_change = statbuf.st_mtime; | |
412 | latest = res; | |
413 | } | |
414 | } | |
415 | res = res->next; | |
416 | } | |
417 | ||
de37951c | 418 | if (latest && difftime(last_change, resolv_changed) > 0.0) |
9e4abcb5 SK |
419 | { |
420 | resolv_changed = last_change; | |
de37951c SK |
421 | servers = check_servers(reload_servers(latest->name, dnamebuff, servers, query_port), |
422 | interfaces, &sfds); | |
423 | last_server = NULL; | |
9e4abcb5 SK |
424 | } |
425 | } | |
426 | } | |
427 | ||
428 | for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next) | |
429 | if (FD_ISSET(serverfdp->fd, &rset)) | |
de37951c SK |
430 | last_server = reply_query(serverfdp, options, packet, now, |
431 | dnamebuff, servers, last_server, bogus_addr, doctors); | |
9e4abcb5 | 432 | |
44a2a316 | 433 | if (dhcp && FD_ISSET(dhcpfd, &rset)) |
a84fa1d0 | 434 | dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs, dhcp_vendors, |
44a2a316 SK |
435 | now, dnamebuff, domain_suffix, dhcp_file, |
436 | dhcp_sname, dhcp_next_server, dhcpfd, dhcp_raw_fd, | |
437 | if_names, if_addrs, if_except); | |
9e4abcb5 | 438 | |
44a2a316 SK |
439 | for (listener = listeners; listener; listener = listener->next) |
440 | if (FD_ISSET(listener->fd, &rset)) | |
441 | last_server = receive_query(listener, packet, | |
de37951c | 442 | mxnames, mxtarget, options, now, local_ttl, dnamebuff, |
44a2a316 | 443 | if_names, if_addrs, if_except, last_server, servers); |
9e4abcb5 SK |
444 | } |
445 | ||
446 | syslog(LOG_INFO, "exiting on receipt of SIGTERM"); | |
44a2a316 SK |
447 | |
448 | #ifdef HAVE_BROKEN_RTC | |
449 | if (dhcp) | |
450 | lease_update_file(1, now); | |
451 | #endif | |
452 | ||
453 | if (leasefd != -1) | |
454 | close(leasefd); | |
9e4abcb5 SK |
455 | return 0; |
456 | } | |
457 | ||
458 | ||
459 | ||
460 | ||
461 | ||
462 |