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