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