3
* $Id: ipcache.cc,v 1.258 2006/08/21 00:50:41 robertc 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"
44
typedef struct _ipcache_entry ipcache_entry;
48
hash_link hash; /* must be first */
56
struct timeval request_time;
63
unsigned int negcached:
66
unsigned int fromhosts:
84
static dlink_list lru_list;
86
static FREE ipcacheFreeEntry;
88
static HLPCB ipcacheHandleReply;
90
static IDNSCB ipcacheHandleReply;
92
static IPH dummy_handler;
93
static int ipcacheExpiredEntry(ipcache_entry *);
94
static int ipcache_testname(void);
96
static int ipcacheParse(ipcache_entry *, const char *buf);
98
static int ipcacheParse(ipcache_entry *, rfc1035_rr *, int, const char *error);
100
static ipcache_entry *ipcache_get(const char *);
101
static void ipcacheLockEntry(ipcache_entry *);
102
static void ipcacheStatPrint(ipcache_entry *, StoreEntry *);
103
static void ipcacheUnlockEntry(ipcache_entry *);
104
static void ipcacheRelease(ipcache_entry *);
106
static ipcache_addrs static_addrs;
107
static hash_table *ip_table = NULL;
109
static long ipcache_low = 180;
110
static long ipcache_high = 200;
112
#if LIBRESOLV_DNS_TTL_HACK
113
extern int _dns_ttl_;
117
ipcache_testname(void)
120
debug(14, 1) ("Performing DNS Tests...\n");
122
if ((w = Config.dns_testname_list) == NULL)
125
for (; w; w = w->next) {
126
if (gethostbyname(w->key) != NULL)
133
/* removes the given ipcache entry */
135
ipcacheRelease(ipcache_entry * i)
137
debug(14, 3) ("ipcacheRelease: Releasing entry for '%s'\n", (const char *) i->hash.key);
138
hash_remove_link(ip_table, (hash_link *) i);
139
dlinkDelete(&i->lru, &lru_list);
143
static ipcache_entry *
144
ipcache_get(const char *name)
146
if (ip_table != NULL)
147
return (ipcache_entry *) hash_lookup(ip_table, name);
153
ipcacheExpiredEntry(ipcache_entry * i)
155
/* all static entries are locked, so this takes care of them too */
160
if (i->addrs.count == 0)
161
if (0 == i->flags.negcached)
164
if (i->expires > squid_curtime)
171
ipcache_purgelru(void *voidnotused)
174
dlink_node *prev = NULL;
177
eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1);
179
for (m = lru_list.tail; m; m = prev) {
180
if (memInUse(MEM_IPCACHE_ENTRY) < ipcache_low)
185
i = (ipcache_entry *)m->data;
195
debug(14, 9) ("ipcache_purgelru: removed %d entries\n", removed);
198
/* purges entries added from /etc/hosts (or whatever). */
200
purge_entries_fromhosts(void)
202
dlink_node *m = lru_list.head;
203
ipcache_entry *i = NULL, *t;
206
if (i != NULL) { /* need to delay deletion */
207
ipcacheRelease(i); /* we just override locks */
211
t = (ipcache_entry*)m->data;
213
if (t->flags.fromhosts)
223
/* create blank ipcache_entry */
224
static ipcache_entry *
225
ipcacheCreateEntry(const char *name)
227
static ipcache_entry *i;
228
i = (ipcache_entry *)memAllocate(MEM_IPCACHE_ENTRY);
229
i->hash.key = xstrdup(name);
230
i->expires = squid_curtime + Config.negativeDnsTtl;
235
ipcacheAddEntry(ipcache_entry * i)
237
hash_link *e = (hash_link *)hash_lookup(ip_table, i->hash.key);
240
/* avoid colission */
241
ipcache_entry *q = (ipcache_entry *) e;
245
hash_join(ip_table, &i->hash);
246
dlinkAdd(i, &i->lru, &lru_list);
247
i->lastref = squid_curtime;
250
/* walks down the pending list, calling handlers */
252
ipcacheCallback(ipcache_entry * i)
254
IPH *callback = i->handler;
256
i->lastref = squid_curtime;
263
callback = i->handler;
267
if (cbdataReferenceValidDone(i->handlerData, &cbdata)) {
268
dns_error_message = i->error_message;
269
callback(i->flags.negcached ? NULL : &i->addrs, cbdata);
272
ipcacheUnlockEntry(i);
277
ipcacheParse(ipcache_entry *i, const char *inbuf)
279
LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
284
const char *name = (const char *)i->hash.key;
285
i->expires = squid_curtime + Config.negativeDnsTtl;
286
i->flags.negcached = 1;
287
safe_free(i->addrs.in_addrs);
288
safe_free(i->addrs.bad_mask);
289
safe_free(i->error_message);
293
debug(14, 1) ("ipcacheParse: Got <NULL> reply\n");
294
i->error_message = xstrdup("Internal Error");
298
xstrncpy(buf, inbuf, DNS_INBUF_SZ);
299
debug(14, 5) ("ipcacheParse: parsing: {%s}\n", buf);
300
token = strtok(buf, w_space);
303
debug(14, 1) ("ipcacheParse: expecting result, got '%s'\n", inbuf);
304
i->error_message = xstrdup("Internal Error");
308
if (0 == strcmp(token, "$fail")) {
309
token = strtok(NULL, "\n");
310
assert(NULL != token);
311
i->error_message = xstrdup(token);
315
if (0 != strcmp(token, "$addr")) {
316
debug(14, 1) ("ipcacheParse: expecting '$addr', got '%s' in response to '%s'\n", inbuf, name);
317
i->error_message = xstrdup("Internal Error");
321
token = strtok(NULL, w_space);
324
debug(14, 1) ("ipcacheParse: expecting TTL, got '%s' in response to '%s'\n", inbuf, name);
325
i->error_message = xstrdup("Internal Error");
331
while (NULL != (token = strtok(NULL, w_space))) {
341
i->addrs.in_addrs = (struct IN_ADDR *)xcalloc(ipcount, sizeof(struct IN_ADDR));
342
i->addrs.bad_mask = (unsigned char *)xcalloc(ipcount, sizeof(unsigned char));
344
for (j = 0, k = 0; k < ipcount; k++) {
345
if (safe_inet_addr(A[k], &i->addrs.in_addrs[j]))
348
debug(14, 1) ("ipcacheParse: Invalid IP address '%s' in response to '%s'\n", A[k], name);
351
i->addrs.count = (unsigned char) j;
354
if (i->addrs.count <= 0) {
355
debug(14, 1) ("ipcacheParse: No addresses in response to '%s'\n", name);
359
if (ttl == 0 || ttl > Config.positiveDnsTtl)
360
ttl = Config.positiveDnsTtl;
362
if (ttl < Config.negativeDnsTtl)
363
ttl = Config.negativeDnsTtl;
365
i->expires = squid_curtime + ttl;
367
i->flags.negcached = 0;
369
return i->addrs.count;
374
ipcacheParse(ipcache_entry *i, rfc1035_rr * answers, int nr, const char *error_message)
380
const char *name = (const char *)i->hash.key;
381
i->expires = squid_curtime + Config.negativeDnsTtl;
382
i->flags.negcached = 1;
383
safe_free(i->addrs.in_addrs);
384
safe_free(i->addrs.bad_mask);
385
safe_free(i->error_message);
389
debug(14, 3) ("ipcacheParse: Lookup failed '%s' for '%s'\n",
390
error_message, (const char *)i->hash.key);
391
i->error_message = xstrdup(error_message);
396
debug(14, 3) ("ipcacheParse: No DNS records in response to '%s'\n", name);
397
i->error_message = xstrdup("No DNS records");
403
for (k = 0; k < nr; k++) {
404
if (answers[k].type != RFC1035_TYPE_A)
407
if (answers[k]._class != RFC1035_CLASS_IN)
410
if (answers[k].rdlength != 4) {
411
debug(14, 1)("ipcacheParse: Invalid IP address in response to '%s'\n", name);
419
debug(14, 1) ("ipcacheParse: No Address records in response to '%s'\n", name);
420
i->error_message = xstrdup("No Address records");
424
i->addrs.in_addrs = (struct IN_ADDR *)xcalloc(na, sizeof(struct IN_ADDR));
425
i->addrs.bad_mask = (unsigned char *)xcalloc(na, sizeof(unsigned char));
427
for (j = 0, k = 0; k < nr; k++) {
428
if (answers[k]._class != RFC1035_CLASS_IN)
431
if (answers[k].type == RFC1035_TYPE_A) {
432
if (answers[k].rdlength != 4)
435
xmemcpy(&i->addrs.in_addrs[j++], answers[k].rdata, 4);
437
debug(14, 3) ("ipcacheParse: #%d %s\n",
439
inet_ntoa(i->addrs.in_addrs[j - 1]));
440
} else if (answers[k].type != RFC1035_TYPE_CNAME)
443
if (ttl == 0 || (int) answers[k].ttl < ttl)
444
ttl = answers[k].ttl;
451
i->addrs.count = (unsigned char) na;
453
i->addrs.count = 255;
455
if (ttl == 0 || ttl > Config.positiveDnsTtl)
456
ttl = Config.positiveDnsTtl;
458
if (ttl < Config.negativeDnsTtl)
459
ttl = Config.negativeDnsTtl;
461
i->expires = squid_curtime + ttl;
463
i->flags.negcached = 0;
465
return i->addrs.count;
472
ipcacheHandleReply(void *data, char *reply)
474
ipcacheHandleReply(void *data, rfc1035_rr * answers, int na, const char *error_message)
478
static_cast<generic_cbdata *>(data)->unwrap(&i);
479
IpcacheStats.replies++;
480
statHistCount(&statCounter.dns.svc_time,
481
tvSubMsec(i->request_time, current_time));
484
ipcacheParse(i, reply);
487
ipcacheParse(i, answers, na, error_message);
495
ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)
497
ipcache_entry *i = NULL;
498
const ipcache_addrs *addrs = NULL;
500
assert(handler != NULL);
501
debug(14, 4) ("ipcache_nbgethostbyname: Name '%s'.\n", name);
502
IpcacheStats.requests++;
504
if (name == NULL || name[0] == '\0') {
505
debug(14, 4) ("ipcache_nbgethostbyname: Invalid name!\n");
506
dns_error_message = "Invalid hostname";
507
handler(NULL, handlerData);
511
if ((addrs = ipcacheCheckNumeric(name))) {
512
dns_error_message = NULL;
513
handler(addrs, handlerData);
517
i = ipcache_get(name);
522
} else if (ipcacheExpiredEntry(i)) {
523
/* hit, but expired -- bummer */
528
debug(14, 4) ("ipcache_nbgethostbyname: HIT for '%s'\n", name);
530
if (i->flags.negcached)
531
IpcacheStats.negative_hits++;
535
i->handler = handler;
537
i->handlerData = cbdataReference(handlerData);
544
debug(14, 5) ("ipcache_nbgethostbyname: MISS for '%s'\n", name);
545
IpcacheStats.misses++;
546
i = ipcacheCreateEntry(name);
547
i->handler = handler;
548
i->handlerData = cbdataReference(handlerData);
549
i->request_time = current_time;
550
c = new generic_cbdata(i);
553
dnsSubmit(hashKeyStr(&i->hash), ipcacheHandleReply, c);
556
idnsALookup(hashKeyStr(&i->hash), ipcacheHandleReply, c);
560
/* initialize the ipcache */
565
debug(14, 3) ("Initializing IP Cache...\n");
566
memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
567
memset(&lru_list, '\0', sizeof(lru_list));
568
/* test naming lookup */
570
if (!opt_dns_tests) {
571
debug(14, 4) ("ipcache_init: Skipping DNS name lookup tests.\n");
572
} else if (!ipcache_testname()) {
573
fatal("ipcache_init: DNS name lookup tests failed.");
575
debug(14, 1) ("Successful DNS name lookup tests...\n");
578
memset(&static_addrs, '\0', sizeof(ipcache_addrs));
580
static_addrs.in_addrs = (struct IN_ADDR *)xcalloc(1, sizeof(struct IN_ADDR));
581
static_addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
582
ipcache_high = (long) (((float) Config.ipcache.size *
583
(float) Config.ipcache.high) / (float) 100);
584
ipcache_low = (long) (((float) Config.ipcache.size *
585
(float) Config.ipcache.low) / (float) 100);
586
n = hashPrime(ipcache_high / 4);
587
ip_table = hash_create((HASHCMP *) strcmp, n, hash4);
588
memDataInit(MEM_IPCACHE_ENTRY, "ipcache_entry", sizeof(ipcache_entry), 0);
592
ipcacheRegisterWithCacheManager(CacheManager & manager)
594
manager.registerAction("ipcache",
595
"IP Cache Stats and Contents",
596
stat_ipcache_get, 0, 1);
599
const ipcache_addrs *
600
ipcache_gethostbyname(const char *name, int flags)
602
ipcache_entry *i = NULL;
603
ipcache_addrs *addrs;
605
debug(14, 3) ("ipcache_gethostbyname: '%s', flags=%x\n", name, flags);
606
IpcacheStats.requests++;
607
i = ipcache_get(name);
611
} else if (ipcacheExpiredEntry(i)) {
614
} else if (i->flags.negcached) {
615
IpcacheStats.negative_hits++;
616
dns_error_message = i->error_message;
620
i->lastref = squid_curtime;
621
dns_error_message = i->error_message;
625
dns_error_message = NULL;
627
if ((addrs = ipcacheCheckNumeric(name)))
630
IpcacheStats.misses++;
632
if (flags & IP_LOOKUP_IF_MISS)
633
ipcache_nbgethostbyname(name, dummy_handler, NULL);
639
ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
642
storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)",
643
hashKeyStr(&i->hash),
644
i->flags.fromhosts ? 'H' : ' ',
645
i->flags.negcached ? 'N' : ' ',
646
(int) (squid_curtime - i->lastref),
647
(int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
648
(int) i->addrs.count,
649
(int) i->addrs.badcount);
651
for (k = 0; k < (int) i->addrs.count; k++) {
652
storeAppendPrintf(sentry, " %15s-%3s", inet_ntoa(i->addrs.in_addrs[k]),
653
i->addrs.bad_mask[k] ? "BAD" : "OK ");
656
storeAppendPrintf(sentry, "\n");
659
/* process objects list */
661
stat_ipcache_get(StoreEntry * sentry)
664
assert(ip_table != NULL);
665
storeAppendPrintf(sentry, "IP Cache Statistics:\n");
666
storeAppendPrintf(sentry, "IPcache Entries: %d\n",
667
memInUse(MEM_IPCACHE_ENTRY));
668
storeAppendPrintf(sentry, "IPcache Requests: %d\n",
669
IpcacheStats.requests);
670
storeAppendPrintf(sentry, "IPcache Hits: %d\n",
672
storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n",
673
IpcacheStats.negative_hits);
674
storeAppendPrintf(sentry, "IPcache Misses: %d\n",
675
IpcacheStats.misses);
676
storeAppendPrintf(sentry, "\n\n");
677
storeAppendPrintf(sentry, "IP Cache Contents:\n\n");
678
storeAppendPrintf(sentry, " %-29.29s %3s %6s %6s %1s\n",
685
for (m = lru_list.head; m; m = m->next)
686
ipcacheStatPrint((ipcache_entry *)m->data, sentry);
690
dummy_handler(const ipcache_addrs * addrsnotused, void *datanotused)
696
ipcacheInvalidate(const char *name)
700
if ((i = ipcache_get(name)) == NULL)
703
i->expires = squid_curtime;
706
* NOTE, don't call ipcacheRelease here becuase we might be here due
707
* to a thread started from a callback.
712
ipcacheInvalidateNegative(const char *name)
716
if ((i = ipcache_get(name)) == NULL)
719
if (i->flags.negcached)
720
i->expires = squid_curtime;
723
* NOTE, don't call ipcacheRelease here becuase we might be here due
724
* to a thread started from a callback.
729
ipcacheCheckNumeric(const char *name)
733
/* check if it's already a IP address in text form. */
735
if (!safe_inet_addr(name, &ip))
738
static_addrs.count = 1;
740
static_addrs.cur = 0;
742
static_addrs.in_addrs[0].s_addr = ip.s_addr;
744
static_addrs.bad_mask[0] = FALSE;
746
static_addrs.badcount = 0;
748
return &static_addrs;
752
ipcacheLockEntry(ipcache_entry * i)
754
if (i->locks++ == 0) {
755
dlinkDelete(&i->lru, &lru_list);
756
dlinkAdd(i, &i->lru, &lru_list);
761
ipcacheUnlockEntry(ipcache_entry * i)
763
assert(i->locks > 0);
766
if (ipcacheExpiredEntry(i))
771
ipcacheCycleAddr(const char *name, ipcache_addrs * ia)
778
if ((i = ipcache_get(name)) == NULL)
781
if (i->flags.negcached)
787
for (k = 0; k < ia->count; k++) {
788
if (++ia->cur == ia->count)
791
if (!ia->bad_mask[ia->cur])
795
if (k == ia->count) {
796
/* All bad, reset to All good */
797
debug(14, 3) ("ipcacheCycleAddr: Changing ALL %s addrs from BAD to OK\n",
800
for (k = 0; k < ia->count; k++)
808
debug(14, 3) ("ipcacheCycleAddr: %s now at %s\n", name,
809
inet_ntoa(ia->in_addrs[ia->cur]));
813
* Marks the given address as BAD and calls ipcacheCycleAddr to
814
* advance the current pointer to the next OK address.
818
ipcacheMarkBadAddr(const char *name, struct IN_ADDR addr)
824
if ((i = ipcache_get(name)) == NULL)
829
for (k = 0; k < (int) ia->count; k++)
831
if (ia->in_addrs[k].s_addr == addr.s_addr)
835
if (k == (int) ia->count) /* not found */
838
if (!ia->bad_mask[k])
840
ia->bad_mask[k] = TRUE;
842
i->expires = XMIN(squid_curtime + XMAX((time_t)60, Config.negativeDnsTtl), i->expires);
843
debug(14, 2) ("ipcacheMarkBadAddr: %s [%s]\n", name, inet_ntoa(addr));
846
ipcacheCycleAddr(name, ia);
851
ipcacheMarkGoodAddr(const char *name, struct IN_ADDR addr)
857
if ((i = ipcache_get(name)) == NULL)
862
for (k = 0; k < (int) ia->count; k++)
864
if (ia->in_addrs[k].s_addr == addr.s_addr)
868
if (k == (int) ia->count) /* not found */
871
if (!ia->bad_mask[k]) /* already OK */
874
ia->bad_mask[k] = FALSE;
878
debug(14, 2) ("ipcacheMarkGoodAddr: %s [%s]\n", name, inet_ntoa(addr));
882
ipcacheFreeEntry(void *data)
884
ipcache_entry *i = (ipcache_entry *)data;
885
safe_free(i->addrs.in_addrs);
886
safe_free(i->addrs.bad_mask);
887
safe_free(i->hash.key);
888
safe_free(i->error_message);
889
memFree(i, MEM_IPCACHE_ENTRY);
893
ipcacheFreeMemory(void)
895
hashFreeItems(ip_table, ipcacheFreeEntry);
896
hashFreeMemory(ip_table);
900
/* Recalculate IP cache size upon reconfigure */
902
ipcache_restart(void)
904
ipcache_high = (long) (((float) Config.ipcache.size *
905
(float) Config.ipcache.high) / (float) 100);
906
ipcache_low = (long) (((float) Config.ipcache.size *
907
(float) Config.ipcache.low) / (float) 100);
908
purge_entries_fromhosts();
912
* adds a "static" entry from /etc/hosts.
913
* returns 0 upon success, 1 if the ip address is invalid
916
ipcacheAddEntryFromHosts(const char *name, const char *ipaddr)
922
if (!safe_inet_addr(ipaddr, &ip)) {
923
if (strchr(ipaddr, ':') && strspn(ipaddr, "0123456789abcdefABCDEF:") == strlen(ipaddr)) {
924
debug(14, 3) ("ipcacheAddEntryFromHosts: Skipping IPv6 address '%s'\n", ipaddr);
926
debug(14, 1) ("ipcacheAddEntryFromHosts: Bad IP address '%s'\n",
933
if ((i = ipcache_get(name))) {
934
if (1 == i->flags.fromhosts) {
935
ipcacheUnlockEntry(i);
936
} else if (i->locks > 0) {
937
debug(14, 1) ("ipcacheAddEntryFromHosts: can't add static entry"
938
" for locked name '%s'\n", name);
945
i = ipcacheCreateEntry(name);
948
i->addrs.badcount = 0;
950
i->addrs.in_addrs = (struct IN_ADDR *)xcalloc(1, sizeof(struct IN_ADDR));
951
i->addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
952
i->addrs.in_addrs[0].s_addr = ip.s_addr;
953
i->addrs.bad_mask[0] = FALSE;
954
i->flags.fromhosts = 1;
962
* The function to return the ip cache statistics to via SNMP
966
snmp_netIpFn(variable_list * Var, snint * ErrP)
968
variable_list *Answer = NULL;
969
debug(49, 5) ("snmp_netIpFn: Processing request:\n");
970
snmpDebugOid(5, Var->name, Var->name_length);
971
*ErrP = SNMP_ERR_NOERROR;
973
switch (Var->name[LEN_SQ_NET + 1]) {
976
Answer = snmp_var_new_integer(Var->name, Var->name_length,
977
memInUse(MEM_IPCACHE_ENTRY),
982
Answer = snmp_var_new_integer(Var->name, Var->name_length,
983
IpcacheStats.requests,
988
Answer = snmp_var_new_integer(Var->name, Var->name_length,
994
Answer = snmp_var_new_integer(Var->name, Var->name_length,
1000
Answer = snmp_var_new_integer(Var->name, Var->name_length,
1001
IpcacheStats.negative_hits,
1006
Answer = snmp_var_new_integer(Var->name, Var->name_length,
1007
IpcacheStats.misses,
1012
Answer = snmp_var_new_integer(Var->name, Var->name_length,
1018
Answer = snmp_var_new_integer(Var->name, Var->name_length,
1024
*ErrP = SNMP_ERR_NOSUCHNAME;
1025
snmp_var_free(Answer);
1032
#endif /*SQUID_SNMP */