]>
Commit | Line | Data |
---|---|---|
f88bb09c | 1 | |
2 | /* | |
a8f7d3ee | 3 | * $Id: fqdncache.cc,v 1.13 1996/08/30 23:23:29 wessels Exp $ |
f88bb09c | 4 | * |
7cf620a9 | 5 | * DEBUG: section 35 FQDN Cache |
f88bb09c | 6 | * AUTHOR: Harvest Derived |
7 | * | |
8 | * SQUID Internet Object Cache http://www.nlanr.net/Squid/ | |
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 | |
114 | #define MAX_HOST_NAME 256 | |
115 | ||
116 | struct _fqdn_pending { | |
117 | int fd; | |
118 | FQDNH handler; | |
119 | void *handlerData; | |
120 | struct _fqdn_pending *next; | |
121 | }; | |
122 | ||
123 | struct fqdncacheQueueData { | |
124 | struct fqdncacheQueueData *next; | |
125 | fqdncache_entry *f; | |
126 | }; | |
127 | ||
128 | static struct { | |
129 | int requests; | |
130 | int replies; | |
131 | int hits; | |
132 | int misses; | |
133 | int pending_hits; | |
134 | int negative_hits; | |
135 | int errors; | |
136 | int avg_svc_time; | |
137 | int ghba_calls; /* # calls to blocking gethostbyaddr() */ | |
138 | } FqdncacheStats; | |
139 | ||
140 | typedef struct _line_entry { | |
141 | char *line; | |
142 | struct _line_entry *next; | |
143 | } line_entry; | |
144 | ||
145 | static int fqdncache_compareLastRef _PARAMS((fqdncache_entry **, fqdncache_entry **)); | |
146 | static int fqdncache_dnsHandleRead _PARAMS((int, dnsserver_t *)); | |
147 | static int fqdncache_parsebuffer _PARAMS((char *buf, unsigned int offset, dnsserver_t *)); | |
148 | static int fqdncache_purgelru _PARAMS((void)); | |
149 | static void fqdncache_release _PARAMS((fqdncache_entry *)); | |
150 | static fqdncache_entry *fqdncache_GetFirst _PARAMS((void)); | |
151 | static fqdncache_entry *fqdncache_GetNext _PARAMS((void)); | |
152 | static fqdncache_entry *fqdncache_create _PARAMS((void)); | |
153 | static void free_lines _PARAMS((line_entry *)); | |
154 | static void fqdncache_add_to_hash _PARAMS((fqdncache_entry *)); | |
155 | static void fqdncache_call_pending _PARAMS((fqdncache_entry *)); | |
156 | static void fqdncache_call_pending_badname _PARAMS((int fd, FQDNH handler, void *)); | |
157 | static void fqdncache_add _PARAMS((char *, fqdncache_entry *, struct hostent *, int)); | |
158 | static int fqdncacheHasPending _PARAMS((fqdncache_entry *)); | |
159 | static fqdncache_entry *fqdncache_get _PARAMS((char *)); | |
160 | static void dummy_handler _PARAMS((int, char *, void *)); | |
161 | static int fqdncacheExpiredEntry _PARAMS((fqdncache_entry *)); | |
162 | static void fqdncacheAddPending _PARAMS((fqdncache_entry *, int fd, FQDNH, void *)); | |
163 | static void fqdncacheEnqueue _PARAMS((fqdncache_entry *)); | |
164 | static void *fqdncacheDequeue _PARAMS((void)); | |
165 | static void fqdncache_dnsDispatch _PARAMS((dnsserver_t *, fqdncache_entry *)); | |
166 | ||
167 | static struct hostent *static_result = NULL; | |
168 | static HashID fqdn_table = 0; | |
169 | static struct fqdncacheQueueData *fqdncacheQueueHead = NULL; | |
170 | static struct fqdncacheQueueData **fqdncacheQueueTailP = &fqdncacheQueueHead; | |
171 | ||
172 | static char fqdncache_status_char[] = | |
173 | { | |
174 | 'C', | |
175 | 'N', | |
176 | 'P', | |
177 | 'D' | |
178 | }; | |
179 | ||
180 | long fqdncache_low = 180; | |
181 | long fqdncache_high = 200; | |
182 | ||
183 | static void fqdncacheEnqueue(f) | |
184 | fqdncache_entry *f; | |
185 | { | |
186 | struct fqdncacheQueueData *new = xcalloc(1, sizeof(struct fqdncacheQueueData)); | |
187 | new->f = f; | |
188 | *fqdncacheQueueTailP = new; | |
189 | fqdncacheQueueTailP = &new->next; | |
190 | } | |
191 | ||
192 | static void *fqdncacheDequeue() | |
193 | { | |
194 | struct fqdncacheQueueData *old = NULL; | |
195 | fqdncache_entry *f = NULL; | |
196 | if (fqdncacheQueueHead) { | |
197 | f = fqdncacheQueueHead->f; | |
198 | old = fqdncacheQueueHead; | |
199 | fqdncacheQueueHead = fqdncacheQueueHead->next; | |
200 | if (fqdncacheQueueHead == NULL) | |
201 | fqdncacheQueueTailP = &fqdncacheQueueHead; | |
202 | safe_free(old); | |
203 | } | |
204 | return f; | |
205 | } | |
206 | ||
207 | /* removes the given fqdncache entry */ | |
208 | static void fqdncache_release(f) | |
209 | fqdncache_entry *f; | |
210 | { | |
211 | fqdncache_entry *result = NULL; | |
212 | hash_link *table_entry = NULL; | |
213 | int k; | |
214 | ||
215 | if ((table_entry = hash_lookup(fqdn_table, f->name)) == NULL) { | |
7cf620a9 | 216 | debug(35, 0, "fqdncache_release: Could not find key '%s'\n", f->name); |
f88bb09c | 217 | return; |
218 | } | |
219 | result = (fqdncache_entry *) table_entry; | |
220 | if (f != result) | |
221 | fatal_dump("fqdncache_release: expected f == result!"); | |
222 | if (f->status == FQDN_PENDING) { | |
7cf620a9 | 223 | debug(35, 1, "fqdncache_release: Someone called on a PENDING entry\n"); |
f88bb09c | 224 | return; |
225 | } | |
226 | if (f->status == FQDN_DISPATCHED) { | |
7cf620a9 | 227 | debug(35, 1, "fqdncache_release: Someone called on a DISPATCHED entry\n"); |
f88bb09c | 228 | return; |
229 | } | |
230 | if (hash_remove_link(fqdn_table, table_entry)) { | |
7cf620a9 | 231 | debug(35, 0, "fqdncache_release: hash_remove_link() failed for '%s'\n", |
f88bb09c | 232 | result->name); |
233 | return; | |
234 | } | |
235 | if (result->status == FQDN_CACHED) { | |
236 | for (k = 0; k < (int) f->name_count; k++) | |
237 | safe_free(f->names[k]); | |
7cf620a9 | 238 | debug(35, 5, "fqdncache_release: Released FQDN record for '%s'.\n", |
f88bb09c | 239 | result->name); |
240 | } | |
241 | safe_free(result->name); | |
242 | safe_free(result->error_message); | |
243 | memset(result, '\0', sizeof(fqdncache_entry)); | |
244 | safe_free(result); | |
245 | --meta_data.fqdncache_count; | |
246 | return; | |
247 | } | |
248 | ||
249 | /* return match for given name */ | |
250 | static fqdncache_entry *fqdncache_get(name) | |
251 | char *name; | |
252 | { | |
253 | hash_link *e; | |
254 | static fqdncache_entry *f; | |
255 | ||
256 | f = NULL; | |
257 | if (fqdn_table) { | |
258 | if ((e = hash_lookup(fqdn_table, name)) != NULL) | |
259 | f = (fqdncache_entry *) e; | |
260 | } | |
261 | return f; | |
262 | } | |
263 | ||
264 | static fqdncache_entry *fqdncache_GetFirst() | |
265 | { | |
266 | return (fqdncache_entry *) hash_first(fqdn_table); | |
267 | } | |
268 | ||
269 | static fqdncache_entry *fqdncache_GetNext() | |
270 | { | |
271 | return (fqdncache_entry *) hash_next(fqdn_table); | |
272 | } | |
273 | ||
274 | static int fqdncache_compareLastRef(e1, e2) | |
275 | fqdncache_entry **e1, **e2; | |
276 | { | |
277 | if (!e1 || !e2) | |
278 | fatal_dump(NULL); | |
279 | if ((*e1)->lastref > (*e2)->lastref) | |
280 | return (1); | |
281 | if ((*e1)->lastref < (*e2)->lastref) | |
282 | return (-1); | |
283 | return (0); | |
284 | } | |
285 | ||
286 | static int fqdncacheExpiredEntry(f) | |
287 | fqdncache_entry *f; | |
288 | { | |
289 | if (f->status == FQDN_PENDING) | |
290 | return 0; | |
291 | if (f->status == FQDN_DISPATCHED) | |
292 | return 0; | |
293 | if (f->ttl + f->timestamp > squid_curtime) | |
294 | return 0; | |
295 | return 1; | |
296 | } | |
297 | ||
298 | /* finds the LRU and deletes */ | |
299 | static int fqdncache_purgelru() | |
300 | { | |
301 | fqdncache_entry *f = NULL; | |
302 | int local_fqdn_count = 0; | |
303 | int local_fqdn_notpending_count = 0; | |
304 | int removed = 0; | |
305 | int k; | |
306 | fqdncache_entry **LRU_list = NULL; | |
307 | int LRU_list_count = 0; | |
308 | int LRU_cur_size = meta_data.fqdncache_count; | |
309 | ||
310 | LRU_list = xcalloc(LRU_cur_size, sizeof(fqdncache_entry *)); | |
311 | ||
312 | for (f = fqdncache_GetFirst(); f; f = fqdncache_GetNext()) { | |
313 | if (fqdncacheExpiredEntry(f)) { | |
314 | fqdncache_release(f); | |
315 | removed++; | |
316 | continue; | |
317 | } | |
318 | local_fqdn_count++; | |
319 | ||
320 | if (LRU_list_count >= LRU_cur_size) { | |
321 | /* have to realloc */ | |
322 | LRU_cur_size += 16; | |
7cf620a9 | 323 | debug(35, 3, "fqdncache_purgelru: Have to grow LRU_list to %d. This shouldn't happen.\n", |
f88bb09c | 324 | LRU_cur_size); |
325 | LRU_list = xrealloc((char *) LRU_list, | |
326 | LRU_cur_size * sizeof(fqdncache_entry *)); | |
327 | } | |
328 | if (f->status == FQDN_PENDING) | |
329 | continue; | |
330 | if (f->status == FQDN_DISPATCHED) | |
331 | continue; | |
332 | local_fqdn_notpending_count++; | |
333 | LRU_list[LRU_list_count++] = f; | |
334 | } | |
335 | ||
7cf620a9 | 336 | debug(35, 3, "fqdncache_purgelru: fqdncache_count: %5d\n", meta_data.fqdncache_count); |
337 | debug(35, 3, " actual count : %5d\n", local_fqdn_count); | |
338 | debug(35, 3, " high W mark : %5d\n", fqdncache_high); | |
339 | debug(35, 3, " low W mark : %5d\n", fqdncache_low); | |
340 | debug(35, 3, " not pending : %5d\n", local_fqdn_notpending_count); | |
341 | debug(35, 3, " LRU candidates : %5d\n", LRU_list_count); | |
f88bb09c | 342 | |
343 | /* sort LRU candidate list */ | |
344 | qsort((char *) LRU_list, | |
345 | LRU_list_count, | |
346 | sizeof(f), | |
347 | (int (*)(const void *, const void *)) fqdncache_compareLastRef); | |
348 | for (k = 0; LRU_list[k] && (meta_data.fqdncache_count > fqdncache_low) | |
349 | && k < LRU_list_count; | |
350 | ++k) { | |
351 | fqdncache_release(LRU_list[k]); | |
352 | removed++; | |
353 | } | |
354 | ||
7cf620a9 | 355 | debug(35, 3, " removed : %5d\n", removed); |
f88bb09c | 356 | safe_free(LRU_list); |
357 | return (removed > 0) ? 0 : -1; | |
358 | } | |
359 | ||
360 | ||
361 | /* create blank fqdncache_entry */ | |
362 | static fqdncache_entry *fqdncache_create() | |
363 | { | |
364 | static fqdncache_entry *new; | |
365 | ||
366 | if (meta_data.fqdncache_count > fqdncache_high) { | |
367 | if (fqdncache_purgelru() < 0) | |
7cf620a9 | 368 | debug(35, 0, "HELP!! FQDN Cache is overflowing!\n"); |
f88bb09c | 369 | } |
370 | meta_data.fqdncache_count++; | |
371 | new = xcalloc(1, sizeof(fqdncache_entry)); | |
372 | return new; | |
373 | ||
374 | } | |
375 | ||
376 | static void fqdncache_add_to_hash(f) | |
377 | fqdncache_entry *f; | |
378 | { | |
379 | if (hash_join(fqdn_table, (hash_link *) f)) { | |
7cf620a9 | 380 | debug(35, 1, "fqdncache_add_to_hash: Cannot add %s (%p) to hash table %d.\n", |
f88bb09c | 381 | f->name, f, fqdn_table); |
382 | } | |
7cf620a9 | 383 | debug(35, 5, "fqdncache_add_to_hash: name <%s>\n", f->name); |
f88bb09c | 384 | } |
385 | ||
386 | ||
387 | static void fqdncache_add(name, f, hp, cached) | |
388 | char *name; | |
389 | fqdncache_entry *f; | |
390 | struct hostent *hp; | |
391 | int cached; | |
392 | { | |
393 | int k; | |
394 | ||
395 | if (fqdncache_get(name)) | |
396 | fatal_dump("fqdncache_add: somebody adding a duplicate!"); | |
7cf620a9 | 397 | debug(35, 10, "fqdncache_add: Adding name '%s' (%s).\n", name, |
f88bb09c | 398 | cached ? "cached" : "not cached"); |
399 | f->name = xstrdup(name); | |
400 | if (cached) { | |
401 | f->name_count = 0; | |
402 | f->names[f->name_count++] = xstrdup(hp->h_name); | |
403 | for (k = 0; hp->h_aliases[k]; k++) { | |
404 | f->names[f->name_count++] = xstrdup(hp->h_aliases[k]); | |
405 | if (f->name_count == FQDN_MAX_NAMES) | |
406 | break; | |
407 | } | |
408 | f->lastref = f->timestamp = squid_curtime; | |
409 | f->status = FQDN_CACHED; | |
b6f794d6 | 410 | f->ttl = Config.positiveDnsTtl; |
f88bb09c | 411 | } else { |
412 | f->lastref = f->timestamp = squid_curtime; | |
413 | f->status = FQDN_NEGATIVE_CACHED; | |
b6f794d6 | 414 | f->ttl = Config.negativeDnsTtl; |
f88bb09c | 415 | } |
416 | fqdncache_add_to_hash(f); | |
417 | } | |
418 | ||
419 | /* walks down the pending list, calling handlers */ | |
420 | static void fqdncache_call_pending(f) | |
421 | fqdncache_entry *f; | |
422 | { | |
423 | struct _fqdn_pending *p = NULL; | |
424 | int nhandler = 0; | |
425 | ||
426 | f->lastref = squid_curtime; | |
427 | ||
428 | while (f->pending_head != NULL) { | |
429 | p = f->pending_head; | |
430 | f->pending_head = p->next; | |
431 | if (p->handler) { | |
432 | nhandler++; | |
433 | dns_error_message = f->error_message; | |
434 | p->handler(p->fd, | |
435 | (f->status == FQDN_CACHED) ? f->names[0] : NULL, | |
436 | p->handlerData); | |
437 | } | |
438 | memset(p, '\0', sizeof(struct _fqdn_pending)); | |
439 | safe_free(p); | |
440 | } | |
441 | f->pending_head = NULL; /* nuke list */ | |
7cf620a9 | 442 | debug(35, 10, "fqdncache_call_pending: Called %d handlers.\n", nhandler); |
f88bb09c | 443 | } |
444 | ||
445 | static void fqdncache_call_pending_badname(fd, handler, data) | |
446 | int fd; | |
447 | FQDNH handler; | |
448 | void *data; | |
449 | { | |
7cf620a9 | 450 | debug(35, 0, "fqdncache_call_pending_badname: Bad Name: Calling handler with NULL result.\n"); |
f88bb09c | 451 | handler(fd, NULL, data); |
452 | } | |
453 | ||
454 | /* free all lines in the list */ | |
455 | static void free_lines(line) | |
456 | line_entry *line; | |
457 | { | |
458 | line_entry *tmp; | |
459 | ||
460 | while (line) { | |
461 | tmp = line; | |
462 | line = line->next; | |
463 | safe_free(tmp->line); | |
464 | safe_free(tmp); | |
465 | } | |
466 | } | |
467 | ||
468 | /* scan through buffer and do a conversion if possible | |
469 | * return number of char used */ | |
470 | static int fqdncache_parsebuffer(buf, offset, dnsData) | |
471 | char *buf; | |
472 | unsigned int offset; | |
473 | dnsserver_t *dnsData; | |
474 | { | |
475 | char *pos = NULL; | |
476 | char *tpos = NULL; | |
477 | char *endpos = NULL; | |
478 | char *token = NULL; | |
479 | char *tmp_ptr = NULL; | |
480 | line_entry *line_head = NULL; | |
481 | line_entry *line_tail = NULL; | |
482 | line_entry *line_cur = NULL; | |
483 | int ipcount; | |
484 | int aliascount; | |
485 | fqdncache_entry *f = NULL; | |
486 | ||
487 | ||
488 | pos = buf; | |
489 | while (pos < (buf + offset)) { | |
490 | ||
491 | /* no complete record here */ | |
492 | if ((endpos = strstr(pos, "$end\n")) == NULL) { | |
7cf620a9 | 493 | debug(35, 2, "fqdncache_parsebuffer: DNS response incomplete.\n"); |
f88bb09c | 494 | break; |
495 | } | |
496 | line_head = line_tail = NULL; | |
497 | ||
498 | while (pos < endpos) { | |
499 | /* add the next line to the end of the list */ | |
500 | line_cur = xcalloc(1, sizeof(line_entry)); | |
501 | ||
502 | if ((tpos = memchr(pos, '\n', 4096)) == NULL) { | |
7cf620a9 | 503 | debug(35, 2, "fqdncache_parsebuffer: DNS response incomplete.\n"); |
f88bb09c | 504 | return -1; |
505 | } | |
506 | *tpos = '\0'; | |
507 | line_cur->line = xstrdup(pos); | |
7cf620a9 | 508 | debug(35, 7, "fqdncache_parsebuffer: %s\n", line_cur->line); |
f88bb09c | 509 | *tpos = '\n'; |
510 | ||
511 | if (line_tail) | |
512 | line_tail->next = line_cur; | |
513 | if (line_head == NULL) | |
514 | line_head = line_cur; | |
515 | line_tail = line_cur; | |
516 | line_cur = NULL; | |
517 | ||
518 | /* update pointer */ | |
519 | pos = tpos + 1; | |
520 | } | |
521 | pos = endpos + 5; /* strlen("$end\n") */ | |
522 | ||
523 | /* | |
524 | * At this point, the line_head is a linked list with each | |
525 | * link node containing another line of the DNS response. | |
526 | * Start parsing... | |
527 | */ | |
528 | if (strstr(line_head->line, "$alive")) { | |
529 | dnsData->answer = squid_curtime; | |
530 | free_lines(line_head); | |
7cf620a9 | 531 | debug(35, 10, "fqdncache_parsebuffer: $alive succeeded.\n"); |
f88bb09c | 532 | } else if (strstr(line_head->line, "$fail")) { |
533 | /* | |
534 | * The $fail messages look like: | |
535 | * $fail host\n$message msg\n$end\n | |
536 | */ | |
537 | token = strtok(line_head->line, w_space); /* skip first token */ | |
538 | if ((token = strtok(NULL, w_space)) == NULL) { | |
7cf620a9 | 539 | debug(35, 1, "fqdncache_parsebuffer: Invalid $fail?\n"); |
f88bb09c | 540 | } else { |
541 | line_cur = line_head->next; | |
542 | f = dnsData->data; | |
543 | f->lastref = f->timestamp = squid_curtime; | |
b6f794d6 | 544 | f->ttl = Config.negativeDnsTtl; |
f88bb09c | 545 | f->status = FQDN_NEGATIVE_CACHED; |
546 | if (line_cur && !strncmp(line_cur->line, "$message", 8)) | |
547 | f->error_message = xstrdup(line_cur->line + 8); | |
548 | dns_error_message = f->error_message; | |
549 | fqdncache_call_pending(f); | |
550 | } | |
551 | free_lines(line_head); | |
552 | } else if (strstr(line_head->line, "$name")) { | |
553 | tmp_ptr = line_head->line; | |
554 | /* skip the first token */ | |
555 | token = strtok(tmp_ptr, w_space); | |
556 | if ((token = strtok(NULL, w_space)) == NULL) { | |
7cf620a9 | 557 | debug(35, 0, "fqdncache_parsebuffer: Invalid OPCODE?\n"); |
f88bb09c | 558 | } else { |
559 | f = dnsData->data; | |
560 | if (f->status != FQDN_DISPATCHED) { | |
7cf620a9 | 561 | debug(35, 0, "fqdncache_parsebuffer: DNS record already resolved.\n"); |
f88bb09c | 562 | } else { |
563 | f->lastref = f->timestamp = squid_curtime; | |
b6f794d6 | 564 | f->ttl = Config.positiveDnsTtl; |
f88bb09c | 565 | f->status = FQDN_CACHED; |
566 | ||
567 | line_cur = line_head->next; | |
568 | ||
569 | /* get $h_name */ | |
570 | if (line_cur == NULL || | |
571 | !strstr(line_cur->line, "$h_name")) { | |
7cf620a9 | 572 | debug(35, 1, "fqdncache_parsebuffer: DNS record in invalid format? No $h_name.\n"); |
f88bb09c | 573 | /* abandon this record */ |
574 | break; | |
575 | } | |
576 | tmp_ptr = line_cur->line; | |
577 | /* skip the first token */ | |
578 | token = strtok(tmp_ptr, w_space); | |
579 | tmp_ptr = NULL; | |
580 | token = strtok(tmp_ptr, w_space); | |
581 | f->names[0] = xstrdup(token); | |
582 | f->name_count = 1; | |
583 | ||
584 | line_cur = line_cur->next; | |
585 | ||
586 | /* get $h_length */ | |
587 | if (line_cur == NULL || | |
588 | !strstr(line_cur->line, "$h_len")) { | |
7cf620a9 | 589 | debug(35, 1, "fqdncache_parsebuffer: DNS record in invalid format? No $h_len.\n"); |
f88bb09c | 590 | /* abandon this record */ |
591 | break; | |
592 | } | |
593 | tmp_ptr = line_cur->line; | |
594 | /* skip the first token */ | |
595 | token = strtok(tmp_ptr, w_space); | |
596 | tmp_ptr = NULL; | |
597 | token = strtok(tmp_ptr, w_space); | |
598 | ||
599 | line_cur = line_cur->next; | |
600 | ||
601 | /* get $ipcount */ | |
602 | if (line_cur == NULL || | |
603 | !strstr(line_cur->line, "$ipcount")) { | |
7cf620a9 | 604 | debug(35, 1, "fqdncache_parsebuffer: DNS record in invalid format? No $ipcount.\n"); |
f88bb09c | 605 | /* abandon this record */ |
606 | break; | |
607 | } | |
608 | tmp_ptr = line_cur->line; | |
609 | /* skip the first token */ | |
610 | token = strtok(tmp_ptr, w_space); | |
611 | tmp_ptr = NULL; | |
612 | token = strtok(tmp_ptr, w_space); | |
613 | ipcount = atoi(token); | |
614 | ||
615 | /* get ip addresses */ | |
616 | { | |
617 | int k = 0; | |
618 | line_cur = line_cur->next; | |
619 | while (k < ipcount) { | |
620 | if (line_cur == NULL) { | |
7cf620a9 | 621 | debug(35, 1, "fqdncache_parsebuffer: DNS record in invalid format? No $ipcount data.\n"); |
f88bb09c | 622 | break; |
623 | } | |
624 | line_cur = line_cur->next; | |
625 | k++; | |
626 | } | |
627 | } | |
628 | ||
629 | /* get $aliascount */ | |
630 | if (line_cur == NULL || | |
631 | !strstr(line_cur->line, "$aliascount")) { | |
7cf620a9 | 632 | debug(35, 1, "fqdncache_parsebuffer: DNS record in invalid format? No $aliascount.\n"); |
f88bb09c | 633 | /* abandon this record */ |
634 | break; | |
635 | } | |
636 | tmp_ptr = line_cur->line; | |
637 | /* skip the first token */ | |
638 | token = strtok(tmp_ptr, w_space); | |
639 | tmp_ptr = NULL; | |
640 | token = strtok(tmp_ptr, w_space); | |
641 | aliascount = atoi(token); | |
642 | ||
643 | /* get aliases */ | |
644 | { | |
645 | int k = 0; | |
646 | line_cur = line_cur->next; | |
647 | while (k < aliascount) { | |
648 | if (line_cur == NULL) { | |
7cf620a9 | 649 | debug(35, 1, "fqdncache_parsebuffer: DNS record in invalid format? No $aliascount data.\n"); |
f88bb09c | 650 | break; |
651 | } | |
652 | if (f->name_count < FQDN_MAX_NAMES) | |
653 | f->names[f->name_count++] = xstrdup(line_cur->line); | |
654 | line_cur = line_cur->next; | |
655 | k++; | |
656 | } | |
657 | } | |
658 | fqdncache_call_pending(f); | |
7cf620a9 | 659 | debug(35, 10, "fqdncache_parsebuffer: $name succeeded.\n"); |
f88bb09c | 660 | } |
661 | } | |
662 | free_lines(line_head); | |
663 | } else { | |
664 | free_lines(line_head); | |
7cf620a9 | 665 | debug(35, 1, "fqdncache_parsebuffer: Invalid OPCODE for DNS table?\n"); |
f88bb09c | 666 | return -1; |
667 | } | |
668 | } | |
669 | return (int) (pos - buf); | |
670 | } | |
671 | ||
672 | ||
673 | static int fqdncache_dnsHandleRead(fd, dnsData) | |
674 | int fd; | |
675 | dnsserver_t *dnsData; | |
676 | { | |
677 | int char_scanned; | |
678 | int len; | |
679 | int svc_time; | |
680 | int n; | |
681 | fqdncache_entry *f = NULL; | |
682 | ||
683 | len = read(fd, | |
684 | dnsData->ip_inbuf + dnsData->offset, | |
685 | dnsData->size - dnsData->offset); | |
7cf620a9 | 686 | debug(35, 5, "fqdncache_dnsHandleRead: Result from DNS ID %d (%d bytes)\n", |
f88bb09c | 687 | dnsData->id, len); |
688 | if (len <= 0) { | |
7cf620a9 | 689 | debug(35, dnsData->flags & DNS_FLAG_CLOSING ? 5 : 1, |
f88bb09c | 690 | "FD %d: Connection from DNSSERVER #%d is closed, disabling\n", |
691 | fd, dnsData->id); | |
692 | dnsData->flags = 0; | |
693 | comm_set_select_handler(fd, | |
694 | COMM_SELECT_WRITE, | |
695 | NULL, | |
696 | NULL); | |
697 | comm_close(fd); | |
698 | return 0; | |
699 | } | |
700 | n = ++FqdncacheStats.replies; | |
b0c9fba9 | 701 | DnsStats.replies++; |
f88bb09c | 702 | dnsData->offset += len; |
703 | dnsData->ip_inbuf[dnsData->offset] = '\0'; | |
704 | ||
705 | if (strstr(dnsData->ip_inbuf, "$end\n")) { | |
706 | /* end of record found */ | |
707 | svc_time = tvSubMsec(dnsData->dispatch_time, current_time); | |
708 | if (n > FQDNCACHE_AV_FACTOR) | |
709 | n = FQDNCACHE_AV_FACTOR; | |
710 | FqdncacheStats.avg_svc_time | |
711 | = (FqdncacheStats.avg_svc_time * (n - 1) + svc_time) / n; | |
712 | char_scanned = fqdncache_parsebuffer(dnsData->ip_inbuf, | |
713 | dnsData->offset, | |
714 | dnsData); | |
715 | if (char_scanned > 0) { | |
716 | /* update buffer */ | |
717 | xmemcpy(dnsData->ip_inbuf, | |
718 | dnsData->ip_inbuf + char_scanned, | |
719 | dnsData->offset - char_scanned); | |
720 | dnsData->offset -= char_scanned; | |
721 | dnsData->ip_inbuf[dnsData->offset] = '\0'; | |
722 | } | |
723 | } | |
724 | if (dnsData->offset == 0) { | |
725 | dnsData->data = NULL; | |
726 | dnsData->flags &= ~DNS_FLAG_BUSY; | |
727 | } | |
728 | while ((dnsData = dnsGetFirstAvailable()) && (f = fqdncacheDequeue())) | |
729 | fqdncache_dnsDispatch(dnsData, f); | |
730 | return 0; | |
731 | } | |
732 | ||
733 | static void fqdncacheAddPending(f, fd, handler, handlerData) | |
734 | fqdncache_entry *f; | |
735 | int fd; | |
736 | FQDNH handler; | |
737 | void *handlerData; | |
738 | { | |
739 | struct _fqdn_pending *pending = xcalloc(1, sizeof(struct _fqdn_pending)); | |
740 | struct _fqdn_pending **I = NULL; | |
741 | ||
742 | pending->fd = fd; | |
743 | pending->handler = handler; | |
744 | pending->handlerData = handlerData; | |
745 | ||
746 | for (I = &(f->pending_head); *I; I = &((*I)->next)); | |
747 | *I = pending; | |
748 | } | |
749 | ||
750 | int fqdncache_nbgethostbyaddr(addr, fd, handler, handlerData) | |
751 | struct in_addr addr; | |
752 | int fd; | |
753 | FQDNH handler; | |
754 | void *handlerData; | |
755 | { | |
756 | fqdncache_entry *f = NULL; | |
757 | dnsserver_t *dnsData = NULL; | |
758 | char *name = inet_ntoa(addr); | |
759 | ||
760 | if (!handler) | |
761 | fatal_dump("fqdncache_nbgethostbyaddr: NULL handler"); | |
762 | ||
7cf620a9 | 763 | debug(35, 4, "fqdncache_nbgethostbyaddr: FD %d: Name '%s'.\n", fd, name); |
f88bb09c | 764 | FqdncacheStats.requests++; |
765 | ||
766 | if (name == NULL || name[0] == '\0') { | |
7cf620a9 | 767 | debug(35, 4, "fqdncache_nbgethostbyaddr: Invalid name!\n"); |
f88bb09c | 768 | fqdncache_call_pending_badname(fd, handler, handlerData); |
769 | return 0; | |
770 | } | |
771 | if ((f = fqdncache_get(name))) { | |
772 | if (fqdncacheExpiredEntry(f)) { | |
773 | fqdncache_release(f); | |
774 | f = NULL; | |
775 | } | |
776 | } | |
777 | if (f == NULL) { | |
778 | /* MISS: No entry, create the new one */ | |
7cf620a9 | 779 | debug(35, 5, "fqdncache_nbgethostbyaddr: MISS for '%s'\n", name); |
f88bb09c | 780 | FqdncacheStats.misses++; |
781 | f = fqdncache_create(); | |
782 | f->name = xstrdup(name); | |
783 | f->status = FQDN_PENDING; | |
784 | fqdncacheAddPending(f, fd, handler, handlerData); | |
785 | fqdncache_add_to_hash(f); | |
786 | } else if (f->status == FQDN_CACHED || f->status == FQDN_NEGATIVE_CACHED) { | |
787 | /* HIT */ | |
7cf620a9 | 788 | debug(35, 4, "fqdncache_nbgethostbyaddr: HIT for '%s'\n", name); |
f88bb09c | 789 | if (f->status == FQDN_NEGATIVE_CACHED) |
790 | FqdncacheStats.negative_hits++; | |
791 | else | |
792 | FqdncacheStats.hits++; | |
793 | fqdncacheAddPending(f, fd, handler, handlerData); | |
794 | fqdncache_call_pending(f); | |
795 | return 0; | |
796 | } else if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { | |
7cf620a9 | 797 | debug(35, 4, "fqdncache_nbgethostbyaddr: PENDING for '%s'\n", name); |
f88bb09c | 798 | FqdncacheStats.pending_hits++; |
799 | fqdncacheAddPending(f, fd, handler, handlerData); | |
800 | return 0; | |
801 | } else { | |
802 | fatal_dump("fqdncache_nbgethostbyaddr: BAD fqdncache_entry status"); | |
803 | } | |
804 | ||
805 | /* for HIT, PENDING, DISPATCHED we've returned. For MISS we continue */ | |
806 | ||
807 | if ((dnsData = dnsGetFirstAvailable())) | |
808 | fqdncache_dnsDispatch(dnsData, f); | |
809 | else | |
810 | fqdncacheEnqueue(f); | |
811 | return 0; | |
812 | } | |
813 | ||
814 | static void fqdncache_dnsDispatch(dns, f) | |
815 | dnsserver_t *dns; | |
816 | fqdncache_entry *f; | |
817 | { | |
818 | char *buf = NULL; | |
819 | if (!fqdncacheHasPending(f)) { | |
7cf620a9 | 820 | debug(35, 0, "fqdncache_dnsDispatch: skipping '%s' because no handler.\n", |
f88bb09c | 821 | f->name); |
822 | f->status = FQDN_NEGATIVE_CACHED; | |
823 | fqdncache_release(f); | |
824 | return; | |
825 | } | |
826 | f->status = FQDN_DISPATCHED; | |
827 | buf = xcalloc(1, 256); | |
828 | sprintf(buf, "%1.254s\n", f->name); | |
829 | dns->flags |= DNS_FLAG_BUSY; | |
830 | dns->data = f; | |
831 | comm_write(dns->outpipe, | |
832 | buf, | |
833 | strlen(buf), | |
834 | 0, /* timeout */ | |
835 | NULL, /* Handler */ | |
836 | NULL, /* Handler-data */ | |
837 | xfree); | |
838 | comm_set_select_handler(dns->outpipe, | |
839 | COMM_SELECT_READ, | |
840 | (PF) fqdncache_dnsHandleRead, | |
841 | dns); | |
7cf620a9 | 842 | debug(35, 5, "fqdncache_dnsDispatch: Request sent to DNS server #%d.\n", |
f88bb09c | 843 | dns->id); |
844 | dns->dispatch_time = current_time; | |
845 | DnsStats.requests++; | |
846 | DnsStats.hist[dns->id - 1]++; | |
847 | } | |
848 | ||
849 | ||
850 | /* initialize the fqdncache */ | |
851 | void fqdncache_init() | |
852 | { | |
7cf620a9 | 853 | debug(35, 3, "Initializing FQDN Cache...\n"); |
f88bb09c | 854 | |
855 | memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats)); | |
856 | ||
857 | fqdn_table = hash_create(urlcmp, 229, hash_string); /* small hash table */ | |
858 | /* init static area */ | |
859 | static_result = xcalloc(1, sizeof(struct hostent)); | |
860 | static_result->h_length = 4; | |
861 | static_result->h_addr_list = xcalloc(2, sizeof(char *)); | |
862 | *(static_result->h_addr_list + 0) = xcalloc(1, 4); | |
863 | static_result->h_name = xcalloc(1, MAX_HOST_NAME + 1); | |
864 | ||
865 | fqdncache_high = (long) (((float) MAX_FQDN * | |
866 | (float) FQDN_HIGH_WATER) / (float) 100); | |
867 | fqdncache_low = (long) (((float) MAX_FQDN * | |
868 | (float) FQDN_LOW_WATER) / (float) 100); | |
869 | } | |
870 | ||
871 | /* clean up the pending entries in dnsserver */ | |
872 | /* return 1 if we found the host, 0 otherwise */ | |
03047798 | 873 | int fqdncacheUnregister(addr, fd) |
874 | struct in_addr addr; | |
f88bb09c | 875 | int fd; |
876 | { | |
03047798 | 877 | char *name = inet_ntoa(addr); |
f88bb09c | 878 | fqdncache_entry *f = NULL; |
879 | struct _fqdn_pending *p = NULL; | |
880 | int n = 0; | |
881 | ||
7cf620a9 | 882 | debug(35, 3, "fqdncache_unregister: FD %d, name '%s'\n", fd, name); |
f88bb09c | 883 | if ((f = fqdncache_get(name)) == NULL) |
884 | return 0; | |
885 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { | |
886 | for (p = f->pending_head; p; p = p->next) { | |
887 | if (p->fd == fd && p->handler != NULL) { | |
888 | p->handler = NULL; | |
889 | p->fd = -1; | |
890 | n++; | |
891 | } | |
892 | } | |
893 | } | |
7cf620a9 | 894 | debug(35, 3, "fqdncache_unregister: unregistered %d handlers\n", n); |
f88bb09c | 895 | return n; |
896 | } | |
897 | ||
898 | char *fqdncache_gethostbyaddr(addr, flags) | |
899 | struct in_addr addr; | |
900 | int flags; | |
901 | { | |
902 | char *name = inet_ntoa(addr); | |
903 | fqdncache_entry *f = NULL; | |
904 | struct hostent *hp = NULL; | |
905 | unsigned int ip; | |
906 | ||
907 | if (!name) | |
908 | fatal_dump("fqdncache_gethostbyaddr: NULL name"); | |
909 | FqdncacheStats.requests++; | |
910 | if ((f = fqdncache_get(name))) { | |
911 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { | |
912 | FqdncacheStats.pending_hits++; | |
913 | return NULL; | |
914 | } else if (f->status == FQDN_NEGATIVE_CACHED) { | |
915 | FqdncacheStats.negative_hits++; | |
916 | dns_error_message = f->error_message; | |
917 | return NULL; | |
918 | } else { | |
919 | FqdncacheStats.hits++; | |
920 | f->lastref = squid_curtime; | |
921 | return f->names[0]; | |
922 | } | |
923 | } | |
924 | FqdncacheStats.misses++; | |
925 | /* check if it's already a FQDN address in text form. */ | |
926 | if (inet_addr(name) == INADDR_NONE) { | |
927 | return name; | |
928 | } | |
929 | if (flags & FQDN_BLOCKING_LOOKUP) { | |
930 | FqdncacheStats.ghba_calls++; | |
931 | ip = inet_addr(name); | |
f7a5493b | 932 | hp = gethostbyaddr((char *) &ip, 4, AF_INET); |
f88bb09c | 933 | if (hp && hp->h_name && (hp->h_name[0] != '\0') && fqdn_table) { |
934 | /* good address, cached */ | |
935 | fqdncache_add(name, fqdncache_create(), hp, 1); | |
936 | f = fqdncache_get(name); | |
937 | return f->names[0]; | |
938 | } | |
939 | /* bad address, negative cached */ | |
940 | if (fqdn_table) | |
941 | fqdncache_add(name, fqdncache_create(), hp, 0); | |
942 | return NULL; | |
943 | } | |
944 | if (flags & FQDN_LOOKUP_IF_MISS) | |
945 | fqdncache_nbgethostbyaddr(addr, -1, dummy_handler, NULL); | |
946 | return NULL; | |
947 | } | |
948 | ||
949 | ||
950 | /* process objects list */ | |
951 | void fqdnStats(sentry) | |
952 | StoreEntry *sentry; | |
953 | { | |
954 | fqdncache_entry *f = NULL; | |
955 | int k; | |
956 | int ttl; | |
957 | ||
958 | if (!fqdn_table) | |
959 | return; | |
960 | ||
961 | storeAppendPrintf(sentry, "{FQDN Cache Statistics:\n"); | |
962 | storeAppendPrintf(sentry, "{FQDNcache Entries: %d}\n", | |
963 | meta_data.fqdncache_count); | |
964 | storeAppendPrintf(sentry, "{FQDNcache Requests: %d}\n", | |
965 | FqdncacheStats.requests); | |
966 | storeAppendPrintf(sentry, "{FQDNcache Hits: %d}\n", | |
967 | FqdncacheStats.hits); | |
968 | storeAppendPrintf(sentry, "{FQDNcache Pending Hits: %d}\n", | |
969 | FqdncacheStats.pending_hits); | |
970 | storeAppendPrintf(sentry, "{FQDNcache Negative Hits: %d}\n", | |
971 | FqdncacheStats.negative_hits); | |
972 | storeAppendPrintf(sentry, "{FQDNcache Misses: %d}\n", | |
973 | FqdncacheStats.misses); | |
974 | storeAppendPrintf(sentry, "{Blocking calls to gethostbyaddr(): %d}\n", | |
975 | FqdncacheStats.ghba_calls); | |
976 | storeAppendPrintf(sentry, "{dnsserver avg service time: %d msec}\n", | |
977 | FqdncacheStats.avg_svc_time); | |
978 | storeAppendPrintf(sentry, "}\n\n"); | |
979 | storeAppendPrintf(sentry, "{FQDN Cache Contents:\n\n"); | |
980 | ||
981 | for (f = fqdncache_GetFirst(); f; f = fqdncache_GetNext()) { | |
982 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) | |
983 | ttl = 0; | |
984 | else | |
985 | ttl = (f->ttl - squid_curtime + f->timestamp); | |
986 | storeAppendPrintf(sentry, " {%-32.32s %c %6d %d", | |
987 | f->name, | |
988 | fqdncache_status_char[f->status], | |
989 | ttl, | |
990 | (int) f->name_count); | |
991 | for (k = 0; k < (int) f->name_count; k++) | |
992 | storeAppendPrintf(sentry, " %s", f->names[k]); | |
993 | storeAppendPrintf(sentry, close_bracket); | |
994 | } | |
995 | storeAppendPrintf(sentry, close_bracket); | |
996 | } | |
997 | ||
998 | static void dummy_handler(u1, u2, u3) | |
999 | int u1; | |
1000 | char *u2; | |
1001 | void *u3; | |
1002 | { | |
1003 | return; | |
1004 | } | |
1005 | ||
1006 | static int fqdncacheHasPending(f) | |
1007 | fqdncache_entry *f; | |
1008 | { | |
1009 | struct _fqdn_pending *p = NULL; | |
1010 | if (f->status != FQDN_PENDING) | |
1011 | return 0; | |
1012 | for (p = f->pending_head; p; p = p->next) | |
1013 | if (p->handler) | |
1014 | return 1; | |
1015 | return 0; | |
1016 | } | |
1017 | ||
1018 | void fqdncacheReleaseInvalid(name) | |
1019 | char *name; | |
1020 | { | |
1021 | fqdncache_entry *f; | |
1022 | if ((f = fqdncache_get(name)) == NULL) | |
1023 | return; | |
1024 | if (f->status != FQDN_NEGATIVE_CACHED) | |
1025 | return; | |
1026 | fqdncache_release(f); | |
1027 | } | |
28ab0c0a | 1028 | |
1029 | char *fqdnFromAddr(addr) | |
1030 | struct in_addr addr; | |
1031 | { | |
1032 | char *n; | |
39de381a | 1033 | static char buf[32]; |
b0c9fba9 | 1034 | if (Config.Log.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0))) |
28ab0c0a | 1035 | return n; |
ed83f7e9 | 1036 | strncpy(buf, inet_ntoa(addr), 31); |
39de381a | 1037 | return buf; |
28ab0c0a | 1038 | } |
f201f309 | 1039 | |
1040 | int fqdncacheQueueDrain() | |
1041 | { | |
1042 | fqdncache_entry *i; | |
1043 | dnsserver_t *dnsData; | |
1044 | if (!fqdncacheQueueHead) | |
a8f7d3ee | 1045 | return 0; |
f201f309 | 1046 | while ((dnsData = dnsGetFirstAvailable()) && (i = fqdncacheDequeue())) |
a8f7d3ee | 1047 | fqdncache_dnsDispatch(dnsData, i); |
f201f309 | 1048 | return 1; |
1049 | } |