2 * DEBUG: section 14 IP Cache
3 * AUTHOR: Harvest Derived
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 #include "CacheManager.h"
36 #include "DnsLookupDetails.h"
38 #include "ip/Address.h"
42 #include "mgr/Registration.h"
44 #include "SquidConfig.h"
46 #include "SquidTime.h"
47 #include "StatCounters.h"
52 #include "snmp_core.h"
56 \defgroup IPCacheAPI IP Cache API
58 \section Introduction Introduction
60 * The IP cache is a built-in component of squid providing
61 * Hostname to IP-Number translation functionality and managing
62 * the involved data-structures. Efficiency concerns require
63 * mechanisms that allow non-blocking access to these mappings.
64 * The IP cache usually doesn't block on a request except for
65 * special cases where this is desired (see below).
67 \todo IP Cache should have its own API *.h header file.
71 \defgroup IPCacheInternal IP Cache Internals
73 \todo when IP cache is provided as a class. These sub-groups will be obsolete
74 * for now they are used to seperate the public and private functions.
75 * with the private ones all being in IPCachInternal and public in IPCacheAPI
77 \section InternalOperation Internal Operation
79 * Internally, the execution flow is as follows: On a miss,
80 * ipcache_getnbhostbyname checks whether a request for
81 * this name is already pending, and if positive, it creates
82 * a new entry using ipcacheAddNew with the IP_PENDING
83 * flag set . Then it calls ipcacheAddPending to add a
84 * request to the queue together with data and handler. Else,
85 * ipcache_dnsDispatch() is called to directly create a
86 * DNS query or to ipcacheEnqueue() if all no DNS port
87 * is free. ipcache_call_pending() is called regularly
88 * to walk down the pending list and call handlers. LRU clean-up
89 * is performed through ipcache_purgelru() according to
90 * the ipcache_high threshold.
96 * The data structure used for storing name-address mappings
97 * is a small hashtable (static hash_table *ip_table),
98 * where structures of type ipcache_entry whose most
99 * interesting members are:
104 hash_link hash
; /* must be first */
112 struct timeval request_time
;
114 unsigned short locks
;
116 unsigned int negcached
:1;
117 unsigned int fromhosts
:1;
120 int age() const; ///< time passed since request_time or -1 if unknown
123 /// \ingroup IPCacheInternal
124 static struct _ipcache_stats
{
138 /// \ingroup IPCacheInternal
139 static dlink_list lru_list
;
142 static void stat_ipcache_get(StoreEntry
*);
144 static FREE ipcacheFreeEntry
;
146 static HLPCB ipcacheHandleReply
;
148 static IDNSCB ipcacheHandleReply
;
150 static int ipcacheExpiredEntry(ipcache_entry
*);
152 static int ipcacheParse(ipcache_entry
*, const char *buf
);
154 static int ipcacheParse(ipcache_entry
*, const rfc1035_rr
*, int, const char *error
);
156 static ipcache_entry
*ipcache_get(const char *);
157 static void ipcacheLockEntry(ipcache_entry
*);
158 static void ipcacheStatPrint(ipcache_entry
*, StoreEntry
*);
159 static void ipcacheUnlockEntry(ipcache_entry
*);
160 static void ipcacheRelease(ipcache_entry
*, bool dofree
= true);
162 /// \ingroup IPCacheInternal
163 static ipcache_addrs static_addrs
;
164 /// \ingroup IPCacheInternal
165 static hash_table
*ip_table
= NULL
;
167 /// \ingroup IPCacheInternal
168 static long ipcache_low
= 180;
169 /// \ingroup IPCacheInternal
170 static long ipcache_high
= 200;
172 #if LIBRESOLV_DNS_TTL_HACK
173 extern int _dns_ttl_
;
176 /// \ingroup IPCacheInternal
177 inline int ipcacheCount() { return ip_table
? ip_table
->count
: 0; }
180 ipcache_entry::age() const
182 return request_time
.tv_sec
? tvSubMsec(request_time
, current_time
) : -1;
186 \ingroup IPCacheInternal
188 * removes the given ipcache entry
191 ipcacheRelease(ipcache_entry
* i
, bool dofree
)
194 debugs(14, DBG_CRITICAL
, "ipcacheRelease: Releasing entry with i=<NULL>");
198 if (!i
|| !i
->hash
.key
) {
199 debugs(14, DBG_CRITICAL
, "ipcacheRelease: Releasing entry without hash link!");
203 debugs(14, 3, "ipcacheRelease: Releasing entry for '" << (const char *) i
->hash
.key
<< "'");
205 hash_remove_link(ip_table
, (hash_link
*) i
);
206 dlinkDelete(&i
->lru
, &lru_list
);
211 /// \ingroup IPCacheInternal
212 static ipcache_entry
*
213 ipcache_get(const char *name
)
215 if (ip_table
!= NULL
)
216 return (ipcache_entry
*) hash_lookup(ip_table
, name
);
221 /// \ingroup IPCacheInternal
223 ipcacheExpiredEntry(ipcache_entry
* i
)
225 /* all static entries are locked, so this takes care of them too */
230 if (i
->addrs
.count
== 0)
231 if (0 == i
->flags
.negcached
)
234 if (i
->expires
> squid_curtime
)
240 /// \ingroup IPCacheAPI
242 ipcache_purgelru(void *voidnotused
)
245 dlink_node
*prev
= NULL
;
248 eventAdd("ipcache_purgelru", ipcache_purgelru
, NULL
, 10.0, 1);
250 for (m
= lru_list
.tail
; m
; m
= prev
) {
251 if (ipcacheCount() < ipcache_low
)
256 i
= (ipcache_entry
*)m
->data
;
266 debugs(14, 9, "ipcache_purgelru: removed " << removed
<< " entries");
270 \ingroup IPCacheInternal
272 * purges entries added from /etc/hosts (or whatever).
275 purge_entries_fromhosts(void)
277 dlink_node
*m
= lru_list
.head
;
278 ipcache_entry
*i
= NULL
, *t
;
281 if (i
!= NULL
) { /* need to delay deletion */
282 ipcacheRelease(i
); /* we just override locks */
286 t
= (ipcache_entry
*)m
->data
;
288 if (t
->flags
.fromhosts
)
299 \ingroup IPCacheInternal
301 * create blank ipcache_entry
303 static ipcache_entry
*
304 ipcacheCreateEntry(const char *name
)
306 static ipcache_entry
*i
;
307 i
= (ipcache_entry
*)memAllocate(MEM_IPCACHE_ENTRY
);
308 i
->hash
.key
= xstrdup(name
);
309 Tolower(static_cast<char*>(i
->hash
.key
));
310 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
314 /// \ingroup IPCacheInternal
316 ipcacheAddEntry(ipcache_entry
* i
)
318 hash_link
*e
= (hash_link
*)hash_lookup(ip_table
, i
->hash
.key
);
321 /* avoid colission */
322 ipcache_entry
*q
= (ipcache_entry
*) e
;
326 hash_join(ip_table
, &i
->hash
);
327 dlinkAdd(i
, &i
->lru
, &lru_list
);
328 i
->lastref
= squid_curtime
;
332 \ingroup IPCacheInternal
334 * walks down the pending list, calling handlers
337 ipcacheCallback(ipcache_entry
*i
, int wait
)
339 IPH
*callback
= i
->handler
;
341 i
->lastref
= squid_curtime
;
348 callback
= i
->handler
;
352 if (cbdataReferenceValidDone(i
->handlerData
, &cbdata
)) {
353 const DnsLookupDetails
details(i
->error_message
, wait
);
354 callback((i
->addrs
.count
? &i
->addrs
: NULL
), details
, cbdata
);
357 ipcacheUnlockEntry(i
);
360 /// \ingroup IPCacheAPI
363 ipcacheParse(ipcache_entry
*i
, const char *inbuf
)
365 LOCAL_ARRAY(char, buf
, DNS_INBUF_SZ
);
370 const char *name
= (const char *)i
->hash
.key
;
371 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
372 i
->flags
.negcached
= 1;
373 safe_free(i
->addrs
.in_addrs
);
374 safe_free(i
->addrs
.bad_mask
);
375 safe_free(i
->error_message
);
379 debugs(14, DBG_IMPORTANT
, "ipcacheParse: Got <NULL> reply");
380 i
->error_message
= xstrdup("Internal Error");
384 xstrncpy(buf
, inbuf
, DNS_INBUF_SZ
);
385 debugs(14, 5, "ipcacheParse: parsing: {" << buf
<< "}");
386 token
= strtok(buf
, w_space
);
389 debugs(14, DBG_IMPORTANT
, "ipcacheParse: expecting result, got '" << inbuf
<< "'");
391 i
->error_message
= xstrdup("Internal Error");
395 if (0 == strcmp(token
, "$fail")) {
396 token
= strtok(NULL
, "\n");
397 assert(NULL
!= token
);
398 i
->error_message
= xstrdup(token
);
402 if (0 != strcmp(token
, "$addr")) {
403 debugs(14, DBG_IMPORTANT
, "ipcacheParse: expecting '$addr', got '" << inbuf
<< "' in response to '" << name
<< "'");
405 i
->error_message
= xstrdup("Internal Error");
409 token
= strtok(NULL
, w_space
);
412 debugs(14, DBG_IMPORTANT
, "ipcacheParse: expecting TTL, got '" << inbuf
<< "' in response to '" << name
<< "'");
414 i
->error_message
= xstrdup("Internal Error");
420 while (NULL
!= (token
= strtok(NULL
, w_space
))) {
430 i
->addrs
.in_addrs
= static_cast<Ip::Address
*>(xcalloc(ipcount
, sizeof(Ip::Address
)));
431 for (int l
= 0; l
< ipcount
; ++l
)
432 i
->addrs
.in_addrs
[l
].SetEmpty(); // perform same init actions as constructor would.
433 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(ipcount
, sizeof(unsigned char));
434 memset(i
->addrs
.bad_mask
, 0, sizeof(unsigned char) * ipcount
);
436 for (j
= 0, k
= 0; k
< ipcount
; ++k
) {
437 if ( i
->addrs
.in_addrs
[j
] = A
[k
] )
440 debugs(14, DBG_IMPORTANT
, "ipcacheParse: Invalid IP address '" << A
[k
] << "' in response to '" << name
<< "'");
443 i
->addrs
.count
= (unsigned char) j
;
446 if (i
->addrs
.count
<= 0) {
447 debugs(14, DBG_IMPORTANT
, "ipcacheParse: No addresses in response to '" << name
<< "'");
451 if (ttl
== 0 || ttl
> Config
.positiveDnsTtl
)
452 ttl
= Config
.positiveDnsTtl
;
454 if (ttl
< Config
.negativeDnsTtl
)
455 ttl
= Config
.negativeDnsTtl
;
457 i
->expires
= squid_curtime
+ ttl
;
459 i
->flags
.negcached
= 0;
461 return i
->addrs
.count
;
466 ipcacheParse(ipcache_entry
*i
, const rfc1035_rr
* answers
, int nr
, const char *error_message
)
472 const char *name
= (const char *)i
->hash
.key
;
475 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
476 i
->flags
.negcached
= 1;
477 safe_free(i
->addrs
.in_addrs
);
478 assert(i
->addrs
.in_addrs
== NULL
);
479 safe_free(i
->addrs
.bad_mask
);
480 assert(i
->addrs
.bad_mask
== NULL
);
481 safe_free(i
->error_message
);
482 assert(i
->error_message
== NULL
);
486 debugs(14, 3, "ipcacheParse: Lookup failed '" << error_message
<< "' for '" << (const char *)i
->hash
.key
<< "'");
487 i
->error_message
= xstrdup(error_message
);
492 debugs(14, 3, "ipcacheParse: No DNS records in response to '" << name
<< "'");
493 i
->error_message
= xstrdup("No DNS records");
497 debugs(14, 3, "ipcacheParse: " << nr
<< " answers for '" << name
<< "'");
500 for (k
= 0; k
< nr
; ++k
) {
502 if (Ip::EnableIpv6
&& answers
[k
].type
== RFC1035_TYPE_AAAA
) {
503 if (answers
[k
].rdlength
!= sizeof(struct in6_addr
)) {
504 debugs(14, DBG_IMPORTANT
, "ipcacheParse: Invalid IPv6 address in response to '" << name
<< "'");
508 ++IpcacheStats
.rr_aaaa
;
512 if (answers
[k
].type
== RFC1035_TYPE_A
) {
513 if (answers
[k
].rdlength
!= sizeof(struct in_addr
)) {
514 debugs(14, DBG_IMPORTANT
, "ipcacheParse: Invalid IPv4 address in response to '" << name
<< "'");
522 /* With A and AAAA, the CNAME does not necessarily come with additional records to use. */
523 if (answers
[k
].type
== RFC1035_TYPE_CNAME
) {
525 ++IpcacheStats
.rr_cname
;
529 // otherwise its an unknown RR. debug at level 9 since we usually want to ignore these and they are common.
530 debugs(14, 9, HERE
<< "Unknown RR type received: type=" << answers
[k
].type
<< " starting at " << &(answers
[k
]) );
533 debugs(14, DBG_IMPORTANT
, "ipcacheParse: No Address records in response to '" << name
<< "'");
534 i
->error_message
= xstrdup("No Address records");
536 ++IpcacheStats
.cname_only
;
540 i
->addrs
.in_addrs
= static_cast<Ip::Address
*>(xcalloc(na
, sizeof(Ip::Address
)));
541 for (int l
= 0; l
< na
; ++l
)
542 i
->addrs
.in_addrs
[l
].SetEmpty(); // perform same init actions as constructor would.
543 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(na
, sizeof(unsigned char));
545 for (j
= 0, k
= 0; k
< nr
; ++k
) {
547 if (answers
[k
].type
== RFC1035_TYPE_A
) {
548 if (answers
[k
].rdlength
!= sizeof(struct in_addr
))
552 memcpy(&temp
, answers
[k
].rdata
, sizeof(struct in_addr
));
553 i
->addrs
.in_addrs
[j
] = temp
;
555 debugs(14, 3, "ipcacheParse: " << name
<< " #" << j
<< " " << i
->addrs
.in_addrs
[j
]);
558 } else if (Ip::EnableIpv6
&& answers
[k
].type
== RFC1035_TYPE_AAAA
) {
559 if (answers
[k
].rdlength
!= sizeof(struct in6_addr
))
562 struct in6_addr temp
;
563 memcpy(&temp
, answers
[k
].rdata
, sizeof(struct in6_addr
));
564 i
->addrs
.in_addrs
[j
] = temp
;
566 debugs(14, 3, "ipcacheParse: " << name
<< " #" << j
<< " " << i
->addrs
.in_addrs
[j
] );
569 if (ttl
== 0 || (int) answers
[k
].ttl
< ttl
)
570 ttl
= answers
[k
].ttl
;
576 i
->addrs
.count
= (unsigned char) na
;
578 i
->addrs
.count
= 255;
580 if (ttl
> Config
.positiveDnsTtl
)
581 ttl
= Config
.positiveDnsTtl
;
583 if (ttl
< Config
.negativeDnsTtl
)
584 ttl
= Config
.negativeDnsTtl
;
586 i
->expires
= squid_curtime
+ ttl
;
588 i
->flags
.negcached
= 0;
590 return i
->addrs
.count
;
595 /// \ingroup IPCacheInternal
598 ipcacheHandleReply(void *data
, char *reply
)
600 ipcacheHandleReply(void *data
, const rfc1035_rr
* answers
, int na
, const char *error_message
)
604 static_cast<generic_cbdata
*>(data
)->unwrap(&i
);
605 ++IpcacheStats
.replies
;
606 const int age
= i
->age();
607 statCounter
.dns
.svcTime
.count(age
);
610 ipcacheParse(i
, reply
);
613 int done
= ipcacheParse(i
, answers
, na
, error_message
);
615 /* If we have not produced either IPs or Error immediately, wait for recursion to finish. */
616 if (done
!= 0 || error_message
!= NULL
)
621 ipcacheCallback(i
, age
);
628 \param name Host to resolve.
629 \param handler Pointer to the function to be called when the reply
630 * from the IP cache (or the DNS if the IP cache misses)
631 \param handlerData Information that is passed to the handler and does not affect the IP cache.
633 * XXX: on hits and some errors, the handler is called immediately instead
634 * of scheduling an async call. This reentrant behavior means that the
635 * user job must be extra careful after calling ipcache_nbgethostbyname,
636 * especially if the handler destroys the job. Moreover, the job has
637 * no way of knowing whether the reentrant call happened.
638 * Comm::Connection setup usually protects the job by scheduling an async call,
639 * but some user code calls ipcache_nbgethostbyname directly.
642 ipcache_nbgethostbyname(const char *name
, IPH
* handler
, void *handlerData
)
644 ipcache_entry
*i
= NULL
;
645 const ipcache_addrs
*addrs
= NULL
;
647 debugs(14, 4, "ipcache_nbgethostbyname: Name '" << name
<< "'.");
648 ++IpcacheStats
.requests
;
650 if (name
== NULL
|| name
[0] == '\0') {
651 debugs(14, 4, "ipcache_nbgethostbyname: Invalid name!");
652 ++IpcacheStats
.invalid
;
653 const DnsLookupDetails
details("Invalid hostname", -1); // error, no lookup
655 handler(NULL
, details
, handlerData
);
659 if ((addrs
= ipcacheCheckNumeric(name
))) {
660 debugs(14, 4, "ipcache_nbgethostbyname: BYPASS for '" << name
<< "' (already numeric)");
661 ++IpcacheStats
.numeric_hits
;
662 const DnsLookupDetails
details(NULL
, -1); // no error, no lookup
664 handler(addrs
, details
, handlerData
);
668 i
= ipcache_get(name
);
673 } else if (ipcacheExpiredEntry(i
)) {
674 /* hit, but expired -- bummer */
679 debugs(14, 4, "ipcache_nbgethostbyname: HIT for '" << name
<< "'");
681 if (i
->flags
.negcached
)
682 ++IpcacheStats
.negative_hits
;
686 i
->handler
= handler
;
688 i
->handlerData
= cbdataReference(handlerData
);
690 ipcacheCallback(i
, -1); // no lookup
695 debugs(14, 5, "ipcache_nbgethostbyname: MISS for '" << name
<< "'");
696 ++IpcacheStats
.misses
;
697 i
= ipcacheCreateEntry(name
);
698 i
->handler
= handler
;
699 i
->handlerData
= cbdataReference(handlerData
);
700 i
->request_time
= current_time
;
701 c
= new generic_cbdata(i
);
703 dnsSubmit(hashKeyStr(&i
->hash
), ipcacheHandleReply
, c
);
705 idnsALookup(hashKeyStr(&i
->hash
), ipcacheHandleReply
, c
);
709 /// \ingroup IPCacheInternal
711 ipcacheRegisterWithCacheManager(void)
713 Mgr::RegisterAction("ipcache",
714 "IP Cache Stats and Contents",
715 stat_ipcache_get
, 0, 1);
721 * Initialize the ipcache.
722 * Is called from mainInitialize() after disk initialization
723 * and prior to the reverse FQDNCache initialization
729 debugs(14, DBG_IMPORTANT
, "Initializing IP Cache...");
730 memset(&IpcacheStats
, '\0', sizeof(IpcacheStats
));
731 memset(&lru_list
, '\0', sizeof(lru_list
));
732 memset(&static_addrs
, '\0', sizeof(ipcache_addrs
));
734 static_addrs
.in_addrs
= static_cast<Ip::Address
*>(xcalloc(1, sizeof(Ip::Address
)));
735 static_addrs
.in_addrs
->SetEmpty(); // properly setup the Ip::Address!
736 static_addrs
.bad_mask
= (unsigned char *)xcalloc(1, sizeof(unsigned char));
737 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
738 (float) Config
.ipcache
.high
) / (float) 100);
739 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
740 (float) Config
.ipcache
.low
) / (float) 100);
741 n
= hashPrime(ipcache_high
/ 4);
742 ip_table
= hash_create((HASHCMP
*) strcmp
, n
, hash4
);
743 memDataInit(MEM_IPCACHE_ENTRY
, "ipcache_entry", sizeof(ipcache_entry
), 0);
745 ipcacheRegisterWithCacheManager();
751 * Is different from ipcache_nbgethostbyname in that it only checks
752 * if an entry exists in the cache and does not by default contact the DNS,
753 * unless this is requested, by setting the flags.
755 \param name Host name to resolve.
756 \param flags Default is NULL, set to IP_LOOKUP_IF_MISS
757 * to explicitly perform DNS lookups.
759 \retval NULL An error occured during lookup
760 \retval NULL No results available in cache and no lookup specified
761 \retval * Pointer to the ipcahce_addrs structure containing the lookup results
763 const ipcache_addrs
*
764 ipcache_gethostbyname(const char *name
, int flags
)
766 ipcache_entry
*i
= NULL
;
767 ipcache_addrs
*addrs
;
769 debugs(14, 3, "ipcache_gethostbyname: '" << name
<< "', flags=" << std::hex
<< flags
);
770 ++IpcacheStats
.requests
;
771 i
= ipcache_get(name
);
775 } else if (ipcacheExpiredEntry(i
)) {
778 } else if (i
->flags
.negcached
) {
779 ++IpcacheStats
.negative_hits
;
780 // ignore i->error_message: the caller just checks IP cache presence
784 i
->lastref
= squid_curtime
;
785 // ignore i->error_message: the caller just checks IP cache presence
789 /* no entry [any more] */
791 if ((addrs
= ipcacheCheckNumeric(name
))) {
792 ++IpcacheStats
.numeric_hits
;
796 ++IpcacheStats
.misses
;
798 if (flags
& IP_LOOKUP_IF_MISS
)
799 ipcache_nbgethostbyname(name
, NULL
, NULL
);
804 /// \ingroup IPCacheInternal
806 ipcacheStatPrint(ipcache_entry
* i
, StoreEntry
* sentry
)
809 char buf
[MAX_IPSTRLEN
];
812 debugs(14, DBG_CRITICAL
, HERE
<< "CRITICAL: sentry is NULL!");
817 debugs(14, DBG_CRITICAL
, HERE
<< "CRITICAL: ipcache_entry is NULL!");
818 storeAppendPrintf(sentry
, "CRITICAL ERROR\n");
822 int count
= i
->addrs
.count
;
824 storeAppendPrintf(sentry
, " %-32.32s %c%c %6d %6d %2d(%2d)",
825 hashKeyStr(&i
->hash
),
826 i
->flags
.fromhosts
? 'H' : ' ',
827 i
->flags
.negcached
? 'N' : ' ',
828 (int) (squid_curtime
- i
->lastref
),
829 (int) ((i
->flags
.fromhosts
? -1 : i
->expires
- squid_curtime
)),
830 (int) i
->addrs
.count
,
831 (int) i
->addrs
.badcount
);
834 * Negative-cached entries have no IPs listed. */
835 if (i
->flags
.negcached
) {
836 storeAppendPrintf(sentry
, "\n");
841 * Cached entries have IPs listed with a BNF of: ip-address '-' ('OK'|'BAD') */
842 for (k
= 0; k
< count
; ++k
) {
843 /* Display tidy-up: IPv6 are so big make the list vertical */
845 storeAppendPrintf(sentry
, " %45.45s-%3s\n",
846 i
->addrs
.in_addrs
[k
].NtoA(buf
,MAX_IPSTRLEN
),
847 i
->addrs
.bad_mask
[k
] ? "BAD" : "OK ");
849 storeAppendPrintf(sentry
, "%s %45.45s-%3s\n",
850 " ", /* blank-space indenting IP list */
851 i
->addrs
.in_addrs
[k
].NtoA(buf
,MAX_IPSTRLEN
),
852 i
->addrs
.bad_mask
[k
] ? "BAD" : "OK ");
857 \ingroup IPCacheInternal
859 * process objects list
862 stat_ipcache_get(StoreEntry
* sentry
)
865 assert(ip_table
!= NULL
);
866 storeAppendPrintf(sentry
, "IP Cache Statistics:\n");
867 storeAppendPrintf(sentry
, "IPcache Entries In Use: %d\n",
868 memInUse(MEM_IPCACHE_ENTRY
));
869 storeAppendPrintf(sentry
, "IPcache Entries Cached: %d\n",
871 storeAppendPrintf(sentry
, "IPcache Requests: %d\n",
872 IpcacheStats
.requests
);
873 storeAppendPrintf(sentry
, "IPcache Hits: %d\n",
875 storeAppendPrintf(sentry
, "IPcache Negative Hits: %d\n",
876 IpcacheStats
.negative_hits
);
877 storeAppendPrintf(sentry
, "IPcache Numeric Hits: %d\n",
878 IpcacheStats
.numeric_hits
);
879 storeAppendPrintf(sentry
, "IPcache Misses: %d\n",
880 IpcacheStats
.misses
);
881 storeAppendPrintf(sentry
, "IPcache Retrieved A: %d\n",
883 storeAppendPrintf(sentry
, "IPcache Retrieved AAAA: %d\n",
884 IpcacheStats
.rr_aaaa
);
885 storeAppendPrintf(sentry
, "IPcache Retrieved CNAME: %d\n",
886 IpcacheStats
.rr_cname
);
887 storeAppendPrintf(sentry
, "IPcache CNAME-Only Response: %d\n",
888 IpcacheStats
.cname_only
);
889 storeAppendPrintf(sentry
, "IPcache Invalid Request: %d\n",
890 IpcacheStats
.invalid
);
891 storeAppendPrintf(sentry
, "\n\n");
892 storeAppendPrintf(sentry
, "IP Cache Contents:\n\n");
893 storeAppendPrintf(sentry
, " %-31.31s %3s %6s %6s %4s\n",
900 for (m
= lru_list
.head
; m
; m
= m
->next
) {
901 assert( m
->next
!= m
);
902 ipcacheStatPrint((ipcache_entry
*)m
->data
, sentry
);
906 /// \ingroup IPCacheAPI
908 ipcacheInvalidate(const char *name
)
912 if ((i
= ipcache_get(name
)) == NULL
)
915 i
->expires
= squid_curtime
;
918 * NOTE, don't call ipcacheRelease here because we might be here due
919 * to a thread started from a callback.
923 /// \ingroup IPCacheAPI
925 ipcacheInvalidateNegative(const char *name
)
929 if ((i
= ipcache_get(name
)) == NULL
)
932 if (i
->flags
.negcached
)
933 i
->expires
= squid_curtime
;
936 * NOTE, don't call ipcacheRelease here because we might be here due
937 * to a thread started from a callback.
941 /// \ingroup IPCacheAPI
943 ipcacheCheckNumeric(const char *name
)
946 /* check if it's already a IP address in text form. */
948 /* it may be IPv6-wrapped */
949 if (name
[0] == '[') {
950 char *tmp
= xstrdup(&name
[1]);
951 tmp
[strlen(tmp
)-1] = '\0';
957 } else if (!(ip
= name
))
960 debugs(14, 4, "ipcacheCheckNumeric: HIT_BYPASS for '" << name
<< "' == " << ip
);
962 static_addrs
.count
= 1;
964 static_addrs
.cur
= 0;
966 static_addrs
.in_addrs
[0] = ip
;
968 static_addrs
.bad_mask
[0] = FALSE
;
970 static_addrs
.badcount
= 0;
972 return &static_addrs
;
975 /// \ingroup IPCacheInternal
977 ipcacheLockEntry(ipcache_entry
* i
)
979 if (i
->locks
++ == 0) {
980 dlinkDelete(&i
->lru
, &lru_list
);
981 dlinkAdd(i
, &i
->lru
, &lru_list
);
985 /// \ingroup IPCacheInternal
987 ipcacheUnlockEntry(ipcache_entry
* i
)
990 debugs(14, DBG_IMPORTANT
, "WARNING: ipcacheEntry unlocked with no lock! locks=" << i
->locks
);
996 if (ipcacheExpiredEntry(i
))
1000 /// \ingroup IPCacheAPI
1002 ipcacheCycleAddr(const char *name
, ipcache_addrs
* ia
)
1009 if ((i
= ipcache_get(name
)) == NULL
)
1012 if (i
->flags
.negcached
)
1018 for (k
= 0; k
< ia
->count
; ++k
) {
1019 if (++ia
->cur
== ia
->count
)
1022 if (!ia
->bad_mask
[ia
->cur
])
1026 if (k
== ia
->count
) {
1027 /* All bad, reset to All good */
1028 debugs(14, 3, "ipcacheCycleAddr: Changing ALL " << name
<< " addrs from BAD to OK");
1030 for (k
= 0; k
< ia
->count
; ++k
)
1031 ia
->bad_mask
[k
] = 0;
1038 /* NP: zero-based so we increase the human-readable number of our position */
1039 debugs(14, 3, "ipcacheCycleAddr: " << name
<< " now at " << ia
->in_addrs
[ia
->cur
] << " (" << (ia
->cur
+1) << " of " << ia
->count
<< ")");
1045 \param name domain name to have an IP marked bad
1046 \param addr specific addres to be marked bad
1049 ipcacheMarkBadAddr(const char *name
, const Ip::Address
&addr
)
1055 /** Does nothing if the domain name does not exist. */
1056 if ((i
= ipcache_get(name
)) == NULL
)
1061 for (k
= 0; k
< (int) ia
->count
; ++k
) {
1062 if (addr
== ia
->in_addrs
[k
] )
1066 /** Does nothing if the IP does not exist for the doamin. */
1067 if (k
== (int) ia
->count
)
1070 /** Marks the given address as BAD */
1071 if (!ia
->bad_mask
[k
]) {
1072 ia
->bad_mask
[k
] = TRUE
;
1074 i
->expires
= min(squid_curtime
+ max((time_t)60, Config
.negativeDnsTtl
), i
->expires
);
1075 debugs(14, 2, "ipcacheMarkBadAddr: " << name
<< " " << addr
);
1078 /** then calls ipcacheCycleAddr() to advance the current pointer to the next OK address. */
1079 ipcacheCycleAddr(name
, ia
);
1082 /// \ingroup IPCacheAPI
1084 ipcacheMarkAllGood(const char *name
)
1090 if ((i
= ipcache_get(name
)) == NULL
)
1095 /* All bad, reset to All good */
1096 debugs(14, 3, "ipcacheMarkAllGood: Changing ALL " << name
<< " addrs to OK (" << ia
->badcount
<< "/" << ia
->count
<< " bad)");
1098 for (k
= 0; k
< ia
->count
; ++k
)
1099 ia
->bad_mask
[k
] = 0;
1104 /// \ingroup IPCacheAPI
1106 ipcacheMarkGoodAddr(const char *name
, const Ip::Address
&addr
)
1112 if ((i
= ipcache_get(name
)) == NULL
)
1117 for (k
= 0; k
< (int) ia
->count
; ++k
) {
1118 if (addr
== ia
->in_addrs
[k
])
1122 if (k
== (int) ia
->count
) /* not found */
1125 if (!ia
->bad_mask
[k
]) /* already OK */
1128 ia
->bad_mask
[k
] = FALSE
;
1132 debugs(14, 2, "ipcacheMarkGoodAddr: " << name
<< " " << addr
);
1135 /// \ingroup IPCacheInternal
1137 ipcacheFreeEntry(void *data
)
1139 ipcache_entry
*i
= (ipcache_entry
*)data
;
1140 safe_free(i
->addrs
.in_addrs
);
1141 safe_free(i
->addrs
.bad_mask
);
1142 safe_free(i
->hash
.key
);
1143 safe_free(i
->error_message
);
1144 memFree(i
, MEM_IPCACHE_ENTRY
);
1147 /// \ingroup IPCacheAPI
1149 ipcacheFreeMemory(void)
1151 hashFreeItems(ip_table
, ipcacheFreeEntry
);
1152 hashFreeMemory(ip_table
);
1159 * Recalculate IP cache size upon reconfigure.
1160 * Is called to clear the IPCache's data structures,
1161 * cancel all pending requests.
1164 ipcache_restart(void)
1166 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
1167 (float) Config
.ipcache
.high
) / (float) 100);
1168 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
1169 (float) Config
.ipcache
.low
) / (float) 100);
1170 purge_entries_fromhosts();
1176 * Adds a "static" entry from /etc/hosts
1178 \param name Hostname to be linked with IP
1179 \param ipaddr IP Address to be cached.
1182 \retval 1 IP address is invalid or other error.
1185 ipcacheAddEntryFromHosts(const char *name
, const char *ipaddr
)
1191 if (!(ip
= ipaddr
)) {
1192 if (strchr(ipaddr
, ':') && strspn(ipaddr
, "0123456789abcdefABCDEF:") == strlen(ipaddr
)) {
1193 debugs(14, 3, "ipcacheAddEntryFromHosts: Skipping IPv6 address '" << ipaddr
<< "'");
1195 debugs(14, DBG_IMPORTANT
, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr
<< "'");
1201 if ((i
= ipcache_get(name
))) {
1202 if (1 == i
->flags
.fromhosts
) {
1203 ipcacheUnlockEntry(i
);
1204 } else if (i
->locks
> 0) {
1205 debugs(14, DBG_IMPORTANT
, "ipcacheAddEntryFromHosts: can't add static entry for locked name '" << name
<< "'");
1212 i
= ipcacheCreateEntry(name
);
1215 i
->addrs
.badcount
= 0;
1217 i
->addrs
.in_addrs
= static_cast<Ip::Address
*>(xcalloc(1, sizeof(Ip::Address
)));
1218 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(1, sizeof(unsigned char));
1219 i
->addrs
.in_addrs
[0] = ip
;
1220 i
->addrs
.bad_mask
[0] = FALSE
;
1221 i
->flags
.fromhosts
= 1;
1223 ipcacheLockEntry(i
);
1231 * The function to return the ip cache statistics to via SNMP
1234 snmp_netIpFn(variable_list
* Var
, snint
* ErrP
)
1236 variable_list
*Answer
= NULL
;
1238 debugs(49, 5, "snmp_netIpFn: Processing request:" << snmpDebugOid(Var
->name
, Var
->name_length
, tmp
));
1239 *ErrP
= SNMP_ERR_NOERROR
;
1241 switch (Var
->name
[LEN_SQ_NET
+ 1]) {
1244 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1250 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1251 IpcacheStats
.requests
,
1256 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1262 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1268 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1269 IpcacheStats
.negative_hits
,
1274 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1275 IpcacheStats
.misses
,
1280 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1286 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1292 *ErrP
= SNMP_ERR_NOSUCHNAME
;
1293 snmp_var_free(Answer
);
1300 #endif /*SQUID_SNMP */