diff options
Diffstat (limited to 'lib/dns/resolver.c')
-rw-r--r-- | lib/dns/resolver.c | 222 |
1 files changed, 151 insertions, 71 deletions
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index a56fecfd3ce1c..16d22e08b332e 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -1,8 +1,8 @@ /* - * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * - * Permission to use, copy, modify, and distribute this software for any + * 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. * @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.c,v 1.218.2.18.4.64.4.2 2007/01/11 05:05:10 marka Exp $ */ +/* $Id: resolver.c,v 1.218.2.18.4.77 2008/01/17 23:45:27 tbox Exp $ */ #include <config.h> @@ -187,6 +187,7 @@ struct fetchctx { isc_sockaddrlist_t forwarders; dns_fwdpolicy_t fwdpolicy; isc_sockaddrlist_t bad; + dns_validator_t *validator; ISC_LIST(dns_validator_t) validators; dns_db_t * cache; dns_adb_t * adb; @@ -207,7 +208,7 @@ struct fetchctx { unsigned int restarts; /* - * The number of timeouts that have occurred since we + * The number of timeouts that have occurred since we * last successfully received a response packet. This * is used for EDNS0 black hole detection. */ @@ -215,7 +216,7 @@ struct fetchctx { /* * Look aside state for DS lookups. */ - dns_name_t nsname; + dns_name_t nsname; dns_fetch_t * nsfetch; dns_rdataset_t nsrrset; @@ -245,7 +246,7 @@ struct fetchctx { #define ADDRWAIT(f) (((f)->attributes & FCTX_ATTR_ADDRWAIT) != \ 0) #define SHUTTINGDOWN(f) (((f)->attributes & FCTX_ATTR_SHUTTINGDOWN) \ - != 0) + != 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) @@ -289,8 +290,8 @@ struct dns_resolver { unsigned int magic; isc_mem_t * mctx; isc_mutex_t lock; - isc_mutex_t nlock; - isc_mutex_t primelock; + isc_mutex_t nlock; + isc_mutex_t primelock; dns_rdataclass_t rdclass; isc_socketmgr_t * socketmgr; isc_timermgr_t * timermgr; @@ -342,6 +343,8 @@ struct dns_resolver { #define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) +#define dns_db_transfernode(a,b,c) do { (*c) = (*b); (*b) = NULL; } while (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); @@ -355,7 +358,7 @@ static isc_result_t ncache_adderesult(dns_message_t *message, isc_stdtime_t now, dns_ttl_t maxttl, dns_rdataset_t *ardataset, isc_result_t *eresultp); -static void validated(isc_task_t *task, isc_event_t *event); +static void validated(isc_task_t *task, isc_event_t *event); static void maybe_destroy(fetchctx_t *fctx); static isc_result_t @@ -382,9 +385,13 @@ valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name, sigrdataset, fctx->rmessage, valoptions, task, validated, valarg, &validator); - if (result == ISC_R_SUCCESS) + if (result == ISC_R_SUCCESS) { + if ((valoptions & DNS_VALIDATOR_DEFER) == 0) { + INSIST(fctx->validator == NULL); + fctx->validator = validator; + } ISC_LIST_APPEND(fctx->validators, validator, link); - else + } else isc_mem_put(fctx->res->mctx, valarg, sizeof(*valarg)); return (result); } @@ -779,6 +786,15 @@ fctx_sendevents(fetchctx_t *fctx, isc_result_t result) { fctx->type == dns_rdatatype_rrsig || fctx->type == dns_rdatatype_sig); + /* + * Negative results must be indicated in event->result. + */ + if (dns_rdataset_isassociated(event->rdataset) && + event->rdataset->type == dns_rdatatype_none) { + INSIST(event->result == DNS_R_NCACHENXDOMAIN || + event->result == DNS_R_NCACHENXRRSET); + } + isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event)); } } @@ -844,7 +860,7 @@ resquery_senddone(isc_task_t *task, isc_event_t *event) { isc_socket_detach(&query->tcpsocket); resquery_destroy(&query); } - } else + } else switch (sevent->result) { case ISC_R_SUCCESS: break; @@ -1501,7 +1517,7 @@ resquery_connected(isc_task_t *task, isc_event_t *event) { } isc_event_free(&event); - + if (retry) { /* * Behave as if the idle timer has expired. For TCP @@ -1661,7 +1677,7 @@ mark_bad(fetchctx_t *fctx) { } static void -add_bad(fetchctx_t *fctx, isc_sockaddr_t *address, isc_result_t reason) { +add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason) { char namebuf[DNS_NAME_FORMATSIZE]; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; char classbuf[64]; @@ -1670,6 +1686,7 @@ add_bad(fetchctx_t *fctx, isc_sockaddr_t *address, isc_result_t reason) { isc_buffer_t b; isc_sockaddr_t *sa; const char *sep1, *sep2; + isc_sockaddr_t *address = &addrinfo->sockaddr; if (bad_server(fctx, address)) { /* @@ -1689,6 +1706,11 @@ add_bad(fetchctx_t *fctx, isc_sockaddr_t *address, isc_result_t reason) { if (reason == DNS_R_LAME) /* already logged */ return; + if (reason == DNS_R_UNEXPECTEDRCODE && + fctx->rmessage->opcode == dns_rcode_servfail && + ISFORWARDER(addrinfo)) + return; + if (reason == DNS_R_UNEXPECTEDRCODE) { isc_buffer_init(&b, code, sizeof(code) - 1); dns_rcode_totext(fctx->rmessage->rcode, &b); @@ -2154,7 +2176,7 @@ possibly_mark(fetchctx_t *fctx, dns_adbaddrinfo_t *addr) isc_netaddr_fromsockaddr(&ipaddr, sa); blackhole = dns_dispatchmgr_getblackhole(res->dispatchmgr); (void) dns_peerlist_peerbyaddr(res->view->peers, &ipaddr, &peer); - + if (blackhole != NULL) { int match; @@ -2559,7 +2581,7 @@ fctx_doshutdown(isc_task_t *task, isc_event_t *event) { dns_validator_cancel(validator); validator = ISC_LIST_NEXT(validator, link); } - + if (fctx->nsfetch != NULL) dns_resolver_cancelfetch(fctx->nsfetch); @@ -2781,6 +2803,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, fctx->fwdpolicy = dns_fwdpolicy_none; ISC_LIST_INIT(fctx->bad); ISC_LIST_INIT(fctx->validators); + fctx->validator = NULL; fctx->find = NULL; fctx->altfind = NULL; fctx->pending = 0; @@ -2981,7 +3004,7 @@ is_lame(fetchctx_t *fctx) { if (rdataset->type != dns_rdatatype_ns) continue; namereln = dns_name_fullcompare(name, &fctx->domain, - &order, &labels); + &order, &labels); if (namereln == dns_namereln_equal && (message->flags & DNS_MESSAGEFLAG_AA) != 0) return (ISC_FALSE); @@ -2998,9 +3021,9 @@ is_lame(fetchctx_t *fctx) { static inline void log_lame(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo) { char namebuf[DNS_NAME_FORMATSIZE]; - char domainbuf[DNS_NAME_FORMATSIZE]; + char domainbuf[DNS_NAME_FORMATSIZE]; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; - + dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); isc_sockaddr_format(&addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); @@ -3110,7 +3133,7 @@ maybe_destroy(fetchctx_t *fctx) { unsigned int bucketnum; isc_boolean_t bucket_empty = ISC_FALSE; dns_resolver_t *res = fctx->res; - dns_validator_t *validator; + dns_validator_t *validator, *next_validator; REQUIRE(SHUTTINGDOWN(fctx)); @@ -3118,16 +3141,22 @@ maybe_destroy(fetchctx_t *fctx) { return; for (validator = ISC_LIST_HEAD(fctx->validators); - validator != NULL; - validator = ISC_LIST_HEAD(fctx->validators)) { - ISC_LIST_UNLINK(fctx->validators, validator, link); + validator != NULL; validator = next_validator) { + next_validator = ISC_LIST_NEXT(validator, link); dns_validator_cancel(validator); + /* + * If this is a active validator wait for the cancel + * to complete before calling dns_validator_destroy(). + */ + if (validator == fctx->validator) + continue; + ISC_LIST_UNLINK(fctx->validators, validator, link); dns_validator_destroy(&validator); } bucketnum = fctx->bucketnum; LOCK(&res->buckets[bucketnum].lock); - if (fctx->references == 0) + if (fctx->references == 0 && ISC_LIST_EMPTY(fctx->validators)) bucket_empty = fctx_destroy(fctx); UNLOCK(&res->buckets[bucketnum].lock); @@ -3174,6 +3203,7 @@ validated(isc_task_t *task, isc_event_t *event) { FCTXTRACE("received validation completion event"); ISC_LIST_UNLINK(fctx->validators, vevent->validator, link); + fctx->validator = NULL; /* * Destroy the validator early so that we can @@ -3255,12 +3285,14 @@ validated(isc_task_t *task, isc_event_t *event) { if (result == ISC_R_SUCCESS) dns_db_detachnode(fctx->cache, &node); result = vevent->result; - add_bad(fctx, &addrinfo->sockaddr, result); + add_bad(fctx, addrinfo, result); isc_event_free(&event); UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); - if (!ISC_LIST_EMPTY(fctx->validators)) - dns_validator_send(ISC_LIST_HEAD(fctx->validators)); - else if (sentresponse) + INSIST(fctx->validator == NULL); + fctx->validator = ISC_LIST_HEAD(fctx->validators); + if (fctx->validator != NULL) { + dns_validator_send(fctx->validator); + } else if (sentresponse) fctx_done(fctx, result); /* Locks bucket. */ else fctx_try(fctx); /* Locks bucket. */ @@ -3327,7 +3359,12 @@ validated(isc_task_t *task, isc_event_t *event) { if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) goto noanswer_response; - if (vevent->sigrdataset != NULL) { + if (ardataset != NULL && ardataset->type == 0) { + if (NXDOMAIN(ardataset)) + eresult = DNS_R_NCACHENXDOMAIN; + else + eresult = DNS_R_NCACHENXRRSET; + } else if (vevent->sigrdataset != NULL) { result = dns_db_addrdataset(fctx->cache, node, NULL, now, vevent->sigrdataset, 0, asigrdataset); @@ -3341,6 +3378,7 @@ validated(isc_task_t *task, isc_event_t *event) { * If we only deferred the destroy because we wanted to cache * the data, destroy now. */ + dns_db_detachnode(fctx->cache, &node); UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); if (SHUTTINGDOWN(fctx)) maybe_destroy(fctx); /* Locks bucket. */ @@ -3357,6 +3395,7 @@ validated(isc_task_t *task, isc_event_t *event) { * more rdatasets that still need to * be validated. */ + dns_db_detachnode(fctx->cache, &node); UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); dns_validator_send(ISC_LIST_HEAD(fctx->validators)); goto cleanup_event; @@ -3422,8 +3461,7 @@ validated(isc_task_t *task, isc_event_t *event) { dns_fixedname_name(&hevent->foundname), NULL) == ISC_R_SUCCESS); dns_db_attach(fctx->cache, &hevent->db); - hevent->node = node; - node = NULL; + dns_db_transfernode(fctx->cache, &node, &hevent->node); clone_results(fctx); } @@ -3436,12 +3474,14 @@ validated(isc_task_t *task, isc_event_t *event) { fctx_done(fctx, result); /* Locks bucket. */ cleanup_event: + INSIST(node == NULL); isc_event_free(&event); } static inline isc_result_t cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, - isc_stdtime_t now) { + isc_stdtime_t now) +{ dns_rdataset_t *rdataset, *sigrdataset; dns_rdataset_t *addedrdataset, *ardataset, *asigrdataset; dns_rdataset_t *valrdataset = NULL, *valsigrdataset = NULL; @@ -3547,14 +3587,16 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, sizeof(typebuf)); dns_rdataclass_format(rdataset->rdclass, classbuf, sizeof(classbuf)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE, - "check-names %s %s/%s/%s", + "check-names %s %s/%s/%s", fail ? "failure" : "warning", namebuf, typebuf, classbuf); if (fail) { - if (ANSWER(rdataset)) + if (ANSWER(rdataset)) { + dns_db_detachnode(fctx->cache, &node); return (DNS_R_BADNAME); + } continue; } } @@ -3619,8 +3661,29 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, result = dns_db_addrdataset(fctx->cache, node, NULL, now, rdataset, 0, addedrdataset); - if (result == DNS_R_UNCHANGED) + if (result == DNS_R_UNCHANGED) { result = ISC_R_SUCCESS; + if (!need_validation && + ardataset != NULL && + ardataset->type == 0) { + /* + * The answer in the cache is better + * than the answer we found, and is + * a negative cache entry, so we + * must set eresult appropriately. + */ + if (NXDOMAIN(ardataset)) + eresult = DNS_R_NCACHENXDOMAIN; + else + eresult = DNS_R_NCACHENXRRSET; + /* + * We have a negative response from + * the cache so don't attempt to + * add the RRSIG rrset. + */ + continue; + } + } if (result != ISC_R_SUCCESS) break; if (sigrdataset != NULL) { @@ -3737,12 +3800,10 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, * a negative cache entry, so we * must set eresult appropriately. */ - if (NXDOMAIN(ardataset)) - eresult = - DNS_R_NCACHENXDOMAIN; - else - eresult = - DNS_R_NCACHENXRRSET; + if (NXDOMAIN(ardataset)) + eresult = DNS_R_NCACHENXDOMAIN; + else + eresult = DNS_R_NCACHENXRRSET; } result = ISC_R_SUCCESS; } else if (result != ISC_R_SUCCESS) @@ -3753,15 +3814,22 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo, if (valrdataset != NULL) result = valcreate(fctx, addrinfo, name, fctx->type, valrdataset, valsigrdataset, valoptions, - task); + task); if (result == ISC_R_SUCCESS && have_answer) { fctx->attributes |= FCTX_ATTR_HAVEANSWER; if (event != NULL) { + /* + * Negative results must be indicated in event->result. + */ + if (dns_rdataset_isassociated(event->rdataset) && + event->rdataset->type == dns_rdatatype_none) { + INSIST(eresult == DNS_R_NCACHENXDOMAIN || + eresult == DNS_R_NCACHENXRRSET); + } event->result = eresult; dns_db_attach(fctx->cache, adbp); - *anodep = node; - node = NULL; + dns_db_transfernode(fctx->cache, &node, anodep); clone_results(fctx); } } @@ -3999,8 +4067,7 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, if (event != NULL) { event->result = eresult; dns_db_attach(fctx->cache, adbp); - *anodep = node; - node = NULL; + dns_db_transfernode(fctx->cache, &node, anodep); clone_results(fctx); } } @@ -4107,7 +4174,7 @@ chase_additional(fetchctx_t *fctx) { again: rescan = ISC_FALSE; - + for (result = dns_message_firstname(fctx->rmessage, section); result == ISC_R_SUCCESS; result = dns_message_nextname(fctx->rmessage, section)) { @@ -4187,7 +4254,7 @@ dname_target(dns_rdataset_t *rdataset, dns_name_t *qname, dns_name_t *oname, return (DNS_R_FORMERR); } dns_fixedname_init(&prefix); - dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL); + dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL); dns_fixedname_init(fixeddname); result = dns_name_concatenate(dns_fixedname_name(&prefix), &dname.dname, @@ -4211,7 +4278,7 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, dns_message_t *message; dns_name_t *name, *qname, *ns_name, *soa_name, *ds_name; dns_rdataset_t *rdataset, *ns_rdataset; - isc_boolean_t done, aa, negative_response; + isc_boolean_t aa, negative_response; dns_rdatatype_t type; dns_section_t section = bind8_ns_resp ? DNS_SECTION_ANSWER : DNS_SECTION_AUTHORITY; @@ -4270,13 +4337,12 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, /* * Process the authority section. */ - done = ISC_FALSE; ns_name = NULL; ns_rdataset = NULL; soa_name = NULL; ds_name = NULL; result = dns_message_firstname(message, section); - while (!done && result == ISC_R_SUCCESS) { + while (result == ISC_R_SUCCESS) { name = NULL; dns_message_currentname(message, section, &name); if (dns_name_issubdomain(name, &fctx->domain)) { @@ -4338,15 +4404,29 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, dns_trust_additional; } } - /* - * A negative response has a SOA record (Type 2) - * and a optional NS RRset (Type 1) or it has neither - * a SOA or a NS RRset (Type 3, handled above) or - * rcode is NXDOMAIN (handled above) in which case - * the NS RRset is allowed (Type 4). - */ - if (soa_name != NULL) - negative_response = ISC_TRUE; + } + result = dns_message_nextname(message, section); + if (result == ISC_R_NOMORE) + break; + else if (result != ISC_R_SUCCESS) + return (result); + } + + /* + * A negative response has a SOA record (Type 2) + * and a optional NS RRset (Type 1) or it has neither + * a SOA or a NS RRset (Type 3, handled above) or + * rcode is NXDOMAIN (handled above) in which case + * the NS RRset is allowed (Type 4). + */ + if (soa_name != NULL) + negative_response = ISC_TRUE; + + result = dns_message_firstname(message, section); + while (result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(message, section, &name); + if (dns_name_issubdomain(name, &fctx->domain)) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { @@ -5041,7 +5121,7 @@ checknamessection(dns_message_t *message, dns_section_t section) { dns_name_t *name; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_t *rdataset; - + for (result = dns_message_firstname(message, section); result == ISC_R_SUCCESS; result = dns_message_nextname(message, section)) @@ -5060,7 +5140,7 @@ checknamessection(dns_message_t *message, dns_section_t section) { ISC_FALSE) || !dns_rdata_checknames(&rdata, name, NULL)) { - rdataset->attributes |= + rdataset->attributes |= DNS_RDATASETATTR_CHECKNAMES; } dns_rdata_reset(&rdata); @@ -5599,7 +5679,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { * Add this server to the list of bad servers for * this fctx. */ - add_bad(fctx, &addrinfo->sockaddr, broken_server); + add_bad(fctx, addrinfo, broken_server); } if (get_nameservers) { @@ -5679,7 +5759,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { fctx_done(fctx, result); } else if (result == DNS_R_CHASEDSSERVERS) { unsigned int n; - add_bad(fctx, &addrinfo->sockaddr, result); + add_bad(fctx, addrinfo, result); fctx_cancelqueries(fctx, ISC_TRUE); fctx_cleanupfinds(fctx); fctx_cleanupforwaddrs(fctx); @@ -6534,7 +6614,7 @@ free_algorithm(void *node, void *arg) { isc_mem_put(mctx, algorithms, *algorithms); } - + void dns_resolver_reset_algorithms(dns_resolver_t *resolver) { @@ -6578,7 +6658,7 @@ dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name, mask = 1 << (alg%8); result = dns_rbt_addnode(resolver->algorithms, name, &node); - + if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) { algorithms = node->data; if (algorithms == NULL || len > *algorithms) { @@ -6594,7 +6674,7 @@ dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name, *new = len; node->data = new; if (algorithms != NULL) - isc_mem_put(resolver->mctx, algorithms, + isc_mem_put(resolver->mctx, algorithms, *algorithms); } else algorithms[len-1] |= mask; @@ -6655,12 +6735,12 @@ dns_resolver_resetmustbesecure(dns_resolver_t *resolver) { RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write); #endif } - + static isc_boolean_t yes = ISC_TRUE, no = ISC_FALSE; isc_result_t dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name, - isc_boolean_t value) + isc_boolean_t value) { isc_result_t result; @@ -6675,7 +6755,7 @@ dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name, if (result != ISC_R_SUCCESS) goto cleanup; } - result = dns_rbt_addname(resolver->mustbesecure, name, + result = dns_rbt_addname(resolver->mustbesecure, name, value ? &yes : &no); cleanup: #if USE_MBSLOCK |