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