diff --git a/lib/dns/include/dns/ncache.h b/lib/dns/include/dns/ncache.h index 3436ce4fc9..67dc401990 100644 --- a/lib/dns/include/dns/ncache.h +++ b/lib/dns/include/dns/ncache.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: ncache.h,v 1.15 2002/02/20 03:34:35 marka Exp $ */ +/* $Id: ncache.h,v 1.16 2002/07/19 03:29:15 marka Exp $ */ #ifndef DNS_NCACHE_H #define DNS_NCACHE_H 1 @@ -125,6 +125,34 @@ dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx, * dns_name_towire(). */ +isc_result_t +dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name, + dns_rdatatype_t type, dns_rdataset_t *rdataset); +/* + * Search the negative caching rdataset for an rdataset with the + * specified name and type. + * + * Requires: + * 'ncacherdataset' is a valid negative caching rdataset. + * + * 'ncacherdataset' is not empty. + * + * 'name' is a valid name. + * + * 'type' is not SIG, or a meta-RR type. + * + * 'rdataset' is a valid disassociated rdataset. + * + * Ensures: + * On a return of ISC_R_SUCCESS, 'rdataset' is bound to the found + * rdataset. + * + * Returns: + * ISC_R_SUCCESS - the rdataset was found. + * ISC_R_NOTFOUND - the rdataset was not found. + * + */ + ISC_LANG_ENDDECLS #endif /* DNS_NCACHE_H */ diff --git a/lib/dns/ncache.c b/lib/dns/ncache.c index 7446dd0002..d15a65bf3e 100644 --- a/lib/dns/ncache.c +++ b/lib/dns/ncache.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: ncache.c,v 1.29 2002/02/20 03:34:15 marka Exp $ */ +/* $Id: ncache.c,v 1.30 2002/07/19 03:29:15 marka Exp $ */ #include @@ -378,3 +378,170 @@ dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx, return (result); } + +static void +rdataset_disassociate(dns_rdataset_t *rdataset) { + UNUSED(rdataset); +} + +static isc_result_t +rdataset_first(dns_rdataset_t *rdataset) { + unsigned char *raw = rdataset->private3; + unsigned int count; + + count = raw[0] * 256 + raw[1]; + if (count == 0) { + rdataset->private5 = NULL; + return (ISC_R_NOMORE); + } + raw += 2; + /* + * The private4 field is the number of rdata beyond the cursor + * position, so we decrement the total count by one before storing + * it. + */ + count--; + rdataset->private4 = (void *)count; + rdataset->private5 = raw; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +rdataset_next(dns_rdataset_t *rdataset) { + unsigned int count; + unsigned int length; + unsigned char *raw; + + count = (unsigned int)rdataset->private4; + if (count == 0) + return (ISC_R_NOMORE); + count--; + rdataset->private4 = (void *)count; + raw = rdataset->private5; + length = raw[0] * 256 + raw[1]; + raw += length + 2; + rdataset->private5 = raw; + + return (ISC_R_SUCCESS); +} + +static void +rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { + unsigned char *raw = rdataset->private5; + isc_region_t r; + + REQUIRE(raw != NULL); + + r.length = raw[0] * 256 + raw[1]; + raw += 2; + r.base = raw; + dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r); +} + +static void +rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) { + *target = *source; + + /* + * Reset iterator state. + */ + target->private4 = NULL; + target->private5 = NULL; +} + +static unsigned int +rdataset_count(dns_rdataset_t *rdataset) { + unsigned char *raw = rdataset->private3; + unsigned int count; + + count = raw[0] * 256 + raw[1]; + + return (count); +} + +static dns_rdatasetmethods_t rdataset_methods = { + rdataset_disassociate, + rdataset_first, + rdataset_next, + rdataset_current, + rdataset_clone, + rdataset_count +}; + +isc_result_t +dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name, + dns_rdatatype_t type, dns_rdataset_t *rdataset) +{ + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_region_t remaining; + isc_buffer_t source; + dns_name_t tname; + dns_rdatatype_t ttype; + unsigned int i, rcount; + isc_uint16_t length; + + REQUIRE(ncacherdataset != NULL); + REQUIRE(ncacherdataset->type == 0); + REQUIRE(name != NULL); + REQUIRE(!dns_rdataset_isassociated(rdataset)); + REQUIRE(type != dns_rdatatype_sig); + + result = dns_rdataset_first(ncacherdataset); + if (result != ISC_R_SUCCESS) + return (result); + dns_rdataset_current(ncacherdataset, &rdata); + INSIST(dns_rdataset_next(ncacherdataset) == ISC_R_NOMORE); + isc_buffer_init(&source, rdata.data, rdata.length); + isc_buffer_add(&source, rdata.length); + + do { + dns_name_init(&tname, NULL); + isc_buffer_remainingregion(&source, &remaining); + dns_name_fromregion(&tname, &remaining); + INSIST(remaining.length >= tname.length); + isc_buffer_forward(&source, tname.length); + remaining.length -= tname.length; + + INSIST(remaining.length >= 4); + ttype = isc_buffer_getuint16(&source); + + if (ttype == type && dns_name_equal(&tname, name)) { + isc_buffer_remainingregion(&source, &remaining); + break; + } + + rcount = isc_buffer_getuint16(&source); + for (i = 0; i < rcount; i++) { + isc_buffer_remainingregion(&source, &remaining); + INSIST(remaining.length >= 2); + length = isc_buffer_getuint16(&source); + isc_buffer_remainingregion(&source, &remaining); + INSIST(remaining.length >= length); + isc_buffer_forward(&source, length); + } + isc_buffer_remainingregion(&source, &remaining); + } while (remaining.length > 0); + + if (remaining.length == 0) + return (ISC_R_NOTFOUND); + + rdataset->methods = &rdataset_methods; + rdataset->rdclass = ncacherdataset->rdclass; + rdataset->type = type; + rdataset->covers = 0; + rdataset->ttl = ncacherdataset->ttl; + rdataset->trust = ncacherdataset->trust; + rdataset->private1 = NULL; + rdataset->private2 = NULL; + + rdataset->private3 = remaining.base; + + /* + * Reset iterator state. + */ + rdataset->private4 = NULL; + rdataset->private5 = NULL; + return (ISC_R_SUCCESS); +} diff --git a/lib/dns/validator.c b/lib/dns/validator.c index e25ab8877b..acb084e656 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: validator.c,v 1.108 2002/07/15 03:25:28 marka Exp $ */ +/* $Id: validator.c,v 1.109 2002/07/19 03:29:15 marka Exp $ */ #include @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,7 @@ #include #define VALIDATOR_MAGIC ISC_MAGIC('V', 'a', 'l', '?') -#define VALID_VALIDATOR(v) ISC_MAGIC_VALID(v, VALIDATOR_MAGIC) +#define VALID_VALIDATOR(v) ISC_MAGIC_VALID(v, VALIDATOR_MAGIC) #define VALATTR_SHUTDOWN 0x01 #define VALATTR_FOUNDNONEXISTENCE 0x02 @@ -142,6 +143,39 @@ auth_nonpending(dns_message_t *message) { } } +static isc_boolean_t +isdelegation(dns_name_t *name, dns_rdataset_t *rdataset, + isc_result_t dbresult) +{ + dns_rdataset_t set; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_boolean_t found; + isc_result_t result; + + REQUIRE(dbresult == DNS_R_NXRRSET || dbresult == DNS_R_NCACHENXRRSET); + + dns_rdataset_init(&set); + if (dbresult == DNS_R_NXRRSET) + dns_rdataset_clone(rdataset, &set); + else { + result = dns_ncache_getrdataset(rdataset, name, + dns_rdatatype_nxt, &set); + if (result != ISC_R_SUCCESS) + return (ISC_FALSE); + } + + INSIST(set.type == dns_rdatatype_nxt); + + found = ISC_FALSE; + result = dns_rdataset_first(&set); + if (result == ISC_R_SUCCESS) { + dns_rdataset_current(&set, &rdata); + found = dns_nxt_typepresent(&rdata, dns_rdatatype_ns); + } + dns_rdataset_disassociate(&set); + return (found); +} + static void fetch_callback_validator(isc_task_t *task, isc_event_t *event) { dns_fetchevent_t *devent; @@ -256,6 +290,7 @@ dsfetched2(isc_task_t *task, isc_event_t *event) { dns_fetchevent_t *devent; dns_validator_t *val; dns_rdataset_t *rdataset; + dns_name_t *tname; isc_boolean_t want_destroy; isc_result_t result; isc_result_t eresult; @@ -267,7 +302,6 @@ dsfetched2(isc_task_t *task, isc_event_t *event) { rdataset = &val->frdataset; eresult = devent->result; - isc_event_free(&event); dns_resolver_destroyfetch(&val->fetch); INSIST(val->event != NULL); @@ -276,10 +310,17 @@ dsfetched2(isc_task_t *task, isc_event_t *event) { LOCK(&val->lock); if (eresult == DNS_R_NXRRSET || eresult == DNS_R_NCACHENXRRSET) { /* - * There is no DS. We're done. + * There is no DS. If this is a delegation, we're done. */ - val->event->rdataset->trust = dns_trust_answer; - validator_done(val, ISC_R_SUCCESS); + tname = dns_fixedname_name(&devent->foundname); + if (isdelegation(tname, &val->frdataset, eresult)) { + val->event->rdataset->trust = dns_trust_answer; + validator_done(val, ISC_R_SUCCESS); + } else { + result = proveunsecure(val, ISC_TRUE); + if (result != DNS_R_WAIT) + validator_done(val, result); + } } else if (eresult == ISC_R_SUCCESS || eresult == DNS_R_NXDOMAIN || eresult == DNS_R_NCACHENXDOMAIN) @@ -296,6 +337,7 @@ dsfetched2(isc_task_t *task, isc_event_t *event) { else validator_done(val, DNS_R_NOVALIDDS); } + isc_event_free(&event); want_destroy = exit_check(val); UNLOCK(&val->lock); if (want_destroy) @@ -1480,7 +1522,8 @@ proveunsecure(dns_validator_t *val, isc_boolean_t resume) { result = view_find(val, tname, dns_rdatatype_ds); if (result == DNS_R_NXRRSET || result == DNS_R_NCACHENXRRSET) { /* - * There is no DS. We're hopefully done. + * There is no DS. If this is a delegation, + * we're done. */ if (val->frdataset.trust < dns_trust_secure) { /* @@ -1492,11 +1535,14 @@ proveunsecure(dns_validator_t *val, isc_boolean_t resume) { result = DNS_R_NOVALIDSIG; goto out; } - val->event->rdataset->trust = dns_trust_answer; - return (ISC_R_SUCCESS); + if (isdelegation(tname, &val->frdataset, result)) { + val->event->rdataset->trust = dns_trust_answer; + return (ISC_R_SUCCESS); + } + continue; } else if (result == ISC_R_SUCCESS) { /* - * There is a DS here. Verify that it's secure and + * There is a DS here. Verify that it's secure and * continue. */ if (val->frdataset.trust >= dns_trust_secure) @@ -1518,7 +1564,7 @@ proveunsecure(dns_validator_t *val, isc_boolean_t resume) { result == DNS_R_NCACHENXDOMAIN) { /* - * This is not a zone cut. Assuming things are + * This is not a zone cut. Assuming things are * as expected, continue. */ if (!dns_rdataset_isassociated(&val->frdataset)) {