4 * DEBUG: section 78 DNS lookups; interacts with lib/rfc1035.c
5 * AUTHOR: Duane Wessels
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
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.
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.
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.
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.
36 #include "CacheManager.h"
37 #include "comm/Connection.h"
38 #include "comm/ConnOpener.h"
44 #include "SquidTime.h"
48 #if HAVE_ARPA_NAMESER_H
49 #include <arpa/nameser.h>
55 /* MS Visual Studio Projects are monolithic, so we need the following
56 #ifndef to exclude the internal DNS code from compile process when
57 using external DNS process.
61 #include "squid_windows.h"
62 #define REG_TCPIP_PARA_INTERFACES "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"
63 #define REG_TCPIP_PARA "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
64 #define REG_VXD_MSTCP "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP"
67 #define _PATH_RESCONF "/etc/resolv.conf"
69 #ifndef NS_DEFAULTPORT
70 #define NS_DEFAULTPORT 53
74 #define NS_MAXDNAME 1025
81 /* The buffer size required to store the maximum allowed search path */
83 #define RESOLV_BUFSZ NS_MAXDNAME * MAXDNSRCH + sizeof("search ") + 1
86 #define IDNS_MAX_TRIES 20
89 static int RcodeMatrix
[MAX_RCODE
][MAX_ATTEMPT
];
91 typedef struct _idns_query idns_query
;
93 typedef struct _ns ns
;
95 typedef struct _sp sp
;
97 typedef struct _nsvc nsvc
;
102 char buf
[RESOLV_BUFSZ
];
103 char name
[NS_MAXDNAME
+ 1];
104 char orig
[NS_MAXDNAME
+ 1];
110 struct timeval start_t
;
111 struct timeval sent_t
;
112 struct timeval queue_t
;
119 unsigned short domain
;
120 unsigned short do_searchpath
;
131 unsigned short msglen
;
146 char domain
[NS_MAXDNAME
];
151 CBDATA_TYPE(idns_query
);
153 static ns
*nameservers
= NULL
;
154 static sp
*searchpath
= NULL
;
156 static int nns_alloc
= 0;
158 static int npc_alloc
= 0;
159 static int ndots
= 1;
160 static dlink_list lru_list
;
161 static int event_queued
= 0;
162 static hash_table
*idns_lookup_hash
= NULL
;
164 static OBJH idnsStats
;
165 static void idnsAddNameserver(const char *buf
);
166 static void idnsAddPathComponent(const char *buf
);
167 static void idnsFreeNameservers(void);
168 static void idnsFreeSearchpath(void);
169 static void idnsParseNameservers(void);
170 #ifndef _SQUID_MSWIN_
171 static void idnsParseResolvConf(void);
174 static void idnsParseWIN32Registry(void);
175 static void idnsParseWIN32SearchList(const char *);
177 static void idnsCacheQuery(idns_query
* q
);
178 static void idnsSendQuery(idns_query
* q
);
179 static CNCB idnsInitVCConnected
;
180 static IOCB idnsReadVCHeader
;
181 static void idnsDoSendQueryVC(nsvc
*vc
);
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
);
187 static EVH idnsCheckQueue
;
188 static void idnsTickleQueue(void);
189 static void idnsRcodeCount(int, int);
190 static void idnsVCClosed(int fd
, void *data
);
193 idnsAddNameserver(const char *buf
)
198 debugs(78, 0, "WARNING: rejecting '" << buf
<< "' as a name server, because it is not a numeric IP address");
203 debugs(78, 0, "WARNING: Squid does not accept " << A
<< " in DNS server specifications.");
205 debugs(78, 0, "Will be using " << A
<< " instead, assuming you meant that DNS is running on the same machine");
208 if (nns
== nns_alloc
) {
209 int oldalloc
= nns_alloc
;
210 ns
*oldptr
= nameservers
;
217 nameservers
= (ns
*)xcalloc(nns_alloc
, sizeof(*nameservers
));
219 if (oldptr
&& oldalloc
)
220 xmemcpy(nameservers
, oldptr
, oldalloc
* sizeof(*nameservers
));
226 assert(nns
< nns_alloc
);
227 A
.SetPort(NS_DEFAULTPORT
);
228 nameservers
[nns
].S
= A
;
229 debugs(78, 3, "idnsAddNameserver: Added nameserver #" << nns
<< " (" << A
<< ")");
234 idnsAddPathComponent(const char *buf
)
236 if (npc
== npc_alloc
) {
237 int oldalloc
= npc_alloc
;
238 sp
*oldptr
= searchpath
;
245 searchpath
= (sp
*)xcalloc(npc_alloc
, sizeof(*searchpath
));
247 if (oldptr
&& oldalloc
)
248 xmemcpy(searchpath
, oldptr
, oldalloc
* sizeof(*searchpath
));
254 assert(npc
< npc_alloc
);
255 strcpy(searchpath
[npc
].domain
, buf
);
256 debugs(78, 3, "idnsAddPathComponent: Added domain #" << npc
<< ": " << searchpath
[npc
].domain
);
262 idnsFreeNameservers(void)
264 safe_free(nameservers
);
269 idnsFreeSearchpath(void)
271 safe_free(searchpath
);
278 idnsParseNameservers(void)
282 for (w
= Config
.dns_nameservers
; w
; w
= w
->next
) {
283 debugs(78, 1, "Adding nameserver " << w
->key
<< " from squid.conf");
284 idnsAddNameserver(w
->key
);
288 #ifndef _SQUID_MSWIN_
290 idnsParseResolvConf(void)
293 char buf
[RESOLV_BUFSZ
];
295 fp
= fopen(_PATH_RESCONF
, "r");
298 debugs(78, 1, "" << _PATH_RESCONF
<< ": " << xstrerror());
302 #if defined(_SQUID_CYGWIN_)
303 setmode(fileno(fp
), O_TEXT
);
307 while (fgets(buf
, RESOLV_BUFSZ
, fp
)) {
308 t
= strtok(buf
, w_space
);
312 } else if (strcasecmp(t
, "nameserver") == 0) {
313 t
= strtok(NULL
, w_space
);
318 debugs(78, 1, "Adding nameserver " << t
<< " from " << _PATH_RESCONF
);
320 idnsAddNameserver(t
);
321 } else if (strcasecmp(t
, "domain") == 0) {
322 idnsFreeSearchpath();
323 t
= strtok(NULL
, w_space
);
328 debugs(78, 1, "Adding domain " << t
<< " from " << _PATH_RESCONF
);
330 idnsAddPathComponent(t
);
331 } else if (strcasecmp(t
, "search") == 0) {
332 idnsFreeSearchpath();
334 t
= strtok(NULL
, w_space
);
339 debugs(78, 1, "Adding domain " << t
<< " from " << _PATH_RESCONF
);
341 idnsAddPathComponent(t
);
343 } else if (strcasecmp(t
, "options") == 0) {
345 t
= strtok(NULL
, w_space
);
350 if (strncmp(t
, "ndots:", 6) == 0) {
356 debugs(78, 1, "Adding ndots " << ndots
<< " from " << _PATH_RESCONF
);
361 if (npc
== 0 && (t
= getMyHostname())) {
364 idnsAddPathComponent(t
+1);
374 idnsParseWIN32SearchList(const char * Separator
)
380 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REG_TCPIP_PARA
, 0, KEY_QUERY_VALUE
, &hndKey
) == ERROR_SUCCESS
) {
384 Result
= RegQueryValueEx(hndKey
, "Domain", NULL
, &Type
, NULL
, &Size
);
386 if (Result
== ERROR_SUCCESS
&& Size
) {
387 t
= (char *) xmalloc(Size
);
388 RegQueryValueEx(hndKey
, "Domain", NULL
, &Type
, (LPBYTE
) t
, &Size
);
389 debugs(78, 1, "Adding domain " << t
<< " from Registry");
390 idnsAddPathComponent(t
);
393 Result
= RegQueryValueEx(hndKey
, "SearchList", NULL
, &Type
, NULL
, &Size
);
395 if (Result
== ERROR_SUCCESS
&& Size
) {
396 t
= (char *) xmalloc(Size
);
397 RegQueryValueEx(hndKey
, "SearchList", NULL
, &Type
, (LPBYTE
) t
, &Size
);
398 token
= strtok(t
, Separator
);
401 idnsAddPathComponent(token
);
402 debugs(78, 1, "Adding domain " << token
<< " from Registry");
403 token
= strtok(NULL
, Separator
);
410 if (npc
== 0 && (t
= (char *) getMyHostname())) {
413 idnsAddPathComponent(t
+ 1);
418 idnsParseWIN32Registry(void)
422 HKEY hndKey
, hndKey2
;
424 switch (WIN32_OS_version
) {
427 /* get nameservers from the Windows NT registry */
429 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REG_TCPIP_PARA
, 0, KEY_QUERY_VALUE
, &hndKey
) == ERROR_SUCCESS
) {
433 Result
= RegQueryValueEx(hndKey
, "DhcpNameServer", NULL
, &Type
, NULL
, &Size
);
435 if (Result
== ERROR_SUCCESS
&& Size
) {
436 t
= (char *) xmalloc(Size
);
437 RegQueryValueEx(hndKey
, "DhcpNameServer", NULL
, &Type
, (LPBYTE
) t
, &Size
);
438 token
= strtok(t
, ", ");
441 idnsAddNameserver(token
);
442 debugs(78, 1, "Adding DHCP nameserver " << token
<< " from Registry");
443 token
= strtok(NULL
, ",");
448 Result
= RegQueryValueEx(hndKey
, "NameServer", NULL
, &Type
, NULL
, &Size
);
450 if (Result
== ERROR_SUCCESS
&& Size
) {
451 t
= (char *) xmalloc(Size
);
452 RegQueryValueEx(hndKey
, "NameServer", NULL
, &Type
, (LPBYTE
) t
, &Size
);
453 token
= strtok(t
, ", ");
456 debugs(78, 1, "Adding nameserver " << token
<< " from Registry");
457 idnsAddNameserver(token
);
458 token
= strtok(NULL
, ", ");
466 idnsParseWIN32SearchList(" ");
479 /* get nameservers from the Windows 2000 registry */
480 /* search all interfaces for DNS server addresses */
482 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REG_TCPIP_PARA_INTERFACES
, 0, KEY_READ
, &hndKey
) == ERROR_SUCCESS
) {
484 DWORD MaxSubkeyLen
, InterfacesCount
;
486 FILETIME ftLastWriteTime
;
488 if (RegQueryInfoKey(hndKey
, NULL
, NULL
, NULL
, &InterfacesCount
, &MaxSubkeyLen
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
) == ERROR_SUCCESS
) {
489 keyname
= (char *) xmalloc(++MaxSubkeyLen
);
490 for (i
= 0; i
< (int) InterfacesCount
; i
++) {
493 if (RegEnumKeyEx(hndKey
, i
, keyname
, &j
, NULL
, NULL
, NULL
, &ftLastWriteTime
) == ERROR_SUCCESS
) {
495 newkeyname
= (char *) xmalloc(sizeof(REG_TCPIP_PARA_INTERFACES
) + j
+ 2);
496 strcpy(newkeyname
, REG_TCPIP_PARA_INTERFACES
);
497 strcat(newkeyname
, "\\");
498 strcat(newkeyname
, keyname
);
499 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, newkeyname
, 0, KEY_QUERY_VALUE
, &hndKey2
) == ERROR_SUCCESS
) {
503 Result
= RegQueryValueEx(hndKey2
, "DhcpNameServer", NULL
, &Type
, NULL
, &Size
);
504 if (Result
== ERROR_SUCCESS
&& Size
) {
505 t
= (char *) xmalloc(Size
);
506 RegQueryValueEx(hndKey2
, "DhcpNameServer", NULL
, &Type
, (LPBYTE
)t
, &Size
);
507 token
= strtok(t
, ", ");
509 debugs(78, 1, "Adding DHCP nameserver " << token
<< " from Registry");
510 idnsAddNameserver(token
);
511 token
= strtok(NULL
, ", ");
516 Result
= RegQueryValueEx(hndKey2
, "NameServer", NULL
, &Type
, NULL
, &Size
);
517 if (Result
== ERROR_SUCCESS
&& Size
) {
518 t
= (char *) xmalloc(Size
);
519 RegQueryValueEx(hndKey2
, "NameServer", NULL
, &Type
, (LPBYTE
)t
, &Size
);
520 token
= strtok(t
, ", ");
522 debugs(78, 1, "Adding nameserver " << token
<< " from Registry");
523 idnsAddNameserver(token
);
524 token
= strtok(NULL
, ", ");
530 RegCloseKey(hndKey2
);
543 idnsParseWIN32SearchList(", ");
552 /* get nameservers from the Windows 9X registry */
554 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REG_VXD_MSTCP
, 0, KEY_QUERY_VALUE
, &hndKey
) == ERROR_SUCCESS
) {
558 Result
= RegQueryValueEx(hndKey
, "NameServer", NULL
, &Type
, NULL
, &Size
);
560 if (Result
== ERROR_SUCCESS
&& Size
) {
561 t
= (char *) xmalloc(Size
);
562 RegQueryValueEx(hndKey
, "NameServer", NULL
, &Type
, (LPBYTE
) t
, &Size
);
563 token
= strtok(t
, ", ");
566 debugs(78, 1, "Adding nameserver " << token
<< " from Registry");
567 idnsAddNameserver(token
);
568 token
= strtok(NULL
, ", ");
579 debugs(78, 1, "Failed to read nameserver from Registry: Unknown System Type.");
587 idnsStats(StoreEntry
* sentry
)
593 char buf
[MAX_IPSTRLEN
];
594 storeAppendPrintf(sentry
, "Internal DNS Statistics:\n");
595 storeAppendPrintf(sentry
, "\nThe Queue:\n");
596 storeAppendPrintf(sentry
, " DELAY SINCE\n");
597 storeAppendPrintf(sentry
, " ID SIZE SENDS FIRST SEND LAST SEND\n");
598 storeAppendPrintf(sentry
, "------ ---- ----- ---------- ---------\n");
600 for (n
= lru_list
.head
; n
; n
= n
->next
) {
601 q
= (idns_query
*)n
->data
;
602 storeAppendPrintf(sentry
, "%#06x %4d %5d %10.3f %9.3f\n",
603 (int) q
->id
, (int) q
->sz
, q
->nsends
,
604 tvSubDsec(q
->start_t
, current_time
),
605 tvSubDsec(q
->sent_t
, current_time
));
608 storeAppendPrintf(sentry
, "\nNameservers:\n");
609 storeAppendPrintf(sentry
, "IP ADDRESS # QUERIES # REPLIES\n");
610 storeAppendPrintf(sentry
, "---------------------------------------------- --------- ---------\n");
612 for (i
= 0; i
< nns
; i
++) {
613 storeAppendPrintf(sentry
, "%-45s %9d %9d\n", /* Let's take the maximum: (15 IPv4/45 IPv6) */
614 nameservers
[i
].S
.NtoA(buf
,MAX_IPSTRLEN
),
615 nameservers
[i
].nqueries
,
616 nameservers
[i
].nreplies
);
619 storeAppendPrintf(sentry
, "\nRcode Matrix:\n");
620 storeAppendPrintf(sentry
, "RCODE");
622 for (i
= 0; i
< MAX_ATTEMPT
; i
++)
623 storeAppendPrintf(sentry
, " ATTEMPT%d", i
+ 1);
625 storeAppendPrintf(sentry
, "\n");
627 for (j
= 0; j
< MAX_RCODE
; j
++) {
628 storeAppendPrintf(sentry
, "%5d", j
);
630 for (i
= 0; i
< MAX_ATTEMPT
; i
++)
631 storeAppendPrintf(sentry
, " %8d", RcodeMatrix
[j
][i
]);
633 storeAppendPrintf(sentry
, "\n");
637 storeAppendPrintf(sentry
, "\nSearch list:\n");
639 for (i
=0; i
< npc
; i
++)
640 storeAppendPrintf(sentry
, "%s\n", searchpath
[i
].domain
);
642 storeAppendPrintf(sentry
, "\n");
647 idnsTickleQueue(void)
652 if (NULL
== lru_list
.tail
)
655 eventAdd("idnsCheckQueue", idnsCheckQueue
, NULL
, 1.0, 1);
661 idnsSentQueryVC(int fd
, char *buf
, size_t size
, comm_err_t flag
, int xerrno
, void *data
)
663 nsvc
* vc
= (nsvc
*)data
;
665 if (flag
== COMM_ERR_CLOSING
)
668 if (fd_table
[fd
].closing())
671 if (flag
!= COMM_OK
|| size
<= 0) {
677 idnsDoSendQueryVC(vc
);
681 idnsDoSendQueryVC(nsvc
*vc
)
686 if (vc
->queue
->contentSize() == 0)
689 MemBuf
*mb
= vc
->queue
;
691 vc
->queue
= new MemBuf
;
695 commSetTimeout(vc
->fd
, Config
.Timeout
.idns_query
, NULL
, NULL
);
697 comm_write_mbuf(vc
->fd
, mb
, idnsSentQueryVC
, vc
);
703 idnsInitVCConnected(Comm::ConnectionPointer
&conn
, comm_err_t status
, int xerrno
, void *data
)
705 nsvc
* vc
= (nsvc
*)data
;
707 if (status
!= COMM_OK
|| !conn
) {
708 char buf
[MAX_IPSTRLEN
];
709 debugs(78, DBG_IMPORTANT
, "Failed to connect to nameserver " << nameservers
[vc
->ns
].S
.NtoA(buf
,MAX_IPSTRLEN
) << " using TCP!");
714 vc
->fd
= conn
->fd
; // TODO: make the vc store the conn instead?
716 comm_add_close_handler(conn
->fd
, idnsVCClosed
, vc
);
717 comm_read(conn
->fd
, (char *)&vc
->msglen
, 2 , idnsReadVCHeader
, vc
);
719 idnsDoSendQueryVC(vc
);
723 idnsVCClosed(int fd
, void *data
)
725 nsvc
* vc
= (nsvc
*)data
;
728 nameservers
[vc
->ns
].vc
= NULL
;
735 nsvc
*vc
= cbdataAlloc(nsvc
);
736 nameservers
[ns
].vc
= vc
;
738 vc
->queue
= new MemBuf
;
739 vc
->msg
= new MemBuf
;
742 Comm::ConnectionPointer conn
= new Comm::Connection
;
744 if (!Config
.Addrs
.udp_outgoing
.IsNoAddr())
745 conn
->local
= Config
.Addrs
.udp_outgoing
;
747 conn
->local
= Config
.Addrs
.udp_incoming
;
749 conn
->remote
= nameservers
[ns
].S
;
751 AsyncCall::Pointer call
= commCbCall(78,3, "idnsInitVCConnected", CommConnectCbPtrFun(idnsInitVCConnected
, vc
));
753 Comm::ConnOpener
*cs
= new Comm::ConnOpener(conn
, call
, Config
.Timeout
.connect
);
754 cs
->setHost("DNS TCP Socket");
755 AsyncJob::AsyncStart(cs
);
759 idnsSendQueryVC(idns_query
* q
, int ns
)
761 if (nameservers
[ns
].vc
== NULL
)
764 nsvc
*vc
= nameservers
[ns
].vc
;
767 char buf
[MAX_IPSTRLEN
];
768 debugs(78, 1, "idnsSendQuery: Failed to initiate TCP connection to nameserver " << nameservers
[ns
].S
.NtoA(buf
,MAX_IPSTRLEN
) << "!");
775 short head
= htons(q
->sz
);
777 vc
->queue
->append((char *)&head
, 2);
779 vc
->queue
->append(q
->buf
, q
->sz
);
781 idnsDoSendQueryVC(vc
);
785 idnsSendQuery(idns_query
* q
)
787 if (DnsSocketA
< 0 && DnsSocketB
< 0) {
788 debugs(78, 1, "WARNING: idnsSendQuery: Can't send query, no DNS socket!");
793 debugs(78, 1, "WARNING: idnsSendQuery: Can't send query, no DNS nameservers known!");
797 assert(q
->lru
.next
== NULL
);
799 assert(q
->lru
.prev
== NULL
);
805 ns
= q
->nsends
% nns
;
808 idnsSendQueryVC(q
, ns
);
811 if (DnsSocketB
>= 0 && nameservers
[ns
].S
.IsIPv6())
812 y
= comm_udp_sendto(DnsSocketB
, nameservers
[ns
].S
, q
->buf
, q
->sz
);
814 x
= comm_udp_sendto(DnsSocketA
, nameservers
[ns
].S
, q
->buf
, q
->sz
);
819 q
->queue_t
= q
->sent_t
= current_time
;
821 if (y
< 0 && nameservers
[ns
].S
.IsIPv6())
822 debugs(50, 1, "idnsSendQuery: FD " << DnsSocketB
<< ": sendto: " << xstrerror());
823 if (x
< 0 && nameservers
[ns
].S
.IsIPv4())
824 debugs(50, 1, "idnsSendQuery: FD " << DnsSocketA
<< ": sendto: " << xstrerror());
826 } while ( (x
<0 && y
<0) && q
->nsends
% nns
!= 0);
829 fd_bytes(DnsSocketB
, y
, FD_WRITE
);
830 commSetSelect(DnsSocketB
, COMM_SELECT_READ
, idnsRead
, NULL
, 0);
834 fd_bytes(DnsSocketA
, x
, FD_WRITE
);
835 commSetSelect(DnsSocketA
, COMM_SELECT_READ
, idnsRead
, NULL
, 0);
838 nameservers
[ns
].nqueries
++;
839 q
->queue_t
= current_time
;
840 dlinkAdd(q
, &q
->lru
, &lru_list
);
845 idnsFromKnownNameserver(Ip::Address
const &from
)
849 for (i
= 0; i
< nns
; i
++) {
850 if (nameservers
[i
].S
!= from
)
853 if (nameservers
[i
].S
.GetPort() != from
.GetPort())
863 idnsFindQuery(unsigned short id
)
868 for (n
= lru_list
.tail
; n
; n
= n
->prev
) {
869 q
= (idns_query
*)n
->data
;
878 static unsigned short
881 unsigned short id
= squid_random() & 0xFFFF;
882 unsigned short first_id
= id
;
884 while (idnsFindQuery(id
)) {
887 if (id
== first_id
) {
888 debugs(78, 1, "idnsQueryID: Warning, too many pending DNS requests");
897 idnsCallback(idns_query
*q
, rfc1035_rr
*answers
, int n
, const char *error
)
902 callback
= q
->callback
;
905 if (cbdataReferenceValidDone(q
->callback_data
, &cbdata
))
906 callback(cbdata
, answers
, n
, error
);
909 idns_query
*q2
= q
->queue
;
910 q
->queue
= q2
->queue
;
911 callback
= q2
->callback
;
914 if (cbdataReferenceValidDone(q2
->callback_data
, &cbdata
))
915 callback(cbdata
, answers
, n
, error
);
921 hash_remove_link(idns_lookup_hash
, &q
->hash
);
927 idnsDropMessage(rfc1035_message
*message
, idns_query
*q
)
929 rfc1035MessageDestroy(&message
);
931 hash_remove_link(idns_lookup_hash
, &q
->hash
);
937 idnsGrokReply(const char *buf
, size_t sz
)
940 rfc1035_message
*message
= NULL
;
943 n
= rfc1035MessageUnpack(buf
, sz
, &message
);
945 if (message
== NULL
) {
946 debugs(78, 1, "idnsGrokReply: Malformed DNS response");
950 debugs(78, 3, "idnsGrokReply: ID 0x" << std::hex
<< message
->id
<< ", " << std::dec
<< n
<< " answers");
952 q
= idnsFindQuery(message
->id
);
955 debugs(78, 3, "idnsGrokReply: Late response");
956 rfc1035MessageDestroy(&message
);
960 if (rfc1035QueryCompare(&q
->query
, message
->query
) != 0) {
961 debugs(78, 3, "idnsGrokReply: Query mismatch (" << q
->query
.name
<< " != " << message
->query
->name
<< ")");
962 rfc1035MessageDestroy(&message
);
967 debugs(78, 3, HERE
<< "Resolver requested TC (" << q
->query
.name
<< ")");
968 dlinkDelete(&q
->lru
, &lru_list
);
969 rfc1035MessageDestroy(&message
);
980 dlinkDelete(&q
->lru
, &lru_list
);
981 idnsRcodeCount(n
, q
->attempt
);
985 debugs(78, 3, "idnsGrokReply: error " << rfc1035ErrorMessage(n
) << " (" << q
->rcode
<< ")");
987 if (q
->rcode
== 2 && ++q
->attempt
< MAX_ATTEMPT
) {
989 * RCODE 2 is "Server failure - The name server was
990 * unable to process this query due to a problem with
993 debugs(78, 3, "idnsGrokReply: Query result: SERV_FAIL");
994 rfc1035MessageDestroy(&message
);
995 q
->start_t
= current_time
;
996 q
->id
= idnsQueryID();
997 rfc1035SetQueryID(q
->buf
, q
->id
);
1002 if (q
->rcode
== 3 && q
->do_searchpath
&& q
->attempt
< MAX_ATTEMPT
) {
1003 assert(NULL
== message
->answer
);
1004 strcpy(q
->name
, q
->orig
);
1006 debugs(78, 3, "idnsGrokReply: Query result: NXDOMAIN - " << q
->name
);
1008 if (q
->domain
< npc
) {
1009 strcat(q
->name
, ".");
1010 strcat(q
->name
, searchpath
[q
->domain
].domain
);
1011 debugs(78, 3, "idnsGrokReply: searchpath used for " << q
->name
);
1017 idnsDropMessage(message
, q
);
1019 q
->start_t
= current_time
;
1020 q
->id
= idnsQueryID();
1021 rfc1035SetQueryID(q
->buf
, q
->id
);
1022 if (Ip::EnableIpv6
&& q
->query
.qtype
== RFC1035_TYPE_AAAA
) {
1023 debugs(78, 3, "idnsGrokReply: Trying AAAA Query for " << q
->name
);
1024 q
->sz
= rfc3596BuildAAAAQuery(q
->name
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1026 debugs(78, 3, "idnsGrokReply: Trying A Query for " << q
->name
);
1027 q
->sz
= rfc3596BuildAQuery(q
->name
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1035 if (q
->need_A
&& (Config
.onoff
.dns_require_A
== 1 || n
<= 0 ) ) {
1036 /* ERROR or NO AAAA exist. Failover to A records. */
1037 /* Apparently its also a good idea to lookup and store the A records
1038 * just in case the AAAA are not available when we need them.
1039 * This could occur due to number of network failings beyond our control
1040 * thus the || above allowing the user to request always both.
1044 debugs(78, 3, "idnsGrokReply: " << q
->name
<< " has no AAAA records. Looking up A record instead.");
1045 else if (q
->need_A
&& n
<= 0)
1046 debugs(78, 3, "idnsGrokReply: " << q
->name
<< " AAAA query failed. Trying A now instead.");
1047 else // admin requested this.
1048 debugs(78, 3, "idnsGrokReply: " << q
->name
<< " AAAA query done. Configured to retrieve A now also.");
1050 // move the initial message results into the failover query for merging later.
1052 q
->initial_AAAA
.count
= message
->ancount
;
1053 q
->initial_AAAA
.answers
= message
->answer
;
1054 message
->answer
= NULL
;
1057 // remove the hashed query info
1058 idnsDropMessage(message
, q
);
1060 // reset the query as an A query
1062 q
->start_t
= current_time
;
1063 q
->id
= idnsQueryID();
1064 rfc1035SetQueryID(q
->buf
, q
->id
);
1065 q
->sz
= rfc3596BuildAQuery(q
->name
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1072 /** If there are two result sets from preceeding AAAA and A lookups merge them with a preference for AAAA */
1073 if (q
->initial_AAAA
.count
> 0 && n
> 0) {
1074 /* two sets of RR need merging */
1075 rfc1035_rr
*result
= (rfc1035_rr
*) xmalloc( sizeof(rfc1035_rr
)*(n
+ q
->initial_AAAA
.count
) );
1076 rfc1035_rr
*tmp
= result
;
1078 debugs(78, 6, HERE
<< "Merging DNS results " << q
->name
<< " AAAA has " << q
->initial_AAAA
.count
<< " RR, A has " << n
<< " RR");
1080 memcpy(tmp
, q
->initial_AAAA
.answers
, (sizeof(rfc1035_rr
)*(q
->initial_AAAA
.count
)) );
1081 tmp
+= q
->initial_AAAA
.count
;
1082 /* free the RR object without freeing its child strings (they are now taken by the copy above) */
1083 safe_free(q
->initial_AAAA
.answers
);
1085 memcpy( tmp
, message
->answer
, (sizeof(rfc1035_rr
)*n
) );
1086 /* free the RR object without freeing its child strings (they are now taken by the copy above) */
1087 safe_free(message
->answer
);
1089 message
->answer
= result
;
1090 n
+= q
->initial_AAAA
.count
;
1091 q
->initial_AAAA
.count
=0;
1092 } else if (q
->initial_AAAA
.count
> 0 && n
<= 0) {
1093 /* initial of dual queries was the only result set. */
1094 debugs(78, 6, HERE
<< "Merging DNS results " << q
->name
<< " AAAA has " << q
->initial_AAAA
.count
<< " RR, A has " << n
<< " RR");
1095 rfc1035RRDestroy(&(message
->answer
), n
);
1096 message
->answer
= q
->initial_AAAA
.answers
;
1097 n
= q
->initial_AAAA
.count
;
1099 /* else initial results were empty. just use the final set as authoritative */
1101 debugs(78, 6, HERE
<< "Sending " << n
<< " DNS results to caller.");
1102 idnsCallback(q
, message
->answer
, n
, rfc1035ErrorMessage(n
));
1103 rfc1035MessageDestroy(&message
);
1108 idnsRead(int fd
, void *data
)
1110 int *N
= &incoming_sockets_accepted
;
1112 int max
= INCOMING_DNS_MAX
;
1113 static char rbuf
[SQUID_UDP_SO_RCVBUF
];
1117 debugs(78, 3, "idnsRead: starting with FD " << fd
);
1120 * two code lines after returning from comm_udprecvfrom()
1121 * something overwrites the memory behind the from parameter.
1122 * NO matter where in the stack declaration list above it is placed
1123 * The cause of this is still unknown, however copying the data appears
1124 * to allow it to be passed further without this erasure.
1126 Ip::Address bugbypass
;
1129 len
= comm_udp_recvfrom(fd
, rbuf
, SQUID_UDP_SO_RCVBUF
, 0, bugbypass
);
1131 from
= bugbypass
; // BUG BYPASS. see notes above.
1137 if (ignoreErrno(errno
))
1140 #ifdef _SQUID_LINUX_
1141 /* Some Linux systems seem to set the FD for reading and then
1142 * return ECONNREFUSED when sendto() fails and generates an ICMP
1143 * port unreachable message. */
1144 /* or maybe an EHOSTUNREACH "No route to host" message */
1145 if (errno
!= ECONNREFUSED
&& errno
!= EHOSTUNREACH
)
1148 debugs(50, 1, "idnsRead: FD " << fd
<< " recvfrom: " << xstrerror());
1153 fd_bytes(fd
, len
, FD_READ
);
1158 debugs(78, 3, "idnsRead: FD " << fd
<< ": received " << len
<< " bytes from " << from
);
1160 /* BUG: see above. Its here that it becomes apparent that the content of bugbypass is gone. */
1161 ns
= idnsFromKnownNameserver(from
);
1164 nameservers
[ns
].nreplies
++;
1165 } else if (Config
.onoff
.ignore_unknown_nameservers
) {
1166 static time_t last_warning
= 0;
1168 if (squid_curtime
- last_warning
> 60) {
1169 debugs(78, 1, "WARNING: Reply from unknown nameserver " << from
);
1170 last_warning
= squid_curtime
;
1172 debugs(78, 1, "WARNING: Reply from unknown nameserver " << from
<< " (retrying..." << (squid_curtime
-last_warning
) << "<=60)" );
1177 idnsGrokReply(rbuf
, len
);
1180 if (lru_list
.head
) {
1181 commSetSelect(fd
, COMM_SELECT_READ
, idnsRead
, NULL
, 0);
1186 idnsCheckQueue(void *unused
)
1189 dlink_node
*p
= NULL
;
1194 /* name servers went away; reconfiguring or shutting down */
1197 for (n
= lru_list
.tail
; n
; n
= p
) {
1200 q
= static_cast<idns_query
*>(n
->data
);
1202 /* Anything to process in the queue? */
1203 if (tvSubDsec(q
->queue_t
, current_time
) < Config
.Timeout
.idns_retransmit
)
1206 /* Query timer expired? */
1207 if (tvSubDsec(q
->sent_t
, current_time
) < Config
.Timeout
.idns_retransmit
* 1 << ((q
->nsends
- 1) / nns
)) {
1208 dlinkDelete(&q
->lru
, &lru_list
);
1209 q
->queue_t
= current_time
;
1210 dlinkAdd(q
, &q
->lru
, &lru_list
);
1214 debugs(78, 3, "idnsCheckQueue: ID 0x" << std::hex
<< std::setfill('0') << std::setw(4) << q
->id
<< "timeout" );
1216 dlinkDelete(&q
->lru
, &lru_list
);
1218 if (tvSubDsec(q
->start_t
, current_time
) < Config
.Timeout
.idns_query
) {
1221 debugs(78, 2, "idnsCheckQueue: ID " << std::hex
<< q
->id
<<
1222 ": giving up after " << std::dec
<< q
->nsends
<< " tries and " <<
1223 std::setw(5)<< std::setprecision(2) << tvSubDsec(q
->start_t
, current_time
) << " seconds");
1226 idnsCallback(q
, NULL
, -q
->rcode
, rfc1035ErrorMessage(q
->rcode
));
1228 idnsCallback(q
, NULL
, -16, "Timeout");
1238 idnsReadVC(int fd
, char *buf
, size_t len
, comm_err_t flag
, int xerrno
, void *data
)
1240 nsvc
* vc
= (nsvc
*)data
;
1242 if (flag
== COMM_ERR_CLOSING
)
1245 if (flag
!= COMM_OK
|| len
<= 0) {
1250 vc
->msg
->size
+= len
; // XXX should not access -> size directly
1252 if (vc
->msg
->contentSize() < vc
->msglen
) {
1253 comm_read(fd
, buf
+ len
, vc
->msglen
- vc
->msg
->contentSize(), idnsReadVC
, vc
);
1257 debugs(78, 3, "idnsReadVC: FD " << fd
<< ": received " <<
1258 (int) vc
->msg
->contentSize() << " bytes via tcp from " <<
1259 nameservers
[vc
->ns
].S
<< ".");
1261 idnsGrokReply(vc
->msg
->buf
, vc
->msg
->contentSize());
1263 comm_read(fd
, (char *)&vc
->msglen
, 2 , idnsReadVCHeader
, vc
);
1267 idnsReadVCHeader(int fd
, char *buf
, size_t len
, comm_err_t flag
, int xerrno
, void *data
)
1269 nsvc
* vc
= (nsvc
*)data
;
1271 if (flag
== COMM_ERR_CLOSING
)
1274 if (flag
!= COMM_OK
|| len
<= 0) {
1279 vc
->read_msglen
+= len
;
1281 assert(vc
->read_msglen
<= 2);
1283 if (vc
->read_msglen
< 2) {
1284 comm_read(fd
, buf
+ len
, 2 - vc
->read_msglen
, idnsReadVCHeader
, vc
);
1288 vc
->read_msglen
= 0;
1290 vc
->msglen
= ntohs(vc
->msglen
);
1292 vc
->msg
->init(vc
->msglen
, vc
->msglen
);
1293 comm_read(fd
, vc
->msg
->buf
, vc
->msglen
, idnsReadVC
, vc
);
1297 * rcode < 0 indicates an error, rocde >= 0 indicates success
1300 idnsRcodeCount(int rcode
, int attempt
)
1307 if (rcode
< MAX_RCODE
)
1308 if (attempt
< MAX_ATTEMPT
)
1309 RcodeMatrix
[rcode
][attempt
]++;
1312 /* ====================================================================== */
1315 idnsRegisterWithCacheManager(void)
1317 CacheManager::GetInstance()->
1318 registerAction("idns", "Internal DNS Statistics", idnsStats
, 0, 1);
1324 static int init
= 0;
1326 CBDATA_INIT_TYPE(nsvc
);
1327 CBDATA_INIT_TYPE(idns_query
);
1329 if (DnsSocketA
< 0 && DnsSocketB
< 0) {
1332 Ip::Address addrA
; // since we don't want to alter Config.Addrs.udp_* and dont have one of our own.
1334 if (!Config
.Addrs
.udp_outgoing
.IsNoAddr())
1335 addrA
= Config
.Addrs
.udp_outgoing
;
1337 addrA
= Config
.Addrs
.udp_incoming
;
1339 Ip::Address addrB
= addrA
;
1342 if (Ip::EnableIpv6
&& (addrB
.IsAnyAddr() || addrB
.IsIPv6())) {
1343 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrB
);
1344 DnsSocketB
= comm_open_listener(SOCK_DGRAM
,
1351 if (addrA
.IsAnyAddr() || addrA
.IsIPv4()) {
1352 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrA
);
1353 DnsSocketA
= comm_open_listener(SOCK_DGRAM
,
1360 if (DnsSocketA
< 0 && DnsSocketB
< 0)
1361 fatal("Could not create a DNS socket");
1363 /* Ouch... we can't call functions using debug from a debug
1364 * statement. Doing so messes up the internal Debug::level
1366 if (DnsSocketB
>= 0) {
1367 port
= comm_local_port(DnsSocketB
);
1368 debugs(78, 1, "DNS Socket created at " << addrB
<< ", FD " << DnsSocketB
);
1370 if (DnsSocketA
>= 0) {
1371 port
= comm_local_port(DnsSocketA
);
1372 debugs(78, 1, "DNS Socket created at " << addrA
<< ", FD " << DnsSocketA
);
1377 idnsParseNameservers();
1378 #ifndef _SQUID_MSWIN_
1381 idnsParseResolvConf();
1384 #ifdef _SQUID_WIN32_
1387 idnsParseWIN32Registry();
1392 debugs(78, 1, "Warning: Could not find any nameservers. Trying to use localhost");
1393 #ifdef _SQUID_WIN32_
1395 debugs(78, 1, "Please check your TCP-IP settings or /etc/resolv.conf file");
1398 debugs(78, 1, "Please check your /etc/resolv.conf file");
1401 debugs(78, 1, "or use the 'dns_nameservers' option in squid.conf.");
1402 idnsAddNameserver("127.0.0.1");
1406 memDataInit(MEM_IDNS_QUERY
, "idns_query", sizeof(idns_query
), 0);
1407 memset(RcodeMatrix
, '\0', sizeof(RcodeMatrix
));
1408 idns_lookup_hash
= hash_create((HASHCMP
*) strcmp
, 103, hash_string
);
1412 idnsRegisterWithCacheManager();
1418 if (DnsSocketA
< 0 && DnsSocketB
< 0)
1421 if (DnsSocketA
>= 0 ) {
1422 comm_close(DnsSocketA
);
1426 if (DnsSocketB
>= 0 ) {
1427 comm_close(DnsSocketB
);
1431 for (int i
= 0; i
< nns
; i
++) {
1432 if (nsvc
*vc
= nameservers
[i
].vc
) {
1438 idnsFreeNameservers();
1439 idnsFreeSearchpath();
1443 idnsCachedLookup(const char *key
, IDNSCB
* callback
, void *data
)
1447 idns_query
*old
= (idns_query
*) hash_lookup(idns_lookup_hash
, key
);
1452 q
= cbdataAlloc(idns_query
);
1454 q
->callback
= callback
;
1456 q
->callback_data
= cbdataReference(data
);
1458 q
->queue
= old
->queue
;
1466 idnsCacheQuery(idns_query
*q
)
1468 q
->hash
.key
= q
->query
.name
;
1469 hash_join(idns_lookup_hash
, &q
->hash
);
1473 idnsALookup(const char *name
, IDNSCB
* callback
, void *data
)
1479 if (idnsCachedLookup(name
, callback
, data
))
1482 q
= cbdataAlloc(idns_query
);
1484 q
->id
= idnsQueryID();
1486 for (i
= 0; i
< strlen(name
); i
++)
1490 if (Config
.onoff
.res_defnames
&& npc
> 0 && name
[strlen(name
)-1] != '.') {
1491 q
->do_searchpath
= 1;
1493 q
->do_searchpath
= 0;
1496 strcpy(q
->orig
, name
);
1497 strcpy(q
->name
, q
->orig
);
1499 if (q
->do_searchpath
&& nd
< ndots
) {
1501 strcat(q
->name
, ".");
1502 strcat(q
->name
, searchpath
[q
->domain
].domain
);
1503 debugs(78, 3, "idnsALookup: searchpath used for " << q
->name
);
1506 if (Ip::EnableIpv6
) {
1507 q
->sz
= rfc3596BuildAAAAQuery(q
->name
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1510 q
->sz
= rfc3596BuildAQuery(q
->name
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1515 /* problem with query data -- query not sent */
1516 callback(data
, NULL
, 0, "Internal error");
1521 debugs(78, 3, "idnsALookup: buf is " << q
->sz
<< " bytes for " << q
->name
<<
1522 ", id = 0x" << std::hex
<< q
->id
);
1524 q
->callback
= callback
;
1526 q
->callback_data
= cbdataReference(data
);
1528 q
->start_t
= current_time
;
1536 idnsPTRLookup(const Ip::Address
&addr
, IDNSCB
* callback
, void *data
)
1540 char ip
[MAX_IPSTRLEN
];
1542 addr
.NtoA(ip
,MAX_IPSTRLEN
);
1544 q
= cbdataAlloc(idns_query
);
1546 q
->id
= idnsQueryID();
1548 if (Ip::EnableIpv6
&& addr
.IsIPv6()) {
1549 struct in6_addr addr6
;
1550 addr
.GetInAddr(addr6
);
1551 q
->sz
= rfc3596BuildPTRQuery6(addr6
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1553 struct in_addr addr4
;
1554 addr
.GetInAddr(addr4
);
1555 q
->sz
= rfc3596BuildPTRQuery4(addr4
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1558 /* PTR does not do inbound A/AAAA */
1562 /* problem with query data -- query not sent */
1563 callback(data
, NULL
, 0, "Internal error");
1568 if (idnsCachedLookup(q
->query
.name
, callback
, data
)) {
1573 debugs(78, 3, "idnsPTRLookup: buf is " << q
->sz
<< " bytes for " << ip
<<
1574 ", id = 0x" << std::hex
<< q
->id
);
1576 q
->callback
= callback
;
1578 q
->callback_data
= cbdataReference(data
);
1580 q
->start_t
= current_time
;
1589 * The function to return the DNS via SNMP
1592 snmp_netIdnsFn(variable_list
* Var
, snint
* ErrP
)
1595 variable_list
*Answer
= NULL
;
1597 debugs(49, 5, "snmp_netDnsFn: Processing request: " << snmpDebugOid(Var
->name
, Var
->name_length
, tmp
));
1598 *ErrP
= SNMP_ERR_NOERROR
;
1600 switch (Var
->name
[LEN_SQ_NET
+ 1]) {
1604 for (i
= 0; i
< nns
; i
++)
1605 n
+= nameservers
[i
].nqueries
;
1607 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1614 for (i
= 0; i
< nns
; i
++)
1615 n
+= nameservers
[i
].nreplies
;
1617 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1624 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1631 *ErrP
= SNMP_ERR_NOSUCHNAME
;
1639 #endif /*SQUID_SNMP */
1640 #endif /* USE_DNSSERVERS */