Commit 0bc5c68e authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[1781] introduced addRecordToNSEC3Zone interface to DB accessors.

this is the first step to support updating the NSEC3 namespace of a zone.
to help support various cases in addRRset() in a less expensive way,
introduced a helper RRParameterConverter class.
one simple test case was added to confirm the behavior.
parent 6d7a2068
......@@ -1191,31 +1191,80 @@ 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_;
};
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()) {
string sigtype;
if (rrset.getType() == RRType::RRSIG()) {
// XXX: the current interface (based on the current sqlite3
// data source schema) requires a separate "sigtype" column,
......@@ -1224,16 +1273,25 @@ DatabaseUpdater::addRRset(const AbstractRRset& rrset) {
// 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 = rrsig_rdata.typeCovered().toText();
}
columns[Accessor::ADD_RDATA] = it->getCurrent().toText();
const string rdata = it->getCurrent().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 };
accessor_->addRecordDiff(zone_id_, serial_.getValue(),
Accessor::DIFF_ADD, journal);
}
accessor_->addRecordToZone(columns);
if (rrset.getType() == RRType::NSEC3()) {
const string nsec3_columns[Accessor::ADD_NSEC3_COLUMN_COUNT] =
{ cvtr.getNSEC3Name(), cvtr.getTTL(), cvtr.getType(), rdata };
accessor_->addRecordToNSEC3Zone(nsec3_columns);
} else {
const string columns[Accessor::ADD_COLUMN_COUNT] =
{ cvtr.getName(), cvtr.getRevName(), cvtr.getTTL(),
cvtr.getType(), sigtype, rdata };
accessor_->addRecordToZone(columns);
}
}
}
......
......@@ -95,6 +95,16 @@ public:
ADD_COLUMN_COUNT = 6 ///< Number of columns
};
enum AddNSEC3RecordColumns {
ADD_NSEC3_HASH = 0, ///< The owner name of the record (a domain name)
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)
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()
///
/// Each derived implementation of deleteRecordInZone() should expect
......@@ -432,6 +442,9 @@ public:
virtual void addRecordToZone(
const std::string (&columns)[ADD_COLUMN_COUNT]) = 0;
virtual void addRecordToNSEC3Zone(
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
......@@ -684,7 +697,7 @@ public:
/// This is used to find previous NSEC3 hashes, to find covering NSEC3 in
/// case none match exactly.
///
/// In case a hash before before the lowest or the lowest is provided,
/// In case a hash before the lowest or the lowest is provided,
/// this should return the largest one in the zone (NSEC3 needs a
/// wrap-around semantics).
///
......
......@@ -1121,6 +1121,13 @@ SQLite3Accessor::addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
*dbparameters_, ADD_RECORD, columns, "add record to zone");
}
void
SQLite3Accessor::addRecordToNSEC3Zone(
const string (&/*columns*/)[ADD_NSEC3_COLUMN_COUNT])
{
isc_throw(NotImplemented, "not yet implemented");
}
void
SQLite3Accessor::deleteRecordInZone(const string (&params)[DEL_PARAM_COUNT]) {
if (!dbparameters_->updating_zone) {
......
......@@ -214,6 +214,9 @@ public:
virtual void addRecordToZone(
const std::string (&columns)[ADD_COLUMN_COUNT]);
virtual void addRecordToNSEC3Zone(
const std::string (&columns)[ADD_NSEC3_COLUMN_COUNT]);
virtual void deleteRecordInZone(
const std::string (&params)[DEL_PARAM_COUNT]);
......
......@@ -14,6 +14,7 @@
#include <exceptions/exceptions.h>
#include <dns/masterload.h>
#include <dns/name.h>
#include <dns/rrttl.h>
#include <dns/rrset.h>
......@@ -29,6 +30,7 @@
#include <gtest/gtest.h>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
......@@ -247,6 +249,8 @@ public:
virtual void commit() {}
virtual void rollback() {}
virtual void addRecordToZone(const string (&)[ADD_COLUMN_COUNT]) {}
virtual void addRecordToNSEC3Zone(const string (&)[ADD_NSEC3_COLUMN_COUNT])
{}
virtual void deleteRecordInZone(const string (&)[DEL_PARAM_COUNT]) {}
virtual void addRecordDiff(int, uint32_t, DiffOperation,
const std::string (&)[DIFF_PARAM_COUNT]) {}
......@@ -687,6 +691,7 @@ public:
virtual void commit() {
*readonly_records_ = *update_records_;
}
virtual void rollback() {
// Special hook: if something with a name of "throw.example.org"
// has been added, trigger an imaginary unexpected event with an
......@@ -697,6 +702,7 @@ public:
rollbacked_ = true;
}
virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
// Copy the current value to cur_name. If it doesn't exist,
// operator[] will create a new one.
......@@ -718,6 +724,17 @@ public:
columns_lastadded_);
}
virtual void addRecordToNSEC3Zone(
const string (&columns)[ADD_NSEC3_COLUMN_COUNT])
{
columns_lastadded_[ADD_NAME] = columns[ADD_NSEC3_HASH];
columns_lastadded_[ADD_REV_NAME].clear();
columns_lastadded_[ADD_TTL] = columns[ADD_NSEC3_TTL];
columns_lastadded_[ADD_TYPE] = columns[ADD_NSEC3_TYPE];
columns_lastadded_[ADD_SIGTYPE].clear();
columns_lastadded_[ADD_RDATA] = columns[ADD_NSEC3_RDATA];
}
// Helper predicate class used in deleteRecordInZone().
struct deleteMatch {
deleteMatch(const string& type, const string& rdata) :
......@@ -2693,6 +2710,42 @@ TYPED_TEST(DatabaseClientTest, addRRsetToNewZone) {
this->checkLastAdded(rrset_added);
}
// TODO: maybe we should share this stuff
// A helper callback of masterLoad() used in InMemoryZoneFinderTest.
void
setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
*(*it) = rrset;
++it;
}
ConstRRsetPtr
textToRRset(const string& text_rrset, const RRClass& rrclass = RRClass::IN()) {
stringstream ss(text_rrset);
RRsetPtr rrset;
vector<RRsetPtr*> rrsets;
rrsets.push_back(&rrset);
masterLoad(ss, Name::ROOT_NAME(), rrclass,
boost::bind(setRRset, _1, rrsets.begin()));
return (rrset);
}
// 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.
TEST_F(MockDatabaseClientTest, addNSEC3ToZone) {
const char* const nsec3_hash = "1BB7SO0452U1QHL98UISNDD9218GELR5";
const char* const nsec3_rdata = "1 1 12 AABBCCDD "
"2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA RRSIG NSEC3PARAM";
this->updater_ = this->client_->getUpdater(this->zname_, true);
this->updater_->addRRset(
*textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
string(nsec3_rdata)));
this->updater_->commit();
const char* const rrset_added[] = {
nsec3_hash, "", "3600", "NSEC3", "", nsec3_rdata };
this->checkLastAdded(rrset_added);
}
TYPED_TEST(DatabaseClientTest, addRRsetToCurrentZone) {
// Similar to the previous test, but not replacing the existing data.
boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment