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
;
130 Comm::ConnectionPointer conn
;
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
;
181 static IOCB idnsReadVCHeader
;
182 static void idnsDoSendQueryVC(nsvc
*vc
);
184 static int idnsFromKnownNameserver(Ip::Address
const &from
);
185 static idns_query
*idnsFindQuery(unsigned short id
);
186 static void idnsGrokReply(const char *buf
, size_t sz
);
188 static EVH idnsCheckQueue
;
189 static void idnsTickleQueue(void);
190 static void idnsRcodeCount(int, int);
191 static void idnsVCClosed(int fd
, void *data
);
194 idnsAddNameserver(const char *buf
)
199 debugs(78, DBG_CRITICAL
, "WARNING: rejecting '" << buf
<< "' as a name server, because it is not a numeric IP address");
204 debugs(78, DBG_CRITICAL
, "WARNING: Squid does not accept " << A
<< " in DNS server specifications.");
206 debugs(78, DBG_CRITICAL
, "Will be using " << A
<< " instead, assuming you meant that DNS is running on the same machine");
209 if (!Ip::EnableIpv6
&& !A
.SetIPv4()) {
210 debugs(78, DBG_IMPORTANT
, "WARNING: IPv6 is disabled. Discarding " << A
<< " in DNS server specifications.");
214 if (nns
== nns_alloc
) {
215 int oldalloc
= nns_alloc
;
216 ns
*oldptr
= nameservers
;
223 nameservers
= (ns
*)xcalloc(nns_alloc
, sizeof(*nameservers
));
225 if (oldptr
&& oldalloc
)
226 xmemcpy(nameservers
, oldptr
, oldalloc
* sizeof(*nameservers
));
232 assert(nns
< nns_alloc
);
233 A
.SetPort(NS_DEFAULTPORT
);
234 nameservers
[nns
].S
= A
;
235 debugs(78, 3, "idnsAddNameserver: Added nameserver #" << nns
<< " (" << A
<< ")");
240 idnsAddPathComponent(const char *buf
)
242 if (npc
== npc_alloc
) {
243 int oldalloc
= npc_alloc
;
244 sp
*oldptr
= searchpath
;
251 searchpath
= (sp
*)xcalloc(npc_alloc
, sizeof(*searchpath
));
253 if (oldptr
&& oldalloc
)
254 xmemcpy(searchpath
, oldptr
, oldalloc
* sizeof(*searchpath
));
260 assert(npc
< npc_alloc
);
261 strcpy(searchpath
[npc
].domain
, buf
);
262 debugs(78, 3, "idnsAddPathComponent: Added domain #" << npc
<< ": " << searchpath
[npc
].domain
);
268 idnsFreeNameservers(void)
270 safe_free(nameservers
);
275 idnsFreeSearchpath(void)
277 safe_free(searchpath
);
284 idnsParseNameservers(void)
288 for (w
= Config
.dns_nameservers
; w
; w
= w
->next
) {
289 debugs(78, 1, "Adding nameserver " << w
->key
<< " from squid.conf");
290 idnsAddNameserver(w
->key
);
294 #ifndef _SQUID_MSWIN_
296 idnsParseResolvConf(void)
299 char buf
[RESOLV_BUFSZ
];
301 fp
= fopen(_PATH_RESCONF
, "r");
304 debugs(78, 1, "" << _PATH_RESCONF
<< ": " << xstrerror());
308 #if defined(_SQUID_CYGWIN_)
309 setmode(fileno(fp
), O_TEXT
);
313 while (fgets(buf
, RESOLV_BUFSZ
, fp
)) {
314 t
= strtok(buf
, w_space
);
318 } else if (strcasecmp(t
, "nameserver") == 0) {
319 t
= strtok(NULL
, w_space
);
324 debugs(78, 1, "Adding nameserver " << t
<< " from " << _PATH_RESCONF
);
326 idnsAddNameserver(t
);
327 } else if (strcasecmp(t
, "domain") == 0) {
328 idnsFreeSearchpath();
329 t
= strtok(NULL
, w_space
);
334 debugs(78, 1, "Adding domain " << t
<< " from " << _PATH_RESCONF
);
336 idnsAddPathComponent(t
);
337 } else if (strcasecmp(t
, "search") == 0) {
338 idnsFreeSearchpath();
340 t
= strtok(NULL
, w_space
);
345 debugs(78, 1, "Adding domain " << t
<< " from " << _PATH_RESCONF
);
347 idnsAddPathComponent(t
);
349 } else if (strcasecmp(t
, "options") == 0) {
351 t
= strtok(NULL
, w_space
);
356 if (strncmp(t
, "ndots:", 6) == 0) {
362 debugs(78, 1, "Adding ndots " << ndots
<< " from " << _PATH_RESCONF
);
367 if (npc
== 0 && (t
= getMyHostname())) {
370 idnsAddPathComponent(t
+1);
380 idnsParseWIN32SearchList(const char * Separator
)
386 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REG_TCPIP_PARA
, 0, KEY_QUERY_VALUE
, &hndKey
) == ERROR_SUCCESS
) {
390 Result
= RegQueryValueEx(hndKey
, "Domain", NULL
, &Type
, NULL
, &Size
);
392 if (Result
== ERROR_SUCCESS
&& Size
) {
393 t
= (char *) xmalloc(Size
);
394 RegQueryValueEx(hndKey
, "Domain", NULL
, &Type
, (LPBYTE
) t
, &Size
);
395 debugs(78, 1, "Adding domain " << t
<< " from Registry");
396 idnsAddPathComponent(t
);
399 Result
= RegQueryValueEx(hndKey
, "SearchList", NULL
, &Type
, NULL
, &Size
);
401 if (Result
== ERROR_SUCCESS
&& Size
) {
402 t
= (char *) xmalloc(Size
);
403 RegQueryValueEx(hndKey
, "SearchList", NULL
, &Type
, (LPBYTE
) t
, &Size
);
404 token
= strtok(t
, Separator
);
407 idnsAddPathComponent(token
);
408 debugs(78, 1, "Adding domain " << token
<< " from Registry");
409 token
= strtok(NULL
, Separator
);
416 if (npc
== 0 && (t
= (char *) getMyHostname())) {
419 idnsAddPathComponent(t
+ 1);
424 idnsParseWIN32Registry(void)
428 HKEY hndKey
, hndKey2
;
430 switch (WIN32_OS_version
) {
433 /* get nameservers from the Windows NT registry */
435 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REG_TCPIP_PARA
, 0, KEY_QUERY_VALUE
, &hndKey
) == ERROR_SUCCESS
) {
439 Result
= RegQueryValueEx(hndKey
, "DhcpNameServer", NULL
, &Type
, NULL
, &Size
);
441 if (Result
== ERROR_SUCCESS
&& Size
) {
442 t
= (char *) xmalloc(Size
);
443 RegQueryValueEx(hndKey
, "DhcpNameServer", NULL
, &Type
, (LPBYTE
) t
, &Size
);
444 token
= strtok(t
, ", ");
447 idnsAddNameserver(token
);
448 debugs(78, 1, "Adding DHCP nameserver " << token
<< " from Registry");
449 token
= strtok(NULL
, ",");
454 Result
= RegQueryValueEx(hndKey
, "NameServer", NULL
, &Type
, NULL
, &Size
);
456 if (Result
== ERROR_SUCCESS
&& Size
) {
457 t
= (char *) xmalloc(Size
);
458 RegQueryValueEx(hndKey
, "NameServer", NULL
, &Type
, (LPBYTE
) t
, &Size
);
459 token
= strtok(t
, ", ");
462 debugs(78, 1, "Adding nameserver " << token
<< " from Registry");
463 idnsAddNameserver(token
);
464 token
= strtok(NULL
, ", ");
472 idnsParseWIN32SearchList(" ");
485 /* get nameservers from the Windows 2000 registry */
486 /* search all interfaces for DNS server addresses */
488 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REG_TCPIP_PARA_INTERFACES
, 0, KEY_READ
, &hndKey
) == ERROR_SUCCESS
) {
490 DWORD MaxSubkeyLen
, InterfacesCount
;
492 FILETIME ftLastWriteTime
;
494 if (RegQueryInfoKey(hndKey
, NULL
, NULL
, NULL
, &InterfacesCount
, &MaxSubkeyLen
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
) == ERROR_SUCCESS
) {
495 keyname
= (char *) xmalloc(++MaxSubkeyLen
);
496 for (i
= 0; i
< (int) InterfacesCount
; i
++) {
499 if (RegEnumKeyEx(hndKey
, i
, keyname
, &j
, NULL
, NULL
, NULL
, &ftLastWriteTime
) == ERROR_SUCCESS
) {
501 newkeyname
= (char *) xmalloc(sizeof(REG_TCPIP_PARA_INTERFACES
) + j
+ 2);
502 strcpy(newkeyname
, REG_TCPIP_PARA_INTERFACES
);
503 strcat(newkeyname
, "\\");
504 strcat(newkeyname
, keyname
);
505 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, newkeyname
, 0, KEY_QUERY_VALUE
, &hndKey2
) == ERROR_SUCCESS
) {
509 Result
= RegQueryValueEx(hndKey2
, "DhcpNameServer", NULL
, &Type
, NULL
, &Size
);
510 if (Result
== ERROR_SUCCESS
&& Size
) {
511 t
= (char *) xmalloc(Size
);
512 RegQueryValueEx(hndKey2
, "DhcpNameServer", NULL
, &Type
, (LPBYTE
)t
, &Size
);
513 token
= strtok(t
, ", ");
515 debugs(78, 1, "Adding DHCP nameserver " << token
<< " from Registry");
516 idnsAddNameserver(token
);
517 token
= strtok(NULL
, ", ");
522 Result
= RegQueryValueEx(hndKey2
, "NameServer", NULL
, &Type
, NULL
, &Size
);
523 if (Result
== ERROR_SUCCESS
&& Size
) {
524 t
= (char *) xmalloc(Size
);
525 RegQueryValueEx(hndKey2
, "NameServer", NULL
, &Type
, (LPBYTE
)t
, &Size
);
526 token
= strtok(t
, ", ");
528 debugs(78, 1, "Adding nameserver " << token
<< " from Registry");
529 idnsAddNameserver(token
);
530 token
= strtok(NULL
, ", ");
536 RegCloseKey(hndKey2
);
549 idnsParseWIN32SearchList(", ");
558 /* get nameservers from the Windows 9X registry */
560 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, REG_VXD_MSTCP
, 0, KEY_QUERY_VALUE
, &hndKey
) == ERROR_SUCCESS
) {
564 Result
= RegQueryValueEx(hndKey
, "NameServer", NULL
, &Type
, NULL
, &Size
);
566 if (Result
== ERROR_SUCCESS
&& Size
) {
567 t
= (char *) xmalloc(Size
);
568 RegQueryValueEx(hndKey
, "NameServer", NULL
, &Type
, (LPBYTE
) t
, &Size
);
569 token
= strtok(t
, ", ");
572 debugs(78, 1, "Adding nameserver " << token
<< " from Registry");
573 idnsAddNameserver(token
);
574 token
= strtok(NULL
, ", ");
585 debugs(78, 1, "Failed to read nameserver from Registry: Unknown System Type.");
593 idnsStats(StoreEntry
* sentry
)
599 char buf
[MAX_IPSTRLEN
];
600 storeAppendPrintf(sentry
, "Internal DNS Statistics:\n");
601 storeAppendPrintf(sentry
, "\nThe Queue:\n");
602 storeAppendPrintf(sentry
, " DELAY SINCE\n");
603 storeAppendPrintf(sentry
, " ID SIZE SENDS FIRST SEND LAST SEND\n");
604 storeAppendPrintf(sentry
, "------ ---- ----- ---------- ---------\n");
606 for (n
= lru_list
.head
; n
; n
= n
->next
) {
607 q
= (idns_query
*)n
->data
;
608 storeAppendPrintf(sentry
, "%#06x %4d %5d %10.3f %9.3f\n",
609 (int) q
->id
, (int) q
->sz
, q
->nsends
,
610 tvSubDsec(q
->start_t
, current_time
),
611 tvSubDsec(q
->sent_t
, current_time
));
614 storeAppendPrintf(sentry
, "\nNameservers:\n");
615 storeAppendPrintf(sentry
, "IP ADDRESS # QUERIES # REPLIES\n");
616 storeAppendPrintf(sentry
, "---------------------------------------------- --------- ---------\n");
618 for (i
= 0; i
< nns
; i
++) {
619 storeAppendPrintf(sentry
, "%-45s %9d %9d\n", /* Let's take the maximum: (15 IPv4/45 IPv6) */
620 nameservers
[i
].S
.NtoA(buf
,MAX_IPSTRLEN
),
621 nameservers
[i
].nqueries
,
622 nameservers
[i
].nreplies
);
625 storeAppendPrintf(sentry
, "\nRcode Matrix:\n");
626 storeAppendPrintf(sentry
, "RCODE");
628 for (i
= 0; i
< MAX_ATTEMPT
; i
++)
629 storeAppendPrintf(sentry
, " ATTEMPT%d", i
+ 1);
631 storeAppendPrintf(sentry
, "\n");
633 for (j
= 0; j
< MAX_RCODE
; j
++) {
634 storeAppendPrintf(sentry
, "%5d", j
);
636 for (i
= 0; i
< MAX_ATTEMPT
; i
++)
637 storeAppendPrintf(sentry
, " %8d", RcodeMatrix
[j
][i
]);
639 storeAppendPrintf(sentry
, "\n");
643 storeAppendPrintf(sentry
, "\nSearch list:\n");
645 for (i
=0; i
< npc
; i
++)
646 storeAppendPrintf(sentry
, "%s\n", searchpath
[i
].domain
);
648 storeAppendPrintf(sentry
, "\n");
653 idnsTickleQueue(void)
658 if (NULL
== lru_list
.tail
)
661 eventAdd("idnsCheckQueue", idnsCheckQueue
, NULL
, 1.0, 1);
667 idnsSentQueryVC(int fd
, char *buf
, size_t size
, comm_err_t flag
, int xerrno
, void *data
)
669 nsvc
* vc
= (nsvc
*)data
;
671 if (flag
== COMM_ERR_CLOSING
)
674 if (fd_table
[fd
].closing())
677 if (flag
!= COMM_OK
|| size
<= 0) {
683 idnsDoSendQueryVC(vc
);
687 idnsDoSendQueryVC(nsvc
*vc
)
692 if (vc
->queue
->contentSize() == 0)
695 MemBuf
*mb
= vc
->queue
;
697 vc
->queue
= new MemBuf
;
701 commSetTimeout(vc
->conn
->fd
, Config
.Timeout
.idns_query
, NULL
, NULL
);
703 comm_write_mbuf(vc
->conn
->fd
, mb
, idnsSentQueryVC
, vc
);
709 idnsInitVCConnected(const Comm::ConnectionPointer
&conn
, comm_err_t status
, int xerrno
, void *data
)
711 nsvc
* vc
= (nsvc
*)data
;
713 if (status
!= COMM_OK
|| !conn
) {
714 char buf
[MAX_IPSTRLEN
] = "";
716 nameservers
[vc
->ns
].S
.NtoA(buf
,MAX_IPSTRLEN
);
717 debugs(78, 1, HERE
<< "Failed to connect to nameserver " << buf
<< " using TCP.");
723 comm_add_close_handler(conn
->fd
, idnsVCClosed
, vc
);
724 comm_read(conn
->fd
, (char *)&vc
->msglen
, 2 , idnsReadVCHeader
, vc
);
726 idnsDoSendQueryVC(vc
);
730 idnsVCClosed(int fd
, void *data
)
732 nsvc
* vc
= (nsvc
*)data
;
736 if (vc
->ns
< nns
) // XXX: idnsShutdown may have freed nameservers[]
737 nameservers
[vc
->ns
].vc
= NULL
;
744 nsvc
*vc
= cbdataAlloc(nsvc
);
746 assert(vc
->conn
== NULL
); // MUST be NULL from the construction process!
747 nameservers
[ns
].vc
= vc
;
749 vc
->queue
= new MemBuf
;
750 vc
->msg
= new MemBuf
;
753 Comm::ConnectionPointer conn
= new Comm::Connection();
755 if (!Config
.Addrs
.udp_outgoing
.IsNoAddr())
756 conn
->local
= Config
.Addrs
.udp_outgoing
;
758 conn
->local
= Config
.Addrs
.udp_incoming
;
760 conn
->remote
= nameservers
[ns
].S
;
762 AsyncCall::Pointer call
= commCbCall(78,3, "idnsInitVCConnected", CommConnectCbPtrFun(idnsInitVCConnected
, vc
));
764 Comm::ConnOpener
*cs
= new Comm::ConnOpener(conn
, call
, Config
.Timeout
.connect
);
765 cs
->setHost("DNS TCP Socket");
770 idnsSendQueryVC(idns_query
* q
, int ns
)
773 if (nameservers
[ns
].vc
== NULL
)
776 nsvc
*vc
= nameservers
[ns
].vc
;
779 char buf
[MAX_IPSTRLEN
];
780 debugs(78, 1, "idnsSendQuery: Failed to initiate TCP connection to nameserver " << nameservers
[ns
].S
.NtoA(buf
,MAX_IPSTRLEN
) << "!");
787 short head
= htons(q
->sz
);
789 vc
->queue
->append((char *)&head
, 2);
791 vc
->queue
->append(q
->buf
, q
->sz
);
793 idnsDoSendQueryVC(vc
);
797 idnsSendQuery(idns_query
* q
)
799 if (DnsSocketA
< 0 && DnsSocketB
< 0) {
800 debugs(78, 1, "WARNING: idnsSendQuery: Can't send query, no DNS socket!");
805 debugs(78, 1, "WARNING: idnsSendQuery: Can't send query, no DNS nameservers known!");
809 assert(q
->lru
.next
== NULL
);
811 assert(q
->lru
.prev
== NULL
);
817 ns
= q
->nsends
% nns
;
820 idnsSendQueryVC(q
, ns
);
823 if (DnsSocketB
>= 0 && nameservers
[ns
].S
.IsIPv6())
824 y
= comm_udp_sendto(DnsSocketB
, nameservers
[ns
].S
, q
->buf
, q
->sz
);
826 x
= comm_udp_sendto(DnsSocketA
, nameservers
[ns
].S
, q
->buf
, q
->sz
);
831 q
->queue_t
= q
->sent_t
= current_time
;
833 if (y
< 0 && nameservers
[ns
].S
.IsIPv6())
834 debugs(50, 1, "idnsSendQuery: FD " << DnsSocketB
<< ": sendto: " << xstrerror());
835 if (x
< 0 && nameservers
[ns
].S
.IsIPv4())
836 debugs(50, 1, "idnsSendQuery: FD " << DnsSocketA
<< ": sendto: " << xstrerror());
838 } while ( (x
<0 && y
<0) && q
->nsends
% nns
!= 0);
841 fd_bytes(DnsSocketB
, y
, FD_WRITE
);
844 fd_bytes(DnsSocketA
, x
, FD_WRITE
);
847 nameservers
[ns
].nqueries
++;
848 q
->queue_t
= current_time
;
849 dlinkAdd(q
, &q
->lru
, &lru_list
);
854 idnsFromKnownNameserver(Ip::Address
const &from
)
858 for (i
= 0; i
< nns
; i
++) {
859 if (nameservers
[i
].S
!= from
)
862 if (nameservers
[i
].S
.GetPort() != from
.GetPort())
872 idnsFindQuery(unsigned short id
)
877 for (n
= lru_list
.tail
; n
; n
= n
->prev
) {
878 q
= (idns_query
*)n
->data
;
887 static unsigned short
890 unsigned short id
= squid_random() & 0xFFFF;
891 unsigned short first_id
= id
;
893 while (idnsFindQuery(id
)) {
896 if (id
== first_id
) {
897 debugs(78, 1, "idnsQueryID: Warning, too many pending DNS requests");
906 idnsCallback(idns_query
*q
, rfc1035_rr
*answers
, int n
, const char *error
)
911 callback
= q
->callback
;
914 if (cbdataReferenceValidDone(q
->callback_data
, &cbdata
))
915 callback(cbdata
, answers
, n
, error
);
918 idns_query
*q2
= q
->queue
;
919 q
->queue
= q2
->queue
;
920 callback
= q2
->callback
;
923 if (cbdataReferenceValidDone(q2
->callback_data
, &cbdata
))
924 callback(cbdata
, answers
, n
, error
);
930 hash_remove_link(idns_lookup_hash
, &q
->hash
);
936 idnsDropMessage(rfc1035_message
*message
, idns_query
*q
)
938 rfc1035MessageDestroy(&message
);
940 hash_remove_link(idns_lookup_hash
, &q
->hash
);
946 idnsGrokReply(const char *buf
, size_t sz
)
949 rfc1035_message
*message
= NULL
;
952 n
= rfc1035MessageUnpack(buf
, sz
, &message
);
954 if (message
== NULL
) {
955 debugs(78, 1, "idnsGrokReply: Malformed DNS response");
959 debugs(78, 3, "idnsGrokReply: ID 0x" << std::hex
<< message
->id
<< ", " << std::dec
<< n
<< " answers");
961 q
= idnsFindQuery(message
->id
);
964 debugs(78, 3, "idnsGrokReply: Late response");
965 rfc1035MessageDestroy(&message
);
969 if (rfc1035QueryCompare(&q
->query
, message
->query
) != 0) {
970 debugs(78, 3, "idnsGrokReply: Query mismatch (" << q
->query
.name
<< " != " << message
->query
->name
<< ")");
971 rfc1035MessageDestroy(&message
);
976 debugs(78, 3, HERE
<< "Resolver requested TC (" << q
->query
.name
<< ")");
977 dlinkDelete(&q
->lru
, &lru_list
);
978 rfc1035MessageDestroy(&message
);
989 dlinkDelete(&q
->lru
, &lru_list
);
990 idnsRcodeCount(n
, q
->attempt
);
994 debugs(78, 3, "idnsGrokReply: error " << rfc1035ErrorMessage(n
) << " (" << q
->rcode
<< ")");
996 if (q
->rcode
== 2 && ++q
->attempt
< MAX_ATTEMPT
) {
998 * RCODE 2 is "Server failure - The name server was
999 * unable to process this query due to a problem with
1002 debugs(78, 3, "idnsGrokReply: Query result: SERV_FAIL");
1003 rfc1035MessageDestroy(&message
);
1004 q
->start_t
= current_time
;
1005 q
->id
= idnsQueryID();
1006 rfc1035SetQueryID(q
->buf
, q
->id
);
1011 if (q
->rcode
== 3 && q
->do_searchpath
&& q
->attempt
< MAX_ATTEMPT
) {
1012 assert(NULL
== message
->answer
);
1013 strcpy(q
->name
, q
->orig
);
1015 debugs(78, 3, "idnsGrokReply: Query result: NXDOMAIN - " << q
->name
);
1017 if (q
->domain
< npc
) {
1018 strcat(q
->name
, ".");
1019 strcat(q
->name
, searchpath
[q
->domain
].domain
);
1020 debugs(78, 3, "idnsGrokReply: searchpath used for " << q
->name
);
1026 idnsDropMessage(message
, q
);
1028 q
->start_t
= current_time
;
1029 q
->id
= idnsQueryID();
1030 rfc1035SetQueryID(q
->buf
, q
->id
);
1031 if (Ip::EnableIpv6
&& q
->query
.qtype
== RFC1035_TYPE_AAAA
) {
1032 debugs(78, 3, "idnsGrokReply: Trying AAAA Query for " << q
->name
);
1033 q
->sz
= rfc3596BuildAAAAQuery(q
->name
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1035 debugs(78, 3, "idnsGrokReply: Trying A Query for " << q
->name
);
1036 q
->sz
= rfc3596BuildAQuery(q
->name
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1044 if (q
->need_A
&& (Config
.onoff
.dns_require_A
== 1 || n
<= 0 ) ) {
1045 /* ERROR or NO AAAA exist. Failover to A records. */
1046 /* Apparently its also a good idea to lookup and store the A records
1047 * just in case the AAAA are not available when we need them.
1048 * This could occur due to number of network failings beyond our control
1049 * thus the || above allowing the user to request always both.
1053 debugs(78, 3, "idnsGrokReply: " << q
->name
<< " has no AAAA records. Looking up A record instead.");
1054 else if (q
->need_A
&& n
<= 0)
1055 debugs(78, 3, "idnsGrokReply: " << q
->name
<< " AAAA query failed. Trying A now instead.");
1056 else // admin requested this.
1057 debugs(78, 3, "idnsGrokReply: " << q
->name
<< " AAAA query done. Configured to retrieve A now also.");
1059 // move the initial message results into the failover query for merging later.
1061 q
->initial_AAAA
.count
= message
->ancount
;
1062 q
->initial_AAAA
.answers
= message
->answer
;
1063 message
->answer
= NULL
;
1066 // remove the hashed query info
1067 idnsDropMessage(message
, q
);
1069 // reset the query as an A query
1071 q
->start_t
= current_time
;
1072 q
->id
= idnsQueryID();
1073 rfc1035SetQueryID(q
->buf
, q
->id
);
1074 q
->sz
= rfc3596BuildAQuery(q
->name
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1081 /** If there are two result sets from preceeding AAAA and A lookups merge them with a preference for AAAA */
1082 if (q
->initial_AAAA
.count
> 0 && n
> 0) {
1083 /* two sets of RR need merging */
1084 rfc1035_rr
*result
= (rfc1035_rr
*) xmalloc( sizeof(rfc1035_rr
)*(n
+ q
->initial_AAAA
.count
) );
1085 rfc1035_rr
*tmp
= result
;
1087 debugs(78, 6, HERE
<< "Merging DNS results " << q
->name
<< " AAAA has " << q
->initial_AAAA
.count
<< " RR, A has " << n
<< " RR");
1089 memcpy(tmp
, q
->initial_AAAA
.answers
, (sizeof(rfc1035_rr
)*(q
->initial_AAAA
.count
)) );
1090 tmp
+= q
->initial_AAAA
.count
;
1091 /* free the RR object without freeing its child strings (they are now taken by the copy above) */
1092 safe_free(q
->initial_AAAA
.answers
);
1094 memcpy( tmp
, message
->answer
, (sizeof(rfc1035_rr
)*n
) );
1095 /* free the RR object without freeing its child strings (they are now taken by the copy above) */
1096 safe_free(message
->answer
);
1098 message
->answer
= result
;
1099 message
->ancount
+= q
->initial_AAAA
.count
;
1100 n
+= q
->initial_AAAA
.count
;
1101 q
->initial_AAAA
.count
=0;
1102 } else if (q
->initial_AAAA
.count
> 0 && n
<= 0) {
1103 /* initial of dual queries was the only result set. */
1104 debugs(78, 6, HERE
<< "Merging DNS results " << q
->name
<< " AAAA has " << q
->initial_AAAA
.count
<< " RR, A has " << n
<< " RR");
1105 rfc1035RRDestroy(&(message
->answer
), n
);
1106 message
->answer
= q
->initial_AAAA
.answers
;
1107 n
= q
->initial_AAAA
.count
;
1109 /* else initial results were empty. just use the final set as authoritative */
1111 debugs(78, 6, HERE
<< "Sending " << n
<< " DNS results to caller.");
1112 idnsCallback(q
, message
->answer
, n
, rfc1035ErrorMessage(n
));
1113 rfc1035MessageDestroy(&message
);
1118 idnsRead(int fd
, void *data
)
1120 int *N
= &incoming_sockets_accepted
;
1122 int max
= INCOMING_DNS_MAX
;
1123 static char rbuf
[SQUID_UDP_SO_RCVBUF
];
1127 debugs(78, 3, "idnsRead: starting with FD " << fd
);
1129 // Always keep reading. This stops (or at least makes harder) several
1130 // attacks on the DNS client.
1131 commSetSelect(fd
, COMM_SELECT_READ
, idnsRead
, NULL
, 0);
1134 * two code lines after returning from comm_udprecvfrom()
1135 * something overwrites the memory behind the from parameter.
1136 * NO matter where in the stack declaration list above it is placed
1137 * The cause of this is still unknown, however copying the data appears
1138 * to allow it to be passed further without this erasure.
1140 Ip::Address bugbypass
;
1143 len
= comm_udp_recvfrom(fd
, rbuf
, SQUID_UDP_SO_RCVBUF
, 0, bugbypass
);
1145 from
= bugbypass
; // BUG BYPASS. see notes above.
1151 if (ignoreErrno(errno
))
1154 #ifdef _SQUID_LINUX_
1155 /* Some Linux systems seem to set the FD for reading and then
1156 * return ECONNREFUSED when sendto() fails and generates an ICMP
1157 * port unreachable message. */
1158 /* or maybe an EHOSTUNREACH "No route to host" message */
1159 if (errno
!= ECONNREFUSED
&& errno
!= EHOSTUNREACH
)
1162 debugs(50, 1, "idnsRead: FD " << fd
<< " recvfrom: " << xstrerror());
1167 fd_bytes(fd
, len
, FD_READ
);
1172 debugs(78, 3, "idnsRead: FD " << fd
<< ": received " << len
<< " bytes from " << from
);
1174 /* BUG: see above. Its here that it becomes apparent that the content of bugbypass is gone. */
1175 ns
= idnsFromKnownNameserver(from
);
1178 nameservers
[ns
].nreplies
++;
1181 // Before unknown_nameservers check to avoid flooding cache.log on attacks,
1182 // but after the ++ above to keep statistics right.
1184 continue; // Don't process replies if there is no pending query.
1186 if (ns
< 0 && Config
.onoff
.ignore_unknown_nameservers
) {
1187 static time_t last_warning
= 0;
1189 if (squid_curtime
- last_warning
> 60) {
1190 debugs(78, 1, "WARNING: Reply from unknown nameserver " << from
);
1191 last_warning
= squid_curtime
;
1193 debugs(78, 1, "WARNING: Reply from unknown nameserver " << from
<< " (retrying..." << (squid_curtime
-last_warning
) << "<=60)" );
1198 idnsGrokReply(rbuf
, len
);
1203 idnsCheckQueue(void *unused
)
1206 dlink_node
*p
= NULL
;
1211 /* name servers went away; reconfiguring or shutting down */
1214 for (n
= lru_list
.tail
; n
; n
= p
) {
1217 q
= static_cast<idns_query
*>(n
->data
);
1219 /* Anything to process in the queue? */
1220 if (tvSubDsec(q
->queue_t
, current_time
) < Config
.Timeout
.idns_retransmit
)
1223 /* Query timer expired? */
1224 if (tvSubDsec(q
->sent_t
, current_time
) < Config
.Timeout
.idns_retransmit
* 1 << ((q
->nsends
- 1) / nns
)) {
1225 dlinkDelete(&q
->lru
, &lru_list
);
1226 q
->queue_t
= current_time
;
1227 dlinkAdd(q
, &q
->lru
, &lru_list
);
1231 debugs(78, 3, "idnsCheckQueue: ID 0x" << std::hex
<< std::setfill('0') << std::setw(4) << q
->id
<< "timeout" );
1233 dlinkDelete(&q
->lru
, &lru_list
);
1235 if (tvSubDsec(q
->start_t
, current_time
) < Config
.Timeout
.idns_query
) {
1238 debugs(78, 2, "idnsCheckQueue: ID " << std::hex
<< q
->id
<<
1239 ": giving up after " << std::dec
<< q
->nsends
<< " tries and " <<
1240 std::setw(5)<< std::setprecision(2) << tvSubDsec(q
->start_t
, current_time
) << " seconds");
1243 idnsCallback(q
, NULL
, -q
->rcode
, rfc1035ErrorMessage(q
->rcode
));
1245 idnsCallback(q
, NULL
, -16, "Timeout");
1255 idnsReadVC(int fd
, char *buf
, size_t len
, comm_err_t flag
, int xerrno
, void *data
)
1257 nsvc
* vc
= (nsvc
*)data
;
1259 if (flag
== COMM_ERR_CLOSING
)
1262 if (flag
!= COMM_OK
|| len
<= 0) {
1267 vc
->msg
->size
+= len
; // XXX should not access -> size directly
1269 if (vc
->msg
->contentSize() < vc
->msglen
) {
1270 comm_read(fd
, buf
+ len
, vc
->msglen
- vc
->msg
->contentSize(), idnsReadVC
, vc
);
1274 assert(vc
->ns
< nns
);
1275 debugs(78, 3, "idnsReadVC: FD " << fd
<< ": received " <<
1276 (int) vc
->msg
->contentSize() << " bytes via tcp from " <<
1277 nameservers
[vc
->ns
].S
<< ".");
1279 idnsGrokReply(vc
->msg
->buf
, vc
->msg
->contentSize());
1281 comm_read(fd
, (char *)&vc
->msglen
, 2 , idnsReadVCHeader
, vc
);
1285 idnsReadVCHeader(int fd
, char *buf
, size_t len
, comm_err_t flag
, int xerrno
, void *data
)
1287 nsvc
* vc
= (nsvc
*)data
;
1289 if (flag
== COMM_ERR_CLOSING
)
1292 if (flag
!= COMM_OK
|| len
<= 0) {
1297 vc
->read_msglen
+= len
;
1299 assert(vc
->read_msglen
<= 2);
1301 if (vc
->read_msglen
< 2) {
1302 comm_read(fd
, buf
+ len
, 2 - vc
->read_msglen
, idnsReadVCHeader
, vc
);
1306 vc
->read_msglen
= 0;
1308 vc
->msglen
= ntohs(vc
->msglen
);
1310 vc
->msg
->init(vc
->msglen
, vc
->msglen
);
1311 comm_read(fd
, vc
->msg
->buf
, vc
->msglen
, idnsReadVC
, vc
);
1315 * rcode < 0 indicates an error, rocde >= 0 indicates success
1318 idnsRcodeCount(int rcode
, int attempt
)
1325 if (rcode
< MAX_RCODE
)
1326 if (attempt
< MAX_ATTEMPT
)
1327 RcodeMatrix
[rcode
][attempt
]++;
1330 /* ====================================================================== */
1333 idnsRegisterWithCacheManager(void)
1335 CacheManager::GetInstance()->
1336 registerAction("idns", "Internal DNS Statistics", idnsStats
, 0, 1);
1342 static int init
= 0;
1344 CBDATA_INIT_TYPE(nsvc
);
1345 CBDATA_INIT_TYPE(idns_query
);
1347 if (DnsSocketA
< 0 && DnsSocketB
< 0) {
1350 Ip::Address addrA
; // since we don't want to alter Config.Addrs.udp_* and dont have one of our own.
1352 if (!Config
.Addrs
.udp_outgoing
.IsNoAddr())
1353 addrA
= Config
.Addrs
.udp_outgoing
;
1355 addrA
= Config
.Addrs
.udp_incoming
;
1357 Ip::Address addrB
= addrA
;
1360 if (Ip::EnableIpv6
&& (addrB
.IsAnyAddr() || addrB
.IsIPv6())) {
1361 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrB
);
1362 DnsSocketB
= comm_open_listener(SOCK_DGRAM
,
1369 if (addrA
.IsAnyAddr() || addrA
.IsIPv4()) {
1370 debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrA
);
1371 DnsSocketA
= comm_open_listener(SOCK_DGRAM
,
1378 if (DnsSocketA
< 0 && DnsSocketB
< 0)
1379 fatal("Could not create a DNS socket");
1381 /* Ouch... we can't call functions using debug from a debug
1382 * statement. Doing so messes up the internal Debug::level
1384 if (DnsSocketB
>= 0) {
1385 port
= comm_local_port(DnsSocketB
);
1386 debugs(78, 1, "DNS Socket created at " << addrB
<< ", FD " << DnsSocketB
);
1387 commSetSelect(DnsSocketB
, COMM_SELECT_READ
, idnsRead
, NULL
, 0);
1389 if (DnsSocketA
>= 0) {
1390 port
= comm_local_port(DnsSocketA
);
1391 debugs(78, 1, "DNS Socket created at " << addrA
<< ", FD " << DnsSocketA
);
1392 commSetSelect(DnsSocketA
, COMM_SELECT_READ
, idnsRead
, NULL
, 0);
1397 idnsParseNameservers();
1398 #ifndef _SQUID_MSWIN_
1401 idnsParseResolvConf();
1404 #ifdef _SQUID_WIN32_
1407 idnsParseWIN32Registry();
1412 debugs(78, 1, "Warning: Could not find any nameservers. Trying to use localhost");
1413 #ifdef _SQUID_WIN32_
1415 debugs(78, 1, "Please check your TCP-IP settings or /etc/resolv.conf file");
1418 debugs(78, 1, "Please check your /etc/resolv.conf file");
1421 debugs(78, 1, "or use the 'dns_nameservers' option in squid.conf.");
1422 idnsAddNameserver("127.0.0.1");
1426 memDataInit(MEM_IDNS_QUERY
, "idns_query", sizeof(idns_query
), 0);
1427 memset(RcodeMatrix
, '\0', sizeof(RcodeMatrix
));
1428 idns_lookup_hash
= hash_create((HASHCMP
*) strcmp
, 103, hash_string
);
1432 idnsRegisterWithCacheManager();
1438 if (DnsSocketA
< 0 && DnsSocketB
< 0)
1441 if (DnsSocketA
>= 0 ) {
1442 comm_close(DnsSocketA
);
1446 if (DnsSocketB
>= 0 ) {
1447 comm_close(DnsSocketB
);
1451 for (int i
= 0; i
< nns
; i
++) {
1452 if (nsvc
*vc
= nameservers
[i
].vc
) {
1453 if (Comm::IsConnOpen(vc
->conn
))
1458 // XXX: vcs are not closed/freed yet and may try to access nameservers[]
1459 idnsFreeNameservers();
1460 idnsFreeSearchpath();
1464 idnsCachedLookup(const char *key
, IDNSCB
* callback
, void *data
)
1468 idns_query
*old
= (idns_query
*) hash_lookup(idns_lookup_hash
, key
);
1473 q
= cbdataAlloc(idns_query
);
1475 q
->callback
= callback
;
1477 q
->callback_data
= cbdataReference(data
);
1479 q
->queue
= old
->queue
;
1487 idnsCacheQuery(idns_query
*q
)
1489 q
->hash
.key
= q
->query
.name
;
1490 hash_join(idns_lookup_hash
, &q
->hash
);
1494 idnsALookup(const char *name
, IDNSCB
* callback
, void *data
)
1500 if (idnsCachedLookup(name
, callback
, data
))
1503 q
= cbdataAlloc(idns_query
);
1505 q
->id
= idnsQueryID();
1507 for (i
= 0; i
< strlen(name
); i
++)
1511 if (Config
.onoff
.res_defnames
&& npc
> 0 && name
[strlen(name
)-1] != '.') {
1512 q
->do_searchpath
= 1;
1514 q
->do_searchpath
= 0;
1517 strcpy(q
->orig
, name
);
1518 strcpy(q
->name
, q
->orig
);
1520 if (q
->do_searchpath
&& nd
< ndots
) {
1522 strcat(q
->name
, ".");
1523 strcat(q
->name
, searchpath
[q
->domain
].domain
);
1524 debugs(78, 3, "idnsALookup: searchpath used for " << q
->name
);
1527 if (Ip::EnableIpv6
) {
1528 q
->sz
= rfc3596BuildAAAAQuery(q
->name
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1531 q
->sz
= rfc3596BuildAQuery(q
->name
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1536 /* problem with query data -- query not sent */
1537 callback(data
, NULL
, 0, "Internal error");
1542 debugs(78, 3, "idnsALookup: buf is " << q
->sz
<< " bytes for " << q
->name
<<
1543 ", id = 0x" << std::hex
<< q
->id
);
1545 q
->callback
= callback
;
1547 q
->callback_data
= cbdataReference(data
);
1549 q
->start_t
= current_time
;
1557 idnsPTRLookup(const Ip::Address
&addr
, IDNSCB
* callback
, void *data
)
1561 char ip
[MAX_IPSTRLEN
];
1563 addr
.NtoA(ip
,MAX_IPSTRLEN
);
1565 q
= cbdataAlloc(idns_query
);
1567 q
->id
= idnsQueryID();
1569 if (Ip::EnableIpv6
&& addr
.IsIPv6()) {
1570 struct in6_addr addr6
;
1571 addr
.GetInAddr(addr6
);
1572 q
->sz
= rfc3596BuildPTRQuery6(addr6
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1574 struct in_addr addr4
;
1575 addr
.GetInAddr(addr4
);
1576 q
->sz
= rfc3596BuildPTRQuery4(addr4
, q
->buf
, sizeof(q
->buf
), q
->id
, &q
->query
);
1579 /* PTR does not do inbound A/AAAA */
1583 /* problem with query data -- query not sent */
1584 callback(data
, NULL
, 0, "Internal error");
1589 if (idnsCachedLookup(q
->query
.name
, callback
, data
)) {
1594 debugs(78, 3, "idnsPTRLookup: buf is " << q
->sz
<< " bytes for " << ip
<<
1595 ", id = 0x" << std::hex
<< q
->id
);
1597 q
->callback
= callback
;
1599 q
->callback_data
= cbdataReference(data
);
1601 q
->start_t
= current_time
;
1610 * The function to return the DNS via SNMP
1613 snmp_netIdnsFn(variable_list
* Var
, snint
* ErrP
)
1616 variable_list
*Answer
= NULL
;
1618 debugs(49, 5, "snmp_netDnsFn: Processing request: " << snmpDebugOid(Var
->name
, Var
->name_length
, tmp
));
1619 *ErrP
= SNMP_ERR_NOERROR
;
1621 switch (Var
->name
[LEN_SQ_NET
+ 1]) {
1625 for (i
= 0; i
< nns
; i
++)
1626 n
+= nameservers
[i
].nqueries
;
1628 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1635 for (i
= 0; i
< nns
; i
++)
1636 n
+= nameservers
[i
].nreplies
;
1638 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1645 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1652 *ErrP
= SNMP_ERR_NOSUCHNAME
;
1660 #endif /*SQUID_SNMP */
1661 #endif /* USE_DNSSERVERS */