📄 adb.c
字号:
/* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. *//* $Id: adb.c,v 1.181.2.11.2.19 2004/09/01 05:19:57 marka Exp $ *//* * Implementation notes * -------------------- * * In finds, if task == NULL, no events will be generated, and no events * have been sent. If task != NULL but taskaction == NULL, an event has been * posted but not yet freed. If neither are NULL, no event was posted. * *//* * After we have cleaned all buckets, dump the database contents. */#if 0#define DUMP_ADB_AFTER_CLEANING#endif#include <config.h>#include <limits.h>#include <isc/mutexblock.h>#include <isc/netaddr.h>#include <isc/random.h>#include <isc/string.h> /* Required for HP/UX (and others?) */#include <isc/task.h>#include <isc/timer.h>#include <isc/util.h>#include <dns/adb.h>#include <dns/db.h>#include <dns/events.h>#include <dns/log.h>#include <dns/rdata.h>#include <dns/rdataset.h>#include <dns/rdatastruct.h>#include <dns/resolver.h>#include <dns/result.h>#define DNS_ADB_MAGIC ISC_MAGIC('D', 'a', 'd', 'b')#define DNS_ADB_VALID(x) ISC_MAGIC_VALID(x, DNS_ADB_MAGIC)#define DNS_ADBNAME_MAGIC ISC_MAGIC('a', 'd', 'b', 'N')#define DNS_ADBNAME_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAME_MAGIC)#define DNS_ADBNAMEHOOK_MAGIC ISC_MAGIC('a', 'd', 'N', 'H')#define DNS_ADBNAMEHOOK_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAMEHOOK_MAGIC)#define DNS_ADBZONEINFO_MAGIC ISC_MAGIC('a', 'd', 'b', 'Z')#define DNS_ADBZONEINFO_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBZONEINFO_MAGIC)#define DNS_ADBENTRY_MAGIC ISC_MAGIC('a', 'd', 'b', 'E')#define DNS_ADBENTRY_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBENTRY_MAGIC)#define DNS_ADBFETCH_MAGIC ISC_MAGIC('a', 'd', 'F', '4')#define DNS_ADBFETCH_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH_MAGIC)#define DNS_ADBFETCH6_MAGIC ISC_MAGIC('a', 'd', 'F', '6')#define DNS_ADBFETCH6_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH6_MAGIC)/* * The number of buckets needs to be a prime (for good hashing). * * XXXRTH How many buckets do we need? */#define NBUCKETS 1009 /* how many buckets for names/addrs *//* * For type 3 negative cache entries, we will remember that the address is * broken for this long. XXXMLG This is also used for actual addresses, too. * The intent is to keep us from constantly asking about A/AAAA records * if the zone has extremely low TTLs. */#define ADB_CACHE_MINIMUM 10 /* seconds */#define ADB_CACHE_MAXIMUM 86400 /* seconds (86400 = 24 hours) */#define ADB_ENTRY_WINDOW 1800 /* seconds *//* * Wake up every CLEAN_SECONDS and clean CLEAN_BUCKETS buckets, so that all * buckets are cleaned in CLEAN_PERIOD seconds. */#define CLEAN_PERIOD 3600#define CLEAN_SECONDS 30#define CLEAN_BUCKETS ((NBUCKETS * CLEAN_SECONDS) / CLEAN_PERIOD)#define FREE_ITEMS 64 /* free count for memory pools */#define FILL_COUNT 16 /* fill count for memory pools */#define DNS_ADB_INVALIDBUCKET (-1) /* invalid bucket address */#define DNS_ADB_MINADBSIZE (1024*1024) /* 1 Megabyte */typedef ISC_LIST(dns_adbname_t) dns_adbnamelist_t;typedef struct dns_adbnamehook dns_adbnamehook_t;typedef ISC_LIST(dns_adbnamehook_t) dns_adbnamehooklist_t;typedef struct dns_adbzoneinfo dns_adbzoneinfo_t;typedef ISC_LIST(dns_adbentry_t) dns_adbentrylist_t;typedef struct dns_adbfetch dns_adbfetch_t;typedef struct dns_adbfetch6 dns_adbfetch6_t;struct dns_adb { unsigned int magic; isc_mutex_t lock; isc_mutex_t reflock; /* Covers irefcnt, erefcnt */ isc_mem_t *mctx; dns_view_t *view; isc_timermgr_t *timermgr; isc_timer_t *timer; isc_taskmgr_t *taskmgr; isc_task_t *task; isc_boolean_t overmem; isc_interval_t tick_interval; int next_cleanbucket; unsigned int irefcnt; unsigned int erefcnt; isc_mutex_t mplock; isc_mempool_t *nmp; /* dns_adbname_t */ isc_mempool_t *nhmp; /* dns_adbnamehook_t */ isc_mempool_t *zimp; /* dns_adbzoneinfo_t */ isc_mempool_t *emp; /* dns_adbentry_t */ isc_mempool_t *ahmp; /* dns_adbfind_t */ isc_mempool_t *aimp; /* dns_adbaddrinfo_t */ isc_mempool_t *afmp; /* dns_adbfetch_t */ /* * Bucketized locks and lists for names. * * XXXRTH Have a per-bucket structure that contains all of these? */ dns_adbnamelist_t names[NBUCKETS]; isc_mutex_t namelocks[NBUCKETS]; isc_boolean_t name_sd[NBUCKETS]; unsigned int name_refcnt[NBUCKETS]; /* * Bucketized locks for entries. * * XXXRTH Have a per-bucket structure that contains all of these? */ dns_adbentrylist_t entries[NBUCKETS]; isc_mutex_t entrylocks[NBUCKETS]; isc_boolean_t entry_sd[NBUCKETS]; /* shutting down */ unsigned int entry_refcnt[NBUCKETS]; isc_event_t cevent; isc_boolean_t cevent_sent; isc_boolean_t shutting_down; isc_eventlist_t whenshutdown;};/* * XXXMLG Document these structures. */struct dns_adbname { unsigned int magic; dns_name_t name; dns_adb_t *adb; unsigned int partial_result; unsigned int flags; int lock_bucket; dns_name_t target; isc_stdtime_t expire_target; isc_stdtime_t expire_v4; isc_stdtime_t expire_v6; unsigned int chains; dns_adbnamehooklist_t v4; dns_adbnamehooklist_t v6; dns_adbfetch_t *fetch_a; dns_adbfetch_t *fetch_aaaa; unsigned int fetch_err; unsigned int fetch6_err; dns_adbfindlist_t finds; ISC_LINK(dns_adbname_t) plink;};struct dns_adbfetch { unsigned int magic; dns_adbnamehook_t *namehook; dns_adbentry_t *entry; dns_fetch_t *fetch; dns_rdataset_t rdataset;};/* * dns_adbnamehook_t * * This is a small widget that dangles off a dns_adbname_t. It contains a * pointer to the address information about this host, and a link to the next * namehook that will contain the next address this host has. */struct dns_adbnamehook { unsigned int magic; dns_adbentry_t *entry; ISC_LINK(dns_adbnamehook_t) plink;};/* * dns_adbzoneinfo_t * * This is a small widget that holds zone-specific information about an * address. Currently limited to lameness, but could just as easily be * extended to other types of information about zones. */struct dns_adbzoneinfo { unsigned int magic; dns_name_t zone; isc_stdtime_t lame_timer; ISC_LINK(dns_adbzoneinfo_t) plink;};/* * An address entry. It holds quite a bit of information about addresses, * including edns state (in "flags"), rtt, and of course the address of * the host. */struct dns_adbentry { unsigned int magic; int lock_bucket; unsigned int refcnt; unsigned int flags; unsigned int srtt; isc_sockaddr_t sockaddr; isc_stdtime_t expires; /* * A nonzero 'expires' field indicates that the entry should * persist until that time. This allows entries found * using dns_adb_findaddrinfo() to persist for a limited time * even though they are not necessarily associated with a * name. */ ISC_LIST(dns_adbzoneinfo_t) zoneinfo; ISC_LINK(dns_adbentry_t) plink;};/* * Internal functions (and prototypes). */static inline dns_adbname_t *new_adbname(dns_adb_t *, dns_name_t *);static inline void free_adbname(dns_adb_t *, dns_adbname_t **);static inline dns_adbnamehook_t *new_adbnamehook(dns_adb_t *, dns_adbentry_t *);static inline void free_adbnamehook(dns_adb_t *, dns_adbnamehook_t **);static inline dns_adbzoneinfo_t *new_adbzoneinfo(dns_adb_t *, dns_name_t *);static inline void free_adbzoneinfo(dns_adb_t *, dns_adbzoneinfo_t **);static inline dns_adbentry_t *new_adbentry(dns_adb_t *);static inline void free_adbentry(dns_adb_t *, dns_adbentry_t **);static inline dns_adbfind_t *new_adbfind(dns_adb_t *);static inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **);static inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *, in_port_t);static inline dns_adbfetch_t *new_adbfetch(dns_adb_t *);static inline void free_adbfetch(dns_adb_t *, dns_adbfetch_t **);static inline dns_adbname_t *find_name_and_lock(dns_adb_t *, dns_name_t *, unsigned int, int *);static inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *, isc_sockaddr_t *, int *);static void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug);static void print_dns_name(FILE *, dns_name_t *);static void print_namehook_list(FILE *, const char *legend, dns_adbnamehooklist_t *list, isc_boolean_t debug, isc_stdtime_t now);static void print_find_list(FILE *, dns_adbname_t *);static void print_fetch_list(FILE *, dns_adbname_t *);static inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *);static inline void inc_adb_irefcnt(dns_adb_t *);static inline void inc_adb_erefcnt(dns_adb_t *);static inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *, isc_boolean_t);static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, dns_adbentry_t *, isc_boolean_t);static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *);static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);static void clean_target(dns_adb_t *, dns_name_t *);static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, unsigned int);static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t, isc_boolean_t);static void cancel_fetches_at_name(dns_adbname_t *);static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t, dns_rdatatype_t);static isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t, dns_rdatatype_t);static inline void check_exit(dns_adb_t *);static void timer_cleanup(isc_task_t *, isc_event_t *);static void destroy(dns_adb_t *);static isc_boolean_t shutdown_names(dns_adb_t *);static isc_boolean_t shutdown_entries(dns_adb_t *);static inline void link_name(dns_adb_t *, int, dns_adbname_t *);static inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *);static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *);static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *);static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t);static void water(void *arg, int mark);/* * MUST NOT overlap DNS_ADBFIND_* flags! */#define FIND_EVENT_SENT 0x40000000#define FIND_EVENT_FREED 0x80000000#define FIND_EVENTSENT(h) (((h)->flags & FIND_EVENT_SENT) != 0)#define FIND_EVENTFREED(h) (((h)->flags & FIND_EVENT_FREED) != 0)#define NAME_NEEDS_POKE 0x80000000#define NAME_IS_DEAD 0x40000000#define NAME_HINT_OK DNS_ADBFIND_HINTOK#define NAME_GLUE_OK DNS_ADBFIND_GLUEOK#define NAME_STARTATZONE DNS_ADBFIND_STARTATZONE#define NAME_DEAD(n) (((n)->flags & NAME_IS_DEAD) != 0)#define NAME_NEEDSPOKE(n) (((n)->flags & NAME_NEEDS_POKE) != 0)#define NAME_GLUEOK(n) (((n)->flags & NAME_GLUE_OK) != 0)#define NAME_HINTOK(n) (((n)->flags & NAME_HINT_OK) != 0)/* * To the name, address classes are all that really exist. If it has a * V6 address it doesn't care if it came from a AAAA query. */#define NAME_HAS_V4(n) (!ISC_LIST_EMPTY((n)->v4))#define NAME_HAS_V6(n) (!ISC_LIST_EMPTY((n)->v6))#define NAME_HAS_ADDRS(n) (NAME_HAS_V4(n) || NAME_HAS_V6(n))/* * Fetches are broken out into A and AAAA types. In some cases, * however, it makes more sense to test for a particular class of fetches, * like V4 or V6 above. * Note: since we have removed the support of A6 in adb, FETCH_A and FETCH_AAAA * are now equal to FETCH_V4 and FETCH_V6, respectively. */#define NAME_FETCH_A(n) ((n)->fetch_a != NULL)#define NAME_FETCH_AAAA(n) ((n)->fetch_aaaa != NULL)#define NAME_FETCH_V4(n) (NAME_FETCH_A(n))#define NAME_FETCH_V6(n) (NAME_FETCH_AAAA(n))#define NAME_FETCH(n) (NAME_FETCH_V4(n) || NAME_FETCH_V6(n))/* * Find options and tests to see if there are addresses on the list. */#define FIND_WANTEVENT(fn) (((fn)->options & DNS_ADBFIND_WANTEVENT) != 0)#define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0)#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) \ != 0)#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) \ != 0)#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0)#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0)#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list))#define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0)/* * These are currently used on simple unsigned ints, so they are * not really associated with any particular type. */#define WANT_INET(x) (((x) & DNS_ADBFIND_INET) != 0)#define WANT_INET6(x) (((x) & DNS_ADBFIND_INET6) != 0)#define EXPIRE_OK(exp, now) ((exp == INT_MAX) || (exp < now))/* * Find out if the flags on a name (nf) indicate if it is a hint or * glue, and compare this to the appropriate bits set in o, to see if * this is ok. */#define GLUE_OK(nf, o) (!NAME_GLUEOK(nf) || (((o) & DNS_ADBFIND_GLUEOK) != 0))#define HINT_OK(nf, o) (!NAME_HINTOK(nf) || (((o) & DNS_ADBFIND_HINTOK) != 0))#define GLUEHINT_OK(nf, o) (GLUE_OK(nf, o) || HINT_OK(nf, o))#define STARTATZONE_MATCHES(nf, o) (((nf)->flags & NAME_STARTATZONE) == \ ((o) & DNS_ADBFIND_STARTATZONE))#define ENTER_LEVEL ISC_LOG_DEBUG(50)#define EXIT_LEVEL ENTER_LEVEL#define CLEAN_LEVEL ISC_LOG_DEBUG(100)#define DEF_LEVEL ISC_LOG_DEBUG(5)#define NCACHE_LEVEL ISC_LOG_DEBUG(20)#define NCACHE_RESULT(r) ((r) == DNS_R_NCACHENXDOMAIN || \ (r) == DNS_R_NCACHENXRRSET)#define AUTH_NX(r) ((r) == DNS_R_NXDOMAIN || \ (r) == DNS_R_NXRRSET)#define NXDOMAIN_RESULT(r) ((r) == DNS_R_NXDOMAIN || \ (r) == DNS_R_NCACHENXDOMAIN)#define NXRRSET_RESULT(r) ((r) == DNS_R_NCACHENXRRSET || \ (r) == DNS_R_NXRRSET || \ (r) == DNS_R_HINTNXRRSET)/* * Error state rankings. */#define FIND_ERR_SUCCESS 0 /* highest rank */#define FIND_ERR_CANCELED 1#define FIND_ERR_FAILURE 2#define FIND_ERR_NXDOMAIN 3#define FIND_ERR_NXRRSET 4#define FIND_ERR_UNEXPECTED 5#define FIND_ERR_NOTFOUND 6#define FIND_ERR_MAX 7static const char *errnames[] = { "success", "canceled", "failure", "nxdomain", "nxrrset", "unexpected", "not_found"};#define NEWERR(old, new) (ISC_MIN((old), (new)))static isc_result_t find_err_map[FIND_ERR_MAX] = { ISC_R_SUCCESS, ISC_R_CANCELED, ISC_R_FAILURE, DNS_R_NXDOMAIN, DNS_R_NXRRSET, ISC_R_UNEXPECTED, ISC_R_NOTFOUND /* not YET found */};static voidDP(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3);static voidDP(int level, const char *format, ...) { va_list args; va_start(args, format); isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB, level, format, args); va_end(args);}static inline dns_ttl_tttlclamp(dns_ttl_t ttl) { if (ttl < ADB_CACHE_MINIMUM) ttl = ADB_CACHE_MINIMUM; if (ttl > ADB_CACHE_MAXIMUM) ttl = ADB_CACHE_MAXIMUM; return (ttl);}/* * Requires the adbname bucket be locked and that no entry buckets be locked. * * This code handles A and AAAA rdatasets only. */static isc_result_timport_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, isc_stdtime_t now){ isc_result_t result; dns_adb_t *adb; dns_adbnamehook_t *nh; dns_adbnamehook_t *anh; dns_rdata_t rdata = DNS_RDATA_INIT; struct in_addr ina; struct in6_addr in6a; isc_sockaddr_t sockaddr; dns_adbentry_t *foundentry; /* NO CLEAN UP! */ int addr_bucket; isc_boolean_t new_addresses_added; dns_rdatatype_t rdtype; unsigned int findoptions; INSIST(DNS_ADBNAME_VALID(adbname)); adb = adbname->adb; INSIST(DNS_ADB_VALID(adb)); rdtype = rdataset->type; INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa)); if (rdtype == dns_rdatatype_a) findoptions = DNS_ADBFIND_INET; else findoptions = DNS_ADBFIND_INET6; addr_bucket = DNS_ADB_INVALIDBUCKET; new_addresses_added = ISC_FALSE; nh = NULL; result = dns_rdataset_first(rdataset); while (result == ISC_R_SUCCESS) { dns_rdata_reset(&rdata); dns_rdataset_current(rdataset, &rdata); if (rdtype == dns_rdatatype_a) { INSIST(rdata.length == 4); memcpy(&ina.s_addr, rdata.data, 4);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -