]> git.ipfire.org Git - thirdparty/squid.git/blame - src/dns_internal.cc
SourceFormat Enforcement
[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 */
32d002cb 59#if !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
2c2da7d3 349 if (strncmp(t, "ndots:", 6) == 0) {
68836b58 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;
04f7fd38 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 }
04f7fd38 392 Result = RegQueryValueEx(hndKey, "SearchList", NULL, &Type, NULL, &Size);
6b610e34 393
394 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 395 t = (char *) xmalloc(Size);
04f7fd38 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
04f7fd38 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;
04f7fd38 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
04f7fd38 481 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_TCPIP_PARA_INTERFACES, 0, KEY_READ, &hndKey) == ERROR_SUCCESS) {
62e76326 482 int i;
04f7fd38
AJ
483 DWORD MaxSubkeyLen, InterfacesCount;
484 char *keyname;
485 FILETIME ftLastWriteTime;
486
487 if (RegQueryInfoKey(hndKey, NULL, NULL, NULL, &InterfacesCount, &MaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
488 keyname = (char *) xmalloc(++MaxSubkeyLen);
489 for (i = 0; i < (int) InterfacesCount; i++) {
490 DWORD j;
491 j = MaxSubkeyLen;
492 if (RegEnumKeyEx(hndKey, i, keyname, &j, NULL, NULL, NULL, &ftLastWriteTime) == ERROR_SUCCESS) {
493 char *newkeyname;
494 newkeyname = (char *) xmalloc(sizeof(REG_TCPIP_PARA_INTERFACES) + j + 2);
495 strcpy(newkeyname, REG_TCPIP_PARA_INTERFACES);
496 strcat(newkeyname, "\\");
497 strcat(newkeyname, keyname);
498 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newkeyname, 0, KEY_QUERY_VALUE, &hndKey2) == ERROR_SUCCESS) {
499 DWORD Type = 0;
500 DWORD Size = 0;
501 LONG Result;
502 Result = RegQueryValueEx(hndKey2, "DhcpNameServer", NULL, &Type, NULL, &Size);
503 if (Result == ERROR_SUCCESS && Size) {
504 t = (char *) xmalloc(Size);
505 RegQueryValueEx(hndKey2, "DhcpNameServer", NULL, &Type, (LPBYTE)t, &Size);
506 token = strtok(t, ", ");
507 while (token) {
0dde2160 508 debugs(78, 1, "Adding DHCP nameserver " << token << " from Registry");
04f7fd38
AJ
509 idnsAddNameserver(token);
510 token = strtok(NULL, ", ");
511 }
512 xfree(t);
513 }
514
515 Result = RegQueryValueEx(hndKey2, "NameServer", NULL, &Type, NULL, &Size);
516 if (Result == ERROR_SUCCESS && Size) {
517 t = (char *) xmalloc(Size);
518 RegQueryValueEx(hndKey2, "NameServer", NULL, &Type, (LPBYTE)t, &Size);
519 token = strtok(t, ", ");
520 while (token) {
521 debugs(78, 1, "Adding nameserver " << token << " from Registry");
522 idnsAddNameserver(token);
523 token = strtok(NULL, ", ");
524 }
525
526 xfree(t);
62e76326 527 }
62e76326 528
0dde2160 529 RegCloseKey(hndKey2);
62e76326 530 }
531
04f7fd38 532 xfree(newkeyname);
62e76326 533 }
534 }
0dde2160 535
04f7fd38 536 xfree(keyname);
62e76326 537 }
538
539 RegCloseKey(hndKey);
540 }
541
6b610e34 542 idnsParseWIN32SearchList(", ");
543
62e76326 544 break;
545
0e6d05ef 546 case _WIN_OS_WIN95:
62e76326 547
0e6d05ef 548 case _WIN_OS_WIN98:
62e76326 549
be20dac7 550 case _WIN_OS_WINME:
62e76326 551 /* get nameservers from the Windows 9X registry */
552
04f7fd38 553 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_VXD_MSTCP, 0, KEY_QUERY_VALUE, &hndKey) == ERROR_SUCCESS) {
62e76326 554 DWORD Type = 0;
555 DWORD Size = 0;
556 LONG Result;
04f7fd38 557 Result = RegQueryValueEx(hndKey, "NameServer", NULL, &Type, NULL, &Size);
62e76326 558
559 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 560 t = (char *) xmalloc(Size);
a6cae565 561 RegQueryValueEx(hndKey, "NameServer", NULL, &Type, (LPBYTE) t, &Size);
a16cb11f 562 token = strtok(t, ", ");
62e76326 563
564 while (token) {
6b610e34 565 debugs(78, 1, "Adding nameserver " << token << " from Registry");
62e76326 566 idnsAddNameserver(token);
567 token = strtok(NULL, ", ");
568 }
26ac0430 569 xfree(t);
62e76326 570 }
571
572 RegCloseKey(hndKey);
573 }
574
575 break;
576
0e6d05ef 577 default:
6b610e34 578 debugs(78, 1, "Failed to read nameserver from Registry: Unknown System Type.");
62e76326 579 return;
0e6d05ef 580 }
581}
62e76326 582
0e6d05ef 583#endif
584
7b724b86 585static void
586idnsStats(StoreEntry * sentry)
587{
a16b4aa0 588 dlink_node *n;
589 idns_query *q;
7cfc1c9a 590 int i;
558be27a 591 int j;
cc192b50 592 char buf[MAX_IPSTRLEN];
7b724b86 593 storeAppendPrintf(sentry, "Internal DNS Statistics:\n");
a16b4aa0 594 storeAppendPrintf(sentry, "\nThe Queue:\n");
0a69973e 595 storeAppendPrintf(sentry, " DELAY SINCE\n");
596 storeAppendPrintf(sentry, " ID SIZE SENDS FIRST SEND LAST SEND\n");
597 storeAppendPrintf(sentry, "------ ---- ----- ---------- ---------\n");
62e76326 598
a16b4aa0 599 for (n = lru_list.head; n; n = n->next) {
62e76326 600 q = (idns_query *)n->data;
601 storeAppendPrintf(sentry, "%#06x %4d %5d %10.3f %9.3f\n",
602 (int) q->id, (int) q->sz, q->nsends,
603 tvSubDsec(q->start_t, current_time),
604 tvSubDsec(q->sent_t, current_time));
7cfc1c9a 605 }
62e76326 606
7cfc1c9a 607 storeAppendPrintf(sentry, "\nNameservers:\n");
cc192b50 608 storeAppendPrintf(sentry, "IP ADDRESS # QUERIES # REPLIES\n");
609 storeAppendPrintf(sentry, "---------------------------------------------- --------- ---------\n");
62e76326 610
7cfc1c9a 611 for (i = 0; i < nns; i++) {
cc192b50 612 storeAppendPrintf(sentry, "%-45s %9d %9d\n", /* Let's take the maximum: (15 IPv4/45 IPv6) */
613 nameservers[i].S.NtoA(buf,MAX_IPSTRLEN),
62e76326 614 nameservers[i].nqueries,
615 nameservers[i].nreplies);
a16b4aa0 616 }
62e76326 617
558be27a 618 storeAppendPrintf(sentry, "\nRcode Matrix:\n");
619 storeAppendPrintf(sentry, "RCODE");
62e76326 620
558be27a 621 for (i = 0; i < MAX_ATTEMPT; i++)
62e76326 622 storeAppendPrintf(sentry, " ATTEMPT%d", i + 1);
623
7928b8df 624 storeAppendPrintf(sentry, "\n");
62e76326 625
558be27a 626 for (j = 0; j < MAX_RCODE; j++) {
62e76326 627 storeAppendPrintf(sentry, "%5d", j);
628
629 for (i = 0; i < MAX_ATTEMPT; i++)
630 storeAppendPrintf(sentry, " %8d", RcodeMatrix[j][i]);
631
632 storeAppendPrintf(sentry, "\n");
558be27a 633 }
6b610e34 634
635 if (npc) {
636 storeAppendPrintf(sentry, "\nSearch list:\n");
637
638 for (i=0; i < npc; i++)
639 storeAppendPrintf(sentry, "%s\n", searchpath[i].domain);
640
641 storeAppendPrintf(sentry, "\n");
642 }
7b724b86 643}
644
efd900cb 645static void
646idnsTickleQueue(void)
647{
648 if (event_queued)
62e76326 649 return;
650
efd900cb 651 if (NULL == lru_list.tail)
62e76326 652 return;
653
efd900cb 654 eventAdd("idnsCheckQueue", idnsCheckQueue, NULL, 1.0, 1);
62e76326 655
efd900cb 656 event_queued = 1;
657}
658
d24ef4e9 659static void
2b663917 660idnsSentQueryVC(int fd, char *buf, size_t size, comm_err_t flag, int xerrno, void *data)
d24ef4e9 661{
662 nsvc * vc = (nsvc *)data;
663
664 if (flag == COMM_ERR_CLOSING)
665 return;
af6a12ee 666
2c471ba8 667 if (fd_table[fd].closing())
af6a12ee 668 return;
d24ef4e9 669
670 if (flag != COMM_OK || size <= 0) {
671 comm_close(fd);
672 return;
673 }
674
675 vc->busy = 0;
676 idnsDoSendQueryVC(vc);
677}
678
679static void
680idnsDoSendQueryVC(nsvc *vc)
681{
682 if (vc->busy)
683 return;
684
032785bf 685 if (vc->queue->contentSize() == 0)
d24ef4e9 686 return;
687
032785bf 688 MemBuf *mb = vc->queue;
d24ef4e9 689
032785bf 690 vc->queue = new MemBuf;
d24ef4e9 691
692 vc->busy = 1;
693
694 commSetTimeout(vc->fd, Config.Timeout.idns_query, NULL, NULL);
695
2b663917 696 comm_write_mbuf(vc->fd, mb, idnsSentQueryVC, vc);
032785bf 697
698 delete mb;
d24ef4e9 699}
700
701static void
3ff65596 702idnsInitVCConnected(int fd, const DnsLookupDetails &, comm_err_t status, int xerrno, void *data)
d24ef4e9 703{
704 nsvc * vc = (nsvc *)data;
705
706 if (status != COMM_OK) {
445962f3
HN
707 char buf[MAX_IPSTRLEN];
708 debugs(78, 1, "idnsInitVCConnected: Failed to connect to nameserver " << nameservers[vc->ns].S.NtoA(buf,MAX_IPSTRLEN) << " using TCP!");
d24ef4e9 709 comm_close(fd);
710 return;
711 }
712
713 comm_read(fd, (char *)&vc->msglen, 2 , idnsReadVCHeader, vc);
714 vc->busy = 0;
715 idnsDoSendQueryVC(vc);
716}
717
718static void
719idnsVCClosed(int fd, void *data)
720{
721 nsvc * vc = (nsvc *)data;
032785bf 722 delete vc->queue;
723 delete vc->msg;
d24ef4e9 724 nameservers[vc->ns].vc = NULL;
63a0e6b7 725 cbdataFree(vc);
d24ef4e9 726}
727
728static void
729idnsInitVC(int ns)
730{
cc192b50 731 char buf[MAX_IPSTRLEN];
732
d24ef4e9 733 nsvc *vc = cbdataAlloc(nsvc);
734 nameservers[ns].vc = vc;
445962f3 735 vc->ns = ns;
d24ef4e9 736
ad61a2b4 737 IpAddress addr;
d24ef4e9 738
cc192b50 739 if (!Config.Addrs.udp_outgoing.IsNoAddr())
d24ef4e9 740 addr = Config.Addrs.udp_outgoing;
741 else
742 addr = Config.Addrs.udp_incoming;
743
032785bf 744 vc->queue = new MemBuf;
d24ef4e9 745
032785bf 746 vc->msg = new MemBuf;
d24ef4e9 747
748 vc->fd = comm_open(SOCK_STREAM,
749 IPPROTO_TCP,
750 addr,
d24ef4e9 751 COMM_NONBLOCKING,
63a0e6b7 752 "DNS TCP Socket");
d24ef4e9 753
754 if (vc->fd < 0)
755 fatal("Could not create a DNS socket");
756
757 comm_add_close_handler(vc->fd, idnsVCClosed, vc);
758
759 vc->busy = 1;
760
cc192b50 761 commConnectStart(vc->fd, nameservers[ns].S.NtoA(buf,MAX_IPSTRLEN), nameservers[ns].S.GetPort(), idnsInitVCConnected, vc);
d24ef4e9 762}
763
764static void
765idnsSendQueryVC(idns_query * q, int ns)
766{
767 if (nameservers[ns].vc == NULL)
768 idnsInitVC(ns);
769
770 nsvc *vc = nameservers[ns].vc;
771
445962f3
HN
772 if (!vc) {
773 char buf[MAX_IPSTRLEN];
774 debugs(78, 1, "idnsSendQuery: Failed to initiate TCP connection to nameserver " << nameservers[ns].S.NtoA(buf,MAX_IPSTRLEN) << "!");
775
776 return;
777 }
778
2fe7eff9 779 vc->queue->reset();
d24ef4e9 780
781 short head = htons(q->sz);
782
2fe7eff9 783 vc->queue->append((char *)&head, 2);
d24ef4e9 784
2fe7eff9 785 vc->queue->append(q->buf, q->sz);
d24ef4e9 786
787 idnsDoSendQueryVC(vc);
788}
789
7b724b86 790static void
791idnsSendQuery(idns_query * q)
792{
4d6c8504 793 if (DnsSocketA < 0 && DnsSocketB < 0) {
cc192b50 794 debugs(78, 1, "WARNING: idnsSendQuery: Can't send query, no DNS socket!");
62e76326 795 return;
0a69973e 796 }
62e76326 797
cc192b50 798 if (nns <= 0) {
799 debugs(78, 1, "WARNING: idnsSendQuery: Can't send query, no DNS nameservers known!");
800 return;
801 }
62e76326 802
7b724b86 803 assert(q->lru.next == NULL);
62e76326 804
7b724b86 805 assert(q->lru.prev == NULL);
62e76326 806
4d6c8504
AJ
807 int x = -1, y = -1;
808 int ns;
809
213b6c90
AJ
810 do {
811 ns = q->nsends % nns;
812
813 if (q->need_vc) {
814 idnsSendQueryVC(q, ns);
4d6c8504 815 x = y = 0;
213b6c90 816 } else {
4d6c8504
AJ
817#if IPV6_SPECIAL_SPLITSTACK
818 if (nameservers[ns].S.IsIPv6() && DnsSocketB > 0)
819 y = comm_udp_sendto(DnsSocketB, nameservers[ns].S, q->buf, q->sz);
820 else
821#endif
822 x = comm_udp_sendto(DnsSocketA, nameservers[ns].S, q->buf, q->sz);
213b6c90 823 }
62e76326 824
213b6c90 825 q->nsends++;
62e76326 826
820ee9fa 827 q->queue_t = q->sent_t = current_time;
62e76326 828
4d6c8504
AJ
829#if IPV6_SPECIAL_SPLITSTACK
830 if (y < 0 && nameservers[ns].S.IsIPv6())
831 debugs(50, 1, "idnsSendQuery: FD " << DnsSocketB << ": sendto: " << xstrerror());
832 if (x < 0 && nameservers[ns].S.IsIPv4())
833#else
213b6c90 834 if (x < 0)
4d6c8504
AJ
835#endif
836 debugs(50, 1, "idnsSendQuery: FD " << DnsSocketA << ": sendto: " << xstrerror());
837
838 } while ( (x<0 && y<0) && q->nsends % nns != 0);
62e76326 839
4d6c8504
AJ
840#if IPV6_SPECIAL_SPLITSTACK
841 if (y >= 0) {
842 fd_bytes(DnsSocketB, y, FD_WRITE);
843 commSetSelect(DnsSocketB, COMM_SELECT_READ, idnsRead, NULL, 0);
844 }
845#endif
62e76326 846
26ac0430 847 if (x >= 0) {
4d6c8504
AJ
848 fd_bytes(DnsSocketA, x, FD_WRITE);
849 commSetSelect(DnsSocketA, COMM_SELECT_READ, idnsRead, NULL, 0);
0a69973e 850 }
62e76326 851
7cfc1c9a 852 nameservers[ns].nqueries++;
c131f066 853 q->queue_t = current_time;
7b724b86 854 dlinkAdd(q, &q->lru, &lru_list);
efd900cb 855 idnsTickleQueue();
7b724b86 856}
857
858static int
ad61a2b4 859idnsFromKnownNameserver(IpAddress const &from)
7b724b86 860{
861 int i;
62e76326 862
26ac0430 863 for (i = 0; i < nns; i++) {
cc192b50 864 if (nameservers[i].S != from)
62e76326 865 continue;
866
cc192b50 867 if (nameservers[i].S.GetPort() != from.GetPort())
62e76326 868 continue;
869
870 return i;
7b724b86 871 }
62e76326 872
7cfc1c9a 873 return -1;
7b724b86 874}
875
876static idns_query *
877idnsFindQuery(unsigned short id)
878{
879 dlink_node *n;
880 idns_query *q;
62e76326 881
7b724b86 882 for (n = lru_list.tail; n; n = n->prev) {
62e76326 883 q = (idns_query*)n->data;
884
885 if (q->id == id)
886 return q;
7b724b86 887 }
62e76326 888
7b724b86 889 return NULL;
890}
891
108d67a0 892static unsigned short
893idnsQueryID(void)
894{
895 unsigned short id = squid_random() & 0xFFFF;
896 unsigned short first_id = id;
897
26ac0430 898 while (idnsFindQuery(id)) {
108d67a0 899 id++;
900
846c8960 901 if (id == first_id) {
bf8fe701 902 debugs(78, 1, "idnsQueryID: Warning, too many pending DNS requests");
108d67a0 903 break;
846c8960 904 }
108d67a0 905 }
906
846c8960 907 return id;
108d67a0 908}
909
3074b9d1 910static void
911idnsCallback(idns_query *q, rfc1035_rr *answers, int n, const char *error)
912{
913 IDNSCB *callback;
914 void *cbdata;
915
916 callback = q->callback;
917 q->callback = NULL;
918
919 if (cbdataReferenceValidDone(q->callback_data, &cbdata))
920 callback(cbdata, answers, n, error);
921
26ac0430 922 while (q->queue) {
3074b9d1 923 idns_query *q2 = q->queue;
924 q->queue = q2->queue;
925 callback = q2->callback;
926 q2->callback = NULL;
927
928 if (cbdataReferenceValidDone(q2->callback_data, &cbdata))
929 callback(cbdata, answers, n, error);
930
63a0e6b7 931 cbdataFree(q2);
3074b9d1 932 }
933
934 if (q->hash.key) {
935 hash_remove_link(idns_lookup_hash, &q->hash);
936 q->hash.key = NULL;
937 }
938}
939
cc192b50 940void
941idnsDropMessage(rfc1035_message *message, idns_query *q)
942{
943 rfc1035MessageDestroy(&message);
944 if (q->hash.key) {
945 hash_remove_link(idns_lookup_hash, &q->hash);
946 q->hash.key = NULL;
947 }
948}
949
7b724b86 950static void
951idnsGrokReply(const char *buf, size_t sz)
952{
953 int n;
ec7bade0 954 rfc1035_message *message = NULL;
7b724b86 955 idns_query *q;
3074b9d1 956
cc192b50 957 n = rfc1035MessageUnpack(buf, sz, &message);
62e76326 958
ec7bade0 959 if (message == NULL) {
bf8fe701 960 debugs(78, 1, "idnsGrokReply: Malformed DNS response");
62e76326 961 return;
7b724b86 962 }
62e76326 963
cc192b50 964 debugs(78, 3, "idnsGrokReply: ID 0x" << std::hex << message->id << ", " << std::dec << n << " answers");
ec7bade0 965
966 q = idnsFindQuery(message->id);
967
7b724b86 968 if (q == NULL) {
bf8fe701 969 debugs(78, 3, "idnsGrokReply: Late response");
cc192b50 970 rfc1035MessageDestroy(&message);
62e76326 971 return;
7b724b86 972 }
62e76326 973
9e1f210d 974 if (rfc1035QueryCompare(&q->query, message->query) != 0) {
bf8fe701 975 debugs(78, 3, "idnsGrokReply: Query mismatch (" << q->query.name << " != " << message->query->name << ")");
cc192b50 976 rfc1035MessageDestroy(&message);
9e1f210d 977 return;
978 }
979
d24ef4e9 980 if (message->tc) {
bae9832d 981 debugs(78, 3, HERE << "Resolver requested TC (" << q->query.name << ")");
d24ef4e9 982 dlinkDelete(&q->lru, &lru_list);
cc192b50 983 rfc1035MessageDestroy(&message);
d24ef4e9 984
985 if (!q->need_vc) {
986 q->need_vc = 1;
987 q->nsends--;
988 idnsSendQuery(q);
989 }
990
991 return;
992 }
9e1f210d 993
a16b4aa0 994 dlinkDelete(&q->lru, &lru_list);
558be27a 995 idnsRcodeCount(n, q->attempt);
7ba8d0b8 996 q->error = NULL;
62e76326 997
558be27a 998 if (n < 0) {
bf8fe701 999 debugs(78, 3, "idnsGrokReply: error " << rfc1035_error_message << " (" << rfc1035_errno << ")");
62e76326 1000
7ba8d0b8 1001 q->error = rfc1035_error_message;
1002 q->rcode = -n;
1003
1004 if (q->rcode == 2 && ++q->attempt < MAX_ATTEMPT) {
62e76326 1005 /*
1006 * RCODE 2 is "Server failure - The name server was
1007 * unable to process this query due to a problem with
1008 * the name server."
1009 */
cc192b50 1010 debugs(78, 3, "idnsGrokReply: Query result: SERV_FAIL");
1011 rfc1035MessageDestroy(&message);
62e76326 1012 q->start_t = current_time;
108d67a0 1013 q->id = idnsQueryID();
1014 rfc1035SetQueryID(q->buf, q->id);
62e76326 1015 idnsSendQuery(q);
1016 return;
1017 }
68836b58 1018
1019 if (q->rcode == 3 && q->do_searchpath && q->attempt < MAX_ATTEMPT) {
1020 assert(NULL == message->answer);
1021 strcpy(q->name, q->orig);
1022
cc192b50 1023 debugs(78, 3, "idnsGrokReply: Query result: NXDOMAIN - " << q->name );
1024
68836b58 1025 if (q->domain < npc) {
1026 strcat(q->name, ".");
1027 strcat(q->name, searchpath[q->domain].domain);
bf8fe701 1028 debugs(78, 3, "idnsGrokReply: searchpath used for " << q->name);
68836b58 1029 q->domain++;
1030 } else {
1031 q->attempt++;
1032 }
1033
cc192b50 1034 idnsDropMessage(message, q);
1035
68836b58 1036 q->start_t = current_time;
1037 q->id = idnsQueryID();
1038 rfc1035SetQueryID(q->buf, q->id);
cc192b50 1039#if USE_IPV6
26ac0430 1040 if (q->query.qtype == RFC1035_TYPE_AAAA) {
cc192b50 1041 debugs(78, 3, "idnsGrokReply: Trying AAAA Query for " << q->name);
1042 q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
26ac0430 1043 } else
cc192b50 1044#endif
1045 {
1046 debugs(78, 3, "idnsGrokReply: Trying A Query for " << q->name);
1047 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
1048 }
68836b58 1049 idnsCacheQuery(q);
1050 idnsSendQuery(q);
1051 return;
1052 }
558be27a 1053 }
62e76326 1054
cc192b50 1055#if USE_IPV6
26ac0430 1056 if (q->need_A && (Config.onoff.dns_require_A == 1 || n <= 0 ) ) {
cc192b50 1057 /* ERROR or NO AAAA exist. Failover to A records. */
bae9832d 1058 /* Apparently its also a good idea to lookup and store the A records
cc192b50 1059 * just in case the AAAA are not available when we need them.
1060 * This could occur due to number of network failings beyond our control
1061 * thus the || above allowing the user to request always both.
1062 */
1063
26ac0430 1064 if (n == 0)
cc192b50 1065 debugs(78, 3, "idnsGrokReply: " << q->name << " has no AAAA records. Looking up A record instead.");
26ac0430 1066 else if (q->need_A && n <= 0)
cc192b50 1067 debugs(78, 3, "idnsGrokReply: " << q->name << " AAAA query failed. Trying A now instead.");
1068 else // admin requested this.
1069 debugs(78, 3, "idnsGrokReply: " << q->name << " AAAA query done. Configured to retrieve A now also.");
1070
bae9832d 1071 // move the initial message results into the failover query for merging later.
26ac0430 1072 if (n > 0) {
bae9832d 1073 q->initial_AAAA.count = message->ancount;
1074 q->initial_AAAA.answers = message->answer;
1075 message->answer = NULL;
1076 }
1077
1078 // remove the hashed query info
cc192b50 1079 idnsDropMessage(message, q);
1080
1081 q->start_t = current_time;
1082 q->id = idnsQueryID();
1083 rfc1035SetQueryID(q->buf, q->id);
1084 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
1085 q->need_A = false;
1086 idnsCacheQuery(q);
1087 idnsSendQuery(q);
1088 return;
1089 }
1090#endif
1091
26ac0430
AJ
1092 /** If there are two result sets from preceeding AAAA and A lookups merge them with a preference for AAAA */
1093 if (q->initial_AAAA.count > 0 && n > 0) {
bae9832d 1094 /* two sets of RR need merging */
1095 rfc1035_rr *result = (rfc1035_rr*) xmalloc( sizeof(rfc1035_rr)*(n + q->initial_AAAA.count) );
1096 rfc1035_rr *tmp = result;
1097
1098 debugs(78, 6, HERE << "Merging DNS results " << q->name << " AAAA has " << q->initial_AAAA.count << " RR, A has " << n << " RR");
1099
1100 memcpy(tmp, q->initial_AAAA.answers, (sizeof(rfc1035_rr)*(q->initial_AAAA.count)) );
1101 tmp += q->initial_AAAA.count;
1102 /* free the RR object without freeing its child strings (they are now taken by the copy above) */
1103 safe_free(q->initial_AAAA.answers);
1104
1105 memcpy( tmp, message->answer, (sizeof(rfc1035_rr)*n) );
1106 /* free the RR object without freeing its child strings (they are now taken by the copy above) */
1107 safe_free(message->answer);
1108
1109 message->answer = result;
1110 n += q->initial_AAAA.count;
1111 q->initial_AAAA.count=0;
26ac0430 1112 } else if (q->initial_AAAA.count > 0 && n <= 0) {
bae9832d 1113 /* initial of dual queries was the only result set. */
1114 debugs(78, 6, HERE << "Merging DNS results " << q->name << " AAAA has " << q->initial_AAAA.count << " RR, A has " << n << " RR");
1115 rfc1035RRDestroy(&(message->answer), n);
1116 message->answer = q->initial_AAAA.answers;
1117 n = q->initial_AAAA.count;
1118 }
1119 /* else initial results were empty. just use the final set as authoritative */
1120
1121 debugs(78, 6, HERE << "Sending " << n << " DNS results to caller.");
ec7bade0 1122 idnsCallback(q, message->answer, n, q->error);
cc192b50 1123 rfc1035MessageDestroy(&message);
63a0e6b7 1124 cbdataFree(q);
7b724b86 1125}
1126
1127static void
1128idnsRead(int fd, void *data)
1129{
d193a436 1130 int *N = &incoming_sockets_accepted;
0b6d1955 1131 int len;
42b51993 1132 int max = INCOMING_DNS_MAX;
d29b40de 1133 static char rbuf[SQUID_UDP_SO_RCVBUF];
7cfc1c9a 1134 int ns;
ad61a2b4 1135 IpAddress from;
cc192b50 1136
d7e7eaa7 1137 debugs(78, 3, "idnsRead: starting with FD " << fd);
cc192b50 1138
1139 /* BUG (UNRESOLVED)
1140 * two code lines after returning from comm_udprecvfrom()
1141 * something overwrites the memory behind the from parameter.
1142 * NO matter where in the stack declaration list above it is placed
1143 * The cause of this is still unknown, however copying the data appears
1144 * to allow it to be passed further without this erasure.
1145 */
ad61a2b4 1146 IpAddress bugbypass;
62e76326 1147
7b724b86 1148 while (max--) {
cc192b50 1149 len = comm_udp_recvfrom(fd, rbuf, SQUID_UDP_SO_RCVBUF, 0, bugbypass);
62e76326 1150
cc192b50 1151 from = bugbypass; // BUG BYPASS. see notes above.
62e76326 1152
1153 if (len == 0)
1154 break;
1155
1156 if (len < 0) {
1157 if (ignoreErrno(errno))
1158 break;
1159
7b724b86 1160#ifdef _SQUID_LINUX_
62e76326 1161 /* Some Linux systems seem to set the FD for reading and then
1162 * return ECONNREFUSED when sendto() fails and generates an ICMP
1163 * port unreachable message. */
1164 /* or maybe an EHOSTUNREACH "No route to host" message */
1165 if (errno != ECONNREFUSED && errno != EHOSTUNREACH)
7b724b86 1166#endif
62e76326 1167
bf8fe701 1168 debugs(50, 1, "idnsRead: FD " << fd << " recvfrom: " << xstrerror());
62e76326 1169
1170 break;
1171 }
1172
4d6c8504
AJ
1173#if IPV6_SPECIAL_SPLITSTACK
1174 if ( from.IsIPv6() )
1175 fd_bytes(DnsSocketB, len, FD_READ);
1176 else
1177#endif
1178 fd_bytes(DnsSocketA, len, FD_READ);
1179
62e76326 1180 assert(N);
1181 (*N)++;
cc192b50 1182
1183 debugs(78, 3, "idnsRead: FD " << fd << ": received " << len << " bytes from " << from);
1184
1185 /* BUG: see above. Its here that it becomes apparent that the content of bugbypass is gone. */
1186 ns = idnsFromKnownNameserver(from);
62e76326 1187
1188 if (ns >= 0) {
1189 nameservers[ns].nreplies++;
1190 } else if (Config.onoff.ignore_unknown_nameservers) {
1191 static time_t last_warning = 0;
1192
1193 if (squid_curtime - last_warning > 60) {
cc192b50 1194 debugs(78, 1, "WARNING: Reply from unknown nameserver " << from);
62e76326 1195 last_warning = squid_curtime;
26ac0430 1196 } else {
cc192b50 1197 debugs(78, 1, "WARNING: Reply from unknown nameserver " << from << " (retrying..." << (squid_curtime-last_warning) << "<=60)" );
26ac0430
AJ
1198 }
1199 continue;
62e76326 1200 }
1201
62e76326 1202 idnsGrokReply(rbuf, len);
7b724b86 1203 }
62e76326 1204
4d6c8504
AJ
1205 if (lru_list.head) {
1206#if IPV6_SPECIAL_SPLITSTACK
1207 if ( from.IsIPv6() )
1208 commSetSelect(DnsSocketB, COMM_SELECT_READ, idnsRead, NULL, 0);
1209 else
1210#endif
1211 commSetSelect(DnsSocketA, COMM_SELECT_READ, idnsRead, NULL, 0);
1212 }
7b724b86 1213}
1214
7cfc1c9a 1215static void
1216idnsCheckQueue(void *unused)
1217{
1218 dlink_node *n;
0c4fe9e4 1219 dlink_node *p = NULL;
7cfc1c9a 1220 idns_query *q;
1221 event_queued = 0;
62e76326 1222
820ee9fa
AJ
1223 if (0 == nns)
1224 /* name servers went away; reconfiguring or shutting down */
1225 return;
1226
0c4fe9e4 1227 for (n = lru_list.tail; n; n = p) {
62e76326 1228
820ee9fa 1229 p = n->prev;
733cedc0 1230 q = static_cast<idns_query*>(n->data);
62e76326 1231
820ee9fa
AJ
1232 /* Anything to process in the queue? */
1233 if (tvSubDsec(q->queue_t, current_time) < Config.Timeout.idns_retransmit )
26ac0430 1234 break;
62e76326 1235
820ee9fa
AJ
1236 /* Query timer expired? */
1237 if (tvSubDsec(q->sent_t, current_time) < Config.Timeout.idns_retransmit * 1 << ((q->nsends - 1) / nns)) {
1238 dlinkDelete(&q->lru, &lru_list);
1239 q->queue_t = current_time;
1240 dlinkAdd(q, &q->lru, &lru_list);
1241 continue;
1242 }
62e76326 1243
820ee9fa 1244 debugs(78, 3, "idnsCheckQueue: ID 0x" << std::hex << std::setfill('0') << std::setw(4) << q->id << "timeout" );
62e76326 1245
1246 dlinkDelete(&q->lru, &lru_list);
1247
1248 if (tvSubDsec(q->start_t, current_time) < Config.Timeout.idns_query) {
1249 idnsSendQuery(q);
1250 } else {
26ac0430
AJ
1251 debugs(78, 2, "idnsCheckQueue: ID " << std::hex << q->id <<
1252 ": giving up after " << std::dec << q->nsends << " tries and " <<
1253 std::setw(5)<< std::setprecision(2) << tvSubDsec(q->start_t, current_time) << " seconds");
3074b9d1 1254
1255 if (q->rcode != 0)
1256 idnsCallback(q, NULL, -q->rcode, q->error);
1257 else
1258 idnsCallback(q, NULL, -16, "Timeout");
62e76326 1259
63a0e6b7 1260 cbdataFree(q);
62e76326 1261 }
7cfc1c9a 1262 }
62e76326 1263
efd900cb 1264 idnsTickleQueue();
7cfc1c9a 1265}
1266
d24ef4e9 1267static void
1268idnsReadVC(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
032785bf 1280 vc->msg->size += len; // XXX should not access -> size directly
d24ef4e9 1281
032785bf 1282 if (vc->msg->contentSize() < vc->msglen) {
1283 comm_read(fd, buf + len, vc->msglen - vc->msg->contentSize(), idnsReadVC, vc);
d24ef4e9 1284 return;
1285 }
1286
bf8fe701 1287 debugs(78, 3, "idnsReadVC: FD " << fd << ": received " <<
1288 (int) vc->msg->contentSize() << " bytes via tcp from " <<
cc192b50 1289 nameservers[vc->ns].S << ".");
d24ef4e9 1290
032785bf 1291 idnsGrokReply(vc->msg->buf, vc->msg->contentSize());
2fe7eff9 1292 vc->msg->clean();
d24ef4e9 1293 comm_read(fd, (char *)&vc->msglen, 2 , idnsReadVCHeader, vc);
1294}
1295
1296static void
1297idnsReadVCHeader(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
1298{
1299 nsvc * vc = (nsvc *)data;
1300
1301 if (flag == COMM_ERR_CLOSING)
1302 return;
1303
1304 if (flag != COMM_OK || len <= 0) {
1305 comm_close(fd);
1306 return;
1307 }
1308
1309 vc->read_msglen += len;
1310
1311 assert(vc->read_msglen <= 2);
1312
1313 if (vc->read_msglen < 2) {
1314 comm_read(fd, buf + len, 2 - vc->read_msglen, idnsReadVCHeader, vc);
1315 return;
1316 }
1317
1318 vc->read_msglen = 0;
1319
1320 vc->msglen = ntohs(vc->msglen);
1321
2fe7eff9 1322 vc->msg->init(vc->msglen, vc->msglen);
032785bf 1323 comm_read(fd, vc->msg->buf, vc->msglen, idnsReadVC, vc);
d24ef4e9 1324}
1325
558be27a 1326/*
1327 * rcode < 0 indicates an error, rocde >= 0 indicates success
1328 */
1329static void
1330idnsRcodeCount(int rcode, int attempt)
1331{
1332 if (rcode > 0)
62e76326 1333 rcode = 0;
558be27a 1334 else if (rcode < 0)
62e76326 1335 rcode = -rcode;
1336
558be27a 1337 if (rcode < MAX_RCODE)
62e76326 1338 if (attempt < MAX_ATTEMPT)
1339 RcodeMatrix[rcode][attempt]++;
558be27a 1340}
1341
7b724b86 1342/* ====================================================================== */
1343
5f5e883f
FC
1344static void
1345idnsRegisterWithCacheManager(void)
1346{
1347 CacheManager::GetInstance()->
26ac0430 1348 registerAction("idns", "Internal DNS Statistics", idnsStats, 0, 1);
5f5e883f
FC
1349}
1350
7b724b86 1351void
1352idnsInit(void)
1353{
1354 static int init = 0;
62e76326 1355
d24ef4e9 1356 CBDATA_INIT_TYPE(nsvc);
63a0e6b7 1357 CBDATA_INIT_TYPE(idns_query);
d24ef4e9 1358
4d6c8504 1359 if (DnsSocketA < 0 && DnsSocketB < 0) {
62e76326 1360 int port;
1361
31be869c 1362 IpAddress addr; // since we don't want to alter Config.Addrs.udp_* and dont have one of our own.
62e76326 1363
cc192b50 1364 if (!Config.Addrs.udp_outgoing.IsNoAddr())
62e76326 1365 addr = Config.Addrs.udp_outgoing;
1366 else
1367 addr = Config.Addrs.udp_incoming;
1368
4d6c8504 1369#if IPV6_SPECIAL_SPLITSTACK
7e07ced1
AJ
1370 IpAddress addr4 = addr;
1371
1372 if ( addr.IsAnyAddr() || addr.IsIPv6() ) {
1373 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addr);
4d6c8504 1374 DnsSocketB = comm_open_listener(SOCK_DGRAM,
04f7fd38
AJ
1375 IPPROTO_UDP,
1376 addr,
1377 COMM_NONBLOCKING,
1378 "DNS Socket v6");
7e07ced1 1379 }
4d6c8504 1380
7e07ced1
AJ
1381 if ( addr.IsAnyAddr() || addr.IsIPv4() ) {
1382 addr4.SetIPv4();
1383 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addr4);
4d6c8504 1384 DnsSocketA = comm_open_listener(SOCK_DGRAM,
04f7fd38 1385 IPPROTO_UDP,
7e07ced1 1386 addr4,
04f7fd38
AJ
1387 COMM_NONBLOCKING,
1388 "DNS Socket v4");
7e07ced1 1389 }
4d6c8504 1390#else
7e07ced1 1391 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addr);
04f7fd38
AJ
1392 DnsSocketA = comm_open_listener(SOCK_DGRAM,
1393 IPPROTO_UDP,
1394 addr,
1395 COMM_NONBLOCKING,
1396 "DNS Socket");
4d6c8504 1397#endif
62e76326 1398
4d6c8504 1399 if (DnsSocketA < 0 && DnsSocketB < 0)
62e76326 1400 fatal("Could not create a DNS socket");
1401
1402 /* Ouch... we can't call functions using debug from a debug
1403 * statement. Doing so messes up the internal Debug::level
1404 */
4d6c8504 1405#if IPV6_SPECIAL_SPLITSTACK
04f7fd38 1406 if (DnsSocketB >= 0) {
4d6c8504
AJ
1407 port = comm_local_port(DnsSocketB);
1408 debugs(78, 1, "DNS Socket created at " << addr << ", FD " << DnsSocketB);
1409 }
1410#endif
04f7fd38 1411 if (DnsSocketA >= 0) {
4d6c8504 1412 port = comm_local_port(DnsSocketA);
84cb2e79 1413#if IPV6_SPECIAL_SPLITSTACK
7e07ced1 1414 debugs(78, 1, "DNS Socket created at " << addr4 << ", FD " << DnsSocketA);
84cb2e79
AJ
1415#else
1416 debugs(78, 1, "DNS Socket created at " << addr << ", FD " << DnsSocketA);
1417#endif
4d6c8504 1418 }
7b724b86 1419 }
62e76326 1420
efd900cb 1421 assert(0 == nns);
1422 idnsParseNameservers();
0e6d05ef 1423#ifndef _SQUID_MSWIN_
62e76326 1424
efd900cb 1425 if (0 == nns)
62e76326 1426 idnsParseResolvConf();
1427
0e6d05ef 1428#endif
ec4daaa5 1429#ifdef _SQUID_WIN32_
62e76326 1430
0e6d05ef 1431 if (0 == nns)
62e76326 1432 idnsParseWIN32Registry();
1433
0e6d05ef 1434#endif
62e76326 1435
29fe9bd5 1436 if (0 == nns) {
1437 debugs(78, 1, "Warning: Could not find any nameservers. Trying to use localhost");
ec4daaa5 1438#ifdef _SQUID_WIN32_
29fe9bd5 1439
1440 debugs(78, 1, "Please check your TCP-IP settings or /etc/resolv.conf file");
0e6d05ef 1441#else
29fe9bd5 1442
1443 debugs(78, 1, "Please check your /etc/resolv.conf file");
0e6d05ef 1444#endif
29fe9bd5 1445
1446 debugs(78, 1, "or use the 'dns_nameservers' option in squid.conf.");
1447 idnsAddNameserver("127.0.0.1");
1448 }
62e76326 1449
7b724b86 1450 if (!init) {
62e76326 1451 memDataInit(MEM_IDNS_QUERY, "idns_query", sizeof(idns_query), 0);
62e76326 1452 memset(RcodeMatrix, '\0', sizeof(RcodeMatrix));
30abd221 1453 idns_lookup_hash = hash_create((HASHCMP *) strcmp, 103, hash_string);
62e76326 1454 init++;
7b724b86 1455 }
372fb44e
FC
1456
1457 idnsRegisterWithCacheManager();
7b724b86 1458}
1459
1460void
1461idnsShutdown(void)
1462{
4d6c8504 1463 if (DnsSocketA < 0 && DnsSocketB < 0)
62e76326 1464 return;
1465
04f7fd38 1466 if (DnsSocketA >= 0 ) {
4d6c8504
AJ
1467 comm_close(DnsSocketA);
1468 DnsSocketA = -1;
1469 }
62e76326 1470
4d6c8504 1471#if IPV6_SPECIAL_SPLITSTACK
04f7fd38 1472 if (DnsSocketA >= 0 ) {
4d6c8504
AJ
1473 comm_close(DnsSocketB);
1474 DnsSocketB = -1;
1475 }
1476#endif
62e76326 1477
b26754c4 1478 for (int i = 0; i < nns; i++) {
1479 if (nsvc *vc = nameservers[i].vc) {
1480 if (vc->fd >= 0)
1481 comm_close(vc->fd);
1482 }
1483 }
1484
efd900cb 1485 idnsFreeNameservers();
68836b58 1486 idnsFreeSearchpath();
7b724b86 1487}
1488
3074b9d1 1489static int
1490idnsCachedLookup(const char *key, IDNSCB * callback, void *data)
1491{
1492 idns_query *q;
1493
1494 idns_query *old = (idns_query *) hash_lookup(idns_lookup_hash, key);
1495
1496 if (!old)
1497 return 0;
1498
63a0e6b7 1499 q = cbdataAlloc(idns_query);
3074b9d1 1500
1501 q->callback = callback;
1502
1503 q->callback_data = cbdataReference(data);
1504
1505 q->queue = old->queue;
1506
1507 old->queue = q;
1508
1509 return 1;
1510}
1511
1512static void
9e1f210d 1513idnsCacheQuery(idns_query *q)
3074b9d1 1514{
9e1f210d 1515 q->hash.key = q->query.name;
3074b9d1 1516 hash_join(idns_lookup_hash, &q->hash);
1517}
1518
7b724b86 1519void
1520idnsALookup(const char *name, IDNSCB * callback, void *data)
1521{
68836b58 1522 unsigned int i;
1523 int nd = 0;
3074b9d1 1524 idns_query *q;
1525
1526 if (idnsCachedLookup(name, callback, data))
1527 return;
1528
63a0e6b7 1529 q = cbdataAlloc(idns_query);
3074b9d1 1530
71952967 1531 q->id = idnsQueryID();
1532
68836b58 1533 for (i = 0; i < strlen(name); i++)
1534 if (name[i] == '.')
1535 nd++;
1536
1537 if (Config.onoff.res_defnames && npc > 0 && name[strlen(name)-1] != '.') {
1538 q->do_searchpath = 1;
1539 } else {
1540 q->do_searchpath = 0;
1541 }
1542
1543 strcpy(q->orig, name);
1544 strcpy(q->name, q->orig);
1545
1546 if (q->do_searchpath && nd < ndots) {
1547 q->domain = 0;
1548 strcat(q->name, ".");
1549 strcat(q->name, searchpath[q->domain].domain);
bf8fe701 1550 debugs(78, 3, "idnsALookup: searchpath used for " << q->name);
68836b58 1551 }
1552
cc192b50 1553#if USE_IPV6
26ac0430 1554 q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
cc192b50 1555 q->need_A = true;
1556#else
1557 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->id, &q->query);
1558 q->need_A = false;
1559#endif
62e76326 1560
2041330b 1561 if (q->sz < 0) {
1562 /* problem with query data -- query not sent */
1563 callback(data, NULL, 0, "Internal error");
63a0e6b7 1564 cbdataFree(q);
2041330b 1565 return;
1566 }
1567
26ac0430 1568 debugs(78, 3, "idnsALookup: buf is " << q->sz << " bytes for " << q->name <<
bf8fe701 1569 ", id = 0x" << std::hex << q->id);
aa6c61e3 1570
7b724b86 1571 q->callback = callback;
aa6c61e3 1572
fa80a8ef 1573 q->callback_data = cbdataReference(data);
aa6c61e3 1574
7cfc1c9a 1575 q->start_t = current_time;
aa6c61e3 1576
9e1f210d 1577 idnsCacheQuery(q);
aa6c61e3 1578
7b724b86 1579 idnsSendQuery(q);
1580}
8db71107 1581
1582void
ad61a2b4 1583idnsPTRLookup(const IpAddress &addr, IDNSCB * callback, void *data)
8db71107 1584{
3074b9d1 1585 idns_query *q;
1586
cc192b50 1587 char ip[MAX_IPSTRLEN];
1588
1589 addr.NtoA(ip,MAX_IPSTRLEN);
3074b9d1 1590
63a0e6b7 1591 q = cbdataAlloc(idns_query);
3074b9d1 1592
71952967 1593 q->id = idnsQueryID();
1594
cc192b50 1595#if USE_IPV6
26ac0430 1596 if ( addr.IsIPv6() ) {
cc192b50 1597 struct in6_addr addr6;
1598 addr.GetInAddr(addr6);
1599 q->sz = rfc3596BuildPTRQuery6(addr6, q->buf, sizeof(q->buf), q->id, &q->query);
26ac0430 1600 } else
cc192b50 1601#endif
1602 {
1603 struct in_addr addr4;
1604 addr.GetInAddr(addr4);
1605 q->sz = rfc3596BuildPTRQuery4(addr4, q->buf, sizeof(q->buf), q->id, &q->query);
1606 }
1607
1608 /* PTR does not do inbound A/AAAA */
1609 q->need_A = false;
3074b9d1 1610
26ac0430 1611 if (q->sz < 0) {
2041330b 1612 /* problem with query data -- query not sent */
1613 callback(data, NULL, 0, "Internal error");
63a0e6b7 1614 cbdataFree(q);
2041330b 1615 return;
1616 }
1617
37f3da43 1618 if (idnsCachedLookup(q->query.name, callback, data)) {
26ac0430
AJ
1619 cbdataFree(q);
1620 return;
37f3da43 1621 }
1622
26ac0430 1623 debugs(78, 3, "idnsPTRLookup: buf is " << q->sz << " bytes for " << ip <<
bf8fe701 1624 ", id = 0x" << std::hex << q->id);
3074b9d1 1625
8db71107 1626 q->callback = callback;
3074b9d1 1627
fa80a8ef 1628 q->callback_data = cbdataReference(data);
3074b9d1 1629
8db71107 1630 q->start_t = current_time;
3074b9d1 1631
9e1f210d 1632 idnsCacheQuery(q);
3074b9d1 1633
8db71107 1634 idnsSendQuery(q);
1635}
eb824054 1636
59ad6d31 1637#if SQUID_SNMP
3c573763 1638/*
1639 * The function to return the DNS via SNMP
1640 */
1641variable_list *
1642snmp_netIdnsFn(variable_list * Var, snint * ErrP)
1643{
1644 int i, n = 0;
1645 variable_list *Answer = NULL;
6a644e75
AJ
1646 MemBuf tmp;
1647 debugs(49, 5, "snmp_netDnsFn: Processing request: " << snmpDebugOid(Var->name, Var->name_length, tmp));
3c573763 1648 *ErrP = SNMP_ERR_NOERROR;
62e76326 1649
3c573763 1650 switch (Var->name[LEN_SQ_NET + 1]) {
62e76326 1651
3c573763 1652 case DNS_REQ:
62e76326 1653
1654 for (i = 0; i < nns; i++)
1655 n += nameservers[i].nqueries;
1656
1657 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1658 n,
1659 SMI_COUNTER32);
1660
1661 break;
1662
3c573763 1663 case DNS_REP:
62e76326 1664 for (i = 0; i < nns; i++)
1665 n += nameservers[i].nreplies;
1666
1667 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1668 n,
1669 SMI_COUNTER32);
1670
1671 break;
1672
3c573763 1673 case DNS_SERVERS:
62e76326 1674 Answer = snmp_var_new_integer(Var->name, Var->name_length,
e494f5e9 1675 nns,
62e76326 1676 SMI_COUNTER32);
1677
1678 break;
1679
3c573763 1680 default:
62e76326 1681 *ErrP = SNMP_ERR_NOSUCHNAME;
1682
1683 break;
3c573763 1684 }
62e76326 1685
3c573763 1686 return Answer;
1687}
62e76326 1688
3c573763 1689#endif /*SQUID_SNMP */
1f1ae50a 1690#endif /* USE_DNSSERVERS */