3 * $Id: ipcache.cc,v 1.141 1997/11/05 05:29:30 wessels Exp $
5 * DEBUG: section 14 IP 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.
112 struct _ip_pending
*next
;
115 struct ipcacheQueueData
{
116 struct ipcacheQueueData
*next
;
129 int ghbn_calls
; /* # calls to blocking gethostbyname() */
133 static int ipcache_testname(void);
134 static QS ipcache_compareLastRef
;
135 static QS ipcache_reverseLastRef
;
136 static PF ipcache_dnsHandleRead
;
137 static ipcache_entry
*ipcache_parsebuffer(const char *buf
, dnsserver_t
*);
138 static void ipcache_release(ipcache_entry
*);
139 static ipcache_entry
*ipcache_GetFirst(void);
140 static ipcache_entry
*ipcache_GetNext(void);
141 static ipcache_entry
*ipcache_create(const char *name
);
142 static void ipcache_add_to_hash(ipcache_entry
*);
143 static void ipcache_call_pending(ipcache_entry
*);
144 static ipcache_entry
*ipcacheAddNew(const char *, const struct hostent
*, ipcache_status_t
);
145 static void ipcacheAddHostent(ipcache_entry
*, const struct hostent
*);
146 static int ipcacheHasPending(ipcache_entry
*);
147 static ipcache_entry
*ipcache_get(const char *);
148 static IPH dummy_handler
;
149 static int ipcacheExpiredEntry(ipcache_entry
*);
150 static void ipcacheAddPending(ipcache_entry
*, IPH
*, void *);
151 static void ipcacheEnqueue(ipcache_entry
*);
152 static void *ipcacheDequeue(void);
153 static void ipcache_dnsDispatch(dnsserver_t
*, ipcache_entry
*);
154 static void ipcacheStatPrint(ipcache_entry
*, StoreEntry
*);
155 static void ipcacheUnlockEntry(ipcache_entry
*);
156 static void ipcacheLockEntry(ipcache_entry
*);
157 static void ipcacheNudgeQueue(void);
158 static void ipcacheChangeKey(ipcache_entry
* i
);
160 static ipcache_addrs static_addrs
;
161 static hash_table
*ip_table
= NULL
;
162 static struct ipcacheQueueData
*ipcacheQueueHead
= NULL
;
163 static struct ipcacheQueueData
**ipcacheQueueTailP
= &ipcacheQueueHead
;
164 static int queue_length
= 0;
166 static char ipcache_status_char
[] =
174 static long ipcache_low
= 180;
175 static long ipcache_high
= 200;
177 #if LIBRESOLV_DNS_TTL_HACK
178 extern int _dns_ttl_
;
182 ipcacheEnqueue(ipcache_entry
* i
)
184 static time_t last_warning
= 0;
185 struct ipcacheQueueData
*new = xcalloc(1, sizeof(struct ipcacheQueueData
));
187 *ipcacheQueueTailP
= new;
188 ipcacheQueueTailP
= &new->next
;
190 if (queue_length
< NDnsServersAlloc
)
192 if (squid_curtime
- last_warning
< 600)
194 last_warning
= squid_curtime
;
195 debug(14, 1) ("ipcacheEnqueue: WARNING: All dnsservers are busy.\n");
196 debug(14, 1) ("ipcacheEnqueue: WARNING: %d DNS lookups queued\n", queue_length
);
197 if (Config
.dnsChildren
>= DefaultDnsChildrenMax
)
199 debug(14, 1) ("ipcacheEnqueue: Consider increasing 'dns_children' in your config file.\n");
205 struct ipcacheQueueData
*old
= NULL
;
206 ipcache_entry
*i
= NULL
;
207 if (ipcacheQueueHead
) {
208 i
= ipcacheQueueHead
->i
;
209 old
= ipcacheQueueHead
;
210 ipcacheQueueHead
= ipcacheQueueHead
->next
;
211 if (ipcacheQueueHead
== NULL
)
212 ipcacheQueueTailP
= &ipcacheQueueHead
;
217 assert(i
->status
== IP_PENDING
);
222 ipcache_testname(void)
225 debug(14, 1) ("Performing DNS Tests...\n");
226 if ((w
= Config
.dns_testname_list
) == NULL
)
228 for (; w
; w
= w
->next
) {
229 IpcacheStats
.ghbn_calls
++;
230 if (gethostbyname(w
->key
) != NULL
)
236 /* removes the given ipcache entry */
238 ipcache_release(ipcache_entry
* i
)
240 hash_link
*table_entry
= NULL
;
241 if ((table_entry
= hash_lookup(ip_table
, i
->name
)) == NULL
) {
242 debug(14, 0) ("ipcache_release: Could not find key '%s'\n", i
->name
);
245 assert(i
== (ipcache_entry
*) table_entry
);
247 i
->expires
= squid_curtime
;
249 IpcacheStats
.release_locked
++;
252 if (hash_remove_link(ip_table
, table_entry
)) {
253 debug(14, 0) ("ipcache_release: hash_remove_link() failed for '%s'\n",
257 if (i
->status
== IP_CACHED
) {
258 safe_free(i
->addrs
.in_addrs
);
259 safe_free(i
->addrs
.bad_mask
);
260 debug(14, 5) ("ipcache_release: Released IP cached record for '%s'.\n",
264 safe_free(i
->error_message
);
266 --meta_data
.ipcache_count
;
270 /* return match for given name */
271 static ipcache_entry
*
272 ipcache_get(const char *name
)
275 static ipcache_entry
*i
;
279 if ((e
= hash_lookup(ip_table
, name
)) != NULL
)
280 i
= (ipcache_entry
*) e
;
285 /* get the first ip entry in the storage */
286 static ipcache_entry
*
287 ipcache_GetFirst(void)
289 return (ipcache_entry
*) hash_first(ip_table
);
292 /* get the next ip entry in the storage for a given search pointer */
293 static ipcache_entry
*
294 ipcache_GetNext(void)
296 return (ipcache_entry
*) hash_next(ip_table
);
300 ipcache_compareLastRef(const void *A
, const void *B
)
302 const ipcache_entry
*const *e1
= A
;
303 const ipcache_entry
*const *e2
= B
;
304 assert(e1
!= NULL
&& e2
!= NULL
);
305 if ((*e1
)->lastref
> (*e2
)->lastref
)
307 if ((*e1
)->lastref
< (*e2
)->lastref
)
313 ipcache_reverseLastRef(const void *A
, const void *B
)
315 const ipcache_entry
*const *e1
= A
;
316 const ipcache_entry
*const *e2
= B
;
317 assert(e1
!= NULL
&& e2
!= NULL
);
318 if ((*e1
)->lastref
< (*e2
)->lastref
)
320 if ((*e1
)->lastref
> (*e2
)->lastref
)
326 ipcacheExpiredEntry(ipcache_entry
* i
)
328 if (i
->status
== IP_PENDING
)
330 if (i
->status
== IP_DISPATCHED
)
334 if (i
->addrs
.count
== 0)
336 if (i
->expires
> squid_curtime
)
341 /* finds the LRU and deletes */
343 ipcache_purgelru(void *voidnotused
)
345 ipcache_entry
*i
= NULL
;
346 int local_ip_notpending_count
= 0;
349 ipcache_entry
**LRU_list
= NULL
;
350 int LRU_list_count
= 0;
352 eventAdd("ipcache_purgelru", ipcache_purgelru
, NULL
, 10);
353 LRU_list
= xcalloc(meta_data
.ipcache_count
, sizeof(ipcache_entry
*));
355 for (i
= ipcache_GetFirst(); i
; i
= ipcache_GetNext()) {
356 if (ipcacheExpiredEntry(i
)) {
361 if (LRU_list_count
== meta_data
.ipcache_count
)
363 if (i
->status
== IP_PENDING
)
365 if (i
->status
== IP_DISPATCHED
)
369 local_ip_notpending_count
++;
370 LRU_list
[LRU_list_count
++] = i
;
373 /* sort LRU candidate list */
374 qsort((char *) LRU_list
,
376 sizeof(ipcache_entry
*),
377 ipcache_compareLastRef
);
378 for (k
= 0; k
< LRU_list_count
; k
++) {
379 if (meta_data
.ipcache_count
< ipcache_low
)
381 if (LRU_list
[k
] == NULL
)
383 ipcache_release(LRU_list
[k
]);
386 assert(meta_data
.ipcache_count
<= ipcache_low
);
387 debug(14, 3) ("ipcache_purgelru: removed %d entries\n", removed
);
392 /* create blank ipcache_entry */
393 static ipcache_entry
*
394 ipcache_create(const char *name
)
396 static ipcache_entry
*new;
397 if (meta_data
.ipcache_count
> ipcache_high
)
398 ipcache_purgelru(NULL
);
399 meta_data
.ipcache_count
++;
400 new = xcalloc(1, sizeof(ipcache_entry
));
401 new->name
= xstrdup(name
);
402 new->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
403 ipcache_add_to_hash(new);
408 ipcache_add_to_hash(ipcache_entry
* i
)
410 if (hash_join(ip_table
, (hash_link
*) i
)) {
411 debug(14, 1) ("ipcache_add_to_hash: Cannot add %s (%p) to hash table %d.\n",
412 i
->name
, i
, ip_table
);
414 debug(14, 5) ("ipcache_add_to_hash: name <%s>\n", i
->name
);
418 ipcacheAddHostent(ipcache_entry
* i
, const struct hostent
*hp
)
422 safe_free(i
->addrs
.in_addrs
);
423 safe_free(i
->addrs
.bad_mask
);
424 while ((addr_count
< 255) && *(hp
->h_addr_list
+ addr_count
))
426 i
->addrs
.count
= (unsigned char) addr_count
;
427 i
->addrs
.in_addrs
= xcalloc(addr_count
, sizeof(struct in_addr
));
428 i
->addrs
.bad_mask
= xcalloc(addr_count
, sizeof(unsigned char));
429 i
->addrs
.badcount
= 0;
430 for (k
= 0; k
< addr_count
; k
++)
431 xmemcpy(&i
->addrs
.in_addrs
[k
].s_addr
,
432 *(hp
->h_addr_list
+ k
),
436 static ipcache_entry
*
437 ipcacheAddNew(const char *name
, const struct hostent
*hp
, ipcache_status_t status
)
440 if (ipcache_get(name
))
441 fatal_dump("ipcacheAddNew: somebody adding a duplicate!");
442 debug(14, 10) ("ipcacheAddNew: Adding '%s', status=%c\n",
444 ipcache_status_char
[status
]);
445 i
= ipcache_create(name
);
447 ipcacheAddHostent(i
, hp
);
449 i
->lastref
= squid_curtime
;
453 /* walks down the pending list, calling handlers */
455 ipcache_call_pending(ipcache_entry
* i
)
457 struct _ip_pending
*p
= NULL
;
459 i
->lastref
= squid_curtime
;
461 while (i
->pending_head
!= NULL
) {
463 i
->pending_head
= p
->next
;
466 dns_error_message
= i
->error_message
;
467 if (cbdataValid(p
->handlerData
)) {
468 p
->handler(i
->status
== IP_CACHED
? &i
->addrs
: NULL
,
471 cbdataUnlock(p
->handlerData
);
475 i
->pending_head
= NULL
; /* nuke list */
476 debug(14, 10) ("ipcache_call_pending: Called %d handlers.\n", nhandler
);
477 ipcacheUnlockEntry(i
);
481 static ipcache_entry
*
482 ipcache_parsebuffer(const char *inbuf
, dnsserver_t
* dnsData
)
484 char *buf
= xstrdup(inbuf
);
486 static ipcache_entry i
;
490 debug(14, 5) ("ipcache_parsebuffer: parsing:\n%s", inbuf
);
491 memset(&i
, '\0', sizeof(ipcache_entry
));
492 i
.expires
= squid_curtime
+ Config
.positiveDnsTtl
;
493 for (token
= strtok(buf
, w_space
); token
; token
= strtok(NULL
, w_space
)) {
494 if (!strcmp(token
, "$end")) {
496 } else if (!strcmp(token
, "$alive")) {
497 dnsData
->answer
= squid_curtime
;
498 } else if (!strcmp(token
, "$fail")) {
499 if ((token
= strtok(NULL
, "\n")) == NULL
)
500 fatal_dump("Invalid $fail");
501 i
.expires
= squid_curtime
+ Config
.negativeDnsTtl
;
502 i
.status
= IP_NEGATIVE_CACHED
;
503 } else if (!strcmp(token
, "$message")) {
504 if ((token
= strtok(NULL
, "\n")) == NULL
)
505 fatal_dump("Invalid $message");
506 i
.error_message
= xstrdup(token
);
507 } else if (!strcmp(token
, "$name")) {
508 if ((token
= strtok(NULL
, w_space
)) == NULL
)
509 fatal_dump("Invalid $name");
510 i
.status
= IP_CACHED
;
511 } else if (!strcmp(token
, "$h_name")) {
512 if ((token
= strtok(NULL
, w_space
)) == NULL
)
513 fatal_dump("Invalid $h_name");
515 } else if (!strcmp(token
, "$h_len")) {
516 if ((token
= strtok(NULL
, w_space
)) == NULL
)
517 fatal_dump("Invalid $h_len");
518 /* ignore $h_length */
519 } else if (!strcmp(token
, "$ipcount")) {
520 if ((token
= strtok(NULL
, w_space
)) == NULL
)
521 fatal_dump("Invalid $ipcount");
522 ipcount
= atoi(token
);
523 i
.addrs
.count
= (unsigned char) ipcount
;
525 i
.addrs
.in_addrs
= NULL
;
526 i
.addrs
.bad_mask
= NULL
;
528 i
.addrs
.in_addrs
= xcalloc(ipcount
, sizeof(struct in_addr
));
529 i
.addrs
.bad_mask
= xcalloc(ipcount
, sizeof(unsigned char));
531 for (k
= 0; k
< ipcount
; k
++) {
532 if ((token
= strtok(NULL
, w_space
)) == NULL
)
533 fatal_dump("Invalid IP address");
534 if (!safe_inet_addr(token
, &i
.addrs
.in_addrs
[k
]))
535 fatal_dump("Invalid IP address");
537 } else if (!strcmp(token
, "$aliascount")) {
538 if ((token
= strtok(NULL
, w_space
)) == NULL
)
539 fatal_dump("Invalid $aliascount");
540 aliascount
= atoi(token
);
541 for (k
= 0; k
< aliascount
; k
++) {
542 if ((token
= strtok(NULL
, w_space
)) == NULL
)
543 fatal_dump("Invalid alias");
545 } else if (!strcmp(token
, "$ttl")) {
546 if ((token
= strtok(NULL
, w_space
)) == NULL
)
547 fatal_dump("Invalid $ttl");
548 i
.expires
= squid_curtime
+ atoi(token
);
550 debug(14, 0) ("--> %s <--\n", inbuf
);
551 debug_trap("Invalid dnsserver output");
559 ipcacheNudgeQueue(void)
561 dnsserver_t
*dnsData
;
562 ipcache_entry
*i
= NULL
;
563 while ((dnsData
= dnsGetFirstAvailable()) && (i
= ipcacheDequeue()))
564 ipcache_dnsDispatch(dnsData
, i
);
568 ipcache_dnsHandleRead(int fd
, void *data
)
570 dnsserver_t
*dnsData
= data
;
573 ipcache_entry
*i
= NULL
;
574 ipcache_entry
*x
= NULL
;
577 dnsData
->ip_inbuf
+ dnsData
->offset
,
578 dnsData
->size
- dnsData
->offset
);
579 fd_bytes(fd
, len
, FD_READ
);
580 debug(14, 5) ("ipcache_dnsHandleRead: Result from DNS ID %d (%d bytes)\n",
583 if (len
< 0 && (errno
== EWOULDBLOCK
|| errno
== EAGAIN
|| errno
== EINTR
)) {
586 ipcache_dnsHandleRead
,
591 debug(14, dnsData
->flags
& DNS_FLAG_CLOSING
? 5 : 1)
592 ("FD %d: Connection from DNSSERVER #%d is closed, disabling\n",
602 n
= ++IpcacheStats
.replies
;
604 dnsData
->offset
+= len
;
605 dnsData
->ip_inbuf
[dnsData
->offset
] = '\0';
608 debug_trap("NULL ipcache_entry");
611 if (i
->status
!= IP_DISPATCHED
)
612 fatal_dump("ipcache_dnsHandleRead: bad status");
613 if (strstr(dnsData
->ip_inbuf
, "$end\n")) {
614 /* end of record found */
615 IpcacheStats
.avg_svc_time
= intAverage(IpcacheStats
.avg_svc_time
,
616 tvSubMsec(dnsData
->dispatch_time
, current_time
),
617 n
, IPCACHE_AV_FACTOR
);
618 if ((x
= ipcache_parsebuffer(dnsData
->ip_inbuf
, dnsData
)) == NULL
) {
619 debug(14, 0) ("ipcache_dnsHandleRead: ipcache_parsebuffer failed?!\n");
622 dnsData
->ip_inbuf
[0] = '\0';
624 i
->error_message
= x
->error_message
;
625 i
->status
= x
->status
;
626 i
->expires
= x
->expires
;
627 ipcache_call_pending(i
);
629 ipcacheUnlockEntry(i
); /* unlock from IP_DISPATCHED */
631 debug(14, 5) ("ipcache_dnsHandleRead: Incomplete reply\n");
634 ipcache_dnsHandleRead
,
638 if (dnsData
->offset
== 0) {
639 dnsData
->data
= NULL
;
640 dnsData
->flags
&= ~DNS_FLAG_BUSY
;
646 ipcacheAddPending(ipcache_entry
* i
, IPH
* handler
, void *handlerData
)
648 struct _ip_pending
*pending
= xcalloc(1, sizeof(struct _ip_pending
));
649 struct _ip_pending
**I
= NULL
;
650 i
->lastref
= squid_curtime
;
651 pending
->handler
= handler
;
652 pending
->handlerData
= handlerData
;
653 cbdataLock(handlerData
);
654 for (I
= &(i
->pending_head
); *I
; I
= &((*I
)->next
));
656 if (i
->status
== IP_PENDING
)
661 ipcache_nbgethostbyname(const char *name
, IPH
* handler
, void *handlerData
)
663 ipcache_entry
*i
= NULL
;
664 dnsserver_t
*dnsData
= NULL
;
665 const ipcache_addrs
*addrs
= NULL
;
668 fatal_dump("ipcache_nbgethostbyname: NULL handler");
670 debug(14, 4) ("ipcache_nbgethostbyname: Name '%s'.\n", name
);
671 IpcacheStats
.requests
++;
673 if (name
== NULL
|| name
[0] == '\0') {
674 debug(14, 4) ("ipcache_nbgethostbyname: Invalid name!\n");
675 handler(NULL
, handlerData
);
678 if ((addrs
= ipcacheCheckNumeric(name
))) {
679 handler(addrs
, handlerData
);
682 if ((i
= ipcache_get(name
))) {
683 if (ipcacheExpiredEntry(i
)) {
689 /* MISS: No entry, create the new one */
690 debug(14, 5) ("ipcache_nbgethostbyname: MISS for '%s'\n", name
);
691 IpcacheStats
.misses
++;
692 i
= ipcacheAddNew(name
, NULL
, IP_PENDING
);
693 ipcacheAddPending(i
, handler
, handlerData
);
694 } else if (i
->status
== IP_CACHED
|| i
->status
== IP_NEGATIVE_CACHED
) {
696 debug(14, 4) ("ipcache_nbgethostbyname: HIT for '%s'\n", name
);
697 if (i
->status
== IP_NEGATIVE_CACHED
)
698 IpcacheStats
.negative_hits
++;
701 ipcacheAddPending(i
, handler
, handlerData
);
702 ipcache_call_pending(i
);
704 } else if (i
->status
== IP_PENDING
|| i
->status
== IP_DISPATCHED
) {
705 debug(14, 4) ("ipcache_nbgethostbyname: PENDING for '%s'\n", name
);
706 IpcacheStats
.pending_hits
++;
707 ipcacheAddPending(i
, handler
, handlerData
);
708 if (squid_curtime
- i
->expires
> 600) {
709 debug(14, 0) ("ipcache_nbgethostbyname: '%s' PENDING for %d seconds, aborting\n", name
, squid_curtime
+ Config
.negativeDnsTtl
- i
->expires
);
711 ipcache_call_pending(i
);
715 fatal_dump("ipcache_nbgethostbyname: BAD ipcache_entry status");
718 /* for HIT, PENDING, DISPATCHED we've returned. For MISS we continue */
720 if ((dnsData
= dnsGetFirstAvailable())) {
721 ipcache_dnsDispatch(dnsData
, i
);
722 } else if (NDnsServersAlloc
) {
725 /* generate abort if we get here */
726 assert(NDnsServersAlloc
);
731 ipcache_dnsDispatch(dnsserver_t
* dns
, ipcache_entry
* i
)
734 assert(BIT_TEST(dns
->flags
, DNS_FLAG_ALIVE
));
735 if (!ipcacheHasPending(i
)) {
736 debug(14, 0) ("Skipping lookup of '%s' because client(s) disappeared.\n",
738 i
->status
= IP_NEGATIVE_CACHED
;
742 assert(i
->status
== IP_PENDING
);
743 buf
= xcalloc(1, 256);
744 snprintf(buf
, 256, "%s\n", i
->name
);
745 dns
->flags
|= DNS_FLAG_BUSY
;
747 i
->status
= IP_DISPATCHED
;
748 comm_write(dns
->outpipe
,
752 NULL
, /* Handler-data */
754 commSetSelect(dns
->outpipe
,
756 ipcache_dnsHandleRead
,
758 debug(14, 5) ("ipcache_dnsDispatch: Request sent to DNS server #%d.\n",
760 dns
->dispatch_time
= current_time
;
762 DnsStats
.hist
[dns
->id
- 1]++;
763 ipcacheLockEntry(i
); /* lock while IP_DISPATCHED */
767 /* initialize the ipcache */
771 debug(14, 3) ("Initializing IP Cache...\n");
773 memset(&IpcacheStats
, '\0', sizeof(IpcacheStats
));
775 /* test naming lookup */
776 if (!opt_dns_tests
) {
777 debug(14, 4) ("ipcache_init: Skipping DNS name lookup tests.\n");
778 } else if (!ipcache_testname()) {
779 fatal("ipcache_init: DNS name lookup tests failed.");
781 debug(14, 1) ("Successful DNS name lookup tests...\n");
784 ip_table
= hash_create(urlcmp
, 229, hash4
); /* small hash table */
785 memset(&static_addrs
, '\0', sizeof(ipcache_addrs
));
786 static_addrs
.in_addrs
= xcalloc(1, sizeof(struct in_addr
));
787 static_addrs
.bad_mask
= xcalloc(1, sizeof(unsigned char));
789 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
790 (float) Config
.ipcache
.high
) / (float) 100);
791 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
792 (float) Config
.ipcache
.low
) / (float) 100);
796 ipcacheUnregister(const char *name
, void *data
)
798 ipcache_entry
*i
= NULL
;
799 struct _ip_pending
*p
= NULL
;
801 debug(14, 3) ("ipcacheUnregister: name '%s'\n", name
);
802 if ((i
= ipcache_get(name
)) == NULL
)
804 if (i
->status
== IP_PENDING
|| i
->status
== IP_DISPATCHED
) {
805 for (p
= i
->pending_head
; p
; p
= p
->next
) {
806 if (p
->handlerData
!= data
)
813 debug(14, 3) ("ipcacheUnregister: unregistered %d handlers\n", n
);
817 const ipcache_addrs
*
818 ipcache_gethostbyname(const char *name
, int flags
)
820 ipcache_entry
*i
= NULL
;
821 ipcache_addrs
*addrs
;
824 fatal_dump("ipcache_gethostbyname: NULL name");
825 debug(14, 3) ("ipcache_gethostbyname: '%s', flags=%x\n", name
, flags
);
826 IpcacheStats
.requests
++;
827 if ((i
= ipcache_get(name
))) {
828 if (ipcacheExpiredEntry(i
)) {
834 if (i
->status
== IP_NEGATIVE_CACHED
) {
835 IpcacheStats
.negative_hits
++;
836 dns_error_message
= i
->error_message
;
840 i
->lastref
= squid_curtime
;
844 if ((addrs
= ipcacheCheckNumeric(name
)))
846 IpcacheStats
.misses
++;
847 if (flags
& IP_LOOKUP_IF_MISS
)
848 ipcache_nbgethostbyname(name
, dummy_handler
, NULL
);
853 ipcacheStatPrint(ipcache_entry
* i
, StoreEntry
* sentry
)
856 storeAppendPrintf(sentry
, " {%-32.32s %c%c %6d %6d %2d(%2d)",
858 ipcache_status_char
[i
->status
],
859 i
->locks
? 'L' : ' ',
860 (int) (squid_curtime
- i
->lastref
),
861 (int) (i
->expires
- squid_curtime
),
862 (int) i
->addrs
.count
,
863 (int) i
->addrs
.badcount
);
864 for (k
= 0; k
< (int) i
->addrs
.count
; k
++)
865 storeAppendPrintf(sentry
, " %15s-%3s", inet_ntoa(i
->addrs
.in_addrs
[k
]),
866 i
->addrs
.bad_mask
[k
] ? "BAD" : "OK ");
867 storeAppendPrintf(sentry
, close_bracket
);
870 /* process objects list */
872 stat_ipcache_get(StoreEntry
* sentry
)
876 ipcache_entry
*i
= NULL
;
877 ipcache_entry
**list
= NULL
;
880 storeAppendPrintf(sentry
, "{IP Cache Statistics:\n");
881 storeAppendPrintf(sentry
, "{IPcache Entries: %d}\n",
882 meta_data
.ipcache_count
);
883 storeAppendPrintf(sentry
, "{IPcache Requests: %d}\n",
884 IpcacheStats
.requests
);
885 storeAppendPrintf(sentry
, "{IPcache Hits: %d}\n",
887 storeAppendPrintf(sentry
, "{IPcache Pending Hits: %d}\n",
888 IpcacheStats
.pending_hits
);
889 storeAppendPrintf(sentry
, "{IPcache Negative Hits: %d}\n",
890 IpcacheStats
.negative_hits
);
891 storeAppendPrintf(sentry
, "{IPcache Misses: %d}\n",
892 IpcacheStats
.misses
);
893 storeAppendPrintf(sentry
, "{Blocking calls to gethostbyname(): %d}\n",
894 IpcacheStats
.ghbn_calls
);
895 storeAppendPrintf(sentry
, "{Attempts to release locked entries: %d}\n",
896 IpcacheStats
.release_locked
);
897 storeAppendPrintf(sentry
, "{dnsserver avg service time: %d msec}\n",
898 IpcacheStats
.avg_svc_time
);
899 storeAppendPrintf(sentry
, "{pending queue length: %d}\n", queue_length
);
900 storeAppendPrintf(sentry
, "}\n\n");
901 storeAppendPrintf(sentry
, "{IP Cache Contents:\n\n");
902 storeAppendPrintf(sentry
, " {%-29.29s %5s %6s %6s %1s}\n",
908 list
= xcalloc(meta_data
.ipcache_count
, sizeof(ipcache_entry
*));
910 for (i
= ipcache_GetFirst(); i
; i
= ipcache_GetNext()) {
912 assert(++N
<= meta_data
.ipcache_count
);
916 sizeof(ipcache_entry
*),
917 ipcache_reverseLastRef
);
918 for (k
= 0; k
< N
; k
++)
919 ipcacheStatPrint(*(list
+ k
), sentry
);
920 storeAppendPrintf(sentry
, close_bracket
);
925 dummy_handler(const ipcache_addrs
* addrsnotused
, void *datanotused
)
931 ipcacheHasPending(ipcache_entry
* i
)
933 struct _ip_pending
*p
= NULL
;
934 if (i
->status
!= IP_PENDING
)
936 for (p
= i
->pending_head
; p
; p
= p
->next
)
943 ipcacheReleaseInvalid(const char *name
)
946 if ((i
= ipcache_get(name
)) == NULL
)
948 if (i
->status
!= IP_NEGATIVE_CACHED
)
954 ipcacheInvalidate(const char *name
)
957 if ((i
= ipcache_get(name
)) == NULL
)
959 i
->expires
= squid_curtime
;
960 /* NOTE, don't call ipcache_release here becuase we might be here due
961 * to a thread started from ipcache_call_pending() which will cause a
966 ipcacheCheckNumeric(const char *name
)
969 /* check if it's already a IP address in text form. */
970 if (!safe_inet_addr(name
, &ip
))
972 static_addrs
.count
= 1;
973 static_addrs
.cur
= 0;
974 static_addrs
.in_addrs
[0].s_addr
= ip
.s_addr
;
975 static_addrs
.bad_mask
[0] = FALSE
;
976 static_addrs
.badcount
= 0;
977 return &static_addrs
;
981 ipcacheQueueDrain(void)
983 if (!ipcacheQueueHead
)
990 ipcacheLockEntry(ipcache_entry
* i
)
996 ipcacheUnlockEntry(ipcache_entry
* i
)
998 assert(i
->locks
> 0);
1000 if (ipcacheExpiredEntry(i
))
1005 ipcacheCycleAddr(const char *name
)
1009 unsigned char fullcircle
;
1010 if ((i
= ipcache_get(name
)) == NULL
)
1012 if (i
->status
!= IP_CACHED
)
1015 fullcircle
= ia
->cur
;
1016 while (ia
->bad_mask
[ia
->cur
]) {
1017 if (++ia
->cur
== ia
->count
)
1019 if (ia
->cur
== fullcircle
) { /* All bad, just use next one */
1020 if (++ia
->cur
== ia
->count
)
1027 /* "MarkBad" function must leave the "cur" pointer at the next
1028 * available good address, or the next bad address, in the list.
1029 * This simulates the functionality of RemoveBadAddr() which it
1030 * replaces. Marking, instead of removing, allows bad addresses
1031 * to be retried as a last resort before returning an error to
1035 ipcacheMarkBadAddr(const char *name
, struct in_addr addr
)
1040 if ((i
= ipcache_get(name
)) == NULL
)
1043 for (k
= 0; k
< (int) ia
->count
; k
++) {
1044 if (ia
->in_addrs
[k
].s_addr
== addr
.s_addr
)
1047 if (k
== (int) ia
->count
)
1049 if (!ia
->bad_mask
[k
]) {
1050 ia
->bad_mask
[k
] = TRUE
;
1052 debug(14, 2) ("ipcacheMarkBadAddr: %s [%s]\n",
1053 name
, inet_ntoa(ia
->in_addrs
[k
]));
1054 if (ia
->badcount
!= ia
->count
) {
1055 /* at least one good address left */
1056 i
->expires
= squid_curtime
+ Config
.positiveDnsTtl
;
1057 while (ia
->bad_mask
[ia
->cur
])
1058 if (++ia
->cur
== ia
->count
)
1063 if (++ia
->cur
== ia
->count
)
1068 ipcacheMarkGoodAddr(const char *name
, struct in_addr addr
)
1073 if ((i
= ipcache_get(name
)) == NULL
)
1076 for (k
= 0; k
< (int) ia
->count
; k
++) {
1077 if (ia
->in_addrs
[k
].s_addr
== addr
.s_addr
)
1080 if (k
== (int) ia
->count
)
1082 i
->expires
= squid_curtime
+ Config
.positiveDnsTtl
;
1083 if (ia
->bad_mask
[k
]) {
1084 ia
->bad_mask
[k
] = FALSE
;
1086 i
->expires
= squid_curtime
+ Config
.positiveDnsTtl
;
1087 debug(14, 2) ("ipcacheMarkGoodAddr: %s [%s]\n",
1088 name
, inet_ntoa(ia
->in_addrs
[k
]));
1093 ipcacheFreeMemory(void)
1096 ipcache_entry
**list
;
1099 list
= xcalloc(meta_data
.ipcache_count
, sizeof(ipcache_entry
*));
1100 i
= (ipcache_entry
*) hash_first(ip_table
);
1101 while (i
&& k
< meta_data
.ipcache_count
) {
1104 i
= (ipcache_entry
*) hash_next(ip_table
);
1106 for (j
= 0; j
< k
; j
++) {
1108 safe_free(i
->addrs
.in_addrs
);
1109 safe_free(i
->addrs
.bad_mask
);
1111 safe_free(i
->error_message
);
1115 hashFreeMemory(ip_table
);
1120 ipcacheChangeKey(ipcache_entry
* i
)
1122 static int index
= 0;
1123 LOCAL_ARRAY(char, new_key
, 256);
1124 hash_link
*table_entry
= hash_lookup(ip_table
, i
->name
);
1125 if (table_entry
== NULL
) {
1126 debug(14, 0) ("ipcacheChangeKey: Could not find key '%s'\n", i
->name
);
1129 assert(i
== (ipcache_entry
*) table_entry
);
1130 if (hash_remove_link(ip_table
, table_entry
)) {
1131 debug_trap("ipcacheChangeKey: hash_remove_link() failed\n");
1134 snprintf(new_key
, 256, "%d/", ++index
);
1135 strncat(new_key
, i
->name
, 128);
1136 debug(14, 1) ("ipcacheChangeKey: from '%s' to '%s'\n", i
->name
, new_key
);
1138 i
->name
= xstrdup(new_key
);
1139 ipcache_add_to_hash(i
);
1142 /* call during reconfigure phase to clear out all the
1143 * pending and dispatched reqeusts that got lost */
1145 ipcache_restart(void)
1147 ipcache_entry
*this;
1148 ipcache_entry
*next
;
1149 assert(ip_table
!= NULL
);
1150 while (ipcacheDequeue());
1151 next
= (ipcache_entry
*) hash_first(ip_table
);
1152 while ((this = next
) != NULL
) {
1153 next
= (ipcache_entry
*) hash_next(ip_table
);
1154 if (this->status
== IP_CACHED
)
1156 if (this->status
== IP_NEGATIVE_CACHED
)
1158 /* else its PENDING or DISPATCHED; there are no dnsservers
1159 * running, so abort it */
1160 this->status
= IP_NEGATIVE_CACHED
;
1161 ipcache_release(this);
1163 /* recalculate these while we're at it */
1164 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
1165 (float) Config
.ipcache
.high
) / (float) 100);
1166 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
1167 (float) Config
.ipcache
.low
) / (float) 100);