// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <functional>
+#include <algorithm>
#include <map>
-#include <set>
+#include <utility>
+#include <cctype>
#include <cassert>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
typedef RBTree<Domain> DomainTree;
typedef RBNode<Domain> DomainNode;
-// Separate storage for NSEC3 RRs (and their RRSIGs)
-struct NameCompare : public binary_function<ConstRRsetPtr, ConstRRsetPtr, bool>
-{
- bool operator()(const ConstRRsetPtr& n1, const ConstRRsetPtr& n2) const {
- return (n1->getName().compare(n2->getName()).getOrder() < 0);
- }
-};
-typedef set<ConstRRsetPtr, NameCompare> NSEC3Set;
+// Separate storage for NSEC3 RRs (and their RRSIGs). It's an STL map
+// from string to the NSEC3 RRset. The map key is the first label
+// (upper cased) of the owner name of the corresponding NSEC3 (i.e., map
+// value). We can use the standard string comparison (if the comparison
+// target is also upper cased) due to the nature of NSEC3 owner names.
+typedef map<string, ConstRRsetPtr> NSEC3Map;
+typedef NSEC3Map::value_type NSEC3Pair;
// Actual zone data: Essentially a set of zone's RRs. This is defined as
// a separate structure so that it'll be replaceable on reload.
DomainTree domains_;
// The optional NSEC3 storage (TBD: should allocate it on demand)
- NSEC3Set nsec3_set_;
+ NSEC3Map nsec3_map_;
};
}
// The actual zone data
scoped_ptr<ZoneData> zone_data_;
- //DomainTree domains_;
// Add the necessary magic for any wildcard contained in 'name'
// (including itself) to be found in the zone.
return (result::SUCCESS);
}
+ // A helper functor to convert the 1st NSEC3 label to all upper-cased
+ // characters. Note: technically there's a subtle issue when char
+ // is signed, but in practice the label should consist of all positive
+ // character values for a valid NSEC3 hash name (if it's invalid the
+ // resulting zone doesn't work correctly anyway).
+ struct ToUpper {
+ char operator()(char ch) { return (toupper(ch)); }
+ };
+
/*
* Implementation of longer methods. We put them here, because the
* access is without the impl_-> and it will get inlined anyway.
arg(rrset->getName()).arg(rrset->getType()).arg(origin_);
if (rrset->getType() == RRType::NSEC3()) {
- // TBD: Duplicate check
- zone_data.nsec3_set_.insert(rrset);
+ string fst_label = rrset->getName().split(0, 1).toText(true);
+ transform(fst_label.begin(), fst_label.end(), fst_label.begin(),
+ ToUpper());
+ zone_data.nsec3_map_.insert(NSEC3Pair(fst_label, rrset));
return (result::SUCCESS);
}
"data source");
}
+ZoneFinder::FindNSEC3Result
+InMemoryZoneFinder::findNSEC3Tmp(const Name& name, bool recursive) {
+ if (recursive) {
+ isc_throw(Unexpected, "recursive mode isn't expected in tests");
+ }
+
+ // A temporary workaround for testing: convert the original name to
+ // NSEC3-hashed name using hardcoded mapping.
+ string hname_text;
+ if (name == Name("example.org")) {
+ hname_text = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+ } else {
+ isc_throw(Unexpected, "unexpected name for NSEC3 test: " << name);
+ }
+
+ NSEC3Map::const_iterator found =
+ impl_->zone_data_->nsec3_map_.find(hname_text);
+ if (found != impl_->zone_data_->nsec3_map_.end()) {
+ return (FindNSEC3Result(true, 2, found->second, ConstRRsetPtr()));
+ }
+
+ isc_throw(Unexpected, "unexpected NSEC3 search result for " << name);
+}
+
result::Result
InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) {
return (impl_->add(rrset, *impl_->zone_data_));
InMemoryZoneFinder::AddError);
}
+//
+// (Faked) NSEC3 hash data. Arbitrarily borrowed from RFC515 examples.
+//
+// Commonly used NSEC3 suffix. It's incorrect to use it for all NSEC3s, but
+// doesn't matter for the purpose of our tests.
+const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
+ "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
+// For apex (example.org)
+const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+const char* const apex_hash_down = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
+
TEST_F(InMemoryZoneFinderTest, addNSEC3) {
- zone_finder_.add(textToRRset(
- "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. "
- "300 IN NSEC3 1 1 12 aabbccdd "
- "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"));
+ const string nsec3_text = string(apex_hash) + ".example.org." +
+ string(nsec3_common);
+ // This name shouldn't be found in the normal domain tree.
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
EXPECT_EQ(ZoneFinder::NXDOMAIN,
- zone_finder_.find(
- Name("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org"),
- RRType::NSEC3()).code);
+ zone_finder_.find(Name(string(apex_hash) + ".example.org"),
+ RRType::NSEC3()).code);
+ // Dedicated NSEC3 find should be able to find it.
+ ZoneFinder::FindNSEC3Result result =
+ zone_finder_.findNSEC3Tmp(Name("example.org"), false);
+ EXPECT_TRUE(result.matched);
+ ASSERT_TRUE(result.closest_proof);
+ actual_rrsets_.push_back(result.closest_proof);
+ rrsetsCheck(nsec3_text, actual_rrsets_.begin(), actual_rrsets_.end());
+}
+
+TEST_F(InMemoryZoneFinderTest, addNSEC3Lower) {
+ // Similar to the previous case, but NSEC3 owner name is lower-cased.
+ const string nsec3_text = string(apex_hash_down) + ".example.org." +
+ string(nsec3_common);
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
+ ZoneFinder::FindNSEC3Result result =
+ zone_finder_.findNSEC3Tmp(Name("example.org"), false);
+ EXPECT_TRUE(result.matched);
+ ASSERT_TRUE(result.closest_proof);
+ actual_rrsets_.push_back(result.closest_proof);
+ rrsetsCheck(nsec3_text, actual_rrsets_.begin(), actual_rrsets_.end());
+
+ // - duplicate
+ // - adding RRSIG for NSEC3
+ // - bogus NSEC3 owner name: redundant labels, wildcard
+ // - case where the main tree has NSEC3 name
+ // - parameter consistency
+ // - existence of NSEC3PARAM
+ // - parameter consistency with NSEC3PARAM
+ // - add NSEC3PARAM first/second
}
}