update to 9.7.1-P2
[tridge/bind9.git] / lib / dns / rbtdb.c
index 229bc62cf109275e4abfac9b4a2d5de8bc06b604..2229938366424a1cb1268b777b8de3290aa7055c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004-2009  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2004-2010  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.6.10.1 2009/11/18 23:58:04 marka Exp $ */
+/* $Id: rbtdb.c,v 1.292.8.9 2010/05/10 01:41:11 marka Exp $ */
 
 /*! \file */
 
@@ -258,21 +258,8 @@ typedef struct rdatasetheader {
 
        dns_rbtnode_t                   *node;
        isc_stdtime_t                   last_used;
-       ISC_LINK(struct rdatasetheader) lru_link;
-       /*%<
-        * Used for LRU-based cache management.  We should probably make
-        * these cache-DB specific.  We might also make it a pointer and
-        * ensure only the top header has a valid link to save memory.
-        * The linked-list is locked by the rbtdb->lrulock.
-        */
+       ISC_LINK(struct rdatasetheader) link;
 
-       /*
-        * It's possible this should not be here anymore, but instead
-        * referenced from the bucket's heap directly.
-        */
-#if 0
-       isc_heap_t                      *heap;
-#endif
        unsigned int                    heap_index;
        /*%<
         * Used for TTL-based cache cleaning.
@@ -396,7 +383,7 @@ typedef struct rbtdb_version {
        isc_uint8_t                     flags;
        isc_uint16_t                    iterations;
        isc_uint8_t                     salt_length;
-       unsigned char                   salt[NSEC3_MAX_HASH_LENGTH];
+       unsigned char                   salt[DNS_NSEC3_SALTSIZE];
 } rbtdb_version_t;
 
 typedef ISC_LIST(rbtdb_version_t)       rbtdb_versionlist_t;
@@ -449,6 +436,7 @@ typedef struct {
 
        /* Locked by tree_lock. */
        dns_rbt_t *                     tree;
+       dns_rbt_t *                     nsec;
        dns_rbt_t *                     nsec3;
 
        /* Unlocked */
@@ -534,6 +522,8 @@ static void overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
 static isc_result_t resign_insert(dns_rbtdb_t *rbtdb, int idx,
                                  rdatasetheader_t *newheader);
 static void prune_tree(isc_task_t *task, isc_event_t *event);
+static void rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
+static void rdataset_expire(dns_rdataset_t *rdataset);
 
 static dns_rdatasetmethods_t rdataset_methods = {
        rdataset_disassociate,
@@ -548,7 +538,9 @@ static dns_rdatasetmethods_t rdataset_methods = {
        rdataset_getclosest,
        rdataset_getadditional,
        rdataset_setadditional,
-       rdataset_putadditional
+       rdataset_putadditional,
+       rdataset_settrust,
+       rdataset_expire
 };
 
 static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);
@@ -625,8 +617,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
@@ -832,6 +825,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)
@@ -868,33 +862,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)
@@ -908,11 +895,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)
@@ -1227,7 +1214,7 @@ free_noqname(isc_mem_t *mctx, struct noqname **noqname) {
 static inline void
 init_rdataset(dns_rbtdb_t *rbtdb, rdatasetheader_t *h)
 {
-       ISC_LINK_INIT(h, lru_link);
+       ISC_LINK_INIT(h, link);
        h->heap_index = 0;
 
 #if TRACE_HEADER
@@ -1267,8 +1254,10 @@ free_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *rdataset)
        }
 
        idx = rdataset->node->locknum;
-       if (ISC_LINK_LINKED(rdataset, lru_link))
-               ISC_LIST_UNLINK(rbtdb->rdatasets[idx], rdataset, lru_link);
+       if (ISC_LINK_LINKED(rdataset, link)) {
+               INSIST(IS_CACHE(rbtdb));
+               ISC_LIST_UNLINK(rbtdb->rdatasets[idx], rdataset, link);
+       }
        if (rdataset->heap_index != 0)
                isc_heap_delete(rbtdb->heaps[idx], rdataset->heap_index);
        rdataset->heap_index = 0;
@@ -1488,6 +1477,71 @@ 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:
+               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);
+               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 +1553,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 +1566,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 +1816,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 +1952,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 +1994,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 +2048,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,20 +2083,9 @@ 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
 
-                               INSIST(nsec3param.salt_length <=
-                                      sizeof(version->salt));
                                memcpy(version->salt, nsec3param.salt,
                                       nsec3param.salt_length);
                                version->hash = nsec3param.hash;
@@ -2098,6 +2107,35 @@ 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) {
+       dns_rbtdb_t *rbtdb = event->ev_arg;
+       isc_boolean_t again = ISC_FALSE;
+       unsigned int locknum;
+       unsigned int refs;
+
+       RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+       for (locknum = 0; locknum < rbtdb->node_lock_count; locknum++) {
+               NODE_LOCK(&rbtdb->node_locks[locknum].lock,
+                         isc_rwlocktype_write);
+               cleanup_dead_nodes(rbtdb, locknum);
+               if (ISC_LIST_HEAD(rbtdb->deadnodes[locknum]) != NULL)
+                       again = ISC_TRUE;
+               NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
+                           isc_rwlocktype_write);
+       }
+       RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+       if (again)
+               isc_task_send(task, &event);
+       else {
+               isc_event_free(&event);
+               isc_refcount_decrement(&rbtdb->references, &refs);
+               if (refs == 0)
+                       maybe_free_rbtdb(rbtdb);
+       }
+}
 
 static void
 closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
@@ -2284,29 +2322,43 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
        for (header = HEAD(resigned_list);
             header != NULL;
             header = HEAD(resigned_list)) {
-               ISC_LIST_UNLINK(resigned_list, header, lru_link);
-               if (rollback) {
-                       nodelock_t *lock;
-                       lock = &rbtdb->node_locks[header->node->locknum].lock;
-                       NODE_LOCK(lock, isc_rwlocktype_write);
+               nodelock_t *lock;
+
+               ISC_LIST_UNLINK(resigned_list, header, link);
+
+               lock = &rbtdb->node_locks[header->node->locknum].lock;
+               NODE_LOCK(lock, isc_rwlocktype_write);
+               if (rollback)
                        resign_insert(rbtdb, header->node->locknum, header);
-                       NODE_UNLOCK(lock, isc_rwlocktype_write);
-               }
                decrement_reference(rbtdb, header->node, least_serial,
                                    isc_rwlocktype_write, isc_rwlocktype_none,
                                    ISC_FALSE);
+               NODE_UNLOCK(lock, isc_rwlocktype_write);
        }
 
        if (!EMPTY(cleanup_list)) {
-               /*
-                * We acquire a tree write lock here in order to make sure
-                * that stale nodes will be removed in decrement_reference().
-                * If we didn't have the lock, those nodes could miss the
-                * chance to be removed until the server stops.  The write lock
-                * is expensive, but this event should be rare enough to justify
-                * the cost.
-                */
-               RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+               isc_event_t *event = NULL;
+               isc_rwlocktype_t tlock = isc_rwlocktype_none;
+
+               if (rbtdb->task != NULL)
+                       event = isc_event_allocate(rbtdb->common.mctx, NULL,
+                                                  DNS_EVENT_RBTDEADNODES,
+                                                  cleanup_dead_nodes_callback,
+                                                  rbtdb, sizeof(isc_event_t));
+               if (event == NULL) {
+                       /*
+                        * We acquire a tree write lock here in order to make
+                        * sure that stale nodes will be removed in
+                        * decrement_reference().  If we didn't have the lock,
+                        * those nodes could miss the chance to be removed
+                        * until the server stops.  The write lock is
+                        * expensive, but this event should be rare enough
+                        * to justify the cost.
+                        */
+                       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+                       tlock = isc_rwlocktype_write;
+               }
+
                for (changed = HEAD(cleanup_list);
                     changed != NULL;
                     changed = next_changed) {
@@ -2321,20 +2373,25 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
                         * This is a good opportunity to purge any dead nodes,
                         * so use it.
                         */
-                       cleanup_dead_nodes(rbtdb, rbtnode->locknum);
+                       if (event == NULL)
+                               cleanup_dead_nodes(rbtdb, rbtnode->locknum);
 
                        if (rollback)
                                rollback_node(rbtnode, serial);
                        decrement_reference(rbtdb, rbtnode, least_serial,
-                                           isc_rwlocktype_write,
-                                           isc_rwlocktype_write, ISC_FALSE);
+                                           isc_rwlocktype_write, tlock,
+                                           ISC_FALSE);
 
                        NODE_UNLOCK(lock, isc_rwlocktype_write);
 
                        isc_mem_put(rbtdb->common.mctx, changed,
                                    sizeof(*changed));
                }
-               RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+               if (event != NULL) {
+                       isc_refcount_increment(&rbtdb->references, NULL);
+                       isc_task_send(rbtdb->task, &event);
+               } else
+                       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
        }
 
  end:
