3
* $Id: fqdncache.cc,v 1.170 2006/08/21 00:50:41 robertc Exp $
5
* DEBUG: section 35 FQDN 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
#define FQDN_LOW_WATER 90
45
#define FQDN_HIGH_WATER 95
47
typedef struct _fqdncache_entry fqdncache_entry;
49
struct _fqdncache_entry
51
hash_link hash; /* must be first */
54
unsigned char name_count;
55
char *names[FQDN_MAX_NAMES + 1];
60
struct timeval request_time;
67
unsigned int negcached:
70
unsigned int fromhosts:
88
static dlink_list lru_list;
91
static HLPCB fqdncacheHandleReply;
92
static int fqdncacheParse(fqdncache_entry *, const char *buf);
94
static IDNSCB fqdncacheHandleReply;
95
static int fqdncacheParse(fqdncache_entry *, rfc1035_rr *, int, const char *error_message);
97
static void fqdncacheRelease(fqdncache_entry *);
98
static fqdncache_entry *fqdncacheCreateEntry(const char *name);
99
static void fqdncacheCallback(fqdncache_entry *);
100
static fqdncache_entry *fqdncache_get(const char *);
101
static FQDNH dummy_handler;
102
static int fqdncacheExpiredEntry(const fqdncache_entry *);
103
static void fqdncacheLockEntry(fqdncache_entry * f);
104
static void fqdncacheUnlockEntry(fqdncache_entry * f);
105
static FREE fqdncacheFreeEntry;
106
static void fqdncacheAddEntry(fqdncache_entry * f);
108
static hash_table *fqdn_table = NULL;
110
static long fqdncache_low = 180;
111
static long fqdncache_high = 200;
113
/* removes the given fqdncache entry */
115
fqdncacheRelease(fqdncache_entry * f)
118
hash_remove_link(fqdn_table, (hash_link *) f);
120
for (k = 0; k < (int) f->name_count; k++)
121
safe_free(f->names[k]);
123
debug(35, 5) ("fqdncacheRelease: Released FQDN record for '%s'.\n",
124
hashKeyStr(&f->hash));
126
dlinkDelete(&f->lru, &lru_list);
128
safe_free(f->hash.key);
130
safe_free(f->error_message);
132
memFree(f, MEM_FQDNCACHE_ENTRY);
135
/* return match for given name */
136
static fqdncache_entry *
137
fqdncache_get(const char *name)
140
static fqdncache_entry *f;
144
if ((e = (hash_link *)hash_lookup(fqdn_table, name)) != NULL)
145
f = (fqdncache_entry *) e;
152
fqdncacheExpiredEntry(const fqdncache_entry * f)
154
/* all static entries are locked, so this takes care of them too */
159
if (f->expires > squid_curtime)
166
fqdncache_purgelru(void *notused)
169
dlink_node *prev = NULL;
172
eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 10.0, 1);
174
for (m = lru_list.tail; m; m = prev) {
175
if (memInUse(MEM_FQDNCACHE_ENTRY) < fqdncache_low)
180
f = (fqdncache_entry *)m->data;
190
debug(35, 9) ("fqdncache_purgelru: removed %d entries\n", removed);
194
purge_entries_fromhosts(void)
196
dlink_node *m = lru_list.head;
197
fqdncache_entry *i = NULL;
201
if (i != NULL) { /* need to delay deletion */
202
fqdncacheRelease(i); /* we just override locks */
206
t = (fqdncache_entry *)m->data;
208
if (t->flags.fromhosts)
218
/* create blank fqdncache_entry */
219
static fqdncache_entry *
220
fqdncacheCreateEntry(const char *name)
222
static fqdncache_entry *f;
223
f = (fqdncache_entry *)memAllocate(MEM_FQDNCACHE_ENTRY);
224
f->hash.key = xstrdup(name);
225
f->expires = squid_curtime + Config.negativeDnsTtl;
230
fqdncacheAddEntry(fqdncache_entry * f)
232
hash_link *e = (hash_link *)hash_lookup(fqdn_table, f->hash.key);
235
/* avoid colission */
236
fqdncache_entry *q = (fqdncache_entry *) e;
240
hash_join(fqdn_table, &f->hash);
241
dlinkAdd(f, &f->lru, &lru_list);
242
f->lastref = squid_curtime;
245
/* walks down the pending list, calling handlers */
247
fqdncacheCallback(fqdncache_entry * f)
251
f->lastref = squid_curtime;
256
fqdncacheLockEntry(f);
258
callback = f->handler;
262
if (cbdataReferenceValidDone(f->handlerData, &cbdata)) {
263
dns_error_message = f->error_message;
264
callback(f->flags.negcached ? NULL : f->names[0], cbdata);
267
fqdncacheUnlockEntry(f);
272
fqdncacheParse(fqdncache_entry *f, const char *inbuf)
274
LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
277
const char *name = (const char *)f->hash.key;
278
f->expires = squid_curtime + Config.negativeDnsTtl;
279
f->flags.negcached = 1;
282
debug(35, 1) ("fqdncacheParse: Got <NULL> reply in response to '%s'\n", name);
283
f->error_message = xstrdup("Internal Error");
287
xstrncpy(buf, inbuf, DNS_INBUF_SZ);
288
debug(35, 5) ("fqdncacheParse: parsing: {%s}\n", buf);
289
token = strtok(buf, w_space);
292
debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting '$name' in response to '%s'\n", name);
293
f->error_message = xstrdup("Internal Error");
297
if (0 == strcmp(token, "$fail")) {
298
token = strtok(NULL, "\n");
299
assert(NULL != token);
300
f->error_message = xstrdup(token);
304
if (0 != strcmp(token, "$name")) {
305
debug(35, 1) ("fqdncacheParse: Got '%s', expecting '$name' in response to '%s'\n", inbuf, name);
306
f->error_message = xstrdup("Internal Error");
310
token = strtok(NULL, w_space);
313
debug(35, 1) ("fqdncacheParse: Got '%s', expecting TTL in response to '%s'\n", inbuf, name);
314
f->error_message = xstrdup("Internal Error");
320
token = strtok(NULL, w_space);
323
debug(35, 1) ("fqdncacheParse: Got '%s', expecting hostname in response to '%s'\n", inbuf, name);
324
f->error_message = xstrdup("Internal Error");
328
f->names[0] = xstrdup(token);
331
if (ttl == 0 || ttl > Config.positiveDnsTtl)
332
ttl = Config.positiveDnsTtl;
334
if (ttl < Config.negativeDnsTtl)
335
ttl = Config.negativeDnsTtl;
337
f->expires = squid_curtime + ttl;
339
f->flags.negcached = 0;
341
return f->name_count;
346
fqdncacheParse(fqdncache_entry *f, rfc1035_rr * answers, int nr, const char *error_message)
350
const char *name = (const char *)f->hash.key;
351
f->expires = squid_curtime + Config.negativeDnsTtl;
352
f->flags.negcached = 1;
355
debug(35, 3) ("fqdncacheParse: Lookup of '%s' failed (%s)\n", name, error_message);
356
f->error_message = xstrdup(error_message);
361
debug(35, 3) ("fqdncacheParse: No DNS records for '%s'\n", name);
362
f->error_message = xstrdup("No DNS records");
366
debug(35, 3) ("fqdncacheParse: %d answers for '%s'\n", nr, name);
369
for (k = 0; k < nr; k++) {
370
if (answers[k]._class != RFC1035_CLASS_IN)
373
if (answers[k].type == RFC1035_TYPE_PTR) {
374
if (!answers[k].rdata[0]) {
375
debug(35, 2) ("fqdncacheParse: blank PTR record for '%s'\n", name);
379
if (strchr(answers[k].rdata, ' ')) {
380
debug(35, 2) ("fqdncacheParse: invalid PTR record '%s' for '%s'\n", answers[k].rdata, name);
384
f->names[f->name_count++] = xstrdup(answers[k].rdata);
385
} else if (answers[k].type != RFC1035_TYPE_CNAME)
388
if (ttl == 0 || (int) answers[k].ttl < ttl)
389
ttl = answers[k].ttl;
391
if (f->name_count >= FQDN_MAX_NAMES)
395
if (f->name_count == 0) {
396
debug(35, 1) ("fqdncacheParse: No PTR record for '%s'\n", name);
400
if (ttl == 0 || ttl > Config.positiveDnsTtl)
401
ttl = Config.positiveDnsTtl;
403
if (ttl < Config.negativeDnsTtl)
404
ttl = Config.negativeDnsTtl;
406
f->expires = squid_curtime + ttl;
408
f->flags.negcached = 0;
410
return f->name_count;
418
fqdncacheHandleReply(void *data, char *reply)
420
fqdncacheHandleReply(void *data, rfc1035_rr * answers, int na, const char *error_message)
425
static_cast<generic_cbdata *>(data)->unwrap(&f);
426
n = ++FqdncacheStats.replies;
427
statHistCount(&statCounter.dns.svc_time,
428
tvSubMsec(f->request_time, current_time));
431
fqdncacheParse(f, reply);
435
fqdncacheParse(f, answers, na, error_message);
438
fqdncacheAddEntry(f);
440
fqdncacheCallback(f);
445
fqdncache_nbgethostbyaddr(struct IN_ADDR addr, FQDNH * handler, void *handlerData)
447
fqdncache_entry *f = NULL;
448
char *name = inet_ntoa(addr);
451
debug(35, 4) ("fqdncache_nbgethostbyaddr: Name '%s'.\n", name);
452
FqdncacheStats.requests++;
454
if (name == NULL || name[0] == '\0')
456
debug(35, 4) ("fqdncache_nbgethostbyaddr: Invalid name!\n");
457
dns_error_message = "Invalid hostname";
458
handler(NULL, handlerData);
462
f = fqdncache_get(name);
468
} else if (fqdncacheExpiredEntry(f))
470
/* hit, but expired -- bummer */
476
debug(35, 4) ("fqdncache_nbgethostbyaddr: HIT for '%s'\n", name);
478
if (f->flags.negcached)
479
FqdncacheStats.negative_hits++;
481
FqdncacheStats.hits++;
483
f->handler = handler;
485
f->handlerData = cbdataReference(handlerData);
487
fqdncacheCallback(f);
492
debug(35, 5) ("fqdncache_nbgethostbyaddr: MISS for '%s'\n", name);
493
FqdncacheStats.misses++;
494
f = fqdncacheCreateEntry(name);
495
f->handler = handler;
496
f->handlerData = cbdataReference(handlerData);
497
f->request_time = current_time;
498
c = new generic_cbdata(f);
501
dnsSubmit(hashKeyStr(&f->hash), fqdncacheHandleReply, c);
504
idnsPTRLookup(addr, fqdncacheHandleReply, c);
508
/* initialize the fqdncache */
517
debug(35, 3) ("Initializing FQDN Cache...\n");
519
memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats));
521
memset(&lru_list, '\0', sizeof(lru_list));
523
fqdncache_high = (long) (((float) Config.fqdncache.size *
524
(float) FQDN_HIGH_WATER) / (float) 100);
526
fqdncache_low = (long) (((float) Config.fqdncache.size *
527
(float) FQDN_LOW_WATER) / (float) 100);
529
n = hashPrime(fqdncache_high / 4);
531
fqdn_table = hash_create((HASHCMP *) strcmp, n, hash4);
533
memDataInit(MEM_FQDNCACHE_ENTRY, "fqdncache_entry",
534
sizeof(fqdncache_entry), 0);
538
fqdncacheRegisterWithCacheManager(CacheManager & manager)
540
manager.registerAction("fqdncache",
541
"FQDN Cache Stats and Contents",
548
fqdncache_gethostbyaddr(struct IN_ADDR addr, int flags)
550
char *name = inet_ntoa(addr);
551
fqdncache_entry *f = NULL;
555
FqdncacheStats.requests++;
556
f = fqdncache_get(name);
561
} else if (fqdncacheExpiredEntry(f))
565
} else if (f->flags.negcached)
567
FqdncacheStats.negative_hits++;
568
dns_error_message = f->error_message;
572
FqdncacheStats.hits++;
573
f->lastref = squid_curtime;
574
dns_error_message = f->error_message;
578
dns_error_message = NULL;
580
/* check if it's already a FQDN address in text form. */
582
if (!safe_inet_addr(name, &ip))
585
FqdncacheStats.misses++;
587
if (flags & FQDN_LOOKUP_IF_MISS)
588
fqdncache_nbgethostbyaddr(addr, dummy_handler, NULL);
594
/* process objects list */
596
fqdnStats(StoreEntry * sentry)
598
fqdncache_entry *f = NULL;
602
if (fqdn_table == NULL)
605
storeAppendPrintf(sentry, "FQDN Cache Statistics:\n");
607
storeAppendPrintf(sentry, "FQDNcache Entries: %d\n",
608
memInUse(MEM_FQDNCACHE_ENTRY));
610
storeAppendPrintf(sentry, "FQDNcache Requests: %d\n",
611
FqdncacheStats.requests);
613
storeAppendPrintf(sentry, "FQDNcache Hits: %d\n",
614
FqdncacheStats.hits);
616
storeAppendPrintf(sentry, "FQDNcache Negative Hits: %d\n",
617
FqdncacheStats.negative_hits);
619
storeAppendPrintf(sentry, "FQDNcache Misses: %d\n",
620
FqdncacheStats.misses);
622
storeAppendPrintf(sentry, "FQDN Cache Contents:\n\n");
624
storeAppendPrintf(sentry, "%-15.15s %3s %3s %3s %s\n",
625
"Address", "Flg", "TTL", "Cnt", "Hostnames");
627
hash_first(fqdn_table);
629
while ((f = (fqdncache_entry *) hash_next(fqdn_table))) {
630
ttl = (f->flags.fromhosts ? -1 : (f->expires - squid_curtime));
631
storeAppendPrintf(sentry, "%-15.15s %c%c %3.3d % 3d",
632
hashKeyStr(&f->hash),
633
f->flags.negcached ? 'N' : ' ',
634
f->flags.fromhosts ? 'H' : ' ',
636
(int) f->name_count);
638
for (k = 0; k < (int) f->name_count; k++)
639
storeAppendPrintf(sentry, " %s", f->names[k]);
641
storeAppendPrintf(sentry, "\n");
646
dummy_handler(const char *bufnotused, void *datanotused)
653
fqdnFromAddr(struct IN_ADDR addr)
658
if (Config.onoff.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0)))
661
xstrncpy(buf, inet_ntoa(addr), 32);
667
fqdncacheLockEntry(fqdncache_entry * f)
669
if (f->locks++ == 0) {
670
dlinkDelete(&f->lru, &lru_list);
671
dlinkAdd(f, &f->lru, &lru_list);
676
fqdncacheUnlockEntry(fqdncache_entry * f)
678
assert(f->locks > 0);
681
if (fqdncacheExpiredEntry(f))
686
fqdncacheFreeEntry(void *data)
688
fqdncache_entry *f = (fqdncache_entry *)data;
691
for (k = 0; k < (int) f->name_count; k++)
692
safe_free(f->names[k]);
694
safe_free(f->hash.key);
696
safe_free(f->error_message);
698
memFree(f, MEM_FQDNCACHE_ENTRY);
702
fqdncacheFreeMemory(void)
704
hashFreeItems(fqdn_table, fqdncacheFreeEntry);
705
hashFreeMemory(fqdn_table);
709
/* Recalculate FQDN cache size upon reconfigure */
711
fqdncache_restart(void)
713
fqdncache_high = (long) (((float) Config.fqdncache.size *
714
(float) FQDN_HIGH_WATER) / (float) 100);
715
fqdncache_low = (long) (((float) Config.fqdncache.size *
716
(float) FQDN_LOW_WATER) / (float) 100);
717
purge_entries_fromhosts();
721
* adds a "static" entry from /etc/hosts. the worldist is to be
722
* managed by the caller, including pointed-to strings
725
fqdncacheAddEntryFromHosts(char *addr, wordlist * hostnames)
727
fqdncache_entry *fce;
730
if ((fce = fqdncache_get(addr))) {
731
if (1 == fce->flags.fromhosts) {
732
fqdncacheUnlockEntry(fce);
733
} else if (fce->locks > 0) {
734
debug(35, 1) ("fqdncacheAddEntryFromHosts: can't add static entry for locked address '%s'\n", addr);
737
fqdncacheRelease(fce);
741
fce = fqdncacheCreateEntry(addr);
744
fce->names[j] = xstrdup(hostnames->key);
746
hostnames = hostnames->next;
748
if (j >= FQDN_MAX_NAMES)
753
fce->names[j] = NULL; /* it's safe */
754
fce->flags.fromhosts = 1;
755
fqdncacheAddEntry(fce);
756
fqdncacheLockEntry(fce);
762
* The function to return the fqdn statistics via SNMP
766
snmp_netFqdnFn(variable_list * Var, snint * ErrP)
768
variable_list *Answer = NULL;
769
debug(49, 5) ("snmp_netFqdnFn: Processing request:\n");
770
snmpDebugOid(5, Var->name, Var->name_length);
771
*ErrP = SNMP_ERR_NOERROR;
773
switch (Var->name[LEN_SQ_NET + 1]) {
776
Answer = snmp_var_new_integer(Var->name, Var->name_length,
777
memInUse(MEM_FQDNCACHE_ENTRY),
782
Answer = snmp_var_new_integer(Var->name, Var->name_length,
783
FqdncacheStats.requests,
788
Answer = snmp_var_new_integer(Var->name, Var->name_length,
794
/* this is now worthless */
795
Answer = snmp_var_new_integer(Var->name, Var->name_length,
801
Answer = snmp_var_new_integer(Var->name, Var->name_length,
802
FqdncacheStats.negative_hits,
807
Answer = snmp_var_new_integer(Var->name, Var->name_length,
808
FqdncacheStats.misses,
813
Answer = snmp_var_new_integer(Var->name, Var->name_length,
819
*ErrP = SNMP_ERR_NOSUCHNAME;
826
#endif /*SQUID_SNMP */