2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 14:05:33 +00:00

Merge branch 'trac1579suggest'

This commit is contained in:
haikuo zhang
2012-04-13 16:05:17 +08:00
3 changed files with 487 additions and 145 deletions

View File

@@ -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));
}
@@ -329,6 +327,17 @@ NSEC_TYPES() {
return (result);
}
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);
@@ -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));
}
}

View File

@@ -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.

View File

@@ -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) {