]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
2890. [bug] Handle the introduction of new trusted-keys and
authorMark Andrews <marka@isc.org>
Fri, 14 May 2010 00:13:43 +0000 (00:13 +0000)
committerMark Andrews <marka@isc.org>
Fri, 14 May 2010 00:13:43 +0000 (00:13 +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 daa875f4288870c95b98730e7addffe57a56341f..c93459a14c3853467a192d6a0603d369d2c5c488 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]
+
 2889.  [bug]           Elements of the grammar where not properly reported.
                        [RT #21046]
 
index 8f5f8f21a5e4994989fce136012089c3d04e65f3..500a26f1eadea20a8d049e620962d272d908c402 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: ncache.h,v 1.27 2009/12/30 23:49:14 tbox Exp $ */
+/* $Id: ncache.h,v 1.28 2010/05/14 00:13:43 marka Exp $ */
 
 #ifndef DNS_NCACHE_H
 #define DNS_NCACHE_H 1
@@ -161,6 +161,13 @@ 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);
index 7e26a4133bb71e15ff47bf996f9d40f3c8e389d8..ae690e14e6460e575bc72ad275af979c07ae0d21 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: types.h,v 1.139 2009/12/30 08:02:23 jinmei Exp $ */
+/* $Id: types.h,v 1.140 2010/05/14 00:13:43 marka Exp $ */
 
 #ifndef DNS_TYPES_H
 #define DNS_TYPES_H 1
@@ -321,6 +321,7 @@ enum {
 #define DNS_TRUST_ADDITIONAL(x)                ((x) == dns_trust_additional || \
                                         (x) == dns_trust_pending_additional)
 #define DNS_TRUST_GLUE(x)              ((x) == dns_trust_glue)
+#define DNS_TRUST_ANSWER(x)            ((x) == dns_trust_answer)
 
 
 /*%
index 5224a29850edc807825b9d60b789963b3371db3b..a976bdce9ebe9a3e8758b4f08a44ea1e1698b516 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: ncache.c,v 1.45 2010/02/25 05:08:01 tbox Exp $ */
+/* $Id: ncache.c,v 1.46 2010/05/14 00:13:43 marka Exp $ */
 
 /*! \file */
 
@@ -40,6 +40,7 @@
  *
  *     owner name
  *     type
+ *     trust
  *     rdata count
  *             rdata length                    These two occur 'rdata count'
  *             rdata                           times.
@@ -189,6 +190,8 @@ dns_ncache_addoptout(dns_message_t *message, dns_db_t *cache,
                                                return (ISC_R_NOSPACE);
                                        isc_buffer_putuint16(&buffer,
                                                             rdataset->type);
+                                       isc_buffer_putuint8(&buffer,
+                                                            rdataset->trust);
                                        /*
                                         * Copy the rdataset into the buffer.
                                         */
@@ -335,8 +338,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++) {
@@ -506,6 +510,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,
@@ -520,7 +531,7 @@ static dns_rdatasetmethods_t rdataset_methods = {
        NULL,
        NULL,
        NULL,
-       NULL,
+       rdataset_settrust,
        NULL
 };
 
@@ -534,6 +545,8 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
        isc_buffer_t source;
        dns_name_t tname;
        dns_rdatatype_t ttype;
+       dns_trust_t trust = dns_trust_none;
+       dns_rdataset_t clone;
 
        REQUIRE(ncacherdataset != NULL);
        REQUIRE(ncacherdataset->type == 0);
@@ -541,9 +554,11 @@ 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);
+       dns_rdataset_init(&clone);
+       dns_rdataset_clone(ncacherdataset, &clone);
+       result = dns_rdataset_first(&clone);
        while (result == ISC_R_SUCCESS) {
-               dns_rdataset_current(ncacherdataset, &rdata);
+               dns_rdataset_current(&clone, &rdata);
                isc_buffer_init(&source, rdata.data, rdata.length);
                isc_buffer_add(&source, rdata.length);
                dns_name_init(&tname, NULL);
@@ -553,16 +568,19 @@ 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(ncacherdataset);
+               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)
@@ -575,7 +593,108 @@ dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
        rdataset->type = type;
        rdataset->covers = 0;
        rdataset->ttl = ncacherdataset->ttl;
