]> git.ipfire.org Git - thirdparty/squid.git/blob - src/dns_internal.cc
Merge from trunk. and Save Comm::Connection in IoCallback
[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 "base/InstanceId.h"
37 #include "comm/Connection.h"
38 #include "comm/ConnOpener.h"
39 #include "comm/Write.h"
40 #include "comm.h"
41 #include "event.h"
42 #include "fde.h"
43 #include "ip/tools.h"
44 #include "MemBuf.h"
45 #include "SquidTime.h"
46 #include "Store.h"
47 #include "mgr/Registration.h"
48 #include "util.h"
49 #include "wordlist.h"
50
51 #if HAVE_ARPA_NAMESER_H
52 #include <arpa/nameser.h>
53 #endif
54 #if HAVE_RESOLV_H
55 #include <resolv.h>
56 #endif
57
58 /* MS Visual Studio Projects are monolithic, so we need the following
59 #ifndef to exclude the internal DNS code from compile process when
60 using external DNS process.
61 */
62 #if !USE_DNSSERVERS
63 #ifdef _SQUID_WIN32_
64 #include "squid_windows.h"
65 #define REG_TCPIP_PARA_INTERFACES "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"
66 #define REG_TCPIP_PARA "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
67 #define REG_VXD_MSTCP "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP"
68 #endif
69 #ifndef _PATH_RESCONF
70 #define _PATH_RESCONF "/etc/resolv.conf"
71 #endif
72 #ifndef NS_DEFAULTPORT
73 #define NS_DEFAULTPORT 53
74 #endif
75
76 #ifndef NS_MAXDNAME
77 #define NS_MAXDNAME 1025
78 #endif
79
80 #ifndef MAXDNSRCH
81 #define MAXDNSRCH 6
82 #endif
83
84 /* The buffer size required to store the maximum allowed search path */
85 #ifndef RESOLV_BUFSZ
86 #define RESOLV_BUFSZ NS_MAXDNAME * MAXDNSRCH + sizeof("search ") + 1
87 #endif
88
89 #define IDNS_MAX_TRIES 20
90 #define MAX_RCODE 17
91 #define MAX_ATTEMPT 3
92 static int RcodeMatrix[MAX_RCODE][MAX_ATTEMPT];
93 // NP: see http://www.iana.org/assignments/dns-parameters
94 static const char *Rcodes[] = {
95 /* RFC 1035 */
96 "Success",
97 "Packet Format Error",
98 "DNS Server Failure",
99 "Non-Existent Domain",
100 "Not Implemented",
101 "Query Refused",
102 /* RFC 2136 */
103 "Name Exists when it should not",
104 "RR Set Exists when it should not",
105 "RR Set that should exist does not",
106 "Server Not Authoritative for zone",
107 "Name not contained in zone",
108 /* unassigned */
109 "","","","","",
110 /* RFC 2671 */
111 "Bad OPT Version or TSIG Signature Failure"
112 };
113
114 typedef struct _idns_query idns_query;
115
116 typedef struct _ns ns;
117
118 typedef struct _sp sp;
119
120 typedef struct _nsvc nsvc;
121
122 struct _idns_query {
123 hash_link hash;
124 rfc1035_query query;
125 char buf[RESOLV_BUFSZ];
126 char name[NS_MAXDNAME + 1];
127 char orig[NS_MAXDNAME + 1];
128 size_t sz;
129 unsigned short msg_id; /// random query ID sent to server; changes with every query sent
130 InstanceId<idns_query> xact_id; /// identifies our "transaction", stays constant when query is retried
131
132 int nsends;
133 int need_vc;
134
135 struct timeval start_t;
136 struct timeval sent_t;
137 struct timeval queue_t;
138 dlink_node lru;
139 IDNSCB *callback;
140 void *callback_data;
141 int attempt;
142 int rcode;
143 idns_query *queue;
144 unsigned short domain;
145 unsigned short do_searchpath;
146 bool need_A;
147 struct {
148 int count;
149 rfc1035_rr *answers;
150 } initial_AAAA;
151 };
152 InstanceIdDefinitions(idns_query, "dns");
153
154 struct _nsvc {
155 int ns;
156 Comm::ConnectionPointer conn;
157 unsigned short msglen;
158 int read_msglen;
159 MemBuf *msg;
160 MemBuf *queue;
161 bool busy;
162 };
163
164 struct _ns {
165 Ip::Address S;
166 int nqueries;
167 int nreplies;
168 #if WHEN_EDNS_RESPONSES_ARE_PARSED
169 int last_seen_edns;
170 #endif
171 nsvc *vc;
172 };
173
174 struct _sp {
175 char domain[NS_MAXDNAME];
176 int queries;
177 };
178
179 CBDATA_TYPE(nsvc);
180 CBDATA_TYPE(idns_query);
181
182 static ns *nameservers = NULL;
183 static sp *searchpath = NULL;
184 static int nns = 0;
185 static int nns_alloc = 0;
186 static int npc = 0;
187 static int npc_alloc = 0;
188 static int ndots = 1;
189 static dlink_list lru_list;
190 static int event_queued = 0;
191 static hash_table *idns_lookup_hash = NULL;
192
193 /*
194 * Notes on EDNS:
195 *
196 * IPv4:
197 * EDNS as specified may be sent as an additional record for any request.
198 * early testing has revealed that it works on common devices, but cannot
199 * be reliably used on any A or PTR requet done for IPv4 addresses.
200 *
201 * As such the IPv4 packets are still hard-coded not to contain EDNS (0)
202 *
203 * Squid design:
204 * Squid is optimized to generate one packet and re-send it to all NS
205 * due to this we cannot customize the EDNS size per NS.
206 *
207 * As such we take the configuration option value as fixed.
208 *
209 * FUTURE TODO:
210 * This may not be worth doing, but if/when additional-records are parsed
211 * we will be able to recover the OPT value specific to any one NS and
212 * cache it. Effectively automating the tuning of EDNS advertised to the
213 * size our active NS are capable.
214 * Default would need to start with 512 bytes RFC1035 says every NS must accept.
215 * Responses from the configured NS may cause this to be raised or turned off.
216 */
217 #if WHEN_EDNS_RESPONSES_ARE_PARSED
218 static int max_shared_edns = RFC1035_DEFAULT_PACKET_SZ;
219 #endif
220
221 static OBJH idnsStats;
222 static void idnsAddNameserver(const char *buf);
223 static void idnsAddPathComponent(const char *buf);
224 static void idnsFreeNameservers(void);
225 static void idnsFreeSearchpath(void);
226 static void idnsParseNameservers(void);
227 #ifndef _SQUID_MSWIN_
228 static void idnsParseResolvConf(void);
229 #endif
230 #ifdef _SQUID_WIN32_
231 static void idnsParseWIN32Registry(void);
232 static void idnsParseWIN32SearchList(const char *);
233 #endif
234 static void idnsCacheQuery(idns_query * q);
235 static void idnsSendQuery(idns_query * q);
236 static void idnsDoSendQueryVC(nsvc *vc);
237 static CNCB idnsInitVCConnected;
238 static IOCB idnsReadVCHeader;
239 static IOCB idnsReadVC;
240 static IOCB idnsSentQueryVC;
241
242 static int idnsFromKnownNameserver(Ip::Address const &from);
243 static idns_query *idnsFindQuery(unsigned short id);
244 static void idnsGrokReply(const char *buf, size_t sz, int from_ns);
245 static PF idnsRead;
246 static EVH idnsCheckQueue;
247 static void idnsTickleQueue(void);
248 static void idnsRcodeCount(int, int);
249 static void idnsVCClosed(int fd, void *data);
250 static unsigned short idnsQueryID(void);
251
252 static void
253 idnsAddNameserver(const char *buf)
254 {
255 Ip::Address A;
256
257 if (!(A = buf)) {
258 debugs(78, DBG_CRITICAL, "WARNING: rejecting '" << buf << "' as a name server, because it is not a numeric IP address");
259 return;
260 }
261
262 if (A.IsAnyAddr()) {
263 debugs(78, DBG_CRITICAL, "WARNING: Squid does not accept " << A << " in DNS server specifications.");
264 A.SetLocalhost();
265 debugs(78, DBG_CRITICAL, "Will be using " << A << " instead, assuming you meant that DNS is running on the same machine");
266 }
267
268 if (!Ip::EnableIpv6 && !A.SetIPv4()) {
269 debugs(78, DBG_IMPORTANT, "WARNING: IPv6 is disabled. Discarding " << A << " in DNS server specifications.");
270 return;
271 }
272
273 if (nns == nns_alloc) {
274 int oldalloc = nns_alloc;
275 ns *oldptr = nameservers;
276
277 if (nns_alloc == 0)
278 nns_alloc = 2;
279 else
280 nns_alloc <<= 1;
281
282 nameservers = (ns *)xcalloc(nns_alloc, sizeof(*nameservers));
283
284 if (oldptr && oldalloc)
285 xmemcpy(nameservers, oldptr, oldalloc * sizeof(*nameservers));
286
287 if (oldptr)
288 safe_free(oldptr);
289 }
290
291 assert(nns < nns_alloc);
292 A.SetPort(NS_DEFAULTPORT);
293 nameservers[nns].S = A;
294 #if WHEN_EDNS_RESPONSES_ARE_PARSED
295 nameservers[nns].last_seen_edns = RFC1035_DEFAULT_PACKET_SZ;
296 // TODO generate a test packet to probe this NS from EDNS size and ability.
297 #endif
298 debugs(78, 3, "idnsAddNameserver: Added nameserver #" << nns << " (" << A << ")");
299 nns++;
300 }
301
302 static void
303 idnsAddPathComponent(const char *buf)
304 {
305 if (npc == npc_alloc) {
306 int oldalloc = npc_alloc;
307 sp *oldptr = searchpath;
308
309 if (0 == npc_alloc)
310 npc_alloc = 2;
311 else
312 npc_alloc <<= 1;
313
314 searchpath = (sp *)xcalloc(npc_alloc, sizeof(*searchpath));
315
316 if (oldptr && oldalloc)
317 xmemcpy(searchpath, oldptr, oldalloc * sizeof(*searchpath));
318
319 if (oldptr)
320 safe_free(oldptr);
321 }
322
323 assert(npc < npc_alloc);
324 strcpy(searchpath[npc].domain, buf);
325 Tolower(searchpath[npc].domain);
326 debugs(78, 3, "idnsAddPathComponent: Added domain #" << npc << ": " << searchpath[npc].domain);
327 npc++;
328 }
329
330
331 static void
332 idnsFreeNameservers(void)
333 {
334 safe_free(nameservers);
335 nns = nns_alloc = 0;
336 }
337
338 static void
339 idnsFreeSearchpath(void)
340 {
341 safe_free(searchpath);
342 npc = npc_alloc = 0;
343 }
344
345
346
347 static void
348 idnsParseNameservers(void)
349 {
350 wordlist *w;
351
352 for (w = Config.dns_nameservers; w; w = w->next) {
353 debugs(78, 1, "Adding nameserver " << w->key << " from squid.conf");
354 idnsAddNameserver(w->key);
355 }
356 }
357
358 #ifndef _SQUID_MSWIN_
359 static void
360 idnsParseResolvConf(void)
361 {
362 FILE *fp;
363 char buf[RESOLV_BUFSZ];
364 const char *t;
365 fp = fopen(_PATH_RESCONF, "r");
366
367 if (fp == NULL) {
368 debugs(78, 1, "" << _PATH_RESCONF << ": " << xstrerror());
369 return;
370 }
371
372 #if defined(_SQUID_CYGWIN_)
373 setmode(fileno(fp), O_TEXT);
374
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 #ifdef _SQUID_WIN32_
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 eventAdd("idnsCheckQueue", idnsCheckQueue, NULL, 1.0, 1);
734
735 event_queued = 1;
736 }
737
738 static void
739 idnsSentQueryVC(const Comm::ConnectionPointer &conn, char *buf, size_t size, comm_err_t flag, int xerrno, void *data)
740 {
741 nsvc * vc = (nsvc *)data;
742
743 if (flag == COMM_ERR_CLOSING)
744 return;
745
746 // XXX: irrelevant now that we have conn pointer?
747 if (!Comm::IsConnOpen(conn) || fd_table[conn->fd].closing())
748 return;
749
750 if (flag != COMM_OK || size <= 0) {
751 conn->close();
752 return;
753 }
754
755 vc->busy = 0;
756 idnsDoSendQueryVC(vc);
757 }
758
759 static void
760 idnsDoSendQueryVC(nsvc *vc)
761 {
762 if (vc->busy)
763 return;
764
765 if (vc->queue->contentSize() == 0)
766 return;
767
768 MemBuf *mb = vc->queue;
769
770 vc->queue = new MemBuf;
771
772 vc->busy = 1;
773
774 commSetTimeout(vc->conn->fd, Config.Timeout.idns_query, NULL, NULL);
775
776 AsyncCall::Pointer call = commCbCall(78, 5, "idnsSentQueryVC",
777 CommIoCbPtrFun(&idnsSentQueryVC, vc));
778 Comm::Write(vc->conn, mb, call);
779
780 delete mb;
781 }
782
783 static void
784 idnsInitVCConnected(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
785 {
786 nsvc * vc = (nsvc *)data;
787
788 if (status != COMM_OK || !conn) {
789 char buf[MAX_IPSTRLEN] = "";
790 if (vc->ns < nns)
791 nameservers[vc->ns].S.NtoA(buf,MAX_IPSTRLEN);
792 debugs(78, 1, HERE << "Failed to connect to nameserver " << buf << " using TCP.");
793 return;
794 }
795
796 vc->conn = conn;
797
798 comm_add_close_handler(conn->fd, idnsVCClosed, vc);
799 comm_read(conn, (char *)&vc->msglen, 2 , idnsReadVCHeader, vc);
800 vc->busy = 0;
801 idnsDoSendQueryVC(vc);
802 }
803
804 static void
805 idnsVCClosed(int fd, void *data)
806 {
807 nsvc * vc = (nsvc *)data;
808 delete vc->queue;
809 delete vc->msg;
810 vc->conn = NULL;
811 if (vc->ns < nns) // XXX: idnsShutdown may have freed nameservers[]
812 nameservers[vc->ns].vc = NULL;
813 cbdataFree(vc);
814 }
815
816 static void
817 idnsInitVC(int ns)
818 {
819 nsvc *vc = cbdataAlloc(nsvc);
820 assert(ns < nns);
821 assert(vc->conn == NULL); // MUST be NULL from the construction process!
822 nameservers[ns].vc = vc;
823 vc->ns = ns;
824 vc->queue = new MemBuf;
825 vc->msg = new MemBuf;
826 vc->busy = 1;
827
828 Comm::ConnectionPointer conn = new Comm::Connection();
829
830 if (!Config.Addrs.udp_outgoing.IsNoAddr())
831 conn->local = Config.Addrs.udp_outgoing;
832 else
833 conn->local = Config.Addrs.udp_incoming;
834
835 conn->remote = nameservers[ns].S;
836
837 AsyncCall::Pointer call = commCbCall(78,3, "idnsInitVCConnected", CommConnectCbPtrFun(idnsInitVCConnected, vc));
838
839 Comm::ConnOpener *cs = new Comm::ConnOpener(conn, call, Config.Timeout.connect);
840 cs->setHost("DNS TCP Socket");
841 AsyncJob::Start(cs);
842 }
843
844 static void
845 idnsSendQueryVC(idns_query * q, int ns)
846 {
847 assert(ns < nns);
848 if (nameservers[ns].vc == NULL)
849 idnsInitVC(ns);
850
851 nsvc *vc = nameservers[ns].vc;
852
853 if (!vc) {
854 char buf[MAX_IPSTRLEN];
855 debugs(78, 1, "idnsSendQuery: Failed to initiate TCP connection to nameserver " << nameservers[ns].S.NtoA(buf,MAX_IPSTRLEN) << "!");
856
857 return;
858 }
859
860 vc->queue->reset();
861
862 short head = htons(q->sz);
863
864 vc->queue->append((char *)&head, 2);
865
866 vc->queue->append(q->buf, q->sz);
867
868 idnsDoSendQueryVC(vc);
869 }
870
871 static void
872 idnsSendQuery(idns_query * q)
873 {
874 if (DnsSocketA < 0 && DnsSocketB < 0) {
875 debugs(78, 1, "WARNING: idnsSendQuery: Can't send query, no DNS socket!");
876 return;
877 }
878
879 if (nns <= 0) {
880 debugs(78, 1, "WARNING: idnsSendQuery: Can't send query, no DNS nameservers known!");
881 return;
882 }
883
884 assert(q->lru.next == NULL);
885
886 assert(q->lru.prev == NULL);
887
888 int x = -1, y = -1;
889 int ns;
890
891 q->start_t = current_time;
892 q->msg_id = idnsQueryID();
893 rfc1035SetQueryID(q->buf, q->msg_id);
894
895 do {
896 ns = q->nsends % nns;
897
898 if (q->need_vc) {
899 idnsSendQueryVC(q, ns);
900 x = y = 0;
901 } else {
902 if (DnsSocketB >= 0 && nameservers[ns].S.IsIPv6())
903 y = comm_udp_sendto(DnsSocketB, nameservers[ns].S, q->buf, q->sz);
904 else if (DnsSocketA)
905 x = comm_udp_sendto(DnsSocketA, nameservers[ns].S, q->buf, q->sz);
906 }
907
908 q->nsends++;
909
910 q->queue_t = q->sent_t = current_time;
911
912 if (y < 0 && nameservers[ns].S.IsIPv6())
913 debugs(50, 1, "idnsSendQuery: FD " << DnsSocketB << ": sendto: " << xstrerror());
914 if (x < 0 && nameservers[ns].S.IsIPv4())
915 debugs(50, 1, "idnsSendQuery: FD " << DnsSocketA << ": sendto: " << xstrerror());
916
917 } while ( (x<0 && y<0) && q->nsends % nns != 0);
918
919 if (y > 0) {
920 fd_bytes(DnsSocketB, y, FD_WRITE);
921 }
922 if (x > 0) {
923 fd_bytes(DnsSocketA, x, FD_WRITE);
924 }
925
926 nameservers[ns].nqueries++;
927 q->queue_t = current_time;
928 dlinkAdd(q, &q->lru, &lru_list);
929 idnsTickleQueue();
930 }
931
932 static int
933 idnsFromKnownNameserver(Ip::Address const &from)
934 {
935 int i;
936
937 for (i = 0; i < nns; i++) {
938 if (nameservers[i].S != from)
939 continue;
940
941 if (nameservers[i].S.GetPort() != from.GetPort())
942 continue;
943
944 return i;
945 }
946
947 return -1;
948 }
949
950 static idns_query *
951 idnsFindQuery(unsigned short id)
952 {
953 dlink_node *n;
954 idns_query *q;
955
956 for (n = lru_list.tail; n; n = n->prev) {
957 q = (idns_query*)n->data;
958
959 if (q->msg_id == id)
960 return q;
961 }
962
963 return NULL;
964 }
965
966 static unsigned short
967 idnsQueryID(void)
968 {
969 unsigned short id = squid_random() & 0xFFFF;
970 unsigned short first_id = id;
971
972 while (idnsFindQuery(id)) {
973 id++;
974
975 if (id == first_id) {
976 debugs(78, 1, "idnsQueryID: Warning, too many pending DNS requests");
977 break;
978 }
979 }
980
981 return id;
982 }
983
984 static void
985 idnsCallback(idns_query *q, rfc1035_rr *answers, int n, const char *error)
986 {
987 IDNSCB *callback;
988 void *cbdata;
989
990 callback = q->callback;
991 q->callback = NULL;
992
993 if (cbdataReferenceValidDone(q->callback_data, &cbdata))
994 callback(cbdata, answers, n, error);
995
996 while (q->queue) {
997 idns_query *q2 = q->queue;
998 q->queue = q2->queue;
999 callback = q2->callback;
1000 q2->callback = NULL;
1001
1002 if (cbdataReferenceValidDone(q2->callback_data, &cbdata))
1003 callback(cbdata, answers, n, error);
1004
1005 cbdataFree(q2);
1006 }
1007
1008 if (q->hash.key) {
1009 hash_remove_link(idns_lookup_hash, &q->hash);
1010 q->hash.key = NULL;
1011 }
1012 }
1013
1014 void
1015 idnsDropMessage(rfc1035_message *message, idns_query *q)
1016 {
1017 rfc1035MessageDestroy(&message);
1018 if (q->hash.key) {
1019 hash_remove_link(idns_lookup_hash, &q->hash);
1020 q->hash.key = NULL;
1021 }
1022 }
1023
1024 static void
1025 idnsGrokReply(const char *buf, size_t sz, int from_ns)
1026 {
1027 int n;
1028 rfc1035_message *message = NULL;
1029 idns_query *q;
1030
1031 n = rfc1035MessageUnpack(buf, sz, &message);
1032
1033 if (message == NULL) {
1034 debugs(78, 1, "idnsGrokReply: Malformed DNS response");
1035 return;
1036 }
1037
1038 debugs(78, 3, "idnsGrokReply: QID 0x" << std::hex << message->id << ", " << std::dec << n << " answers");
1039
1040 q = idnsFindQuery(message->id);
1041
1042 if (q == NULL) {
1043 debugs(78, 3, "idnsGrokReply: Late response");
1044 rfc1035MessageDestroy(&message);
1045 return;
1046 }
1047
1048 if (rfc1035QueryCompare(&q->query, message->query) != 0) {
1049 debugs(78, 3, "idnsGrokReply: Query mismatch (" << q->query.name << " != " << message->query->name << ")");
1050 rfc1035MessageDestroy(&message);
1051 return;
1052 }
1053
1054 #if WHEN_EDNS_RESPONSES_ARE_PARSED
1055 // TODO: actually gr the message right here.
1056 // pull out the DNS meta data we need (A records, AAAA records and EDNS OPT) and store in q
1057 // this is overall better than force-feeding A response with AAAA an section later anyway.
1058 // AND allows us to merge AN+AR sections from both responses (one day)
1059
1060 if (q->edns_seen >= 0) {
1061 if (max_shared_edns == nameservers[from_ns].last_seen_edns && max_shared_edns < q->edns_seen) {
1062 nameservers[from_ns].last_seen_edns = q->edns_seen;
1063 // the altered NS was limiting the whole group.
1064 max_shared_edns = q->edns_seen;
1065 // may be limited by one of the others still
1066 for (int i = 0; i < nns; i++)
1067 max_shared_edns = min(max_shared_edns, nameservers[i].last_seen_edns);
1068 } else {
1069 nameservers[from_ns].last_seen_edns = q->edns_seen;
1070 // maybe reduce the global limit downwards to accomodate this NS
1071 max_shared_edns = min(max_shared_edns, q->edns_seen);
1072 }
1073 if (max_shared_edns < RFC1035_DEFAULT_PACKET_SZ)
1074 max_shared_edns = -1;
1075 }
1076 #endif
1077
1078 if (message->tc) {
1079 debugs(78, 3, HERE << "Resolver requested TC (" << q->query.name << ")");
1080 dlinkDelete(&q->lru, &lru_list);
1081 rfc1035MessageDestroy(&message);
1082
1083 if (!q->need_vc) {
1084 q->need_vc = 1;
1085 q->nsends--;
1086 idnsSendQuery(q);
1087 } else {
1088 // Strange: A TCP DNS response with the truncation bit (TC) set.
1089 // Return an error and cleanup; no point in trying TCP again.
1090 debugs(78, 3, HERE << "TCP DNS response");
1091 idnsCallback(q, NULL, 0, "Truncated TCP DNS response");
1092 cbdataFree(q);
1093 }
1094
1095 return;
1096 }
1097
1098 dlinkDelete(&q->lru, &lru_list);
1099 idnsRcodeCount(n, q->attempt);
1100
1101 if (n < 0) {
1102 q->rcode = -n;
1103 debugs(78, 3, "idnsGrokReply: error " << rfc1035ErrorMessage(n) << " (" << q->rcode << ")");
1104
1105 if (q->rcode == 2 && ++q->attempt < MAX_ATTEMPT) {
1106 /*
1107 * RCODE 2 is "Server failure - The name server was
1108 * unable to process this query due to a problem with
1109 * the name server."
1110 */
1111 debugs(78, 3, "idnsGrokReply: Query result: SERV_FAIL");
1112 rfc1035MessageDestroy(&message);
1113 idnsSendQuery(q);
1114 return;
1115 }
1116
1117 if (q->rcode == 3 && q->do_searchpath && q->attempt < MAX_ATTEMPT) {
1118 assert(NULL == message->answer);
1119 strcpy(q->name, q->orig);
1120
1121 debugs(78, 3, "idnsGrokReply: Query result: NXDOMAIN - " << q->name );
1122
1123 if (q->domain < npc) {
1124 strcat(q->name, ".");
1125 strcat(q->name, searchpath[q->domain].domain);
1126 debugs(78, 3, "idnsGrokReply: searchpath used for " << q->name);
1127 q->domain++;
1128 } else {
1129 q->attempt++;
1130 }
1131
1132 idnsDropMessage(message, q);
1133
1134 if (Ip::EnableIpv6 && q->query.qtype == RFC1035_TYPE_AAAA) {
1135 debugs(78, 3, "idnsGrokReply: Trying AAAA Query for " << q->name);
1136 q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), 0, &q->query, Config.dns.packet_max);
1137 } else {
1138 debugs(78, 3, "idnsGrokReply: Trying A Query for " << q->name);
1139 // see EDNS notes at top of file why this sends 0
1140 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), 0, &q->query, 0);
1141 }
1142 idnsCacheQuery(q);
1143 idnsSendQuery(q);
1144 return;
1145 }
1146 }
1147
1148 if (q->need_A && (Config.onoff.dns_require_A == 1 || n <= 0 ) ) {
1149 /* ERROR or NO AAAA exist. Failover to A records. */
1150 /* Apparently its also a good idea to lookup and store the A records
1151 * just in case the AAAA are not available when we need them.
1152 * This could occur due to number of network failings beyond our control
1153 * thus the || above allowing the user to request always both.
1154 */
1155
1156 if (n == 0)
1157 debugs(78, 3, "idnsGrokReply: " << q->name << " has no AAAA records. Looking up A record instead.");
1158 else if (q->need_A && n <= 0)
1159 debugs(78, 3, "idnsGrokReply: " << q->name << " AAAA query failed. Trying A now instead.");
1160 else // admin requested this.
1161 debugs(78, 3, "idnsGrokReply: " << q->name << " AAAA query done. Configured to retrieve A now also.");
1162
1163 // move the initial message results into the failover query for merging later.
1164 if (n > 0) {
1165 q->initial_AAAA.count = message->ancount;
1166 q->initial_AAAA.answers = message->answer;
1167 message->answer = NULL;
1168 }
1169
1170 // remove the hashed query info
1171 idnsDropMessage(message, q);
1172
1173 // reset the query as an A query
1174 q->nsends = 0;
1175 // see EDNS notes at top of file why this sends 0
1176 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), 0, &q->query, 0);
1177 q->need_A = false;
1178 idnsCacheQuery(q);
1179 idnsSendQuery(q);
1180 return;
1181 }
1182
1183 /** If there are two result sets from preceeding AAAA and A lookups merge them with a preference for AAAA */
1184 if (q->initial_AAAA.count > 0 && n > 0) {
1185 /* two sets of RR need merging */
1186 rfc1035_rr *result = (rfc1035_rr*) xmalloc( sizeof(rfc1035_rr)*(n + q->initial_AAAA.count) );
1187 rfc1035_rr *tmp = result;
1188
1189 debugs(78, 6, HERE << "Merging DNS results " << q->name << " AAAA has " << q->initial_AAAA.count << " RR, A has " << n << " RR");
1190
1191 memcpy(tmp, q->initial_AAAA.answers, (sizeof(rfc1035_rr)*(q->initial_AAAA.count)) );
1192 tmp += q->initial_AAAA.count;
1193 /* free the RR object without freeing its child strings (they are now taken by the copy above) */
1194 safe_free(q->initial_AAAA.answers);
1195
1196 memcpy( tmp, message->answer, (sizeof(rfc1035_rr)*n) );
1197 /* free the RR object without freeing its child strings (they are now taken by the copy above) */
1198 safe_free(message->answer);
1199
1200 message->answer = result;
1201 message->ancount += q->initial_AAAA.count;
1202 n += q->initial_AAAA.count;
1203 q->initial_AAAA.count=0;
1204 } else if (q->initial_AAAA.count > 0 && n <= 0) {
1205 /* initial of dual queries was the only result set. */
1206 debugs(78, 6, HERE << "Merging DNS results " << q->name << " AAAA has " << q->initial_AAAA.count << " RR, A has " << n << " RR");
1207 rfc1035RRDestroy(&(message->answer), n);
1208 message->answer = q->initial_AAAA.answers;
1209 n = q->initial_AAAA.count;
1210 }
1211 /* else initial results were empty. just use the final set as authoritative */
1212
1213 debugs(78, 6, HERE << "Sending " << n << " DNS results to caller.");
1214 idnsCallback(q, message->answer, n, rfc1035ErrorMessage(n));
1215 rfc1035MessageDestroy(&message);
1216 cbdataFree(q);
1217 }
1218
1219 static void
1220 idnsRead(int fd, void *data)
1221 {
1222 int *N = &incoming_sockets_accepted;
1223 int len;
1224 int max = INCOMING_DNS_MAX;
1225 static char rbuf[SQUID_UDP_SO_RCVBUF];
1226 int ns;
1227 Ip::Address from;
1228
1229 debugs(78, 3, "idnsRead: starting with FD " << fd);
1230
1231 // Always keep reading. This stops (or at least makes harder) several
1232 // attacks on the DNS client.
1233 commSetSelect(fd, COMM_SELECT_READ, idnsRead, NULL, 0);
1234
1235 /* BUG (UNRESOLVED)
1236 * two code lines after returning from comm_udprecvfrom()
1237 * something overwrites the memory behind the from parameter.
1238 * NO matter where in the stack declaration list above it is placed
1239 * The cause of this is still unknown, however copying the data appears
1240 * to allow it to be passed further without this erasure.
1241 */
1242 Ip::Address bugbypass;
1243
1244 while (max--) {
1245 len = comm_udp_recvfrom(fd, rbuf, SQUID_UDP_SO_RCVBUF, 0, bugbypass);
1246
1247 from = bugbypass; // BUG BYPASS. see notes above.
1248
1249 if (len == 0)
1250 break;
1251
1252 if (len < 0) {
1253 if (ignoreErrno(errno))
1254 break;
1255
1256 #ifdef _SQUID_LINUX_
1257 /* Some Linux systems seem to set the FD for reading and then
1258 * return ECONNREFUSED when sendto() fails and generates an ICMP
1259 * port unreachable message. */
1260 /* or maybe an EHOSTUNREACH "No route to host" message */
1261 if (errno != ECONNREFUSED && errno != EHOSTUNREACH)
1262 #endif
1263
1264 debugs(50, 1, "idnsRead: FD " << fd << " recvfrom: " << xstrerror());
1265
1266 break;
1267 }
1268
1269 fd_bytes(fd, len, FD_READ);
1270
1271 assert(N);
1272 (*N)++;
1273
1274 debugs(78, 3, "idnsRead: FD " << fd << ": received " << len << " bytes from " << from);
1275
1276 /* BUG: see above. Its here that it becomes apparent that the content of bugbypass is gone. */
1277 ns = idnsFromKnownNameserver(from);
1278
1279 if (ns >= 0) {
1280 nameservers[ns].nreplies++;
1281 }
1282
1283 // Before unknown_nameservers check to avoid flooding cache.log on attacks,
1284 // but after the ++ above to keep statistics right.
1285 if (!lru_list.head)
1286 continue; // Don't process replies if there is no pending query.
1287
1288 if (ns < 0 && Config.onoff.ignore_unknown_nameservers) {
1289 static time_t last_warning = 0;
1290
1291 if (squid_curtime - last_warning > 60) {
1292 debugs(78, 1, "WARNING: Reply from unknown nameserver " << from);
1293 last_warning = squid_curtime;
1294 } else {
1295 debugs(78, 1, "WARNING: Reply from unknown nameserver " << from << " (retrying..." << (squid_curtime-last_warning) << "<=60)" );
1296 }
1297 continue;
1298 }
1299
1300 idnsGrokReply(rbuf, len, ns);
1301 }
1302 }
1303
1304 static void
1305 idnsCheckQueue(void *unused)
1306 {
1307 dlink_node *n;
1308 dlink_node *p = NULL;
1309 idns_query *q;
1310 event_queued = 0;
1311
1312 if (0 == nns)
1313 /* name servers went away; reconfiguring or shutting down */
1314 return;
1315
1316 for (n = lru_list.tail; n; n = p) {
1317
1318 p = n->prev;
1319 q = static_cast<idns_query*>(n->data);
1320
1321 /* Anything to process in the queue? */
1322 if (tvSubDsec(q->queue_t, current_time) < Config.Timeout.idns_retransmit )
1323 break;
1324
1325 /* Query timer expired? */
1326 if (tvSubDsec(q->sent_t, current_time) < Config.Timeout.idns_retransmit * 1 << ((q->nsends - 1) / nns)) {
1327 dlinkDelete(&q->lru, &lru_list);
1328 q->queue_t = current_time;
1329 dlinkAdd(q, &q->lru, &lru_list);
1330 continue;
1331 }
1332
1333 debugs(78, 3, "idnsCheckQueue: ID " << q->xact_id <<
1334 " QID 0x" << std::hex << std::setfill('0') <<
1335 std::setw(4) << q->msg_id << ": timeout" );
1336
1337 dlinkDelete(&q->lru, &lru_list);
1338
1339 if (tvSubDsec(q->start_t, current_time) < Config.Timeout.idns_query) {
1340 idnsSendQuery(q);
1341 } else {
1342 debugs(78, 2, "idnsCheckQueue: ID " << q->xact_id <<
1343 " QID 0x" << std::hex << q->msg_id <<
1344 " : giving up after " << std::dec << q->nsends << " tries and " <<
1345 std::setw(5)<< std::setprecision(2) << tvSubDsec(q->start_t, current_time) << " seconds");
1346
1347 if (q->rcode != 0)
1348 idnsCallback(q, NULL, -q->rcode, rfc1035ErrorMessage(q->rcode));
1349 else
1350 idnsCallback(q, NULL, -16, "Timeout");
1351
1352 cbdataFree(q);
1353 }
1354 }
1355
1356 idnsTickleQueue();
1357 }
1358
1359 static void
1360 idnsReadVC(const Comm::ConnectionPointer &conn, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
1361 {
1362 nsvc * vc = (nsvc *)data;
1363
1364 if (flag == COMM_ERR_CLOSING)
1365 return;
1366
1367 if (flag != COMM_OK || len <= 0) {
1368 if (Comm::IsConnOpen(conn))
1369 conn->close();
1370 return;
1371 }
1372
1373 vc->msg->size += len; // XXX should not access -> size directly
1374
1375 if (vc->msg->contentSize() < vc->msglen) {
1376 comm_read(conn, buf + len, vc->msglen - vc->msg->contentSize(), idnsReadVC, vc);
1377 return;
1378 }
1379
1380 assert(vc->ns < nns);
1381 debugs(78, 3, HERE << conn << ": received " << vc->msg->contentSize() << " bytes via TCP from " << nameservers[vc->ns].S << ".");
1382
1383 idnsGrokReply(vc->msg->buf, vc->msg->contentSize(), vc->ns);
1384 vc->msg->clean();
1385 comm_read(conn, (char *)&vc->msglen, 2 , idnsReadVCHeader, vc);
1386 }
1387
1388 static void
1389 idnsReadVCHeader(const Comm::ConnectionPointer &conn, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
1390 {
1391 nsvc * vc = (nsvc *)data;
1392
1393 if (flag == COMM_ERR_CLOSING)
1394 return;
1395
1396 if (flag != COMM_OK || len <= 0) {
1397 if (Comm::IsConnOpen(conn))
1398 conn->close();
1399 return;
1400 }
1401
1402 vc->read_msglen += len;
1403
1404 assert(vc->read_msglen <= 2);
1405
1406 if (vc->read_msglen < 2) {
1407 comm_read(conn, buf + len, 2 - vc->read_msglen, idnsReadVCHeader, vc);
1408 return;
1409 }
1410
1411 vc->read_msglen = 0;
1412
1413 vc->msglen = ntohs(vc->msglen);
1414
1415 vc->msg->init(vc->msglen, vc->msglen);
1416 comm_read(conn, vc->msg->buf, vc->msglen, idnsReadVC, vc);
1417 }
1418
1419 /*
1420 * rcode < 0 indicates an error, rocde >= 0 indicates success
1421 */
1422 static void
1423 idnsRcodeCount(int rcode, int attempt)
1424 {
1425 if (rcode > 0)
1426 rcode = 0;
1427 else if (rcode < 0)
1428 rcode = -rcode;
1429
1430 if (rcode < MAX_RCODE)
1431 if (attempt < MAX_ATTEMPT)
1432 RcodeMatrix[rcode][attempt]++;
1433 }
1434
1435 /* ====================================================================== */
1436
1437 static void
1438 idnsRegisterWithCacheManager(void)
1439 {
1440 Mgr::RegisterAction("idns", "Internal DNS Statistics", idnsStats, 0, 1);
1441 }
1442
1443 void
1444 idnsInit(void)
1445 {
1446 static int init = 0;
1447
1448 CBDATA_INIT_TYPE(nsvc);
1449 CBDATA_INIT_TYPE(idns_query);
1450
1451 if (DnsSocketA < 0 && DnsSocketB < 0) {
1452 int port;
1453
1454 Ip::Address addrA; // since we don't want to alter Config.Addrs.udp_* and dont have one of our own.
1455
1456 if (!Config.Addrs.udp_outgoing.IsNoAddr())
1457 addrA = Config.Addrs.udp_outgoing;
1458 else
1459 addrA = Config.Addrs.udp_incoming;
1460
1461 Ip::Address addrB = addrA;
1462 addrA.SetIPv4();
1463
1464 if (Ip::EnableIpv6 && (addrB.IsAnyAddr() || addrB.IsIPv6())) {
1465 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrB);
1466 DnsSocketB = comm_open_listener(SOCK_DGRAM,
1467 IPPROTO_UDP,
1468 addrB,
1469 COMM_NONBLOCKING,
1470 "DNS Socket IPv6");
1471 }
1472
1473 if (addrA.IsAnyAddr() || addrA.IsIPv4()) {
1474 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrA);
1475 DnsSocketA = comm_open_listener(SOCK_DGRAM,
1476 IPPROTO_UDP,
1477 addrA,
1478 COMM_NONBLOCKING,
1479 "DNS Socket IPv4");
1480 }
1481
1482 if (DnsSocketA < 0 && DnsSocketB < 0)
1483 fatal("Could not create a DNS socket");
1484
1485 /* Ouch... we can't call functions using debug from a debug
1486 * statement. Doing so messes up the internal Debug::level
1487 */
1488 if (DnsSocketB >= 0) {
1489 port = comm_local_port(DnsSocketB);
1490 debugs(78, 1, "DNS Socket created at " << addrB << ", FD " << DnsSocketB);
1491 commSetSelect(DnsSocketB, COMM_SELECT_READ, idnsRead, NULL, 0);
1492 }
1493 if (DnsSocketA >= 0) {
1494 port = comm_local_port(DnsSocketA);
1495 debugs(78, 1, "DNS Socket created at " << addrA << ", FD " << DnsSocketA);
1496 commSetSelect(DnsSocketA, COMM_SELECT_READ, idnsRead, NULL, 0);
1497 }
1498 }
1499
1500 assert(0 == nns);
1501 idnsParseNameservers();
1502 #ifndef _SQUID_MSWIN_
1503
1504 if (0 == nns)
1505 idnsParseResolvConf();
1506
1507 #endif
1508 #ifdef _SQUID_WIN32_
1509
1510 if (0 == nns)
1511 idnsParseWIN32Registry();
1512
1513 #endif
1514
1515 if (0 == nns) {
1516 debugs(78, 1, "Warning: Could not find any nameservers. Trying to use localhost");
1517 #ifdef _SQUID_WIN32_
1518
1519 debugs(78, 1, "Please check your TCP-IP settings or /etc/resolv.conf file");
1520 #else
1521
1522 debugs(78, 1, "Please check your /etc/resolv.conf file");
1523 #endif
1524
1525 debugs(78, 1, "or use the 'dns_nameservers' option in squid.conf.");
1526 idnsAddNameserver("127.0.0.1");
1527 }
1528
1529 if (!init) {
1530 memDataInit(MEM_IDNS_QUERY, "idns_query", sizeof(idns_query), 0);
1531 memset(RcodeMatrix, '\0', sizeof(RcodeMatrix));
1532 idns_lookup_hash = hash_create((HASHCMP *) strcmp, 103, hash_string);
1533 init++;
1534 }
1535
1536 #if WHEN_EDNS_RESPONSES_ARE_PARSED
1537 if (Config.onoff.ignore_unknown_nameservers && max_shared_edns > 0) {
1538 debugs(0, DBG_IMPORTANT, "ERROR: cannot negotiate EDNS with unknown nameservers. Disabling");
1539 max_shared_edns = -1; // disable if we might receive random replies.
1540 }
1541 #endif
1542
1543 idnsRegisterWithCacheManager();
1544 }
1545
1546 void
1547 idnsShutdown(void)
1548 {
1549 if (DnsSocketA < 0 && DnsSocketB < 0)
1550 return;
1551
1552 if (DnsSocketA >= 0 ) {
1553 comm_close(DnsSocketA);
1554 DnsSocketA = -1;
1555 }
1556
1557 if (DnsSocketB >= 0 ) {
1558 comm_close(DnsSocketB);
1559 DnsSocketB = -1;
1560 }
1561
1562 for (int i = 0; i < nns; i++) {
1563 if (nsvc *vc = nameservers[i].vc) {
1564 if (Comm::IsConnOpen(vc->conn))
1565 vc->conn->close();
1566 }
1567 }
1568
1569 // XXX: vcs are not closed/freed yet and may try to access nameservers[]
1570 idnsFreeNameservers();
1571 idnsFreeSearchpath();
1572 }
1573
1574 static int
1575 idnsCachedLookup(const char *key, IDNSCB * callback, void *data)
1576 {
1577 idns_query *q;
1578
1579 idns_query *old = (idns_query *) hash_lookup(idns_lookup_hash, key);
1580
1581 if (!old)
1582 return 0;
1583
1584 q = cbdataAlloc(idns_query);
1585 // idns_query is POD so no constructors are called after allocation
1586 q->xact_id.change();
1587
1588 q->callback = callback;
1589
1590 q->callback_data = cbdataReference(data);
1591
1592 q->queue = old->queue;
1593
1594 old->queue = q;
1595
1596 return 1;
1597 }
1598
1599 static void
1600 idnsCacheQuery(idns_query *q)
1601 {
1602 q->hash.key = q->query.name;
1603 hash_join(idns_lookup_hash, &q->hash);
1604 }
1605
1606 void
1607 idnsALookup(const char *name, IDNSCB * callback, void *data)
1608 {
1609 unsigned int i;
1610 int nd = 0;
1611 idns_query *q;
1612
1613 if (idnsCachedLookup(name, callback, data))
1614 return;
1615
1616 q = cbdataAlloc(idns_query);
1617 // idns_query is POD so no constructors are called after allocation
1618 q->xact_id.change();
1619
1620 for (i = 0; i < strlen(name); i++)
1621 if (name[i] == '.')
1622 nd++;
1623
1624 if (Config.onoff.res_defnames && npc > 0 && name[strlen(name)-1] != '.') {
1625 q->do_searchpath = 1;
1626 } else {
1627 q->do_searchpath = 0;
1628 }
1629
1630 strcpy(q->orig, name);
1631 strcpy(q->name, q->orig);
1632
1633 if (q->do_searchpath && nd < ndots) {
1634 q->domain = 0;
1635 strcat(q->name, ".");
1636 strcat(q->name, searchpath[q->domain].domain);
1637 debugs(78, 3, "idnsALookup: searchpath used for " << q->name);
1638 }
1639
1640 if (Ip::EnableIpv6) {
1641 q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), 0, &q->query, Config.dns.packet_max);
1642 q->need_A = true;
1643 } else {
1644 // see EDNS notes at top of file why this sends 0
1645 q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), 0, &q->query, 0);
1646 q->need_A = false;
1647 }
1648
1649 if (q->sz < 0) {
1650 /* problem with query data -- query not sent */
1651 callback(data, NULL, 0, "Internal error");
1652 cbdataFree(q);
1653 return;
1654 }
1655
1656 debugs(78, 3, "idnsALookup: buf is " << q->sz << " bytes for " << q->name <<
1657 ", id = 0x" << std::hex << q->msg_id);
1658
1659 q->callback = callback;
1660
1661 q->callback_data = cbdataReference(data);
1662
1663 idnsCacheQuery(q);
1664
1665 idnsSendQuery(q);
1666 }
1667
1668 void
1669 idnsPTRLookup(const Ip::Address &addr, IDNSCB * callback, void *data)
1670 {
1671 idns_query *q;
1672
1673 char ip[MAX_IPSTRLEN];
1674
1675 addr.NtoA(ip,MAX_IPSTRLEN);
1676
1677 q = cbdataAlloc(idns_query);
1678
1679 // idns_query is POD so no constructors are called after allocation
1680 q->xact_id.change();
1681
1682 if (Ip::EnableIpv6 && addr.IsIPv6()) {
1683 struct in6_addr addr6;
1684 addr.GetInAddr(addr6);
1685 q->sz = rfc3596BuildPTRQuery6(addr6, q->buf, sizeof(q->buf), 0, &q->query, Config.dns.packet_max);
1686 } else {
1687 struct in_addr addr4;
1688 addr.GetInAddr(addr4);
1689 // see EDNS notes at top of file why this sends 0
1690 q->sz = rfc3596BuildPTRQuery4(addr4, q->buf, sizeof(q->buf), 0, &q->query, 0);
1691 }
1692
1693 /* PTR does not do inbound A/AAAA */
1694 q->need_A = false;
1695
1696 if (q->sz < 0) {
1697 /* problem with query data -- query not sent */
1698 callback(data, NULL, 0, "Internal error");
1699 cbdataFree(q);
1700 return;
1701 }
1702
1703 if (idnsCachedLookup(q->query.name, callback, data)) {
1704 cbdataFree(q);
1705 return;
1706 }
1707
1708 debugs(78, 3, "idnsPTRLookup: buf is " << q->sz << " bytes for " << ip <<
1709 ", id = 0x" << std::hex << q->msg_id);
1710
1711 q->callback = callback;
1712
1713 q->callback_data = cbdataReference(data);
1714
1715 idnsCacheQuery(q);
1716
1717 idnsSendQuery(q);
1718 }
1719
1720 #if SQUID_SNMP
1721 /*
1722 * The function to return the DNS via SNMP
1723 */
1724 variable_list *
1725 snmp_netIdnsFn(variable_list * Var, snint * ErrP)
1726 {
1727 int i, n = 0;
1728 variable_list *Answer = NULL;
1729 MemBuf tmp;
1730 debugs(49, 5, "snmp_netDnsFn: Processing request: " << snmpDebugOid(Var->name, Var->name_length, tmp));
1731 *ErrP = SNMP_ERR_NOERROR;
1732
1733 switch (Var->name[LEN_SQ_NET + 1]) {
1734
1735 case DNS_REQ:
1736
1737 for (i = 0; i < nns; i++)
1738 n += nameservers[i].nqueries;
1739
1740 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1741 n,
1742 SMI_COUNTER32);
1743
1744 break;
1745
1746 case DNS_REP:
1747 for (i = 0; i < nns; i++)
1748 n += nameservers[i].nreplies;
1749
1750 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1751 n,
1752 SMI_COUNTER32);
1753
1754 break;
1755
1756 case DNS_SERVERS:
1757 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1758 nns,
1759 SMI_COUNTER32);
1760
1761 break;
1762
1763 default:
1764 *ErrP = SNMP_ERR_NOSUCHNAME;
1765
1766 break;
1767 }
1768
1769 return Answer;
1770 }
1771
1772 #endif /*SQUID_SNMP */
1773 #endif /* USE_DNSSERVERS */