]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
2890. [bug] Handle the introduction of new trusted-keys and
authorMark Andrews <marka@isc.org>
Thu, 3 Jun 2010 00:07:59 +0000 (00:07 +0000)
committerMark Andrews <marka@isc.org>
Thu, 3 Jun 2010 00:07:59 +0000 (00:07 +0000)
                        DS, DLV RRsets better. [RT #21097]

CHANGES
lib/dns/include/dns/ncache.h
lib/dns/include/dns/types.h
lib/dns/ncache.c
lib/dns/validator.c

diff --git a/CHANGES b/CHANGES
index 34c8e8243628ea6faf5ba5b4649b5edbf1733b90..9d1faf51dbd3ef03134daac1740204e8a905da1e 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+2890.  [bug]           Handle the introduction of new trusted-keys and
+                       DS, DLV RRsets better. [RT #21097]
+
        --- 9.4-ESV-R2 released ---
 
 2876.  [bug]           Named could return SERVFAIL for negative responses
index 459effb909d7dd4af1721011c7c546b202c6a7d3..42e73ff255d3cd9dfe1133137e08bdba74f29b7c 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: ncache.h,v 1.17.18.2 2005/04/29 00:16:16 marka Exp $ */
+/* $Id: ncache.h,v 1.17.18.3 2010/06/03 00:07:59 marka Exp $ */
 
 #ifndef DNS_NCACHE_H
 #define DNS_NCACHE_H 1
@@ -63,6 +63,11 @@ isc_result_t
 dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
               dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
               dns_rdataset_t *addedrdataset);
+isc_result_t
+dns_ncache_addoptout(dns_message_t *message, dns_db_t *cache,
+                    dns_dbnode_t *node, dns_rdatatype_t covers,
+                    isc_stdtime_t now, dns_ttl_t maxttl,
+                    isc_boolean_t optout, dns_rdataset_t *addedrdataset);
 /*%<
  * Convert the authority data from 'message' into a negative cache
  * rdataset, and store it in 'cache' at 'node' with a TTL limited to
@@ -71,6 +76,8 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
  * The 'covers' argument is the RR type whose nonexistence we are caching,
  * or dns_rdatatype_any when caching a NXDOMAIN response.
  *
+ * 'optout' indicates a DNS_RDATASETATTR_OPTOUT should be set.
+ *
  * Note:
  *\li  If 'addedrdataset' is not NULL, then it will be attached to the added
  *     rdataset.  See dns_db_addrdataset() for more details.
@@ -154,6 +161,26 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
  *
  */
 
+isc_result_t
+dns_ncache_getsigrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
+                          dns_rdatatype_t covers, dns_rdataset_t *rdataset);
+/*%<
+ * Similar to dns_ncache_getrdataset() but get the rrsig that matches.
+ */
+
+void
+dns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found,
+                  dns_rdataset_t *rdataset);
+
+/*%<
+ * Extract the current rdataset and name from a ncache entry.
+ *
+ * Requires:
+ * \li 'ncacherdataset' to be valid and to be a negative cache entry
+ * \li 'found' to be valid.
+ * \li 'rdataset' to be unassociated.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_NCACHE_H */
index 94c2d86f8233fa8485da285b01c77440d6a85a2e..e61c713b42964eade7f607d02851f07a130cf8a0 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: types.h,v 1.109.18.15 2009/11/25 04:50:25 marka Exp $ */
+/* $Id: types.h,v 1.109.18.16 2010/06/03 00:07:59 marka Exp $ */
 
 #ifndef DNS_TYPES_H
 #define DNS_TYPES_H 1
@@ -285,6 +285,7 @@ enum {
 #define DNS_TRUST_PENDING(x)           ((x) == dns_trust_pending_answer || \
                                         (x) == dns_trust_pending_additional)
 #define DNS_TRUST_GLUE(x)              ((x) == dns_trust_glue)
+#define DNS_TRUST_ANSWER(x)            ((x) == dns_trust_answer)
 
 
 /*%
index 89106dad7c063df88b55b1045b42e47b7b2d03f5..5791c5f54284b1639af1d90573cac735bce7f228 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: ncache.c,v 1.36.18.5 2010/02/26 23:46:36 tbox Exp $ */
+/* $Id: ncache.c,v 1.36.18.6 2010/06/03 00:07:58 marka Exp $ */
 
 /*! \file */
 
@@ -30,6 +30,9 @@
 #include <dns/rdata.h>
 #include <dns/rdatalist.h>
 #include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+
+#define DNS_NCACHE_RDATA 20U
 
 /*
  * The format of an ncache rdata is a sequence of one or more records of
@@ -37,6 +40,7 @@
  *
  *     owner name
  *     type
+ *     trust
  *     rdata count
  *             rdata length                    These two occur 'rdata count'
  *             rdata                           times.
@@ -100,10 +104,11 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
        dns_name_t *name;
        dns_ttl_t ttl;
        dns_trust_t trust;
-       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdata_t rdata[DNS_NCACHE_RDATA];
        dns_rdataset_t ncrdataset;
        dns_rdatalist_t ncrdatalist;
        unsigned char data[4096];
+       unsigned int next = 0;
 
        /*
         * Convert the authority data from 'message' into a negative cache
@@ -118,7 +123,17 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
         */
 
        /*
-        * First, build an ncache rdata in buffer.
+        * Initialize the list.
+        */
+       ncrdatalist.rdclass = dns_db_class(cache);
+       ncrdatalist.type = 0;
+       ncrdatalist.covers = covers;
+       ncrdatalist.ttl = maxttl;
+       ISC_LIST_INIT(ncrdatalist.rdata);
+       ISC_LINK_INIT(&ncrdatalist, link);
+
+       /*
+        * Build an ncache rdatas into buffer.
         */
        ttl = maxttl;
        trust = 0xffff;
@@ -164,6 +179,8 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
                                                return (ISC_R_NOSPACE);
                                        isc_buffer_putuint16(&buffer,
                                                             rdataset->type);
+                                       isc_buffer_putuint8(&buffer,
+                                                            rdataset->trust);
                                        /*
                                         * Copy the rdataset into the buffer.
                                         */
@@ -171,6 +188,21 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
                                                               &buffer);
                                        if (result != ISC_R_SUCCESS)
                                                return (result);
