]>
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 | ||
19 | static int sigterm, sighup, sigusr1, sigusr2; | |
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; | |
29 | else if (sig == SIGUSR2) | |
30 | sigusr2 = 1; | |
31 | } | |
32 | ||
33 | int main (int argc, char **argv) | |
34 | { | |
35 | char *int_err_string; | |
36 | int cachesize = CACHESIZ; | |
37 | int port = NAMESERVER_PORT; | |
38 | int query_port = 0; | |
39 | int first_loop = 1; | |
40 | unsigned long local_ttl = 0; | |
41 | unsigned int options; | |
42 | char *runfile = RUNFILE; | |
43 | time_t resolv_changed = 0; | |
44 | time_t now, last = 0; | |
45 | struct irec *iface, *interfaces = NULL; | |
46 | char *mxname = NULL; | |
47 | char *mxtarget = NULL; | |
48 | char *lease_file = NULL; | |
49 | char *addn_hosts = NULL; | |
50 | char *domain_suffix = NULL; | |
51 | char *username = CHUSER; | |
52 | char *groupname = CHGRP; | |
53 | struct iname *if_names = NULL; | |
54 | struct iname *if_addrs = NULL; | |
55 | struct iname *if_except = NULL; | |
56 | struct iname *if_tmp; | |
57 | struct server *serv_addrs = NULL; | |
58 | char *dnamebuff, *packet; | |
59 | struct server *servers, *last_server; | |
60 | struct resolvc default_resolv = { NULL, 1, 0, RESOLVFILE }; | |
61 | struct resolvc *resolv = &default_resolv; | |
62 | struct bogus_addr *bogus_addr = NULL; | |
63 | struct serverfd *serverfdp, *sfds = NULL; | |
64 | struct dhcp_context *dhcp_tmp, *dhcp = NULL; | |
65 | struct dhcp_config *dhcp_configs = NULL; | |
66 | struct dhcp_opt *dhcp_options = NULL; | |
67 | char *dhcp_file = NULL, *dhcp_sname = NULL; | |
68 | struct in_addr dhcp_next_server; | |
69 | int leasefd = 0; | |
70 | struct sigaction sigact; | |
71 | sigset_t sigmask; | |
72 | ||
73 | sighup = 1; /* init cache the first time through */ | |
74 | sigusr1 = 0; /* but don't dump */ | |
75 | sigusr2 = 0; /* or rescan interfaces */ | |
76 | sigterm = 0; /* or die */ | |
77 | ||
78 | sigact.sa_handler = sig_handler; | |
79 | sigact.sa_flags = 0; | |
80 | sigemptyset(&sigact.sa_mask); | |
81 | sigaction(SIGUSR1, &sigact, NULL); | |
82 | sigaction(SIGUSR2, &sigact, NULL); | |
83 | sigaction(SIGHUP, &sigact, NULL); | |
84 | sigaction(SIGTERM, &sigact, NULL); | |
85 | ||
86 | /* now block all the signals, they stay that way except | |
87 | during the call to pselect */ | |
88 | sigaddset(&sigact.sa_mask, SIGUSR1); | |
89 | sigaddset(&sigact.sa_mask, SIGUSR2); | |
90 | sigaddset(&sigact.sa_mask, SIGTERM); | |
91 | sigaddset(&sigact.sa_mask, SIGHUP); | |
92 | sigprocmask(SIG_BLOCK, &sigact.sa_mask, &sigmask); | |
93 | ||
94 | /* These get allocated here to avoid overflowing the small stack | |
95 | on embedded systems. dnamebuff is big enough to hold one | |
96 | maximal sixed domain name and gets passed into all the processing | |
97 | code. We manage to get away with one buffer. */ | |
98 | dnamebuff = safe_malloc(MAXDNAME); | |
99 | /* Size: we check after adding each record, so there must be | |
100 | memory for the largest packet, and the largest record */ | |
101 | packet = safe_malloc(PACKETSZ+MAXDNAME+RRFIXEDSZ); | |
102 | ||
103 | dhcp_next_server.s_addr = 0; | |
104 | options = read_opts(argc, argv, dnamebuff, &resolv, &mxname, &mxtarget, &lease_file, | |
105 | &username, &groupname, &domain_suffix, &runfile, | |
106 | &if_names, &if_addrs, &if_except, &bogus_addr, | |
107 | &serv_addrs, &cachesize, &port, &query_port, &local_ttl, &addn_hosts, | |
108 | &dhcp, &dhcp_configs, &dhcp_options, | |
109 | &dhcp_file, &dhcp_sname, &dhcp_next_server); | |
110 | ||
111 | if (!lease_file) | |
112 | lease_file = LEASEFILE; | |
113 | else | |
114 | { | |
115 | if (!dhcp) | |
116 | { | |
117 | complain("********* dhcp-lease option set, but not dhcp-range.", NULL); | |
118 | complain("********* Are you trying to use the obsolete ISC dhcpd integration?", NULL); | |
119 | complain("********* Please configure the dnsmasq integrated DHCP server by using", NULL); | |
120 | complain("********* the \"dhcp-range\" option, and remove any other DHCP server.", NULL); | |
121 | } | |
122 | } | |
123 | ||
124 | if ((int_err_string = enumerate_interfaces(&interfaces, if_names, if_addrs, if_except, dhcp, port))) | |
125 | die(int_err_string, NULL); | |
126 | ||
127 | for (if_tmp = if_names; if_tmp; if_tmp = if_tmp->next) | |
128 | if (if_tmp->name && !if_tmp->found) | |
129 | die("unknown interface %s", if_tmp->name); | |
130 | ||
131 | for (if_tmp = if_addrs; if_tmp; if_tmp = if_tmp->next) | |
132 | if (!if_tmp->found) | |
133 | { | |
134 | #ifdef HAVE_IPV6 | |
135 | if (if_tmp->addr.sa.sa_family == AF_INET) | |
136 | inet_ntop(AF_INET, &if_tmp->addr.in.sin_addr, | |
137 | dnamebuff, MAXDNAME); | |
138 | else | |
139 | inet_ntop(AF_INET6, &if_tmp->addr.in6.sin6_addr, | |
140 | dnamebuff, MAXDNAME); | |
141 | die("no interface with address %s", dnamebuff); | |
142 | #else | |
143 | die("no interface with address %s", inet_ntoa(if_tmp->addr.in.sin_addr)); | |
144 | #endif | |
145 | } | |
146 | ||
147 | forward_init(1); | |
148 | cache_init(cachesize, options & OPT_LOG); | |
149 | ||
150 | if (dhcp) | |
151 | { | |
152 | ||
153 | #if !defined(HAVE_PF_PACKET) && !defined(HAVE_BPF) | |
154 | die("no DHCP support available on this OS.", NULL); | |
155 | #endif | |
156 | ||
157 | for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) | |
158 | if (!dhcp_tmp->iface) | |
159 | die("No suitable interface for DHCP service at address %s", inet_ntoa(dhcp_tmp->start)); | |
160 | ||
1ab84e2f | 161 | set_configs_from_cache(dhcp_configs); |
9e4abcb5 SK |
162 | leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, time(NULL), dhcp_configs); |
163 | lease_update_dns(1); /* must follow cache_init and lease_init */ | |
164 | } | |
165 | ||
166 | setbuf(stdout, NULL); | |
167 | ||
168 | if (!(options & OPT_DEBUG)) | |
169 | { | |
170 | FILE *pidfile; | |
171 | struct passwd *ent_pw; | |
172 | int i; | |
173 | ||
174 | /* The following code "daemonizes" the process. | |
175 | See Stevens section 12.4 */ | |
176 | ||
177 | #ifndef NO_FORK | |
178 | if (fork() != 0 ) | |
179 | exit(0); | |
180 | ||
181 | setsid(); | |
182 | ||
183 | if (fork() != 0) | |
184 | exit(0); | |
185 | #endif | |
186 | ||
187 | chdir("/"); | |
188 | umask(022); /* make pidfile 0644 */ | |
189 | ||
190 | /* write pidfile _after_ forking ! */ | |
191 | if (runfile && (pidfile = fopen(runfile, "w"))) | |
192 | { | |
193 | fprintf(pidfile, "%d\n", (int) getpid()); | |
194 | fclose(pidfile); | |
195 | } | |
196 | ||
197 | umask(0); | |
198 | ||
199 | for (i=0; i<64; i++) | |
200 | { | |
201 | for (iface = interfaces; iface; iface = iface->next) | |
202 | if (iface->fd == i) | |
203 | break; | |
204 | if (iface) | |
205 | continue; | |
206 | ||
207 | for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) | |
208 | if (dhcp_tmp->fd == i || dhcp_tmp->rawfd == i) | |
209 | break; | |
210 | if (dhcp_tmp) | |
211 | continue; | |
212 | ||
213 | if (dhcp && (i == leasefd)) | |
214 | continue; | |
215 | ||
216 | close(i); | |
217 | } | |
218 | ||
219 | /* Change uid and gid for security */ | |
220 | if (username && (ent_pw = getpwnam(username))) | |
221 | { | |
222 | gid_t dummy; | |
223 | struct group *gp; | |
224 | /* remove all supplimentary groups */ | |
225 | setgroups(0, &dummy); | |
226 | /* change group for /etc/ppp/resolv.conf | |
227 | otherwise get the group for "nobody" */ | |
228 | if ((groupname && (gp = getgrnam(groupname))) || | |
229 | (gp = getgrgid(ent_pw->pw_gid))) | |
230 | setgid(gp->gr_gid); | |
231 | /* finally drop root */ | |
232 | setuid(ent_pw->pw_uid); | |
233 | } | |
234 | } | |
235 | ||
236 | openlog("dnsmasq", | |
237 | DNSMASQ_LOG_OPT(options & OPT_DEBUG), | |
238 | DNSMASQ_LOG_FAC(options & OPT_DEBUG)); | |
239 | ||
240 | if (cachesize) | |
241 | syslog(LOG_INFO, "started, version %s cachesize %d", VERSION, cachesize); | |
242 | else | |
243 | syslog(LOG_INFO, "started, version %s cache disabled", VERSION); | |
244 | ||
245 | if (options & OPT_LOCALMX) | |
246 | syslog(LOG_INFO, "serving MX record for local hosts target %s", mxtarget); | |
247 | else if (mxname) | |
248 | syslog(LOG_INFO, "serving MX record for mailhost %s target %s", | |
249 | mxname, mxtarget); | |
250 | ||
251 | for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) | |
252 | { | |
253 | strcpy(dnamebuff, inet_ntoa(dhcp_tmp->start)); | |
254 | if (dhcp_tmp->lease_time == 0) | |
255 | sprintf(packet, "infinite"); | |
256 | else | |
257 | sprintf(packet, "%ds", (int)dhcp_tmp->lease_time); | |
258 | syslog(LOG_INFO, "DHCP on %s, IP range %s -- %s, lease time %s", | |
259 | dhcp_tmp->iface, dnamebuff, inet_ntoa(dhcp_tmp->end), packet); | |
260 | } | |
261 | ||
262 | if (getuid() == 0 || geteuid() == 0) | |
263 | syslog(LOG_WARNING, "failed to drop root privs"); | |
264 | ||
265 | servers = last_server = check_servers(serv_addrs, interfaces, &sfds); | |
266 | ||
267 | while (sigterm == 0) | |
268 | { | |
269 | fd_set rset; | |
270 | ||
271 | if (sighup) | |
272 | { | |
273 | cache_reload(options, dnamebuff, domain_suffix, addn_hosts); | |
1ab84e2f | 274 | set_configs_from_cache(dhcp_configs); |
9e4abcb5 SK |
275 | lease_update_dns(1); |
276 | if (resolv && (options & OPT_NO_POLL)) | |
277 | servers = last_server = | |
278 | check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port), | |
279 | interfaces, &sfds); | |
280 | sighup = 0; | |
281 | } | |
282 | ||
283 | if (sigusr1) | |
284 | { | |
285 | dump_cache(options & (OPT_DEBUG | OPT_LOG), cachesize); | |
286 | sigusr1 = 0; | |
287 | } | |
288 | ||
289 | if (sigusr2) | |
290 | { | |
291 | if (getuid() != 0 && port <= 1024) | |
292 | syslog(LOG_ERR, "cannot re-scan interfaces unless --user=root"); | |
293 | else | |
294 | { | |
295 | syslog(LOG_INFO, "rescanning network interfaces"); | |
296 | int_err_string = enumerate_interfaces(&interfaces, if_names, if_addrs, if_except, NULL, port); | |
297 | if (int_err_string) | |
298 | syslog(LOG_ERR, int_err_string, strerror(errno)); | |
299 | } | |
300 | sigusr2 = 0; | |
301 | } | |
302 | ||
303 | FD_ZERO(&rset); | |
304 | ||
305 | if (!first_loop) | |
306 | { | |
307 | int maxfd = 0; | |
308 | ||
309 | for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next) | |
310 | { | |
311 | FD_SET(serverfdp->fd, &rset); | |
312 | if (serverfdp->fd > maxfd) | |
313 | maxfd = serverfdp->fd; | |
314 | } | |
315 | ||
316 | for (iface = interfaces; iface; iface = iface->next) | |
317 | { | |
318 | FD_SET(iface->fd, &rset); | |
319 | if (iface->fd > maxfd) | |
320 | maxfd = iface->fd; | |
321 | } | |
322 | ||
323 | for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) | |
324 | { | |
325 | FD_SET(dhcp_tmp->fd, &rset); | |
326 | if (dhcp_tmp->fd > maxfd) | |
327 | maxfd = dhcp_tmp->fd; | |
328 | } | |
329 | #ifdef HAVE_PSELECT | |
330 | if (pselect(maxfd+1, &rset, NULL, NULL, NULL, &sigmask) < 0) | |
331 | FD_ZERO(&rset); /* rset otherwise undefined after error */ | |
332 | #else | |
333 | { | |
334 | sigset_t save_mask; | |
335 | sigprocmask(SIG_SETMASK, &sigmask, &save_mask); | |
336 | if (select(maxfd+1, &rset, NULL, NULL, NULL) < 0) | |
337 | FD_ZERO(&rset); /* rset otherwise undefined after error */ | |
338 | sigprocmask(SIG_SETMASK, &save_mask, NULL); | |
339 | } | |
340 | #endif | |
341 | ||
342 | } | |
343 | ||
344 | first_loop = 0; | |
345 | now = time(NULL); | |
346 | ||
347 | /* Check for changes to resolv files once per second max. */ | |
348 | if (last == 0 || difftime(now, last) > 1.0) | |
349 | { | |
350 | last = now; | |
351 | if (!(options & OPT_NO_POLL)) | |
352 | { | |
353 | struct resolvc *res = resolv, *latest = NULL; | |
354 | time_t last_change = 0; | |
355 | struct stat statbuf; | |
356 | /* There may be more than one possible file. | |
357 | Go through and find the one which changed _last_. | |
358 | Warn of any which can't be read. */ | |
359 | while (res) | |
360 | { | |
361 | if (stat(res->name, &statbuf) == -1) | |
362 | { | |
363 | if (!res->logged) | |
364 | syslog(LOG_WARNING, "failed to access %s: %m", res->name); | |
365 | res->logged = 1; | |
366 | } | |
367 | else | |
368 | { | |
369 | res->logged = 0; | |
370 | if (statbuf.st_mtime > last_change) | |
371 | { | |
372 | last_change = statbuf.st_mtime; | |
373 | latest = res; | |
374 | } | |
375 | } | |
376 | res = res->next; | |
377 | } | |
378 | ||
379 | if (latest && last_change > resolv_changed) | |
380 | { | |
381 | resolv_changed = last_change; | |
382 | servers = last_server = | |
383 | check_servers(reload_servers(latest->name, dnamebuff, servers, query_port), | |
384 | interfaces, &sfds); | |
385 | } | |
386 | } | |
387 | } | |
388 | ||
389 | for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next) | |
390 | if (FD_ISSET(serverfdp->fd, &rset)) | |
391 | last_server = reply_query(serverfdp->fd, options, packet, now, | |
392 | dnamebuff, last_server, bogus_addr); | |
393 | ||
394 | for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next) | |
395 | if (FD_ISSET(dhcp_tmp->fd, &rset)) | |
396 | dhcp_packet(dhcp_tmp, packet, dhcp_options, dhcp_configs, | |
397 | now, dnamebuff, domain_suffix, dhcp_file, | |
398 | dhcp_sname, dhcp_next_server); | |
399 | ||
400 | for (iface = interfaces; iface; iface = iface->next) | |
401 | { | |
402 | if (FD_ISSET(iface->fd, &rset)) | |
403 | { | |
404 | /* request packet, deal with query */ | |
405 | union mysockaddr udpaddr; | |
406 | socklen_t udplen = sizeof(udpaddr); | |
407 | HEADER *header = (HEADER *)packet; | |
408 | int m, n = recvfrom(iface->fd, packet, PACKETSZ, 0, &udpaddr.sa, &udplen); | |
409 | udpaddr.sa.sa_family = iface->addr.sa.sa_family; | |
410 | #ifdef HAVE_IPV6 | |
411 | if (udpaddr.sa.sa_family == AF_INET6) | |
412 | udpaddr.in6.sin6_flowinfo = htonl(0); | |
413 | #endif | |
414 | if (n >= (int)sizeof(HEADER) && !header->qr) | |
415 | { | |
416 | if (extract_request(header, (unsigned int)n, dnamebuff)) | |
417 | { | |
418 | if (udpaddr.sa.sa_family == AF_INET) | |
419 | log_query(F_QUERY | F_IPV4 | F_FORWARD, dnamebuff, | |
420 | (struct all_addr *)&udpaddr.in.sin_addr); | |
421 | #ifdef HAVE_IPV6 | |
422 | else | |
423 | log_query(F_QUERY | F_IPV6 | F_FORWARD, dnamebuff, | |
424 | (struct all_addr *)&udpaddr.in6.sin6_addr); | |
425 | #endif | |
426 | } | |
427 | ||
428 | m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, | |
429 | mxname, mxtarget, options, now, local_ttl, dnamebuff); | |
430 | if (m >= 1) | |
431 | { | |
432 | /* answered from cache, send reply */ | |
433 | sendto(iface->fd, (char *)header, m, 0, | |
434 | &udpaddr.sa, sa_len(&udpaddr)); | |
435 | } | |
436 | else | |
437 | { | |
438 | /* cannot answer from cache, send on to real nameserver */ | |
439 | last_server = forward_query(iface->fd, &udpaddr, header, n, | |
440 | options, dnamebuff, servers, | |
441 | last_server, now, local_ttl); | |
442 | } | |
443 | } | |
444 | ||
445 | } | |
446 | } | |
447 | } | |
448 | ||
449 | syslog(LOG_INFO, "exiting on receipt of SIGTERM"); | |
450 | return 0; | |
451 | } | |
452 | ||
453 | ||
454 | ||
455 | ||
456 | ||
457 |