]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fqdncache.cc
memory problem
[thirdparty/squid.git] / src / fqdncache.cc
CommitLineData
f88bb09c 1
2/*
24382924 3 * $Id: fqdncache.cc,v 1.22 1996/10/09 15:34:25 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;
370
371}
372
b8d8561b 373static void
374fqdncache_add_to_hash(fqdncache_entry * f)
f88bb09c 375{
376 if (hash_join(fqdn_table, (hash_link *) f)) {
7cf620a9 377 debug(35, 1, "fqdncache_add_to_hash: Cannot add %s (%p) to hash table %d.\n",
f88bb09c 378 f->name, f, fqdn_table);
379 }
7cf620a9 380 debug(35, 5, "fqdncache_add_to_hash: name <%s>\n", f->name);
f88bb09c 381}
382
383
b8d8561b 384static void
385fqdncache_add(char *name, fqdncache_entry * f, struct hostent *hp, int cached)
f88bb09c 386{
387 int k;
388
389 if (fqdncache_get(name))
390 fatal_dump("fqdncache_add: somebody adding a duplicate!");
7cf620a9 391 debug(35, 10, "fqdncache_add: Adding name '%s' (%s).\n", name,
f88bb09c 392 cached ? "cached" : "not cached");
393 f->name = xstrdup(name);
394 if (cached) {
395 f->name_count = 0;
396 f->names[f->name_count++] = xstrdup(hp->h_name);
397 for (k = 0; hp->h_aliases[k]; k++) {
398 f->names[f->name_count++] = xstrdup(hp->h_aliases[k]);
399 if (f->name_count == FQDN_MAX_NAMES)
400 break;
401 }
e84703ad 402 f->lastref = squid_curtime;
f88bb09c 403 f->status = FQDN_CACHED;
e84703ad 404 f->expires = squid_curtime + Config.positiveDnsTtl;
f88bb09c 405 } else {
e84703ad 406 f->lastref = squid_curtime;
f88bb09c 407 f->status = FQDN_NEGATIVE_CACHED;
e84703ad 408 f->expires = squid_curtime + Config.negativeDnsTtl;
f88bb09c 409 }
410 fqdncache_add_to_hash(f);
411}
412
413/* walks down the pending list, calling handlers */
b8d8561b 414static void
415fqdncache_call_pending(fqdncache_entry * f)
f88bb09c 416{
417 struct _fqdn_pending *p = NULL;
418 int nhandler = 0;
419
420 f->lastref = squid_curtime;
421
422 while (f->pending_head != NULL) {
423 p = f->pending_head;
424 f->pending_head = p->next;
425 if (p->handler) {
426 nhandler++;
427 dns_error_message = f->error_message;
428 p->handler(p->fd,
429 (f->status == FQDN_CACHED) ? f->names[0] : NULL,
430 p->handlerData);
431 }
432 memset(p, '\0', sizeof(struct _fqdn_pending));
433 safe_free(p);
434 }
435 f->pending_head = NULL; /* nuke list */
7cf620a9 436 debug(35, 10, "fqdncache_call_pending: Called %d handlers.\n", nhandler);
f88bb09c 437}
438
b8d8561b 439static void
440fqdncache_call_pending_badname(int fd, FQDNH handler, void *data)
f88bb09c 441{
7cf620a9 442 debug(35, 0, "fqdncache_call_pending_badname: Bad Name: Calling handler with NULL result.\n");
f88bb09c 443 handler(fd, NULL, data);
444}
445
b8d8561b 446static fqdncache_entry *
447fqdncache_parsebuffer(char *inbuf, dnsserver_t * dnsData)
f88bb09c 448{
e84703ad 449 char *buf = xstrdup(inbuf);
450 char *token;
451 static fqdncache_entry f;
452 int k;
f88bb09c 453 int ipcount;
454 int aliascount;
e84703ad 455 debug(35, 5, "fqdncache_parsebuffer: parsing:\n%s", inbuf);
456 memset(&f, '\0', sizeof(fqdncache_entry));
457 f.expires = squid_curtime + Config.positiveDnsTtl;
620da955 458 f.status = FQDN_DISPATCHED;
e84703ad 459 for (token = strtok(buf, w_space); token; token = strtok(NULL, w_space)) {
460 if (!strcmp(token, "$end")) {
f88bb09c 461 break;
e84703ad 462 } else if (!strcmp(token, "$alive")) {
f88bb09c 463 dnsData->answer = squid_curtime;
e84703ad 464 } else if (!strcmp(token, "$fail")) {
465 if ((token = strtok(NULL, w_space)) == NULL)
466 fatal_dump("Invalid $fail");
467 f.expires = squid_curtime + Config.negativeDnsTtl;
620da955 468 f.status = FQDN_NEGATIVE_CACHED;
e84703ad 469 } else if (!strcmp(token, "$message")) {
470 if ((token = strtok(NULL, "\n")) == NULL)
471 fatal_dump("Invalid $message");
472 f.error_message = xstrdup(token);
473 } else if (!strcmp(token, "$name")) {
474 if ((token = strtok(NULL, w_space)) == NULL)
475 fatal_dump("Invalid $name");
620da955 476 f.status = FQDN_CACHED;
e84703ad 477 } else if (!strcmp(token, "$h_name")) {
478 if ((token = strtok(NULL, w_space)) == NULL)
479 fatal_dump("Invalid $h_name");
480 f.names[0] = xstrdup(token);
481 f.name_count = 1;
482 } else if (!strcmp(token, "$h_len")) {
483 if ((token = strtok(NULL, w_space)) == NULL)
484 fatal_dump("Invalid $h_len");
485 } else if (!strcmp(token, "$ipcount")) {
486 if ((token = strtok(NULL, w_space)) == NULL)
487 fatal_dump("Invalid $ipcount");
488 ipcount = atoi(token);
489 for (k = 0; k < ipcount; k++) {
490 if ((token = strtok(NULL, w_space)) == NULL)
620da955 491 fatal_dump("Invalid FQDN address");
f88bb09c 492 }
e84703ad 493 } else if (!strcmp(token, "$aliascount")) {
494 if ((token = strtok(NULL, w_space)) == NULL)
495 fatal_dump("Invalid $aliascount");
496 aliascount = atoi(token);
497 for (k = 0; k < aliascount; k++) {
498 if ((token = strtok(NULL, w_space)) == NULL)
499 fatal_dump("Invalid alias");
f88bb09c 500 }
e84703ad 501 } else if (!strcmp(token, "$ttl")) {
502 if ((token = strtok(NULL, w_space)) == NULL)
503 fatal_dump("Invalid $ttl");
504 f.expires = squid_curtime + atoi(token);
f88bb09c 505 } else {
e84703ad 506 fatal_dump("Invalid dnsserver output");
f88bb09c 507 }
508 }
e84703ad 509 xfree(buf);
510 return &f;
f88bb09c 511}
512
b8d8561b 513static int
514fqdncache_dnsHandleRead(int fd, dnsserver_t * dnsData)
f88bb09c 515{
f88bb09c 516 int len;
517 int svc_time;
518 int n;
519 fqdncache_entry *f = NULL;
e84703ad 520 fqdncache_entry *x = NULL;
f88bb09c 521
522 len = read(fd,
523 dnsData->ip_inbuf + dnsData->offset,
524 dnsData->size - dnsData->offset);
7cf620a9 525 debug(35, 5, "fqdncache_dnsHandleRead: Result from DNS ID %d (%d bytes)\n",
f88bb09c 526 dnsData->id, len);
527 if (len <= 0) {
7cf620a9 528 debug(35, dnsData->flags & DNS_FLAG_CLOSING ? 5 : 1,
f88bb09c 529 "FD %d: Connection from DNSSERVER #%d is closed, disabling\n",
530 fd, dnsData->id);
531 dnsData->flags = 0;
532 comm_set_select_handler(fd,
533 COMM_SELECT_WRITE,
534 NULL,
535 NULL);
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);
673 comm_set_select_handler(dns->outpipe,
674 COMM_SELECT_READ,
675 (PF) fqdncache_dnsHandleRead,
676 dns);
7cf620a9 677 debug(35, 5, "fqdncache_dnsDispatch: Request sent to DNS server #%d.\n",
f88bb09c 678 dns->id);
679 dns->dispatch_time = current_time;
680 DnsStats.requests++;
681 DnsStats.hist[dns->id - 1]++;
682}
683
684
685/* initialize the fqdncache */
b8d8561b 686void
0673c0ba 687fqdncache_init(void)
f88bb09c 688{
7cf620a9 689 debug(35, 3, "Initializing FQDN Cache...\n");
f88bb09c 690 memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats));
aabcd584 691 /* small hash table */
692 fqdn_table = hash_create(urlcmp, 229, hash_string);
f88bb09c 693 fqdncache_high = (long) (((float) MAX_FQDN *
694 (float) FQDN_HIGH_WATER) / (float) 100);
695 fqdncache_low = (long) (((float) MAX_FQDN *
696 (float) FQDN_LOW_WATER) / (float) 100);
697}
698
699/* clean up the pending entries in dnsserver */
700/* return 1 if we found the host, 0 otherwise */
b8d8561b 701int
702fqdncacheUnregister(struct in_addr addr, int fd)
f88bb09c 703{
03047798 704 char *name = inet_ntoa(addr);
f88bb09c 705 fqdncache_entry *f = NULL;
706 struct _fqdn_pending *p = NULL;
707 int n = 0;
708
7cf620a9 709 debug(35, 3, "fqdncache_unregister: FD %d, name '%s'\n", fd, name);
f88bb09c 710 if ((f = fqdncache_get(name)) == NULL)
711 return 0;
712 if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) {
713 for (p = f->pending_head; p; p = p->next) {
714 if (p->fd == fd && p->handler != NULL) {
715 p->handler = NULL;
716 p->fd = -1;
717 n++;
718 }
719 }
720 }
7cf620a9 721 debug(35, 3, "fqdncache_unregister: unregistered %d handlers\n", n);
f88bb09c 722 return n;
723}
724
b8d8561b 725char *
726fqdncache_gethostbyaddr(struct in_addr addr, int flags)
f88bb09c 727{
728 char *name = inet_ntoa(addr);
729 fqdncache_entry *f = NULL;
730 struct hostent *hp = NULL;
731 unsigned int ip;
732
733 if (!name)
734 fatal_dump("fqdncache_gethostbyaddr: NULL name");
735 FqdncacheStats.requests++;
736 if ((f = fqdncache_get(name))) {
737 if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) {
738 FqdncacheStats.pending_hits++;
739 return NULL;
740 } else if (f->status == FQDN_NEGATIVE_CACHED) {
741 FqdncacheStats.negative_hits++;
742 dns_error_message = f->error_message;
743 return NULL;
744 } else {
745 FqdncacheStats.hits++;
746 f->lastref = squid_curtime;
747 return f->names[0];
748 }
749 }
750 FqdncacheStats.misses++;
751 /* check if it's already a FQDN address in text form. */
752 if (inet_addr(name) == INADDR_NONE) {
753 return name;
754 }
755 if (flags & FQDN_BLOCKING_LOOKUP) {
756 FqdncacheStats.ghba_calls++;
757 ip = inet_addr(name);
f7a5493b 758 hp = gethostbyaddr((char *) &ip, 4, AF_INET);
f88bb09c 759 if (hp && hp->h_name && (hp->h_name[0] != '\0') && fqdn_table) {
760 /* good address, cached */
761 fqdncache_add(name, fqdncache_create(), hp, 1);
762 f = fqdncache_get(name);
763 return f->names[0];
764 }
765 /* bad address, negative cached */
766 if (fqdn_table)
767 fqdncache_add(name, fqdncache_create(), hp, 0);
768 return NULL;
769 }
770 if (flags & FQDN_LOOKUP_IF_MISS)
771 fqdncache_nbgethostbyaddr(addr, -1, dummy_handler, NULL);
772 return NULL;
773}
774
775
776/* process objects list */
b8d8561b 777void
778fqdnStats(StoreEntry * sentry)
f88bb09c 779{
780 fqdncache_entry *f = NULL;
781 int k;
782 int ttl;
783
784 if (!fqdn_table)
785 return;
786
787 storeAppendPrintf(sentry, "{FQDN Cache Statistics:\n");
788 storeAppendPrintf(sentry, "{FQDNcache Entries: %d}\n",
789 meta_data.fqdncache_count);
790 storeAppendPrintf(sentry, "{FQDNcache Requests: %d}\n",
791 FqdncacheStats.requests);
792 storeAppendPrintf(sentry, "{FQDNcache Hits: %d}\n",
793 FqdncacheStats.hits);
794 storeAppendPrintf(sentry, "{FQDNcache Pending Hits: %d}\n",
795 FqdncacheStats.pending_hits);
796 storeAppendPrintf(sentry, "{FQDNcache Negative Hits: %d}\n",
797 FqdncacheStats.negative_hits);
798 storeAppendPrintf(sentry, "{FQDNcache Misses: %d}\n",
799 FqdncacheStats.misses);
800 storeAppendPrintf(sentry, "{Blocking calls to gethostbyaddr(): %d}\n",
801 FqdncacheStats.ghba_calls);
802 storeAppendPrintf(sentry, "{dnsserver avg service time: %d msec}\n",
803 FqdncacheStats.avg_svc_time);
804 storeAppendPrintf(sentry, "}\n\n");
805 storeAppendPrintf(sentry, "{FQDN Cache Contents:\n\n");
806
807 for (f = fqdncache_GetFirst(); f; f = fqdncache_GetNext()) {
808 if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED)
809 ttl = 0;
810 else
e84703ad 811 ttl = (f->expires - squid_curtime);
f88bb09c 812 storeAppendPrintf(sentry, " {%-32.32s %c %6d %d",
813 f->name,
814 fqdncache_status_char[f->status],
815 ttl,
816 (int) f->name_count);
817 for (k = 0; k < (int) f->name_count; k++)
818 storeAppendPrintf(sentry, " %s", f->names[k]);
819 storeAppendPrintf(sentry, close_bracket);
820 }
821 storeAppendPrintf(sentry, close_bracket);
822}
823
b8d8561b 824static void
825dummy_handler(int u1, char *u2, void *u3)
f88bb09c 826{
827 return;
828}
829
b8d8561b 830static int
831fqdncacheHasPending(fqdncache_entry * f)
f88bb09c 832{
833 struct _fqdn_pending *p = NULL;
834 if (f->status != FQDN_PENDING)
835 return 0;
836 for (p = f->pending_head; p; p = p->next)
837 if (p->handler)
838 return 1;
839 return 0;
840}
841
b8d8561b 842void
843fqdncacheReleaseInvalid(char *name)
f88bb09c 844{
845 fqdncache_entry *f;
846 if ((f = fqdncache_get(name)) == NULL)
847 return;
848 if (f->status != FQDN_NEGATIVE_CACHED)
849 return;
850 fqdncache_release(f);
851}
28ab0c0a 852
b8d8561b 853char *
854fqdnFromAddr(struct in_addr addr)
28ab0c0a 855{
856 char *n;
39de381a 857 static char buf[32];
b0c9fba9 858 if (Config.Log.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0)))
28ab0c0a 859 return n;
ed83f7e9 860 strncpy(buf, inet_ntoa(addr), 31);
39de381a 861 return buf;
28ab0c0a 862}
f201f309 863
b8d8561b 864int
0673c0ba 865fqdncacheQueueDrain(void)
f201f309 866{
867 fqdncache_entry *i;
868 dnsserver_t *dnsData;
869 if (!fqdncacheQueueHead)
a8f7d3ee 870 return 0;
f201f309 871 while ((dnsData = dnsGetFirstAvailable()) && (i = fqdncacheDequeue()))
a8f7d3ee 872 fqdncache_dnsDispatch(dnsData, i);
f201f309 873 return 1;
874}