📄 resolver.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: resolver.c,v 1.218.2.18.4.43 2004/08/28 06:25:19 marka Exp $ */#include <config.h>#include <isc/print.h>#include <isc/string.h>#include <isc/task.h>#include <isc/timer.h>#include <isc/util.h>#include <dns/acl.h>#include <dns/adb.h>#include <dns/db.h>#include <dns/dispatch.h>#include <dns/events.h>#include <dns/forward.h>#include <dns/keytable.h>#include <dns/log.h>#include <dns/message.h>#include <dns/ncache.h>#include <dns/opcode.h>#include <dns/peer.h>#include <dns/rbt.h>#include <dns/rcode.h>#include <dns/rdata.h>#include <dns/rdataclass.h>#include <dns/rdatalist.h>#include <dns/rdataset.h>#include <dns/rdatastruct.h>#include <dns/rdatatype.h>#include <dns/resolver.h>#include <dns/result.h>#include <dns/tsig.h>#include <dns/validator.h>#define DNS_RESOLVER_TRACE#ifdef DNS_RESOLVER_TRACE#define RTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "res %p: %s", res, (m))#define RRTRACE(r, m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "res %p: %s", (r), (m))#define FCTXTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "fctx %p(%s'): %s", fctx, fctx->info, (m))#define FCTXTRACE2(m1, m2) \ isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "fctx %p(%s): %s %s", \ fctx, fctx->info, (m1), (m2))#define FTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "fetch %p (fctx %p(%s)): %s", \ fetch, fetch->private, \ fetch->private->info, (m))#define QTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ "resquery %p (fctx %p(%s)): %s", \ query, query->fctx, \ query->fctx->info, (m))#else#define RTRACE(m)#define RRTRACE(r, m)#define FCTXTRACE(m)#define FTRACE(m)#define QTRACE(m)#endif/* * Maximum EDNS0 input packet size. */#define RECV_BUFFER_SIZE 4096 /* XXXRTH Constant. *//* * This defines the maximum number of timeouts we will permit before we * disable EDNS0 on the query. */#define MAX_EDNS0_TIMEOUTS 3typedef struct fetchctx fetchctx_t;typedef struct query { /* Locked by task event serialization. */ unsigned int magic; fetchctx_t * fctx; isc_mem_t * mctx; dns_dispatchmgr_t * dispatchmgr; dns_dispatch_t * dispatch; dns_adbaddrinfo_t * addrinfo; isc_socket_t * tcpsocket; isc_time_t start; dns_messageid_t id; dns_dispentry_t * dispentry; ISC_LINK(struct query) link; isc_buffer_t buffer; isc_buffer_t *tsig; dns_tsigkey_t *tsigkey; unsigned int options; unsigned int attributes; unsigned int sends; unsigned int connects; unsigned char data[512];} resquery_t;#define QUERY_MAGIC ISC_MAGIC('Q', '!', '!', '!')#define VALID_QUERY(query) ISC_MAGIC_VALID(query, QUERY_MAGIC)#define RESQUERY_ATTR_CANCELED 0x02#define RESQUERY_CONNECTING(q) ((q)->connects > 0)#define RESQUERY_CANCELED(q) (((q)->attributes & \ RESQUERY_ATTR_CANCELED) != 0)#define RESQUERY_SENDING(q) ((q)->sends > 0)typedef enum { fetchstate_init = 0, /* Start event has not run yet. */ fetchstate_active, fetchstate_done /* FETCHDONE events posted. */} fetchstate;struct fetchctx { /* Not locked. */ unsigned int magic; dns_resolver_t * res; dns_name_t name; dns_rdatatype_t type; unsigned int options; unsigned int bucketnum; char * info; /* Locked by appropriate bucket lock. */ fetchstate state; isc_boolean_t want_shutdown; isc_boolean_t cloned; unsigned int references; isc_event_t control_event; ISC_LINK(struct fetchctx) link; ISC_LIST(dns_fetchevent_t) events; /* Locked by task event serialization. */ dns_name_t domain; dns_rdataset_t nameservers; unsigned int attributes; isc_timer_t * timer; isc_time_t expires; isc_interval_t interval; dns_message_t * qmessage; dns_message_t * rmessage; ISC_LIST(resquery_t) queries; dns_adbfindlist_t finds; dns_adbfind_t * find; dns_adbfindlist_t altfinds; dns_adbfind_t * altfind; dns_adbaddrinfolist_t forwaddrs; dns_adbaddrinfolist_t altaddrs; isc_sockaddrlist_t forwarders; dns_fwdpolicy_t fwdpolicy; isc_sockaddrlist_t bad; ISC_LIST(dns_validator_t) validators; dns_db_t * cache; dns_adb_t * adb; /* * The number of events we're waiting for. */ unsigned int pending; /* * The number of times we've "restarted" the current * nameserver set. This acts as a failsafe to prevent * us from pounding constantly on a particular set of * servers that, for whatever reason, are not giving * us useful responses, but are responding in such a * way that they are not marked "bad". */ unsigned int restarts; /* * The number of timeouts that have occurred since we * last successfully received a response packet. This * is used for EDNS0 black hole detection. */ unsigned int timeouts; /* * Look aside state for DS lookups. */ dns_name_t nsname; dns_fetch_t * nsfetch; dns_rdataset_t nsrrset;};#define FCTX_MAGIC ISC_MAGIC('F', '!', '!', '!')#define VALID_FCTX(fctx) ISC_MAGIC_VALID(fctx, FCTX_MAGIC)#define FCTX_ATTR_HAVEANSWER 0x0001#define FCTX_ATTR_GLUING 0x0002#define FCTX_ATTR_ADDRWAIT 0x0004#define FCTX_ATTR_SHUTTINGDOWN 0x0008#define FCTX_ATTR_WANTCACHE 0x0010#define FCTX_ATTR_WANTNCACHE 0x0020#define FCTX_ATTR_NEEDEDNS0 0x0040#define FCTX_ATTR_TRIEDFIND 0x0080#define FCTX_ATTR_TRIEDALT 0x0100#define HAVE_ANSWER(f) (((f)->attributes & FCTX_ATTR_HAVEANSWER) != \ 0)#define GLUING(f) (((f)->attributes & FCTX_ATTR_GLUING) != \ 0)#define ADDRWAIT(f) (((f)->attributes & FCTX_ATTR_ADDRWAIT) != \ 0)#define SHUTTINGDOWN(f) (((f)->attributes & FCTX_ATTR_SHUTTINGDOWN) \ != 0)#define WANTCACHE(f) (((f)->attributes & FCTX_ATTR_WANTCACHE) != 0)#define WANTNCACHE(f) (((f)->attributes & FCTX_ATTR_WANTNCACHE) != 0)#define NEEDEDNS0(f) (((f)->attributes & FCTX_ATTR_NEEDEDNS0) != 0)#define TRIEDFIND(f) (((f)->attributes & FCTX_ATTR_TRIEDFIND) != 0)#define TRIEDALT(f) (((f)->attributes & FCTX_ATTR_TRIEDALT) != 0)struct dns_fetch { unsigned int magic; fetchctx_t * private;};#define DNS_FETCH_MAGIC ISC_MAGIC('F', 't', 'c', 'h')#define DNS_FETCH_VALID(fetch) ISC_MAGIC_VALID(fetch, DNS_FETCH_MAGIC)typedef struct fctxbucket { isc_task_t * task; isc_mutex_t lock; ISC_LIST(fetchctx_t) fctxs; isc_boolean_t exiting;} fctxbucket_t;typedef struct alternate { isc_boolean_t isaddress; union { isc_sockaddr_t addr; struct { dns_name_t name; in_port_t port; } _n; } _u; ISC_LINK(struct alternate) link;} alternate_t;struct dns_resolver { /* Unlocked. */ unsigned int magic; isc_mem_t * mctx; isc_mutex_t lock; isc_mutex_t nlock; isc_mutex_t primelock; dns_rdataclass_t rdclass; isc_socketmgr_t * socketmgr; isc_timermgr_t * timermgr; isc_taskmgr_t * taskmgr; dns_view_t * view; isc_boolean_t frozen; unsigned int options; dns_dispatchmgr_t * dispatchmgr; dns_dispatch_t * dispatchv4; dns_dispatch_t * dispatchv6; unsigned int nbuckets; fctxbucket_t * buckets; isc_uint32_t lame_ttl; ISC_LIST(alternate_t) alternates; isc_uint16_t udpsize;#if USE_ALGLOCK isc_rwlock_t alglock;#endif dns_rbt_t * algorithms;#if USE_MBSLOCK isc_rwlock_t mbslock;#endif dns_rbt_t * mustbesecure; /* Locked by lock. */ unsigned int references; isc_boolean_t exiting; isc_eventlist_t whenshutdown; unsigned int activebuckets; isc_boolean_t priming; /* Locked by primelock. */ dns_fetch_t * primefetch; /* Locked by nlock. */ unsigned int nfctx;};#define RES_MAGIC ISC_MAGIC('R', 'e', 's', '!')#define VALID_RESOLVER(res) ISC_MAGIC_VALID(res, RES_MAGIC)/* * Private addrinfo flags. These must not conflict with DNS_FETCHOPT_NOEDNS0, * which we also use as an addrinfo flag. */#define FCTX_ADDRINFO_MARK 0x0001#define FCTX_ADDRINFO_FORWARDER 0x1000#define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \ == 0)#define ISFORWARDER(a) (((a)->flags & \ FCTX_ADDRINFO_FORWARDER) != 0)#define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)static void destroy(dns_resolver_t *res);static void empty_bucket(dns_resolver_t *res);static isc_result_t resquery_send(resquery_t *query);static void resquery_response(isc_task_t *task, isc_event_t *event);static void resquery_connected(isc_task_t *task, isc_event_t *event);static void fctx_try(fetchctx_t *fctx);static isc_boolean_t fctx_destroy(fetchctx_t *fctx);static isc_result_t ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl, dns_rdataset_t *ardataset, isc_result_t *eresultp);static isc_boolean_tfix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) { dns_name_t *name; dns_name_t *domain = &fctx->domain; dns_rdataset_t *rdataset; dns_rdatatype_t type; isc_result_t result; isc_boolean_t keep_auth = ISC_FALSE; if (message->rcode == dns_rcode_nxdomain) return (ISC_FALSE); /* * Look for BIND 8 style delegations. * Also look for answers to ANY queries where the duplicate NS RRset * may have been stripped from the authority section. */ if (message->counts[DNS_SECTION_ANSWER] != 0 && (fctx->type == dns_rdatatype_ns || fctx->type == dns_rdatatype_any)) { result = dns_message_firstname(message, DNS_SECTION_ANSWER); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, DNS_SECTION_ANSWER, &name); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { type = rdataset->type; if (type != dns_rdatatype_ns) continue; if (dns_name_issubdomain(name, domain)) return (ISC_FALSE); } result = dns_message_nextname(message, DNS_SECTION_ANSWER); } } /* Look for referral. */ if (message->counts[DNS_SECTION_AUTHORITY] == 0) goto munge; result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { type = rdataset->type; if (type == dns_rdatatype_soa && dns_name_equal(name, domain)) keep_auth = ISC_TRUE; if (type != dns_rdatatype_ns && type != dns_rdatatype_soa) continue; if (dns_name_equal(name, domain)) goto munge; if (dns_name_issubdomain(name, domain)) return (ISC_FALSE); } result = dns_message_nextname(message, DNS_SECTION_AUTHORITY); } munge: message->rcode = dns_rcode_nxdomain; message->counts[DNS_SECTION_ANSWER] = 0; if (!keep_auth) message->counts[DNS_SECTION_AUTHORITY] = 0; message->counts[DNS_SECTION_ADDITIONAL] = 0; return (ISC_TRUE);}static inline isc_result_tfctx_starttimer(fetchctx_t *fctx) { /* * Start the lifetime timer for fctx. * * This is also used for stopping the idle timer; in that * case we must purge events already posted to ensure that * no further idle events are delivered. */ return (isc_timer_reset(fctx->timer, isc_timertype_once, &fctx->expires, NULL, ISC_TRUE));}static inline voidfctx_stoptimer(fetchctx_t *fctx) { isc_result_t result; /* * We don't return a result if resetting the timer to inactive fails * since there's nothing to be done about it. Resetting to inactive * should never fail anyway, since the code as currently written * cannot fail in that case. */ result = isc_timer_reset(fctx->timer, isc_timertype_inactive, NULL, NULL, ISC_TRUE); if (result != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_timer_reset(): %s", isc_result_totext(result)); }}static inline isc_result_tfctx_startidletimer(fetchctx_t *fctx) { /* * Start the idle timer for fctx. The lifetime timer continues * to be in effect. */ return (isc_timer_reset(fctx->timer, isc_timertype_once, &fctx->expires, &fctx->interval, ISC_FALSE));}/* * Stopping the idle timer is equivalent to calling fctx_starttimer(), but * we use fctx_stopidletimer for readability in the code below. */#define fctx_stopidletimer fctx_starttimerstatic inline voidresquery_destroy(resquery_t **queryp) { resquery_t *query; REQUIRE(queryp != NULL); query = *queryp; REQUIRE(!ISC_LINK_LINKED(query, link)); INSIST(query->tcpsocket == NULL); query->magic = 0; isc_mem_put(query->mctx, query, sizeof(*query)); *queryp = NULL;}static voidfctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp, isc_time_t *finish, isc_boolean_t no_response){ fetchctx_t *fctx; resquery_t *query; unsigned int rtt; unsigned int factor; dns_adbfind_t *find; dns_adbaddrinfo_t *addrinfo; query = *queryp; fctx = query->fctx; FCTXTRACE("cancelquery"); REQUIRE(!RESQUERY_CANCELED(query)); query->attributes |= RESQUERY_ATTR_CANCELED; /* * Should we update the RTT? */ if (finish != NULL || no_response) { if (finish != NULL) { /* * We have both the start and finish times for this * packet, so we can compute a real RTT. */ rtt = (unsigned int)isc_time_microdiff(finish, &query->start);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -