BIND 10 master, updated. 300c412c3f474b537a739fc38bd37a89cf35a78e [master] Merge branch 'trac2100'
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Aug 24 23:00:25 UTC 2012
The branch, master has been updated
via 300c412c3f474b537a739fc38bd37a89cf35a78e (commit)
via 48ff20698455bb03fadef08daa7c1176534d7e41 (commit)
via 0c16bd99c081bbdf9a16081e4fc9783edbea0cb9 (commit)
via ef7d33797149380197fbdf259464d8455d6c1400 (commit)
via da5c0338f55a35680ac316863ef3f4f95aea9060 (commit)
via 0a15919f5570c67cc6cfee64fbbb83732098cd51 (commit)
via 920a2f5a59f8c78e407e4a0cd49e73f466adacd8 (commit)
via aef961dacd7f9ed32410426fa5cce015cb35c224 (commit)
via 86f3e7053f03a475f5a56e0017d2459c95461f00 (commit)
via ed62080de7677102f09acc0eca4e6026178c9d25 (commit)
via 450fef21a63c432b7cd439228ac98a839d335a87 (commit)
via 2e9fc841b3315b7c52166a49ce8299de2d251699 (commit)
via 3e433eb3ea7937dec94549d04053b808f8f3869e (commit)
via c2f339c9a062542e1f2918de73f48e14d8b66645 (commit)
via 311f8b58911f6d4cb2760d224c67e16c85675684 (commit)
via a98c9ab9a636d075e10f826620fa1aab2ded892c (commit)
via 5cd828127e95f334d4983bb4175de01ce33cabb2 (commit)
via 57de7551b2cc0206f8d1a3ffae9d60b4097ecde1 (commit)
from 3e963e68d9f5828fb4c4993a83e15be24eaeafc1 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 300c412c3f474b537a739fc38bd37a89cf35a78e
Merge: 3e963e6 48ff206
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Fri Aug 24 15:52:02 2012 -0700
[master] Merge branch 'trac2100'
-----------------------------------------------------------------------
Summary of changes:
src/lib/datasrc/memory/Makefile.am | 8 +-
src/lib/datasrc/memory/domaintree.h | 34 ++--
src/lib/datasrc/memory/tests/Makefile.am | 1 +
.../datasrc/memory/tests/zone_table_unittest.cc | 142 ++++++++++++++++
.../memory/zone_data.h} | 43 +++--
src/lib/datasrc/memory/zone_table.cc | 144 ++++++++++++++++
src/lib/datasrc/memory/zone_table.h | 178 ++++++++++++++++++++
src/lib/datasrc/rbtree.h | 6 +-
src/lib/dns/labelsequence.cc | 22 ++-
src/lib/dns/labelsequence.h | 33 +++-
src/lib/dns/tests/labelsequence_unittest.cc | 31 +++-
11 files changed, 597 insertions(+), 45 deletions(-)
create mode 100644 src/lib/datasrc/memory/tests/zone_table_unittest.cc
copy src/lib/{python/isc/datasrc/configurableclientlist_python.h => datasrc/memory/zone_data.h} (52%)
create mode 100644 src/lib/datasrc/memory/zone_table.cc
create mode 100644 src/lib/datasrc/memory/zone_table.h
-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index 90ab7b8..8e7b03e 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/src/lib/datasrc/memory/Makefile.am
@@ -10,7 +10,7 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
noinst_LTLIBRARIES = libdatasrc_memory.la
-libdatasrc_memory_la_SOURCES = \
- rdata_encoder.h \
- rdata_encoder.cc \
- domaintree.h
+libdatasrc_memory_la_SOURCES = rdata_encoder.h rdata_encoder.cc
+libdatasrc_memory_la_SOURCES += domaintree.h
+libdatasrc_memory_la_SOURCES += zone_data.h
+libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h
index ad8f085..2c4bd59 100644
--- a/src/lib/datasrc/memory/domaintree.h
+++ b/src/lib/datasrc/memory/domaintree.h
@@ -229,37 +229,33 @@ public:
///
/// You should not delete the data, it is deleted when the tree is
/// destroyed.
- T* getData() { return (data_); }
+ T* getData() { return (data_.get()); }
/// \brief Return the data stored in this node (const).
- const T* getData() const { return (data_); }
+ const T* getData() const { return (data_.get()); }
/// \brief return whether the node has related data.
///
/// There can be empty nodes inside the DomainTree. They are usually the
/// non-terminal domains, but it is possible (yet probably meaningless)
/// empty nodes anywhere.
- bool isEmpty() const { return (data_ == NULL); }
+ bool isEmpty() const { return (!data_); }
//@}
/// \name Setter functions.
//@{
- /// \brief Set the data stored in the node. If there is old data, it
- /// is either returned or destroyed based on what is passed in \c
- /// old_data.
+ /// \brief Set the data stored in the node.
+ ///
+ /// Any old data is destroyed.
+ ///
/// \param mem_sgmt The \c MemorySegment that allocated memory for
/// the node data.
/// \param data The new data to set.
- /// \param old_data If \c NULL is passed here, any old data is
- /// destroyed. Otherwise, the old data is returned
- /// in this location.
- void setData(util::MemorySegment& mem_sgmt, T* data, T** old_data = NULL) {
- if (old_data != NULL) {
- *old_data = data;
- } else {
+ void setData(util::MemorySegment& mem_sgmt, T* data) {
+ if (data_) {
const DT deleter;
- deleter(mem_sgmt, data_);
+ deleter(mem_sgmt, data_.get());
}
data_ = data;
}
@@ -476,7 +472,7 @@ private:
}
/// \brief Data stored here.
- T* data_;
+ boost::interprocess::offset_ptr<T> data_;
/// \brief Internal or user-configurable flags of node's properties.
///
@@ -1390,7 +1386,7 @@ DomainTree<T, DT>::deleteHelper(util::MemorySegment& mem_sgmt,
// free this one and go back to its parent.
DomainTreeNode<T, DT>* node = root;
root = root->getParent();
- deleter(mem_sgmt, node->data_);
+ deleter(mem_sgmt, node->data_.get());
DomainTreeNode<T, DT>::destroy(mem_sgmt, node);
--node_count_;
}
@@ -1652,8 +1648,12 @@ DomainTree<T, DT>::insert(util::MemorySegment& mem_sgmt,
isc::dns::LabelSequence target_labels(target_name);
int order = -1;
+ // For possible LabelSequence serialization we always store labels data
+ // in the separate local buffer.
+ uint8_t labels_buf[dns::LabelSequence::MAX_SERIALIZED_LENGTH];
while (current != NULL) {
- const dns::LabelSequence current_labels(current->getLabels());
+ const dns::LabelSequence current_labels(
+ dns::LabelSequence(current->getLabels(), labels_buf));
const isc::dns::NameComparisonResult compare_result =
target_labels.compare(current_labels);
const isc::dns::NameComparisonResult::NameRelation relation =
diff --git a/src/lib/datasrc/memory/tests/Makefile.am b/src/lib/datasrc/memory/tests/Makefile.am
index 1396600..2275d84 100644
--- a/src/lib/datasrc/memory/tests/Makefile.am
+++ b/src/lib/datasrc/memory/tests/Makefile.am
@@ -20,6 +20,7 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += rdata_encoder_unittest.cc
run_unittests_SOURCES += domaintree_unittest.cc
+run_unittests_SOURCES += zone_table_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
diff --git a/src/lib/datasrc/memory/tests/zone_table_unittest.cc b/src/lib/datasrc/memory/tests/zone_table_unittest.cc
new file mode 100644
index 0000000..8078a9b
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/zone_table_unittest.cc
@@ -0,0 +1,142 @@
+// 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:
+ ZoneTableTest() : zname1(Name("example.com")),
+ zname2(Name("example.net")),
+ zname3(Name("example")),
+ zone_table(ZoneTable::create(mem_sgmt_))
+ {}
+ ~ZoneTableTest() {
+ if (zone_table != NULL) {
+ ZoneTable::destroy(mem_sgmt_, zone_table);
+ }
+ }
+ void TearDown() {
+ ZoneTable::destroy(mem_sgmt_, zone_table);
+ zone_table = NULL;
+ EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
+ }
+ 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);
+ // 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);
+ 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(result1.zone_data,
+ zone_table->addZone(mem_sgmt_, zname1).zone_data);
+ // names are compared in a case insensitive manner.
+ EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_,
+ 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);
+
+ // 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")),
+ std::bad_alloc);
+}
+
+TEST_F(ZoneTableTest, findZone) {
+ const ZoneTable::AddResult add_result1 =
+ zone_table->addZone(mem_sgmt_, 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);
+
+ 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(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_,
+ Name("com")).code);
+ EXPECT_EQ(add_result1.zone_data,
+ zone_table->findZone(Name("www.example.com")).zone_data);
+}
+}
diff --git a/src/lib/datasrc/memory/zone_data.h b/src/lib/datasrc/memory/zone_data.h
new file mode 100644
index 0000000..31c4520
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_data.h
@@ -0,0 +1,51 @@
+// 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_DATA_H
+#define DATASRC_MEMORY_ZONE_DATA_H 1
+
+#include <util/memory_segment.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+/// \brief Data for a single zone.
+///
+/// It's currently empty and is only provided for the implementation of
+/// ZoneTable. The actual implementation of this class is the subject of
+/// Trac #2107.
+class ZoneData {
+private:
+ ZoneData() {}
+ ~ZoneData() {}
+public:
+ static ZoneData* create(util::MemorySegment& mem_sgmt) {
+ void* p = mem_sgmt.allocate(sizeof(ZoneData));
+ ZoneData* zone_data = new(p) ZoneData();
+ return (zone_data);
+ }
+ static void destroy(util::MemorySegment& mem_sgmt, ZoneData* zone_data) {
+ zone_data->~ZoneData();
+ mem_sgmt.deallocate(zone_data, sizeof(ZoneData));
+ }
+};
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_ZONE_DATA_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc
new file mode 100644
index 0000000..9c410d0
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_table.cc
@@ -0,0 +1,144 @@
+// 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 <cassert>
+
+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 <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_;
+};
+}
+
+void
+ZoneTable::ZoneDataDeleter::operator()(util::MemorySegment& mem_sgmt,
+ ZoneData* zone_data) const
+{
+ ZoneData::destroy(mem_sgmt, zone_data);
+}
+
+ZoneTable*
+ZoneTable::create(util::MemorySegment& mem_sgmt) {
+ Holder<ZoneTableTree> holder(mem_sgmt, ZoneTableTree::create(mem_sgmt));
+ 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());
+ mem_sgmt.deallocate(ztable, sizeof(ZoneTable));
+}
+
+ZoneTable::AddResult
+ZoneTable::addZone(util::MemorySegment& mem_sgmt, 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<ZoneData> holder(mem_sgmt, ZoneData::create(mem_sgmt));
+
+ // 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());
+ 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 --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h
new file mode 100644
index 0000000..b5da957
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_table.h
@@ -0,0 +1,178 @@
+// 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 <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
+ typedef DomainTree<ZoneData, ZoneDataDeleter> ZoneTableTree;
+ typedef DomainTreeNode<ZoneData, ZoneDataDeleter> 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);
+
+ /// \brief Destruct and deallocate \c ZoneTable
+ ///
+ /// \throw none
+ ///
+ /// \param mem_sgmt The \c MemorySegment that allocated memory for
+ /// \c ztable.
+ /// \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);
+
+ /// 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.
+ ///
+ /// \throw std::bad_alloc Internal resource allocation fails.
+ ///
+ /// \param zone_name The name of the zone to be added.
+ /// \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,
+ 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:
diff --git a/src/lib/datasrc/rbtree.h b/src/lib/datasrc/rbtree.h
index 2ec5347..eb971e8 100644
--- a/src/lib/datasrc/rbtree.h
+++ b/src/lib/datasrc/rbtree.h
@@ -1622,8 +1622,12 @@ RBTree<T>::insert(util::MemorySegment& mem_sgmt,
isc::dns::LabelSequence target_labels(target_name);
int order = -1;
+ // For possible LabelSequence serialization we always store labels data
+ // in the separate local buffer.
+ uint8_t labels_buf[dns::LabelSequence::MAX_SERIALIZED_LENGTH];
while (current != NULL) {
- const dns::LabelSequence current_labels(current->getLabels());
+ const dns::LabelSequence current_labels(
+ dns::LabelSequence(current->getLabels(), labels_buf));
const isc::dns::NameComparisonResult compare_result =
target_labels.compare(current_labels);
const isc::dns::NameComparisonResult::NameRelation relation =
diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc
index ba85b91..ed23f26 100644
--- a/src/lib/dns/labelsequence.cc
+++ b/src/lib/dns/labelsequence.cc
@@ -91,6 +91,19 @@ LabelSequence::getSerializedLength() const {
return (1 + getLabelCount() + getDataLength());
}
+namespace {
+// Check if buf is not in the range of [bp, ep), which means
+// - end of buffer is before bp, or
+// - beginning of buffer is on or after ep
+bool
+isOutOfRange(const uint8_t* bp, const uint8_t* ep,
+ const uint8_t* buf, size_t buf_len)
+{
+ return (bp >= buf + buf_len || // end of buffer is before bp
+ ep <= buf); // beginning of buffer is on or after ep
+}
+}
+
void
LabelSequence::serialize(void* buf, size_t buf_len) const {
const size_t expected_size = getSerializedLength();
@@ -101,12 +114,19 @@ LabelSequence::serialize(void* buf, size_t buf_len) const {
const size_t offsets_len = getLabelCount();
assert(offsets_len < 256); // should be in the 8-bit range
+ // Overridden check. Buffer shouldn't overwrap the offset of name data
+ // regions.
uint8_t* bp = reinterpret_cast<uint8_t*>(buf);
+ const size_t ndata_len = getDataLength();
+ if (!isOutOfRange(offsets_, offsets_ + offsets_len, bp, buf_len) ||
+ !isOutOfRange(data_, data_ + ndata_len, bp, buf_len)) {
+ isc_throw(BadValue, "serialize would break the source sequence");
+ }
+
*bp++ = offsets_len;
for (size_t i = 0; i < offsets_len; ++i) {
*bp++ = offsets_[first_label_ + i] - offsets_[first_label_];
}
- const size_t ndata_len = getDataLength();
std::memcpy(bp, &data_[offsets_[first_label_]], ndata_len);
bp += ndata_len;
diff --git a/src/lib/dns/labelsequence.h b/src/lib/dns/labelsequence.h
index 186bda6..caadd90 100644
--- a/src/lib/dns/labelsequence.h
+++ b/src/lib/dns/labelsequence.h
@@ -179,6 +179,34 @@ public:
/// the value returned by getSerializedLength() (it can be larger than
/// that).
///
+ /// Be careful about where the buffer is located; due to the nature
+ /// of the buffer, it's quite possible that the memory region is being used
+ /// to construct another active \c LabelSequence. In such a case
+ /// the serialization would silently break that sequence object, and
+ /// it will be very difficult to identify the cause. This method
+ /// has minimal level checks to avoid such disruption: If the serialization
+ /// would break "this" \c LabelSequence object, it doesn't write anything
+ /// to the given buffer and throw a \c isc::BadValue exception.
+ ///
+ /// In general, it should be safe to call this method on a
+ /// \c LabelSequence object constructed from a \c Name object or
+ /// a copy of such \c LabelSequence. When you construct \c LabelSequence
+ /// from pre-serialized data, calling this method on it can be unsafe.
+ /// One safe (but a bit less efficient) way in such a case is to make
+ /// the source \c LabelSequence temporary and immediately create a
+ /// local copy using an explicit buffer, and call this method on the
+ /// latter:
+ /// \code
+ /// // don't do this, it's not safe (and would result in exception):
+ /// // LabelSequence(buf).serialize(buf, buf_len);
+ ///
+ /// // The following are the safe way:
+ /// uint8_t ext_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+ /// LabelSequence seq(LabelSequence(buf), ext_buf);
+ /// ... (strip the labels, etc)
+ /// seq.serialize(buf, buf_len); // it's safe to override buf here
+ /// \endcode
+ ///
/// The serialized image would be as follows:
/// - olen: number of offsets (1 byte)
/// - binary sequence of offsets (olen bytes, verbatim copy of offsets_
@@ -186,13 +214,14 @@ public:
/// - binary sequence of name data (length determined by itself, verbatim
/// copy of data_ of the corresponding size)
///
- /// Applications must use the resulting image opaque value and must not
+ /// Applications must use the resulting image as opaque value and must not
/// use it for other purposes than input to the corresponding constructor
/// to restore it. Application behavior that assumes the specific
/// organization of the image is not guaranteed.
///
/// \throw isc::BadValue buf_len is too short (this method never throws
- /// otherwise)
+ /// otherwise) or the serialization would override internal data of
+ /// of the source LabelSequence.
///
/// \param buf Pointer to the placeholder to dump the serialized image
/// \param buf_len The size of available region in \c buf
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
index e8e2846..2ce1ef7 100644
--- a/src/lib/dns/tests/labelsequence_unittest.cc
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -712,8 +712,9 @@ TEST_F(LabelSequenceTest, LeftShiftOperator) {
}
TEST_F(LabelSequenceTest, serialize) {
- // placeholder for serialized data
- uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+ // placeholder for serialized data. We use a sufficiently large space
+ // for testing the overwrapping cases below.
+ uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH * 3];
// vector to store expected and actual data
vector<LabelSequence> actual_labelseqs;
@@ -777,6 +778,32 @@ TEST_F(LabelSequenceTest, serialize) {
EXPECT_EQ(NameComparisonResult::EQUAL,
LabelSequence(labels_buf).compare(*itl).getRelation());
+
+ // Shift the data to the middle of the buffer for overwrap check
+ uint8_t* const bp = labels_buf;
+ std::memcpy(bp + serialized_len, bp, serialized_len);
+ // Memory layout is now as follows:
+ // <- ser_len -> <- ser_len ------>
+ // bp bp+ser_len bp+(ser_len*2)
+ // olen,odata,ndata
+
+ // end of buffer would be the first byte of offsets: invalid.
+ EXPECT_THROW(LabelSequence(bp + serialized_len).
+ serialize(bp + 2, serialized_len),
+ isc::BadValue);
+ // begin of buffer would be the last byte of ndata: invalid.
+ EXPECT_THROW(LabelSequence(bp + serialized_len).
+ serialize(bp + (2 * serialized_len) - 1, serialized_len),
+ isc::BadValue);
+ // A boundary safe case: buffer is placed after the sequence data.
+ // should cause no disruption.
+ LabelSequence(bp + serialized_len).
+ serialize(bp + 2 * serialized_len, serialized_len);
+ // A boundary safe case: buffer is placed before the sequence data
+ // should cause no disruption. (but the original serialized data will
+ // be overridden, so it can't be used any more)
+ LabelSequence(bp + serialized_len).
+ serialize(bp + 1, serialized_len);
}
EXPECT_THROW(ls1.serialize(labels_buf, ls1.getSerializedLength() - 1),
More information about the bind10-changes
mailing list