]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fqdncache.cc
1.1.alpha15
[thirdparty/squid.git] / src / fqdncache.cc
CommitLineData
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
116struct _fqdn_pending {
117 int fd;
118 FQDNH handler;
119 void *handlerData;
120 struct _fqdn_pending *next;
121};
122
123struct fqdncacheQueueData {
124 struct fqdncacheQueueData *next;
125 fqdncache_entry *f;
126};
127
128static 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
140typedef struct _line_entry {
141 char *line;
142 struct _line_entry *next;
143} line_entry;
144
145static int fqdncache_compareLastRef _PARAMS((fqdncache_entry **, fqdncache_entry **));
146static int fqdncache_dnsHandleRead _PARAMS((int, dnsserver_t *));
147static int fqdncache_parsebuffer _PARAMS((char *buf, unsigned int offset, dnsserver_t *));
148static int fqdncache_purgelru _PARAMS((void));
149static void fqdncache_release _PARAMS((fqdncache_entry *));
150static fqdncache_entry *fqdncache_GetFirst _PARAMS((void));
151static fqdncache_entry *fqdncache_GetNext _PARAMS((void));
152static fqdncache_entry *fqdncache_create _PARAMS((void));
153static void free_lines _PARAMS((line_entry *));
154static void fqdncache_add_to_hash _PARAMS((fqdncache_entry *));
155static void fqdncache_call_pending _PARAMS((fqdncache_entry *));
156static void fqdncache_call_pending_badname _PARAMS((int fd, FQDNH handler, void *));
157static void fqdncache_add _PARAMS((char *, fqdncache_entry *, struct hostent *, int));
158static int fqdncacheHasPending _PARAMS((fqdncache_entry *));
159static fqdncache_entry *fqdncache_get _PARAMS((char *));
160static void dummy_handler _PARAMS((int, char *, void *));
161static int fqdncacheExpiredEntry _PARAMS((fqdncache_entry *));
162static void fqdncacheAddPending _PARAMS((fqdncache_entry *, int fd, FQDNH, void *));
163static void fqdncacheEnqueue _PARAMS((fqdncache_entry *));
164static void *fqdncacheDequeue _PARAMS((void));
165static void fqdncache_dnsDispatch _PARAMS((dnsserver_t *, fqdncache_entry *));
166
167static struct hostent *static_result = NULL;
168static HashID fqdn_table = 0;
169static struct fqdncacheQueueData *fqdncacheQueueHead = NULL;
170static struct fqdncacheQueueData **fqdncacheQueueTailP = &fqdncacheQueueHead;
171
172static char fqdncache_status_char[] =
173{
174 'C',
175 'N',
176 'P',
177 'D'
178};
179
180long fqdncache_low = 180;
181long fqdncache_high = 200;
182
183static 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
192static 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 */
208static 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 */
250static 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
264static fqdncache_entry *fqdncache_GetFirst()
265{
266 return (fqdncache_entry *) hash_first(fqdn_table);
267}
268
269static fqdncache_entry *fqdncache_GetNext()
270{
271 return (fqdncache_entry *) hash_next(fqdn_table);
272}
273
274static 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
286static 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 */
299static 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 */
362static 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
376static 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
387static 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 */
420static 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
445static 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 */
455static 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 */
470static 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
673static 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
733static 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
750int 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
814static 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 */
851void 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 873int 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
898char *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 */
951void 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
998static void dummy_handler(u1, u2, u3)
999 int u1;
1000 char *u2;
1001 void *u3;
1002{
1003 return;
1004}
1005
1006static 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
1018void 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
1029char *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
1040int 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}