From: JINMEI Tatuya Date: Wed, 9 May 2012 09:07:40 +0000 (-0700) Subject: [1805] completed implementation of getClosestNSEC(). X-Git-Tag: trac2351_base~226^2~158^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1322f10b28fb7ccbc69768c409ac36fe26ce0657;p=thirdparty%2Fkea.git [1805] completed implementation of getClosestNSEC(). for tests, we use some of the NXDOMAIN cases (part of #1809) --- diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 59087469f8..88a974c6df 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -212,12 +212,61 @@ public: // 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 ResultType findNode(const Name& name, RBTreeNodeChain& 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& node_path, + ZoneFinder::FindOptions options) const; }; +ConstRBNodeRRsetPtr +ZoneData::getClosestNSEC(RBTreeNodeChain& 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(). /// @@ -420,7 +469,8 @@ ZoneData::findNode(const Name& name, RBTreeNodeChain& node_path, } // 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. diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index 07d1fb9605..4474a3a8f6 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -1002,9 +1002,27 @@ InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags) { 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); } @@ -1271,9 +1289,18 @@ InMemoryZoneFinderTest::wildcardCheck( 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); + } } }