]>
Commit | Line | Data |
---|---|---|
7b724b86 | 1 | |
2 | /* | |
846c8960 | 3 | * $Id: dns_internal.cc,v 1.77 2005/05/11 19:22:20 hno Exp $ |
7b724b86 | 4 | * |
5 | * DEBUG: section 78 DNS lookups; interacts with lib/rfc1035.c | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
7b724b86 | 9 | * ---------------------------------------------------------- |
10 | * | |
2b6662ba | 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. | |
7b724b86 | 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 "squid.h" | |
e6ccf245 | 37 | #include "Store.h" |
063dc1eb | 38 | #include "comm.h" |
7b724b86 | 39 | |
1f1ae50a | 40 | /* MS VisualStudio Projects are monolitich, so we need the following |
41 | #ifndef to exclude the internal DNS code from compile process when | |
42 | using external DNS process. | |
43 | */ | |
44 | #ifndef USE_DNSSERVERS | |
ec4daaa5 | 45 | #ifdef _SQUID_WIN32_ |
1a774556 | 46 | #include "squid_windows.h" |
0e6d05ef | 47 | #endif |
7b724b86 | 48 | #ifndef _PATH_RESOLV_CONF |
49 | #define _PATH_RESOLV_CONF "/etc/resolv.conf" | |
50 | #endif | |
51 | #ifndef DOMAIN_PORT | |
52 | #define DOMAIN_PORT 53 | |
53 | #endif | |
54 | ||
42b51993 | 55 | #define IDNS_MAX_TRIES 20 |
558be27a | 56 | #define MAX_RCODE 6 |
57 | #define MAX_ATTEMPT 3 | |
58 | static int RcodeMatrix[MAX_RCODE][MAX_ATTEMPT]; | |
59 | ||
58a39dc9 | 60 | typedef struct _idns_query idns_query; |
62e76326 | 61 | |
7b724b86 | 62 | typedef struct _ns ns; |
58a39dc9 | 63 | |
62e76326 | 64 | struct _idns_query |
65 | { | |
3074b9d1 | 66 | hash_link hash; |
9e1f210d | 67 | rfc1035_query query; |
58a39dc9 | 68 | char buf[512]; |
69 | size_t sz; | |
70 | unsigned short id; | |
71 | int nsends; | |
62e76326 | 72 | |
58a39dc9 | 73 | struct timeval start_t; |
62e76326 | 74 | |
58a39dc9 | 75 | struct timeval sent_t; |
76 | dlink_node lru; | |
77 | IDNSCB *callback; | |
78 | void *callback_data; | |
558be27a | 79 | int attempt; |
7ba8d0b8 | 80 | const char *error; |
81 | int rcode; | |
3074b9d1 | 82 | idns_query *queue; |
58a39dc9 | 83 | }; |
84 | ||
62e76326 | 85 | struct _ns |
86 | { | |
87 | ||
7b724b86 | 88 | struct sockaddr_in S; |
89 | int nqueries; | |
90 | int nreplies; | |
d29b40de | 91 | int large_pkts; |
7b724b86 | 92 | }; |
58a39dc9 | 93 | |
7b724b86 | 94 | static ns *nameservers = NULL; |
95 | static int nns = 0; | |
96 | static int nns_alloc = 0; | |
7b724b86 | 97 | static dlink_list lru_list; |
7cfc1c9a | 98 | static int event_queued = 0; |
3074b9d1 | 99 | static hash_table *idns_lookup_hash = NULL; |
7b724b86 | 100 | |
101 | static OBJH idnsStats; | |
102 | static void idnsAddNameserver(const char *buf); | |
103 | static void idnsFreeNameservers(void); | |
efd900cb | 104 | static void idnsParseNameservers(void); |
1f1ae50a | 105 | #ifndef _SQUID_MSWIN_ |
7b724b86 | 106 | static void idnsParseResolvConf(void); |
1f1ae50a | 107 | #endif |
ec4daaa5 | 108 | #ifdef _SQUID_WIN32_ |
0e6d05ef | 109 | static void idnsParseWIN32Registry(void); |
110 | #endif | |
7b724b86 | 111 | static void idnsSendQuery(idns_query * q); |
62e76326 | 112 | |
7b724b86 | 113 | static int idnsFromKnownNameserver(struct sockaddr_in *from); |
114 | static idns_query *idnsFindQuery(unsigned short id); | |
115 | static void idnsGrokReply(const char *buf, size_t sz); | |
116 | static PF idnsRead; | |
7cfc1c9a | 117 | static EVH idnsCheckQueue; |
efd900cb | 118 | static void idnsTickleQueue(void); |
558be27a | 119 | static void idnsRcodeCount(int, int); |
7b724b86 | 120 | |
121 | static void | |
122 | idnsAddNameserver(const char *buf) | |
123 | { | |
62e76326 | 124 | |
ddfcbc22 | 125 | struct IN_ADDR A; |
62e76326 | 126 | |
d20b1cd0 | 127 | if (!safe_inet_addr(buf, &A)) { |
62e76326 | 128 | debug(78, 0) ("WARNING: rejecting '%s' as a name server, because it is not a numeric IP address\n", buf); |
129 | return; | |
d20b1cd0 | 130 | } |
62e76326 | 131 | |
5c5a349d | 132 | if (A.s_addr == 0) { |
62e76326 | 133 | debug(78, 0) ("WARNING: Squid does not accept 0.0.0.0 in DNS server specifications.\n"); |
134 | debug(78, 0) ("Will be using 127.0.0.1 instead, assuming you meant that DNS is running on the same machine\n"); | |
135 | safe_inet_addr("127.0.0.1", &A); | |
5c5a349d | 136 | } |
62e76326 | 137 | |
7b724b86 | 138 | if (nns == nns_alloc) { |
62e76326 | 139 | int oldalloc = nns_alloc; |
140 | ns *oldptr = nameservers; | |
141 | ||
142 | if (nns_alloc == 0) | |
143 | nns_alloc = 2; | |
144 | else | |
145 | nns_alloc <<= 1; | |
146 | ||
147 | nameservers = (ns *)xcalloc(nns_alloc, sizeof(*nameservers)); | |
148 | ||
149 | if (oldptr && oldalloc) | |
150 | xmemcpy(nameservers, oldptr, oldalloc * sizeof(*nameservers)); | |
151 | ||
152 | if (oldptr) | |
153 | safe_free(oldptr); | |
7b724b86 | 154 | } |
62e76326 | 155 | |
7b724b86 | 156 | assert(nns < nns_alloc); |
157 | nameservers[nns].S.sin_family = AF_INET; | |
158 | nameservers[nns].S.sin_port = htons(DOMAIN_PORT); | |
d20b1cd0 | 159 | nameservers[nns].S.sin_addr.s_addr = A.s_addr; |
efd900cb | 160 | debug(78, 3) ("idnsAddNameserver: Added nameserver #%d: %s\n", |
62e76326 | 161 | nns, inet_ntoa(nameservers[nns].S.sin_addr)); |
7b724b86 | 162 | nns++; |
163 | } | |
164 | ||
165 | static void | |
166 | idnsFreeNameservers(void) | |
167 | { | |
168 | safe_free(nameservers); | |
169 | nns = nns_alloc = 0; | |
170 | } | |
171 | ||
efd900cb | 172 | static void |
173 | idnsParseNameservers(void) | |
174 | { | |
175 | wordlist *w; | |
62e76326 | 176 | |
efd900cb | 177 | for (w = Config.dns_nameservers; w; w = w->next) { |
62e76326 | 178 | debug(78, 1) ("Adding nameserver %s from squid.conf\n", w->key); |
179 | idnsAddNameserver(w->key); | |
efd900cb | 180 | } |
181 | } | |
182 | ||
1f1ae50a | 183 | #ifndef _SQUID_MSWIN_ |
7b724b86 | 184 | static void |
185 | idnsParseResolvConf(void) | |
186 | { | |
187 | FILE *fp; | |
188 | char buf[512]; | |
189 | char *t; | |
190 | fp = fopen(_PATH_RESOLV_CONF, "r"); | |
62e76326 | 191 | |
7b724b86 | 192 | if (fp == NULL) { |
62e76326 | 193 | debug(78, 1) ("%s: %s\n", _PATH_RESOLV_CONF, xstrerror()); |
194 | return; | |
7b724b86 | 195 | } |
62e76326 | 196 | |
1f1ae50a | 197 | #if defined(_SQUID_CYGWIN_) |
c4aefe96 | 198 | setmode(fileno(fp), O_TEXT); |
62e76326 | 199 | |
c4aefe96 | 200 | #endif |
62e76326 | 201 | |
7b724b86 | 202 | while (fgets(buf, 512, fp)) { |
62e76326 | 203 | t = strtok(buf, w_space); |
204 | ||
205 | if (NULL == t) | |
206 | continue; | |
207 | ||
208 | if (strcasecmp(t, "nameserver")) | |
209 | continue; | |
210 | ||
211 | t = strtok(NULL, w_space); | |
212 | ||
213 | if (t == NULL) | |
214 | continue; | |
215 | ||
216 | debug(78, 1) ("Adding nameserver %s from %s\n", t, _PATH_RESOLV_CONF); | |
217 | ||
218 | idnsAddNameserver(t); | |
7b724b86 | 219 | } |
62e76326 | 220 | |
7b724b86 | 221 | fclose(fp); |
222 | } | |
223 | ||
1f1ae50a | 224 | #endif |
225 | ||
ec4daaa5 | 226 | #ifdef _SQUID_WIN32_ |
0e6d05ef | 227 | static void |
228 | idnsParseWIN32Registry(void) | |
229 | { | |
e6ccf245 | 230 | BYTE *t; |
0e6d05ef | 231 | char *token; |
232 | HKEY hndKey, hndKey2; | |
233 | ||
234 | idnsFreeNameservers(); | |
62e76326 | 235 | |
0e6d05ef | 236 | switch (WIN32_OS_version) { |
62e76326 | 237 | |
0e6d05ef | 238 | case _WIN_OS_WINNT: |
62e76326 | 239 | /* get nameservers from the Windows NT registry */ |
240 | ||
241 | if (RegOpenKey(HKEY_LOCAL_MACHINE, | |
242 | "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", | |
243 | &hndKey) == ERROR_SUCCESS) { | |
244 | DWORD Type = 0; | |
245 | DWORD Size = 0; | |
246 | LONG Result; | |
247 | Result = | |
248 | RegQueryValueEx(hndKey, "DhcpNameServer", NULL, &Type, NULL, | |
249 | &Size); | |
250 | ||
251 | if (Result == ERROR_SUCCESS && Size) { | |
252 | t = (unsigned char *) xmalloc(Size); | |
253 | RegQueryValueEx(hndKey, "DhcpNameServer", NULL, &Type, t, | |
254 | &Size); | |
255 | token = strtok((char *) t, ", "); | |
256 | ||
257 | while (token) { | |
258 | idnsAddNameserver(token); | |
259 | debug(78, 1) ("Adding DHCP nameserver %s from Registry\n", | |
260 | token); | |
261 | token = strtok(NULL, ", "); | |
262 | } | |
263 | } | |
264 | ||
265 | Result = | |
266 | RegQueryValueEx(hndKey, "NameServer", NULL, &Type, NULL, &Size); | |
267 | ||
268 | if (Result == ERROR_SUCCESS && Size) { | |
269 | t = (unsigned char *) xmalloc(Size); | |
270 | RegQueryValueEx(hndKey, "NameServer", NULL, &Type, t, &Size); | |
271 | token = strtok((char *) t, ", "); | |
272 | ||
273 | while (token) { | |
274 | debug(78, 1) ("Adding nameserver %s from Registry\n", | |
275 | token); | |
276 | idnsAddNameserver(token); | |
277 | token = strtok(NULL, ", "); | |
278 | } | |
279 | } | |
280 | ||
281 | RegCloseKey(hndKey); | |
282 | } | |
283 | ||
284 | break; | |
285 | ||
0e6d05ef | 286 | case _WIN_OS_WIN2K: |
62e76326 | 287 | |
b671cc68 | 288 | case _WIN_OS_WINXP: |
62e76326 | 289 | |
6b1846cf | 290 | case _WIN_OS_WINNET: |
62e76326 | 291 | /* get nameservers from the Windows 2000 registry */ |
292 | /* search all interfaces for DNS server addresses */ | |
293 | ||
294 | if (RegOpenKey(HKEY_LOCAL_MACHINE, | |
295 | "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces", | |
296 | &hndKey) == ERROR_SUCCESS) { | |
297 | int i; | |
298 | char keyname[255]; | |
299 | ||
300 | for (i = 0; i < 10; i++) { | |
301 | if (RegEnumKey(hndKey, i, (char *) &keyname, | |
302 | 255) == ERROR_SUCCESS) { | |
303 | char newkeyname[255]; | |
304 | strcpy(newkeyname, | |
305 | "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\"); | |
306 | strcat(newkeyname, keyname); | |
307 | ||
308 | if (RegOpenKey(HKEY_LOCAL_MACHINE, newkeyname, | |
309 | &hndKey2) == ERROR_SUCCESS) { | |
310 | DWORD Type = 0; | |
311 | DWORD Size = 0; | |
312 | LONG Result; | |
313 | Result = | |
314 | RegQueryValueEx(hndKey2, "DhcpNameServer", NULL, | |
315 | &Type, NULL, &Size); | |
316 | ||
317 | if (Result == ERROR_SUCCESS && Size) { | |
318 | t = (unsigned char *) xmalloc(Size); | |
319 | RegQueryValueEx(hndKey2, "DhcpNameServer", NULL, | |
320 | &Type, t, &Size); | |
321 | token = strtok((char *) t, ", "); | |
322 | ||
323 | while (token) { | |
324 | debug(78, 1) | |
325 | ("Adding DHCP nameserver %s from Registry\n", | |
326 | token); | |
327 | idnsAddNameserver(token); | |
328 | token = strtok(NULL, ", "); | |
329 | } | |
330 | } | |
331 | ||
332 | Result = | |
333 | RegQueryValueEx(hndKey2, "NameServer", NULL, &Type, | |
334 | NULL, &Size); | |
335 | ||
336 | if (Result == ERROR_SUCCESS && Size) { | |
337 | t = (unsigned char *) xmalloc(Size); | |
338 | RegQueryValueEx(hndKey2, "NameServer", NULL, &Type, | |
339 | t, &Size); | |
340 | token = strtok((char *) t, ", "); | |
341 | ||
342 | while (token) { | |
343 | debug(78, | |
344 | 1) ("Adding nameserver %s from Registry\n", | |
345 | token); | |
346 | idnsAddNameserver(token); | |
347 | token = strtok(NULL, ", "); | |
348 | } | |
349 | } | |
350 | ||
351 | RegCloseKey(hndKey2); | |
352 | } | |
353 | } | |
354 | } | |
355 | ||
356 | RegCloseKey(hndKey); | |
357 | } | |
358 | ||
359 | break; | |
360 | ||
0e6d05ef | 361 | case _WIN_OS_WIN95: |
62e76326 | 362 | |
0e6d05ef | 363 | case _WIN_OS_WIN98: |
62e76326 | 364 | |
be20dac7 | 365 | case _WIN_OS_WINME: |
62e76326 | 366 | /* get nameservers from the Windows 9X registry */ |
367 | ||
368 | if (RegOpenKey(HKEY_LOCAL_MACHINE, | |
369 | "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP", | |
370 | &hndKey) == ERROR_SUCCESS) { | |
371 | DWORD Type = 0; | |
372 | DWORD Size = 0; | |
373 | LONG Result; | |
374 | Result = | |
375 | RegQueryValueEx(hndKey, "NameServer", NULL, &Type, NULL, &Size); | |
376 | ||
377 | if (Result == ERROR_SUCCESS && Size) { | |
378 | t = (unsigned char *) xmalloc(Size); | |
379 | RegQueryValueEx(hndKey, "NameServer", NULL, &Type, t, &Size); | |
380 | token = strtok((char *) t, ", "); | |
381 | ||
382 | while (token) { | |
383 | debug(78, 1) ("Adding nameserver %s from Registry\n", | |
384 | token); | |
385 | idnsAddNameserver(token); | |
386 | token = strtok(NULL, ", "); | |
387 | } | |
388 | } | |
389 | ||
390 | RegCloseKey(hndKey); | |
391 | } | |
392 | ||
393 | break; | |
394 | ||
0e6d05ef | 395 | default: |
62e76326 | 396 | debug(78, 1) |
397 | ("Failed to read nameserver from Registry: Unknown System Type.\n"); | |
398 | return; | |
0e6d05ef | 399 | } |
400 | } | |
62e76326 | 401 | |
0e6d05ef | 402 | #endif |
403 | ||
7b724b86 | 404 | static void |
405 | idnsStats(StoreEntry * sentry) | |
406 | { | |
a16b4aa0 | 407 | dlink_node *n; |
408 | idns_query *q; | |
7cfc1c9a | 409 | int i; |
558be27a | 410 | int j; |
7b724b86 | 411 | storeAppendPrintf(sentry, "Internal DNS Statistics:\n"); |
a16b4aa0 | 412 | storeAppendPrintf(sentry, "\nThe Queue:\n"); |
0a69973e | 413 | storeAppendPrintf(sentry, " DELAY SINCE\n"); |
414 | storeAppendPrintf(sentry, " ID SIZE SENDS FIRST SEND LAST SEND\n"); | |
415 | storeAppendPrintf(sentry, "------ ---- ----- ---------- ---------\n"); | |
62e76326 | 416 | |
a16b4aa0 | 417 | for (n = lru_list.head; n; n = n->next) { |
62e76326 | 418 | q = (idns_query *)n->data; |
419 | storeAppendPrintf(sentry, "%#06x %4d %5d %10.3f %9.3f\n", | |
420 | (int) q->id, (int) q->sz, q->nsends, | |
421 | tvSubDsec(q->start_t, current_time), | |
422 | tvSubDsec(q->sent_t, current_time)); | |
7cfc1c9a | 423 | } |
62e76326 | 424 | |
7cfc1c9a | 425 | storeAppendPrintf(sentry, "\nNameservers:\n"); |
426 | storeAppendPrintf(sentry, "IP ADDRESS # QUERIES # REPLIES\n"); | |
427 | storeAppendPrintf(sentry, "--------------- --------- ---------\n"); | |
62e76326 | 428 | |
7cfc1c9a | 429 | for (i = 0; i < nns; i++) { |
62e76326 | 430 | storeAppendPrintf(sentry, "%-15s %9d %9d\n", |
431 | inet_ntoa(nameservers[i].S.sin_addr), | |
432 | nameservers[i].nqueries, | |
433 | nameservers[i].nreplies); | |
a16b4aa0 | 434 | } |
62e76326 | 435 | |
558be27a | 436 | storeAppendPrintf(sentry, "\nRcode Matrix:\n"); |
437 | storeAppendPrintf(sentry, "RCODE"); | |
62e76326 | 438 | |
558be27a | 439 | for (i = 0; i < MAX_ATTEMPT; i++) |
62e76326 | 440 | storeAppendPrintf(sentry, " ATTEMPT%d", i + 1); |
441 | ||
7928b8df | 442 | storeAppendPrintf(sentry, "\n"); |
62e76326 | 443 | |
558be27a | 444 | for (j = 0; j < MAX_RCODE; j++) { |
62e76326 | 445 | storeAppendPrintf(sentry, "%5d", j); |
446 | ||
447 | for (i = 0; i < MAX_ATTEMPT; i++) | |
448 | storeAppendPrintf(sentry, " %8d", RcodeMatrix[j][i]); | |
449 | ||
450 | storeAppendPrintf(sentry, "\n"); | |
558be27a | 451 | } |
7b724b86 | 452 | } |
453 | ||
efd900cb | 454 | static void |
455 | idnsTickleQueue(void) | |
456 | { | |
457 | if (event_queued) | |
62e76326 | 458 | return; |
459 | ||
efd900cb | 460 | if (NULL == lru_list.tail) |
62e76326 | 461 | return; |
462 | ||
efd900cb | 463 | eventAdd("idnsCheckQueue", idnsCheckQueue, NULL, 1.0, 1); |
62e76326 | 464 | |
efd900cb | 465 | event_queued = 1; |
466 | } | |
467 | ||
7b724b86 | 468 | static void |
469 | idnsSendQuery(idns_query * q) | |
470 | { | |
471 | int x; | |
ef523f99 | 472 | int ns; |
62e76326 | 473 | |
0a69973e | 474 | if (DnsSocket < 0) { |
62e76326 | 475 | debug(78, 1) ("idnsSendQuery: Can't send query, no DNS socket!\n"); |
476 | return; | |
0a69973e | 477 | } |
62e76326 | 478 | |
7b724b86 | 479 | /* XXX Select nameserver */ |
480 | assert(nns > 0); | |
62e76326 | 481 | |
7b724b86 | 482 | assert(q->lru.next == NULL); |
62e76326 | 483 | |
7b724b86 | 484 | assert(q->lru.prev == NULL); |
62e76326 | 485 | |
486 | try_again: | |
ef523f99 | 487 | ns = q->nsends % nns; |
62e76326 | 488 | |
ef523f99 | 489 | x = comm_udp_sendto(DnsSocket, |
62e76326 | 490 | &nameservers[ns].S, |
491 | sizeof(nameservers[ns].S), | |
492 | q->buf, | |
493 | q->sz); | |
494 | ||
4fe0e1d0 | 495 | q->nsends++; |
62e76326 | 496 | |
4fe0e1d0 | 497 | q->sent_t = current_time; |
62e76326 | 498 | |
0a69973e | 499 | if (x < 0) { |
62e76326 | 500 | debug(50, 1) ("idnsSendQuery: FD %d: sendto: %s\n", |
501 | DnsSocket, xstrerror()); | |
502 | ||
503 | if (q->nsends % nns != 0) | |
504 | goto try_again; | |
0a69973e | 505 | } else { |
62e76326 | 506 | fd_bytes(DnsSocket, x, FD_WRITE); |
507 | commSetSelect(DnsSocket, COMM_SELECT_READ, idnsRead, NULL, 0); | |
0a69973e | 508 | } |
62e76326 | 509 | |
7cfc1c9a | 510 | nameservers[ns].nqueries++; |
7b724b86 | 511 | dlinkAdd(q, &q->lru, &lru_list); |
efd900cb | 512 | idnsTickleQueue(); |
7b724b86 | 513 | } |
514 | ||
515 | static int | |
62e76326 | 516 | |
7b724b86 | 517 | idnsFromKnownNameserver(struct sockaddr_in *from) |
518 | { | |
519 | int i; | |
62e76326 | 520 | |
521 | for (i = 0; i < nns; i++) | |
522 | { | |
523 | if (nameservers[i].S.sin_addr.s_addr != from->sin_addr.s_addr) | |
524 | continue; | |
525 | ||
526 | if (nameservers[i].S.sin_port != from->sin_port) | |
527 | continue; | |
528 | ||
529 | return i; | |
7b724b86 | 530 | } |
62e76326 | 531 | |
7cfc1c9a | 532 | return -1; |
7b724b86 | 533 | } |
534 | ||
535 | static idns_query * | |
536 | idnsFindQuery(unsigned short id) | |
537 | { | |
538 | dlink_node *n; | |
539 | idns_query *q; | |
62e76326 | 540 | |
7b724b86 | 541 | for (n = lru_list.tail; n; n = n->prev) { |
62e76326 | 542 | q = (idns_query*)n->data; |
543 | ||
544 | if (q->id == id) | |
545 | return q; | |
7b724b86 | 546 | } |
62e76326 | 547 | |
7b724b86 | 548 | return NULL; |
549 | } | |
550 | ||
108d67a0 | 551 | static unsigned short |
552 | idnsQueryID(void) | |
553 | { | |
554 | unsigned short id = squid_random() & 0xFFFF; | |
555 | unsigned short first_id = id; | |
556 | ||
557 | while(idnsFindQuery(id)) { | |
558 | id++; | |
559 | ||
846c8960 | 560 | if (id == first_id) { |
561 | debug(78, 1) ("idnsQueryID: Warning, too many pending DNS requests\n"); | |
108d67a0 | 562 | break; |
846c8960 | 563 | } |
108d67a0 | 564 | } |
565 | ||
846c8960 | 566 | return id; |
108d67a0 | 567 | } |
568 | ||
3074b9d1 | 569 | static void |
570 | idnsCallback(idns_query *q, rfc1035_rr *answers, int n, const char *error) | |
571 | { | |
572 | IDNSCB *callback; | |
573 | void *cbdata; | |
574 | ||
575 | callback = q->callback; | |
576 | q->callback = NULL; | |
577 | ||
578 | if (cbdataReferenceValidDone(q->callback_data, &cbdata)) | |
579 | callback(cbdata, answers, n, error); | |
580 | ||
581 | while(q->queue) { | |
582 | idns_query *q2 = q->queue; | |
583 | q->queue = q2->queue; | |
584 | callback = q2->callback; | |
585 | q2->callback = NULL; | |
586 | ||
587 | if (cbdataReferenceValidDone(q2->callback_data, &cbdata)) | |
588 | callback(cbdata, answers, n, error); | |
589 | ||
590 | memFree(q2, MEM_IDNS_QUERY); | |
591 | } | |
592 | ||
593 | if (q->hash.key) { | |
594 | hash_remove_link(idns_lookup_hash, &q->hash); | |
595 | q->hash.key = NULL; | |
596 | } | |
597 | } | |
598 | ||
7b724b86 | 599 | static void |
600 | idnsGrokReply(const char *buf, size_t sz) | |
601 | { | |
602 | int n; | |
ec7bade0 | 603 | rfc1035_message *message = NULL; |
7b724b86 | 604 | idns_query *q; |
3074b9d1 | 605 | |
ec7bade0 | 606 | n = rfc1035MessageUnpack(buf, |
62e76326 | 607 | sz, |
ec7bade0 | 608 | &message); |
62e76326 | 609 | |
ec7bade0 | 610 | if (message == NULL) { |
4f3b04b7 | 611 | debug(78, 1) ("idnsGrokReply: Malformed DNS response\n"); |
62e76326 | 612 | return; |
7b724b86 | 613 | } |
62e76326 | 614 | |
ec7bade0 | 615 | debug(78, 3) ("idnsGrokReply: ID %#hx, %d answers\n", message->id, n); |
616 | ||
617 | q = idnsFindQuery(message->id); | |
618 | ||
7b724b86 | 619 | if (q == NULL) { |
62e76326 | 620 | debug(78, 3) ("idnsGrokReply: Late response\n"); |
ec7bade0 | 621 | rfc1035MessageDestroy(message); |
62e76326 | 622 | return; |
7b724b86 | 623 | } |
62e76326 | 624 | |
9e1f210d | 625 | if (rfc1035QueryCompare(&q->query, message->query) != 0) { |
626 | debug(78, 3) ("idnsGrokReply: Query mismatch (%s != %s)\n", q->query.name, message->query->name); | |
627 | rfc1035MessageDestroy(message); | |
628 | return; | |
629 | } | |
630 | ||
631 | ||
a16b4aa0 | 632 | dlinkDelete(&q->lru, &lru_list); |
558be27a | 633 | idnsRcodeCount(n, q->attempt); |
7ba8d0b8 | 634 | q->error = NULL; |
62e76326 | 635 | |
558be27a | 636 | if (n < 0) { |
ec7bade0 | 637 | debug(78, 3) ("idnsGrokReply: error %s (%d)\n", rfc1035_error_message, rfc1035_errno); |
62e76326 | 638 | |
7ba8d0b8 | 639 | q->error = rfc1035_error_message; |
640 | q->rcode = -n; | |
641 | ||
642 | if (q->rcode == 2 && ++q->attempt < MAX_ATTEMPT) { | |
62e76326 | 643 | /* |
644 | * RCODE 2 is "Server failure - The name server was | |
645 | * unable to process this query due to a problem with | |
646 | * the name server." | |
647 | */ | |
ec7bade0 | 648 | rfc1035MessageDestroy(message); |
62e76326 | 649 | q->start_t = current_time; |
108d67a0 | 650 | q->id = idnsQueryID(); |
651 | rfc1035SetQueryID(q->buf, q->id); | |
62e76326 | 652 | idnsSendQuery(q); |
653 | return; | |
654 | } | |
558be27a | 655 | } |
62e76326 | 656 | |
ec7bade0 | 657 | idnsCallback(q, message->answer, n, q->error); |
658 | rfc1035MessageDestroy(message); | |
62e76326 | 659 | |
a16b4aa0 | 660 | memFree(q, MEM_IDNS_QUERY); |
7b724b86 | 661 | } |
662 | ||
663 | static void | |
664 | idnsRead(int fd, void *data) | |
665 | { | |
d193a436 | 666 | int *N = &incoming_sockets_accepted; |
7b724b86 | 667 | ssize_t len; |
62e76326 | 668 | |
7b724b86 | 669 | struct sockaddr_in from; |
670 | socklen_t from_len; | |
42b51993 | 671 | int max = INCOMING_DNS_MAX; |
d29b40de | 672 | static char rbuf[SQUID_UDP_SO_RCVBUF]; |
7cfc1c9a | 673 | int ns; |
62e76326 | 674 | |
7b724b86 | 675 | while (max--) { |
62e76326 | 676 | from_len = sizeof(from); |
677 | memset(&from, '\0', from_len); | |
678 | ||
0343293b | 679 | len = comm_udp_recvfrom(fd, rbuf, sizeof(rbuf), 0, (struct sockaddr *) &from, &from_len); |
62e76326 | 680 | |
681 | if (len == 0) | |
682 | break; | |
683 | ||
684 | if (len < 0) { | |
685 | if (ignoreErrno(errno)) | |
686 | break; | |
687 | ||
7b724b86 | 688 | #ifdef _SQUID_LINUX_ |
62e76326 | 689 | /* Some Linux systems seem to set the FD for reading and then |
690 | * return ECONNREFUSED when sendto() fails and generates an ICMP | |
691 | * port unreachable message. */ | |
692 | /* or maybe an EHOSTUNREACH "No route to host" message */ | |
693 | if (errno != ECONNREFUSED && errno != EHOSTUNREACH) | |
7b724b86 | 694 | #endif |
62e76326 | 695 | |
696 | debug(50, 1) ("idnsRead: FD %d recvfrom: %s\n", | |
697 | fd, xstrerror()); | |
698 | ||
699 | break; | |
700 | } | |
701 | ||
702 | fd_bytes(DnsSocket, len, FD_READ); | |
703 | assert(N); | |
704 | (*N)++; | |
705 | debug(78, 3) ("idnsRead: FD %d: received %d bytes from %s.\n", | |
706 | fd, | |
707 | (int) len, | |
708 | inet_ntoa(from.sin_addr)); | |
709 | ns = idnsFromKnownNameserver(&from); | |
710 | ||
711 | if (ns >= 0) { | |
712 | nameservers[ns].nreplies++; | |
713 | } else if (Config.onoff.ignore_unknown_nameservers) { | |
714 | static time_t last_warning = 0; | |
715 | ||
716 | if (squid_curtime - last_warning > 60) { | |
717 | debug(78, 1) ("WARNING: Reply from unknown nameserver [%s]\n", | |
718 | inet_ntoa(from.sin_addr)); | |
719 | last_warning = squid_curtime; | |
720 | } | |
721 | ||
722 | continue; | |
723 | } | |
724 | ||
62e76326 | 725 | idnsGrokReply(rbuf, len); |
7b724b86 | 726 | } |
62e76326 | 727 | |
2e6caa54 | 728 | if (lru_list.head) |
62e76326 | 729 | commSetSelect(DnsSocket, COMM_SELECT_READ, idnsRead, NULL, 0); |
7b724b86 | 730 | } |
731 | ||
7cfc1c9a | 732 | static void |
733 | idnsCheckQueue(void *unused) | |
734 | { | |
735 | dlink_node *n; | |
0c4fe9e4 | 736 | dlink_node *p = NULL; |
7cfc1c9a | 737 | idns_query *q; |
738 | event_queued = 0; | |
62e76326 | 739 | |
0c4fe9e4 | 740 | for (n = lru_list.tail; n; n = p) { |
62e76326 | 741 | if (0 == nns) |
742 | /* name servers went away; reconfiguring or shutting down */ | |
743 | break; | |
744 | ||
745 | q = (idns_query *)n->data; | |
746 | ||
da0c4437 | 747 | if (tvSubDsec(q->sent_t, current_time) < Config.Timeout.idns_retransmit * (1 << (q->nsends - 1) % nns)) |
62e76326 | 748 | break; |
749 | ||
750 | debug(78, 3) ("idnsCheckQueue: ID %#04x timeout\n", | |
751 | q->id); | |
752 | ||
753 | p = n->prev; | |
754 | ||
755 | dlinkDelete(&q->lru, &lru_list); | |
756 | ||
757 | if (tvSubDsec(q->start_t, current_time) < Config.Timeout.idns_query) { | |
758 | idnsSendQuery(q); | |
759 | } else { | |
62e76326 | 760 | debug(78, 2) ("idnsCheckQueue: ID %x: giving up after %d tries and %5.1f seconds\n", |
761 | (int) q->id, q->nsends, | |
762 | tvSubDsec(q->start_t, current_time)); | |
3074b9d1 | 763 | |
764 | if (q->rcode != 0) | |
765 | idnsCallback(q, NULL, -q->rcode, q->error); | |
766 | else | |
767 | idnsCallback(q, NULL, -16, "Timeout"); | |
62e76326 | 768 | |
769 | memFree(q, MEM_IDNS_QUERY); | |
770 | } | |
7cfc1c9a | 771 | } |
62e76326 | 772 | |
efd900cb | 773 | idnsTickleQueue(); |
7cfc1c9a | 774 | } |
775 | ||
558be27a | 776 | /* |
777 | * rcode < 0 indicates an error, rocde >= 0 indicates success | |
778 | */ | |
779 | static void | |
780 | idnsRcodeCount(int rcode, int attempt) | |
781 | { | |
782 | if (rcode > 0) | |
62e76326 | 783 | rcode = 0; |
558be27a | 784 | else if (rcode < 0) |
62e76326 | 785 | rcode = -rcode; |
786 | ||
558be27a | 787 | if (rcode < MAX_RCODE) |
62e76326 | 788 | if (attempt < MAX_ATTEMPT) |
789 | RcodeMatrix[rcode][attempt]++; | |
558be27a | 790 | } |
791 | ||
7b724b86 | 792 | /* ====================================================================== */ |
793 | ||
794 | void | |
795 | idnsInit(void) | |
796 | { | |
797 | static int init = 0; | |
62e76326 | 798 | |
ef523f99 | 799 | if (DnsSocket < 0) { |
62e76326 | 800 | int port; |
801 | ||
ddfcbc22 | 802 | struct IN_ADDR addr; |
62e76326 | 803 | |
804 | if (Config.Addrs.udp_outgoing.s_addr != no_addr.s_addr) | |
805 | addr = Config.Addrs.udp_outgoing; | |
806 | else | |
807 | addr = Config.Addrs.udp_incoming; | |
808 | ||
809 | DnsSocket = comm_open(SOCK_DGRAM, | |
bdb741f4 | 810 | IPPROTO_UDP, |
62e76326 | 811 | addr, |
812 | 0, | |
813 | COMM_NONBLOCKING, | |
814 | "DNS Socket"); | |
815 | ||
816 | if (DnsSocket < 0) | |
817 | fatal("Could not create a DNS socket"); | |
818 | ||
819 | /* Ouch... we can't call functions using debug from a debug | |
820 | * statement. Doing so messes up the internal Debug::level | |
821 | */ | |
822 | port = comm_local_port(DnsSocket); | |
823 | ||
824 | debug(78, 1) ("DNS Socket created at %s, port %d, FD %d\n", | |
825 | inet_ntoa(addr), | |
826 | port, DnsSocket); | |
7b724b86 | 827 | } |
62e76326 | 828 | |
efd900cb | 829 | assert(0 == nns); |
830 | idnsParseNameservers(); | |
0e6d05ef | 831 | #ifndef _SQUID_MSWIN_ |
62e76326 | 832 | |
efd900cb | 833 | if (0 == nns) |
62e76326 | 834 | idnsParseResolvConf(); |
835 | ||
0e6d05ef | 836 | #endif |
ec4daaa5 | 837 | #ifdef _SQUID_WIN32_ |
62e76326 | 838 | |
0e6d05ef | 839 | if (0 == nns) |
62e76326 | 840 | idnsParseWIN32Registry(); |
841 | ||
0e6d05ef | 842 | #endif |
62e76326 | 843 | |
42b51993 | 844 | if (0 == nns) |
62e76326 | 845 | fatal("Could not find any nameservers.\n" |
ec4daaa5 | 846 | #ifdef _SQUID_WIN32_ |
62e76326 | 847 | " Please check your TCP-IP settings or /etc/resolv.conf file\n" |
0e6d05ef | 848 | #else |
62e76326 | 849 | " Please check your /etc/resolv.conf file\n" |
0e6d05ef | 850 | #endif |
62e76326 | 851 | " or use the 'dns_nameservers' option in squid.conf."); |
852 | ||
7b724b86 | 853 | if (!init) { |
62e76326 | 854 | memDataInit(MEM_IDNS_QUERY, "idns_query", sizeof(idns_query), 0); |
855 | cachemgrRegister("idns", | |
856 | "Internal DNS Statistics", | |
857 | idnsStats, 0, 1); | |
858 | memset(RcodeMatrix, '\0', sizeof(RcodeMatrix)); | |
3074b9d1 | 859 | idns_lookup_hash = hash_create((HASHCMP *) strcmp, 103, hash_string); |
62e76326 | 860 | init++; |
7b724b86 | 861 | } |
7b724b86 | 862 | } |
863 | ||
864 | void | |
865 | idnsShutdown(void) | |
866 | { | |
ef523f99 | 867 | if (DnsSocket < 0) |
62e76326 | 868 | return; |
869 | ||
ef523f99 | 870 | comm_close(DnsSocket); |
62e76326 | 871 | |
ef523f99 | 872 | DnsSocket = -1; |
62e76326 | 873 | |
efd900cb | 874 | idnsFreeNameservers(); |
7b724b86 | 875 | } |
876 | ||
3074b9d1 | 877 | static int |
878 | idnsCachedLookup(const char *key, IDNSCB * callback, void *data) | |
879 | { | |
880 | idns_query *q; | |
881 | ||
882 | idns_query *old = (idns_query *) hash_lookup(idns_lookup_hash, key); | |
883 | ||
884 | if (!old) | |
885 | return 0; | |
886 | ||
887 | q = (idns_query *)memAllocate(MEM_IDNS_QUERY); | |
888 | ||
889 | q->callback = callback; | |
890 | ||
891 | q->callback_data = cbdataReference(data); | |
892 | ||
893 | q->queue = old->queue; | |
894 | ||
895 | old->queue = q; | |
896 | ||
897 | return 1; | |
898 | } | |
899 | ||
900 | static void | |
9e1f210d | 901 | idnsCacheQuery(idns_query *q) |
3074b9d1 | 902 | { |
9e1f210d | 903 | q->hash.key = q->query.name; |
3074b9d1 | 904 | hash_join(idns_lookup_hash, &q->hash); |
905 | } | |
906 | ||
7b724b86 | 907 | void |
908 | idnsALookup(const char *name, IDNSCB * callback, void *data) | |
909 | { | |
3074b9d1 | 910 | idns_query *q; |
911 | ||
912 | if (idnsCachedLookup(name, callback, data)) | |
913 | return; | |
914 | ||
915 | q = (idns_query *)memAllocate(MEM_IDNS_QUERY); | |
916 | ||
71952967 | 917 | q->id = idnsQueryID(); |
918 | ||
9e1f210d | 919 | q->sz = rfc1035BuildAQuery(name, q->buf, sizeof(q->buf), q->id, &q->query); |
62e76326 | 920 | |
2041330b | 921 | if (q->sz < 0) { |
922 | /* problem with query data -- query not sent */ | |
923 | callback(data, NULL, 0, "Internal error"); | |
924 | memFree(q, MEM_IDNS_QUERY); | |
925 | return; | |
926 | } | |
927 | ||
a16b4aa0 | 928 | debug(78, 3) ("idnsALookup: buf is %d bytes for %s, id = %#hx\n", |
62e76326 | 929 | (int) q->sz, name, q->id); |
aa6c61e3 | 930 | |
7b724b86 | 931 | q->callback = callback; |
aa6c61e3 | 932 | |
fa80a8ef | 933 | q->callback_data = cbdataReference(data); |
aa6c61e3 | 934 | |
7cfc1c9a | 935 | q->start_t = current_time; |
aa6c61e3 | 936 | |
9e1f210d | 937 | idnsCacheQuery(q); |
aa6c61e3 | 938 | |
7b724b86 | 939 | idnsSendQuery(q); |
940 | } | |
8db71107 | 941 | |
942 | void | |
62e76326 | 943 | |
ddfcbc22 | 944 | idnsPTRLookup(const struct IN_ADDR addr, IDNSCB * callback, void *data) |
8db71107 | 945 | { |
3074b9d1 | 946 | idns_query *q; |
947 | ||
948 | const char *ip = inet_ntoa(addr); | |
949 | ||
950 | if (idnsCachedLookup(ip, callback, data)) | |
951 | return; | |
952 | ||
953 | q = (idns_query *)memAllocate(MEM_IDNS_QUERY); | |
954 | ||
71952967 | 955 | q->id = idnsQueryID(); |
956 | ||
9e1f210d | 957 | q->sz = rfc1035BuildPTRQuery(addr, q->buf, sizeof(q->buf), q->id, &q->query); |
3074b9d1 | 958 | |
2041330b | 959 | if (q->sz < 0) |
960 | { | |
961 | /* problem with query data -- query not sent */ | |
962 | callback(data, NULL, 0, "Internal error"); | |
963 | memFree(q, MEM_IDNS_QUERY); | |
964 | return; | |
965 | } | |
966 | ||
8db71107 | 967 | debug(78, 3) ("idnsPTRLookup: buf is %d bytes for %s, id = %#hx\n", |
3074b9d1 | 968 | (int) q->sz, ip, q->id); |
969 | ||
8db71107 | 970 | q->callback = callback; |
3074b9d1 | 971 | |
fa80a8ef | 972 | q->callback_data = cbdataReference(data); |
3074b9d1 | 973 | |
8db71107 | 974 | q->start_t = current_time; |
3074b9d1 | 975 | |
9e1f210d | 976 | idnsCacheQuery(q); |
3074b9d1 | 977 | |
8db71107 | 978 | idnsSendQuery(q); |
979 | } | |
eb824054 | 980 | |
3c573763 | 981 | #ifdef SQUID_SNMP |
982 | /* | |
983 | * The function to return the DNS via SNMP | |
984 | */ | |
985 | variable_list * | |
986 | snmp_netIdnsFn(variable_list * Var, snint * ErrP) | |
987 | { | |
988 | int i, n = 0; | |
989 | variable_list *Answer = NULL; | |
38650cc8 | 990 | debug(49, 5) ("snmp_netDnsFn: Processing request: \n"); |
3c573763 | 991 | snmpDebugOid(5, Var->name, Var->name_length); |
992 | *ErrP = SNMP_ERR_NOERROR; | |
62e76326 | 993 | |
3c573763 | 994 | switch (Var->name[LEN_SQ_NET + 1]) { |
62e76326 | 995 | |
3c573763 | 996 | case DNS_REQ: |
62e76326 | 997 | |
998 | for (i = 0; i < nns; i++) | |
999 | n += nameservers[i].nqueries; | |
1000 | ||
1001 | Answer = snmp_var_new_integer(Var->name, Var->name_length, | |
1002 | n, | |
1003 | SMI_COUNTER32); | |
1004 | ||
1005 | break; | |
1006 | ||
3c573763 | 1007 | case DNS_REP: |
62e76326 | 1008 | for (i = 0; i < nns; i++) |
1009 | n += nameservers[i].nreplies; | |
1010 | ||
1011 | Answer = snmp_var_new_integer(Var->name, Var->name_length, | |
1012 | n, | |
1013 | SMI_COUNTER32); | |
1014 | ||
1015 | break; | |
1016 | ||
3c573763 | 1017 | case DNS_SERVERS: |
62e76326 | 1018 | Answer = snmp_var_new_integer(Var->name, Var->name_length, |
e494f5e9 | 1019 | nns, |
62e76326 | 1020 | SMI_COUNTER32); |
1021 | ||
1022 | break; | |
1023 | ||
3c573763 | 1024 | default: |
62e76326 | 1025 | *ErrP = SNMP_ERR_NOSUCHNAME; |
1026 | ||
1027 | break; | |
3c573763 | 1028 | } |
62e76326 | 1029 | |
3c573763 | 1030 | return Answer; |
1031 | } | |
62e76326 | 1032 | |
3c573763 | 1033 | #endif /*SQUID_SNMP */ |
1f1ae50a | 1034 | #endif /* USE_DNSSERVERS */ |