-       rdataset->trust = ncacherdataset->trust;
+       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);
+                       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);
+
+       INSIST(remaining.length != 0);
+
+       rdataset->methods = &rdataset_methods;
+       rdataset->rdclass = ncacherdataset->rdclass;
+       rdataset->type = dns_rdatatype_rrsig;
+       rdataset->covers = covers;
+       rdataset->ttl = ncacherdataset->ttl;
+       rdataset->trust = trust;
        rdataset->private1 = NULL;
        rdataset->private2 = NULL;
 
@@ -595,6 +714,7 @@ 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;
@@ -619,8 +739,10 @@ dns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found,
        isc_buffer_forward(&source, found->length);
        remaining.length -= found->length;
 
-       INSIST(remaining.length >= 4);
+       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;
@@ -645,7 +767,7 @@ dns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found,
        } else
                rdataset->covers = 0;
        rdataset->ttl = ncacherdataset->ttl;
-       rdataset->trust = ncacherdataset->trust;
+       rdataset->trust = trust;
        rdataset->private1 = NULL;
        rdataset->private2 = NULL;
 
index f183d0b50fc8139f286fcc0956168d1b5b8cb9a5..f28a62467dbaceff5c763e2ee1e2a24c683ef17b 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: validator.c,v 1.190 2010/04/21 05:45:47 marka Exp $ */
+/* $Id: validator.c,v 1.191 2010/05/14 00:13:43 marka Exp $ */
 
 #include <config.h>
 
 #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 FOUNDCLOSEST(val) ((val->attributes & VALATTR_FOUNDCLOSEST) != 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)
@@ -174,8 +179,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)
@@ -186,7 +191,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
@@ -564,7 +570,7 @@ dsfetched2(isc_task_t *task, isc_event_t *event) {
                                              " and this is a delegation");
                                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);
@@ -690,11 +696,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);
@@ -1139,29 +1166,7 @@ nsec3noexistnodata(dns_validator_t *val, dns_name_t* name,
                                dns_name_copy(qname, nearest, NULL);
                                *setnearest = ISC_TRUE;
                        }
-#if 0
-                       /*
-                        * The closest encloser may be the zone name.
-                        */
-                       if (closest != NULL &&
-                           dns_name_countlabels(closest) == 0 &&
-                           !dns_nsec3_typepresent(&rdata, dns_rdatatype_ds) &&
-                           !dns_nsec3_typepresent(&rdata, dns_rdatatype_dname) &&
-                           (dns_nsec3_typepresent(&rdata, dns_rdatatype_soa) ||
-                            !dns_nsec3_typepresent(&rdata, dns_rdatatype_ns)))
-                       {
-                               char namebuf[DNS_NAME_FORMATSIZE];
 
-                               dns_name_format(zone, namebuf,
-                                               sizeof(namebuf));
-                               validator_log(val, ISC_LOG_DEBUG(3),
-                                             "NSEC3 potential closest "
-                                              "encloser from zone name: '%s'",
-                                              namebuf);
-                               dns_name_copy(zone, closest, NULL);
-                               *setclosest = ISC_TRUE;
-                       }
-#endif
                        *exists = ISC_FALSE;
                        *data = ISC_FALSE;
                        if (optout != NULL) {
@@ -1237,10 +1242,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)
@@ -1650,11 +1653,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,
@@ -1899,7 +1905,7 @@ validate(dns_validator_t *val, isc_boolean_t resume) {
                                              " data as insecure also.");
                                return (DNS_R_MUSTBESECURE);
                        }
-                       markanswer(val);
+                       markanswer(val, "validate");
                        return (ISC_R_SUCCESS);
                }
 
@@ -1958,7 +1964,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");
@@ -2157,7 +2163,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);
@@ -2212,7 +2218,11 @@ validatezonekey(dns_validator_t *val) {
                     result = dns_rdataset_next(val->event->sigrdataset))
                {
                        dns_keynode_t *keynode = 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);
