2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 14 IP Cache */
12 #include "CacheManager.h"
15 #include "dns/LookupDetails.h"
16 #include "dns/rfc3596.h"
18 #include "ip/Address.h"
21 #include "mgr/Registration.h"
22 #include "SquidConfig.h"
23 #include "SquidTime.h"
24 #include "StatCounters.h"
30 #include "snmp_core.h"
34 \defgroup IPCacheAPI IP Cache API
36 \section Introduction Introduction
38 * The IP cache is a built-in component of squid providing
39 * Hostname to IP-Number translation functionality and managing
40 * the involved data-structures. Efficiency concerns require
41 * mechanisms that allow non-blocking access to these mappings.
42 * The IP cache usually doesn't block on a request except for
43 * special cases where this is desired (see below).
45 \todo IP Cache should have its own API *.h header file.
49 \defgroup IPCacheInternal IP Cache Internals
51 \todo when IP cache is provided as a class. These sub-groups will be obsolete
52 * for now they are used to seperate the public and private functions.
53 * with the private ones all being in IPCachInternal and public in IPCacheAPI
55 \section InternalOperation Internal Operation
57 * Internally, the execution flow is as follows: On a miss,
58 * ipcache_getnbhostbyname checks whether a request for
59 * this name is already pending, and if positive, it creates
60 * a new entry using ipcacheAddNew with the IP_PENDING
61 * flag set . Then it calls ipcacheAddPending to add a
62 * request to the queue together with data and handler. Else,
63 * ipcache_dnsDispatch() is called to directly create a
64 * DNS query or to ipcacheEnqueue() if all no DNS port
65 * is free. ipcache_call_pending() is called regularly
66 * to walk down the pending list and call handlers. LRU clean-up
67 * is performed through ipcache_purgelru() according to
68 * the ipcache_high threshold.
74 * The data structure used for storing name-address mappings
75 * is a small hashtable (static hash_table *ip_table),
76 * where structures of type ipcache_entry whose most
77 * interesting members are:
81 MEMPROXY_CLASS(ipcache_entry
);
84 ipcache_entry(const char *);
87 hash_link hash
; /* must be first */
95 struct timeval request_time
;
99 Flags() : negcached(false), fromhosts(false) {}
105 int age() const; ///< time passed since request_time or -1 if unknown
108 /// \ingroup IPCacheInternal
109 static struct _ipcache_stats
{
123 /// \ingroup IPCacheInternal
124 static dlink_list lru_list
;
127 static void stat_ipcache_get(StoreEntry
*);
129 static FREE ipcacheFreeEntry
;
130 static IDNSCB ipcacheHandleReply
;
131 static int ipcacheExpiredEntry(ipcache_entry
*);
132 static ipcache_entry
*ipcache_get(const char *);
133 static void ipcacheLockEntry(ipcache_entry
*);
134 static void ipcacheStatPrint(ipcache_entry
*, StoreEntry
*);
135 static void ipcacheUnlockEntry(ipcache_entry
*);
136 static void ipcacheRelease(ipcache_entry
*, bool dofree
= true);
138 /// \ingroup IPCacheInternal
139 static ipcache_addrs static_addrs
;
140 /// \ingroup IPCacheInternal
141 static hash_table
*ip_table
= NULL
;
143 /// \ingroup IPCacheInternal
144 static long ipcache_low
= 180;
145 /// \ingroup IPCacheInternal
146 static long ipcache_high
= 200;
148 #if LIBRESOLV_DNS_TTL_HACK
149 extern int _dns_ttl_
;
152 /// \ingroup IPCacheInternal
153 inline int ipcacheCount() { return ip_table
? ip_table
->count
: 0; }
156 ipcache_entry::age() const
158 return request_time
.tv_sec
? tvSubMsec(request_time
, current_time
) : -1;
162 \ingroup IPCacheInternal
164 * removes the given ipcache entry
167 ipcacheRelease(ipcache_entry
* i
, bool dofree
)
170 debugs(14, DBG_CRITICAL
, "ipcacheRelease: Releasing entry with i=<NULL>");
174 if (!i
|| !i
->hash
.key
) {
175 debugs(14, DBG_CRITICAL
, "ipcacheRelease: Releasing entry without hash link!");
179 debugs(14, 3, "ipcacheRelease: Releasing entry for '" << (const char *) i
->hash
.key
<< "'");
181 hash_remove_link(ip_table
, (hash_link
*) i
);
182 dlinkDelete(&i
->lru
, &lru_list
);
187 /// \ingroup IPCacheInternal
188 static ipcache_entry
*
189 ipcache_get(const char *name
)
191 if (ip_table
!= NULL
)
192 return (ipcache_entry
*) hash_lookup(ip_table
, name
);
197 /// \ingroup IPCacheInternal
199 ipcacheExpiredEntry(ipcache_entry
* i
)
201 /* all static entries are locked, so this takes care of them too */
206 if (i
->addrs
.count
== 0)
207 if (0 == i
->flags
.negcached
)
210 if (i
->expires
> squid_curtime
)
216 /// \ingroup IPCacheAPI
218 ipcache_purgelru(void *)
221 dlink_node
*prev
= NULL
;
224 eventAdd("ipcache_purgelru", ipcache_purgelru
, NULL
, 10.0, 1);
226 for (m
= lru_list
.tail
; m
; m
= prev
) {
227 if (ipcacheCount() < ipcache_low
)
232 i
= (ipcache_entry
*)m
->data
;
242 debugs(14, 9, "ipcache_purgelru: removed " << removed
<< " entries");
246 \ingroup IPCacheInternal
248 * purges entries added from /etc/hosts (or whatever).
251 purge_entries_fromhosts(void)
253 dlink_node
*m
= lru_list
.head
;
254 ipcache_entry
*i
= NULL
, *t
;
257 if (i
!= NULL
) { /* need to delay deletion */
258 ipcacheRelease(i
); /* we just override locks */
262 t
= (ipcache_entry
*)m
->data
;
264 if (t
->flags
.fromhosts
)
274 ipcache_entry::ipcache_entry(const char *name
) :
278 handlerData(nullptr),
279 error_message(nullptr),
280 locks(0) // XXX: use Lock type ?
282 hash
.key
= xstrdup(name
);
283 Tolower(static_cast<char*>(hash
.key
));
284 expires
= squid_curtime
+ Config
.negativeDnsTtl
;
286 memset(&request_time
, 0, sizeof(request_time
));
289 /// \ingroup IPCacheInternal
291 ipcacheAddEntry(ipcache_entry
* i
)
293 hash_link
*e
= (hash_link
*)hash_lookup(ip_table
, i
->hash
.key
);
296 /* avoid colission */
297 ipcache_entry
*q
= (ipcache_entry
*) e
;
301 hash_join(ip_table
, &i
->hash
);
302 dlinkAdd(i
, &i
->lru
, &lru_list
);
303 i
->lastref
= squid_curtime
;
307 \ingroup IPCacheInternal
309 * walks down the pending list, calling handlers
312 ipcacheCallback(ipcache_entry
*i
, int wait
)
314 IPH
*callback
= i
->handler
;
316 i
->lastref
= squid_curtime
;
323 callback
= i
->handler
;
327 if (cbdataReferenceValidDone(i
->handlerData
, &cbdata
)) {
328 const Dns::LookupDetails
details(i
->error_message
, wait
);
329 callback((i
->addrs
.count
? &i
->addrs
: NULL
), details
, cbdata
);
332 ipcacheUnlockEntry(i
);
336 ipcacheParse(ipcache_entry
*i
, const rfc1035_rr
* answers
, int nr
, const char *error_message
)
342 const char *name
= (const char *)i
->hash
.key
;
345 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
346 i
->flags
.negcached
= true;
347 safe_free(i
->addrs
.in_addrs
);
348 assert(i
->addrs
.in_addrs
== NULL
);
349 safe_free(i
->addrs
.bad_mask
);
350 assert(i
->addrs
.bad_mask
== NULL
);
351 safe_free(i
->error_message
);
352 assert(i
->error_message
== NULL
);
356 debugs(14, 3, "Lookup failed '" << error_message
<< "' for '" << (const char *)i
->hash
.key
<< "'");
357 i
->error_message
= xstrdup(error_message
);
362 debugs(14, 3, "No DNS records in response to '" << name
<< "'");
363 i
->error_message
= xstrdup("No DNS records");
367 debugs(14, 3, nr
<< " answers for '" << name
<< "'");
370 for (k
= 0; k
< nr
; ++k
) {
372 if (Ip::EnableIpv6
&& answers
[k
].type
== RFC1035_TYPE_AAAA
) {
373 if (answers
[k
].rdlength
!= sizeof(struct in6_addr
)) {
374 debugs(14, DBG_IMPORTANT
, MYNAME
<< "Invalid IPv6 address in response to '" << name
<< "'");
378 ++IpcacheStats
.rr_aaaa
;
382 if (answers
[k
].type
== RFC1035_TYPE_A
) {
383 if (answers
[k
].rdlength
!= sizeof(struct in_addr
)) {
384 debugs(14, DBG_IMPORTANT
, MYNAME
<< "Invalid IPv4 address in response to '" << name
<< "'");
392 /* With A and AAAA, the CNAME does not necessarily come with additional records to use. */
393 if (answers
[k
].type
== RFC1035_TYPE_CNAME
) {
395 ++IpcacheStats
.rr_cname
;
399 // otherwise its an unknown RR. debug at level 9 since we usually want to ignore these and they are common.
400 debugs(14, 9, "Unknown RR type received: type=" << answers
[k
].type
<< " starting at " << &(answers
[k
]) );
403 debugs(14, DBG_IMPORTANT
, MYNAME
<< "No Address records in response to '" << name
<< "'");
404 i
->error_message
= xstrdup("No Address records");
406 ++IpcacheStats
.cname_only
;
410 i
->addrs
.in_addrs
= static_cast<Ip::Address
*>(xcalloc(na
, sizeof(Ip::Address
)));
411 for (int l
= 0; l
< na
; ++l
)
412 i
->addrs
.in_addrs
[l
].setEmpty(); // perform same init actions as constructor would.
413 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(na
, sizeof(unsigned char));
415 for (j
= 0, k
= 0; k
< nr
; ++k
) {
417 if (answers
[k
].type
== RFC1035_TYPE_A
) {
418 if (answers
[k
].rdlength
!= sizeof(struct in_addr
))
422 memcpy(&temp
, answers
[k
].rdata
, sizeof(struct in_addr
));
423 i
->addrs
.in_addrs
[j
] = temp
;
425 debugs(14, 3, name
<< " #" << j
<< " " << i
->addrs
.in_addrs
[j
]);
428 } else if (Ip::EnableIpv6
&& answers
[k
].type
== RFC1035_TYPE_AAAA
) {
429 if (answers
[k
].rdlength
!= sizeof(struct in6_addr
))
432 struct in6_addr temp
;
433 memcpy(&temp
, answers
[k
].rdata
, sizeof(struct in6_addr
));
434 i
->addrs
.in_addrs
[j
] = temp
;
436 debugs(14, 3, name
<< " #" << j
<< " " << i
->addrs
.in_addrs
[j
] );
439 if (ttl
== 0 || (int) answers
[k
].ttl
< ttl
)
440 ttl
= answers
[k
].ttl
;
446 i
->addrs
.count
= (unsigned char) na
;
448 i
->addrs
.count
= 255;
450 if (ttl
> Config
.positiveDnsTtl
)
451 ttl
= Config
.positiveDnsTtl
;
453 if (ttl
< Config
.negativeDnsTtl
)
454 ttl
= Config
.negativeDnsTtl
;
456 i
->expires
= squid_curtime
+ ttl
;
458 i
->flags
.negcached
= false;
461 /// \ingroup IPCacheInternal
463 ipcacheHandleReply(void *data
, const rfc1035_rr
* answers
, int na
, const char *error_message
)
466 static_cast<generic_cbdata
*>(data
)->unwrap(&i
);
467 ++IpcacheStats
.replies
;
468 const int age
= i
->age();
469 statCounter
.dns
.svcTime
.count(age
);
471 ipcacheParse(i
, answers
, na
, error_message
);
473 ipcacheCallback(i
, age
);
479 \param name Host to resolve.
480 \param handler Pointer to the function to be called when the reply
481 * from the IP cache (or the DNS if the IP cache misses)
482 \param handlerData Information that is passed to the handler and does not affect the IP cache.
484 * XXX: on hits and some errors, the handler is called immediately instead
485 * of scheduling an async call. This reentrant behavior means that the
486 * user job must be extra careful after calling ipcache_nbgethostbyname,
487 * especially if the handler destroys the job. Moreover, the job has
488 * no way of knowing whether the reentrant call happened.
489 * Comm::Connection setup usually protects the job by scheduling an async call,
490 * but some user code calls ipcache_nbgethostbyname directly.
493 ipcache_nbgethostbyname(const char *name
, IPH
* handler
, void *handlerData
)
495 ipcache_entry
*i
= NULL
;
496 const ipcache_addrs
*addrs
= NULL
;
498 debugs(14, 4, "ipcache_nbgethostbyname: Name '" << name
<< "'.");
499 ++IpcacheStats
.requests
;
501 if (name
== NULL
|| name
[0] == '\0') {
502 debugs(14, 4, "ipcache_nbgethostbyname: Invalid name!");
503 ++IpcacheStats
.invalid
;
504 const Dns::LookupDetails
details("Invalid hostname", -1); // error, no lookup
506 handler(NULL
, details
, handlerData
);
510 if ((addrs
= ipcacheCheckNumeric(name
))) {
511 debugs(14, 4, "ipcache_nbgethostbyname: BYPASS for '" << name
<< "' (already numeric)");
512 ++IpcacheStats
.numeric_hits
;
513 const Dns::LookupDetails details
; // no error, no lookup
515 handler(addrs
, details
, handlerData
);
519 i
= ipcache_get(name
);
524 } else if (ipcacheExpiredEntry(i
)) {
525 /* hit, but expired -- bummer */
530 debugs(14, 4, "ipcache_nbgethostbyname: HIT for '" << name
<< "'");
532 if (i
->flags
.negcached
)
533 ++IpcacheStats
.negative_hits
;
537 i
->handler
= handler
;
539 i
->handlerData
= cbdataReference(handlerData
);
541 ipcacheCallback(i
, -1); // no lookup
546 debugs(14, 5, "ipcache_nbgethostbyname: MISS for '" << name
<< "'");
547 ++IpcacheStats
.misses
;
548 i
= new ipcache_entry(name
);
549 i
->handler
= handler
;
550 i
->handlerData
= cbdataReference(handlerData
);
551 i
->request_time
= current_time
;
552 c
= new generic_cbdata(i
);
553 idnsALookup(hashKeyStr(&i
->hash
), ipcacheHandleReply
, c
);
556 /// \ingroup IPCacheInternal
558 ipcacheRegisterWithCacheManager(void)
560 Mgr::RegisterAction("ipcache",
561 "IP Cache Stats and Contents",
562 stat_ipcache_get
, 0, 1);
568 * Initialize the ipcache.
569 * Is called from mainInitialize() after disk initialization
570 * and prior to the reverse FQDNCache initialization
576 debugs(14, DBG_IMPORTANT
, "Initializing IP Cache...");
577 memset(&IpcacheStats
, '\0', sizeof(IpcacheStats
));
578 memset(&lru_list
, '\0', sizeof(lru_list
));
579 memset(&static_addrs
, '\0', sizeof(ipcache_addrs
));
581 static_addrs
.in_addrs
= static_cast<Ip::Address
*>(xcalloc(1, sizeof(Ip::Address
)));
582 static_addrs
.in_addrs
->setEmpty(); // properly setup the Ip::Address!
583 static_addrs
.bad_mask
= (unsigned char *)xcalloc(1, sizeof(unsigned char));
584 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
585 (float) Config
.ipcache
.high
) / (float) 100);
586 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
587 (float) Config
.ipcache
.low
) / (float) 100);
588 n
= hashPrime(ipcache_high
/ 4);
589 ip_table
= hash_create((HASHCMP
*) strcmp
, n
, hash4
);
591 ipcacheRegisterWithCacheManager();
597 * Is different from ipcache_nbgethostbyname in that it only checks
598 * if an entry exists in the cache and does not by default contact the DNS,
599 * unless this is requested, by setting the flags.
601 \param name Host name to resolve.
602 \param flags Default is NULL, set to IP_LOOKUP_IF_MISS
603 * to explicitly perform DNS lookups.
605 \retval NULL An error occured during lookup
606 \retval NULL No results available in cache and no lookup specified
607 \retval * Pointer to the ipcahce_addrs structure containing the lookup results
609 const ipcache_addrs
*
610 ipcache_gethostbyname(const char *name
, int flags
)
612 ipcache_entry
*i
= NULL
;
613 ipcache_addrs
*addrs
;
615 debugs(14, 3, "ipcache_gethostbyname: '" << name
<< "', flags=" << std::hex
<< flags
);
616 ++IpcacheStats
.requests
;
617 i
= ipcache_get(name
);
621 } else if (ipcacheExpiredEntry(i
)) {
624 } else if (i
->flags
.negcached
) {
625 ++IpcacheStats
.negative_hits
;
626 // ignore i->error_message: the caller just checks IP cache presence
630 i
->lastref
= squid_curtime
;
631 // ignore i->error_message: the caller just checks IP cache presence
635 /* no entry [any more] */
637 if ((addrs
= ipcacheCheckNumeric(name
))) {
638 ++IpcacheStats
.numeric_hits
;
642 ++IpcacheStats
.misses
;
644 if (flags
& IP_LOOKUP_IF_MISS
)
645 ipcache_nbgethostbyname(name
, NULL
, NULL
);
650 /// \ingroup IPCacheInternal
652 ipcacheStatPrint(ipcache_entry
* i
, StoreEntry
* sentry
)
655 char buf
[MAX_IPSTRLEN
];
658 debugs(14, DBG_CRITICAL
, HERE
<< "CRITICAL: sentry is NULL!");
663 debugs(14, DBG_CRITICAL
, HERE
<< "CRITICAL: ipcache_entry is NULL!");
664 storeAppendPrintf(sentry
, "CRITICAL ERROR\n");
668 int count
= i
->addrs
.count
;
670 storeAppendPrintf(sentry
, " %-32.32s %c%c %6d %6d %2d(%2d)",
671 hashKeyStr(&i
->hash
),
672 i
->flags
.fromhosts
? 'H' : ' ',
673 i
->flags
.negcached
? 'N' : ' ',
674 (int) (squid_curtime
- i
->lastref
),
675 (int) ((i
->flags
.fromhosts
? -1 : i
->expires
- squid_curtime
)),
676 (int) i
->addrs
.count
,
677 (int) i
->addrs
.badcount
);
680 * Negative-cached entries have no IPs listed. */
681 if (i
->flags
.negcached
) {
682 storeAppendPrintf(sentry
, "\n");
687 * Cached entries have IPs listed with a BNF of: ip-address '-' ('OK'|'BAD') */
688 for (k
= 0; k
< count
; ++k
) {
689 /* Display tidy-up: IPv6 are so big make the list vertical */
691 storeAppendPrintf(sentry
, " %45.45s-%3s\n",
692 i
->addrs
.in_addrs
[k
].toStr(buf
,MAX_IPSTRLEN
),
693 i
->addrs
.bad_mask
[k
] ? "BAD" : "OK ");
695 storeAppendPrintf(sentry
, "%s %45.45s-%3s\n",
696 " ", /* blank-space indenting IP list */
697 i
->addrs
.in_addrs
[k
].toStr(buf
,MAX_IPSTRLEN
),
698 i
->addrs
.bad_mask
[k
] ? "BAD" : "OK ");
703 \ingroup IPCacheInternal
705 * process objects list
708 stat_ipcache_get(StoreEntry
* sentry
)
711 assert(ip_table
!= NULL
);
712 storeAppendPrintf(sentry
, "IP Cache Statistics:\n");
713 storeAppendPrintf(sentry
, "IPcache Entries In Use: %d\n",
714 ipcache_entry::UseCount());
715 storeAppendPrintf(sentry
, "IPcache Entries Cached: %d\n",
717 storeAppendPrintf(sentry
, "IPcache Requests: %d\n",
718 IpcacheStats
.requests
);
719 storeAppendPrintf(sentry
, "IPcache Hits: %d\n",
721 storeAppendPrintf(sentry
, "IPcache Negative Hits: %d\n",
722 IpcacheStats
.negative_hits
);
723 storeAppendPrintf(sentry
, "IPcache Numeric Hits: %d\n",
724 IpcacheStats
.numeric_hits
);
725 storeAppendPrintf(sentry
, "IPcache Misses: %d\n",
726 IpcacheStats
.misses
);
727 storeAppendPrintf(sentry
, "IPcache Retrieved A: %d\n",
729 storeAppendPrintf(sentry
, "IPcache Retrieved AAAA: %d\n",
730 IpcacheStats
.rr_aaaa
);
731 storeAppendPrintf(sentry
, "IPcache Retrieved CNAME: %d\n",
732 IpcacheStats
.rr_cname
);
733 storeAppendPrintf(sentry
, "IPcache CNAME-Only Response: %d\n",
734 IpcacheStats
.cname_only
);
735 storeAppendPrintf(sentry
, "IPcache Invalid Request: %d\n",
736 IpcacheStats
.invalid
);
737 storeAppendPrintf(sentry
, "\n\n");
738 storeAppendPrintf(sentry
, "IP Cache Contents:\n\n");
739 storeAppendPrintf(sentry
, " %-31.31s %3s %6s %6s %4s\n",
746 for (m
= lru_list
.head
; m
; m
= m
->next
) {
747 assert( m
->next
!= m
);
748 ipcacheStatPrint((ipcache_entry
*)m
->data
, sentry
);
752 /// \ingroup IPCacheAPI
754 ipcacheInvalidate(const char *name
)
758 if ((i
= ipcache_get(name
)) == NULL
)
761 i
->expires
= squid_curtime
;
764 * NOTE, don't call ipcacheRelease here because we might be here due
765 * to a thread started from a callback.
769 /// \ingroup IPCacheAPI
771 ipcacheInvalidateNegative(const char *name
)
775 if ((i
= ipcache_get(name
)) == NULL
)
778 if (i
->flags
.negcached
)
779 i
->expires
= squid_curtime
;
782 * NOTE, don't call ipcacheRelease here because we might be here due
783 * to a thread started from a callback.
787 /// \ingroup IPCacheAPI
789 ipcacheCheckNumeric(const char *name
)
792 /* check if it's already a IP address in text form. */
794 /* it may be IPv6-wrapped */
795 if (name
[0] == '[') {
796 char *tmp
= xstrdup(&name
[1]);
797 tmp
[strlen(tmp
)-1] = '\0';
803 } else if (!(ip
= name
))
806 debugs(14, 4, "ipcacheCheckNumeric: HIT_BYPASS for '" << name
<< "' == " << ip
);
808 static_addrs
.count
= 1;
810 static_addrs
.cur
= 0;
812 static_addrs
.in_addrs
[0] = ip
;
814 static_addrs
.bad_mask
[0] = FALSE
;
816 static_addrs
.badcount
= 0;
818 return &static_addrs
;
821 /// \ingroup IPCacheInternal
823 ipcacheLockEntry(ipcache_entry
* i
)
825 if (i
->locks
++ == 0) {
826 dlinkDelete(&i
->lru
, &lru_list
);
827 dlinkAdd(i
, &i
->lru
, &lru_list
);
831 /// \ingroup IPCacheInternal
833 ipcacheUnlockEntry(ipcache_entry
* i
)
836 debugs(14, DBG_IMPORTANT
, "WARNING: ipcacheEntry unlocked with no lock! locks=" << i
->locks
);
842 if (ipcacheExpiredEntry(i
))
846 /// \ingroup IPCacheAPI
848 ipcacheCycleAddr(const char *name
, ipcache_addrs
* ia
)
855 if ((i
= ipcache_get(name
)) == NULL
)
858 if (i
->flags
.negcached
)
864 for (k
= 0; k
< ia
->count
; ++k
) {
865 if (++ia
->cur
== ia
->count
)
868 if (!ia
->bad_mask
[ia
->cur
])
872 if (k
== ia
->count
) {
873 /* All bad, reset to All good */
874 debugs(14, 3, "ipcacheCycleAddr: Changing ALL " << name
<< " addrs from BAD to OK");
876 for (k
= 0; k
< ia
->count
; ++k
)
884 /* NP: zero-based so we increase the human-readable number of our position */
885 debugs(14, 3, "ipcacheCycleAddr: " << name
<< " now at " << ia
->in_addrs
[ia
->cur
] << " (" << (ia
->cur
+1) << " of " << ia
->count
<< ")");
891 \param name domain name to have an IP marked bad
892 \param addr specific addres to be marked bad
895 ipcacheMarkBadAddr(const char *name
, const Ip::Address
&addr
)
901 /** Does nothing if the domain name does not exist. */
902 if ((i
= ipcache_get(name
)) == NULL
)
907 for (k
= 0; k
< (int) ia
->count
; ++k
) {
908 if (addr
== ia
->in_addrs
[k
] )
912 /** Does nothing if the IP does not exist for the doamin. */
913 if (k
== (int) ia
->count
)
916 /** Marks the given address as BAD */
917 if (!ia
->bad_mask
[k
]) {
918 ia
->bad_mask
[k
] = TRUE
;
920 debugs(14, 2, "ipcacheMarkBadAddr: " << name
<< " " << addr
);
923 /** then calls ipcacheCycleAddr() to advance the current pointer to the next OK address. */
924 ipcacheCycleAddr(name
, ia
);
927 /// \ingroup IPCacheAPI
929 ipcacheMarkAllGood(const char *name
)
935 if ((i
= ipcache_get(name
)) == NULL
)
940 /* All bad, reset to All good */
941 debugs(14, 3, "ipcacheMarkAllGood: Changing ALL " << name
<< " addrs to OK (" << ia
->badcount
<< "/" << ia
->count
<< " bad)");
943 for (k
= 0; k
< ia
->count
; ++k
)
949 /// \ingroup IPCacheAPI
951 ipcacheMarkGoodAddr(const char *name
, const Ip::Address
&addr
)
957 if ((i
= ipcache_get(name
)) == NULL
)
962 for (k
= 0; k
< (int) ia
->count
; ++k
) {
963 if (addr
== ia
->in_addrs
[k
])
967 if (k
== (int) ia
->count
) /* not found */
970 if (!ia
->bad_mask
[k
]) /* already OK */
973 ia
->bad_mask
[k
] = FALSE
;
977 debugs(14, 2, "ipcacheMarkGoodAddr: " << name
<< " " << addr
);
980 /// \ingroup IPCacheInternal
982 ipcacheFreeEntry(void *data
)
984 ipcache_entry
*i
= (ipcache_entry
*)data
;
988 ipcache_entry::~ipcache_entry()
990 xfree(addrs
.in_addrs
);
991 xfree(addrs
.bad_mask
);
992 xfree(error_message
);
996 /// \ingroup IPCacheAPI
998 ipcacheFreeMemory(void)
1000 hashFreeItems(ip_table
, ipcacheFreeEntry
);
1001 hashFreeMemory(ip_table
);
1008 * Recalculate IP cache size upon reconfigure.
1009 * Is called to clear the IPCache's data structures,
1010 * cancel all pending requests.
1013 ipcache_restart(void)
1015 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
1016 (float) Config
.ipcache
.high
) / (float) 100);
1017 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
1018 (float) Config
.ipcache
.low
) / (float) 100);
1019 purge_entries_fromhosts();
1025 * Adds a "static" entry from /etc/hosts
1027 \param name Hostname to be linked with IP
1028 \param ipaddr IP Address to be cached.
1031 \retval 1 IP address is invalid or other error.
1034 ipcacheAddEntryFromHosts(const char *name
, const char *ipaddr
)
1040 if (!(ip
= ipaddr
)) {
1041 if (strchr(ipaddr
, ':') && strspn(ipaddr
, "0123456789abcdefABCDEF:") == strlen(ipaddr
)) {
1042 debugs(14, 3, "ipcacheAddEntryFromHosts: Skipping IPv6 address '" << ipaddr
<< "'");
1044 debugs(14, DBG_IMPORTANT
, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr
<< "'");
1050 if ((i
= ipcache_get(name
))) {
1051 if (1 == i
->flags
.fromhosts
) {
1052 ipcacheUnlockEntry(i
);
1053 } else if (i
->locks
> 0) {
1054 debugs(14, DBG_IMPORTANT
, "ipcacheAddEntryFromHosts: can't add static entry for locked name '" << name
<< "'");
1061 i
= new ipcache_entry(name
);
1064 i
->addrs
.badcount
= 0;
1066 i
->addrs
.in_addrs
= static_cast<Ip::Address
*>(xcalloc(1, sizeof(Ip::Address
)));
1067 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(1, sizeof(unsigned char));
1068 i
->addrs
.in_addrs
[0] = ip
;
1069 i
->addrs
.bad_mask
[0] = FALSE
;
1070 i
->flags
.fromhosts
= true;
1072 ipcacheLockEntry(i
);
1080 * The function to return the ip cache statistics to via SNMP
1083 snmp_netIpFn(variable_list
* Var
, snint
* ErrP
)
1085 variable_list
*Answer
= NULL
;
1087 debugs(49, 5, "snmp_netIpFn: Processing request:" << snmpDebugOid(Var
->name
, Var
->name_length
, tmp
));
1088 *ErrP
= SNMP_ERR_NOERROR
;
1090 switch (Var
->name
[LEN_SQ_NET
+ 1]) {
1093 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1099 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1100 IpcacheStats
.requests
,
1105 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1111 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1117 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1118 IpcacheStats
.negative_hits
,
1123 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1124 IpcacheStats
.misses
,
1129 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1135 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1141 *ErrP
= SNMP_ERR_NOSUCHNAME
;
1142 snmp_var_free(Answer
);
1149 #endif /*SQUID_SNMP */