diff options
Diffstat (limited to 'contrib/bind9/lib/dns/rbtdb.c')
-rw-r--r-- | contrib/bind9/lib/dns/rbtdb.c | 826 |
1 files changed, 692 insertions, 134 deletions
diff --git a/contrib/bind9/lib/dns/rbtdb.c b/contrib/bind9/lib/dns/rbtdb.c index 87b70e376e05..d4415d8906d6 100644 --- a/contrib/bind9/lib/dns/rbtdb.c +++ b/contrib/bind9/lib/dns/rbtdb.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2010 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbtdb.c,v 1.270.12.26.4.1 2011-06-21 20:13:23 each Exp $ */ +/* $Id: rbtdb.c,v 1.310.8.1.2.1 2011-06-21 20:15:48 each Exp $ */ /*! \file */ @@ -53,6 +53,7 @@ #include <dns/nsec.h> #include <dns/nsec3.h> #include <dns/rbt.h> +#include <dns/rpz.h> #include <dns/rdata.h> #include <dns/rdataset.h> #include <dns/rdatasetiter.h> @@ -441,7 +442,9 @@ typedef struct { /* Locked by tree_lock. */ dns_rbt_t * tree; + dns_rbt_t * nsec; dns_rbt_t * nsec3; + dns_rpz_cidr_t * rpz_cidr; /* Unlocked */ unsigned int quantum; @@ -621,8 +624,9 @@ typedef struct rbtdb_dbiterator { static void free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event); static void overmem(dns_db_t *db, isc_boolean_t overmem); -static void setnsec3parameters(dns_db_t *db, rbtdb_version_t *version, - isc_boolean_t *nsec3createflag); +#ifdef BIND9 +static void setnsec3parameters(dns_db_t *db, rbtdb_version_t *version); +#endif /*% * 'init_count' is used to initialize 'newheader->count' which inturn @@ -830,6 +834,7 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { isc_ondestroy_t ondest; isc_result_t result; char buf[DNS_NAME_FORMATSIZE]; + dns_rbt_t **treep; isc_time_t start; if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in) @@ -866,33 +871,26 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { if (event == NULL) rbtdb->quantum = (rbtdb->task != NULL) ? 100 : 0; - again: - if (rbtdb->tree != NULL) { - isc_time_now(&start); - result = dns_rbt_destroy2(&rbtdb->tree, rbtdb->quantum); - if (result == ISC_R_QUOTA) { - INSIST(rbtdb->task != NULL); - if (rbtdb->quantum != 0) - rbtdb->quantum = adjust_quantum(rbtdb->quantum, - &start); - if (event == NULL) - event = isc_event_allocate(rbtdb->common.mctx, - NULL, - DNS_EVENT_FREESTORAGE, - free_rbtdb_callback, - rbtdb, - sizeof(isc_event_t)); - if (event == NULL) - goto again; - isc_task_send(rbtdb->task, &event); - return; + + for (;;) { + /* + * pick the next tree to (start to) destroy + */ + treep = &rbtdb->tree; + if (*treep == NULL) { + treep = &rbtdb->nsec; + if (*treep == NULL) { + treep = &rbtdb->nsec3; + /* + * we're finished after clear cutting + */ + if (*treep == NULL) + break; + } } - INSIST(result == ISC_R_SUCCESS && rbtdb->tree == NULL); - } - if (rbtdb->nsec3 != NULL) { isc_time_now(&start); - result = dns_rbt_destroy2(&rbtdb->nsec3, rbtdb->quantum); + result = dns_rbt_destroy2(treep, rbtdb->quantum); if (result == ISC_R_QUOTA) { INSIST(rbtdb->task != NULL); if (rbtdb->quantum != 0) @@ -906,11 +904,11 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { rbtdb, sizeof(isc_event_t)); if (event == NULL) - goto again; + continue; isc_task_send(rbtdb->task, &event); return; } - INSIST(result == ISC_R_SUCCESS && rbtdb->nsec3 == NULL); + INSIST(result == ISC_R_SUCCESS && *treep == NULL); } if (event != NULL) @@ -965,6 +963,11 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { if (rbtdb->rrsetstats != NULL) dns_stats_detach(&rbtdb->rrsetstats); +#ifdef BIND9 + if (rbtdb->rpz_cidr != NULL) + dns_rpz_cidr_free(&rbtdb->rpz_cidr); +#endif + isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks, rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t)); isc_rwlock_destroy(&rbtdb->tree_lock); @@ -1488,6 +1491,82 @@ clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, node->dirty = 0; } +static void +delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) +{ + dns_rbtnode_t *nsecnode; + dns_fixedname_t fname; + dns_name_t *name; + isc_result_t result = ISC_R_UNEXPECTED; + + INSIST(!ISC_LINK_LINKED(node, deadlink)); + + switch (node->nsec) { + case DNS_RBT_NSEC_NORMAL: +#ifdef BIND9 + if (rbtdb->rpz_cidr != NULL) { + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_rbt_fullnamefromnode(node, name); + dns_rpz_cidr_deleteip(rbtdb->rpz_cidr, name); + } +#endif + result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE); + break; + case DNS_RBT_NSEC_HAS_NSEC: + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_rbt_fullnamefromnode(node, name); + /* + * Delete the corresponding node from the auxiliary NSEC + * tree before deleting from the main tree. + */ + nsecnode = NULL; + result = dns_rbt_findnode(rbtdb->nsec, name, NULL, &nsecnode, + NULL, DNS_RBTFIND_EMPTYDATA, + NULL, NULL); + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, + "delete_node: " + "dns_rbt_findnode(nsec): %s", + isc_result_totext(result)); + } else { + result = dns_rbt_deletenode(rbtdb->nsec, nsecnode, + ISC_FALSE); + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, + ISC_LOG_WARNING, + "delete_nsecnode(): " + "dns_rbt_deletenode(nsecnode): %s", + isc_result_totext(result)); + } + } + result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE); +#ifdef BIND9 + dns_rpz_cidr_deleteip(rbtdb->rpz_cidr, name); +#endif + break; + case DNS_RBT_NSEC_NSEC: + result = dns_rbt_deletenode(rbtdb->nsec, node, ISC_FALSE); + break; + case DNS_RBT_NSEC_NSEC3: + result = dns_rbt_deletenode(rbtdb->nsec3, node, ISC_FALSE); + break; + } + if (result != ISC_R_SUCCESS) { + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, + ISC_LOG_WARNING, + "delete_nsecnode(): " + "dns_rbt_deletenode: %s", + isc_result_totext(result)); + } +} + /*% * Clean up dead nodes. These are nodes which have no references, and * have no data. They are dead but we could not or chose not to delete @@ -1499,7 +1578,6 @@ clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, static void cleanup_dead_nodes(dns_rbtdb_t *rbtdb, int bucketnum) { dns_rbtnode_t *node; - isc_result_t result; int count = 10; /* XXXJT: should be adjustable */ node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]); @@ -1513,19 +1591,8 @@ cleanup_dead_nodes(dns_rbtdb_t *rbtdb, int bucketnum) { INSIST(dns_rbtnode_refcurrent(node) == 0 && node->data == NULL); - INSIST(!ISC_LINK_LINKED(node, deadlink)); - if (node->nsec3) - result = dns_rbt_deletenode(rbtdb->nsec3, node, - ISC_FALSE); - else - result = dns_rbt_deletenode(rbtdb->tree, node, - ISC_FALSE); - if (result != ISC_R_SUCCESS) - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, - "cleanup_dead_nodes: " - "dns_rbt_deletenode: %s", - isc_result_totext(result)); + delete_node(rbtdb, node); + node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]); count--; } @@ -1774,22 +1841,7 @@ decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, sizeof(printname))); } - INSIST(!ISC_LINK_LINKED(node, deadlink)); - if (node->nsec3) - result = dns_rbt_deletenode(rbtdb->nsec3, node, - ISC_FALSE); - else - result = dns_rbt_deletenode(rbtdb->tree, node, - ISC_FALSE); - if (result != ISC_R_SUCCESS) { - isc_log_write(dns_lctx, - DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, - ISC_LOG_WARNING, - "decrement_reference: " - "dns_rbt_deletenode: %s", - isc_result_totext(result)); - } + delete_node(rbtdb, node); } } else if (dns_rbtnode_refcurrent(node) == 0) { INSIST(!ISC_LINK_LINKED(node, deadlink)); @@ -1925,13 +1977,17 @@ cleanup_nondirty(rbtdb_version_t *version, rbtdb_changedlist_t *cleanup_list) { static void iszonesecure(dns_db_t *db, rbtdb_version_t *version, dns_dbnode_t *origin) { +#ifndef BIND9 + UNUSED(db); + UNUSED(version); + UNUSED(origin); + + return; +#else dns_rdataset_t keyset; dns_rdataset_t nsecset, signsecset; - dns_rdata_t rdata = DNS_RDATA_INIT; isc_boolean_t haszonekey = ISC_FALSE; isc_boolean_t hasnsec = ISC_FALSE; - isc_boolean_t hasoptbit = ISC_FALSE; - isc_boolean_t nsec3createflag = ISC_FALSE; isc_result_t result; dns_rdataset_init(&keyset); @@ -1963,41 +2019,30 @@ iszonesecure(dns_db_t *db, rbtdb_version_t *version, dns_dbnode_t *origin) { if (result == ISC_R_SUCCESS) { if (dns_rdataset_isassociated(&signsecset)) { hasnsec = ISC_TRUE; - result = dns_rdataset_first(&nsecset); - if (result == ISC_R_SUCCESS) { - dns_rdataset_current(&nsecset, &rdata); - hasoptbit = dns_nsec_typepresent(&rdata, - dns_rdatatype_opt); - } dns_rdataset_disassociate(&signsecset); } dns_rdataset_disassociate(&nsecset); } - setnsec3parameters(db, version, &nsec3createflag); + setnsec3parameters(db, version); /* * Do we have a valid NSEC/NSEC3 chain? */ - if (version->havensec3 || (hasnsec && !hasoptbit)) + if (version->havensec3 || hasnsec) version->secure = dns_db_secure; - /* - * Do we have a NSEC/NSEC3 chain under creation? - */ - else if (hasoptbit || nsec3createflag) - version->secure = dns_db_partial; else version->secure = dns_db_insecure; +#endif } /*%< * Walk the origin node looking for NSEC3PARAM records. * Cache the nsec3 parameters. */ +#ifdef BIND9 static void -setnsec3parameters(dns_db_t *db, rbtdb_version_t *version, - isc_boolean_t *nsec3createflag) -{ +setnsec3parameters(dns_db_t *db, rbtdb_version_t *version) { dns_rbtnode_t *node; dns_rdata_nsec3param_t nsec3param; dns_rdata_t rdata = DNS_RDATA_INIT; @@ -2028,7 +2073,7 @@ setnsec3parameters(dns_db_t *db, rbtdb_version_t *version, } while (header != NULL); if (header != NULL && - header->type == dns_rdatatype_nsec3param) { + (header->type == dns_rdatatype_nsec3param)) { /* * Find A NSEC3PARAM with a supported algorithm. */ @@ -2063,17 +2108,8 @@ setnsec3parameters(dns_db_t *db, rbtdb_version_t *version, !dns_nsec3_supportedhash(nsec3param.hash)) continue; -#ifdef RFC5155_STRICT if (nsec3param.flags != 0) continue; -#else - if ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) - != 0) - *nsec3createflag = ISC_TRUE; - if ((nsec3param.flags & ~DNS_NSEC3FLAG_OPTOUT) - != 0) - continue; -#endif memcpy(version->salt, nsec3param.salt, nsec3param.salt_length); @@ -2096,6 +2132,7 @@ setnsec3parameters(dns_db_t *db, rbtdb_version_t *version, isc_rwlocktype_read); RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); } +#endif static void cleanup_dead_nodes_callback(isc_task_t *task, isc_event_t *event) { @@ -2415,7 +2452,8 @@ add_wildcard_magic(dns_rbtdb_t *rbtdb, dns_name_t *name) { result = dns_rbt_addnode(rbtdb->tree, &foundname, &node); if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) return (result); - node->nsec3 = 0; + if (result == ISC_R_SUCCESS) + node->nsec = DNS_RBT_NSEC_NORMAL; node->find_callback = 1; node->wild = 1; return (ISC_R_SUCCESS); @@ -2443,7 +2481,8 @@ add_empty_wildcards(dns_rbtdb_t *rbtdb, dns_name_t *name) { &node); if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) return (result); - node->nsec3 = 0; + if (result == ISC_R_SUCCESS) + node->nsec = DNS_RBT_NSEC_NORMAL; } i++; } @@ -2482,6 +2521,17 @@ findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create, node = NULL; result = dns_rbt_addnode(rbtdb->tree, name, &node); if (result == ISC_R_SUCCESS) { +#ifdef BIND9 + if (rbtdb->rpz_cidr != NULL) { + dns_fixedname_t fnamef; + dns_name_t *fname; + + dns_fixedname_init(&fnamef); + fname = dns_fixedname_name(&fnamef); + dns_rbt_fullnamefromnode(node, fname); + dns_rpz_cidr_addip(rbtdb->rpz_cidr, fname); + } +#endif dns_rbt_namefromnode(node, &nodename); #ifdef DNS_RBT_USEHASH node->locknum = node->hashval % rbtdb->node_lock_count; @@ -2489,7 +2539,6 @@ 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 - node->nsec3 = 0; add_empty_wildcards(rbtdb, name); if (dns_name_iswildcard(name)) { @@ -2551,13 +2600,14 @@ findnsec3node(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 - node->nsec3 = 1U; + node->nsec = DNS_RBT_NSEC_NSEC3; } else if (result != ISC_R_EXISTS) { RWUNLOCK(&rbtdb->tree_lock, locktype); return (result); } - } else - INSIST(node->nsec3); + } else { + INSIST(node->nsec == DNS_RBT_NSEC_NSEC3); + } NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock); new_reference(rbtdb, node); NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock); @@ -3268,13 +3318,125 @@ matchparams(rdatasetheader_t *header, rbtdb_search_t *search) * Find node of the NSEC/NSEC3 record that is 'name'. */ static inline isc_result_t +previous_closest_nsec(dns_rdatatype_t type, rbtdb_search_t *search, + dns_name_t *name, dns_name_t *origin, + dns_rbtnode_t **nodep, dns_rbtnodechain_t *nsecchain, + isc_boolean_t *firstp) +{ + dns_fixedname_t ftarget; + dns_name_t *target; + dns_rbtnode_t *nsecnode; + isc_result_t result; + + REQUIRE(nodep != NULL && *nodep == NULL); + + if (type == dns_rdatatype_nsec3) { + result = dns_rbtnodechain_prev(&search->chain, NULL, NULL); + if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) + return (result); + result = dns_rbtnodechain_current(&search->chain, name, origin, + nodep); + return (result); + } + + dns_fixedname_init(&ftarget); + target = dns_fixedname_name(&ftarget); + + for (;;) { + if (*firstp) { + /* + * Construct the name of the second node to check. + * It is the first node sought in the NSEC tree. + */ + *firstp = ISC_FALSE; + dns_rbtnodechain_init(nsecchain, NULL); + result = dns_name_concatenate(name, origin, + target, NULL); + if (result != ISC_R_SUCCESS) + return (result); + nsecnode = NULL; + result = dns_rbt_findnode(search->rbtdb->nsec, + target, NULL, + &nsecnode, nsecchain, + DNS_RBTFIND_NOOPTIONS, + NULL, NULL); + if (result == ISC_R_SUCCESS) { + /* + * Since this was the first loop, finding the + * name in the NSEC tree implies that the first + * node checked in the main tree had an + * unacceptable NSEC record. + * Try the previous node in the NSEC tree. + */ + result = dns_rbtnodechain_prev(nsecchain, + name, origin); + if (result == DNS_R_NEWORIGIN) + result = ISC_R_SUCCESS; + } else if (result == ISC_R_NOTFOUND || + result == DNS_R_PARTIALMATCH) { + result = dns_rbtnodechain_current(nsecchain, + name, origin, NULL); + if (result == ISC_R_NOTFOUND) + result = ISC_R_NOMORE; + } + } else { + /* + * This is a second or later trip through the auxiliary + * tree for the name of a third or earlier NSEC node in + * the main tree. Previous trips through the NSEC tree + * must have found nodes in the main tree with NSEC + * records. Perhaps they lacked signature records. + */ + result = dns_rbtnodechain_prev(nsecchain, name, origin); + if (result == DNS_R_NEWORIGIN) + result = ISC_R_SUCCESS; + } + if (result != ISC_R_SUCCESS) + return (result); + + /* + * Construct the name to seek in the main tree. + */ + result = dns_name_concatenate(name, origin, target, NULL); + if (result != ISC_R_SUCCESS) + return (result); + + *nodep = NULL; + result = dns_rbt_findnode(search->rbtdb->tree, target, NULL, + nodep, &search->chain, + DNS_RBTFIND_NOOPTIONS, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (result); + + /* + * There should always be a node in the main tree with the + * same name as the node in the auxiliary NSEC tree, except for + * nodes in the auxiliary tree that are awaiting deletion. + */ + if (result != DNS_R_PARTIALMATCH && result != ISC_R_NOTFOUND) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, ISC_LOG_ERROR, + "previous_closest_nsec(): %s", + isc_result_totext(result)); + return (DNS_R_BADDB); + } + } +} + +/* + * Find the NSEC/NSEC3 which is or before the current point on the + * search chain. For NSEC3 records only NSEC3 records that match the + * current NSEC3PARAM record are considered. + */ +static inline isc_result_t find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, dns_rbt_t *tree, dns_db_secure_t secure) { - dns_rbtnode_t *node; + dns_rbtnode_t *node, *prevnode; rdatasetheader_t *header, *header_next, *found, *foundsig; + dns_rbtnodechain_t nsecchain; isc_boolean_t empty_node; isc_result_t result; dns_fixedname_t fname, forigin; @@ -3282,6 +3444,7 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep, dns_rdatatype_t type; rbtdb_rdatatype_t sigtype; isc_boolean_t wraps; + isc_boolean_t first = ISC_TRUE; isc_boolean_t need_sig = ISC_TF(secure == dns_db_secure); if (tree == search->rbtdb->nsec3) { @@ -3294,17 +3457,21 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep, wraps = ISC_FALSE; } + /* + * Use the auxiliary tree only starting with the second node in the + * hope that the original node will be right much of the time. + */ + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_fixedname_init(&forigin); + origin = dns_fixedname_name(&forigin); again: + node = NULL; + prevnode = NULL; + result = dns_rbtnodechain_current(&search->chain, name, origin, &node); + if (result != ISC_R_SUCCESS) + return (result); do { - node = NULL; - dns_fixedname_init(&fname); - name = dns_fixedname_name(&fname); - dns_fixedname_init(&forigin); - origin = dns_fixedname_name(&forigin); - result = dns_rbtnodechain_current(&search->chain, name, - origin, &node); - if (result != ISC_R_SUCCESS) - return (result); NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock), isc_rwlocktype_read); found = NULL; @@ -3354,11 +3521,12 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep, empty_node = ISC_TRUE; found = NULL; foundsig = NULL; - result = dns_rbtnodechain_prev(&search->chain, - NULL, NULL); + result = previous_closest_nsec(type, search, + name, origin, + &prevnode, NULL, + NULL); } else if (found != NULL && - (foundsig != NULL || !need_sig)) - { + (foundsig != NULL || !need_sig)) { /* * We've found the right NSEC/NSEC3 record. * @@ -3395,8 +3563,11 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep, * node as if it were empty and keep looking. */ empty_node = ISC_TRUE; - result = dns_rbtnodechain_prev(&search->chain, - NULL, NULL); + result = previous_closest_nsec(type, search, + name, origin, + &prevnode, + &nsecchain, + &first); } else { /* * We found an active node, but either the @@ -3410,13 +3581,19 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep, * This node isn't active. We've got to keep * looking. */ - result = dns_rbtnodechain_prev(&search->chain, NULL, - NULL); + result = previous_closest_nsec(type, search, + name, origin, &prevnode, + &nsecchain, &first); } NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), isc_rwlocktype_read); + node = prevnode; + prevnode = NULL; } while (empty_node && result == ISC_R_SUCCESS); + if (!first) + dns_rbtnodechain_invalidate(&nsecchain); + if (result == ISC_R_NOMORE && wraps) { result = dns_rbtnodechain_last(&search->chain, tree, NULL, NULL); @@ -3960,6 +4137,7 @@ zone_findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options, FATAL_ERROR(__FILE__, __LINE__, "zone_findzonecut() called!"); + /* NOTREACHED */ return (ISC_R_NOTIMPLEMENTED); } @@ -4371,6 +4549,200 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep, return (result); } +/* + * Mark a database for response policy rewriting. + */ +#ifdef BIND9 +static void +get_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st) +{ + dns_rbtdb_t *rbtdb; + + rbtdb = (dns_rbtdb_t *)db; + REQUIRE(VALID_RBTDB(rbtdb)); + RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + dns_rpz_enabled(rbtdb->rpz_cidr, st); + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); +} + +/* + * Search the CDIR block tree of a response policy tree of trees for all of + * the IP addresses in an A or AAAA rdataset. + * Among the policies for all IPv4 and IPv6 addresses for a name, choose + * the longest prefix. Among those with the longest prefix, the first + * configured policy. Among answers for with the longest prefixes for + * two or more IP addresses in the A and AAAA rdatasets the lexically + * smallest address. + */ +static isc_result_t +rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, + dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, + dns_rdataset_t *ardataset, dns_rpz_st_t *st) +{ + dns_rbtdb_t *rbtdb; + struct in_addr ina; + struct in6_addr in6a; + isc_netaddr_t netaddr; + dns_fixedname_t selfnamef, qnamef; + dns_name_t *selfname, *qname; + dns_rbtnode_t *node; + dns_rdataset_t zrdataset; + dns_rpz_cidr_bits_t prefix; + isc_result_t result; + dns_rpz_policy_t rpz_policy; + dns_ttl_t ttl; + + rbtdb = (dns_rbtdb_t *)db; + REQUIRE(VALID_RBTDB(rbtdb)); + RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + + if (rbtdb->rpz_cidr == NULL) { + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + dns_db_detach(&db); + dns_zone_detach(&zone); + return (ISC_R_UNEXPECTED); + } + + dns_fixedname_init(&selfnamef); + dns_fixedname_init(&qnamef); + selfname = dns_fixedname_name(&selfnamef); + qname = dns_fixedname_name(&qnamef); + + for (result = dns_rdataset_first(ardataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(ardataset)) { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdataset_current(ardataset, &rdata); + switch (rdata.type) { + case dns_rdatatype_a: + INSIST(rdata.length == 4); + memcpy(&ina.s_addr, rdata.data, 4); + isc_netaddr_fromin(&netaddr, &ina); + break; + case dns_rdatatype_aaaa: + INSIST(rdata.length == 16); + memcpy(in6a.s6_addr, rdata.data, 16); + isc_netaddr_fromin6(&netaddr, &in6a); + break; + default: + continue; + } + + result = dns_rpz_cidr_find(rbtdb->rpz_cidr, &netaddr, rpz_type, + selfname, qname, &prefix); + if (result != ISC_R_SUCCESS) + continue; + + /* + * Choose the policy with the longest matching prefix. + * Between policies with the same prefix, choose the first + * configured. + */ + if (st->m.policy != DNS_RPZ_POLICY_MISS) { + if (prefix < st->m.prefix) + continue; + if (prefix == st->m.prefix && + rpz->num > st->m.rpz->num) + continue; + } + + /* + * We have rpz_st an entry with a prefix at least as long as + * the prefix of the entry we had before. Find the node + * corresponding to CDIR tree entry. + */ + node = NULL; + result = dns_rbt_findnode(rbtdb->tree, qname, NULL, + &node, NULL, 0, NULL, NULL); + if (result != ISC_R_SUCCESS) { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(qname, namebuf, sizeof(namebuf)); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, DNS_RPZ_ERROR_LEVEL, + "rpz_findips findnode(%s): %s", + namebuf, isc_result_totext(result)); + continue; + } + /* + * First look for a simple rewrite of the IP address. + * If that fails, look for a CNAME. If we cannot find + * a CNAME or the CNAME is neither of the special forms + * "*" or ".", treat it like a real CNAME. + */ + dns_rdataset_init(&zrdataset); + result = dns_db_findrdataset(db, node, version, ardataset->type, + 0, 0, &zrdataset, NULL); + if (result != ISC_R_SUCCESS) + result = dns_db_findrdataset(db, node, version, + dns_rdatatype_cname, + 0, 0, &zrdataset, NULL); + if (result == ISC_R_SUCCESS) { + if (zrdataset.type != dns_rdatatype_cname) { + rpz_policy = DNS_RPZ_POLICY_RECORD; + } else { + rpz_policy = dns_rpz_decode_cname(&zrdataset, + selfname); + if (rpz_policy == DNS_RPZ_POLICY_RECORD) + result = DNS_R_CNAME; + } + ttl = zrdataset.ttl; + } else { + rpz_policy = DNS_RPZ_POLICY_RECORD; + result = DNS_R_NXRRSET; + ttl = DNS_RPZ_TTL_DEFAULT; + } + + /* + * Use an overriding action specified in the configuration file + */ + if (rpz->policy != DNS_RPZ_POLICY_GIVEN && + rpz_policy != DNS_RPZ_POLICY_NO_OP) + rpz_policy = rpz->policy; + + /* + * We know the new prefix is at least as long as the current. + * Prefer the new answer if the new prefix is longer. + * Prefer the zone configured first if the prefixes are equal. + * With two actions from the same zone, prefer the action + * on the "smallest" name. + */ + if (st->m.policy == DNS_RPZ_POLICY_MISS || + prefix > st->m.prefix || + rpz->num <= st->m.rpz->num || + 0 > dns_name_compare(qname, st->qname)) { + if (dns_rdataset_isassociated(st->m.rdataset)) + dns_rdataset_disassociate(st->m.rdataset); + if (st->m.node != NULL) + dns_db_detachnode(st->m.db, &st->m.node); + if (st->m.db != NULL) + dns_db_detach(&st->m.db); + if (st->m.zone != NULL) + dns_zone_detach(&st->m.zone); + st->m.rpz = rpz; + st->m.type = rpz_type; + st->m.prefix = prefix; + st->m.policy = rpz_policy; + st->m.ttl = ttl; + st->m.result = result; + dns_name_copy(qname, st->qname, NULL); + if (rpz_policy == DNS_RPZ_POLICY_RECORD && + result != DNS_R_NXRRSET) { + dns_rdataset_clone(&zrdataset,st->m.rdataset); + dns_db_attachnode(db, node, &st->m.node); + } + dns_db_attach(db, &st->m.db); + dns_zone_attach(zone, &st->m.zone); + } + if (dns_rdataset_isassociated(&zrdataset)) + dns_rdataset_disassociate(&zrdataset); + } + + RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); + return (ISC_R_SUCCESS); +} +#endif + static isc_result_t cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, @@ -5693,6 +6065,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, free_rdataset(rbtdb, rbtdb->common.mctx, newheader); newheader = (rdatasetheader_t *)merged; + init_rdataset(rbtdb, newheader); if (loading && RESIGN(newheader) && RESIGN(header) && header->resign < newheader->resign) @@ -6015,16 +6388,17 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, rdatasetheader_t *header; isc_result_t result; isc_boolean_t delegating; + isc_boolean_t newnsec; isc_boolean_t tree_locked = ISC_FALSE; isc_boolean_t cache_is_overmem = ISC_FALSE; REQUIRE(VALID_RBTDB(rbtdb)); if (rbtdb->common.methods == &zone_methods) - REQUIRE(((rbtnode->nsec3 && + REQUIRE(((rbtnode->nsec == DNS_RBT_NSEC_NSEC3 && (rdataset->type == dns_rdatatype_nsec3 || rdataset->covers == dns_rdatatype_nsec3)) || - (!rbtnode->nsec3 && + (rbtnode->nsec != DNS_RBT_NSEC_NSEC3 && rdataset->type != dns_rdatatype_nsec3 && rdataset->covers != dns_rdatatype_nsec3))); @@ -6101,14 +6475,23 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, delegating = ISC_FALSE; /* - * If we're adding a delegation type or the DB is a cache in an overmem - * state, hold an exclusive lock on the tree. In the latter case - * the lock does not necessarily have to be acquired but it will help - * purge stale entries more effectively. + * Add to the auxiliary NSEC tree if we're adding an NSEC record. + */ + if (rbtnode->nsec != DNS_RBT_NSEC_HAS_NSEC && + rdataset->type == dns_rdatatype_nsec) + newnsec = ISC_TRUE; + else + newnsec = ISC_FALSE; + + /* + * If we're adding a delegation type, adding to the auxiliary NSEC tree, + * or the DB is a cache in an overmem state, hold an exclusive lock on + * the tree. In the latter case the lock does not necessarily have to + * be acquired but it will help purge stale entries more effectively. */ if (IS_CACHE(rbtdb) && isc_mem_isovermem(rbtdb->common.mctx)) cache_is_overmem = ISC_TRUE; - if (delegating || cache_is_overmem) { + if (delegating || newnsec || cache_is_overmem) { tree_locked = ISC_TRUE; RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); } @@ -6137,14 +6520,35 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, * cleaning, we can release it now. However, we still need the * node lock. */ - if (tree_locked && !delegating) { + if (tree_locked && !delegating && !newnsec) { RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write); tree_locked = ISC_FALSE; } } - result = add(rbtdb, rbtnode, rbtversion, newheader, options, ISC_FALSE, - addedrdataset, now); + result = ISC_R_SUCCESS; + if (newnsec) { + dns_fixedname_t fname; + dns_name_t *name; + dns_rbtnode_t *nsecnode; + + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_rbt_fullnamefromnode(rbtnode, name); + nsecnode = NULL; + result = dns_rbt_addnode(rbtdb->nsec, name, &nsecnode); + if (result == ISC_R_SUCCESS) { + nsecnode->nsec = DNS_RBT_NSEC_NSEC; + rbtnode->nsec = DNS_RBT_NSEC_HAS_NSEC; + } else if (result == ISC_R_EXISTS) { + rbtnode->nsec = DNS_RBT_NSEC_HAS_NSEC; + result = ISC_R_SUCCESS; + } + } + + if (result == ISC_R_SUCCESS) + result = add(rbtdb, rbtnode, rbtversion, newheader, options, + ISC_FALSE, addedrdataset, now); if (result == ISC_R_SUCCESS && delegating) rbtnode->find_callback = 1; @@ -6181,10 +6585,10 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, REQUIRE(VALID_RBTDB(rbtdb)); if (rbtdb->common.methods == &zone_methods) - REQUIRE(((rbtnode->nsec3 && + REQUIRE(((rbtnode->nsec == DNS_RBT_NSEC_NSEC3 && (rdataset->type == dns_rdatatype_nsec3 || rdataset->covers == dns_rdatatype_nsec3)) || - (!rbtnode->nsec3 && + (rbtnode->nsec != DNS_RBT_NSEC_NSEC3 && rdataset->type != dns_rdatatype_nsec3 && rdataset->covers != dns_rdatatype_nsec3))); @@ -6403,6 +6807,78 @@ deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, return (result); } +/* + * load a non-NSEC3 node in the main tree and optionally to the auxiliary NSEC + */ +static isc_result_t +loadnode(dns_rbtdb_t *rbtdb, dns_name_t *name, dns_rbtnode_t **nodep, + isc_boolean_t hasnsec) +{ + isc_result_t noderesult, nsecresult; + dns_rbtnode_t *nsecnode; + + noderesult = dns_rbt_addnode(rbtdb->tree, name, nodep); + +#ifdef BIND9 + if (noderesult == ISC_R_SUCCESS) + dns_rpz_cidr_addip(rbtdb->rpz_cidr, name); +#endif + + if (!hasnsec) + return (noderesult); + if (noderesult == ISC_R_EXISTS) { + /* + * Add a node to the auxiliary NSEC tree for an old node + * just now getting an NSEC record. + */ + if ((*nodep)->nsec == DNS_RBT_NSEC_HAS_NSEC) + return (noderesult); + } else if (noderesult != ISC_R_SUCCESS) { + return (noderesult); + } + + /* + * Build the auxiliary tree for NSECs as we go. + * This tree speeds searches for closest NSECs that would otherwise + * need to examine many irrelevant nodes in large TLDs. + * + * Add nodes to the auxiliary tree after corresponding nodes have + * been added to the main tree. + */ + nsecnode = NULL; + nsecresult = dns_rbt_addnode(rbtdb->nsec, name, &nsecnode); + if (nsecresult == ISC_R_SUCCESS) { + nsecnode->nsec = DNS_RBT_NSEC_NSEC; + (*nodep)->nsec = DNS_RBT_NSEC_HAS_NSEC; + return (noderesult); + } + + if (nsecresult == ISC_R_EXISTS) { +#if 1 /* 0 */ + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, + ISC_LOG_WARNING, + "addnode: NSEC node already exists"); +#endif + (*nodep)->nsec = DNS_RBT_NSEC_HAS_NSEC; + return (noderesult); + } + + nsecresult = dns_rbt_deletenode(rbtdb->tree, *nodep, ISC_FALSE); + if (nsecresult != ISC_R_SUCCESS) + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_CACHE, + ISC_LOG_WARNING, + "loading_addrdataset: " + "dns_rbt_deletenode: %s after " + "dns_rbt_addnode(NSEC): %s", + isc_result_totext(nsecresult), + isc_result_totext(noderesult)); + return (noderesult); +} + static isc_result_t loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) { rbtdb_load_t *loadctx = arg; @@ -6451,15 +6927,15 @@ loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) { rdataset->covers == dns_rdatatype_nsec3) { result = dns_rbt_addnode(rbtdb->nsec3, name, &node); if (result == ISC_R_SUCCESS) - node->nsec3 = 1; + node->nsec = DNS_RBT_NSEC_NSEC3; + } else if (rdataset->type == dns_rdatatype_nsec) { + result = loadnode(rbtdb, name, &node, ISC_TRUE); } else { - result = dns_rbt_addnode(rbtdb->tree, name, &node); - if (result == ISC_R_SUCCESS) - node->nsec3 = 0; + result = loadnode(rbtdb, name, &node, ISC_FALSE); } if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) return (result); - if (result != ISC_R_EXISTS) { + if (result == ISC_R_SUCCESS) { dns_name_t foundname; dns_name_init(&foundname, NULL); dns_rbt_namefromnode(node, &foundname); @@ -6585,9 +7061,17 @@ dump(dns_db_t *db, dns_dbversion_t *version, const char *filename, REQUIRE(VALID_RBTDB(rbtdb)); +#ifdef BIND9 return (dns_master_dump2(rbtdb->common.mctx, db, version, &dns_master_style_default, filename, masterformat)); +#else + UNUSED(version); + UNUSED(filename); + UNUSED(masterformat); + + return (ISC_R_NOTIMPLEMENTED); +#endif /* BIND9 */ } static void @@ -6768,7 +7252,7 @@ setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) { } else if (resign < oldresign) isc_heap_increased(rbtdb->heaps[header->node->locknum], header->heap_index); - else + else if (resign > oldresign) isc_heap_decreased(rbtdb->heaps[header->node->locknum], header->heap_index); } else if (resign && header->heap_index == 0) { @@ -6913,7 +7397,14 @@ static dns_dbmethods_t zone_methods = { getsigningtime, resigned, isdnssec, + NULL, +#ifdef BIND9 + get_rpz_enabled, + rpz_findips +#else + NULL, NULL +#endif }; static dns_dbmethods_t cache_methods = { @@ -6952,7 +7443,9 @@ static dns_dbmethods_t cache_methods = { NULL, NULL, isdnssec, - getrrsetstats + getrrsetstats, + NULL, + NULL }; isc_result_t @@ -7122,12 +7615,36 @@ dns_rbtdb_create return (result); } + result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->nsec); + if (result != ISC_R_SUCCESS) { + free_rbtdb(rbtdb, ISC_FALSE, NULL); + return (result); + } + result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->nsec3); if (result != ISC_R_SUCCESS) { free_rbtdb(rbtdb, ISC_FALSE, NULL); return (result); } +#ifdef BIND9 + /* + * Get ready for response policy IP address searching if at least one + * zone has been configured as a response policy zone and this + * is not a cache zone. + * It would be better to know that this database is for a policy + * zone named for a view, but that would require knowledge from + * above such as an argv[] set from data in the zone. + */ + if (type == dns_dbtype_zone && !dns_name_equal(origin, dns_rootname)) { + result = dns_rpz_new_cidr(mctx, origin, &rbtdb->rpz_cidr); + if (result != ISC_R_SUCCESS) { + free_rbtdb(rbtdb, ISC_FALSE, NULL); + return (result); + } + } +#endif + /* * In order to set the node callback bit correctly in zone databases, * we need to know if the node has the origin name of the zone. @@ -7152,7 +7669,7 @@ dns_rbtdb_create free_rbtdb(rbtdb, ISC_FALSE, NULL); return (result); } - rbtdb->origin_node->nsec3 = 0; + rbtdb->origin_node->nsec = DNS_RBT_NSEC_NORMAL; /* * We need to give the origin node the right locknum. */ @@ -7180,7 +7697,7 @@ dns_rbtdb_create free_rbtdb(rbtdb, ISC_FALSE, NULL); return (result); } - nsec3node->nsec3 = 1; + nsec3node->nsec = DNS_RBT_NSEC_NSEC3; /* * We need to give the nsec3 origin node the right locknum. */ @@ -8238,6 +8755,21 @@ rdataset_getadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type, dns_name_t *fname, dns_message_t *msg, isc_stdtime_t now) { +#ifndef BIND9 + UNUSED(rdataset); + UNUSED(type); + UNUSED(qtype); + UNUSED(acache); + UNUSED(zonep); + UNUSED(dbp); + UNUSED(versionp); + UNUSED(nodep); + UNUSED(fname); + UNUSED(msg); + UNUSED(now); + + return (ISC_R_NOTIMPLEMENTED); +#else dns_rbtdb_t *rbtdb = rdataset->private1; dns_rbtnode_t *rbtnode = rdataset->private2; unsigned char *raw = rdataset->private3; /* RDATASLAB */ @@ -8354,8 +8886,10 @@ acache_callback(dns_acacheentry_t *entry, void **arg) { dns_db_detach((dns_db_t **)(void*)&rbtdb); *arg = NULL; +#endif /* BIND9 */ } +#ifdef BIND9 static void acache_cancelentry(isc_mem_t *mctx, dns_acacheentry_t *entry, acache_cbarg_t **cbargp) @@ -8376,6 +8910,7 @@ acache_cancelentry(isc_mem_t *mctx, dns_acacheentry_t *entry, *cbargp = NULL; } +#endif /* BIND9 */ static isc_result_t rdataset_setadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type, @@ -8384,6 +8919,19 @@ rdataset_setadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type, dns_dbversion_t *version, dns_dbnode_t *node, dns_name_t *fname) { +#ifndef BIND9 + UNUSED(rdataset); + UNUSED(type); + UNUSED(qtype); + UNUSED(acache); + UNUSED(zone); + UNUSED(db); + UNUSED(version); + UNUSED(node); + UNUSED(fname); + + return (ISC_R_NOTIMPLEMENTED); +#else dns_rbtdb_t *rbtdb = rdataset->private1; dns_rbtnode_t *rbtnode = rdataset->private2; unsigned char *raw = rdataset->private3; /* RDATASLAB */ @@ -8507,12 +9055,21 @@ rdataset_setadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type, } return (result); +#endif } static isc_result_t rdataset_putadditional(dns_acache_t *acache, dns_rdataset_t *rdataset, dns_rdatasetadditional_t type, dns_rdatatype_t qtype) { +#ifndef BIND9 + UNUSED(acache); + UNUSED(rdataset); + UNUSED(type); + UNUSED(qtype); + + return (ISC_R_NOTIMPLEMENTED); +#else dns_rbtdb_t *rbtdb = rdataset->private1; dns_rbtnode_t *rbtnode = rdataset->private2; unsigned char *raw = rdataset->private3; /* RDATASLAB */ @@ -8577,6 +9134,7 @@ rdataset_putadditional(dns_acache_t *acache, dns_rdataset_t *rdataset, } return (ISC_R_SUCCESS); +#endif } /*% |