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.
36 #include "CacheManager.h"
37 #include "SquidTime.h"
40 #include "ip/IpAddress.h"
43 \defgroup IPCacheAPI IP Cache API
45 \section Introduction Introduction
47 * The IP cache is a built-in component of squid providing
48 * Hostname to IP-Number translation functionality and managing
49 * the involved data-structures. Efficiency concerns require
50 * mechanisms that allow non-blocking access to these mappings.
51 * The IP cache usually doesn't block on a request except for
52 * special cases where this is desired (see below).
54 \todo IP Cache should have its own API *.h header file.
58 \defgroup IPCacheInternal IP Cache Internals
60 \todo when IP cache is provided as a class. These sub-groups will be obsolete
61 * for now they are used to seperate the public and private functions.
62 * with the private ones all being in IPCachInternal and public in IPCacheAPI
64 \section InternalOperation Internal Operation
66 * Internally, the execution flow is as follows: On a miss,
67 * ipcache_getnbhostbyname checks whether a request for
68 * this name is already pending, and if positive, it creates
69 * a new entry using ipcacheAddNew with the IP_PENDING
70 * flag set . Then it calls ipcacheAddPending to add a
71 * request to the queue together with data and handler. Else,
72 * ipcache_dnsDispatch() is called to directly create a
73 * DNS query or to ipcacheEnqueue() if all no DNS port
74 * is free. ipcache_call_pending() is called regularly
75 * to walk down the pending list and call handlers. LRU clean-up
76 * is performed through ipcache_purgelru() according to
77 * the ipcache_high threshold.
80 /// \ingroup IPCacheAPI
81 typedef struct _ipcache_entry ipcache_entry
;
86 * The data structure used for storing name-address mappings
87 * is a small hashtable (static hash_table *ip_table),
88 * where structures of type ipcache_entry whose most
89 * interesting members are:
91 struct _ipcache_entry
{
92 hash_link hash
; /* must be first */
100 struct timeval request_time
;
102 unsigned short locks
;
104 unsigned short cname_wait
;
108 unsigned int negcached
:1;
109 unsigned int fromhosts
:1;
113 /// \ingroup IPCacheInternal
114 static struct _ipcache_stats
{
128 /// \ingroup IPCacheInternal
129 static dlink_list lru_list
;
131 static FREE ipcacheFreeEntry
;
133 static HLPCB ipcacheHandleReply
;
135 static IDNSCB ipcacheHandleReply
;
137 static IPH ipcacheHandleCnameRecurse
;
138 static int ipcacheExpiredEntry(ipcache_entry
*);
140 static int ipcacheParse(ipcache_entry
*, const char *buf
);
142 static int ipcacheParse(ipcache_entry
*, rfc1035_rr
*, int, const char *error
);
144 static ipcache_entry
*ipcache_get(const char *);
145 static void ipcacheLockEntry(ipcache_entry
*);
146 static void ipcacheStatPrint(ipcache_entry
*, StoreEntry
*);
147 static void ipcacheUnlockEntry(ipcache_entry
*);
148 static void ipcacheRelease(ipcache_entry
*, bool dofree
= true);
150 /// \ingroup IPCacheInternal
151 static ipcache_addrs static_addrs
;
152 /// \ingroup IPCacheInternal
153 static hash_table
*ip_table
= NULL
;
155 /// \ingroup IPCacheInternal
156 static long ipcache_low
= 180;
157 /// \ingroup IPCacheInternal
158 static long ipcache_high
= 200;
160 #if LIBRESOLV_DNS_TTL_HACK
161 extern int _dns_ttl_
;
165 \ingroup IPCacheInternal
167 * removes the given ipcache entry
170 ipcacheRelease(ipcache_entry
* i
, bool dofree
)
173 debugs(14, 0, "ipcacheRelease: Releasing entry with i=<NULL>");
177 if (!i
|| !i
->hash
.key
) {
178 debugs(14, 0, "ipcacheRelease: Releasing entry without hash link!");
182 debugs(14, 3, "ipcacheRelease: Releasing entry for '" << (const char *) i
->hash
.key
<< "'");
184 hash_remove_link(ip_table
, (hash_link
*) i
);
185 dlinkDelete(&i
->lru
, &lru_list
);
190 /// \ingroup IPCacheInternal
191 static ipcache_entry
*
192 ipcache_get(const char *name
)
194 if (ip_table
!= NULL
)
195 return (ipcache_entry
*) hash_lookup(ip_table
, name
);
200 /// \ingroup IPCacheInternal
202 ipcacheExpiredEntry(ipcache_entry
* i
)
204 /* all static entries are locked, so this takes care of them too */
209 if (i
->addrs
.count
== 0)
210 if (0 == i
->flags
.negcached
)
213 if (i
->expires
> squid_curtime
)
219 /// \ingroup IPCacheAPI
221 ipcache_purgelru(void *voidnotused
)
224 dlink_node
*prev
= NULL
;
227 eventAdd("ipcache_purgelru", ipcache_purgelru
, NULL
, 10.0, 1);
229 for (m
= lru_list
.tail
; m
; m
= prev
) {
230 if (memInUse(MEM_IPCACHE_ENTRY
) < ipcache_low
)
235 i
= (ipcache_entry
*)m
->data
;
245 debugs(14, 9, "ipcache_purgelru: removed " << removed
<< " entries");
249 \ingroup IPCacheInternal
251 * purges entries added from /etc/hosts (or whatever).
254 purge_entries_fromhosts(void)
256 dlink_node
*m
= lru_list
.head
;
257 ipcache_entry
*i
= NULL
, *t
;
260 if (i
!= NULL
) { /* need to delay deletion */
261 ipcacheRelease(i
); /* we just override locks */
265 t
= (ipcache_entry
*)m
->data
;
267 if (t
->flags
.fromhosts
)
278 \ingroup IPCacheInternal
280 * create blank ipcache_entry
282 static ipcache_entry
*
283 ipcacheCreateEntry(const char *name
)
285 static ipcache_entry
*i
;
286 i
= (ipcache_entry
*)memAllocate(MEM_IPCACHE_ENTRY
);
287 i
->hash
.key
= xstrdup(name
);
288 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
292 /// \ingroup IPCacheInternal
294 ipcacheAddEntry(ipcache_entry
* i
)
296 hash_link
*e
= (hash_link
*)hash_lookup(ip_table
, i
->hash
.key
);
299 /* INET6 : should NOT be adding this entry until all CNAME have been received. */
300 assert(i
->cname_wait
== 0);
304 /* avoid colission */
305 ipcache_entry
*q
= (ipcache_entry
*) e
;
308 /* can occur with Multiple-depth CNAME Recursion if parent returned early with additional */
309 /* just need to drop from the hash without releasing actual memory */
310 ipcacheRelease(q
, false);
316 hash_join(ip_table
, &i
->hash
);
317 dlinkAdd(i
, &i
->lru
, &lru_list
);
318 i
->lastref
= squid_curtime
;
322 \ingroup IPCacheInternal
324 * walks down the pending list, calling handlers
327 ipcacheCallback(ipcache_entry
* i
)
329 IPH
*callback
= i
->handler
;
331 i
->lastref
= squid_curtime
;
338 callback
= i
->handler
;
342 if (cbdataReferenceValidDone(i
->handlerData
, &cbdata
)) {
343 dns_error_message
= i
->error_message
;
344 callback(i
->addrs
.count
? &i
->addrs
: NULL
, cbdata
);
347 ipcacheUnlockEntry(i
);
350 /// \ingroup IPCacheAPI
353 ipcacheParse(ipcache_entry
*i
, const char *inbuf
)
355 LOCAL_ARRAY(char, buf
, DNS_INBUF_SZ
);
360 const char *name
= (const char *)i
->hash
.key
;
361 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
362 i
->flags
.negcached
= 1;
363 safe_free(i
->addrs
.in_addrs
);
364 safe_free(i
->addrs
.bad_mask
);
365 safe_free(i
->error_message
);
369 debugs(14, 1, "ipcacheParse: Got <NULL> reply");
370 i
->error_message
= xstrdup("Internal Error");
374 xstrncpy(buf
, inbuf
, DNS_INBUF_SZ
);
375 debugs(14, 5, "ipcacheParse: parsing: {" << buf
<< "}");
376 token
= strtok(buf
, w_space
);
379 debugs(14, 1, "ipcacheParse: expecting result, got '" << inbuf
<< "'");
381 i
->error_message
= xstrdup("Internal Error");
385 if (0 == strcmp(token
, "$fail")) {
386 token
= strtok(NULL
, "\n");
387 assert(NULL
!= token
);
388 i
->error_message
= xstrdup(token
);
392 if (0 != strcmp(token
, "$addr")) {
393 debugs(14, 1, "ipcacheParse: expecting '$addr', got '" << inbuf
<< "' in response to '" << name
<< "'");
395 i
->error_message
= xstrdup("Internal Error");
399 token
= strtok(NULL
, w_space
);
402 debugs(14, 1, "ipcacheParse: expecting TTL, got '" << inbuf
<< "' in response to '" << name
<< "'");
404 i
->error_message
= xstrdup("Internal Error");
410 while (NULL
!= (token
= strtok(NULL
, w_space
))) {
420 i
->addrs
.in_addrs
= (IpAddress
*)xcalloc(ipcount
, sizeof(IpAddress
));
421 for (int l
= 0; l
< ipcount
; l
++)
422 i
->addrs
.in_addrs
[l
].SetEmpty(); // perform same init actions as constructor would.
423 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(ipcount
, sizeof(unsigned char));
424 memset(i
->addrs
.bad_mask
, 0, sizeof(unsigned char) * ipcount
);
426 for (j
= 0, k
= 0; k
< ipcount
; k
++) {
427 if ( i
->addrs
.in_addrs
[j
] = A
[k
] )
430 debugs(14, 1, "ipcacheParse: Invalid IP address '" << A
[k
] << "' in response to '" << name
<< "'");
433 i
->addrs
.count
= (unsigned char) j
;
436 if (i
->addrs
.count
<= 0) {
437 debugs(14, 1, "ipcacheParse: No addresses in response to '" << name
<< "'");
441 if (ttl
== 0 || ttl
> Config
.positiveDnsTtl
)
442 ttl
= Config
.positiveDnsTtl
;
444 if (ttl
< Config
.negativeDnsTtl
)
445 ttl
= Config
.negativeDnsTtl
;
447 i
->expires
= squid_curtime
+ ttl
;
449 i
->flags
.negcached
= 0;
451 return i
->addrs
.count
;
456 ipcacheParse(ipcache_entry
*i
, rfc1035_rr
* answers
, int nr
, const char *error_message
)
462 const char *name
= (const char *)i
->hash
.key
;
465 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
466 i
->flags
.negcached
= 1;
467 safe_free(i
->addrs
.in_addrs
);
468 assert(i
->addrs
.in_addrs
== NULL
);
469 safe_free(i
->addrs
.bad_mask
);
470 assert(i
->addrs
.bad_mask
== NULL
);
471 safe_free(i
->error_message
);
472 assert(i
->error_message
== NULL
);
476 debugs(14, 3, "ipcacheParse: Lookup failed '" << error_message
<< "' for '" << (const char *)i
->hash
.key
<< "'");
477 i
->error_message
= xstrdup(error_message
);
482 debugs(14, 3, "ipcacheParse: No DNS records in response to '" << name
<< "'");
483 i
->error_message
= xstrdup("No DNS records");
489 for (k
= 0; k
< nr
; k
++) {
492 if (answers
[k
].type
== RFC1035_TYPE_AAAA
) {
493 if (answers
[k
].rdlength
!= sizeof(struct in6_addr
)) {
494 debugs(14, 1, "ipcacheParse: Invalid IPv6 address in response to '" << name
<< "'");
498 IpcacheStats
.rr_aaaa
++;
503 if (answers
[k
].type
== RFC1035_TYPE_A
) {
504 if (answers
[k
].rdlength
!= sizeof(struct in_addr
)) {
505 debugs(14, 1, "ipcacheParse: Invalid IPv4 address in response to '" << name
<< "'");
513 /* With A and AAAA, the CNAME does not necessarily come with additional records to use. */
514 if (answers
[k
].type
== RFC1035_TYPE_CNAME
) {
516 IpcacheStats
.rr_cname
++;
519 debugs(14, 5, "ipcacheParse: " << name
<< " CNAME " << answers
[k
].rdata
<< " (checking destination: " << i
<< ").");
520 const ipcache_addrs
*res
= ipcache_gethostbyname(answers
[k
].rdata
, 0);
523 debugs(14, 5, "ipcacheParse: CNAME " << answers
[k
].rdata
<< " already has " << res
->count
<< " IPs cached.");
525 /* keep going on this, but flag the fact that we need to wait for a CNAME lookup to finish */
526 debugs(14, 5, "ipcacheParse: CNAME " << answers
[k
].rdata
<< " has no IPs! Recursing.");
527 ipcache_nbgethostbyname(answers
[k
].rdata
, ipcacheHandleCnameRecurse
, new generic_cbdata(i
) );
530 #endif /* DNS_CNAME */
535 // otherwise its an unknown RR. debug at level 9 since we usually want to ignore these and they are common.
536 debugs(14, 9, HERE
<< "Unknown RR type received: type=" << answers
[k
].type
<< " starting at " << &(answers
[k
]) );
540 if (na
== 0 && i
->cname_wait
>0 ) {
541 /* don't set any error message (yet). Allow recursion to do its work first. */
542 IpcacheStats
.cname_only
++;
545 #endif /* DNS_CNAME */
548 debugs(14, 1, "ipcacheParse: No Address records in response to '" << name
<< "'");
549 i
->error_message
= xstrdup("No Address records");
551 IpcacheStats
.cname_only
++;
555 i
->addrs
.in_addrs
= (IpAddress
*)xcalloc(na
, sizeof(IpAddress
));
556 for (int l
= 0; l
< na
; l
++)
557 i
->addrs
.in_addrs
[l
].SetEmpty(); // perform same init actions as constructor would.
558 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(na
, sizeof(unsigned char));
560 for (j
= 0, k
= 0; k
< nr
; k
++) {
562 if (answers
[k
].type
== RFC1035_TYPE_A
) {
563 if (answers
[k
].rdlength
!= sizeof(struct in_addr
))
567 xmemcpy(&temp
, answers
[k
].rdata
, sizeof(struct in_addr
));
568 i
->addrs
.in_addrs
[j
] = temp
;
570 debugs(14, 3, "ipcacheParse: " << name
<< " #" << j
<< " " << i
->addrs
.in_addrs
[j
]);
574 } else if (answers
[k
].type
== RFC1035_TYPE_AAAA
) {
575 if (answers
[k
].rdlength
!= sizeof(struct in6_addr
))
578 struct in6_addr temp
;
579 xmemcpy(&temp
, answers
[k
].rdata
, sizeof(struct in6_addr
));
580 i
->addrs
.in_addrs
[j
] = temp
;
582 debugs(14, 3, "ipcacheParse: " << name
<< " #" << j
<< " " << i
->addrs
.in_addrs
[j
] );
587 else if (answers
[k
].type
== RFC1035_TYPE_CNAME
) {
588 debugs(14, 3, "ipcacheParse: " << name
<< " #x CNAME " << answers
[k
].rdata
);
589 const ipcache_addrs
*res
= ipcache_gethostbyname(answers
[k
].rdata
, 0);
591 /* NP: the results of *that* query need to be integrated in place of the CNAME */
592 /* Ideally we should also integrate the min TTL of the above IPA's into ttl. */
593 for (int l
= 0; l
< res
->count
; l
++, j
++) {
594 i
->addrs
.in_addrs
[j
] = res
->in_addrs
[l
];
595 debugs(14, 3, "ipcacheParse: " << name
<< " #" << j
<< " " << i
->addrs
.in_addrs
[j
] );
598 debugs(14, 9, "ipcacheParse: " << answers
[k
].rdata
<< " (CNAME) waiting on A/AAAA records.");
601 #endif /* DNS_CNAME */
603 if (ttl
== 0 || (int) answers
[k
].ttl
< ttl
)
604 ttl
= answers
[k
].ttl
;
610 i
->addrs
.count
= (unsigned char) na
;
612 i
->addrs
.count
= 255;
614 if (ttl
> Config
.positiveDnsTtl
)
615 ttl
= Config
.positiveDnsTtl
;
617 if (ttl
< Config
.negativeDnsTtl
)
618 ttl
= Config
.negativeDnsTtl
;
620 i
->expires
= squid_curtime
+ ttl
;
622 i
->flags
.negcached
= 0;
625 /* SPECIAL CASE: may get here IFF CNAME received with Additional records */
626 /* reurn 0/'wait for further details' value. */
627 /* NP: 'No DNS Results' is a return -1 +msg */
631 #endif /* DNS_CNAME */
632 return i
->addrs
.count
;
637 /// \ingroup IPCacheInternal
640 ipcacheHandleReply(void *data
, char *reply
)
642 ipcacheHandleReply(void *data
, rfc1035_rr
* answers
, int na
, const char *error_message
)
647 static_cast<generic_cbdata
*>(data
)->unwrap(&i
);
648 IpcacheStats
.replies
++;
649 statHistCount(&statCounter
.dns
.svc_time
,
650 tvSubMsec(i
->request_time
, current_time
));
653 done
= ipcacheParse(i
, reply
);
656 done
= ipcacheParse(i
, answers
, na
, error_message
);
658 /* If we have not produced either IPs or Error immediately, wait for recursion to finish. */
659 if (done
!= 0 || error_message
!= NULL
)
671 \param name Host to resolve.
672 \param handler Pointer to the function to be called when the reply
673 * from the IP cache (or the DNS if the IP cache misses)
674 \param handlerData Information that is passed to the handler and does not affect the IP cache.
676 * XXX: on hits and some errors, the handler is called immediately instead
677 * of scheduling an async call. This reentrant behavior means that the
678 * user job must be extra careful after calling ipcache_nbgethostbyname,
679 * especially if the handler destroys the job. Moreover, the job has
680 * no way of knowing whether the reentrant call happened. commConnectStart
681 * protects the job by scheduling an async call, but some user code calls
682 * ipcache_nbgethostbyname directly.
685 ipcache_nbgethostbyname(const char *name
, IPH
* handler
, void *handlerData
)
687 ipcache_entry
*i
= NULL
;
688 const ipcache_addrs
*addrs
= NULL
;
690 assert(handler
!= NULL
);
691 debugs(14, 4, "ipcache_nbgethostbyname: Name '" << name
<< "'.");
692 IpcacheStats
.requests
++;
694 if (name
== NULL
|| name
[0] == '\0') {
695 debugs(14, 4, "ipcache_nbgethostbyname: Invalid name!");
696 IpcacheStats
.invalid
++;
697 dns_error_message
= "Invalid hostname";
698 handler(NULL
, handlerData
);
702 if ((addrs
= ipcacheCheckNumeric(name
))) {
703 debugs(14, 4, "ipcache_nbgethostbyname: BYPASS for '" << name
<< "' (already numeric)");
704 dns_error_message
= NULL
;
705 IpcacheStats
.numeric_hits
++;
706 handler(addrs
, handlerData
);
710 i
= ipcache_get(name
);
715 } else if (ipcacheExpiredEntry(i
)) {
716 /* hit, but expired -- bummer */
721 debugs(14, 4, "ipcache_nbgethostbyname: HIT for '" << name
<< "'");
723 if (i
->flags
.negcached
)
724 IpcacheStats
.negative_hits
++;
728 i
->handler
= handler
;
730 i
->handlerData
= cbdataReference(handlerData
);
737 debugs(14, 5, "ipcache_nbgethostbyname: MISS for '" << name
<< "'");
738 IpcacheStats
.misses
++;
739 i
= ipcacheCreateEntry(name
);
740 i
->handler
= handler
;
741 i
->handlerData
= cbdataReference(handlerData
);
742 i
->request_time
= current_time
;
743 c
= new generic_cbdata(i
);
746 dnsSubmit(hashKeyStr(&i
->hash
), ipcacheHandleReply
, c
);
749 idnsALookup(hashKeyStr(&i
->hash
), ipcacheHandleReply
, c
);
753 /// \ingroup IPCacheInternal
755 ipcacheRegisterWithCacheManager(void)
757 CacheManager::GetInstance()->
758 registerAction("ipcache",
759 "IP Cache Stats and Contents",
760 stat_ipcache_get
, 0, 1);
767 * Initialize the ipcache.
768 * Is called from mainInitialize() after disk initialization
769 * and prior to the reverse FQDNCache initialization
775 debugs(14, DBG_IMPORTANT
, "Initializing IP Cache...");
776 memset(&IpcacheStats
, '\0', sizeof(IpcacheStats
));
777 memset(&lru_list
, '\0', sizeof(lru_list
));
778 memset(&static_addrs
, '\0', sizeof(ipcache_addrs
));
780 static_addrs
.in_addrs
= (IpAddress
*)xcalloc(1, sizeof(IpAddress
));
781 static_addrs
.in_addrs
->SetEmpty(); // properly setup the IpAddress!
782 static_addrs
.bad_mask
= (unsigned char *)xcalloc(1, sizeof(unsigned char));
783 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
784 (float) Config
.ipcache
.high
) / (float) 100);
785 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
786 (float) Config
.ipcache
.low
) / (float) 100);
787 n
= hashPrime(ipcache_high
/ 4);
788 ip_table
= hash_create((HASHCMP
*) strcmp
, n
, hash4
);
789 memDataInit(MEM_IPCACHE_ENTRY
, "ipcache_entry", sizeof(ipcache_entry
), 0);
791 ipcacheRegisterWithCacheManager();
797 * Is different from ipcache_nbgethostbyname in that it only checks
798 * if an entry exists in the cache and does not by default contact the DNS,
799 * unless this is requested, by setting the flags.
801 \param name Host name to resolve.
802 \param flags Default is NULL, set to IP_LOOKUP_IF_MISS
803 * to explicitly perform DNS lookups.
805 \retval NULL An error occured during lookup
806 \retval NULL No results available in cache and no lookup specified
807 \retval * Pointer to the ipcahce_addrs structure containing the lookup results
809 const ipcache_addrs
*
810 ipcache_gethostbyname(const char *name
, int flags
)
812 ipcache_entry
*i
= NULL
;
813 ipcache_addrs
*addrs
;
815 debugs(14, 3, "ipcache_gethostbyname: '" << name
<< "', flags=" << std::hex
<< flags
);
816 IpcacheStats
.requests
++;
817 i
= ipcache_get(name
);
821 } else if (ipcacheExpiredEntry(i
)) {
824 } else if (i
->flags
.negcached
) {
825 IpcacheStats
.negative_hits
++;
826 dns_error_message
= i
->error_message
;
830 i
->lastref
= squid_curtime
;
831 dns_error_message
= i
->error_message
;
835 dns_error_message
= NULL
;
837 if ((addrs
= ipcacheCheckNumeric(name
))) {
838 IpcacheStats
.numeric_hits
++;
842 IpcacheStats
.misses
++;
844 if (flags
& IP_LOOKUP_IF_MISS
)
845 ipcache_nbgethostbyname(name
, ipcacheHandleCnameRecurse
, NULL
);
850 /// \ingroup IPCacheInternal
852 ipcacheStatPrint(ipcache_entry
* i
, StoreEntry
* sentry
)
855 char buf
[MAX_IPSTRLEN
];
858 debugs(14, 0, HERE
<< "CRITICAL: sentry is NULL!");
863 debugs(14, 0, HERE
<< "CRITICAL: ipcache_entry is NULL!");
864 storeAppendPrintf(sentry
, "CRITICAL ERROR\n");
868 int count
= i
->addrs
.count
;
870 storeAppendPrintf(sentry
, " %-32.32s %c%c %6d %6d %2d(%2d)",
871 hashKeyStr(&i
->hash
),
872 i
->flags
.fromhosts
? 'H' : ' ',
873 i
->flags
.negcached
? 'N' : ' ',
874 (int) (squid_curtime
- i
->lastref
),
875 (int) ((i
->flags
.fromhosts
? -1 : i
->expires
- squid_curtime
)),
876 (int) i
->addrs
.count
,
877 (int) i
->addrs
.badcount
);
880 * Negative-cached entries have no IPs listed. */
881 if (i
->flags
.negcached
) {
882 storeAppendPrintf(sentry
, "\n");
887 * Cached entries have IPs listed with a BNF of: ip-address '-' ('OK'|'BAD') */
888 for (k
= 0; k
< count
; k
++) {
889 /* Display tidy-up: IPv6 are so big make the list vertical */
891 storeAppendPrintf(sentry
, " %45.45s-%3s\n",
892 i
->addrs
.in_addrs
[k
].NtoA(buf
,MAX_IPSTRLEN
),
893 i
->addrs
.bad_mask
[k
] ? "BAD" : "OK ");
895 storeAppendPrintf(sentry
, "%s %45.45s-%3s\n",
896 " ", /* blank-space indenting IP list */
897 i
->addrs
.in_addrs
[k
].NtoA(buf
,MAX_IPSTRLEN
),
898 i
->addrs
.bad_mask
[k
] ? "BAD" : "OK ");
903 \ingroup IPCacheInternal
905 * process objects list
908 stat_ipcache_get(StoreEntry
* sentry
)
911 assert(ip_table
!= NULL
);
912 storeAppendPrintf(sentry
, "IP Cache Statistics:\n");
913 storeAppendPrintf(sentry
, "IPcache Entries: %d\n",
914 memInUse(MEM_IPCACHE_ENTRY
));
915 storeAppendPrintf(sentry
, "IPcache Requests: %d\n",
916 IpcacheStats
.requests
);
917 storeAppendPrintf(sentry
, "IPcache Hits: %d\n",
919 storeAppendPrintf(sentry
, "IPcache Negative Hits: %d\n",
920 IpcacheStats
.negative_hits
);
921 storeAppendPrintf(sentry
, "IPcache Numeric Hits: %d\n",
922 IpcacheStats
.numeric_hits
);
923 storeAppendPrintf(sentry
, "IPcache Misses: %d\n",
924 IpcacheStats
.misses
);
925 storeAppendPrintf(sentry
, "IPcache Retrieved A: %d\n",
927 storeAppendPrintf(sentry
, "IPcache Retrieved AAAA: %d\n",
928 IpcacheStats
.rr_aaaa
);
929 storeAppendPrintf(sentry
, "IPcache Retrieved CNAME: %d\n",
930 IpcacheStats
.rr_cname
);
931 storeAppendPrintf(sentry
, "IPcache CNAME-Only Response: %d\n",
932 IpcacheStats
.cname_only
);
933 storeAppendPrintf(sentry
, "IPcache Invalid Request: %d\n",
934 IpcacheStats
.invalid
);
935 storeAppendPrintf(sentry
, "\n\n");
936 storeAppendPrintf(sentry
, "IP Cache Contents:\n\n");
937 storeAppendPrintf(sentry
, " %-31.31s %3s %6s %6s %4s\n",
944 for (m
= lru_list
.head
; m
; m
= m
->next
) {
945 assert( m
->next
!= m
);
946 ipcacheStatPrint((ipcache_entry
*)m
->data
, sentry
);
952 * Takes two IpAddress arrays and merges them into a single array
953 * which is allocated dynamically to fit the number of unique addresses
955 \param aaddrs One list to merge
956 \param alen Size of list aaddrs
957 \param baddrs Other list to merge
958 \param alen Size of list baddrs
959 \param out Combined list of unique addresses (sorted with IPv6 first in IPv6-mode)
960 \param outlen Size of list out
963 ipcacheMergeIPLists(const IpAddress
*aaddrs
, const int alen
,
964 const IpAddress
*baddrs
, const int blen
,
965 IpAddress
**out
, int &outlen
)
969 IpAddress
const *ip4ptrs
[255];
971 IpAddress
const *ip6ptrs
[255];
976 memset(ip4ptrs
, 0, sizeof(IpAddress
*)*255);
978 memset(ip6ptrs
, 0, sizeof(IpAddress
*)*255);
981 // for each unique address in list A - grab ptr
982 for (t
= 0; t
< alen
; t
++) {
983 if (aaddrs
[t
].IsIPv4()) {
984 // check against IPv4 pruned list
985 for (c
= 0; c
<= num_ip4
; c
++) {
986 if (ip4ptrs
[c
] && aaddrs
[t
] == *(ip4ptrs
[c
]) ) break; // duplicate.
989 ip4ptrs
[num_ip4
] = &aaddrs
[t
];
994 else if (aaddrs
[t
].IsIPv6()) {
995 debugs(14,8, HERE
<< "A[" << t
<< "]=IPv6 " << aaddrs
[t
]);
996 // check against IPv6 pruned list
997 for (c
= 0; c
<= num_ip6
; c
++) {
998 if (ip6ptrs
[c
] && aaddrs
[t
] == *ip6ptrs
[c
]) break; // duplicate.
1001 ip6ptrs
[num_ip6
] = &aaddrs
[t
];
1008 // for each unique address in list B - grab ptr
1009 for (t
= 0; t
< blen
; t
++) {
1010 if (baddrs
[t
].IsIPv4()) {
1011 // check against IPv4 pruned list
1012 for (c
= 0; c
<= num_ip4
; c
++) {
1013 if (ip4ptrs
[c
] && baddrs
[t
] == *ip4ptrs
[c
]) break; // duplicate.
1016 ip4ptrs
[num_ip4
] = &baddrs
[t
];
1021 else if (baddrs
[t
].IsIPv6()) {
1022 // check against IPv6 pruned list
1023 for (c
= 0; c
<= num_ip6
; c
++) {
1024 if (ip6ptrs
[c
] && baddrs
[t
] == *ip6ptrs
[c
]) break; // duplicate.
1027 ip6ptrs
[num_ip6
] = &baddrs
[t
];
1034 fc
= num_ip6
+ num_ip4
;
1038 debugs(14, 5, "ipcacheMergeIPLists: Merge " << alen
<< "+" << blen
<< " into " << fc
<< " unique IPs.");
1040 // copy the old IPs into the new list buffer.
1041 (*out
) = (IpAddress
*)xcalloc(fc
, sizeof(IpAddress
));
1044 assert(out
!= NULL
);
1047 /* IPv6 are preferred (tried first) over IPv4 */
1049 for (int l
= 0; outlen
< num_ip6
; l
++, outlen
++) {
1050 (*out
)[outlen
] = *ip6ptrs
[l
];
1051 debugs(14, 5, "ipcacheMergeIPLists: #" << outlen
<< " " << (*out
)[outlen
] );
1053 #endif /* USE_IPV6 */
1055 for (int l
= 0; outlen
< num_ip4
; l
++, outlen
++) {
1056 (*out
)[outlen
] = *ip4ptrs
[l
];
1057 debugs(14, 5, "ipcacheMergeIPLists: #" << outlen
<< " " << (*out
)[outlen
] );
1060 assert(outlen
== fc
); // otherwise something broke badly!
1062 #endif /* DNS_CNAME */
1064 /// \ingroup IPCacheInternal
1067 ipcacheHandleCnameRecurse(const ipcache_addrs
*addrs
, void *cbdata
)
1070 ipcache_entry
*i
= NULL
;
1072 IpAddress
*tmpbuf
= NULL
;
1075 generic_cbdata
* gcb
= (generic_cbdata
*)cbdata
;
1076 // count of addrs at parent and child (REQ as .count is a char type!)
1077 int ccount
= 0, pcount
= 0;
1079 debugs(14, 5, "ipcacheHandleCnameRecurse: Handling basic A/AAAA response.");
1081 /* IFF no CNAME recursion being processed. do nothing. */
1088 // make sure we are actualy waiting for a CNAME callback to be run.
1089 assert(i
->cname_wait
> 0);
1090 // count this event. its being handled.
1093 pname
= (char*)i
->hash
.key
;
1094 assert(pname
!= NULL
);
1096 debugs(14, 5, "ipcacheHandleCnameRecurse: Handling CNAME recursion. CBDATA('" << gcb
->data
<< "')='" << pname
<< "' -> " << std::hex
<< i
);
1099 return; // Parent has expired. Don't merge, just leave for future Ref:
1102 /* IFF addrs is NULL (Usually an Error or Timeout occured on lookup.) */
1103 /* Ignore it and HOPE that we got some Additional records to use. */
1107 ccount
= (0+ addrs
->count
);
1108 pcount
= (0+ i
->addrs
.count
);
1111 /* IFF no CNAME results. do none of the processing BUT finish anyway. */
1114 debugs(14, 5, "ipcacheHandleCnameRecurse: Merge IP Lists for " << pname
<< " (" << pcount
<< "+" << ccount
<< ")");
1116 /* add new IP records to entry */
1117 tmpbuf
= i
->addrs
.in_addrs
;
1118 i
->addrs
.in_addrs
= NULL
;
1119 ipcacheMergeIPLists(tmpbuf
, pcount
, addrs
->in_addrs
, ccount
, &(i
->addrs
.in_addrs
), fc
);
1120 debugs(14,8, HERE
<< "in=" << tmpbuf
<< ", out=" << i
->addrs
.in_addrs
);
1121 assert( (pcount
>0 ? tmpbuf
!=NULL
: tmpbuf
==NULL
) );
1125 /* IFF the parent initial lookup was given Additional records with A */
1126 // clear the 'bad IP mask'
1127 safe_free(i
->addrs
.bad_mask
);
1129 // create a new bad IP mask to fit the new size needed.
1131 i
->addrs
.bad_mask
= (unsigned char*)xcalloc(fc
, sizeof(unsigned char));
1132 memset(i
->addrs
.bad_mask
, 0, sizeof(unsigned char)*fc
);
1136 i
->addrs
.count
= (unsigned char) fc
;
1138 i
->addrs
.count
= 255;
1140 if (ttl
== 0 || ttl
> Config
.positiveDnsTtl
)
1141 ttl
= Config
.positiveDnsTtl
;
1143 if (ttl
< Config
.negativeDnsTtl
)
1144 ttl
= Config
.negativeDnsTtl
;
1146 i
->expires
= squid_curtime
+ ttl
;
1148 i
->flags
.negcached
= 0;
1152 i
->addrs
.badcount
= 0;
1156 i
->error_message
= xstrdup("No DNS Records");
1159 /* finish the lookup we were doing on parent when we got side-tracked for CNAME loop */
1160 if (i
->cname_wait
== 0) {
1164 // else still more CNAME to be found.
1165 #endif /* DNS_CNAME */
1168 /// \ingroup IPCacheAPI
1170 ipcacheInvalidate(const char *name
)
1174 if ((i
= ipcache_get(name
)) == NULL
)
1177 i
->expires
= squid_curtime
;
1180 * NOTE, don't call ipcacheRelease here because we might be here due
1181 * to a thread started from a callback.
1185 /// \ingroup IPCacheAPI
1187 ipcacheInvalidateNegative(const char *name
)
1191 if ((i
= ipcache_get(name
)) == NULL
)
1194 if (i
->flags
.negcached
)
1195 i
->expires
= squid_curtime
;
1198 * NOTE, don't call ipcacheRelease here because we might be here due
1199 * to a thread started from a callback.
1203 /// \ingroup IPCacheAPI
1205 ipcacheCheckNumeric(const char *name
)
1209 /* check if it's already a IP address in text form. */
1211 /* it may be IPv6-wrapped */
1212 if (name
[0] == '[') {
1213 char *tmp
= xstrdup(&name
[1]);
1214 tmp
[strlen(tmp
)-1] = '\0';
1220 } else if (!(ip
= name
))
1223 debugs(14, 4, "ipcacheCheckNumeric: HIT_BYPASS for '" << name
<< "' == " << ip
);
1225 static_addrs
.count
= 1;
1227 static_addrs
.cur
= 0;
1229 static_addrs
.in_addrs
[0] = ip
;
1231 static_addrs
.bad_mask
[0] = FALSE
;
1233 static_addrs
.badcount
= 0;
1235 return &static_addrs
;
1238 /// \ingroup IPCacheInternal
1240 ipcacheLockEntry(ipcache_entry
* i
)
1242 if (i
->locks
++ == 0) {
1243 dlinkDelete(&i
->lru
, &lru_list
);
1244 dlinkAdd(i
, &i
->lru
, &lru_list
);
1248 /// \ingroup IPCacheInternal
1250 ipcacheUnlockEntry(ipcache_entry
* i
)
1253 debugs(14, 1, "WARNING: ipcacheEntry unlocked with no lock! locks=" << i
->locks
);
1259 if (ipcacheExpiredEntry(i
))
1263 /// \ingroup IPCacheAPI
1265 ipcacheCycleAddr(const char *name
, ipcache_addrs
* ia
)
1272 if ((i
= ipcache_get(name
)) == NULL
)
1275 if (i
->flags
.negcached
)
1281 for (k
= 0; k
< ia
->count
; k
++) {
1282 if (++ia
->cur
== ia
->count
)
1285 if (!ia
->bad_mask
[ia
->cur
])
1289 if (k
== ia
->count
) {
1290 /* All bad, reset to All good */
1291 debugs(14, 3, "ipcacheCycleAddr: Changing ALL " << name
<< " addrs from BAD to OK");
1293 for (k
= 0; k
< ia
->count
; k
++)
1294 ia
->bad_mask
[k
] = 0;
1301 debugs(14, 3, "ipcacheCycleAddr: " << name
<< " now at " << ia
->in_addrs
[ia
->cur
] << " (" << ia
->cur
<< " of " << ia
->count
<< ")");
1307 \param name domain name to have an IP marked bad
1308 \param addr specific addres to be marked bad
1311 ipcacheMarkBadAddr(const char *name
, IpAddress
&addr
)
1317 /** Does nothing if the domain name does not exist. */
1318 if ((i
= ipcache_get(name
)) == NULL
)
1323 for (k
= 0; k
< (int) ia
->count
; k
++) {
1324 if (addr
== ia
->in_addrs
[k
] )
1328 /** Does nothing if the IP does not exist for the doamin. */
1329 if (k
== (int) ia
->count
)
1332 /** Marks the given address as BAD */
1333 if (!ia
->bad_mask
[k
]) {
1334 ia
->bad_mask
[k
] = TRUE
;
1336 i
->expires
= min(squid_curtime
+ max((time_t)60, Config
.negativeDnsTtl
), i
->expires
);
1337 debugs(14, 2, "ipcacheMarkBadAddr: " << name
<< " " << addr
);
1340 /** then calls ipcacheCycleAddr() to advance the current pointer to the next OK address. */
1341 ipcacheCycleAddr(name
, ia
);
1344 /// \ingroup IPCacheAPI
1346 ipcacheMarkGoodAddr(const char *name
, IpAddress
&addr
)
1352 if ((i
= ipcache_get(name
)) == NULL
)
1357 for (k
= 0; k
< (int) ia
->count
; k
++) {
1358 if (addr
== ia
->in_addrs
[k
])
1362 if (k
== (int) ia
->count
) /* not found */
1365 if (!ia
->bad_mask
[k
]) /* already OK */
1368 ia
->bad_mask
[k
] = FALSE
;
1372 debugs(14, 2, "ipcacheMarkGoodAddr: " << name
<< " " << addr
);
1375 /// \ingroup IPCacheInternal
1377 ipcacheFreeEntry(void *data
)
1379 ipcache_entry
*i
= (ipcache_entry
*)data
;
1380 safe_free(i
->addrs
.in_addrs
);
1381 safe_free(i
->addrs
.bad_mask
);
1382 safe_free(i
->hash
.key
);
1383 safe_free(i
->error_message
);
1384 memFree(i
, MEM_IPCACHE_ENTRY
);
1387 /// \ingroup IPCacheAPI
1389 ipcacheFreeMemory(void)
1391 hashFreeItems(ip_table
, ipcacheFreeEntry
);
1392 hashFreeMemory(ip_table
);
1399 * Recalculate IP cache size upon reconfigure.
1400 * Is called to clear the IPCache's data structures,
1401 * cancel all pending requests.
1404 ipcache_restart(void)
1406 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
1407 (float) Config
.ipcache
.high
) / (float) 100);
1408 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
1409 (float) Config
.ipcache
.low
) / (float) 100);
1410 purge_entries_fromhosts();
1416 * Adds a "static" entry from /etc/hosts
1418 \param name Hostname to be linked with IP
1419 \param ipaddr IP Address to be cached.
1422 \retval 1 IP address is invalid or other error.
1425 ipcacheAddEntryFromHosts(const char *name
, const char *ipaddr
)
1431 if (!(ip
= ipaddr
)) {
1433 if (strchr(ipaddr
, ':') && strspn(ipaddr
, "0123456789abcdefABCDEF:") == strlen(ipaddr
)) {
1434 debugs(14, 3, "ipcacheAddEntryFromHosts: Skipping IPv6 address '" << ipaddr
<< "'");
1436 debugs(14, 1, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr
<< "'");
1439 debugs(14, 1, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr
<< "'");
1445 if ((i
= ipcache_get(name
))) {
1446 if (1 == i
->flags
.fromhosts
) {
1447 ipcacheUnlockEntry(i
);
1448 } else if (i
->locks
> 0) {
1449 debugs(14, 1, "ipcacheAddEntryFromHosts: can't add static entry for locked name '" << name
<< "'");
1456 i
= ipcacheCreateEntry(name
);
1459 i
->addrs
.badcount
= 0;
1461 i
->addrs
.in_addrs
= (IpAddress
*)xcalloc(1, sizeof(IpAddress
));
1462 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(1, sizeof(unsigned char));
1463 i
->addrs
.in_addrs
[0] = ip
;
1464 i
->addrs
.bad_mask
[0] = FALSE
;
1465 i
->flags
.fromhosts
= 1;
1467 ipcacheLockEntry(i
);
1475 * The function to return the ip cache statistics to via SNMP
1478 snmp_netIpFn(variable_list
* Var
, snint
* ErrP
)
1480 variable_list
*Answer
= NULL
;
1481 debugs(49, 5, "snmp_netIpFn: Processing request:");
1482 snmpDebugOid(5, Var
->name
, Var
->name_length
);
1483 *ErrP
= SNMP_ERR_NOERROR
;
1485 switch (Var
->name
[LEN_SQ_NET
+ 1]) {
1488 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1489 memInUse(MEM_IPCACHE_ENTRY
),
1494 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1495 IpcacheStats
.requests
,
1500 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1506 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1512 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1513 IpcacheStats
.negative_hits
,
1518 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1519 IpcacheStats
.misses
,
1524 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1530 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1536 *ErrP
= SNMP_ERR_NOSUCHNAME
;
1537 snmp_var_free(Answer
);
1544 #endif /*SQUID_SNMP */