// Identify the RBTree node that best matches the given name.
// See implementation notes below.
+ // The caller should pass an empty node_path, and it will contain the
+ // search context for possible later use at the caller side.
template <typename ResultType>
ResultType findNode(const Name& name,
RBTreeNodeChain<Domain>& node_path,
ZoneFinder::FindOptions options) const;
+
+private:
+ // A helper method for NSEC-signed zones. It searches the zone for
+ // the "closest" NSEC corresponding to the search context stored in
+ // node_path (it should contain sufficient information to identify the
+ // previous name of the query name in the zone). In some cases the
+ // immediate closest name may not have NSEC (when it's under a zone cut
+ // for glue records, or even when the zone is partly broken), so this
+ // method continues the search until it finds a name that has NSEC,
+ // and returns the one found first. Due to the prerequisite (see below),
+ // it should always succeed.
+ //
+ // node_path must store valid search context (in practice, it's expected
+ // to be set by findNode()); otherwise the underlying RBTree implementation
+ // throws.
+ //
+ // If the zone is not considered NSEC-signed or DNSSEC records were not
+ // required in the original search context (specified in options), this
+ // method doesn't bother to find NSEC, and simply returns NULL. So, by
+ // definition of "NSEC-signed", when it really tries to find an NSEC it
+ // should succeed; there should be one at least at the zone origin.
+ ConstRBNodeRRsetPtr
+ getClosestNSEC(RBTreeNodeChain<Domain>& node_path,
+ ZoneFinder::FindOptions options) const;
};
+ConstRBNodeRRsetPtr
+ZoneData::getClosestNSEC(RBTreeNodeChain<Domain>& node_path,
+ ZoneFinder::FindOptions options) const
+{
+ if ((options & ZoneFinder::FIND_DNSSEC) == 0 || !nsec_signed_) {
+ return (ConstRBNodeRRsetPtr());
+ }
+
+ const DomainNode* prev_node;
+ while ((prev_node = domains_.previousNode(node_path)) != NULL) {
+ if (!prev_node->isEmpty()) {
+ const Domain::const_iterator found =
+ prev_node->getData()->find(RRType::NSEC());
+ if (found != prev_node->getData()->end()) {
+ return (found->second);
+ }
+ }
+ }
+ // This must be impossible and should be an internal bug.
+ // See the description at the method declaration.
+ assert(false);
+}
+
/// Maintain intermediate data specific to the search context used in
/// \c find().
///
}
// Nothing really matched.
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
- return (ResultType(ZoneFinder::NXDOMAIN, node, state.rrset_));
+ return (ResultType(ZoneFinder::NXDOMAIN, node,
+ getClosestNSEC(node_path, options)));
} else {
// If the name is neither an exact or partial match, it is
// out of bailiwick, which is considered an error.
findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true,
ConstRRsetPtr(), expected_flags);
- // These domains don't exist (and one is out of the zone)
- findTest(Name("nothere.example.org"), RRType::A(), ZoneFinder::NXDOMAIN,
- true, ConstRRsetPtr(), expected_flags);
+ // These domains don't exist. (and one is out of the zone). In an
+ // NSEC-signed zone, it should return the covering NSEC for the query
+ // name (the actual NSEC in the test data may not really "cover" it,
+ // but for the purpose of this test it's okay).
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ // There's no other name between this one and the origin, so it
+ // should return the origin NSEC.
+ findTest(Name("nothere.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, rr_nsec_, expected_flags,
+ NULL, ZoneFinder::FIND_DNSSEC);
+
+ // The previous name in the zone is "ns.example.org", but it doesn't
+ // have an NSEC. It should be skipped and the origin NSEC will be
+ // returned as the "closest NSEC".
+ findTest(Name("nxdomain.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, rr_nsec_, expected_flags,
+ NULL, ZoneFinder::FIND_DNSSEC);
+ } else {
+ findTest(Name("nothere.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags);
+ }
EXPECT_THROW(zone_finder_.find(Name("example.net"), RRType::A()),
OutOfZone);
}
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_under_wild_));
{
SCOPED_TRACE("Search under non-wildcard");
- findTest(Name("bar.foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags,
- NULL, find_options);
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ // For the test setup of "nsec-signed" zone, there's actually
+ // only one NSEC; the one at the apex. So find() will return it
+ // as the covering NSEC.
+ findTest(Name("bar.foo.wild.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, rr_nsec_, expected_flags,
+ NULL, find_options);
+ } else {
+ findTest(Name("bar.foo.wild.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(),
+ expected_flags, NULL, find_options);
+ }
}
}