BIND 10 master, updated. 7c75154f52853de902bfd7c880b6e16fe3a79c0f Merge branch 'trac1579suggest'
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Apr 13 08:06:08 UTC 2012
The branch, master has been updated
via 7c75154f52853de902bfd7c880b6e16fe3a79c0f (commit)
via d127560b02816a7ba12869d19ca51a30e695ff35 (commit)
via 793772f302013324ee4964e5b2c3439c0eed2221 (commit)
via be2b8d67e266598e0fba9e658042986c6833d220 (commit)
via 6cd82ad2a63dfc9bcd15dabdf650da242eaf924b (commit)
via 493f952e6937adf7045732a2f7e0c4a4313fc0a5 (commit)
via 9b6993002b4ba9019551e50613c8a2c6c7ff9fec (commit)
via fb2317550507bb357cba2eee89c3a469f1a89803 (commit)
via 52e971851f0c7ee8f45c511d810497e3c038dc71 (commit)
via 07274f662a772c856f0bf80213b246e689582409 (commit)
via c3bc4e02519d15e27b8e32291bda1a59ed08f42b (commit)
via b77375be27718eea1619f4e4fdb4899a29eea18e (commit)
via 05793b5a18793908b17190008382a27e133e5979 (commit)
via 186bacfc7ba324647f6689fd627c8d9d2d724c0c (commit)
via 058af3dc4b9f1e03d46c549f3ea848fb1a5c7960 (commit)
via 2d99288b3400b01e3eb1402717a182f5e828c7b7 (commit)
via 52ee8a28742a20415742651cbdf7982387050641 (commit)
via 33f9ea32c67a6e5e7df816432c98f3c9772b0b0f (commit)
via ed4c07d55c90e871d0c2b8ce571273cf83740e66 (commit)
via 77b918b70668c7755f3b7fb8335f5fc1f9f119a6 (commit)
via 4eeff0e79de122645ffd3bf117a1486147fc9541 (commit)
via c31cfdf8449030f874406f4efad754ba7eb786e6 (commit)
via cd6faa250e082808a86d9ccb1f11ea07ab81a618 (commit)
via 5893305969aec78850e2462859b3bf4b7a157057 (commit)
via 419665026524f1a1b46efba377d41bd1f7f806d0 (commit)
via ad0ce258df14fa88a299ef37238d4c2527f273c8 (commit)
via fa4aa9c87c599a985bb19b78ae3f2d1d4ab9bb63 (commit)
via 94793e41d922cb10e35e0ff146b19c38ace415b1 (commit)
via dfd2aeefef39f064183c84c23451637247e32399 (commit)
via 1c57fb2350d9b8440ae4cf50a94be7c61cc462b0 (commit)
via 8c55200c178773691ace2785240bfc65f4e351a9 (commit)
via ae40f56b7e12776479161c7d7a2d6616fae09850 (commit)
via dd8d9d4ab35db98d5269dd9c0728a5af6e748f1a (commit)
via 29e01b55a5acd72e651c2b2bd5cc63ffe8b21da8 (commit)
via c96ac865ac20c4e80b3206a00c15fa998cb85bfd (commit)
via 8e4c96040527d952da60338f7cf061f976780543 (commit)
via 0b1b0da6ffc60991c0ccad85695631dec02db4da (commit)
from d8b8e46b853f13d3e9ef2857f0fce424f1876ef5 (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 7c75154f52853de902bfd7c880b6e16fe3a79c0f
Merge: d8b8e46b853f13d3e9ef2857f0fce424f1876ef5 d127560b02816a7ba12869d19ca51a30e695ff35
Author: haikuo zhang <zhanghaikuo at cnnic.cn>
Date: Fri Apr 13 16:05:17 2012 +0800
Merge branch 'trac1579suggest'
-----------------------------------------------------------------------
Summary of changes:
src/lib/datasrc/database.cc | 272 +++++++++++++++++-----------
src/lib/datasrc/database.h | 148 +++++++++++++--
src/lib/datasrc/tests/database_unittest.cc | 214 ++++++++++++++++++++--
3 files changed, 488 insertions(+), 146 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 160d1da..d71ed8b 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -286,13 +286,11 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
i != result.end(); ++ i) {
sig_store.appendSignatures(i->second);
}
-
if (records_found && any) {
result[RRType::ANY()] = RRsetPtr();
// These will be sitting on the other RRsets.
result.erase(RRType::RRSIG());
}
-
return (FoundRRsets(records_found, result));
}
@@ -330,6 +328,17 @@ NSEC_TYPES() {
}
const WantedTypes&
+NSEC3PARAM_TYPES() {
+ static bool initialized(false);
+ static WantedTypes result;
+ if (!initialized) {
+ result.insert(RRType::NSEC3PARAM());
+ initialized = true;
+ }
+ return (result);
+}
+
+const WantedTypes&
DELEGATION_TYPES() {
static bool initialized(false);
static WantedTypes result;
@@ -355,45 +364,6 @@ FINAL_TYPES() {
}
return (result);
}
-
-}
-
-ConstRRsetPtr
-DatabaseClient::Finder::findNSECCover(const Name& name) {
- try {
- // Which one should contain the NSEC record?
- const Name coverName(findPreviousName(name));
- // Get the record and copy it out
- const FoundRRsets found = getRRsets(coverName.toText(), NSEC_TYPES(),
- coverName != getOrigin());
- const FoundIterator
- nci(found.second.find(RRType::NSEC()));
- if (nci != found.second.end()) {
- return (nci->second);
- } else {
- // The previous doesn't contain NSEC.
- // Badly signed zone or a bug?
-
- // FIXME: Currently, if the zone is not signed, we could get
- // here. In that case we can't really throw, but for now, we can't
- // recognize it. So we don't throw at all, enable it once
- // we have a is_signed flag or something.
-#if 0
- isc_throw(DataSourceError, "No NSEC in " +
- coverName.toText() + ", but it was "
- "returned as previous - "
- "accessor error? Badly signed zone?");
-#endif
- }
- }
- catch (const isc::NotImplemented&) {
- // Well, they want DNSSEC, but there is no available.
- // So we don't provide anything.
- LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED).
- arg(accessor_->getDBName()).arg(name);
- }
- // We didn't find it, return nothing
- return (ConstRRsetPtr());
}
ZoneFinderContextPtr
@@ -416,8 +386,8 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
isc_throw(isc::Unexpected, "Use findAll to answer ANY");
}
return (ZoneFinderContextPtr(new Context(*this, options,
- findInternal(name, type,
- NULL, options))));
+ findInternal(name, type, NULL,
+ options))));
}
DatabaseClient::Finder::DelegationSearchResult
@@ -580,9 +550,9 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
// If none of the above applies in any level, the search fails with NXDOMAIN.
ZoneFinder::ResultContext
DatabaseClient::Finder::findWildcardMatch(
- const isc::dns::Name& name, const isc::dns::RRType& type,
- const FindOptions options, const DelegationSearchResult& dresult,
- std::vector<isc::dns::ConstRRsetPtr>* target)
+ const Name& name, const RRType& type, const FindOptions options,
+ const DelegationSearchResult& dresult, vector<ConstRRsetPtr>* target,
+ FindDNSSECContext& dnssec_ctx)
{
// Note that during the search we are going to search not only for the
// requested type, but also for types that indicate a delegation -
@@ -625,8 +595,8 @@ DatabaseClient::Finder::findWildcardMatch(
} else if (!hasSubdomains(name.split(i - 1).toText())) {
// The wildcard match is the best one, find the final result
// at it. Note that wildcard should never be the zone origin.
- return (findOnNameResult(name, type, options, false,
- found, &wildcard, target));
+ return (findOnNameResult(name, type, options, false, found,
+ &wildcard, target, dnssec_ctx));
} else {
// more specified match found, cancel wildcard match
@@ -642,15 +612,11 @@ DatabaseClient::Finder::findWildcardMatch(
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD_EMPTY).
arg(accessor_->getDBName()).arg(wildcard).arg(name);
- if ((options & FIND_DNSSEC) != 0) {
- ConstRRsetPtr nsec = findNSECCover(Name(wildcard));
- if (nsec) {
- return (ResultContext(NXRRSET, nsec,
- RESULT_WILDCARD |
- RESULT_NSEC_SIGNED));
- }
- }
- return (ResultContext(NXRRSET, ConstRRsetPtr(), RESULT_WILDCARD));
+ const FindResultFlags flags = (RESULT_WILDCARD |
+ dnssec_ctx.getResultFlags());
+ return (ResultContext(NXRRSET,
+ dnssec_ctx.getDNSSECRRset(Name(wildcard),
+ true), flags));
}
}
@@ -688,6 +654,121 @@ DatabaseClient::Finder::logAndCreateResult(
return (ResultContext(code, rrset, flags));
}
+DatabaseClient::Finder::FindDNSSECContext::FindDNSSECContext(
+ DatabaseClient::Finder& finder,
+ const FindOptions options) :
+ finder_(finder),
+ need_dnssec_((options & FIND_DNSSEC) != 0),
+ is_nsec3_(false),
+ is_nsec_(false),
+ probed_(false)
+{}
+
+void
+DatabaseClient::Finder::FindDNSSECContext::probe() {
+ if (!probed_) {
+ probed_ = true;
+ if (need_dnssec_) {
+ // If an NSEC3PARAM RR exists at the zone apex, it's quite likely
+ // that the zone is signed with NSEC3. (If not the zone is more
+ // or less broken, but it's caller's responsibility how to handle
+ // such cases).
+ const string origin = finder_.getOrigin().toText();
+ const FoundRRsets nsec3_found =
+ finder_.getRRsets(origin, NSEC3PARAM_TYPES(), false);
+ const FoundIterator nfi=
+ nsec3_found.second.find(RRType::NSEC3PARAM());
+ is_nsec3_ = (nfi != nsec3_found.second.end());
+
+ // Likewise for NSEC, depending on the apex has an NSEC RR.
+ // If we know the zone is NSEC3-signed, however, we don't bother
+ // to check that. This is aligned with the transition guideline
+ // described in Section 10.4 of RFC 5155.
+ if (!is_nsec3_) {
+ const FoundRRsets nsec_found =
+ finder_.getRRsets(origin, NSEC_TYPES(), false);
+ const FoundIterator nfi =
+ nsec_found.second.find(RRType::NSEC());
+ is_nsec_ = (nfi != nsec_found.second.end());
+ }
+ }
+ }
+}
+
+bool
+DatabaseClient::Finder::FindDNSSECContext::isNSEC3() {
+ if (!probed_) {
+ probe();
+ }
+ return (is_nsec3_);
+}
+
+bool
+DatabaseClient::Finder::FindDNSSECContext::isNSEC() {
+ if (!probed_) {
+ probe();
+ }
+ return (is_nsec_);
+}
+
+isc::dns::ConstRRsetPtr
+DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(
+ const FoundRRsets& found_set)
+{
+ if (!isNSEC()) {
+ return (ConstRRsetPtr());
+ }
+
+ const FoundIterator nci = found_set.second.find(RRType::NSEC());
+ if (nci != found_set.second.end()) {
+ return (nci->second);
+ } else {
+ return (ConstRRsetPtr());
+ }
+}
+
+isc::dns::ConstRRsetPtr
+DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(const Name &name,
+ bool covering)
+{
+ if (!isNSEC()) {
+ return (ConstRRsetPtr());
+ }
+
+ try {
+ const Name& nsec_name =
+ covering ? finder_.findPreviousName(name) : name;
+ const bool need_nscheck = (nsec_name != finder_.getOrigin());
+ const FoundRRsets found = finder_.getRRsets(nsec_name.toText(),
+ NSEC_TYPES(),
+ need_nscheck);
+ const FoundIterator nci = found.second.find(RRType::NSEC());
+ if (nci != found.second.end()) {
+ return (nci->second);
+ }
+ } catch (const isc::NotImplemented&) {
+ // This happens when the underlying database accessor doesn't support
+ // findPreviousName() (it probably doesn't support DNSSEC at all) but
+ // there is somehow an NSEC RR at the zone apex. We log the fact but
+ // otherwise let the caller decide what to do (so, for example, a
+ // higher level query processing won't completely fail but can return
+ // anything it can get).
+ LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED).
+ arg(finder_.accessor_->getDBName()).arg(name);
+ }
+ return (ConstRRsetPtr());
+}
+
+ZoneFinder::FindResultFlags
+DatabaseClient::Finder::FindDNSSECContext::getResultFlags() {
+ if (isNSEC3()) {
+ return (RESULT_NSEC3_SIGNED);
+ } else if (isNSEC()) {
+ return (RESULT_NSEC_SIGNED);
+ }
+ return (RESULT_DEFAULT);
+}
+
ZoneFinder::ResultContext
DatabaseClient::Finder::findOnNameResult(const Name& name,
const RRType& type,
@@ -696,28 +777,22 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
const FoundRRsets& found,
const string* wildname,
std::vector<isc::dns::ConstRRsetPtr>*
- target)
+ target, FindDNSSECContext& dnssec_ctx)
{
const bool wild = (wildname != NULL);
- FindResultFlags flags = wild ? RESULT_WILDCARD : RESULT_DEFAULT;
+ // For wildcard case with DNSSEC required, the caller would need to
+ // know whether it's NSEC or NSEC3 signed. getResultFlags returns
+ // appropriate flag based on the query context and zone status.
+ const FindResultFlags flags =
+ wild ? (RESULT_WILDCARD | dnssec_ctx.getResultFlags()) : RESULT_DEFAULT;
// Get iterators for the different types of records we are interested in -
// CNAME, NS and Wanted types.
const FoundIterator nsi(found.second.find(RRType::NS()));
const FoundIterator cni(found.second.find(RRType::CNAME()));
const FoundIterator wti(found.second.find(type));
- // For wildcard case with DNSSEC required, the caller would need to know
- // whether it's NSEC or NSEC3 signed. So we need to do an additional
- // search here, even though the NSEC RR may not be returned.
- // TODO: this part should be revised when we support NSEC3; ideally we
- // should use more effective and efficient way to identify (whether and)
- // in which way the zone is signed.
- if (wild && (options & FIND_DNSSEC) != 0 &&
- found.second.find(RRType::NSEC()) != found.second.end()) {
- flags = flags | RESULT_NSEC_SIGNED;
- }
-
- if (!is_origin && ((options & FIND_GLUE_OK) == 0) &&
+
+ if (!is_origin && (options & FIND_GLUE_OK) == 0 &&
nsi != found.second.end()) {
// A NS RRset was found at the domain we were searching for. As it is
// not at the origin of the zone, it is a delegation and indicates that
@@ -744,7 +819,6 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
wild ? DATASRC_DATABASE_WILDCARD_CNAME :
DATASRC_DATABASE_FOUND_CNAME,
flags));
-
} else if (wti != found.second.end()) {
bool any(type == RRType::ANY());
isc::log::MessageID lid(wild ? DATASRC_DATABASE_WILDCARD_MATCH :
@@ -776,32 +850,20 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
// provide the NSEC records. If it's for wildcard, we need to get the
// NSEC records in the name of the wildcard, not the substituted one,
// so we need to search the tree again.
- ConstRRsetPtr nsec_rrset; // possibly used with DNSSEC, otherwise NULL
- if ((options & FIND_DNSSEC) != 0) {
- if (wild) {
- const FoundRRsets wfound = getRRsets(*wildname, NSEC_TYPES(),
- true);
- const FoundIterator nci = wfound.second.find(RRType::NSEC());
- if (nci != wfound.second.end()) {
- nsec_rrset = nci->second;
- }
- } else {
- const FoundIterator nci = found.second.find(RRType::NSEC());
- if (nci != found.second.end()) {
- nsec_rrset = nci->second;
- }
- }
- }
- if (nsec_rrset) {
+ const ConstRRsetPtr dnssec_rrset =
+ wild ? dnssec_ctx.getDNSSECRRset(Name(*wildname), false) :
+ dnssec_ctx.getDNSSECRRset(found);
+ if (dnssec_rrset) {
// This log message covers both normal and wildcard cases, so we pass
// NULL for 'wildname'.
- return (logAndCreateResult(name, NULL, type, NXRRSET, nsec_rrset,
+ return (logAndCreateResult(name, NULL, type, NXRRSET, dnssec_rrset,
DATASRC_DATABASE_FOUND_NXRRSET_NSEC,
flags | RESULT_NSEC_SIGNED));
}
- return (logAndCreateResult(name, wildname, type, NXRRSET, nsec_rrset,
+ return (logAndCreateResult(name, wildname, type, NXRRSET, dnssec_rrset,
wild ? DATASRC_DATABASE_WILDCARD_NXRRSET :
- DATASRC_DATABASE_FOUND_NXRRSET, flags));
+ DATASRC_DATABASE_FOUND_NXRRSET,
+ flags | dnssec_ctx.getResultFlags()));
}
ZoneFinder::ResultContext
@@ -809,10 +871,8 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
FindOptions options,
const DelegationSearchResult& dresult,
std::vector<isc::dns::ConstRRsetPtr>*
- target)
+ target, FindDNSSECContext& dnssec_ctx)
{
- const bool dnssec_data = ((options & FIND_DNSSEC) != 0);
-
// On entry to this method, we know that the database doesn't have any
// entry for this name. Before returning NXDOMAIN, we need to check
// for special cases.
@@ -824,17 +884,16 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
arg(accessor_->getDBName()).arg(name);
- const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
- ConstRRsetPtr();
- return (ResultContext(NXRRSET, nsec,
- nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
+ return (ResultContext(NXRRSET, dnssec_ctx.getDNSSECRRset(name, true),
+ dnssec_ctx.getResultFlags()));
} else if ((options & NO_WILDCARD) == 0) {
// It's not an empty non-terminal and wildcard matching is not
// disabled, so check for wildcards. If there is a wildcard match
// (i.e. all results except NXDOMAIN) return it; otherwise fall
// through to the NXDOMAIN case below.
const ResultContext wcontext =
- findWildcardMatch(name, type, options, dresult, target);
+ findWildcardMatch(name, type, options, dresult, target,
+ dnssec_ctx);
if (wcontext.code != NXDOMAIN) {
return (wcontext);
}
@@ -844,10 +903,8 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
// NSEC records if requested).
LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_NO_MATCH).
arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
- const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
- ConstRRsetPtr();
- return (ResultContext(NXDOMAIN, nsec,
- nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
+ return (ResultContext(NXDOMAIN, dnssec_ctx.getDNSSECRRset(name, true),
+ dnssec_ctx.getResultFlags()));
}
ZoneFinder::ResultContext
@@ -897,16 +954,17 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
const FoundRRsets found = getRRsets(name.toText(), final_types,
!is_origin, NULL,
type == RRType::ANY());
-
+ FindDNSSECContext dnssec_ctx(*this, options);
if (found.first) {
// Something found at the domain name. Look into it further to get
// the final result.
return (findOnNameResult(name, type, options, is_origin, found, NULL,
- target));
+ target, dnssec_ctx));
} else {
// Did not find anything at all at the domain name, so check for
// subdomains or wildcards.
- return (findNoNameResult(name, type, options, dresult, target));
+ return (findNoNameResult(name, type, options, dresult, target,
+ dnssec_ctx));
}
}
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index d9d6dfa..0318589 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -736,6 +736,7 @@ public:
DatabaseClient(isc::dns::RRClass rrclass,
boost::shared_ptr<DatabaseAccessor> accessor);
+
/// \brief Corresponding ZoneFinder implementation
///
/// The zone finder implementation for database data sources. Similarly
@@ -845,6 +846,7 @@ public:
boost::shared_ptr<DatabaseAccessor> accessor_;
const int zone_id_;
const isc::dns::Name origin_;
+
/// \brief Shortcut name for the result of getRRsets
typedef std::pair<bool, std::map<dns::RRType, dns::RRsetPtr> >
FoundRRsets;
@@ -900,6 +902,119 @@ public:
const std::string* construct_name = NULL,
bool any = false);
+ /// \brief DNSSEC related context for ZoneFinder::findInternal.
+ ///
+ /// This class is a helper for the ZoneFinder::findInternal method,
+ /// encapsulating DNSSEC related information and processing logic.
+ /// Specifically, it tells the finder whether the zone under search
+ /// is DNSSEC signed or not, and if it is, whether it's with NSEC or
+ /// with NSEC3. It also provides a RRset DNSSEC proof RRset for some
+ /// specific situations (in practice, this means an NSEC RRs for
+ /// negative proof when they are needed and expected).
+ ///
+ /// The purpose of this class is to keep the main finder implementation
+ /// unaware of DNSSEC related details. It's also intended to help
+ /// avoid unnecessary lookup for DNSSEC proof RRsets; this class
+ /// doesn't look into the DB for these RRsets unless it's known to
+ /// be needed. The same optimization could be implemented in the
+ /// main code, but it will result in duplicate similar code logic
+ /// and make the code more complicated. By encapsulating and unifying
+ /// the logic in a single separate class, we can keep the main
+ /// search logic readable.
+ class FindDNSSECContext {
+ public:
+ /// \brief Constructor for FindDNSSECContext class.
+ ///
+ /// This constructor doesn't involve any expensive operation such
+ /// as database lookups. It only initializes some internal
+ /// states (in a cheap way) and remembers if DNSSEC proof
+ /// is requested.
+ ///
+ /// \param finder The Finder for the findInternal that uses this
+ /// context.
+ /// \param options Find options given to the finder.
+ FindDNSSECContext(Finder& finder, const FindOptions options);
+
+ /// \brief Return DNSSEC related result flags for the context.
+ ///
+ /// This method returns a FindResultFlags value related to
+ /// DNSSEC, based on the context. If DNSSEC proof is requested
+ /// and the zone is signed with NSEC/NSEC3, it returns
+ /// RESULT_NSEC_SIGNED/RESULT_NSEC3_SIGNED, respectively;
+ /// otherwise it returns RESULT_DEFAULT. So the caller can simply
+ /// take a logical OR for the returned value of this method and
+ /// whatever other flags it's going to set, without knowing
+ /// DNSSEC specific information.
+ ///
+ /// If it's not yet identified whether and how the zone is DNSSEC
+ /// signed at the time of the call, it now detects that via
+ /// database lookups (if necessary). (And this is because why
+ /// this method cannot be a const member function).
+ ZoneFinder::FindResultFlags getResultFlags();
+
+ /// \brief Get DNSSEC negative proof for a given name.
+ ///
+ /// If the zone is considered NSEC-signed and the context
+ /// requested DNSSEC proofs, this method tries to find NSEC RRs
+ /// for the give name. If \c covering is true, it means a
+ /// "no name" proof is requested, so it calls findPreviousName on
+ /// the given name and extracts an NSEC record on the result;
+ /// otherwise it tries to get NSEC RRs for the given name. If
+ /// the NSEC is found, this method returns it; otherwise it returns
+ /// NULL.
+ ///
+ /// In all other cases this method simply returns NULL.
+ ///
+ /// \param name The name which the NSEC RRset belong to.
+ /// \param covering true if a covering NSEC is required; false if
+ /// a matching NSEC is required.
+ /// \return Any found DNSSEC proof RRset or NULL
+ isc::dns::ConstRRsetPtr getDNSSECRRset(
+ const isc::dns::Name& name, bool covering);
+
+ /// \brief Get DNSSEC negative proof for a given name.
+ ///
+ /// If the zone is considered NSEC-signed and the context
+ /// requested DNSSEC proofs, this method tries to find NSEC RRset
+ /// from the given set (\c found_set) and returns it if found;
+ /// in other cases this method simply returns NULL.
+ ///
+ /// \param found_set The RRset which may contain an NSEC RRset.
+ /// \return Any found DNSSEC proof RRset or NULL
+ isc::dns::ConstRRsetPtr getDNSSECRRset(const FoundRRsets&
+ found_set);
+
+ private:
+ /// \brief Returns whether the zone is signed with NSEC3.
+ ///
+ /// This method returns true if the zone for the finder that
+ /// uses this context is considered DNSSEC signed with NSEC3;
+ /// otherwise it returns false. If it's not yet detected,
+ /// this method now detects that via database lookups (if
+ /// necessary).
+ bool isNSEC3();
+
+ /// \brief Returns whether the zone is signed with NSEC.
+ ///
+ /// This is similar to isNSEC3(), but works for NSEC.
+ bool isNSEC();
+
+ /// \brief Probe into the database to see if/how the zone is
+ /// signed.
+ ///
+ /// This is a subroutine of isNSEC3() and isNSEC(), and performs
+ /// delayed database probe to detect whether the zone used by
+ /// the finder is DNSSEC signed, and if it is, with NSEC or NSEC3.
+ void probe();
+
+ DatabaseClient::Finder& finder_;
+ const bool need_dnssec_;
+
+ bool is_nsec3_;
+ bool is_nsec_;
+ bool probed_;
+ };
+
/// \brief Search result of \c findDelegationPoint().
///
/// This is a tuple combining the result of the search - a status code
@@ -1002,7 +1117,8 @@ public:
/// \param target If the type happens to be ANY, it will insert all
/// the RRsets of the found name (if any is found) here instead
/// of being returned by the result.
- ///
+ /// \param dnssec_ctx The dnssec context, it is a DNSSEC wrapper for
+ /// find function.
/// \return Tuple holding the result of the search - the RRset of the
/// wildcard records matching the name, together with a status
/// indicating the match type (e.g. CNAME at the wildcard
@@ -1010,12 +1126,12 @@ public:
/// success due to an exact match). Also returned if there
/// is no match is an indication as to whether there was an
/// NXDOMAIN or an NXRRSET.
- ResultContext findWildcardMatch(
- const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options,
- const DelegationSearchResult& dresult,
- std::vector<isc::dns::ConstRRsetPtr>* target);
+ ResultContext findWildcardMatch(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options,
+ const DelegationSearchResult& dresult,
+ std::vector<isc::dns::ConstRRsetPtr>*
+ target, FindDNSSECContext& dnssec_ctx);
/// \brief Handle matching results for name
///
@@ -1048,7 +1164,9 @@ public:
/// it's NULL in the case of non wildcard match.
/// \param target When the query is any, this must be set to a vector
/// where the result will be stored.
- ///
+ /// \param dnssec_ctx The dnssec context, it is a DNSSEC wrapper for
+ /// find function.
+
/// \return Tuple holding the result of the search - the RRset of the
/// wildcard records matching the name, together with a status
/// indicating the match type (corresponding to the each of
@@ -1062,7 +1180,7 @@ public:
const FoundRRsets& found,
const std::string* wildname,
std::vector<isc::dns::ConstRRsetPtr>*
- target);
+ target, FindDNSSECContext& dnssec_ctx);
/// \brief Handle no match for name
///
@@ -1087,7 +1205,8 @@ public:
/// \param target If the query is for type ANY, the successfull result,
/// if there happens to be one, will be returned through the
/// parameter, as it doesn't fit into the result.
- ///
+ /// \param dnssec_ctx The dnssec context, it is a DNSSEC wrapper for
+ /// find function.
/// \return Tuple holding the result of the search - the RRset of the
/// wildcard records matching the name, together with a status
/// indicating the match type (e.g. CNAME at the wildcard
@@ -1098,7 +1217,7 @@ public:
FindOptions options,
const DelegationSearchResult& dresult,
std::vector<isc::dns::ConstRRsetPtr>*
- target);
+ target, FindDNSSECContext& dnssec_ctx);
/// Logs condition and creates result
///
@@ -1139,13 +1258,6 @@ public:
/// \return true if the name has subdomains, false if not.
bool hasSubdomains(const std::string& name);
- /// \brief Get the NSEC covering a name.
- ///
- /// This one calls findPreviousName on the given name and extracts an
- /// NSEC record on the result. It handles various error cases. The
- /// method exists to share code present at more than one location.
- dns::ConstRRsetPtr findNSECCover(const dns::Name& name);
-
/// \brief Convenience type shortcut.
///
/// To find stuff in the result of getRRsets.
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index 1da39c1..cd844e4 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -167,7 +167,10 @@ const char* const TEST_RECORDS[][5] = {
"1234 3600 1800 2419200 7200" },
{"example.org.", "NS", "3600", "", "ns.example.com."},
{"example.org.", "A", "3600", "", "192.0.2.1"},
- {"example.org.", "NSEC", "3600", "", "acnamesig1.example.org. NS A NSEC RRSIG"},
+ // Note that the RDATA text is "normalized", i.e., identical to what
+ // Rdata::toText() would produce. some tests rely on that behavior.
+ {"example.org.", "NSEC", "3600", "",
+ "acnamesig1.example.org. A NS RRSIG NSEC"},
{"example.org.", "RRSIG", "3600", "", "SOA 5 3 3600 20000101000000 "
"20000201000000 12345 example.org. FAKEFAKEFAKE"},
{"example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 "
@@ -1571,12 +1574,14 @@ doFindTest(ZoneFinder& finder,
isc::dns::RRType::RRSIG(), expected_ttl,
expected_sig_rdatas);
} else if (expected_sig_rdatas.empty()) {
- EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig());
+ EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig()) <<
+ "Unexpected RRSIG: " << result->rrset->getRRsig()->toText();
} else {
ADD_FAILURE() << "Missing RRSIG";
}
} else if (expected_rdatas.empty()) {
- EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset);
+ EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset) <<
+ "Unexpected RRset: " << result->rrset->toText();
} else {
ADD_FAILURE() << "Missing result";
}
@@ -1590,7 +1595,9 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
const isc::dns::Name& expected_name =
isc::dns::Name::ROOT_NAME(),
const ZoneFinder::FindOptions options =
- ZoneFinder::FIND_DEFAULT)
+ ZoneFinder::FIND_DEFAULT,
+ ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT)
{
SCOPED_TRACE("All test for " + name.toText());
std::vector<ConstRRsetPtr> target;
@@ -1598,6 +1605,15 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
EXPECT_TRUE(target.empty());
EXPECT_EQ(expected_result, result->code);
EXPECT_EQ(expected_type, result->rrset->getType());
+ if (expected_flags != ZoneFinder::RESULT_DEFAULT){
+ EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+ result->isWildcard());
+ EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
+ result->isNSECSigned());
+ EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0,
+ result->isNSEC3Signed());
+
+ }
RdataIteratorPtr it(result->rrset->getRdataIterator());
std::vector<std::string> rdata;
while (!it->isLast()) {
@@ -2403,10 +2419,169 @@ TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) {
Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC);
}
+// Subroutine for dnssecFlagCheck defined below. It performs some simple
+// checks regarding DNSSEC related result flags for findAll().
+void
+dnssecFlagCheckForAny(ZoneFinder& finder, const Name& name,
+ ZoneFinder::FindResultFlags sec_flag)
+{
+ std::vector<ConstRRsetPtr> target; // just for placeholder
+ ConstZoneFinderContextPtr all_result =
+ finder.findAll(name, target, ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
+ all_result->isNSECSigned());
+ EXPECT_EQ((sec_flag & ZoneFinder::RESULT_NSEC3_SIGNED) != 0,
+ all_result->isNSEC3Signed());
+}
+
+// Common tests about DNSSEC related result flags. Shared for the NSEC
+// and NSEC3 cases.
+void
+dnssecFlagCheck(ZoneFinder& finder, ZoneFinder::FindResultFlags sec_flag) {
+ std::vector<std::string> expected_rdatas;
+ std::vector<std::string> expected_sig_rdatas;
+
+ // Check NXDOMAIN case in NSEC signed zone, and RESULT_NSEC_SIGNED flag
+ // should be returned to upper layer caller.
+ if ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ expected_rdatas.push_back("www2.example.org. A AAAA NSEC RRSIG");
+ expected_sig_rdatas.push_back("NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. "
+ "FAKEFAKEFAKE");
+ }
+ doFindTest(finder, Name("www1.example.org"), RRType::A(), RRType::NSEC(),
+ RRTTL(3600), ZoneFinder::NXDOMAIN, expected_rdatas,
+ expected_sig_rdatas, sec_flag, Name("www.example.org."),
+ ZoneFinder::FIND_DNSSEC);
+ dnssecFlagCheckForAny(finder, Name("www1.example.org"), sec_flag);
+
+ // Check NXRRSET case in NSEC signed zone, and RESULT_NSEC_SIGNED flag
+ // should be return.
+ // No "findAll" test case for this because NXRRSET shouldn't happen for it.
+ expected_rdatas.clear();
+ expected_sig_rdatas.clear();
+ if ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ expected_rdatas.push_back("www2.example.org. A AAAA NSEC RRSIG");
+ expected_sig_rdatas.push_back("NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. "
+ "FAKEFAKEFAKE");
+ }
+ doFindTest(finder, Name("www.example.org."), RRType::TXT(), RRType::NSEC(),
+ RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas,
+ expected_sig_rdatas, sec_flag, Name::ROOT_NAME(),
+ ZoneFinder::FIND_DNSSEC);
+
+ // Empty name, should result in NXRRSET (in this test setup the NSEC
+ // doesn't have RRSIG).
+ expected_rdatas.clear();
+ expected_sig_rdatas.clear();
+ if ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ expected_rdatas.push_back("empty.nonterminal.example.org. NSEC");
+ }
+ doFindTest(finder, Name("nonterminal.example.org."), RRType::A(),
+ RRType::NSEC(), RRTTL(3600), ZoneFinder::NXRRSET,
+ expected_rdatas,expected_sig_rdatas, sec_flag,
+ Name("l.example.org."), ZoneFinder::FIND_DNSSEC);
+ dnssecFlagCheckForAny(finder, Name("nonterminal.example.org"), sec_flag);
+
+ // Wildcard match
+ expected_rdatas.clear();
+ expected_sig_rdatas.clear();
+ expected_rdatas.push_back("192.0.2.5");
+ expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. "
+ "FAKEFAKEFAKE");
+ doFindTest(finder, Name("b.a.wild.example.org"), RRType::A(),
+ RRType::A(), RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas,
+ expected_sig_rdatas, (ZoneFinder::RESULT_WILDCARD | sec_flag),
+ Name("b.a.wild.example.org"), ZoneFinder::FIND_DNSSEC);
+ dnssecFlagCheckForAny(finder, Name("b.a.wild.example.org"), sec_flag);
+
+ // Wildcard + NXRRSET (no "findAll" test for this case)
+ expected_rdatas.clear();
+ expected_sig_rdatas.clear();
+ if ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ expected_rdatas.push_back("cancel.here.wild.example.org. "
+ "A NSEC RRSIG");
+ expected_sig_rdatas.push_back("NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. "
+ "FAKEFAKEFAKE");
+ }
+ doFindTest(finder, Name("b.a.wild.example.org"),
+ RRType::TXT(), RRType::NSEC(), RRTTL(3600), ZoneFinder::NXRRSET,
+ expected_rdatas, expected_sig_rdatas,
+ (ZoneFinder::RESULT_WILDCARD | sec_flag),
+ Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC);
+
+ // Empty wildcard (this NSEC doesn't have RRSIG in our test data)
+ expected_rdatas.clear();
+ expected_sig_rdatas.clear();
+ if ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ expected_rdatas.push_back("wild.*.foo.*.bar.example.org. NSEC");
+ }
+ doFindTest(finder, Name("foo.wild.bar.example.org"),
+ RRType::TXT(), RRType::NSEC(), RRTTL(3600), ZoneFinder::NXRRSET,
+ expected_rdatas, expected_sig_rdatas,
+ (ZoneFinder::RESULT_WILDCARD | sec_flag),
+ Name("bao.example.org"), ZoneFinder::FIND_DNSSEC);
+ dnssecFlagCheckForAny(finder, Name("foo.wild.bar.example.org"), sec_flag);
+}
+
+TYPED_TEST(DatabaseClientTest, dnssecResultFlags) {
+ // ZoneFinder::find() for negative cases and wildcard cases should check
+ // whether the zone is signed with NSEC or NSEC3.
+
+ // In the default test setup, the zone should be considered NSEC-signed
+ // (the apex node has an NSEC RR).
+ {
+ SCOPED_TRACE("NSEC only");
+ dnssecFlagCheck(*this->getFinder(), ZoneFinder::RESULT_NSEC_SIGNED);
+ }
+
+ // Then add an NSEC3PARAM RRset at the apex (it may look weird if the
+ // zone only has NSEC3PARM RRset (but no NSEC3s), but it is okay for the
+ // purpose of this test). The zone should now be considered NSEC3-signed.
+ // Note that the apex NSEC still exists; NSEC3 should override NSEC.
+ this->updater_ = this->client_->getUpdater(this->zname_, false);
+ this->rrset_.reset(new RRset(this->zname_, this->qclass_,
+ RRType::NSEC3PARAM(), this->rrttl_));
+ this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(),
+ this->rrset_->getClass(),
+ "1 0 12 aabbccdd"));
+ this->updater_->addRRset(*this->rrset_);
+ {
+ SCOPED_TRACE("NSEC and NSEC3");
+ dnssecFlagCheck(this->updater_->getFinder(),
+ ZoneFinder::RESULT_NSEC3_SIGNED);
+ }
+
+ // Next, delete the apex NSEC. Since NSEC3PARAM remains, the zone should
+ // still be considered NSEC3-signed.
+ RRsetPtr nsec_rrset(new RRset(this->zname_, this->qclass_, RRType::NSEC(),
+ this->rrttl_));
+ nsec_rrset->addRdata(rdata::createRdata(RRType::NSEC(), this->qclass_,
+ "acnamesig1.example.org. NS A "
+ "NSEC RRSIG"));
+ this->updater_->deleteRRset(*nsec_rrset);
+ {
+ SCOPED_TRACE("NSEC3 only");
+ dnssecFlagCheck(this->updater_->getFinder(),
+ ZoneFinder::RESULT_NSEC3_SIGNED);
+ }
+
+ // Finally, delete the NSEC3PARAM we just added above. The zone should
+ // then be considered unsigned.
+ this->updater_->deleteRRset(*this->rrset_);
+ {
+ SCOPED_TRACE("unsigned");
+ dnssecFlagCheck(this->updater_->getFinder(),
+ ZoneFinder::RESULT_DEFAULT);
+ }
+}
+
TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
// The domain doesn't exist, so we must get the right NSEC
boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
-
this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG");
this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
"20000201000000 12345 example.org. "
@@ -2433,14 +2608,13 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
if (!this->is_mock_) {
return; // We don't make the real DB to throw
}
- EXPECT_NO_THROW(doFindTest(*finder,
- isc::dns::Name("notimplnsec.example.org."),
- isc::dns::RRType::TXT(),
- isc::dns::RRType::NSEC(), this->rrttl_,
- ZoneFinder::NXDOMAIN, this->empty_rdatas_,
- this->empty_rdatas_,
- ZoneFinder::RESULT_DEFAULT,
- Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
+ // In this case the accessor doesn't support findPreviousName(), but the
+ // zone apex has NSEC, and the zone itself is considered NSEC-signed.
+ doFindTest(*finder, Name("notimplnsec.example.org."),
+ RRType::TXT(), RRType::NSEC(), this->rrttl_,
+ ZoneFinder::NXDOMAIN, this->empty_rdatas_,
+ this->empty_rdatas_, ZoneFinder::RESULT_NSEC_SIGNED,
+ Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC);
}
TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
@@ -2460,14 +2634,12 @@ TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
if (!this->is_mock_) {
return; // We don't make the real DB to throw
}
- EXPECT_NO_THROW(doFindTest(*finder,
- isc::dns::Name("here.wild.example.org."),
- isc::dns::RRType::TXT(),
- isc::dns::RRType::NSEC(),
- this->rrttl_, ZoneFinder::NXRRSET,
- this->empty_rdatas_, this->empty_rdatas_,
- ZoneFinder::RESULT_DEFAULT,
- Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
+ // See the corresponding case of NXDOMAIN_NSEC.
+ doFindTest(*finder, Name("here.wild.example.org."),
+ RRType::TXT(), RRType::NSEC(), this->rrttl_,
+ ZoneFinder::NXRRSET, this->empty_rdatas_,
+ this->empty_rdatas_, ZoneFinder::RESULT_NSEC_SIGNED,
+ Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC);
}
TYPED_TEST(DatabaseClientTest, anyFromFind) {
More information about the bind10-changes
mailing list