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