From: JINMEI Tatuya Date: Wed, 29 Aug 2012 00:09:26 +0000 (-0700) Subject: [2107merge] Merge branch 'trac2107' into trac2107merge with fixing conflicts. X-Git-Tag: trac2351_base~109^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0aeb26af21dc9828af28565e4da127b4d898bbdd;p=thirdparty%2Fkea.git [2107merge] Merge branch 'trac2107' into trac2107merge with fixing conflicts. Conflict resolution is mostly straightforward adujstment for the latest interface, but it's not super trivial and should be reviewed. --- 0aeb26af21dc9828af28565e4da127b4d898bbdd diff --cc src/lib/datasrc/memory/Makefile.am index 7cdc7369fe,4b427937d7..b8307c0a7e --- a/src/lib/datasrc/memory/Makefile.am +++ b/src/lib/datasrc/memory/Makefile.am @@@ -10,10 -10,11 +10,9 @@@ CLEANFILES = *.gcno *.gcda datasrc_mess 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 diff --cc src/lib/datasrc/memory/tests/Makefile.am index 128b8e7f95,e210a4ad13..22fd4bf947 --- a/src/lib/datasrc/memory/tests/Makefile.am +++ b/src/lib/datasrc/memory/tests/Makefile.am @@@ -21,7 -21,9 +21,10 @@@ run_unittests_SOURCES = run_unittests.c 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) diff --cc src/lib/datasrc/memory/tests/zone_data_unittest.cc index 0000000000,d2844ea84e..d15fe8beb2 mode 000000,100644..100644 --- a/src/lib/datasrc/memory/tests/zone_data_unittest.cc +++ b/src/lib/datasrc/memory/tests/zone_data_unittest.cc @@@ -1,0 -1,255 +1,255 @@@ + // 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 + + #include + + #include + #include + #include + -#include ++#include + #include + #include + + #include + + #include + + #include // for bad_alloc + #include + + 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 + 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(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(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(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(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(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(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()); + } + } diff --cc src/lib/datasrc/memory/tests/zone_table_unittest.cc index 4f0af5e7d1,0000000000..359df4923f mode 100644,000000..100644 --- a/src/lib/datasrc/memory/tests/zone_table_unittest.cc +++ b/src/lib/datasrc/memory/tests/zone_table_unittest.cc @@@ -1,143 -1,0 +1,150 @@@ +// 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 + +#include + +#include +#include + +#include +#include +#include + +#include + +#include // 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: - ZoneTableTest() : zname1(Name("example.com")), ++ ZoneTableTest() : zclass_(RRClass::IN()), ++ zname1(Name("example.com")), + zname2(Name("example.net")), + zname3(Name("example")), - zone_table(ZoneTable::create(mem_sgmt_)) ++ 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() { - ZoneTable::destroy(mem_sgmt_, zone_table); ++ 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); - EXPECT_THROW(ZoneTable::create(mem_sgmt_), std::bad_alloc); ++ 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 = - zone_table->addZone(mem_sgmt_, zname1); ++ zone_table->addZone(mem_sgmt_, zclass_, zname1); + EXPECT_EQ(result::SUCCESS, result1.code); + + // Duplicate add doesn't replace the existing data. - EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zname1).code); ++ EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, ++ zname1).code); + EXPECT_EQ(result1.zone_data, - zone_table->addZone(mem_sgmt_, zname1).zone_data); ++ zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data); + // names are compared in a case insensitive manner. - EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, ++ EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, + Name("EXAMPLE.COM")).code); + // Add some more different ones. Should just succeed. - 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::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); - EXPECT_THROW(zone_table->addZone(mem_sgmt_, Name("example.org")), ++ EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")), + std::bad_alloc); +} + +TEST_F(ZoneTableTest, findZone) { + const ZoneTable::AddResult add_result1 = - zone_table->addZone(mem_sgmt_, zname1); ++ zone_table->addZone(mem_sgmt_, zclass_, zname1); + EXPECT_EQ(result::SUCCESS, add_result1.code); - 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::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(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_, ++ 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); +} +} diff --cc src/lib/datasrc/memory/zone_data.cc index 0000000000,bb780086c2..033af150c3 mode 000000,100644..100644 --- a/src/lib/datasrc/memory/zone_data.cc +++ b/src/lib/datasrc/memory/zone_data.cc @@@ -1,0 -1,170 +1,170 @@@ + // 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 + + #include + #include + #include + + #include "rdataset.h" -#include "rdata_encoder.h" ++#include "rdata_serialization.h" + #include "zone_data.h" + #include "segment_object_holder.h" + + #include + #include + + #include + #include // for the placement new + #include + + 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& 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 RdataSetDeleterType; + detail::SegmentObjectHolder 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 RdataSetDeleterType; + detail::SegmentObjectHolder 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 diff --cc src/lib/datasrc/memory/zone_table.cc index 9c410d081a,0000000000..f9b2768e8d mode 100644,000000..100644 --- a/src/lib/datasrc/memory/zone_table.cc +++ b/src/lib/datasrc/memory/zone_table.cc @@@ -1,144 -1,0 +1,137 @@@ +// 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 + +#include + +#include +#include +#include ++#include ++ ++#include ++#include + +#include + +using namespace std; +using namespace isc::dns; + +namespace isc { +namespace datasrc { +namespace memory { - 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 - 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_; - }; - } ++using detail::SegmentObjectHolder; + ++namespace { +void - ZoneTable::ZoneDataDeleter::operator()(util::MemorySegment& mem_sgmt, - ZoneData* zone_data) const ++deleteZoneData(util::MemorySegment* mem_sgmt, ZoneData* zone_data, ++ RRClass rrclass) +{ - ZoneData::destroy(mem_sgmt, zone_data); ++ if (zone_data != NULL) { ++ ZoneData::destroy(*mem_sgmt, zone_data, rrclass); ++ } ++} ++typedef boost::function ZoneDataDeleterType; +} + +ZoneTable* - ZoneTable::create(util::MemorySegment& mem_sgmt) { - Holder holder(mem_sgmt, ZoneTableTree::create(mem_sgmt)); ++ZoneTable::create(util::MemorySegment& mem_sgmt, RRClass zone_class) { ++ SegmentObjectHolder 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::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable) { - ZoneTableTree::destroy(mem_sgmt, ztable->zones_.get()); ++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 - ZoneTable::addZone(util::MemorySegment& mem_sgmt, const Name& zone_name) { ++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. - Holder holder(mem_sgmt, ZoneData::create(mem_sgmt)); ++ SegmentObjectHolder 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(mem_sgmt, holder.get()); ++ 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 diff --cc src/lib/datasrc/memory/zone_table.h index b5da957eb4,0000000000..2faf606dff mode 100644,000000..100644 --- a/src/lib/datasrc/memory/zone_table.h +++ b/src/lib/datasrc/memory/zone_table.h @@@ -1,178 -1,0 +1,198 @@@ +// 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 + ++#include ++ +#include +#include + +#include +#include + +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 - typedef DomainTree ZoneTableTree; - typedef DomainTreeNode ZoneTableNode; ++ typedef DomainTree ZoneTableTree; ++ typedef DomainTreeNode 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. - static ZoneTable* create(util::MemorySegment& mem_sgmt); ++ /// \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 - /// \c 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). - static void destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable); ++ 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, ++ 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 zones_; +}; +} +} +} +#endif // __DATASRC_MEMORY_ZONE_TABLE_H + +// Local Variables: +// mode: c++ +// End: