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