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