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