diff options
Diffstat (limited to 'contrib/bind9/lib/dns/resolver.c')
-rw-r--r-- | contrib/bind9/lib/dns/resolver.c | 224 |
1 files changed, 167 insertions, 57 deletions
diff --git a/contrib/bind9/lib/dns/resolver.c b/contrib/bind9/lib/dns/resolver.c index 717c932..3084dd8 100644 --- a/contrib/bind9/lib/dns/resolver.c +++ b/contrib/bind9/lib/dns/resolver.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2011 Internet Systems Consortium, Inc. ("ISC") + * 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 @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.c,v 1.428.6.7 2011-06-08 23:02:43 each Exp $ */ +/* $Id$ */ /*! \file */ @@ -216,6 +216,8 @@ struct fetchctx { 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. @@ -453,7 +455,7 @@ static isc_result_t ncache_adderesult(dns_message_t *message, dns_rdataset_t *ardataset, isc_result_t *eresultp); static void validated(isc_task_t *task, isc_event_t *event); -static void maybe_destroy(fetchctx_t *fctx); +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); @@ -746,8 +748,11 @@ resquery_destroy(resquery_t **queryp) { INSIST(query->tcpsocket == NULL); query->fctx->nqueries--; - if (SHUTTINGDOWN(query->fctx)) - maybe_destroy(query->fctx); /* Locks bucket. */ + 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; @@ -1562,9 +1567,11 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_dispatch_detach(&query->dispatch); cleanup_query: - query->magic = 0; - isc_mem_put(res->buckets[fctx->bucketnum].mctx, - query, sizeof(*query)); + if (query->connects == 0) { + query->magic = 0; + isc_mem_put(res->buckets[fctx->bucketnum].mctx, + query, sizeof(*query)); + } stop_idle_timer: RUNTIME_CHECK(fctx_stopidletimer(fctx) == ISC_R_SUCCESS); @@ -1682,6 +1689,7 @@ resquery_send(resquery_t *query) { 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"); @@ -1972,6 +1980,7 @@ resquery_send(resquery_t *query) { query); if (result != ISC_R_SUCCESS) goto cleanup_message; + connecting = ISC_TRUE; query->connects++; } } @@ -1983,8 +1992,19 @@ resquery_send(resquery_t *query) { */ result = isc_socket_sendto(socket, &r, task, resquery_senddone, query, address, NULL); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { + if (connecting) { + /* + * This query is still connecting. + * Mark it as canceled so that it will just be + * cleaned up when the connected event is received. + * Keep fctx around until the event is processed. + */ + query->fctx->nqueries++; + query->attributes |= RESQUERY_ATTR_CANCELED; + } goto cleanup_message; + } query->sends++; @@ -2146,6 +2166,7 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) { isc_boolean_t want_try = ISC_FALSE; isc_boolean_t want_done = ISC_FALSE; isc_boolean_t bucket_empty = ISC_FALSE; + isc_boolean_t destroy = ISC_FALSE; unsigned int bucketnum; find = event->ev_sender; @@ -2157,6 +2178,9 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) { FCTXTRACE("finddone"); + bucketnum = fctx->bucketnum; + LOCK(&res->buckets[bucketnum].lock); + INSIST(fctx->pending > 0); fctx->pending--; @@ -2181,17 +2205,17 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) { } } else if (SHUTTINGDOWN(fctx) && fctx->pending == 0 && fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) { - bucketnum = fctx->bucketnum; - LOCK(&res->buckets[bucketnum].lock); /* * Note that we had to wait until we had the lock before * looking at fctx->references. */ if (fctx->references == 0) - bucket_empty = fctx_destroy(fctx); - UNLOCK(&res->buckets[bucketnum].lock); + destroy = ISC_TRUE; } + UNLOCK(&res->buckets[bucketnum].lock); + if (destroy) + bucket_empty = fctx_destroy(fctx); isc_event_free(&event); dns_adb_destroyfind(&find); @@ -3421,6 +3445,20 @@ fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_sockaddr_t *client, return (ISC_R_SUCCESS); } +static inline void +log_ns_ttl(fetchctx_t *fctx, const char *where) { + char namebuf[DNS_NAME_FORMATSIZE]; + char domainbuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); + dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(10), + "log_ns_ttl: fctx %p: %s: %s (in '%s'?): %u %u", + fctx, where, namebuf, domainbuf, + fctx->ns_ttl_ok, fctx->ns_ttl); +} + static isc_result_t fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, dns_name_t *domain, dns_rdataset_t *nameservers, @@ -3514,6 +3552,8 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, fctx->timeout = ISC_FALSE; fctx->addrinfo = NULL; fctx->client = NULL; + fctx->ns_ttl = 0; + fctx->ns_ttl_ok = ISC_FALSE; dns_name_init(&fctx->nsname, NULL); fctx->nsfetch = NULL; @@ -3563,6 +3603,8 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, dns_rdataset_disassociate(&fctx->nameservers); goto cleanup_name; } + fctx->ns_ttl = fctx->nameservers.ttl; + fctx->ns_ttl_ok = ISC_TRUE; } else { /* * We're in forward-only mode. Set the query domain. @@ -3580,8 +3622,12 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, if (result != ISC_R_SUCCESS) goto cleanup_name; dns_rdataset_clone(nameservers, &fctx->nameservers); + fctx->ns_ttl = fctx->nameservers.ttl; + fctx->ns_ttl_ok = ISC_TRUE; } + log_ns_ttl(fctx, "fctx_create"); + INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain)); fctx->qmessage = NULL; @@ -3874,14 +3920,16 @@ clone_results(fetchctx_t *fctx) { /* * Destroy '*fctx' if it is ready to be destroyed (i.e., if it has - * no references and is no longer waiting for any events). If this - * was the last fctx in the resolver, destroy the resolver. + * no references and is no longer waiting for any events). * * Requires: * '*fctx' is shutting down. + * + * Returns: + * true if the resolver is exiting and this is the last fctx in the bucket. */ -static void -maybe_destroy(fetchctx_t *fctx) { +static isc_boolean_t +maybe_destroy(fetchctx_t *fctx, isc_boolean_t locked) { unsigned int bucketnum; isc_boolean_t bucket_empty = ISC_FALSE; dns_resolver_t *res = fctx->res; @@ -3889,8 +3937,11 @@ maybe_destroy(fetchctx_t *fctx) { REQUIRE(SHUTTINGDOWN(fctx)); + bucketnum = fctx->bucketnum; + if (!locked) + LOCK(&res->buckets[bucketnum].lock); if (fctx->pending != 0 || fctx->nqueries != 0) - return; + goto unlock; for (validator = ISC_LIST_HEAD(fctx->validators); validator != NULL; validator = next_validator) { @@ -3898,14 +3949,12 @@ maybe_destroy(fetchctx_t *fctx) { dns_validator_cancel(validator); } - bucketnum = fctx->bucketnum; - LOCK(&res->buckets[bucketnum].lock); if (fctx->references == 0 && ISC_LIST_EMPTY(fctx->validators)) bucket_empty = fctx_destroy(fctx); - UNLOCK(&res->buckets[bucketnum].lock); - - if (bucket_empty) - empty_bucket(res); + unlock: + if (!locked) + UNLOCK(&res->buckets[bucketnum].lock); + return (bucket_empty); } /* @@ -3913,31 +3962,33 @@ maybe_destroy(fetchctx_t *fctx) { */ static void validated(isc_task_t *task, isc_event_t *event) { - isc_result_t result = ISC_R_SUCCESS; - isc_result_t eresult = ISC_R_SUCCESS; - isc_stdtime_t now; - fetchctx_t *fctx; - dns_validatorevent_t *vevent; - dns_fetchevent_t *hevent; - dns_rdataset_t *ardataset = NULL; - dns_rdataset_t *asigrdataset = NULL; + dns_adbaddrinfo_t *addrinfo; dns_dbnode_t *node = NULL; - isc_boolean_t negative; - isc_boolean_t chaining; - isc_boolean_t sentresponse; - isc_uint32_t ttl; dns_dbnode_t *nsnode = NULL; + dns_fetchevent_t *hevent; dns_name_t *name; + dns_rdataset_t *ardataset = NULL; + dns_rdataset_t *asigrdataset = NULL; dns_rdataset_t *rdataset; dns_rdataset_t *sigrdataset; + dns_resolver_t *res; dns_valarg_t *valarg; - dns_adbaddrinfo_t *addrinfo; + dns_validatorevent_t *vevent; + fetchctx_t *fctx; + isc_boolean_t chaining; + isc_boolean_t negative; + isc_boolean_t sentresponse; + isc_result_t eresult = ISC_R_SUCCESS; + isc_result_t result = ISC_R_SUCCESS; + isc_stdtime_t now; + isc_uint32_t ttl; UNUSED(task); /* for now */ REQUIRE(event->ev_type == DNS_EVENT_VALIDATORDONE); valarg = event->ev_arg; fctx = valarg->fctx; + res = fctx->res; addrinfo = valarg->addrinfo; REQUIRE(VALID_FCTX(fctx)); REQUIRE(!ISC_LIST_EMPTY(fctx->validators)); @@ -3947,6 +3998,8 @@ validated(isc_task_t *task, isc_event_t *event) { FCTXTRACE("received validation completion event"); + LOCK(&res->buckets[fctx->bucketnum].lock); + ISC_LIST_UNLINK(fctx->validators, vevent->validator, link); fctx->validator = NULL; @@ -3955,7 +4008,7 @@ validated(isc_task_t *task, isc_event_t *event) { * destroy the fctx if necessary. */ dns_validator_destroy(&vevent->validator); - isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx, + isc_mem_put(res->buckets[fctx->bucketnum].mctx, valarg, sizeof(*valarg)); negative = ISC_TF(vevent->rdataset == NULL); @@ -3968,12 +4021,15 @@ validated(isc_task_t *task, isc_event_t *event) { * so, destroy the fctx. */ if (SHUTTINGDOWN(fctx) && !sentresponse) { - maybe_destroy(fctx); /* Locks bucket. */ + isc_uint32_t bucketnum = fctx->bucketnum; + isc_boolean_t bucket_empty; + bucket_empty = maybe_destroy(fctx, ISC_TRUE); + UNLOCK(&res->buckets[bucketnum].lock); + if (bucket_empty) + empty_bucket(res); goto cleanup_event; } - LOCK(&fctx->res->buckets[fctx->bucketnum].lock); - isc_stdtime_get(&now); /* @@ -4019,7 +4075,7 @@ validated(isc_task_t *task, isc_event_t *event) { if (vevent->result != ISC_R_SUCCESS) { FCTXTRACE("validation failed"); - inc_stats(fctx->res, dns_resstatscounter_valfail); + inc_stats(res, dns_resstatscounter_valfail); fctx->valfail++; fctx->vresult = vevent->result; if (fctx->vresult != DNS_R_BROKENCHAIN) { @@ -4068,7 +4124,7 @@ validated(isc_task_t *task, isc_event_t *event) { result = fctx->vresult; add_bad(fctx, addrinfo, result, badns_validation); isc_event_free(&event); - UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); + UNLOCK(&res->buckets[fctx->bucketnum].lock); INSIST(fctx->validator == NULL); fctx->validator = ISC_LIST_HEAD(fctx->validators); if (fctx->validator != NULL) @@ -4087,8 +4143,7 @@ validated(isc_task_t *task, isc_event_t *event) { fctx->type == dns_rdatatype_dlv || fctx->type == dns_rdatatype_ds) && tresult == ISC_R_SUCCESS) - dns_resolver_addbadcache(fctx->res, - &fctx->name, + dns_resolver_addbadcache(res, &fctx->name, fctx->type, &expire); fctx_done(fctx, result, __LINE__); /* Locks bucket. */ } else @@ -4101,7 +4156,7 @@ validated(isc_task_t *task, isc_event_t *event) { dns_rdatatype_t covers; FCTXTRACE("nonexistence validation OK"); - inc_stats(fctx->res, dns_resstatscounter_valnegsuccess); + inc_stats(res, dns_resstatscounter_valnegsuccess); if (fctx->rmessage->rcode == dns_rcode_nxdomain) covers = dns_rdatatype_any; @@ -4118,10 +4173,9 @@ validated(isc_task_t *task, isc_event_t *event) { * to zero to facilitate locating the containing zone of * a arbitrary zone. */ - ttl = fctx->res->view->maxncachettl; + ttl = res->view->maxncachettl; if (fctx->type == dns_rdatatype_soa && - covers == dns_rdatatype_any && - fctx->res->zero_no_soa_ttl) + covers == dns_rdatatype_any && res->zero_no_soa_ttl) ttl = 0; result = ncache_adderesult(fctx->rmessage, fctx->cache, node, @@ -4131,7 +4185,7 @@ validated(isc_task_t *task, isc_event_t *event) { goto noanswer_response; goto answer_response; } else - inc_stats(fctx->res, dns_resstatscounter_valsuccess); + inc_stats(res, dns_resstatscounter_valsuccess); FCTXTRACE("validation OK"); @@ -4179,14 +4233,17 @@ validated(isc_task_t *task, isc_event_t *event) { } if (sentresponse) { + isc_boolean_t bucket_empty = ISC_FALSE; /* * 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. */ + bucket_empty = maybe_destroy(fctx, ISC_TRUE); + UNLOCK(&res->buckets[fctx->bucketnum].lock); + if (bucket_empty) + empty_bucket(res); goto cleanup_event; } @@ -4201,7 +4258,7 @@ validated(isc_task_t *task, isc_event_t *event) { * be validated. */ dns_db_detachnode(fctx->cache, &node); - UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); + UNLOCK(&res->buckets[fctx->bucketnum].lock); dns_validator_send(ISC_LIST_HEAD(fctx->validators)); goto cleanup_event; } @@ -4276,8 +4333,7 @@ validated(isc_task_t *task, isc_event_t *event) { if (node != NULL) dns_db_detachnode(fctx->cache, &node); - UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); - + UNLOCK(&res->buckets[fctx->bucketnum].lock); fctx_done(fctx, result, __LINE__); /* Locks bucket. */ cleanup_event: @@ -5248,6 +5304,26 @@ is_answertarget_allowed(dns_view_t *view, dns_name_t *name, return (ISC_TRUE); } +static void +trim_ns_ttl(fetchctx_t *fctx, dns_name_t *name, dns_rdataset_t *rdataset) { + char ns_namebuf[DNS_NAME_FORMATSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + char tbuf[DNS_RDATATYPE_FORMATSIZE]; + + if (fctx->ns_ttl_ok && rdataset->ttl > fctx->ns_ttl) { + dns_name_format(name, ns_namebuf, sizeof(ns_namebuf)); + dns_name_format(&fctx->name, namebuf, sizeof(namebuf)); + dns_rdatatype_format(fctx->type, tbuf, sizeof(tbuf)); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(10), + "fctx %p: trimming ttl of %s/NS for %s/%s: " + "%u -> %u", fctx, ns_namebuf, namebuf, tbuf, + rdataset->ttl, fctx->ns_ttl); + rdataset->ttl = fctx->ns_ttl; + } +} + /* * Handle a no-answer response (NXDOMAIN, NXRRSET, or referral). * If look_in_options has LOOK_FOR_NS_IN_ANSWER then we look in the answer @@ -5418,6 +5494,9 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, if (aa) rdataset->trust = dns_trust_authauthority; + else if (ISFORWARDER(fctx->addrinfo)) + rdataset->trust = + dns_trust_answer; else rdataset->trust = dns_trust_additional; @@ -5431,6 +5510,12 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, return (result); } + log_ns_ttl(fctx, "noanswer_response"); + + if (ns_rdataset != NULL && dns_name_equal(&fctx->domain, ns_name) && + !dns_name_equal(ns_name, dns_rootname)) + trim_ns_ttl(fctx, ns_name, ns_rdataset); + /* * A negative response has a SOA record (Type 2) * and a optional NS RRset (Type 1) or it has neither @@ -5471,6 +5556,9 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, if (aa) rdataset->trust = dns_trust_authauthority; + else if (ISFORWARDER(fctx->addrinfo)) + rdataset->trust = + dns_trust_answer; else rdataset->trust = dns_trust_additional; @@ -5512,6 +5600,9 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, if (aa) rdataset->trust = dns_trust_authauthority; + else if (ISFORWARDER(fctx->addrinfo)) + rdataset->trust = + dns_trust_answer; else rdataset->trust = dns_trust_additional; @@ -5643,6 +5734,8 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, if (result != ISC_R_SUCCESS) return (result); fctx->attributes |= FCTX_ATTR_WANTCACHE; + fctx->ns_ttl_ok = ISC_FALSE; + log_ns_ttl(fctx, "DELEGATION"); return (DNS_R_DELEGATION); } @@ -5663,8 +5756,8 @@ static isc_result_t answer_response(fetchctx_t *fctx) { isc_result_t result; dns_message_t *message; - dns_name_t *name, *qname, tname; - dns_rdataset_t *rdataset; + dns_name_t *name, *qname, tname, *ns_name; + dns_rdataset_t *rdataset, *ns_rdataset; isc_boolean_t done, external, chaining, aa, found, want_chaining; isc_boolean_t have_answer, found_cname, found_type, wanted_chaining; unsigned int aflag; @@ -6064,6 +6157,8 @@ answer_response(fetchctx_t *fctx) { * in this section, and we expect that it is not external. */ done = ISC_FALSE; + ns_name = NULL; + ns_rdataset = NULL; result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); while (!done && result == ISC_R_SUCCESS) { name = NULL; @@ -6091,6 +6186,10 @@ answer_response(fetchctx_t *fctx) { rdataset->trust = dns_trust_additional; + if (rdataset->type == dns_rdatatype_ns) { + ns_name = name; + ns_rdataset = rdataset; + } /* * Mark any additional data related * to this rdataset. @@ -6108,6 +6207,12 @@ answer_response(fetchctx_t *fctx) { if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; + log_ns_ttl(fctx, "answer_response"); + + if (ns_rdataset != NULL && dns_name_equal(&fctx->domain, ns_name) && + !dns_name_equal(ns_name, dns_rootname)) + trim_ns_ttl(fctx, ns_name, ns_rdataset); + return (result); } @@ -6179,6 +6284,9 @@ resume_dslookup(isc_task_t *task, isc_event_t *event) { if (dns_rdataset_isassociated(&fctx->nameservers)) dns_rdataset_disassociate(&fctx->nameservers); dns_rdataset_clone(fevent->rdataset, &fctx->nameservers); + fctx->ns_ttl = fctx->nameservers.ttl; + fctx->ns_ttl_ok = ISC_TRUE; + log_ns_ttl(fctx, "resume_dslookup"); dns_name_free(&fctx->domain, fctx->res->buckets[bucketnum].mctx); dns_name_init(&fctx->domain, NULL); @@ -7112,6 +7220,8 @@ resquery_response(isc_task_t *task, isc_event_t *event) { fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); return; } + fctx->ns_ttl = fctx->nameservers.ttl; + fctx->ns_ttl_ok = ISC_TRUE; fctx_cancelqueries(fctx, ISC_TRUE); fctx_cleanupfinds(fctx); fctx_cleanupaltfinds(fctx); |