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