]> git.ipfire.org Git - thirdparty/squid.git/blob - src/dnsserver.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / dnsserver.cc
1 /*
2 * DEBUG: section 00 DNS Resolver Daemon
3 * AUTHOR: Harvest Derived
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
32
33 #include "squid.h"
34
35 #if HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #if HAVE_STDIO_H
39 #include <stdio.h>
40 #endif
41 #if HAVE_CTYPE_H
42 #include <ctype.h>
43 #endif
44 #if HAVE_ERRNO_H
45 #include <errno.h>
46 #endif
47 #if HAVE_FCNTL_H
48 #include <fcntl.h>
49 #endif
50 #if HAVE_GRP_H
51 #include <grp.h>
52 #endif
53 #if HAVE_GNUMALLOC_H
54 #include <gnumalloc.h>
55 #elif HAVE_MALLOC_H
56 #include <malloc.h>
57 #endif
58 #if HAVE_MEMORY_H
59 #include <memory.h>
60 #endif
61 #if HAVE_NETDB_H
62 #include <netdb.h>
63 #endif
64 #if HAVE_PWD_H
65 #include <pwd.h>
66 #endif
67 #if HAVE_SIGNAL_H
68 #include <signal.h>
69 #endif
70 #if HAVE_TIME_H
71 #include <time.h>
72 #endif
73 #if HAVE_SYS_PARAM_H
74 #include <sys/param.h>
75 #endif
76 #if HAVE_SYS_SOCKET_H
77 #include <sys/socket.h>
78 #endif
79 #if HAVE_NETINET_IN_H
80 #include <netinet/in.h>
81 #endif
82 #if HAVE_ARPA_INET_H
83 #include <arpa/inet.h>
84 #endif
85 #if HAVE_SYS_STAT_H
86 #include <sys/stat.h>
87 #endif
88 #if HAVE_SYS_UN_H
89 #include <sys/un.h>
90 #endif
91 #if HAVE_SYS_WAIT_H
92 #include <sys/wait.h>
93 #endif
94 #if HAVE_LIBC_H
95 #include <libc.h>
96 #endif
97 #if HAVE_SYS_SYSCALL_H
98 #include <sys/syscall.h>
99 #endif
100 #if HAVE_STRING_H
101 #include <string.h>
102 #endif
103 #if HAVE_STRINGS_H
104 #include <strings.h>
105 #endif
106 #if HAVE_BSTRING_H
107 #include <bstring.h>
108 #endif
109 #if HAVE_CRYPT_H
110 #include <crypt.h>
111 #endif
112 #if HAVE_GETOPT_H
113 #include <getopt.h>
114 #endif
115
116 #if HAVE_ARPA_NAMESER_H
117 #include <arpa/nameser.h>
118 #endif
119 #if HAVE_RESOLV_H
120 #include <resolv.h>
121 #endif
122
123 /**
124 \defgroup dnsserver dnsserver
125 \ingroup ExternalPrograms
126 \par
127 Because the standard gethostbyname() library call
128 blocks, Squid must use external processes to actually make
129 these calls. Typically there will be ten dnsserver
130 processes spawned from Squid. Communication occurs via
131 TCP sockets bound to the loopback interface. The functions
132 in dns.cc are primarily concerned with starting and
133 stopping the dnsservers. Reading and writing to and from
134 the dnsservers occurs in the \link IPCacheAPI IP\endlink and
135 \link FQDNCacheAPI FQDN\endlink cache modules.
136
137 \section dnsserverInterface Command Line Interface
138 \verbatim
139 usage: dnsserver -Dhv -s nameserver
140 -D Enable resolver RES_DEFNAMES and RES_DNSRCH options
141 -h Help
142 -v Version
143 -s nameserver Specify alternate name server(s). 'nameserver'
144 must be an IP address, -s option may be repeated
145 \endverbatim
146 */
147
148 #if LIBRESOLV_DNS_TTL_HACK
149 /// \ingroup dnsserver
150 extern int _dns_ttl_; /* this is a really *dirty* hack - bne */
151 #endif
152
153 /*
154 * res_init() is a macro re-definition of __res_init on: Debian
155 */
156 #if !defined(HAVE_RES_INIT) && defined(HAVE___RES_INIT)
157 #ifndef res_init
158 #define res_init __res_init
159 #endif
160 #define HAVE_RES_INIT HAVE___RES_INIT
161 #endif
162
163 /// \ingroup dnsserver
164 #define REQ_SZ 512
165
166 /**
167 \ingroup dnsserver
168 */
169 static void
170 lookup(const char *buf)
171 {
172 int ttl = 0;
173 int retry = 0;
174 unsigned int i = 0;
175 char ntoabuf[256];
176 struct addrinfo hints;
177 struct addrinfo *AI = NULL;
178 struct addrinfo *aiptr = NULL;
179 struct addrinfo *prev_addr = NULL;
180 int res = 0;
181
182 if (0 == strcmp(buf, "$shutdown"))
183 exit(0);
184
185 if (0 == strcmp(buf, "$hello")) {
186 printf("$alive\n");
187 return;
188 }
189
190 /* check if it's already an IP address in text form. */
191 memset(&hints, '\0', sizeof(struct addrinfo));
192 hints.ai_family = AF_UNSPEC;
193 hints.ai_flags = AI_NUMERICHOST; // only succeed if its numeric.
194 const bool isDomain = (getaddrinfo(buf,NULL,&hints,&AI) != 0);
195
196 // reset for real lookup
197 if (AI != NULL) {
198 freeaddrinfo(AI);
199 AI = NULL;
200 }
201
202 // resolve the address/name
203 memset(&hints, '\0', sizeof(struct addrinfo));
204 hints.ai_family = AF_UNSPEC;
205 hints.ai_flags = AI_CANONNAME;
206 for (;;) {
207 if (AI != NULL) {
208 freeaddrinfo(AI);
209 AI = NULL;
210 }
211
212 if ( 0 == (res = getaddrinfo(buf,NULL,&hints,&AI)) )
213 break;
214
215 if (res != EAI_AGAIN)
216 break;
217
218 if (++retry == 3)
219 break;
220
221 sleep(1);
222 }
223
224 if (isDomain) {
225 /* its a domain name. Use the forward-DNS lookup already done */
226
227 if (res == 0) {
228 #if LIBRESOLV_DNS_TTL_HACK
229 /* DNS TTL handling - bne@CareNet.hu
230 * for first try it's a dirty hack, by hacking getanswer
231 * to place the ttl in a global variable */
232 if (_dns_ttl_ > -1)
233 ttl = _dns_ttl_;
234 #endif
235 printf("$addr %d", ttl);
236
237 i = 0;
238 aiptr = AI;
239 while (NULL != aiptr && 32 >= i) {
240 memset(ntoabuf, 0, sizeof(ntoabuf));
241
242 /* getaddrinfo given a host has a nasty tendency to return duplicate addr's */
243 /* BUT sorted fortunately, so we can drop most of them easily */
244 if ( prev_addr &&
245 prev_addr->ai_family==aiptr->ai_family &&
246 memcmp(aiptr->ai_addr, prev_addr->ai_addr, aiptr->ai_addrlen)==0
247 ) {
248 prev_addr = aiptr;
249 aiptr = aiptr->ai_next;
250 continue;
251 } else {
252 prev_addr = aiptr;
253 }
254
255 /* annoying inet_ntop breaks the nice code by requiring the in*_addr */
256 switch (aiptr->ai_family) {
257 case AF_INET:
258 inet_ntop(aiptr->ai_family, &((struct sockaddr_in*)aiptr->ai_addr)->sin_addr, ntoabuf, sizeof(ntoabuf));
259 break;
260 case AF_INET6:
261 inet_ntop(aiptr->ai_family, &((struct sockaddr_in6*)aiptr->ai_addr)->sin6_addr, ntoabuf, sizeof(ntoabuf));
262 break;
263 default:
264 aiptr = aiptr->ai_next;
265 continue;
266 }
267 printf(" %s", ntoabuf);
268 ++i;
269 aiptr = aiptr->ai_next;
270 }
271
272 prev_addr=NULL;
273 printf("\n");
274 }
275 } else { /* its an IPA in text form. perform rDNS */
276 /* You'd expect getaddrinfo given AI_CANONNAME would do a lookup on
277 * missing FQDN. But no, it only copies the input string to that
278 * position regardless of its content.
279 */
280 if (NULL != AI && NULL != AI->ai_addr) {
281 for (;;) {
282 if ( 0 == (res = getnameinfo(AI->ai_addr, AI->ai_addrlen, ntoabuf, sizeof(ntoabuf), NULL,0,0)) )
283 break;
284
285 if (res != EAI_AGAIN)
286 break;
287
288 if (++retry == 3)
289 break;
290
291 sleep(1);
292 }
293 }
294
295 if (res == 0) {
296 #if LIBRESOLV_DNS_TTL_HACK
297 /* DNS TTL handling - bne@CareNet.hu
298 * for first try it's a dirty hack, by hacking getanswer
299 * to place the ttl in a global variable */
300 if (_dns_ttl_ > -1)
301 ttl = _dns_ttl_;
302 #endif
303
304 printf("$name %d %s\n", ttl, ntoabuf);
305 }
306 }
307
308 switch (res) {
309 case 0:
310 /* no error. */
311 break;
312
313 case EAI_AGAIN:
314 printf("$fail Name Server for domain '%s' is unavailable.\n", buf);
315 break;
316
317 case EAI_FAIL:
318 printf("$fail DNS Domain/IP '%s' does not exist: %s.\n", buf, gai_strerror(res));
319 break;
320
321 #if defined(EAI_NODATA) || defined(EAI_NONAME)
322 #if EAI_NODATA
323 /* deprecated. obsolete on some OS */
324 case EAI_NODATA:
325 #endif
326 #if EAI_NONAME
327 case EAI_NONAME:
328 #endif
329 printf("$fail DNS Domain/IP '%s' exists without any FQDN/IPs: %s.\n", buf, gai_strerror(res));
330 break;
331 #endif
332 default:
333 printf("$fail A system error occured looking up Domain/IP '%s': %s.\n", buf, gai_strerror(res));
334 }
335
336 if (AI != NULL)
337 freeaddrinfo(AI);
338 }
339
340 /**
341 \ingroup dnsserver
342 */
343 static void
344 usage(void)
345 {
346 fprintf(stderr, "usage: dnsserver -hv -s nameserver\n"
347 "\t-h Help\n"
348 "\t-v Version\n"
349 "\t-s nameserver Specify alternate name server(s). 'nameserver'\n"
350 "\t must be an IPv4 address, -s option may be repeated\n"
351 );
352 }
353
354 #if defined(_SQUID_RES_NSADDR6_LARRAY)
355 /// \ingroup dnsserver
356 #define _SQUID_RES_NSADDR6_LIST(i) _SQUID_RES_NSADDR6_LARRAY[i].sin6_addr
357 #endif
358 #if defined(_SQUID_RES_NSADDR6_LPTR)
359 /// \ingroup dnsserver
360 #define _SQUID_RES_NSADDR6_LIST(i) _SQUID_RES_NSADDR6_LPTR[i]->sin6_addr
361 #endif
362
363 /**
364 * \ingroup dnsserver
365 *
366 * Override the system DNS nameservers with some local ones.
367 * Equivalent to the bind res_setservers() call but for any
368 * system where we can find the needed _res fields.
369 */
370 void
371 squid_res_setservers(int reset)
372 {
373 #if _SQUID_FREEBSD_ && defined(_SQUID_RES_NSADDR6_COUNT)
374 /* Only seems to be valid on FreeBSD 5.5 where _res_ext was provided without an ns6addr counter! */
375 /* Gone again on FreeBSD 6.2 along with _res_ext itself in any form. */
376 int ns6count = 0;
377 #endif
378 #if HAVE_RES_INIT && defined(_SQUID_RES_NSADDR_LIST)
379 extern char *optarg;
380 #endif
381
382 #if HAVE_RES_INIT && (defined(_SQUID_RES_NSADDR_LIST) || defined(_SQUID_RES_NSADDR6_LIST))
383
384 if (reset == 0) {
385 #if defined(_SQUID_RES_NSADDR_COUNT)
386 _SQUID_RES_NSADDR_COUNT = 0;
387 /* because I don't trust the nscount super-count entirely, make sure these are ALL invalid */
388 memset(_SQUID_RES_NSADDR_LIST, 0, sizeof(struct sockaddr_in)*MAXNS);
389 #endif
390 #if defined(_SQUID_RES_NSADDR6_COUNT)
391 _SQUID_RES_NSADDR6_COUNT = 0;
392 #endif
393 }
394
395 /* AYJ:
396 * I experimented with all the permutations of mixed/unmixed nscount/nscount6 IPv4/IPv6/Both/invalid
397 *
398 * I'm not sure if splitting them really helps.
399 * I've seen no evidence of IPv4 resolver *ever* being used when some IPv6 are set (or not even)
400 * BUT, have seen segfault when IPv4 is added to NSADDR6 list (_res._u._ext).
401 * It also appears to not do ANY lookup when _res.nscount==0.
402 *
403 * BUT, even if _res.nsaddrs is memset to NULL, it resolves IFF IPv6 set in _ext.
404 *
405 * SO, am splitting the IPv4/v6 into the seperate _res fields
406 * and making nscount a total of IPv4+IPv6 /w nscount6 the IPv6 sub-counter
407 * ie. nscount = count(NSv4)+count(NSv6) & nscount6 = count(NSv6)
408 *
409 * If ANYONE knows better please let us know.
410 */
411 struct addrinfo hints;
412 memset(&hints, '\0', sizeof(struct addrinfo));
413 hints.ai_family = AF_UNSPEC;
414 hints.ai_flags = AI_NUMERICHOST; // prevent repeated DNS lookups!
415 struct addrinfo *AI = NULL;
416 if ( getaddrinfo(optarg, NULL, &hints, &AI) != 0) {
417 fprintf(stderr, "%s appears to be a bad nameserver FQDN/IP.\n",optarg);
418 } else if ( AI->ai_family == AF_INET ) {
419 if (_SQUID_RES_NSADDR_COUNT == MAXNS) {
420 fprintf(stderr, "Too many -s options, only %d are allowed\n", MAXNS);
421 } else {
422 _SQUID_RES_NSADDR_LIST[_SQUID_RES_NSADDR_COUNT] = _SQUID_RES_NSADDR_LIST[0];
423 memcpy(&_SQUID_RES_NSADDR_LIST[_SQUID_RES_NSADDR_COUNT++].sin_addr, &((struct sockaddr_in*)AI->ai_addr)->sin_addr, sizeof(struct in_addr));
424 }
425 } else if ( AI->ai_family == AF_INET6 ) {
426 #if USE_IPV6 && defined(_SQUID_RES_NSADDR6_LIST)
427 /* because things NEVER seem to resolve in tests without _res.nscount being a total. */
428 if (_SQUID_RES_NSADDR_COUNT == MAXNS) {
429 fprintf(stderr, "Too many -s options, only %d are allowed\n", MAXNS);
430 } else {
431 ++ _SQUID_RES_NSADDR_COUNT;
432 memcpy(&_SQUID_RES_NSADDR6_LIST(_SQUID_RES_NSADDR6_COUNT++), &((struct sockaddr_in6*)AI->ai_addr)->sin6_addr, sizeof(struct in6_addr));
433 }
434 #else
435 fprintf(stderr, "IPv6 nameservers not supported on this resolver\n");
436 #endif
437 }
438 if (AI != NULL)
439 freeaddrinfo(AI);
440
441 #else /* !HAVE_RES_INIT || !defined(_SQUID_RES_NSADDR_LIST) */
442
443 fprintf(stderr, "-s is not supported on this resolver\n");
444
445 #endif /* HAVE_RES_INIT */
446 }
447
448 /**
449 * \ingroup dnsserver
450 *
451 * This is the external dnsserver process.
452 */
453 int
454 main(int argc, char *argv[])
455 {
456 char request[512];
457 char *t = NULL;
458 int c;
459 int opt_s = 0;
460
461 #if HAVE_RES_INIT
462 res_init();
463 #endif
464
465 #if USE_IPV6
466 /* perform AAAA lookups *before* A lookups in IPv6 mode. */
467 _res.options |= RES_USE_INET6;
468 #endif
469
470 while ((c = getopt(argc, argv, "Dhs:v")) != -1) {
471 switch (c) {
472
473 case 'D':
474 fprintf(stderr, "-D is now default behaviour from this tool.\n");
475 break;
476
477 case 's':
478 squid_res_setservers(opt_s);
479 opt_s = 1;
480 break;
481
482 case 'v':
483 printf("dnsserver version %s\n", VERSION);
484
485 exit(0);
486
487 break;
488
489 case 'h':
490
491 default:
492 usage();
493
494 exit(1);
495
496 break;
497 }
498 }
499
500 #if _SQUID_MSWIN_
501 {
502 WSADATA wsaData;
503
504 WSAStartup(2, &wsaData);
505 }
506
507 fflush(stderr);
508 #endif
509
510 for (;;) {
511 memset(request, '\0', REQ_SZ);
512
513 if (fgets(request, REQ_SZ, stdin) == NULL) {
514 #if _SQUID_MSWIN_
515 WSACleanup();
516 #endif
517 exit(1);
518 }
519
520 t = strrchr(request, '\n');
521
522 if (t == NULL) /* Ignore if no newline */
523 continue;
524
525 *t = '\0'; /* strip NL */
526
527 if ((t = strrchr(request, '\r')) != NULL)
528 *t = '\0'; /* strip CR */
529
530 lookup(request);
531
532 fflush(stdout);
533 }
534
535 /* NOTREACHED */
536 return 0;
537 }