BIND 10 master, updated. b372a33a10fc7518f3dd76a4a94eeec1307eef46 [master] Merge branch 'trac2098'
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Sep 3 12:59:18 UTC 2012
The branch, master has been updated
via b372a33a10fc7518f3dd76a4a94eeec1307eef46 (commit)
via 83a400743d99693d21fe04636c4958c3d82399f8 (commit)
via 49aabed090bd58b4aa295af86f106adbe2d5492b (commit)
via 740e0113d51fe26a7683477e7e0e19d1390119eb (commit)
via e0bdb21d3bc3e0c4c23df61fe204bb6e35fa4057 (commit)
via 19e4b209bd92898239781336796e9dd339dde415 (commit)
via a92987802f0f8106a6c1e2fbf41adb53d8ba67fa (commit)
via cb4c5b31b47ea1062cb74077ff5788f192d60eac (commit)
via 8440ca544d013e4081c4e0762cdb88c9888c0af5 (commit)
via 04c06cecc291057718100127c381c8385aaa9e92 (commit)
via e8ec4c911eb69b969df42a60fb28d5f727b8a937 (commit)
via b166b6061c760ccef7fd83ec354f6338d88162ee (commit)
via 8d2f900a8a8db49df9d7ad6fb26c02c3235b22b0 (commit)
via 36baee1d22c2527174ac34f2c144854f43e0c5f0 (commit)
via b4bbfceb45dbfa2f7e55936d9601e642ea3f5583 (commit)
via 43243f323512a7ef589bf215f3c591741cb65c6b (commit)
via f8965029cc4090ed45d8a13fbd1500e9a45873aa (commit)
via a5996789406457639b70e5fa24678ae3dbe114d4 (commit)
via 87c9372f4978909cf97b471c8d70878ef30f94da (commit)
via a65a2f9e380d98b1e1306e50d53a3f54260abb82 (commit)
via d0fb65a669710690160128bfd9c73cc7fb74110d (commit)
via 0d8f1b7c378dc78b057df7e19481c845aa9afebb (commit)
via 4b07f9b77d647422b6c2f9c49fb8405467252e72 (commit)
via b08ec1b998593ef1cf4f4edabcfdfa10bd7afb5b (commit)
via 542d9e20cdeeeddfff2e986ad0e46d337a103341 (commit)
via acd239395130de74310f719d74fd1401029b6e79 (commit)
via 823275807bc226ca99f6c89284a037e1524e43b3 (commit)
via f16244fadd945f124477ac7b69bc9a928a68b9f8 (commit)
via 0e19763b34f647c61a14ee642f015cc5f80d1436 (commit)
via 068ecfa5b8908672e35209754b16b302f715fad7 (commit)
via 2c34181ad710911cf18ce0efe95a7af52a2c96b0 (commit)
via b350749a8455f99095609ba20f9fefdb7fe1aeee (commit)
via 949291a4b2404a704beadf14b9b8ceb11891c9b0 (commit)
from b31ee71173d901621f6200e3e57179e9e6e13d5e (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 b372a33a10fc7518f3dd76a4a94eeec1307eef46
Merge: b31ee71 83a4007
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Mon Sep 3 21:55:10 2012 +0900
[master] Merge branch 'trac2098'
-----------------------------------------------------------------------
Summary of changes:
src/lib/datasrc/memory/Makefile.am | 1 +
src/lib/datasrc/memory/benchmarks/Makefile.am | 7 +-
.../memory/benchmarks/rrset_render_bench.cc | 247 +++++++++
src/lib/datasrc/memory/domaintree.h | 35 ++
src/lib/datasrc/memory/rdata_serialization.cc | 4 +
src/lib/datasrc/memory/tests/Makefile.am | 1 +
.../datasrc/memory/tests/domaintree_unittest.cc | 52 ++
.../memory/tests/treenode_rrset_unittest.cc | 568 ++++++++++++++++++++
src/lib/datasrc/memory/treenode_rrset.cc | 356 ++++++++++++
src/lib/datasrc/memory/treenode_rrset.h | 270 ++++++++++
src/lib/dns/benchmarks/message_renderer_bench.cc | 6 +
src/lib/dns/benchmarks/oldmessagerenderer.cc | 8 +
src/lib/dns/benchmarks/oldmessagerenderer.h | 1 +
src/lib/dns/messagerenderer.h | 32 +-
src/lib/dns/rdatafields.cc | 1 +
15 files changed, 1570 insertions(+), 19 deletions(-)
create mode 100644 src/lib/datasrc/memory/benchmarks/rrset_render_bench.cc
create mode 100644 src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
create mode 100644 src/lib/datasrc/memory/treenode_rrset.cc
create mode 100644 src/lib/datasrc/memory/treenode_rrset.h
-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index ef46abe..b04647c 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/src/lib/datasrc/memory/Makefile.am
@@ -12,6 +12,7 @@ noinst_LTLIBRARIES = libdatasrc_memory.la
libdatasrc_memory_la_SOURCES = domaintree.h
libdatasrc_memory_la_SOURCES += rdataset.h rdataset.cc
+libdatasrc_memory_la_SOURCES += treenode_rrset.h treenode_rrset.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 += segment_object_holder.h
diff --git a/src/lib/datasrc/memory/benchmarks/Makefile.am b/src/lib/datasrc/memory/benchmarks/Makefile.am
index 81d5642..3328699 100644
--- a/src/lib/datasrc/memory/benchmarks/Makefile.am
+++ b/src/lib/datasrc/memory/benchmarks/Makefile.am
@@ -9,10 +9,13 @@ endif
CLEANFILES = *.gcno *.gcda
-noinst_PROGRAMS = rdata_reader_bench
+noinst_PROGRAMS = rdata_reader_bench rrset_render_bench
rdata_reader_bench_SOURCES = rdata_reader_bench.cc
-
rdata_reader_bench_LDADD = $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la
rdata_reader_bench_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
rdata_reader_bench_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+
+rrset_render_bench_SOURCES = rrset_render_bench.cc
+rrset_render_bench_LDADD = $(top_builddir)/src/lib/datasrc/memory/libdatasrc_memory.la
+rrset_render_bench_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
diff --git a/src/lib/datasrc/memory/benchmarks/rrset_render_bench.cc b/src/lib/datasrc/memory/benchmarks/rrset_render_bench.cc
new file mode 100644
index 0000000..bc418e8
--- /dev/null
+++ b/src/lib/datasrc/memory/benchmarks/rrset_render_bench.cc
@@ -0,0 +1,247 @@
+// 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 <bench/benchmark.h>
+
+#include <util/buffer.h>
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <dns/rrset.h>
+#include <dns/rrclass.h>
+#include <dns/masterload.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/zone_data.h>
+
+#include <boost/bind.hpp>
+
+#include <cassert>
+#include <vector>
+#include <sstream>
+
+#include <unistd.h>
+
+using std::vector;
+using namespace isc::bench;
+using namespace isc::datasrc::memory;
+using namespace isc::dns;
+
+namespace {
+// A simple benchmark just repeating rendering a given set of RRsets.
+class RRsetRenderBenchMark {
+public:
+ RRsetRenderBenchMark(const vector<ConstRRsetPtr>& rrsets,
+ MessageRenderer& renderer) :
+ rrsets_(rrsets), renderer_(renderer)
+ {}
+ unsigned int run() {
+ renderer_.clear();
+ vector<ConstRRsetPtr>::const_iterator it;
+ const vector<ConstRRsetPtr>::const_iterator it_end = rrsets_.end();
+ for (it = rrsets_.begin(); it != it_end; ++it) {
+ (*it)->toWire(renderer_);
+ }
+ return (1);
+ }
+private:
+ const vector<ConstRRsetPtr>& rrsets_;
+ MessageRenderer& renderer_;
+};
+
+// Builtin benchmark data. This is a list of RDATA (of RRs) in a response
+// from a root server for the query for "www.example.com" (as of this
+// implementation). We use a real world example to make the case practical.
+const char* const delegation_rrsets_txt =
+ // AUTHORITY SECTION (NS)
+ "com. 172800 IN NS a.gtld-servers.net.\n"
+ "com. 172800 IN NS b.gtld-servers.net.\n"
+ "com. 172800 IN NS c.gtld-servers.net.\n"
+ "com. 172800 IN NS d.gtld-servers.net.\n"
+ "com. 172800 IN NS e.gtld-servers.net.\n"
+ "com. 172800 IN NS f.gtld-servers.net.\n"
+ "com. 172800 IN NS g.gtld-servers.net.\n"
+ "com. 172800 IN NS h.gtld-servers.net.\n"
+ "com. 172800 IN NS i.gtld-servers.net.\n"
+ "com. 172800 IN NS j.gtld-servers.net.\n"
+ "com. 172800 IN NS k.gtld-servers.net.\n"
+ "com. 172800 IN NS l.gtld-servers.net.\n"
+ "com. 172800 IN NS m.gtld-servers.net.\n"
+ // AUTHORITY SECTION (DS)
+ "com. 86400 IN DS 30909 8 2 "
+ "E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CFC41A5766\n"
+ // AUTHORITY SECTION (RRSIG for DS)
+ "com. 86400 IN RRSIG DS 8 1 86400 20120822000000 20120814230000 50398 . "
+ "lcIpLRq4s91Fh1FihDXiDvVMMRqgy2jjlpiP4Y6sSjIrLue6Boi7xraj"
+ "Ouka34ubpl4KuIcopWe99LI/7Npvq0MYr9DaqfnX9dTW6Vc2C7/hKSsz"
+ "POYjraZZA3SCApgfNVzq+AscYlShi56f1vm7DQWw1eh1wHLdatidrQwNyDo=\n"
+ // ADDITIONAL SECTION
+ "a.gtld-servers.net. 172800 IN A 192.5.6.30\n"
+ "b.gtld-servers.net. 172800 IN A 192.33.14.30\n"
+ "c.gtld-servers.net. 172800 IN A 192.26.92.30\n"
+ "d.gtld-servers.net. 172800 IN A 192.31.80.30\n"
+ "e.gtld-servers.net. 172800 IN A 192.12.94.30\n"
+ "f.gtld-servers.net. 172800 IN A 192.35.51.30\n"
+ "g.gtld-servers.net. 172800 IN A 192.42.93.30\n"
+ "h.gtld-servers.net. 172800 IN A 192.54.112.30\n"
+ "i.gtld-servers.net. 172800 IN A 192.43.172.30\n"
+ "j.gtld-servers.net. 172800 IN A 192.48.79.30\n"
+ "k.gtld-servers.net. 172800 IN A 192.52.178.30\n"
+ "l.gtld-servers.net. 172800 IN A 192.41.162.30\n"
+ "m.gtld-servers.net. 172800 IN A 192.55.83.30\n"
+ "a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30\n"
+ "b.gtld-servers.net. 172800 IN AAAA 2001:503:231d::2:30";
+
+// Likewise, it's a response from a root server for www.example.notexistent.
+// An example that involves a few more RRSIGs.
+const char* const nxdomain_rrsets_txt =
+ // AUTHORITY SECTION (NS)
+ ". 86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2012082901 1800 900 604800 86400\n"
+ ". 86400 IN RRSIG SOA 8 0 86400 20120905000000 20120828230000 50398 . Wj6NgkA2TyJug9XfWYG/Cjh4d1JI99/Dv9S1TClbrhLdeMI4s9BEcBSA xdlPJN6L4uaupcYeEyRWGx3p0iq7VWFyjL4xy/+jXpUoHCgj0NlWa6eu C2litcnfcumTI97XJ5iod3ET+6vWnhwZQHDnz1fj4FuS8PPEJJ+Oy3M5 mcM=\n"
+ ". 86400 IN NSEC ac. NS SOA RRSIG NSEC DNSKEY\n"
+ ". 86400 IN RRSIG NSEC 8 0 86400 20120905000000 20120828230000 50398 . f8FEG1HzQtn/shZCdDgkB7TIpHAmH2hGlp5RubczukzD8XI3EGMVNoaf 2cUNPLpyhoBQkO1Rz+hANzI6Jkqq8PzhiQnLcW+y2Wl/BU2SaBQ2Otb2 5+VOOXux1veKaCZsRRU1VJnTUYguDayAgcS7BtB/rc7ez4nokkK98MA9 zcw=\n"
+ "no. 86400 IN NSEC np. NS RRSIG NSEC\n"
+ "no. 86400 IN RRSIG NSEC 8 1 86400 20120905000000 20120828230000 50398 . Z/E4hb6MMSpueGGjGoCWwnN2uKsQf88HPS1gbwVHBEhR+5eSn0BGqExs fQsjYL47SF/6pwMXjzmXCt8NPXXf9ImY/93GUuFL6j5OuL2MDIxt24MS u6hfxxiYTn+zF0dM8cn+UK5n1VEBB5JVJXf7FOr3OmaRLMD33Gl6yZJ/ l1E=";
+
+void
+usage() {
+ std::cerr << "Usage: rrset_render_bench [-n iterations]" << std::endl;
+ exit (1);
+}
+
+// Helper callback for masterLoad() used in main() to build test data.
+void
+setRRset(vector<ConstRRsetPtr>* rrsets, ConstRRsetPtr rrset) {
+ rrsets->push_back(rrset);
+}
+
+void
+buildZone(isc::util::MemorySegmentLocal& mem_sgmt,
+ ZoneData* zone_data, const vector<ConstRRsetPtr>& rrsets,
+ vector<ConstRRsetPtr>& rrsets_build)
+{
+ RdataEncoder encoder;
+
+ for (vector<ConstRRsetPtr>::const_iterator it = rrsets.begin();
+ it != rrsets.end();
+ ++it)
+ {
+ ConstRRsetPtr rrset = *it;
+ ZoneNode* node;
+ zone_data->insertName(mem_sgmt, rrset->getName(), &node);
+
+ // For simplicity, we assume that if the current RRset has a next
+ // one that is of type RRSIG, that RRSIG should be associated with
+ // the current RRset (which should be the case for our test data).
+ ConstRRsetPtr sig_rrset;
+ if ((it + 1) != rrsets.end() &&
+ (*(it + 1))->getType() == RRType::RRSIG()) {
+ sig_rrset = *(++it);
+ assert(it != rrsets.end()); // to be safe, and silence cppcheck
+ }
+ RdataSet* rdataset =
+ RdataSet::create(mem_sgmt, encoder, rrset, sig_rrset);
+ rdataset->next = node->getData();
+ node->setData(rdataset);
+
+ rrsets_build.push_back(
+ ConstRRsetPtr(new TreeNodeRRset(rrset->getClass(), node, rdataset,
+ true)));
+ }
+}
+}
+
+int
+main(int argc, char* argv[]) {
+ int ch;
+ int iteration = 100000;
+ while ((ch = getopt(argc, argv, "n:")) != -1) {
+ switch (ch) {
+ case 'n':
+ iteration = atoi(optarg);
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0) {
+ usage();
+ }
+
+ // Build test data. rrsets will consist of a list of RRsets corresponding
+ // to the text defined above.
+ vector<ConstRRsetPtr> delegation_rrsets;
+ std::stringstream rrsets_stream(delegation_rrsets_txt);
+ masterLoad(rrsets_stream, Name::ROOT_NAME(), RRClass::IN(),
+ boost::bind(setRRset, &delegation_rrsets, _1));
+
+ vector<ConstRRsetPtr> nxdomain_rrsets;
+ std::stringstream rrsets_stream2(nxdomain_rrsets_txt);
+ masterLoad(rrsets_stream2, Name::ROOT_NAME(), RRClass::IN(),
+ boost::bind(setRRset, &nxdomain_rrsets, _1));
+
+ // Build in-memory zone using RRsets constructed above, storing
+ // the same set of RRsets as TreeNodeRRsets in separate vectors.
+ // This code below is not 100% exception safe (for simplicity), but at
+ // least it shouldn't leak memory in normal cases.
+ isc::util::MemorySegmentLocal mem_sgmt;
+ ZoneData* zone_data = ZoneData::create(mem_sgmt, Name::ROOT_NAME());
+ vector<ConstRRsetPtr> delegation_treenode_rrsets;
+ buildZone(mem_sgmt, zone_data, delegation_rrsets,
+ delegation_treenode_rrsets);
+ vector<ConstRRsetPtr> nxdomain_treenode_rrsets;
+ buildZone(mem_sgmt, zone_data, nxdomain_rrsets,
+ nxdomain_treenode_rrsets);
+
+ // The benchmark test uses a message renderer. Create it now and keep
+ // using it throughout the test.
+ isc::util::OutputBuffer buffer(4096); // 4096 should be sufficiently large
+ MessageRenderer renderer;
+ renderer.setBuffer(&buffer);
+
+ std::cout << "Benchmark for rendering basic RRsets (delegation)"
+ << std::endl;
+ BenchMark<RRsetRenderBenchMark>(iteration,
+ RRsetRenderBenchMark(delegation_rrsets,
+ renderer));
+
+ std::cout << "Benchmark for rendering tree node RRsets (delegation)"
+ << std::endl;
+ BenchMark<RRsetRenderBenchMark>(iteration,
+ RRsetRenderBenchMark(
+ delegation_treenode_rrsets, renderer));
+
+ std::cout << "Benchmark for rendering basic RRsets (nxdomain)"
+ << std::endl;
+ BenchMark<RRsetRenderBenchMark>(iteration,
+ RRsetRenderBenchMark(nxdomain_rrsets,
+ renderer));
+
+ std::cout << "Benchmark for rendering tree node RRsets (nxdomain)"
+ << std::endl;
+ BenchMark<RRsetRenderBenchMark>(iteration,
+ RRsetRenderBenchMark(
+ nxdomain_treenode_rrsets, renderer));
+
+ // Cleanup, and memory leak check
+ ZoneData::destroy(mem_sgmt, zone_data, RRClass::IN());
+ assert(mem_sgmt.allMemoryDeallocated());
+
+ return (0);
+}
diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h
index 3ee1552..db2f7e6 100644
--- a/src/lib/datasrc/memory/domaintree.h
+++ b/src/lib/datasrc/memory/domaintree.h
@@ -226,6 +226,26 @@ public:
return (dns::LabelSequence(getLabelsData()));
}
+ /// \brief Return the absolute label sequence of the node.
+ ///
+ /// This method returns the label sequence corresponding to the full
+ /// name of the node; i.e. the entire name as it appears in the zone.
+ ///
+ /// It takes the (partial) name of the node itself, and extends it
+ /// with all upper nodes.
+ ///
+ /// \note Care must be taken with the buffer that is used here; this
+ /// method overwrites its data, so it should not be associated with
+ /// any other LabelSequence during the lifetime of the LabelSequence
+ /// returned by this method. See LabelSequence::extend(), which is used
+ /// by this method.
+ ///
+ /// \param buf A data buffer where the label sequence will be built.
+ /// The data in this buffer will be overwritten by this call.
+ /// \return A LabelSequence with the absolute name of this node.
+ isc::dns::LabelSequence getAbsoluteLabels(
+ uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const;
+
/// \brief Return the data stored in this node.
///
/// You should not delete the data, it is deleted when the tree is
@@ -531,6 +551,21 @@ DomainTreeNode<T>::getUpperNode() const {
}
template <typename T>
+isc::dns::LabelSequence
+DomainTreeNode<T>::getAbsoluteLabels(
+ uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const
+{
+ isc::dns::LabelSequence result(getLabels(), buf);
+ const DomainTreeNode<T>* upper = getUpperNode();
+ while (upper != NULL) {
+ result.extend(upper->getLabels(), buf);
+ upper = upper->getUpperNode();
+ }
+
+ return (result);
+}
+
+template <typename T>
const DomainTreeNode<T>*
DomainTreeNode<T>::abstractSuccessor(
typename DomainTreeNode<T>::DomainTreeNodePtr DomainTreeNode<T>::*left,
diff --git a/src/lib/datasrc/memory/rdata_serialization.cc b/src/lib/datasrc/memory/rdata_serialization.cc
index 841bc13..6ac18d0 100644
--- a/src/lib/datasrc/memory/rdata_serialization.cc
+++ b/src/lib/datasrc/memory/rdata_serialization.cc
@@ -248,6 +248,10 @@ public:
virtual void setTruncated() {}
virtual void setLengthLimit(size_t) {}
virtual void setCompressMode(CompressMode) {}
+ virtual void writeName(const LabelSequence&, bool) {
+ // We don't need this version of writeName
+ isc_throw(Unexpected, "unexpected version of writeName is called");
+ }
// Called for each domain name in the RDATA, from the RDATA's toWire()
// implementation.
diff --git a/src/lib/datasrc/memory/tests/Makefile.am b/src/lib/datasrc/memory/tests/Makefile.am
index 22fd4bf..334094d 100644
--- a/src/lib/datasrc/memory/tests/Makefile.am
+++ b/src/lib/datasrc/memory/tests/Makefile.am
@@ -21,6 +21,7 @@ run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += rdata_serialization_unittest.cc
run_unittests_SOURCES += rdataset_unittest.cc
run_unittests_SOURCES += domaintree_unittest.cc
+run_unittests_SOURCES += treenode_rrset_unittest.cc
run_unittests_SOURCES += zone_table_unittest.cc
run_unittests_SOURCES += zone_data_unittest.cc
run_unittests_SOURCES += memory_segment_test.h
diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc
index ab6cb1e..3d1ab56 100644
--- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc
+++ b/src/lib/datasrc/memory/tests/domaintree_unittest.cc
@@ -114,6 +114,7 @@ protected:
TestDomainTree& dtree_expose_empty_node;
TestDomainTreeNode* dtnode;
const TestDomainTreeNode* cdtnode;
+ uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH];
};
TEST_F(DomainTreeTest, nodeCount) {
@@ -470,6 +471,11 @@ TEST_F(DomainTreeTest, chainLevel) {
tree.find(node_name, &cdtnode, chain));
EXPECT_EQ(1, chain.getLevelCount());
+ // Check the name of the found node (should have '.' as both non-absolute
+ // and absolute name
+ EXPECT_EQ(".", cdtnode->getLabels().toText());
+ EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
+
/*
* Now creating a possibly deepest tree with MAX_LABELS levels.
* it should look like:
@@ -493,6 +499,12 @@ TEST_F(DomainTreeTest, chainLevel) {
EXPECT_EQ(TestDomainTree::EXACTMATCH,
tree.find(node_name, &cdtnode, found_chain));
EXPECT_EQ(i, found_chain.getLevelCount());
+
+ // The non-absolute name should only have the first label
+ EXPECT_EQ("a", cdtnode->getLabels().toText());
+ // But the absolute name should have all labels
+ EXPECT_EQ(node_name.toText(),
+ cdtnode->getAbsoluteLabels(buf).toText());
}
// Confirm the last inserted name has the possible maximum length with
@@ -1039,4 +1051,44 @@ TEST_F(DomainTreeTest, root) {
root.find(Name("example.com"), &cdtnode));
EXPECT_EQ(dtnode, cdtnode);
}
+
+TEST_F(DomainTreeTest, getAbsoluteLabels) {
+ // The full absolute names of the nodes in the tree
+ // with the addition of the explicit root node
+ const char* const domain_names[] = {
+ "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+ "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
+ // The names of the nodes themselves, as they end up in the tree
+ const char* const first_labels[] = {
+ "c", "b", "a", "x", "z", "g.h", "i", "o",
+ "j", "p", "q", "k"};
+
+ const int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
+ for (int i = 0; i < name_count; ++i) {
+ EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name(domain_names[i]),
+ &cdtnode));
+
+ // First make sure the names themselves are not absolute
+ const LabelSequence ls(cdtnode->getLabels());
+ EXPECT_EQ(first_labels[i], ls.toText());
+ EXPECT_FALSE(ls.isAbsolute());
+
+ // Now check the absolute names
+ const LabelSequence abs_ls(cdtnode->getAbsoluteLabels(buf));
+ EXPECT_EQ(Name(domain_names[i]).toText(), abs_ls.toText());
+ EXPECT_TRUE(abs_ls.isAbsolute());
+ }
+
+ // Explicitly add and find a root node, to see that getAbsoluteLabels
+ // also works when getLabels() already returns an absolute LabelSequence
+ dtree.insert(mem_sgmt_, Name("."), &dtnode);
+ dtnode->setData(new int(1));
+
+ EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("."), &cdtnode));
+
+ EXPECT_TRUE(cdtnode->getLabels().isAbsolute());
+ EXPECT_EQ(".", cdtnode->getLabels().toText());
+ EXPECT_TRUE(cdtnode->getAbsoluteLabels(buf).isAbsolute());
+ EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
+}
}
diff --git a/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc b/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
new file mode 100644
index 0000000..b95e706
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
@@ -0,0 +1,568 @@
+// 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/buffer.h>
+#include <util/memory_segment_local.h>
+
+#include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/zone_data.h>
+
+#include <util/unittests/wiredata.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+using std::vector;
+using std::string;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+using isc::util::unittests::matchWireData;
+using isc::util::OutputBuffer;
+
+namespace {
+
+class TreeNodeRRsetTest : public ::testing::Test {
+protected:
+ TreeNodeRRsetTest() :
+ rrclass_(RRClass::IN()),
+ origin_name_("example.com"), www_name_("www.example.com"),
+ wildcard_name_("*.example.com"), match_name_("match.example.com"),
+ ns_rrset_(textToRRset("example.com. 3600 IN NS ns.example.com.")),
+ a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1\n"
+ "www.example.com. 3600 IN A 192.0.2.2")),
+ aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA "
+ "2001:db8::1\n")),
+ dname_rrset_(textToRRset("example.com. 3600 IN DNAME d.example.org.")),
+ a_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG "
+ "A 5 2 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE")),
+ aaaa_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG AAAA 5 2"
+ " 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE\n"
+ "www.example.com. 3600 IN RRSIG AAAA 5 2"
+ " 3600 20120814220826 20120715220826 "
+ "4321 example.com. FAKE\n")),
+ txt_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG TXT 5 2"
+ " 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE\n")),
+ wildmatch_rrset_(textToRRset(
+ "match.example.com. 3600 IN A 192.0.2.1\n"
+ "match.example.com. 3600 IN A 192.0.2.2")),
+ wildmatch_rrsig_rrset_(textToRRset(
+ "match.example.com. 3600 IN RRSIG "
+ "A 5 2 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE")),
+ zone_data_(NULL), origin_node_(NULL), www_node_(NULL),
+ wildcard_node_(NULL), ns_rdataset_(NULL), dname_rdataset_(NULL),
+ a_rdataset_(NULL), aaaa_rdataset_(NULL), rrsig_only_rdataset_(NULL),
+ wildcard_rdataset_(NULL)
+ {}
+ void SetUp() {
+ // We create some common test data here in SetUp() so it will be
+ // as exception safe as possible.
+
+ zone_data_ = ZoneData::create(mem_sgmt_, origin_name_);
+
+ zone_data_->insertName(mem_sgmt_, origin_name_, &origin_node_);
+ ns_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, ns_rrset_,
+ ConstRRsetPtr());
+ origin_node_->setData(ns_rdataset_);
+ dname_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, dname_rrset_,
+ ConstRRsetPtr());
+ ns_rdataset_->next = dname_rdataset_;
+
+ zone_data_->insertName(mem_sgmt_, www_name_, &www_node_);
+ a_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ a_rrsig_rrset_);
+ www_node_->setData(a_rdataset_);
+
+ aaaa_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_,
+ aaaa_rrsig_rrset_);
+ a_rdataset_->next = aaaa_rdataset_;
+
+ // A rare (half broken) case of RRSIG-only set
+ rrsig_only_rdataset_ = RdataSet::create(mem_sgmt_, encoder_,
+ ConstRRsetPtr(),
+ txt_rrsig_rrset_);
+ aaaa_rdataset_->next = rrsig_only_rdataset_;
+
+ zone_data_->insertName(mem_sgmt_, wildcard_name_, &wildcard_node_);
+ wildcard_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ a_rrsig_rrset_);
+ wildcard_node_->setData(wildcard_rdataset_);
+ }
+ void TearDown() {
+ ZoneData::destroy(mem_sgmt_, zone_data_, rrclass_);
+ // detect any memory leak
+ EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+ }
+
+ const RRClass rrclass_;
+ const Name origin_name_, www_name_, wildcard_name_, match_name_;
+ isc::util::MemorySegmentLocal mem_sgmt_;
+ RdataEncoder encoder_;
+ MessageRenderer renderer_, renderer_expected_;
+ ConstRRsetPtr ns_rrset_, a_rrset_, aaaa_rrset_, dname_rrset_,
+ a_rrsig_rrset_, aaaa_rrsig_rrset_, txt_rrsig_rrset_,
+ wildmatch_rrset_, wildmatch_rrsig_rrset_;
+ ZoneData* zone_data_;
+ ZoneNode* origin_node_;
+ ZoneNode* www_node_;
+ ZoneNode* wildcard_node_;
+ RdataSet* ns_rdataset_;
+ RdataSet* dname_rdataset_;
+ RdataSet* a_rdataset_;
+ RdataSet* aaaa_rdataset_;
+ RdataSet* rrsig_only_rdataset_;
+ RdataSet* wildcard_rdataset_; // for wildcard (type doesn't matter much)
+};
+
+// Check some trivial fields of a constructed TreeNodeRRset (passed as
+// AbstractRRset as we'd normally use it in polymorphic way).
+// Other complicated fields are checked through rendering tests.
+void
+checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name,
+ const RRClass& expected_class, const RRType& expected_type,
+ size_t expected_rdatacount, size_t expected_sigcount)
+{
+ EXPECT_EQ(expected_name, actual_rrset.getName());
+ EXPECT_EQ(expected_class, actual_rrset.getClass());
+ EXPECT_EQ(expected_type, actual_rrset.getType());
+ EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
+ EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
+}
+
+TEST_F(TreeNodeRRsetTest, create) {
+ // Constructed with RRSIG, and it should be visible.
+ checkBasicFields(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true),
+ www_name_, rrclass_, RRType::A(), 2, 1);
+ // Constructed with RRSIG, and it should be invisible.
+ checkBasicFields(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false),
+ www_name_, rrclass_, RRType::A(), 2, 0);
+ // Constructed without RRSIG, and it would be visible (but of course won't)
+ checkBasicFields(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_, true),
+ origin_name_, rrclass_, RRType::NS(), 1, 0);
+ // Constructed without RRSIG, and it should be visible
+ checkBasicFields(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_,
+ false),
+ origin_name_, rrclass_, RRType::NS(), 1, 0);
+ // RRSIG-only case (note the RRset's type is covered type)
+ checkBasicFields(TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ true),
+ www_name_, rrclass_, RRType::TXT(), 0, 1);
+ // RRSIG-only case (note the RRset's type is covered type), but it's
+ // invisible
+ checkBasicFields(TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ false),
+ www_name_, rrclass_, RRType::TXT(), 0, 0);
+ // Wildcard substitution
+ checkBasicFields(TreeNodeRRset(match_name_, rrclass_,
+ wildcard_node_, wildcard_rdataset_,
+ true),
+ match_name_, rrclass_, RRType::A(), 2, 1);
+}
+
+// The following two templated functions are helper to encapsulate the
+// concept truncation and handle MessageRenderer and OutputBuffer transparently
+// in templated test cases.
+template <typename OutputType>
+void
+setOutputLengthLimit(OutputType& output, size_t len_limit) {
+ output.setLengthLimit(len_limit);
+}
+template <>
+void
+setOutputLengthLimit<OutputBuffer>(OutputBuffer&, size_t) {
+}
+
+template <typename OutputType>
+bool
+isOutputTruncated(OutputType& output) {
+ return (output.isTruncated());
+}
+template <>
+bool
+isOutputTruncated<OutputBuffer>(OutputBuffer&) {
+ return (false);
+}
+
+// Templated so we so can support OutputBuffer version of toWire().
+// We use the above helper templated functions for some renderer only methods.
+// We test two sets of cases: normal rendering case and case when truncation
+// is expected. The latter is effectively for MessageRenderer only.
+// If len_limit == 0, we consider it the normal case; otherwise it's for
+// truncation. prepended_name isn't used for the truncation case.
+template <typename OutputType>
+void
+checkToWireResult(OutputType& expected_output, OutputType& actual_output,
+ const AbstractRRset& actual_rrset,
+ const Name& prepended_name,
+ ConstRRsetPtr rrset, ConstRRsetPtr rrsig_rrset,
+ bool dnssec_ok,
+ size_t len_limit = 0,
+ size_t expected_result = 0)
+{
+ expected_output.clear();
+ actual_output.clear();
+
+ if (len_limit == 0) { // normal rendering
+ // Prepare "actual" rendered data. We prepend a name to confirm the
+ // owner name should be compressed in both cases.
+ prepended_name.toWire(actual_output);
+ const size_t rdata_count = rrset ? rrset->getRdataCount() : 0;
+ const int expected_ret = (dnssec_ok && rrsig_rrset) ?
+ rdata_count + rrsig_rrset->getRdataCount() : rdata_count;
+ EXPECT_EQ(expected_ret, actual_rrset.toWire(actual_output));
+ } else { // truncation
+ setOutputLengthLimit(actual_output, len_limit);
+ EXPECT_EQ(expected_result, actual_rrset.toWire(actual_output));
+ EXPECT_TRUE(isOutputTruncated(actual_output)); // always true here
+ }
+
+ // Prepare "expected" data.
+ if (len_limit == 0) { // normal rendering
+ prepended_name.toWire(expected_output);
+ } else { // truncation
+ setOutputLengthLimit(expected_output, len_limit);
+ }
+ if (rrset) {
+ rrset->toWire(expected_output);
+ }
+ if (!isOutputTruncated(expected_output) && dnssec_ok && rrsig_rrset) {
+ rrsig_rrset->toWire(expected_output);
+ }
+
+ // Compare the two.
+ matchWireData(expected_output.getData(), expected_output.getLength(),
+ actual_output.getData(), actual_output.getLength());
+}
+
+TEST_F(TreeNodeRRsetTest, toWire) {
+ MessageRenderer expected_renderer, actual_renderer;
+ OutputBuffer expected_buffer(0), actual_buffer(0);
+
+ {
+ SCOPED_TRACE("with RRSIG, DNSSEC OK");
+ const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ www_name_, a_rrset_, a_rrsig_rrset_, true);
+ // Currently the buffer version throws
+ EXPECT_THROW(
+ checkToWireResult(expected_buffer, actual_buffer, rrset,
+ www_name_, a_rrset_, a_rrsig_rrset_, true),
+ isc::Unexpected);
+ }
+
+ {
+ SCOPED_TRACE("with RRSIG, DNSSEC not OK");
+ const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, false);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ www_name_, a_rrset_, a_rrsig_rrset_, false);
+ }
+
+ {
+ SCOPED_TRACE("without RRSIG, DNSSEC OK");
+ const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_, true);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ origin_name_, ns_rrset_, ConstRRsetPtr(), true);
+ }
+
+ {
+ SCOPED_TRACE("without RRSIG, DNSSEC not OK");
+ const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_,
+ false);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ origin_name_, ns_rrset_, ConstRRsetPtr(), false);
+ }
+
+ {
+ // RDATA of DNAME DR shouldn't be compressed. Prepending "example.org"
+ // will check that.
+ SCOPED_TRACE("uncompressed RDATA");
+ const TreeNodeRRset rrset(rrclass_, origin_node_, dname_rdataset_,
+ false);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ Name("example.org"), dname_rrset_, ConstRRsetPtr(),
+ false);
+ }
+
+ {
+ SCOPED_TRACE("wildcard with RRSIG");
+ checkToWireResult(expected_renderer, actual_renderer,
+ TreeNodeRRset(match_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, true),
+ origin_name_, wildmatch_rrset_,
+ wildmatch_rrsig_rrset_, true);
+ }
+
+ {
+ SCOPED_TRACE("wildcard without RRSIG");
+ checkToWireResult(expected_renderer, actual_renderer,
+ TreeNodeRRset(match_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, false),
+ origin_name_, wildmatch_rrset_,
+ wildmatch_rrsig_rrset_, false);
+ }
+
+ {
+ // Very unusual case: the set only contains RRSIG (already rare)
+ // and it's requested to be dumped to wire (can only happen in
+ // ANY or type-RRSIG queries, which are rare also). But can still
+ // happen.
+ SCOPED_TRACE("RRSIG only, DNSSEC OK");
+ const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
+ true);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,true);
+ }
+
+ {
+ // Similar to the previous case, but DNSSEC records aren't requested.
+ // In practice this case wouldn't happen, but API-wise possible, so
+ // we test it explicitly.
+ SCOPED_TRACE("RRSIG only, DNSSEC not OK");
+ const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
+ false);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,false);
+ }
+}
+
+TEST_F(TreeNodeRRsetTest, toWireTruncated) {
+ MessageRenderer expected_renderer, actual_renderer;
+ // dummy parameter to checkToWireResult (unused for the this test case)
+ const Name& name = Name::ROOT_NAME();
+
+ // Set the truncation limit to name len + 14 bytes of fixed data for A RR
+ // (type, class, TTL, rdlen, and 4-byte IPv4 address). Then we can only
+ // render just one RR, without any garbage trailing data.
+ checkToWireResult(expected_renderer, actual_renderer,
+ TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true),
+ name, a_rrset_, a_rrsig_rrset_, true,
+ www_name_.getLength() + 14,
+ 1); // 1 main RR, no RRSIG
+
+ // The first main RRs should fit in the renderer (the name will be
+ // fully compressed, so its size is 2 bytes), but the RRSIG doesn't.
+ checkToWireResult(expected_renderer, actual_renderer,
+ TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true),
+ name, a_rrset_, a_rrsig_rrset_, true,
+ www_name_.getLength() + 14 + 2 + 14,
+ 2); // 2 main RR, no RRSIG
+
+ // This RRset has one main RR and two RRSIGs. Rendering the second RRSIG
+ // causes truncation.
+ // First, compute the rendered length for the main RR and a single RRSIG.
+ // The length of the RRSIG should be the same if we "accidentally"
+ // rendered the RRSIG for the A RR (which only contains one RRSIG).
+ expected_renderer.clear();
+ aaaa_rrset_->toWire(expected_renderer);
+ a_rrsig_rrset_->toWire(expected_renderer);
+ const size_t limit_len = expected_renderer.getLength();
+ // Then perform the test
+ checkToWireResult(expected_renderer, actual_renderer,
+ TreeNodeRRset(rrclass_, www_node_, aaaa_rdataset_, true),
+ name, aaaa_rrset_, aaaa_rrsig_rrset_, true, limit_len,
+ 2); // 1 main RR, 1 RRSIG
+
+ // RRSIG only case. Render length limit being 1, so it won't fit,
+ // and will cause truncation.
+ checkToWireResult(expected_renderer, actual_renderer,
+ TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ true),
+ name, ConstRRsetPtr(), txt_rrsig_rrset_, true, 1,
+ 0); // no RR
+}
+
+void
+checkRdataIterator(const vector<string>& expected, RdataIteratorPtr rit) {
+ for (vector<string>::const_iterator it = expected.begin();
+ it != expected.end();
+ ++it)
+ {
+ ASSERT_FALSE(rit->isLast());
+ EXPECT_EQ(*it, rit->getCurrent().toText());
+ rit->next();
+ }
+ // We should have reached the end of RDATA
+ EXPECT_TRUE(rit->isLast());
+
+ // move to the first RDATA again, and check the value.
+ rit->first();
+ if (!expected.empty()) {
+ EXPECT_EQ(expected[0], rit->getCurrent().toText());
+ } else {
+ EXPECT_TRUE(rit->isLast());
+ }
+}
+
+TEST_F(TreeNodeRRsetTest, getRdataIterator) {
+ // This RRset should have 2 A RDATAs
+ vector<string> expected;
+ expected.push_back("192.0.2.1");
+ expected.push_back("192.0.2.2");
+ checkRdataIterator(expected,
+ TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true).
+ getRdataIterator());
+
+ // The iterator shouldn't work different with or without RRSIG
+ checkRdataIterator(expected,
+ TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false).
+ getRdataIterator());
+
+ // This RRset should have 1 NS RDATA (containing name field)
+ expected.clear();
+ expected.push_back("ns.example.com.");
+ checkRdataIterator(expected,
+ TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_,
+ false).getRdataIterator());
+
+ // RRSIG only. Iterator will be empty and shouldn't cause any disruption.
+ expected.clear();
+ checkRdataIterator(expected,
+ TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ true).getRdataIterator());
+}
+
+void
+checkToText(const AbstractRRset& actual_rrset,
+ ConstRRsetPtr expected_rrset, ConstRRsetPtr expected_sig_rrset)
+{
+ const string actual_text = actual_rrset.toText();
+ const string expected_text =
+ (expected_rrset ? expected_rrset->toText() : "") +
+ (expected_sig_rrset ? expected_sig_rrset->toText() : "");
+ EXPECT_EQ(expected_text, actual_text);
+}
+
+TEST_F(TreeNodeRRsetTest, toText) {
+ // Constructed with RRSIG, and it should be visible.
+ checkToText(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true),
+ a_rrset_, a_rrsig_rrset_);
+ // Constructed with RRSIG, and it should be invisible.
+ checkToText(TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false),
+ a_rrset_, ConstRRsetPtr());
+ // Constructed without RRSIG, and it would be visible (but of course won't)
+ checkToText(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_, true),
+ ns_rrset_, ConstRRsetPtr());
+ // Constructed without RRSIG, and it should be visible
+ checkToText(TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_, false),
+ ns_rrset_, ConstRRsetPtr());
+ // Wildcard expanded name with RRSIG
+ checkToText(TreeNodeRRset(match_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, true),
+ wildmatch_rrset_, wildmatch_rrsig_rrset_);
+ // Wildcard expanded name without RRSIG
+ checkToText(TreeNodeRRset(match_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, false),
+ wildmatch_rrset_, ConstRRsetPtr());
+ // RRSIG case
+ checkToText(TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ true),
+ ConstRRsetPtr(), txt_rrsig_rrset_);
+ // Similar to the previous case, but completely empty.
+ checkToText(TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ false),
+ ConstRRsetPtr(), ConstRRsetPtr());
+}
+
+TEST_F(TreeNodeRRsetTest, isSameKind) {
+ const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+
+ // Same name (node), same type (rdataset) => same kind
+ EXPECT_TRUE(rrset.isSameKind(TreeNodeRRset(rrclass_, www_node_,
+ a_rdataset_, true)));
+
+ // Same name (node), different type (rdataset) => not same kind
+ EXPECT_FALSE(rrset.isSameKind(TreeNodeRRset(rrclass_, www_node_,
+ aaaa_rdataset_, true)));
+
+ // Different name, different type => not same kind
+ EXPECT_FALSE(rrset.isSameKind(TreeNodeRRset(rrclass_, origin_node_,
+ ns_rdataset_, true)));
+
+ // Different name, same type => not same kind.
+ // Note: this shouldn't happen in our in-memory data source implementation,
+ // but API doesn't prohibit it.
+ EXPECT_FALSE(rrset.isSameKind(TreeNodeRRset(rrclass_, origin_node_,
+ a_rdataset_, true)));
+
+ // Wildcard and expanded RRset
+ const TreeNodeRRset wildcard_rrset(rrclass_, wildcard_node_,
+ wildcard_rdataset_, true);
+ const TreeNodeRRset match_rrset(match_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, true);
+ EXPECT_FALSE(wildcard_rrset.isSameKind(match_rrset));
+ EXPECT_FALSE(match_rrset.isSameKind(wildcard_rrset));
+
+ // Both are wildcard expanded, and have different names
+ const TreeNodeRRset match2_rrset(Name("match2.example.com"), rrclass_,
+ wildcard_node_, wildcard_rdataset_, true);
+ EXPECT_FALSE(match_rrset.isSameKind(match2_rrset));
+ EXPECT_FALSE(match2_rrset.isSameKind(match_rrset));
+
+ // Pathological case. "badwild" is constructed as if expanded due to
+ // a wildcard, but has the same owner name of the wildcard itself.
+ // Technically, they should be considered of the same kind, but this
+ // implementation considers they are not. But this case shouldn't happen
+ // as long as the RRsets are only constructed inside the in-memory
+ // zone finder implementation.
+ const TreeNodeRRset badwild_rrset(wildcard_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, true);
+ EXPECT_FALSE(wildcard_rrset.isSameKind(badwild_rrset));
+ EXPECT_EQ(wildcard_rrset.toText(), badwild_rrset.toText());
+
+ // Pathological case: Same name, same type, but different class.
+ // This case should be impossible because if the RRsets share the same
+ // tree node, they must belong to the same RR class. This case is
+ // a caller's bug, and the isSameKind() implementation returns the
+ // "wrong" (= true) answer.
+ EXPECT_TRUE(rrset.isSameKind(TreeNodeRRset(RRClass::CH(), www_node_,
+ a_rdataset_, true)));
+
+ // Same kind of different RRset class
+ EXPECT_TRUE(rrset.isSameKind(*a_rrset_));
+
+ // Different kind of different RRset class
+ EXPECT_FALSE(rrset.isSameKind(*aaaa_rrset_));
+}
+
+TEST_F(TreeNodeRRsetTest, unexpectedMethods) {
+ // Note: buffer version of toWire() is checked in the toWire test.
+
+ TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+
+ EXPECT_THROW(rrset.getTTL(), isc::Unexpected);
+ EXPECT_THROW(rrset.setTTL(RRTTL(0)), isc::Unexpected);
+ EXPECT_THROW(rrset.setName(Name("example")), isc::Unexpected);
+ EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
+ isc::Unexpected);
+ EXPECT_THROW(rrset.getRRsig(), isc::Unexpected);
+ RdataPtr sig_rdata = createRdata(
+ RRType::RRSIG(), rrclass_,
+ "A 5 2 3600 20120814220826 20120715220826 5300 example.com. FAKE");
+ EXPECT_THROW(rrset.addRRsig(sig_rdata), isc::Unexpected);
+ EXPECT_THROW(rrset.addRRsig(*a_rrsig_rrset_), isc::Unexpected);
+ EXPECT_THROW(rrset.addRRsig(a_rrsig_rrset_), isc::Unexpected);
+ EXPECT_THROW(rrset.addRRsig(RRsetPtr()), isc::Unexpected);
+ EXPECT_THROW(rrset.removeRRsig(), isc::Unexpected);
+}
+}
diff --git a/src/lib/datasrc/memory/treenode_rrset.cc b/src/lib/datasrc/memory/treenode_rrset.cc
new file mode 100644
index 0000000..5c333ff
--- /dev/null
+++ b/src/lib/datasrc/memory/treenode_rrset.cc
@@ -0,0 +1,356 @@
+// 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/buffer.h>
+
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+
+#include "treenode_rrset.h"
+#include "rdata_serialization.h"
+
+#include <boost/bind.hpp>
+
+#include <cassert>
+#include <string>
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+const Name&
+TreeNodeRRset::getName() const {
+ if (realname_ != NULL) {
+ return (*realname_);
+ }
+ if (name_ == NULL) {
+ uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+ const LabelSequence name_labels = getOwnerLabels(labels_buf);
+ size_t data_len;
+ const uint8_t* data = name_labels.getData(&data_len);
+ util::InputBuffer buffer(data, data_len);
+ name_ = new Name(buffer);
+ }
+ return (*name_);
+}
+
+const RRTTL&
+TreeNodeRRset::getTTL() const {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::setName(const Name&) {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::setTTL(const RRTTL&) {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+std::string
+TreeNodeRRset::toText() const {
+ // Create TTL from internal data
+ util::InputBuffer ttl_buffer(rdataset_->getTTLData(), sizeof(uint32_t));
+ const RRTTL ttl(ttl_buffer);
+
+ // Dump the main RRset, if not empty
+ std::string ret;
+ RRsetPtr tmp_rrset;
+ for (RdataIteratorPtr rit = getRdataIterator();
+ !rit->isLast();
+ rit->next())
+ {
+ if (!tmp_rrset) {
+ tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_, getType(),
+ ttl));
+ }
+ tmp_rrset->addRdata(rit->getCurrent());
+ }
+ if (tmp_rrset) {
+ ret = tmp_rrset->toText();
+ }
+
+ // Dump any RRSIGs
+ tmp_rrset.reset();
+ for (RdataIteratorPtr rit = getSigRdataIterator();
+ !rit->isLast();
+ rit->next())
+ {
+ if (!tmp_rrset) {
+ tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_,
+ RRType::RRSIG(), ttl));
+ }
+ tmp_rrset->addRdata(rit->getCurrent());
+ }
+ if (tmp_rrset) {
+ ret += tmp_rrset->toText();
+ }
+
+ return (ret);
+}
+
+namespace {
+void
+renderName(const LabelSequence& name_labels, RdataNameAttributes attr,
+ AbstractMessageRenderer* renderer)
+{
+ renderer->writeName(name_labels, (attr & NAMEATTR_COMPRESSIBLE) != 0);
+}
+
+void
+renderData(const void* data, size_t data_len,
+ AbstractMessageRenderer* renderer)
+{
+ renderer->writeData(data, data_len);
+}
+
+// Common code logic for rendering a single (either main or RRSIG) RRset.
+size_t
+writeRRs(AbstractMessageRenderer& renderer, size_t rr_count,
+ const LabelSequence& name_labels, const RRType& rrtype,
+ const RRClass& rrclass, const void* ttl_data,
+ RdataReader& reader, bool (RdataReader::* rdata_iterate_fn)())
+{
+ for (size_t i = 0; i < rr_count; ++i) {
+ const size_t pos0 = renderer.getLength();
+
+ // Name, type, class, TTL
+ renderer.writeName(name_labels, true);
+ rrtype.toWire(renderer);
+ rrclass.toWire(renderer);
+ renderer.writeData(ttl_data, sizeof(uint32_t));
+
+ // RDLEN and RDATA
+ const size_t pos = renderer.getLength();
+ renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
+ const bool rendered = (reader.*rdata_iterate_fn)();
+ assert(rendered == true);
+ renderer.writeUint16At(renderer.getLength() - pos - sizeof(uint16_t),
+ pos);
+
+ // Check if truncation would happen
+ if (renderer.getLength() > renderer.getLengthLimit()) {
+ renderer.trim(renderer.getLength() - pos0);
+ renderer.setTruncated();
+ return (i);
+ }
+ }
+ return (rr_count);
+}
+}
+
+unsigned int
+TreeNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
+ RdataReader reader(rrclass_, rdataset_->type, rdataset_->getDataBuf(),
+ rdataset_->getRdataCount(), rrsig_count_,
+ boost::bind(renderName, _1, _2, &renderer),
+ boost::bind(renderData, _1, _2, &renderer));
+
+ // Get the owner name of the RRset in the form of LabelSequence.
+ uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+ const LabelSequence name_labels = getOwnerLabels(labels_buf);
+
+ // Render the main (non RRSIG) RRs
+ const size_t rendered_rdata_count =
+ writeRRs(renderer, rdataset_->getRdataCount(), name_labels,
+ rdataset_->type, rrclass_, rdataset_->getTTLData(), reader,
+ &RdataReader::iterateRdata);
+ if (renderer.isTruncated()) {
+ return (rendered_rdata_count);
+ }
+ const bool rendered = reader.iterateRdata();
+ assert(rendered == false); // we should've reached the end
+
+ // Render any RRSIGs, if we supposed to do so
+ const size_t rendered_rrsig_count = dnssec_ok_ ?
+ writeRRs(renderer, rrsig_count_, name_labels, RRType::RRSIG(),
+ rrclass_, rdataset_->getTTLData(), reader,
+ &RdataReader::iterateSingleSig) : 0;
+
+ return (rendered_rdata_count + rendered_rrsig_count);
+}
+
+unsigned int
+TreeNodeRRset::toWire(isc::util::OutputBuffer&) const {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRdata(rdata::ConstRdataPtr) {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRdata(const rdata::Rdata&) {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+namespace {
+// In this namespace we define a set of helper stuff to implement the
+// RdataIterator for the TreeNodeRRset. We should eventually optimize
+// the code so that performance sensitive path won't require the iterator,
+// so, at the moment, the implementation is straightforward, but less
+// efficient one: It builds a vector of Rdata objects on construction,
+// and its getCurrent() returns the stored data.
+
+class TreeNodeRdataIterator : public RdataIterator {
+public:
+ TreeNodeRdataIterator(const std::vector<ConstRdataPtr>& rdata_list) :
+ rdata_list_(rdata_list), rdata_it_(rdata_list_.begin())
+ {}
+ virtual void first() { rdata_it_ = rdata_list_.begin(); }
+ virtual void next() {
+ ++rdata_it_;
+ }
+ virtual const rdata::Rdata& getCurrent() const {
+ return (**rdata_it_);
+ }
+ virtual bool isLast() const { return (rdata_it_ == rdata_list_.end()); }
+private:
+ const std::vector<ConstRdataPtr> rdata_list_;
+ std::vector<ConstRdataPtr>::const_iterator rdata_it_;
+};
+
+void
+renderNameToBuffer(const LabelSequence& name_labels, RdataNameAttributes,
+ util::OutputBuffer* buffer)
+{
+ size_t data_len;
+ const uint8_t *data = name_labels.getData(&data_len);
+ buffer->writeData(data, data_len);
+}
+
+void
+renderDataToBuffer(const void* data, size_t data_len,
+ util::OutputBuffer* buffer)
+{
+ buffer->writeData(data, data_len);
+}
+}
+
+RdataIteratorPtr
+TreeNodeRRset::getRdataIteratorInternal(bool is_rrsig, size_t count) const {
+ util::OutputBuffer buffer(0);
+ RdataReader reader(rrclass_, rdataset_->type, rdataset_->getDataBuf(),
+ rdataset_->getRdataCount(), rrsig_count_,
+ boost::bind(renderNameToBuffer, _1, _2, &buffer),
+ boost::bind(renderDataToBuffer, _1, _2, &buffer));
+
+ std::vector<ConstRdataPtr> rdata_list;
+ for (size_t i = 0; i < count; ++i) {
+ buffer.clear();
+ const bool rendered = is_rrsig ? reader.iterateSingleSig() :
+ reader.iterateRdata();
+ assert(rendered == true);
+ util::InputBuffer ib(buffer.getData(), buffer.getLength());
+ rdata_list.push_back(
+ createRdata(is_rrsig ? RRType::RRSIG() : rdataset_->type, rrclass_,
+ ib, ib.getLength()));
+ }
+ return (RdataIteratorPtr(new TreeNodeRdataIterator(rdata_list)));
+}
+
+RdataIteratorPtr
+TreeNodeRRset::getRdataIterator() const {
+ return (getRdataIteratorInternal(false, rdataset_->getRdataCount()));
+}
+
+RdataIteratorPtr
+TreeNodeRRset::getSigRdataIterator() const {
+ return (getRdataIteratorInternal(true, dnssec_ok_ ? rrsig_count_ : 0));
+}
+
+RRsetPtr
+TreeNodeRRset::getRRsig() const {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRRsig(const rdata::ConstRdataPtr&) {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRRsig(const rdata::RdataPtr&) {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRRsig(const AbstractRRset&) {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRRsig(const ConstRRsetPtr&) {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::addRRsig(const RRsetPtr&) {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+void
+TreeNodeRRset::removeRRsig() {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
+bool
+TreeNodeRRset::isSameKind(const AbstractRRset& abs_other) const {
+ const TreeNodeRRset* other =
+ dynamic_cast<const TreeNodeRRset*>(&abs_other);
+ if (other != NULL) {
+ // If type is different, they are not the same kind
+ if (rdataset_ != other->rdataset_) {
+ return (false);
+ }
+ // Same for the owner name. Comparing the nodes also detect
+ // the case where RR classes are different (see the method description
+ // of the header for details).
+ if (node_ != other->node_ ) {
+ return (false);
+ }
+ // If one is constructed with a "real name" and the other isn't
+ // *we consider* them different.
+ if ((realname_ == NULL && other->realname_ != NULL) ||
+ (realname_ != NULL && other->realname_ == NULL)) {
+ return (false);
+ }
+ // If both are constructed with a "real name", we compare their names
+ // explicitly.
+ if (realname_ != NULL && other->realname_ != NULL &&
+ realname_->nequals(*other->realname_)) {
+ return (false);
+ }
+ return (true);
+ }
+ return (AbstractRRset::isSameKind(abs_other));
+}
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/datasrc/memory/treenode_rrset.h b/src/lib/datasrc/memory/treenode_rrset.h
new file mode 100644
index 0000000..dd94245
--- /dev/null
+++ b/src/lib/datasrc/memory/treenode_rrset.h
@@ -0,0 +1,270 @@
+// 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_TREENODE_RRSET_H
+#define DATASRC_MEMORY_TREENODE_RRSET_H 1
+
+#include <util/buffer.h>
+
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/rdataset.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <string>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief Special RRset for optimizing memory datasource requirement
+///
+/// This is a derived class of \c dns::AbstractRRset intended to be used
+/// by the in-memory data source finder implementation. It is designed
+/// so performance sensitive operations will be lightweight; for example,
+/// (in the general case) the construction is just set up references to
+/// pre-loaded in-memory objects, not involving any dynamic memory allocation.
+/// Its \c toWire() method is also customized so it won't have to use
+/// the generic but expensive \c dns::RdataIterator.
+///
+/// On the other hand, some other performance-insensitive methods could be
+/// even less efficient than the generic version. Those include \c getName(),
+/// \c toText(), and \c getRdataIterator() methods.
+///
+/// \note Right now, the authoritative server's query processing still needs
+/// to use \c getRdataIterator() and \c getName() for relatively rare case
+/// operations. We should revise that part of the authoritative server
+/// implementation in the next phase in order to eliminate the bottleneck.
+///
+/// Since this class is assumed to be instantiated only from the in-memory
+/// zone finder, which only returns immutable (const) \c RRset objects,
+/// we skip implementing non const virtual methods of this class.
+/// Unless the application intentionally breaks the constness or the class
+/// is abused outside of the in-memory data source implementation, this
+/// should be safe because such methods should never be called.
+///
+/// Some other const member methods are still incomplete; if they are called
+/// it will result in an exception. In the expected usage of this class
+/// it should be safe, but we should eventually provide complete
+/// implementations of these methods.
+///
+/// This class can internally maintain dynamically allocated resource.
+/// It would cause copying a class object complicated while objects of
+/// this class are not expected to be copyable in the usage, so it's
+/// explicitly defined non copyable.
+///
+/// \note This class is exposed in this separate header file so that other
+/// part of the in-memory data source implementation and test code
+/// can refer to its definition, and only for that purpose. Otherwise this is
+/// essentially a private class of the in-memory data source implementation,
+/// and an application shouldn't directly refer to this class.
+class TreeNodeRRset : boost::noncopyable, public dns::AbstractRRset {
+public:
+ /// \brief Normal case constructor.
+ ///
+ /// This class object is basically defined with a \c ZoneNode and
+ /// \c RdataSet. The former determines the owner name of the RRset,
+ /// and the latter provides the rest of the RRset parameters.
+ /// Since the RR class is maintained outside of the \c ZoneData,
+ /// it must be explicitly given as a constructor parameter.
+ ///
+ /// The \c RdataSet may or may not be associated with RRSIGs. It's
+ /// fixed at the load time, but depending on the query context they
+ /// may or may not be requested (and supposed to be visible to the
+ /// caller). Since \c rdataset cannot be modified at the time of
+ /// construction, a separate parameter (\c dnssec_ok) controls this
+ /// policy. Any associated RRSIGs are visible if and only if \c dnssec_ok
+ /// is true. If the RRset is not associated with RRSIGs, the value
+ /// does not have any effect.
+ ///
+ /// In some rare cases \c rdataset may only consist of RRSIGs (when
+ /// the zone contains an RRSIG that doesn't have covered RRsets).
+ /// This class works for such cases, too.
+ ///
+ /// \throw none
+ ///
+ /// \param rrclass The RR class of the RRset. This must be consistent
+ /// with the corresponding zone class.
+ /// \param node The \c ZoneNode for the \c RRset. Must not be NULL.
+ /// \param rdataset The \c RdataSet for the \c RRset. Must not be NULL.
+ /// \param dnssec_ok Whether the RRSIGs for the RRset (if associated)
+ /// should be visible to the caller.
+ TreeNodeRRset(const dns::RRClass& rrclass, const ZoneNode* node,
+ const RdataSet* rdataset, bool dnssec_ok) :
+ node_(node), rdataset_(rdataset),
+ rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
+ dnssec_ok_(dnssec_ok), name_(NULL), realname_(NULL)
+ {}
+
+ /// \brief Constructor for wildcard-expanded owner name.
+ ///
+ /// This constructor is mostly the same as the other version, but takes
+ /// an extra parameter, \c realname. It effectively overrides the owner
+ /// name of the RRset; wherever the owner name is used (e.g., in the
+ /// \c toWire() method), the specified name will be used instead of
+ /// the name associated with \c node.
+ ///
+ /// The expected usage is \c node has a wildcard name (such as
+ /// *.example.com), but this constructor does not enforce the assumption.
+ ///
+ /// \throw std::bad_alloc Memory allocation fails
+ TreeNodeRRset(const dns::Name& realname, const dns::RRClass& rrclass,
+ const ZoneNode* node, const RdataSet* rdataset,
+ bool dnssec_ok) :
+ node_(node), rdataset_(rdataset),
+ rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
+ dnssec_ok_(dnssec_ok), name_(NULL), realname_(new dns::Name(realname))
+ {}
+
+ virtual ~TreeNodeRRset() {
+ delete realname_;
+ delete name_;
+ }
+
+ virtual unsigned int getRdataCount() const {
+ return (rdataset_->getRdataCount());
+ }
+
+ virtual const dns::Name& getName() const;
+ virtual const dns::RRClass& getClass() const {
+ return (rrclass_);
+ }
+
+ virtual const dns::RRType& getType() const {
+ return (rdataset_->type);
+ }
+
+ /// \brief Specialized version of \c getTTL() for \c TreeNodeRRset.
+ ///
+ /// It throws \c isc::Unexpected unconditionally.
+ virtual const dns::RRTTL& getTTL() const;
+
+ /// \brief Specialized version of \c setName() for \c TreeNodeRRset.
+ ///
+ /// It throws \c isc::Unexpected unconditionally.
+ virtual void setName(const dns::Name& name);
+
+ /// \brief Specialized version of \c setName() for \c TreeNodeRRset.
+ ///
+ /// It throws \c isc::Unexpected unconditionally.
+ virtual void setTTL(const dns::RRTTL& ttl);
+
+ virtual std::string toText() const;
+
+ virtual unsigned int toWire(dns::AbstractMessageRenderer& renderer) const;
+
+ /// \brief Specialized version of \c toWire(buffer) for \c TreeNodeRRset.
+ ///
+ /// It throws \c isc::Unexpected unconditionally.
+ virtual unsigned int toWire(util::OutputBuffer& buffer) const;
+
+ /// \brief Specialized version of \c addRdata() for \c TreeNodeRRset.
+ ///
+ /// It throws \c isc::Unexpected unconditionally.
+ virtual void addRdata(dns::rdata::ConstRdataPtr rdata);
+
+ /// \brief Specialized version of \c addRdata() for \c TreeNodeRRset.
+ ///
+ /// It throws \c isc::Unexpected unconditionally.
+ virtual void addRdata(const dns::rdata::Rdata& rdata);
+
+ virtual dns::RdataIteratorPtr getRdataIterator() const;
+
+ /// \brief Specialized version of \c getRRsig() for \c TreeNodeRRset.
+ ///
+ /// It throws \c isc::Unexpected unconditionally.
+ virtual dns::RRsetPtr getRRsig() const;
+
+ virtual unsigned int getRRsigDataCount() const {
+ return (dnssec_ok_ ? rrsig_count_ : 0);
+ }
+
+ ///
+ /// \name Specialized version of RRsig related methods for
+ /// \c TreeNodeRRset.
+ ///
+ /// These throw \c isc::Unexpected unconditionally.
+ ////
+ //@{
+ virtual void addRRsig(const dns::rdata::ConstRdataPtr& rdata);
+ virtual void addRRsig(const dns::rdata::RdataPtr& rdata);
+ virtual void addRRsig(const dns::AbstractRRset& sigs);
+ virtual void addRRsig(const dns::ConstRRsetPtr& sigs);
+ virtual void addRRsig(const dns::RRsetPtr& sigs);
+ virtual void removeRRsig();
+ //@}
+
+ /// \brief Specialized version of \c isSameKind() for \c TreeNodeRRset.
+ ///
+ /// As a kind of optimization, this implementation exploits the assumption
+ /// of how \c TreeNodeRRset objects are created: They must be always
+ /// created inside the in-memory data source finder implementation,
+ /// and they are constructed with the \c realname parameter if and only
+ /// if the corresponding query name is subject to wildcard substitution.
+ ///
+ /// So, if the given RRset is of \c TreeNodeRRset, and one and only one of
+ /// of them has \c realname, they are considered to have different names.
+ ///
+ /// Also, this implementation does not compare RR classes explicitly;
+ /// if two \c TreeNodeRRset objects belong to different RR classes,
+ /// they should belong to different zone trees (according to the assumption
+ /// of how the zone data are built), and therefore they cannot be at
+ /// same zone node. So it's sufficient to compare the (address of the)
+ /// node; if they are different they cannot be of the same kind.
+ virtual bool isSameKind(const dns::AbstractRRset& abs_other) const;
+
+private:
+ dns::RdataIteratorPtr getSigRdataIterator() const;
+
+ // Common backend for getRdataIterator() and getSigRdataIterator()
+ dns::RdataIteratorPtr getRdataIteratorInternal(bool is_rrsig,
+ size_t count) const;
+
+ // Return \c LabelSequence for the owner name regardless of how this
+ /// class is constructed (with or without 'realname')
+ dns::LabelSequence getOwnerLabels(
+ uint8_t labels_buf[dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const
+ {
+ if (realname_ != NULL) {
+ return (dns::LabelSequence(*realname_));
+ }
+ return (node_->getAbsoluteLabels(labels_buf));
+ }
+
+ const ZoneNode* node_;
+ const RdataSet* rdataset_;
+ const size_t rrsig_count_;
+ const dns::RRClass rrclass_;
+ const bool dnssec_ok_;
+ mutable dns::Name* name_;
+ const dns::Name* const realname_;
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_TREENODE_RRSET_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/benchmarks/message_renderer_bench.cc b/src/lib/dns/benchmarks/message_renderer_bench.cc
index cc2fc88..6376498 100644
--- a/src/lib/dns/benchmarks/message_renderer_bench.cc
+++ b/src/lib/dns/benchmarks/message_renderer_bench.cc
@@ -15,6 +15,7 @@
#include <bench/benchmark.h>
#include <dns/name.h>
+#include <dns/labelsequence.h>
#include <dns/messagerenderer.h>
#include <oldmessagerenderer.h>
@@ -115,6 +116,11 @@ public:
virtual void writeName(const Name& name, const bool = false) {
name.toWire(getBuffer());
}
+ virtual void writeName(const LabelSequence&, const bool) {
+ // We shouldn't use this version of writeName (and we internally
+ // control it, so we simply assert it here)
+ assert(false);
+ }
};
void
diff --git a/src/lib/dns/benchmarks/oldmessagerenderer.cc b/src/lib/dns/benchmarks/oldmessagerenderer.cc
index cd5c4e2..ed575a0 100644
--- a/src/lib/dns/benchmarks/oldmessagerenderer.cc
+++ b/src/lib/dns/benchmarks/oldmessagerenderer.cc
@@ -15,6 +15,7 @@
#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <dns/name.h>
+#include <dns/labelsequence.h>
#include <oldmessagerenderer.h>
#include <cctype>
@@ -274,5 +275,12 @@ OldMessageRenderer::writeName(const Name& name, const bool compress) {
}
}
+void
+OldMessageRenderer::writeName(const LabelSequence&, const bool) {
+ // We shouldn't use this version of writeName (and we internally
+ // control it, so we simply assert it here)
+ assert(false);
+}
+
}
}
diff --git a/src/lib/dns/benchmarks/oldmessagerenderer.h b/src/lib/dns/benchmarks/oldmessagerenderer.h
index 14e7aee..d408081 100644
--- a/src/lib/dns/benchmarks/oldmessagerenderer.h
+++ b/src/lib/dns/benchmarks/oldmessagerenderer.h
@@ -42,6 +42,7 @@ public:
virtual void setCompressMode(CompressMode mode);
virtual void clear();
virtual void writeName(const Name& name, bool compress = true);
+ virtual void writeName(const LabelSequence& labels, bool compress);
private:
struct MessageRendererImpl;
MessageRendererImpl* impl_;
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index d8f4634..4816ad5 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -332,8 +332,21 @@ public:
/// for compressing subsequent names.
///
/// \param name A \c Name object to be written.
- /// \param compress A boolean indicating whether to enable name compression.
+ /// \param compress A boolean indicating whether to enable name
+ /// compression.
virtual void writeName(const Name& name, bool compress = true) = 0;
+
+ /// \brief Write a \c LabelSequence object into the internal buffer
+ /// in wire format, with or without name compression.
+ ///
+ /// This is the same as the other version, which takes \c Name instead
+ /// of \c LabelSequence, except for the parameter type. The passed
+ /// \c LabelSequence must be absolute.
+ ///
+ /// \param ls A \c LabelSequence object to be written.
+ /// \param compress A boolean indicating whether to enable name
+ /// compression.
+ virtual void writeName(const LabelSequence& ls, bool compress = true) = 0;
//@}
};
@@ -376,22 +389,7 @@ public:
virtual void clear();
virtual void writeName(const Name& name, bool compress = true);
-
- /// \brief Write a \c LabelSequence object into the internal buffer
- /// in wire format, with or without name compression.
- ///
- /// If the optional parameter \c compress is \c true, this method tries to
- /// compress the \c ls if possible, searching the entire message that has
- /// been rendered. Otherwise name compression is omitted. Its default
- /// value is \c true.
- ///
- /// Note: even if \c compress is \c true, the position of the \c ls (and
- /// possibly its ancestor names) in the message is recorded and may be used
- /// for compressing subsequent names.
- ///
- /// \param ls A \c LabelSequence object to be written.
- /// \param compress A boolean indicating whether to enable name compression.
- void writeName(const LabelSequence& ls, bool compress = true);
+ virtual void writeName(const LabelSequence& ls, bool compress = true);
private:
struct MessageRendererImpl;
diff --git a/src/lib/dns/rdatafields.cc b/src/lib/dns/rdatafields.cc
index 94c9dbf..5737ef7 100644
--- a/src/lib/dns/rdatafields.cc
+++ b/src/lib/dns/rdatafields.cc
@@ -81,6 +81,7 @@ public:
virtual void setTruncated() { truncated_ = true; }
virtual void setLengthLimit(size_t len) { length_limit_ = len; }
virtual void setCompressMode(CompressMode mode) { mode_ = mode; }
+ virtual void writeName(const LabelSequence&, bool) {}
virtual void writeName(const Name& name, bool compress) {
extendData();
const RdataFields::Type field_type =
More information about the bind10-changes
mailing list