📄 cache.c~
字号:
/************************************************************************************************** $Id: cache.c,v 1.105 2006/01/18 20:46:46 bboy Exp $ Copyright (C) 2002-2005 Don Moore <bboy@bboy.net> 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-1307 USA**************************************************************************************************/#include "named.h"/* Make this nonzero to enable debugging for this source file */#define DEBUG_CACHE 1/* Set this to nonzero to debug frequency of SQL queries */#define DEBUG_SQL_QUERIES 1CACHE *ZoneCache = NULL; /* Data cache */CACHE *ReplyCache = NULL; /* Reply cache */#if USE_NEGATIVE_CACHECACHE *NegativeCache = NULL; /* Negative zone cache */#endif#ifdef DN_COLUMN_NAMESextern char *dn_default_ns; /* Default NS for directNIC */#endifint select_matched_zone(SQL *sqlConn, /*perl DBI sql connection*/MYDNS_SOA **rptr, /*MYDNS_SOA structure pointer*/dns_qtype_t type, /*dns query type*/char *name, /*domain name(label)*/char *origin, /*origin name*/ const char *ipchs /*requestor ip section*/){ size_t querylen; uchar query[DNS_QUERYBUFSIZ] = ""; uchar namequery[DNS_MAXNAMELEN + DNS_MAXNAMELEN + DNS_MAXNAMELEN + 25] = ""; int namelen = name ? strlen(name) : 0; uchar *wheretype; register char *c; SQL_RES *res; SQL_ROW row; /* Verify args */ if (!sqlConn || !rptr) { errno = EINVAL; return (-1); } /* Make sure 'name' and 'origin' (if present) are valid */ if (name) { for (c = name; *c; c++) if (SQL_BADCHAR(*c)) return (0); } if (origin) { for (c = origin; *c; c++) if (SQL_BADCHAR(*c)) return (0); } /*check for matched zone*/ MYDNS_SOA *soa_ptr; soa_ptr = *rptr; while(soa_ptr){ /* Construct query */ if (name) { if (origin) { if (!name[0]) snprintf(namequery, sizeof(namequery), "(name='' OR name='%s')", origin); else snprintf(namequery, sizeof(namequery), "(name='%s' OR name='%s.%s')", name, name, origin); } else snprintf(namequery, sizeof(namequery), "name='%s'", name); } querylen = snprintf(query, sizeof(query), "SELECT "MYDNS_RR_FIELDS"%s FROM %s WHERE " "zone=%u AND type='%s'" "%s%s%s%s", (mydns_rr_use_active ? ",active" : ""), mydns_rr_table_name, soa_ptr->id, "A", namequery[0] ? " AND " : "", namequery, (mydns_rr_where_clause) ? " AND " : "", (mydns_rr_where_clause) ? mydns_rr_where_clause : ""); /* Submit query */ if (!(res = sql_query(sqlConn, query, querylen))) return (-1);#if DEBUG_ENABLED && DEBUG_LIB_RR { int numresults = sql_num_rows(res); Debug("[*%s*][*%s*]%s RR query: %d row%s: %s",__FILE__,__LINE__, numresults, S(numresults), query); }#endif while((row = mysql_row(res))){ } soa_ptr = (*rptr)->next;}//check for matched zone}#if (HASH_TYPE == ORIGINAL_HASH) || (HASH_TYPE == ADDITIVE_HASH)/************************************************************************************************** ISPRIME Returns 1 if `number' is a prime number, 0 if not.**************************************************************************************************/static intisprime(unsigned int number){ register unsigned int divn = 3; while (divn * divn < number && number % divn != 0) divn += 2; return (number % divn != 0);}/*--- isprime() ---------------------------------------------------------------------------------*/#endif/************************************************************************************************** _CACHE_INIT Create, initialize, and return a new CACHE structure.**************************************************************************************************/static CACHE *_cache_init(uint32_t limit, uint32_t expire, const char *desc){ CACHE *C; if (!(C = calloc(1, sizeof(CACHE)))) Err(_("out of memory")); C->limit = limit; C->expire = expire;#if (HASH_TYPE == ORIGINAL_HASH) || (HASH_TYPE == ADDITIVE_HASH) /* Make `slots' prime */ C->slots = limit * CACHE_SLOT_MULTIPLIER; C->slots |= 1; while (!isprime(C->slots)) C->slots += 2;#elif (HASH_TYPE == ROTATING_HASH) || (HASH_TYPE == FNV_HASH) /* Make `slots' a power of two */ { int bits; uint32_t slots; C->slots = limit * CACHE_SLOT_MULTIPLIER; for (slots = C->slots, bits = 0; slots != 1; ) { slots >>= 1; bits++; } if (C->slots & ((1 << bits) - 1)) bits++; slots = 1 << bits; C->slots = slots;#if (HASH_TYPE == ROTATING_HASH) C->mask = C->slots - 1;#endif#if (HASH_TYPE == FNV_HASH) /* A 16-bit hash lets us use XOR instead of MOD to clamp the value - very fast */ if (C->slots < 65536) C->slots = 65536; for (C->bits = 0; C->bits < 32; C->bits++) if (((uint32_t)1 << C->bits) == C->slots) break;#endif }#else# error Hash method unknown or unspecified#endif if (!(C->nodes = calloc(C->slots, sizeof(CNODE *)))) Err(_("out of memory"));#if DEBUG_ENABLED && DEBUG_CACHE#if (HASH_TYPE == ORIGINAL_HASH) Debug("%s cache initialized (%u nodes, %u elements max) (original hash)", desc, C->slots, limit);#elif (HASH_TYPE == ADDITIVE_HASH) Debug("%s cache initialized (%u nodes, %u elements max) (additive hash)", desc, C->slots, limit);#elif (HASH_TYPE == ROTATING_HASH) Debug("%s cache initialized (%u nodes, %u elements max) (rotating hash)", desc, C->slots, limit);#elif (HASH_TYPE == FNV_HASH) Debug("%s cache initialized (%u nodes, %u elements max) (%d-bit FNV hash)", desc, C->slots, limit, C->bits);#else# error Hash method unknown or unspecified#endif#endif strncpy(C->name, desc, sizeof(C->name)-1); return (C);}/*--- _cache_init() -----------------------------------------------------------------------------*//************************************************************************************************** CACHE_INIT Create the caches used by MyDNS.**************************************************************************************************/voidcache_init(void){ uint32_t cache_size, zone_cache_size, reply_cache_size; int defaulted; int zone_cache_expire, reply_cache_expire; /* Get ZoneCache size */ zone_cache_size = atou(conf_get(&Conf, "zone-cache-size", &defaulted)); if (defaulted) { cache_size = atou(conf_get(&Conf, "cache-size", NULL)); zone_cache_size = cache_size - (cache_size / 3); } zone_cache_expire = atou(conf_get(&Conf, "zone-cache-expire", &defaulted)); if (defaulted) zone_cache_expire = atou(conf_get(&Conf, "cache-expire", NULL)); /* Get ReplyCache size */ reply_cache_size = atou(conf_get(&Conf, "reply-cache-size", &defaulted)); if (defaulted) { cache_size = atou(conf_get(&Conf, "cache-size", NULL)); reply_cache_size = cache_size / 3; } reply_cache_expire = atou(conf_get(&Conf, "reply-cache-expire", &defaulted)); if (defaulted) reply_cache_expire = atou(conf_get(&Conf, "cache-expire", NULL)) / 2; /* Initialize caches */ if (zone_cache_size) ZoneCache = _cache_init(zone_cache_size, zone_cache_expire, "zone");#if USE_NEGATIVE_CACHE if (zone_cache_size) NegativeCache = _cache_init(zone_cache_size, zone_cache_expire, "negative");#endif if (reply_cache_size) ReplyCache = _cache_init(reply_cache_size, reply_cache_expire, "reply");}/*--- cache_init() ------------------------------------------------------------------------------*//************************************************************************************************** CACHE_SIZE_UPDATE Updates the 'size' variable in a cache.**************************************************************************************************/static voidcache_size_update(CACHE *C){ register int n; register CNODE *N; C->size = 0; C->size += sizeof(CACHE); /* Get size of all data in cache */ for (n = 0; n < C->slots; n++) for (N = C->nodes[n]; N; N = N->next_node) { C->size += sizeof(CNODE); if (C == ZoneCache) { if (N->data) { if (N->type == DNS_QTYPE_SOA) C->size += mydns_soa_size((MYDNS_SOA *)N->data); else C->size += mydns_rr_size((MYDNS_RR *)N->data); } } else C->size += N->datalen; }}/*--- cache_size_update() -----------------------------------------------------------------------*//************************************************************************************************** CACHE_STATUS Called when SIGUSR1 is received, returns a string to append to status.**************************************************************************************************/voidcache_status(CACHE *C){ if (C) { register int ct, collisions; register CNODE *n; /* Update cache size (bytes) */ cache_size_update(C); /* Count number of collisions */ for (ct = collisions = 0; ct < C->slots; ct++) if (C->nodes[ct] && C->nodes[ct]->next_node) for (n = C->nodes[ct]->next_node; n; n = n->next_node) collisions++; Notice(_("%s cache %.0f%% useful (%u hits, %u misses)," " %u collisions (%.0f%%), %.0f%% full (%u records), %u bytes, avg life %u sec"), C->name, PCT(C->questions, C->hits), C->hits, C->misses, collisions, PCT(C->slots, collisions), PCT(C->limit, C->count), C->count, C->size, (unsigned int)(C->removed ? C->removed_secs / C->removed : (time(NULL) - Status.start_time)) ); }}/*--- cache_status() ----------------------------------------------------------------------------*//************************************************************************************************** MRULIST_ADD Adds the specified node to the head of the MRU list.**************************************************************************************************/static voidmrulist_add(CACHE *ThisCache, CNODE *n){ register CNODE *head = ThisCache->mruHead; if (!n || !ThisCache) return; if (!ThisCache->mruHead) { ThisCache->mruHead = n; ThisCache->mruHead->mruPrev = NULL; ThisCache->mruHead->mruNext = NULL; ThisCache->mruTail = n; } else { n->mruNext = head; n->mruPrev = head->mruPrev; if (head->mruPrev == NULL) ThisCache->mruHead = n; else head->mruPrev->mruNext = n; head->mruPrev = n; }}/*--- mrulist_add() -----------------------------------------------------------------------------*//************************************************************************************************** MRULIST_DEL**************************************************************************************************/static voidmrulist_del(CACHE *ThisCache, CNODE *n){ if (!n || !ThisCache || !ThisCache->mruHead) return; if (n == ThisCache->mruHead) { ThisCache->mruHead = n->mruNext; if (ThisCache->mruHead == NULL) ThisCache->mruTail = NULL; else n->mruNext->mruPrev = NULL; } else { n->mruPrev->mruNext = n->mruNext; if (n->mruNext == NULL) ThisCache->mruTail = n->mruPrev; else n->mruNext->mruPrev = n->mruPrev; }}/*--- mrulist_del() -----------------------------------------------------------------------------*//************************************************************************************************** CACHE_FREE_NODE Frees the node specified and removes it from the cache.**************************************************************************************************/static voidcache_free_node(CACHE *ThisCache, uint32_t hash, CNODE *n){ register CNODE *prev = NULL, *cur, *next; if (!n || hash >= ThisCache->slots) return; for (cur = ThisCache->nodes[hash]; cur; cur = next) { next = cur->next_node; if (cur == n) /* Delete this node */ { mrulist_del(ThisCache, n); /* Remove from MRU/LRU list */ /* Remove the node */ if (cur->datalen) { Free(cur->data); } else if (cur->type == DNS_QTYPE_SOA) { mydns_soa_free(cur->data); } else { mydns_rr_free(cur->data); } if (cur == ThisCache->nodes[hash]) /* Head of node? */ ThisCache->nodes[hash] = cur->next_node; else if (prev) prev->next_node = cur->next_node; Free(cur); ThisCache->out++; ThisCache->count--; return; } else prev = cur; } Errx(_("tried to free invalid node %p at %u in cache"), n, hash);}/*--- cache_free_node() -------------------------------------------------------------------------*//************************************************************************************************** CACHE_EMPTY Deletes all nodes within the cache.**************************************************************************************************/voidcache_empty(CACHE *ThisCache){ register int ct; register CNODE *n, *tmp; if (!ThisCache) return; for (ct = 0; ct < ThisCache->slots; ct++) for (n = ThisCache->nodes[ct]; n; n = tmp) { tmp = n->next_node; cache_free_node(ThisCache, ct, n); } ThisCache->mruHead = ThisCache->mruTail = NULL;}/*--- cache_empty() -----------------------------------------------------------------------------*//************************************************************************************************** CACHE_CLEANUP Deletes all expired nodes within the cache.**************************************************************************************************/voidcache_cleanup(CACHE *ThisCache){ register int ct; register CNODE *n, *tmp; if (!ThisCache) return; for (ct = 0; ct < ThisCache->slots; ct++) for (n = ThisCache->nodes[ct]; n; n = tmp) { tmp = n->next_node; if (n->expire && (current_time > n->expire)) { ThisCache->expired++; cache_free_node(ThisCache, ct, n); } }}/*--- cache_cleanup() ---------------------------------------------------------------------------*//************************************************************************************************** CACHE_PURGE_ZONE Deletes all nodes within the cache for the specified zone.**************************************************************************************************/voidcache_purge_zone(CACHE *ThisCache, uint32_t zone){ register int ct; register CNODE *n, *tmp; if (!ThisCache) return; for (ct = 0; ct < ThisCache->slots; ct++) for (n = ThisCache->nodes[ct]; n; n = tmp) { tmp = n->next_node; if (n->zone == zone) cache_free_node(ThisCache, ct, n); }}/*--- cache_purge_zone() ------------------------------------------------------------------------*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -