diff --git a/CHANGES b/CHANGES index 10da04fe70..2b697f3d4b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +1410. [func] handle records that live in the parent zone, e.g. DS. + 1409. [bug] DS should have attibute DNS_RDATATYPEATTR_DNSSEC. 1408. [bug] distclean was not complete. [RT #4700] diff --git a/bin/named/query.c b/bin/named/query.c index 82b6da7930..4b7caaebcc 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: query.c,v 1.238 2002/11/27 09:52:45 marka Exp $ */ +/* $Id: query.c,v 1.239 2003/01/14 00:28:49 marka Exp $ */ #include @@ -88,6 +88,7 @@ #define DNS_GETDB_NOEXACT 0x01U #define DNS_GETDB_NOLOG 0x02U +#define DNS_GETDB_PARTIAL 0x04U static void query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype); @@ -544,6 +545,7 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, unsigned int options, unsigned int ztoptions; dns_zone_t *zone = NULL; dns_db_t *db = NULL; + isc_boolean_t partial = ISC_FALSE; REQUIRE(zonep != NULL && *zonep == NULL); REQUIRE(dbp != NULL && *dbp == NULL); @@ -556,6 +558,8 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, unsigned int options, result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL, &zone); + if (result == DNS_R_PARTIALMATCH) + partial = ISC_TRUE; if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) result = dns_zone_getdb(zone, &db); @@ -687,6 +691,8 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, unsigned int options, *dbp = db; *versionp = dbversion->version; + if (partial && (options & DNS_GETDB_PARTIAL) != 0) + return (DNS_R_PARTIALMATCH); return (ISC_R_SUCCESS); refuse: @@ -2266,6 +2272,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) version = NULL; zone = NULL; need_wildcardproof = ISC_FALSE; + options = 0; if (event != NULL) { /* @@ -2340,6 +2347,39 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) options |= DNS_GETDB_NOEXACT; result = query_getdb(client, client->query.qname, options, &zone, &db, &version, &is_zone); + if ((result != ISC_R_SUCCESS || !is_zone) && !RECURSIONOK(client) && + (options & DNS_GETDB_NOEXACT) != 0 && qtype == dns_rdatatype_ds) { + /* + * Look to see if we are authoritative for the + * child zone if the query type is DS. + */ + dns_db_t *tdb = NULL; + dns_zone_t *tzone = NULL; + dns_dbversion_t *tversion = NULL; + isc_result_t tresult; + + tresult = query_getzonedb(client, client->query.qname, + DNS_GETDB_PARTIAL, &tzone, &tdb, + &tversion); + if (tresult == ISC_R_SUCCESS) { + options &= ~DNS_GETDB_NOEXACT; + query_putrdataset(client, &rdataset); + if (db != NULL) + dns_db_detach(&db); + if (zone != NULL) + dns_zone_detach(&zone); + version = tversion; + db = tdb; + zone = tzone; + is_zone = ISC_TRUE; + result = ISC_R_SUCCESS; + } else { + if (tdb != NULL) + dns_db_detach(&tdb); + if (tzone != NULL) + dns_zone_detach(&tzone); + } + } if (result != ISC_R_SUCCESS) { if (result == DNS_R_REFUSED) QUERY_ERROR(DNS_R_REFUSED); @@ -2466,6 +2506,47 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) case DNS_R_DELEGATION: authoritative = ISC_FALSE; if (is_zone) { + /* + * Look to see if we are authoritative for the + * child zone if the query type is DS. + */ + if (!RECURSIONOK(client) && + (options & DNS_GETDB_NOEXACT) != 0 && + qtype == dns_rdatatype_ds) { + dns_db_t *tdb = NULL; + dns_zone_t *tzone = NULL; + dns_dbversion_t *tversion = NULL; + result = query_getzonedb(client, + client->query.qname, + DNS_GETDB_PARTIAL, + &tzone, &tdb, + &tversion); + if (result == ISC_R_SUCCESS) { + options &= ~DNS_GETDB_NOEXACT; + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, + &sigrdataset); + if (fname != NULL) + query_releasename(client, + &fname); + if (node != NULL) + dns_db_detachnode(db, &node); + if (db != NULL) + dns_db_detach(&db); + if (zone != NULL) + dns_zone_detach(&zone); + version = tversion; + db = tdb; + zone = tzone; + authoritative = ISC_TRUE; + goto db_find; + } + if (tdb != NULL) + dns_db_detach(&tdb); + if (tzone != NULL) + dns_zone_detach(&tzone); + } /* * We're authoritative for an ancestor of QNAME. */ diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h index 31aa1daf5f..c706360ce4 100644 --- a/lib/dns/include/dns/result.h +++ b/lib/dns/include/dns/result.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.h,v 1.94 2002/08/27 04:53:43 marka Exp $ */ +/* $Id: result.h,v 1.95 2003/01/14 00:28:50 marka Exp $ */ #ifndef DNS_RESULT_H #define DNS_RESULT_H 1 @@ -132,8 +132,9 @@ #define DNS_R_LAME (ISC_RESULTCLASS_DNS + 88) #define DNS_R_UNEXPECTEDRCODE (ISC_RESULTCLASS_DNS + 89) #define DNS_R_UNEXPECTEDOPCODE (ISC_RESULTCLASS_DNS + 90) +#define DNS_R_CHASEDSSERVERS (ISC_RESULTCLASS_DNS + 91) -#define DNS_R_NRESULTS 91 /* Number of results */ +#define DNS_R_NRESULTS 92 /* Number of results */ /* * DNS wire format rcodes. diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index fdc009daa1..d9f61016c0 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.c,v 1.256 2003/01/05 23:19:29 marka Exp $ */ +/* $Id: resolver.c,v 1.257 2003/01/14 00:28:49 marka Exp $ */ #include @@ -199,6 +199,12 @@ struct fetchctx { * 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', '!', '!', '!') @@ -2024,6 +2030,9 @@ 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); /* * Shut down anything that is still running on behalf of this @@ -2234,6 +2243,10 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, fctx->timeouts = 0; fctx->attributes = 0; + dns_name_init(&fctx->nsname, NULL); + fctx->nsfetch = NULL; + dns_rdataset_init(&fctx->nsrrset); + if (domain == NULL) { dns_forwarders_t *forwarders = NULL; result = dns_fwdtable_find(fctx->res->view->fwdtable, @@ -3710,6 +3723,15 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, return (result); } + /* + * Trigger lookups for DNS nameservers. + */ + if (negative_response && message->rcode == dns_rcode_noerror && + fctx->type == dns_rdatatype_ds && soa_name != NULL && + dns_name_equal(soa_name, qname) && + !dns_name_equal(qname, dns_rootname)) + return (DNS_R_CHASEDSSERVERS); + /* * Did we find anything? */ @@ -4211,6 +4233,98 @@ answer_response(fetchctx_t *fctx) { return (result); } +static void +resume_dslookup(isc_task_t *task, isc_event_t *event) { + dns_fetchevent_t *fevent; + dns_resolver_t *res; + fetchctx_t *fctx; + isc_result_t result; + isc_boolean_t bucket_empty = ISC_FALSE; + isc_boolean_t locked = ISC_FALSE; + + REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE); + fevent = (dns_fetchevent_t *)event; + fctx = event->ev_arg; + REQUIRE(VALID_FCTX(fctx)); + res = fctx->res; + + UNUSED(task); + FCTXTRACE("resume_dslookup"); + + if (fevent->node != NULL) + dns_db_detachnode(fevent->db, &fevent->node); + if (fevent->db != NULL) + dns_db_detach(&fevent->db); + + dns_resolver_destroyfetch(&fctx->nsfetch); + + if (fevent->result == ISC_R_CANCELED) + fctx_done(fctx, ISC_R_CANCELED); + else if (fevent->result == ISC_R_SUCCESS) { + + FCTXTRACE("resuming DS lookup"); + + if (dns_rdataset_isassociated(&fctx->nameservers)) + dns_rdataset_disassociate(&fctx->nameservers); + dns_rdataset_clone(fevent->rdataset, &fctx->nameservers); + dns_name_free(&fctx->domain, fctx->res->mctx); + dns_name_init(&fctx->domain, NULL); + result = dns_name_dup(&fctx->nsname, fctx->res->mctx, + &fctx->domain); + if (result != ISC_R_SUCCESS) { + fctx_done(fctx, DNS_R_SERVFAIL); + goto cleanup; + } + /* + * Try again. + */ + fctx_try(fctx); + } else { + unsigned int n; + + n = dns_name_countlabels(&fctx->nsname); + dns_name_getlabelsequence(&fctx->nsname, 1, n - 1, + &fctx->nsname); + + if (dns_name_equal(&fctx->nsname, &fctx->domain)) { + fctx_done(fctx, DNS_R_SERVFAIL); + goto cleanup; + } + if (dns_rdataset_isassociated(fevent->rdataset)) + dns_rdataset_disassociate(fevent->rdataset); + FCTXTRACE("continuing to look for parent's NS records"); + result = dns_resolver_createfetch(fctx->res, &fctx->nsname, + dns_rdatatype_ns, + &fctx->domain, + &fctx->nameservers, NULL, + 0, task, + resume_dslookup, fctx, + &fctx->nsrrset, NULL, + &fctx->nsfetch); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + else { + LOCK(&res->buckets[fctx->bucketnum].lock); + locked = ISC_TRUE; + fctx->references++; + } + } + + cleanup: + if (dns_rdataset_isassociated(fevent->rdataset)) + dns_rdataset_disassociate(fevent->rdataset); + INSIST(fevent->sigrdataset == NULL); + isc_event_free(&event); + if (!locked) + LOCK(&res->buckets[fctx->bucketnum].lock); + fctx->references--; + if (fctx->references == 0) + bucket_empty = fctx_destroy(fctx); + UNLOCK(&res->buckets[fctx->bucketnum].lock); + if (bucket_empty) + empty_bucket(res); +} + static void resquery_response(isc_task_t *task, isc_event_t *event) { isc_result_t result = ISC_R_SUCCESS; @@ -4235,7 +4349,6 @@ resquery_response(isc_task_t *task, isc_event_t *event) { REQUIRE(VALID_FCTX(fctx)); REQUIRE(event->ev_type == DNS_EVENT_DISPATCH); - UNUSED(task); QTRACE("response"); (void)isc_timer_touch(fctx->timer); @@ -4569,7 +4682,8 @@ resquery_response(isc_task_t *task, isc_event_t *event) { * NXDOMAIN, NXRDATASET, or referral. */ result = noanswer_response(fctx, NULL, ISC_FALSE); - if (result == DNS_R_DELEGATION) { + if (result == DNS_R_CHASEDSSERVERS) { + } else if (result == DNS_R_DELEGATION) { force_referral: /* * We don't have the answer, but we know a better @@ -4728,6 +4842,34 @@ resquery_response(isc_task_t *task, isc_event_t *event) { result = fctx_stopidletimer(fctx); if (result != ISC_R_SUCCESS) fctx_done(fctx, result); + } else if (result == DNS_R_CHASEDSSERVERS) { + unsigned int n; + add_bad(fctx, &addrinfo->sockaddr, result); + fctx_cancelqueries(fctx, ISC_TRUE); + fctx_cleanupfinds(fctx); + fctx_cleanupforwaddrs(fctx); + + n = dns_name_countlabels(&fctx->name); + dns_name_getlabelsequence(&fctx->name, 1, n - 1, &fctx->nsname); + + FCTXTRACE("suspending DS lookup to find parent's NS records"); + + result = dns_resolver_createfetch(fctx->res, &fctx->nsname, + dns_rdatatype_ns, + &fctx->domain, + &fctx->nameservers, NULL, + 0, task, + resume_dslookup, fctx, + &fctx->nsrrset, NULL, + &fctx->nsfetch); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); + LOCK(&fctx->res->buckets[fctx->bucketnum].lock); + fctx->references++; + UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock); + result = fctx_stopidletimer(fctx); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result); } else { /* * We're done. diff --git a/lib/dns/result.c b/lib/dns/result.c index 1242f216a1..4d2afb16dd 100644 --- a/lib/dns/result.c +++ b/lib/dns/result.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.c,v 1.104 2002/08/27 04:53:43 marka Exp $ */ +/* $Id: result.c,v 1.105 2003/01/14 00:28:49 marka Exp $ */ #include @@ -137,7 +137,9 @@ static const char *text[DNS_R_NRESULTS] = { "truncated TCP response", /* 87 DNS_R_TRUNCATEDTCP */ "lame server detected", /* 88 DNS_R_LAME */ "unexpected RCODE", /* 89 DNS_R_UNEXPECTEDRCODE */ - "unexpected OPCODE" /* 90 DNS_R_UNEXPECTEDOPCODE */ + + "unexpected OPCODE", /* 90 DNS_R_UNEXPECTEDOPCODE */ + "chase DS servers" /* 91 DNS_R_CHASEDSSERVERS */ }; static const char *rcode_text[DNS_R_NRCODERESULTS] = {