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:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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) {
|
||||
|
Reference in New Issue
Block a user