fqdncache.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:18k
- /*
- * $Id: fqdncache.c,v 1.126 1998/12/05 00:54:24 wessels Exp $
- *
- * DEBUG: section 35 FQDN Cache
- * AUTHOR: Harvest Derived
- *
- * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
- * ----------------------------------------------------------
- *
- * Squid is the result of efforts by numerous individuals from the
- * Internet community. Development is led by Duane Wessels of the
- * National Laboratory for Applied Network Research and funded by the
- * National Science Foundation. Squid is Copyrighted (C) 1998 by
- * Duane Wessels and the University of California San Diego. Please
- * see the COPYRIGHT file for full details. Squid incorporates
- * software developed and/or copyrighted by other sources. Please see
- * the CREDITS file for full details.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
- *
- */
- #include "squid.h"
- #define FQDN_LOW_WATER 90
- #define FQDN_HIGH_WATER 95
- static struct {
- int requests;
- int replies;
- int hits;
- int misses;
- int pending_hits;
- int negative_hits;
- int errors;
- int ghba_calls; /* # calls to blocking gethostbyaddr() */
- } FqdncacheStats;
- static dlink_list lru_list;
- static HLPCB fqdncacheHandleReply;
- static fqdncache_entry *fqdncacheParse(const char *buf);
- static void fqdncache_release(fqdncache_entry *);
- static fqdncache_entry *fqdncache_create(const char *name);
- static void fqdncache_call_pending(fqdncache_entry *);
- static void fqdncacheAddHostent(fqdncache_entry *, const struct hostent *);
- static fqdncache_entry *fqdncache_get(const char *);
- static FQDNH dummy_handler;
- static int fqdncacheExpiredEntry(const fqdncache_entry *);
- static void fqdncacheAddPending(fqdncache_entry *, FQDNH *, void *);
- static void fqdncacheChangeKey(fqdncache_entry * i);
- static void fqdncacheLockEntry(fqdncache_entry * f);
- static void fqdncacheUnlockEntry(fqdncache_entry * f);
- static FREE fqdncacheFreeEntry;
- static hash_table *fqdn_table = NULL;
- static char fqdncache_status_char[] =
- {
- 'C',
- 'N',
- 'P',
- 'D'
- };
- static long fqdncache_low = 180;
- static long fqdncache_high = 200;
- /* removes the given fqdncache entry */
- static void
- fqdncache_release(fqdncache_entry * f)
- {
- int k;
- assert(f->status != FQDN_PENDING);
- assert(f->status != FQDN_DISPATCHED);
- assert(f->pending_head == NULL);
- hash_remove_link(fqdn_table, (hash_link *) f);
- if (f->status == FQDN_CACHED) {
- for (k = 0; k < (int) f->name_count; k++)
- safe_free(f->names[k]);
- debug(35, 5) ("fqdncache_release: Released FQDN record for '%s'.n",
- f->name);
- }
- dlinkDelete(&f->lru, &lru_list);
- safe_free(f->name);
- safe_free(f->error_message);
- memFree(f, MEM_FQDNCACHE_ENTRY);
- }
- /* return match for given name */
- static fqdncache_entry *
- fqdncache_get(const char *name)
- {
- hash_link *e;
- static fqdncache_entry *f;
- f = NULL;
- if (fqdn_table) {
- if ((e = hash_lookup(fqdn_table, name)) != NULL)
- f = (fqdncache_entry *) e;
- }
- return f;
- }
- static int
- fqdncacheExpiredEntry(const fqdncache_entry * f)
- {
- if (f->status == FQDN_PENDING)
- return 0;
- if (f->status == FQDN_DISPATCHED)
- return 0;
- if (f->locks != 0)
- return 0;
- if (f->expires > squid_curtime)
- return 0;
- return 1;
- }
- void
- fqdncache_purgelru(void *notused)
- {
- dlink_node *m;
- dlink_node *prev = NULL;
- fqdncache_entry *f;
- int removed = 0;
- eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 10.0, 1);
- for (m = lru_list.tail; m; m = prev) {
- if (memInUse(MEM_FQDNCACHE_ENTRY) < fqdncache_low)
- break;
- prev = m->prev;
- f = m->data;
- if (f->status == FQDN_PENDING)
- continue;
- if (f->status == FQDN_DISPATCHED)
- continue;
- if (f->locks != 0)
- continue;
- fqdncache_release(f);
- removed++;
- }
- debug(35, 9) ("fqdncache_purgelru: removed %d entriesn", removed);
- }
- /* create blank fqdncache_entry */
- static fqdncache_entry *
- fqdncache_create(const char *name)
- {
- static fqdncache_entry *f;
- f = memAllocate(MEM_FQDNCACHE_ENTRY);
- f->name = xstrdup(name);
- f->expires = squid_curtime + Config.negativeDnsTtl;
- hash_join(fqdn_table, (hash_link *) f);
- dlinkAdd(f, &f->lru, &lru_list);
- return f;
- }
- static void
- fqdncacheAddHostent(fqdncache_entry * f, const struct hostent *hp)
- {
- int k;
- f->name_count = 0;
- f->names[f->name_count++] = xstrdup((char *) hp->h_name);
- for (k = 0; hp->h_aliases[k]; k++) {
- f->names[f->name_count++] = xstrdup(hp->h_aliases[k]);
- if (f->name_count == FQDN_MAX_NAMES)
- break;
- }
- }
- static fqdncache_entry *
- fqdncacheAddNew(const char *name, const struct hostent *hp, fqdncache_status_t status)
- {
- fqdncache_entry *f;
- assert(fqdncache_get(name) == NULL);
- debug(35, 10) ("fqdncacheAddNew: Adding '%s', status=%cn",
- name,
- fqdncache_status_char[status]);
- f = fqdncache_create(name);
- if (hp)
- fqdncacheAddHostent(f, hp);
- f->status = status;
- f->lastref = squid_curtime;
- return f;
- }
- /* walks down the pending list, calling handlers */
- static void
- fqdncache_call_pending(fqdncache_entry * f)
- {
- fqdn_pending *p = NULL;
- int nhandler = 0;
- f->lastref = squid_curtime;
- fqdncacheLockEntry(f);
- while (f->pending_head != NULL) {
- p = f->pending_head;
- f->pending_head = p->next;
- if (p->handler) {
- nhandler++;
- dns_error_message = f->error_message;
- p->handler((f->status == FQDN_CACHED) ? f->names[0] : NULL,
- p->handlerData);
- }
- memFree(p, MEM_FQDNCACHE_PENDING);
- }
- f->pending_head = NULL; /* nuke list */
- debug(35, 10) ("fqdncache_call_pending: Called %d handlers.n", nhandler);
- fqdncacheUnlockEntry(f);
- }
- static fqdncache_entry *
- fqdncacheParse(const char *inbuf)
- {
- LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
- char *token;
- static fqdncache_entry f;
- int ttl;
- xstrncpy(buf, inbuf, DNS_INBUF_SZ);
- debug(35, 5) ("fqdncacheParse: parsing: {%s}n", buf);
- memset(&f, ' ', sizeof(f));
- f.expires = squid_curtime;
- f.status = FQDN_NEGATIVE_CACHED;
- if (inbuf == NULL) {
- debug(35, 1) ("fqdncacheParse: Got <NULL> replyn");
- return &f;
- }
- token = strtok(buf, w_space);
- if (NULL == token) {
- debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting '$name'n");
- return &f;
- }
- if (0 == strcmp(token, "$fail")) {
- f.expires = squid_curtime + Config.negativeDnsTtl;
- token = strtok(NULL, "n");
- assert(NULL != token);
- f.error_message = xstrdup(token);
- return &f;
- }
- if (0 != strcmp(token, "$name")) {
- debug(35, 1) ("fqdncacheParse: Got '%s', expecting '$name'n", token);
- return &f;
- }
- token = strtok(NULL, w_space);
- if (NULL == token) {
- debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting TTLn");
- return &f;
- }
- f.status = FQDN_CACHED;
- ttl = atoi(token);
- if (ttl > 0)
- f.expires = squid_curtime + ttl;
- else
- f.expires = squid_curtime + Config.positiveDnsTtl;
- token = strtok(NULL, w_space);
- if (NULL != token) {
- f.names[0] = xstrdup(token);
- f.name_count = 1;
- }
- return &f;
- }
- static void
- fqdncacheHandleReply(void *data, char *reply)
- {
- int n;
- generic_cbdata *c = data;
- fqdncache_entry *f = c->data;
- fqdncache_entry *x = NULL;
- assert(f->status == FQDN_DISPATCHED);
- assert(f->locks);
- cbdataFree(c);
- c = NULL;
- n = ++FqdncacheStats.replies;
- statHistCount(&Counter.dns.svc_time,
- tvSubMsec(f->request_time, current_time));
- x = fqdncacheParse(reply);
- assert(x);
- f->name_count = x->name_count;
- for (n = 0; n < (int) f->name_count; n++)
- f->names[n] = x->names[n];
- f->error_message = x->error_message;
- f->status = x->status;
- f->expires = x->expires;
- fqdncache_call_pending(f);
- fqdncacheUnlockEntry(f); /* unlock from FQDN_DISPATCHED */
- }
- static void
- fqdncacheAddPending(fqdncache_entry * f, FQDNH * handler, void *handlerData)
- {
- fqdn_pending *pending = memAllocate(MEM_FQDNCACHE_PENDING);
- fqdn_pending **I = NULL;
- f->lastref = squid_curtime;
- pending->handler = handler;
- pending->handlerData = handlerData;
- for (I = &(f->pending_head); *I; I = &((*I)->next));
- *I = pending;
- }
- void
- fqdncache_nbgethostbyaddr(struct in_addr addr, FQDNH * handler, void *handlerData)
- {
- fqdncache_entry *f = NULL;
- char *name = inet_ntoa(addr);
- generic_cbdata *c;
- assert(handler);
- debug(35, 4) ("fqdncache_nbgethostbyaddr: Name '%s'.n", name);
- FqdncacheStats.requests++;
- if (name == NULL || name[0] == ' ') {
- debug(35, 4) ("fqdncache_nbgethostbyaddr: Invalid name!n");
- handler(NULL, handlerData);
- return;
- }
- if ((f = fqdncache_get(name))) {
- if (fqdncacheExpiredEntry(f)) {
- fqdncache_release(f);
- f = NULL;
- }
- }
- if (f == NULL) {
- /* MISS: No entry, create the new one */
- debug(35, 5) ("fqdncache_nbgethostbyaddr: MISS for '%s'n", name);
- FqdncacheStats.misses++;
- f = fqdncacheAddNew(name, NULL, FQDN_PENDING);
- fqdncacheAddPending(f, handler, handlerData);
- f->request_time = current_time;
- } else if (f->status == FQDN_CACHED || f->status == FQDN_NEGATIVE_CACHED) {
- /* HIT */
- debug(35, 4) ("fqdncache_nbgethostbyaddr: HIT for '%s'n", name);
- if (f->status == FQDN_NEGATIVE_CACHED)
- FqdncacheStats.negative_hits++;
- else
- FqdncacheStats.hits++;
- fqdncacheAddPending(f, handler, handlerData);
- fqdncache_call_pending(f);
- return;
- } else if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) {
- debug(35, 4) ("fqdncache_nbgethostbyaddr: PENDING for '%s'n", name);
- FqdncacheStats.pending_hits++;
- fqdncacheAddPending(f, handler, handlerData);
- if (squid_curtime - f->expires > 600) {
- debug(35, 0) ("fqdncache_nbgethostbyname: '%s' PENDING for %d seconds, abortingn", name,
- (int) (squid_curtime + Config.negativeDnsTtl - f->expires));
- fqdncacheChangeKey(f);
- fqdncache_call_pending(f);
- }
- return;
- } else {
- debug(35, 1) ("fqdncache_nbgethostbyaddr: BAD status %d",
- (int) f->status);
- assert(0);
- }
- /* for HIT, PENDING, DISPATCHED we've returned. For MISS we submit */
- c = xcalloc(1, sizeof(*c));
- c->data = f;
- cbdataAdd(c, cbdataXfree, 0);
- f->status = FQDN_DISPATCHED;
- fqdncacheLockEntry(f); /* lock while FQDN_DISPATCHED */
- dnsSubmit(f->name, fqdncacheHandleReply, c);
- }
- /* initialize the fqdncache */
- void
- fqdncache_init(void)
- {
- int n;
- if (fqdn_table)
- return;
- debug(35, 3) ("Initializing FQDN Cache...n");
- memset(&FqdncacheStats, ' ', sizeof(FqdncacheStats));
- memset(&lru_list, ' ', sizeof(lru_list));
- fqdncache_high = (long) (((float) Config.fqdncache.size *
- (float) FQDN_HIGH_WATER) / (float) 100);
- fqdncache_low = (long) (((float) Config.fqdncache.size *
- (float) FQDN_LOW_WATER) / (float) 100);
- n = hashPrime(fqdncache_high / 4);
- fqdn_table = hash_create((HASHCMP *) strcmp, n, hash4);
- cachemgrRegister("fqdncache",
- "FQDN Cache Stats and Contents",
- fqdnStats, 0, 1);
- }
- /* clean up the pending entries in dnsserver */
- /* return 1 if we found the host, 0 otherwise */
- int
- fqdncacheUnregister(struct in_addr addr, void *data)
- {
- char *name = inet_ntoa(addr);
- fqdncache_entry *f = NULL;
- fqdn_pending *p = NULL;
- int n = 0;
- debug(35, 3) ("fqdncacheUnregister: name '%s'n", name);
- if ((f = fqdncache_get(name)) == NULL)
- return 0;
- if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) {
- for (p = f->pending_head; p; p = p->next) {
- if (p->handlerData != data)
- continue;
- p->handler = NULL;
- n++;
- }
- }
- if (n == 0)
- debug_trap("fqdncacheUnregister: callback data not found");
- debug(35, 3) ("fqdncacheUnregister: unregistered %d handlersn", n);
- return n;
- }
- const char *
- fqdncache_gethostbyaddr(struct in_addr addr, int flags)
- {
- char *name = inet_ntoa(addr);
- fqdncache_entry *f = NULL;
- struct in_addr ip;
- assert(name);
- FqdncacheStats.requests++;
- if ((f = fqdncache_get(name))) {
- if (fqdncacheExpiredEntry(f)) {
- fqdncache_release(f);
- f = NULL;
- }
- }
- if (f) {
- if (f->status == FQDN_NEGATIVE_CACHED) {
- FqdncacheStats.negative_hits++;
- dns_error_message = f->error_message;
- return NULL;
- } else {
- FqdncacheStats.hits++;
- f->lastref = squid_curtime;
- return f->names[0];
- }
- }
- /* check if it's already a FQDN address in text form. */
- if (!safe_inet_addr(name, &ip))
- return name;
- FqdncacheStats.misses++;
- if (flags & FQDN_LOOKUP_IF_MISS)
- fqdncache_nbgethostbyaddr(addr, dummy_handler, NULL);
- return NULL;
- }
- /* process objects list */
- void
- fqdnStats(StoreEntry * sentry)
- {
- fqdncache_entry *f = NULL;
- int k;
- int ttl;
- if (fqdn_table == NULL)
- return;
- storeAppendPrintf(sentry, "FQDN Cache Statistics:n");
- storeAppendPrintf(sentry, "FQDNcache Entries: %dn",
- memInUse(MEM_FQDNCACHE_ENTRY));
- storeAppendPrintf(sentry, "FQDNcache Requests: %dn",
- FqdncacheStats.requests);
- storeAppendPrintf(sentry, "FQDNcache Hits: %dn",
- FqdncacheStats.hits);
- storeAppendPrintf(sentry, "FQDNcache Pending Hits: %dn",
- FqdncacheStats.pending_hits);
- storeAppendPrintf(sentry, "FQDNcache Negative Hits: %dn",
- FqdncacheStats.negative_hits);
- storeAppendPrintf(sentry, "FQDNcache Misses: %dn",
- FqdncacheStats.misses);
- storeAppendPrintf(sentry, "Blocking calls to gethostbyaddr(): %dn",
- FqdncacheStats.ghba_calls);
- storeAppendPrintf(sentry, "FQDN Cache Contents:nn");
- hash_first(fqdn_table);
- while ((f = (fqdncache_entry *) hash_next(fqdn_table))) {
- if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED)
- ttl = 0;
- else
- ttl = (f->expires - squid_curtime);
- storeAppendPrintf(sentry, " %-32.32s %c %6d %d",
- f->name,
- fqdncache_status_char[f->status],
- ttl,
- (int) f->name_count);
- for (k = 0; k < (int) f->name_count; k++)
- storeAppendPrintf(sentry, " %s", f->names[k]);
- storeAppendPrintf(sentry, "n");
- }
- }
- static void
- dummy_handler(const char *bufnotused, void *datanotused)
- {
- return;
- }
- void
- fqdncacheReleaseInvalid(const char *name)
- {
- fqdncache_entry *f;
- if ((f = fqdncache_get(name)) == NULL)
- return;
- if (f->status != FQDN_NEGATIVE_CACHED)
- return;
- fqdncache_release(f);
- }
- const char *
- fqdnFromAddr(struct in_addr addr)
- {
- const char *n;
- static char buf[32];
- if (Config.onoff.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0)))
- return n;
- xstrncpy(buf, inet_ntoa(addr), 32);
- return buf;
- }
- static void
- fqdncacheLockEntry(fqdncache_entry * f)
- {
- if (f->locks++ == 0) {
- dlinkDelete(&f->lru, &lru_list);
- dlinkAdd(f, &f->lru, &lru_list);
- }
- }
- static void
- fqdncacheUnlockEntry(fqdncache_entry * f)
- {
- if (f->locks == 0) {
- debug_trap("fqdncacheUnlockEntry: Entry has no locks");
- return;
- }
- f->locks--;
- if (fqdncacheExpiredEntry(f))
- fqdncache_release(f);
- }
- static void
- fqdncacheFreeEntry(void *data)
- {
- fqdncache_entry *f = data;
- fqdn_pending *p = NULL;
- int k;
- while ((p = f->pending_head)) {
- f->pending_head = p->next;
- memFree(p, MEM_FQDNCACHE_PENDING);
- }
- for (k = 0; k < (int) f->name_count; k++)
- safe_free(f->names[k]);
- safe_free(f->name);
- safe_free(f->error_message);
- memFree(f, MEM_FQDNCACHE_ENTRY);
- }
- void
- fqdncacheFreeMemory(void)
- {
- hashFreeItems(fqdn_table, fqdncacheFreeEntry);
- hashFreeMemory(fqdn_table);
- fqdn_table = NULL;
- }
- static void
- fqdncacheChangeKey(fqdncache_entry * f)
- {
- static int index = 0;
- LOCAL_ARRAY(char, new_key, 256);
- hash_link *table_entry = hash_lookup(fqdn_table, f->name);
- if (table_entry == NULL) {
- debug(35, 0) ("fqdncacheChangeKey: Could not find key '%s'n", f->name);
- return;
- }
- if (f != (fqdncache_entry *) table_entry) {
- debug_trap("fqdncacheChangeKey: f != table_entry!");
- return;
- }
- hash_remove_link(fqdn_table, table_entry);
- snprintf(new_key, 256, "%d/", ++index);
- strncat(new_key, f->name, 128);
- debug(35, 1) ("fqdncacheChangeKey: from '%s' to '%s'n", f->name, new_key);
- safe_free(f->name);
- f->name = xstrdup(new_key);
- hash_join(fqdn_table, (hash_link *) f);
- }
- /* call during reconfigure phase to clear out all the
- * pending and dispatched reqeusts that got lost */
- void
- fqdncache_restart(void)
- {
- fqdncache_entry *this;
- assert(fqdn_table);
- hash_first(fqdn_table);
- while ((this = (fqdncache_entry *) hash_next(fqdn_table))) {
- if (this->status == FQDN_CACHED)
- continue;
- if (this->status == FQDN_NEGATIVE_CACHED)
- continue;
- }
- fqdncache_high = (long) (((float) Config.fqdncache.size *
- (float) FQDN_HIGH_WATER) / (float) 100);
- fqdncache_low = (long) (((float) Config.fqdncache.size *
- (float) FQDN_LOW_WATER) / (float) 100);
- }
- #ifdef SQUID_SNMP
- /*
- * The function to return the fqdn statistics via SNMP
- */
- variable_list *
- snmp_netFqdnFn(variable_list * Var, snint * ErrP)
- {
- variable_list *Answer;
- debug(49, 5) ("snmp_netFqdnFn: Processing request:n", Var->name[LEN_SQ_NET +
- 1]);
- snmpDebugOid(5, Var->name, Var->name_length);
- Answer = snmp_var_new(Var->name, Var->name_length);
- *ErrP = SNMP_ERR_NOERROR;
- Answer->val_len = sizeof(snint);
- Answer->val.integer = xmalloc(Answer->val_len);
- Answer->type = SMI_COUNTER32;
- switch (Var->name[LEN_SQ_NET + 1]) {
- case FQDN_ENT:
- *(Answer->val.integer) = memInUse(MEM_FQDNCACHE_ENTRY);
- Answer->type = SMI_GAUGE32;
- break;
- case FQDN_REQ:
- *(Answer->val.integer) = FqdncacheStats.requests;
- break;
- case FQDN_HITS:
- *(Answer->val.integer) = FqdncacheStats.hits;
- break;
- case FQDN_PENDHIT:
- *(Answer->val.integer) = FqdncacheStats.pending_hits;
- Answer->type = SMI_GAUGE32;
- break;
- case FQDN_NEGHIT:
- *(Answer->val.integer) = FqdncacheStats.negative_hits;
- break;
- case FQDN_MISS:
- *(Answer->val.integer) = FqdncacheStats.misses;
- break;
- case FQDN_GHBN:
- *(Answer->val.integer) = FqdncacheStats.ghba_calls;
- break;
- default:
- *ErrP = SNMP_ERR_NOSUCHNAME;
- snmp_var_free(Answer);
- return (NULL);
- }
- return Answer;
- }
- #endif /*SQUID_SNMP */