Commit 0140368e authored by Stephen Morris's avatar Stephen Morris
Browse files

[2546] Merge branch 'master' into trac2546

parents fecfe82b ce492ecf
This diff is collapsed.
......@@ -38,6 +38,7 @@ libb10_datasrc_la_SOURCES += client_list.h client_list.cc
libb10_datasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc
libb10_datasrc_la_SOURCES += master_loader_callbacks.h
libb10_datasrc_la_SOURCES += master_loader_callbacks.cc
libb10_datasrc_la_SOURCES += zone_loader.h zone_loader.cc
nodist_libb10_datasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
libb10_datasrc_la_LDFLAGS = -no-undefined -version-info 1:0:1
......
......@@ -48,6 +48,19 @@ logWarning(const isc::dns::Name& name, const isc::dns::RRClass& rrclass,
arg(name).arg(rrclass).arg(reason);
}
void
addRR(const isc::dns::Name& name, const isc::dns::RRClass& rrclass,
const isc::dns::RRType& type, const isc::dns::RRTTL& ttl,
const isc::dns::rdata::RdataPtr& data, ZoneUpdater* updater)
{
// We get description of one RR. The updater takes RRset, so we
// wrap it up and push there. It should collate the RRsets of the
// same name and type together, since the addRRset should "merge".
isc::dns::BasicRRset rrset(name, rrclass, type, ttl);
rrset.addRdata(data);
updater->addRRset(rrset);
}
}
isc::dns::MasterLoaderCallbacks
......@@ -61,12 +74,9 @@ createMasterLoaderCallbacks(const isc::dns::Name& name,
rrclass, _1, _2, _3)));
}
isc::dns::AddRRsetCallback
isc::dns::AddRRCallback
createMasterLoaderAddCallback(ZoneUpdater& updater) {
return (boost::bind(&ZoneUpdater::addRRset, &updater,
// The callback provides a shared pointer, we
// need the object. This bind unpacks the object.
boost::bind(&isc::dns::RRsetPtr::operator*, _1)));
return (boost::bind(addRR, _1, _2, _3, _4, _5, &updater));
}
}
......
......@@ -58,7 +58,7 @@ createMasterLoaderCallbacks(const isc::dns::Name& name,
/// \param updater The zone updater to use.
/// \return The callback to be passed to MasterLoader.
/// \throw std::bad_alloc when allocation fails.
isc::dns::AddRRsetCallback
isc::dns::AddRRCallback
createMasterLoaderAddCallback(ZoneUpdater& updater);
}
......
......@@ -209,14 +209,20 @@ private:
RdataIteratorPtr rdata_iterator_;
bool separate_rrs_;
bool ready_;
bool examined_rrsigs_;
// In case there's nsec3 namespace in the zone, it is represented the same
// way as the usual namespace. So we reuse the iterator implementation for
// it.
ZoneIteratorPtr nsec3_namespace_;
public:
MemoryIterator(const RRClass& rrclass,
const ZoneTree& tree, const Name& origin,
bool separate_rrs) :
const ZoneTree& tree, const NSEC3Data* nsec3_data,
const Name& origin, bool separate_rrs) :
rrclass_(rrclass),
tree_(tree),
separate_rrs_(separate_rrs),
ready_(true)
ready_(true),
examined_rrsigs_(false)
{
// Find the first node (origin) and preserve the node chain for future
// searches
......@@ -235,10 +241,25 @@ public:
rdata_iterator_ = rrset_->getRdataIterator();
}
}
// If we have the NSEC3 namespace, get an iterator for it so we can
// delegate to it later.
if (nsec3_data != NULL) {
nsec3_namespace_ =
ZoneIteratorPtr(new MemoryIterator(rrclass,
nsec3_data->getNSEC3Tree(),
NULL, origin,
separate_rrs));
}
}
virtual ConstRRsetPtr getNextRRset() {
if (!ready_) {
// We are done iterating. But in case there's the nsec3 one,
// iterate through that one.
if (nsec3_namespace_ != ZoneIteratorPtr()) {
return (nsec3_namespace_->getNextRRset());
}
isc_throw(Unexpected, "Iterating past the zone end");
}
/*
......@@ -259,13 +280,19 @@ public:
rrset_.reset(new TreeNodeRRset(rrclass_,
node_, set_node_, true));
rdata_iterator_ = rrset_->getRdataIterator();
examined_rrsigs_ = false;
}
}
}
if (node_ == NULL) {
// That's all, folks
ready_ = false;
return (ConstRRsetPtr());
if (nsec3_namespace_ != ZoneIteratorPtr()) {
// In case we have the NSEC3 namespace, get one from there.
return (nsec3_namespace_->getNextRRset());
} else {
return (ConstRRsetPtr());
}
}
if (separate_rrs_) {
......@@ -273,10 +300,24 @@ public:
// 'current' rdata
RRsetPtr result(new RRset(rrset_->getName(),
rrset_->getClass(),
rrset_->getType(),
// If we are looking into the signature,
// we need to adjust the type too.
examined_rrsigs_ ? RRType::RRSIG() :
rrset_->getType(),
rrset_->getTTL()));
result->addRdata(rdata_iterator_->getCurrent());
rdata_iterator_->next();
if (!examined_rrsigs_ && rdata_iterator_->isLast()) {
// We got to the last RR of the RRset, but we need to look at
// the signatures too, if there are any.
examined_rrsigs_ = true;
const ConstRRsetPtr rrsig = rrset_->getRRsig();
if (rrsig != ConstRRsetPtr()) {
rrset_ = rrsig;
rdata_iterator_ = rrsig->getRdataIterator();
} // else - no RRSIG. rdata_iterator_ stays at last, next
// condition applies
}
if (rdata_iterator_->isLast()) {
// all used up, next.
set_node_ = set_node_->getNext();
......@@ -286,6 +327,7 @@ public:
rrset_.reset(new TreeNodeRRset(rrclass_,
node_, set_node_, true));
rdata_iterator_ = rrset_->getRdataIterator();
examined_rrsigs_ = false;
}
}
return (result);
......@@ -317,7 +359,8 @@ InMemoryClient::getIterator(const Name& name, bool separate_rrs) const {
return (ZoneIteratorPtr(new MemoryIterator(
getClass(),
result.zone_data->getZoneTree(), name,
result.zone_data->getZoneTree(),
result.zone_data->getNSEC3Data(), name,
separate_rrs)));
}
......
......@@ -60,6 +60,7 @@ run_unittests_SOURCES += zone_finder_context_unittest.cc
run_unittests_SOURCES += faked_nsec3.h faked_nsec3.cc
run_unittests_SOURCES += client_list_unittest.cc
run_unittests_SOURCES += master_loader_callbacks_test.cc
run_unittests_SOURCES += zone_loader_unittest.cc
# We need the actual module implementation in the tests (they are not part
# of libdatasrc)
......
......@@ -18,6 +18,9 @@
#include <dns/rrset.h>
#include <dns/rrclass.h>
#include <dns/rrttl.h>
#include <dns/rdata.h>
#include <testutils/dnsmessage_test.h>
#include <exceptions/exceptions.h>
......@@ -40,8 +43,21 @@ public:
// the correct ones, according to a predefined set in a list.
virtual void addRRset(const isc::dns::AbstractRRset& rrset) {
ASSERT_FALSE(expected_rrsets_.empty());
// In our tests, pointer equality is enough.
EXPECT_EQ(expected_rrsets_.front().get(), &rrset);
// As the rrsetCheck requires a shared pointer, we need to create
// a copy.
isc::dns::RRsetPtr copy(new isc::dns::BasicRRset(rrset.getName(),
rrset.getClass(),
rrset.getType(),
rrset.getTTL()));
EXPECT_FALSE(rrset.getRRsig()) << "Unexpected RRSIG on rrset, not "
"copying. Following check will likely fail as a result.";
for (isc::dns::RdataIteratorPtr it(rrset.getRdataIterator());
!it->isLast(); it->next()) {
copy->addRdata(it->getCurrent());
}
isc::testutils::rrsetCheck(expected_rrsets_.front(), copy);
// And remove this RRset, as it has been used.
expected_rrsets_.pop_front();
}
......@@ -67,14 +83,22 @@ protected:
isc::dns::RRClass::IN(), &ok_))
{}
// Generate a new RRset, put it to the updater and return it.
isc::dns::RRsetPtr generateRRset() {
void generateRRset(isc::dns::AddRRCallback callback) {
const isc::dns::RRsetPtr
result(new isc::dns::RRset(isc::dns::Name("example.org"),
isc::dns::RRClass::IN(),
isc::dns::RRType::A(),
isc::dns::RRTTL(3600)));
const isc::dns::rdata::RdataPtr
data(isc::dns::rdata::createRdata(isc::dns::RRType::A(),
isc::dns::RRClass::IN(),
"192.0.2.1"));
result->addRdata(data);
updater_.expected_rrsets_.push_back(result);
return (result);
callback(result->getName(), result->getClass(), result->getType(),
result->getTTL(), data);
}
// An updater to be passed to the context
MockUpdater updater_;
......@@ -112,11 +136,11 @@ TEST_F(MasterLoaderCallbackTest, callbacks) {
// Try adding some RRsets.
TEST_F(MasterLoaderCallbackTest, addRRset) {
isc::dns::AddRRsetCallback
isc::dns::AddRRCallback
callback(createMasterLoaderAddCallback(updater_));
// Put some of them in.
EXPECT_NO_THROW(callback(generateRRset()));
EXPECT_NO_THROW(callback(generateRRset()));
EXPECT_NO_THROW(generateRRset(callback));
EXPECT_NO_THROW(generateRRset(callback));
// They all get pushed there right away, so there are none in the queue
EXPECT_TRUE(updater_.expected_rrsets_.empty());
}
......
......@@ -690,6 +690,25 @@ TEST_F(MemoryClientTest, getIteratorSeparateRRs) {
EXPECT_EQ(ConstRRsetPtr(), iterator2->getNextRRset());
}
// Test we get RRSIGs and NSEC3s too for iterating with separate RRs
TEST_F(MemoryClientTest, getIteratorSeparateSigned) {
client_->load(Name("example.org"),
TEST_DATA_DIR "/example.org-nsec3-signed.zone");
ZoneIteratorPtr iterator(client_->getIterator(Name("example.org"), true));
bool seen_rrsig = false, seen_nsec3 = false;
for (ConstRRsetPtr rrset = iterator->getNextRRset();
rrset != ConstRRsetPtr(); rrset = iterator->getNextRRset()) {
if (rrset->getType() == RRType::RRSIG()) {
seen_rrsig = true;
} else if (rrset->getType() == RRType::NSEC3()) {
seen_nsec3 = true;
}
}
EXPECT_TRUE(seen_rrsig);
EXPECT_TRUE(seen_nsec3);
}
TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <datasrc/zone_loader.h>
#include <datasrc/data_source.h>
#include <datasrc/memory/zone_table_segment.h>
#include <datasrc/memory/memory_client.h>
#include <dns/rrclass.h>
#include <dns/name.h>
#include <dns/rrset.h>
#include <util/memory_segment_local.h>
#include <exceptions/exceptions.h>
#include <gtest/gtest.h>
#include <boost/shared_ptr.hpp>
#include <string>
#include <vector>
using isc::dns::RRClass;
using isc::dns::Name;
using isc::dns::RRType;
using isc::dns::ConstRRsetPtr;
using std::string;
using std::vector;
using boost::shared_ptr;
using namespace isc::datasrc;
namespace {
class MockClient : public DataSourceClient {
public:
MockClient() :
commit_called_(false),
missing_zone_(false),
rrclass_(RRClass::IN())
{}
virtual FindResult findZone(const Name&) const {
isc_throw(isc::NotImplemented, "Method not used in tests");
};
virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
getJournalReader(const Name&, uint32_t, uint32_t) const
{
isc_throw(isc::NotImplemented, "Method not used in tests");
}
virtual ZoneUpdaterPtr getUpdater(const Name& name, bool replace,
bool journaling) const;
// We store some information about what was happening here.
// It is publicly accessible, since this is private testing class
// anyway, so no need to dress it fancy into getters. Some are mutable,
// since many client methods are const, but we still want to know they
// were called.
mutable vector<Name> provided_updaters_;
// We store string representations of the RRsets. This is simpler than
// copying them and we can't really put them into shared pointers, because
// we get them as references.
vector<string> rrsets_;
bool commit_called_;
// If set to true, getUpdater returns NULL
bool missing_zone_;
// The pretended class of the client. Usualy IN, but can be overriden.
RRClass rrclass_;
};
// The updater isn't really correct according to the API. For example,
// the whole client can be committed only once in its lifetime. The
// updaters would influence each other if there were more. But we
// don't need more updaters in the same test, so it doesn't matter
// and this way, it is much simpler.
class Updater : public ZoneUpdater {
public:
Updater(MockClient* client) :
client_(client),
finder_(client_->rrclass_)
{}
virtual ZoneFinder& getFinder() {
return (finder_);
}
virtual void addRRset(const isc::dns::AbstractRRset& rrset) {
if (client_->commit_called_) {
isc_throw(DataSourceError, "Add after commit");
}
client_->rrsets_.push_back(rrset.toText());
}
virtual void deleteRRset(const isc::dns::AbstractRRset&) {
isc_throw(isc::NotImplemented, "Method not used in tests");
}
virtual void commit() {
client_->commit_called_ = true;
}
private:
MockClient* client_;
class Finder : public ZoneFinder {
public:
Finder(const RRClass& rrclass) :
class_(rrclass)
{}
virtual RRClass getClass() const {
return (class_);
}
virtual Name getOrigin() const {
isc_throw(isc::NotImplemented, "Method not used in tests");
}
virtual shared_ptr<Context> find(const Name&, const RRType&,
const FindOptions)
{
isc_throw(isc::NotImplemented, "Method not used in tests");
}
virtual shared_ptr<Context> findAll(const Name&,
vector<ConstRRsetPtr>&,
const FindOptions)
{
isc_throw(isc::NotImplemented, "Method not used in tests");
}
virtual FindNSEC3Result findNSEC3(const Name&, bool) {
isc_throw(isc::NotImplemented, "Method not used in tests");
}
private:
const RRClass class_;
} finder_;
};
ZoneUpdaterPtr
MockClient::getUpdater(const Name& name, bool replace, bool journaling) const {
if (missing_zone_) {
return (ZoneUpdaterPtr());
}
EXPECT_TRUE(replace);
EXPECT_FALSE(journaling);
provided_updaters_.push_back(name);
// const_cast is bad. But the const on getUpdater seems wrong in the first
// place, since updater will be modifying the data there. And the updater
// wants to store data into the client so we can examine it later.
return (ZoneUpdaterPtr(new Updater(const_cast<MockClient*>(this))));
}
class ZoneLoaderTest : public ::testing::Test {
protected:
ZoneLoaderTest() :
rrclass_(RRClass::IN()),
ztable_segment_(memory::ZoneTableSegment::
create(isc::data::NullElement(), rrclass_)),
source_client_(ztable_segment_, rrclass_)
{}
void prepareSource(const Name& zone, const char* filename) {
// TODO:
// Currently, load uses an urelated implementation. In the long term,
// the method will probably be deprecated. At that time, we should
// probably prepare the data in some other way (using sqlite3 or
// something). This is simpler for now.
source_client_.load(zone, string(TEST_DATA_DIR) + "/" + filename);
}
private:
const RRClass rrclass_;
// This is because of the in-memory client. We use it to read data
// from. It is still easier than setting up sqlite3 client, since
// we have this one in the linked library.
// FIXME: We should be destroying it by ZoneTableSegment::destroy.
// But the shared pointer won't let us, will it?
shared_ptr<memory::ZoneTableSegment> ztable_segment_;
protected:
memory::InMemoryClient source_client_;
// This one is mocked. It will help us see what is happening inside.
// Also, mocking it is simpler than setting up an sqlite3 client.
MockClient destination_client_;
};
// Use the loader to load an unsigned zone.
TEST_F(ZoneLoaderTest, copyUnsigned) {
prepareSource(Name::ROOT_NAME(), "root.zone");
ZoneLoader loader(destination_client_, Name::ROOT_NAME(), source_client_);
// It gets the updater directly in the constructor
ASSERT_EQ(1, destination_client_.provided_updaters_.size());
EXPECT_EQ(Name::ROOT_NAME(), destination_client_.provided_updaters_[0]);
// Now load the whole zone
loader.load();
EXPECT_TRUE(destination_client_.commit_called_);
// We don't check the whole zone. We check the first and last and the
// count, which should be enough.
// The count is 34 because we expect the RRs to be separated.
EXPECT_EQ(34, destination_client_.rrsets_.size());
// Ensure known order.
std::sort(destination_client_.rrsets_.begin(),
destination_client_.rrsets_.end());
EXPECT_EQ(". 518400 IN NS a.root-servers.net.\n",
destination_client_.rrsets_.front());
EXPECT_EQ("m.root-servers.net. 3600000 IN AAAA 2001:dc3::35\n",
destination_client_.rrsets_.back());
// It isn't possible to try again now
EXPECT_THROW(loader.load(), isc::InvalidOperation);
EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
// Even 0, which should load nothing, returns the error
EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
}
// Try loading incrementally.
TEST_F(ZoneLoaderTest, copyUnsignedIncremental) {
prepareSource(Name::ROOT_NAME(), "root.zone");
ZoneLoader loader(destination_client_, Name::ROOT_NAME(), source_client_);
// Try loading few RRs first.
loader.loadIncremental(10);
// We should get the 10 we asked for
EXPECT_EQ(10, destination_client_.rrsets_.size());
// Not committed yet, we didn't complete the loading
EXPECT_FALSE(destination_client_.commit_called_);
// This is unusual, but allowed. Check it doesn't do anything
loader.loadIncremental(0);
EXPECT_EQ(10, destination_client_.rrsets_.size());
EXPECT_FALSE(destination_client_.commit_called_);
// We can finish the rest
loader.loadIncremental(30);
EXPECT_EQ(34, destination_client_.rrsets_.size());
EXPECT_TRUE(destination_client_.commit_called_);
// No more loading now
EXPECT_THROW(loader.load(), isc::InvalidOperation);
EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
}
// Check we can load RRSIGs and NSEC3 (which could break due to them being
// in separate namespace)
TEST_F(ZoneLoaderTest, copySigned) {
prepareSource(Name("example.org"), "example.org.nsec3-signed");
ZoneLoader loader(destination_client_, Name("example.org"),
source_client_);
loader.load();
// All the RRs are there, including the ones in NSEC3 namespace
EXPECT_EQ(14, destination_client_.rrsets_.size());
EXPECT_TRUE(destination_client_.commit_called_);
// Same trick with sorting to know where they are
std::sort(destination_client_.rrsets_.begin(),
destination_client_.rrsets_.end());
// Due to the R at the beginning, this one should be last
EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 "
"1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG\n",
destination_client_.rrsets_[0]);
EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG "
"NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org."
" EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKOyfZc8w"
"KRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVvcD3dFksPyiKHf"
"/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3CTM=\n",
destination_client_.rrsets_[1]);
}
// If the destination zone does not exist, it throws
TEST_F(ZoneLoaderTest, copyMissingDestination) {
destination_client_.missing_zone_ = true;
prepareSource(Name::ROOT_NAME(), "root.zone");
EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
source_client_), DataSourceError);
}
// If the source zone does not exist, it throws
TEST_F(ZoneLoaderTest, copyMissingSource) {
EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
source_client_), DataSourceError);
}
// The class of the source and destination are different
TEST_F(ZoneLoaderTest, classMismatch) {
destination_client_.rrclass_ = RRClass::CH();
prepareSource(Name::ROOT_NAME(), "root.zone");
EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
source_client_), isc::InvalidParameter);
}
// Load an unsigned zone, all at once
TEST_F(ZoneLoaderTest, loadUnsigned) {
ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
TEST_DATA_DIR "/root.zone");
// It gets the updater directly in the constructor
ASSERT_EQ(1, destination_client_.provided_updaters_.size());
EXPECT_EQ(Name::ROOT_NAME(), destination_client_.provided_updaters_[0]);
// Now load the whole zone
loader.load();
EXPECT_TRUE(destination_client_.commit_called_);
// We don't check the whole zone. We check the first and last and the
// count, which should be enough.
// The count is 34 because we expect the RRs to be separated.
EXPECT_EQ(34, destination_client_.rrsets_.size());
// Ensure known order.
std::sort(destination_client_.rrsets_.begin(),
destination_client_.rrsets_.end());
EXPECT_EQ(". 518400 IN NS a.root-servers.net.\n",
destination_client_.rrsets_.front());
EXPECT_EQ("m.root-servers.net. 3600000 IN AAAA 2001:dc3::35\n",
destination_client_.rrsets_.back());
// It isn't possible to try again now
EXPECT_THROW(loader.load(), isc::InvalidOperation);
EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
// Even 0, which should load nothing, returns the error
EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
}
// Try loading from master file incrementally.
TEST_F(ZoneLoaderTest