BIND 10 master, updated. f93cd951e3ead684e4efbc8d2eaa523141e0cf65 [master] Merge branch 'trac1781'
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed Apr 18 07:56:33 UTC 2012
The branch, master has been updated
via f93cd951e3ead684e4efbc8d2eaa523141e0cf65 (commit)
via a318061e3c20ad5deeebe41884cbe3dcf9a3a413 (commit)
via 4a7381b8b8a03c010372c7a62192bec325a40a8a (commit)
via 211934fe44eb6ea5a74857d4312e10b6cd6a3c55 (commit)
via 136e63b8234f46a46c4278e4ccfe9712a76ebfc9 (commit)
via d78d0dbcec53a339b7789b181954768d181c2a1b (commit)
via be9562a0d38bdb2ba5d3e8456dd13a0cbc19b48e (commit)
via 5149a8149791f6f828704a2aa63178ca98707ebc (commit)
via 78c6b5331109e94c7c5217c500162106b587ae59 (commit)
via ffbfe8fb2978b60c8309b3d7b7d006c98778b304 (commit)
via fa7b9c53b18d220062ed252abbc241e8871e8927 (commit)
via 61fa7a41089bf18f39ae6415fa92c6c2bca6a026 (commit)
via 0bc5c68ef6c5811eb93227e1d8d44f7762a3f7b2 (commit)
via 6d7a20689e5ac2dd8139c5d6cece17b5d1a4f178 (commit)
from c3be9fa408b02750d56f63ba593b9a0c92647928 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit f93cd951e3ead684e4efbc8d2eaa523141e0cf65
Merge: c3be9fa a318061
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed Apr 18 00:50:24 2012 -0700
[master] Merge branch 'trac1781'
-----------------------------------------------------------------------
Summary of changes:
src/lib/datasrc/database.cc | 148 ++++++++++---
src/lib/datasrc/database.h | 88 +++++++
src/lib/datasrc/sqlite3_accessor.cc | 14 ++
src/lib/datasrc/sqlite3_accessor.h | 6 +
src/lib/datasrc/tests/database_unittest.cc | 263 +++++++++++++++++++---
src/lib/datasrc/tests/memory_datasrc_unittest.cc | 76 ++++++-
src/lib/testutils/dnsmessage_test.cc | 26 +++
src/lib/testutils/dnsmessage_test.h | 23 ++
8 files changed, 568 insertions(+), 76 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index fcb2971..7b271f1 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -1415,49 +1415,132 @@ DatabaseUpdater::validateAddOrDelete(const char* const op_str,
}
}
+// This is a helper class used in adding/deleting RRsets to/from a database.
+// The purpose of this class is to provide conversion interface from various
+// parameters of the RRset to corresponding textual representations that the
+// underlying database interface expects. The necessary parameters and how
+// to convert them depend on several things, such as whether it's NSEC3 related
+// or not, or whether journaling is requested. In order to avoid unnecessary
+// conversion, this class also performs the conversion in a lazy manner.
+// Also, in order to avoid redundant conversion when the conversion is
+// requested for the same parameter multiple times, it remembers the
+// conversion result first time, and reuses it for subsequent requests
+// (this implicitly assumes copying std::string objects is not very expensive;
+// this is often the case in some common implementations that have
+// copy-on-write semantics for the string class).
+class RRParameterConverter {
+public:
+ RRParameterConverter(const AbstractRRset& rrset) : rrset_(rrset)
+ {}
+ const string& getName() {
+ if (name_.empty()) {
+ name_ = rrset_.getName().toText();
+ }
+ return (name_);
+ }
+ const string& getNSEC3Name() {
+ if (nsec3_name_.empty()) {
+ nsec3_name_ = rrset_.getName().split(0, 1).toText(true);
+ }
+ return (nsec3_name_);
+ }
+ const string& getRevName() {
+ if (revname_.empty()) {
+ revname_ = rrset_.getName().reverse().toText();
+ }
+ return (revname_);
+ }
+ const string& getTTL() {
+ if (ttl_.empty()) {
+ ttl_ = rrset_.getTTL().toText();
+ }
+ return (ttl_);
+ }
+ const string& getType() {
+ if (type_.empty()) {
+ type_ = rrset_.getType().toText();
+ }
+ return (type_);
+ }
+
+private:
+ string name_;
+ string nsec3_name_;
+ string revname_;
+ string ttl_;
+ string type_;
+ const AbstractRRset& rrset_;
+};
+
+namespace {
+// A shared shortcut to detect if the given type of RDATA is NSEC3 or
+// RRSIG covering NSEC3. RRSIG for NSEC3 should go to the (conceptual)
+// separate namespace, so we need to check the covered type.
+// Note: in principle the type covered should be the same for
+// all RDATA, but the RRset interface doesn't ensure that condition.
+// So we explicitly check that for every RDATA below.
+bool
+isNSEC3KindType(RRType rrtype, const Rdata& rdata) {
+ if (rrtype == RRType::NSEC3()) {
+ return (true);
+ }
+ if (rrtype == RRType::RRSIG() &&
+ dynamic_cast<const generic::RRSIG&>(rdata).typeCovered() ==
+ RRType::NSEC3())
+ {
+ return (true);
+ }
+ return (false);
+}
+}
+
void
DatabaseUpdater::addRRset(const AbstractRRset& rrset) {
validateAddOrDelete("add", rrset, DELETE, ADD);
// It's guaranteed rrset has at least one RDATA at this point.
RdataIteratorPtr it = rrset.getRdataIterator();
-
- string columns[Accessor::ADD_COLUMN_COUNT]; // initialized with ""
- columns[Accessor::ADD_NAME] = rrset.getName().toText();
- columns[Accessor::ADD_REV_NAME] = rrset.getName().reverse().toText();
- columns[Accessor::ADD_TTL] = rrset.getTTL().toText();
- columns[Accessor::ADD_TYPE] = rrset.getType().toText();
- string journal[Accessor::DIFF_PARAM_COUNT];
if (journaling_) {
- journal[Accessor::DIFF_NAME] = columns[Accessor::ADD_NAME];
- journal[Accessor::DIFF_TYPE] = columns[Accessor::ADD_TYPE];
- journal[Accessor::DIFF_TTL] = columns[Accessor::ADD_TTL];
diff_phase_ = ADD;
if (rrset.getType() == RRType::SOA()) {
- serial_ =
- dynamic_cast<const generic::SOA&>(it->getCurrent()).
+ serial_ = dynamic_cast<const generic::SOA&>(it->getCurrent()).
getSerial();
}
}
+
+ RRParameterConverter cvtr(rrset);
for (; !it->isLast(); it->next()) {
+ const Rdata& rdata = it->getCurrent();
+ const bool nsec3_type = isNSEC3KindType(rrset.getType(), rdata);
+
+ string sigtype;
if (rrset.getType() == RRType::RRSIG()) {
// XXX: the current interface (based on the current sqlite3
// data source schema) requires a separate "sigtype" column,
// even though it won't be used in a newer implementation.
// We should eventually clean up the schema design and simplify
// the interface, but until then we have to conform to the schema.
- const generic::RRSIG& rrsig_rdata =
- dynamic_cast<const generic::RRSIG&>(it->getCurrent());
- columns[Accessor::ADD_SIGTYPE] =
- rrsig_rdata.typeCovered().toText();
+ sigtype = dynamic_cast<const generic::RRSIG&>(rdata).
+ typeCovered().toText();
}
- columns[Accessor::ADD_RDATA] = it->getCurrent().toText();
+ const string& rdata_txt = rdata.toText();
if (journaling_) {
- journal[Accessor::DIFF_RDATA] = columns[Accessor::ADD_RDATA];
+ const string journal[Accessor::DIFF_PARAM_COUNT] =
+ { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata_txt };
accessor_->addRecordDiff(zone_id_, serial_.getValue(),
Accessor::DIFF_ADD, journal);
}
- accessor_->addRecordToZone(columns);
+ if (nsec3_type) {
+ const string nsec3_columns[Accessor::ADD_NSEC3_COLUMN_COUNT] =
+ { cvtr.getNSEC3Name(), cvtr.getTTL(), cvtr.getType(),
+ rdata_txt };
+ accessor_->addNSEC3RecordToZone(nsec3_columns);
+ } else {
+ const string columns[Accessor::ADD_COLUMN_COUNT] =
+ { cvtr.getName(), cvtr.getRevName(), cvtr.getTTL(),
+ cvtr.getType(), sigtype, rdata_txt };
+ accessor_->addRecordToZone(columns);
+ }
}
}
@@ -1472,15 +1555,7 @@ DatabaseUpdater::deleteRRset(const AbstractRRset& rrset) {
validateAddOrDelete("delete", rrset, ADD, DELETE);
RdataIteratorPtr it = rrset.getRdataIterator();
-
- string params[Accessor::DEL_PARAM_COUNT]; // initialized with ""
- params[Accessor::DEL_NAME] = rrset.getName().toText();
- params[Accessor::DEL_TYPE] = rrset.getType().toText();
- string journal[Accessor::DIFF_PARAM_COUNT];
if (journaling_) {
- journal[Accessor::DIFF_NAME] = params[Accessor::DEL_NAME];
- journal[Accessor::DIFF_TYPE] = params[Accessor::DEL_TYPE];
- journal[Accessor::DIFF_TTL] = rrset.getTTL().toText();
diff_phase_ = DELETE;
if (rrset.getType() == RRType::SOA()) {
serial_ =
@@ -1488,14 +1563,27 @@ DatabaseUpdater::deleteRRset(const AbstractRRset& rrset) {
getSerial();
}
}
+
+ RRParameterConverter cvtr(rrset);
for (; !it->isLast(); it->next()) {
- params[Accessor::DEL_RDATA] = it->getCurrent().toText();
+ const Rdata& rdata = it->getCurrent();
+ const bool nsec3_type = isNSEC3KindType(rrset.getType(), rdata);
+ const string& rdata_txt = it->getCurrent().toText();
+
if (journaling_) {
- journal[Accessor::DIFF_RDATA] = params[Accessor::DEL_RDATA];
+ const string journal[Accessor::DIFF_PARAM_COUNT] =
+ { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata_txt };
accessor_->addRecordDiff(zone_id_, serial_.getValue(),
Accessor::DIFF_DELETE, journal);
}
- accessor_->deleteRecordInZone(params);
+ const string params[Accessor::DEL_PARAM_COUNT] =
+ { nsec3_type ? cvtr.getNSEC3Name() : cvtr.getName(),
+ cvtr.getType(), rdata_txt };
+ if (nsec3_type) {
+ accessor_->deleteNSEC3RecordInZone(params);
+ } else {
+ accessor_->deleteRecordInZone(params);
+ }
}
}
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 40134fc..8083322 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -95,13 +95,36 @@ public:
ADD_COLUMN_COUNT = 6 ///< Number of columns
};
+ /// \brief Definitions of the fields to be passed to addNSEC3RecordToZone()
+ ///
+ /// Each derived implementation of addNSEC3RecordToZone() should expect
+ /// the "columns" array to be filled with the values as described in this
+ /// enumeration, in this order.
+ ///
+ /// Note that there is no "reversed name" column. Since the conceptual
+ /// separate namespace for NSEC3 is very simplified and essentially only
+ /// consists of a single-label names, there is no need for using reversed
+ /// names to identify the "previous hash".
+ enum AddNSEC3RecordColumns {
+ ADD_NSEC3_HASH = 0, ///< The hash (1st) label of the owner name,
+ ///< excluding the dot character
+ ADD_NSEC3_TTL = 1, ///< The TTL of the record (in numeric form)
+ ADD_NSEC3_TYPE = 2, ///< The RRType of the record (either NSEC3 or
+ ///< RRSIG for NSEC3)
+ ADD_NSEC3_RDATA = 3, ///< Full text representation of the record's
+ ///< RDATA
+ ADD_NSEC3_COLUMN_COUNT = 4 ///< Number of columns
+ };
+
/// \brief Definitions of the fields to be passed to deleteRecordInZone()
+ /// and deleteNSEC3RecordInZone()
///
/// Each derived implementation of deleteRecordInZone() should expect
/// the "params" array to be filled with the values as described in this
/// enumeration, in this order.
enum DeleteRecordParams {
DEL_NAME = 0, ///< The owner name of the record (a domain name)
+ ///< or the hash label for deleteNSEC3RecordInZone()
DEL_TYPE = 1, ///< The RRType of the record (A/NS/TXT etc.)
DEL_RDATA = 2, ///< Full text representation of the record's RDATA
DEL_PARAM_COUNT = 3 ///< Number of parameters
@@ -432,6 +455,46 @@ public:
virtual void addRecordToZone(
const std::string (&columns)[ADD_COLUMN_COUNT]) = 0;
+ /// \brief Add a single NSEC3-related record to the zone to be updated.
+ ///
+ /// This method is similar to \c addRecordToZone(), but is expected to
+ /// be only used for NSEC3 RRs or RRSIG RRs that cover NSEC3. In terms
+ /// of the DNS protocol, these types of RRs reside in a separate space
+ /// of the zone. While this interface does not mandate a specific way
+ /// of implementing the separate namespaces in the underlying database,
+ /// it would be more convenient for the underlying implementation if the
+ /// interfaces are separated; for example, the implementation does not
+ /// have to examine the given data to identify the appropriate namespace.
+ ///
+ /// An implementation may choose to skip providing this interface if the
+ /// zones managed by that data source are known to not support NSEC3.
+ /// In that case the implementation should throw the
+ /// \c isc::NotImplemented exception.
+ ///
+ /// Note that the \c ADD_NSEC3_HASH column of \c columns is expected to
+ /// store only the hash label, not the entire owner name. This is similar
+ /// to the \c hash parameter of \c getNSEC3Records().
+ ///
+ /// The RRs to be added using this method are expected to be limited to
+ /// NSEC3 or RRSIG RRs that cover NSEC3, but it's generally assumed to
+ /// be the caller's responsibility to ensure that; the implementation
+ /// is not required to check that condition. The result of adding
+ /// unexpected type of RRs (and the result of subsequent lookups) is
+ /// undefined.
+ ///
+ /// Other general notes for \c addRecordToZone() also apply to this
+ /// method.
+ ///
+ /// \exception DataSourceError Invalid call without starting a transaction,
+ /// or other internal database error.
+ /// \exception isc::NotImplemented in case the database does not support
+ /// NSEC3
+ ///
+ /// \param columns An array of strings that defines a record to be added
+ /// to the NSEC3 namespace of the zone.
+ virtual void addNSEC3RecordToZone(
+ const std::string (&columns)[ADD_NSEC3_COLUMN_COUNT]) = 0;
+
/// \brief Delete a single record from the zone to be updated.
///
/// This method provides a simple interface to delete a record
@@ -469,6 +532,31 @@ public:
virtual void deleteRecordInZone(
const std::string (¶ms)[DEL_PARAM_COUNT]) = 0;
+ /// \brief Delete a single NSEC3-related record from the zone to be
+ /// updated.
+ ///
+ /// This method is similar to \c deleteRecordInZone(), but is expected to
+ /// be only used for NSEC3 RRs or RRSIG RRs that cover NSEC3. The
+ /// relationship between these two methods is similar to that between
+ /// \c addRecordToZone() and \c addNSEC3RecordToZone(), and the same
+ /// notes apply to this method.
+ ///
+ /// This method uses the same set of parameters to specify the record
+ /// to be deleted as \c deleteRecordInZone(), but the \c DEL_NAME column
+ /// is expected to only store the hash label of the owner name.
+ /// This is the same as \c ADD_NSEC3_HASH column for
+ /// \c addNSEC3RecordToZone().
+ ///
+ /// \exception DataSourceError Invalid call without starting a transaction,
+ /// or other internal database error.
+ /// \exception isc::NotImplemented in case the database does not support
+ /// NSEC3
+ ///
+ /// \param params An array of strings that defines a record to be deleted
+ /// from the NSEC3 namespace of the zone.
+ virtual void deleteNSEC3RecordInZone(
+ const std::string (¶ms)[DEL_PARAM_COUNT]) = 0;
+
/// \brief Start a general transaction.
///
/// Each derived class version of this method starts a database
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 0214762..cbc3011 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -1133,6 +1133,13 @@ SQLite3Accessor::addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
}
void
+SQLite3Accessor::addNSEC3RecordToZone(
+ const string (&/*columns*/)[ADD_NSEC3_COLUMN_COUNT])
+{
+ isc_throw(NotImplemented, "not yet implemented");
+}
+
+void
SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
if (!dbparameters_->updating_zone) {
isc_throw(DataSourceError, "deleting record in SQLite3 "
@@ -1143,6 +1150,13 @@ SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
}
void
+SQLite3Accessor::deleteNSEC3RecordInZone(
+ const string (&/*params*/)[DEL_PARAM_COUNT])
+{
+ isc_throw(NotImplemented, "not yet implemented");
+}
+
+void
SQLite3Accessor::addRecordDiff(int zone_id, uint32_t serial,
DiffOperation operation,
const std::string (¶ms)[DIFF_PARAM_COUNT])
diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h
index 9f3b60e..3e44d5b 100644
--- a/src/lib/datasrc/sqlite3_accessor.h
+++ b/src/lib/datasrc/sqlite3_accessor.h
@@ -214,9 +214,15 @@ public:
virtual void addRecordToZone(
const std::string (&columns)[ADD_COLUMN_COUNT]);
+ virtual void addNSEC3RecordToZone(
+ const std::string (&columns)[ADD_NSEC3_COLUMN_COUNT]);
+
virtual void deleteRecordInZone(
const std::string (¶ms)[DEL_PARAM_COUNT]);
+ virtual void deleteNSEC3RecordInZone(
+ const std::string (¶ms)[DEL_PARAM_COUNT]);
+
/// This derived version of the method prepares an SQLite3 statement
/// for adding the diff first time it's called, and if it fails throws
// an \c SQLite3Error exception.
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index c18cfad..5b3a5dc 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -14,15 +14,9 @@
#include "faked_nsec3.h"
-#include <stdlib.h>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/lexical_cast.hpp>
-
-#include <gtest/gtest.h>
-
#include <exceptions/exceptions.h>
+#include <dns/masterload.h>
#include <dns/name.h>
#include <dns/rrttl.h>
#include <dns/rrset.h>
@@ -37,6 +31,13 @@
#include <testutils/dnsmessage_test.h>
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <cstdlib>
#include <map>
#include <vector>
@@ -47,6 +48,7 @@ using namespace std;
using boost::dynamic_pointer_cast;
using boost::lexical_cast;
using namespace isc::dns;
+using namespace isc::testutils;
using namespace isc::datasrc::test;
namespace {
@@ -261,7 +263,10 @@ public:
virtual void commit() {}
virtual void rollback() {}
virtual void addRecordToZone(const string (&)[ADD_COLUMN_COUNT]) {}
+ virtual void addNSEC3RecordToZone(const string (&)[ADD_NSEC3_COLUMN_COUNT])
+ {}
virtual void deleteRecordInZone(const string (&)[DEL_PARAM_COUNT]) {}
+ virtual void deleteNSEC3RecordInZone(const string (&)[DEL_PARAM_COUNT]) {}
virtual void addRecordDiff(int, uint32_t, DiffOperation,
const std::string (&)[DIFF_PARAM_COUNT]) {}
@@ -374,6 +379,8 @@ public:
MockAccessor() : rollbacked_(false), did_transaction_(false) {
readonly_records_ = &readonly_records_master_;
update_records_ = &update_records_master_;
+ nsec3_namespace_ = &nsec3_namespace_master_;
+ update_nsec3_namespace_ = &update_nsec3_namespace_master_;
empty_records_ = &empty_records_master_;
journal_entries_ = &journal_entries_master_;
fillData();
@@ -383,6 +390,9 @@ public:
boost::shared_ptr<MockAccessor> cloned_accessor(new MockAccessor());
cloned_accessor->readonly_records_ = &readonly_records_master_;
cloned_accessor->update_records_ = &update_records_master_;
+ cloned_accessor->nsec3_namespace_ = &nsec3_namespace_master_;
+ cloned_accessor->update_nsec3_namespace_ =
+ &update_nsec3_namespace_master_;
cloned_accessor->empty_records_ = &empty_records_master_;
cloned_accessor->journal_entries_ = &journal_entries_master_;
latest_clone_ = cloned_accessor;
@@ -649,8 +659,8 @@ public:
virtual IteratorContextPtr getNSEC3Records(const std::string& hash,
int) const
{
- Domains::const_iterator it(nsec3_namespace_.find(hash));
- if (it == nsec3_namespace_.end()) {
+ Domains::const_iterator it(nsec3_namespace_->find(hash));
+ if (it == nsec3_namespace_->end()) {
return (IteratorContextPtr(new EmptyIteratorContext()));
} else {
return (IteratorContextPtr(new DomainIterator(it->second)));
@@ -670,8 +680,10 @@ public:
// original.
if (replace) {
update_records_->clear();
+ update_nsec3_namespace_->clear();
} else {
*update_records_ = *readonly_records_;
+ *update_nsec3_namespace_ = nsec3_namespace_master_;
}
if (zone_name == "bad.example.org.") {
@@ -684,7 +696,9 @@ public:
}
virtual void commit() {
*readonly_records_ = *update_records_;
+ *nsec3_namespace_ = *update_nsec3_namespace_;
}
+
virtual void rollback() {
// Special hook: if something with a name of "throw.example.org"
// has been added, trigger an imaginary unexpected event with an
@@ -695,27 +709,54 @@ public:
rollbacked_ = true;
}
- virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
+
+private:
+ // Common subroutine for addRecordToZone and addNSEC3RecordToZone.
+ void addRecord(Domains& domains,
+ const string (&columns)[ADD_COLUMN_COUNT])
+ {
// Copy the current value to cur_name. If it doesn't exist,
// operator[] will create a new one.
- cur_name_ = (*update_records_)[columns[DatabaseAccessor::ADD_NAME]];
+ cur_name_ = domains[columns[ADD_NAME]];
vector<string> record_columns;
- record_columns.push_back(columns[DatabaseAccessor::ADD_TYPE]);
- record_columns.push_back(columns[DatabaseAccessor::ADD_TTL]);
- record_columns.push_back(columns[DatabaseAccessor::ADD_SIGTYPE]);
- record_columns.push_back(columns[DatabaseAccessor::ADD_RDATA]);
- record_columns.push_back(columns[DatabaseAccessor::ADD_NAME]);
+ record_columns.push_back(columns[ADD_TYPE]);
+ record_columns.push_back(columns[ADD_TTL]);
+ record_columns.push_back(columns[ADD_SIGTYPE]);
+ record_columns.push_back(columns[ADD_RDATA]);
+ record_columns.push_back(columns[ADD_NAME]);
// copy back the added entry
cur_name_.push_back(record_columns);
- (*update_records_)[columns[DatabaseAccessor::ADD_NAME]] = cur_name_;
+ domains[columns[DatabaseAccessor::ADD_NAME]] = cur_name_;
// remember this one so that test cases can check it.
copy(columns, columns + DatabaseAccessor::ADD_COLUMN_COUNT,
columns_lastadded_);
}
+public:
+ virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
+ addRecord(*update_records_, columns);
+ }
+
+ virtual void addNSEC3RecordToZone(
+ const string (&columns)[ADD_NSEC3_COLUMN_COUNT])
+ {
+ // Convert the NSEC3 parameters in the normal (non NSEC3) style so
+ // we can share the merge code, and then update using addRecord().
+ string normal_columns[ADD_COLUMN_COUNT];
+
+ normal_columns[ADD_TYPE] = columns[ADD_NSEC3_TYPE];
+ normal_columns[ADD_TTL] = columns[ADD_NSEC3_TTL];
+ normal_columns[ADD_SIGTYPE] = "";
+ normal_columns[ADD_RDATA] = columns[ADD_NSEC3_RDATA];
+ normal_columns[ADD_NAME] = columns[ADD_NSEC3_HASH];
+
+ addRecord(*update_nsec3_namespace_, normal_columns);
+ }
+
+private:
// Helper predicate class used in deleteRecordInZone().
struct deleteMatch {
deleteMatch(const string& type, const string& rdata) :
@@ -728,19 +769,33 @@ public:
const string& rdata_;
};
- virtual void deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
+ // Common subroutine for deleteRecordinZone and deleteNSEC3RecordInZone.
+ void deleteRecord(Domains& domains,
+ const string (¶ms)[DEL_PARAM_COUNT])
+ {
vector<vector<string> >& records =
- (*update_records_)[params[DatabaseAccessor::DEL_NAME]];
+ domains[params[DatabaseAccessor::DEL_NAME]];
records.erase(remove_if(records.begin(), records.end(),
deleteMatch(
params[DatabaseAccessor::DEL_TYPE],
params[DatabaseAccessor::DEL_RDATA])),
records.end());
if (records.empty()) {
- (*update_records_).erase(params[DatabaseAccessor::DEL_NAME]);
+ domains.erase(params[DatabaseAccessor::DEL_NAME]);
}
}
+public:
+ virtual void deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
+ deleteRecord(*update_records_, params);
+ }
+
+ virtual void deleteNSEC3RecordInZone(
+ const string (¶ms)[DEL_PARAM_COUNT])
+ {
+ deleteRecord(*update_nsec3_namespace_, params);
+ }
+
//
// Helper methods to keep track of some update related activities
//
@@ -799,13 +854,13 @@ public:
{
// TODO: Provide some broken data, but it is not known yet how broken
// they'll have to be.
- Domains::const_iterator it(nsec3_namespace_.lower_bound(hash));
+ Domains::const_iterator it(nsec3_namespace_->lower_bound(hash));
// We got just after the one we want
- if (it == nsec3_namespace_.begin()) {
+ if (it == nsec3_namespace_->begin()) {
// Hmm, we got something really small. So we wrap around.
// This is one after the last, so after decreasing it we'll get
// the biggest.
- it = nsec3_namespace_.end();
+ it = nsec3_namespace_->end();
}
return ((--it)->first);
}
@@ -889,12 +944,13 @@ private:
Domains* readonly_records_;
Domains update_records_master_;
Domains* update_records_;
+ Domains nsec3_namespace_master_;
+ Domains* nsec3_namespace_;
+ Domains update_nsec3_namespace_master_;
+ Domains* update_nsec3_namespace_;
const Domains empty_records_master_;
const Domains* empty_records_;
- // The NSEC3 namespace. The above trick will be added once it is needed.
- Domains nsec3_namespace_;
-
// The journal data
std::vector<JournalEntry> journal_entries_master_;
std::vector<JournalEntry>* journal_entries_;
@@ -959,13 +1015,13 @@ private:
// the NSEC3 namespace. You don't provide the full name, only
// the hash part.
void addCurHash(const std::string& hash) {
- ASSERT_EQ(0, nsec3_namespace_.count(hash));
+ ASSERT_EQ(0, nsec3_namespace_->count(hash));
// Append the name to all of them
for (std::vector<std::vector<std::string> >::iterator
i = cur_name_.begin(); i != cur_name_.end(); ++ i) {
i->push_back(hash);
}
- nsec3_namespace_[hash] = cur_name_;
+ (*nsec3_namespace_)[hash] = cur_name_;
cur_name_.clear();
}
@@ -1207,7 +1263,7 @@ public:
rdata::createRdata(expected_rrset->getType(),
expected_rrset->getClass(),
(*it).data_[Accessor::DIFF_RDATA]));
- isc::testutils::rrsetCheck(expected_rrset, rrset);
+ rrsetCheck(expected_rrset, rrset);
}
// We should have examined all entries of both expected and
// actual data.
@@ -1376,7 +1432,7 @@ checkRRset(isc::dns::ConstRRsetPtr rrset,
isc::dns::rdata::createRdata(rrtype, rrclass,
rdatas[i]));
}
- isc::testutils::rrsetCheck(expected_rrset, rrset);
+ rrsetCheck(expected_rrset, rrset);
}
// Iterate through a zone, common case
@@ -1512,7 +1568,7 @@ TYPED_TEST(DatabaseClientTest, getSOAFromIterator) {
}
ASSERT_TRUE(rrset);
// It should be identical to the result of getSOA().
- isc::testutils::rrsetCheck(it->getSOA(), rrset);
+ rrsetCheck(it->getSOA(), rrset);
}
TYPED_TEST(DatabaseClientTest, noSOAFromIterator) {
@@ -1550,7 +1606,7 @@ TYPED_TEST(DatabaseClientTest, iterateThenUpdate) {
}
ASSERT_TRUE(rrset);
// It should be identical to the result of getSOA().
- isc::testutils::rrsetCheck(it->getSOA(), rrset);
+ rrsetCheck(it->getSOA(), rrset);
}
TYPED_TEST(DatabaseClientTest, updateThenIterateThenUpdate) {
@@ -2946,6 +3002,97 @@ TYPED_TEST(DatabaseClientTest, addRRsetToNewZone) {
this->checkLastAdded(rrset_added);
}
+// Below we define a set of NSEC3 update tests. Right now this only works
+// for the mock DB, but the plan is to make it a TYPED_TEST to share the case
+// with SQLite3 implementation, too.
+
+// Commonly used data for NSEC3 update tests below.
+const char* const nsec3_hash = "1BB7SO0452U1QHL98UISNDD9218GELR5";
+const char* const nsec3_rdata = "1 1 12 AABBCCDD "
+ "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA RRSIG NSEC3PARAM";
+const char* const nsec3_rdata2 = "1 1 12 AABBCCDD "
+ "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA RRSIG"; // differ in bitmaps
+const char* const nsec3_sig_rdata = "NSEC3 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. FAKEFAKEFAKE";
+const char* const nsec3_sig_rdata2 = "NSEC3 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. FAKEFAKE"; // differ in the signature
+
+// Commonly used subroutine that checks if we can get the expected record.
+// According to the API, implementations can skip filling in columns other
+// than those explicitly checked below, so we don't check them.
+void
+nsec3Check(const vector<ConstRRsetPtr>& expected_rrsets,
+ const Name& zone_name, const string& expected_hash,
+ DatabaseAccessor& accessor)
+{
+ const int zone_id = accessor.getZone(zone_name.toText()).second;
+ DatabaseAccessor::IteratorContextPtr itctx =
+ accessor.getNSEC3Records(expected_hash, zone_id);
+ ASSERT_TRUE(itctx);
+
+ // Build a list of matched RRsets and compare the both expected and built
+ // ones as sets.
+ string columns[DatabaseAccessor::COLUMN_COUNT];
+ vector<ConstRRsetPtr> actual_rrsets;
+ while (itctx->getNext(columns)) {
+ actual_rrsets.push_back(
+ textToRRset(expected_hash + "." + zone_name.toText() + " " +
+ columns[DatabaseAccessor::TTL_COLUMN] + " IN " +
+ columns[DatabaseAccessor::TYPE_COLUMN] + " " +
+ columns[DatabaseAccessor::RDATA_COLUMN]));
+ }
+ rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+ actual_rrsets.begin(), actual_rrsets.end());
+}
+
+TEST_F(MockDatabaseClientTest, addDeleteNSEC3InZone) {
+ // Add one NSEC3 RR to the zone, delete it, and add another one.
+ this->updater_ = this->client_->getUpdater(this->zname_, true);
+ const ConstRRsetPtr nsec3_rrset =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+ string(nsec3_rdata));
+ const ConstRRsetPtr nsec3_rrset2 =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+ string(nsec3_rdata2));
+ this->updater_->addRRset(*nsec3_rrset);
+ this->updater_->deleteRRset(*nsec3_rrset);
+ this->updater_->addRRset(*nsec3_rrset2);
+ this->updater_->commit();
+
+ // Check if we can get the expected record.
+ vector<ConstRRsetPtr> expected_rrsets;
+ expected_rrsets.push_back(nsec3_rrset2);
+ nsec3Check(expected_rrsets, this->zname_, nsec3_hash,
+ *this->current_accessor_);
+}
+
+TEST_F(MockDatabaseClientTest, addDeleteNSEC3AndRRSIGToZone) {
+ // Add one NSEC3 RR and its RRSIG to the zone, delete the RRSIG and add
+ // a new one.
+ this->updater_ = this->client_->getUpdater(this->zname_, true);
+ const ConstRRsetPtr nsec3_rrset =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+ string(nsec3_rdata));
+ const ConstRRsetPtr nsec3_sig_rrset =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN RRSIG " +
+ string(nsec3_sig_rdata));
+ const ConstRRsetPtr nsec3_sig_rrset2 =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN RRSIG " +
+ string(nsec3_sig_rdata2));
+ this->updater_->addRRset(*nsec3_rrset);
+ this->updater_->addRRset(*nsec3_sig_rrset);
+ this->updater_->deleteRRset(*nsec3_sig_rrset);
+ this->updater_->addRRset(*nsec3_sig_rrset2);
+ this->updater_->commit();
+
+ // Check if we can get the expected record.
+ vector<ConstRRsetPtr> expected_rrsets;
+ expected_rrsets.push_back(nsec3_rrset);
+ expected_rrsets.push_back(nsec3_sig_rrset2);
+ nsec3Check(expected_rrsets, this->zname_, nsec3_hash,
+ *this->current_accessor_);
+}
+
TYPED_TEST(DatabaseClientTest, addRRsetToCurrentZone) {
// Similar to the previous test, but not replacing the existing data.
boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
@@ -3492,6 +3639,52 @@ TYPED_TEST(DatabaseClientTest, journal) {
this->checkJournal(expected);
}
+// At the moment this only works for the mock accessor. Once sqlite3
+// accessor supports updating NSEC3, this should be merged to the previous
+// test
+TEST_F(MockDatabaseClientTest, journalForNSEC3) {
+ // Similar to the previous test, but adding/deleting NSEC3 RRs, just to
+ // confirm that NSEC3 is not special for managing diffs.
+ const ConstRRsetPtr nsec3_rrset =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+ string(nsec3_rdata));
+
+ this->updater_ = this->client_->getUpdater(this->zname_, false, true);
+ this->updater_->deleteRRset(*this->soa_);
+ this->updater_->deleteRRset(*nsec3_rrset);
+
+ this->soa_.reset(new RRset(this->zname_, this->qclass_, RRType::SOA(),
+ this->rrttl_));
+ this->soa_->addRdata(rdata::createRdata(this->soa_->getType(),
+ this->soa_->getClass(),
+ "ns1.example.org. "
+ "admin.example.org. "
+ "1235 3600 1800 2419200 7200"));
+ this->updater_->addRRset(*this->soa_);
+ this->updater_->addRRset(*nsec3_rrset);
+ this->updater_->commit();
+ std::vector<JournalEntry> expected;
+ expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1234,
+ DatabaseAccessor::DIFF_DELETE,
+ "example.org.", "SOA", "3600",
+ "ns1.example.org. admin.example.org. "
+ "1234 3600 1800 2419200 7200"));
+ expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1234,
+ DatabaseAccessor::DIFF_DELETE,
+ string(nsec3_hash) + ".example.org.",
+ "NSEC3", "3600", nsec3_rdata));
+ expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1235,
+ DatabaseAccessor::DIFF_ADD,
+ "example.org.", "SOA", "3600",
+ "ns1.example.org. admin.example.org. "
+ "1235 3600 1800 2419200 7200"));
+ expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1235,
+ DatabaseAccessor::DIFF_ADD,
+ string(nsec3_hash) + ".example.org.",
+ "NSEC3", "3600", nsec3_rdata));
+ this->checkJournal(expected);
+}
+
/*
* Push multiple delete-add sequences. Checks it is allowed and all is
* saved.
@@ -3673,10 +3866,10 @@ TYPED_TEST(DatabaseClientTest, journalReader) {
ASSERT_TRUE(jnl_reader);
ConstRRsetPtr rrset = jnl_reader->getNextDiff();
ASSERT_TRUE(rrset);
- isc::testutils::rrsetCheck(this->soa_, rrset);
+ rrsetCheck(this->soa_, rrset);
rrset = jnl_reader->getNextDiff();
ASSERT_TRUE(rrset);
- isc::testutils::rrsetCheck(soa_end, rrset);
+ rrsetCheck(soa_end, rrset);
rrset = jnl_reader->getNextDiff();
ASSERT_FALSE(rrset);
@@ -3720,7 +3913,7 @@ TYPED_TEST(DatabaseClientTest, readLargeJournal) {
ConstRRsetPtr actual;
int i = 0;
while ((actual = jnl_reader->getNextDiff()) != NULL) {
- isc::testutils::rrsetCheck(expected.at(i++), actual);
+ rrsetCheck(expected.at(i++), actual);
}
EXPECT_EQ(expected.size(), i); // we should have eaten all expected data
}
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index a7d13d5..f54af75 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -293,17 +293,71 @@ setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
++it;
}
-RRsetPtr
-textToRRset(const string& text_rrset, const RRClass& rrclass = RRClass::IN(),
- const Name& origin = Name::ROOT_NAME())
-{
- stringstream ss(text_rrset);
- RRsetPtr rrset;
- vector<RRsetPtr*> rrsets;
- rrsets.push_back(&rrset);
- masterLoad(ss, origin, 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<Name, string> 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 {
diff --git a/src/lib/testutils/dnsmessage_test.cc b/src/lib/testutils/dnsmessage_test.cc
index 3d653c0..ec6914d 100644
--- a/src/lib/testutils/dnsmessage_test.cc
+++ b/src/lib/testutils/dnsmessage_test.cc
@@ -23,6 +23,12 @@
#include <testutils/dnsmessage_test.h>
+#include <boost/bind.hpp>
+
+#include <string>
+#include <sstream>
+
+using namespace std;
using namespace isc::dns;
namespace isc {
@@ -80,6 +86,26 @@ matchRdata(const char*, const char*,
}
return (::testing::AssertionSuccess());
}
+
+// A helper callback of masterLoad() used by textToRRset() below.
+void
+setRRset(RRsetPtr rrset, RRsetPtr* rrsetp) {
+ if (*rrsetp) {
+ isc_throw(isc::Unexpected,
+ "multiple RRsets are given to textToRRset");
+ }
+ *rrsetp = rrset;
+}
+}
+
+RRsetPtr
+textToRRset(const string& text_rrset, const RRClass& rrclass,
+ const Name& origin)
+{
+ stringstream ss(text_rrset);
+ RRsetPtr rrset;
+ masterLoad(ss, origin, rrclass, boost::bind(setRRset, _1, &rrset));
+ return (rrset);
}
void
diff --git a/src/lib/testutils/dnsmessage_test.h b/src/lib/testutils/dnsmessage_test.h
index 5c74011..57cb72c 100644
--- a/src/lib/testutils/dnsmessage_test.h
+++ b/src/lib/testutils/dnsmessage_test.h
@@ -174,6 +174,29 @@ private:
};
}
+/// \brief A converter from a string to RRset.
+///
+/// This is a convenient shortcut for tests that need to create an RRset
+/// from textual representation with a single call to a function.
+///
+/// An RRset consisting of multiple RRs can be constructed, but only one
+/// RRset is allowed. If the given string contains mixed types of RRs
+/// it throws an \c isc::Unexpected exception.
+///
+/// \param text_rrset A complete textual representation of an RRset.
+/// It must meets the assumption of the \c dns::masterLoad() function.
+/// \param rrclass The RR class of the RRset. Note that \c text_rrset should
+/// contain the RR class, but it's needed for \c dns::masterLoad().
+/// \param origin The zone origin where the RR is expected to belong. This
+/// parameter normally doesn't have to be specified, but for an SOA RR it
+/// must be set to its owner name, due to the internal check of
+/// \c dns::masterLoad().
+isc::dns::RRsetPtr textToRRset(const std::string& text_rrset,
+ const isc::dns::RRClass& rrclass =
+ isc::dns::RRClass::IN(),
+ const isc::dns::Name& origin =
+ isc::dns::Name::ROOT_NAME());
+
/// Set of unit tests to check if two sets of RRsets are identical.
///
/// This templated function takes two sets of sequences, each defined by
More information about the bind10-changes
mailing list