3 * $Id: ipcache.cc,v 1.265 2007/12/14 23:11:47 amosjeffries Exp $
5 * DEBUG: section 14 IP Cache
6 * AUTHOR: Harvest Derived
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
39 #include "CacheManager.h"
40 #include "SquidTime.h"
43 #include "IPAddress.h"
45 typedef struct _ipcache_entry ipcache_entry
;
49 hash_link hash
; /* must be first */
57 struct timeval request_time
;
60 unsigned short cname_wait
;
65 unsigned int negcached
:
68 unsigned int fromhosts
:
87 static dlink_list lru_list
;
89 static FREE ipcacheFreeEntry
;
91 static HLPCB ipcacheHandleReply
;
93 static IDNSCB ipcacheHandleReply
;
95 static IPH ipcacheHandleCnameRecurse
;
96 static int ipcacheExpiredEntry(ipcache_entry
*);
97 static int ipcache_testname(void);
99 static int ipcacheParse(ipcache_entry
*, const char *buf
);
101 static int ipcacheParse(ipcache_entry
*, rfc1035_rr
*, int, const char *error
);
103 static ipcache_entry
*ipcache_get(const char *);
104 static void ipcacheLockEntry(ipcache_entry
*);
105 static void ipcacheStatPrint(ipcache_entry
*, StoreEntry
*);
106 static void ipcacheUnlockEntry(ipcache_entry
*);
107 static void ipcacheRelease(ipcache_entry
*, bool dofree
= true);
109 static ipcache_addrs static_addrs
;
110 static hash_table
*ip_table
= NULL
;
112 static long ipcache_low
= 180;
113 static long ipcache_high
= 200;
115 #if LIBRESOLV_DNS_TTL_HACK
116 extern int _dns_ttl_
;
120 ipcache_testname(void)
123 debugs(14, 1, "Performing DNS Tests...");
125 if ((w
= Config
.dns_testname_list
) == NULL
)
128 for (; w
; w
= w
->next
) {
129 if (gethostbyname(w
->key
) != NULL
)
136 /* removes the given ipcache entry */
138 ipcacheRelease(ipcache_entry
* i
, bool dofree
)
141 debugs(14, 0, "ipcacheRelease: Releasing entry with i=<NULL>");
145 if(!i
|| !i
->hash
.key
) {
146 debugs(14, 0, "ipcacheRelease: Releasing entry without hash link!");
150 debugs(14, 3, "ipcacheRelease: Releasing entry for '" << (const char *) i
->hash
.key
<< "'");
152 hash_remove_link(ip_table
, (hash_link
*) i
);
153 dlinkDelete(&i
->lru
, &lru_list
);
158 static ipcache_entry
*
159 ipcache_get(const char *name
)
161 if (ip_table
!= NULL
)
162 return (ipcache_entry
*) hash_lookup(ip_table
, name
);
168 ipcacheExpiredEntry(ipcache_entry
* i
)
170 /* all static entries are locked, so this takes care of them too */
175 if (i
->addrs
.count
== 0)
176 if (0 == i
->flags
.negcached
)
179 if (i
->expires
> squid_curtime
)
186 ipcache_purgelru(void *voidnotused
)
189 dlink_node
*prev
= NULL
;
192 eventAdd("ipcache_purgelru", ipcache_purgelru
, NULL
, 10.0, 1);
194 for (m
= lru_list
.tail
; m
; m
= prev
) {
195 if (memInUse(MEM_IPCACHE_ENTRY
) < ipcache_low
)
200 i
= (ipcache_entry
*)m
->data
;
210 debugs(14, 9, "ipcache_purgelru: removed " << removed
<< " entries");
213 /* purges entries added from /etc/hosts (or whatever). */
215 purge_entries_fromhosts(void)
217 dlink_node
*m
= lru_list
.head
;
218 ipcache_entry
*i
= NULL
, *t
;
221 if (i
!= NULL
) { /* need to delay deletion */
222 ipcacheRelease(i
); /* we just override locks */
226 t
= (ipcache_entry
*)m
->data
;
228 if (t
->flags
.fromhosts
)
238 /* create blank ipcache_entry */
239 static ipcache_entry
*
240 ipcacheCreateEntry(const char *name
)
242 static ipcache_entry
*i
;
243 i
= (ipcache_entry
*)memAllocate(MEM_IPCACHE_ENTRY
);
244 i
->hash
.key
= xstrdup(name
);
245 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
250 ipcacheAddEntry(ipcache_entry
* i
)
252 hash_link
*e
= (hash_link
*)hash_lookup(ip_table
, i
->hash
.key
);
254 /* INET6 : should NOT be adding this entry until all CNAME have been received. */
255 assert(i
->cname_wait
== 0);
258 /* avoid colission */
259 ipcache_entry
*q
= (ipcache_entry
*) e
;
261 /* can occur with Multiple-depth CNAME Recursion if parent returned early with additional */
262 /* just need to drop from the hash without releasing actual memory */
263 ipcacheRelease(q
, false);
269 hash_join(ip_table
, &i
->hash
);
270 dlinkAdd(i
, &i
->lru
, &lru_list
);
271 i
->lastref
= squid_curtime
;
274 /* walks down the pending list, calling handlers */
276 ipcacheCallback(ipcache_entry
* i
)
278 IPH
*callback
= i
->handler
;
280 i
->lastref
= squid_curtime
;
287 callback
= i
->handler
;
291 if (cbdataReferenceValidDone(i
->handlerData
, &cbdata
)) {
292 dns_error_message
= i
->error_message
;
293 callback(i
->addrs
.count
? &i
->addrs
: NULL
, cbdata
);
296 ipcacheUnlockEntry(i
);
301 ipcacheParse(ipcache_entry
*i
, const char *inbuf
)
303 LOCAL_ARRAY(char, buf
, DNS_INBUF_SZ
);
308 const char *name
= (const char *)i
->hash
.key
;
309 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
310 i
->flags
.negcached
= 1;
311 safe_free(i
->addrs
.in_addrs
);
312 safe_free(i
->addrs
.bad_mask
);
313 safe_free(i
->error_message
);
317 debugs(14, 1, "ipcacheParse: Got <NULL> reply");
318 i
->error_message
= xstrdup("Internal Error");
322 xstrncpy(buf
, inbuf
, DNS_INBUF_SZ
);
323 debugs(14, 5, "ipcacheParse: parsing: {" << buf
<< "}");
324 token
= strtok(buf
, w_space
);
327 debugs(14, 1, "ipcacheParse: expecting result, got '" << inbuf
<< "'");
329 i
->error_message
= xstrdup("Internal Error");
333 if (0 == strcmp(token
, "$fail")) {
334 token
= strtok(NULL
, "\n");
335 assert(NULL
!= token
);
336 i
->error_message
= xstrdup(token
);
340 if (0 != strcmp(token
, "$addr")) {
341 debugs(14, 1, "ipcacheParse: expecting '$addr', got '" << inbuf
<< "' in response to '" << name
<< "'");
343 i
->error_message
= xstrdup("Internal Error");
347 token
= strtok(NULL
, w_space
);
350 debugs(14, 1, "ipcacheParse: expecting TTL, got '" << inbuf
<< "' in response to '" << name
<< "'");
352 i
->error_message
= xstrdup("Internal Error");
358 while (NULL
!= (token
= strtok(NULL
, w_space
))) {
368 i
->addrs
.in_addrs
= (IPAddress
*)xcalloc(ipcount
, sizeof(IPAddress
));
369 for(int l
= 0; l
< ipcount
; l
++)
370 i
->addrs
.in_addrs
[l
].SetEmpty(); // perform same init actions as constructor would.
371 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(ipcount
, sizeof(unsigned char));
372 memset(i
->addrs
.bad_mask
, 0, sizeof(unsigned char) * ipcount
);
374 for (j
= 0, k
= 0; k
< ipcount
; k
++) {
375 if ( i
->addrs
.in_addrs
[j
] = A
[k
] )
378 debugs(14, 1, "ipcacheParse: Invalid IP address '" << A
[k
] << "' in response to '" << name
<< "'");
381 i
->addrs
.count
= (unsigned char) j
;
384 if (i
->addrs
.count
<= 0) {
385 debugs(14, 1, "ipcacheParse: No addresses in response to '" << name
<< "'");
389 if (ttl
== 0 || ttl
> Config
.positiveDnsTtl
)
390 ttl
= Config
.positiveDnsTtl
;
392 if (ttl
< Config
.negativeDnsTtl
)
393 ttl
= Config
.negativeDnsTtl
;
395 i
->expires
= squid_curtime
+ ttl
;
397 i
->flags
.negcached
= 0;
399 return i
->addrs
.count
;
404 ipcacheParse(ipcache_entry
*i
, rfc1035_rr
* answers
, int nr
, const char *error_message
)
410 const char *name
= (const char *)i
->hash
.key
;
411 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
412 i
->flags
.negcached
= 1;
413 safe_free(i
->addrs
.in_addrs
);
414 assert(i
->addrs
.in_addrs
== NULL
);
415 safe_free(i
->addrs
.bad_mask
);
416 assert(i
->addrs
.bad_mask
== NULL
);
417 safe_free(i
->error_message
);
418 assert(i
->error_message
== NULL
);
422 debugs(14, 3, "ipcacheParse: Lookup failed '" << error_message
<< "' for '" << (const char *)i
->hash
.key
<< "'");
423 i
->error_message
= xstrdup(error_message
);
428 debugs(14, 3, "ipcacheParse: No DNS records in response to '" << name
<< "'");
429 i
->error_message
= xstrdup("No DNS records");
435 for (k
= 0; k
< nr
; k
++) {
438 if (answers
[k
].type
== RFC1035_TYPE_AAAA
) {
439 if (answers
[k
].rdlength
!= sizeof(struct in6_addr
)) {
440 debugs(14, 1, "ipcacheParse: Invalid IPv6 address in response to '" << name
<< "'");
448 if (answers
[k
].type
== RFC1035_TYPE_A
) {
449 if (answers
[k
].rdlength
!= sizeof(struct in_addr
)) {
450 debugs(14, 1, "ipcacheParse: Invalid IPv4 address in response to '" << name
<< "'");
457 /* With A and AAAA, the CNAME does not necessarily come with additional records to use. */
458 if (answers
[k
].type
== RFC1035_TYPE_CNAME
) {
459 debugs(14, 5, "ipcacheParse: " << name
<< " CNAME " << answers
[k
].rdata
<< " (checking destination: " << i
<< ").");
460 const ipcache_addrs
*res
= ipcache_gethostbyname(answers
[k
].rdata
, 0);
463 debugs(14, 5, "ipcacheParse: CNAME " << answers
[k
].rdata
<< " already has " << res
->count
<< " IPs cached.");
466 /* keep going on this, but flag the fact that we need to wait for a CNAME lookup to finish */
467 debugs(14, 5, "ipcacheParse: CNAME " << answers
[k
].rdata
<< " has no IPs! Recursing.");
468 ipcache_nbgethostbyname(answers
[k
].rdata
, ipcacheHandleCnameRecurse
, new generic_cbdata(i
) );
475 if(na
== 0 && i
->cname_wait
>0 ) {
476 /* don't set any error message (yet). Allow recursion to do its work first. */
481 debugs(14, 1, "ipcacheParse: No Address records in response to '" << name
<< "'");
482 i
->error_message
= xstrdup("No Address records");
486 i
->addrs
.in_addrs
= (IPAddress
*)xcalloc(na
, sizeof(IPAddress
));
487 for(int l
= 0; l
< na
; l
++)
488 i
->addrs
.in_addrs
[l
].SetEmpty(); // perform same init actions as constructor would.
489 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(na
, sizeof(unsigned char));
491 for (j
= 0, k
= 0; k
< nr
; k
++) {
493 if (answers
[k
].type
== RFC1035_TYPE_A
) {
494 if (answers
[k
].rdlength
!= sizeof(struct in_addr
))
498 xmemcpy(&temp
, answers
[k
].rdata
, sizeof(struct in_addr
));
499 i
->addrs
.in_addrs
[j
] = temp
;
501 debugs(14, 3, "ipcacheParse: " << name
<< " #" << j
<< " " << i
->addrs
.in_addrs
[j
]);
505 } else if (answers
[k
].type
== RFC1035_TYPE_AAAA
) {
506 if (answers
[k
].rdlength
!= sizeof(struct in6_addr
))
509 struct in6_addr temp
;
510 xmemcpy(&temp
, answers
[k
].rdata
, sizeof(struct in6_addr
));
511 i
->addrs
.in_addrs
[j
] = temp
;
513 debugs(14, 3, "ipcacheParse: " << name
<< " #" << j
<< " " << i
->addrs
.in_addrs
[j
] );
516 } else if (answers
[k
].type
== RFC1035_TYPE_CNAME
) {
517 debugs(14, 3, "ipcacheParse: " << name
<< " #x CNAME " << answers
[k
].rdata
);
518 const ipcache_addrs
*res
= ipcache_gethostbyname(answers
[k
].rdata
, 0);
520 /* NP: the results of *that* query need to be integrated in place of the CNAME */
521 /* Ideally we should also integrate the min TTL of the above IPA's into ttl. */
522 for(int l
= 0; l
< res
->count
; l
++, j
++) {
523 i
->addrs
.in_addrs
[j
] = res
->in_addrs
[l
];
524 debugs(14, 3, "ipcacheParse: " << name
<< " #" << j
<< " " << i
->addrs
.in_addrs
[j
] );
528 debugs(14, 9, "ipcacheParse: " << answers
[k
].rdata
<< " (CNAME) waiting on A/AAAA records.");
532 if (ttl
== 0 || (int) answers
[k
].ttl
< ttl
)
533 ttl
= answers
[k
].ttl
;
539 i
->addrs
.count
= (unsigned char) na
;
541 i
->addrs
.count
= 255;
543 if (ttl
> Config
.positiveDnsTtl
)
544 ttl
= Config
.positiveDnsTtl
;
546 if (ttl
< Config
.negativeDnsTtl
)
547 ttl
= Config
.negativeDnsTtl
;
549 i
->expires
= squid_curtime
+ ttl
;
551 i
->flags
.negcached
= 0;
553 /* SPECIAL CASE: may get here IFF CNAME received with Additional records */
554 /* reurn 0/'wait for further details' value. */
555 /* NP: 'No DNS Results' is a return -1 +msg */
559 return i
->addrs
.count
;
566 ipcacheHandleReply(void *data
, char *reply
)
568 ipcacheHandleReply(void *data
, rfc1035_rr
* answers
, int na
, const char *error_message
)
573 static_cast<generic_cbdata
*>(data
)->unwrap(&i
);
574 IpcacheStats
.replies
++;
575 statHistCount(&statCounter
.dns
.svc_time
,
576 tvSubMsec(i
->request_time
, current_time
));
579 done
= ipcacheParse(i
, reply
);
582 done
= ipcacheParse(i
, answers
, na
, error_message
);
584 /* If we have not produced either IPs or Error immediately, wait for recursion to finish. */
585 if(done
!= 0 || error_message
!= NULL
)
595 ipcache_nbgethostbyname(const char *name
, IPH
* handler
, void *handlerData
)
597 ipcache_entry
*i
= NULL
;
598 const ipcache_addrs
*addrs
= NULL
;
600 assert(handler
!= NULL
);
601 debugs(14, 4, "ipcache_nbgethostbyname: Name '" << name
<< "'.");
602 IpcacheStats
.requests
++;
604 if (name
== NULL
|| name
[0] == '\0') {
605 debugs(14, 4, "ipcache_nbgethostbyname: Invalid name!");
606 IpcacheStats
.invalid
++;
607 dns_error_message
= "Invalid hostname";
608 handler(NULL
, handlerData
);
612 if ((addrs
= ipcacheCheckNumeric(name
))) {
613 debugs(14, 4, "ipcache_nbgethostbyname: BYPASS for '" << name
<< "' (already numeric)");
614 dns_error_message
= NULL
;
615 IpcacheStats
.numeric_hits
++;
616 handler(addrs
, handlerData
);
620 i
= ipcache_get(name
);
625 } else if (ipcacheExpiredEntry(i
)) {
626 /* hit, but expired -- bummer */
631 debugs(14, 4, "ipcache_nbgethostbyname: HIT for '" << name
<< "'");
633 if (i
->flags
.negcached
)
634 IpcacheStats
.negative_hits
++;
638 i
->handler
= handler
;
640 i
->handlerData
= cbdataReference(handlerData
);
647 debugs(14, 5, "ipcache_nbgethostbyname: MISS for '" << name
<< "'");
648 IpcacheStats
.misses
++;
649 i
= ipcacheCreateEntry(name
);
650 i
->handler
= handler
;
651 i
->handlerData
= cbdataReference(handlerData
);
652 i
->request_time
= current_time
;
653 c
= new generic_cbdata(i
);
656 dnsSubmit(hashKeyStr(&i
->hash
), ipcacheHandleReply
, c
);
659 idnsALookup(hashKeyStr(&i
->hash
), ipcacheHandleReply
, c
);
663 /* initialize the ipcache */
668 debugs(14, 3, "Initializing IP Cache...");
669 memset(&IpcacheStats
, '\0', sizeof(IpcacheStats
));
670 memset(&lru_list
, '\0', sizeof(lru_list
));
671 /* test naming lookup */
673 if (!opt_dns_tests
) {
674 debugs(14, 4, "ipcache_init: Skipping DNS name lookup tests.");
675 } else if (!ipcache_testname()) {
676 fatal("ipcache_init: DNS name lookup tests failed.");
678 debugs(14, 1, "Successful DNS name lookup tests...");
681 memset(&static_addrs
, '\0', sizeof(ipcache_addrs
));
683 static_addrs
.in_addrs
= (IPAddress
*)xcalloc(1, sizeof(IPAddress
));
684 static_addrs
.in_addrs
->SetEmpty(); // properly setup the IPAddress!
685 static_addrs
.bad_mask
= (unsigned char *)xcalloc(1, sizeof(unsigned char));
686 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
687 (float) Config
.ipcache
.high
) / (float) 100);
688 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
689 (float) Config
.ipcache
.low
) / (float) 100);
690 n
= hashPrime(ipcache_high
/ 4);
691 ip_table
= hash_create((HASHCMP
*) strcmp
, n
, hash4
);
692 memDataInit(MEM_IPCACHE_ENTRY
, "ipcache_entry", sizeof(ipcache_entry
), 0);
696 ipcacheRegisterWithCacheManager(CacheManager
& manager
)
698 manager
.registerAction("ipcache",
699 "IP Cache Stats and Contents",
700 stat_ipcache_get
, 0, 1);
703 const ipcache_addrs
*
704 ipcache_gethostbyname(const char *name
, int flags
)
706 ipcache_entry
*i
= NULL
;
707 ipcache_addrs
*addrs
;
709 debugs(14, 3, "ipcache_gethostbyname: '" << name
<< "', flags=" << std::hex
<< flags
);
710 IpcacheStats
.requests
++;
711 i
= ipcache_get(name
);
715 } else if (ipcacheExpiredEntry(i
)) {
718 } else if (i
->flags
.negcached
) {
719 IpcacheStats
.negative_hits
++;
720 dns_error_message
= i
->error_message
;
724 i
->lastref
= squid_curtime
;
725 dns_error_message
= i
->error_message
;
729 dns_error_message
= NULL
;
731 if ((addrs
= ipcacheCheckNumeric(name
))) {
732 IpcacheStats
.numeric_hits
++;
736 IpcacheStats
.misses
++;
738 if (flags
& IP_LOOKUP_IF_MISS
)
739 ipcache_nbgethostbyname(name
, ipcacheHandleCnameRecurse
, NULL
);
745 ipcacheStatPrint(ipcache_entry
* i
, StoreEntry
* sentry
)
748 int count
= i
->addrs
.count
;
749 char buf
[MAX_IPSTRLEN
];
752 debugs(14, 0, HERE
<< "CRITICAL: sentry is NULL!");
756 debugs(14, 0, HERE
<< "CRITICAL: ipcache_entry is NULL!");
757 storeAppendPrintf(sentry
, "CRITICAL ERROR\n");
761 storeAppendPrintf(sentry
, " %-32.32s %c%c %6d %6d %2d(%2d)",
762 hashKeyStr(&i
->hash
),
763 i
->flags
.fromhosts
? 'H' : ' ',
764 i
->flags
.negcached
? 'N' : ' ',
765 (int) (squid_curtime
- i
->lastref
),
766 (int) ((i
->flags
.fromhosts
? -1 : i
->expires
- squid_curtime
)),
767 (int) i
->addrs
.count
,
768 (int) i
->addrs
.badcount
);
771 * Negative-cached entries have no IPs listed. */
772 if(i
->flags
.negcached
) {
773 storeAppendPrintf(sentry
, "\n");
778 * Cached entries have IPs listed with a BNF of: <IP> '-' ('OK'|'BAD') */
779 for (k
= 0; k
< count
; k
++) {
780 /* Display tidy-up: IPv6 are so big make the list vertical */
782 storeAppendPrintf(sentry
, " %45.45s-%3s\n",
783 i
->addrs
.in_addrs
[k
].NtoA(buf
,MAX_IPSTRLEN
),
784 i
->addrs
.bad_mask
[k
] ? "BAD" : "OK ");
786 storeAppendPrintf(sentry
, "%s %45.45s-%3s\n",
787 " ", /* blank-space indenting IP list */
788 i
->addrs
.in_addrs
[k
].NtoA(buf
,MAX_IPSTRLEN
),
789 i
->addrs
.bad_mask
[k
] ? "BAD" : "OK ");
793 /* process objects list */
795 stat_ipcache_get(StoreEntry
* sentry
)
798 assert(ip_table
!= NULL
);
799 storeAppendPrintf(sentry
, "IP Cache Statistics:\n");
800 storeAppendPrintf(sentry
, "IPcache Entries: %d\n",
801 memInUse(MEM_IPCACHE_ENTRY
));
802 storeAppendPrintf(sentry
, "IPcache Requests: %d\n",
803 IpcacheStats
.requests
);
804 storeAppendPrintf(sentry
, "IPcache Hits: %d\n",
806 storeAppendPrintf(sentry
, "IPcache Negative Hits: %d\n",
807 IpcacheStats
.negative_hits
);
808 storeAppendPrintf(sentry
, "IPcache Numeric Hits: %d\n",
809 IpcacheStats
.numeric_hits
);
810 storeAppendPrintf(sentry
, "IPcache Misses: %d\n",
811 IpcacheStats
.misses
);
812 storeAppendPrintf(sentry
, "IPcache Invalid Request: %d\n",
813 IpcacheStats
.invalid
);
814 storeAppendPrintf(sentry
, "\n\n");
815 storeAppendPrintf(sentry
, "IP Cache Contents:\n\n");
816 storeAppendPrintf(sentry
, " %-31.31s %3s %6s %6s %4s\n",
823 for (m
= lru_list
.head
; m
; m
= m
->next
) {
824 assert( m
->next
!= m
);
825 ipcacheStatPrint((ipcache_entry
*)m
->data
, sentry
);
830 * Takes two IPAddress arrays and merges them into a single array
831 * which is allocated dynamically to fit the number of unique addresses
833 \param aaddrs One list to merge
834 \param alen Size of list aaddrs
835 \param baddrs Other list to merge
836 \param alen Size of list baddrs
837 \param out Combined list of unique addresses (sorted with IPv6 first in IPv6-mode)
838 \param outlen Size of list out
841 ipcacheMergeIPLists(IPAddress
*aaddrs
, int alen
,
842 IPAddress
*baddrs
, int blen
,
843 IPAddress
**out
, int &outlen
)
847 IPAddress
*ip4ptrs
[255];
849 IPAddress
*ip6ptrs
[255];
854 memset(ip4ptrs
, 0, sizeof(IPAddress
*)*255);
856 memset(ip6ptrs
, 0, sizeof(IPAddress
*)*255);
859 // for each unique address in list A - grab ptr
860 for(t
= 0; t
< alen
; t
++) {
861 if(aaddrs
[t
].IsIPv4()) {
862 // check against IPv4 pruned list
863 for(c
= 0; c
<= num_ip4
; c
++) {
864 if(ip4ptrs
[c
] && aaddrs
[t
] == *(ip4ptrs
[c
]) ) break; // duplicate.
867 ip4ptrs
[num_ip4
] = &aaddrs
[t
];
872 else if(aaddrs
[t
].IsIPv6()) {
873 debugs(14,8, HERE
<< "A[" << t
<< "]=IPv6 " << aaddrs
[t
]);
874 // check against IPv6 pruned list
875 for(c
= 0; c
<= num_ip6
; c
++) {
876 if(ip6ptrs
[c
] && aaddrs
[t
] == *ip6ptrs
[c
]) break; // duplicate.
879 ip6ptrs
[num_ip6
] = &aaddrs
[t
];
886 // for each unique address in list B - grab ptr
887 for(t
= 0; t
< blen
; t
++) {
888 if(baddrs
[t
].IsIPv4()) {
889 // check against IPv4 pruned list
890 for(c
= 0; c
<= num_ip4
; c
++) {
891 if(ip4ptrs
[c
] && baddrs
[t
] == *ip4ptrs
[c
]) break; // duplicate.
894 ip4ptrs
[num_ip4
] = &baddrs
[t
];
899 else if(baddrs
[t
].IsIPv6()) {
900 // check against IPv6 pruned list
901 for(c
= 0; c
<= num_ip6
; c
++) {
902 if(ip6ptrs
[c
] && baddrs
[t
] == *ip6ptrs
[c
]) break; // duplicate.
905 ip6ptrs
[num_ip6
] = &baddrs
[t
];
912 fc
= num_ip6
+ num_ip4
;
916 debugs(14, 5, "ipcacheMergeIPLists: Merge " << alen
<< "+" << blen
<< " into " << fc
<< " unique IPs.");
918 // copy the old IPs into the new list buffer.
919 (*out
) = (IPAddress
*)xcalloc(fc
, sizeof(IPAddress
));
925 /* IPv6 are preferred (tried first) over IPv4 */
927 for(int l
= 0; outlen
< num_ip6
; l
++, outlen
++) {
928 (*out
)[outlen
] = *ip6ptrs
[l
];
929 debugs(14, 5, "ipcacheMergeIPLists: #" << outlen
<< " " << (*out
)[outlen
] );
931 #endif /* USE_IPV6 */
933 for(int l
= 0; outlen
< num_ip4
; l
++, outlen
++) {
934 (*out
)[outlen
] = *ip4ptrs
[l
];
935 debugs(14, 5, "ipcacheMergeIPLists: #" << outlen
<< " " << (*out
)[outlen
] );
938 assert(outlen
== fc
); // otherwise something broke badly!
942 ipcacheHandleCnameRecurse(const ipcache_addrs
*addrs
, void *cbdata
)
944 ipcache_entry
*i
= NULL
;
946 IPAddress
*tmpbuf
= NULL
;
949 generic_cbdata
* gcb
= (generic_cbdata
*)cbdata
;
950 // count of addrs at parent and child (REQ as .count is a char type!)
951 int ccount
= 0, pcount
= 0;
953 debugs(14, 5, "ipcacheHandleCnameRecurse: Handling basic A/AAAA response.");
955 /* IFF no CNAME recursion being processed. do nothing. */
962 // make sure we are actualy waiting for a CNAME callback to be run.
963 assert(i
->cname_wait
> 0);
964 // count this event. its being handled.
967 pname
= (char*)i
->hash
.key
;
968 assert(pname
!= NULL
);
970 debugs(14, 5, "ipcacheHandleCnameRecurse: Handling CNAME recursion. CBDATA('" << gcb
->data
<< "')='" << pname
<< "' -> " << std::hex
<< i
);
973 return; // Parent has expired. Don't merge, just leave for future Ref:
976 /* IFF addrs is NULL (Usually an Error or Timeout occured on lookup.) */
977 /* Ignore it and HOPE that we got some Additional records to use. */
981 ccount
= (0+ addrs
->count
);
982 pcount
= (0+ i
->addrs
.count
);
985 /* IFF no CNAME results. do none of the processing BUT finish anyway. */
988 debugs(14, 5, "ipcacheHandleCnameRecurse: Merge IP Lists for " << pname
<< " (" << pcount
<< "+" << ccount
<< ")");
990 /* add new IP records to entry */
991 tmpbuf
= i
->addrs
.in_addrs
;
992 i
->addrs
.in_addrs
= NULL
;
993 ipcacheMergeIPLists(tmpbuf
, pcount
, addrs
->in_addrs
, ccount
, &(i
->addrs
.in_addrs
), fc
);
994 debugs(14,8, HERE
<< "in=" << tmpbuf
<< ", out=" << i
->addrs
.in_addrs
);
999 /* IFF the parent initial lookup was given Additional records with A */
1000 // clear the 'bad IP mask'
1001 safe_free(i
->addrs
.bad_mask
);
1003 // create a new bad IP mask to fit the new size needed.
1005 i
->addrs
.bad_mask
= (unsigned char*)xcalloc(fc
, sizeof(unsigned char));
1006 memset(i
->addrs
.bad_mask
, 0, sizeof(unsigned char)*fc
);
1010 i
->addrs
.count
= (unsigned char) fc
;
1012 i
->addrs
.count
= 255;
1014 if (ttl
== 0 || ttl
> Config
.positiveDnsTtl
)
1015 ttl
= Config
.positiveDnsTtl
;
1017 if (ttl
< Config
.negativeDnsTtl
)
1018 ttl
= Config
.negativeDnsTtl
;
1020 i
->expires
= squid_curtime
+ ttl
;
1022 i
->flags
.negcached
= 0;
1026 i
->addrs
.badcount
= 0;
1030 i
->error_message
= xstrdup("No DNS Records");
1033 /* finish the lookup we were doing on parent when we got side-tracked for CNAME loop */
1034 if(i
->cname_wait
== 0) {
1038 // else still more CNAME to be found.
1042 ipcacheInvalidate(const char *name
)
1046 if ((i
= ipcache_get(name
)) == NULL
)
1049 i
->expires
= squid_curtime
;
1052 * NOTE, don't call ipcacheRelease here becuase we might be here due
1053 * to a thread started from a callback.
1058 ipcacheInvalidateNegative(const char *name
)
1062 if ((i
= ipcache_get(name
)) == NULL
)
1065 if (i
->flags
.negcached
)
1066 i
->expires
= squid_curtime
;
1069 * NOTE, don't call ipcacheRelease here becuase we might be here due
1070 * to a thread started from a callback.
1075 ipcacheCheckNumeric(const char *name
)
1079 /* check if it's already a IP address in text form. */
1081 /* it may be IPv6-wrapped */
1082 if(name
[0] == '[') {
1083 char *tmp
= xstrdup(&name
[1]);
1084 tmp
[strlen(tmp
)-1] = '\0';
1091 else if (!(ip
= name
))
1094 debugs(14, 4, "ipcacheCheckNumeric: HIT_BYPASS for '" << name
<< "' == " << ip
);
1096 static_addrs
.count
= 1;
1098 static_addrs
.cur
= 0;
1100 static_addrs
.in_addrs
[0] = ip
;
1102 static_addrs
.bad_mask
[0] = FALSE
;
1104 static_addrs
.badcount
= 0;
1106 return &static_addrs
;
1110 ipcacheLockEntry(ipcache_entry
* i
)
1112 if (i
->locks
++ == 0) {
1113 dlinkDelete(&i
->lru
, &lru_list
);
1114 dlinkAdd(i
, &i
->lru
, &lru_list
);
1119 ipcacheUnlockEntry(ipcache_entry
* i
)
1122 debugs(14, 1, "WARNING: ipcacheEntry unlocked with no lock! locks=" << i
->locks
);
1128 if (ipcacheExpiredEntry(i
))
1133 ipcacheCycleAddr(const char *name
, ipcache_addrs
* ia
)
1140 if ((i
= ipcache_get(name
)) == NULL
)
1143 if (i
->flags
.negcached
)
1149 for (k
= 0; k
< ia
->count
; k
++) {
1150 if (++ia
->cur
== ia
->count
)
1153 if (!ia
->bad_mask
[ia
->cur
])
1157 if (k
== ia
->count
) {
1158 /* All bad, reset to All good */
1159 debugs(14, 3, "ipcacheCycleAddr: Changing ALL " << name
<< " addrs from BAD to OK");
1161 for (k
= 0; k
< ia
->count
; k
++)
1162 ia
->bad_mask
[k
] = 0;
1169 debugs(14, 3, "ipcacheCycleAddr: " << name
<< " now at " << ia
->in_addrs
[ia
->cur
] << " (" << ia
->cur
<< " of " << ia
->count
<< ")");
1173 * Marks the given address as BAD and calls ipcacheCycleAddr to
1174 * advance the current pointer to the next OK address.
1177 ipcacheMarkBadAddr(const char *name
, IPAddress
&addr
)
1183 if ((i
= ipcache_get(name
)) == NULL
)
1188 for (k
= 0; k
< (int) ia
->count
; k
++)
1190 if (addr
== ia
->in_addrs
[k
] )
1194 if (k
== (int) ia
->count
) /* not found */
1197 if (!ia
->bad_mask
[k
])
1199 ia
->bad_mask
[k
] = TRUE
;
1201 i
->expires
= XMIN(squid_curtime
+ XMAX((time_t)60, Config
.negativeDnsTtl
), i
->expires
);
1202 debugs(14, 2, "ipcacheMarkBadAddr: " << name
<< " " << addr
);
1205 ipcacheCycleAddr(name
, ia
);
1210 ipcacheMarkGoodAddr(const char *name
, IPAddress
&addr
)
1216 if ((i
= ipcache_get(name
)) == NULL
)
1221 for (k
= 0; k
< (int) ia
->count
; k
++)
1223 if (addr
== ia
->in_addrs
[k
])
1227 if (k
== (int) ia
->count
) /* not found */
1230 if (!ia
->bad_mask
[k
]) /* already OK */
1233 ia
->bad_mask
[k
] = FALSE
;
1237 debugs(14, 2, "ipcacheMarkGoodAddr: " << name
<< " " << addr
);
1241 ipcacheFreeEntry(void *data
)
1243 ipcache_entry
*i
= (ipcache_entry
*)data
;
1244 safe_free(i
->addrs
.in_addrs
);
1245 safe_free(i
->addrs
.bad_mask
);
1246 safe_free(i
->hash
.key
);
1247 safe_free(i
->error_message
);
1248 memFree(i
, MEM_IPCACHE_ENTRY
);
1252 ipcacheFreeMemory(void)
1254 hashFreeItems(ip_table
, ipcacheFreeEntry
);
1255 hashFreeMemory(ip_table
);
1259 /* Recalculate IP cache size upon reconfigure */
1261 ipcache_restart(void)
1263 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
1264 (float) Config
.ipcache
.high
) / (float) 100);
1265 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
1266 (float) Config
.ipcache
.low
) / (float) 100);
1267 purge_entries_fromhosts();
1271 * adds a "static" entry from /etc/hosts.
1272 * returns 0 upon success, 1 if the ip address is invalid
1275 ipcacheAddEntryFromHosts(const char *name
, const char *ipaddr
)
1281 if (!(ip
= ipaddr
)) {
1283 if (strchr(ipaddr
, ':') && strspn(ipaddr
, "0123456789abcdefABCDEF:") == strlen(ipaddr
)) {
1284 debugs(14, 3, "ipcacheAddEntryFromHosts: Skipping IPv6 address '" << ipaddr
<< "'");
1286 debugs(14, 1, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr
<< "'");
1289 debugs(14, 1, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr
<< "'");
1295 if ((i
= ipcache_get(name
))) {
1296 if (1 == i
->flags
.fromhosts
) {
1297 ipcacheUnlockEntry(i
);
1298 } else if (i
->locks
> 0) {
1299 debugs(14, 1, "ipcacheAddEntryFromHosts: can't add static entry for locked name '" << name
<< "'");
1306 i
= ipcacheCreateEntry(name
);
1309 i
->addrs
.badcount
= 0;
1311 i
->addrs
.in_addrs
= (IPAddress
*)xcalloc(1, sizeof(IPAddress
));
1312 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(1, sizeof(unsigned char));
1313 i
->addrs
.in_addrs
[0] = ip
;
1314 i
->addrs
.bad_mask
[0] = FALSE
;
1315 i
->flags
.fromhosts
= 1;
1317 ipcacheLockEntry(i
);
1323 * The function to return the ip cache statistics to via SNMP
1327 snmp_netIpFn(variable_list
* Var
, snint
* ErrP
)
1329 variable_list
*Answer
= NULL
;
1330 debugs(49, 5, "snmp_netIpFn: Processing request:");
1331 snmpDebugOid(5, Var
->name
, Var
->name_length
);
1332 *ErrP
= SNMP_ERR_NOERROR
;
1334 switch (Var
->name
[LEN_SQ_NET
+ 1]) {
1337 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1338 memInUse(MEM_IPCACHE_ENTRY
),
1343 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1344 IpcacheStats
.requests
,
1349 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1355 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1361 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1362 IpcacheStats
.negative_hits
,
1367 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1368 IpcacheStats
.misses
,
1373 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1379 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
1385 *ErrP
= SNMP_ERR_NOSUCHNAME
;
1386 snmp_var_free(Answer
);
1393 #endif /*SQUID_SNMP */