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