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