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