@@ -2227,6 +2237,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;
@@ -2308,7 +2335,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,
@@ -2372,7 +2400,7 @@ validatezonekey(dns_validator_t *val) {
                                      " insecure DS");
                        return (DNS_R_MUSTBESECURE);
                }
-               markanswer(val);
+               markanswer(val, "validatezonekey (2)");
                return (ISC_R_SUCCESS);
        }
 
@@ -2519,7 +2547,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 {
                validator_log(val, ISC_LOG_INFO,
@@ -2548,6 +2576,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
@@ -2559,12 +2661,14 @@ start_positive_validation(dns_validator_t *val) {
 static isc_result_t
 checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename)
 {
-       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);
 
        if (dns_name_countlabels(wild) == 0) {
@@ -2576,103 +2680,91 @@ checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename)
        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;
-
-               name = NULL;
-               dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+               if (rdataset->type != type ||
+                   rdataset->trust != dns_trust_secure)
+                       continue;
 
-               for (rdataset = ISC_LIST_HEAD(name->list);
-                    rdataset != NULL;
-                    rdataset = ISC_LIST_NEXT(rdataset, link))
+               if (rdataset->type == dns_rdatatype_nsec &&
+                   (NEEDNODATA(val) || NEEDNOWILDCARD(val)) &&
+                   !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) &&
+                   nsecnoexistnodata(val, wild, name, rdataset,
+                                     &exists, &data, NULL)
+                                      == ISC_R_SUCCESS)
                {
-                       if (rdataset->type != type)
-                               continue;
-
-                       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 (rdataset->type == dns_rdatatype_nsec &&
-                           ((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 (rdataset->type == dns_rdatatype_nsec3 &&
-                           ((val->attributes & VALATTR_NEEDNODATA) != 0 ||
-                            (val->attributes & VALATTR_NEEDNOWILDCARD) != 0) &&
-                           (val->attributes & VALATTR_FOUNDNODATA) == 0 &&
-                           (val->attributes & VALATTR_FOUNDNOWILDCARD) == 0 &&
-                           nsec3noexistnodata(val, wild, name, rdataset,
-                                              zonename, &exists, &data,
-                                              NULL, NULL, NULL, NULL, NULL,
-                                              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);
-                       }
+               if (rdataset->type == dns_rdatatype_nsec3 &&
+                   (NEEDNODATA(val) || NEEDNOWILDCARD(val)) &&
+                   !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) &&
+                   nsec3noexistnodata(val, wild, name, rdataset,
+                                      zonename, &exists, &data,
+                                      NULL, NULL, NULL, NULL, NULL,
+                                      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;
+                       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);
 }
 
-
 static isc_result_t
 findnsec3proofs(dns_validator_t *val) {
-       dns_name_t *name;
-       dns_message_t *message = val->event->message;
+       dns_name_t *name, tname;
        isc_result_t result;
        isc_boolean_t exists, data, optout, unknown;
        isc_boolean_t setclosest, setnearest;
        dns_fixedname_t fclosest, fnearest, fzonename;
        dns_name_t *closest, *nearest, *zonename;
        dns_name_t **proofs = val->event->proofs;
+       dns_rdataset_t *rdataset, trdataset;
 
+       dns_name_init(&tname, NULL);
+       dns_rdataset_init(&trdataset);
        dns_fixedname_init(&fclosest);
        dns_fixedname_init(&fnearest);
        dns_fixedname_init(&fzonename);
@@ -2680,43 +2772,31 @@ findnsec3proofs(dns_validator_t *val) {
        nearest = dns_fixedname_name(&fnearest);
        zonename = dns_fixedname_name(&fzonename);
 
-       for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
-            result == ISC_R_SUCCESS;
-            result = dns_message_nextname(message, DNS_SECTION_AUTHORITY))
-       {
-               dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
-
+       if (val->event->message == NULL) {
+               name = &tname;
+               rdataset = &trdataset;
+       } else {
                name = NULL;
-               dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
-
-               for (rdataset = ISC_LIST_HEAD(name->list);
-                    rdataset != NULL;
-                    rdataset = ISC_LIST_NEXT(rdataset, link))
-               {
-                       if (rdataset->type != dns_rdatatype_nsec3)
-                               continue;
-
-                       for (sigrdataset = ISC_LIST_HEAD(name->list);
-                            sigrdataset != NULL;
-                            sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
-                       {
-                               if (sigrdataset->type == dns_rdatatype_rrsig &&
-                                   sigrdataset->covers == dns_rdatatype_nsec3)
-                                       break;
-                       }
-                       if (sigrdataset == NULL)
-                               continue;
+               rdataset = NULL;
+       }
 
-                       if (rdataset->trust != dns_trust_secure)
-                               continue;
+       for (result = val_rdataset_first(val, &name, &rdataset);
+            result == ISC_R_SUCCESS;
+            result = val_rdataset_next(val, &name, &rdataset))
+       {
+               if (rdataset->type != dns_rdatatype_nsec3 ||
+                   rdataset->trust != dns_trust_secure)
+                       continue;
 
-                       result = nsec3noexistnodata(val, val->event->name,
-                                                   name, rdataset,
-                                                   zonename, NULL, NULL, NULL,
-                                                   NULL, NULL, NULL, NULL,
-                                                   NULL);
-                       if (result != ISC_R_IGNORE && result != ISC_R_SUCCESS)
-                               return (result);
+               result = nsec3noexistnodata(val, val->event->name,
+                                           name, rdataset,
+                                           zonename, NULL, NULL, NULL,
+                                           NULL, NULL, NULL, NULL,
+                                           NULL);
+               if (result != ISC_R_IGNORE && result != ISC_R_SUCCESS) {
+                       if (dns_rdataset_isassociated(&trdataset))
+                               dns_rdataset_disassociate(&trdataset);
+                       return (result);
                }
        }
        if (result != ISC_R_NOMORE)
@@ -2725,68 +2805,43 @@ findnsec3proofs(dns_validator_t *val) {
        if (dns_name_countlabels(zonename) == 0)
                return (ISC_R_SUCCESS);
 
-       for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+       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;
-
-               name = NULL;
-               dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
-
-               for (rdataset = ISC_LIST_HEAD(name->list);
-                    rdataset != NULL;
-                    rdataset = ISC_LIST_NEXT(rdataset, link))
-               {
-                       if (rdataset->type != dns_rdatatype_nsec3)
-                               continue;
-
-                       for (sigrdataset = ISC_LIST_HEAD(name->list);
-                            sigrdataset != NULL;
-                            sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
-                       {
-                               if (sigrdataset->type == dns_rdatatype_rrsig &&
-                                   sigrdataset->covers == dns_rdatatype_nsec3)
-                                       break;
-                       }
-                       if (sigrdataset == NULL)
-                               continue;
-
-                       if (rdataset->trust != dns_trust_secure)
-                               continue;
+               if (rdataset->type != dns_rdatatype_nsec3 ||
+                   rdataset->trust != dns_trust_secure)
+                       continue;
 
-                       /*
-                        * We process all NSEC3 records to find the closest
-                        * encloser and nearest name to the closest encloser.
-                        */
-                       setclosest = setnearest = ISC_FALSE;
-                       optout = ISC_FALSE;
-                       unknown = ISC_FALSE;
-                       result = nsec3noexistnodata(val, val->event->name,
-                                                   name, rdataset,
-                                                   zonename, &exists,
-                                                   &data, &optout, &unknown,
-                                                   &setclosest, &setnearest,
-                                                   closest, nearest);
-                       if (setclosest)
-                               proofs[DNS_VALIDATOR_CLOSESTENCLOSER] = name;
-                       if (unknown)
-                               val->attributes |= VALATTR_FOUNDUNKNOWN;
-                       if (result != ISC_R_SUCCESS)
-                               continue;
-                       if (exists && !data && NEEDNODATA(val)) {
-                               val->attributes |= VALATTR_FOUNDNODATA;
-                               proofs[DNS_VALIDATOR_NODATAPROOF] = name;
-                       }
-                       if (!exists && setnearest) {
-                               val->attributes |= VALATTR_FOUNDNOQNAME;
-                               proofs[DNS_VALIDATOR_NOQNAMEPROOF] = name;
-                               if (optout)
-                                       val->attributes |= VALATTR_FOUNDOPTOUT;
-                       }
+               /*
+                * We process all NSEC3 records to find the closest
+                * encloser and nearest name to the closest encloser.
+                */
+               setclosest = setnearest = ISC_FALSE;
+               optout = ISC_FALSE;
+               unknown = ISC_FALSE;
+               (void)nsec3noexistnodata(val, val->event->name, name, rdataset,
+                                        zonename, &exists, &data, &optout,
+                                        &unknown, &setclosest, &setnearest,
+                                        closest, nearest);
+               if (setclosest)
+                       proofs[DNS_VALIDATOR_CLOSESTENCLOSER] = name;
+               if (unknown)
+                       val->attributes |= VALATTR_FOUNDUNKNOWN;
+               if (result != ISC_R_SUCCESS)
+                       continue;
+               if (exists && !data && NEEDNODATA(val)) {
+                       val->attributes |= VALATTR_FOUNDNODATA;
+                       proofs[DNS_VALIDATOR_NODATAPROOF] = name;
+               }
+               if (!exists && setnearest) {
+                       val->attributes |= VALATTR_FOUNDNOQNAME;
+                       proofs[DNS_VALIDATOR_NOQNAMEPROOF] = name;
+                       if (optout)
+                               val->attributes |= VALATTR_FOUNDOPTOUT;
                }
        }
-       if (result != ISC_R_NOMORE)
+       if (result == ISC_R_NOMORE)
                result = ISC_R_SUCCESS;
 
        /*
@@ -2813,11 +2868,8 @@ findnsec3proofs(dns_validator_t *val) {
        /*
         * Do we need to check for the wildcard?
         */
-       if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
-           (val->attributes & VALATTR_FOUNDCLOSEST) != 0 &&
-           (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
-             (val->attributes & VALATTR_FOUNDNODATA) == 0) ||
-            (val->attributes & VALATTR_NEEDNOWILDCARD) != 0)) {
+       if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) &&
+           ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) {
                result = checkwildcard(val, dns_rdatatype_nsec3, zonename);
                if (result != ISC_R_SUCCESS)
                        return (result);
@@ -2826,29 +2878,20 @@ findnsec3proofs(dns_validator_t *val) {
 }
 
 /*%
- * 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;
@@ -2911,16 +2954,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);
 
@@ -2928,29 +3078,26 @@ nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
         * Do we only need to check for NOQNAME?  To get here we must have
         * had a secure wildcard answer.
         */
-       if ((val->attributes & VALATTR_NEEDNODATA) == 0 &&
-           (val->attributes & VALATTR_NEEDNOWILDCARD) == 0 &&
-           (val->attributes & VALATTR_NEEDNOQNAME) != 0) {
-               if ((val->attributes & VALATTR_FOUNDNOQNAME) == 0)
+       if (!NEEDNODATA(val) && !NEEDNOWILDCARD(val) && NEEDNOQNAME(val)) {
+               if (!FOUNDNOQNAME(val))
                        findnsec3proofs(val);
-               if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
-                   (val->attributes & VALATTR_FOUNDCLOSEST) != 0) {
+               if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val)) {
                        validator_log(val, ISC_LOG_DEBUG(3),
                                      "marking as secure, noqname proof found");
                        marksecure(val->event);
                        return (ISC_R_SUCCESS);
-               } else if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0 &&
+               } else if (FOUNDOPTOUT(val) &&
                           dns_name_countlabels(dns_fixedname_name(&val->wild))
                                         != 0) {
                        validator_log(val, ISC_LOG_DEBUG(3),
                                      "optout proof found");
                        val->event->optout = ISC_TRUE;
-                       markanswer(val);
+                       markanswer(val, "nsecvalidate (1)");
                        return (ISC_R_SUCCESS);
                } else if ((val->attributes & VALATTR_FOUNDUNKNOWN) != 0) {
                        validator_log(val, ISC_LOG_DEBUG(3),
                                      "unknown NSEC3 hash algorithm found");
-                       markanswer(val);
+                       markanswer(val, "nsecvalidate (2)");
                        return (ISC_R_SUCCESS);
                }
                validator_log(val, ISC_LOG_DEBUG(3),
@@ -2958,35 +3105,29 @@ nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
                return (DNS_R_NOVALIDNSEC);
        }
 
-       if ((val->attributes & VALATTR_FOUNDNOQNAME) == 0 &&
-           (val->attributes & VALATTR_FOUNDNODATA) == 0)
+       if (!FOUNDNOQNAME(val) && !FOUNDNODATA(val))
                findnsec3proofs(val);
 
        /*
         * Do we need to check for the wildcard?
         */
-       if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
-           (val->attributes & VALATTR_FOUNDCLOSEST) != 0 &&
-           (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
-             (val->attributes & VALATTR_FOUNDNODATA) == 0) ||
-            (val->attributes & VALATTR_NEEDNOWILDCARD) != 0)) {
+       if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) &&
+           ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) {
                result = checkwildcard(val, dns_rdatatype_nsec, NULL);
                if (result != ISC_R_SUCCESS)
                        return (result);
        }
 
-       if (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
-            ((val->attributes & VALATTR_FOUNDNODATA) != 0 ||
-             (val->attributes & VALATTR_FOUNDOPTOUT) != 0)) ||
-           ((val->attributes & VALATTR_NEEDNOQNAME) != 0 &&
-            (val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
-            (val->attributes & VALATTR_NEEDNOWILDCARD) != 0 &&
-            (val->attributes & VALATTR_FOUNDNOWILDCARD) != 0 &&
-            (val->attributes & VALATTR_FOUNDCLOSEST) != 0)) {
+       if ((NEEDNODATA(val) && (FOUNDNODATA(val) || FOUNDOPTOUT(val))) ||
+           (NEEDNOQNAME(val) && FOUNDNOQNAME(val) &&
+            NEEDNOWILDCARD(val) && FOUNDNOWILDCARD(val) &&
+            FOUNDCLOSEST(val))) {
                if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0)
                        val->event->optout = ISC_TRUE;
                validator_log(val, ISC_LOG_DEBUG(3),
                              "nonexistence proof(s) found");
+               if (val->event->message == NULL)
+                       marksecure(val->event);
                return (ISC_R_SUCCESS);
        }
 
@@ -3054,7 +3195,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 {
@@ -3123,7 +3264,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 ||
@@ -3142,12 +3283,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",
@@ -3198,7 +3339,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) {
@@ -3215,7 +3356,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);
 }
@@ -3412,7 +3553,7 @@ proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
                                validator_log(val, ISC_LOG_DEBUG(3),
                                              "not beneath secure root");
                        if (val->view->dlv == NULL || DLVTRIED(val)) {
-                               markanswer(val);
+                               markanswer(val, "proveunsecure (1)");
                                return (ISC_R_SUCCESS);
                        }
                        return (startfinddlvsep(val, dns_rootname));
@@ -3451,7 +3592,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;
                        }
@@ -3481,17 +3622,22 @@ 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 may be done.
                         */
-                       if (DNS_TRUST_PENDING(val->frdataset.trust)) {
-                               result = create_fetch(val, tname,
-                                                     dns_rdatatype_ds,
-                                                     dsfetched2,
-                                                     "proveunsecure");
+                       /*
+                        * 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);
@@ -3514,7 +3660,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));
@@ -3541,7 +3687,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 (4)");
                                        return (ISC_R_SUCCESS);
                                }
                                return (startfinddlvsep(val, tname));
@@ -3569,7 +3715,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;
                                        }
@@ -3585,6 +3732,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,
@@ -3634,13 +3784,6 @@ proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
                        return (result);
        }
 
-/*
-       if ((val->attributes & VALATTR_NEEDOPTOUT) == 0 &&
-           val->event->message != NULL) {
-               val->attributes |= VALATTR_NEEDOPTOUT;
-               return (nsecvalidate(val, ISC_FALSE));
-       }
-*/
        /* Couldn't complete insecurity proof */
        validator_log(val, ISC_LOG_DEBUG(3), "insecurity proof failed");
        return (DNS_R_NOTINSECURE);
@@ -3736,7 +3879,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.
@@ -3766,6 +3910,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.