]> git.ipfire.org Git - thirdparty/squid.git/blame - src/dns_internal.cc
Sync autoconf requirements with bootstrap script.
[thirdparty/squid.git] / src / dns_internal.cc
CommitLineData
7b724b86 1
2/*
454e8283 3 * $Id: dns_internal.cc,v 1.105 2008/01/20 19:46:35 serassio Exp $
7b724b86 4 *
5 * DEBUG: section 78 DNS lookups; interacts with lib/rfc1035.c
6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
7b724b86 9 * ----------------------------------------------------------
10 *
2b6662ba 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.
7b724b86 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.
26ac0430 24 *
7b724b86 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.
26ac0430 29 *
7b724b86 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
d295d770 36#include "config.h"
7b724b86 37#include "squid.h"
a553a5a3 38#include "event.h"
62ee09ca 39#include "CacheManager.h"
985c86bc 40#include "SquidTime.h"
e6ccf245 41#include "Store.h"
063dc1eb 42#include "comm.h"
0eb49b6d 43#include "MemBuf.h"
7b724b86 44
d295d770 45#include "wordlist.h"
68836b58 46
47#if HAVE_ARPA_NAMESER_H
48#include <arpa/nameser.h>
49#endif
50#if HAVE_RESOLV_H
51#include <resolv.h>
52#endif
53
454e8283 54/* MS Visual Studio Projects are monolithic, so we need the following
1f1ae50a 55 #ifndef to exclude the internal DNS code from compile process when
56 using external DNS process.
57 */
58#ifndef USE_DNSSERVERS
ec4daaa5 59#ifdef _SQUID_WIN32_
1a774556 60#include "squid_windows.h"
0e6d05ef 61#endif
68836b58 62#ifndef _PATH_RESCONF
63#define _PATH_RESCONF "/etc/resolv.conf"
64#endif
65#ifndef NS_DEFAULTPORT
66#define NS_DEFAULTPORT 53
67#endif
68
69#ifndef NS_MAXDNAME
70#define NS_MAXDNAME 1025
71#endif
72
73#ifndef MAXDNSRCH
74#define MAXDNSRCH 6
7b724b86 75#endif
68836b58 76
77/* The buffer size required to store the maximum allowed search path */
78#ifndef RESOLV_BUFSZ
79#define RESOLV_BUFSZ NS_MAXDNAME * MAXDNSRCH + sizeof("search ") + 1
7b724b86 80#endif
81
42b51993 82#define IDNS_MAX_TRIES 20
558be27a 83#define MAX_RCODE 6
84#define MAX_ATTEMPT 3
85static int RcodeMatrix[MAX_RCODE][MAX_ATTEMPT];
86
58a39dc9 87typedef struct _idns_query idns_query;
62e76326 88
7b724b86 89typedef struct _ns ns;
58a39dc9 90
68836b58 91typedef struct _sp sp;
92
d24ef4e9 93typedef struct _nsvc nsvc;
94
26ac0430 95struct _idns_query {
3074b9d1 96 hash_link hash;
9e1f210d 97 rfc1035_query query;
68836b58 98 char buf[RESOLV_BUFSZ];
99 char name[NS_MAXDNAME + 1];
100 char orig[NS_MAXDNAME + 1];
58a39dc9 101 size_t sz;
102 unsigned short id;
103 int nsends;
d24ef4e9 104 int need_vc;
62e76326 105
58a39dc9 106 struct timeval start_t;
107 struct timeval sent_t;
820ee9fa 108 struct timeval queue_t;
58a39dc9 109 dlink_node lru;
110 IDNSCB *callback;
111 void *callback_data;
558be27a 112 int attempt;
7ba8d0b8 113 const char *error;
114 int rcode;
3074b9d1 115 idns_query *queue;
68836b58 116 unsigned short domain;
117 unsigned short do_searchpath;
cc192b50 118 bool need_A;
bae9832d 119 struct {
120 int count;
121 rfc1035_rr *answers;
122 } initial_AAAA;
58a39dc9 123};
124
26ac0430 125struct _nsvc {
d24ef4e9 126 int ns;
127 int fd;
128 unsigned short msglen;
129 int read_msglen;
032785bf 130 MemBuf *msg;
131 MemBuf *queue;
d24ef4e9 132 bool busy;
133};
134
26ac0430 135struct _ns {
ad61a2b4 136 IpAddress S;
7b724b86 137 int nqueries;
138 int nreplies;
d24ef4e9 139 nsvc *vc;
7b724b86 140};
58a39dc9 141
26ac0430 142struct _sp {
68836b58 143 char domain[NS_MAXDNAME];
144 int queries;
145};
146
d24ef4e9 147CBDATA_TYPE(nsvc);
63a0e6b7 148CBDATA_TYPE(idns_query);
d24ef4e9 149
7b724b86 150static ns *nameservers = NULL;
68836b58 151static sp *searchpath = NULL;
7b724b86 152static int nns = 0;
153static int nns_alloc = 0;
68836b58 154static int npc = 0;
155static int npc_alloc = 0;
156static int ndots = 1;
7b724b86 157static dlink_list lru_list;
7cfc1c9a 158static int event_queued = 0;
3074b9d1 159static hash_table *idns_lookup_hash = NULL;
7b724b86 160
161static OBJH idnsStats;
162static void idnsAddNameserver(const char *buf);
68836b58 163static void idnsAddPathComponent(const char *buf);
7b724b86 164static void idnsFreeNameservers(void);
68836b58 165static void idnsFreeSearchpath(void);
efd900cb 166static void idnsParseNameservers(void);
1f1ae50a 167#ifndef _SQUID_MSWIN_
7b724b86 168static void idnsParseResolvConf(void);
1f1ae50a 169#endif
ec4daaa5 170#ifdef _SQUID_WIN32_
0e6d05ef 171static void idnsParseWIN32Registry(void);
d02b72b4 172static void idnsParseWIN32SearchList(const char *);
0e6d05ef 173#endif
68836b58 174static void idnsCacheQuery(idns_query * q);
7b724b86 175static void idnsSendQuery(idns_query * q);
d24ef4e9 176static IOCB idnsReadVCHeader;
63a0e6b7 177static void idnsDoSendQueryVC(nsvc *vc);
62e76326 178
ad61a2b4 179static int idnsFromKnownNameserver(IpAddress const &from);
7b724b86 180static idns_query *idnsFindQuery(unsigned short id);
181static void idnsGrokReply(const char *buf, size_t sz);
182static PF idnsRead;
7cfc1c9a 183static EVH idnsCheckQueue;
efd900cb 184static void idnsTickleQueue(void);
558be27a 185static void idnsRcodeCount(int, int);
7b724b86 186
187static void
188idnsAddNameserver(const char *buf)
189{
ad61a2b4 190 IpAddress A;
62e76326 191
cc192b50 192 if (!(A = buf)) {
bf8fe701 193 debugs(78, 0, "WARNING: rejecting '" << buf << "' as a name server, because it is not a numeric IP address");
62e76326 194 return;
d20b1cd0 195 }
62e76326 196
cc192b50 197 if (A.IsAnyAddr()) {
198 debugs(78, 0, "WARNING: Squid does not accept " << A << " in DNS server specifications.");
199 A = "127.0.0.1";
200 debugs(78, 0, "Will be using " << A << " instead, assuming you meant that DNS is running on the same machine");
5c5a349d 201 }
62e76326 202
7b724b86 203 if (nns == nns_alloc) {
62e76326 204 int oldalloc = nns_alloc;
205 ns *oldptr = nameservers;
206
207 if (nns_alloc == 0)
208 nns_alloc = 2;
209 else
210 nns_alloc <<= 1;
211
212 nameservers = (ns *)xcalloc(nns_alloc, sizeof(*nameservers));
213
214 if (oldptr && oldalloc)
215 xmemcpy(nameservers, oldptr, oldalloc * sizeof(*nameservers));
216
217 if (oldptr)
218 safe_free(oldptr);
7b724b86 219 }
62e76326 220
7b724b86 221 assert(nns < nns_alloc);
cc192b50 222 A.SetPort(NS_DEFAULTPORT);
223 nameservers[nns].S = A;
224 debugs(78, 3, "idnsAddNameserver: Added nameserver #" << nns << " (" << A << ")");
7b724b86 225 nns++;
226}
227
68836b58 228static void
229idnsAddPathComponent(const char *buf)
230{
231 if (npc == npc_alloc) {
232 int oldalloc = npc_alloc;
233 sp *oldptr = searchpath;
234
235 if (0 == npc_alloc)
236 npc_alloc = 2;
237 else
238 npc_alloc <<= 1;
239
240 searchpath = (sp *)xcalloc(npc_alloc, sizeof(*searchpath));
241
242 if (oldptr && oldalloc)
243 xmemcpy(searchpath, oldptr, oldalloc * sizeof(*searchpath));
244
245 if (oldptr)
246 safe_free(oldptr);
247 }
248
249 assert(npc < npc_alloc);
250 strcpy(searchpath[npc].domain, buf);
bf8fe701 251 debugs(78, 3, "idnsAddPathComponent: Added domain #" << npc << ": " << searchpath[npc].domain);
68836b58 252 npc++;
253}
254
2ea84e89 255
7b724b86 256static void
257idnsFreeNameservers(void)
258{
259 safe_free(nameservers);
260 nns = nns_alloc = 0;
261}
262
68836b58 263static void
264idnsFreeSearchpath(void)
265{
266 safe_free(searchpath);
267 npc = npc_alloc = 0;
268}
269
270
271
efd900cb 272static void
273idnsParseNameservers(void)
274{
275 wordlist *w;
62e76326 276
efd900cb 277 for (w = Config.dns_nameservers; w; w = w->next) {
bf8fe701 278 debugs(78, 1, "Adding nameserver " << w->key << " from squid.conf");
62e76326 279 idnsAddNameserver(w->key);
efd900cb 280 }
281}
282
1f1ae50a 283#ifndef _SQUID_MSWIN_
7b724b86 284static void
285idnsParseResolvConf(void)
286{
287 FILE *fp;
68836b58 288 char buf[RESOLV_BUFSZ];
a083e75a 289 const char *t;
68836b58 290 fp = fopen(_PATH_RESCONF, "r");
62e76326 291
7b724b86 292 if (fp == NULL) {
bf8fe701 293 debugs(78, 1, "" << _PATH_RESCONF << ": " << xstrerror());
62e76326 294 return;
7b724b86 295 }
62e76326 296
1f1ae50a 297#if defined(_SQUID_CYGWIN_)
c4aefe96 298 setmode(fileno(fp), O_TEXT);
62e76326 299
c4aefe96 300#endif
62e76326 301
68836b58 302 while (fgets(buf, RESOLV_BUFSZ, fp)) {
62e76326 303 t = strtok(buf, w_space);
304
68836b58 305 if (NULL == t) {
62e76326 306 continue;
68836b58 307 } else if (strcasecmp(t, "nameserver") == 0) {
308 t = strtok(NULL, w_space);
62e76326 309
68836b58 310 if (NULL == t)
311 continue;
62e76326 312
bf8fe701 313 debugs(78, 1, "Adding nameserver " << t << " from " << _PATH_RESCONF);
62e76326 314
68836b58 315 idnsAddNameserver(t);
a083e75a 316 } else if (strcasecmp(t, "domain") == 0) {
26ac0430
AJ
317 idnsFreeSearchpath();
318 t = strtok(NULL, w_space);
a083e75a 319
26ac0430
AJ
320 if (NULL == t)
321 continue;
a083e75a 322
26ac0430 323 debugs(78, 1, "Adding domain " << t << " from " << _PATH_RESCONF);
a083e75a 324
26ac0430 325 idnsAddPathComponent(t);
68836b58 326 } else if (strcasecmp(t, "search") == 0) {
26ac0430 327 idnsFreeSearchpath();
68836b58 328 while (NULL != t) {
329 t = strtok(NULL, w_space);
330
331 if (NULL == t)
332 continue;
333
bf8fe701 334 debugs(78, 1, "Adding domain " << t << " from " << _PATH_RESCONF);
68836b58 335
336 idnsAddPathComponent(t);
337 }
338 } else if (strcasecmp(t, "options") == 0) {
339 while (NULL != t) {
340 t = strtok(NULL, w_space);
62e76326 341
68836b58 342 if (NULL == t)
343 continue;
62e76326 344
68836b58 345 if (strncmp(t, "ndots:", 6) != 0) {
346 ndots = atoi(t + 6);
347
348 if (ndots < 1)
349 ndots = 1;
350
bf8fe701 351 debugs(78, 1, "Adding ndots " << ndots << " from " << _PATH_RESCONF);
68836b58 352 }
353 }
354 }
7b724b86 355 }
a083e75a 356 if (npc == 0 && (t = getMyHostname())) {
26ac0430
AJ
357 t = strchr(t, '.');
358 if (t)
359 idnsAddPathComponent(t+1);
a083e75a 360 }
62e76326 361
7b724b86 362 fclose(fp);
363}
364
1f1ae50a 365#endif
366
ec4daaa5 367#ifdef _SQUID_WIN32_
6b610e34 368static void
d02b72b4 369idnsParseWIN32SearchList(const char * Separator)
6b610e34 370{
a16cb11f 371 char *t;
6b610e34 372 char *token;
373 HKEY hndKey;
374
375 if (RegOpenKey(HKEY_LOCAL_MACHINE,
376 "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
377 &hndKey) == ERROR_SUCCESS) {
378 DWORD Type = 0;
379 DWORD Size = 0;
380 LONG Result;
26ac0430
AJ
381 Result =
382 RegQueryValueEx(hndKey, "Domain", NULL, &Type, NULL,
383 &Size);
384
385 if (Result == ERROR_SUCCESS && Size) {
386 t = (char *) xmalloc(Size);
387 RegQueryValueEx(hndKey, "Domain", NULL, &Type, (LPBYTE) t,
388 &Size);
389 debugs(78, 1, "Adding domain " << t << " from Registry");
390 idnsAddPathComponent(t);
391 xfree(t);
392 }
6b610e34 393 Result =
394 RegQueryValueEx(hndKey, "SearchList", NULL, &Type, NULL,
395 &Size);
396
397 if (Result == ERROR_SUCCESS && Size) {
a16cb11f
GS
398 t = (char *) xmalloc(Size);
399 RegQueryValueEx(hndKey, "SearchList", NULL, &Type, (LPBYTE) t,
6b610e34 400 &Size);
a16cb11f 401 token = strtok(t, Separator);
6b610e34 402
403 while (token) {
404 idnsAddPathComponent(token);
405 debugs(78, 1, "Adding domain " << token << " from Registry");
406 token = strtok(NULL, Separator);
407 }
26ac0430 408 xfree(t);
6b610e34 409 }
410
411 RegCloseKey(hndKey);
412 }
a6cae565 413 if (npc == 0 && (t = (char *) getMyHostname())) {
26ac0430
AJ
414 t = strchr(t, '.');
415 if (t)
416 idnsAddPathComponent(t + 1);
a16cb11f 417 }
6b610e34 418}
419
0e6d05ef 420static void
421idnsParseWIN32Registry(void)
422{
a16cb11f 423 char *t;
0e6d05ef 424 char *token;
425 HKEY hndKey, hndKey2;
426
0e6d05ef 427 switch (WIN32_OS_version) {
62e76326 428
0e6d05ef 429 case _WIN_OS_WINNT:
62e76326 430 /* get nameservers from the Windows NT registry */
431
432 if (RegOpenKey(HKEY_LOCAL_MACHINE,
433 "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
434 &hndKey) == ERROR_SUCCESS) {
435 DWORD Type = 0;
436 DWORD Size = 0;
437 LONG Result;
438 Result =
439 RegQueryValueEx(hndKey, "DhcpNameServer", NULL, &Type, NULL,
440 &Size);
441
442 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 443 t = (char *) xmalloc(Size);
a6cae565 444 RegQueryValueEx(hndKey, "DhcpNameServer", NULL, &Type, (LPBYTE) t,
62e76326 445 &Size);
a16cb11f 446 token = strtok(t, ", ");
62e76326 447
448 while (token) {
449 idnsAddNameserver(token);
6b610e34 450 debugs(78, 1, "Adding DHCP nameserver " << token << " from Registry");
451 token = strtok(NULL, ",");
62e76326 452 }
26ac0430 453 xfree(t);
62e76326 454 }
455
456 Result =
457 RegQueryValueEx(hndKey, "NameServer", NULL, &Type, NULL, &Size);
458
459 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 460 t = (char *) xmalloc(Size);
a6cae565 461 RegQueryValueEx(hndKey, "NameServer", NULL, &Type, (LPBYTE) t, &Size);
a16cb11f 462 token = strtok(t, ", ");
62e76326 463
464 while (token) {
6b610e34 465 debugs(78, 1, "Adding nameserver " << token << " from Registry");
62e76326 466 idnsAddNameserver(token);
467 token = strtok(NULL, ", ");
468 }
26ac0430 469 xfree(t);
62e76326 470 }
471
472 RegCloseKey(hndKey);
473 }
474
6b610e34 475 idnsParseWIN32SearchList(" ");
476
62e76326 477 break;
478
0e6d05ef 479 case _WIN_OS_WIN2K:
62e76326 480
b671cc68 481 case _WIN_OS_WINXP:
62e76326 482
6b1846cf 483 case _WIN_OS_WINNET:
28170269 484
485 case _WIN_OS_WINLON:
62e76326 486 /* get nameservers from the Windows 2000 registry */
487 /* search all interfaces for DNS server addresses */
488
489 if (RegOpenKey(HKEY_LOCAL_MACHINE,
490 "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces",
491 &hndKey) == ERROR_SUCCESS) {
492 int i;
493 char keyname[255];
494
495 for (i = 0; i < 10; i++) {
496 if (RegEnumKey(hndKey, i, (char *) &keyname,
497 255) == ERROR_SUCCESS) {
498 char newkeyname[255];
499 strcpy(newkeyname,
500 "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\");
501 strcat(newkeyname, keyname);
502
503 if (RegOpenKey(HKEY_LOCAL_MACHINE, newkeyname,
504 &hndKey2) == ERROR_SUCCESS) {
505 DWORD Type = 0;
506 DWORD Size = 0;
507 LONG Result;
508 Result =
509 RegQueryValueEx(hndKey2, "DhcpNameServer", NULL,
510 &Type, NULL, &Size);
511
512 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 513 t = (char *) xmalloc(Size);
62e76326 514 RegQueryValueEx(hndKey2, "DhcpNameServer", NULL,
a6cae565 515 &Type, (LPBYTE) t, &Size);
a16cb11f 516 token = strtok(t, ", ");
62e76326 517
518 while (token) {
6b610e34 519 debugs(78, 1, "Adding DHCP nameserver " << token << " from Registry");
62e76326 520 idnsAddNameserver(token);
521 token = strtok(NULL, ", ");
522 }
26ac0430 523 xfree(t);
62e76326 524 }
525
526 Result =
527 RegQueryValueEx(hndKey2, "NameServer", NULL, &Type,
528 NULL, &Size);
529
530 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 531 t = (char *) xmalloc(Size);
62e76326 532 RegQueryValueEx(hndKey2, "NameServer", NULL, &Type,
a6cae565 533 (LPBYTE) t, &Size);
a16cb11f 534 token = strtok(t, ", ");
62e76326 535
536 while (token) {
6b610e34 537 debugs(78, 1, "Adding nameserver " << token << " from Registry");
62e76326 538 idnsAddNameserver(token);
539 token = strtok(NULL, ", ");
540 }
26ac0430 541 xfree(t);
62e76326 542 }
543
544 RegCloseKey(hndKey2);
545 }
546 }
547 }
548
549 RegCloseKey(hndKey);
550 }
551
6b610e34 552 idnsParseWIN32SearchList(", ");
553
62e76326 554 break;
555
0e6d05ef 556 case _WIN_OS_WIN95:
62e76326 557
0e6d05ef 558 case _WIN_OS_WIN98:
62e76326 559
be20dac7 560 case _WIN_OS_WINME:
62e76326 561 /* get nameservers from the Windows 9X registry */
562
563 if (RegOpenKey(HKEY_LOCAL_MACHINE,
564 "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP",
565 &hndKey) == ERROR_SUCCESS) {
566 DWORD Type = 0;
567 DWORD Size = 0;
568 LONG Result;
569 Result =
570 RegQueryValueEx(hndKey, "NameServer", NULL, &Type, NULL, &Size);
571
572 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 573 t = (char *) xmalloc(Size);
a6cae565 574 RegQueryValueEx(hndKey, "NameServer", NULL, &Type, (LPBYTE) t, &Size);
a16cb11f 575 token = strtok(t, ", ");
62e76326 576
577 while (token) {
6b610e34 578 debugs(78, 1, "Adding nameserver " << token << " from Registry");
62e76326 579 idnsAddNameserver(token);
580 token = strtok(NULL, ", ");
581 }
26ac0430 582 xfree(t);
62e76326 583 }
584
585 RegCloseKey(hndKey);
586 }
587
588 break;
589
0e6d05ef 590 default:
6b610e34 591 debugs(78, 1, "Failed to read nameserver from Registry: Unknown System Type.");
62e76326 592 return;
0e6d05ef 593 }
594}
62e76326 595
0e6d05ef 596#endif
597
7b724b86 598static void
599idnsStats(StoreEntry * sentry)
600{
a16b4aa0 601 dlink_node *n;
602 idns_query *q;
7cfc1c9a 603 int i;
558be27a 604 int j;
cc192b50 605 char buf[MAX_IPSTRLEN];
7b724b86 606 storeAppendPrintf(sentry, "Internal DNS Statistics:\n");
a16b4aa0 607 storeAppendPrintf(sentry, "\nThe Queue:\n");
0a69973e 608 storeAppendPrintf(sentry, " DELAY SINCE\n");
609 storeAppendPrintf(sentry, " ID SIZE SENDS FIRST SEND LAST SEND\n");
610 storeAppendPrintf(sentry, "------ ---- ----- ---------- ---------\n");
62e76326 611
a16b4aa0 612 for (n = lru_list.head; n; n = n->next) {
62e76326 613 q = (idns_query *)n->data;
614 storeAppendPrintf(sentry, "%#06x %4d %5d %10.3f %9.3f\n",
615 (int) q->id, (int) q->sz, q->nsends,
616 tvSubDsec(q->start_t, current_time),
617 tvSubDsec(q->sent_t, current_time));
7cfc1c9a 618 }
62e76326 619
7cfc1c9a 620 storeAppendPrintf(sentry, "\nNameservers:\n");
cc192b50 621 storeAppendPrintf(sentry, "IP ADDRESS # QUERIES # REPLIES\n");
622 storeAppendPrintf(sentry, "---------------------------------------------- --------- ---------\n");
62e76326 623
7cfc1c9a 624 for (i = 0; i < nns; i++) {
cc192b50 625 storeAppendPrintf(sentry, "%-45s %9d %9d\n", /* Let's take the maximum: (15 IPv4/45 IPv6) */
626 nameservers[i].S.NtoA(buf,MAX_IPSTRLEN),
62e76326 627 nameservers[i].nqueries,
628 nameservers[i].nreplies);
a16b4aa0 629 }
62e76326 630
558be27a 631 storeAppendPrintf(sentry, "\nRcode Matrix:\n");
632 storeAppendPrintf(sentry, "RCODE");
62e76326 633
558be27a 634 for (i = 0; i < MAX_ATTEMPT; i++)
62e76326 635 storeAppendPrintf(sentry, " ATTEMPT%d", i + 1);
636
7928b8df 637 storeAppendPrintf(sentry, "\n");
62e76326 638
558be27a 639 for (j = 0; j < MAX_RCODE; j++) {
62e76326 640 storeAppendPrintf(sentry, "%5d", j);
641
642 for (i = 0; i < MAX_ATTEMPT; i++)
643 storeAppendPrintf(sentry, " %8d", RcodeMatrix[j][i]);
644
645 storeAppendPrintf(sentry, "\n");
558be27a 646 }
6b610e34 647
648 if (npc) {
649 storeAppendPrintf(sentry, "\nSearch list:\n");
650
651 for (i=0; i < npc; i++)
652 storeAppendPrintf(sentry, "%s\n", searchpath[i].domain);
653
654 storeAppendPrintf(sentry, "\n");
655 }
7b724b86 656}
657
efd900cb 658static void
659idnsTickleQueue(void)
660{
661 if (event_queued)
62e76326 662 return;
663
efd900cb 664 if (NULL == lru_list.tail)
62e76326 665 return;
666
efd900cb 667 eventAdd("idnsCheckQueue", idnsCheckQueue, NULL, 1.0, 1);
62e76326 668
efd900cb 669 event_queued = 1;
670}
671
d24ef4e9 672static void
2b663917 673idnsSentQueryVC(int fd, char *buf, size_t size, comm_err_t flag, int xerrno, void *data)
d24ef4e9 674{
675 nsvc * vc = (nsvc *)data;
676
677 if (flag == COMM_ERR_CLOSING)
678 return;
679
680 if (flag != COMM_OK || size <= 0) {
681 comm_close(fd);
682 return;
683 }
684
685 vc->busy = 0;
686 idnsDoSendQueryVC(vc);
687}
688
689static void
690idnsDoSendQueryVC(nsvc *vc)
691{
692 if (vc->busy)
693 return;
694
032785bf 695 if (vc->queue->contentSize() == 0)
d24ef4e9 696 return;
697
032785bf 698 MemBuf *mb = vc->queue;
d24ef4e9 699
032785bf 700 vc->queue = new MemBuf;
d24ef4e9 701
702 vc->busy = 1;
703
704 commSetTimeout(vc->fd, Config.Timeout.idns_query, NULL, NULL);
705
2b663917 706 comm_write_mbuf(vc->fd, mb, idnsSentQueryVC, vc);
032785bf 707
708 delete mb;
d24ef4e9 709}
710
711static void
712idnsInitVCConnected(int fd, comm_err_t status, int xerrno, void *data)
713{
714 nsvc * vc = (nsvc *)data;
715
716 if (status != COMM_OK) {
445962f3
HN
717 char buf[MAX_IPSTRLEN];
718 debugs(78, 1, "idnsInitVCConnected: Failed to connect to nameserver " << nameservers[vc->ns].S.NtoA(buf,MAX_IPSTRLEN) << " using TCP!");
d24ef4e9 719 comm_close(fd);
720 return;
721 }
722
723 comm_read(fd, (char *)&vc->msglen, 2 , idnsReadVCHeader, vc);
724 vc->busy = 0;
725 idnsDoSendQueryVC(vc);
726}
727
728static void
729idnsVCClosed(int fd, void *data)
730{
731 nsvc * vc = (nsvc *)data;
032785bf 732 delete vc->queue;
733 delete vc->msg;
d24ef4e9 734 nameservers[vc->ns].vc = NULL;
63a0e6b7 735 cbdataFree(vc);
d24ef4e9 736}
737
738static void
739idnsInitVC(int ns)
740{
cc192b50 741 char buf[MAX_IPSTRLEN];
742
d24ef4e9 743 nsvc *vc = cbdataAlloc(nsvc);
744 nameservers[ns].vc = vc;
445962f3 745 vc->ns = ns;
d24ef4e9 746
ad61a2b4 747 IpAddress addr;
d24ef4e9 748
cc192b50 749 if (!Config.Addrs.udp_outgoing.IsNoAddr())
d24ef4e9 750 addr = Config.Addrs.udp_outgoing;
751 else
752 addr = Config.Addrs.udp_incoming;
753
032785bf 754 vc->queue = new MemBuf;
d24ef4e9 755
032785bf 756 vc->msg = new MemBuf;
d24ef4e9 757
758 vc->fd = comm_open(SOCK_STREAM,
759 IPPROTO_TCP,
760 addr,
d24ef4e9 761 COMM_NONBLOCKING,
63a0e6b7 762 "DNS TCP Socket");
d24ef4e9 763
764 if (vc->fd < 0)
765 fatal("Could not create a DNS socket");
766
767 comm_add_close_handler(vc->fd, idnsVCClosed, vc);
768
769 vc->busy = 1;
770
cc192b50 771 commConnectStart(vc->fd, nameservers[ns].S.NtoA(buf,MAX_IPSTRLEN), nameservers[ns].S.GetPort(), idnsInitVCConnected, vc);
d24ef4e9 772}
773
774static void
775idnsSendQueryVC(idns_query * q, int ns)
776{
777 if (nameservers[ns].vc == NULL)
778 idnsInitVC(ns);
779
780 nsvc *vc = nameservers[ns].vc;
781
445962f3
HN
782 if (!vc) {
783 char buf[MAX_IPSTRLEN];
784 debugs(78, 1, "idnsSendQuery: Failed to initiate TCP connection to nameserver " << nameservers[ns].S.NtoA(buf,MAX_IPSTRLEN) << "!");
785
786 return;
787 }
788
2fe7eff9 789 vc->queue->reset();
d24ef4e9 790
791 short head = htons(q->sz);
792
2fe7eff9 793 vc->queue->append((char *)&head, 2);
d24ef4e9 794
2fe7eff9 795 vc->queue->append(q->buf, q->sz);
d24ef4e9 796
797 idnsDoSendQueryVC(vc);
798}
799
7b724b86 800static void
801idnsSendQuery(idns_query * q)
802{
803 int x;
ef523f99 804 int ns;
62e76326 805
0a69973e 806 if (DnsSocket < 0) {
cc192b50 807 debugs(78, 1, "WARNING: idnsSendQuery: Can't send query, no DNS socket!");
62e76326 808 return;
0a69973e 809 }
62e76326 810
cc192b50 811 if (nns <= 0) {
812 debugs(78, 1, "WARNING: idnsSendQuery: Can't send query, no DNS nameservers known!");
813 return;
814 }
62e76326 815
7b724b86 816 assert(q->lru.next == NULL);
62e76326 817
7b724b86 818 assert(q->lru.prev == NULL);
62e76326 819
213b6c90
AJ
820 do {
821 ns = q->nsends % nns;
822
823 if (q->need_vc) {
824 idnsSendQueryVC(q, ns);
825 x = 0;
826 } else {
827 x = comm_udp_sendto(DnsSocket, nameservers[ns].S, q->buf, q->sz);
828 }
62e76326 829
213b6c90 830 q->nsends++;
62e76326 831
820ee9fa 832 q->queue_t = q->sent_t = current_time;
62e76326 833
213b6c90
AJ
834 if (x < 0)
835 debugs(50, 1, "idnsSendQuery: FD " << DnsSocket << ": sendto: " << xstrerror());
62e76326 836
26ac0430 837 } while ( x<0 && q->nsends % nns != 0);
62e76326 838
26ac0430 839 if (x >= 0) {
62e76326 840 fd_bytes(DnsSocket, x, FD_WRITE);
841 commSetSelect(DnsSocket, COMM_SELECT_READ, idnsRead, NULL, 0);
0a69973e 842 }
62e76326 843
7cfc1c9a 844 nameservers[ns].nqueries++;
c131f066 845 q->queue_t = current_time;
7b724b86 846 dlinkAdd(q, &q->lru, &lru_list);
efd900cb 847 idnsTickleQueue();
7b724b86 848}
849
850static int
ad61a2b4 851idnsFromKnownNameserver(IpAddress const &from)
7b724b86 852{
853 int i;
62e76326 854
26ac0430 855 for (i = 0; i < nns; i++) {
cc192b50 856 if (nameservers[i].S != from)
62e76326 857 continue;
858
cc192b50 859 if (nameservers[i].S.GetPort() != from.GetPort())
62e76326 860 continue;
861
862 return i;
7b724b86 863 }
62e76326 864
7cfc1c9a 865 return -1;
7b724b86 866}
867
868static idns_query *
869idnsFindQuery(unsigned short id)
870{
871 dlink_node *n;
872 idns_query *q;
62e76326 873
7b724b86 874 for (n = lru_list.tail; n; n = n->prev) {
62e76326 875 q = (idns_query*)n->data;
876
877 if (q->id == id)
878 return q;
7b724b86 879 }
62e76326 880
7b724b86 881 return NULL;
882}
883
108d67a0 884static unsigned short
885idnsQueryID(void)
886{
887 unsigned short id = squid_random() & 0xFFFF;
888 unsigned short first_id = id;
889
26ac0430 890 while (idnsFindQuery(id)) {
108d67a0 891 id++;
892
846c8960 893 if (id == first_id) {
bf8fe701 894 debugs(78, 1, "idnsQueryID: Warning, too many pending DNS requests");
108d67a0 895 break;
846c8960 896 }
108d67a0 897 }
898
846c8960 899 return id;
108d67a0 900}
901
3074b9d1 902static void
903idnsCallback(idns_query *q, rfc1035_rr *answers, int n, const char *error)
904{
905 IDNSCB *callback;
906 void *cbdata;
907
908 callback = q->callback;
909 q->callback = NULL;
910
911 if (cbdataReferenceValidDone(q->callback_data, &cbdata))
912 callback(cbdata, answers, n, error);
913
26ac0430 914 while (q->queue) {
3074b9d1 915 idns_query *q2 = q->queue;
916 q->queue = q2->queue;
917 callback = q2->callback;
918 q2->callback = NULL;
919
920 if (cbdataReferenceValidDone(q2->callback_data, &cbdata))
921 callback(cbdata, answers, n, error);
922
63a0e6b7 923 cbdataFree(q2);
3074b9d1 924 }
925
926 if (q->hash.key) {
927 hash_remove_link(idns_lookup_hash, &q->hash);
928 q->hash.key = NULL;
929 }
930}
931
cc192b50 932void
933idnsDropMessage(rfc1035_message *message, idns_query *q)
934{
935 rfc1035MessageDestroy(&message);
936 if (q->hash.key) {
937 hash_remove_link(idns_lookup_hash, &q->hash);
938 q->hash.key = NULL;
939 }
940}
941
7b724b86 942static void
943idnsGrokReply(const char *buf, size_t sz)
944{
945 int n;
ec7bade0 946 rfc1035_message *message = NULL;
7b724b86 947 idns_query *q;
3074b9d1 948
cc192b50 949 n = rfc1035MessageUnpack(buf, sz, &message);
62e76326 950
ec7bade0 951 if (message == NULL) {
bf8fe701 952 debugs(78, 1, "idnsGrokReply: Malformed DNS response");
62e76326 953 return;
7b724b86 954 }
62e76326 955
cc192b50 956 debugs(78, 3, "idnsGrokReply: ID 0x" << std::hex << message->id << ", " << std::dec << n << " answers");
ec7bade0 957
958 q = idnsFindQuery(message->id);
959
7b724b86 960 if (q == NULL) {
bf8fe701 961 debugs(78, 3, "idnsGrokReply: Late response");
cc192b50 962 rfc1035MessageDestroy(&message);
62e76326 963 return;
7b724b86 964 }
62e76326 965
9e1f210d 966 if (rfc1035QueryCompare(&q->query, message->query) != 0) {
bf8fe701 967 debugs(78, 3, "idnsGrokReply: Query mismatch (" << q->query.name << " != " << message->query->name << ")");
cc192b50 968 rfc1035MessageDestroy(&message);
9e1f210d 969 return;
970 }
971
d24ef4e9 972 if (message->tc) {
bae9832d 973 debugs(78, 3, HERE << "Resolver requested TC (" << q->query.name << ")");
d24ef4e9 974 dlinkDelete(&q->lru, &lru_list);
cc192b50 975 rfc1035MessageDestroy(&message);
d24ef4e9 976
977 if (!q->need_vc) {
978 q->need_vc = 1;
979 q->nsends--;
980 idnsSendQuery(q);
981 }
982
983 return;
984 }
9e1f210d 985
a16b4aa0 986 dlinkDelete(&q->lru, &lru_list);
558be27a 987 idnsRcodeCount(n, q->attempt);
7ba8d0b8 988 q->error = NULL;
62e76326 989
558be27a 990 if (n < 0) {
bf8fe701 991 debugs(78, 3, "idnsGrokReply: error " << rfc1035_error_message << " (" << rfc1035_errno << ")");
62e76326 992
7ba8d0b8 993 q->error = rfc1035_error_message;
994 q->rcode = -n;
995
996 if (q->rcode == 2 && ++q->attempt < MAX_ATTEMPT) {
62e76326 997 /*
998 * RCODE 2 is "Server failure - The name server was
999 * unable to process this query due to a problem with
1000 * the name server."
1001 */
cc192b50 1002 debugs(78, 3, "idnsGrokReply: Query result: SERV_FAIL");
1003 rfc1035MessageDestroy(&message);
62e76326 1004 q->start_t = current_time;
108d67a0 1005 q->id = idnsQueryID();
1006 rfc1035SetQueryID(q->buf, q->id);
62e76326 1007 idnsSendQuery(q);
1008 return;
1009 }
68836b58 1010
1011 if (q->rcode == 3 && q->do_searchpath && q->attempt < MAX_ATTEMPT) {
1012 assert(NULL == message->answer);
1013 strcpy(q->name, q->orig);
1014
cc192b50 1015 debugs(78, 3, "idnsGrokReply: Query result: NXDOMAIN - " << q->name );
1016
68836b58 1017 if (q->domain < npc) {
1018 strcat(q->name, ".");
1019 strcat(q->name, searchpath[q->domain].domain);
bf8fe701 1020 debugs(78, 3, "idnsGrokReply: searchpath used for " << q->name);
68836b58 1021 q->domain++;
1022 } else {
1023 q->attempt++;
1024 }
1025
cc192b50 1026 idnsDropMessage(message, q);
1027
68836b58 1028 q->start_t = current_time;
1029 q->id = idnsQueryID();
1030 rfc1035SetQueryID(q->buf, q->id);
cc192b50 1031#if USE_IPV6
26ac0430 1032 if (q->query.qtype == RFC1035_TYPE_AAAA) {
cc192b50 1033 debugs(78, 3, "idnsGrokReply: Trying AAAA Query for " << q->name);
1034 q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
26ac0430 1035 } else
cc192b50 1036#endif
1037 {
1038 debugs(78, 3, "idnsGrokReply: Trying A Query for " << q->name);
1039 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
1040 }
68836b58 1041 idnsCacheQuery(q);
1042 idnsSendQuery(q);
1043 return;
1044 }
558be27a 1045 }
62e76326 1046
cc192b50 1047#if USE_IPV6
26ac0430 1048 if (q->need_A && (Config.onoff.dns_require_A == 1 || n <= 0 ) ) {
cc192b50 1049 /* ERROR or NO AAAA exist. Failover to A records. */
bae9832d 1050 /* Apparently its also a good idea to lookup and store the A records
cc192b50 1051 * just in case the AAAA are not available when we need them.
1052 * This could occur due to number of network failings beyond our control
1053 * thus the || above allowing the user to request always both.
1054 */
1055
26ac0430 1056 if (n == 0)
cc192b50 1057 debugs(78, 3, "idnsGrokReply: " << q->name << " has no AAAA records. Looking up A record instead.");
26ac0430 1058 else if (q->need_A && n <= 0)
cc192b50 1059 debugs(78, 3, "idnsGrokReply: " << q->name << " AAAA query failed. Trying A now instead.");
1060 else // admin requested this.
1061 debugs(78, 3, "idnsGrokReply: " << q->name << " AAAA query done. Configured to retrieve A now also.");
1062
bae9832d 1063 // move the initial message results into the failover query for merging later.
26ac0430 1064 if (n > 0) {
bae9832d 1065 q->initial_AAAA.count = message->ancount;
1066 q->initial_AAAA.answers = message->answer;
1067 message->answer = NULL;
1068 }
1069
1070 // remove the hashed query info
cc192b50 1071 idnsDropMessage(message, q);
1072
1073 q->start_t = current_time;
1074 q->id = idnsQueryID();
1075 rfc1035SetQueryID(q->buf, q->id);
1076 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
1077 q->need_A = false;
1078 idnsCacheQuery(q);
1079 idnsSendQuery(q);
1080 return;
1081 }
1082#endif
1083
26ac0430
AJ
1084 /** If there are two result sets from preceeding AAAA and A lookups merge them with a preference for AAAA */
1085 if (q->initial_AAAA.count > 0 && n > 0) {
bae9832d 1086 /* two sets of RR need merging */
1087 rfc1035_rr *result = (rfc1035_rr*) xmalloc( sizeof(rfc1035_rr)*(n + q->initial_AAAA.count) );
1088 rfc1035_rr *tmp = result;
1089
1090 debugs(78, 6, HERE << "Merging DNS results " << q->name << " AAAA has " << q->initial_AAAA.count << " RR, A has " << n << " RR");
1091
1092 memcpy(tmp, q->initial_AAAA.answers, (sizeof(rfc1035_rr)*(q->initial_AAAA.count)) );
1093 tmp += q->initial_AAAA.count;
1094 /* free the RR object without freeing its child strings (they are now taken by the copy above) */
1095 safe_free(q->initial_AAAA.answers);
1096
1097 memcpy( tmp, message->answer, (sizeof(rfc1035_rr)*n) );
1098 /* free the RR object without freeing its child strings (they are now taken by the copy above) */
1099 safe_free(message->answer);
1100
1101 message->answer = result;
1102 n += q->initial_AAAA.count;
1103 q->initial_AAAA.count=0;
26ac0430 1104 } else if (q->initial_AAAA.count > 0 && n <= 0) {
bae9832d 1105 /* initial of dual queries was the only result set. */
1106 debugs(78, 6, HERE << "Merging DNS results " << q->name << " AAAA has " << q->initial_AAAA.count << " RR, A has " << n << " RR");
1107 rfc1035RRDestroy(&(message->answer), n);
1108 message->answer = q->initial_AAAA.answers;
1109 n = q->initial_AAAA.count;
1110 }
1111 /* else initial results were empty. just use the final set as authoritative */
1112
1113 debugs(78, 6, HERE << "Sending " << n << " DNS results to caller.");
ec7bade0 1114 idnsCallback(q, message->answer, n, q->error);
cc192b50 1115 rfc1035MessageDestroy(&message);
63a0e6b7 1116 cbdataFree(q);
7b724b86 1117}
1118
1119static void
1120idnsRead(int fd, void *data)
1121{
d193a436 1122 int *N = &incoming_sockets_accepted;
0b6d1955 1123 int len;
42b51993 1124 int max = INCOMING_DNS_MAX;
d29b40de 1125 static char rbuf[SQUID_UDP_SO_RCVBUF];
7cfc1c9a 1126 int ns;
ad61a2b4 1127 IpAddress from;
cc192b50 1128
d7e7eaa7 1129 debugs(78, 3, "idnsRead: starting with FD " << fd);
cc192b50 1130
1131 /* BUG (UNRESOLVED)
1132 * two code lines after returning from comm_udprecvfrom()
1133 * something overwrites the memory behind the from parameter.
1134 * NO matter where in the stack declaration list above it is placed
1135 * The cause of this is still unknown, however copying the data appears
1136 * to allow it to be passed further without this erasure.
1137 */
ad61a2b4 1138 IpAddress bugbypass;
62e76326 1139
7b724b86 1140 while (max--) {
cc192b50 1141 len = comm_udp_recvfrom(fd, rbuf, SQUID_UDP_SO_RCVBUF, 0, bugbypass);
62e76326 1142
cc192b50 1143 from = bugbypass; // BUG BYPASS. see notes above.
62e76326 1144
1145 if (len == 0)
1146 break;
1147
1148 if (len < 0) {
1149 if (ignoreErrno(errno))
1150 break;
1151
7b724b86 1152#ifdef _SQUID_LINUX_
62e76326 1153 /* Some Linux systems seem to set the FD for reading and then
1154 * return ECONNREFUSED when sendto() fails and generates an ICMP
1155 * port unreachable message. */
1156 /* or maybe an EHOSTUNREACH "No route to host" message */
1157 if (errno != ECONNREFUSED && errno != EHOSTUNREACH)
7b724b86 1158#endif
62e76326 1159
bf8fe701 1160 debugs(50, 1, "idnsRead: FD " << fd << " recvfrom: " << xstrerror());
62e76326 1161
1162 break;
1163 }
1164
1165 fd_bytes(DnsSocket, len, FD_READ);
1166 assert(N);
1167 (*N)++;
cc192b50 1168
1169 debugs(78, 3, "idnsRead: FD " << fd << ": received " << len << " bytes from " << from);
1170
1171 /* BUG: see above. Its here that it becomes apparent that the content of bugbypass is gone. */
1172 ns = idnsFromKnownNameserver(from);
62e76326 1173
1174 if (ns >= 0) {
1175 nameservers[ns].nreplies++;
1176 } else if (Config.onoff.ignore_unknown_nameservers) {
1177 static time_t last_warning = 0;
1178
1179 if (squid_curtime - last_warning > 60) {
cc192b50 1180 debugs(78, 1, "WARNING: Reply from unknown nameserver " << from);
62e76326 1181 last_warning = squid_curtime;
26ac0430 1182 } else {
cc192b50 1183 debugs(78, 1, "WARNING: Reply from unknown nameserver " << from << " (retrying..." << (squid_curtime-last_warning) << "<=60)" );
26ac0430
AJ
1184 }
1185 continue;
62e76326 1186 }
1187
62e76326 1188 idnsGrokReply(rbuf, len);
7b724b86 1189 }
62e76326 1190
2e6caa54 1191 if (lru_list.head)
62e76326 1192 commSetSelect(DnsSocket, COMM_SELECT_READ, idnsRead, NULL, 0);
7b724b86 1193}
1194
7cfc1c9a 1195static void
1196idnsCheckQueue(void *unused)
1197{
1198 dlink_node *n;
0c4fe9e4 1199 dlink_node *p = NULL;
7cfc1c9a 1200 idns_query *q;
1201 event_queued = 0;
62e76326 1202
820ee9fa
AJ
1203 if (0 == nns)
1204 /* name servers went away; reconfiguring or shutting down */
1205 return;
1206
0c4fe9e4 1207 for (n = lru_list.tail; n; n = p) {
62e76326 1208
820ee9fa 1209 p = n->prev;
733cedc0 1210 q = static_cast<idns_query*>(n->data);
62e76326 1211
820ee9fa
AJ
1212 /* Anything to process in the queue? */
1213 if (tvSubDsec(q->queue_t, current_time) < Config.Timeout.idns_retransmit )
26ac0430 1214 break;
62e76326 1215
820ee9fa
AJ
1216 /* Query timer expired? */
1217 if (tvSubDsec(q->sent_t, current_time) < Config.Timeout.idns_retransmit * 1 << ((q->nsends - 1) / nns)) {
1218 dlinkDelete(&q->lru, &lru_list);
1219 q->queue_t = current_time;
1220 dlinkAdd(q, &q->lru, &lru_list);
1221 continue;
1222 }
62e76326 1223
820ee9fa 1224 debugs(78, 3, "idnsCheckQueue: ID 0x" << std::hex << std::setfill('0') << std::setw(4) << q->id << "timeout" );
62e76326 1225
1226 dlinkDelete(&q->lru, &lru_list);
1227
1228 if (tvSubDsec(q->start_t, current_time) < Config.Timeout.idns_query) {
1229 idnsSendQuery(q);
1230 } else {
26ac0430
AJ
1231 debugs(78, 2, "idnsCheckQueue: ID " << std::hex << q->id <<
1232 ": giving up after " << std::dec << q->nsends << " tries and " <<
1233 std::setw(5)<< std::setprecision(2) << tvSubDsec(q->start_t, current_time) << " seconds");
3074b9d1 1234
1235 if (q->rcode != 0)
1236 idnsCallback(q, NULL, -q->rcode, q->error);
1237 else
1238 idnsCallback(q, NULL, -16, "Timeout");
62e76326 1239
63a0e6b7 1240 cbdataFree(q);
62e76326 1241 }
7cfc1c9a 1242 }
62e76326 1243
efd900cb 1244 idnsTickleQueue();
7cfc1c9a 1245}
1246
d24ef4e9 1247static void
1248idnsReadVC(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
1249{
1250 nsvc * vc = (nsvc *)data;
1251
1252 if (flag == COMM_ERR_CLOSING)
1253 return;
1254
1255 if (flag != COMM_OK || len <= 0) {
1256 comm_close(fd);
1257 return;
1258 }
1259
032785bf 1260 vc->msg->size += len; // XXX should not access -> size directly
d24ef4e9 1261
032785bf 1262 if (vc->msg->contentSize() < vc->msglen) {
1263 comm_read(fd, buf + len, vc->msglen - vc->msg->contentSize(), idnsReadVC, vc);
d24ef4e9 1264 return;
1265 }
1266
bf8fe701 1267 debugs(78, 3, "idnsReadVC: FD " << fd << ": received " <<
1268 (int) vc->msg->contentSize() << " bytes via tcp from " <<
cc192b50 1269 nameservers[vc->ns].S << ".");
d24ef4e9 1270
032785bf 1271 idnsGrokReply(vc->msg->buf, vc->msg->contentSize());
2fe7eff9 1272 vc->msg->clean();
d24ef4e9 1273 comm_read(fd, (char *)&vc->msglen, 2 , idnsReadVCHeader, vc);
1274}
1275
1276static void
1277idnsReadVCHeader(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
1278{
1279 nsvc * vc = (nsvc *)data;
1280
1281 if (flag == COMM_ERR_CLOSING)
1282 return;
1283
1284 if (flag != COMM_OK || len <= 0) {
1285 comm_close(fd);
1286 return;
1287 }
1288
1289 vc->read_msglen += len;
1290
1291 assert(vc->read_msglen <= 2);
1292
1293 if (vc->read_msglen < 2) {
1294 comm_read(fd, buf + len, 2 - vc->read_msglen, idnsReadVCHeader, vc);
1295 return;
1296 }
1297
1298 vc->read_msglen = 0;
1299
1300 vc->msglen = ntohs(vc->msglen);
1301
2fe7eff9 1302 vc->msg->init(vc->msglen, vc->msglen);
032785bf 1303 comm_read(fd, vc->msg->buf, vc->msglen, idnsReadVC, vc);
d24ef4e9 1304}
1305
558be27a 1306/*
1307 * rcode < 0 indicates an error, rocde >= 0 indicates success
1308 */
1309static void
1310idnsRcodeCount(int rcode, int attempt)
1311{
1312 if (rcode > 0)
62e76326 1313 rcode = 0;
558be27a 1314 else if (rcode < 0)
62e76326 1315 rcode = -rcode;
1316
558be27a 1317 if (rcode < MAX_RCODE)
62e76326 1318 if (attempt < MAX_ATTEMPT)
1319 RcodeMatrix[rcode][attempt]++;
558be27a 1320}
1321
7b724b86 1322/* ====================================================================== */
1323
5f5e883f
FC
1324static void
1325idnsRegisterWithCacheManager(void)
1326{
1327 CacheManager::GetInstance()->
26ac0430 1328 registerAction("idns", "Internal DNS Statistics", idnsStats, 0, 1);
5f5e883f
FC
1329}
1330
7b724b86 1331void
1332idnsInit(void)
1333{
1334 static int init = 0;
62e76326 1335
d24ef4e9 1336 CBDATA_INIT_TYPE(nsvc);
63a0e6b7 1337 CBDATA_INIT_TYPE(idns_query);
d24ef4e9 1338
ef523f99 1339 if (DnsSocket < 0) {
62e76326 1340 int port;
1341
ad61a2b4 1342 IpAddress addr;
62e76326 1343
cc192b50 1344 if (!Config.Addrs.udp_outgoing.IsNoAddr())
62e76326 1345 addr = Config.Addrs.udp_outgoing;
1346 else
1347 addr = Config.Addrs.udp_incoming;
1348
1349 DnsSocket = comm_open(SOCK_DGRAM,
bdb741f4 1350 IPPROTO_UDP,
62e76326 1351 addr,
62e76326 1352 COMM_NONBLOCKING,
1353 "DNS Socket");
1354
cc192b50 1355 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addr);
1356
62e76326 1357 if (DnsSocket < 0)
1358 fatal("Could not create a DNS socket");
1359
1360 /* Ouch... we can't call functions using debug from a debug
1361 * statement. Doing so messes up the internal Debug::level
1362 */
1363 port = comm_local_port(DnsSocket);
1364
cc192b50 1365 debugs(78, 1, "DNS Socket created at " << addr << ", FD " << DnsSocket);
7b724b86 1366 }
62e76326 1367
efd900cb 1368 assert(0 == nns);
1369 idnsParseNameservers();
0e6d05ef 1370#ifndef _SQUID_MSWIN_
62e76326 1371
efd900cb 1372 if (0 == nns)
62e76326 1373 idnsParseResolvConf();
1374
0e6d05ef 1375#endif
ec4daaa5 1376#ifdef _SQUID_WIN32_
62e76326 1377
0e6d05ef 1378 if (0 == nns)
62e76326 1379 idnsParseWIN32Registry();
1380
0e6d05ef 1381#endif
62e76326 1382
29fe9bd5 1383 if (0 == nns) {
1384 debugs(78, 1, "Warning: Could not find any nameservers. Trying to use localhost");
ec4daaa5 1385#ifdef _SQUID_WIN32_
29fe9bd5 1386
1387 debugs(78, 1, "Please check your TCP-IP settings or /etc/resolv.conf file");
0e6d05ef 1388#else
29fe9bd5 1389
1390 debugs(78, 1, "Please check your /etc/resolv.conf file");
0e6d05ef 1391#endif
29fe9bd5 1392
1393 debugs(78, 1, "or use the 'dns_nameservers' option in squid.conf.");
1394 idnsAddNameserver("127.0.0.1");
1395 }
62e76326 1396
7b724b86 1397 if (!init) {
62e76326 1398 memDataInit(MEM_IDNS_QUERY, "idns_query", sizeof(idns_query), 0);
62e76326 1399 memset(RcodeMatrix, '\0', sizeof(RcodeMatrix));
30abd221 1400 idns_lookup_hash = hash_create((HASHCMP *) strcmp, 103, hash_string);
62e76326 1401 init++;
7b724b86 1402 }
372fb44e
FC
1403
1404 idnsRegisterWithCacheManager();
7b724b86 1405}
1406
1407void
1408idnsShutdown(void)
1409{
ef523f99 1410 if (DnsSocket < 0)
62e76326 1411 return;
1412
ef523f99 1413 comm_close(DnsSocket);
62e76326 1414
ef523f99 1415 DnsSocket = -1;
62e76326 1416
b26754c4 1417 for (int i = 0; i < nns; i++) {
1418 if (nsvc *vc = nameservers[i].vc) {
1419 if (vc->fd >= 0)
1420 comm_close(vc->fd);
1421 }
1422 }
1423
efd900cb 1424 idnsFreeNameservers();
68836b58 1425
1426 idnsFreeSearchpath();
7b724b86 1427}
1428
3074b9d1 1429static int
1430idnsCachedLookup(const char *key, IDNSCB * callback, void *data)
1431{
1432 idns_query *q;
1433
1434 idns_query *old = (idns_query *) hash_lookup(idns_lookup_hash, key);
1435
1436 if (!old)
1437 return 0;
1438
63a0e6b7 1439 q = cbdataAlloc(idns_query);
3074b9d1 1440
1441 q->callback = callback;
1442
1443 q->callback_data = cbdataReference(data);
1444
1445 q->queue = old->queue;
1446
1447 old->queue = q;
1448
1449 return 1;
1450}
1451
1452static void
9e1f210d 1453idnsCacheQuery(idns_query *q)
3074b9d1 1454{
9e1f210d 1455 q->hash.key = q->query.name;
3074b9d1 1456 hash_join(idns_lookup_hash, &q->hash);
1457}
1458
7b724b86 1459void
1460idnsALookup(const char *name, IDNSCB * callback, void *data)
1461{
68836b58 1462 unsigned int i;
1463 int nd = 0;
3074b9d1 1464 idns_query *q;
1465
1466 if (idnsCachedLookup(name, callback, data))
1467 return;
1468
63a0e6b7 1469 q = cbdataAlloc(idns_query);
3074b9d1 1470
71952967 1471 q->id = idnsQueryID();
1472
68836b58 1473 for (i = 0; i < strlen(name); i++)
1474 if (name[i] == '.')
1475 nd++;
1476
1477 if (Config.onoff.res_defnames && npc > 0 && name[strlen(name)-1] != '.') {
1478 q->do_searchpath = 1;
1479 } else {
1480 q->do_searchpath = 0;
1481 }
1482
1483 strcpy(q->orig, name);
1484 strcpy(q->name, q->orig);
1485
1486 if (q->do_searchpath && nd < ndots) {
1487 q->domain = 0;
1488 strcat(q->name, ".");
1489 strcat(q->name, searchpath[q->domain].domain);
bf8fe701 1490 debugs(78, 3, "idnsALookup: searchpath used for " << q->name);
68836b58 1491 }
1492
cc192b50 1493#if USE_IPV6
26ac0430 1494 q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
cc192b50 1495 q->need_A = true;
1496#else
1497 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
1498 q->need_A = false;
1499#endif
62e76326 1500
2041330b 1501 if (q->sz < 0) {
1502 /* problem with query data -- query not sent */
1503 callback(data, NULL, 0, "Internal error");
63a0e6b7 1504 cbdataFree(q);
2041330b 1505 return;
1506 }
1507
26ac0430 1508 debugs(78, 3, "idnsALookup: buf is " << q->sz << " bytes for " << q->name <<
bf8fe701 1509 ", id = 0x" << std::hex << q->id);
aa6c61e3 1510
7b724b86 1511 q->callback = callback;
aa6c61e3 1512
fa80a8ef 1513 q->callback_data = cbdataReference(data);
aa6c61e3 1514
7cfc1c9a 1515 q->start_t = current_time;
aa6c61e3 1516
9e1f210d 1517 idnsCacheQuery(q);
aa6c61e3 1518
7b724b86 1519 idnsSendQuery(q);
1520}
8db71107 1521
1522void
ad61a2b4 1523idnsPTRLookup(const IpAddress &addr, IDNSCB * callback, void *data)
8db71107 1524{
3074b9d1 1525 idns_query *q;
1526
cc192b50 1527 char ip[MAX_IPSTRLEN];
1528
1529 addr.NtoA(ip,MAX_IPSTRLEN);
3074b9d1 1530
63a0e6b7 1531 q = cbdataAlloc(idns_query);
3074b9d1 1532
71952967 1533 q->id = idnsQueryID();
1534
cc192b50 1535#if USE_IPV6
26ac0430 1536 if ( addr.IsIPv6() ) {
cc192b50 1537 struct in6_addr addr6;
1538 addr.GetInAddr(addr6);
1539 q->sz = rfc3596BuildPTRQuery6(addr6, q->buf, sizeof(q->buf), q->id, &q->query);
26ac0430 1540 } else
cc192b50 1541#endif
1542 {
1543 struct in_addr addr4;
1544 addr.GetInAddr(addr4);
1545 q->sz = rfc3596BuildPTRQuery4(addr4, q->buf, sizeof(q->buf), q->id, &q->query);
1546 }
1547
1548 /* PTR does not do inbound A/AAAA */
1549 q->need_A = false;
3074b9d1 1550
26ac0430 1551 if (q->sz < 0) {
2041330b 1552 /* problem with query data -- query not sent */
1553 callback(data, NULL, 0, "Internal error");
63a0e6b7 1554 cbdataFree(q);
2041330b 1555 return;
1556 }
1557
37f3da43 1558 if (idnsCachedLookup(q->query.name, callback, data)) {
26ac0430
AJ
1559 cbdataFree(q);
1560 return;
37f3da43 1561 }
1562
26ac0430 1563 debugs(78, 3, "idnsPTRLookup: buf is " << q->sz << " bytes for " << ip <<
bf8fe701 1564 ", id = 0x" << std::hex << q->id);
3074b9d1 1565
8db71107 1566 q->callback = callback;
3074b9d1 1567
fa80a8ef 1568 q->callback_data = cbdataReference(data);
3074b9d1 1569
8db71107 1570 q->start_t = current_time;
3074b9d1 1571
9e1f210d 1572 idnsCacheQuery(q);
3074b9d1 1573
8db71107 1574 idnsSendQuery(q);
1575}
eb824054 1576
3c573763 1577#ifdef SQUID_SNMP
1578/*
1579 * The function to return the DNS via SNMP
1580 */
1581variable_list *
1582snmp_netIdnsFn(variable_list * Var, snint * ErrP)
1583{
1584 int i, n = 0;
1585 variable_list *Answer = NULL;
bf8fe701 1586 debugs(49, 5, "snmp_netDnsFn: Processing request: ");
3c573763 1587 snmpDebugOid(5, Var->name, Var->name_length);
1588 *ErrP = SNMP_ERR_NOERROR;
62e76326 1589
3c573763 1590 switch (Var->name[LEN_SQ_NET + 1]) {
62e76326 1591
3c573763 1592 case DNS_REQ:
62e76326 1593
1594 for (i = 0; i < nns; i++)
1595 n += nameservers[i].nqueries;
1596
1597 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1598 n,
1599 SMI_COUNTER32);
1600
1601 break;
1602
3c573763 1603 case DNS_REP:
62e76326 1604 for (i = 0; i < nns; i++)
1605 n += nameservers[i].nreplies;
1606
1607 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1608 n,
1609 SMI_COUNTER32);
1610
1611 break;
1612
3c573763 1613 case DNS_SERVERS:
62e76326 1614 Answer = snmp_var_new_integer(Var->name, Var->name_length,
e494f5e9 1615 nns,
62e76326 1616 SMI_COUNTER32);
1617
1618 break;
1619
3c573763 1620 default:
62e76326 1621 *ErrP = SNMP_ERR_NOSUCHNAME;
1622
1623 break;
3c573763 1624 }
62e76326 1625
3c573763 1626 return Answer;
1627}
62e76326 1628
3c573763 1629#endif /*SQUID_SNMP */
1f1ae50a 1630#endif /* USE_DNSSERVERS */