Commit 52456f2f authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[1574b] Merge branch 'trac1574' into trac1574b

parents d046d809 a8c8f392
......@@ -16,6 +16,13 @@ $NAMESPACE isc::datasrc
# \brief Messages for the data source library
% DATASRC_BAD_NSEC3_NAME NSEC3 record has a bad owner name '%1'
The software refuses to load NSEC3 records into a wildcard domain or
the owner name has two or more labels below the zone origin.
It isn't explicitly forbidden, but no sane zone wouldn have such names
for NSEC3. BIND 9 also refuses NSEC3 at wildcard, so this behavior is
compatible with BIND 9.
% DATASRC_CACHE_CREATE creating the hotspot cache
This is a debug message issued during startup when the hotspot cache
is created.
......@@ -143,6 +150,34 @@ were found to be different. This isn't allowed on the wire and is considered
an error, so we set it to the lowest value we found (but we don't modify the
database). The data in database should be checked and fixed.
% DATASRC_DATABASE_JOURNALREADER_END %1/%2 on %3 from %4 to %5
This is a debug message indicating that the program (successfully)
reaches the end of sequences of a zone's differences. The zone's name
and class, database name, and the start and end serials are shown in
the message.
% DATASRC_DATABASE_JOURNALREADER_NEXT %1/%2 in %3/%4 on %5
This is a debug message indicating that the program retrieves one
difference in difference sequences of a zone and successfully converts
it to an RRset. The zone's name and class, database name, and the
name and RR type of the retrieved diff are shown in the message.
% DATASRC_DATABASE_JOURNALREADER_START %1/%2 on %3 from %4 to %5
This is a debug message indicating that the program starts reading
a zone's difference sequences from a database-based data source. The
zone's name and class, database name, and the start and end serials
are shown in the message.
% DATASRC_DATABASE_JOURNALREADR_BADDATA failed to convert a diff to RRset in %1/%2 on %3 between %4 and %5: %6
This is an error message indicating that a zone's diff is broken and
the data source library failed to convert it to a valid RRset. The
most likely cause of this is that someone has manually modified the
zone's diff in the database and inserted invalid data as a result.
The zone's name and class, database name, and the start and end
serials, and an additional detail of the error are shown in the
message. The administrator should examine the diff in the database
to find any invalid data and fix it.
% DATASRC_DATABASE_NO_MATCH not match for %2/%3/%4 in %1
No match (not even a wildcard) was found in the named data source for the given
name/type/class in the data source.
......@@ -671,66 +706,3 @@ data source.
% DATASRC_UNEXPECTED_QUERY_STATE unexpected query state
This indicates a programming error. An internal task of unknown type was
generated.
% DATASRC_DATABASE_UPDATER_CREATED zone updater created for '%1/%2' on %3
Debug information. A zone updater object is created to make updates to
the shown zone on the shown backend database.
% DATASRC_DATABASE_UPDATER_DESTROYED zone updater destroyed for '%1/%2' on %3
Debug information. A zone updater object is destroyed, either successfully
or after failure of, making updates to the shown zone on the shown backend
database.
%DATASRC_DATABASE_UPDATER_ROLLBACK zone updates roll-backed for '%1/%2' on %3
A zone updater is being destroyed without committing the changes.
This would typically mean the update attempt was aborted due to some
error, but may also be a bug of the application that forgets committing
the changes. The intermediate changes made through the updater won't
be applied to the underlying database. The zone name, its class, and
the underlying database name are shown in the log message.
%DATASRC_DATABASE_UPDATER_ROLLBACKFAIL failed to roll back zone updates for '%1/%2' on %3: %4
A zone updater is being destroyed without committing the changes to
the database, and attempts to rollback incomplete updates, but it
unexpectedly fails. The higher level implementation does not expect
it to fail, so this means either a serious operational error in the
underlying data source (such as a system failure of a database) or
software bug in the underlying data source implementation. In either
case if this message is logged the administrator should carefully
examine the underlying data source to see what exactly happens and
whether the data is still valid. The zone name, its class, and the
underlying database name as well as the error message thrown from the
database module are shown in the log message.
% DATASRC_DATABASE_UPDATER_COMMIT updates committed for '%1/%2' on %3
Debug information. A set of updates to a zone has been successfully
committed to the corresponding database backend. The zone name,
its class and the database name are printed.
% DATASRC_DATABASE_JOURNALREADER_START %1/%2 on %3 from %4 to %5
This is a debug message indicating that the program starts reading
a zone's difference sequences from a database-based data source. The
zone's name and class, database name, and the start and end serials
are shown in the message.
% DATASRC_DATABASE_JOURNALREADER_NEXT %1/%2 in %3/%4 on %5
This is a debug message indicating that the program retrieves one
difference in difference sequences of a zone and successfully converts
it to an RRset. The zone's name and class, database name, and the
name and RR type of the retrieved diff are shown in the message.
% DATASRC_DATABASE_JOURNALREADER_END %1/%2 on %3 from %4 to %5
This is a debug message indicating that the program (successfully)
reaches the end of sequences of a zone's differences. The zone's name
and class, database name, and the start and end serials are shown in
the message.
% DATASRC_DATABASE_JOURNALREADR_BADDATA failed to convert a diff to RRset in %1/%2 on %3 between %4 and %5: %6
This is an error message indicating that a zone's diff is broken and
the data source library failed to convert it to a valid RRset. The
most likely cause of this is that someone has manually modified the
zone's diff in the database and inserted invalid data as a result.
The zone's name and class, database name, and the start and end
serials, and an additional detail of the error are shown in the
message. The administrator should examine the diff in the database
to find any invalid data and fix it.
......@@ -12,9 +12,13 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <algorithm>
#include <map>
#include <utility>
#include <cctype>
#include <cassert>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
......@@ -39,6 +43,7 @@ using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::data;
using boost::scoped_ptr;
namespace isc {
namespace datasrc {
......@@ -62,6 +67,30 @@ typedef boost::shared_ptr<Domain> DomainPtr;
// The tree stores domains
typedef RBTree<Domain> DomainTree;
typedef RBNode<Domain> DomainNode;
// 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.
struct ZoneData {
ZoneData() : domains_(true) {}
// The main data (name + RRsets)
DomainTree domains_;
// The optional NSEC3 related data
struct NSEC3Data {
NSEC3Map map_; // Actual NSEC3 RRs
// We should also have hash parameters here (maybe hold NSEC3Hash?)
};
scoped_ptr<NSEC3Data> nsec3_data_; // non NULL only when it's NSEC3 signed
};
}
// Private data and hidden methods of InMemoryZoneFinder
......@@ -69,10 +98,10 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
// Constructor
InMemoryZoneFinderImpl(const RRClass& zone_class, const Name& origin) :
zone_class_(zone_class), origin_(origin), origin_data_(NULL),
domains_(true)
zone_data_(new ZoneData)
{
// We create the node for origin (it needs to exist anyway in future)
domains_.insert(origin, &origin_data_);
zone_data_->domains_.insert(origin, &origin_data_);
DomainPtr origin_domain(new Domain);
origin_data_->setData(origin_domain);
}
......@@ -85,7 +114,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
string file_name_;
// The actual zone data
DomainTree domains_;
scoped_ptr<ZoneData> zone_data_;
// Add the necessary magic for any wildcard contained in 'name'
// (including itself) to be found in the zone.
......@@ -234,19 +263,34 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
rrset->getName());
}
}
}
result::Result addRRsig(const ConstRRsetPtr sig_rrset,
DomainTree& domains)
{
DomainNode* node = NULL;
if (domains.find(sig_rrset->getName(), &node) !=
DomainTree::EXACTMATCH || node == NULL || !node->getData()) {
isc_throw(AddError,
"RRSIG is being added, but no RR to be covered: "
<< sig_rrset->getName());
// Owner names of NSEC3 have special format as defined in RFC5155,
// and cannot be a wildcard name or must be one label longer than
// the zone origin. While the RFC doesn't prohibit other forms of
// names, no sane zone would have such names for NSEC3.
// BIND 9 also refuses NSEC3 at wildcard.
if (rrset->getType() == RRType::NSEC3() &&
(rrset->getName().isWildcard() ||
rrset->getName().getLabelCount() !=
origin_.getLabelCount() + 1)) {
LOG_ERROR(logger, DATASRC_BAD_NSEC3_NAME).
arg(rrset->getName());
isc_throw(AddError, "Invalid NSEC3 owner name (wildcard): " <<
rrset->getName());
}
}
// 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)); }
};
result::Result addRRsig(const ConstRRsetPtr sig_rrset, ZoneData& zone_data)
{
// Check consistency of the type covered.
// We know the RRset isn't empty, so the following check is safe.
RdataIteratorPtr rit = sig_rrset->getRdataIterator();
......@@ -262,16 +306,43 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
// Find the RRset to be covered; if not found, treat it as an error
// for now.
const Domain::const_iterator it = node->getData()->find(covered);
if (it == node->getData()->end()) {
isc_throw(AddError,
"RRSIG is being added, but no RR of covered type found: "
<< sig_rrset->toText());
ConstRRsetPtr covered_rrset;
if (covered != RRType::NSEC3()) {
DomainNode* node = NULL;
if (zone_data.domains_.find(sig_rrset->getName(), &node) !=
DomainTree::EXACTMATCH || node == NULL || !node->getData()) {
isc_throw(AddError,
"RRSIG is being added, but no RR to be covered: "
<< sig_rrset->getName());
}
const Domain::const_iterator it = node->getData()->find(covered);
if (it != node->getData()->end()) {
covered_rrset = it->second;
}
} else {
// In case of NSEC3 if something is found it must be NSEC3 RRset
// under the assumption of our current implementation.
if (zone_data.nsec3_data_) {
string fst_label = sig_rrset->getName().split(0, 1).
toText(true);
transform(fst_label.begin(), fst_label.end(),
fst_label.begin(), ToUpper());
NSEC3Map::const_iterator found =
zone_data.nsec3_data_->map_.find(fst_label);
if (found != zone_data.nsec3_data_->map_.end()) {
covered_rrset = found->second;
assert(covered_rrset->getType() == covered);
}
}
}
if (!covered_rrset) {
isc_throw(AddError, "RRSIG is being added, but no RR of "
"covered type found: " << sig_rrset->toText());
}
// The current implementation doesn't allow an existing RRSIG to be
// overridden (or updated with additional ones).
if ((it->second)->getRRsig()) {
if (covered_rrset->getRRsig()) {
isc_throw(AddError,
"RRSIG is being added to override an existing one: "
<< sig_rrset->toText());
......@@ -286,17 +357,36 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
// Note: there's a slight chance of getting an exception.
// As noted in add(), we give up strong exception guarantee in such
// cases.
boost::const_pointer_cast<RRset>(it->second)->addRRsig(sig_rrset);
boost::const_pointer_cast<RRset>(covered_rrset)->addRRsig(sig_rrset);
return (result::SUCCESS);
}
result::Result addNSEC3(const ConstRRsetPtr rrset, ZoneData& zone_data) {
if (!zone_data.nsec3_data_) {
zone_data.nsec3_data_.reset(new ZoneData::NSEC3Data);
}
string fst_label = rrset->getName().split(0, 1).toText(true);
transform(fst_label.begin(), fst_label.end(), fst_label.begin(),
ToUpper());
// Our current implementation doesn't allow an existing NSEC3 to be
// updated/overridden.
if (zone_data.nsec3_data_->map_.find(fst_label) !=
zone_data.nsec3_data_->map_.end()) {
return (result::EXIST);
}
zone_data.nsec3_data_->map_.insert(NSEC3Pair(fst_label, rrset));
return (result::SUCCESS);
}
/*
* Implementation of longer methods. We put them here, because the
* access is without the impl_-> and it will get inlined anyway.
*/
// Implementation of InMemoryZoneFinder::add
result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) {
result::Result add(const ConstRRsetPtr& rrset, ZoneData& zone_data) {
// Sanitize input. This will cause an exception to be thrown
// if the input RRset is empty.
addValidation(rrset);
......@@ -305,21 +395,26 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET).
arg(rrset->getName()).arg(rrset->getType()).arg(origin_);
if (rrset->getType() == RRType::NSEC3()) {
return (addNSEC3(rrset, zone_data));
}
// RRSIGs are special in various points, so we handle it in a
// separate dedicated method.
if (rrset->getType() == RRType::RRSIG()) {
return (addRRsig(rrset, *domains));
return (addRRsig(rrset, zone_data));
}
// Add wildcards possibly contained in the owner name to the domain
// tree.
// Note: this can throw an exception, breaking strong exception
// guarantee. (see also the note for contextCheck() below).
addWildcards(*domains, rrset->getName());
addWildcards(zone_data.domains_, rrset->getName());
// Get the node
DomainNode* node;
DomainTree::Result result = domains->insert(rrset->getName(), &node);
DomainTree::Result result = zone_data.domains_.insert(rrset->getName(),
&node);
// Just check it returns reasonable results
assert((result == DomainTree::SUCCESS ||
result == DomainTree::ALREADYEXISTS) && node!= NULL);
......@@ -366,18 +461,18 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
* Same as above, but it checks the return value and if it already exists,
* it throws.
*/
void addFromLoad(const ConstRRsetPtr& set, DomainTree* domains) {
switch (add(set, domains)) {
case result::EXIST:
LOG_ERROR(logger, DATASRC_MEM_DUP_RRSET).
arg(set->getName()).arg(set->getType());
isc_throw(dns::MasterLoadError, "Duplicate rrset: " <<
set->toText());
case result::SUCCESS:
return;
default:
assert(0);
}
void addFromLoad(const ConstRRsetPtr& set, ZoneData* zone_data) {
switch (add(set, *zone_data)) {
case result::EXIST:
LOG_ERROR(logger, DATASRC_MEM_DUP_RRSET).
arg(set->getName()).arg(set->getType());
isc_throw(dns::MasterLoadError, "Duplicate rrset: " <<
set->toText());
case result::SUCCESS:
return;
default:
assert(0);
}
}
// Maintain intermediate data specific to the search context used in
......@@ -499,7 +594,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
FindState state(options);
RBTreeNodeChain<Domain> node_path;
bool rename(false);
switch (domains_.find(name, &node, node_path, cutCallback, &state)) {
switch (zone_data_->domains_.find(name, &node, node_path, cutCallback,
&state)) {
case DomainTree::PARTIALMATCH:
/*
* In fact, we could use a single variable instead of
......@@ -584,7 +680,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
}
Name wildcard(Name("*").concatenate(
node_path.getAbsoluteName()));
DomainTree::Result result(domains_.find(wildcard, &node));
DomainTree::Result result =
zone_data_->domains_.find(wildcard, &node);
/*
* Otherwise, why would the DOMAINFLAG_WILD be there if
* there was no wildcard under it?
......@@ -717,9 +814,57 @@ InMemoryZoneFinder::findNSEC3(const Name&, bool) {
"data source");
}
ZoneFinder::FindNSEC3Result
InMemoryZoneFinder::findNSEC3Tmp(const Name& name, bool recursive) {
if (!impl_->zone_data_->nsec3_data_) {
isc_throw(Unexpected, "findNSEC3 is called for non NSEC3 zone");
}
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 if (name == Name("www.example.org")) {
hname_text = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
} else if (name == Name("xxx.example.org")) {
hname_text = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
} else if (name == Name("yyy.example.org")) {
hname_text = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
} else {
isc_throw(Unexpected, "unexpected name for NSEC3 test: " << name);
}
// Below we assume the map is not empty for simplicity.
NSEC3Map::const_iterator found =
impl_->zone_data_->nsec3_data_->map_.lower_bound(hname_text);
if (found != impl_->zone_data_->nsec3_data_->map_.end() &&
found->first == hname_text) {
// exact match
return (FindNSEC3Result(true, 2, found->second, ConstRRsetPtr()));
} else if (found == impl_->zone_data_->nsec3_data_->map_.end() ||
found == impl_->zone_data_->nsec3_data_->map_.begin()) {
// the search key is "smaller" than the smallest or "larger" than
// largest. In either case "previous" is the largest one.
return (FindNSEC3Result(false, 2,
impl_->zone_data_->nsec3_data_->map_.
rbegin()->second, ConstRRsetPtr()));
} else {
// Otherwise, H(found_domain-1) < given_hash < H(found_domain)
// The covering proof is the first one.
return (FindNSEC3Result(false, 2, (--found)->second, ConstRRsetPtr()));
}
// We should have covered all cases.
isc_throw(Unexpected, "Impossible NSEC3 search result for " << name);
}
result::Result
InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) {
return (impl_->add(rrset, &impl_->domains_));
return (impl_->add(rrset, *impl_->zone_data_));
}
......@@ -727,13 +872,14 @@ void
InMemoryZoneFinder::load(const string& filename) {
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()).
arg(filename);
// Load it into a temporary tree
DomainTree tmp;
// Load it into temporary zone data
scoped_ptr<ZoneData> tmp(new ZoneData);
masterLoad(filename.c_str(), getOrigin(), getClass(),
boost::bind(&InMemoryZoneFinderImpl::addFromLoad, impl_, _1, &tmp));
boost::bind(&InMemoryZoneFinderImpl::addFromLoad, impl_,
_1, tmp.get()));
// If it went well, put it inside
impl_->file_name_ = filename;
tmp.swap(impl_->domains_);
tmp.swap(impl_->zone_data_);
// And let the old data die with tmp
}
......@@ -924,8 +1070,9 @@ InMemoryClient::getIterator(const Name& name, bool separate_rrs) const {
isc_throw(Unexpected, "The zone at " + name.toText() +
" is not InMemoryZoneFinder");
}
return (ZoneIteratorPtr(new MemoryIterator(zone->impl_->domains_, name,
separate_rrs)));
return (ZoneIteratorPtr(new MemoryIterator(
zone->impl_->zone_data_->domains_, name,
separate_rrs)));
}
ZoneUpdaterPtr
......
......@@ -89,6 +89,16 @@ public:
virtual FindNSEC3Result
findNSEC3(const isc::dns::Name& name, bool recursive);
// A temporary fake version of findNSEC3 for tests
//
// This method intentionally has the same interface as findNSEC3 but
// uses internally hardcoded hash values and offers a limited set
// of functionality for the convenience of tests. This is a temporary
// workaround until #1577 is completed. At that point this method
// should be removed.
FindNSEC3Result
findNSEC3Tmp(const isc::dns::Name& name, bool recursive);
/// \brief Imelementation of the ZoneFinder::findPreviousName method
///
/// This one throws NotImplemented exception, as InMemory doesn't
......
......@@ -1307,4 +1307,180 @@ TEST_F(InMemoryZoneFinderTest, addbadRRsig) {
EXPECT_THROW(zone_finder_.add(textToRRset(rrsig_a_txt)),
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";
// Likewise, common RRSIG suffix for NSEC3s.
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,
bool expected_sig = false)
{
vector<ConstRRsetPtr> actual_rrsets;
EXPECT_EQ(expected_matched, result.matched);
ASSERT_TRUE(result.closest_proof);
if (expected_sig) {
ASSERT_TRUE(result.closest_proof->getRRsig());
}
actual_rrsets.push_back(result.closest_proof);
if (expected_sig) {
actual_rrsets.push_back(result.closest_proof->getRRsig());
}
rrsetsCheck(expected_rrsets_txt, actual_rrsets.begin(),
actual_rrsets.end());
}
// In the following tests we use a temporary faked version of findNSEC3
// as the real version isn't implemented yet (it's a task for #1577).
// When #1577 is done the tests should be updated using the real version.
// If we can use fake hash calculator (see #1575), we should be able to
// just replace findNSEC3Tmp with findNSEC3.
TEST_F(InMemoryZoneFinderTest, addNSEC3) {
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(string(apex_hash) + ".example.org"),
RRType::NSEC3()).code);
// Dedicated NSEC3 find should be able to find it.
nsec3Check(true, nsec3_text,
zone_finder_.findNSEC3Tmp(Name("example.org"), false));
// This implementation rejects duplicate/update add of the same hash name
EXPECT_EQ(result::EXIST,
zone_finder_.add(textToRRset(
string(apex_hash) + ".example.org." +
string(nsec3_common) + " AAAA")));
// The original NSEC3 should be intact
nsec3Check(true, nsec3_text,
zone_finder_.findNSEC3Tmp(Name("example.org"), false));
// NSEC3-like name but of ordinary RR type should go to normal tree.
const string nonsec3_text = string(apex_hash) + ".example.org. " +
"300 IN A 192.0.2.1";
EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nonsec3_text)));
EXPECT_EQ(ZoneFinder::SUCCESS,
zone_finder_.find(Name(string(apex_hash) + ".example.org"),
RRType::A()).code);
}
TEST_F(InMemoryZoneFinderTest, addNSEC3Lower) {
// Similar to the previous case, but NSEC3 owner name is lower-cased.
const string nsec3_text = string(apex_hash_lower) + ".example.org." +
string(nsec3_common);
EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
nsec3Check(true, nsec3_text,
zone_finder_.findNSEC3Tmp(Name("example.org"), false));
}
TEST_F(InMemoryZoneFinderTest, addNSEC3Ordering) {
// Check that the internal storage ensures comparison based on the NSEC3
// semantics, regardless of the add order or the letter-case of hash.
// Adding "0P..", "2v..", then "2T..".
const string smallest = string(apex_hash) + ".example.org." +
string(nsec3_common);
const string middle = string(ns1_hash) + ".example.org." +
string(nsec3_common);
const string largest = string(xrw_hash) + ".example.org." +
string(nsec3_common);
zone_finder_.add(textToRRset(smallest));
zone_finder_.add(textToRRset(largest));
zone_finder_.add(textToRRset(middle));