From: Remi Gacogne Date: Fri, 12 May 2017 16:26:36 +0000 (+0200) Subject: rec: Compute zone cuts and states beforehand X-Git-Tag: rec-4.1.0-alpha1~50^2~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=70b3fe7a1c8ae01d05335a83499a5b3c8fab589d;p=thirdparty%2Fpdns.git rec: Compute zone cuts and states beforehand --- diff --git a/pdns/recursordist/test-syncres_cc.cc b/pdns/recursordist/test-syncres_cc.cc index d98039e51c..947c27ee0b 100644 --- a/pdns/recursordist/test-syncres_cc.cc +++ b/pdns/recursordist/test-syncres_cc.cc @@ -224,11 +224,6 @@ static void addRecordToList(std::vector& records, const DNSName& name records.push_back(rec); } -static void addRecordToList(std::vector& records, const std::string& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place, uint32_t ttl) -{ - addRecordToList(records, name, type, content, place, ttl); -} - static void addRecordToLW(LWResult* res, const DNSName& name, uint16_t type, const std::string& content, DNSResourceRecord::Place place=DNSResourceRecord::ANSWER, uint32_t ttl=60) { addRecordToList(res->d_records, name, type, content, place, ttl); @@ -4882,6 +4877,186 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure) { BOOST_CHECK_EQUAL(queriesCount, 4); } +BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_skipped_cut) { + std::unique_ptr sr; + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("www.sub.powerdns.com."); + const ComboAddress targetAddr("192.0.2.42"); + testkeysset_t keys; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys); + generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys); + + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,targetAddr,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (type == QType::DS) { + if (domain == DNSName("sub.powerdns.com.")) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300); + addNSECRecordToLW(domain, DNSName("z.powerdns.com."), { QType::NS }, 600, res->d_records); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300); + return 1; + } + else if (domain == DNSName("www.sub.powerdns.com.")) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, DNSName("sub.powerdns.com."), QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600); + return 1; + } + } + else if (type == QType::DNSKEY) { + if (domain == g_rootdnsname || domain == DNSName("com.")) { + setLWResult(res, 0, true, false, true); + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + return 1; + } + else { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600); + return 1; + } + } + else if (domain == target) { + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600); + addDS(DNSName("com."), 300, res->d_records, keys); + addRRSIG(keys, res->d_records, DNSName("."), 300); + addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.1:53")) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600); + addDS(DNSName("powerdns.com."), 300, res->d_records, keys); + addRRSIG(keys, res->d_records, DNSName("com."), 300); + addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.2:53")) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600); + return 1; + } + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure); + BOOST_REQUIRE_EQUAL(ret.size(), 1); + BOOST_CHECK(ret[0].d_type == QType::A); + BOOST_CHECK_EQUAL(queriesCount, 5); + + /* again, to test the cache */ + ret.clear(); + res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure); + BOOST_REQUIRE_EQUAL(ret.size(), 1); + BOOST_CHECK(ret[0].d_type == QType::A); + BOOST_CHECK_EQUAL(queriesCount, 5); +} + +BOOST_AUTO_TEST_CASE(test_dnssec_insecure_to_ta_skipped_cut) { + std::unique_ptr sr; + initSR(sr, true, true); + + g_dnssecmode = DNSSECMode::ValidateAll; + + primeHints(); + const DNSName target("www.sub.powerdns.com."); + const ComboAddress targetAddr("192.0.2.42"); + testkeysset_t keys; + + auto luaconfsCopy = g_luaconfs.getCopy(); + luaconfsCopy.dsAnchors.clear(); + generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys, luaconfsCopy.dsAnchors); + /* No key material for .com */ + /* But TA for sub.powerdns.com. */ + generateKeyMaterial(DNSName("sub.powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys); + luaconfsCopy.dsAnchors[DNSName("sub.powerdns.com.")].insert(keys[DNSName("sub.powerdns.com.")].second); + g_luaconfs.setState(luaconfsCopy); + + size_t queriesCount = 0; + + sr->setAsyncCallback([target,targetAddr,&queriesCount,keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, boost::optional context, std::shared_ptr outgoingLogger, LWResult* res) { + queriesCount++; + + if (type == QType::DS) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, domain, QType::SOA, "pdns-public-ns1.powerdns.com. pieter\\.lexis.powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600); + return 1; + } + else if (type == QType::DNSKEY) { + if (domain == g_rootdnsname || domain == DNSName("sub.powerdns.com.")) { + setLWResult(res, 0, true, false, true); + addDNSKEY(keys, domain, 300, res->d_records); + addRRSIG(keys, res->d_records, domain, 300); + return 1; + } + } + else if (domain == target) { + if (isRootServer(ip)) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600); + /* no DS */ + addNSECRecordToLW(DNSName("com."), DNSName("dom."), { QType::NS }, 600, res->d_records); + addRRSIG(keys, res->d_records, DNSName("."), 300); + addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.1:53")) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600); + addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + return 1; + } + else if (ip == ComboAddress("192.0.2.2:53")) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600); + addRRSIG(keys, res->d_records, DNSName("sub.powerdns.com."), 300); + return 1; + } + } + + return 0; + }); + + vector ret; + int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), Secure); + BOOST_REQUIRE_EQUAL(ret.size(), 1); + BOOST_CHECK(ret[0].d_type == QType::A); + BOOST_CHECK_EQUAL(queriesCount, 5); + + /* again, to test the cache */ + ret.clear(); + res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), Secure); + BOOST_REQUIRE_EQUAL(ret.size(), 1); + BOOST_CHECK(ret[0].d_type == QType::A); + BOOST_CHECK_EQUAL(queriesCount, 5); +} + BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_nodata) { std::unique_ptr sr; initSR(sr, true); @@ -5646,8 +5821,8 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_ta_norrsig) { return 1; } } - else if (domain == target) { - if (isRootServer(ip)) { + else { + if (target.isPartOf(domain) && isRootServer(ip)) { setLWResult(res, 0, false, false, true); addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600); addNSECRecordToLW(DNSName("com."), DNSName("com."), { QType::NS }, 600, res->d_records); @@ -5656,14 +5831,26 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_ta_norrsig) { return 1; } else if (ip == ComboAddress("192.0.2.1:53")) { - setLWResult(res, 0, false, false, true); - addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600); - addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + if (target == domain) { + setLWResult(res, 0, false, false, true); + addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600); + addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + } + else if (domain == DNSName("com.")) { + setLWResult(res, 0, true, false, true); + addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com."); + addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600); + } return 1; } - else if (ip == ComboAddress("192.0.2.2:53")) { + else if (domain == target && ip == ComboAddress("192.0.2.2:53")) { setLWResult(res, 0, true, false, true); - addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600); + if (type == QType::NS) { + addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com."); + } + else { + addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600); + } /* No RRSIG in a now (thanks to TA) Secure zone -> Bogus*/ return 1; } diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 66e871e8c7..2e8ca341f2 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -558,16 +558,19 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector dsrecords; vState state = Indeterminate; - int rcode = doResolve(zone, QType(QType::DS), dsrecords, depth, beenthere, state); + int rcode = doResolve(zone, QType(QType::DS), dsrecords, depth + 1, beenthere, state); d_skipCNAMECheck = oldSkipCNAME; d_requireAuthData = oldRequireAuthData; @@ -1342,6 +1345,26 @@ vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsi return Bogus; } +vState SyncRes::getValidationStatus(const SyncRes::zonesStates_t& cuts, const DNSName& subdomain) +{ + vState result = Indeterminate; + + if (!validationEnabled()) { + return result; + } + DNSName name(subdomain); + do { + const auto& it = cuts.find(name); + if (it != cuts.cend()) { + return it->second; + } + } + while (name.chopOff()); + + return result; +} + +#if 0 vState SyncRes::getValidationStatus(const DNSName& subdomain, unsigned int depth) { if (!validationEnabled()) { @@ -1406,8 +1429,90 @@ vState SyncRes::getValidationStatus(const DNSName& subdomain, unsigned int depth return Secure; } +#endif /* 0 */ + +SyncRes::zonesStates_t SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned int depth) +{ + SyncRes::zonesStates_t cuts; + dsmap_t ds; + vState cutState = getDSRecords(end, ds, false, depth); + if (cutState == TA) { + cutState = Secure; + } + else if (cutState == NTA) { + cutState = Insecure; + } + cuts[end] = cutState; + + cerr<<__func__<<", from "< labelsToAdd = begin.makeRelative(end).getRawLabels(); + + while(qname != begin) { + bool foundCut = false; + if (labelsToAdd.empty()) + break; + + qname.prependRawLabel(labelsToAdd.back()); + labelsToAdd.pop_back(); + cerr<<"- Looking for a cut at "< beenthere; + std::vector nsrecords; + + vState state = Indeterminate; + int rcode = doResolve(qname, QType(QType::NS), nsrecords, depth + 1, beenthere, state); + d_skipCNAMECheck = oldSkipCNAME; + d_requireAuthData = oldRequireAuthData; + + if (rcode == RCode::NoError && !nsrecords.empty()) { + for (const auto& record : nsrecords) { + if(record.d_type != QType::NS || record.d_name != qname) + continue; + foundCut = true; + break; + } + if (foundCut) { + cerr<<"- Found cut at "< ednsmask, vState& state, bool& needWildcardProof) +RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const DNSName& auth, bool wasForwarded, const boost::optional ednsmask, vState& state, const SyncRes::zonesStates_t& cuts, bool& needWildcardProof) { struct CacheEntry { @@ -1669,12 +1774,15 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr } for(tcache_t::iterator i = tcache.begin(); i != tcache.end(); ++i) { - vState recordState = state; if(i->second.records.empty()) // this happens when we did store signatures, but passed on the records themselves continue; - if (validationEnabled() && state == Secure) { +// vState recordState = state; + vState recordState = getValidationStatus(cuts, i->first.name); + cerr<<"Got status "<first.name<first.place != DNSResourceRecord::ADDITIONAL) { /* the additional entries can be insecure, @@ -1984,7 +2092,7 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, return true; } -bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, DNSName& auth, bool wasForwarded, const boost::optional ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state) +bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, DNSName& auth, bool wasForwarded, const boost::optional ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state, const SyncRes::zonesStates_t& cuts) { string prefix; if(doLog()) { @@ -1999,7 +2107,7 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn } bool needWildcardProof = false; - *rcode = updateCacheFromRecords(depth, lwr, qname, auth, wasForwarded, ednsmask, state, needWildcardProof); + *rcode = updateCacheFromRecords(depth, lwr, qname, auth, wasForwarded, ednsmask, state, cuts, needWildcardProof); if (*rcode != RCode::NoError) { return true; } @@ -2080,7 +2188,7 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn } LOG("looping to them"<&ret, - unsigned int depth, set&beenthere, vState& state) + unsigned int depth, set&beenthere, vState& state, const SyncRes::zonesStates_t& cuts) { auto luaconfsLocal = g_luaconfs.getLocal(); string prefix; @@ -2153,7 +2261,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con lwr.d_aabit=true; /* we have received an answer, are we done ? */ - bool done = processAnswer(depth, lwr, qname, qtype, auth, false, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, state); + bool done = processAnswer(depth, lwr, qname, qtype, auth, false, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, state, cuts); if (done) { return rcode; } @@ -2218,7 +2326,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con t_sstorage.nsSpeeds[*tns].submit(*remoteIP, lwr.d_usec, &d_now); /* we have received an answer, are we done ? */ - bool done = processAnswer(depth, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, state); + bool done = processAnswer(depth, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, state, cuts); if (done) { return rcode; } diff --git a/pdns/syncres.hh b/pdns/syncres.hh index f1d8a74451..f43d49271d 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -699,10 +699,12 @@ private: } }; + typedef std::map zonesStates_t; + int doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype, vector&ret, - unsigned int depth, set&beenthere, vState& state); + unsigned int depth, set&beenthere, vState& state, const zonesStates_t& cuts); bool doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional& ednsmask, const DNSName& auth, bool const sendRDQuery, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated); - bool processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, DNSName& auth, bool wasForwarded, const boost::optional ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state); + bool processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, DNSName& auth, bool wasForwarded, const boost::optional ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state, const zonesStates_t& cuts); int doResolve(const DNSName &qname, const QType &qtype, vector&ret, unsigned int depth, set& beenthere, vState& state); bool doOOBResolve(const AuthDomain& domain, const DNSName &qname, const QType &qtype, vector&ret, int& res) const; @@ -722,7 +724,7 @@ private: bool throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType& qtype, bool pierceDontQuery); vector retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector::const_iterator& tns, const unsigned int depth, set& beenthere, const vector& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet); - RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const DNSName& auth, bool wasForwarded, const boost::optional, vState& state, bool& needWildcardProof); + RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const DNSName& auth, bool wasForwarded, const boost::optional, vState& state, const zonesStates_t& cuts, bool& needWildcardProof); bool processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector& ret, set& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, bool needWildcardProof); bool doSpecialNamesResolve(const DNSName &qname, const QType &qtype, const uint16_t qclass, vector &ret); @@ -734,14 +736,16 @@ private: bool validationEnabled() const; uint32_t computeLowestTTD(const std::vector& records, const std::vector >& signatures, uint32_t signaturesTTL) const; void updateValidationState(vState& state, const vState stateUpdate); - void updateValidationStatusAfterReferral(const DNSName& oldauth, const DNSName& newauth, vState& state, unsigned int depth); + void updateValidationStatusAfterReferral(const DNSName& qname, const DNSName& oldauth, const DNSName& newauth, vState& state, unsigned int depth); vState validateRecordsWithSigs(unsigned int depth, const DNSName& name, const std::vector& records, const std::vector >& signatures); vState validateDNSKeys(const DNSName& zone, const std::vector& dnskeys, const std::vector >& signatures, unsigned int depth); vState getDSRecords(const DNSName& zone, dsmap_t& ds, bool onlyTA, unsigned int depth); vState getDNSKeys(const DNSName& signer, skeyset_t& keys, unsigned int depth); void getDenialValidationState(NegCache::NegCacheEntry& ne, vState& state, const dState expectedState, bool allowOptOut); vState getTA(const DNSName& zone, dsmap_t& ds); - vState getValidationStatus(const DNSName& subdomain, unsigned int depth); + vState getValidationStatus(const zonesStates_t& cuts, const DNSName& subdomain); + + zonesStates_t computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned int depth); void setUpdatingRootNS() {