noinst_LTLIBRARIES = libdatasrc_memory.la
--libdatasrc_memory_la_SOURCES = \
- rdata_encoder.h rdata_encoder.cc \
- rdata_field.h rdata_field.cc \
- rdata_reader.h rdata_reader.cc \
-- rdataset.h rdataset.cc \
- rdata_serialization.h rdata_serialization.cc \
- domaintree.h
- libdatasrc_memory_la_SOURCES += zone_data.h
- domaintree.h \
- segment_object_holder.h \
- zone_data.h zone_data.cc
++libdatasrc_memory_la_SOURCES = domaintree.h
++libdatasrc_memory_la_SOURCES += rdataset.h rdataset.cc
++libdatasrc_memory_la_SOURCES += rdata_serialization.h rdata_serialization.cc
++libdatasrc_memory_la_SOURCES += zone_data.h zone_data.cc
+libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
+EXTRA_DIST = rdata_serialization_priv.cc
run_unittests_SOURCES += rdata_serialization_unittest.cc
run_unittests_SOURCES += rdataset_unittest.cc
run_unittests_SOURCES += domaintree_unittest.cc
+run_unittests_SOURCES += zone_table_unittest.cc
+ run_unittests_SOURCES += zone_data_unittest.cc
+ run_unittests_SOURCES += memory_segment_test.h
+ run_unittests_SOURCES += segment_object_holder_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
--- /dev/null
-#include <datasrc/memory/rdata_encoder.h>
+ // 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 "memory_segment_test.h"
+
+ #include <dns/rdataclass.h>
+
+ #include <exceptions/exceptions.h>
+
+ #include <dns/name.h>
+ #include <dns/labelsequence.h>
+ #include <dns/rrclass.h>
+
++#include <datasrc/memory/rdata_serialization.h>
+ #include <datasrc/memory/rdataset.h>
+ #include <datasrc/memory/zone_data.h>
+
+ #include <testutils/dnsmessage_test.h>
+
+ #include <gtest/gtest.h>
+
+ #include <new> // for bad_alloc
+ #include <string>
+
+ using namespace isc::dns;
+ using namespace isc::dns::rdata;
+ using namespace isc::datasrc::memory;
+ using namespace isc::datasrc::memory::test;
+ using namespace isc::testutils;
+
+ namespace {
+
+ // With this single fixture we'll test both NSEC3Data and ZoneData
+ class ZoneDataTest : public ::testing::Test {
+ protected:
+ ZoneDataTest() :
+ nsec3_data_(NULL), param_rdata_("1 0 12 aabbccdd"),
+ param_rdata_nosalt_("1 1 10 -"),
+ param_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a')),
+ nsec3_rdata_("1 0 12 aabbccdd TDK23RP6 SOA"),
+ nsec3_rdata_nosalt_("1 1 10 - TDK23RP6 SOA"),
+ nsec3_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a') +
+ " TDK23RP6 SOA"),
+ zname_("example.com"),
+ zone_data_(ZoneData::create(mem_sgmt_, zname_)),
+ a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1")),
+ aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA 2001:db8::1")),
+ nsec3_rrset_(textToRRset("TDK23RP6.example.com. 3600 IN NSEC3 "
+ "1 0 12 aabbccdd TDK23RP6 SOA"))
+ {}
+ void TearDown() {
+ if (nsec3_data_ != NULL) {
+ NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN());
+ }
+ if (zone_data_ != NULL) {
+ ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
+ }
+ // detect any memory leak in the test memory segment
+ EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+ }
+
+ MemorySegmentTest mem_sgmt_;
+ NSEC3Data* nsec3_data_;
+ const generic::NSEC3PARAM param_rdata_, param_rdata_nosalt_,
+ param_rdata_largesalt_;
+ const generic::NSEC3 nsec3_rdata_, nsec3_rdata_nosalt_,
+ nsec3_rdata_largesalt_;
+ const Name zname_;
+ ZoneData* zone_data_;
+ const ConstRRsetPtr a_rrset_, aaaa_rrset_, nsec3_rrset_;
+ RdataEncoder encoder_;
+ };
+
+ // Shared by both test cases using NSEC3 and NSEC3PARAM Rdata
+ template <typename RdataType>
+ void
+ checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) {
+ NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, expect_rdata);
+
+ // Internal tree should be created and empty.
+ EXPECT_EQ(0, nsec3_data->getNSEC3Tree().getNodeCount());
+
+ EXPECT_EQ(expect_rdata.getHashalg(), nsec3_data->hashalg);
+ EXPECT_EQ(expect_rdata.getFlags(), nsec3_data->flags);
+ EXPECT_EQ(expect_rdata.getIterations(), nsec3_data->iterations);
+ EXPECT_EQ(expect_rdata.getSalt().size(), nsec3_data->getSaltLen());
+ if (expect_rdata.getSalt().size() > 0) {
+ EXPECT_EQ(0, memcmp(&expect_rdata.getSalt()[0],
+ nsec3_data->getSaltData(),
+ expect_rdata.getSalt().size()));
+ }
+
+ NSEC3Data::destroy(mem_sgmt, nsec3_data, RRClass::IN());
+ }
+
+ void
+ checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
+ const RdataSet* expected_set)
+ {
+ ZoneNode* node = NULL;
+ tree.find(name, &node);
+ ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+ EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type));
+ }
+
+ TEST_F(ZoneDataTest, createNSEC3Data) {
+ // Create an NSEC3Data object from various types of RDATA (of NSEC3PARAM
+ // and of NSEC3), check if the resulting parameters match.
+ checkNSEC3Data(mem_sgmt_, param_rdata_); // one 'usual' form of params
+ checkNSEC3Data(mem_sgmt_, param_rdata_nosalt_); // empty salt
+ checkNSEC3Data(mem_sgmt_, param_rdata_largesalt_); // max-len salt
+
+ // Same concepts of the tests, using NSEC3 RDATA.
+ checkNSEC3Data(mem_sgmt_, nsec3_rdata_);
+ checkNSEC3Data(mem_sgmt_, nsec3_rdata_nosalt_);
+ checkNSEC3Data(mem_sgmt_, nsec3_rdata_largesalt_);
+ }
+
+ TEST_F(ZoneDataTest, addNSEC3) {
+ nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
+
+ ZoneNode* node = NULL;
+ nsec3_data_->insertName(mem_sgmt_, nsec3_rrset_->getName(), &node);
+ ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+ EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+
+ RdataSet* rdataset_nsec3 =
+ RdataSet::create(mem_sgmt_, encoder_, nsec3_rrset_, ConstRRsetPtr());
+ node->setData(rdataset_nsec3);
+
+ // Confirm we can find the added ones from the zone data.
+ checkFindRdataSet(nsec3_data_->getNSEC3Tree(), nsec3_rrset_->getName(),
+ RRType::NSEC3(), rdataset_nsec3);
+
+ // TearDown() will confirm there's no leak on destroy
+ }
+
+ TEST_F(ZoneDataTest, getOriginNode) {
+ EXPECT_EQ(LabelSequence(zname_), zone_data_->getOriginNode()->getLabels());
+ }
+
+ TEST_F(ZoneDataTest, exceptionSafetyOnCreate) {
+ // Note: below, we use our knowledge of how memory allocation happens
+ // within the NSEC3Data, the zone data and the underlying domain tree
+ // implementation. We'll emulate rare situations where allocate() fails
+ // with an exception, and confirm it doesn't cause any harsh disruption
+ // or leak.
+
+ // Creating internal NSEC3 tree will succeed, but allocation of NSEC3Data
+ // will fail due to bad_alloc. It shouldn't cause memory leak
+ // (that would be caught in TearDown()).
+ mem_sgmt_.setThrowCount(2);
+ EXPECT_THROW(NSEC3Data::create(mem_sgmt_, param_rdata_), std::bad_alloc);
+
+ // allocate() will throw on the insertion of the origin node.
+ mem_sgmt_.setThrowCount(2);
+ EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+
+ // allocate() will throw on creating the zone data.
+ mem_sgmt_.setThrowCount(3);
+ EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+
+ // These incomplete create() attempts shouldn't cause memory leak
+ // (that would be caught in TearDown()).
+ }
+
+ TEST_F(ZoneDataTest, addRdataSets) {
+ // Insert a name to the zone, and add a couple the data (RdataSet) objects
+ // to the corresponding node.
+
+ ZoneNode* node = NULL;
+ zone_data_->insertName(mem_sgmt_, a_rrset_->getName(), &node);
+ ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+ EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+
+ RdataSet* rdataset_a =
+ RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr());
+ node->setData(rdataset_a);
+
+ RdataSet* rdataset_aaaa =
+ RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_, ConstRRsetPtr());
+ // make a linked list and replace the list head
+ rdataset_aaaa->next = rdataset_a;
+ node->setData(rdataset_aaaa);
+
+ // Confirm we can find the added ones from the zone data.
+ checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+ RRType::A(), rdataset_a);
+ checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+ RRType::AAAA(), rdataset_aaaa);
+ // There's no NS (or anything other than AAAA or A) RdataSet in the list
+ checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+ RRType::NS(), NULL);
+
+ // TearDown() will confirm there's no leak on destroy
+ }
+
+ TEST_F(ZoneDataTest, getSetNSEC3Data) {
+ // Initially there's no NSEC3 data
+ EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+ // isNSEC3Signed is true iff zone data has non NULL NSEC3 data
+ EXPECT_FALSE(zone_data_->isNSEC3Signed());
+
+ // Set a new one. The set method should return NULL. The get method
+ // should return the new one.
+ NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, param_rdata_);
+ NSEC3Data* old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data);
+ EXPECT_EQ(static_cast<NSEC3Data*>(NULL), old_nsec3_data);
+ EXPECT_EQ(nsec3_data, zone_data_->getNSEC3Data());
+ EXPECT_TRUE(zone_data_->isNSEC3Signed());
+
+ // Replace an existing one with a yet another one.
+ // We're responsible for destroying the old one.
+ NSEC3Data* nsec3_data2 = NSEC3Data::create(mem_sgmt_, nsec3_rdata_);
+ old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data2);
+ EXPECT_EQ(nsec3_data, old_nsec3_data);
+ EXPECT_EQ(nsec3_data2, zone_data_->getNSEC3Data());
+ EXPECT_TRUE(zone_data_->isNSEC3Signed());
+ NSEC3Data::destroy(mem_sgmt_, old_nsec3_data, RRClass::IN());
+
+ // Setting NULL clears any existing one.
+ old_nsec3_data = zone_data_->setNSEC3Data(NULL);
+ EXPECT_EQ(nsec3_data2, old_nsec3_data);
+ EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+ EXPECT_FALSE(zone_data_->isNSEC3Signed());
+
+ // Then set it again. The zone data should destroy it on its own
+ // destruction.
+ zone_data_->setNSEC3Data(old_nsec3_data);
+ }
+
+ TEST_F(ZoneDataTest, isSigned) {
+ // By default it's considered unsigned
+ EXPECT_FALSE(zone_data_->isSigned());
+
+ // declare it's signed, the isSigned() says so too
+ zone_data_->setSigned(true);
+ EXPECT_TRUE(zone_data_->isSigned());
+
+ // change it to unsigned again
+ zone_data_->setSigned(false);
+ EXPECT_FALSE(zone_data_->isSigned());
+ }
+ }
--- /dev/null
- ZoneTableTest() : zname1(Name("example.com")),
+// 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 <exceptions/exceptions.h>
+
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/result.h>
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/zone_table.h>
+
+#include <gtest/gtest.h>
+
+#include <new> // for bad_alloc
+
+using namespace isc::dns;
+using namespace isc::datasrc;
+using namespace isc::datasrc::memory;
+
+namespace {
+// Memory segment specified for tests. It normally behaves like a "local"
+// memory segment. If "throw count" is set to non 0 via setThrowCount(),
+// it continues the normal behavior up to the specified number of calls to
+// allocate(), and throws an exception at the next call.
+class TestMemorySegment : public isc::util::MemorySegmentLocal {
+public:
+ TestMemorySegment() : throw_count_(0) {}
+ virtual void* allocate(size_t size) {
+ if (throw_count_ > 0) {
+ if (--throw_count_ == 0) {
+ throw std::bad_alloc();
+ }
+ }
+ return (isc::util::MemorySegmentLocal::allocate(size));
+ }
+ void setThrowCount(size_t count) { throw_count_ = count; }
+
+private:
+ size_t throw_count_;
+};
+
+class ZoneTableTest : public ::testing::Test {
+protected:
- zone_table(ZoneTable::create(mem_sgmt_))
++ ZoneTableTest() : zclass_(RRClass::IN()),
++ zname1(Name("example.com")),
+ zname2(Name("example.net")),
+ zname3(Name("example")),
- ZoneTable::destroy(mem_sgmt_, zone_table);
++ zone_table(ZoneTable::create(mem_sgmt_, zclass_))
+ {}
+ ~ZoneTableTest() {
+ if (zone_table != NULL) {
- ZoneTable::destroy(mem_sgmt_, zone_table);
++ ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
+ }
+ }
+ void TearDown() {
- EXPECT_THROW(ZoneTable::create(mem_sgmt_), std::bad_alloc);
++ ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
+ zone_table = NULL;
+ EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
+ }
++ const RRClass zclass_;
+ const Name zname1, zname2, zname3;
+ TestMemorySegment mem_sgmt_;
+ ZoneTable* zone_table;
+};
+
+TEST_F(ZoneTableTest, create) {
+ // Test about creating a zone table. Normal case covers through other
+ // tests. We only check exception safety by letting the test memory
+ // segment throw.
+ mem_sgmt_.setThrowCount(2);
- zone_table->addZone(mem_sgmt_, zname1);
++ EXPECT_THROW(ZoneTable::create(mem_sgmt_, zclass_), std::bad_alloc);
+ // This shouldn't cause memory leak (that would be caught in TearDown()).
+}
+
+TEST_F(ZoneTableTest, addZone) {
+ // Normal successful case.
+ const ZoneTable::AddResult result1 =
- EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zname1).code);
++ zone_table->addZone(mem_sgmt_, zclass_, zname1);
+ EXPECT_EQ(result::SUCCESS, result1.code);
+
+ // Duplicate add doesn't replace the existing data.
- zone_table->addZone(mem_sgmt_, zname1).zone_data);
++ EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
++ zname1).code);
+ EXPECT_EQ(result1.zone_data,
- EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_,
++ zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
+ // names are compared in a case insensitive manner.
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
++ EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
+ Name("EXAMPLE.COM")).code);
+ // Add some more different ones. Should just succeed.
- EXPECT_THROW(zone_table->addZone(mem_sgmt_, Name("example.org")),
++ EXPECT_EQ(result::SUCCESS,
++ zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
++ EXPECT_EQ(result::SUCCESS,
++ zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
+
+ // Have the memory segment throw an exception in extending the internal
+ // tree. It still shouldn't cause memory leak (which would be detected
+ // in TearDown()).
+ mem_sgmt_.setThrowCount(2);
- zone_table->addZone(mem_sgmt_, zname1);
++ EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")),
+ std::bad_alloc);
+}
+
+TEST_F(ZoneTableTest, findZone) {
+ const ZoneTable::AddResult add_result1 =
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname2).code);
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zname3).code);
++ zone_table->addZone(mem_sgmt_, zclass_, zname1);
+ EXPECT_EQ(result::SUCCESS, add_result1.code);
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_,
++ EXPECT_EQ(result::SUCCESS,
++ zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
++ EXPECT_EQ(result::SUCCESS,
++ zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
+
+ const ZoneTable::FindResult find_result1 =
+ zone_table->findZone(Name("example.com"));
+ EXPECT_EQ(result::SUCCESS, find_result1.code);
+ EXPECT_EQ(add_result1.zone_data, find_result1.zone_data);
+
+ EXPECT_EQ(result::NOTFOUND,
+ zone_table->findZone(Name("example.org")).code);
+ EXPECT_EQ(static_cast<ZoneData*>(NULL),
+ zone_table->findZone(Name("example.org")).zone_data);
+
+ // there's no exact match. the result should be the longest match,
+ // and the code should be PARTIALMATCH.
+ EXPECT_EQ(result::PARTIALMATCH,
+ zone_table->findZone(Name("www.example.com")).code);
+ EXPECT_EQ(add_result1.zone_data,
+ zone_table->findZone(Name("www.example.com")).zone_data);
+
+ // make sure the partial match is indeed the longest match by adding
+ // a zone with a shorter origin and query again.
++ EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
+ Name("com")).code);
+ EXPECT_EQ(add_result1.zone_data,
+ zone_table->findZone(Name("www.example.com")).zone_data);
+}
+}
--- /dev/null
-#include "rdata_encoder.h"
+ // 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 <util/memory_segment.h>
+
+ #include <dns/name.h>
+ #include <dns/rrclass.h>
+ #include <dns/rdataclass.h>
+
+ #include "rdataset.h"
++#include "rdata_serialization.h"
+ #include "zone_data.h"
+ #include "segment_object_holder.h"
+
+ #include <boost/bind.hpp>
+ #include <boost/function.hpp>
+
+ #include <cassert>
+ #include <new> // for the placement new
+ #include <vector>
+
+ using namespace isc::dns;
+ using namespace isc::dns::rdata;
+
+ namespace isc {
+ namespace datasrc {
+ namespace memory {
+
+ namespace {
+ void
+ rdataSetDeleter(RRClass rrclass, util::MemorySegment* mem_sgmt,
+ RdataSet* rdataset_head)
+ {
+ RdataSet* rdataset_next;
+ for (RdataSet* rdataset = rdataset_head;
+ rdataset != NULL;
+ rdataset = rdataset_next)
+ {
+ rdataset_next = rdataset->getNext();
+ RdataSet::destroy(*mem_sgmt, rrclass, rdataset);
+ }
+ }
+
+ void
+ nullDeleter(RdataSet* rdataset_head) {
+ assert(rdataset_head == NULL);
+ }
+ }
+
+ NSEC3Data*
+ NSEC3Data::create(util::MemorySegment& mem_sgmt,
+ const generic::NSEC3PARAM& rdata)
+ {
+ return (NSEC3Data::create(mem_sgmt, rdata.getHashalg(), rdata.getFlags(),
+ rdata.getIterations(), rdata.getSalt()));
+ }
+
+ NSEC3Data*
+ NSEC3Data::create(util::MemorySegment& mem_sgmt, const generic::NSEC3& rdata) {
+ return (NSEC3Data::create(mem_sgmt, rdata.getHashalg(), rdata.getFlags(),
+ rdata.getIterations(), rdata.getSalt()));
+ }
+
+ NSEC3Data*
+ NSEC3Data::create(util::MemorySegment& mem_sgmt, uint8_t hashalg,
+ uint8_t flags, uint16_t iterations,
+ const std::vector<uint8_t>& salt)
+ {
+ // NSEC3Data allocation can throw. To avoid leaking the tree, we manage
+ // it in the holder.
+ // Note: we won't add any RdataSet, so we use the NO-OP deleter
+ // (with an assertion check for that).
+ typedef boost::function<void(RdataSet*)> RdataSetDeleterType;
+ detail::SegmentObjectHolder<ZoneTree, RdataSetDeleterType> holder(
+ mem_sgmt, ZoneTree::create(mem_sgmt, true),
+ boost::bind(nullDeleter, _1));
+
+ const size_t salt_len = salt.size();
+
+ void* p = mem_sgmt.allocate(sizeof(NSEC3Data) + 1 + salt_len);
+ NSEC3Data* const param_data =
+ new(p) NSEC3Data(holder.release(), hashalg, flags, iterations);
+ uint8_t* dp = param_data->getSaltBuf();
+ *dp++ = salt_len;
+ if (salt_len > 0) {
+ memcpy(dp, &salt.at(0), salt_len); // use at for safety
+ }
+
+ return (param_data);
+ }
+
+ void
+ NSEC3Data::destroy(util::MemorySegment& mem_sgmt, NSEC3Data* data,
+ RRClass nsec3_class)
+ {
+ ZoneTree::destroy(mem_sgmt, data->nsec3_tree_.get(),
+ boost::bind(rdataSetDeleter, nsec3_class, &mem_sgmt,
+ _1));
+ mem_sgmt.deallocate(data, sizeof(NSEC3Data) + 1 + data->getSaltLen());
+ }
+
+ void
+ NSEC3Data::insertName(util::MemorySegment& mem_sgmt, const Name& name,
+ ZoneNode** node)
+ {
+ const ZoneTree::Result result = nsec3_tree_->insert(mem_sgmt, name, node);
+
+ // This should be ensured by the API:
+ assert((result == ZoneTree::SUCCESS ||
+ result == ZoneTree::ALREADYEXISTS) && node != NULL);
+ }
+
+ ZoneData*
+ ZoneData::create(util::MemorySegment& mem_sgmt, const Name& zone_origin) {
+ // ZoneTree::insert() and ZoneData allocation can throw. See also
+ // NSEC3Data::create().
+ typedef boost::function<void(RdataSet*)> RdataSetDeleterType;
+ detail::SegmentObjectHolder<ZoneTree, RdataSetDeleterType> holder(
+ mem_sgmt, ZoneTree::create(mem_sgmt, true),
+ boost::bind(nullDeleter, _1));
+
+ ZoneTree* tree = holder.get();
+ ZoneNode* origin_node = NULL;
+ const ZoneTree::Result result =
+ tree->insert(mem_sgmt, zone_origin, &origin_node);
+ assert(result == ZoneTree::SUCCESS);
+ void* p = mem_sgmt.allocate(sizeof(ZoneData));
+ ZoneData* zone_data = new(p) ZoneData(holder.release(), origin_node);
+
+ return (zone_data);
+ }
+
+ void
+ ZoneData::destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data,
+ RRClass zone_class)
+ {
+ ZoneTree::destroy(mem_sgmt, zone_data->zone_tree_.get(),
+ boost::bind(rdataSetDeleter, zone_class, &mem_sgmt,
+ _1));
+ if (zone_data->nsec3_data_) {
+ NSEC3Data::destroy(mem_sgmt, zone_data->nsec3_data_.get(), zone_class);
+ }
+ mem_sgmt.deallocate(zone_data, sizeof(ZoneData));
+ }
+
+ void
+ ZoneData::insertName(util::MemorySegment& mem_sgmt, const Name& name,
+ ZoneNode** node)
+ {
+ const ZoneTree::Result result = zone_tree_->insert(mem_sgmt, name, node);
+
+ // This should be ensured by the API:
+ assert((result == ZoneTree::SUCCESS ||
+ result == ZoneTree::ALREADYEXISTS) && node != NULL);
+ }
+
+ } // namespace memory
+ } // namespace datasrc
+ } // datasrc isc
--- /dev/null
- namespace {
- // A simple holder to create and use some objects in this implementation
- // in an exception safe manner. It works like std::auto_ptr but much
- // more simplified.
- template <typename T>
- class Holder {
- public:
- Holder(util::MemorySegment& mem_sgmt, T* obj) :
- mem_sgmt_(mem_sgmt), obj_(obj)
- {}
- ~Holder() {
- if (obj_ != NULL) {
- T::destroy(mem_sgmt_, obj_);
- }
- }
- T* get() { return (obj_); }
- T* release() {
- T* ret = obj_;
- obj_ = NULL;
- return (ret);
- }
- private:
- util::MemorySegment& mem_sgmt_;
- T* obj_;
- };
- }
+// 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 <util/memory_segment.h>
+
+#include <dns/name.h>
+
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/zone_table.h>
+#include <datasrc/memory/domaintree.h>
++#include <datasrc/memory/segment_object_holder.h>
++
++#include <boost/function.hpp>
++#include <boost/bind.hpp>
+
+#include <cassert>
+
+using namespace std;
+using namespace isc::dns;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
- ZoneTable::ZoneDataDeleter::operator()(util::MemorySegment& mem_sgmt,
- ZoneData* zone_data) const
++using detail::SegmentObjectHolder;
+
++namespace {
+void
- ZoneData::destroy(mem_sgmt, zone_data);
++deleteZoneData(util::MemorySegment* mem_sgmt, ZoneData* zone_data,
++ RRClass rrclass)
+{
- ZoneTable::create(util::MemorySegment& mem_sgmt) {
- Holder<ZoneTableTree> holder(mem_sgmt, ZoneTableTree::create(mem_sgmt));
++ if (zone_data != NULL) {
++ ZoneData::destroy(*mem_sgmt, zone_data, rrclass);
++ }
++}
++typedef boost::function<void(ZoneData*)> ZoneDataDeleterType;
+}
+
+ZoneTable*
- ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable) {
- ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get());
++ZoneTable::create(util::MemorySegment& mem_sgmt, RRClass zone_class) {
++ SegmentObjectHolder<ZoneTableTree, ZoneDataDeleterType> holder(
++ mem_sgmt, ZoneTableTree::create(mem_sgmt),
++ boost::bind(deleteZoneData, &mem_sgmt, _1, zone_class));
+ void* p = mem_sgmt.allocate(sizeof(ZoneTable));
+ ZoneTable* zone_table = new(p) ZoneTable(holder.get());
+ holder.release();
+ return (zone_table);
+}
+
+void
- ZoneTable::addZone(util::MemorySegment& mem_sgmt, const Name& zone_name) {
++ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable,
++ RRClass zone_class)
++{
++ ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get(),
++ boost::bind(deleteZoneData, &mem_sgmt, _1,
++ zone_class));
+ mem_sgmt.deallocate(ztable, sizeof(ZoneTable));
+}
+
+ZoneTable::AddResult
- Holder<ZoneData> holder(mem_sgmt, ZoneData::create(mem_sgmt));
++ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
++ const Name& zone_name)
++{
+ // Create a new ZoneData instance first. If the specified name already
+ // exists in the table, the new data will soon be destroyed, but we want
+ // to make sure if this allocation fails the tree won't be changed to
+ // provide as strong guarantee as possible. In practice, we generally
+ // expect the caller tries to add a zone only when it's a new one, so
+ // this should be a minor concern.
- node->setData(mem_sgmt, holder.get());
++ SegmentObjectHolder<ZoneData, RRClass> holder(
++ mem_sgmt, ZoneData::create(mem_sgmt, zone_name), zone_class);
+
+ // Get the node where we put the zone
+ ZoneTableNode* node(NULL);
+ switch (zones_->insert(mem_sgmt, zone_name, &node)) {
+ case ZoneTableTree::SUCCESS:
+ case ZoneTableTree::ALREADYEXISTS:
+ // These are OK
+ break;
+ default:
+ // Can Not Happen
+ assert(false);
+ }
+ // Can Not Happen
+ assert(node != NULL);
+
+ // Is it empty? We either just created it or it might be nonterminal
+ if (node->isEmpty()) {
++ node->setData(holder.get());
+ return (AddResult(result::SUCCESS, holder.release()));
+ } else { // There's something there already
+ return (AddResult(result::EXIST, node->getData()));
+ }
+}
+
+ZoneTable::FindResult
+ZoneTable::findZone(const Name& name) const {
+ ZoneTableNode* node(NULL);
+ result::Result my_result;
+
+ // Translate the return codes
+ switch (zones_->find(name, &node)) {
+ case ZoneTableTree::EXACTMATCH:
+ my_result = result::SUCCESS;
+ break;
+ case ZoneTableTree::PARTIALMATCH:
+ my_result = result::PARTIALMATCH;
+ break;
+ case ZoneTableTree::NOTFOUND:
+ // We have no data there, so translate the pointer to NULL as well
+ return (FindResult(result::NOTFOUND, NULL));
+ default:
+ // Can Not Happen
+ assert(0);
+ // Because of warning
+ return (FindResult(result::NOTFOUND, NULL));
+ }
+
+ // Can Not Happen (remember, NOTFOUND is handled)
+ assert(node != NULL);
+
+ return (FindResult(my_result, node->getData()));
+}
+
+} // end of namespace memory
+} // end of namespace datasrc
+} // end of namespace isc
--- /dev/null
- typedef DomainTree<ZoneData, ZoneDataDeleter> ZoneTableTree;
- typedef DomainTreeNode<ZoneData, ZoneDataDeleter> ZoneTableNode;
+// 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.
+
+#ifndef __DATASRC_MEMORY_ZONE_TABLE_H
+#define __DATASRC_MEMORY_ZONE_TABLE_H 1
+
+#include <util/memory_segment.h>
+
++#include <dns/rrclass.h>
++
+#include <datasrc/result.h>
+#include <datasrc/memory/domaintree.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/interprocess/offset_ptr.hpp>
+
+namespace isc {
+namespace dns {
+class Name;
+class RRClass;
+}
+
+namespace datasrc {
+namespace memory {
+// forward declaration: in this header it's mostly an opaque type.
+class ZoneData;
+
+/// \brief A conceptual table of authoritative zones.
+///
+/// This class is actually a simple wrapper for a \c DomainTree whose data is
+/// of \c ZoneData, and provides allocator, deallocator, and some basic
+/// manipulation methods.
+///
+/// A single \c ZoneData object is intended to be used for a single specific
+/// RR class, and provides a mapping from a name to a \c ZoneData (using the
+/// best matching search semantics). The \c ZoneData class itself does not
+/// maintain the information of the RR class; the user of this class is
+/// responsible for associating a specific RR class to a corresponding
+/// \c ZoneData object.
+///
+/// This class is designed so an instance can be stored in a shared memory
+/// region. So it only contains straightforward data (e.g., it doesn't hold
+/// a pointer to an object of some base class that contains virtual methods),
+/// and some pointers (either as a direct or indirect member variable) are
+/// represented as offset pointers. For the same reason this class should
+/// never has virtual methods (and as a result, should never be inherited
+/// in practice). When this class is extended these properties must be
+/// retained.
+///
+/// This class is intended to be used as a backend for the \c MemoryDataSrc
+/// class, and is not intended to be used for other general purposes.
+class ZoneTable : boost::noncopyable {
+private:
+ // The deleter for the zone data stored in the table.
+ struct ZoneDataDeleter {
+ ZoneDataDeleter() {}
+ void operator()(util::MemorySegment& mem_sgmt,
+ ZoneData* zone_data) const;
+ };
+
+ // Type aliases to make it shorter
- static ZoneTable* create(util::MemorySegment& mem_sgmt);
++ typedef DomainTree<ZoneData> ZoneTableTree;
++ typedef DomainTreeNode<ZoneData> ZoneTableNode;
+
+public:
+ /// \brief Result data of addZone() method.
+ struct AddResult {
+ AddResult(result::Result param_code, ZoneData* param_zone_data) :
+ code(param_code), zone_data(param_zone_data)
+ {}
+ const result::Result code;
+ ZoneData* const zone_data;
+ };
+
+ /// \brief Result data of findZone() method.
+ struct FindResult {
+ FindResult(result::Result param_code,
+ const ZoneData* param_zone_data) :
+ code(param_code), zone_data(param_zone_data)
+ {}
+ const result::Result code;
+ const ZoneData* const zone_data;
+ };
+
+private:
+ /// Constructor.
+ ///
+ /// An object of this class is always expected to be created by the
+ /// allocator (\c create()), so the constructor is hidden as private.
+ ///
+ /// This constructor internally involves resource allocation, and if
+ /// it fails, a corresponding standard exception will be thrown.
+ /// It never throws an exception otherwise.
+ ZoneTable(ZoneTableTree* zones) : zones_(zones)
+ {}
+
+public:
+ /// \brief Allocate and construct \c ZoneTable
+ ///
+ /// This static method allocates memory for a new \c ZoneTable object
+ /// from the given memory segment, constructs the object, and returns
+ /// a pointer to it.
+ ///
+ /// \throw std::bad_alloc Memory allocation fails.
+ ///
+ /// \param mem_sgmt A \c MemorySegment from which memory for the new
+ /// \c ZoneTable is allocated.
- /// \c ztable.
++ /// \param zone_class The RR class of the zone. It must be the RR class
++ /// that is supposed to be associated to the zone table.
++ static ZoneTable* create(util::MemorySegment& mem_sgmt,
++ dns::RRClass zone_class);
+
+ /// \brief Destruct and deallocate \c ZoneTable
+ ///
++ /// This method releases all internal resources including all zone data
++ /// created via \c addZone() calls.
++ ///
+ /// \throw none
+ ///
+ /// \param mem_sgmt The \c MemorySegment that allocated memory for
- static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable);
++ /// \c ztable and used for prior calls to \c addZone().
++ /// \param zone_class The RR class of the zone. It must be the RR class
++ /// that is supposed to be associated to the zone table.
+ /// \param ztable A non NULL pointer to a valid \c ZoneTable object
+ /// that was originally created by the \c create() method (the behavior
+ /// is undefined if this condition isn't met).
- AddResult addZone(util::MemorySegment& mem_sgmt,
++ static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable,
++ dns::RRClass zone_class);
+
+ /// Add a new zone to the \c ZoneTable.
+ ///
+ /// This method creates a new \c ZoneData for the given zone name and
+ /// holds it in the internal table. The newly created zone data will be
+ /// returned via the \c zone_data member of the return value. If the given
+ /// zone name already exists in the table, a new data object won't be
+ /// created; instead, the existing corresponding data will be returned.
+ ///
++ /// The zone table keeps the ownership of the created zone data; the
++ /// caller must not try to destroy it directly. (We'll eventually
++ /// add an interface to delete specific zone data from the table).
++ ///
+ /// \throw std::bad_alloc Internal resource allocation fails.
+ ///
++ /// \param mem_sgmt The \c MemorySegment to allocate zone data to be
++ /// created. It must be the same segment that was used to create
++ /// the zone table at the time of create().
+ /// \param zone_name The name of the zone to be added.
++ /// \param zone_class The RR class of the zone. It must be the RR class
++ /// that is supposed to be associated to the zone table.
+ /// \return \c result::SUCCESS If the zone is successfully
+ /// added to the zone table.
+ /// \return \c result::EXIST The zone table already contains
+ /// zone of the same origin.
++ AddResult addZone(util::MemorySegment& mem_sgmt, dns::RRClass zone_class,
+ const dns::Name& zone_name);
+
+ /// Find a zone that best matches the given name in the \c ZoneTable.
+ ///
+ /// It searches the internal storage for a zone that gives the
+ /// longest match against \c name, and returns the result in the
+ /// form of a \c FindResult object as follows:
+ /// - \c code: The result code of the operation.
+ /// - \c result::SUCCESS: A zone that gives an exact match
+ /// is found
+ /// - \c result::PARTIALMATCH: A zone whose origin is a
+ /// super domain of \c name is found (but there is no exact match)
+ /// - \c result::NOTFOUND: For all other cases.
+ /// - \c zone_data: corresponding zone data of the found zone; NULL if
+ /// no matching zone is found.
+ ///
+ /// \throw none
+ ///
+ /// \param name A domain name for which the search is performed.
+ /// \return A \c FindResult object enclosing the search result (see above).
+ FindResult findZone(const isc::dns::Name& name) const;
+
+private:
+ boost::interprocess::offset_ptr<ZoneTableTree> zones_;
+};
+}
+}
+}
+#endif // __DATASRC_MEMORY_ZONE_TABLE_H
+
+// Local Variables:
+// mode: c++
+// End: