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