+
+                                       if (next >= DNS_NCACHE_RDATA)
+                                               return (ISC_R_NOSPACE);
+                                       dns_rdata_init(&rdata[next]);
+                                       isc_buffer_remainingregion(&buffer, &r);
+                                       rdata[next].data = r.base;
+                                       rdata[next].length = r.length;
+                                       rdata[next].rdclass =
+                                               ncrdatalist.rdclass;
+                                       rdata[next].type = 0;
+                                       rdata[next].flags = 0;
+                                       ISC_LIST_APPEND(ncrdatalist.rdata,
+                                                       &rdata[next], link);
+                                       isc_buffer_forward(&buffer, r.length);
+                                       next++;
                                }
                        }
                }
@@ -226,27 +258,24 @@ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
                        trust = dns_trust_authauthority;
                } else
                        trust = dns_trust_additional;
+               /*
+                * Now add it to the cache.
+                */
+               if (next >= DNS_NCACHE_RDATA)
+                       return (ISC_R_NOSPACE);
+               dns_rdata_init(&rdata[next]);
+               isc_buffer_remainingregion(&buffer, &r);
+               rdata[next].data = r.base;
+               rdata[next].length = r.length;
+               rdata[next].rdclass = ncrdatalist.rdclass;
+               rdata[next].type = 0;
+               rdata[next].flags = 0;
+               ISC_LIST_APPEND(ncrdatalist.rdata, &rdata[next], link);
        }
 
-       /*
-        * Now add it to the cache.
-        */
        INSIST(trust != 0xffff);
-       isc_buffer_usedregion(&buffer, &r);
-       rdata.data = r.base;
-       rdata.length = r.length;
-       rdata.rdclass = dns_db_class(cache);
-       rdata.type = 0;
-       rdata.flags = 0;
-
-       ncrdatalist.rdclass = rdata.rdclass;
-       ncrdatalist.type = 0;
-       ncrdatalist.covers = covers;
-       ncrdatalist.ttl = ttl;
-       ISC_LIST_INIT(ncrdatalist.rdata);
-       ISC_LINK_INIT(&ncrdatalist, link);
 
