]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fqdncache.cc
update
[thirdparty/squid.git] / src / fqdncache.cc
CommitLineData
f88bb09c 1
2/*
1b50df4b 3 * $Id: fqdncache.cc,v 1.30 1996/10/25 00:22:12 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
f88bb09c 114
115struct _fqdn_pending {
116 int fd;
117 FQDNH handler;
118 void *handlerData;
119 struct _fqdn_pending *next;
120};
121
122struct fqdncacheQueueData {
123 struct fqdncacheQueueData *next;
124 fqdncache_entry *f;
125};
126
127static struct {
128 int requests;
129 int replies;
130 int hits;
131 int misses;
132 int pending_hits;
133 int negative_hits;
134 int errors;
135 int avg_svc_time;
136 int ghba_calls; /* # calls to blocking gethostbyaddr() */
137} FqdncacheStats;
138
67508012 139static int fqdncache_compareLastRef _PARAMS((fqdncache_entry **, fqdncache_entry **));
140static int fqdncache_dnsHandleRead _PARAMS((int, dnsserver_t *));
141static fqdncache_entry *fqdncache_parsebuffer _PARAMS((char *buf, dnsserver_t *));
142static int fqdncache_purgelru _PARAMS((void));
143static void fqdncache_release _PARAMS((fqdncache_entry *));
144static fqdncache_entry *fqdncache_GetFirst _PARAMS((void));
145static fqdncache_entry *fqdncache_GetNext _PARAMS((void));
146static fqdncache_entry *fqdncache_create _PARAMS((void));
147static void fqdncache_add_to_hash _PARAMS((fqdncache_entry *));
148static void fqdncache_call_pending _PARAMS((fqdncache_entry *));
149static void fqdncache_call_pending_badname _PARAMS((int fd, FQDNH handler, void *));
150static void fqdncache_add _PARAMS((char *, fqdncache_entry *, struct hostent *, int));
151static int fqdncacheHasPending _PARAMS((fqdncache_entry *));
152static fqdncache_entry *fqdncache_get _PARAMS((char *));
153static void dummy_handler _PARAMS((int, char *, void *));
154static int fqdncacheExpiredEntry _PARAMS((fqdncache_entry *));
155static void fqdncacheAddPending _PARAMS((fqdncache_entry *, int fd, FQDNH, void *));
156static void fqdncacheEnqueue _PARAMS((fqdncache_entry *));
157static void *fqdncacheDequeue _PARAMS((void));
158static void fqdncache_dnsDispatch _PARAMS((dnsserver_t *, fqdncache_entry *));
f88bb09c 159
f88bb09c 160static HashID fqdn_table = 0;
161static struct fqdncacheQueueData *fqdncacheQueueHead = NULL;
162static struct fqdncacheQueueData **fqdncacheQueueTailP = &fqdncacheQueueHead;
163
164static char fqdncache_status_char[] =
165{
166 'C',
167 'N',
168 'P',
169 'D'
170};
171
24382924 172static long fqdncache_low = 180;
173static long fqdncache_high = 200;
f88bb09c 174
b8d8561b 175static void
176fqdncacheEnqueue(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 184static void *
0673c0ba 185fqdncacheDequeue(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 }
197 return f;
198}
199
200/* removes the given fqdncache entry */
b8d8561b 201static void
202fqdncache_release(fqdncache_entry * f)
f88bb09c 203{
204 fqdncache_entry *result = NULL;
205 hash_link *table_entry = NULL;
206 int k;
207
208 if ((table_entry = hash_lookup(fqdn_table, f->name)) == NULL) {
7cf620a9 209 debug(35, 0, "fqdncache_release: Could not find key '%s'\n", f->name);
f88bb09c 210 return;
211 }
212 result = (fqdncache_entry *) table_entry;
213 if (f != result)
214 fatal_dump("fqdncache_release: expected f == result!");
215 if (f->status == FQDN_PENDING) {
7cf620a9 216 debug(35, 1, "fqdncache_release: Someone called on a PENDING entry\n");
f88bb09c 217 return;
218 }
219 if (f->status == FQDN_DISPATCHED) {
7cf620a9 220 debug(35, 1, "fqdncache_release: Someone called on a DISPATCHED entry\n");
f88bb09c 221 return;
222 }
223 if (hash_remove_link(fqdn_table, table_entry)) {
7cf620a9 224 debug(35, 0, "fqdncache_release: hash_remove_link() failed for '%s'\n",
f88bb09c 225 result->name);
226 return;
227 }
228 if (result->status == FQDN_CACHED) {
229 for (k = 0; k < (int) f->name_count; k++)
230 safe_free(f->names[k]);
7cf620a9 231 debug(35, 5, "fqdncache_release: Released FQDN record for '%s'.\n",
f88bb09c 232 result->name);
233 }
234 safe_free(result->name);
235 safe_free(result->error_message);
236 memset(result, '\0', sizeof(fqdncache_entry));
237 safe_free(result);
238 --meta_data.fqdncache_count;
239 return;
240}
241
242/* return match for given name */
b8d8561b 243static fqdncache_entry *
244fqdncache_get(char *name)
f88bb09c 245{
246 hash_link *e;
247 static fqdncache_entry *f;
248
249 f = NULL;
250 if (fqdn_table) {
251 if ((e = hash_lookup(fqdn_table, name)) != NULL)
252 f = (fqdncache_entry *) e;
253 }
254 return f;
255}
256
b8d8561b 257static fqdncache_entry *
0673c0ba 258fqdncache_GetFirst(void)
f88bb09c 259{
260 return (fqdncache_entry *) hash_first(fqdn_table);
261}
262
b8d8561b 263static fqdncache_entry *
0673c0ba 264fqdncache_GetNext(void)
f88bb09c 265{
266 return (fqdncache_entry *) hash_next(fqdn_table);
267}
268
b8d8561b 269static int
270fqdncache_compareLastRef(fqdncache_entry ** e1, fqdncache_entry ** e2)
f88bb09c 271{
272 if (!e1 || !e2)
273 fatal_dump(NULL);
274 if ((*e1)->lastref > (*e2)->lastref)
275 return (1);
276 if ((*e1)->lastref < (*e2)->lastref)
277 return (-1);
278 return (0);
279}
280
b8d8561b 281static int
282fqdncacheExpiredEntry(fqdncache_entry * f)
f88bb09c 283{
284 if (f->status == FQDN_PENDING)
285 return 0;
286 if (f->status == FQDN_DISPATCHED)
287 return 0;
e84703ad 288 if (f->expires > squid_curtime)
f88bb09c 289 return 0;
290 return 1;
291}
292
293/* finds the LRU and deletes */
b8d8561b 294static int
0673c0ba 295fqdncache_purgelru(void)
f88bb09c 296{
297 fqdncache_entry *f = NULL;
298 int local_fqdn_count = 0;
299 int local_fqdn_notpending_count = 0;
300 int removed = 0;
301 int k;
302 fqdncache_entry **LRU_list = NULL;
303 int LRU_list_count = 0;
304 int LRU_cur_size = meta_data.fqdncache_count;
305
306 LRU_list = xcalloc(LRU_cur_size, sizeof(fqdncache_entry *));
307
308 for (f = fqdncache_GetFirst(); f; f = fqdncache_GetNext()) {
309 if (fqdncacheExpiredEntry(f)) {
310 fqdncache_release(f);
311 removed++;
312 continue;
313 }
314 local_fqdn_count++;
315
316 if (LRU_list_count >= LRU_cur_size) {
317 /* have to realloc */
318 LRU_cur_size += 16;
7cf620a9 319 debug(35, 3, "fqdncache_purgelru: Have to grow LRU_list to %d. This shouldn't happen.\n",
f88bb09c 320 LRU_cur_size);
321 LRU_list = xrealloc((char *) LRU_list,
322 LRU_cur_size * sizeof(fqdncache_entry *));
323 }
324 if (f->status == FQDN_PENDING)
325 continue;
326 if (f->status == FQDN_DISPATCHED)
327 continue;
328 local_fqdn_notpending_count++;
329 LRU_list[LRU_list_count++] = f;
330 }
331
7cf620a9 332 debug(35, 3, "fqdncache_purgelru: fqdncache_count: %5d\n", meta_data.fqdncache_count);
333 debug(35, 3, " actual count : %5d\n", local_fqdn_count);
334 debug(35, 3, " high W mark : %5d\n", fqdncache_high);
335 debug(35, 3, " low W mark : %5d\n", fqdncache_low);
336 debug(35, 3, " not pending : %5d\n", local_fqdn_notpending_count);
337 debug(35, 3, " LRU candidates : %5d\n", LRU_list_count);
f88bb09c 338
339 /* sort LRU candidate list */
340 qsort((char *) LRU_list,
341 LRU_list_count,
342 sizeof(f),
343 (int (*)(const void *, const void *)) fqdncache_compareLastRef);
344 for (k = 0; LRU_list[k] && (meta_data.fqdncache_count > fqdncache_low)
345 && k < LRU_list_count;
346 ++k) {
347 fqdncache_release(LRU_list[k]);
348 removed++;
349 }
350
7cf620a9 351 debug(35, 3, " removed : %5d\n", removed);
f88bb09c 352 safe_free(LRU_list);
353 return (removed > 0) ? 0 : -1;
354}
355
356
357/* create blank fqdncache_entry */
b8d8561b 358static fqdncache_entry *
0673c0ba 359fqdncache_create(void)
f88bb09c 360{
361 static fqdncache_entry *new;
362
363 if (meta_data.fqdncache_count > fqdncache_high) {
364 if (fqdncache_purgelru() < 0)
7cf620a9 365 debug(35, 0, "HELP!! FQDN Cache is overflowing!\n");
f88bb09c 366 }
367 meta_data.fqdncache_count++;
368 new = xcalloc(1, sizeof(fqdncache_entry));
369 return new;
f88bb09c 370}
371
b8d8561b 372static void
373fqdncache_add_to_hash(fqdncache_entry * f)
f88bb09c 374{
375 if (hash_join(fqdn_table, (hash_link *) f)) {
7cf620a9 376 debug(35, 1, "fqdncache_add_to_hash: Cannot add %s (%p) to hash table %d.\n",
f88bb09c 377 f->name, f, fqdn_table);
378 }
7cf620a9 379 debug(35, 5, "fqdncache_add_to_hash: name <%s>\n", f->name);
f88bb09c 380}
381
382
b8d8561b 383static void
384fqdncache_add(char *name, fqdncache_entry * f, struct hostent *hp, int cached)
f88bb09c 385{
386 int k;
387
388 if (fqdncache_get(name))
389 fatal_dump("fqdncache_add: somebody adding a duplicate!");
7cf620a9 390 debug(35, 10, "fqdncache_add: Adding name '%s' (%s).\n", name,
f88bb09c 391 cached ? "cached" : "not cached");
392 f->name = xstrdup(name);
393 if (cached) {
394 f->name_count = 0;
ceb8994e 395 f->names[f->name_count++] = xstrdup((char *) hp->h_name);
f88bb09c 396 for (k = 0; hp->h_aliases[k]; k++) {
397 f->names[f->name_count++] = xstrdup(hp->h_aliases[k]);
398 if (f->name_count == FQDN_MAX_NAMES)
399 break;
400 }
e84703ad 401 f->lastref = squid_curtime;
f88bb09c 402 f->status = FQDN_CACHED;
e84703ad 403 f->expires = squid_curtime + Config.positiveDnsTtl;
f88bb09c 404 } else {
e84703ad 405 f->lastref = squid_curtime;
f88bb09c 406 f->status = FQDN_NEGATIVE_CACHED;
e84703ad 407 f->expires = squid_curtime + Config.negativeDnsTtl;
f88bb09c 408 }
409 fqdncache_add_to_hash(f);
410}
411
412/* walks down the pending list, calling handlers */
b8d8561b 413static void
414fqdncache_call_pending(fqdncache_entry * f)
f88bb09c 415{
416 struct _fqdn_pending *p = NULL;
417 int nhandler = 0;
418
419 f->lastref = squid_curtime;
420
421 while (f->pending_head != NULL) {
422 p = f->pending_head;
423 f->pending_head = p->next;
424 if (p->handler) {
425 nhandler++;
426 dns_error_message = f->error_message;
427 p->handler(p->fd,
428 (f->status == FQDN_CACHED) ? f->names[0] : NULL,
429 p->handlerData);
430 }
431 memset(p, '\0', sizeof(struct _fqdn_pending));
432 safe_free(p);
433 }
434 f->pending_head = NULL; /* nuke list */
7cf620a9 435 debug(35, 10, "fqdncache_call_pending: Called %d handlers.\n", nhandler);
f88bb09c 436}
437
b8d8561b 438static void
439fqdncache_call_pending_badname(int fd, FQDNH handler, void *data)
f88bb09c 440{
7cf620a9 441 debug(35, 0, "fqdncache_call_pending_badname: Bad Name: Calling handler with NULL result.\n");
f88bb09c 442 handler(fd, NULL, data);
443}
444
b8d8561b 445static fqdncache_entry *
446fqdncache_parsebuffer(char *inbuf, dnsserver_t * dnsData)
f88bb09c 447{
e84703ad 448 char *buf = xstrdup(inbuf);
449 char *token;
450 static fqdncache_entry f;
451 int k;
f88bb09c 452 int ipcount;
453 int aliascount;
e84703ad 454 debug(35, 5, "fqdncache_parsebuffer: parsing:\n%s", inbuf);
455 memset(&f, '\0', sizeof(fqdncache_entry));
456 f.expires = squid_curtime + Config.positiveDnsTtl;
620da955 457 f.status = FQDN_DISPATCHED;
e84703ad 458 for (token = strtok(buf, w_space); token; token = strtok(NULL, w_space)) {
459 if (!strcmp(token, "$end")) {
f88bb09c 460 break;
e84703ad 461 } else if (!strcmp(token, "$alive")) {
f88bb09c 462 dnsData->answer = squid_curtime;
e84703ad 463 } else if (!strcmp(token, "$fail")) {
464 if ((token = strtok(NULL, w_space)) == NULL)
465 fatal_dump("Invalid $fail");
466 f.expires = squid_curtime + Config.negativeDnsTtl;
620da955 467 f.status = FQDN_NEGATIVE_CACHED;
e84703ad 468 } else if (!strcmp(token, "$message")) {
469 if ((token = strtok(NULL, "\n")) == NULL)
470 fatal_dump("Invalid $message");
471 f.error_message = xstrdup(token);
472 } else if (!strcmp(token, "$name")) {
473 if ((token = strtok(NULL, w_space)) == NULL)
474 fatal_dump("Invalid $name");
620da955 475 f.status = FQDN_CACHED;
e84703ad 476 } else if (!strcmp(token, "$h_name")) {
477 if ((token = strtok(NULL, w_space)) == NULL)
478 fatal_dump("Invalid $h_name");
479 f.names[0] = xstrdup(token);
480 f.name_count = 1;
481 } else if (!strcmp(token, "$h_len")) {
482 if ((token = strtok(NULL, w_space)) == NULL)
483 fatal_dump("Invalid $h_len");
484 } else if (!strcmp(token, "$ipcount")) {
485 if ((token = strtok(NULL, w_space)) == NULL)
486 fatal_dump("Invalid $ipcount");
487 ipcount = atoi(token);
488 for (k = 0; k < ipcount; k++) {
489 if ((token = strtok(NULL, w_space)) == NULL)
620da955 490 fatal_dump("Invalid FQDN address");
f88bb09c 491 }
e84703ad 492 } else if (!strcmp(token, "$aliascount")) {
493 if ((token = strtok(NULL, w_space)) == NULL)
494 fatal_dump("Invalid $aliascount");
495 aliascount = atoi(token);
496 for (k = 0; k < aliascount; k++) {
497 if ((token = strtok(NULL, w_space)) == NULL)
498 fatal_dump("Invalid alias");
f88bb09c 499 }
e84703ad 500 } else if (!strcmp(token, "$ttl")) {
501 if ((token = strtok(NULL, w_space)) == NULL)
502 fatal_dump("Invalid $ttl");
503 f.expires = squid_curtime + atoi(token);
f88bb09c 504 } else {
e84703ad 505 fatal_dump("Invalid dnsserver output");
f88bb09c 506 }
507 }
e84703ad 508 xfree(buf);
509 return &f;
f88bb09c 510}
511
b8d8561b 512static int
513fqdncache_dnsHandleRead(int fd, dnsserver_t * dnsData)
f88bb09c 514{
f88bb09c 515 int len;
516 int svc_time;
517 int n;
518 fqdncache_entry *f = NULL;
e84703ad 519 fqdncache_entry *x = NULL;
f88bb09c 520
521 len = read(fd,
522 dnsData->ip_inbuf + dnsData->offset,
523 dnsData->size - dnsData->offset);
7cf620a9 524 debug(35, 5, "fqdncache_dnsHandleRead: Result from DNS ID %d (%d bytes)\n",
f88bb09c 525 dnsData->id, len);
526 if (len <= 0) {
7cf620a9 527 debug(35, dnsData->flags & DNS_FLAG_CLOSING ? 5 : 1,
f88bb09c 528 "FD %d: Connection from DNSSERVER #%d is closed, disabling\n",
529 fd, dnsData->id);
530 dnsData->flags = 0;
b177367b 531 commSetSelect(fd,
f88bb09c 532 COMM_SELECT_WRITE,
533 NULL,
b177367b 534 NULL,
85d7ea98 535 0);
f88bb09c 536 comm_close(fd);
537 return 0;
538 }
539 n = ++FqdncacheStats.replies;
b0c9fba9 540 DnsStats.replies++;
f88bb09c 541 dnsData->offset += len;
542 dnsData->ip_inbuf[dnsData->offset] = '\0';
543
544 if (strstr(dnsData->ip_inbuf, "$end\n")) {
545 /* end of record found */
546 svc_time = tvSubMsec(dnsData->dispatch_time, current_time);
547 if (n > FQDNCACHE_AV_FACTOR)
548 n = FQDNCACHE_AV_FACTOR;
549 FqdncacheStats.avg_svc_time
550 = (FqdncacheStats.avg_svc_time * (n - 1) + svc_time) / n;
e84703ad 551 if ((x = fqdncache_parsebuffer(dnsData->ip_inbuf, dnsData)) == NULL) {
552 debug(35, 0, "fqdncache_dnsHandleRead: fqdncache_parsebuffer failed?!\n");
553 } else {
554 dnsData->offset = 0;
555 dnsData->ip_inbuf[0] = '\0';
556 f = dnsData->data;
557 f->name_count = x->name_count;
7690e8eb 558 for (n = 0; n < (int) f->name_count; n++)
e84703ad 559 f->names[n] = x->names[n];
560 f->error_message = x->error_message;
561 f->status = x->status;
562 f->expires = x->expires;
563 fqdncache_call_pending(f);
f88bb09c 564 }
565 }
566 if (dnsData->offset == 0) {
567 dnsData->data = NULL;
568 dnsData->flags &= ~DNS_FLAG_BUSY;
569 }
570 while ((dnsData = dnsGetFirstAvailable()) && (f = fqdncacheDequeue()))
571 fqdncache_dnsDispatch(dnsData, f);
572 return 0;
573}
574
b8d8561b 575static void
576fqdncacheAddPending(fqdncache_entry * f, int fd, FQDNH handler, void *handlerData)
f88bb09c 577{
578 struct _fqdn_pending *pending = xcalloc(1, sizeof(struct _fqdn_pending));
579 struct _fqdn_pending **I = NULL;
580
581 pending->fd = fd;
582 pending->handler = handler;
583 pending->handlerData = handlerData;
584
585 for (I = &(f->pending_head); *I; I = &((*I)->next));
586 *I = pending;
587}
588
b8d8561b 589int
590fqdncache_nbgethostbyaddr(struct in_addr addr, int fd, FQDNH handler, void *handlerData)
f88bb09c 591{
592 fqdncache_entry *f = NULL;
593 dnsserver_t *dnsData = NULL;
594 char *name = inet_ntoa(addr);
595
596 if (!handler)
597 fatal_dump("fqdncache_nbgethostbyaddr: NULL handler");
598
7cf620a9 599 debug(35, 4, "fqdncache_nbgethostbyaddr: FD %d: Name '%s'.\n", fd, name);
f88bb09c 600 FqdncacheStats.requests++;
601
602 if (name == NULL || name[0] == '\0') {
7cf620a9 603 debug(35, 4, "fqdncache_nbgethostbyaddr: Invalid name!\n");
f88bb09c 604 fqdncache_call_pending_badname(fd, handler, handlerData);
605 return 0;
606 }
607 if ((f = fqdncache_get(name))) {
608 if (fqdncacheExpiredEntry(f)) {
609 fqdncache_release(f);
610 f = NULL;
611 }
612 }
613 if (f == NULL) {
614 /* MISS: No entry, create the new one */
7cf620a9 615 debug(35, 5, "fqdncache_nbgethostbyaddr: MISS for '%s'\n", name);
f88bb09c 616 FqdncacheStats.misses++;
617 f = fqdncache_create();
618 f->name = xstrdup(name);
619 f->status = FQDN_PENDING;
620 fqdncacheAddPending(f, fd, handler, handlerData);
621 fqdncache_add_to_hash(f);
622 } else if (f->status == FQDN_CACHED || f->status == FQDN_NEGATIVE_CACHED) {
623 /* HIT */
7cf620a9 624 debug(35, 4, "fqdncache_nbgethostbyaddr: HIT for '%s'\n", name);
f88bb09c 625 if (f->status == FQDN_NEGATIVE_CACHED)
626 FqdncacheStats.negative_hits++;
627 else
628 FqdncacheStats.hits++;
629 fqdncacheAddPending(f, fd, handler, handlerData);
630 fqdncache_call_pending(f);
631 return 0;
632 } else if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) {
7cf620a9 633 debug(35, 4, "fqdncache_nbgethostbyaddr: PENDING for '%s'\n", name);
f88bb09c 634 FqdncacheStats.pending_hits++;
635 fqdncacheAddPending(f, fd, handler, handlerData);
636 return 0;
637 } else {
638 fatal_dump("fqdncache_nbgethostbyaddr: BAD fqdncache_entry status");
639 }
640
641 /* for HIT, PENDING, DISPATCHED we've returned. For MISS we continue */
642
643 if ((dnsData = dnsGetFirstAvailable()))
644 fqdncache_dnsDispatch(dnsData, f);
645 else
646 fqdncacheEnqueue(f);
647 return 0;
648}
649
b8d8561b 650static void
651fqdncache_dnsDispatch(dnsserver_t * dns, fqdncache_entry * f)
f88bb09c 652{
653 char *buf = NULL;
654 if (!fqdncacheHasPending(f)) {
7cf620a9 655 debug(35, 0, "fqdncache_dnsDispatch: skipping '%s' because no handler.\n",
f88bb09c 656 f->name);
657 f->status = FQDN_NEGATIVE_CACHED;
658 fqdncache_release(f);
659 return;
660 }
661 f->status = FQDN_DISPATCHED;
662 buf = xcalloc(1, 256);
663 sprintf(buf, "%1.254s\n", f->name);
664 dns->flags |= DNS_FLAG_BUSY;
665 dns->data = f;
666 comm_write(dns->outpipe,
667 buf,
668 strlen(buf),
669 0, /* timeout */
670 NULL, /* Handler */
671 NULL, /* Handler-data */
672 xfree);
b177367b 673 commSetSelect(dns->outpipe,
f88bb09c 674 COMM_SELECT_READ,
675 (PF) fqdncache_dnsHandleRead,
b177367b 676 dns,
677 0);
7cf620a9 678 debug(35, 5, "fqdncache_dnsDispatch: Request sent to DNS server #%d.\n",
f88bb09c 679 dns->id);
680 dns->dispatch_time = current_time;
681 DnsStats.requests++;
682 DnsStats.hist[dns->id - 1]++;
683}
684
685
686/* initialize the fqdncache */
b8d8561b 687void
0673c0ba 688fqdncache_init(void)
f88bb09c 689{
7cf620a9 690 debug(35, 3, "Initializing FQDN Cache...\n");
f88bb09c 691 memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats));
aabcd584 692 /* small hash table */
1b50df4b 693 fqdn_table = hash_create(urlcmp, 229, hash4);
f88bb09c 694 fqdncache_high = (long) (((float) MAX_FQDN *
695 (float) FQDN_HIGH_WATER) / (float) 100);
696 fqdncache_low = (long) (((float) MAX_FQDN *
697 (float) FQDN_LOW_WATER) / (float) 100);
698}
699
700/* clean up the pending entries in dnsserver */
701/* return 1 if we found the host, 0 otherwise */
b8d8561b 702int
703fqdncacheUnregister(struct in_addr addr, int fd)
f88bb09c 704{
03047798 705 char *name = inet_ntoa(addr);
f88bb09c 706 fqdncache_entry *f = NULL;
707 struct _fqdn_pending *p = NULL;
708 int n = 0;
709
7cf620a9 710 debug(35, 3, "fqdncache_unregister: FD %d, name '%s'\n", fd, name);
f88bb09c 711 if ((f = fqdncache_get(name)) == NULL)
712 return 0;
713 if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) {
714 for (p = f->pending_head; p; p = p->next) {
715 if (p->fd == fd && p->handler != NULL) {
716 p->handler = NULL;
717 p->fd = -1;
718 n++;
719 }
720 }
721 }
7cf620a9 722 debug(35, 3, "fqdncache_unregister: unregistered %d handlers\n", n);
f88bb09c 723 return n;
724}
725
b8d8561b 726char *
727fqdncache_gethostbyaddr(struct in_addr addr, int flags)
f88bb09c 728{
729 char *name = inet_ntoa(addr);
730 fqdncache_entry *f = NULL;
731 struct hostent *hp = NULL;
732 unsigned int ip;
733
734 if (!name)
735 fatal_dump("fqdncache_gethostbyaddr: NULL name");
736 FqdncacheStats.requests++;
737 if ((f = fqdncache_get(name))) {
738 if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) {
739 FqdncacheStats.pending_hits++;
740 return NULL;
741 } else if (f->status == FQDN_NEGATIVE_CACHED) {
742 FqdncacheStats.negative_hits++;
743 dns_error_message = f->error_message;
744 return NULL;
745 } else {
746 FqdncacheStats.hits++;
747 f->lastref = squid_curtime;
748 return f->names[0];
749 }
750 }
751 FqdncacheStats.misses++;
752 /* check if it's already a FQDN address in text form. */
753 if (inet_addr(name) == INADDR_NONE) {
754 return name;
755 }
756 if (flags & FQDN_BLOCKING_LOOKUP) {
757 FqdncacheStats.ghba_calls++;
758 ip = inet_addr(name);
f7a5493b 759 hp = gethostbyaddr((char *) &ip, 4, AF_INET);
f88bb09c 760 if (hp && hp->h_name && (hp->h_name[0] != '\0') && fqdn_table) {
761 /* good address, cached */
762 fqdncache_add(name, fqdncache_create(), hp, 1);
763 f = fqdncache_get(name);
764 return f->names[0];
765 }
766 /* bad address, negative cached */
767 if (fqdn_table)
768 fqdncache_add(name, fqdncache_create(), hp, 0);
769 return NULL;
770 }
771 if (flags & FQDN_LOOKUP_IF_MISS)
772 fqdncache_nbgethostbyaddr(addr, -1, dummy_handler, NULL);
773 return NULL;
774}
775
776
777/* process objects list */
b8d8561b 778void
779fqdnStats(StoreEntry * sentry)
f88bb09c 780{
781 fqdncache_entry *f = NULL;
782 int k;
783 int ttl;
784
785 if (!fqdn_table)
786 return;
787
788 storeAppendPrintf(sentry, "{FQDN Cache Statistics:\n");
789 storeAppendPrintf(sentry, "{FQDNcache Entries: %d}\n",
790 meta_data.fqdncache_count);
791 storeAppendPrintf(sentry, "{FQDNcache Requests: %d}\n",
792 FqdncacheStats.requests);
793 storeAppendPrintf(sentry, "{FQDNcache Hits: %d}\n",
794 FqdncacheStats.hits);
795 storeAppendPrintf(sentry, "{FQDNcache Pending Hits: %d}\n",
796 FqdncacheStats.pending_hits);
797 storeAppendPrintf(sentry, "{FQDNcache Negative Hits: %d}\n",
798 FqdncacheStats.negative_hits);
799 storeAppendPrintf(sentry, "{FQDNcache Misses: %d}\n",
800 FqdncacheStats.misses);
801 storeAppendPrintf(sentry, "{Blocking calls to gethostbyaddr(): %d}\n",
802 FqdncacheStats.ghba_calls);
803 storeAppendPrintf(sentry, "{dnsserver avg service time: %d msec}\n",
804 FqdncacheStats.avg_svc_time);
805 storeAppendPrintf(sentry, "}\n\n");
806 storeAppendPrintf(sentry, "{FQDN Cache Contents:\n\n");
807
808 for (f = fqdncache_GetFirst(); f; f = fqdncache_GetNext()) {
809 if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED)
810 ttl = 0;
811 else
e84703ad 812 ttl = (f->expires - squid_curtime);
f88bb09c 813 storeAppendPrintf(sentry, " {%-32.32s %c %6d %d",
814 f->name,
815 fqdncache_status_char[f->status],
816 ttl,
817 (int) f->name_count);
818 for (k = 0; k < (int) f->name_count; k++)
819 storeAppendPrintf(sentry, " %s", f->names[k]);
820 storeAppendPrintf(sentry, close_bracket);
821 }
822 storeAppendPrintf(sentry, close_bracket);
823}
824
b8d8561b 825static void
826dummy_handler(int u1, char *u2, void *u3)
f88bb09c 827{
828 return;
829}
830
b8d8561b 831static int
832fqdncacheHasPending(fqdncache_entry * f)
f88bb09c 833{
834 struct _fqdn_pending *p = NULL;
835 if (f->status != FQDN_PENDING)
836 return 0;
837 for (p = f->pending_head; p; p = p->next)
838 if (p->handler)
839 return 1;
840 return 0;
841}
842
b8d8561b 843void
844fqdncacheReleaseInvalid(char *name)
f88bb09c 845{
846 fqdncache_entry *f;
847 if ((f = fqdncache_get(name)) == NULL)
848 return;
849 if (f->status != FQDN_NEGATIVE_CACHED)
850 return;
851 fqdncache_release(f);
852}
28ab0c0a 853
b8d8561b 854char *
855fqdnFromAddr(struct in_addr addr)
28ab0c0a 856{
857 char *n;
39de381a 858 static char buf[32];
b0c9fba9 859 if (Config.Log.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0)))
28ab0c0a 860 return n;
ed83f7e9 861 strncpy(buf, inet_ntoa(addr), 31);
39de381a 862 return buf;
28ab0c0a 863}
f201f309 864
b8d8561b 865int
0673c0ba 866fqdncacheQueueDrain(void)
f201f309 867{
868 fqdncache_entry *i;
869 dnsserver_t *dnsData;
870 if (!fqdncacheQueueHead)
a8f7d3ee 871 return 0;
f201f309 872 while ((dnsData = dnsGetFirstAvailable()) && (i = fqdncacheDequeue()))
a8f7d3ee 873 fqdncache_dnsDispatch(dnsData, i);
f201f309 874 return 1;
875}
56e15c50 876
877void
878fqdncacheFreeMemory(void)
879{
880 fqdncache_entry *f;
881 fqdncache_entry **list;
c7433536 882 int i = 0;
883 int j = 0;
56e15c50 884 int k = 0;
56e15c50 885 list = xcalloc(meta_data.fqdncache_count, sizeof(fqdncache_entry *));
886 f = (fqdncache_entry *) hash_first(fqdn_table);
c7433536 887 while (f && i < meta_data.fqdncache_count) {
f6610c4e 888 *(list + i) = f;
889 i++;
890 f = (fqdncache_entry *) hash_next(fqdn_table);
56e15c50 891 }
c7433536 892 for (j = 0; j < i; j++) {
f6610c4e 893 f = *(list + j);
894 for (k = 0; k < (int) f->name_count; k++)
895 safe_free(f->names[k]);
896 safe_free(f->name);
897 safe_free(f->error_message);
898 safe_free(f);
56e15c50 899 }
900 xfree(list);
901 hashFreeMemory(fqdn_table);
902}