@@ -2370,7 +2427,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);
@@ -2398,7 +2456,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++;
        }
@@ -2444,7 +2503,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)) {
@@ -2506,13 +2564,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);
@@ -3217,14 +3276,126 @@ matchparams(rdatasetheader_t *header, rbtdb_search_t *search)
        return (ISC_FALSE);
 }
 
+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;
+
+       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);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+               return (ISC_R_SUCCESS);
+       }
+
+       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);
+               }
+               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;
+
+               if (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);
+               }
+       }
+}
+
 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;
@@ -3232,6 +3403,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) {
@@ -3244,17 +3416,20 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
                wraps = ISC_FALSE;
        }
 
- again:
-       do {
-               node = NULL;
+       /*
+        * 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);
-               result = dns_rbtnodechain_current(&search->chain, name,
-                                                 origin, &node);
+ again:
+       node = NULL;
+       result = dns_rbtnodechain_current(&search->chain, name, origin, &node);
                if (result != ISC_R_SUCCESS)
                        return (result);
+       do {
                NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
                          isc_rwlocktype_read);
                found = NULL;
@@ -3307,8 +3482,7 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
                                result = dns_rbtnodechain_prev(&search->chain,
                                                               NULL, NULL);
                        } else if (found != NULL &&
-                                  (foundsig != NULL || !need_sig))
-                       {
+                                  (foundsig != NULL || !need_sig)) {
                                /*
                                 * We've found the right NSEC/NSEC3 record.
                                 *
@@ -3345,8 +3519,9 @@ 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
@@ -3360,13 +3535,18 @@ 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;
        } 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);
@@ -3524,11 +3704,17 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                /*
                 * The node may be a zone cut itself.  If it might be one,
                 * make sure we check for it later.
+                *
+                * DS records live above the zone cut in ordinary zone so
+                * we want to ignore any referral.
+                *
+                * Stub zones don't have anything "above" the delgation so
+                * we always return a referral.
                 */
                if (node->find_callback &&
-                   (node != search.rbtdb->origin_node ||
-                    IS_STUB(search.rbtdb)) &&
-                   !dns_rdatatype_atparent(type))
+                   ((node != search.rbtdb->origin_node &&
+                     !dns_rdatatype_atparent(type)) ||
+                    IS_STUB(search.rbtdb)))
                        maybe_zonecut = ISC_TRUE;
        }
 
@@ -3546,8 +3732,8 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
         * We now go looking for rdata...
         */
 
-       NODE_LOCK(&(search.rbtdb->node_locks[node->locknum].lock),
-                 isc_rwlocktype_read);
+       lock = &search.rbtdb->node_locks[node->locknum].lock;
+       NODE_LOCK(lock, isc_rwlocktype_read);
 
        found = NULL;
        foundsig = NULL;
@@ -3625,8 +3811,10 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                         * we are using behave as if it isn't here.
                         */
                        if (header->type == dns_rdatatype_nsec3 &&
-                           !matchparams(header, &search))
+                          !matchparams(header, &search)) {
+                               NODE_UNLOCK(lock, isc_rwlocktype_read);
                                goto partial_match;
+                       }
                        /*
                         * If we found a type we were looking for,
                         * remember it.
@@ -3705,7 +3893,6 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                 * we really have a partial match.
                 */
                if (!wild) {
-                       lock = &search.rbtdb->node_locks[node->locknum].lock;
                        NODE_UNLOCK(lock, isc_rwlocktype_read);
                        goto partial_match;
                }
@@ -3722,7 +3909,6 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                         *
                         * Return the delegation.
                         */
-                       lock = &search.rbtdb->node_locks[node->locknum].lock;
                        NODE_UNLOCK(lock, isc_rwlocktype_read);
                        result = setup_delegation(&search, nodep, foundname,
                                                  rdataset, sigrdataset);
@@ -3744,7 +3930,6 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                                goto node_exit;
                        }
 