-       ISC_LIST_APPEND(ncrdatalist.rdata, &rdata, link);
+       ncrdatalist.ttl = ttl;
 
        dns_rdataset_init(&ncrdataset);
        RUNTIME_CHECK(dns_rdatalist_tordataset(&ncrdatalist, &ncrdataset)
@@ -281,18 +310,14 @@ dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx,
        REQUIRE(rdataset != NULL);
        REQUIRE(rdataset->type == 0);
 
-       result = dns_rdataset_first(rdataset);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-       dns_rdataset_current(rdataset, &rdata);
-       INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
-       isc_buffer_init(&source, rdata.data, rdata.length);
-       isc_buffer_add(&source, rdata.length);
-
        savedbuffer = *target;
-
        count = 0;
-       do {
+
+       result = dns_rdataset_first(rdataset);
+       while (result == ISC_R_SUCCESS) {
+               dns_rdataset_current(rdataset, &rdata);
+               isc_buffer_init(&source, rdata.data, rdata.length);
+               isc_buffer_add(&source, rdata.length);
                dns_name_init(&name, NULL);
                isc_buffer_remainingregion(&source, &remaining);
                dns_name_fromregion(&name, &remaining);
@@ -300,8 +325,9 @@ dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx,
                isc_buffer_forward(&source, name.length);
                remaining.length -= name.length;
 
-               INSIST(remaining.length >= 4);
+               INSIST(remaining.length >= 5);
                type = isc_buffer_getuint16(&source);
+               isc_buffer_forward(&source, 1);
                rcount = isc_buffer_getuint16(&source);
 
                for (i = 0; i < rcount; i++) {
@@ -370,8 +396,12 @@ dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx,
 
                        count++;
                }
-               isc_buffer_remainingregion(&source, &remaining);
-       } while (remaining.length > 0);
+               INSIST(isc_buffer_remaininglength(&source) == 0);
+               result = dns_rdataset_next(rdataset);
+               dns_rdata_reset(&rdata);
+       }
+       if (result != ISC_R_NOMORE)
+               goto rollback;
 
        *countp = count;
 
@@ -467,6 +497,13 @@ rdataset_count(dns_rdataset_t *rdataset) {
        return (count);
 }
 
+static void
+rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
+       unsigned char *raw = rdataset->private3;
+
+       raw[-1] = trust;
+}
+
 static dns_rdatasetmethods_t rdataset_methods = {
        rdataset_disassociate,
        rdataset_first,
@@ -479,8 +516,8 @@ static dns_rdatasetmethods_t rdataset_methods = {
        NULL,
        NULL,
        NULL,
+       rdataset_settrust,
        NULL,
-       NULL
 };
 
 isc_result_t
@@ -493,8 +530,8 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
        isc_buffer_t source;
        dns_name_t tname;
        dns_rdatatype_t ttype;
-       unsigned int i, rcount;
-       isc_uint16_t length;
+       dns_trust_t trust = dns_trust_none;
+       dns_rdataset_t clone;
 
        REQUIRE(ncacherdataset != NULL);
        REQUIRE(ncacherdataset->type == 0);
@@ -502,15 +539,13 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
        REQUIRE(!dns_rdataset_isassociated(rdataset));
        REQUIRE(type != dns_rdatatype_rrsig);
 
-       result = dns_rdataset_first(ncacherdataset);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-       dns_rdataset_current(ncacherdataset, &rdata);
-       INSIST(dns_rdataset_next(ncacherdataset) == ISC_R_NOMORE);
-       isc_buffer_init(&source, rdata.data, rdata.length);
-       isc_buffer_add(&source, rdata.length);
-
-       do {
+       dns_rdataset_init(&clone);
+       dns_rdataset_clone(ncacherdataset, &clone);
+       result = dns_rdataset_first(&clone);
+       while (result == ISC_R_SUCCESS) {
+               dns_rdataset_current(&clone, &rdata);
+               isc_buffer_init(&source, rdata.data, rdata.length);
+               isc_buffer_add(&source, rdata.length);
                dns_name_init(&tname, NULL);
                isc_buffer_remainingregion(&source, &remaining);
                dns_name_fromregion(&tname, &remaining);
@@ -518,35 +553,133 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
                isc_buffer_forward(&source, tname.length);
                remaining.length -= tname.length;
 
-               INSIST(remaining.length >= 4);
+               INSIST(remaining.length >= 3);
                ttype = isc_buffer_getuint16(&source);
 
                if (ttype == type && dns_name_equal(&tname, name)) {
+                       trust = isc_buffer_getuint8(&source);
+                       INSIST(trust <= dns_trust_ultimate);
                        isc_buffer_remainingregion(&source, &remaining);
                        break;
                }
+               result = dns_rdataset_next(&clone);
+               dns_rdata_reset(&rdata);
+       }
+       dns_rdataset_disassociate(&clone);
+       if (result == ISC_R_NOMORE)
+               return (ISC_R_NOTFOUND);
+       if (result != ISC_R_SUCCESS)
+               return (result);
 
-               rcount = isc_buffer_getuint16(&source);
-               for (i = 0; i < rcount; i++) {
-                       isc_buffer_remainingregion(&source, &remaining);
-                       INSIST(remaining.length >= 2);
-                       length = isc_buffer_getuint16(&source);
+       INSIST(remaining.length != 0);
+
+       rdataset->methods = &rdataset_methods;
+       rdataset->rdclass = ncacherdataset->rdclass;
+       rdataset->type = type;
+       rdataset->covers = 0;
+       rdataset->ttl = ncacherdataset->ttl;
+       rdataset->trust = trust;
+       rdataset->private1 = NULL;
+       rdataset->private2 = NULL;
+
+       rdataset->private3 = remaining.base;
+
+       /*
+        * Reset iterator state.
+        */
+       rdataset->privateuint4 = 0;
+       rdataset->private5 = NULL;
+       rdataset->private6 = NULL;
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_ncache_getsigrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
+                         dns_rdatatype_t covers, dns_rdataset_t *rdataset)
+{
+       dns_name_t tname;
+       dns_rdata_rrsig_t rrsig;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdataset_t clone;
+       dns_rdatatype_t type;
+       dns_trust_t trust = dns_trust_none;
+       isc_buffer_t source;
+       isc_region_t remaining, sigregion;
+       isc_result_t result;
+       unsigned char *raw;
+       unsigned int count;
+
+       REQUIRE(ncacherdataset != NULL);
+       REQUIRE(ncacherdataset->type == 0);
+       REQUIRE(name != NULL);
+       REQUIRE(!dns_rdataset_isassociated(rdataset));
+
+       dns_rdataset_init(&clone);
+       dns_rdataset_clone(ncacherdataset, &clone);
+       result = dns_rdataset_first(&clone);
+       while (result == ISC_R_SUCCESS) {
+               dns_rdataset_current(&clone, &rdata);
+               isc_buffer_init(&source, rdata.data, rdata.length);
+               isc_buffer_add(&source, rdata.length);
+               dns_name_init(&tname, NULL);
+               isc_buffer_remainingregion(&source, &remaining);
+               dns_name_fromregion(&tname, &remaining);
+               INSIST(remaining.length >= tname.length);
+               isc_buffer_forward(&source, tname.length);
+               remaining.length -= tname.length;
+               remaining.base += tname.length;
+
+               INSIST(remaining.length >= 2);
+               type = isc_buffer_getuint16(&source);
+               remaining.length -= 2;
+               remaining.base += 2;
+
+               if (type != dns_rdatatype_rrsig ||
+                   !dns_name_equal(&tname, name)) {
+                       result = dns_rdataset_next(&clone);
+                       dns_rdata_reset(&rdata);
+                       continue;
+               }
+
+               INSIST(remaining.length >= 1);
+               trust = isc_buffer_getuint8(&source);
+               INSIST(trust <= dns_trust_ultimate);
+               remaining.length -= 1;
+               remaining.base += 1;
+
+               raw = remaining.base;
+               count = raw[0] * 256 + raw[1];
+               INSIST(count > 0);
+               raw += 2;
+               sigregion.length = raw[0] * 256 + raw[1];
+               raw += 2;
+               sigregion.base = raw;
+               dns_rdata_reset(&rdata);
+               dns_rdata_fromregion(&rdata, rdataset->rdclass,
+                                    dns_rdatatype_rrsig, &sigregion);
+               (void)dns_rdata_tostruct(&rdata, &rrsig, NULL);
+               if (rrsig.covered == covers) {
                        isc_buffer_remainingregion(&source, &remaining);
-                       INSIST(remaining.length >= length);
-                       isc_buffer_forward(&source, length);
+                       break;
                }
-               isc_buffer_remainingregion(&source, &remaining);
-       } while (remaining.length > 0);
 
-       if (remaining.length == 0)
+               result = dns_rdataset_next(&clone);
+               dns_rdata_reset(&rdata);
+       }
+       dns_rdataset_disassociate(&clone);
+       if (result == ISC_R_NOMORE)
                return (ISC_R_NOTFOUND);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       INSIST(remaining.length != 0);
 
        rdataset->methods = &rdataset_methods;
        rdataset->rdclass = ncacherdataset->rdclass;
-       rdataset->type = type;
-       rdataset->covers = 0;
+       rdataset->type = dns_rdatatype_rrsig;
+       rdataset->covers = covers;
        rdataset->ttl = ncacherdataset->ttl;
-       rdataset->trust = ncacherdataset->trust;
+       rdataset->trust = trust;
        rdataset->private1 = NULL;
        rdataset->private2 = NULL;
 
@@ -557,5 +690,78 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
         */
        rdataset->privateuint4 = 0;
        rdataset->private5 = NULL;
+       rdataset->private6 = NULL;
        return (ISC_R_SUCCESS);
 }
+
+void
+dns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found,
+                  dns_rdataset_t *rdataset)
+{
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_trust_t trust;
+       isc_region_t remaining, sigregion;
+       isc_buffer_t source;
+       dns_name_t tname;
+       dns_rdatatype_t type;
+       unsigned int count;
+       dns_rdata_rrsig_t rrsig;
+       unsigned char *raw;
+
+       REQUIRE(ncacherdataset != NULL);
+       REQUIRE(ncacherdataset->type == 0);
+       REQUIRE(found != NULL);
+       REQUIRE(!dns_rdataset_isassociated(rdataset));
+
+       dns_rdataset_current(ncacherdataset, &rdata);
+       isc_buffer_init(&source, rdata.data, rdata.length);
+       isc_buffer_add(&source, rdata.length);
+
+       dns_name_init(&tname, NULL);
+       isc_buffer_remainingregion(&source, &remaining);
+       dns_name_fromregion(found, &remaining);
+       INSIST(remaining.length >= found->length);
+       isc_buffer_forward(&source, found->length);
+       remaining.length -= found->length;
+
+       INSIST(remaining.length >= 5);
+       type = isc_buffer_getuint16(&source);
+       trust = isc_buffer_getuint8(&source);
+       INSIST(trust <= dns_trust_ultimate);
+       isc_buffer_remainingregion(&source, &remaining);
+
+       rdataset->methods = &rdataset_methods;
+       rdataset->rdclass = ncacherdataset->rdclass;
+       rdataset->type = type;
+       if (type == dns_rdatatype_rrsig) {
+               /*
+                * Extract covers from RRSIG.
+                */
+               raw = remaining.base;
+               count = raw[0] * 256 + raw[1];
+               INSIST(count > 0);
+               raw += 2;
+               sigregion.length = raw[0] * 256 + raw[1];
+               raw += 2;
+               sigregion.base = raw;
+               dns_rdata_reset(&rdata);
+               dns_rdata_fromregion(&rdata, rdataset->rdclass,
+                                    rdataset->type, &sigregion);
+               (void)dns_rdata_tostruct(&rdata, &rrsig, NULL);
+               rdataset->covers = rrsig.covered;
+       } else
+               rdataset->covers = 0;
+       rdataset->ttl = ncacherdataset->ttl;
+       rdataset->trust = trust;
+       rdataset->private1 = NULL;
+       rdataset->private2 = NULL;
+
+       rdataset->private3 = remaining.base;
+
+       /*
+        * Reset iterator state.
+        */
+       rdataset->privateuint4 = 0;
+       rdataset->private5 = NULL;
+       rdataset->private6 = NULL;
+}
index 20843464a223c17a536d8f25d87a5dfe6c255ec6..8a36ff3cf601070247273b39b971d78823e69539 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: validator.c,v 1.119.18.54 2010/04/21 04:23:47 marka Exp $ */
+/* $Id: validator.c,v 1.119.18.55 2010/06/03 00:07:58 marka Exp $ */
 
 /*! \file */
 
 #define NEEDNOQNAME(val) ((val->attributes & VALATTR_NEEDNOQNAME) != 0)
 #define NEEDNOWILDCARD(val) ((val->attributes & VALATTR_NEEDNOWILDCARD) != 0)
 #define DLVTRIED(val) ((val->attributes & VALATTR_DLVTRIED) != 0)
+#define FOUNDNODATA(val) ((val->attributes & VALATTR_FOUNDNODATA) != 0)
+#define FOUNDNOQNAME(val) ((val->attributes & VALATTR_FOUNDNOQNAME) != 0)
+#define FOUNDNOWILDCARD(val) ((val->attributes & VALATTR_FOUNDNOWILDCARD) != 0)
+#define FOUNDOPTOUT(val) ((val->attributes & VALATTR_FOUNDOPTOUT) != 0)
 
 #define SHUTDOWN(v)            (((v)->attributes & VALATTR_SHUTDOWN) != 0)
 #define CANCELED(v)            (((v)->attributes & VALATTR_CANCELED) != 0)
@@ -167,8 +171,8 @@ startfinddlvsep(dns_validator_t *val, dns_name_t *unsecure);
  * Mark the RRsets as a answer.
  */
 static inline void
-markanswer(dns_validator_t *val) {
-       validator_log(val, ISC_LOG_DEBUG(3), "marking as answer");
+markanswer(dns_validator_t *val, const char *where) {
+       validator_log(val, ISC_LOG_DEBUG(3), "marking as answer (%s)", where);
        if (val->event->rdataset != NULL)
                dns_rdataset_settrust(val->event->rdataset, dns_trust_answer);
        if (val->event->sigrdataset != NULL)
@@ -179,7 +183,8 @@ markanswer(dns_validator_t *val) {
 static inline void
 marksecure(dns_validatorevent_t *event) {
        dns_rdataset_settrust(event->rdataset, dns_trust_secure);
-       dns_rdataset_settrust(event->sigrdataset, dns_trust_secure);
+       if (event->sigrdataset != NULL)
+               dns_rdataset_settrust(event->sigrdataset, dns_trust_secure);
 }
 
 static void
@@ -475,7 +480,7 @@ dsfetched2(isc_task_t *task, isc_event_t *event) {
                                              "must be secure failure");
                                validator_done(val, DNS_R_MUSTBESECURE);
                        } else if (val->view->dlv == NULL || DLVTRIED(val)) {
-                               markanswer(val);
+                               markanswer(val, "dsfetched2");
                                validator_done(val, ISC_R_SUCCESS);
                        } else {
                                result = startfinddlvsep(val, tname);
@@ -601,11 +606,32 @@ dsvalidated(isc_task_t *task, isc_event_t *event) {
        if (CANCELED(val)) {
                validator_done(val, ISC_R_CANCELED);
        } else if (eresult == ISC_R_SUCCESS) {
+               isc_boolean_t have_dsset;
+               dns_name_t *name;
                validator_log(val, ISC_LOG_DEBUG(3),
-                             "dsset with trust %d", val->frdataset.trust);
-               if ((val->attributes & VALATTR_INSECURITY) != 0)
-                       result = proveunsecure(val, ISC_TRUE, ISC_TRUE);
-               else
+                             "%s with trust %d",
+                             val->frdataset.type == dns_rdatatype_ds ?
+                             "dsset" : "ds non-existance",
+                             val->frdataset.trust);
+               have_dsset = ISC_TF(val->frdataset.type == dns_rdatatype_ds);
+               name = dns_fixedname_name(&val->fname);
+               if ((val->attributes & VALATTR_INSECURITY) != 0 &&
+                   val->frdataset.covers == dns_rdatatype_ds &&
+                   val->frdataset.type == 0 &&
+                   isdelegation(name, &val->frdataset, DNS_R_NCACHENXRRSET)) {
+                       if (val->mustbesecure) {
+                               validator_log(val, ISC_LOG_WARNING,
+                                             "must be secure failure, no DS "
+                                             "and this is a delegation");
+                               result = DNS_R_MUSTBESECURE;
+                       } else if (val->view->dlv == NULL || DLVTRIED(val)) {
+                               markanswer(val, "dsvalidated");
+                               result = ISC_R_SUCCESS;;
+                       } else 
+                               result = startfinddlvsep(val, name);
+               } else if ((val->attributes & VALATTR_INSECURITY) != 0) {
+                       result = proveunsecure(val, have_dsset, ISC_TRUE);
+               } else
                        result = validatezonekey(val);
                if (result != DNS_R_WAIT)
                        validator_done(val, result);
@@ -839,10 +865,8 @@ authvalidated(isc_task_t *task, isc_event_t *event) {
 
                if (rdataset->type == dns_rdatatype_nsec &&
                    rdataset->trust == dns_trust_secure &&
-                   ((val->attributes & VALATTR_NEEDNODATA) != 0 ||
-                    (val->attributes & VALATTR_NEEDNOQNAME) != 0) &&
-                   (val->attributes & VALATTR_FOUNDNODATA) == 0 &&
-                   (val->attributes & VALATTR_FOUNDNOQNAME) == 0 &&
+                   (NEEDNODATA(val) || NEEDNOQNAME(val)) &&
+                   !FOUNDNODATA(val) && !FOUNDNOQNAME(val) &&
                    nsecnoexistnodata(val, val->event->name, devent->name,
                                      rdataset, &exists, &data, wild)
                                      == ISC_R_SUCCESS)
@@ -1227,11 +1251,14 @@ get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) {
                 * We have an rrset for the given keyname.
                 */
                val->keyset = &val->frdataset;
-               if (DNS_TRUST_PENDING(val->frdataset.trust) &&
+               if ((DNS_TRUST_PENDING(val->frdataset.trust) ||
+                    DNS_TRUST_ANSWER(val->frdataset.trust)) &&
                    dns_rdataset_isassociated(&val->fsigrdataset))
                {
                        /*
-                        * We know the key but haven't validated it yet.
+                        * We know the key but haven't validated it yet or
+                        * we have a key of trust answer but a DS/DLV
+                        * record for the zone may have been added.
                         */
                        result = create_validator(val, &siginfo->signer,
                                                  dns_rdatatype_dnskey,
@@ -1474,7 +1501,7 @@ validate(dns_validator_t *val, isc_boolean_t resume) {
                                              "must be secure failure");
                                return (DNS_R_MUSTBESECURE);
                        }
-                       markanswer(val);
+                       markanswer(val, "validate");
                        return (ISC_R_SUCCESS);
                }
 
@@ -1531,7 +1558,7 @@ validate(dns_validator_t *val, isc_boolean_t resume) {
                        }
                }
                val->key = NULL;
-               if ((val->attributes & VALATTR_NEEDNOQNAME) != 0) {
+               if (NEEDNOQNAME(val)) {
                        if (val->event->message == NULL) {
                                validator_log(val, ISC_LOG_DEBUG(3),
                                      "no message available for noqname proof");
@@ -1728,7 +1755,7 @@ dlv_validatezonekey(dns_validator_t *val) {
                }
                validator_log(val, ISC_LOG_DEBUG(3),
                              "no supported algorithm/digest (dlv)");
-               markanswer(val);
+               markanswer(val, "dlv_validatezonekey (2)");
                return (ISC_R_SUCCESS);
        } else
                return (DNS_R_NOVALIDSIG);
@@ -1782,7 +1809,11 @@ validatezonekey(dns_validator_t *val) {
                     result = dns_rdataset_next(val->event->sigrdataset))
                {
                        dns_keynode_t *keynode = NULL, *nextnode = NULL;
+                       dns_fixedname_t fixed;
+                       dns_name_t *found;
 
+                       dns_fixedname_init(&fixed);
+                       found = dns_fixedname_name(&fixed);
                        dns_rdata_reset(&sigrdata);
                        dns_rdataset_current(val->event->sigrdataset,
                                             &sigrdata);
@@ -1797,6 +1828,23 @@ validatezonekey(dns_validator_t *val) {
                                                          sig.algorithm,
                                                          sig.keyid,
                                                          &keynode);
+                       if (result == ISC_R_NOTFOUND &&
+                           dns_keytable_finddeepestmatch(val->keytable,
+                                 val->event->name, found) != ISC_R_SUCCESS) {
+                               if (val->mustbesecure) {
+                                       validator_log(val, ISC_LOG_WARNING,
+                                                     "must be secure failure, "
+                                                     "not beneath secure root");
+                                       return (DNS_R_MUSTBESECURE);
+                               } else
+                                       validator_log(val, ISC_LOG_DEBUG(3),
+                                                     "not beneath secure root");
+                               if (val->view->dlv == NULL || DLVTRIED(val)) {
+                                       markanswer(val, "validatezonekey (1)");
+                                       return (ISC_R_SUCCESS);
+                               }
+                               return (startfinddlvsep(val, dns_rootname));
+                       }
                        if (result == DNS_R_PARTIALMATCH ||
                            result == ISC_R_SUCCESS)
                                atsep = ISC_TRUE;
@@ -1865,7 +1913,8 @@ validatezonekey(dns_validator_t *val) {
                         * We have DS records.
                         */
                        val->dsset = &val->frdataset;
-                       if (DNS_TRUST_PENDING(val->frdataset.trust) &&
+                       if ((DNS_TRUST_PENDING(val->frdataset.trust) ||
+                            DNS_TRUST_ANSWER(val->frdataset.trust)) &&
                            dns_rdataset_isassociated(&val->fsigrdataset))
                        {
                                result = create_validator(val,
@@ -1928,7 +1977,7 @@ validatezonekey(dns_validator_t *val) {
                                      "must be secure failure");
                        return (DNS_R_MUSTBESECURE);
                }
-               markanswer(val);
+               markanswer(val, "validatezonekey (2)");
                return (ISC_R_SUCCESS);
        }
 
@@ -2074,7 +2123,7 @@ validatezonekey(dns_validator_t *val) {
                }
                validator_log(val, ISC_LOG_DEBUG(3),
                              "no supported algorithm/digest (DS)");
-               markanswer(val);
+               markanswer(val, "validatezonekey (3)");
                return (ISC_R_SUCCESS);
        } else
                return (DNS_R_NOVALIDSIG);
@@ -2100,6 +2149,80 @@ start_positive_validation(dns_validator_t *val) {
        return (validatezonekey(val));
 }
 
+/*%
+ * val_rdataset_first and val_rdataset_next provide iteration methods
+ * that hide whether we are iterating across a message or a  negative 
+ * cache rdataset.
+ */
+static isc_result_t
+val_rdataset_first(dns_validator_t *val, dns_name_t **namep,
+                  dns_rdataset_t **rdatasetp)
+{
+       dns_message_t *message = val->event->message;
+       isc_result_t result;
+
+       REQUIRE(rdatasetp != NULL);
+       REQUIRE(namep != NULL);
+       if (message == NULL) {
+               REQUIRE(*rdatasetp != NULL);
+               REQUIRE(*namep != NULL);
+       } else {
+               REQUIRE(*rdatasetp == NULL);
+               REQUIRE(*namep == NULL);
+       }
+
+       if (message != NULL) {
+               result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+               dns_message_currentname(message, DNS_SECTION_AUTHORITY, namep);
+               *rdatasetp = ISC_LIST_HEAD((*namep)->list);
+               INSIST(*rdatasetp != NULL);
+       } else {
+               result = dns_rdataset_first(val->event->rdataset);
+               if (result == ISC_R_SUCCESS)
+                       dns_ncache_current(val->event->rdataset, *namep,
+                                          *rdatasetp);
+       }
+       return (result);
+}
+
+static isc_result_t
+val_rdataset_next(dns_validator_t *val, dns_name_t **namep,
+                 dns_rdataset_t **rdatasetp) 
+{
+       dns_message_t *message = val->event->message;
+       isc_result_t result = ISC_R_SUCCESS;
+
+       REQUIRE(rdatasetp != NULL && *rdatasetp != NULL);
+       REQUIRE(namep != NULL && *namep != NULL);
+
+       if (message != NULL) {
+               dns_rdataset_t *rdataset = *rdatasetp;
+               rdataset = ISC_LIST_NEXT(rdataset, link);
+               if (rdataset == NULL) {
+                       *namep = NULL;
+                       result = dns_message_nextname(message,
+                                                     DNS_SECTION_AUTHORITY);
+                       if (result == ISC_R_SUCCESS) {
+                               dns_message_currentname(message,
+                                                       DNS_SECTION_AUTHORITY,
+                                                       namep);
+                               rdataset = ISC_LIST_HEAD((*namep)->list);
+                               INSIST(rdataset != NULL);
+                       }
+               }
+               *rdatasetp = rdataset;
+       } else {
+               dns_rdataset_disassociate(*rdatasetp);
+               result = dns_rdataset_next(val->event->rdataset);
+               if (result == ISC_R_SUCCESS) 
+                       dns_ncache_current(val->event->rdataset, *namep,
+                                          *rdatasetp);
+       }
+       return (result);
+}
+
 /*%
  * Look for NODATA at the wildcard and NOWILDCARD proofs in the
  * previously validated NSEC records.  As these proofs are mutually
@@ -2110,100 +2233,83 @@ start_positive_validation(dns_validator_t *val) {
  */
 static isc_result_t
 checkwildcard(dns_validator_t *val) {
-       dns_name_t *name, *wild;
-       dns_message_t *message = val->event->message;
+       dns_name_t *name, *wild, tname;
        isc_result_t result;
        isc_boolean_t exists, data;
        char namebuf[DNS_NAME_FORMATSIZE];
+       dns_rdataset_t *rdataset, trdataset;
 
+       dns_name_init(&tname, NULL);
+       dns_rdataset_init(&trdataset);
        wild = dns_fixedname_name(&val->wild);
        dns_name_format(wild, namebuf, sizeof(namebuf));
        validator_log(val, ISC_LOG_DEBUG(3), "in checkwildcard: %s", namebuf);
 
-       for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+       if (val->event->message == NULL) {
+               name = &tname;
+               rdataset = &trdataset;
+       } else {
+               name = NULL;
+               rdataset = NULL;
+       }
+       for (result = val_rdataset_first(val, &name, &rdataset);
             result == ISC_R_SUCCESS;
-            result = dns_message_nextname(message, DNS_SECTION_AUTHORITY))
+            result = val_rdataset_next(val, &name, &rdataset))
        {
-               dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+               if (rdataset->type != dns_rdatatype_nsec ||
+                   rdataset->trust != dns_trust_secure)
+                       continue;
+               val->nsecset = rdataset;
 
-               name = NULL;
-               dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+               if (rdataset->trust != dns_trust_secure)
+                       continue;
 
-               for (rdataset = ISC_LIST_HEAD(name->list);
-                    rdataset != NULL;
-                    rdataset = ISC_LIST_NEXT(rdataset, link))
+               if ((NEEDNODATA(val) || NEEDNOWILDCARD(val)) &&
+                   !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) &&
+                   nsecnoexistnodata(val, wild, name, rdataset,
+                                     &exists, &data, NULL)
+                                      == ISC_R_SUCCESS)
                {
-                       if (rdataset->type != dns_rdatatype_nsec)
-                               continue;
-                       val->nsecset = rdataset;
-
-                       for (sigrdataset = ISC_LIST_HEAD(name->list);
-                            sigrdataset != NULL;
-                            sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
-                       {
-                               if (sigrdataset->type == dns_rdatatype_rrsig &&
-                                   sigrdataset->covers == rdataset->type)
-                                       break;
-                       }
-                       if (sigrdataset == NULL)
-                               continue;
-
-                       if (rdataset->trust != dns_trust_secure)
-                               continue;
-
-                       if (((val->attributes & VALATTR_NEEDNODATA) != 0 ||
-                            (val->attributes & VALATTR_NEEDNOWILDCARD) != 0) &&
-                           (val->attributes & VALATTR_FOUNDNODATA) == 0 &&
-                           (val->attributes & VALATTR_FOUNDNOWILDCARD) == 0 &&
-                           nsecnoexistnodata(val, wild, name, rdataset,
-                                             &exists, &data, NULL)
-                                              == ISC_R_SUCCESS)
-                       {
-                               dns_name_t **proofs = val->event->proofs;
-                               if (exists && !data)
-                                       val->attributes |= VALATTR_FOUNDNODATA;
-                               if (exists && !data && NEEDNODATA(val))
-                                       proofs[DNS_VALIDATOR_NODATAPROOF] =
-                                                        name;
-                               if (!exists)
-                                       val->attributes |=
-                                                VALATTR_FOUNDNOWILDCARD;
-                               if (!exists && NEEDNOQNAME(val))
-                                       proofs[DNS_VALIDATOR_NOWILDCARDPROOF] =
-                                                        name;
-                               return (ISC_R_SUCCESS);
-                       }
+                       dns_name_t **proofs = val->event->proofs;
+                       if (exists && !data)
+                               val->attributes |= VALATTR_FOUNDNODATA;
+                       if (exists && !data && NEEDNODATA(val))
+                               proofs[DNS_VALIDATOR_NODATAPROOF] =
+                                                name;
+                       if (!exists)
+                               val->attributes |=
+                                        VALATTR_FOUNDNOWILDCARD;
+                       if (!exists && NEEDNOQNAME(val))
+                               proofs[DNS_VALIDATOR_NOWILDCARDPROOF] =
+                                                name;
+                       if (dns_rdataset_isassociated(&trdataset))
+                               dns_rdataset_disassociate(&trdataset);
+                       return (ISC_R_SUCCESS);
                }
        }
        if (result == ISC_R_NOMORE)
                result = ISC_R_SUCCESS;
+       if (dns_rdataset_isassociated(&trdataset))
+               dns_rdataset_disassociate(&trdataset);
        return (result);
 }
 
 /*%
- * Prove a negative answer is good or that there is a NOQNAME when the
- * answer is from a wildcard.
- *
- * Loop through the authority section looking for NODATA, NOWILDCARD
- * and NOQNAME proofs in the NSEC records by calling authvalidated().
- *
- * If the required proofs are found we are done.
- *
- * If the proofs are not found attempt to prove this is a unsecure
- * response.
+ * Validate the authority section records.
  */
 static isc_result_t
-nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
+validate_authority(dns_validator_t *val, isc_boolean_t resume) {
        dns_name_t *name;
        dns_message_t *message = val->event->message;
        isc_result_t result;
 
-       if (!resume)
+       if (!resume) {
                result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
-       else {
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+       } else
                result = ISC_R_SUCCESS;
-               validator_log(val, ISC_LOG_DEBUG(3), "resuming nsecvalidate");
-       }
 
        for (;
             result == ISC_R_SUCCESS;
@@ -2266,16 +2372,123 @@ nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
                        result = create_validator(val, name, rdataset->type,
                                                  rdataset, sigrdataset,
                                                  authvalidated,
-                                                 "nsecvalidate");
+                                                 "validate_authority");
                        if (result != ISC_R_SUCCESS)
                                return (result);
                        val->authcount++;
                        return (DNS_R_WAIT);
+               }
+       }
+       if (result == ISC_R_NOMORE)
+               result = ISC_R_SUCCESS;
+       return (result);
+}
+
+/*%
+ * Validate the ncache elements.
+ */
+static isc_result_t
+validate_ncache(dns_validator_t *val, isc_boolean_t resume) {
+       dns_name_t *name;
+       isc_result_t result;
+
+       if (!resume) {
+               result = dns_rdataset_first(val->event->rdataset);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+       } else
+               result = dns_rdataset_next(val->event->rdataset);
+
+       for (;
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(val->event->rdataset))
+       {
+               dns_rdataset_t *rdataset, *sigrdataset = NULL;
+
+               if (dns_rdataset_isassociated(&val->frdataset))
+                       dns_rdataset_disassociate(&val->frdataset);
+               if (dns_rdataset_isassociated(&val->fsigrdataset))
+                       dns_rdataset_disassociate(&val->fsigrdataset);
+
+               dns_fixedname_init(&val->fname);
+               name = dns_fixedname_name(&val->fname);
+               rdataset = &val->frdataset;
+               dns_ncache_current(val->event->rdataset, name, rdataset);
 
+               if (val->frdataset.type == dns_rdatatype_rrsig)
+                       continue;
+       
+               result = dns_ncache_getsigrdataset(val->event->rdataset, name,
+                                                  rdataset->type,
+                                                  &val->fsigrdataset);
+               if (result == ISC_R_SUCCESS)
+                       sigrdataset = &val->fsigrdataset;
+
+               /*
+                * If a signed zone is missing the zone key, bad
+                * things could happen.  A query for data in the zone
+                * would lead to a query for the zone key, which
+                * would return a negative answer, which would contain
+                * an SOA and an NSEC signed by the missing key, which
+                * would trigger another query for the DNSKEY (since
+                * the first one is still in progress), and go into an
+                * infinite loop.  Avoid that.
+                */
+               if (val->event->type == dns_rdatatype_dnskey &&
+                   dns_name_equal(name, val->event->name))
+               {
+                       dns_rdata_t nsec = DNS_RDATA_INIT;
+
+                       if (rdataset->type != dns_rdatatype_nsec)
+                               continue;
+
+                       result = dns_rdataset_first(rdataset);
+                       if (result != ISC_R_SUCCESS)
+                               return (result);
+                       dns_rdataset_current(rdataset, &nsec);
+                       if (dns_nsec_typepresent(&nsec,
+                                               dns_rdatatype_soa))
+                               continue;
                }
+               val->currentset = rdataset;
+               result = create_validator(val, name, rdataset->type,
+                                         rdataset, sigrdataset,
+                                         authvalidated,
+                                         "validate_ncache");
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+               val->authcount++;
+               return (DNS_R_WAIT);
        }
        if (result == ISC_R_NOMORE)
                result = ISC_R_SUCCESS;
+       return (result);
+}
+
+/*%
+ * Prove a negative answer is good or that there is a NOQNAME when the
+ * answer is from a wildcard.
+ *
+ * Loop through the authority section looking for NODATA, NOWILDCARD
+ * and NOQNAME proofs in the NSEC records by calling authvalidated().
+ *
+ * If the required proofs are found we are done.
+ *
+ * If the proofs are not found attempt to prove this is a unsecure
+ * response.
+ */
+static isc_result_t
+nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
+       isc_result_t result;
+
+       if (resume)
+               validator_log(val, ISC_LOG_DEBUG(3), "resuming nsecvalidate");
+       
+       if (val->event->message == NULL) 
+               result = validate_ncache(val, resume);
+       else
+               result = validate_authority(val, resume);
+
        if (result != ISC_R_SUCCESS)
                return (result);
 
@@ -2302,23 +2515,20 @@ nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
        /*
         * Do we need to check for the wildcard?
         */
-       if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
-           (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
-             (val->attributes & VALATTR_FOUNDNODATA) == 0) ||
-            (val->attributes & VALATTR_NEEDNOWILDCARD) != 0)) {
+       if (FOUNDNOQNAME(val) &&
+           ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) {
                result = checkwildcard(val);
                if (result != ISC_R_SUCCESS)
                        return (result);
        }
 
-       if (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
-            (val->attributes & VALATTR_FOUNDNODATA) != 0) ||
-           ((val->attributes & VALATTR_NEEDNOQNAME) != 0 &&
-            (val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
-            (val->attributes & VALATTR_NEEDNOWILDCARD) != 0 &&
-            (val->attributes & VALATTR_FOUNDNOWILDCARD) != 0)) {
+       if ((NEEDNODATA(val) && FOUNDNODATA(val)) ||
+           (NEEDNOQNAME(val) && FOUNDNOQNAME(val) &&
+            NEEDNOWILDCARD(val) && FOUNDNOWILDCARD(val))) {
                validator_log(val, ISC_LOG_DEBUG(3),
                              "nonexistence proof(s) found");
+               if (val->event->message == NULL)
+                       marksecure(val->event);
                return (ISC_R_SUCCESS);
        }
 
@@ -2386,7 +2596,7 @@ dlvvalidated(isc_task_t *task, isc_event_t *event) {
                if (dlv_algorithm_supported(val))
                        dlv_validator_start(val);
                else {
-                       markanswer(val);
+                       markanswer(val, "dlvvalidated");
                        validator_done(val, ISC_R_SUCCESS);
                }
        } else {
@@ -2455,7 +2665,7 @@ dlvfetched(isc_task_t *task, isc_event_t *event) {
                        validator_log(val, ISC_LOG_DEBUG(3),
                                      "DLV %s found with no supported algorithms",
                                      namebuf);
-                       markanswer(val);
+                       markanswer(val, "dlvfetched (1)");
                        validator_done(val, ISC_R_SUCCESS);
                }
        } else if (eresult == DNS_R_NXRRSET ||
@@ -2474,12 +2684,12 @@ dlvfetched(isc_task_t *task, isc_event_t *event) {
                                validator_log(val, ISC_LOG_DEBUG(3),
                                              "DLV %s found with no supported "
                                              "algorithms", namebuf);
-                               markanswer(val);
+                               markanswer(val, "dlvfetched (2)");
                                validator_done(val, ISC_R_SUCCESS);
                        }
                } else if (result == ISC_R_NOTFOUND) {
                        validator_log(val, ISC_LOG_DEBUG(3), "DLV not found");
-                       markanswer(val);
+                       markanswer(val, "dlvfetched (3)");
                        validator_done(val, ISC_R_SUCCESS);
                } else {
                        validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s",
@@ -2529,7 +2739,7 @@ startfinddlvsep(dns_validator_t *val, dns_name_t *unsecure) {
        result = finddlvsep(val, ISC_FALSE);
        if (result == ISC_R_NOTFOUND) {
                validator_log(val, ISC_LOG_DEBUG(3), "DLV not found");
-               markanswer(val);
+               markanswer(val, "startfinddlvsep (1)");
                return (ISC_R_SUCCESS);
        }
        if (result != ISC_R_SUCCESS) {
@@ -2546,7 +2756,7 @@ startfinddlvsep(dns_validator_t *val, dns_name_t *unsecure) {
        }
        validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found with no supported "
                      "algorithms", namebuf);
-       markanswer(val);
+       markanswer(val, "startfinddlvsep (2)");
        validator_done(val, ISC_R_SUCCESS);
        return (ISC_R_SUCCESS);
 }
@@ -2731,7 +2941,7 @@ proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
                                goto out;
                        }
                        if (val->view->dlv == NULL || DLVTRIED(val)) {
-                               markanswer(val);
+                               markanswer(val, "proveunsecure (1)");
                                return (ISC_R_SUCCESS);
                        }
                        return (startfinddlvsep(val, dns_rootname));
@@ -2769,7 +2979,7 @@ proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
                                      "no supported algorithm/digest (%s/DS)",
                                      namebuf);
                        if (val->view->dlv == NULL || DLVTRIED(val)) {
-                               markanswer(val);
+                               markanswer(val, "proveunsecure (2)");
                                result = ISC_R_SUCCESS;
                                goto out;
                        }
@@ -2799,18 +3009,23 @@ proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
                              namebuf);
 
                result = view_find(val, tname, dns_rdatatype_ds);
-
                if (result == DNS_R_NXRRSET || result == DNS_R_NCACHENXRRSET) {
                        /*
                         * There is no DS.  If this is a delegation,
                         * we maybe done.
                         */
-                       if (DNS_TRUST_PENDING(val->frdataset.trust)) {
-                               result = create_fetch(val, tname,
-                                                     dns_rdatatype_ds,
-                                                     dsfetched2,
-                                                     "proveunsecure");
-                               if (result != ISC_R_SUCCESS)
+                        /*
+                         * If we have "trust == answer" then this namespace
+                         * has switched from insecure to should be secure.
+                         */
+                        if (DNS_TRUST_PENDING(val->frdataset.trust) ||
+                            DNS_TRUST_ANSWER(val->frdataset.trust)) {
+                                result = create_validator(val, tname,
+                                                          dns_rdatatype_ds,
+                                                          &val->frdataset,
+                                                          NULL, dsvalidated,
+                                                          "proveunsecure");
+                                if (result != ISC_R_SUCCESS)
                                        goto out;
                                return (DNS_R_WAIT);
                        }
@@ -2831,7 +3046,7 @@ proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
                                        return (DNS_R_MUSTBESECURE);
                                }
                                if (val->view->dlv == NULL || DLVTRIED(val)) {
-                                       markanswer(val);
+                                       markanswer(val, "proveunsecure (3)");
                                        return (ISC_R_SUCCESS);
                                }
                                return (startfinddlvsep(val, tname));
@@ -2856,7 +3071,8 @@ proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
                                        }
                                        if (val->view->dlv == NULL ||
                                            DLVTRIED(val)) {
-                                               markanswer(val);
+                                               markanswer(val,
+                                                          "proveunsecure (5)");
                                                result = ISC_R_SUCCESS;
                                                goto out;
                                        }
@@ -2870,6 +3086,9 @@ proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
                                result = DNS_R_NOVALIDSIG;
                                goto out;
                        }
+                       /*
+                        * Validate / re-validate answer.
+                        */
                        result = create_validator(val, tname, dns_rdatatype_ds,
                                                  &val->frdataset,
                                                  &val->fsigrdataset,
@@ -2914,8 +3133,10 @@ proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
                } else if (result == DNS_R_BROKENCHAIN)
                        return (result);
        }
+
+       /* Couldn't complete insecurity proof */
        validator_log(val, ISC_LOG_DEBUG(3), "insecurity proof failed");
-       return (DNS_R_NOTINSECURE); /* Couldn't complete insecurity proof */
+       return (DNS_R_NOTINSECURE);
 
  out:
        if (dns_rdataset_isassociated(&val->frdataset))
@@ -3008,7 +3229,8 @@ validator_start(isc_task_t *task, isc_event_t *event) {
                        if (result == DNS_R_NOTINSECURE)
                                result = saved_result;
                }
-       } else if (val->event->rdataset != NULL) {
+       } else if (val->event->rdataset != NULL &&
+                  val->event->rdataset->type != 0) {
                /*
                 * This is either an unsecure subdomain or a response from
                 * a broken server.
@@ -3034,6 +3256,21 @@ validator_start(isc_task_t *task, isc_event_t *event) {
                } else
                        val->attributes |= VALATTR_NEEDNODATA;
                result = nsecvalidate(val, ISC_FALSE);
+       } else if (val->event->rdataset != NULL &&
+                   val->event->rdataset->type == 0)
+       {
+               /*
+                * This is a nonexistence validation.
+                */
+               validator_log(val, ISC_LOG_DEBUG(3),
+                             "attempting negative response validation");
+
+               if (val->event->rdataset->covers == dns_rdatatype_any) {
+                       val->attributes |= VALATTR_NEEDNOQNAME;
+                       val->attributes |= VALATTR_NEEDNOWILDCARD;
+               } else
+                       val->attributes |= VALATTR_NEEDNODATA;
+               result = nsecvalidate(val, ISC_FALSE);
        } else {
                /*
                 * This shouldn't happen.