BIND 10 master, updated. 30ef882f31963e8254a6c37b4afc4dc76822c57d [master] Merge branch 'trac1431'
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Jan 23 19:24:05 UTC 2012
The branch, master has been updated
via 30ef882f31963e8254a6c37b4afc4dc76822c57d (commit)
via 1262df829dea239e99206d71aa0281928c5ff426 (commit)
via c790b4dfc1f63128f017f48db730cadc1396126b (commit)
via 93f4485d035d308bf60041f331ce75138ff5d7bb (commit)
via 2528257c4f3621822cdc030d0201453506c5a56b (commit)
via 3dabb15d73f757c4e30ab8dfbfbb84a94eea8a86 (commit)
via 2b16771b9cd34d55fe3916669b53531b29bdf0ce (commit)
via 91a26d94d9a0b70a030d79f7e927610c1e745878 (commit)
via c95f08240cfb08954c361f36fa0877b0cc3385d7 (commit)
via 453b8ba9fa6ae7f846184aa490e93f326250178b (commit)
via 8854cf0ab6ce6b443bc8ad01d1724e0ad6cadff9 (commit)
via a3af8383ee7ca64f7058698a6753ffa74c4457e9 (commit)
via 89258069451e9b293f9967871d3198593353a92b (commit)
via 80f19c3152c510fe58434bc9a420c289642fba6a (commit)
via 0e93f4c179df049891567808accf7dab349398fa (commit)
from 0457ee07936085e0b0843fa645b9873a01e60b8b (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 30ef882f31963e8254a6c37b4afc4dc76822c57d
Merge: 0457ee0 1262df8
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Mon Jan 23 11:22:03 2012 -0800
[master] Merge branch 'trac1431'
-----------------------------------------------------------------------
Summary of changes:
src/bin/auth/query.cc | 98 +++++++----
src/bin/auth/tests/query_unittest.cc | 307 ++++++++++++++++++++++++++++++----
src/lib/datasrc/database.cc | 6 +
src/lib/datasrc/database.h | 6 +
src/lib/datasrc/memory_datasrc.cc | 6 +
src/lib/datasrc/memory_datasrc.h | 6 +
src/lib/datasrc/zone.h | 190 ++++++++++++++++++++-
7 files changed, 545 insertions(+), 74 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 2bf8f5e..7d6886c 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -30,6 +30,42 @@ using namespace isc::dns;
using namespace isc::datasrc;
using namespace isc::dns::rdata;
+namespace {
+// This is a temporary wrapper to convert old format of FindResult to new
+// one, until we update all supported data source implementations.
+ZoneFinder::FindResult
+findWrapper(const ZoneFinder::FindResult& orig_result) {
+ // Retrieve the original flags
+ ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
+ if (orig_result.isWildcard()) {
+ flags = flags | ZoneFinder::RESULT_WILDCARD;
+ }
+ if (orig_result.isNSECSigned()) {
+ flags = flags | ZoneFinder::RESULT_NSEC_SIGNED;
+ }
+ if (orig_result.isNSEC3Signed()) {
+ flags = flags | ZoneFinder::RESULT_NSEC3_SIGNED;
+ }
+
+ // Convert older code to new one, adjusting flags if necessary
+ ZoneFinder::Result code = orig_result.code;
+ if (code == ZoneFinder::WILDCARD) {
+ code = ZoneFinder::SUCCESS;
+ flags = flags | ZoneFinder::RESULT_WILDCARD;
+ } else if (code == ZoneFinder::WILDCARD_CNAME) {
+ code = ZoneFinder::CNAME;
+ flags = flags | ZoneFinder::RESULT_WILDCARD;
+ } else if (code == ZoneFinder::WILDCARD_NXRRSET) {
+ code = ZoneFinder::NXRRSET;
+ flags = flags | ZoneFinder::RESULT_WILDCARD;
+ }
+ if (orig_result.rrset && orig_result.rrset->getType() == RRType::NSEC()) {
+ flags = flags | ZoneFinder::RESULT_NSEC_SIGNED;
+ }
+ return (ZoneFinder::FindResult(code, orig_result.rrset, flags));
+}
+}
+
namespace isc {
namespace auth {
@@ -69,8 +105,8 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
// Find A rrset
if (qname_ != qname || qtype_ != RRType::A()) {
- ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(),
- options | dnssec_opt_);
+ ZoneFinder::FindResult a_result =
+ findWrapper(zone.find(qname, RRType::A(), options | dnssec_opt_));
if (a_result.code == ZoneFinder::SUCCESS) {
response_.addRRset(Message::SECTION_ADDITIONAL,
boost::const_pointer_cast<RRset>(a_result.rrset), dnssec_);
@@ -80,7 +116,8 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
// Find AAAA rrset
if (qname_ != qname || qtype_ != RRType::AAAA()) {
ZoneFinder::FindResult aaaa_result =
- zone.find(qname, RRType::AAAA(), options | dnssec_opt_);
+ findWrapper(zone.find(qname, RRType::AAAA(),
+ options | dnssec_opt_));
if (aaaa_result.code == ZoneFinder::SUCCESS) {
response_.addRRset(Message::SECTION_ADDITIONAL,
boost::const_pointer_cast<RRset>(aaaa_result.rrset),
@@ -91,8 +128,9 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
void
Query::addSOA(ZoneFinder& finder) {
- ZoneFinder::FindResult soa_result(finder.find(finder.getOrigin(),
- RRType::SOA(), dnssec_opt_));
+ ZoneFinder::FindResult soa_result =
+ findWrapper(finder.find(finder.getOrigin(),
+ RRType::SOA(), dnssec_opt_));
if (soa_result.code != ZoneFinder::SUCCESS) {
isc_throw(NoSOA, "There's no SOA record in zone " <<
finder.getOrigin().toText());
@@ -147,9 +185,8 @@ Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
// Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
// otherwise we shouldn't have got NXDOMAIN for the original query in
// the first place).
- const ZoneFinder::FindResult fresult = finder.find(wildname,
- RRType::NSEC(),
- dnssec_opt_);
+ const ZoneFinder::FindResult fresult =
+ findWrapper(finder.find(wildname, RRType::NSEC(), dnssec_opt_));
if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
fresult.rrset->getRdataCount() == 0) {
isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
@@ -173,8 +210,8 @@ Query::addWildcardProof(ZoneFinder& finder) {
// substitution. Confirm that by specifying NO_WILDCARD. It should result
// in NXDOMAIN and an NSEC RR that proves it should be returned.
const ZoneFinder::FindResult fresult =
- finder.find(qname_, RRType::NSEC(),
- dnssec_opt_ | ZoneFinder::NO_WILDCARD);
+ findWrapper(finder.find(qname_, RRType::NSEC(),
+ dnssec_opt_ | ZoneFinder::NO_WILDCARD));
if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
fresult.rrset->getRdataCount() == 0) {
isc_throw(BadNSEC, "Unexpected result for wildcard proof");
@@ -191,13 +228,10 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
if (nsec->getRdataCount() == 0) {
isc_throw(BadNSEC, "NSEC for WILDCARD_NXRRSET is empty");
}
- // Add this NSEC RR to authority section.
- response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(nsec), dnssec_);
const ZoneFinder::FindResult fresult =
- finder.find(qname_, RRType::NSEC(),
- dnssec_opt_ | ZoneFinder::NO_WILDCARD);
+ findWrapper(finder.find(qname_, RRType::NSEC(),
+ dnssec_opt_ | ZoneFinder::NO_WILDCARD));
if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
fresult.rrset->getRdataCount() == 0) {
isc_throw(BadNSEC, "Unexpected result for no match QNAME proof");
@@ -214,8 +248,9 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
void
Query::addAuthAdditional(ZoneFinder& finder) {
// Fill in authority and addtional sections.
- ZoneFinder::FindResult ns_result = finder.find(finder.getOrigin(),
- RRType::NS(), dnssec_opt_);
+ ZoneFinder::FindResult ns_result =
+ findWrapper(finder.find(finder.getOrigin(), RRType::NS(),
+ dnssec_opt_));
// zone origin name should have NS records
if (ns_result.code != ZoneFinder::SUCCESS) {
isc_throw(NoApexNS, "There's no apex NS records in zone " <<
@@ -260,7 +295,7 @@ Query::process() {
find = boost::bind(&ZoneFinder::find, &zfinder, qname_, qtype_,
dnssec_opt_);
}
- ZoneFinder::FindResult db_result(find());
+ ZoneFinder::FindResult db_result(findWrapper(find()));
switch (db_result.code) {
case ZoneFinder::DNAME: {
// First, put the dname into the answer
@@ -306,7 +341,6 @@ Query::process() {
break;
}
case ZoneFinder::CNAME:
- case ZoneFinder::WILDCARD_CNAME:
/*
* We don't do chaining yet. Therefore handling a CNAME is
* mostly the same as handling SUCCESS, but we didn't get
@@ -322,12 +356,11 @@ Query::process() {
// If the answer is a result of wildcard substitution,
// add a proof that there's no closer name.
- if (dnssec_ && db_result.code == ZoneFinder::WILDCARD_CNAME) {
+ if (dnssec_ && db_result.isWildcard()) {
addWildcardProof(*result.zone_finder);
}
break;
case ZoneFinder::SUCCESS:
- case ZoneFinder::WILDCARD:
if (qtype_is_any) {
// If quety type is ANY, insert all RRs under the domain
// into answer section.
@@ -357,7 +390,7 @@ Query::process() {
// If the answer is a result of wildcard substitution,
// add a proof that there's no closer name.
- if (dnssec_ && db_result.code == ZoneFinder::WILDCARD) {
+ if (dnssec_ && db_result.isWildcard()) {
addWildcardProof(*result.zone_finder);
}
break;
@@ -377,17 +410,16 @@ Query::process() {
break;
case ZoneFinder::NXRRSET:
addSOA(*result.zone_finder);
- if (dnssec_ && db_result.rrset) {
- response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(
- db_result.rrset),
- dnssec_);
- }
- break;
- case ZoneFinder::WILDCARD_NXRRSET:
- addSOA(*result.zone_finder);
- if (dnssec_ && db_result.rrset) {
- addWildcardNXRRSETProof(zfinder, db_result.rrset);
+ if (dnssec_) {
+ if (db_result.isNSECSigned() && db_result.rrset) {
+ response_.addRRset(Message::SECTION_AUTHORITY,
+ boost::const_pointer_cast<RRset>(
+ db_result.rrset),
+ dnssec_);
+ if (db_result.isWildcard()) {
+ addWildcardNXRRSETProof(zfinder, db_result.rrset);
+ }
+ }
}
break;
default:
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 858759e..350bc63 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -19,6 +19,8 @@
#include <boost/bind.hpp>
#include <boost/scoped_ptr.hpp>
+#include <exceptions/exceptions.h>
+
#include <dns/masterload.h>
#include <dns/message.h>
#include <dns/name.h>
@@ -153,6 +155,14 @@ const char* const nsec_www_txt =
// Authoritative data without NSEC
const char* const nonsec_a_txt = "nonsec.example.com. 3600 IN A 192.0.2.0\n";
+// NSEC3 RRs. You may also need to add mapping to MockZoneFinder::hash_map_.
+const char* const nsec3_apex_txt =
+ "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN NSEC3 1 1 12 "
+ "aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA NSEC3PARAM RRSIG\n";
+const char* const nsec3_www_txt =
+ "q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN NSEC3 1 1 12 "
+ "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";
+
// A helper function that generates a textual representation of RRSIG RDATA
// for the given covered type. The resulting RRSIG may not necessarily make
// sense in terms of the DNSSEC protocol, but for our testing purposes it's
@@ -185,6 +195,7 @@ public:
has_apex_NS_(true),
rrclass_(RRClass::IN()),
include_rrsig_anyway_(false),
+ use_nsec3_(false),
nsec_name_(origin_)
{
stringstream zone_stream;
@@ -197,7 +208,8 @@ public:
wild_txt << nsec_wild_txt << cnamewild_txt << nsec_cnamewild_txt <<
wild_txt_nxrrset << nsec_wild_txt_nxrrset << wild_txt_next <<
nsec_wild_txt_next << empty_txt << nsec_empty_txt <<
- empty_prev_txt << nsec_empty_prev_txt;
+ empty_prev_txt << nsec_empty_prev_txt <<
+ nsec3_apex_txt << nsec3_www_txt;
masterLoad(zone_stream, origin_, rrclass_,
boost::bind(&MockZoneFinder::loadRRset, this, _1));
@@ -206,6 +218,21 @@ public:
RRClass::IN(),
RRType::NSEC(),
RRTTL(3600)));
+
+ // (Faked) NSEC3 hash map. For convenience we use hardcoded built-in
+ // map instead of calculating and using actual hash.
+ // The used hash values are borrowed from RFC5155 examples.
+ hash_map_[Name("example.com")] = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
+ hash_map_[Name("nxdomain.example.com")] =
+ "v644ebqk9bibcna874givr6joj62mlhv";
+ hash_map_[Name("nx.domain.example.com")] =
+ "v644ebqk9bibcna874givr6joj62mlhv";
+ hash_map_[Name("domain.example.com")] =
+ "v644ebqk9bibcna874givr6joj62mlhv";
+ hash_map_[Name("nxdomain2.example.com")] =
+ "q00jkcevqvmu85r014c7dkba38o0ji5r";
+ hash_map_[Name("nxdomain3.example.com")] =
+ "009mhaveqvm6t7vbl5lop2u3t2rp3tom";
}
virtual isc::dns::Name getOrigin() const { return (origin_); }
virtual isc::dns::RRClass getClass() const { return (rrclass_); }
@@ -216,6 +243,9 @@ public:
std::vector<ConstRRsetPtr>& target,
const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinder::FindNSEC3Result
+ findNSEC3(const Name& name, bool recursive);
+
// If false is passed, it makes the zone broken as if it didn't have the
// SOA.
void setSOAFlag(bool on) { has_SOA_ = on; }
@@ -236,6 +266,10 @@ public:
nsec_result_.reset(new ZoneFinder::FindResult(code, rrset));
}
+ // If true is passed return an empty NSEC3 RRset for some negative
+ // answers when DNSSEC is required.
+ void setNSEC3Flag(bool on) { use_nsec3_ = on; }
+
Name findPreviousName(const Name&) const {
isc_throw(isc::NotImplemented, "Mock doesn't support previous name");
}
@@ -249,7 +283,19 @@ private:
typedef map<RRType, ConstRRsetPtr> RRsetStore;
typedef map<Name, RRsetStore> Domains;
Domains domains_;
+ Domains nsec3_domains_;
void loadRRset(RRsetPtr rrset) {
+ if (rrset->getType() == RRType::NSEC3()) {
+ // NSEC3 should go to the dedicated table
+ nsec3_domains_[rrset->getName()][rrset->getType()] = rrset;
+
+ // By nature it should have RRSIG. (We may want to selectively
+ // omit this to test pathological cases).
+ rrset->addRRsig(RdataPtr(new generic::RRSIG(
+ getCommonRRSIGText(rrset->getType().
+ toText()))));
+ return;
+ }
domains_[rrset->getName()][rrset->getType()] = rrset;
if (rrset->getName() == delegation_name_ &&
rrset->getType() == RRType::NS()) {
@@ -283,9 +329,11 @@ private:
ConstRRsetPtr dname_rrset_;
const RRClass rrclass_;
bool include_rrsig_anyway_;
+ bool use_nsec3_;
// The following two will be used for faked NSEC cases
Name nsec_name_;
boost::scoped_ptr<ZoneFinder::FindResult> nsec_result_;
+ map<Name, string> hash_map_;
};
// A helper function that generates a new RRset based on "wild_rrset",
@@ -327,6 +375,51 @@ MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
return (result);
}
+ZoneFinder::FindNSEC3Result
+MockZoneFinder::findNSEC3(const Name& name, bool recursive) {
+ ConstRRsetPtr covering_proof;
+ const int labels = name.getLabelCount();
+
+ // For brevity, we assume several things below: maps should have an
+ // expected entry when operator[] is used; maps are not empty.
+ for (int i = 0; i < labels; ++i) {
+ const string hlabel = hash_map_[name.split(i, labels - i)];
+ const Name hname = Name(hlabel + ".example.com");
+ // We don't use const_iterator so that we can use operator[] below
+ Domains::iterator found_domain = nsec3_domains_.lower_bound(hname);
+
+ // If the given hash is larger than the largest stored hash or
+ // the first label doesn't match the target, identify the "previous"
+ // hash value and remember it as the candidate next closer proof.
+ if (found_domain == nsec3_domains_.end() ||
+ found_domain->first.split(0, 1).toText(true) != hlabel) {
+ // If the given hash is larger or smaller than everything,
+ // the covering proof is the NSEC3 that has the largest hash.
+ if (found_domain == nsec3_domains_.end() ||
+ found_domain == nsec3_domains_.begin()) {
+ covering_proof =
+ nsec3_domains_.rbegin()->second[RRType::NSEC3()];
+ } else {
+ // Otherwise, H(found_domain-1) < given_hash < H(found_domain)
+ // The covering proof is the first one.
+ covering_proof = (--found_domain)->second[RRType::NSEC3()];
+ }
+ if (!recursive) { // in non recursive mode, we are done.
+ return (ZoneFinder::FindNSEC3Result(false,
+ name.getLabelCount(),
+ covering_proof,
+ ConstRRsetPtr()));
+ }
+ } else { // exact match
+ return (ZoneFinder::FindNSEC3Result(
+ true, name.getLabelCount() - i,
+ found_domain->second[RRType::NSEC3()],
+ covering_proof));
+ }
+ }
+ isc_throw(isc::Unexpected, "findNSEC3() isn't expected to fail");
+}
+
ZoneFinder::FindResult
MockZoneFinder::find(const Name& name, const RRType& type,
const FindOptions options)
@@ -362,7 +455,7 @@ MockZoneFinder::find(const Name& name, const RRType& type,
ConstRRsetPtr rrset;
// Strip whatever signature there is in case DNSSEC is not required
// Just to make sure the Query asks for it when it is needed
- if (options & ZoneFinder::FIND_DNSSEC ||
+ if ((options & ZoneFinder::FIND_DNSSEC) != 0 ||
include_rrsig_anyway_ ||
!found_rrset->second->getRRsig()) {
rrset = found_rrset->second;
@@ -389,12 +482,16 @@ MockZoneFinder::find(const Name& name, const RRType& type,
// Otherwise it's NXRRSET case.
if ((options & FIND_DNSSEC) != 0) {
+ if (use_nsec3_) {
+ return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
+ }
found_rrset = found_domain->second.find(RRType::NSEC());
if (found_rrset != found_domain->second.end()) {
- return (FindResult(NXRRSET, found_rrset->second));
+ return (FindResult(NXRRSET, found_rrset->second,
+ RESULT_NSEC_SIGNED));
}
}
- return (FindResult(NXRRSET, RRsetPtr()));
+ return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC_SIGNED));
}
// query name isn't found in our domains.
@@ -413,10 +510,14 @@ MockZoneFinder::find(const Name& name, const RRType& type,
// the origin of the zone)
--domain; // reset domain to the "previous name"
if ((options & FIND_DNSSEC) != 0) {
+ if (use_nsec3_) {
+ return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
+ }
RRsetStore::const_iterator found_rrset =
(*domain).second.find(RRType::NSEC());
if (found_rrset != (*domain).second.end()) {
- return (FindResult(NXRRSET, found_rrset->second));
+ return (FindResult(NXRRSET, found_rrset->second,
+ RESULT_NSEC_SIGNED));
}
}
return (FindResult(NXRRSET, RRsetPtr()));
@@ -436,36 +537,57 @@ MockZoneFinder::find(const Name& name, const RRType& type,
domain = domains_.find(Name("*").concatenate(wild_suffix));
// Matched the QNAME
if (domain != domains_.end()) {
- RRsetStore::const_iterator found_rrset =
- domain->second.find(type);
- // Matched the QTYPE
- if(found_rrset != domain->second.end()) {
- return (FindResult(WILDCARD,
- substituteWild(*found_rrset->second, name)));
- } else {
- // No matched QTYPE, this case is for WILDCARD_NXRRSET
- found_rrset = domain->second.find(RRType::NSEC());
- assert(found_rrset != domain->second.end());
- Name newName = Name("*").concatenate(wild_suffix);
- return (FindResult(WILDCARD_NXRRSET,
- substituteWild(*found_rrset->second,newName)));
- }
- } else {
+ RRsetStore::const_iterator found_rrset =
+ domain->second.find(type);
+ // Matched the QTYPE
+ if(found_rrset != domain->second.end()) {
+ return (FindResult(SUCCESS,
+ substituteWild(
+ *found_rrset->second, name),
+ RESULT_WILDCARD |
+ (use_nsec3_ ?
+ RESULT_NSEC3_SIGNED :
+ RESULT_NSEC_SIGNED)));
+ } else {
+ // No matched QTYPE, this case is for WILDCARD_NXRRSET
+ if (use_nsec3_) {
+ return (FindResult(NXRRSET, RRsetPtr(),
+ RESULT_WILDCARD |
+ RESULT_NSEC3_SIGNED));
+ }
+ const Name new_name =
+ Name("*").concatenate(wild_suffix);
+ found_rrset = domain->second.find(RRType::NSEC());
+ assert(found_rrset != domain->second.end());
+ return (FindResult(NXRRSET,
+ substituteWild(
+ *found_rrset->second,
+ new_name),
+ RESULT_WILDCARD |
+ RESULT_NSEC_SIGNED));
+ }
+ } else {
// This is empty non terminal name case on wildcard.
- Name emptyName = Name("*").concatenate(wild_suffix);
+ const Name empty_name = Name("*").concatenate(wild_suffix);
+ if (use_nsec3_) {
+ return (FindResult(NXRRSET, RRsetPtr(),
+ RESULT_WILDCARD |
+ RESULT_NSEC3_SIGNED));
+ }
for (Domains::reverse_iterator it = domains_.rbegin();
- it != domains_.rend();
- ++it) {
- RRsetStore::const_iterator nsec_it;
- if ((*it).first < emptyName &&
+ it != domains_.rend();
+ ++it) {
+ RRsetStore::const_iterator nsec_it;
+ if ((*it).first < empty_name &&
(nsec_it = (*it).second.find(RRType::NSEC()))
!= (*it).second.end()) {
- return (FindResult(WILDCARD_NXRRSET,
- (*nsec_it).second));
- }
+ return (FindResult(NXRRSET, (*nsec_it).second,
+ RESULT_WILDCARD |
+ RESULT_NSEC_SIGNED));
}
+ }
}
- return (FindResult(WILDCARD_NXRRSET,RRsetPtr()));
+ return (FindResult(NXRRSET, RRsetPtr(), RESULT_WILDCARD));
}
}
const Name cnamewild_suffix("cnamewild.example.com");
@@ -476,8 +598,11 @@ MockZoneFinder::find(const Name& name, const RRType& type,
RRsetStore::const_iterator found_rrset =
domain->second.find(RRType::CNAME());
assert(found_rrset != domain->second.end());
- return (FindResult(WILDCARD_CNAME,
- substituteWild(*found_rrset->second, name)));
+ return (FindResult(CNAME,
+ substituteWild(*found_rrset->second, name),
+ RESULT_WILDCARD |
+ (use_nsec3_ ? RESULT_NSEC3_SIGNED :
+ RESULT_NSEC_SIGNED)));
}
}
@@ -489,6 +614,10 @@ MockZoneFinder::find(const Name& name, const RRType& type,
// we don't care about pathological cases such as the name is "smaller"
// than the origin)
if ((options & FIND_DNSSEC) != 0) {
+ if (use_nsec3_) {
+ return (FindResult(NXDOMAIN, RRsetPtr(), RESULT_NSEC3_SIGNED));
+ }
+
// Emulate a broken DataSourceClient for some special names.
if (nsec_result_ && nsec_name_ == name) {
return (*nsec_result_);
@@ -504,7 +633,8 @@ MockZoneFinder::find(const Name& name, const RRType& type,
if ((*it).first < name &&
(nsec_it = (*it).second.find(RRType::NSEC()))
!= (*it).second.end()) {
- return (FindResult(NXDOMAIN, (*nsec_it).second));
+ return (FindResult(NXDOMAIN, (*nsec_it).second,
+ RESULT_NSEC_SIGNED));
}
}
}
@@ -1317,4 +1447,117 @@ TEST_F(QueryTest, MaxLenDNAME) {
EXPECT_TRUE(ok) << "The synthetized CNAME not found";
}
+// Test for this test module itself
+void
+nsec3Check(bool expected_matched, uint8_t expected_labels,
+ const string& expected_rrsets_txt,
+ const ZoneFinder::FindNSEC3Result& result)
+{
+ vector<ConstRRsetPtr> actual_rrsets;
+ EXPECT_EQ(expected_matched, result.matched);
+ // Convert to int so the error messages would be more readable:
+ EXPECT_EQ(static_cast<int>(expected_labels),
+ static_cast<int>(result.closest_labels));
+ if (result.closest_proof) {
+ actual_rrsets.push_back(result.closest_proof);
+ }
+ if (result.next_proof) {
+ actual_rrsets.push_back(result.next_proof);
+ }
+ rrsetsCheck(expected_rrsets_txt, actual_rrsets.begin(),
+ actual_rrsets.end());
+}
+
+TEST_F(QueryTest, findNSEC3) {
+ // In all test cases in the recursive mode, the closest encloser is the
+ // apex, and result's closest_labels should be the number of apex labels.
+ // (In non recursive mode closest_labels should be the # labels of the
+ // query name)
+ const uint8_t expected_closest_labels =
+ Name("example.com").getLabelCount();
+
+ // Apex name. It should have a matching NSEC3
+ nsec3Check(true, expected_closest_labels, nsec3_apex_txt,
+ mock_finder->findNSEC3(Name("example.com"), false));
+
+ // Recursive mode doesn't change the result in this case.
+ nsec3Check(true, expected_closest_labels, nsec3_apex_txt,
+ mock_finder->findNSEC3(Name("example.com"), true));
+
+ // Non existent name. Disabling recursion, a covering NSEC3 should be
+ // returned.
+ nsec3Check(false, 4, nsec3_www_txt,
+ mock_finder->findNSEC3(Name("nxdomain.example.com"), false));
+
+ // Non existent name. The closest provable encloser is the apex,
+ // and next closer is the query name.
+ nsec3Check(true, expected_closest_labels,
+ string(nsec3_apex_txt) + string(nsec3_www_txt),
+ mock_finder->findNSEC3(Name("nxdomain.example.com"), true));
+
+ // Similar to the previous case, but next closer name is different
+ // (is the parent) of the non existent name.
+ nsec3Check(true, expected_closest_labels,
+ string(nsec3_apex_txt) + string(nsec3_www_txt),
+ mock_finder->findNSEC3(Name("nx.domain.example.com"), true));
+
+ // In the rest of test we check hash comparison for wrap around cases.
+ nsec3Check(false, 4, nsec3_apex_txt,
+ mock_finder->findNSEC3(Name("nxdomain2.example.com"), false));
+ nsec3Check(false, 4, nsec3_www_txt,
+ mock_finder->findNSEC3(Name("nxdomain3.example.com"), false));
+}
+
+// The following are tentative tests until we really add tests for the
+// query logic for these cases. At that point it's probably better to
+// clean them up.
+TEST_F(QueryTest, nxdomainWithNSEC3) {
+ mock_finder->setNSEC3Flag(true);
+ ZoneFinder::FindResult result = mock_finder->find(
+ Name("nxdomain.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::NXDOMAIN, result.code);
+ EXPECT_FALSE(result.rrset);
+ EXPECT_TRUE(result.isNSEC3Signed());
+ EXPECT_FALSE(result.isWildcard());
+}
+
+TEST_F(QueryTest, nxrrsetWithNSEC3) {
+ mock_finder->setNSEC3Flag(true);
+ ZoneFinder::FindResult result = mock_finder->find(
+ Name("www.example.com"), RRType::TXT(), ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
+ EXPECT_FALSE(result.rrset);
+ EXPECT_TRUE(result.isNSEC3Signed());
+ EXPECT_FALSE(result.isWildcard());
+}
+
+TEST_F(QueryTest, emptyNameWithNSEC3) {
+ mock_finder->setNSEC3Flag(true);
+ ZoneFinder::FindResult result = mock_finder->find(
+ Name("no.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
+ EXPECT_FALSE(result.rrset);
+ EXPECT_TRUE(result.isNSEC3Signed());
+ EXPECT_FALSE(result.isWildcard());
+}
+
+TEST_F(QueryTest, wildcardNxrrsetWithNSEC3) {
+ mock_finder->setNSEC3Flag(true);
+ ZoneFinder::FindResult result = mock_finder->find(
+ Name("www1.uwild.example.com"), RRType::TXT(),
+ ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
+ EXPECT_FALSE(result.rrset);
+ EXPECT_TRUE(result.isNSEC3Signed());
+ EXPECT_TRUE(result.isWildcard());
+}
+
+TEST_F(QueryTest, wildcardEmptyWithNSEC3) {
+ mock_finder->setNSEC3Flag(true);
+ ZoneFinder::FindResult result = mock_finder->find(
+ Name("a.t.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
+ EXPECT_TRUE(result.isNSEC3Signed());
+ EXPECT_TRUE(result.isWildcard());
+}
}
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 2b324fc..0f24cff 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -892,6 +892,12 @@ DatabaseClient::Finder::findInternal(const isc::dns::Name& name,
}
}
+ZoneFinder::FindNSEC3Result
+DatabaseClient::Finder::findNSEC3(const Name&, bool) {
+ isc_throw(NotImplemented, "findNSEC3 is not yet implemented for database "
+ "data source");
+}
+
Name
DatabaseClient::Finder::findPreviousName(const Name& name) const {
const string str(accessor_->findPreviousName(zone_id_,
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index fdbaa0a..9629300 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -754,6 +754,12 @@ public:
virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
const;
+ /// Look for NSEC3 for proving (non)existence of given name.
+ ///
+ /// See documentation in \c Zone.
+ virtual FindNSEC3Result
+ findNSEC3(const isc::dns::Name& name, bool recursive);
+
/// \brief The zone ID
///
/// This function provides the stored zone ID as passed to the
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index d09eb6d..b019ba3 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -641,6 +641,12 @@ InMemoryZoneFinder::findAll(const Name& name,
return (impl_->find(name, RRType::ANY(), &target, options));
}
+ZoneFinder::FindNSEC3Result
+InMemoryZoneFinder::findNSEC3(const Name&, bool) {
+ isc_throw(NotImplemented, "findNSEC3 is not yet implemented for in memory "
+ "data source");
+}
+
result::Result
InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) {
return (impl_->add(rrset, &impl_->domains_));
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index 32cf518..fcd9c8b 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -83,6 +83,12 @@ public:
std::vector<isc::dns::ConstRRsetPtr>& target,
const FindOptions options = FIND_DEFAULT);
+ /// Look for NSEC3 for proving (non)existence of given name.
+ ///
+ /// See documentation in \c Zone.
+ virtual FindNSEC3Result
+ findNSEC3(const isc::dns::Name& name, bool recursive);
+
/// \brief Imelementation of the ZoneFinder::findPreviousName method
///
/// This one throws NotImplemented exception, as InMemory doesn't
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index b922662..f9fce9a 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -15,6 +15,9 @@
#ifndef __ZONE_H
#define __ZONE_H 1
+#include <utility>
+#include <vector>
+
#include <dns/rrset.h>
#include <dns/rrsetlist.h>
@@ -139,19 +142,41 @@ public:
WILDCARD_NXRRSET ///< NXRRSET on wildcard, for DNSSEC
};
+ /// Special attribute flags on the result of the \c find() method
+ ///
+ /// The flag values defined here are intended to signal to the caller
+ /// that it may need special handling on the result. This is particularly
+ /// of concern when DNSSEC is requested. For example, for negative
+ /// responses the caller would want to know whether the zone is signed
+ /// with NSEC or NSEC3 so that it can subsequently provide necessary
+ /// proof of the result.
+ ///
+ /// The caller is generally expected to get access to the information
+ /// via read-only getter methods of \c FindResult so that it won't rely
+ /// on specific details of the representation of the flags. So these
+ /// definitions are basically only meaningful for data source
+ /// implementations.
+ enum FindResultFlags {
+ RESULT_DEFAULT = 0, ///< The default flags
+ RESULT_WILDCARD = 1, ///< find() resulted in a wildcard match
+ RESULT_NSEC_SIGNED = 2, ///< The zone is signed with NSEC RRs
+ RESULT_NSEC3_SIGNED = 4 ///< The zone is signed with NSEC3 RRs
+ };
+
/// A helper structure to represent the search result of \c find().
///
/// This is a straightforward tuple of the result code and a pointer
- /// to the found RRset to represent the result of \c find()
- /// (there will be more members in the future - see the class
- /// description).
+ /// (and optionally special flags) to the found RRset to represent the
+ /// result of \c find() (there will be more members in the future -
+ /// see the class description).
/// We use this in order to avoid overloading the return value for both
/// the result code ("success" or "not found") and the found object,
/// i.e., avoid using \c NULL to mean "not found", etc.
///
/// This is a simple value class whose internal state never changes,
- /// so for convenience we allow the applications to refer to the members
- /// directly.
+ /// so for convenience we allow the applications to refer to some of the
+ /// members directly. For others we provide read-only accessor methods
+ /// to hide specific representation.
///
/// Note: we should eventually include a notion of "zone node", which
/// corresponds to a particular domain name of the zone, so that we can
@@ -162,11 +187,39 @@ public:
/// optimize including the NSEC for no-wildcard proof (FWIW NSD does that).
struct FindResult {
FindResult(Result param_code,
- const isc::dns::ConstRRsetPtr param_rrset) :
- code(param_code), rrset(param_rrset)
+ const isc::dns::ConstRRsetPtr param_rrset,
+ FindResultFlags param_flags = RESULT_DEFAULT) :
+ code(param_code), rrset(param_rrset), flags(param_flags)
{}
const Result code;
const isc::dns::ConstRRsetPtr rrset;
+
+ /// Return true iff find() results in a wildcard match.
+ bool isWildcard() const { return ((flags & RESULT_WILDCARD) != 0); }
+
+ /// Return true when the underlying zone is signed with NSEC.
+ ///
+ /// The \c find() implementation allow this to return false if
+ /// \c FIND_DNSSEC isn't specified regardless of whether the zone
+ /// is signed or which of NSEC/NSEC3 is used.
+ ///
+ /// When this is returned, the implementation of find() must ensure
+ /// that \c rrset be a valid NSEC RRset as described in \c find()
+ /// documentation.
+ bool isNSECSigned() const {
+ return ((flags & RESULT_NSEC_SIGNED) != 0);
+ }
+
+ /// Return true when the underlying zone is signed with NSEC3.
+ ///
+ /// The \c find() implementation allow this to return false if
+ /// \c FIND_DNSSEC isn't specified regardless of whether the zone
+ /// is signed or which of NSEC/NSEC3 is used.
+ bool isNSEC3Signed() const {
+ return ((flags & RESULT_NSEC3_SIGNED) != 0);
+ }
+ private:
+ FindResultFlags flags;
};
/// Find options.
@@ -226,10 +279,15 @@ public:
/// of \c DELEGATION and the NS RRset at the zone cut.
/// - If there is no matching name, it returns the code of \c NXDOMAIN,
/// and, if DNSSEC is requested, the NSEC RRset that proves the
- /// non-existence.
+ /// non-existence if the zone is signed with NSEC.
/// - If there is a matching name but no RRset of the search type, it
/// returns the code of \c NXRRSET, and, if DNSSEC is required,
- /// the NSEC RRset for that name.
+ /// the NSEC RRset for that name if the zone is signed with NSEC.
+ /// - If there is no matching name but there is a matching wild card name,
+ /// but it doesn't have a requested type of RR, and if DNSSEC is
+ /// required, then it returns the code of \c WILDCARD_NXRRSET.
+ /// If the zone is signed with NSEC, it returns corresponding NSEC
+ /// (see the description of \c Result).
/// - If there is a CNAME RR of the searched name but there is no
/// RR of the searched type of the name (so this type is different from
/// CNAME), it returns the code of \c CNAME and that CNAME RR.
@@ -306,6 +364,108 @@ public:
std::vector<isc::dns::ConstRRsetPtr> &target,
const FindOptions options = FIND_DEFAULT) = 0;
+ /// A helper structure to represent the search result of \c findNSEC3().
+ ///
+ /// The idea is similar to that of \c FindResult, but \c findNSEC3() has
+ /// special interface and semantics, we use a different structure to
+ /// represent the result.
+ struct FindNSEC3Result {
+ FindNSEC3Result(bool param_matched, uint8_t param_closest_labels,
+ isc::dns::ConstRRsetPtr param_closest_proof,
+ isc::dns::ConstRRsetPtr param_next_proof) :
+ matched(param_matched), closest_labels(param_closest_labels),
+ closest_proof(param_closest_proof),
+ next_proof(param_next_proof)
+ {}
+
+ /// true iff closest_proof is a matching NSEC3
+ const bool matched;
+
+ /// The number of labels of the identified closest encloser.
+ ///
+ const uint8_t closest_labels;
+
+ /// Either the NSEC3 for the closest provable encloser of the given
+ /// name or NSEC3 that covers the name
+ const isc::dns::ConstRRsetPtr closest_proof;
+
+ /// When non NULL, NSEC3 for the next closer name.
+ const isc::dns::ConstRRsetPtr next_proof;
+ };
+
+ /// Search the zone for the NSEC3 RR(s) that prove existence or non
+ /// existence of a give name.
+ ///
+ /// It searches the NSEC3 namespace of the zone (how that namespace is
+ /// implemented can vary in specific data source implementation) for NSEC3
+ /// RRs that match or cover the NSEC3 hash value for the given name.
+ ///
+ /// If \c recursive is false, it will first look for the NSEC3 that has
+ /// a matching hash. If it doesn't exist, it identifies the covering NSEC3
+ /// for the hash. In either case the search stops at that point and the
+ /// found NSEC3 RR(set) will be returned in the closest_proof member of
+ /// \c FindNSEC3Result. \c matched is true or false depending on
+ /// the found NSEC3 is a matched one or covering one. \c next_proof
+ /// is always NULL. closest_labels must be equal to the number of
+ /// labels of \c name (and therefore meaningless).
+ ///
+ /// If \c recursive is true, it will continue the search toward the zone
+ /// apex (origin name) until it finds a provable encloser, that is,
+ /// an ancestor of \c name that has a matching NSEC3. This is the closest
+ /// provable encloser of \c name as defined in RFC5155. In this case,
+ /// if the found encloser is not equal to \c name, the search should
+ /// have seen a covering NSEC3 for the immediate child of the found
+ /// encloser. That child name is the next closer name as defined in
+ /// RFC5155. In this case, this method returns the NSEC3 for the
+ /// closest encloser in \c closest_proof, and the NSEC3 for the next
+ /// closer name in \c next_proof of \c FindNSEC3Result. This set of
+ /// NSEC3 RRs provide the closest encloser proof as defined in RFC5155.
+ /// closest_labels will be set to the number of labels of the identified
+ /// closest encloser. This will be useful when the caller needs to
+ /// construct the closest encloser name from the original \c name.
+ /// If, on the other hand, the found closest name is equal to \c name,
+ /// this method simply returns it in \c closest_proof. \c next_proof
+ /// is set to NULL. In all cases \c matched is set to true.
+ /// closest_labels will be set to the number of labels of \c name.
+ ///
+ /// When looking for NSEC3, this method retrieves NSEC3 parameters from
+ /// the corresponding zone to calculate hash values. Actual implementation
+ /// of how to do this will differ in different data sources. If the
+ /// NSEC3 parameters are not available \c DataSourceError exception
+ /// will be thrown.
+ ///
+ /// \note This implicitly means this method assumes the zone does not
+ /// have more than one set of parameters. This assumption should be
+ /// reasonable in actual deployment and will help simplify the interface
+ /// and implementation. But if there's a real need for supporting
+ /// multiple sets of parameters in a single zone, we will have to
+ /// extend this method so that, e.g., the caller can specify the parameter
+ /// set.
+ ///
+ /// In general, this method expects the zone is properly signed with NSEC3
+ /// RRs. Specifically, it assumes at least the apex node has a matching
+ /// NSEC3 RR (so the search in the recursive mode must always succeed);
+ /// it also assumes that it can retrieve NSEC parameters (iterations,
+ /// algorithm, and salt) from the zone as noted above. If these
+ /// assumptions aren't met, \c DataSourceError exception will be thrown.
+ ///
+ /// \exception InvalidParameter name is not a subdomain of the zone origin
+ /// \exception DataSourceError Low-level or internal datasource errors
+ /// happened, or the zone isn't properly signed with NSEC3
+ /// (NSEC3 parameters cannot be found, no NSEC3s are available, etc).
+ /// \exception std::bad_alloc The underlying implementation involves
+ /// memory allocation and it fails
+ ///
+ /// \param name The name for which NSEC3 RRs are to be found. It must
+ /// be a subdomain of the zone.
+ /// \param recursive Whether or not search should continue until it finds
+ /// a provable encloser (see above).
+ ///
+ /// \return The search result and whether or not the closest_proof is
+ /// a matching NSEC3, in the form of \c FindNSEC3Result object.
+ virtual FindNSEC3Result
+ findNSEC3(const isc::dns::Name& name, bool recursive) = 0;
+
/// \brief Get previous name in the zone
///
/// Gets the previous name in the DNSSEC order. This can be used
@@ -346,6 +506,18 @@ inline ZoneFinder::FindOptions operator |(ZoneFinder::FindOptions a,
static_cast<unsigned>(b)));
}
+/// \brief Operator to combine FindResultFlags
+///
+/// Similar to the same operator for \c FindOptions. Refer to the description
+/// of that function.
+inline ZoneFinder::FindResultFlags operator |(
+ ZoneFinder::FindResultFlags a,
+ ZoneFinder::FindResultFlags b)
+{
+ return (static_cast<ZoneFinder::FindResultFlags>(
+ static_cast<unsigned>(a) | static_cast<unsigned>(b)));
+}
+
/// \brief A pointer-like type pointing to a \c ZoneFinder object.
typedef boost::shared_ptr<ZoneFinder> ZoneFinderPtr;
More information about the bind10-changes
mailing list