-                       lock = &search.rbtdb->node_locks[node->locknum].lock;
                        NODE_UNLOCK(lock, isc_rwlocktype_read);
                        result = find_closest_nsec(&search, nodep, foundname,
                                                   rdataset, sigrdataset,
@@ -3829,7 +4014,6 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                if (result == DNS_R_GLUE &&
                    (search.options & DNS_DBFIND_VALIDATEGLUE) != 0 &&
                    !valid_glue(&search, foundname, type, node)) {
-                       lock = &search.rbtdb->node_locks[node->locknum].lock;
                        NODE_UNLOCK(lock, isc_rwlocktype_read);
                        result = setup_delegation(&search, nodep, foundname,
                                                  rdataset, sigrdataset);
@@ -3861,8 +4045,7 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
                foundname->attributes |= DNS_NAMEATTR_WILDCARD;
 
  node_exit:
-       NODE_UNLOCK(&(search.rbtdb->node_locks[node->locknum].lock),
-                   isc_rwlocktype_read);
+       NODE_UNLOCK(lock, isc_rwlocktype_read);
 
  tree_exit:
        RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
@@ -3907,6 +4090,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);
 }
 
@@ -5408,8 +5592,10 @@ static isc_result_t
 resign_insert(dns_rbtdb_t *rbtdb, int idx, rdatasetheader_t *newheader) {
        isc_result_t result;
 
+       INSIST(!IS_CACHE(rbtdb));
        INSIST(newheader->heap_index == 0);
-       INSIST(!ISC_LINK_LINKED(newheader, lru_link));
+       INSIST(!ISC_LINK_LINKED(newheader, link));
+
        result = isc_heap_insert(rbtdb->heaps[idx], newheader);
        return (result);
 }
@@ -5628,6 +5814,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)
@@ -5735,7 +5922,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
                        idx = newheader->node->locknum;
                        if (IS_CACHE(rbtdb)) {
                                ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
-                                                newheader, lru_link);
+                                                newheader, link);
                                /*
                                 * XXXMLG We don't check the return value
                                 * here.  If it fails, we will not do TTL
@@ -5794,7 +5981,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
                idx = newheader->node->locknum;
                if (IS_CACHE(rbtdb)) {
                        ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
-                                        newheader, lru_link);
+                                        newheader, link);
                        isc_heap_insert(rbtdb->heaps[idx], newheader);
                } else if (RESIGN(newheader)) {
                        resign_insert(rbtdb, idx, newheader);
@@ -5945,15 +6132,16 @@ 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;
 
        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)));
 
@@ -6028,12 +6216,21 @@ 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 (delegating || (IS_CACHE(rbtdb) && rbtdb->overmem)) {
+       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 (delegating || newnsec || (IS_CACHE(rbtdb) && rbtdb->overmem)) {
                tree_locked = ISC_TRUE;
                RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
        }
@@ -6062,14 +6259,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;
 
@@ -6106,10 +6324,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)));
 
@@ -6328,6 +6546,72 @@ 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);
+       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;
@@ -6376,11 +6660,11 @@ 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);
@@ -6510,20 +6794,34 @@ 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
 delete_callback(void *data, void *arg) {
        dns_rbtdb_t *rbtdb = arg;
        rdatasetheader_t *current, *next;
+       unsigned int locknum;
 
-       for (current = data; current != NULL; current = next) {
+       current = data;
+       locknum = current->node->locknum;
+       NODE_LOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
+       while (current != NULL) {
                next = current->next;
                free_rdataset(rbtdb, rbtdb->common.mctx, current);
+               current = next;
        }
+       NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, isc_rwlocktype_write);
 }
 
 static isc_boolean_t
@@ -6642,8 +6940,8 @@ getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash,
        if (rbtversion->havensec3) {
                if (hash != NULL)
                        *hash = rbtversion->hash;
-               if (salt != NULL && salt_length != 0) {
-                       REQUIRE(*salt_length > rbtversion->salt_length);
+               if (salt != NULL && salt_length != NULL) {
+                       REQUIRE(*salt_length >= rbtversion->salt_length);
                        memcpy(salt, rbtversion->salt, rbtversion->salt_length);
                }
                if (salt_length != NULL)
@@ -6687,7 +6985,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) {
@@ -6707,27 +7005,35 @@ getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
        rdatasetheader_t *header = NULL, *this;
        unsigned int i;
        isc_result_t result = ISC_R_NOTFOUND;
+       unsigned int locknum;
 
        REQUIRE(VALID_RBTDB(rbtdb));
 
        RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read);
 
        for (i = 0; i < rbtdb->node_lock_count; i++) {
+               NODE_LOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_read);
                this = isc_heap_element(rbtdb->heaps[i], 1);
-               if (this == NULL)
+               if (this == NULL) {
+                       NODE_UNLOCK(&rbtdb->node_locks[i].lock,
+                                   isc_rwlocktype_read);
                        continue;
+               }
                if (header == NULL)
                        header = this;
-               else if (isc_serial_lt(this->resign, header->resign))
+               else if (isc_serial_lt(this->resign, header->resign)) {
+                       locknum = header->node->locknum;
+                       NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
+                                   isc_rwlocktype_read);
                        header = this;
+               } else
+                       NODE_UNLOCK(&rbtdb->node_locks[i].lock,
+                                   isc_rwlocktype_read);
        }
 
        if (header == NULL)
                goto unlock;
 
-       NODE_LOCK(&rbtdb->node_locks[header->node->locknum].lock,
-                 isc_rwlocktype_read);
-
        bind_rdataset(rbtdb, header->node, header, 0, rdataset);
 
        if (foundname != NULL)
@@ -6761,7 +7067,7 @@ resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version)
        header = rdataset->private3;
        header--;
 
-       RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read);
+       RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
        NODE_LOCK(&rbtdb->node_locks[node->locknum].lock,
                  isc_rwlocktype_write);
        /*
@@ -6771,11 +7077,11 @@ resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version)
        new_reference(rbtdb, node);
        isc_heap_delete(rbtdb->heaps[node->locknum], header->heap_index);
        header->heap_index = 0;
-       ISC_LIST_APPEND(rbtversion->resigned_list, header, lru_link);
+       ISC_LIST_APPEND(rbtversion->resigned_list, header, link);
 
        NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock,
                    isc_rwlocktype_write);
-       RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read);
+       RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
 }
 
 static dns_stats_t *
@@ -7033,6 +7339,12 @@ 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);
@@ -7061,7 +7373,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.
                 */
