3 * $Id: fqdncache.cc,v 1.65 1997/11/05 05:29:23 wessels Exp $
5 * DEBUG: section 35 FQDN Cache
6 * AUTHOR: Harvest Derived
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * --------------------------------------------------------
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.
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.
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.
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.
33 * Copyright (c) 1994, 1995. All rights reserved.
35 * The Harvest software was developed by the Internet Research Task
36 * Force Research Group on Resource Discovery (IRTF-RD):
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.
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.
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.
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.
72 * Users may make derivative works from the Harvest software, subject
73 * to the following constraints:
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.
81 * - You must notify IRTF-RD regarding your distribution of
82 * the derivative work.
84 * - You must clearly notify users that your are distributing
85 * a modified version and not the original Harvest software.
87 * - Any derivative product is also subject to these copyright
88 * and use restrictions.
90 * Note that the Harvest software is NOT in the public domain. We
91 * retain copyright, as specified above.
93 * HISTORY OF FREE SOFTWARE STATUS
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.
109 #define MAX_LINELEN (4096)
111 #define MAX_FQDN 1024 /* Maximum cached FQDN */
112 #define FQDN_LOW_WATER 90
113 #define FQDN_HIGH_WATER 95
115 struct _fqdn_pending
{
118 struct _fqdn_pending
*next
;
121 struct fqdncacheQueueData
{
122 struct fqdncacheQueueData
*next
;
135 int ghba_calls
; /* # calls to blocking gethostbyaddr() */
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
);
161 static hash_table
*fqdn_table
= NULL
;
162 static struct fqdncacheQueueData
*fqdncacheQueueHead
= NULL
;
163 static struct fqdncacheQueueData
**fqdncacheQueueTailP
= &fqdncacheQueueHead
;
165 static char fqdncache_status_char
[] =
173 static long fqdncache_low
= 180;
174 static long fqdncache_high
= 200;
177 fqdncacheEnqueue(fqdncache_entry
* f
)
179 struct fqdncacheQueueData
*new = xcalloc(1, sizeof(struct fqdncacheQueueData
));
181 *fqdncacheQueueTailP
= new;
182 fqdncacheQueueTailP
= &new->next
;
186 fqdncacheDequeue(void)
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
;
198 if (f
&& f
->status
!= FQDN_PENDING
)
199 debug_trap("fqdncacheDequeue: status != FQDN_PENDING");
203 /* removes the given fqdncache entry */
205 fqdncache_release(fqdncache_entry
* f
)
207 hash_link
*table_entry
= NULL
;
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
);
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");
220 if (f
->status
== FQDN_DISPATCHED
) {
221 debug(35, 1) ("fqdncache_release: Someone called on a DISPATCHED entry\n");
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",
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",
238 safe_free(f
->error_message
);
240 --meta_data
.fqdncache_count
;
244 /* return match for given name */
245 static fqdncache_entry
*
246 fqdncache_get(const char *name
)
249 static fqdncache_entry
*f
;
253 if ((e
= hash_lookup(fqdn_table
, name
)) != NULL
)
254 f
= (fqdncache_entry
*) e
;
259 static fqdncache_entry
*
260 fqdncache_GetFirst(void)
262 return (fqdncache_entry
*) hash_first(fqdn_table
);
265 static fqdncache_entry
*
266 fqdncache_GetNext(void)
268 return (fqdncache_entry
*) hash_next(fqdn_table
);
272 fqdncache_compareLastRef(const void *A
, const void *B
)
274 fqdncache_entry
*const *e1
= A
;
275 fqdncache_entry
*const *e2
= B
;
276 assert(e1
!= NULL
&& e2
!= NULL
);
277 if ((*e1
)->lastref
> (*e2
)->lastref
)
279 if ((*e1
)->lastref
< (*e2
)->lastref
)
285 fqdncacheExpiredEntry(const fqdncache_entry
* f
)
287 if (f
->status
== FQDN_PENDING
)
289 if (f
->status
== FQDN_DISPATCHED
)
293 if (f
->expires
> squid_curtime
)
298 /* finds the LRU and deletes */
300 fqdncache_purgelru(void)
302 fqdncache_entry
*f
= NULL
;
303 int local_fqdn_count
= 0;
304 int local_fqdn_notpending_count
= 0;
307 fqdncache_entry
**LRU_list
= NULL
;
308 int LRU_list_count
= 0;
309 int LRU_cur_size
= meta_data
.fqdncache_count
;
311 LRU_list
= xcalloc(LRU_cur_size
, sizeof(fqdncache_entry
*));
313 for (f
= fqdncache_GetFirst(); f
; f
= fqdncache_GetNext()) {
314 if (fqdncacheExpiredEntry(f
)) {
315 fqdncache_release(f
);
321 if (LRU_list_count
>= LRU_cur_size
) {
322 /* have to realloc */
324 debug(35, 3) ("fqdncache_purgelru: Have to grow LRU_list to %d. This shouldn't happen.\n",
326 LRU_list
= xrealloc((char *) LRU_list
,
327 LRU_cur_size
* sizeof(fqdncache_entry
*));
329 if (f
->status
== FQDN_PENDING
)
331 if (f
->status
== FQDN_DISPATCHED
)
333 local_fqdn_notpending_count
++;
334 LRU_list
[LRU_list_count
++] = f
;
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
);
344 /* sort LRU candidate list */
345 qsort((char *) LRU_list
,
347 sizeof(fqdncache_entry
*),
348 fqdncache_compareLastRef
);
349 for (k
= 0; k
< LRU_list_count
; k
++) {
350 if (meta_data
.fqdncache_count
< fqdncache_low
)
352 if (LRU_list
[k
] == NULL
)
354 fqdncache_release(LRU_list
[k
]);
358 debug(35, 3) (" removed : %5d\n", removed
);
360 return (removed
> 0) ? 0 : -1;
364 /* create blank fqdncache_entry */
365 static fqdncache_entry
*
366 fqdncache_create(const char *name
)
368 static fqdncache_entry
*new;
370 if (meta_data
.fqdncache_count
> fqdncache_high
) {
371 if (fqdncache_purgelru() < 0)
372 debug(35, 0) ("HELP!! FQDN Cache is overflowing!\n");
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);
383 fqdncache_add_to_hash(fqdncache_entry
* f
)
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
);
389 debug(35, 5) ("fqdncache_add_to_hash: name <%s>\n", f
->name
);
393 fqdncacheAddHostent(fqdncache_entry
* f
, const struct hostent
*hp
)
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
)
405 static fqdncache_entry
*
406 fqdncacheAddNew(const char *name
, const struct hostent
*hp
, fqdncache_status_t status
)
409 if (fqdncache_get(name
))
410 fatal_dump("fqdncacheAddNew: somebody adding a duplicate!");
411 debug(14, 10) ("fqdncacheAddNew: Adding '%s', status=%c\n",
413 fqdncache_status_char
[status
]);
414 f
= fqdncache_create(name
);
416 fqdncacheAddHostent(f
, hp
);
418 f
->lastref
= squid_curtime
;
422 /* walks down the pending list, calling handlers */
424 fqdncache_call_pending(fqdncache_entry
* f
)
426 struct _fqdn_pending
*p
= NULL
;
429 f
->lastref
= squid_curtime
;
431 fqdncacheLockEntry(f
);
432 while (f
->pending_head
!= NULL
) {
434 f
->pending_head
= p
->next
;
437 dns_error_message
= f
->error_message
;
438 p
->handler((f
->status
== FQDN_CACHED
) ? f
->names
[0] : NULL
,
443 f
->pending_head
= NULL
; /* nuke list */
444 debug(35, 10) ("fqdncache_call_pending: Called %d handlers.\n", nhandler
);
445 fqdncacheUnlockEntry(f
);
448 static fqdncache_entry
*
449 fqdncache_parsebuffer(const char *inbuf
, dnsserver_t
* dnsData
)
451 char *buf
= xstrdup(inbuf
);
453 static fqdncache_entry f
;
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")) {
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
);
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");
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");
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
);
508 fatal_dump("Invalid dnsserver output");
516 fqdncacheNudgeQueue(void)
518 dnsserver_t
*dnsData
;
519 fqdncache_entry
*f
= NULL
;
520 while ((dnsData
= dnsGetFirstAvailable()) && (f
= fqdncacheDequeue()))
521 fqdncache_dnsDispatch(dnsData
, f
);
525 fqdncache_dnsHandleRead(int fd
, void *data
)
527 dnsserver_t
*dnsData
= data
;
530 fqdncache_entry
*f
= NULL
;
531 fqdncache_entry
*x
= NULL
;
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",
540 if (len
< 0 && (errno
== EINTR
|| errno
== EWOULDBLOCK
|| errno
== EAGAIN
)) {
543 fqdncache_dnsHandleRead
,
547 debug(35, dnsData
->flags
& DNS_FLAG_CLOSING
? 5 : 1)
548 ("FD %d: Connection from DNSSERVER #%d is closed, disabling\n",
559 n
= ++FqdncacheStats
.replies
;
561 dnsData
->offset
+= len
;
562 dnsData
->ip_inbuf
[dnsData
->offset
] = '\0';
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");
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
);
585 fqdncacheUnlockEntry(f
); /* unlock from FQDN_DISPATCHED */
587 if (dnsData
->offset
== 0) {
588 dnsData
->data
= NULL
;
589 dnsData
->flags
&= ~DNS_FLAG_BUSY
;
592 commSetSelect(dnsData
->inpipe
,
594 fqdncache_dnsHandleRead
,
596 fqdncacheNudgeQueue();
600 fqdncacheAddPending(fqdncache_entry
* f
, FQDNH
* handler
, void *handlerData
)
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
));
609 if (f
->status
== IP_PENDING
)
610 fqdncacheNudgeQueue();
614 fqdncache_nbgethostbyaddr(struct in_addr addr
, FQDNH
* handler
, void *handlerData
)
616 fqdncache_entry
*f
= NULL
;
617 dnsserver_t
*dnsData
= NULL
;
618 char *name
= inet_ntoa(addr
);
621 fatal_dump("fqdncache_nbgethostbyaddr: NULL handler");
623 debug(35, 4) ("fqdncache_nbgethostbyaddr: Name '%s'.\n", name
);
624 FqdncacheStats
.requests
++;
626 if (name
== NULL
|| name
[0] == '\0') {
627 debug(35, 4) ("fqdncache_nbgethostbyaddr: Invalid name!\n");
628 handler(NULL
, handlerData
);
631 if ((f
= fqdncache_get(name
))) {
632 if (fqdncacheExpiredEntry(f
)) {
633 fqdncache_release(f
);
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
) {
645 debug(35, 4) ("fqdncache_nbgethostbyaddr: HIT for '%s'\n", name
);
646 if (f
->status
== FQDN_NEGATIVE_CACHED
)
647 FqdncacheStats
.negative_hits
++;
649 FqdncacheStats
.hits
++;
650 fqdncacheAddPending(f
, handler
, handlerData
);
651 fqdncache_call_pending(f
);
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
);
664 fatal_dump("fqdncache_nbgethostbyaddr: BAD fqdncache_entry status");
667 /* for HIT, PENDING, DISPATCHED we've returned. For MISS we continue */
669 if ((dnsData
= dnsGetFirstAvailable())) {
670 fqdncache_dnsDispatch(dnsData
, f
);
671 } else if (NDnsServersAlloc
> 0) {
674 /* abort if we get here */
675 assert(NDnsServersAlloc
);
680 fqdncache_dnsDispatch(dnsserver_t
* dns
, fqdncache_entry
* f
)
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",
688 f
->status
= FQDN_NEGATIVE_CACHED
;
689 fqdncache_release(f
);
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
;
698 f
->status
= FQDN_DISPATCHED
;
699 comm_write(dns
->outpipe
,
703 NULL
, /* Handler-data */
705 commSetSelect(dns
->outpipe
,
707 fqdncache_dnsHandleRead
,
710 debug(35, 5) ("fqdncache_dnsDispatch: Request sent to DNS server #%d.\n",
712 dns
->dispatch_time
= current_time
;
714 DnsStats
.hist
[dns
->id
- 1]++;
715 fqdncacheLockEntry(f
); /* lock while IP_DISPATCHED */
719 /* initialize the fqdncache */
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);
735 /* clean up the pending entries in dnsserver */
736 /* return 1 if we found the host, 0 otherwise */
738 fqdncacheUnregister(struct in_addr addr
, void *data
)
740 char *name
= inet_ntoa(addr
);
741 fqdncache_entry
*f
= NULL
;
742 struct _fqdn_pending
*p
= NULL
;
744 debug(35, 3) ("fqdncacheUnregister: FD %d, name '%s'\n", name
);
745 if ((f
= fqdncache_get(name
)) == NULL
)
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
)
756 debug_trap("fqdncacheUnregister: callback data not found");
757 debug(35, 3) ("fqdncacheUnregister: unregistered %d handlers\n", n
);
762 fqdncache_gethostbyaddr(struct in_addr addr
, int flags
)
764 char *name
= inet_ntoa(addr
);
765 fqdncache_entry
*f
= NULL
;
769 fatal_dump("fqdncache_gethostbyaddr: NULL name");
770 FqdncacheStats
.requests
++;
771 if ((f
= fqdncache_get(name
))) {
772 if (fqdncacheExpiredEntry(f
)) {
773 fqdncache_release(f
);
778 if (f
->status
== FQDN_NEGATIVE_CACHED
) {
779 FqdncacheStats
.negative_hits
++;
780 dns_error_message
= f
->error_message
;
783 FqdncacheStats
.hits
++;
784 f
->lastref
= squid_curtime
;
788 /* check if it's already a FQDN address in text form. */
789 if (!safe_inet_addr(name
, &ip
))
791 FqdncacheStats
.misses
++;
792 if (flags
& FQDN_LOOKUP_IF_MISS
)
793 fqdncache_nbgethostbyaddr(addr
, dummy_handler
, NULL
);
798 /* process objects list */
800 fqdnStats(StoreEntry
* sentry
)
802 fqdncache_entry
*f
= NULL
;
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");
829 for (f
= fqdncache_GetFirst(); f
; f
= fqdncache_GetNext()) {
830 if (f
->status
== FQDN_PENDING
|| f
->status
== FQDN_DISPATCHED
)
833 ttl
= (f
->expires
- squid_curtime
);
834 storeAppendPrintf(sentry
, " {%-32.32s %c %6d %d",
836 fqdncache_status_char
[f
->status
],
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
);
843 storeAppendPrintf(sentry
, close_bracket
);
847 dummy_handler(const char *bufnotused
, void *datanotused
)
853 fqdncacheHasPending(const fqdncache_entry
* f
)
855 const struct _fqdn_pending
*p
= NULL
;
856 if (f
->status
!= FQDN_PENDING
)
858 for (p
= f
->pending_head
; p
; p
= p
->next
)
865 fqdncacheReleaseInvalid(const char *name
)
868 if ((f
= fqdncache_get(name
)) == NULL
)
870 if (f
->status
!= FQDN_NEGATIVE_CACHED
)
872 fqdncache_release(f
);
876 fqdnFromAddr(struct in_addr addr
)
880 if (Config
.onoff
.log_fqdn
&& (n
= fqdncache_gethostbyaddr(addr
, 0)))
882 xstrncpy(buf
, inet_ntoa(addr
), 32);
887 fqdncacheQueueDrain(void)
890 dnsserver_t
*dnsData
;
891 if (!fqdncacheQueueHead
)
893 while ((dnsData
= dnsGetFirstAvailable()) && (i
= fqdncacheDequeue()))
894 fqdncache_dnsDispatch(dnsData
, i
);
899 fqdncacheLockEntry(fqdncache_entry
* f
)
905 fqdncacheUnlockEntry(fqdncache_entry
* f
)
908 debug_trap("fqdncacheUnlockEntry: Entry has no locks");
912 if (fqdncacheExpiredEntry(f
))
913 fqdncache_release(f
);
917 fqdncacheFreeMemory(void)
920 fqdncache_entry
**list
;
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
) {
929 f
= (fqdncache_entry
*) hash_next(fqdn_table
);
931 for (j
= 0; j
< i
; j
++) {
933 for (k
= 0; k
< (int) f
->name_count
; k
++)
934 safe_free(f
->names
[k
]);
936 safe_free(f
->error_message
);
940 hashFreeMemory(fqdn_table
);
945 fqdncacheChangeKey(fqdncache_entry
* f
)
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
);
954 if (f
!= (fqdncache_entry
*) table_entry
) {
955 debug_trap("fqdncacheChangeKey: f != table_entry!");
958 if (hash_remove_link(fqdn_table
, table_entry
)) {
959 debug_trap("fqdncacheChangeKey: hash_remove_link() failed\n");
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
);
966 f
->name
= xstrdup(new_key
);
967 fqdncache_add_to_hash(f
);
970 /* call during reconfigure phase to clear out all the
971 * pending and dispatched reqeusts that got lost */
973 fqdncache_restart(void)
975 fqdncache_entry
*this;
976 fqdncache_entry
*next
;
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
)
985 if (this->status
== FQDN_NEGATIVE_CACHED
)
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);