diff options
Diffstat (limited to 'lib/dns/rbtdb.c')
-rw-r--r-- | lib/dns/rbtdb.c | 378 |
1 files changed, 323 insertions, 55 deletions
diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 79883e3a..67d04f04 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999-2002 Internet Software Consortium. + * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbtdb.c,v 1.168.2.3 2002/08/05 06:57:11 marka Exp $ */ +/* $Id: rbtdb.c,v 1.168.2.11 2003/07/22 04:03:43 marka Exp $ */ /* * Principal Author: Bob Halley @@ -84,7 +84,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 { @@ -108,6 +108,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 @@ -127,6 +128,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. */ @@ -792,10 +795,10 @@ no_references(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, * we only do a trylock. */ if (lock == isc_rwlocktype_read) - RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - - result = isc_rwlock_trylock(&rbtdb->tree_lock, - isc_rwlocktype_write); + result = isc_rwlock_tryupgrade(&rbtdb->tree_lock); + else + result = isc_rwlock_trylock(&rbtdb->tree_lock, + isc_rwlocktype_write); RUNTIME_CHECK(result == ISC_R_SUCCESS || result == ISC_R_LOCKBUSY); @@ -826,12 +829,13 @@ no_references(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, /* * Relock a read lock, or unlock the write lock if no lock was held. */ - if (lock != isc_rwlocktype_write) + if (lock == isc_rwlocktype_none) if (write_locked) RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); if (lock == isc_rwlocktype_read) - RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + if (write_locked) + isc_rwlock_downgrade(&rbtdb->tree_lock); } static inline void @@ -1064,6 +1068,34 @@ add_wildcard_magic(dns_rbtdb_t *rbtdb, dns_name_t *name) { } 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) { @@ -1103,6 +1135,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) { @@ -1287,6 +1321,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); @@ -1295,7 +1331,7 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, /* * Reset iterator state. */ - rdataset->private4 = NULL; + rdataset->privateuint4 = 0; rdataset->private5 = NULL; } @@ -1411,8 +1447,171 @@ 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; + dns_fixedname_t forigin; + dns_fixedname_t fprev; + dns_name_t *next; + dns_name_t *origin; + dns_name_t *prev; + dns_name_t name; + dns_name_t rname; + dns_name_t tname; + dns_rbtdb_t *rbtdb; + dns_rbtnode_t *node; + 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; + + rbtdb = search->rbtdb; + + dns_name_init(&name, NULL); + dns_name_init(&tname, NULL); + dns_name_init(&rname, NULL); + dns_fixedname_init(&fnext); + next = dns_fixedname_name(&fnext); + dns_fixedname_init(&fprev); + prev = dns_fixedname_name(&fprev); + dns_fixedname_init(&forigin); + origin = dns_fixedname_name(&forigin); + + /* + * Find if qname is at or below a empty node. + * Use our own copy of the chain. + */ + + chain = search->chain; + do { + node = NULL; + result = dns_rbtnodechain_current(&chain, &name, + 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_prev(&chain, NULL, NULL); + } while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN); + if (result == ISC_R_SUCCESS) + result = dns_name_concatenate(&name, origin, prev, NULL); + if (result != ISC_R_SUCCESS) + check_prev = ISC_FALSE; + + result = dns_rbtnodechain_next(&chain, NULL, NULL); + while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { + node = NULL; + result = dns_rbtnodechain_current(&chain, &name, + 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(&name, origin, next, NULL); + if (result != ISC_R_SUCCESS) + check_next = ISC_FALSE; + + dns_name_clone(qname, &rname); + + /* + * Remove the wildcard label to find the terminal name. + */ + n = dns_name_countlabels(wname); + dns_name_getlabelsequence(wname, 1, n - 1, &tname); + + do { + if ((check_prev && dns_name_issubdomain(prev, &rname)) || + (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 (answer); +} + static inline isc_result_t -find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep) { +find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep, + dns_name_t *qname) +{ unsigned int i, j; dns_rbtnode_t *node, *level_node, *wnode; rdatasetheader_t *header; @@ -1422,6 +1621,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 @@ -1495,8 +1695,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) { @@ -1514,7 +1715,10 @@ 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); /* * The wildcard node is active! * @@ -1704,6 +1908,9 @@ 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; + isc_boolean_t active; + dns_rbtnodechain_t chain; + search.rbtdb = (dns_rbtdb_t *)db; @@ -1765,7 +1972,7 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * we must see if there's a matching wildcard active * in the current version. */ - result = find_wildcard(&search, &node); + result = find_wildcard(&search, &node, name); if (result == ISC_R_SUCCESS) { result = dns_name_copy(name, foundname, NULL); if (result != ISC_R_SUCCESS) @@ -1777,6 +1984,9 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, goto tree_exit; } + chain = search.chain; + 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. @@ -1785,9 +1995,10 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, result = find_closest_nxt(&search, nodep, foundname, rdataset, sigrdataset); 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; @@ -1970,14 +2181,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; + } } /* @@ -1995,30 +2203,37 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, result = setup_delegation(&search, nodep, foundname, rdataset, sigrdataset); goto tree_exit; - } else { + } + /* + * The desired type doesn't exist. + */ + result = DNS_R_NXRRSET; + if (search.rbtdb->secure && + (nxtheader == NULL || nxtsig == NULL)) { /* - * The desired type doesn't exist. + * The zone is secure but there's no NXT, + * or the NXT has no signature! */ - result = DNS_R_NXRRSET; - if (search.rbtdb->secure && - (nxtheader == NULL || nxtsig == NULL)) { - /* - * The zone is secure but there's no NXT, - * or the NXT has no signature! - */ + if (!wild) { result = DNS_R_BADDB; goto node_exit; } - if (nodep != NULL) { - new_reference(search.rbtdb, node); - *nodep = node; - } - if (search.rbtdb->secure) { - bind_rdataset(search.rbtdb, node, nxtheader, - 0, rdataset); - bind_rdataset(search.rbtdb, node, nxtsig, - 0, sigrdataset); - } + UNLOCK(&(search.rbtdb->node_locks[node->locknum].lock)); + result = find_closest_nxt(&search, nodep, foundname, + rdataset, sigrdataset); + if (result == ISC_R_SUCCESS) + result = DNS_R_EMPTYWILD; + goto tree_exit; + } + if (nodep != NULL) { + new_reference(search.rbtdb, node); + *nodep = node; + } + if (search.rbtdb->secure) { + bind_rdataset(search.rbtdb, node, nxtheader, + 0, rdataset); + bind_rdataset(search.rbtdb, node, nxtsig, + 0, sigrdataset); } goto node_exit; } @@ -2510,7 +2725,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. @@ -2598,7 +2813,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; @@ -2618,7 +2833,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) @@ -3140,7 +3356,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) @@ -3163,7 +3379,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; @@ -3391,8 +3607,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) && @@ -3516,6 +3731,52 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, return (result); } } + /* + * Don't replace existing NS, A and AAAA RRsets + * in the cache if they are already exist. This + * prevents named being locked to old servers. + */ + if (IS_CACHE(rbtdb) && header->ttl > now && + header->type == dns_rdatatype_ns && + !header_nx && !newheader_nx && + header->trust == newheader->trust && + dns_rdataslab_equalx((unsigned char *)header, + (unsigned char *)newheader, + (unsigned int)(sizeof(*newheader)), + rbtdb->common.rdclass, + (dns_rdatatype_t)header->type)) { + /* + * Honour the new ttl if it is less than the + * older one. + */ + if (header->ttl > newheader->ttl) + header->ttl = newheader->ttl; + free_rdataset(rbtdb->common.mctx, newheader); + if (addedrdataset != NULL) + bind_rdataset(rbtdb, rbtnode, header, now, + addedrdataset); + return (ISC_R_SUCCESS); + } + if (IS_CACHE(rbtdb) && header->ttl > now && + (header->type == dns_rdatatype_a || + header->type == dns_rdatatype_aaaa) && + !header_nx && !newheader_nx && + header->trust == newheader->trust && + dns_rdataslab_equal((unsigned char *)header, + (unsigned char *)newheader, + (unsigned int)(sizeof(*newheader)))) { + /* + * Honour the new ttl if it is less than the + * older one. + */ + if (header->ttl > newheader->ttl) + header->ttl = newheader->ttl; + free_rdataset(rbtdb->common.mctx, newheader); + if (addedrdataset != NULL) + bind_rdataset(rbtdb, rbtnode, header, now, + addedrdataset); + return (ISC_R_SUCCESS); + } INSIST(rbtversion == NULL || rbtversion->serial >= topheader->serial); if (topheader_prev != NULL) @@ -3653,6 +3914,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; } /* @@ -3881,6 +4144,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. @@ -4365,12 +4630,12 @@ rdataset_first(dns_rdataset_t *rdataset) { } raw += 2; /* - * The private4 field is the number of rdata beyond the cursor + * The privateuint4 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->privateuint4 = count; rdataset->private5 = raw; return (ISC_R_SUCCESS); @@ -4382,11 +4647,11 @@ rdataset_next(dns_rdataset_t *rdataset) { unsigned int length; unsigned char *raw; - count = (unsigned int)rdataset->private4; + count = rdataset->privateuint4; if (count == 0) return (ISC_R_NOMORE); count--; - rdataset->private4 = (void *)count; + rdataset->privateuint4 = count; raw = rdataset->private5; length = raw[0] * 256 + raw[1]; raw += length + 2; @@ -4420,7 +4685,7 @@ rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) { /* * Reset iterator state. */ - target->private4 = NULL; + target->privateuint4 = 0; target->private5 = NULL; } @@ -4702,6 +4967,7 @@ static void dbiterator_destroy(dns_dbiterator_t **iteratorp) { rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)(*iteratorp); dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; + dns_db_t *db = NULL; if (rbtdbiter->tree_locked == isc_rwlocktype_read) { RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); @@ -4713,10 +4979,12 @@ dbiterator_destroy(dns_dbiterator_t **iteratorp) { flush_deletions(rbtdbiter); + dns_db_attach(rbtdbiter->common.db, &db); dns_db_detach(&rbtdbiter->common.db); dns_rbtnodechain_reset(&rbtdbiter->chain); - isc_mem_put(rbtdb->common.mctx, rbtdbiter, sizeof *rbtdbiter); + isc_mem_put(db->mctx, rbtdbiter, sizeof(*rbtdbiter)); + dns_db_detach(&db); *iteratorp = NULL; } |