@@ -7400,6 +7712,34 @@ rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
        return (ISC_R_SUCCESS);
 }
 
+static void
+rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
+       dns_rbtdb_t *rbtdb = rdataset->private1;
+       dns_rbtnode_t *rbtnode = rdataset->private2;
+       rdatasetheader_t *header = rdataset->private3;
+
+       header--;
+       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+                 isc_rwlocktype_write);
+       header->trust = rdataset->trust = trust;
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+                 isc_rwlocktype_write);
+}
+
+static void
+rdataset_expire(dns_rdataset_t *rdataset) {
+       dns_rbtdb_t *rbtdb = rdataset->private1;
+       dns_rbtnode_t *rbtnode = rdataset->private2;
+       rdatasetheader_t *header = rdataset->private3;
+
+       header--;
+       NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+                 isc_rwlocktype_write);
+       expire_header(rbtdb, header, ISC_FALSE);
+       NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+                 isc_rwlocktype_write);
+}
+
 /*
  * Rdataset Iterator Methods
  */
@@ -8094,6 +8434,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 */
@@ -8210,8 +8565,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)
@@ -8232,6 +8589,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,
@@ -8240,6 +8598,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 */
@@ -8363,12 +8734,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 */
@@ -8433,6 +8813,7 @@ rdataset_putadditional(dns_acache_t *acache, dns_rdataset_t *rdataset,
        }
 
        return (ISC_R_SUCCESS);
+#endif
 }
 
 /*%
@@ -8497,13 +8878,11 @@ update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
        INSIST(IS_CACHE(rbtdb));
 
        /* To be checked: can we really assume this? XXXMLG */
-       INSIST(ISC_LINK_LINKED(header, lru_link));
+       INSIST(ISC_LINK_LINKED(header, link));
 
-       ISC_LIST_UNLINK(rbtdb->rdatasets[header->node->locknum],
-                       header, lru_link);
+       ISC_LIST_UNLINK(rbtdb->rdatasets[header->node->locknum], header, link);
        header->last_used = now;
-       ISC_LIST_PREPEND(rbtdb->rdatasets[header->node->locknum],
-                        header, lru_link);
+       ISC_LIST_PREPEND(rbtdb->rdatasets[header->node->locknum], header, link);
 }
 
 /*%
@@ -8539,7 +8918,7 @@ overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
                for (header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum]);
                     header != NULL && purgecount > 0;
                     header = header_prev) {
-                       header_prev = ISC_LIST_PREV(header, lru_link);
+                       header_prev = ISC_LIST_PREV(header, link);
                        /*
                         * Unlink the entry at this point to avoid checking it
                         * again even if it's currently used someone else and
@@ -8548,7 +8927,7 @@ overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
                         * TTL was reset to 0.
                         */
                        ISC_LIST_UNLINK(rbtdb->rdatasets[locknum], header,
-                                       lru_link);
+                                       link);
                        expire_header(rbtdb, header, tree_locked);
                        purgecount--;
                }