From 421e4cf66e4cba0b0751a34a9c027e39fe0474f9 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Sat, 18 Jan 2003 03:18:31 +0000 Subject: [PATCH] 1416. [bug] Empty node should return NOERROR NODATA, not NXDOMAIN. [RT #4715] developer: marka reviewer: explorer --- CHANGES | 3 ++ bin/named/query.c | 5 ++- lib/dns/include/dns/db.h | 5 ++- lib/dns/include/dns/rdataset.h | 3 +- lib/dns/include/dns/result.h | 5 ++- lib/dns/ncache.c | 4 +- lib/dns/rbtdb.c | 75 +++++++++++++++++++++++++++++----- lib/dns/resolver.c | 11 ++--- lib/dns/result.c | 5 ++- lib/dns/validator.c | 32 ++++++++++++--- 10 files changed, 119 insertions(+), 29 deletions(-) diff --git a/CHANGES b/CHANGES index 3a4da3d4c0..0473cd574f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +1416. [bug] Empty node should return NOERROR NODATA, not NXDOMAIN. + [RT #4715] + 1415. [func] DS TTL now derived from NS ttl. NXT TTL now derived from SOA MINIMUM. diff --git a/bin/named/query.c b/bin/named/query.c index 4b7caaebcc..d6e7f6b5fc 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.239 2003/01/14 00:28:49 marka Exp $ */ +/* $Id: query.c,v 1.240 2003/01/18 03:18:30 marka Exp $ */ #include @@ -2687,6 +2687,9 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) } } goto cleanup; + case DNS_R_EMPTYNAME: + result = DNS_R_NXRRSET; + /* FALLTHROUGH */ case DNS_R_NXRRSET: INSIST(is_zone); if (dns_rdataset_isassociated(rdataset)) { diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index 5b156eece3..4f4d1caac5 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: db.h,v 1.70 2002/08/06 01:50:28 marka Exp $ */ +/* $Id: db.h,v 1.71 2003/01/18 03:18:31 marka Exp $ */ #ifndef DNS_DB_H #define DNS_DB_H 1 @@ -767,6 +767,9 @@ dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * name, and 'rdataset' contains * the negative caching proof. * + * DNS_R_EMPTYNAME The name exists but there is + * no data at the name. + * * Error results: * * ISC_R_NOMEMORY diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h index 290a769f22..710e3c8318 100644 --- a/lib/dns/include/dns/rdataset.h +++ b/lib/dns/include/dns/rdataset.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdataset.h,v 1.45 2002/02/20 03:34:37 marka Exp $ */ +/* $Id: rdataset.h,v 1.46 2003/01/18 03:18:31 marka Exp $ */ #ifndef DNS_RDATASET_H #define DNS_RDATASET_H 1 @@ -129,6 +129,7 @@ struct dns_rdataset { #define DNS_RDATASETATTR_FIXEDORDER 0x0400 #define DNS_RDATASETATTR_RANDOMIZE 0x0800 #define DNS_RDATASETATTR_CHASE 0x1000 /* Used by resolver. */ +#define DNS_RDATASETATTR_NXDOMAIN 0x2000 /* * _OMITDNSSEC: diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h index c706360ce4..263e9e78af 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.95 2003/01/14 00:28:50 marka Exp $ */ +/* $Id: result.h,v 1.96 2003/01/18 03:18:31 marka Exp $ */ #ifndef DNS_RESULT_H #define DNS_RESULT_H 1 @@ -133,8 +133,9 @@ #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_EMPTYNAME (ISC_RESULTCLASS_DNS + 92) -#define DNS_R_NRESULTS 92 /* Number of results */ +#define DNS_R_NRESULTS 93 /* Number of results */ /* * DNS wire format rcodes. diff --git a/lib/dns/ncache.c b/lib/dns/ncache.c index d15a65bf3e..d9e3221c81 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.30 2002/07/19 03:29:15 marka Exp $ */ +/* $Id: ncache.c,v 1.31 2003/01/18 03:18:30 marka Exp $ */ #include @@ -247,6 +247,8 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, RUNTIME_CHECK(dns_rdatalist_tordataset(&ncrdatalist, &ncrdataset) == ISC_R_SUCCESS); ncrdataset.trust = trust; + if (message->rcode == dns_rcode_nxdomain) + ncrdataset.attributes |= DNS_RDATASETATTR_NXDOMAIN; return (dns_db_addrdataset(cache, node, NULL, now, &ncrdataset, 0, addedrdataset)); diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index a7d7a68729..9120f1c296 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbtdb.c,v 1.183 2003/01/14 00:38:07 marka Exp $ */ +/* $Id: rbtdb.c,v 1.184 2003/01/18 03:18:30 marka Exp $ */ /* * Principal Author: Bob Halley @@ -91,7 +91,7 @@ typedef isc_uint32_t rbtdb_rdatatype_t; RBTDB_RDATATYPE_VALUE(dns_rdatatype_sig, dns_rdatatype_cname) #define RBTDB_RDATATYPE_SIGDNAME \ RBTDB_RDATATYPE_VALUE(dns_rdatatype_sig, dns_rdatatype_dname) -#define RBTDB_RDATATYPE_NXDOMAIN \ +#define RBTDB_RDATATYPE_NCACHEANY \ RBTDB_RDATATYPE_VALUE(0, dns_rdatatype_any) typedef struct rdatasetheader { @@ -128,6 +128,7 @@ typedef struct rdatasetheader { #define RDATASET_ATTR_STALE 0x0002 #define RDATASET_ATTR_IGNORE 0x0004 #define RDATASET_ATTR_RETAIN 0x0008 +#define RDATASET_ATTR_NXDOMAIN 0x0010 /* * XXX @@ -147,6 +148,8 @@ typedef struct rdatasetheader { (((header)->attributes & RDATASET_ATTR_IGNORE) != 0) #define RETAIN(header) \ (((header)->attributes & RDATASET_ATTR_RETAIN) != 0) +#define NXDOMAIN(header) \ + (((header)->attributes & RDATASET_ATTR_NXDOMAIN) != 0) #define DEFAULT_NODE_LOCK_COUNT 7 /* Should be prime. */ @@ -1307,6 +1310,8 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdataset->covers = RBTDB_RDATATYPE_EXT(header->type); rdataset->ttl = header->ttl - now; rdataset->trust = header->trust; + if (NXDOMAIN(header)) + rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN; rdataset->private1 = rbtdb; rdataset->private2 = node; raw = (unsigned char *)header + sizeof(*header); @@ -1841,6 +1846,12 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, rdatasetheader_t *header, *header_next, *found, *nxtheader; rdatasetheader_t *foundsig, *cnamesig, *nxtsig; rbtdb_rdatatype_t sigtype; + dns_fixedname_t fnext; + dns_fixedname_t forigin; + dns_name_t nname, *next, *origin; + isc_boolean_t active; + dns_rbtnodechain_t chain; + search.rbtdb = (dns_rbtdb_t *)db; @@ -1914,6 +1925,45 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, goto tree_exit; } + /* + * Find if this is a active empty node (next active node is + * subdomain if 'name'). This is a simpler test than is + * required for activeemptynode() where name is not as + * constained. + */ + active = ISC_FALSE; + dns_fixedname_init(&fnext); + next = dns_fixedname_name(&fnext); + dns_fixedname_init(&forigin); + origin = dns_fixedname_name(&forigin); + dns_name_init(&nname, NULL); + chain = search.chain; + result = dns_rbtnodechain_next(&chain, NULL, NULL); + while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { + node = NULL; + result = dns_rbtnodechain_current(&chain, &nname, + origin, &node); + if (result != ISC_R_SUCCESS) + break; + LOCK(&(search.rbtdb->node_locks[node->locknum].lock)); + for (header = node->data; + header != NULL; + header = header->next) { + if (header->serial <= search.serial && + !IGNORE(header) && EXISTS(header)) + break; + } + UNLOCK(&(search.rbtdb->node_locks[node->locknum].lock)); + if (header != NULL) { + result = dns_name_concatenate(&nname, origin, + next, NULL); + if (result == ISC_R_SUCCESS && + dns_name_issubdomain(next, name)) + active = ISC_TRUE; + break; + } + result = dns_rbtnodechain_next(&chain, NULL, NULL); + } /* * If we're here, then the name does not exist, is not * beneath a zonecut, and there's no matching wildcard. @@ -1925,9 +1975,10 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, rdataset, sigrdataset, search.rbtdb->secure); if (result == ISC_R_SUCCESS) - result = DNS_R_NXDOMAIN; + result = active ? DNS_R_EMPTYNAME : + DNS_R_NXDOMAIN; } else - result = DNS_R_NXDOMAIN; + result = active ? DNS_R_EMPTYNAME : DNS_R_NXDOMAIN; goto tree_exit; } else if (result != ISC_R_SUCCESS) goto tree_exit; @@ -2668,7 +2719,7 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * target type. Remember it. */ foundsig = header; - } else if (header->type == RBTDB_RDATATYPE_NXDOMAIN || + } else if (header->type == RBTDB_RDATATYPE_NCACHEANY || header->type == nxtype) { /* * We've found a negative cache entry. @@ -2756,7 +2807,7 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, /* * We found a negative cache entry. */ - if (found->type == RBTDB_RDATATYPE_NXDOMAIN) + if (NXDOMAIN(found)) result = DNS_R_NCACHENXDOMAIN; else result = DNS_R_NCACHENXRRSET; @@ -2776,7 +2827,8 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, result = ISC_R_SUCCESS; } - if (type != dns_rdatatype_any || result == DNS_R_NCACHENXDOMAIN) { + if (type != dns_rdatatype_any || result == DNS_R_NCACHENXDOMAIN || + result == DNS_R_NCACHENXRRSET) { bind_rdataset(search.rbtdb, node, found, search.now, rdataset); if (foundsig != NULL) @@ -3298,7 +3350,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, 0) { if (header->type == matchtype) found = header; - else if (header->type == RBTDB_RDATATYPE_NXDOMAIN || + else if (header->type == RBTDB_RDATATYPE_NCACHEANY || header->type == nxtype) found = header; else if (header->type == sigmatchtype) @@ -3321,7 +3373,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, /* * We found a negative cache entry. */ - if (found->type == RBTDB_RDATATYPE_NXDOMAIN) + if (NXDOMAIN(found)) result = DNS_R_NCACHENXDOMAIN; else result = DNS_R_NCACHENXRRSET; @@ -3549,8 +3601,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, for (topheader = rbtnode->data; topheader != NULL; topheader = topheader->next) { - if (topheader->type == - RBTDB_RDATATYPE_NXDOMAIN) + if (NXDOMAIN(topheader)) break; } if (topheader != NULL && EXISTS(topheader) && @@ -3857,6 +3908,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, } else { newheader->serial = 1; newheader->trust = rdataset->trust; + if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) + newheader->attributes |= RDATASET_ATTR_NXDOMAIN; } /* diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 77c725f378..981a21f8c3 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.259 2003/01/16 03:59:25 marka Exp $ */ +/* $Id: resolver.c,v 1.260 2003/01/18 03:18:30 marka Exp $ */ #include @@ -309,6 +309,8 @@ struct dns_resolver { #define ISFORWARDER(a) (((a)->flags & \ FCTX_ADDRINFO_FORWARDER) != 0) +#define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 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); @@ -3337,8 +3339,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, isc_stdtime_t now) { * a negative cache entry, so we * must set eresult appropriately. */ - if (ardataset->covers == - dns_rdatatype_any) + if (NXDOMAIN(ardataset)) eresult = DNS_R_NCACHENXDOMAIN; else @@ -3445,7 +3446,7 @@ ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, * The cache data is also a negative cache * entry. */ - if (ardataset->covers == dns_rdatatype_any) + if (NXDOMAIN(ardataset)) *eresultp = DNS_R_NCACHENXDOMAIN; else *eresultp = DNS_R_NCACHENXRRSET; @@ -3464,7 +3465,7 @@ ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node, result = ISC_R_SUCCESS; } } else if (result == ISC_R_SUCCESS) { - if (covers == dns_rdatatype_any) + if (NXDOMAIN(ardataset)) *eresultp = DNS_R_NCACHENXDOMAIN; else *eresultp = DNS_R_NCACHENXRRSET; diff --git a/lib/dns/result.c b/lib/dns/result.c index 4d2afb16dd..823e78ab2f 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.105 2003/01/14 00:28:49 marka Exp $ */ +/* $Id: result.c,v 1.106 2003/01/18 03:18:30 marka Exp $ */ #include @@ -139,7 +139,8 @@ static const char *text[DNS_R_NRESULTS] = { "unexpected RCODE", /* 89 DNS_R_UNEXPECTEDRCODE */ "unexpected OPCODE", /* 90 DNS_R_UNEXPECTEDOPCODE */ - "chase DS servers" /* 91 DNS_R_CHASEDSSERVERS */ + "chase DS servers", /* 91 DNS_R_CHASEDSSERVERS */ + "empty name" /* 92 DNS_R_EMPTYNAME */ }; static const char *rcode_text[DNS_R_NRCODERESULTS] = { diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 053d02d521..6eebef377d 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.110 2002/07/22 03:00:49 marka Exp $ */ +/* $Id: validator.c,v 1.111 2003/01/18 03:18:30 marka Exp $ */ #include @@ -440,6 +440,8 @@ nxtprovesnonexistence(dns_validator_t *val, dns_name_t *nxtname, dns_rdata_t rdata = DNS_RDATA_INIT; isc_boolean_t isnxdomain; isc_result_t result; + dns_namereln_t relation; + unsigned int labels, bits; INSIST(DNS_MESSAGE_VALID(val->event->message)); @@ -486,9 +488,23 @@ nxtprovesnonexistence(dns_validator_t *val, dns_name_t *nxtname, * The NXT owner name is less than the nonexistent name. */ if (!isnxdomain) { + /* + * Is this a empty node? + */ + result = dns_rdata_tostruct(&rdata, &nxt, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + relation = dns_name_fullcompare(&nxt.next, + val->event->name, + &order, &labels, &bits); + dns_rdata_freestruct(&nxt); + if (order <= 0 || relation != dns_namereln_subdomain) { + validator_log(val, ISC_LOG_DEBUG(3), + "missing NXT record at name"); + return (ISC_FALSE); + } validator_log(val, ISC_LOG_DEBUG(3), - "missing NXT record at name"); - return (ISC_FALSE); + "nxt proves empty node, ok"); + return (ISC_TRUE); } if (dns_name_issubdomain(val->event->name, nxtname) && dns_nxt_typepresent(&rdata, dns_rdatatype_ns) && @@ -507,8 +523,9 @@ nxtprovesnonexistence(dns_validator_t *val, dns_name_t *nxtname, if (result != ISC_R_SUCCESS) return (ISC_FALSE); dns_rdata_reset(&rdata); - order = dns_name_compare(val->event->name, &nxt.next); - if (order >= 0) { + relation = dns_name_fullcompare(&nxt.next, val->event->name, + &order, &labels, &bits); + if (order <= 0) { /* * The NXT next name is less than the nonexistent * name. This is only ok if the next name is the zone @@ -522,6 +539,11 @@ nxtprovesnonexistence(dns_validator_t *val, dns_name_t *nxtname, } validator_log(val, ISC_LOG_DEBUG(3), "nxt points to zone apex, ok"); + } else if (relation == dns_namereln_subdomain) { + validator_log(val, ISC_LOG_DEBUG(3), + "nxt proves empty node, bad"); + dns_rdata_freestruct(&nxt); + return (ISC_FALSE); } dns_rdata_freestruct(&nxt); validator_log(val, ISC_LOG_DEBUG(3), "nxt range ok");