From 8b5de9701428e2b5eb50aba96af23dc1186124dd Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 27 Feb 2003 00:19:04 +0000 Subject: [PATCH] 1448. [bug] Handle empty wildcards labels. developer: marka reviewer: explorer --- CHANGES | 2 + bin/named/query.c | 14 ++- lib/dns/include/dns/result.h | 5 +- lib/dns/rbtdb.c | 168 +++++++++++++++++++++++------------ lib/dns/result.c | 5 +- lib/dns/validator.c | 48 +++++++--- 6 files changed, 167 insertions(+), 75 deletions(-) diff --git a/CHANGES b/CHANGES index 91e5f61f20..762d3776d6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +1448. [bug] Handle empty wildcards labels. + 1447. [bug] We were casting (unsigned int) to and from (void *). rdataset->private4 is now rdataset->privateuint4 to reflect a type change. diff --git a/bin/named/query.c b/bin/named/query.c index 6296d06467..57651f3a2c 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.242 2003/01/31 12:07:56 marka Exp $ */ +/* $Id: query.c,v 1.243 2003/02/27 00:19:03 marka Exp $ */ #include @@ -1899,7 +1899,7 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db, */ if (result == ISC_R_SUCCESS && ispositive) break; - if (result == DNS_R_NXDOMAIN) { + if (result == DNS_R_NXDOMAIN || result == DNS_R_EMPTYNAME) { if (!ispositive && dns_name_issubdomain(name, fname)) done = ISC_TRUE; @@ -2269,6 +2269,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) dns_rdata_cname_t cname; dns_rdata_dname_t dname; unsigned int options; + isc_boolean_t empty_wild; CTRACE("query_find"); @@ -2292,6 +2293,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) version = NULL; zone = NULL; need_wildcardproof = ISC_FALSE; + empty_wild = ISC_FALSE; options = 0; if (event != NULL) { @@ -2745,6 +2747,9 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) &rdataset, &sigrdataset); } goto cleanup; + case DNS_R_EMPTYWILD: + empty_wild = ISC_TRUE; + /* FALLTHROUGH */ case DNS_R_NXDOMAIN: INSIST(is_zone); if (dns_rdataset_isassociated(rdataset)) { @@ -2792,7 +2797,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) /* * Set message rcode. */ - client->message->rcode = dns_rcode_nxdomain; + if (empty_wild) + client->message->rcode = dns_rcode_noerror; + else + client->message->rcode = dns_rcode_nxdomain; goto cleanup; case DNS_R_NCACHENXDOMAIN: case DNS_R_NCACHENXRRSET: diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h index 263e9e78af..64c6304b9a 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.96 2003/01/18 03:18:31 marka Exp $ */ +/* $Id: result.h,v 1.97 2003/02/27 00:19:04 marka Exp $ */ #ifndef DNS_RESULT_H #define DNS_RESULT_H 1 @@ -134,8 +134,9 @@ #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_EMPTYWILD (ISC_RESULTCLASS_DNS + 93) -#define DNS_R_NRESULTS 93 /* Number of results */ +#define DNS_R_NRESULTS 94 /* Number of results */ /* * DNS wire format rcodes. diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index b10ccb5017..4ec6a9e185 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.185 2003/02/26 23:52:29 marka Exp $ */ +/* $Id: rbtdb.c,v 1.186 2003/02/27 00:19:03 marka Exp $ */ /* * Principal Author: Bob Halley @@ -1086,6 +1086,34 @@ add_wildcard_magic(dns_rbtdb_t *rbtdb, dns_name_t *name) { return (ISC_R_SUCCESS); } +static isc_result_t +add_empty_wildcards(dns_rbtdb_t *rbtdb, dns_name_t *name) { + isc_result_t result; + dns_name_t foundname; + dns_offsets_t offsets; + unsigned int n, l, i; + + dns_name_init(&foundname, offsets); + n = dns_name_countlabels(name); + l = dns_name_countlabels(&rbtdb->common.origin); + i = l + 1; + while (i < n) { + dns_rbtnode_t *node = NULL; /* dummy */ + dns_name_getlabelsequence(name, n - i, i, &foundname); + if (dns_name_iswildcard(&foundname)) { + result = add_wildcard_magic(rbtdb, &foundname); + if (result != ISC_R_SUCCESS) + return (result); + result = dns_rbt_addnode(rbtdb->tree, &foundname, + &node); + if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) + return (result); + } + i++; + } + return (ISC_R_SUCCESS); +} + static isc_result_t findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, dns_dbnode_t **nodep) @@ -1126,6 +1154,8 @@ findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, node->locknum = dns_name_hash(&nodename, ISC_TRUE) % rbtdb->node_lock_count; #endif + add_empty_wildcards(rbtdb, name); + if (dns_name_iswildcard(name)) { result = add_wildcard_magic(rbtdb, name); if (result != ISC_R_SUCCESS) { @@ -1436,6 +1466,56 @@ valid_glue(rbtdb_search_t *search, dns_name_t *name, rbtdb_rdatatype_t type, return (valid); } +static inline isc_boolean_t +activeempty(rbtdb_search_t *search, dns_rbtnodechain_t *chain, + dns_name_t *name) +{ + dns_fixedname_t fnext; + dns_fixedname_t forigin; + dns_name_t *next; + dns_name_t *origin; + dns_name_t prefix; + dns_rbtdb_t *rbtdb; + dns_rbtnode_t *node; + isc_result_t result; + isc_boolean_t answer = ISC_FALSE; + rdatasetheader_t *header; + + rbtdb = search->rbtdb; + + dns_name_init(&prefix, NULL); + dns_fixedname_init(&fnext); + next = dns_fixedname_name(&fnext); + dns_fixedname_init(&forigin); + origin = dns_fixedname_name(&forigin); + + result = dns_rbtnodechain_next(chain, NULL, NULL); + while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { + node = NULL; + result = dns_rbtnodechain_current(chain, &prefix, + origin, &node); + if (result != ISC_R_SUCCESS) + break; + LOCK(&(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(&(rbtdb->node_locks[node->locknum].lock)); + if (header != NULL) + break; + result = dns_rbtnodechain_next(chain, NULL, NULL); + } + if (result == ISC_R_SUCCESS) + result = dns_name_concatenate(&prefix, origin, next, NULL); + if (result == ISC_R_SUCCESS && dns_name_issubdomain(next, name)) + answer = ISC_TRUE; + return (answer); +} + static inline isc_boolean_t activeemtpynode(rbtdb_search_t *search, dns_name_t *qname, dns_name_t *wname) { dns_fixedname_t fnext; @@ -1452,6 +1532,7 @@ activeemtpynode(rbtdb_search_t *search, dns_name_t *qname, dns_name_t *wname) { dns_rbtnodechain_t chain; isc_boolean_t check_next = ISC_TRUE; isc_boolean_t check_prev = ISC_TRUE; + isc_boolean_t answer = ISC_FALSE; isc_result_t result; rdatasetheader_t *header; unsigned int n; @@ -1533,15 +1614,17 @@ activeemtpynode(rbtdb_search_t *search, dns_name_t *qname, dns_name_t *wname) { do { if ((check_prev && dns_name_issubdomain(prev, &rname)) || - (check_next && dns_name_issubdomain(next, &rname))) - return (ISC_TRUE); + (check_next && dns_name_issubdomain(next, &rname))) { + answer = ISC_TRUE; + break; + } /* * Remove the left hand label. */ n = dns_name_countlabels(&rname); dns_name_getlabelsequence(&rname, 1, n - 1, &rname); } while (!dns_name_equal(&rname, &tname)); - return (ISC_FALSE); + return (answer); } static inline isc_result_t @@ -1557,6 +1640,7 @@ find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep, dns_fixedname_t fwname; dns_rbtdb_t *rbtdb; isc_boolean_t done, wild, active; + dns_rbtnodechain_t wchain; /* * Caller must be holding the tree lock and MUST NOT be holding @@ -1630,8 +1714,9 @@ find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep, break; wnode = NULL; + dns_rbtnodechain_init(&wchain, NULL); result = dns_rbt_findnode(rbtdb->tree, wname, - NULL, &wnode, NULL, + NULL, &wnode, &wchain, DNS_RBTFIND_EMPTYDATA, NULL, NULL); if (result == ISC_R_SUCCESS) { @@ -1649,7 +1734,8 @@ find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep, break; } UNLOCK(&(rbtdb->node_locks[wnode->locknum].lock)); - if (header != NULL) { + if (header != NULL || + activeempty(search, &wchain, wname)) { if (activeemtpynode(search, qname, wname)) return (ISC_R_NOTFOUND); /* @@ -1846,9 +1932,6 @@ 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; @@ -1925,45 +2008,9 @@ 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); - } + active = activeempty(&search, &chain, name); + /* * If we're here, then the name does not exist, is not * beneath a zonecut, and there's no matching wildcard. @@ -2162,14 +2209,11 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * active rdatasets in the desired version. That means that * this node doesn't exist in the desired version, and that * we really have a partial match. - * - * If the node is the result of a wildcard match, then - * it must be active in the desired version, and hence - * empty_node should never be true. We INSIST upon it. */ - INSIST(!wild); - UNLOCK(&(search.rbtdb->node_locks[node->locknum].lock)); - goto partial_match; + if (!wild) { + UNLOCK(&(search.rbtdb->node_locks[node->locknum].lock)); + goto partial_match; + } } /* @@ -2198,8 +2242,18 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * The zone is secure but there's no NXT, * or the NXT has no signature! */ - result = DNS_R_BADDB; - goto node_exit; + if (!wild) { + result = DNS_R_BADDB; + goto node_exit; + } + + UNLOCK(&(search.rbtdb->node_locks[node->locknum].lock)); + result = find_closest_nxt(&search, nodep, foundname, + rdataset, sigrdataset, + search.rbtdb->secure); + if (result == ISC_R_SUCCESS) + result = DNS_R_EMPTYWILD; + goto tree_exit; } if ((search.options & DNS_DBFIND_FORCENXT) != 0 && nxtheader == NULL) @@ -4138,6 +4192,8 @@ loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) { !IS_CACHE(rbtdb) && !dns_name_equal(name, &rbtdb->common.origin)) return (DNS_R_NOTZONETOP); + add_empty_wildcards(rbtdb, name); + if (dns_name_iswildcard(name)) { /* * NS record owners cannot legally be wild cards. diff --git a/lib/dns/result.c b/lib/dns/result.c index 823e78ab2f..02055bd0bf 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.106 2003/01/18 03:18:30 marka Exp $ */ +/* $Id: result.c,v 1.107 2003/02/27 00:19:03 marka Exp $ */ #include @@ -140,7 +140,8 @@ static const char *text[DNS_R_NRESULTS] = { "unexpected OPCODE", /* 90 DNS_R_UNEXPECTEDOPCODE */ "chase DS servers", /* 91 DNS_R_CHASEDSSERVERS */ - "empty name" /* 92 DNS_R_EMPTYNAME */ + "empty name", /* 92 DNS_R_EMPTYNAME */ + "empty wild" /* 93 DNS_R_EMPTYWILD */ }; static const char *rcode_text[DNS_R_NRCODERESULTS] = { diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 6eebef377d..9649ae5fdd 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.111 2003/01/18 03:18:30 marka Exp $ */ +/* $Id: validator.c,v 1.112 2003/02/27 00:19:04 marka Exp $ */ #include @@ -441,7 +441,7 @@ nxtprovesnonexistence(dns_validator_t *val, dns_name_t *nxtname, isc_boolean_t isnxdomain; isc_result_t result; dns_namereln_t relation; - unsigned int labels, bits; + unsigned int olabels, nlabels, labels, bits; INSIST(DNS_MESSAGE_VALID(val->event->message)); @@ -459,7 +459,8 @@ nxtprovesnonexistence(dns_validator_t *val, dns_name_t *nxtname, dns_rdataset_current(nxtset, &rdata); validator_log(val, ISC_LOG_DEBUG(3), "looking for relevant nxt"); - order = dns_name_compare(val->event->name, nxtname); + relation = dns_name_fullcompare(val->event->name, nxtname, + &order, &olabels, &bits); if (order == 0) { /* * The names are the same. Look for the type present bit. @@ -495,16 +496,39 @@ nxtprovesnonexistence(dns_validator_t *val, dns_name_t *nxtname, 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) { + &order, &nlabels, + &bits); + if (order > 0 && relation == dns_namereln_subdomain) { + dns_rdata_freestruct(&nxt); validator_log(val, ISC_LOG_DEBUG(3), - "missing NXT record at name"); - return (ISC_FALSE); - } - validator_log(val, ISC_LOG_DEBUG(3), "nxt proves empty node, ok"); - return (ISC_TRUE); + return (ISC_TRUE); + } + /* + * Look for empty wildcard matches. + */ + labels = dns_name_countlabels(&nxt.next); + if (nlabels >= olabels && nlabels + 1 < labels) { + dns_name_t wild; + dns_name_init(&wild, NULL); + dns_name_getlabelsequence(&nxt.next, + labels - 1 - nlabels, + nlabels + 1, + &wild); + if (dns_name_iswildcard(&wild)) { + dns_rdata_freestruct(&nxt); + validator_log(val, ISC_LOG_DEBUG(3), + "nxt proves empty wildcard, ok"); + return (ISC_TRUE); + } + } + /* + * We are not a empty name. + */ + dns_rdata_freestruct(&nxt); + validator_log(val, ISC_LOG_DEBUG(3), + "missing NXT record at name"); + return (ISC_FALSE); } if (dns_name_issubdomain(val->event->name, nxtname) && dns_nxt_typepresent(&rdata, dns_rdatatype_ns) && @@ -524,7 +548,7 @@ nxtprovesnonexistence(dns_validator_t *val, dns_name_t *nxtname, return (ISC_FALSE); dns_rdata_reset(&rdata); relation = dns_name_fullcompare(&nxt.next, val->event->name, - &order, &labels, &bits); + &order, &nlabels, &bits); if (order <= 0) { /* * The NXT next name is less than the nonexistent