From: JINMEI Tatuya Date: Wed, 8 Feb 2012 01:55:56 +0000 (-0800) Subject: [1576] implemented basic part of in-memory version of findNSEC3(). X-Git-Tag: trac2351_base~97^2~35^2~9 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=46232cfc39be398a04cfbc4e6fe2e75b41d8eb7c;p=thirdparty%2Fkea.git [1576] implemented basic part of in-memory version of findNSEC3(). --- diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 450c8b6c14..be513dc7d6 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -881,9 +881,48 @@ InMemoryZoneFinder::findAll(const Name& name, } ZoneFinder::FindNSEC3Result -InMemoryZoneFinder::findNSEC3(const Name&, bool) { - isc_throw(NotImplemented, "findNSEC3 is not yet implemented for in memory " - "data source"); +InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) { + // TODO validation (no NSEC3 case, out of zone name) + + const NSEC3Hash& nsec3hash = *impl_->zone_data_->nsec3_data_->hash_; + const NSEC3Map& map = impl_->zone_data_->nsec3_data_->map_; + + ConstRRsetPtr covering_proof; // placeholder of the next closer proof + const unsigned int olabels = impl_->origin_.getLabelCount(); + const unsigned int qlabels = name.getLabelCount(); + for (unsigned int labels = qlabels; labels >= olabels; --labels) { + const string hlabel = nsec3hash.calculate( + labels == qlabels ? name : name.split(qlabels - labels, labels)); + + NSEC3Map::const_iterator found = map.lower_bound(hlabel); + + // If the given hash is larger than the largest stored hash or + // the first label doesn't match the target, identify the "previous" + // hash value and remember it as the candidate next closer proof. + if (found == map.end() || found->first != hlabel) { + // If the given hash is larger (or smaller than everything, TBD) + // the covering proof is the NSEC3 that has the largest hash. + // Note that we know the map isn't empty (TBD), so rbegin() is + // safe. + if (found == map.end() || found == map.begin()) { + covering_proof = map.rbegin()->second; + } else { + // Otherwise, H(found_entry-1) < given_hash < H(found_entry). + // The covering proof is the first one (and it's valid + // because found is neither begin nor end) + covering_proof = (--found)->second; + } + if (!recursive) { // in non recursive mode, we are done. + return (FindNSEC3Result(false, labels, covering_proof, + ConstRRsetPtr())); + } + } else { // found an exact match. + return (FindNSEC3Result(true, labels, found->second, + covering_proof)); + } + } + + isc_throw(Unexpected, "uncovered test case"); } ZoneFinder::FindNSEC3Result diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index 944ebbedf0..fda5f89cef 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -275,6 +276,83 @@ setRRset(RRsetPtr rrset, vector::iterator& it) { ++it; } +ConstRRsetPtr +textToRRset(const string& text_rrset, const RRClass& rrclass = RRClass::IN()) { + stringstream ss(text_rrset); + RRsetPtr rrset; + vector rrsets; + rrsets.push_back(&rrset); + masterLoad(ss, Name::ROOT_NAME(), rrclass, + boost::bind(setRRset, _1, rrsets.begin())); + return (rrset); +} + +// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash +// object. +// +// For apex (example.org) +const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"; +const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom"; +// For ns1.example.org +const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR"; +// For w.example.org +const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H"; +// For x.y.w.example.org (lower-cased) +const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s"; +// For zzz.example.org. +const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN"; + +// A simple faked NSEC3 hash calculator with a dedicated creator for it. +// +// This is used in some NSEC3-related tests below. +class TestNSEC3HashCreator : public NSEC3HashCreator { + class TestNSEC3Hash : public NSEC3Hash { + private: + typedef map NSEC3HashMap; + typedef NSEC3HashMap::value_type NSEC3HashPair; + NSEC3HashMap map; + public: + TestNSEC3Hash() { + // Build pre-defined hash + map[Name("example.org")] = apex_hash; + map[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"; + map[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"; + map[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"; + map[Name("x.y.w.example.org")] = + "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S"; + map[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H"; + map[Name("w.example.org")] = w_hash; + map[Name("zzz.example.org")] = zzz_hash; + map[Name("smallest.example.org")] = + "00000000000000000000000000000000"; + map[Name("largest.example.org")] = + "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"; + } + virtual string calculate(const Name& name) const { + const NSEC3HashMap::const_iterator found = map.find(name); + if (found != map.end()) { + return (found->second); + } + isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: " + << name); + } + virtual bool match(const generic::NSEC3PARAM&) const { + return (true); + } + virtual bool match(const generic::NSEC3&) const { + return (true); + } + }; + +public: + virtual NSEC3Hash* create(const generic::NSEC3PARAM&) const { + return (new TestNSEC3Hash); + } + virtual NSEC3Hash* create(const generic::NSEC3&) const { + return (new TestNSEC3Hash); + } +}; + /// \brief Test fixture for the InMemoryZoneFinder class class InMemoryZoneFinderTest : public ::testing::Test { // A straightforward pair of textual RR(set) and a RRsetPtr variable @@ -368,6 +446,12 @@ public: masterLoad(zone_data_stream, Name::ROOT_NAME(), class_, boost::bind(setRRset, _1, rrsets.begin())); } + + virtual ~InMemoryZoneFinderTest() { + // Make sure we reset the hash creator to the default + setNSEC3HashCreator(NULL); + } + // Some data to test with const RRClass class_; const Name origin_; @@ -414,6 +498,12 @@ public: RRsetPtr rr_not_wild_another_; RRsetPtr rr_nsec3_; + // A faked NSEC3 hash calculator for convenience. + // Tests that need to use the faked hashed values should call + // setNSEC3HashCreator() with a pointer to this variable at the beginning + // of the test (at least before adding any NSEC3/NSEC3PARAM RR). + TestNSEC3HashCreator nsec3_hash_creator_; + /** * \brief Test one find query to the zone finder. * @@ -522,18 +612,6 @@ public: rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(), target.begin(), target.end()); } - - ConstRRsetPtr textToRRset(const string& text_rrset, - const RRClass& rrclass = RRClass::IN()) const - { - stringstream ss(text_rrset); - RRsetPtr rrset; - vector rrsets; - rrsets.push_back(&rrset); - masterLoad(ss, Name::ROOT_NAME(), rrclass, - boost::bind(setRRset, _1, rrsets.begin())); - return (rrset); - } }; /** @@ -893,7 +971,7 @@ TEST_F(InMemoryZoneFinderTest, find) { findCheck(); } -TEST_F(InMemoryZoneFinderTest, findNSEC3) { +TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) { findCheck(ZoneFinder::RESULT_NSEC3_SIGNED); } @@ -1516,14 +1594,6 @@ const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd " const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 " "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"; -// For apex (example.org) -const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"; -const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom"; -// For ns1.example.org -const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR"; -// For x.y.w.example.org (lower-cased) -const char* const xrw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s"; - void nsec3Check(bool expected_matched, const string& expected_rrsets_txt, const ZoneFinder::FindNSEC3Result& result, @@ -1597,7 +1667,7 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3Ordering) { string(nsec3_common); const string middle = string(ns1_hash) + ".example.org." + string(nsec3_common); - const string largest = string(xrw_hash) + ".example.org." + + const string largest = string(xyw_hash) + ".example.org." + string(nsec3_common); zone_finder_.add(textToRRset(smallest)); zone_finder_.add(textToRRset(largest)); @@ -1749,4 +1819,109 @@ TEST_F(InMemoryZoneFinderTest, loadNSEC3Zone) { // This is an abnormal case, but the implementation accepts it. zone_finder_.load(TEST_DATA_DIR "/example.org.nsec3-signed-noparam"); } + +void +findNSEC3Check(bool expected_matched, uint8_t expected_labels, + const string& expected_closest, + const string& expected_next, + const ZoneFinder::FindNSEC3Result& result) +{ + EXPECT_EQ(expected_matched, result.matched); + // Convert to int so the error messages would be more readable: + EXPECT_EQ(static_cast(expected_labels), + static_cast(result.closest_labels)); + ASSERT_TRUE(result.closest_proof); + rrsetCheck(textToRRset(expected_closest), result.closest_proof); + if (expected_next.empty()) { + EXPECT_FALSE(result.next_proof); + } else { + ASSERT_TRUE(result.next_proof); + rrsetCheck(textToRRset(expected_next), result.next_proof); + } +} + +TEST_F(InMemoryZoneFinderTest, findNSEC3) { + // Set up the faked hash calculator. + setNSEC3HashCreator(&nsec3_hash_creator_); + + // Add a few NSEC3 records: + // apex (example.org.): hash=0P.. + // ns1.example.org: hash=2T.. + // w.example.org: hash=01.. + // zzz.example.org: hash=R5.. + const string apex_nsec3_text = string(apex_hash) + ".example.org." + + string(nsec3_common); + EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(apex_nsec3_text))); + const string ns1_nsec3_text = string(ns1_hash) + ".example.org." + + string(nsec3_common); + EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(ns1_nsec3_text))); + const string w_nsec3_text = string(w_hash) + ".example.org." + + string(nsec3_common); + EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(w_nsec3_text))); + const string zzz_nsec3_text = string(zzz_hash) + ".example.org." + + string(nsec3_common); + EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(zzz_nsec3_text))); + + // Apex name. It should have a matching NSEC3. + { + SCOPED_TRACE("apex, non recursive mode"); + findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text, "", + zone_finder_.findNSEC3(origin_, false)); + } + + // Recursive mode doesn't change the result in this case. + { + SCOPED_TRACE("apex, recursive mode"); + findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text, "", + zone_finder_.findNSEC3(origin_, true)); + } + + // Non existent name. Disabling recursion, a covering NSEC3 should be + // returned. + const Name www_name("www.example.org"); + { + SCOPED_TRACE("non existent name, non recursive mode"); + findNSEC3Check(false, www_name.getLabelCount(), apex_nsec3_text, "", + zone_finder_.findNSEC3(www_name, false)); + } + + // Non existent name. The closest provable encloser is the apex, + // and next closer is the query name itself (which NSEC3 for ns1 + // covers) + // H(ns1) = 2T... < H(xxx) = Q0... < H(zzz) = R5... + { + SCOPED_TRACE("non existent name, recursive mode"); + findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text, + ns1_nsec3_text, + zone_finder_.findNSEC3(Name("xxx.example.org"), true)); + } + + // Similar to the previous case, but next closer name is different + // from the query name. The closet encloser is w.example.org, and + // next closer is y.w.example.org. + // H(ns1) = 2T.. < H(y.w) = K8.. < H(zzz) = R5 + { + SCOPED_TRACE("non existent name, non qname next closer"); + findNSEC3Check(true, Name("w.example.org").getLabelCount(), + w_nsec3_text, ns1_nsec3_text, + zone_finder_.findNSEC3(Name("x.y.w.example.org"), + true)); + } + + // In the rest of test we check hash comparison for wrap around cases. + { + SCOPED_TRACE("very small hash"); + const Name smallest_name("smallest.example.org"); + findNSEC3Check(false, smallest_name.getLabelCount(), + zzz_nsec3_text, "", + zone_finder_.findNSEC3(smallest_name, false)); + } + { + SCOPED_TRACE("very large hash"); + const Name largest_name("largest.example.org"); + findNSEC3Check(false, largest_name.getLabelCount(), + zzz_nsec3_text, "", + zone_finder_.findNSEC3(largest_name, false)); + } +} }