/contrib/bind9/lib/dns/resolver.c
https://bitbucket.org/freebsd/freebsd-head/ · C · 8823 lines · 6299 code · 960 blank · 1564 comment · 1964 complexity · 651087635c1e860a3303d6aab40e83f7 MD5 · raw file
Large files are truncated click here to view the full file
- /*
- * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC")
- * Copyright (C) 1999-2003 Internet Software Consortium.
- *
- * Permission to use, copy, modify, and/or 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$ */
- /*! \file */
- #include <config.h>
- #include <isc/platform.h>
- #include <isc/print.h>
- #include <isc/string.h>
- #include <isc/random.h>
- #include <isc/task.h>
- #include <isc/stats.h>
- #include <isc/timer.h>
- #include <isc/util.h>
- #include <dns/acl.h>
- #include <dns/adb.h>
- #include <dns/cache.h>
- #include <dns/db.h>
- #include <dns/dispatch.h>
- #include <dns/ds.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/rootns.h>
- #include <dns/stats.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
- #ifndef DEFAULT_QUERY_TIMEOUT
- #define DEFAULT_QUERY_TIMEOUT 30 /* The default time in seconds for the whole query to live. */
- #endif
- #ifndef MAXIMUM_QUERY_TIMEOUT
- #define MAXIMUM_QUERY_TIMEOUT 30 /* The maximum time in seconds for the whole query to live. */
- #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 3
- typedef 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;
- isc_boolean_t exclusivesocket;
- 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;
- typedef enum {
- badns_unreachable = 0,
- badns_response,
- badns_validation
- } badnstype_t;
- 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;
- isc_mem_t * mctx;
- /*% Locked by appropriate bucket lock. */
- fetchstate state;
- isc_boolean_t want_shutdown;
- isc_boolean_t cloned;
- isc_boolean_t spilled;
- 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_sockaddrlist_t edns;
- isc_sockaddrlist_t edns512;
- isc_sockaddrlist_t bad_edns;
- dns_validator_t *validator;
- ISC_LIST(dns_validator_t) validators;
- dns_db_t * cache;
- dns_adb_t * adb;
- isc_boolean_t ns_ttl_ok;
- isc_uint32_t ns_ttl;
- /*%
- * 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;
- /*%
- * Number of queries that reference this context.
- */
- unsigned int nqueries;
- /*%
- * The reason to print when logging a successful
- * response to a query.
- */
- const char * reason;
- /*%
- * Random numbers to use for mixing up server addresses.
- */
- isc_uint32_t rand_buf;
- isc_uint32_t rand_bits;
- /*%
- * Fetch-local statistics for detailed logging.
- */
- isc_result_t result; /*%< fetch result */
- isc_result_t vresult; /*%< validation result */
- int exitline;
- isc_time_t start;
- isc_uint64_t duration;
- isc_boolean_t logged;
- unsigned int querysent;
- unsigned int referrals;
- unsigned int lamecount;
- unsigned int neterr;
- unsigned int badresp;
- unsigned int adberr;
- unsigned int findfail;
- unsigned int valfail;
- isc_boolean_t timeout;
- dns_adbaddrinfo_t *addrinfo;
- isc_sockaddr_t *client;
- };
- #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)
- typedef struct {
- dns_adbaddrinfo_t * addrinfo;
- fetchctx_t * fctx;
- } dns_valarg_t;
- 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;
- isc_mem_t * mctx;
- } 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;
- typedef struct dns_badcache dns_badcache_t;
- struct dns_badcache {
- dns_badcache_t * next;
- dns_rdatatype_t type;
- isc_time_t expire;
- unsigned int hashval;
- dns_name_t name;
- };
- #define DNS_BADCACHE_SIZE 1021
- #define DNS_BADCACHE_TTL(fctx) \
- (((fctx)->res->lame_ttl > 30 ) ? (fctx)->res->lame_ttl : 30)
- 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;
- isc_boolean_t exclusivev4;
- dns_dispatch_t * dispatchv6;
- isc_boolean_t exclusivev6;
- unsigned int ndisps;
- 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;
- unsigned int spillatmax;
- unsigned int spillatmin;
- isc_timer_t * spillattimer;
- isc_boolean_t zero_no_soa_ttl;
- unsigned int query_timeout;
- /* Locked by lock. */
- unsigned int references;
- isc_boolean_t exiting;
- isc_eventlist_t whenshutdown;
- unsigned int activebuckets;
- isc_boolean_t priming;
- unsigned int spillat; /* clients-per-query */
- unsigned int nextdisp;
- /* Bad cache. */
- dns_badcache_t ** badcache;
- unsigned int badcount;
- unsigned int badhash;
- unsigned int badsweep;
- /* 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 FCTX_ADDRINFO_TRIED 0x2000
- #define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \
- == 0)
- #define ISFORWARDER(a) (((a)->flags & \
- FCTX_ADDRINFO_FORWARDER) != 0)
- #define TRIED(a) (((a)->flags & \
- FCTX_ADDRINFO_TRIED) != 0)
- #define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
- #define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 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, isc_boolean_t retrying,
- isc_boolean_t badcache);
- static void fctx_destroy(fetchctx_t *fctx);
- static isc_boolean_t fctx_unlink(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,
- isc_boolean_t optout,
- dns_rdataset_t *ardataset,
- isc_result_t *eresultp);
- static void validated(isc_task_t *task, isc_event_t *event);
- static isc_boolean_t maybe_destroy(fetchctx_t *fctx, isc_boolean_t locked);
- static void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
- isc_result_t reason, badnstype_t badtype);
- /*%
- * Increment resolver-related statistics counters.
- */
- static inline void
- inc_stats(dns_resolver_t *res, isc_statscounter_t counter) {
- if (res->view->resstats != NULL)
- isc_stats_increment(res->view->resstats, counter);
- }
- static isc_result_t
- valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
- dns_rdatatype_t type, dns_rdataset_t *rdataset,
- dns_rdataset_t *sigrdataset, unsigned int valoptions,
- isc_task_t *task)
- {
- dns_validator_t *validator = NULL;
- dns_valarg_t *valarg;
- isc_result_t result;
- valarg = isc_mem_get(fctx->mctx, sizeof(*valarg));
- if (valarg == NULL)
- return (ISC_R_NOMEMORY);
- valarg->fctx = fctx;
- valarg->addrinfo = addrinfo;
- if (!ISC_LIST_EMPTY(fctx->validators))
- INSIST((valoptions & DNS_VALIDATOR_DEFER) != 0);
- result = dns_validator_create(fctx->res->view, name, type, rdataset,
- sigrdataset, fctx->rmessage,
- valoptions, task, validated, valarg,
- &validator);
- if (result == ISC_R_SUCCESS) {
- inc_stats(fctx->res, dns_resstatscounter_val);
- if ((valoptions & DNS_VALIDATOR_DEFER) == 0) {
- INSIST(fctx->validator == NULL);
- fctx->validator = validator;
- }
- ISC_LIST_APPEND(fctx->validators, validator, link);
- } else
- isc_mem_put(fctx->mctx, valarg, sizeof(*valarg));
- return (result);
- }
- static isc_boolean_t
- rrsig_fromchildzone(fetchctx_t *fctx, dns_rdataset_t *rdataset) {
- dns_namereln_t namereln;
- dns_rdata_rrsig_t rrsig;
- dns_rdata_t rdata = DNS_RDATA_INIT;
- int order;
- isc_result_t result;
- unsigned int labels;
- for (result = dns_rdataset_first(rdataset);
- result == ISC_R_SUCCESS;
- result = dns_rdataset_next(rdataset)) {
- dns_rdataset_current(rdataset, &rdata);
- result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
- namereln = dns_name_fullcompare(&rrsig.signer, &fctx->domain,
- &order, &labels);
- if (namereln == dns_namereln_subdomain)
- return (ISC_TRUE);
- dns_rdata_reset(&rdata);
- }
- return (ISC_FALSE);
- }
- static isc_boolean_t
- fix_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);
- /*
- * A DS RRset can appear anywhere in a zone, even for a delegation-only
- * zone. So a response to an explicit query for this type should be
- * excluded from delegation-only fixup.
- *
- * SOA, NS, and DNSKEY can only exist at a zone apex, so a postive
- * response to a query for these types can never violate the
- * delegation-only assumption: if the query name is below a
- * zone cut, the response should normally be a referral, which should
- * be accepted; if the query name is below a zone cut but the server
- * happens to have authority for the zone of the query name, the
- * response is a (non-referral) answer. But this does not violate
- * delegation-only because the query name must be in a different zone
- * due to the "apex-only" nature of these types. Note that if the
- * remote server happens to have authority for a child zone of a
- * delegation-only zone, we may still incorrectly "fix" the response
- * with NXDOMAIN for queries for other types. Unfortunately it's
- * generally impossible to differentiate this case from violation of
- * the delegation-only assumption. Once the resolver learns the
- * correct zone cut, possibly via a separate query for an "apex-only"
- * type, queries for other types will be resolved correctly.
- *
- * A query for type ANY will be accepted if it hits an exceptional
- * type above in the answer section as it should be from a child
- * zone.
- *
- * Also accept answers with RRSIG records from the child zone.
- * Direct queries for RRSIG records should not be answered from
- * the parent zone.
- */
- if (message->counts[DNS_SECTION_ANSWER] != 0 &&
- (fctx->type == dns_rdatatype_ns ||
- fctx->type == dns_rdatatype_ds ||
- fctx->type == dns_rdatatype_soa ||
- fctx->type == dns_rdatatype_any ||
- fctx->type == dns_rdatatype_rrsig ||
- fctx->type == dns_rdatatype_dnskey)) {
- 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)) {
- if (!dns_name_equal(name, &fctx->name))
- continue;
- type = rdataset->type;
- /*
- * RRsig from child?
- */
- if (type == dns_rdatatype_rrsig &&
- rrsig_fromchildzone(fctx, rdataset))
- return (ISC_FALSE);
- /*
- * Direct query for apex records or DS.
- */
- if (fctx->type == type &&
- (type == dns_rdatatype_ds ||
- type == dns_rdatatype_ns ||
- type == dns_rdatatype_soa ||
- type == dns_rdatatype_dnskey))
- return (ISC_FALSE);
- /*
- * Indirect query for apex records or DS.
- */
- if (fctx->type == dns_rdatatype_any &&
- (type == dns_rdatatype_ns ||
- type == dns_rdatatype_ds ||
- type == dns_rdatatype_soa ||
- type == dns_rdatatype_dnskey))
- return (ISC_FALSE);
- }
- result = dns_message_nextname(message,
- DNS_SECTION_ANSWER);
- }
- }
- /*
- * A NODATA response to a DS query?
- */
- if (fctx->type == dns_rdatatype_ds &&
- message->counts[DNS_SECTION_ANSWER] == 0)
- return (ISC_FALSE);
- /* Look for referral or indication of answer from child zone? */
- 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 &&
- type != dns_rdatatype_rrsig)
- continue;
- if (type == dns_rdatatype_rrsig) {
- if (rrsig_fromchildzone(fctx, rdataset))
- return (ISC_FALSE);
- else
- continue;
- }
- /* NS or SOA records. */
- if (dns_name_equal(name, domain)) {
- /*
- * If a query for ANY causes a negative
- * response, we can be sure that this is
- * an empty node. For other type of queries
- * we cannot differentiate an empty node
- * from a node that just doesn't have that
- * type of record. We only accept the former
- * case.
- */
- if (message->counts[DNS_SECTION_ANSWER] == 0 &&
- fctx->type == dns_rdatatype_any)
- return (ISC_FALSE);
- } else if (dns_name_issubdomain(name, domain)) {
- /* Referral or answer from child zone. */
- 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_t
- fctx_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 void
- fctx_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_t
- fctx_startidletimer(fetchctx_t *fctx, isc_interval_t *interval) {
- /*
- * 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, 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_starttimer
- static inline void
- resquery_destroy(resquery_t **queryp) {
- resquery_t *query;
- REQUIRE(queryp != NULL);
- query = *queryp;
- REQUIRE(!ISC_LINK_LINKED(query, link));
- INSIST(query->tcpsocket == NULL);
- query->fctx->nqueries--;
- if (SHUTTINGDOWN(query->fctx)) {
- dns_resolver_t *res = query->fctx->res;
- if (maybe_destroy(query->fctx, ISC_FALSE))
- empty_bucket(res);
- }
- query->magic = 0;
- isc_mem_put(query->mctx, query, sizeof(*query));
- *queryp = NULL;
- }
- static void
- fctx_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, rttms;
- unsigned int factor;
- dns_adbfind_t *find;
- dns_adbaddrinfo_t *addrinfo;
- isc_socket_t *socket;
- 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);
- factor = DNS_ADB_RTTADJDEFAULT;
- rttms = rtt / 1000;
- if (rttms < DNS_RESOLVER_QRYRTTCLASS0) {
- inc_stats(fctx->res,
- dns_resstatscounter_queryrtt0);
- } else if (rttms < DNS_RESOLVER_QRYRTTCLASS1) {
- inc_stats(fctx->res,
- dns_resstatscounter_queryrtt1);
- } else if (rttms < DNS_RESOLVER_QRYRTTCLASS2) {
- inc_stats(fctx->res,
- dns_resstatscounter_queryrtt2);
- } else if (rttms < DNS_RESOLVER_QRYRTTCLASS3) {
- inc_stats(fctx->res,
- dns_resstatscounter_queryrtt3);
- } else if (rttms < DNS_RESOLVER_QRYRTTCLASS4) {
- inc_stats(fctx->res,
- dns_resstatscounter_queryrtt4);
- } else {
- inc_stats(fctx->res,
- dns_resstatscounter_queryrtt5);
- }
- } else {
- /*
- * We don't have an RTT for this query. Maybe the
- * packet was lost, or maybe this server is very
- * slow. We don't know. Increase the RTT.
- */
- INSIST(no_response);
- rtt = query->addrinfo->srtt + 200000;
- if (rtt > 10000000)
- rtt = 10000000;
- /*
- * Replace the current RTT with our value.
- */
- factor = DNS_ADB_RTTADJREPLACE;
- }
- dns_adb_adjustsrtt(fctx->adb, query->addrinfo, rtt, factor);
- }
- /* Remember that the server has been tried. */
- if (!TRIED(query->addrinfo)) {
- dns_adb_changeflags(fctx->adb, query->addrinfo,
- FCTX_ADDRINFO_TRIED, FCTX_ADDRINFO_TRIED);
- }
- /*
- * Age RTTs of servers not tried.
- */
- factor = DNS_ADB_RTTADJAGE;
- if (finish != NULL)
- for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
- addrinfo != NULL;
- addrinfo = ISC_LIST_NEXT(addrinfo, publink))
- if (UNMARKED(addrinfo))
- dns_adb_adjustsrtt(fctx->adb, addrinfo,
- 0, factor);
- if (finish != NULL && TRIEDFIND(fctx))
- for (find = ISC_LIST_HEAD(fctx->finds);
- find != NULL;
- find = ISC_LIST_NEXT(find, publink))
- for (addrinfo = ISC_LIST_HEAD(find->list);
- addrinfo != NULL;
- addrinfo = ISC_LIST_NEXT(addrinfo, publink))
- if (UNMARKED(addrinfo))
- dns_adb_adjustsrtt(fctx->adb, addrinfo,
- 0, factor);
- if (finish != NULL && TRIEDALT(fctx)) {
- for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
- addrinfo != NULL;
- addrinfo = ISC_LIST_NEXT(addrinfo, publink))
- if (UNMARKED(addrinfo))
- dns_adb_adjustsrtt(fctx->adb, addrinfo,
- 0, factor);
- for (find = ISC_LIST_HEAD(fctx->altfinds);
- find != NULL;
- find = ISC_LIST_NEXT(find, publink))
- for (addrinfo = ISC_LIST_HEAD(find->list);
- addrinfo != NULL;
- addrinfo = ISC_LIST_NEXT(addrinfo, publink))
- if (UNMARKED(addrinfo))
- dns_adb_adjustsrtt(fctx->adb, addrinfo,
- 0, factor);
- }
- /*
- * Check for any outstanding socket events. If they exist, cancel
- * them and let the event handlers finish the cleanup. The resolver
- * only needs to worry about managing the connect and send events;
- * the dispatcher manages the recv events.
- */
- if (RESQUERY_CONNECTING(query)) {
- /*
- * Cancel the connect.
- */
- if (query->tcpsocket != NULL) {
- isc_socket_cancel(query->tcpsocket, NULL,
- ISC_SOCKCANCEL_CONNECT);
- } else if (query->dispentry != NULL) {
- INSIST(query->exclusivesocket);
- socket = dns_dispatch_getentrysocket(query->dispentry);
- if (socket != NULL)
- isc_socket_cancel(socket, NULL,
- ISC_SOCKCANCEL_CONNECT);
- }
- } else if (RESQUERY_SENDING(query)) {
- /*
- * Cancel the pending send.
- */
- if (query->exclusivesocket && query->dispentry != NULL)
- socket = dns_dispatch_getentrysocket(query->dispentry);
- else
- socket = dns_dispatch_getsocket(query->dispatch);
- if (socket != NULL)
- isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_SEND);
- }
- if (query->dispentry != NULL)
- dns_dispatch_removeresponse(&query->dispentry, deventp);
- ISC_LIST_UNLINK(fctx->queries, query, link);
- if (query->tsig != NULL)
- isc_buffer_free(&query->tsig);
- if (query->tsigkey != NULL)
- dns_tsigkey_detach(&query->tsigkey);
- if (query->dispatch != NULL)
- dns_dispatch_detach(&query->dispatch);
- if (! (RESQUERY_CONNECTING(query) || RESQUERY_SENDING(query)))
- /*
- * It's safe to destroy the query now.
- */
- resquery_destroy(&query);
- }
- static void
- fctx_cancelqueries(fetchctx_t *fctx, isc_boolean_t no_response) {
- resquery_t *query, *next_query;
- FCTXTRACE("cancelqueries");
- for (query = ISC_LIST_HEAD(fctx->queries);
- query != NULL;
- query = next_query) {
- next_query = ISC_LIST_NEXT(query, link);
- fctx_cancelquery(&query, NULL, NULL, no_response);
- }
- }
- static void
- fctx_cleanupfinds(fetchctx_t *fctx) {
- dns_adbfind_t *find, *next_find;
- REQUIRE(ISC_LIST_EMPTY(fctx->queries));
- for (find = ISC_LIST_HEAD(fctx->finds);
- find != NULL;
- find = next_find) {
- next_find = ISC_LIST_NEXT(find, publink);
- ISC_LIST_UNLINK(fctx->finds, find, publink);
- dns_adb_destroyfind(&find);
- }
- fctx->find = NULL;
- }
- static void
- fctx_cleanupaltfinds(fetchctx_t *fctx) {
- dns_adbfind_t *find, *next_find;
- REQUIRE(ISC_LIST_EMPTY(fctx->queries));
- for (find = ISC_LIST_HEAD(fctx->altfinds);
- find != NULL;
- find = next_find) {
- next_find = ISC_LIST_NEXT(find, publink);
- ISC_LIST_UNLINK(fctx->altfinds, find, publink);
- dns_adb_destroyfind(&find);
- }
- fctx->altfind = NULL;
- }
- static void
- fctx_cleanupforwaddrs(fetchctx_t *fctx) {
- dns_adbaddrinfo_t *addr, *next_addr;
- REQUIRE(ISC_LIST_EMPTY(fctx->queries));
- for (addr = ISC_LIST_HEAD(fctx->forwaddrs);
- addr != NULL;
- addr = next_addr) {
- next_addr = ISC_LIST_NEXT(addr, publink);
- ISC_LIST_UNLINK(fctx->forwaddrs, addr, publink);
- dns_adb_freeaddrinfo(fctx->adb, &addr);
- }
- }
- static void
- fctx_cleanupaltaddrs(fetchctx_t *fctx) {
- dns_adbaddrinfo_t *addr, *next_addr;
- REQUIRE(ISC_LIST_EMPTY(fctx->queries));
- for (addr = ISC_LIST_HEAD(fctx->altaddrs);
- addr != NULL;
- addr = next_addr) {
- next_addr = ISC_LIST_NEXT(addr, publink);
- ISC_LIST_UNLINK(fctx->altaddrs, addr, publink);
- dns_adb_freeaddrinfo(fctx->adb, &addr);
- }
- }
- static inline void
- fctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) {
- FCTXTRACE("stopeverything");
- fctx_cancelqueries(fctx, no_response);
- fctx_cleanupfinds(fctx);
- fctx_cleanupaltfinds(fctx);
- fctx_cleanupforwaddrs(fctx);
- fctx_cleanupaltaddrs(fctx);
- fctx_stoptimer(fctx);
- }
- static inline void
- fctx_sendevents(fetchctx_t *fctx, isc_result_t result, int line) {
- dns_fetchevent_t *event, *next_event;
- isc_task_t *task;
- unsigned int count = 0;
- isc_interval_t i;
- isc_boolean_t logit = ISC_FALSE;
- isc_time_t now;
- unsigned int old_spillat;
- unsigned int new_spillat = 0; /* initialized to silence
- compiler warnings */
- /*
- * Caller must be holding the appropriate bucket lock.
- */
- REQUIRE(fctx->state == fetchstate_done);
- FCTXTRACE("sendevents");
- /*
- * Keep some record of fetch result for logging later (if required).
- */
- fctx->result = result;
- fctx->exitline = line;
- TIME_NOW(&now);
- fctx->duration = isc_time_microdiff(&now, &fctx->start);
- for (event = ISC_LIST_HEAD(fctx->events);
- event != NULL;
- event = next_event) {
- next_event = ISC_LIST_NEXT(event, ev_link);
- ISC_LIST_UNLINK(fctx->events, event, ev_link);
- task = event->ev_sender;
- event->ev_sender = fctx;
- event->vresult = fctx->vresult;
- if (!HAVE_ANSWER(fctx))
- event->result = result;
- INSIST(result != ISC_R_SUCCESS ||
- dns_rdataset_isassociated(event->rdataset) ||
- fctx->type == dns_rdatatype_any ||
- fctx->type == dns_rdatatype_rrsig ||
- fctx->type == dns_rdatatype_sig);
- /*
- * Negative results must be indicated in event->result.
- */
- if (dns_rdataset_isassociated(event->rdataset) &&
- NEGATIVE(event->rdataset)) {
- INSIST(event->result == DNS_R_NCACHENXDOMAIN ||
- event->result == DNS_R_NCACHENXRRSET);
- }
- isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event));
- count++;
- }
- if ((fctx->attributes & FCTX_ATTR_HAVEANSWER) != 0 &&
- fctx->spilled &&
- (count < fctx->res->spillatmax || fctx->res->spillatmax == 0)) {
- LOCK(&fctx->res->lock);
- if (count == fctx->res->spillat && !fctx->res->exiting) {
- old_spillat = fctx->res->spillat;
- fctx->res->spillat += 5;
- if (fctx->res->spillat > fctx->res->spillatmax &&
- fctx->res->spillatmax != 0)
- fctx->res->spillat = fctx->res->spillatmax;
- new_spillat = fctx->res->spillat;
- if (new_spillat != old_spillat) {
- logit = ISC_TRUE;
- }
- isc_interval_set(&i, 20 * 60, 0);
- result = isc_timer_reset(fctx->res->spillattimer,
- isc_timertype_ticker, NULL,
- &i, ISC_TRUE);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
- }
- UNLOCK(&fctx->res->lock);
- if (logit)
- isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
- DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
- "clients-per-query increased to %u",
- new_spillat);
- }
- }
- static inline void
- log_edns(fetchctx_t *fctx) {
- char domainbuf[DNS_NAME_FORMATSIZE];
- if (fctx->reason == NULL)
- return;
- dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
- isc_log_write(dns_lctx, DNS_LOGCATEGORY_EDNS_DISABLED,
- DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
- "success resolving '%s' (in '%s'?) after %s",
- fctx->info, domainbuf, fctx->reason);
- fctx->reason = NULL;
- }
- static void
- fctx_done(fetchctx_t *fctx, isc_result_t result, int line) {
- dns_resolver_t *res;
- isc_boolean_t no_response;
- REQUIRE(line >= 0);
- FCTXTRACE("done");
- res = fctx->res;
- if (result == ISC_R_SUCCESS) {
- /*%
- * Log any deferred EDNS timeout messages.
- */
- log_edns(fctx);
- no_response = ISC_TRUE;
- } else
- no_response = ISC_FALSE;
- fctx->reason = NULL;
- fctx_stopeverything(fctx, no_response);
- LOCK(&res->buckets[fctx->bucketnum].lock);
- fctx->state = fetchstate_done;
- fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
- fctx_sendevents(fctx, result, line);
- UNLOCK(&res->buckets[fctx->bucketnum].lock);
- }
- static void
- process_sendevent(resquery_t *query, isc_event_t *event) {
- isc_socketevent_t *sevent = (isc_socketevent_t *)event;
- isc_boolean_t retry = ISC_FALSE;
- isc_result_t result;
- fetchctx_t *fctx;
- fctx = query->fctx;
- if (RESQUERY_CANCELED(query)) {
- if (query->sends == 0 && query->connects == 0) {
- /*
- * This query was canceled while the
- * isc_socket_sendto/connect() was in progress.
- */
- if (query->tcpsocket != NULL)
- isc_socket_detach(&query->tcpsocket);
- resquery_destroy(&query);
- }
- } else {
- switch (sevent->result) {
- case ISC_R_SUCCESS:
- break;
- case ISC_R_HOSTUNREACH:
- case ISC_R_NETUNREACH:
- case ISC_R_NOPERM:
- case ISC_R_ADDRNOTAVAIL:
- case ISC_R_CONNREFUSED:
- /*
- * No route to remote.
- */
- add_bad(fctx, query->addrinfo, sevent->result,
- badns_unreachable);
- fctx_cancelquery(&query, NULL, NULL, ISC_TRUE);
- retry = ISC_TRUE;
- break;
- default:
- fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
- break;
- }
- }
- isc_event_free(&event);
- if (retry) {
- /*
- * Behave as if the idle timer has expired. For TCP
- * this may not actually reflect the latest timer.
- */
- fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
- result = fctx_stopidletimer(fctx);
- if (result != ISC_R_SUCCESS)
- fctx_done(fctx, result, __LINE__);
- else
- fctx_try(fctx, ISC_TRUE, ISC_FALSE);
- }
- }
- static void
- resquery_udpconnected(isc_task_t *task, isc_event_t *event) {
- resquery_t *query = event->ev_arg;
- REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT);
- QTRACE("udpconnected");
- UNUSED(task);
- INSIST(RESQUERY_CONNECTING(query));
- query->connects--;
- process_sendevent(query, event);
- }
- static void
- resquery_senddone(isc_task_t *task, isc_event_t *event) {
- resquery_t *query = event->ev_arg;
- REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE);
- QTRACE("senddone");
- /*
- * XXXRTH
- *
- * Currently we don't wait for the senddone event before retrying
- * a query. This means that if we get really behind, we may end
- * up doing extra work!
- */
- UNUSED(task);
- INSIST(RESQUERY_SENDING(query));
- query->sends--;
- process_sendevent(query, event);
- }
- static inline isc_result_t
- fctx_addopt(dns_message_t *message, unsigned int version,
- isc_uint16_t udpsize, isc_boolean_t request_nsid)
- {
- dns_rdataset_t *rdataset;
- dns_rdatalist_t *rdatalist;
- dns_rdata_t *rdata;
- isc_result_t result;
- rdatalist = NULL;
- result = dns_message_gettemprdatalist(message, &rdatalist);
- if (result != ISC_R_SUCCESS)
- return (result);
- rdata = NULL;
- result = dns_message_gettemprdata(message, &rdata);
- if (result != ISC_R_SUCCESS)
- return (result);
- rdataset = NULL;
- result = dns_message_gettemprdataset(message, &rdataset);
- if (result != ISC_R_SUCCESS)
- return (result);
- dns_rdataset_init(rdataset);
- rdatalist->type = dns_rdatatype_opt;
- rdatalist->covers = 0;
- /*
- * Set Maximum UDP buffer size.
- */
- rdatalist->rdclass = udpsize;
- /*
- * Set EXTENDED-RCODE and Z to 0, DO to 1.
- */
- rdatalist->ttl = (version << 16);
- rdatalist->ttl |= DNS_MESSAGEEXTFLAG_DO;
- /*
- * Set EDNS options if applicable
- */
- if (request_nsid) {
- /* Send empty NSID option (RFC5001) */
- unsigned char data[4];
- isc_buffer_t buf;
- isc_buffer_init(&buf, data, sizeof(data));
- isc_buffer_putuint16(&buf, DNS_OPT_NSID);
- isc_buffer_putuint16(&buf, 0);
- rdata->data = data;
- rdata->length = sizeof(data);
- } else {
- rdata->data = NULL;
- rdata->length = 0;
- }
- rdata->rdclass = rdatalist->rdclass;
- rdata->type = rdatalist->type;
- rdata->flags = 0;
- ISC_LIST_INIT(rdatalist->rdata);
- ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
- RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == ISC_R_SUCCESS);
- return (dns_message_setopt(message, rdataset));
- }
- static inline void
- fctx_setretryinterval(fetchctx_t *fctx, unsigned int rtt) {
- unsigned int seconds;
- unsigned int us;
- /*
- * We retry every .8 seconds the first two times through the address
- * list, and then we do exponential back-off.
- */
- if (fctx->restarts < 3)
- us = 800000;
- else
- us = (800000 << (fctx->restarts - 2));
- /*
- * Double the round-trip time.
- */
- rtt *= 2;
- /*
- * Always wait for at least the doubled round-trip time.
- */
- if (us < rtt)
- us = rtt;
- /*
- * But don't ever wait for more than 10 seconds.
- */
- if (us > 10000000)
- us = 10000000;
- seconds = us / 1000000;
- us -= seconds * 1000000;
- isc_interval_set(&fctx->interval, seconds, us * 1000);
- }
- static isc_result_t
- fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
- unsigned int options)
- {
- dns_resolver_t *res;
- isc_task_t *task;
- isc_result_t result;
- resquery_t *query;
- isc_sockaddr_t addr;
- isc_boolean_t have_addr = ISC_FALSE;
- unsigned int srtt;
- FCTXTRACE("query");
- res = fctx->res;
- task = res->buckets[fctx->bucketnum].task;
- srtt = addrinfo->srtt;
- if (ISFORWARDER(addrinfo) && srtt < 1000000)
- srtt = 1000000;
- fctx_setretryinterval(fctx, srtt);
- result = fctx_startidletimer(fctx, &fctx->interval);
- if (result != ISC_R_SUCCESS)
- return (result);
- INSIST(ISC_LIST_EMPTY(fctx->validators));
- dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE);
- query = isc_mem_get(fctx->mctx, sizeof(*query));
- if (query == NULL) {
- result = ISC_R_NOMEMORY;
- goto stop_idle_timer;
- }
- query->mctx = fctx->mctx;
- query->options = options;
- query->attributes = 0;
- query->sends = 0;
- query->connects = 0;
- /*
- * Note that the caller MUST guarantee that 'addrinfo' will remain
- * valid until this query is canceled.
- */
- query->addrinfo = addrinfo;
- TIME_NOW(&query->start);
- /*
- * If this is a TCP query, then we need to make a socket and
- * a dispatch for it here. Otherwise we use the resolver's
- * shared dispatch.
- */
- query->dispatchmgr = res->dispatchmgr;
- query->dispatch = NULL;
- query->exclusivesocket = ISC_FALSE;
- query->tcpsocket = NULL;
- if (res->view->peers != NULL) {
- dns_peer_t *peer = NULL;
- isc_netaddr_t dstip;
- isc_netaddr_fromsockaddr(&dstip, &addrinfo->sockaddr);
- result = dns_peerlist_peerbyaddr(res->view->peers,
- &dstip, &peer);
- if (result == ISC_R_SUCCESS) {
- result = dns_peer_getquerysource(peer, &addr);
- if (result == ISC_R_SUCCESS)
- have_addr = ISC_TRUE;
- }
- }
- if ((query->options & DNS_FETCHOPT_TCP) != 0) {
- int pf;
- pf = isc_sockaddr_pf(&addrinfo->sockaddr);
- if (!have_addr) {
- switch (pf) {
- case PF_INET:
- result =
- dns_dispatch_getlocaladdress(res->dispatchv4,
- &addr);
- break;
- case PF_INET6:
- result =
- dns_dispatch_getlocaladdress(res->dispatchv6,
- &addr);
- break;
- default:
- result = ISC_R_NOTIMPLEMENTED;
- break;
- }
- if (result != ISC_R_SUCCESS)
- goto cleanup_query;
- }
- isc_sockaddr_setport(&addr, 0);
- result = isc_socket_create(res->socketmgr, pf,
- isc_sockettype_tcp,
- &query->tcpsocket);
- if (result != ISC_R_SUCCESS)
- goto cleanup_query;
- #ifndef BROKEN_TCP_BIND_BEFORE_CONNECT
- result = isc_socket_bind(query->tcpsocket, &addr, 0);
- if (result != ISC_R_SUCCESS)
- goto cleanup_socket;
- #endif
- /*
- * A dispatch will be created once the connect succeeds.
- */
- } else {
- if (have_addr) {
- unsigned int attrs, attrmask;
- attrs = DNS_DISPATCHATTR_UDP;
- switch (isc_sockaddr_pf(&addr)) {
- case AF_INET:
- attrs |= DNS_DISPATCHATTR_IPV4;
- break;
- case AF_INET6:
- attrs |= DNS_DISPATCHATTR_IPV6;
- break;
- default:
- result = ISC_R_NOTIMPLEMENTED;
- goto cleanup_query;
- }
- attrmask = DNS_DISPATCHATTR_UDP;
- attrmask |= DNS_DISPATCHATTR_TCP;
- attrmask |= DNS_DISPATCHATTR_IPV4;
- attrmask |= DNS_DISPATCHATTR_IPV6;
- result = dns_dispatch_getudp(res->dispatchmgr,
- res->socketmgr,
- res->taskmgr, &addr,
- 4096, 1000, 32768, 16411,
- 16433, attrs, attrmask,
- &query->dispatch);
- if (result != ISC_R_SUCCESS)
- goto cleanup_query;
- } else {
- switch (isc_sockaddr_pf(&addrinfo->sockaddr)) {
- case PF_INET:
- dns_dispatch_attach(res->dispatchv4,
- &query->dispatch);
- query->exclusivesocket = res->exclusivev4;
- break;
- case PF_INET6:
- dns_dispatch_attach(res->dispatchv6,
- &query->dispatch);
- query->exclusivesocket = res->exclusivev6;
- break;
- default:
- result = ISC_R_NOTIMPLEMENTED;
- goto cleanup_query;
- }
- }
- /*
- * We should always have a valid dispatcher here. If we
- * don't support a protocol family, then its dispatcher
- * will be NULL, but we shouldn't be finding addresses for
- * protocol types we don't support, so the dispatcher
- * we found should never be NULL.
- */
- INSIST(query->dispatch != NULL);
- }
- query->dispentry = NULL;
- query->fctx = fctx;
- query->tsig = NULL;
- query->tsigkey = NULL;
- ISC_LINK_INIT(query, link);
- query->magic = QUERY_MAGIC;
- if ((query->options & DNS_FETCHOPT_TCP) != 0) {
- /*
- * Connect to the remote server.
- *
- * XXXRTH Should we attach to the socket?
- */
- result = isc_socket_connect(query->tcpsocket,
- &addrinfo->sockaddr, task,
- resquery_connected, query);
- if (result != ISC_R_SUCCESS)
- goto cleanup_socket;
- query->connects++;
- QTRACE("connecting via TCP");
- } else {
- result = resquery_send(query);
- if (result != ISC_R_SUCCESS)
- goto cleanup_dispatch;
- }
- fctx->querysent++;
- ISC_LIST_APPEND(fctx->queries, query, link);
- query->fctx->nqueries++;
- if (isc_sockaddr_pf(&addrinfo->sockaddr) == PF_INET)
- inc_stats(res, dns_resstatscounter_queryv4);
- else
- inc_stats(res, dns_resstatscounter_queryv6);
- if (res->view->resquerystats != NULL)
- dns_rdatatypestats_increment(res->view->resquerystats,
- fctx->type);
- return (ISC_R_SUCCESS);
- cleanup_socket:
- isc_socket_detach(&query->tcpsocket);
- cleanup_dispatch:
- if (query->dispatch != NULL)
- dns_dispatch_detach(&query->dispatch);
- cleanup_query:
- if (query->connects == 0) {
- query->magic = 0;
- isc_mem_put(fctx->mctx, query, sizeof(*query));
- }
- stop_idle_timer:
- RUNTIME_CHECK(fctx_stopidletimer(fctx) == ISC_R_SUCCESS);
- return (result);
- }
- static isc_boolean_t
- bad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) {
- isc_sockaddr_t *sa;
- for (sa = ISC_LIST_HEAD(fctx->bad_edns);
- sa != NULL;
- sa = ISC_LIST_NEXT(sa, link)) {
- if (isc_sockaddr_equal(sa, address))
- return (ISC_TRUE);
- }
- return (ISC_FALSE);
- }
- static void
- add_bad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) {
- isc_sockaddr_t *sa;
- if (bad_edns(fctx, address))
- return;
- sa = isc_mem_get(fctx->mctx, sizeof(*sa));
- if (sa == NULL)
- return;
- *sa = *address;
- ISC_LIST_INITANDAPPEND(fctx->bad_edns, sa, link);
- }
- static isc_boolean_t
- triededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
- isc_sockaddr_t *sa;
- for (sa = ISC_LIST_HEAD(fctx->edns);
- sa != NULL;
- sa = ISC_LIST_NEXT(sa, link)) {
- if (isc_sockaddr_equal(sa, address))
- return (ISC_TRUE);
- }
- return (ISC_FALSE);
- }
- static void
- add_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
- isc_sockaddr_t *sa;
- if (triededns(fctx, address))
- return;
- sa = isc_mem_get(fctx->mctx, sizeof(*sa));
- if (sa == NULL)
- return;
- *sa = *address;
- ISC_LIST_INITANDAPPEND(fctx->edns, sa, link);
- }
- static isc_boolean_t
- triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
- isc_sockaddr_t *sa;
- for (sa = ISC_LIST_HEAD(fctx->edns512);
- sa != NULL;
- sa = ISC_LIST_NEXT(sa, link)) {
- if (isc_sockaddr_equal(sa, address))
- return (ISC_TRUE);
- }
- return (ISC_FALSE);
- }
- static void
- add_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
- isc_sockaddr_t *sa;
- if (triededns512(fctx, address))
- return;
- sa = isc_mem_get(fctx->mctx, sizeof(*sa));
- if (sa == NULL)
- return;
- *sa = *address;
- ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link);
- }
- static isc_result_t
- resquery_send(resquery_t *query) {
- fetchctx_t *fctx;
- isc_result_t result;
- dns_name_t *qname = NULL;
- dns_rdataset_t *qrdataset = NULL;
- isc_region_t r;
- dns_resolver_t *res;
- isc_task_t *task;
- isc_socket_t *socket;
- isc_buffer_t tcpbuffer;
- isc_sockaddr_t *address;
- isc_buffer_t *buffer;
- isc_netaddr_t ipaddr;
- dns_tsigkey_t *tsigkey = NULL;
- dns_peer_t *peer = NULL;
- isc_boolean_t useedns;
- dns_compress_t cctx;
- isc_boolean_t cleanup_cctx = ISC_FALSE;
- isc_boolean_t secure_domain;
- isc_boolean_t connecting = ISC_FALSE;
- fctx = query->fctx;
- QTRACE("send");
- res = fctx->res;
- task = res->buckets[fctx->bucketnum].task;
- address = NULL;
- if ((query->options & DNS_FETCHOPT_TCP) != 0) {
- /*
- * Reserve space for the TCP message length.
- */
- isc_buffer_init(&tcpbuffer, query->data, sizeof(query->data));
- isc_buffer_init(&query->buffer, query->data + 2,
- sizeof(query->data) - 2);
- buffer = &tcpbuffer;
- } else {
- isc_buffer_init(&query->buffer, query->data,
- sizeof(query->data));
- buffer = &query->buffer;
- }
- result = dns_message_gettempname(fctx->qmessage, &qname);
- if (result != ISC_R_SUCCESS)
- goto cleanup_temps;
- result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset);
- if (result != ISC_R_SUCCESS)
- goto cleanup_temps;
- /*
- * Get a query id from the dispatch.
- */
- result = dns_dispatch_addresponse2(query->dispatch,
- &query->addrinfo->sockaddr,
- task,
- resquery_response,
- query,
- &query->id,
- &query->dispentry,
- res->socketmgr);
- if (result != ISC_R_SUCCESS)
- goto cleanup_temps;
- fctx->qmessage->opcode = dns_opcode_query;
- /*
- * Set up question.
- */
- dns_name_init(qname, NULL);
- dns_name_clone(&fctx->name, qname);
- dns_rdataset_init(qrdataset);
- dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type);
- ISC_LIST_APPEND(qname->list, qrdataset, link);
- dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION);
- qname = NULL;
- qrdataset = NULL;
- /*
- * Set RD if the client has requested that we do a recursive query,
- * or if we're sending to a forwarder.
- */
- if ((query->options & DNS_FETCHOPT_RECURSIVE) != 0 ||
- ISFORWARDER(query->addrinfo))
- fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD;
- /*
- * Set CD if the client says don't validate or the question is
- * under a secure entry point.
- */
- if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) {
- fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
- } else if (res->view->enablevalidation) {
- result = dns_view_issecuredomain(res->view, &fctx->name,
- &secure_domain);
- if (result != ISC_R_SUCCESS)
- secure_domain = ISC_FALSE;
- if (res->view->dlv != NULL)
- secure_domain = ISC_TRUE;
- if (secure_domain)
- fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
- }
- /*
- * We don't have to set opcode because it defaults to query.
- */
- fctx->qmessage->id = query->id;
- /*
- * Convert the question to wire format.
- */
- result = dns_compress_init(&cctx, -1, fctx->res->mctx);
- if (result != ISC_R_SUCCESS)
- goto cleanup_message;
- cleanup_cctx = ISC_TRUE;
- result = dns_message_renderbegin(fctx->qmessage, &cctx,
- &query->buffer);
- if (result != ISC_R_SUCCESS)
- goto cleanup_message;
- result = dns_message_rendersection(fctx->qmessage,
- DNS_SECTION_QUESTION, 0);
- if (result != ISC_R_SUCCESS)
- goto cleanup_message;
- peer = NULL;
- isc_netaddr_fromsockaddr(&ipaddr, &query->addrinfo->sockaddr);
- (void) dns_peerlist_peerbyaddr(fctx->res->view->peers, &ipaddr, &peer);
- /*
- * The ADB does not know about servers with "edns no". Check this,
- * and then inform the ADB for future use.
- */
- if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0 &&
- peer != NULL &&
- dns_peer_getsupportedns(peer, &useedns) == ISC_R_SUCCESS &&
- !useedns)
- {
- query->options |= DNS_FETCHOPT_NOEDNS0;
- dns_adb_changeflags(fctx->adb, query->addrinfo,
- DNS_FETCHOPT_NOEDNS0,
- DNS_FETCHOPT_NOEDNS0);
- }
- /* Sync NOEDNS0 flag in addrinfo->flags and options now. */
- if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) != 0)
- query->options |= DNS_FETCHOPT_NOEDNS0;
- /*
- * Handle timeouts by reducing the UDP response size to 512 bytes
- * then if that doesn't work disabling EDNS (includes DO) and CD.
- *
- * These timeout can be due to:
- * * broken nameservers that don't respond to EDNS queries.
- * * broken/misconfigured firewalls and NAT implementations
- * that don't handle IP fragmentation.
- * * broken/misconfigured firewalls that don't handle responses
- * greater than 512 bytes.
- * * broken/misconfigured firewalls that don't handle EDNS, DO
- * or CD.
- * * packet loss / link outage.
- */
- if (fctx->timeout) {
- if ((triededns512(fctx, &query->addrinfo->sockaddr) ||
- fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) &&
- (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
- query->options |= DNS_FETCHOPT_NOEDNS0;
- fctx->reason = "disabling EDNS";
- } else if ((triededns(fctx, &query->addrinfo->sockaddr) ||
- fctx->timeouts >= MAX_EDNS0_TIMEOUTS)…