]> git.ipfire.org Git - thirdparty/squid.git/blame - src/dnsserver.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / dnsserver.cc
CommitLineData
30a4f2a8 1/*
b510f3a1 2 * DEBUG: section 00 DNS Resolver Daemon
30a4f2a8 3 * AUTHOR: Harvest Derived
4 *
2b6662ba 5 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 6 * ----------------------------------------------------------
30a4f2a8 7 *
2b6662ba 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.
30a4f2a8 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.
26ac0430 21 *
30a4f2a8 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.
26ac0430 26 *
30a4f2a8 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
cbdec147 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 30 *
30a4f2a8 31 */
ed43818f 32
f7f3304a 33#include "squid.h"
af00901c 34
35#if HAVE_UNISTD_H
36#include <unistd.h>
37#endif
af00901c 38#if HAVE_STDIO_H
39#include <stdio.h>
40#endif
af00901c 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
88738790 53#if HAVE_GNUMALLOC_H
54#include <gnumalloc.h>
482aa790 55#elif HAVE_MALLOC_H
af00901c 56#include <malloc.h>
57#endif
58#if HAVE_MEMORY_H
59#include <memory.h>
60#endif
489520a9 61#if HAVE_NETDB_H
af00901c 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
af00901c 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
32d002cb 97#if HAVE_SYS_SYSCALL_H
af00901c 98#include <sys/syscall.h>
99#endif
32d002cb 100#if HAVE_STRING_H
af00901c 101#include <string.h>
102#endif
32d002cb 103#if HAVE_STRINGS_H
af00901c 104#include <strings.h>
105#endif
106#if HAVE_BSTRING_H
107#include <bstring.h>
108#endif
32d002cb 109#if HAVE_CRYPT_H
af00901c 110#include <crypt.h>
111#endif
fcd3a580 112#if HAVE_GETOPT_H
113#include <getopt.h>
114#endif
af00901c 115
30a4f2a8 116#if HAVE_ARPA_NAMESER_H
117#include <arpa/nameser.h>
118#endif
119#if HAVE_RESOLV_H
120#include <resolv.h>
121#endif
090089c4 122
63be0a78 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
139usage: 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
6bf65235 148#if LIBRESOLV_DNS_TTL_HACK
63be0a78 149/// \ingroup dnsserver
28b2f45f 150extern int _dns_ttl_; /* this is a really *dirty* hack - bne */
6bf65235 151#endif
152
cc192b50 153/*
154 * res_init() is a macro re-definition of __res_init on: Debian
3bf15742 155 */
cc192b50 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
62e76326 162
63be0a78 163/// \ingroup dnsserver
88738790 164#define REQ_SZ 512
165
63be0a78 166/**
167 \ingroup dnsserver
168 */
bd34f258 169static void
170lookup(const char *buf)
171{
bd34f258 172 int ttl = 0;
173 int retry = 0;
cc192b50 174 unsigned int i = 0;
c7e76326 175 char ntoabuf[256];
cc192b50 176 struct addrinfo hints;
177 struct addrinfo *AI = NULL;
178 struct addrinfo *aiptr = NULL;
179 struct addrinfo *prev_addr = NULL;
180 int res = 0;
62e76326 181
bd34f258 182 if (0 == strcmp(buf, "$shutdown"))
62e76326 183 exit(0);
184
bd34f258 185 if (0 == strcmp(buf, "$hello")) {
62e76326 186 printf("$alive\n");
187 return;
bd34f258 188 }
62e76326 189
c7e76326 190 /* check if it's already an IP address in text form. */
cc192b50 191 memset(&hints, '\0', sizeof(struct addrinfo));
192 hints.ai_family = AF_UNSPEC;
c7e76326
AJ
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
7a1ddbaf
MW
197 if (AI != NULL) {
198 freeaddrinfo(AI);
199 AI = NULL;
200 }
cc192b50 201
c7e76326
AJ
202 // resolve the address/name
203 memset(&hints, '\0', sizeof(struct addrinfo));
204 hints.ai_family = AF_UNSPEC;
205 hints.ai_flags = AI_CANONNAME;
bd34f258 206 for (;;) {
c8e337c1
AJ
207 if (AI != NULL) {
208 freeaddrinfo(AI);
209 AI = NULL;
210 }
62e76326 211
27bc2077 212 if ( 0 == (res = getaddrinfo(buf,NULL,&hints,&AI)) )
62e76326 213 break;
214
cc192b50 215 if (res != EAI_AGAIN)
62e76326 216 break;
217
218 if (++retry == 3)
219 break;
220
221 sleep(1);
bd34f258 222 }
62e76326 223
c7e76326 224 if (isDomain) {
cc192b50 225 /* its a domain name. Use the forward-DNS lookup already done */
62e76326 226
26ac0430 227 if (res == 0) {
cc192b50 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;
26ac0430 239 while (NULL != aiptr && 32 >= i) {
c7e76326 240 memset(ntoabuf, 0, sizeof(ntoabuf));
cc192b50 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 */
26ac0430
AJ
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 ) {
cc192b50 248 prev_addr = aiptr;
249 aiptr = aiptr->ai_next;
250 continue;
26ac0430 251 } else {
cc192b50 252 prev_addr = aiptr;
253 }
254
255 /* annoying inet_ntop breaks the nice code by requiring the in*_addr */
26ac0430 256 switch (aiptr->ai_family) {
cc192b50 257 case AF_INET:
c7e76326 258 inet_ntop(aiptr->ai_family, &((struct sockaddr_in*)aiptr->ai_addr)->sin_addr, ntoabuf, sizeof(ntoabuf));
cc192b50 259 break;
cc192b50 260 case AF_INET6:
c7e76326 261 inet_ntop(aiptr->ai_family, &((struct sockaddr_in6*)aiptr->ai_addr)->sin6_addr, ntoabuf, sizeof(ntoabuf));
cc192b50 262 break;
cc192b50 263 default:
264 aiptr = aiptr->ai_next;
265 continue;
266 }
267 printf(" %s", ntoabuf);
95dc7ff4 268 ++i;
cc192b50 269 aiptr = aiptr->ai_next;
270 }
271
272 prev_addr=NULL;
273 printf("\n");
274 }
26ac0430 275 } else { /* its an IPA in text form. perform rDNS */
cc192b50 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 */
26ac0430
AJ
280 if (NULL != AI && NULL != AI->ai_addr) {
281 for (;;) {
c7e76326 282 if ( 0 == (res = getnameinfo(AI->ai_addr, AI->ai_addrlen, ntoabuf, sizeof(ntoabuf), NULL,0,0)) )
cc192b50 283 break;
284
285 if (res != EAI_AGAIN)
286 break;
287
288 if (++retry == 3)
289 break;
290
291 sleep(1);
292 }
293 }
62e76326 294
26ac0430 295 if (res == 0) {
bd34f258 296#if LIBRESOLV_DNS_TTL_HACK
cc192b50 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_;
bd34f258 302#endif
62e76326 303
cc192b50 304 printf("$name %d %s\n", ttl, ntoabuf);
305 }
bd34f258 306 }
62e76326 307
26ac0430
AJ
308 switch (res) {
309 case 0:
310 /* no error. */
311 break;
62e76326 312
26ac0430
AJ
313 case EAI_AGAIN:
314 printf("$fail Name Server for domain '%s' is unavailable.\n", buf);
315 break;
62e76326 316
26ac0430 317 case EAI_FAIL:
27bc2077 318 printf("$fail DNS Domain/IP '%s' does not exist: %s.\n", buf, gai_strerror(res));
26ac0430 319 break;
62e76326 320
cc192b50 321#if defined(EAI_NODATA) || defined(EAI_NONAME)
32d002cb 322#if EAI_NODATA
cc192b50 323 /* deprecated. obsolete on some OS */
26ac0430 324 case EAI_NODATA:
cc192b50 325#endif
32d002cb 326#if EAI_NONAME
26ac0430 327 case EAI_NONAME:
cc192b50 328#endif
27bc2077 329 printf("$fail DNS Domain/IP '%s' exists without any FQDN/IPs: %s.\n", buf, gai_strerror(res));
26ac0430 330 break;
cc192b50 331#endif
26ac0430 332 default:
27bc2077 333 printf("$fail A system error occured looking up Domain/IP '%s': %s.\n", buf, gai_strerror(res));
bd34f258 334 }
62e76326 335
7a1ddbaf
MW
336 if (AI != NULL)
337 freeaddrinfo(AI);
bd34f258 338}
339
63be0a78 340/**
341 \ingroup dnsserver
342 */
bd34f258 343static void
344usage(void)
345{
cc192b50 346 fprintf(stderr, "usage: dnsserver -hv -s nameserver\n"
62e76326 347 "\t-h Help\n"
348 "\t-v Version\n"
349 "\t-s nameserver Specify alternate name server(s). 'nameserver'\n"
cc192b50 350 "\t must be an IPv4 address, -s option may be repeated\n"
26ac0430 351 );
bd34f258 352}
353
605f2c3e 354#if defined(_SQUID_RES_NSADDR6_LARRAY)
63be0a78 355/// \ingroup dnsserver
cc192b50 356#define _SQUID_RES_NSADDR6_LIST(i) _SQUID_RES_NSADDR6_LARRAY[i].sin6_addr
357#endif
605f2c3e 358#if defined(_SQUID_RES_NSADDR6_LPTR)
63be0a78 359/// \ingroup dnsserver
cc192b50 360#define _SQUID_RES_NSADDR6_LIST(i) _SQUID_RES_NSADDR6_LPTR[i]->sin6_addr
361#endif
362
63be0a78 363/**
d376ccbf 364 * \ingroup dnsserver
63be0a78 365 *
d376ccbf
AJ
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.
63be0a78 369 */
d376ccbf
AJ
370void
371squid_res_setservers(int reset)
090089c4 372{
605f2c3e 373#if _SQUID_FREEBSD_ && defined(_SQUID_RES_NSADDR6_COUNT)
cc192b50 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
c7e76326 378#if HAVE_RES_INIT && defined(_SQUID_RES_NSADDR_LIST)
09c483ec 379 extern char *optarg;
e6ccf245 380#endif
090089c4 381
cc192b50 382#if HAVE_RES_INIT && (defined(_SQUID_RES_NSADDR_LIST) || defined(_SQUID_RES_NSADDR6_LIST))
62e76326 383
d376ccbf 384 if (reset == 0) {
cc192b50 385#if defined(_SQUID_RES_NSADDR_COUNT)
d376ccbf
AJ
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);
cc192b50 389#endif
390#if defined(_SQUID_RES_NSADDR6_COUNT)
d376ccbf 391 _SQUID_RES_NSADDR6_COUNT = 0;
62e76326 392#endif
d376ccbf 393 }
62e76326 394
d376ccbf
AJ
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 *
c7e76326 405 * SO, am splitting the IPv4/v6 into the seperate _res fields
d376ccbf
AJ
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 */
c7e76326
AJ
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) {
d376ccbf 417 fprintf(stderr, "%s appears to be a bad nameserver FQDN/IP.\n",optarg);
c7e76326 418 } else if ( AI->ai_family == AF_INET ) {
d376ccbf
AJ
419 if (_SQUID_RES_NSADDR_COUNT == MAXNS) {
420 fprintf(stderr, "Too many -s options, only %d are allowed\n", MAXNS);
c7e76326
AJ
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));
d376ccbf 424 }
c7e76326 425 } else if ( AI->ai_family == AF_INET6 ) {
cc192b50 426#if USE_IPV6 && defined(_SQUID_RES_NSADDR6_LIST)
04f7fd38 427 /* because things NEVER seem to resolve in tests without _res.nscount being a total. */
d376ccbf
AJ
428 if (_SQUID_RES_NSADDR_COUNT == MAXNS) {
429 fprintf(stderr, "Too many -s options, only %d are allowed\n", MAXNS);
c7e76326 430 } else {
95dc7ff4 431 ++ _SQUID_RES_NSADDR_COUNT;
c7e76326 432 memcpy(&_SQUID_RES_NSADDR6_LIST(_SQUID_RES_NSADDR6_COUNT++), &((struct sockaddr_in6*)AI->ai_addr)->sin6_addr, sizeof(struct in6_addr));
d376ccbf 433 }
cc192b50 434#else
d376ccbf 435 fprintf(stderr, "IPv6 nameservers not supported on this resolver\n");
4d62b0af 436#endif
d376ccbf 437 }
7a1ddbaf
MW
438 if (AI != NULL)
439 freeaddrinfo(AI);
cc192b50 440
441#else /* !HAVE_RES_INIT || !defined(_SQUID_RES_NSADDR_LIST) */
62e76326 442
d376ccbf 443 fprintf(stderr, "-s is not supported on this resolver\n");
62e76326 444
df087e68 445#endif /* HAVE_RES_INIT */
d376ccbf
AJ
446}
447
d376ccbf
AJ
448/**
449 * \ingroup dnsserver
450 *
451 * This is the external dnsserver process.
452 */
453int
454main(int argc, char *argv[])
455{
456 char request[512];
457 char *t = NULL;
458 int c;
ede9c6a6 459 int opt_s = 0;
62e76326 460
d376ccbf 461#if HAVE_RES_INIT
d376ccbf
AJ
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;
62e76326 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 }
30a4f2a8 498 }
090089c4 499
1191b93b 500#if _SQUID_MSWIN_
099a1791 501 {
62e76326 502 WSADATA wsaData;
099a1791 503
62e76326 504 WSAStartup(2, &wsaData);
099a1791 505 }
62e76326 506
099a1791 507 fflush(stderr);
508#endif
62e76326 509
cb0486c3 510 for (;;) {
62e76326 511 memset(request, '\0', REQ_SZ);
512
26ac0430 513 if (fgets(request, REQ_SZ, stdin) == NULL) {
1191b93b 514#if _SQUID_MSWIN_
26ac0430 515 WSACleanup();
ec556193 516#endif
26ac0430
AJ
517 exit(1);
518 }
62e76326 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);
090089c4 533 }
62e76326 534
b44c0fb4 535 /* NOTREACHED */
7690e8eb 536 return 0;
090089c4 537}