]>
Commit | Line | Data |
---|---|---|
f88bb09c | 1 | |
2 | /* | |
4bc76d59 | 3 | * $Id: fqdncache.cc,v 1.72 1997/12/02 03:19:28 wessels Exp $ |
f88bb09c | 4 | * |
7cf620a9 | 5 | * DEBUG: section 35 FQDN Cache |
f88bb09c | 6 | * AUTHOR: Harvest Derived |
7 | * | |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
f88bb09c | 9 | * -------------------------------------------------------- |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by | |
14 | * the National Science Foundation. | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License as published by | |
18 | * the Free Software Foundation; either version 2 of the License, or | |
19 | * (at your option) any later version. | |
20 | * | |
21 | * This program is distributed in the hope that it will be useful, | |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 | * GNU General Public License for more details. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License | |
27 | * along with this program; if not, write to the Free Software | |
28 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | */ | |
31 | ||
32 | /* | |
33 | * Copyright (c) 1994, 1995. All rights reserved. | |
34 | * | |
35 | * The Harvest software was developed by the Internet Research Task | |
36 | * Force Research Group on Resource Discovery (IRTF-RD): | |
37 | * | |
38 | * Mic Bowman of Transarc Corporation. | |
39 | * Peter Danzig of the University of Southern California. | |
40 | * Darren R. Hardy of the University of Colorado at Boulder. | |
41 | * Udi Manber of the University of Arizona. | |
42 | * Michael F. Schwartz of the University of Colorado at Boulder. | |
43 | * Duane Wessels of the University of Colorado at Boulder. | |
44 | * | |
45 | * This copyright notice applies to software in the Harvest | |
46 | * ``src/'' directory only. Users should consult the individual | |
47 | * copyright notices in the ``components/'' subdirectories for | |
48 | * copyright information about other software bundled with the | |
49 | * Harvest source code distribution. | |
50 | * | |
51 | * TERMS OF USE | |
52 | * | |
53 | * The Harvest software may be used and re-distributed without | |
54 | * charge, provided that the software origin and research team are | |
55 | * cited in any use of the system. Most commonly this is | |
56 | * accomplished by including a link to the Harvest Home Page | |
57 | * (http://harvest.cs.colorado.edu/) from the query page of any | |
58 | * Broker you deploy, as well as in the query result pages. These | |
59 | * links are generated automatically by the standard Broker | |
60 | * software distribution. | |
61 | * | |
62 | * The Harvest software is provided ``as is'', without express or | |
63 | * implied warranty, and with no support nor obligation to assist | |
64 | * in its use, correction, modification or enhancement. We assume | |
65 | * no liability with respect to the infringement of copyrights, | |
66 | * trade secrets, or any patents, and are not responsible for | |
67 | * consequential damages. Proper use of the Harvest software is | |
68 | * entirely the responsibility of the user. | |
69 | * | |
70 | * DERIVATIVE WORKS | |
71 | * | |
72 | * Users may make derivative works from the Harvest software, subject | |
73 | * to the following constraints: | |
74 | * | |
75 | * - You must include the above copyright notice and these | |
76 | * accompanying paragraphs in all forms of derivative works, | |
77 | * and any documentation and other materials related to such | |
78 | * distribution and use acknowledge that the software was | |
79 | * developed at the above institutions. | |
80 | * | |
81 | * - You must notify IRTF-RD regarding your distribution of | |
82 | * the derivative work. | |
83 | * | |
84 | * - You must clearly notify users that your are distributing | |
85 | * a modified version and not the original Harvest software. | |
86 | * | |
87 | * - Any derivative product is also subject to these copyright | |
88 | * and use restrictions. | |
89 | * | |
90 | * Note that the Harvest software is NOT in the public domain. We | |
91 | * retain copyright, as specified above. | |
92 | * | |
93 | * HISTORY OF FREE SOFTWARE STATUS | |
94 | * | |
95 | * Originally we required sites to license the software in cases | |
96 | * where they were going to build commercial products/services | |
97 | * around Harvest. In June 1995 we changed this policy. We now | |
98 | * allow people to use the core Harvest software (the code found in | |
99 | * the Harvest ``src/'' directory) for free. We made this change | |
100 | * in the interest of encouraging the widest possible deployment of | |
101 | * the technology. The Harvest software is really a reference | |
102 | * implementation of a set of protocols and formats, some of which | |
103 | * we intend to standardize. We encourage commercial | |
104 | * re-implementations of code complying to this set of standards. | |
105 | */ | |
106 | ||
107 | #include "squid.h" | |
108 | ||
109 | #define MAX_LINELEN (4096) | |
110 | ||
111 | #define MAX_FQDN 1024 /* Maximum cached FQDN */ | |
112 | #define FQDN_LOW_WATER 90 | |
113 | #define FQDN_HIGH_WATER 95 | |
f88bb09c | 114 | |
115 | struct _fqdn_pending { | |
582b6456 | 116 | FQDNH *handler; |
f88bb09c | 117 | void *handlerData; |
118 | struct _fqdn_pending *next; | |
119 | }; | |
120 | ||
121 | struct fqdncacheQueueData { | |
122 | struct fqdncacheQueueData *next; | |
123 | fqdncache_entry *f; | |
124 | }; | |
125 | ||
126 | static struct { | |
127 | int requests; | |
128 | int replies; | |
129 | int hits; | |
130 | int misses; | |
131 | int pending_hits; | |
132 | int negative_hits; | |
133 | int errors; | |
134 | int avg_svc_time; | |
135 | int ghba_calls; /* # calls to blocking gethostbyaddr() */ | |
136 | } FqdncacheStats; | |
137 | ||
4bc76d59 | 138 | static dlink_list lru_list; |
139 | ||
f5b8bbc4 | 140 | static void fqdncache_dnsHandleRead(int, void *); |
141 | static fqdncache_entry *fqdncache_parsebuffer(const char *buf, dnsserver_t *); | |
4bc76d59 | 142 | static void fqdncache_purgelru(void); |
f5b8bbc4 | 143 | static void fqdncache_release(fqdncache_entry *); |
f5b8bbc4 | 144 | static fqdncache_entry *fqdncache_create(const char *name); |
145 | static void fqdncache_add_to_hash(fqdncache_entry *); | |
146 | static void fqdncache_call_pending(fqdncache_entry *); | |
147 | static void fqdncacheAddHostent(fqdncache_entry *, const struct hostent *); | |
148 | static int fqdncacheHasPending(const fqdncache_entry *); | |
149 | static fqdncache_entry *fqdncache_get(const char *); | |
348b2031 | 150 | static FQDNH dummy_handler; |
f5b8bbc4 | 151 | static int fqdncacheExpiredEntry(const fqdncache_entry *); |
152 | static void fqdncacheAddPending(fqdncache_entry *, FQDNH *, void *); | |
153 | static void fqdncacheEnqueue(fqdncache_entry *); | |
154 | static void *fqdncacheDequeue(void); | |
155 | static void fqdncache_dnsDispatch(dnsserver_t *, fqdncache_entry *); | |
156 | static void fqdncacheChangeKey(fqdncache_entry * i); | |
157 | static void fqdncacheLockEntry(fqdncache_entry * f); | |
158 | static void fqdncacheUnlockEntry(fqdncache_entry * f); | |
f88bb09c | 159 | |
365e5b34 | 160 | static hash_table *fqdn_table = NULL; |
f88bb09c | 161 | static struct fqdncacheQueueData *fqdncacheQueueHead = NULL; |
162 | static struct fqdncacheQueueData **fqdncacheQueueTailP = &fqdncacheQueueHead; | |
163 | ||
164 | static char fqdncache_status_char[] = | |
165 | { | |
166 | 'C', | |
167 | 'N', | |
168 | 'P', | |
169 | 'D' | |
170 | }; | |
171 | ||
24382924 | 172 | static long fqdncache_low = 180; |
173 | static long fqdncache_high = 200; | |
f88bb09c | 174 | |
b8d8561b | 175 | static void |
176 | fqdncacheEnqueue(fqdncache_entry * f) | |
f88bb09c | 177 | { |
178 | struct fqdncacheQueueData *new = xcalloc(1, sizeof(struct fqdncacheQueueData)); | |
179 | new->f = f; | |
180 | *fqdncacheQueueTailP = new; | |
181 | fqdncacheQueueTailP = &new->next; | |
182 | } | |
183 | ||
b8d8561b | 184 | static void * |
0673c0ba | 185 | fqdncacheDequeue(void) |
f88bb09c | 186 | { |
187 | struct fqdncacheQueueData *old = NULL; | |
188 | fqdncache_entry *f = NULL; | |
189 | if (fqdncacheQueueHead) { | |
190 | f = fqdncacheQueueHead->f; | |
191 | old = fqdncacheQueueHead; | |
192 | fqdncacheQueueHead = fqdncacheQueueHead->next; | |
193 | if (fqdncacheQueueHead == NULL) | |
194 | fqdncacheQueueTailP = &fqdncacheQueueHead; | |
195 | safe_free(old); | |
196 | } | |
429fdbec | 197 | if (f && f->status != FQDN_PENDING) |
198 | debug_trap("fqdncacheDequeue: status != FQDN_PENDING"); | |
f88bb09c | 199 | return f; |
200 | } | |
201 | ||
202 | /* removes the given fqdncache entry */ | |
b8d8561b | 203 | static void |
204 | fqdncache_release(fqdncache_entry * f) | |
f88bb09c | 205 | { |
f88bb09c | 206 | int k; |
e144eae4 | 207 | assert(f->status != FQDN_PENDING); |
208 | assert(f->status != FQDN_DISPATCHED); | |
209 | assert(f->pending_head == NULL); | |
210 | if (hash_remove_link(fqdn_table, (hash_link *) f)) { | |
a3d5953d | 211 | debug(35, 0) ("fqdncache_release: hash_remove_link() failed for '%s'\n", |
429fdbec | 212 | f->name); |
f88bb09c | 213 | return; |
214 | } | |
429fdbec | 215 | if (f->status == FQDN_CACHED) { |
f88bb09c | 216 | for (k = 0; k < (int) f->name_count; k++) |
217 | safe_free(f->names[k]); | |
a3d5953d | 218 | debug(35, 5) ("fqdncache_release: Released FQDN record for '%s'.\n", |
429fdbec | 219 | f->name); |
f88bb09c | 220 | } |
4bc76d59 | 221 | dlinkDelete(&f->lru, &lru_list); |
429fdbec | 222 | safe_free(f->name); |
223 | safe_free(f->error_message); | |
224 | safe_free(f); | |
f88bb09c | 225 | --meta_data.fqdncache_count; |
f88bb09c | 226 | } |
227 | ||
228 | /* return match for given name */ | |
b8d8561b | 229 | static fqdncache_entry * |
0ee4272b | 230 | fqdncache_get(const char *name) |
f88bb09c | 231 | { |
232 | hash_link *e; | |
233 | static fqdncache_entry *f; | |
234 | ||
235 | f = NULL; | |
236 | if (fqdn_table) { | |
237 | if ((e = hash_lookup(fqdn_table, name)) != NULL) | |
238 | f = (fqdncache_entry *) e; | |
239 | } | |
240 | return f; | |
241 | } | |
242 | ||
b8d8561b | 243 | static int |
fe4e214f | 244 | fqdncacheExpiredEntry(const fqdncache_entry * f) |
f88bb09c | 245 | { |
246 | if (f->status == FQDN_PENDING) | |
247 | return 0; | |
248 | if (f->status == FQDN_DISPATCHED) | |
249 | return 0; | |
429fdbec | 250 | if (f->locks != 0) |
251 | return 0; | |
e84703ad | 252 | if (f->expires > squid_curtime) |
f88bb09c | 253 | return 0; |
254 | return 1; | |
255 | } | |
256 | ||
4bc76d59 | 257 | static void |
0673c0ba | 258 | fqdncache_purgelru(void) |
f88bb09c | 259 | { |
4bc76d59 | 260 | dlink_node *m; |
261 | dlink_node *prev = NULL; | |
262 | fqdncache_entry *f; | |
f88bb09c | 263 | int removed = 0; |
4bc76d59 | 264 | for (m = lru_list.tail; m; m = prev) { |
265 | if (meta_data.fqdncache_count < fqdncache_low) | |
266 | break; | |
267 | prev = m->prev; | |
268 | f = m->data; | |
f88bb09c | 269 | if (f->status == FQDN_PENDING) |
270 | continue; | |
271 | if (f->status == FQDN_DISPATCHED) | |
272 | continue; | |
4bc76d59 | 273 | if (f->locks != 0) |
274 | continue; | |
275 | fqdncache_release(f); | |
f88bb09c | 276 | removed++; |
277 | } | |
4bc76d59 | 278 | debug(14, 3) ("fqdncache_purgelru: removed %d entries\n", removed); |
f88bb09c | 279 | } |
280 | ||
281 | ||
282 | /* create blank fqdncache_entry */ | |
b8d8561b | 283 | static fqdncache_entry * |
429fdbec | 284 | fqdncache_create(const char *name) |
f88bb09c | 285 | { |
4bc76d59 | 286 | static fqdncache_entry *f; |
287 | if (meta_data.fqdncache_count > fqdncache_high) | |
288 | fqdncache_purgelru(); | |
f88bb09c | 289 | meta_data.fqdncache_count++; |
4bc76d59 | 290 | f = xcalloc(1, sizeof(fqdncache_entry)); |
291 | f->name = xstrdup(name); | |
292 | f->expires = squid_curtime + Config.negativeDnsTtl; | |
293 | fqdncache_add_to_hash(f); | |
294 | dlinkAdd(f, &f->lru, &lru_list); | |
295 | return f; | |
f88bb09c | 296 | } |
297 | ||
b8d8561b | 298 | static void |
299 | fqdncache_add_to_hash(fqdncache_entry * f) | |
f88bb09c | 300 | { |
a3d5953d | 301 | debug(35, 5) ("fqdncache_add_to_hash: name <%s>\n", f->name); |
2e96bbf3 | 302 | hash_join(fqdn_table, (hash_link *) f); |
f88bb09c | 303 | } |
304 | ||
b8d8561b | 305 | static void |
429fdbec | 306 | fqdncacheAddHostent(fqdncache_entry * f, const struct hostent *hp) |
f88bb09c | 307 | { |
308 | int k; | |
429fdbec | 309 | f->name_count = 0; |
310 | f->names[f->name_count++] = xstrdup((char *) hp->h_name); | |
311 | for (k = 0; hp->h_aliases[k]; k++) { | |
312 | f->names[f->name_count++] = xstrdup(hp->h_aliases[k]); | |
313 | if (f->name_count == FQDN_MAX_NAMES) | |
314 | break; | |
315 | } | |
316 | } | |
f88bb09c | 317 | |
429fdbec | 318 | static fqdncache_entry * |
319 | fqdncacheAddNew(const char *name, const struct hostent *hp, fqdncache_status_t status) | |
320 | { | |
321 | fqdncache_entry *f; | |
e144eae4 | 322 | assert(fqdncache_get(name) == NULL); |
a3d5953d | 323 | debug(14, 10) ("fqdncacheAddNew: Adding '%s', status=%c\n", |
429fdbec | 324 | name, |
325 | fqdncache_status_char[status]); | |
326 | f = fqdncache_create(name); | |
327 | if (hp) | |
328 | fqdncacheAddHostent(f, hp); | |
329 | f->status = status; | |
330 | f->lastref = squid_curtime; | |
331 | return f; | |
f88bb09c | 332 | } |
333 | ||
334 | /* walks down the pending list, calling handlers */ | |
b8d8561b | 335 | static void |
336 | fqdncache_call_pending(fqdncache_entry * f) | |
f88bb09c | 337 | { |
338 | struct _fqdn_pending *p = NULL; | |
339 | int nhandler = 0; | |
340 | ||
341 | f->lastref = squid_curtime; | |
342 | ||
429fdbec | 343 | fqdncacheLockEntry(f); |
f88bb09c | 344 | while (f->pending_head != NULL) { |
345 | p = f->pending_head; | |
346 | f->pending_head = p->next; | |
347 | if (p->handler) { | |
348 | nhandler++; | |
349 | dns_error_message = f->error_message; | |
348b2031 | 350 | p->handler((f->status == FQDN_CACHED) ? f->names[0] : NULL, |
f88bb09c | 351 | p->handlerData); |
352 | } | |
f88bb09c | 353 | safe_free(p); |
354 | } | |
355 | f->pending_head = NULL; /* nuke list */ | |
a3d5953d | 356 | debug(35, 10) ("fqdncache_call_pending: Called %d handlers.\n", nhandler); |
429fdbec | 357 | fqdncacheUnlockEntry(f); |
f88bb09c | 358 | } |
359 | ||
b8d8561b | 360 | static fqdncache_entry * |
fe4e214f | 361 | fqdncache_parsebuffer(const char *inbuf, dnsserver_t * dnsData) |
f88bb09c | 362 | { |
e84703ad | 363 | char *buf = xstrdup(inbuf); |
364 | char *token; | |
365 | static fqdncache_entry f; | |
366 | int k; | |
f88bb09c | 367 | int ipcount; |
368 | int aliascount; | |
a3d5953d | 369 | debug(35, 5) ("fqdncache_parsebuffer: parsing:\n%s", inbuf); |
e84703ad | 370 | memset(&f, '\0', sizeof(fqdncache_entry)); |
371 | f.expires = squid_curtime + Config.positiveDnsTtl; | |
620da955 | 372 | f.status = FQDN_DISPATCHED; |
e84703ad | 373 | for (token = strtok(buf, w_space); token; token = strtok(NULL, w_space)) { |
374 | if (!strcmp(token, "$end")) { | |
f88bb09c | 375 | break; |
e84703ad | 376 | } else if (!strcmp(token, "$alive")) { |
f88bb09c | 377 | dnsData->answer = squid_curtime; |
e84703ad | 378 | } else if (!strcmp(token, "$fail")) { |
4bcfeca1 | 379 | if ((token = strtok(NULL, "\n")) == NULL) |
e84703ad | 380 | fatal_dump("Invalid $fail"); |
381 | f.expires = squid_curtime + Config.negativeDnsTtl; | |
620da955 | 382 | f.status = FQDN_NEGATIVE_CACHED; |
e84703ad | 383 | } else if (!strcmp(token, "$message")) { |
384 | if ((token = strtok(NULL, "\n")) == NULL) | |
385 | fatal_dump("Invalid $message"); | |
386 | f.error_message = xstrdup(token); | |
387 | } else if (!strcmp(token, "$name")) { | |
388 | if ((token = strtok(NULL, w_space)) == NULL) | |
389 | fatal_dump("Invalid $name"); | |
620da955 | 390 | f.status = FQDN_CACHED; |
e84703ad | 391 | } else if (!strcmp(token, "$h_name")) { |
392 | if ((token = strtok(NULL, w_space)) == NULL) | |
393 | fatal_dump("Invalid $h_name"); | |
394 | f.names[0] = xstrdup(token); | |
395 | f.name_count = 1; | |
396 | } else if (!strcmp(token, "$h_len")) { | |
397 | if ((token = strtok(NULL, w_space)) == NULL) | |
398 | fatal_dump("Invalid $h_len"); | |
399 | } else if (!strcmp(token, "$ipcount")) { | |
400 | if ((token = strtok(NULL, w_space)) == NULL) | |
401 | fatal_dump("Invalid $ipcount"); | |
402 | ipcount = atoi(token); | |
403 | for (k = 0; k < ipcount; k++) { | |
404 | if ((token = strtok(NULL, w_space)) == NULL) | |
620da955 | 405 | fatal_dump("Invalid FQDN address"); |
f88bb09c | 406 | } |
e84703ad | 407 | } else if (!strcmp(token, "$aliascount")) { |
408 | if ((token = strtok(NULL, w_space)) == NULL) | |
409 | fatal_dump("Invalid $aliascount"); | |
410 | aliascount = atoi(token); | |
411 | for (k = 0; k < aliascount; k++) { | |
412 | if ((token = strtok(NULL, w_space)) == NULL) | |
413 | fatal_dump("Invalid alias"); | |
f88bb09c | 414 | } |
e84703ad | 415 | } else if (!strcmp(token, "$ttl")) { |
416 | if ((token = strtok(NULL, w_space)) == NULL) | |
417 | fatal_dump("Invalid $ttl"); | |
418 | f.expires = squid_curtime + atoi(token); | |
f88bb09c | 419 | } else { |
e84703ad | 420 | fatal_dump("Invalid dnsserver output"); |
f88bb09c | 421 | } |
422 | } | |
e84703ad | 423 | xfree(buf); |
424 | return &f; | |
f88bb09c | 425 | } |
426 | ||
429fdbec | 427 | static void |
428 | fqdncacheNudgeQueue(void) | |
429 | { | |
430 | dnsserver_t *dnsData; | |
431 | fqdncache_entry *f = NULL; | |
432 | while ((dnsData = dnsGetFirstAvailable()) && (f = fqdncacheDequeue())) | |
433 | fqdncache_dnsDispatch(dnsData, f); | |
434 | } | |
435 | ||
b785f8ca | 436 | static void |
4f07153c | 437 | fqdncache_dnsHandleRead(int fd, void *data) |
f88bb09c | 438 | { |
2f549d83 | 439 | dnsserver_t *dnsData = data; |
f88bb09c | 440 | int len; |
f88bb09c | 441 | int n; |
442 | fqdncache_entry *f = NULL; | |
e84703ad | 443 | fqdncache_entry *x = NULL; |
f88bb09c | 444 | |
445 | len = read(fd, | |
446 | dnsData->ip_inbuf + dnsData->offset, | |
447 | dnsData->size - dnsData->offset); | |
4f92c80c | 448 | fd_bytes(fd, len, FD_READ); |
a3d5953d | 449 | debug(35, 5) ("fqdncache_dnsHandleRead: Result from DNS ID %d (%d bytes)\n", |
f88bb09c | 450 | dnsData->id, len); |
451 | if (len <= 0) { | |
b224ea98 | 452 | if (len < 0 && ignoreErrno(errno)) { |
0a0bf5db | 453 | commSetSelect(fd, |
454 | COMM_SELECT_READ, | |
455 | fqdncache_dnsHandleRead, | |
456 | dnsData, 0); | |
457 | return; | |
458 | } | |
79a15e0a | 459 | debug(35, EBIT_TEST(dnsData->flags, HELPER_CLOSING) ? 5 : 1) |
a3d5953d | 460 | ("FD %d: Connection from DNSSERVER #%d is closed, disabling\n", |
f88bb09c | 461 | fd, dnsData->id); |
462 | dnsData->flags = 0; | |
b177367b | 463 | commSetSelect(fd, |
f88bb09c | 464 | COMM_SELECT_WRITE, |
465 | NULL, | |
b177367b | 466 | NULL, |
85d7ea98 | 467 | 0); |
f88bb09c | 468 | comm_close(fd); |
2f549d83 | 469 | return; |
f88bb09c | 470 | } |
471 | n = ++FqdncacheStats.replies; | |
b0c9fba9 | 472 | DnsStats.replies++; |
f88bb09c | 473 | dnsData->offset += len; |
474 | dnsData->ip_inbuf[dnsData->offset] = '\0'; | |
429fdbec | 475 | f = dnsData->data; |
476 | if (f->status != FQDN_DISPATCHED) | |
477 | fatal_dump("fqdncache_dnsHandleRead: bad status"); | |
f88bb09c | 478 | if (strstr(dnsData->ip_inbuf, "$end\n")) { |
479 | /* end of record found */ | |
9e4ad609 | 480 | FqdncacheStats.avg_svc_time = |
481 | intAverage(FqdncacheStats.avg_svc_time, | |
482 | tvSubMsec(dnsData->dispatch_time, current_time), | |
483 | n, FQDNCACHE_AV_FACTOR); | |
e84703ad | 484 | if ((x = fqdncache_parsebuffer(dnsData->ip_inbuf, dnsData)) == NULL) { |
a3d5953d | 485 | debug(35, 0) ("fqdncache_dnsHandleRead: fqdncache_parsebuffer failed?!\n"); |
e84703ad | 486 | } else { |
487 | dnsData->offset = 0; | |
488 | dnsData->ip_inbuf[0] = '\0'; | |
e84703ad | 489 | f->name_count = x->name_count; |
7690e8eb | 490 | for (n = 0; n < (int) f->name_count; n++) |
e84703ad | 491 | f->names[n] = x->names[n]; |
492 | f->error_message = x->error_message; | |
493 | f->status = x->status; | |
494 | f->expires = x->expires; | |
495 | fqdncache_call_pending(f); | |
f88bb09c | 496 | } |
429fdbec | 497 | fqdncacheUnlockEntry(f); /* unlock from FQDN_DISPATCHED */ |
f88bb09c | 498 | } |
499 | if (dnsData->offset == 0) { | |
500 | dnsData->data = NULL; | |
79a15e0a | 501 | EBIT_CLR(dnsData->flags, HELPER_BUSY); |
f88bb09c | 502 | } |
2f549d83 | 503 | /* reschedule */ |
504 | commSetSelect(dnsData->inpipe, | |
505 | COMM_SELECT_READ, | |
506 | fqdncache_dnsHandleRead, | |
507 | dnsData, 0); | |
429fdbec | 508 | fqdncacheNudgeQueue(); |
f88bb09c | 509 | } |
510 | ||
b8d8561b | 511 | static void |
348b2031 | 512 | fqdncacheAddPending(fqdncache_entry * f, FQDNH * handler, void *handlerData) |
f88bb09c | 513 | { |
514 | struct _fqdn_pending *pending = xcalloc(1, sizeof(struct _fqdn_pending)); | |
515 | struct _fqdn_pending **I = NULL; | |
429fdbec | 516 | f->lastref = squid_curtime; |
f88bb09c | 517 | pending->handler = handler; |
518 | pending->handlerData = handlerData; | |
f88bb09c | 519 | for (I = &(f->pending_head); *I; I = &((*I)->next)); |
520 | *I = pending; | |
4bc76d59 | 521 | if (f->status == FQDN_PENDING) |
429fdbec | 522 | fqdncacheNudgeQueue(); |
f88bb09c | 523 | } |
524 | ||
429fdbec | 525 | void |
348b2031 | 526 | fqdncache_nbgethostbyaddr(struct in_addr addr, FQDNH * handler, void *handlerData) |
f88bb09c | 527 | { |
528 | fqdncache_entry *f = NULL; | |
529 | dnsserver_t *dnsData = NULL; | |
530 | char *name = inet_ntoa(addr); | |
531 | ||
532 | if (!handler) | |
533 | fatal_dump("fqdncache_nbgethostbyaddr: NULL handler"); | |
534 | ||
a3d5953d | 535 | debug(35, 4) ("fqdncache_nbgethostbyaddr: Name '%s'.\n", name); |
f88bb09c | 536 | FqdncacheStats.requests++; |
537 | ||
538 | if (name == NULL || name[0] == '\0') { | |
a3d5953d | 539 | debug(35, 4) ("fqdncache_nbgethostbyaddr: Invalid name!\n"); |
348b2031 | 540 | handler(NULL, handlerData); |
429fdbec | 541 | return; |
f88bb09c | 542 | } |
543 | if ((f = fqdncache_get(name))) { | |
544 | if (fqdncacheExpiredEntry(f)) { | |
545 | fqdncache_release(f); | |
546 | f = NULL; | |
547 | } | |
548 | } | |
549 | if (f == NULL) { | |
550 | /* MISS: No entry, create the new one */ | |
a3d5953d | 551 | debug(35, 5) ("fqdncache_nbgethostbyaddr: MISS for '%s'\n", name); |
f88bb09c | 552 | FqdncacheStats.misses++; |
429fdbec | 553 | f = fqdncacheAddNew(name, NULL, FQDN_PENDING); |
348b2031 | 554 | fqdncacheAddPending(f, handler, handlerData); |
f88bb09c | 555 | } else if (f->status == FQDN_CACHED || f->status == FQDN_NEGATIVE_CACHED) { |
556 | /* HIT */ | |
a3d5953d | 557 | debug(35, 4) ("fqdncache_nbgethostbyaddr: HIT for '%s'\n", name); |
f88bb09c | 558 | if (f->status == FQDN_NEGATIVE_CACHED) |
559 | FqdncacheStats.negative_hits++; | |
560 | else | |
561 | FqdncacheStats.hits++; | |
348b2031 | 562 | fqdncacheAddPending(f, handler, handlerData); |
f88bb09c | 563 | fqdncache_call_pending(f); |
429fdbec | 564 | return; |
f88bb09c | 565 | } else if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { |
a3d5953d | 566 | debug(35, 4) ("fqdncache_nbgethostbyaddr: PENDING for '%s'\n", name); |
f88bb09c | 567 | FqdncacheStats.pending_hits++; |
348b2031 | 568 | fqdncacheAddPending(f, handler, handlerData); |
429fdbec | 569 | if (squid_curtime - f->expires > 600) { |
a3d5953d | 570 | debug(14, 0) ("fqdncache_nbgethostbyname: '%s' PENDING for %d seconds, aborting\n", name, squid_curtime + Config.negativeDnsTtl - f->expires); |
429fdbec | 571 | fqdncacheChangeKey(f); |
572 | fqdncache_call_pending(f); | |
573 | } | |
574 | return; | |
f88bb09c | 575 | } else { |
576 | fatal_dump("fqdncache_nbgethostbyaddr: BAD fqdncache_entry status"); | |
577 | } | |
578 | ||
579 | /* for HIT, PENDING, DISPATCHED we've returned. For MISS we continue */ | |
580 | ||
429fdbec | 581 | if ((dnsData = dnsGetFirstAvailable())) { |
f88bb09c | 582 | fqdncache_dnsDispatch(dnsData, f); |
7c63efed | 583 | } else if (NDnsServersAlloc > 0) { |
f88bb09c | 584 | fqdncacheEnqueue(f); |
7c63efed | 585 | } else { |
586 | /* abort if we get here */ | |
587 | assert(NDnsServersAlloc); | |
429fdbec | 588 | } |
f88bb09c | 589 | } |
590 | ||
b8d8561b | 591 | static void |
592 | fqdncache_dnsDispatch(dnsserver_t * dns, fqdncache_entry * f) | |
f88bb09c | 593 | { |
594 | char *buf = NULL; | |
79a15e0a | 595 | assert(EBIT_TEST(dns->flags, HELPER_ALIVE)); |
f88bb09c | 596 | if (!fqdncacheHasPending(f)) { |
a3d5953d | 597 | debug(35, 0) ("fqdncache_dnsDispatch: skipping '%s' because no handler.\n", |
f88bb09c | 598 | f->name); |
599 | f->status = FQDN_NEGATIVE_CACHED; | |
600 | fqdncache_release(f); | |
601 | return; | |
602 | } | |
429fdbec | 603 | if (f->status != FQDN_PENDING) |
604 | debug_trap("fqdncache_dnsDispatch: status != FQDN_PENDING"); | |
f88bb09c | 605 | buf = xcalloc(1, 256); |
2b9a078c | 606 | snprintf(buf, 256, "%s\n", f->name); |
79a15e0a | 607 | EBIT_SET(dns->flags, HELPER_BUSY); |
f88bb09c | 608 | dns->data = f; |
429fdbec | 609 | f->status = FQDN_DISPATCHED; |
f88bb09c | 610 | comm_write(dns->outpipe, |
611 | buf, | |
612 | strlen(buf), | |
f88bb09c | 613 | NULL, /* Handler */ |
614 | NULL, /* Handler-data */ | |
615 | xfree); | |
b177367b | 616 | commSetSelect(dns->outpipe, |
f88bb09c | 617 | COMM_SELECT_READ, |
2f549d83 | 618 | fqdncache_dnsHandleRead, |
b177367b | 619 | dns, |
620 | 0); | |
a3d5953d | 621 | debug(35, 5) ("fqdncache_dnsDispatch: Request sent to DNS server #%d.\n", |
f88bb09c | 622 | dns->id); |
623 | dns->dispatch_time = current_time; | |
624 | DnsStats.requests++; | |
625 | DnsStats.hist[dns->id - 1]++; | |
4bc76d59 | 626 | fqdncacheLockEntry(f); /* lock while FQDN_DISPATCHED */ |
f88bb09c | 627 | } |
628 | ||
629 | ||
630 | /* initialize the fqdncache */ | |
b8d8561b | 631 | void |
0673c0ba | 632 | fqdncache_init(void) |
f88bb09c | 633 | { |
19054954 | 634 | if (fqdn_table) |
635 | return; | |
a3d5953d | 636 | debug(35, 3) ("Initializing FQDN Cache...\n"); |
f88bb09c | 637 | memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats)); |
aabcd584 | 638 | /* small hash table */ |
1b50df4b | 639 | fqdn_table = hash_create(urlcmp, 229, hash4); |
f88bb09c | 640 | fqdncache_high = (long) (((float) MAX_FQDN * |
641 | (float) FQDN_HIGH_WATER) / (float) 100); | |
642 | fqdncache_low = (long) (((float) MAX_FQDN * | |
643 | (float) FQDN_LOW_WATER) / (float) 100); | |
644 | } | |
645 | ||
646 | /* clean up the pending entries in dnsserver */ | |
647 | /* return 1 if we found the host, 0 otherwise */ | |
b8d8561b | 648 | int |
b69f7771 | 649 | fqdncacheUnregister(struct in_addr addr, void *data) |
f88bb09c | 650 | { |
03047798 | 651 | char *name = inet_ntoa(addr); |
f88bb09c | 652 | fqdncache_entry *f = NULL; |
653 | struct _fqdn_pending *p = NULL; | |
654 | int n = 0; | |
a3d5953d | 655 | debug(35, 3) ("fqdncacheUnregister: FD %d, name '%s'\n", name); |
f88bb09c | 656 | if ((f = fqdncache_get(name)) == NULL) |
657 | return 0; | |
658 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { | |
659 | for (p = f->pending_head; p; p = p->next) { | |
b69f7771 | 660 | if (p->handlerData != data) |
661 | continue; | |
662 | p->handler = NULL; | |
b69f7771 | 663 | n++; |
f88bb09c | 664 | } |
665 | } | |
b69f7771 | 666 | if (n == 0) |
667 | debug_trap("fqdncacheUnregister: callback data not found"); | |
a3d5953d | 668 | debug(35, 3) ("fqdncacheUnregister: unregistered %d handlers\n", n); |
f88bb09c | 669 | return n; |
670 | } | |
671 | ||
0ee4272b | 672 | const char * |
b8d8561b | 673 | fqdncache_gethostbyaddr(struct in_addr addr, int flags) |
f88bb09c | 674 | { |
675 | char *name = inet_ntoa(addr); | |
676 | fqdncache_entry *f = NULL; | |
429fdbec | 677 | struct in_addr ip; |
f88bb09c | 678 | |
679 | if (!name) | |
680 | fatal_dump("fqdncache_gethostbyaddr: NULL name"); | |
681 | FqdncacheStats.requests++; | |
682 | if ((f = fqdncache_get(name))) { | |
429fdbec | 683 | if (fqdncacheExpiredEntry(f)) { |
684 | fqdncache_release(f); | |
685 | f = NULL; | |
686 | } | |
687 | } | |
688 | if (f) { | |
7c63efed | 689 | if (f->status == FQDN_NEGATIVE_CACHED) { |
f88bb09c | 690 | FqdncacheStats.negative_hits++; |
691 | dns_error_message = f->error_message; | |
692 | return NULL; | |
693 | } else { | |
694 | FqdncacheStats.hits++; | |
695 | f->lastref = squid_curtime; | |
696 | return f->names[0]; | |
697 | } | |
698 | } | |
f88bb09c | 699 | /* check if it's already a FQDN address in text form. */ |
429fdbec | 700 | if (!safe_inet_addr(name, &ip)) |
f88bb09c | 701 | return name; |
429fdbec | 702 | FqdncacheStats.misses++; |
f88bb09c | 703 | if (flags & FQDN_LOOKUP_IF_MISS) |
348b2031 | 704 | fqdncache_nbgethostbyaddr(addr, dummy_handler, NULL); |
f88bb09c | 705 | return NULL; |
706 | } | |
707 | ||
708 | ||
709 | /* process objects list */ | |
b8d8561b | 710 | void |
711 | fqdnStats(StoreEntry * sentry) | |
f88bb09c | 712 | { |
713 | fqdncache_entry *f = NULL; | |
4bc76d59 | 714 | fqdncache_entry *next = NULL; |
f88bb09c | 715 | int k; |
716 | int ttl; | |
4bc76d59 | 717 | if (fqdn_table == NULL) |
f88bb09c | 718 | return; |
f88bb09c | 719 | storeAppendPrintf(sentry, "{FQDN Cache Statistics:\n"); |
720 | storeAppendPrintf(sentry, "{FQDNcache Entries: %d}\n", | |
721 | meta_data.fqdncache_count); | |
722 | storeAppendPrintf(sentry, "{FQDNcache Requests: %d}\n", | |
723 | FqdncacheStats.requests); | |
724 | storeAppendPrintf(sentry, "{FQDNcache Hits: %d}\n", | |
725 | FqdncacheStats.hits); | |
726 | storeAppendPrintf(sentry, "{FQDNcache Pending Hits: %d}\n", | |
727 | FqdncacheStats.pending_hits); | |
728 | storeAppendPrintf(sentry, "{FQDNcache Negative Hits: %d}\n", | |
729 | FqdncacheStats.negative_hits); | |
730 | storeAppendPrintf(sentry, "{FQDNcache Misses: %d}\n", | |
731 | FqdncacheStats.misses); | |
732 | storeAppendPrintf(sentry, "{Blocking calls to gethostbyaddr(): %d}\n", | |
733 | FqdncacheStats.ghba_calls); | |
734 | storeAppendPrintf(sentry, "{dnsserver avg service time: %d msec}\n", | |
735 | FqdncacheStats.avg_svc_time); | |
736 | storeAppendPrintf(sentry, "}\n\n"); | |
737 | storeAppendPrintf(sentry, "{FQDN Cache Contents:\n\n"); | |
738 | ||
4bc76d59 | 739 | next = (fqdncache_entry *) hash_first(fqdn_table); |
740 | while ((f = next) != NULL) { | |
741 | next = (fqdncache_entry *) hash_next(fqdn_table); | |
f88bb09c | 742 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) |
743 | ttl = 0; | |
744 | else | |
e84703ad | 745 | ttl = (f->expires - squid_curtime); |
f88bb09c | 746 | storeAppendPrintf(sentry, " {%-32.32s %c %6d %d", |
747 | f->name, | |
748 | fqdncache_status_char[f->status], | |
749 | ttl, | |
750 | (int) f->name_count); | |
751 | for (k = 0; k < (int) f->name_count; k++) | |
752 | storeAppendPrintf(sentry, " %s", f->names[k]); | |
753 | storeAppendPrintf(sentry, close_bracket); | |
754 | } | |
755 | storeAppendPrintf(sentry, close_bracket); | |
756 | } | |
757 | ||
b8d8561b | 758 | static void |
79d39a72 | 759 | dummy_handler(const char *bufnotused, void *datanotused) |
f88bb09c | 760 | { |
761 | return; | |
762 | } | |
763 | ||
b8d8561b | 764 | static int |
fe4e214f | 765 | fqdncacheHasPending(const fqdncache_entry * f) |
f88bb09c | 766 | { |
0ee4272b | 767 | const struct _fqdn_pending *p = NULL; |
f88bb09c | 768 | if (f->status != FQDN_PENDING) |
769 | return 0; | |
770 | for (p = f->pending_head; p; p = p->next) | |
771 | if (p->handler) | |
772 | return 1; | |
773 | return 0; | |
774 | } | |
775 | ||
b8d8561b | 776 | void |
0ee4272b | 777 | fqdncacheReleaseInvalid(const char *name) |
f88bb09c | 778 | { |
779 | fqdncache_entry *f; | |
780 | if ((f = fqdncache_get(name)) == NULL) | |
781 | return; | |
782 | if (f->status != FQDN_NEGATIVE_CACHED) | |
783 | return; | |
784 | fqdncache_release(f); | |
785 | } | |
28ab0c0a | 786 | |
0ee4272b | 787 | const char * |
b8d8561b | 788 | fqdnFromAddr(struct in_addr addr) |
28ab0c0a | 789 | { |
0ee4272b | 790 | const char *n; |
39de381a | 791 | static char buf[32]; |
17a0a4ee | 792 | if (Config.onoff.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0))) |
28ab0c0a | 793 | return n; |
f2052513 | 794 | xstrncpy(buf, inet_ntoa(addr), 32); |
39de381a | 795 | return buf; |
28ab0c0a | 796 | } |
f201f309 | 797 | |
b8d8561b | 798 | int |
0673c0ba | 799 | fqdncacheQueueDrain(void) |
f201f309 | 800 | { |
801 | fqdncache_entry *i; | |
802 | dnsserver_t *dnsData; | |
803 | if (!fqdncacheQueueHead) | |
a8f7d3ee | 804 | return 0; |
f201f309 | 805 | while ((dnsData = dnsGetFirstAvailable()) && (i = fqdncacheDequeue())) |
a8f7d3ee | 806 | fqdncache_dnsDispatch(dnsData, i); |
f201f309 | 807 | return 1; |
808 | } | |
56e15c50 | 809 | |
429fdbec | 810 | static void |
811 | fqdncacheLockEntry(fqdncache_entry * f) | |
812 | { | |
4bc76d59 | 813 | if (f->locks++ == 0) { |
814 | dlinkDelete(&f->lru, &lru_list); | |
815 | dlinkAdd(f, &f->lru, &lru_list); | |
816 | } | |
429fdbec | 817 | } |
818 | ||
819 | static void | |
820 | fqdncacheUnlockEntry(fqdncache_entry * f) | |
821 | { | |
822 | if (f->locks == 0) { | |
823 | debug_trap("fqdncacheUnlockEntry: Entry has no locks"); | |
824 | return; | |
825 | } | |
826 | f->locks--; | |
827 | if (fqdncacheExpiredEntry(f)) | |
828 | fqdncache_release(f); | |
829 | } | |
830 | ||
56e15c50 | 831 | void |
832 | fqdncacheFreeMemory(void) | |
833 | { | |
834 | fqdncache_entry *f; | |
835 | fqdncache_entry **list; | |
c7433536 | 836 | int i = 0; |
837 | int j = 0; | |
56e15c50 | 838 | int k = 0; |
56e15c50 | 839 | list = xcalloc(meta_data.fqdncache_count, sizeof(fqdncache_entry *)); |
840 | f = (fqdncache_entry *) hash_first(fqdn_table); | |
c7433536 | 841 | while (f && i < meta_data.fqdncache_count) { |
f6610c4e | 842 | *(list + i) = f; |
843 | i++; | |
844 | f = (fqdncache_entry *) hash_next(fqdn_table); | |
56e15c50 | 845 | } |
c7433536 | 846 | for (j = 0; j < i; j++) { |
f6610c4e | 847 | f = *(list + j); |
848 | for (k = 0; k < (int) f->name_count; k++) | |
849 | safe_free(f->names[k]); | |
850 | safe_free(f->name); | |
851 | safe_free(f->error_message); | |
852 | safe_free(f); | |
56e15c50 | 853 | } |
854 | xfree(list); | |
855 | hashFreeMemory(fqdn_table); | |
afe95a7e | 856 | fqdn_table = NULL; |
56e15c50 | 857 | } |
429fdbec | 858 | |
859 | static void | |
860 | fqdncacheChangeKey(fqdncache_entry * f) | |
861 | { | |
862 | static int index = 0; | |
863 | LOCAL_ARRAY(char, new_key, 256); | |
864 | hash_link *table_entry = hash_lookup(fqdn_table, f->name); | |
865 | if (table_entry == NULL) { | |
a3d5953d | 866 | debug(14, 0) ("fqdncacheChangeKey: Could not find key '%s'\n", f->name); |
429fdbec | 867 | return; |
868 | } | |
869 | if (f != (fqdncache_entry *) table_entry) { | |
870 | debug_trap("fqdncacheChangeKey: f != table_entry!"); | |
871 | return; | |
872 | } | |
873 | if (hash_remove_link(fqdn_table, table_entry)) { | |
874 | debug_trap("fqdncacheChangeKey: hash_remove_link() failed\n"); | |
875 | return; | |
876 | } | |
042461c3 | 877 | snprintf(new_key, 256, "%d/", ++index); |
429fdbec | 878 | strncat(new_key, f->name, 128); |
a3d5953d | 879 | debug(14, 1) ("fqdncacheChangeKey: from '%s' to '%s'\n", f->name, new_key); |
429fdbec | 880 | safe_free(f->name); |
881 | f->name = xstrdup(new_key); | |
882 | fqdncache_add_to_hash(f); | |
883 | } | |
884 | ||
885 | /* call during reconfigure phase to clear out all the | |
886 | * pending and dispatched reqeusts that got lost */ | |
887 | void | |
888 | fqdncache_restart(void) | |
889 | { | |
890 | fqdncache_entry *this; | |
891 | fqdncache_entry *next; | |
892 | if (fqdn_table == 0) | |
893 | fatal_dump("fqdncache_restart: fqdn_table == 0\n"); | |
894 | while (fqdncacheDequeue()); | |
895 | next = (fqdncache_entry *) hash_first(fqdn_table); | |
79d39a72 | 896 | while ((this = next) != NULL) { |
429fdbec | 897 | next = (fqdncache_entry *) hash_next(fqdn_table); |
898 | if (this->status == FQDN_CACHED) | |
899 | continue; | |
900 | if (this->status == FQDN_NEGATIVE_CACHED) | |
901 | continue; | |
e144eae4 | 902 | #if DONT |
429fdbec | 903 | /* else its PENDING or DISPATCHED; there are no dnsservers |
904 | * running, so abort it */ | |
905 | this->status = FQDN_NEGATIVE_CACHED; | |
906 | fqdncache_release(this); | |
e144eae4 | 907 | #endif |
429fdbec | 908 | } |
e144eae4 | 909 | fqdncache_high = (long) (((float) MAX_FQDN * |
910 | (float) FQDN_HIGH_WATER) / (float) 100); | |
911 | fqdncache_low = (long) (((float) MAX_FQDN * | |
912 | (float) FQDN_LOW_WATER) / (float) 100); | |
429fdbec | 913 | } |