]> git.ipfire.org Git - thirdparty/squid.git/blame - src/dns_internal.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / dns_internal.cc
CommitLineData
7b724b86 1/*
262a0e14 2 * $Id$
7b724b86 3 *
4 * DEBUG: section 78 DNS lookups; interacts with lib/rfc1035.c
5 * AUTHOR: Duane Wessels
6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
7b724b86 8 * ----------------------------------------------------------
9 *
2b6662ba 10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
7b724b86 18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
26ac0430 23 *
7b724b86 24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
26ac0430 28 *
7b724b86 29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34
582c2af2
FC
35#include "squid.h"
36#include "base/InstanceId.h"
55cbb02b 37#include "comm/Connection.h"
aed188fd 38#include "comm/ConnOpener.h"
063dc1eb 39#include "comm.h"
d841c88d
AJ
40#include "comm/Loops.h"
41#include "comm/Write.h"
cfd66529 42#include "event.h"
2c471ba8 43#include "fde.h"
055421ee 44#include "ip/tools.h"
0eb49b6d 45#include "MemBuf.h"
8822ebee 46#include "mgr/Registration.h"
582c2af2
FC
47#include "protos.h"
48#include "rfc3596.h"
49#include "SquidTime.h"
50#include "Store.h"
7176768b 51#include "util.h"
d295d770 52#include "wordlist.h"
68836b58 53
54#if HAVE_ARPA_NAMESER_H
55#include <arpa/nameser.h>
56#endif
57#if HAVE_RESOLV_H
58#include <resolv.h>
59#endif
21d845b1
FC
60#if HAVE_ERRNO_H
61#include <errno.h>
62#endif
68836b58 63
454e8283 64/* MS Visual Studio Projects are monolithic, so we need the following
1f1ae50a 65 #ifndef to exclude the internal DNS code from compile process when
66 using external DNS process.
67 */
f64091a7 68#if !USE_DNSHELPER
be266cb2 69#if _SQUID_WINDOWS_
1a774556 70#include "squid_windows.h"
0dde2160
GS
71#define REG_TCPIP_PARA_INTERFACES "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"
72#define REG_TCPIP_PARA "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
73#define REG_VXD_MSTCP "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP"
0e6d05ef 74#endif
68836b58 75#ifndef _PATH_RESCONF
76#define _PATH_RESCONF "/etc/resolv.conf"
77#endif
78#ifndef NS_DEFAULTPORT
79#define NS_DEFAULTPORT 53
80#endif
81
82#ifndef NS_MAXDNAME
83#define NS_MAXDNAME 1025
84#endif
85
86#ifndef MAXDNSRCH
87#define MAXDNSRCH 6
7b724b86 88#endif
68836b58 89
90/* The buffer size required to store the maximum allowed search path */
91#ifndef RESOLV_BUFSZ
92#define RESOLV_BUFSZ NS_MAXDNAME * MAXDNSRCH + sizeof("search ") + 1
7b724b86 93#endif
94
42b51993 95#define IDNS_MAX_TRIES 20
e210930b 96#define MAX_RCODE 17
558be27a 97#define MAX_ATTEMPT 3
98static int RcodeMatrix[MAX_RCODE][MAX_ATTEMPT];
e210930b
AJ
99// NP: see http://www.iana.org/assignments/dns-parameters
100static const char *Rcodes[] = {
24520b1b
AJ
101 /* RFC 1035 */
102 "Success",
103 "Packet Format Error",
104 "DNS Server Failure",
105 "Non-Existent Domain",
106 "Not Implemented",
107 "Query Refused",
108 /* RFC 2136 */
109 "Name Exists when it should not",
110 "RR Set Exists when it should not",
111 "RR Set that should exist does not",
112 "Server Not Authoritative for zone",
113 "Name not contained in zone",
114 /* unassigned */
115 "","","","","",
116 /* RFC 2671 */
117 "Bad OPT Version or TSIG Signature Failure"
118};
558be27a 119
58a39dc9 120typedef struct _idns_query idns_query;
62e76326 121
7b724b86 122typedef struct _ns ns;
58a39dc9 123
68836b58 124typedef struct _sp sp;
125
d24ef4e9 126typedef struct _nsvc nsvc;
127
26ac0430 128struct _idns_query {
3074b9d1 129 hash_link hash;
9e1f210d 130 rfc1035_query query;
68836b58 131 char buf[RESOLV_BUFSZ];
132 char name[NS_MAXDNAME + 1];
133 char orig[NS_MAXDNAME + 1];
c4a7fa29 134 ssize_t sz;
049441d9 135 unsigned short query_id; /// random query ID sent to server; changes with every query sent
13b08e1c 136 InstanceId<idns_query> xact_id; /// identifies our "transaction", stays constant when query is retried
91dcbd43 137
58a39dc9 138 int nsends;
d24ef4e9 139 int need_vc;
049441d9 140 int pending;
62e76326 141
58a39dc9 142 struct timeval start_t;
143 struct timeval sent_t;
820ee9fa 144 struct timeval queue_t;
58a39dc9 145 dlink_node lru;
146 IDNSCB *callback;
147 void *callback_data;
558be27a 148 int attempt;
7ba8d0b8 149 int rcode;
3074b9d1 150 idns_query *queue;
0e6432d0
HN
151 idns_query *slave; // single linked list
152 idns_query *master; // single pointer to a shared master
68836b58 153 unsigned short domain;
154 unsigned short do_searchpath;
049441d9
HN
155 rfc1035_message *message;
156 int ancount;
33ab4aaf 157 const char *error;
58a39dc9 158};
91dcbd43 159InstanceIdDefinitions(idns_query, "dns");
58a39dc9 160
26ac0430 161struct _nsvc {
d24ef4e9 162 int ns;
00406b24 163 Comm::ConnectionPointer conn;
d24ef4e9 164 unsigned short msglen;
165 int read_msglen;
032785bf 166 MemBuf *msg;
167 MemBuf *queue;
d24ef4e9 168 bool busy;
169};
170
26ac0430 171struct _ns {
b7ac5457 172 Ip::Address S;
7b724b86 173 int nqueries;
174 int nreplies;
e210930b
AJ
175#if WHEN_EDNS_RESPONSES_ARE_PARSED
176 int last_seen_edns;
177#endif
d24ef4e9 178 nsvc *vc;
7b724b86 179};
58a39dc9 180
26ac0430 181struct _sp {
68836b58 182 char domain[NS_MAXDNAME];
183 int queries;
184};
185
d24ef4e9 186CBDATA_TYPE(nsvc);
63a0e6b7 187CBDATA_TYPE(idns_query);
d24ef4e9 188
7b724b86 189static ns *nameservers = NULL;
68836b58 190static sp *searchpath = NULL;
7b724b86 191static int nns = 0;
192static int nns_alloc = 0;
68836b58 193static int npc = 0;
194static int npc_alloc = 0;
195static int ndots = 1;
7b724b86 196static dlink_list lru_list;
7cfc1c9a 197static int event_queued = 0;
3074b9d1 198static hash_table *idns_lookup_hash = NULL;
7b724b86 199
e210930b
AJ
200/*
201 * Notes on EDNS:
202 *
203 * IPv4:
204 * EDNS as specified may be sent as an additional record for any request.
205 * early testing has revealed that it works on common devices, but cannot
206 * be reliably used on any A or PTR requet done for IPv4 addresses.
207 *
208 * As such the IPv4 packets are still hard-coded not to contain EDNS (0)
209 *
210 * Squid design:
211 * Squid is optimized to generate one packet and re-send it to all NS
212 * due to this we cannot customize the EDNS size per NS.
213 *
214 * As such we take the configuration option value as fixed.
215 *
216 * FUTURE TODO:
217 * This may not be worth doing, but if/when additional-records are parsed
218 * we will be able to recover the OPT value specific to any one NS and
219 * cache it. Effectively automating the tuning of EDNS advertised to the
220 * size our active NS are capable.
221 * Default would need to start with 512 bytes RFC1035 says every NS must accept.
222 * Responses from the configured NS may cause this to be raised or turned off.
223 */
224#if WHEN_EDNS_RESPONSES_ARE_PARSED
225static int max_shared_edns = RFC1035_DEFAULT_PACKET_SZ;
226#endif
227
7b724b86 228static OBJH idnsStats;
229static void idnsAddNameserver(const char *buf);
68836b58 230static void idnsAddPathComponent(const char *buf);
7b724b86 231static void idnsFreeNameservers(void);
68836b58 232static void idnsFreeSearchpath(void);
efd900cb 233static void idnsParseNameservers(void);
1191b93b 234#if !_SQUID_MSWIN_
7b724b86 235static void idnsParseResolvConf(void);
1f1ae50a 236#endif
be266cb2 237#if _SQUID_WINDOWS_
0e6d05ef 238static void idnsParseWIN32Registry(void);
d02b72b4 239static void idnsParseWIN32SearchList(const char *);
0e6d05ef 240#endif
33ab4aaf 241static void idnsStartQuery(idns_query * q, IDNSCB * callback, void *data);
7b724b86 242static void idnsSendQuery(idns_query * q);
d24ef4e9 243static IOCB idnsReadVCHeader;
e0d28505 244static void idnsDoSendQueryVC(nsvc *vc);
cfd66529 245static CNCB idnsInitVCConnected;
e0d28505
AJ
246static IOCB idnsReadVC;
247static IOCB idnsSentQueryVC;
62e76326 248
b7ac5457 249static int idnsFromKnownNameserver(Ip::Address const &from);
7b724b86 250static idns_query *idnsFindQuery(unsigned short id);
e210930b 251static void idnsGrokReply(const char *buf, size_t sz, int from_ns);
7b724b86 252static PF idnsRead;
7cfc1c9a 253static EVH idnsCheckQueue;
efd900cb 254static void idnsTickleQueue(void);
558be27a 255static void idnsRcodeCount(int, int);
575d05c4 256static CLCB idnsVCClosed;
91dcbd43 257static unsigned short idnsQueryID(void);
049441d9 258static void idnsSendSlaveAAAAQuery(idns_query *q);
7b724b86 259
260static void
261idnsAddNameserver(const char *buf)
262{
b7ac5457 263 Ip::Address A;
62e76326 264
cc192b50 265 if (!(A = buf)) {
fa84c01d 266 debugs(78, DBG_CRITICAL, "WARNING: rejecting '" << buf << "' as a name server, because it is not a numeric IP address");
62e76326 267 return;
d20b1cd0 268 }
62e76326 269
cc192b50 270 if (A.IsAnyAddr()) {
fa84c01d 271 debugs(78, DBG_CRITICAL, "WARNING: Squid does not accept " << A << " in DNS server specifications.");
715b5def 272 A.SetLocalhost();
fa84c01d 273 debugs(78, DBG_CRITICAL, "Will be using " << A << " instead, assuming you meant that DNS is running on the same machine");
5c5a349d 274 }
62e76326 275
715b5def
AJ
276 if (!Ip::EnableIpv6 && !A.SetIPv4()) {
277 debugs(78, DBG_IMPORTANT, "WARNING: IPv6 is disabled. Discarding " << A << " in DNS server specifications.");
278 return;
5c5a349d 279 }
62e76326 280
7b724b86 281 if (nns == nns_alloc) {
62e76326 282 int oldalloc = nns_alloc;
283 ns *oldptr = nameservers;
284
285 if (nns_alloc == 0)
286 nns_alloc = 2;
287 else
288 nns_alloc <<= 1;
289
290 nameservers = (ns *)xcalloc(nns_alloc, sizeof(*nameservers));
291
292 if (oldptr && oldalloc)
41d00cd3 293 memcpy(nameservers, oldptr, oldalloc * sizeof(*nameservers));
62e76326 294
295 if (oldptr)
296 safe_free(oldptr);
7b724b86 297 }
62e76326 298
7b724b86 299 assert(nns < nns_alloc);
cc192b50 300 A.SetPort(NS_DEFAULTPORT);
301 nameservers[nns].S = A;
e210930b
AJ
302#if WHEN_EDNS_RESPONSES_ARE_PARSED
303 nameservers[nns].last_seen_edns = RFC1035_DEFAULT_PACKET_SZ;
304 // TODO generate a test packet to probe this NS from EDNS size and ability.
305#endif
cc192b50 306 debugs(78, 3, "idnsAddNameserver: Added nameserver #" << nns << " (" << A << ")");
95dc7ff4 307 ++nns;
7b724b86 308}
309
68836b58 310static void
311idnsAddPathComponent(const char *buf)
312{
313 if (npc == npc_alloc) {
314 int oldalloc = npc_alloc;
315 sp *oldptr = searchpath;
316
317 if (0 == npc_alloc)
318 npc_alloc = 2;
319 else
320 npc_alloc <<= 1;
321
322 searchpath = (sp *)xcalloc(npc_alloc, sizeof(*searchpath));
323
324 if (oldptr && oldalloc)
41d00cd3 325 memcpy(searchpath, oldptr, oldalloc * sizeof(*searchpath));
68836b58 326
327 if (oldptr)
328 safe_free(oldptr);
329 }
330
331 assert(npc < npc_alloc);
332 strcpy(searchpath[npc].domain, buf);
7176768b 333 Tolower(searchpath[npc].domain);
bf8fe701 334 debugs(78, 3, "idnsAddPathComponent: Added domain #" << npc << ": " << searchpath[npc].domain);
95dc7ff4 335 ++npc;
68836b58 336}
337
7b724b86 338static void
339idnsFreeNameservers(void)
340{
341 safe_free(nameservers);
342 nns = nns_alloc = 0;
343}
344
68836b58 345static void
346idnsFreeSearchpath(void)
347{
348 safe_free(searchpath);
349 npc = npc_alloc = 0;
350}
351
efd900cb 352static void
353idnsParseNameservers(void)
354{
355 wordlist *w;
62e76326 356
efd900cb 357 for (w = Config.dns_nameservers; w; w = w->next) {
e0236918 358 debugs(78, DBG_IMPORTANT, "Adding nameserver " << w->key << " from squid.conf");
62e76326 359 idnsAddNameserver(w->key);
efd900cb 360 }
361}
362
1191b93b 363#if !_SQUID_MSWIN_
7b724b86 364static void
365idnsParseResolvConf(void)
366{
367 FILE *fp;
68836b58 368 char buf[RESOLV_BUFSZ];
a083e75a 369 const char *t;
68836b58 370 fp = fopen(_PATH_RESCONF, "r");
62e76326 371
7b724b86 372 if (fp == NULL) {
e0236918 373 debugs(78, DBG_IMPORTANT, "" << _PATH_RESCONF << ": " << xstrerror());
62e76326 374 return;
7b724b86 375 }
62e76326 376
be266cb2 377#if _SQUID_CYGWIN_
c4aefe96 378 setmode(fileno(fp), O_TEXT);
379#endif
62e76326 380
68836b58 381 while (fgets(buf, RESOLV_BUFSZ, fp)) {
62e76326 382 t = strtok(buf, w_space);
383
68836b58 384 if (NULL == t) {
62e76326 385 continue;
68836b58 386 } else if (strcasecmp(t, "nameserver") == 0) {
387 t = strtok(NULL, w_space);
62e76326 388
68836b58 389 if (NULL == t)
390 continue;
62e76326 391
e0236918 392 debugs(78, DBG_IMPORTANT, "Adding nameserver " << t << " from " << _PATH_RESCONF);
62e76326 393
68836b58 394 idnsAddNameserver(t);
a083e75a 395 } else if (strcasecmp(t, "domain") == 0) {
26ac0430
AJ
396 idnsFreeSearchpath();
397 t = strtok(NULL, w_space);
a083e75a 398
26ac0430
AJ
399 if (NULL == t)
400 continue;
a083e75a 401
e0236918 402 debugs(78, DBG_IMPORTANT, "Adding domain " << t << " from " << _PATH_RESCONF);
a083e75a 403
26ac0430 404 idnsAddPathComponent(t);
68836b58 405 } else if (strcasecmp(t, "search") == 0) {
26ac0430 406 idnsFreeSearchpath();
68836b58 407 while (NULL != t) {
408 t = strtok(NULL, w_space);
409
410 if (NULL == t)
411 continue;
412
e0236918 413 debugs(78, DBG_IMPORTANT, "Adding domain " << t << " from " << _PATH_RESCONF);
68836b58 414
415 idnsAddPathComponent(t);
416 }
417 } else if (strcasecmp(t, "options") == 0) {
418 while (NULL != t) {
419 t = strtok(NULL, w_space);
62e76326 420
68836b58 421 if (NULL == t)
422 continue;
62e76326 423
2c2da7d3 424 if (strncmp(t, "ndots:", 6) == 0) {
68836b58 425 ndots = atoi(t + 6);
426
427 if (ndots < 1)
428 ndots = 1;
429
e0236918 430 debugs(78, DBG_IMPORTANT, "Adding ndots " << ndots << " from " << _PATH_RESCONF);
68836b58 431 }
432 }
433 }
7b724b86 434 }
a083e75a 435 if (npc == 0 && (t = getMyHostname())) {
26ac0430
AJ
436 t = strchr(t, '.');
437 if (t)
438 idnsAddPathComponent(t+1);
a083e75a 439 }
62e76326 440
7b724b86 441 fclose(fp);
442}
443
1f1ae50a 444#endif
445
be266cb2 446#if _SQUID_WINDOWS_
6b610e34 447static void
d02b72b4 448idnsParseWIN32SearchList(const char * Separator)
6b610e34 449{
a16cb11f 450 char *t;
6b610e34 451 char *token;
452 HKEY hndKey;
453
0dde2160 454 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_TCPIP_PARA, 0, KEY_QUERY_VALUE, &hndKey) == ERROR_SUCCESS) {
6b610e34 455 DWORD Type = 0;
456 DWORD Size = 0;
457 LONG Result;
04f7fd38 458 Result = RegQueryValueEx(hndKey, "Domain", NULL, &Type, NULL, &Size);
26ac0430
AJ
459
460 if (Result == ERROR_SUCCESS && Size) {
461 t = (char *) xmalloc(Size);
0dde2160 462 RegQueryValueEx(hndKey, "Domain", NULL, &Type, (LPBYTE) t, &Size);
e0236918 463 debugs(78, DBG_IMPORTANT, "Adding domain " << t << " from Registry");
26ac0430
AJ
464 idnsAddPathComponent(t);
465 xfree(t);
466 }
04f7fd38 467 Result = RegQueryValueEx(hndKey, "SearchList", NULL, &Type, NULL, &Size);
6b610e34 468
469 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 470 t = (char *) xmalloc(Size);
04f7fd38 471 RegQueryValueEx(hndKey, "SearchList", NULL, &Type, (LPBYTE) t, &Size);
a16cb11f 472 token = strtok(t, Separator);
6b610e34 473
474 while (token) {
475 idnsAddPathComponent(token);
e0236918 476 debugs(78, DBG_IMPORTANT, "Adding domain " << token << " from Registry");
6b610e34 477 token = strtok(NULL, Separator);
478 }
26ac0430 479 xfree(t);
6b610e34 480 }
481
482 RegCloseKey(hndKey);
483 }
a6cae565 484 if (npc == 0 && (t = (char *) getMyHostname())) {
26ac0430
AJ
485 t = strchr(t, '.');
486 if (t)
487 idnsAddPathComponent(t + 1);
a16cb11f 488 }
6b610e34 489}
490
0e6d05ef 491static void
492idnsParseWIN32Registry(void)
493{
a16cb11f 494 char *t;
0e6d05ef 495 char *token;
496 HKEY hndKey, hndKey2;
497
0e6d05ef 498 switch (WIN32_OS_version) {
62e76326 499
0e6d05ef 500 case _WIN_OS_WINNT:
62e76326 501 /* get nameservers from the Windows NT registry */
502
04f7fd38 503 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_TCPIP_PARA, 0, KEY_QUERY_VALUE, &hndKey) == ERROR_SUCCESS) {
62e76326 504 DWORD Type = 0;
505 DWORD Size = 0;
506 LONG Result;
04f7fd38 507 Result = RegQueryValueEx(hndKey, "DhcpNameServer", NULL, &Type, NULL, &Size);
62e76326 508
509 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 510 t = (char *) xmalloc(Size);
0dde2160 511 RegQueryValueEx(hndKey, "DhcpNameServer", NULL, &Type, (LPBYTE) t, &Size);
a16cb11f 512 token = strtok(t, ", ");
62e76326 513
514 while (token) {
515 idnsAddNameserver(token);
e0236918 516 debugs(78, DBG_IMPORTANT, "Adding DHCP nameserver " << token << " from Registry");
6b610e34 517 token = strtok(NULL, ",");
62e76326 518 }
26ac0430 519 xfree(t);
62e76326 520 }
521
0dde2160 522 Result = RegQueryValueEx(hndKey, "NameServer", NULL, &Type, NULL, &Size);
62e76326 523
524 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 525 t = (char *) xmalloc(Size);
a6cae565 526 RegQueryValueEx(hndKey, "NameServer", NULL, &Type, (LPBYTE) t, &Size);
a16cb11f 527 token = strtok(t, ", ");
62e76326 528
529 while (token) {
e0236918 530 debugs(78, DBG_IMPORTANT, "Adding nameserver " << token << " from Registry");
62e76326 531 idnsAddNameserver(token);
532 token = strtok(NULL, ", ");
533 }
26ac0430 534 xfree(t);
62e76326 535 }
536
537 RegCloseKey(hndKey);
538 }
539
6b610e34 540 idnsParseWIN32SearchList(" ");
541
62e76326 542 break;
543
0e6d05ef 544 case _WIN_OS_WIN2K:
62e76326 545
b671cc68 546 case _WIN_OS_WINXP:
62e76326 547
6b1846cf 548 case _WIN_OS_WINNET:
28170269 549
550 case _WIN_OS_WINLON:
4f5320f2
GS
551
552 case _WIN_OS_WIN7:
62e76326 553 /* get nameservers from the Windows 2000 registry */
554 /* search all interfaces for DNS server addresses */
555
04f7fd38 556 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_TCPIP_PARA_INTERFACES, 0, KEY_READ, &hndKey) == ERROR_SUCCESS) {
62e76326 557 int i;
04f7fd38
AJ
558 DWORD MaxSubkeyLen, InterfacesCount;
559 char *keyname;
560 FILETIME ftLastWriteTime;
561
562 if (RegQueryInfoKey(hndKey, NULL, NULL, NULL, &InterfacesCount, &MaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
563 keyname = (char *) xmalloc(++MaxSubkeyLen);
95dc7ff4 564 for (i = 0; i < (int) InterfacesCount; ++i) {
04f7fd38
AJ
565 DWORD j;
566 j = MaxSubkeyLen;
567 if (RegEnumKeyEx(hndKey, i, keyname, &j, NULL, NULL, NULL, &ftLastWriteTime) == ERROR_SUCCESS) {
568 char *newkeyname;
569 newkeyname = (char *) xmalloc(sizeof(REG_TCPIP_PARA_INTERFACES) + j + 2);
570 strcpy(newkeyname, REG_TCPIP_PARA_INTERFACES);
571 strcat(newkeyname, "\\");
572 strcat(newkeyname, keyname);
573 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, newkeyname, 0, KEY_QUERY_VALUE, &hndKey2) == ERROR_SUCCESS) {
574 DWORD Type = 0;
575 DWORD Size = 0;
576 LONG Result;
577 Result = RegQueryValueEx(hndKey2, "DhcpNameServer", NULL, &Type, NULL, &Size);
578 if (Result == ERROR_SUCCESS && Size) {
579 t = (char *) xmalloc(Size);
580 RegQueryValueEx(hndKey2, "DhcpNameServer", NULL, &Type, (LPBYTE)t, &Size);
581 token = strtok(t, ", ");
582 while (token) {
e0236918 583 debugs(78, DBG_IMPORTANT, "Adding DHCP nameserver " << token << " from Registry");
04f7fd38
AJ
584 idnsAddNameserver(token);
585 token = strtok(NULL, ", ");
586 }
587 xfree(t);
588 }
589
590 Result = RegQueryValueEx(hndKey2, "NameServer", NULL, &Type, NULL, &Size);
591 if (Result == ERROR_SUCCESS && Size) {
592 t = (char *) xmalloc(Size);
593 RegQueryValueEx(hndKey2, "NameServer", NULL, &Type, (LPBYTE)t, &Size);
594 token = strtok(t, ", ");
595 while (token) {
e0236918 596 debugs(78, DBG_IMPORTANT, "Adding nameserver " << token << " from Registry");
04f7fd38
AJ
597 idnsAddNameserver(token);
598 token = strtok(NULL, ", ");
599 }
600
601 xfree(t);
62e76326 602 }
62e76326 603
0dde2160 604 RegCloseKey(hndKey2);
62e76326 605 }
606
04f7fd38 607 xfree(newkeyname);
62e76326 608 }
609 }
0dde2160 610
04f7fd38 611 xfree(keyname);
62e76326 612 }
613
614 RegCloseKey(hndKey);
615 }
616
6b610e34 617 idnsParseWIN32SearchList(", ");
618
62e76326 619 break;
620
0e6d05ef 621 case _WIN_OS_WIN95:
62e76326 622
0e6d05ef 623 case _WIN_OS_WIN98:
62e76326 624
be20dac7 625 case _WIN_OS_WINME:
62e76326 626 /* get nameservers from the Windows 9X registry */
627
04f7fd38 628 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_VXD_MSTCP, 0, KEY_QUERY_VALUE, &hndKey) == ERROR_SUCCESS) {
62e76326 629 DWORD Type = 0;
630 DWORD Size = 0;
631 LONG Result;
04f7fd38 632 Result = RegQueryValueEx(hndKey, "NameServer", NULL, &Type, NULL, &Size);
62e76326 633
634 if (Result == ERROR_SUCCESS && Size) {
a16cb11f 635 t = (char *) xmalloc(Size);
a6cae565 636 RegQueryValueEx(hndKey, "NameServer", NULL, &Type, (LPBYTE) t, &Size);
a16cb11f 637 token = strtok(t, ", ");
62e76326 638
639 while (token) {
e0236918 640 debugs(78, DBG_IMPORTANT, "Adding nameserver " << token << " from Registry");
62e76326 641 idnsAddNameserver(token);
642 token = strtok(NULL, ", ");
643 }
26ac0430 644 xfree(t);
62e76326 645 }
646
647 RegCloseKey(hndKey);
648 }
649
650 break;
651
0e6d05ef 652 default:
e0236918 653 debugs(78, DBG_IMPORTANT, "Failed to read nameserver from Registry: Unknown System Type.");
62e76326 654 return;
0e6d05ef 655 }
656}
62e76326 657
0e6d05ef 658#endif
659
7b724b86 660static void
661idnsStats(StoreEntry * sentry)
662{
a16b4aa0 663 dlink_node *n;
664 idns_query *q;
7cfc1c9a 665 int i;
558be27a 666 int j;
cc192b50 667 char buf[MAX_IPSTRLEN];
7b724b86 668 storeAppendPrintf(sentry, "Internal DNS Statistics:\n");
a16b4aa0 669 storeAppendPrintf(sentry, "\nThe Queue:\n");
0a69973e 670 storeAppendPrintf(sentry, " DELAY SINCE\n");
671 storeAppendPrintf(sentry, " ID SIZE SENDS FIRST SEND LAST SEND\n");
672 storeAppendPrintf(sentry, "------ ---- ----- ---------- ---------\n");
62e76326 673
a16b4aa0 674 for (n = lru_list.head; n; n = n->next) {
62e76326 675 q = (idns_query *)n->data;
676 storeAppendPrintf(sentry, "%#06x %4d %5d %10.3f %9.3f\n",
049441d9 677 (int) q->query_id, (int) q->sz, q->nsends,
62e76326 678 tvSubDsec(q->start_t, current_time),
679 tvSubDsec(q->sent_t, current_time));
7cfc1c9a 680 }
62e76326 681
e210930b 682 if (Config.dns.packet_max > 0)
d8b258a9 683 storeAppendPrintf(sentry, "DNS jumbo-grams: %zd Bytes\n", Config.dns.packet_max);
e210930b
AJ
684 else
685 storeAppendPrintf(sentry, "DNS jumbo-grams: not working\n");
686
7cfc1c9a 687 storeAppendPrintf(sentry, "\nNameservers:\n");
cc192b50 688 storeAppendPrintf(sentry, "IP ADDRESS # QUERIES # REPLIES\n");
689 storeAppendPrintf(sentry, "---------------------------------------------- --------- ---------\n");
62e76326 690
95dc7ff4 691 for (i = 0; i < nns; ++i) {
cc192b50 692 storeAppendPrintf(sentry, "%-45s %9d %9d\n", /* Let's take the maximum: (15 IPv4/45 IPv6) */
693 nameservers[i].S.NtoA(buf,MAX_IPSTRLEN),
62e76326 694 nameservers[i].nqueries,
695 nameservers[i].nreplies);
a16b4aa0 696 }
62e76326 697
558be27a 698 storeAppendPrintf(sentry, "\nRcode Matrix:\n");
699 storeAppendPrintf(sentry, "RCODE");
62e76326 700
95dc7ff4 701 for (i = 0; i < MAX_ATTEMPT; ++i)
62e76326 702 storeAppendPrintf(sentry, " ATTEMPT%d", i + 1);
703
e210930b 704 storeAppendPrintf(sentry, " PROBLEM\n");
62e76326 705
95dc7ff4 706 for (j = 0; j < MAX_RCODE; ++j) {
e210930b
AJ
707 if (j > 10 && j < 16)
708 continue; // unassigned by IANA.
709
62e76326 710 storeAppendPrintf(sentry, "%5d", j);
711
95dc7ff4 712 for (i = 0; i < MAX_ATTEMPT; ++i)
62e76326 713 storeAppendPrintf(sentry, " %8d", RcodeMatrix[j][i]);
714
e210930b 715 storeAppendPrintf(sentry, " : %s\n",Rcodes[j]);
558be27a 716 }
6b610e34 717
718 if (npc) {
719 storeAppendPrintf(sentry, "\nSearch list:\n");
720
95dc7ff4 721 for (i=0; i < npc; ++i)
6b610e34 722 storeAppendPrintf(sentry, "%s\n", searchpath[i].domain);
723
724 storeAppendPrintf(sentry, "\n");
725 }
7b724b86 726}
727
efd900cb 728static void
729idnsTickleQueue(void)
730{
731 if (event_queued)
62e76326 732 return;
733
efd900cb 734 if (NULL == lru_list.tail)
62e76326 735 return;
736
fd0f51c4
CT
737 const double when = min(Config.Timeout.idns_query, Config.Timeout.idns_retransmit)/1000.0;
738
739 eventAdd("idnsCheckQueue", idnsCheckQueue, NULL, when, 1);
62e76326 740
efd900cb 741 event_queued = 1;
742}
743
d24ef4e9 744static void
e0d28505 745idnsSentQueryVC(const Comm::ConnectionPointer &conn, char *buf, size_t size, comm_err_t flag, int xerrno, void *data)
d24ef4e9 746{
747 nsvc * vc = (nsvc *)data;
748
749 if (flag == COMM_ERR_CLOSING)
750 return;
af6a12ee 751
e0d28505
AJ
752 // XXX: irrelevant now that we have conn pointer?
753 if (!Comm::IsConnOpen(conn) || fd_table[conn->fd].closing())
af6a12ee 754 return;
d24ef4e9 755
756 if (flag != COMM_OK || size <= 0) {
80463bb4 757 conn->close();
d24ef4e9 758 return;
759 }
760
761 vc->busy = 0;
762 idnsDoSendQueryVC(vc);
763}
764
765static void
766idnsDoSendQueryVC(nsvc *vc)
767{
768 if (vc->busy)
769 return;
770
032785bf 771 if (vc->queue->contentSize() == 0)
d24ef4e9 772 return;
773
8401d6fc
AR
774 // if retrying after a TC UDP response, our close handler cb may be pending
775 if (fd_table[vc->conn->fd].closing())
776 return;
777
032785bf 778 MemBuf *mb = vc->queue;
d24ef4e9 779
032785bf 780 vc->queue = new MemBuf;
d24ef4e9 781
782 vc->busy = 1;
783
fd0f51c4 784 // Comm needs seconds but idnsCheckQueue() will check the exact timeout
d4b21972
A
785 const int timeout = (Config.Timeout.idns_query % 1000 ?
786 Config.Timeout.idns_query + 1000 : Config.Timeout.idns_query) / 1000;
8d77a37c 787 AsyncCall::Pointer nil;
d4b21972 788
1b76e6c1 789 commSetConnTimeout(vc->conn, timeout, nil);
d24ef4e9 790
ec41b64c
AJ
791 AsyncCall::Pointer call = commCbCall(78, 5, "idnsSentQueryVC",
792 CommIoCbPtrFun(&idnsSentQueryVC, vc));
b0388924 793 Comm::Write(vc->conn, mb, call);
032785bf 794
795 delete mb;
d24ef4e9 796}
797
798static void
00406b24 799idnsInitVCConnected(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
d24ef4e9 800{
801 nsvc * vc = (nsvc *)data;
802
cfd66529 803 if (status != COMM_OK || !conn) {
dd3fb890
AR
804 char buf[MAX_IPSTRLEN] = "";
805 if (vc->ns < nns)
806 nameservers[vc->ns].S.NtoA(buf,MAX_IPSTRLEN);
e0236918 807 debugs(78, DBG_IMPORTANT, HERE << "Failed to connect to nameserver " << buf << " using TCP.");
d24ef4e9 808 return;
809 }
810
00406b24 811 vc->conn = conn;
cfd66529
AJ
812
813 comm_add_close_handler(conn->fd, idnsVCClosed, vc);
abd8f140
AJ
814 AsyncCall::Pointer call = commCbCall(5,4, "idnsReadVCHeader",
815 CommIoCbPtrFun(idnsReadVCHeader, vc));
816 comm_read(conn, (char *)&vc->msglen, 2, call);
d24ef4e9 817 vc->busy = 0;
818 idnsDoSendQueryVC(vc);
819}
820
821static void
575d05c4 822idnsVCClosed(const CommCloseCbParams &params)
d24ef4e9 823{
575d05c4 824 nsvc * vc = (nsvc *)params.data;
032785bf 825 delete vc->queue;
826 delete vc->msg;
00406b24 827 vc->conn = NULL;
f64091a7 828 if (vc->ns < nns) // XXX: dnsShutdown may have freed nameservers[]
dd3fb890 829 nameservers[vc->ns].vc = NULL;
63a0e6b7 830 cbdataFree(vc);
d24ef4e9 831}
832
833static void
834idnsInitVC(int ns)
835{
836 nsvc *vc = cbdataAlloc(nsvc);
dd3fb890 837 assert(ns < nns);
00406b24 838 assert(vc->conn == NULL); // MUST be NULL from the construction process!
d24ef4e9 839 nameservers[ns].vc = vc;
445962f3 840 vc->ns = ns;
032785bf 841 vc->queue = new MemBuf;
032785bf 842 vc->msg = new MemBuf;
cfd66529 843 vc->busy = 1;
d24ef4e9 844
00406b24 845 Comm::ConnectionPointer conn = new Comm::Connection();
105f7a99
AJ
846
847 if (!Config.Addrs.udp_outgoing.IsNoAddr())
848 conn->local = Config.Addrs.udp_outgoing;
849 else
850 conn->local = Config.Addrs.udp_incoming;
851
cfd66529 852 conn->remote = nameservers[ns].S;
d24ef4e9 853
f5ba22ff
AJ
854 if (conn->remote.IsIPv4()) {
855 conn->local.SetIPv4();
856 }
857
cfd66529 858 AsyncCall::Pointer call = commCbCall(78,3, "idnsInitVCConnected", CommConnectCbPtrFun(idnsInitVCConnected, vc));
d24ef4e9 859
4accd6d8 860 Comm::ConnOpener *cs = new Comm::ConnOpener(conn, call, Config.Timeout.connect);
aed188fd 861 cs->setHost("DNS TCP Socket");
855150a4 862 AsyncJob::Start(cs);
d24ef4e9 863}
864
865static void
866idnsSendQueryVC(idns_query * q, int ns)
867{
dd3fb890 868 assert(ns < nns);
d24ef4e9 869 if (nameservers[ns].vc == NULL)
870 idnsInitVC(ns);
871
872 nsvc *vc = nameservers[ns].vc;
873
445962f3
HN
874 if (!vc) {
875 char buf[MAX_IPSTRLEN];
e0236918 876 debugs(78, DBG_IMPORTANT, "idnsSendQuery: Failed to initiate TCP connection to nameserver " << nameservers[ns].S.NtoA(buf,MAX_IPSTRLEN) << "!");
445962f3
HN
877
878 return;
879 }
880
2fe7eff9 881 vc->queue->reset();
d24ef4e9 882
883 short head = htons(q->sz);
884
2fe7eff9 885 vc->queue->append((char *)&head, 2);
d24ef4e9 886
2fe7eff9 887 vc->queue->append(q->buf, q->sz);
d24ef4e9 888
889 idnsDoSendQueryVC(vc);
890}
891
7b724b86 892static void
893idnsSendQuery(idns_query * q)
894{
4d6c8504 895 if (DnsSocketA < 0 && DnsSocketB < 0) {
e0236918 896 debugs(78, DBG_IMPORTANT, "WARNING: idnsSendQuery: Can't send query, no DNS socket!");
62e76326 897 return;
0a69973e 898 }
62e76326 899
cc192b50 900 if (nns <= 0) {
e0236918 901 debugs(78, DBG_IMPORTANT, "WARNING: idnsSendQuery: Can't send query, no DNS nameservers known!");
cc192b50 902 return;
903 }
62e76326 904
7b724b86 905 assert(q->lru.next == NULL);
62e76326 906
7b724b86 907 assert(q->lru.prev == NULL);
62e76326 908
4d6c8504
AJ
909 int x = -1, y = -1;
910 int ns;
911
213b6c90
AJ
912 do {
913 ns = q->nsends % nns;
914
915 if (q->need_vc) {
916 idnsSendQueryVC(q, ns);
4d6c8504 917 x = y = 0;
213b6c90 918 } else {
055421ee 919 if (DnsSocketB >= 0 && nameservers[ns].S.IsIPv6())
4d6c8504 920 y = comm_udp_sendto(DnsSocketB, nameservers[ns].S, q->buf, q->sz);
049441d9 921 else if (DnsSocketA >= 0)
4d6c8504 922 x = comm_udp_sendto(DnsSocketA, nameservers[ns].S, q->buf, q->sz);
213b6c90 923 }
62e76326 924
95dc7ff4 925 ++ q->nsends;
62e76326 926
33ab4aaf 927 q->sent_t = current_time;
62e76326 928
4d6c8504 929 if (y < 0 && nameservers[ns].S.IsIPv6())
e0236918 930 debugs(50, DBG_IMPORTANT, "idnsSendQuery: FD " << DnsSocketB << ": sendto: " << xstrerror());
4d6c8504 931 if (x < 0 && nameservers[ns].S.IsIPv4())
e0236918 932 debugs(50, DBG_IMPORTANT, "idnsSendQuery: FD " << DnsSocketA << ": sendto: " << xstrerror());
4d6c8504
AJ
933
934 } while ( (x<0 && y<0) && q->nsends % nns != 0);
62e76326 935
bd6a0c8f 936 if (y > 0) {
4d6c8504 937 fd_bytes(DnsSocketB, y, FD_WRITE);
4d6c8504 938 }
bd6a0c8f 939 if (x > 0) {
4d6c8504 940 fd_bytes(DnsSocketA, x, FD_WRITE);
0a69973e 941 }
62e76326 942
95dc7ff4 943 ++ nameservers[ns].nqueries;
c131f066 944 q->queue_t = current_time;
7b724b86 945 dlinkAdd(q, &q->lru, &lru_list);
049441d9 946 q->pending = 1;
efd900cb 947 idnsTickleQueue();
7b724b86 948}
949
950static int
b7ac5457 951idnsFromKnownNameserver(Ip::Address const &from)
7b724b86 952{
953 int i;
62e76326 954
95dc7ff4 955 for (i = 0; i < nns; ++i) {
cc192b50 956 if (nameservers[i].S != from)
62e76326 957 continue;
958
cc192b50 959 if (nameservers[i].S.GetPort() != from.GetPort())
62e76326 960 continue;
961
962 return i;
7b724b86 963 }
62e76326 964
7cfc1c9a 965 return -1;
7b724b86 966}
967
968static idns_query *
969idnsFindQuery(unsigned short id)
970{
971 dlink_node *n;
972 idns_query *q;
62e76326 973
7b724b86 974 for (n = lru_list.tail; n; n = n->prev) {
62e76326 975 q = (idns_query*)n->data;
976
049441d9 977 if (q->query_id == id)
62e76326 978 return q;
7b724b86 979 }
62e76326 980
7b724b86 981 return NULL;
982}
983
108d67a0 984static unsigned short
985idnsQueryID(void)
986{
987 unsigned short id = squid_random() & 0xFFFF;
988 unsigned short first_id = id;
989
26ac0430 990 while (idnsFindQuery(id)) {
95dc7ff4 991 ++id;
108d67a0 992
846c8960 993 if (id == first_id) {
e0236918 994 debugs(78, DBG_IMPORTANT, "idnsQueryID: Warning, too many pending DNS requests");
108d67a0 995 break;
846c8960 996 }
108d67a0 997 }
998
846c8960 999 return id;
108d67a0 1000}
1001
3074b9d1 1002static void
33ab4aaf 1003idnsCallback(idns_query *q, const char *error)
3074b9d1 1004{
1005 IDNSCB *callback;
1006 void *cbdata;
1007
33ab4aaf
HN
1008 if (error)
1009 q->error = error;
1010
1011 if (q->master)
1012 q = q->master;
1013
1014 idns_query *q2;
1015 // If any of our subqueries are still pending then wait for them to complete before continuing
1016 for ( q2 = q; q2; q2 = q2->slave) {
1017 if (q2->pending) {
1018 return;
1019 }
1020 }
1021
1022 /* Merge results */
1023 rfc1035_message *message = q->message;
1024 q->message = NULL;
1025 int n = q->ancount;
1026 error = q->error;
1027
1028 while ( (q2 = q->slave) ) {
1029 debugs(78, 6, HERE << "Merging DNS results " << q->name << " A has " << n << " RR, AAAA has " << q2->ancount << " RR");
1030 q->slave = q2->slave;
1031 if ( !q2->error ) {
1032 if (n > 0) {
1033 // two sets of RR need merging
1034 rfc1035_rr *result = (rfc1035_rr*) xmalloc( sizeof(rfc1035_rr)*(n + q2->ancount) );
1035 if (Config.dns.v4_first) {
1036 memcpy(result, message->answer, (sizeof(rfc1035_rr)*n) );
1037 memcpy(result+n, q2->message->answer, (sizeof(rfc1035_rr)*q2->ancount) );
1038 } else {
1039 memcpy(result, q2->message->answer, (sizeof(rfc1035_rr)*q2->ancount) );
1040 memcpy(result+q2->ancount, message->answer, (sizeof(rfc1035_rr)*n) );
1041 }
1042 n += q2->ancount;
1043 // HACK WARNING, the answer rr:s have been copied in-place to
1044 // result, do not free them here
1045 safe_free(message->answer);
1046 safe_free(q2->message->answer);
1047 message->answer = result;
1048 message->ancount += q2->message->ancount;
1049 } else {
1050 // first response empty or failed, just use the second
1051 rfc1035MessageDestroy(&message);
1052 message = q2->message;
1053 q2->message = NULL;
1054 n = q2->ancount;
1055 error = NULL;
1056 }
1057 }
1058 rfc1035MessageDestroy(&q2->message);
1059 cbdataFree(q2);
1060 }
1061
1062 debugs(78, 6, HERE << "Sending " << n << " (" << (error ? error : "OK") << ") DNS results to caller.");
1063
3074b9d1 1064 callback = q->callback;
1065 q->callback = NULL;
33ab4aaf 1066 const rfc1035_rr *answers = message ? message->answer : NULL;
3074b9d1 1067
1068 if (cbdataReferenceValidDone(q->callback_data, &cbdata))
1069 callback(cbdata, answers, n, error);
1070
26ac0430 1071 while (q->queue) {
3074b9d1 1072 idns_query *q2 = q->queue;
1073 q->queue = q2->queue;
1074 callback = q2->callback;
1075 q2->callback = NULL;
1076
1077 if (cbdataReferenceValidDone(q2->callback_data, &cbdata))
1078 callback(cbdata, answers, n, error);
1079
63a0e6b7 1080 cbdataFree(q2);
3074b9d1 1081 }
1082
1083 if (q->hash.key) {
1084 hash_remove_link(idns_lookup_hash, &q->hash);
1085 q->hash.key = NULL;
1086 }
33ab4aaf
HN
1087
1088 rfc1035MessageDestroy(&message);
1089 cbdataFree(q);
3074b9d1 1090}
1091
7b724b86 1092static void
e210930b 1093idnsGrokReply(const char *buf, size_t sz, int from_ns)
7b724b86 1094{
1095 int n;
ec7bade0 1096 rfc1035_message *message = NULL;
7b724b86 1097 idns_query *q;
3074b9d1 1098
cc192b50 1099 n = rfc1035MessageUnpack(buf, sz, &message);
62e76326 1100
ec7bade0 1101 if (message == NULL) {
e0236918 1102 debugs(78, DBG_IMPORTANT, "idnsGrokReply: Malformed DNS response");
62e76326 1103 return;
7b724b86 1104 }
62e76326 1105
91dcbd43 1106 debugs(78, 3, "idnsGrokReply: QID 0x" << std::hex << message->id << ", " << std::dec << n << " answers");
ec7bade0 1107
1108 q = idnsFindQuery(message->id);
1109
7b724b86 1110 if (q == NULL) {
bf8fe701 1111 debugs(78, 3, "idnsGrokReply: Late response");
cc192b50 1112 rfc1035MessageDestroy(&message);
62e76326 1113 return;
7b724b86 1114 }
62e76326 1115
9e1f210d 1116 if (rfc1035QueryCompare(&q->query, message->query) != 0) {
bf8fe701 1117 debugs(78, 3, "idnsGrokReply: Query mismatch (" << q->query.name << " != " << message->query->name << ")");
cc192b50 1118 rfc1035MessageDestroy(&message);
9e1f210d 1119 return;
1120 }
1121
e210930b
AJ
1122#if WHEN_EDNS_RESPONSES_ARE_PARSED
1123// TODO: actually gr the message right here.
1124// pull out the DNS meta data we need (A records, AAAA records and EDNS OPT) and store in q
1125// this is overall better than force-feeding A response with AAAA an section later anyway.
1126// AND allows us to merge AN+AR sections from both responses (one day)
1127
1128 if (q->edns_seen >= 0) {
1129 if (max_shared_edns == nameservers[from_ns].last_seen_edns && max_shared_edns < q->edns_seen) {
1130 nameservers[from_ns].last_seen_edns = q->edns_seen;
1131 // the altered NS was limiting the whole group.
1132 max_shared_edns = q->edns_seen;
1133 // may be limited by one of the others still
95dc7ff4 1134 for (int i = 0; i < nns; ++i)
e210930b
AJ
1135 max_shared_edns = min(max_shared_edns, nameservers[i].last_seen_edns);
1136 } else {
1137 nameservers[from_ns].last_seen_edns = q->edns_seen;
1138 // maybe reduce the global limit downwards to accomodate this NS
1139 max_shared_edns = min(max_shared_edns, q->edns_seen);
1140 }
1141 if (max_shared_edns < RFC1035_DEFAULT_PACKET_SZ)
1142 max_shared_edns = -1;
1143 }
1144#endif
1145
049441d9
HN
1146 dlinkDelete(&q->lru, &lru_list);
1147 q->pending = 0;
1148
d24ef4e9 1149 if (message->tc) {
bae9832d 1150 debugs(78, 3, HERE << "Resolver requested TC (" << q->query.name << ")");
cc192b50 1151 rfc1035MessageDestroy(&message);
d24ef4e9 1152
1153 if (!q->need_vc) {
1154 q->need_vc = 1;
5e263176 1155 -- q->nsends;
d24ef4e9 1156 idnsSendQuery(q);
91dcbd43
CT
1157 } else {
1158 // Strange: A TCP DNS response with the truncation bit (TC) set.
1159 // Return an error and cleanup; no point in trying TCP again.
1160 debugs(78, 3, HERE << "TCP DNS response");
33ab4aaf 1161 idnsCallback(q, "Truncated TCP DNS response");
d24ef4e9 1162 }
1163
1164 return;
1165 }
9e1f210d 1166
558be27a 1167 idnsRcodeCount(n, q->attempt);
62e76326 1168
558be27a 1169 if (n < 0) {
7ba8d0b8 1170 q->rcode = -n;
42687bb2 1171 debugs(78, 3, "idnsGrokReply: error " << rfc1035ErrorMessage(n) << " (" << q->rcode << ")");
7ba8d0b8 1172
95dc7ff4 1173 if (q->rcode == 2 && (++ q->attempt) < MAX_ATTEMPT) {
62e76326 1174 /*
1175 * RCODE 2 is "Server failure - The name server was
1176 * unable to process this query due to a problem with
1177 * the name server."
1178 */
cc192b50 1179 debugs(78, 3, "idnsGrokReply: Query result: SERV_FAIL");
1180 rfc1035MessageDestroy(&message);
62e76326 1181 idnsSendQuery(q);
1182 return;
1183 }
68836b58 1184
0e6432d0
HN
1185 // Do searchpath processing on the master A query only to keep
1186 // things simple. NXDOMAIN is authorative for the label, not
1187 // the record type.
049441d9 1188 if (q->rcode == 3 && !q->master && q->do_searchpath && q->attempt < MAX_ATTEMPT) {
68836b58 1189 assert(NULL == message->answer);
1190 strcpy(q->name, q->orig);
1191
cc192b50 1192 debugs(78, 3, "idnsGrokReply: Query result: NXDOMAIN - " << q->name );
1193
68836b58 1194 if (q->domain < npc) {
1195 strcat(q->name, ".");
1196 strcat(q->name, searchpath[q->domain].domain);
bf8fe701 1197 debugs(78, 3, "idnsGrokReply: searchpath used for " << q->name);
95dc7ff4 1198 ++ q->domain;
68836b58 1199 } else {
95dc7ff4 1200 ++ q->attempt;
68836b58 1201 }
1202
049441d9 1203 rfc1035MessageDestroy(&message);
cc192b50 1204
33ab4aaf 1205 // cleanup slave AAAA query
049441d9
HN
1206 while (idns_query *slave = q->slave) {
1207 dlinkDelete(&slave->lru, &lru_list);
1208 q->slave = slave->slave;
1209 rfc1035MessageDestroy(&slave->message);
1210 cbdataFree(slave);
cc192b50 1211 }
c7ba545e 1212
049441d9
HN
1213 // Build new query
1214 q->query_id = idnsQueryID();
1215 debugs(78, 3, "idnsGrokReply: Trying A Query for " << q->name);
1216 // see EDNS notes at top of file why this sends 0
1217 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->query_id, &q->query, 0);
c4a7fa29
AJ
1218 if (q->sz < 0) {
1219 /* problem with query data -- query not sent */
33ab4aaf 1220 idnsCallback(q, "Internal error");
c4a7fa29
AJ
1221 return;
1222 }
1223
33ab4aaf
HN
1224 q->nsends = 0;
1225
68836b58 1226 idnsSendQuery(q);
049441d9
HN
1227 if (Ip::EnableIpv6)
1228 idnsSendSlaveAAAAQuery(q);
68836b58 1229 return;
1230 }
558be27a 1231 }
62e76326 1232
049441d9
HN
1233 q->message = message;
1234 q->ancount = n;
cc192b50 1235
33ab4aaf
HN
1236 if (n >= 0)
1237 idnsCallback(q, NULL);
1238 else
1239 idnsCallback(q, rfc1035ErrorMessage(q->rcode));
bae9832d 1240
7b724b86 1241}
1242
1243static void
1244idnsRead(int fd, void *data)
1245{
d193a436 1246 int *N = &incoming_sockets_accepted;
0b6d1955 1247 int len;
42b51993 1248 int max = INCOMING_DNS_MAX;
d29b40de 1249 static char rbuf[SQUID_UDP_SO_RCVBUF];
7cfc1c9a 1250 int ns;
b7ac5457 1251 Ip::Address from;
cc192b50 1252
d7e7eaa7 1253 debugs(78, 3, "idnsRead: starting with FD " << fd);
cc192b50 1254
bd6a0c8f
HN
1255 // Always keep reading. This stops (or at least makes harder) several
1256 // attacks on the DNS client.
d841c88d 1257 Comm::SetSelect(fd, COMM_SELECT_READ, idnsRead, NULL, 0);
bd6a0c8f 1258
cc192b50 1259 /* BUG (UNRESOLVED)
1260 * two code lines after returning from comm_udprecvfrom()
1261 * something overwrites the memory behind the from parameter.
1262 * NO matter where in the stack declaration list above it is placed
1263 * The cause of this is still unknown, however copying the data appears
1264 * to allow it to be passed further without this erasure.
1265 */
b7ac5457 1266 Ip::Address bugbypass;
62e76326 1267
5e263176
FC
1268 while (max) {
1269 --max;
cc192b50 1270 len = comm_udp_recvfrom(fd, rbuf, SQUID_UDP_SO_RCVBUF, 0, bugbypass);
62e76326 1271
cc192b50 1272 from = bugbypass; // BUG BYPASS. see notes above.
62e76326 1273
1274 if (len == 0)
1275 break;
1276
1277 if (len < 0) {
1278 if (ignoreErrno(errno))
1279 break;
1280
1191b93b 1281#if _SQUID_LINUX_
62e76326 1282 /* Some Linux systems seem to set the FD for reading and then
1283 * return ECONNREFUSED when sendto() fails and generates an ICMP
1284 * port unreachable message. */
1285 /* or maybe an EHOSTUNREACH "No route to host" message */
1286 if (errno != ECONNREFUSED && errno != EHOSTUNREACH)
7b724b86 1287#endif
62e76326 1288
e0236918 1289 debugs(50, DBG_IMPORTANT, "idnsRead: FD " << fd << " recvfrom: " << xstrerror());
62e76326 1290
1291 break;
1292 }
1293
055421ee 1294 fd_bytes(fd, len, FD_READ);
4d6c8504 1295
62e76326 1296 assert(N);
aec55359 1297 ++(*N);
cc192b50 1298
1299 debugs(78, 3, "idnsRead: FD " << fd << ": received " << len << " bytes from " << from);
1300
1301 /* BUG: see above. Its here that it becomes apparent that the content of bugbypass is gone. */
1302 ns = idnsFromKnownNameserver(from);
62e76326 1303
1304 if (ns >= 0) {
95dc7ff4 1305 ++ nameservers[ns].nreplies;
bd6a0c8f
HN
1306 }
1307
1308 // Before unknown_nameservers check to avoid flooding cache.log on attacks,
1309 // but after the ++ above to keep statistics right.
1310 if (!lru_list.head)
1311 continue; // Don't process replies if there is no pending query.
1312
1313 if (ns < 0 && Config.onoff.ignore_unknown_nameservers) {
62e76326 1314 static time_t last_warning = 0;
1315
1316 if (squid_curtime - last_warning > 60) {
e0236918 1317 debugs(78, DBG_IMPORTANT, "WARNING: Reply from unknown nameserver " << from);
62e76326 1318 last_warning = squid_curtime;
26ac0430 1319 } else {
e0236918 1320 debugs(78, DBG_IMPORTANT, "WARNING: Reply from unknown nameserver " << from << " (retrying..." << (squid_curtime-last_warning) << "<=60)" );
26ac0430
AJ
1321 }
1322 continue;
62e76326 1323 }
1324
e210930b 1325 idnsGrokReply(rbuf, len, ns);
7b724b86 1326 }
1327}
1328
7cfc1c9a 1329static void
1330idnsCheckQueue(void *unused)
1331{
1332 dlink_node *n;
0c4fe9e4 1333 dlink_node *p = NULL;
7cfc1c9a 1334 idns_query *q;
1335 event_queued = 0;
62e76326 1336
820ee9fa
AJ
1337 if (0 == nns)
1338 /* name servers went away; reconfiguring or shutting down */
1339 return;
1340
0c4fe9e4 1341 for (n = lru_list.tail; n; n = p) {
62e76326 1342
820ee9fa 1343 p = n->prev;
733cedc0 1344 q = static_cast<idns_query*>(n->data);
62e76326 1345
820ee9fa 1346 /* Anything to process in the queue? */
fd0f51c4 1347 if ((time_msec_t)tvSubMsec(q->queue_t, current_time) < Config.Timeout.idns_retransmit )
26ac0430 1348 break;
62e76326 1349
33ab4aaf 1350 /* Query timer still running? */
fd0f51c4 1351 if ((time_msec_t)tvSubMsec(q->sent_t, current_time) < (Config.Timeout.idns_retransmit * 1 << ((q->nsends - 1) / nns))) {
820ee9fa
AJ
1352 dlinkDelete(&q->lru, &lru_list);
1353 q->queue_t = current_time;
1354 dlinkAdd(q, &q->lru, &lru_list);
1355 continue;
1356 }
62e76326 1357
91dcbd43
CT
1358 debugs(78, 3, "idnsCheckQueue: ID " << q->xact_id <<
1359 " QID 0x" << std::hex << std::setfill('0') <<
049441d9 1360 std::setw(4) << q->query_id << ": timeout" );
62e76326 1361
1362 dlinkDelete(&q->lru, &lru_list);
33ab4aaf 1363 q->pending = 0;
62e76326 1364
fd0f51c4 1365 if ((time_msec_t)tvSubMsec(q->start_t, current_time) < Config.Timeout.idns_query) {
62e76326 1366 idnsSendQuery(q);
1367 } else {
91dcbd43 1368 debugs(78, 2, "idnsCheckQueue: ID " << q->xact_id <<
049441d9 1369 " QID 0x" << std::hex << q->query_id <<
91dcbd43 1370 " : giving up after " << std::dec << q->nsends << " tries and " <<
26ac0430 1371 std::setw(5)<< std::setprecision(2) << tvSubDsec(q->start_t, current_time) << " seconds");
3074b9d1 1372
1373 if (q->rcode != 0)
33ab4aaf 1374 idnsCallback(q, rfc1035ErrorMessage(q->rcode));
3074b9d1 1375 else
33ab4aaf 1376 idnsCallback(q, "Timeout");
62e76326 1377 }
7cfc1c9a 1378 }
62e76326 1379
efd900cb 1380 idnsTickleQueue();
7cfc1c9a 1381}
1382
d24ef4e9 1383static void
e0d28505 1384idnsReadVC(const Comm::ConnectionPointer &conn, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
d24ef4e9 1385{
1386 nsvc * vc = (nsvc *)data;
1387
1388 if (flag == COMM_ERR_CLOSING)
1389 return;
1390
1391 if (flag != COMM_OK || len <= 0) {
80463bb4
AJ
1392 if (Comm::IsConnOpen(conn))
1393 conn->close();
d24ef4e9 1394 return;
1395 }
1396
bd6a0c8f 1397 vc->msg->size += len; // XXX should not access -> size directly
d24ef4e9 1398
032785bf 1399 if (vc->msg->contentSize() < vc->msglen) {
abd8f140
AJ
1400 AsyncCall::Pointer call = commCbCall(5,4, "idnsReadVC",
1401 CommIoCbPtrFun(idnsReadVC, vc));
1402 comm_read(conn, buf+len, vc->msglen - vc->msg->contentSize(), call);
d24ef4e9 1403 return;
1404 }
1405
dd3fb890 1406 assert(vc->ns < nns);
e0d28505 1407 debugs(78, 3, HERE << conn << ": received " << vc->msg->contentSize() << " bytes via TCP from " << nameservers[vc->ns].S << ".");
d24ef4e9 1408
e210930b 1409 idnsGrokReply(vc->msg->buf, vc->msg->contentSize(), vc->ns);
2fe7eff9 1410 vc->msg->clean();
abd8f140
AJ
1411 AsyncCall::Pointer call = commCbCall(5,4, "idnsReadVCHeader",
1412 CommIoCbPtrFun(idnsReadVCHeader, vc));
1413 comm_read(conn, (char *)&vc->msglen, 2, call);
d24ef4e9 1414}
1415
1416static void
e0d28505 1417idnsReadVCHeader(const Comm::ConnectionPointer &conn, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
d24ef4e9 1418{
1419 nsvc * vc = (nsvc *)data;
1420
1421 if (flag == COMM_ERR_CLOSING)
1422 return;
1423
1424 if (flag != COMM_OK || len <= 0) {
80463bb4
AJ
1425 if (Comm::IsConnOpen(conn))
1426 conn->close();
d24ef4e9 1427 return;
1428 }
1429
1430 vc->read_msglen += len;
1431
1432 assert(vc->read_msglen <= 2);
1433
1434 if (vc->read_msglen < 2) {
abd8f140
AJ
1435 AsyncCall::Pointer call = commCbCall(5,4, "idnsReadVCHeader",
1436 CommIoCbPtrFun(idnsReadVCHeader, vc));
1437 comm_read(conn, buf+len, 2 - vc->read_msglen, call);
d24ef4e9 1438 return;
1439 }
1440
1441 vc->read_msglen = 0;
1442
1443 vc->msglen = ntohs(vc->msglen);
1444
2fe7eff9 1445 vc->msg->init(vc->msglen, vc->msglen);
abd8f140
AJ
1446 AsyncCall::Pointer call = commCbCall(5,4, "idnsReadVC",
1447 CommIoCbPtrFun(idnsReadVC, vc));
1448 comm_read(conn, vc->msg->buf, vc->msglen, call);
d24ef4e9 1449}
1450
558be27a 1451/*
1452 * rcode < 0 indicates an error, rocde >= 0 indicates success
1453 */
1454static void
1455idnsRcodeCount(int rcode, int attempt)
1456{
1457 if (rcode > 0)
62e76326 1458 rcode = 0;
558be27a 1459 else if (rcode < 0)
62e76326 1460 rcode = -rcode;
1461
558be27a 1462 if (rcode < MAX_RCODE)
62e76326 1463 if (attempt < MAX_ATTEMPT)
95dc7ff4 1464 ++ RcodeMatrix[rcode][attempt];
558be27a 1465}
1466
7b724b86 1467/* ====================================================================== */
1468
5f5e883f
FC
1469static void
1470idnsRegisterWithCacheManager(void)
1471{
8822ebee 1472 Mgr::RegisterAction("idns", "Internal DNS Statistics", idnsStats, 0, 1);
5f5e883f
FC
1473}
1474
7b724b86 1475void
f64091a7 1476dnsInit(void)
7b724b86 1477{
1478 static int init = 0;
62e76326 1479
d24ef4e9 1480 CBDATA_INIT_TYPE(nsvc);
63a0e6b7 1481 CBDATA_INIT_TYPE(idns_query);
d24ef4e9 1482
4d6c8504 1483 if (DnsSocketA < 0 && DnsSocketB < 0) {
049441d9 1484 Ip::Address addrV6; // since we don't want to alter Config.Addrs.udp_* and dont have one of our own.
62e76326 1485
cc192b50 1486 if (!Config.Addrs.udp_outgoing.IsNoAddr())
049441d9 1487 addrV6 = Config.Addrs.udp_outgoing;
62e76326 1488 else
049441d9 1489 addrV6 = Config.Addrs.udp_incoming;
62e76326 1490
049441d9
HN
1491 Ip::Address addrV4 = addrV6;
1492 addrV4.SetIPv4();
7e07ced1 1493
049441d9
HN
1494 if (Ip::EnableIpv6 && addrV6.IsIPv6()) {
1495 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrV6);
4d6c8504 1496 DnsSocketB = comm_open_listener(SOCK_DGRAM,
04f7fd38 1497 IPPROTO_UDP,
049441d9 1498 addrV6,
04f7fd38 1499 COMM_NONBLOCKING,
055421ee 1500 "DNS Socket IPv6");
7e07ced1 1501 }
4d6c8504 1502
049441d9
HN
1503 if (addrV4.IsIPv4()) {
1504 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrV4);
4d6c8504 1505 DnsSocketA = comm_open_listener(SOCK_DGRAM,
04f7fd38 1506 IPPROTO_UDP,
049441d9 1507 addrV4,
04f7fd38 1508 COMM_NONBLOCKING,
055421ee 1509 "DNS Socket IPv4");
7e07ced1 1510 }
62e76326 1511
4d6c8504 1512 if (DnsSocketA < 0 && DnsSocketB < 0)
62e76326 1513 fatal("Could not create a DNS socket");
1514
1515 /* Ouch... we can't call functions using debug from a debug
1516 * statement. Doing so messes up the internal Debug::level
1517 */
04f7fd38 1518 if (DnsSocketB >= 0) {
e91e2a72 1519 comm_local_port(DnsSocketB);
e0236918 1520 debugs(78, DBG_IMPORTANT, "DNS Socket created at " << addrV6 << ", FD " << DnsSocketB);
d841c88d 1521 Comm::SetSelect(DnsSocketB, COMM_SELECT_READ, idnsRead, NULL, 0);
4d6c8504 1522 }
04f7fd38 1523 if (DnsSocketA >= 0) {
e91e2a72 1524 comm_local_port(DnsSocketA);
e0236918 1525 debugs(78, DBG_IMPORTANT, "DNS Socket created at " << addrV4 << ", FD " << DnsSocketA);
d841c88d 1526 Comm::SetSelect(DnsSocketA, COMM_SELECT_READ, idnsRead, NULL, 0);
4d6c8504 1527 }
7b724b86 1528 }
62e76326 1529
efd900cb 1530 assert(0 == nns);
1531 idnsParseNameservers();
1191b93b 1532#if !_SQUID_MSWIN_
62e76326 1533
efd900cb 1534 if (0 == nns)
62e76326 1535 idnsParseResolvConf();
1536
0e6d05ef 1537#endif
be266cb2 1538#if _SQUID_WINDOWS_
0e6d05ef 1539 if (0 == nns)
62e76326 1540 idnsParseWIN32Registry();
0e6d05ef 1541#endif
62e76326 1542
29fe9bd5 1543 if (0 == nns) {
e0236918 1544 debugs(78, DBG_IMPORTANT, "Warning: Could not find any nameservers. Trying to use localhost");
be266cb2 1545#if _SQUID_WINDOWS_
e0236918 1546 debugs(78, DBG_IMPORTANT, "Please check your TCP-IP settings or /etc/resolv.conf file");
0e6d05ef 1547#else
e0236918 1548 debugs(78, DBG_IMPORTANT, "Please check your /etc/resolv.conf file");
0e6d05ef 1549#endif
29fe9bd5 1550
e0236918 1551 debugs(78, DBG_IMPORTANT, "or use the 'dns_nameservers' option in squid.conf.");
29fe9bd5 1552 idnsAddNameserver("127.0.0.1");
1553 }
62e76326 1554
7b724b86 1555 if (!init) {
62e76326 1556 memDataInit(MEM_IDNS_QUERY, "idns_query", sizeof(idns_query), 0);
62e76326 1557 memset(RcodeMatrix, '\0', sizeof(RcodeMatrix));
30abd221 1558 idns_lookup_hash = hash_create((HASHCMP *) strcmp, 103, hash_string);
95dc7ff4 1559 ++init;
7b724b86 1560 }
372fb44e 1561
e210930b
AJ
1562#if WHEN_EDNS_RESPONSES_ARE_PARSED
1563 if (Config.onoff.ignore_unknown_nameservers && max_shared_edns > 0) {
1564 debugs(0, DBG_IMPORTANT, "ERROR: cannot negotiate EDNS with unknown nameservers. Disabling");
1565 max_shared_edns = -1; // disable if we might receive random replies.
1566 }
1567#endif
1568
372fb44e 1569 idnsRegisterWithCacheManager();
7b724b86 1570}
1571
1572void
f64091a7 1573dnsShutdown(void)
7b724b86 1574{
4d6c8504 1575 if (DnsSocketA < 0 && DnsSocketB < 0)
62e76326 1576 return;
1577
04f7fd38 1578 if (DnsSocketA >= 0 ) {
4d6c8504
AJ
1579 comm_close(DnsSocketA);
1580 DnsSocketA = -1;
1581 }
62e76326 1582
055421ee 1583 if (DnsSocketB >= 0 ) {
4d6c8504
AJ
1584 comm_close(DnsSocketB);
1585 DnsSocketB = -1;
1586 }
62e76326 1587
95dc7ff4 1588 for (int i = 0; i < nns; ++i) {
b26754c4 1589 if (nsvc *vc = nameservers[i].vc) {
00406b24
AJ
1590 if (Comm::IsConnOpen(vc->conn))
1591 vc->conn->close();
b26754c4 1592 }
1593 }
1594
dd3fb890 1595 // XXX: vcs are not closed/freed yet and may try to access nameservers[]
efd900cb 1596 idnsFreeNameservers();
68836b58 1597 idnsFreeSearchpath();
7b724b86 1598}
1599
3074b9d1 1600static int
1601idnsCachedLookup(const char *key, IDNSCB * callback, void *data)
1602{
1603 idns_query *q;
1604
1605 idns_query *old = (idns_query *) hash_lookup(idns_lookup_hash, key);
1606
1607 if (!old)
1608 return 0;
1609
63a0e6b7 1610 q = cbdataAlloc(idns_query);
91dcbd43
CT
1611 // idns_query is POD so no constructors are called after allocation
1612 q->xact_id.change();
049441d9 1613 // no query_id on this instance.
3074b9d1 1614
1615 q->callback = callback;
1616
1617 q->callback_data = cbdataReference(data);
1618
1619 q->queue = old->queue;
1620
1621 old->queue = q;
1622
1623 return 1;
1624}
1625
1626static void
33ab4aaf 1627idnsStartQuery(idns_query *q, IDNSCB * callback, void *data)
3074b9d1 1628{
33ab4aaf
HN
1629 q->start_t = current_time;
1630 q->callback = callback;
1631 q->callback_data = cbdataReference(data);
1632
049441d9 1633 q->hash.key = q->orig;
3074b9d1 1634 hash_join(idns_lookup_hash, &q->hash);
33ab4aaf
HN
1635
1636 idnsSendQuery(q);
3074b9d1 1637}
1638
049441d9
HN
1639static void
1640idnsSendSlaveAAAAQuery(idns_query *master)
1641{
1642 idns_query *q = cbdataAlloc(idns_query);
1643 memcpy(q->name, master->name, sizeof(q->name));
1644 memcpy(q->orig, master->orig, sizeof(q->orig));
1645 q->master = master;
1646 q->query_id = idnsQueryID();
1647 q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->query_id, &q->query, Config.dns.packet_max);
33ab4aaf 1648 q->start_t = master->start_t;
049441d9 1649 q->slave = master->slave;
c7ba545e 1650
cafb131e 1651 debugs(78, 3, HERE << "buf is " << q->sz << " bytes for " << q->name <<
049441d9
HN
1652 ", id = 0x" << std::hex << q->query_id);
1653 if (!q->sz) {
1654 cbdataFree(q);
1655 return;
1656 }
1657 master->slave = q;
1658 idnsSendQuery(q);
1659}
1660
7b724b86 1661void
1662idnsALookup(const char *name, IDNSCB * callback, void *data)
1663{
68836b58 1664 unsigned int i;
1665 int nd = 0;
3074b9d1 1666 idns_query *q;
1667
1668 if (idnsCachedLookup(name, callback, data))
1669 return;
1670
63a0e6b7 1671 q = cbdataAlloc(idns_query);
91dcbd43
CT
1672 // idns_query is POD so no constructors are called after allocation
1673 q->xact_id.change();
049441d9 1674 q->query_id = idnsQueryID();
71952967 1675
95dc7ff4 1676 for (i = 0; i < strlen(name); ++i)
68836b58 1677 if (name[i] == '.')
95dc7ff4 1678 ++nd;
68836b58 1679
1680 if (Config.onoff.res_defnames && npc > 0 && name[strlen(name)-1] != '.') {
1681 q->do_searchpath = 1;
1682 } else {
1683 q->do_searchpath = 0;
1684 }
1685
1686 strcpy(q->orig, name);
1687 strcpy(q->name, q->orig);
1688
1689 if (q->do_searchpath && nd < ndots) {
1690 q->domain = 0;
1691 strcat(q->name, ".");
1692 strcat(q->name, searchpath[q->domain].domain);
bf8fe701 1693 debugs(78, 3, "idnsALookup: searchpath used for " << q->name);
68836b58 1694 }
1695
049441d9
HN
1696 // see EDNS notes at top of file why this sends 0
1697 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->query_id, &q->query, 0);
62e76326 1698
2041330b 1699 if (q->sz < 0) {
1700 /* problem with query data -- query not sent */
1701 callback(data, NULL, 0, "Internal error");
63a0e6b7 1702 cbdataFree(q);
2041330b 1703 return;
1704 }
1705
26ac0430 1706 debugs(78, 3, "idnsALookup: buf is " << q->sz << " bytes for " << q->name <<
049441d9 1707 ", id = 0x" << std::hex << q->query_id);
aa6c61e3 1708
33ab4aaf 1709 idnsStartQuery(q, callback, data);
049441d9
HN
1710
1711 if (Ip::EnableIpv6)
1712 idnsSendSlaveAAAAQuery(q);
1713
7b724b86 1714}
8db71107 1715
1716void
b7ac5457 1717idnsPTRLookup(const Ip::Address &addr, IDNSCB * callback, void *data)
8db71107 1718{
3074b9d1 1719 idns_query *q;
1720
cc192b50 1721 char ip[MAX_IPSTRLEN];
1722
1723 addr.NtoA(ip,MAX_IPSTRLEN);
3074b9d1 1724
63a0e6b7 1725 q = cbdataAlloc(idns_query);
3074b9d1 1726
91dcbd43
CT
1727 // idns_query is POD so no constructors are called after allocation
1728 q->xact_id.change();
049441d9 1729 q->query_id = idnsQueryID();
71952967 1730
4e6de170 1731 if (addr.IsIPv6()) {
cc192b50 1732 struct in6_addr addr6;
1733 addr.GetInAddr(addr6);
546ae352 1734 q->sz = rfc3596BuildPTRQuery6(addr6, q->buf, sizeof(q->buf), q->query_id, &q->query, Config.dns.packet_max);
055421ee 1735 } else {
cc192b50 1736 struct in_addr addr4;
1737 addr.GetInAddr(addr4);
e210930b 1738 // see EDNS notes at top of file why this sends 0
546ae352 1739 q->sz = rfc3596BuildPTRQuery4(addr4, q->buf, sizeof(q->buf), q->query_id, &q->query, 0);
cc192b50 1740 }
1741
26ac0430 1742 if (q->sz < 0) {
2041330b 1743 /* problem with query data -- query not sent */
1744 callback(data, NULL, 0, "Internal error");
63a0e6b7 1745 cbdataFree(q);
2041330b 1746 return;
1747 }
1748
37f3da43 1749 if (idnsCachedLookup(q->query.name, callback, data)) {
26ac0430
AJ
1750 cbdataFree(q);
1751 return;
37f3da43 1752 }
1753
26ac0430 1754 debugs(78, 3, "idnsPTRLookup: buf is " << q->sz << " bytes for " << ip <<
049441d9 1755 ", id = 0x" << std::hex << q->query_id);
3074b9d1 1756
33ab4aaf 1757 idnsStartQuery(q, callback, data);
8db71107 1758}
eb824054 1759
59ad6d31 1760#if SQUID_SNMP
3c573763 1761/*
1762 * The function to return the DNS via SNMP
1763 */
1764variable_list *
f64091a7 1765snmp_netDnsFn(variable_list * Var, snint * ErrP)
3c573763 1766{
1767 int i, n = 0;
1768 variable_list *Answer = NULL;
6a644e75
AJ
1769 MemBuf tmp;
1770 debugs(49, 5, "snmp_netDnsFn: Processing request: " << snmpDebugOid(Var->name, Var->name_length, tmp));
3c573763 1771 *ErrP = SNMP_ERR_NOERROR;
62e76326 1772
3c573763 1773 switch (Var->name[LEN_SQ_NET + 1]) {
62e76326 1774
3c573763 1775 case DNS_REQ:
62e76326 1776
95dc7ff4 1777 for (i = 0; i < nns; ++i)
62e76326 1778 n += nameservers[i].nqueries;
1779
1780 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1781 n,
1782 SMI_COUNTER32);
1783
1784 break;
1785
3c573763 1786 case DNS_REP:
95dc7ff4 1787 for (i = 0; i < nns; ++i)
62e76326 1788 n += nameservers[i].nreplies;
1789
1790 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1791 n,
1792 SMI_COUNTER32);
1793
1794 break;
1795
3c573763 1796 case DNS_SERVERS:
62e76326 1797 Answer = snmp_var_new_integer(Var->name, Var->name_length,
e494f5e9 1798 nns,
62e76326 1799 SMI_COUNTER32);
1800
1801 break;
1802
3c573763 1803 default:
62e76326 1804 *ErrP = SNMP_ERR_NOSUCHNAME;
1805
1806 break;
3c573763 1807 }
62e76326 1808
3c573763 1809 return Answer;
1810}
62e76326 1811
3c573763 1812#endif /*SQUID_SNMP */
f64091a7 1813#endif /* USE_DNSHELPER */