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