BIND 10 master, updated. 6e500e577f4fd8d6be953c15561f20c5ef1a784e Merge #2096
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Aug 28 09:48:56 UTC 2012
The branch, master has been updated
via 6e500e577f4fd8d6be953c15561f20c5ef1a784e (commit)
via 2497f5480351f20ebb4f1ae72e9dbfc36b9ea582 (commit)
via aa14f630842559ebe3fb682061a1be805cd23811 (commit)
via 50ab96b1412fe65d26184603e0a96650d9e9c6cc (commit)
via 5b27cee6f2b2ea3199700f2358ab38655c60e5eb (commit)
via b97f714d59f1990b62a01e5ca0c5f1c95834ed7c (commit)
via d9e1e79ffff5bf77517474d811ed7cfece349559 (commit)
via 220c3ff25ab6bb3e0cd4f4888bdfdaadc87a2738 (commit)
via db4962ad778838b6872fa1516a3e2e6c3e2579be (commit)
via 83979200b23f48735babe19b6359660222b2c525 (commit)
via a57188382f9187c8096f198cf2fbf281c5419f2b (commit)
via a22851178b9359cdb68c0284ca9945d325955ebe (commit)
via 70d316e7900864491e1a9ca83aaf2659a8f0aff1 (commit)
via fb6be89241c386c904d45fbd3135dec41caf30b8 (commit)
via 1e8e2101a48216215a50c548ee54c3cad4872aeb (commit)
via 9886e2d5b3f70e59d83af08db4ad00c4a8354a85 (commit)
via 88e51fd004645f182dd631fb42376d53b54cef80 (commit)
via 0a0497e8f5ff374f7488f084e228c321ac097abf (commit)
via bddce14fdd3f92879bad0fa6f88772e5575b7c02 (commit)
via 04c050cb0c48cfb1b4e0f0303c098e47d5da7c2b (commit)
via d2a9f546a9e5d436fcf0e5e02b12f459854f8fd1 (commit)
via 87830ae57ed3fd5da5567a30bf2a8c33132ba955 (commit)
via 8131de02fde6e3948a765a37b660e276d0afa146 (commit)
via 6bcd73aa1c0ef3f2ea13b6aa87f003db437d55f0 (commit)
via 45d9762c6dfed8746f361fa01700899947154363 (commit)
via fdd121bda3753d43f9e322d79b5ad6e8b49064e1 (commit)
via c49d19491e9d93a5908afe667beeba4bcfcc8af3 (commit)
via 909df7ac12941253014a4dafb33c4574989999da (commit)
via 6694e40b41ac8909ead7ec6e59bc23ca310573d7 (commit)
via 256771158ce922d32b0341cfb9109c83b4bbc45a (commit)
via fdb51ca68f97d1aedf06b91a48b8c96733e20bb0 (commit)
via 305f3328e78cb462a7586421cc862a9fe9f327c2 (commit)
via 57ebe0199af9aa5245bc6f7a7cf98c0f780a5b2c (commit)
via 4b92c8b2f217dcd9706d2e1ff02ba2550d7fd87e (commit)
via 2a95f12277a41b38f151660a6fc313e7a98f8e73 (commit)
via cd1a4d1452deaa95e341d867cde73b82ef8e3804 (commit)
via 11cfdc151d5d949cf8c6fd5b05f888225b6a9d9d (commit)
from ace90354f42a66f2915e27f1d1d0098d34869a9d (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 6e500e577f4fd8d6be953c15561f20c5ef1a784e
Merge: ace9035 2497f54
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Tue Aug 28 11:32:00 2012 +0200
Merge #2096
Conflicts:
src/lib/datasrc/memory/Makefile.am
commit 2497f5480351f20ebb4f1ae72e9dbfc36b9ea582
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue Aug 28 11:26:51 2012 +0200
[2096] Clarify about small optimisation
Mostly by jinmei, small changes by vorner (assert->throw, as assert can
be turned off).
commit aa14f630842559ebe3fb682061a1be805cd23811
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Tue Aug 28 11:21:01 2012 +0200
[2096] Document naming intention
-----------------------------------------------------------------------
Summary of changes:
configure.ac | 1 +
src/lib/datasrc/memory/Makefile.am | 8 +-
src/lib/datasrc/memory/benchmarks/.gitignore | 1 +
src/lib/datasrc/memory/benchmarks/Makefile.am | 17 +
.../memory/benchmarks/rdata_reader_bench.cc | 221 ++++++
.../{rdata_encoder.cc => rdata_serialization.cc} | 255 +++---
.../{rdata_encoder.h => rdata_serialization.h} | 289 +++++--
src/lib/datasrc/memory/rdata_serialization_priv.cc | 65 ++
src/lib/datasrc/memory/tests/Makefile.am | 2 +-
.../datasrc/memory/tests/rdata_encoder_unittest.cc | 481 -----------
.../memory/tests/rdata_serialization_unittest.cc | 839 ++++++++++++++++++++
src/lib/dns/benchmarks/message_renderer_bench.cc | 15 +-
src/lib/dns/benchmarks/rdatarender_bench.cc | 13 +-
src/lib/dns/messagerenderer.h | 5 +-
14 files changed, 1560 insertions(+), 652 deletions(-)
create mode 100644 src/lib/datasrc/memory/benchmarks/.gitignore
create mode 100644 src/lib/datasrc/memory/benchmarks/Makefile.am
create mode 100644 src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc
rename src/lib/datasrc/memory/{rdata_encoder.cc => rdata_serialization.cc} (79%)
rename src/lib/datasrc/memory/{rdata_encoder.h => rdata_serialization.h} (56%)
create mode 100644 src/lib/datasrc/memory/rdata_serialization_priv.cc
delete mode 100644 src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc
create mode 100644 src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index f83689b..c00b9cd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1166,6 +1166,7 @@ AC_CONFIG_FILES([Makefile
src/lib/datasrc/Makefile
src/lib/datasrc/memory/Makefile
src/lib/datasrc/memory/tests/Makefile
+ src/lib/datasrc/memory/benchmarks/Makefile
src/lib/datasrc/tests/Makefile
src/lib/datasrc/tests/testdata/Makefile
src/lib/xfr/Makefile
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index 8e7b03e..e0f49cc 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/src/lib/datasrc/memory/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . tests benchmarks
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
@@ -10,7 +10,9 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
noinst_LTLIBRARIES = libdatasrc_memory.la
-libdatasrc_memory_la_SOURCES = rdata_encoder.h rdata_encoder.cc
-libdatasrc_memory_la_SOURCES += domaintree.h
+libdatasrc_memory_la_SOURCES = \
+ rdata_serialization.h rdata_serialization.cc \
+ domaintree.h
libdatasrc_memory_la_SOURCES += zone_data.h
libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
+EXTRA_DIST = rdata_serialization_priv.cc
diff --git a/src/lib/datasrc/memory/benchmarks/.gitignore b/src/lib/datasrc/memory/benchmarks/.gitignore
new file mode 100644
index 0000000..ea243a4
--- /dev/null
+++ b/src/lib/datasrc/memory/benchmarks/.gitignore
@@ -0,0 +1 @@
+/rdata_reader_bench
diff --git a/src/lib/datasrc/memory/benchmarks/Makefile.am b/src/lib/datasrc/memory/benchmarks/Makefile.am
new file mode 100644
index 0000000..e86c194
--- /dev/null
+++ b/src/lib/datasrc/memory/benchmarks/Makefile.am
@@ -0,0 +1,17 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+noinst_PROGRAMS = rdata_reader_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/dns/libb10-dns++.la
diff --git a/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc b/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc
new file mode 100644
index 0000000..4e6af56
--- /dev/null
+++ b/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc
@@ -0,0 +1,221 @@
+// 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 <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 <boost/bind.hpp>
+
+#include <vector>
+#include <sstream>
+
+#include <unistd.h>
+
+using std::vector;
+using namespace isc::bench;
+using namespace isc::datasrc::memory;
+using namespace isc::dns;
+
+namespace {
+struct EncodeParam {
+ EncodeParam(RdataEncoder& encoder, ConstRRsetPtr rrset,
+ ConstRRsetPtr sig_rrset = ConstRRsetPtr()) :
+ rrclass(rrset->getClass()), rrtype(rrset->getType()),
+ rdata_count(rrset->getRdataCount()),
+ sig_count(sig_rrset ? sig_rrset->getRdataCount() : 0)
+ {
+ encoder.start(rrclass, rrtype);
+ for (RdataIteratorPtr it = rrset->getRdataIterator();
+ !it->isLast();
+ it->next()) {
+ encoder.addRdata(it->getCurrent());
+ }
+ if (sig_rrset) {
+ for (RdataIteratorPtr it = sig_rrset->getRdataIterator();
+ !it->isLast();
+ it->next())
+ {
+ encoder.addSIGRdata(it->getCurrent());
+ }
+ }
+ const size_t data_len = encoder.getStorageLength();
+ data.resize(data_len);
+ encoder.encode(&data[0], data.size());
+ }
+ RRClass rrclass;
+ RRType rrtype;
+ size_t rdata_count;
+ size_t sig_count;
+ vector<uint8_t> data;
+};
+
+// Encapsulating parameters for a RdataReader. It extracts from the given
+// RRset and its RRSIGs parameters that are necessary construct an RdataReader.
+// RDATA data will be stored in the 'data' vector.
+// members are defined as non const so we can use the object of this struct
+// in a vector.
+class ReaderBenchMark {
+public:
+ ReaderBenchMark(const vector<EncodeParam>& encode_params,
+ MessageRenderer& renderer) :
+ encode_params_(encode_params), renderer_(renderer)
+ {}
+ unsigned int run() {
+ vector<EncodeParam>::const_iterator it;
+ const vector<EncodeParam>::const_iterator it_end =
+ encode_params_.end();
+ renderer_.clear();
+ for (it = encode_params_.begin(); it != it_end; ++it) {
+ RdataReader reader(it->rrclass, it->rrtype, &it->data[0],
+ it->rdata_count, it->sig_count,
+ boost::bind(&ReaderBenchMark::renderName,
+ this, _1, _2),
+ boost::bind(&ReaderBenchMark::renderData,
+ this, _1, _2));
+ reader.iterate();
+ reader.iterateAllSigs();
+ }
+ return (1);
+ }
+ void renderName(const LabelSequence& labels,
+ RdataNameAttributes attributes)
+ {
+ const bool compress =
+ (attributes & NAMEATTR_COMPRESSIBLE) != 0;
+ renderer_.writeName(labels, compress);
+ }
+ void renderData(const void* data, size_t data_len) {
+ renderer_.writeData(data, data_len);
+ }
+private:
+ const vector<EncodeParam>& encode_params_;
+ 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 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";
+
+void
+usage() {
+ std::cerr << "Usage: rdata_reader_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);
+}
+}
+
+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;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0) {
+ usage();
+ }
+
+ // Build test data. rrsets will consist of a list of RRsets corresponding
+ // to rrsets_txt defined above.
+ vector<ConstRRsetPtr> rrsets;
+ std::stringstream rrsets_stream(rrsets_txt);
+ masterLoad(rrsets_stream, Name::ROOT_NAME(), RRClass::IN(),
+ boost::bind(setRRset, &rrsets, _1));
+
+ // Create EncodeParam for each RRset (possibly with RRSIG) in rrsets,
+ // and store them in encode_param_list. It's the direct test input.
+ vector<EncodeParam> encode_param_list;
+ RdataEncoder encoder;
+ encode_param_list.push_back(EncodeParam(encoder, rrsets.at(0)));
+ encode_param_list.push_back(EncodeParam(encoder, rrsets.at(1),
+ rrsets.at(2)));
+ for (vector<ConstRRsetPtr>::const_iterator it = rrsets.begin() + 3;
+ it != rrsets.end();
+ ++it) {
+ encode_param_list.push_back(EncodeParam(encoder, *it));
+ }
+
+ // 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 RdataReader" << std::endl;
+ BenchMark<ReaderBenchMark>(iteration,
+ ReaderBenchMark(encode_param_list, renderer));
+ return (0);
+}
diff --git a/src/lib/datasrc/memory/rdata_encoder.cc b/src/lib/datasrc/memory/rdata_encoder.cc
deleted file mode 100644
index 1c27d85..0000000
--- a/src/lib/datasrc/memory/rdata_encoder.cc
+++ /dev/null
@@ -1,602 +0,0 @@
-// 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/name.h>
-#include <dns/labelsequence.h>
-#include <dns/messagerenderer.h>
-#include <dns/rdata.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include "rdata_encoder.h"
-
-#include <boost/static_assert.hpp>
-
-#include <cassert>
-#include <cstring>
-#include <vector>
-
-#include <stdint.h>
-
-using namespace isc::dns;
-using std::vector;
-
-namespace isc {
-namespace datasrc {
-namespace memory {
-
-namespace {
-/// Specification of a single RDATA field in terms of internal encoding.
-struct RdataFieldSpec {
- enum FieldType {
- FIXEDLEN_DATA = 0, // fixed-length data field
- VARLEN_DATA, // variable-length data field
- DOMAIN_NAME // domain name
- };
-
- const FieldType type; // field type
-
- // The length of fixed-length data field. Only valid for FIXEDLEN_DATA.
- // For type DOMAIN_NAME, set it to 0.
- const uint16_t fixeddata_len;
-
- // Attributes of the name. Only valid for DOMAIN_NAME.
- // For type _DATA, set it to NAMEATTR_NONE.
- const RdataNameAttributes name_attributes;
-};
-
-/// Specification of RDATA in terms of internal encoding.
-///
-/// The fields must be a sequence of:
-/// <0 or 1 fixed/var-len data field>,
-/// <1 or more domain name fields>,
-/// <1 fixed/var-len data field>,
-/// <1 or more domain name fields>,
-/// <1 fixed/var-len data field>,
-/// ...and so on.
-/// There must not be more than one consecutive data fields (i.e., without
-/// interleaved by a domain name); it would just be inefficient in terms of
-/// memory footprint and iterating over the fields, and it would break
-/// some assumption within the encoder implementation. For consecutive
-/// data fields in the DNS protocol, if all fields have fixed lengths, they
-/// should be combined into a single fixed-length field (like the last 20
-/// bytes of SOA RDATA). If there's a variable length field, they should be
-/// combined into a single variable-length field (such as DNSKEY, which has
-/// 3 fixed-length fields followed by one variable-length field).
-struct RdataEncodeSpec {
- const uint16_t field_count; // total number of fields (# of fields member)
- const uint16_t name_count; // number of domain name fields
- const uint16_t varlen_count; // number of variable-length data fields
- const RdataFieldSpec* const fields; // list of field specs
-};
-
-// Many types of RDATA can be treated as a single-field, variable length
-// field (in terms of our encoding). The following define such most general
-// form of field spec.
-const RdataFieldSpec generic_data_fields[] = {
- {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE}
-};
-const uint16_t n_generic_data_fields =
- sizeof(generic_data_fields) / sizeof(RdataFieldSpec);
-const RdataEncodeSpec generic_data_spec = {
- n_generic_data_fields, 0, 1, generic_data_fields
-};
-
-// RDATA consist of a single IPv4 address field.
-const RdataFieldSpec single_ipv4_fields[] = {
- {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint32_t), NAMEATTR_NONE}
-};
-const uint16_t n_ipv4_fields =
- sizeof(single_ipv4_fields) / sizeof(RdataFieldSpec);
-
-// RDATA consist of a single IPv6 address field.
-const RdataFieldSpec single_ipv6_fields[] = {
- {RdataFieldSpec::FIXEDLEN_DATA, 16, NAMEATTR_NONE} // 128bits = 16 bytes
-};
-const uint16_t n_ipv6_fields =
- sizeof(single_ipv6_fields) / sizeof(RdataFieldSpec);
-
-// There are several RR types that consist of a single domain name.
-const RdataFieldSpec single_noattr_name_fields[] = {
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
-};
-const RdataFieldSpec single_compressible_name_fields[] = {
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE}
-};
-const RdataFieldSpec single_compadditional_name_fields[] = {
- {RdataFieldSpec::DOMAIN_NAME, 0,
- static_cast<RdataNameAttributes>(
- static_cast<unsigned int>(NAMEATTR_COMPRESSIBLE) |
- static_cast<unsigned int>(NAMEATTR_ADDITIONAL))}
-};
-const uint16_t n_single_name_fields =
- sizeof(single_noattr_name_fields) / sizeof(RdataFieldSpec);
-
-// RDATA consisting of two names. There are some of this type.
-const RdataFieldSpec double_compressible_name_fields[] = {
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE}
-};
-const RdataFieldSpec double_noattr_name_fields[] = {
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE},
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
-};
-const uint16_t n_double_name_fields =
- sizeof(double_compressible_name_fields) / sizeof(RdataFieldSpec);
-
-// SOA specific: two compressible names + 5*32-bit data
-const RdataFieldSpec soa_fields[] = {
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
- {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint32_t) * 5, NAMEATTR_NONE}
-};
-const uint16_t n_soa_fields = sizeof(soa_fields) / sizeof(RdataFieldSpec);
-
-// MX specific: 16-bit data + compressible/additional name
-const RdataFieldSpec mx_fields[] = {
- {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t), NAMEATTR_NONE},
- {RdataFieldSpec::DOMAIN_NAME, 0,
- static_cast<RdataNameAttributes>(
- static_cast<unsigned int>(NAMEATTR_COMPRESSIBLE) |
- static_cast<unsigned int>(NAMEATTR_ADDITIONAL))}
-};
-const uint16_t n_mx_fields = sizeof(mx_fields) / sizeof(RdataFieldSpec);
-
-// AFSDB specific: 16-bit data + no-attribute name
-const RdataFieldSpec afsdb_fields[] = {
- {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t), NAMEATTR_NONE},
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
-};
-const uint16_t n_afsdb_fields = sizeof(afsdb_fields) / sizeof(RdataFieldSpec);
-
-// SRV specific: 3*16-bit data + additional name
-const RdataFieldSpec srv_fields[] = {
- {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t) * 3, NAMEATTR_NONE},
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_ADDITIONAL}
-};
-const uint16_t n_srv_fields = sizeof(srv_fields) / sizeof(RdataFieldSpec);
-
-// NAPTR specific: (multi-field) variable data + (additional) name
-// NAPTR requires complicated additional section handling; for now, we skip
-// the additional handling completely.
-const RdataFieldSpec naptr_fields[] = {
- {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE},
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
-};
-const uint16_t n_naptr_fields = sizeof(naptr_fields) / sizeof(RdataFieldSpec);
-
-// NSEC specific: no-attribute name + varlen data
-const RdataFieldSpec nsec_fields[] = {
- {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE},
- {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE}
-};
-const uint16_t n_nsec_fields = sizeof(nsec_fields) / sizeof(RdataFieldSpec);
-
-// Class IN encode specs. This gives a shortcut to the encode spec for
-// some well-known types of RDATA specific to class IN (most of which are
-// generic and can be used for other classes). The array index is the
-// RR type code.
-const RdataEncodeSpec encode_spec_list_in[] = {
- generic_data_spec, // #0: (NONE)
- {n_ipv4_fields, 0, 0, single_ipv4_fields}, // #1: A
- {n_single_name_fields, 1, 0, single_compadditional_name_fields}, // #2: NS
- generic_data_spec, // #3
- generic_data_spec, // #4
- {n_single_name_fields, 1, 0, single_compressible_name_fields}, // #5: CNAME
- {n_soa_fields, 2, 0, soa_fields}, // #6: SOA
- generic_data_spec, // #7
- generic_data_spec, // #8
- generic_data_spec, // #9
- generic_data_spec, // #10
- generic_data_spec, // #11
- {n_single_name_fields, 1, 0, single_compressible_name_fields}, // #12: PTR
- generic_data_spec, // #13: HINFO
- {n_double_name_fields, 2, 0, double_compressible_name_fields}, // #14:HINFO
- {n_mx_fields, 1, 0, mx_fields}, // #15: MX
- generic_data_spec, // #16: TXT
- {n_double_name_fields, 2, 0, double_noattr_name_fields}, // 17: RP
- {n_afsdb_fields, 1, 0, afsdb_fields}, // #18: AFSDB
- // #19-#26
- generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
- generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
- generic_data_spec, // #27
- {n_ipv6_fields, 0, 0, single_ipv6_fields}, // #28: AAAA
- // #29-#32
- generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
- {n_srv_fields, 1, 0, srv_fields}, // #33: SRV
- generic_data_spec, // #34
- {n_naptr_fields, 1, 1, naptr_fields}, // #35: NAPTR
- generic_data_spec, // #36
- generic_data_spec, // #37
- generic_data_spec, // #38
- {n_single_name_fields, 1, 0, single_noattr_name_fields}, // #39 DNAME
- generic_data_spec, // #40
- generic_data_spec, // #41 (OPT)
- generic_data_spec, // #42
- generic_data_spec, // #43: DS (this is opaque for encoding purposes)
- generic_data_spec, // #44: SSHFP (this is opaque for encoding purposes)
- generic_data_spec, // #45
- generic_data_spec, // #46: RRSIG (this is opaque for encoding purposes)
- {n_nsec_fields, 1, 1, nsec_fields} // #47: NSEC
-
- // All others can be treated as single-field variable length data, at
- // least for currently supported RR types.
-};
-
-// # of entries in encode_spec_list_in
-const size_t encode_spec_list_in_size =
- sizeof(encode_spec_list_in) / sizeof(encode_spec_list_in[0]);
-BOOST_STATIC_ASSERT(encode_spec_list_in_size == 48);
-
-inline
-const RdataEncodeSpec&
-getRdataEncodeSpec(RRClass rrclass, RRType rrtype) {
- // Special case: for classes other than IN, we treat RDATA of RR types
- // that are class-IN specific as generic opaque data.
- if (rrclass != RRClass::IN() &&
- (rrtype == RRType::A() || rrtype == RRType::AAAA() ||
- rrtype == RRType::SRV())) {
- return (generic_data_spec);
- }
-
- // Otherwise, if the type is in the pre-defined range, we use the defined
- // spec; otherwise we treat it as opaque data.
- const uint16_t typecode = rrtype.getCode();
- if (typecode < encode_spec_list_in_size) {
- return (encode_spec_list_in[rrtype.getCode()]);
- }
- return (generic_data_spec);
-}
-
-// This class is a helper for RdataEncoder to divide the content of RDATA
-// fields for encoding by "abusing" the message rendering logic.
-// The idea is to identify domain name fields in the writeName() method,
-// while keeping track of the size and position of other types of data
-// around the names.
-//
-// Technically, this use of inheritance may be considered a violation of
-// Liskov Substitution Principle in that it doesn't actually compress domain
-// names, and some of the methods are not expected to be used.
-// In fact, skip() or trim() may not be make much sense in this context.
-// Nevertheless we keep this idea at the moment. Since the usage is limited
-// (it's only used within this file, and only used with \c Rdata variants),
-// it's hopefully an acceptable practice.
-class RdataFieldComposer : public AbstractMessageRenderer {
-public:
- RdataFieldComposer() : last_data_pos_(0), encode_spec_(NULL),
- current_field_(0)
- {}
- virtual ~RdataFieldComposer() {}
- virtual bool isTruncated() const { return (false); }
- virtual size_t getLengthLimit() const { return (65535); }
- virtual CompressMode getCompressMode() const { return (CASE_INSENSITIVE); }
- virtual void setTruncated() {}
- virtual void setLengthLimit(size_t) {}
- virtual void setCompressMode(CompressMode) {}
-
- // Called for each domain name in the RDATA, from the RDATA's toWire()
- // implementation.
- virtual void writeName(const Name& name, bool compress) {
- // First, see if we have other data already stored in the renderer's
- // buffer, and handle it appropriately.
- updateOtherData();
-
- // Then, we should still have a field in the spec, and it must be a
- // domain name field.
- if (current_field_ >= encode_spec_->field_count) {
- isc_throw(BadValue,
- "RDATA encoder encounters an unexpected name data: " <<
- name);
- }
- const RdataFieldSpec& field =
- encode_spec_->fields[current_field_++];
- // Since we know we've passed any prior data field, the next field
- // must be a domain name as long as it exists; otherwise it's a bug
- // in the spec (not a bogus input). So we assert() that condition.
- assert(field.type == RdataFieldSpec::DOMAIN_NAME);
-
- // It would be compressed iff the field has that attribute.
- if (compress !=
- ((field.name_attributes & NAMEATTR_COMPRESSIBLE) != 0)) {
- isc_throw(BadValue, "RDATA encoder error, inconsistent name "
- "compression policy: " << name);
- }
-
- const LabelSequence labels(name);
- labels.serialize(labels_placeholder_, sizeof(labels_placeholder_));
- writeData(labels_placeholder_, labels.getSerializedLength());
-
- last_data_pos_ += labels.getSerializedLength();
- }
- // Clear all internal states and resources for a new set of RDATA.
- void clearLocal(const RdataEncodeSpec* encode_spec) {
- AbstractMessageRenderer::clear();
- encode_spec_ = encode_spec;
- data_lengths_.clear();
- last_data_pos_ = 0;
- }
- // Called at the beginning of an RDATA.
- void startRdata() {
- current_field_ = 0;
- }
- // Called at the end of an RDATA.
- void endRdata() {
- // Handle any remaining data (there should be no more name). Then
- // we should reach the end of the fields.
- updateOtherData();
- if (current_field_ != encode_spec_->field_count) {
- isc_throw(BadValue,
- "RDATA encoder didn't find all expected fields");
- }
- }
-
- // Hold the lengths of variable length fields, in the order of their
- // appearance. For convenience, allow the encoder to refer to it
- // directly.
- vector<uint16_t> data_lengths_;
-
-private:
- // We use generict write* methods, with the exception of writeName.
- // So new data can arrive without us knowing it, this considers all new
- // data to be just data, checking consistency with the field spec, and
- // if it contains variable-length field, record its length.
- size_t last_data_pos_;
- void updateOtherData() {
- // If we've reached the end of the fields or we are expecting a
- // domain name, there's nothing to do here.
- if (current_field_ >= encode_spec_->field_count ||
- encode_spec_->fields[current_field_].type ==
- RdataFieldSpec::DOMAIN_NAME) {
- return;
- }
-
- const size_t cur_pos = getLength();
- const size_t data_len = cur_pos - last_data_pos_;
-
- const RdataFieldSpec& field = encode_spec_->fields[current_field_];
- if (field.type == RdataFieldSpec::FIXEDLEN_DATA) {
- // The data length of a fixed length field must be the one
- // specified in the field spec.
- if (data_len != field.fixeddata_len) {
- isc_throw(BadValue,
- "RDATA encoding: available data too short for the "
- "type");
- }
- } else {
- // For encoding purposes, a variable-length data field is
- // a single field covering all data, even if it may
- // consist of multiple fields as DNS RDATA (e.g. TXT).
- if (data_len > 0xffff) {
- isc_throw(RdataEncodingError, "RDATA field is too large: "
- << data_len << " bytes");
- }
- data_lengths_.push_back(data_len);
- }
-
- ++current_field_;
- last_data_pos_ = cur_pos;
- }
-
- // The RDATA field spec of the current session. Set at the beginning of
- // each session.
- const RdataEncodeSpec* encode_spec_;
- // the RDATA field (for encoding) currently handled. Reset to 0 for
- // each RDATA of the session.
- size_t current_field_;
- // Placeholder to convert a name object to a label sequence.
- uint8_t labels_placeholder_[LabelSequence::MAX_SERIALIZED_LENGTH];
-};
-} // end of unnamed namespace
-
-struct RdataEncoder::RdataEncoderImpl {
- RdataEncoderImpl() : encode_spec_(NULL), rrsig_buffer_(0),
- rdata_count_(0)
- {}
-
- const RdataEncodeSpec* encode_spec_; // encode spec of current RDATA set
- RdataFieldComposer field_composer_;
- util::OutputBuffer rrsig_buffer_;
- size_t rdata_count_;
- vector<uint16_t> rrsig_lengths_;
-};
-
-RdataEncoder::RdataEncoder() :
- impl_(new RdataEncoderImpl)
-{}
-
-RdataEncoder::~RdataEncoder() {
- delete impl_;
-}
-
-void
-RdataEncoder::start(RRClass rrclass, RRType rrtype) {
- if (rrtype == RRType::RRSIG()) {
- isc_throw(BadValue, "RRSIG cannot be encoded as main RDATA type");
- }
-
- impl_->encode_spec_ = &getRdataEncodeSpec(rrclass, rrtype);
- impl_->field_composer_.clearLocal(impl_->encode_spec_);
- impl_->rrsig_buffer_.clear();
- impl_->rdata_count_ = 0;
- impl_->rrsig_lengths_.clear();
-}
-
-void
-RdataEncoder::addRdata(const rdata::Rdata& rdata) {
- if (impl_->encode_spec_ == NULL) {
- isc_throw(InvalidOperation,
- "RdataEncoder::addRdata performed before start");
- }
-
- impl_->field_composer_.startRdata();
- rdata.toWire(impl_->field_composer_);
- impl_->field_composer_.endRdata();
- ++impl_->rdata_count_;
-}
-
-void
-RdataEncoder::addSIGRdata(const rdata::Rdata& sig_rdata) {
- if (impl_->encode_spec_ == NULL) {
- isc_throw(InvalidOperation,
- "RdataEncoder::addSIGRdata performed before start");
- }
- const size_t cur_pos = impl_->rrsig_buffer_.getLength();
- sig_rdata.toWire(impl_->rrsig_buffer_);
- const size_t rrsig_datalen = impl_->rrsig_buffer_.getLength() - cur_pos;
- if (rrsig_datalen > 0xffff) {
- isc_throw(RdataEncodingError, "RRSIG is too large: "
- << rrsig_datalen << " bytes");
- }
- impl_->rrsig_lengths_.push_back(rrsig_datalen);
-}
-
-size_t
-RdataEncoder::getStorageLength() const {
- if (impl_->encode_spec_ == NULL) {
- isc_throw(InvalidOperation,
- "RdataEncoder::getStorageLength performed before start");
- }
-
- return (sizeof(uint16_t) * impl_->field_composer_.data_lengths_.size() +
- sizeof(uint16_t) * impl_->rrsig_lengths_.size() +
- impl_->rrsig_buffer_.getLength() +
- impl_->field_composer_.getLength());
-}
-
-void
-RdataEncoder::encode(void* buf, size_t buf_len) const {
- if (impl_->encode_spec_ == NULL) {
- isc_throw(InvalidOperation,
- "RdataEncoder::encode performed before start");
- }
- if (buf == NULL) {
- isc_throw(BadValue,
- "RdataEncoder::encode NULL buffer is given");
- }
- if (getStorageLength() > buf_len) {
- isc_throw(BadValue, "RdataEncoder::encode short buffer given");
- }
-
- uint8_t* const dp_beg = reinterpret_cast<uint8_t*>(buf);
- uint8_t* dp = dp_beg;
- uint16_t* lenp = reinterpret_cast<uint16_t*>(buf);
-
- // Encode list of lengths for variable length fields (if any)
- if (!impl_->field_composer_.data_lengths_.empty()) {
- const size_t varlen_fields_len =
- impl_->field_composer_.data_lengths_.size() * sizeof(uint16_t);
- std::memcpy(lenp, &impl_->field_composer_.data_lengths_[0],
- varlen_fields_len);
- lenp += impl_->field_composer_.data_lengths_.size();
- dp += varlen_fields_len;
- }
- // Encode list of lengths for RRSIGs (if any)
- if (!impl_->rrsig_lengths_.empty()) {
- const size_t rrsigs_len =
- impl_->rrsig_lengths_.size() * sizeof(uint16_t);
- std::memcpy(lenp, &impl_->rrsig_lengths_[0], rrsigs_len);
- dp += rrsigs_len;
- }
- // Encode main RDATA
- std::memcpy(dp, impl_->field_composer_.getData(),
- impl_->field_composer_.getLength());
- dp += impl_->field_composer_.getLength();
- // Encode RRSIGs, if any
- std::memcpy(dp, impl_->rrsig_buffer_.getData(),
- impl_->rrsig_buffer_.getLength());
- dp += impl_->rrsig_buffer_.getLength();
-
- // The validation at the entrance must ensure this
- assert(buf_len >= dp - dp_beg);
-}
-
-namespace testing {
-void
-foreachRdataField(RRClass rrclass, RRType rrtype,
- size_t rdata_count,
- const vector<uint8_t>& encoded_data,
- const vector<uint16_t>& varlen_list,
- NameCallback name_callback, DataCallback data_callback)
-{
- const RdataEncodeSpec& encode_spec = getRdataEncodeSpec(rrclass, rrtype);
-
- size_t off = 0;
- size_t varlen_count = 0;
- size_t name_count = 0;
- for (size_t count = 0; count < rdata_count; ++count) {
- for (size_t i = 0; i < encode_spec.field_count; ++i) {
- const RdataFieldSpec& field_spec = encode_spec.fields[i];
- switch (field_spec.type) {
- case RdataFieldSpec::FIXEDLEN_DATA:
- if (data_callback) {
- data_callback(&encoded_data.at(off),
- field_spec.fixeddata_len);
- }
- off += field_spec.fixeddata_len;
- break;
- case RdataFieldSpec::VARLEN_DATA:
- {
- const size_t varlen = varlen_list.at(varlen_count);
- if (data_callback && varlen > 0) {
- data_callback(&encoded_data.at(off), varlen);
- }
- off += varlen;
- ++varlen_count;
- break;
- }
- case RdataFieldSpec::DOMAIN_NAME:
- {
- ++name_count;
- const LabelSequence labels(&encoded_data.at(off));
- if (name_callback) {
- name_callback(labels, field_spec.name_attributes);
- }
- off += labels.getSerializedLength();
- break;
- }
- }
- }
- }
- assert(name_count == encode_spec.name_count * rdata_count);
- assert(varlen_count == encode_spec.varlen_count * rdata_count);
-}
-
-void
-foreachRRSig(const vector<uint8_t>& encoded_data,
- const vector<uint16_t>& rrsiglen_list,
- DataCallback data_callback)
-{
- size_t rrsig_totallen = 0;
- for (vector<uint16_t>::const_iterator it = rrsiglen_list.begin();
- it != rrsiglen_list.end();
- ++it) {
- rrsig_totallen += *it;
- }
- assert(encoded_data.size() >= rrsig_totallen);
-
- const uint8_t* dp = &encoded_data[encoded_data.size() - rrsig_totallen];
- for (size_t i = 0; i < rrsiglen_list.size(); ++i) {
- data_callback(dp, rrsiglen_list[i]);
- dp += rrsiglen_list[i];
- }
-}
-} // namespace testing
-
-} // namespace memory
-} // namespace datasrc
-} // datasrc isc
diff --git a/src/lib/datasrc/memory/rdata_encoder.h b/src/lib/datasrc/memory/rdata_encoder.h
deleted file mode 100644
index 7524f94..0000000
--- a/src/lib/datasrc/memory/rdata_encoder.h
+++ /dev/null
@@ -1,319 +0,0 @@
-// 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_RDATA_ENCODER_H
-#define DATASRC_MEMORY_RDATA_ENCODER_H 1
-
-#include <exceptions/exceptions.h>
-
-#include <dns/labelsequence.h>
-#include <dns/rdata.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include <boost/function.hpp>
-#include <boost/noncopyable.hpp>
-
-#include <vector>
-
-/// \file rdata_encoder.h
-/// \brief Set of utility classes for encoding RDATA in memory efficient way.
-///
-/// This file defines a set of interfaces (classes, types, constants) to
-/// manipulate a given set of RDATA of the same type (normally associated with
-/// an RRset) that may be accompanied with RRSIGs in a memory efficient way.
-///
-/// The entire set of RDATA is stored in a packed form in a contiguous
-/// memory region. It's opaque data, without containing non trivial
-/// data structures, so it can be located anywhere in the memory or even
-/// dumped to a file.
-///
-/// Two main classes are provided: one is
-/// \c isc::datasrc::memory::RdataEncoder, which allows
-/// the application to create encoded data for a set of RDATA;
-/// the other (TBD) provides an interface to iterate over encoded set of
-/// RDATA for purposes such as data lookups or rendering the data into the
-/// wire format to create a DNS message.
-///
-/// The actual encoding detail is private information to the implementation,
-/// and the application shouldn't assume anything about that except that
-/// each RDATA is considered to consist of one or more generic fields,
-/// and each field is typed as either opaque data or a domain name.
-/// A domain name field has additional attributes
-/// (see \c isc::datasrc::memory::RdataNameAttributes)
-/// so the application can change how the name should be handled in terms
-/// of the DNS protocol (e.g., whether it's subject to name compression).
-///
-/// The following are the current implementation of internal encoding, shown
-/// only for reference. Applications must not assume this particular form
-/// for the encoded data; in fact, it can change in a future version of the
-/// implementation.
-/// \verbatim
-// The encoded data begin with a series of 16-bit length fields (values are
-// stored in the host byte order). The sequence may be empty.
-// uint16_t n1_1: size of 1st variable len field (if any) of 1st RDATA
-// uint16_t n1_2: size of 2nd variable len field of 1st RDATA
-// ...
-// uint16_t nN_M: size of last (Mth) variable len field of last (Nth) RDATA
-// uint16_t ns1: size of 1st RRSIG (if any) data
-// ...
-// uint16_t nsL: size of last (Lth) RRSIG data
-// A sequence of packed data fields follows:
-// uint8_t[]: data field value, length specified by nI_J (in case it's
-// variable-length) or by the per type field spec (in case it's
-// fixed-length).
-// or
-// opaque data, LabelSequence::getSerializedLength() bytes: data for a name
-// uint8_t[ns1]: 1st RRSIG data
-// ...
-// uint8_t[nsL]: last RRSIG data
-// \endverbatim
-///
-/// As described above, this implementation treats RRSIGs as opaque data
-/// that don't contain any domain names. Technically, it has a "signer"
-/// domain name field in the sense of RFC4034. In practice, however, this
-/// field is essentially mere data; it's not subject to name compression,
-/// and since it's very likely to be a subdomain of (or equal to) the
-/// owner name of the corresponding RR (or, if used in a DNS message,
-/// some domain name that already appears before this field), so it won't
-/// be a target of name compression either. By treating the entire RRSIG
-/// as single-field data we can make the implementation simpler, and probably
-/// make it faster in rendering it into a DNS message.
-
-namespace isc {
-namespace datasrc {
-namespace memory {
-
-/// \brief General error in RDATA encoding.
-///
-/// This is thrown when \c RdataEncoder encounters a rare, unsupported
-/// situation. a method is called for a name or RRset which
-/// is not in or below the zone.
-class RdataEncodingError : public Exception {
-public:
- RdataEncodingError(const char* file, size_t line, const char* what) :
- Exception(file, line, what) {}
-};
-
-/// \brief Attributes of domain name fields of encoded RDATA.
-///
-/// The enum values define special traits of the name that can affect how
-/// it should be handled in rendering or query processing.
-enum RdataNameAttributes {
- NAMEATTR_NONE = 0, ///< No special attributes
- NAMEATTR_COMPRESSIBLE = 1, ///< Name should be compressed when rendered
- NAMEATTR_ADDITIONAL = (NAMEATTR_COMPRESSIBLE << 1) ///< Name requires
- ///< Additional section
- ///< handling
-};
-
-/// \brief RDATA encoder.
-///
-/// This class provides interfaces to encode a set of RDATA of a specific
-/// RR class and type, possibly with their RRSIG RDATAs, in a memory-efficient
-/// format. In many cases these sets of RDATA come from a specific (signed
-/// or unsigned) RRset.
-///
-/// It is expected for a single \c RdataEncoder object to be used multiple
-/// times for different sets of RDATA, such as in loading an entire zone
-/// into memory. Each encoding session begins with the \c start() method,
-/// which sets the context for the specific RR class and type to be encoded.
-/// Any number of calls to \c addRdata() or \c addSIGRdata() follow, each
-/// of which updates the internal state of the encoder with the encoding
-/// information for the given RDATA or RRSIG RDATA, respectively.
-/// The \c addRdata() is expected to be called with an
-/// \c isc::dns::rdata::Rdata object
-/// of the specified class and type, and \c addRdata() checks the consistency
-/// for the purpose of encoding (but it's not completely type safe; for
-/// example, it wouldn't distinguish TXT RDATA and HINFO RDATA.
-/// Likewise, an \c isc::dns::rdata::Rdata given to \c addSIGRdata() is
-/// expected to be of RRSIG, but the method does not check the assumption).
-///
-/// After passing the complete set of RDATA and their RRSIG, the application
-/// is expected to call \c getStorageLength() to know the size of storage
-/// that is sufficient to store all encoded data. Normally the application
-/// would allocate a memory region of that size, and then call \c encode()
-/// with the prepared region. The \c encode() method dumps encoded data
-/// to the given memory region.
-///
-/// The caller can reuse the \c RdataEncoder object for another set of RDATA
-/// by repeating the session from \c start().
-class RdataEncoder : boost::noncopyable {
-public:
- /// \brief Default constructor.
- RdataEncoder();
-
- /// \brief The destrcutor.
- ~RdataEncoder();
-
- /// \brief Start the encoding session.
- ///
- /// It re-initializes the internal encoder state for a new encoding
- /// session. The \c rrclass and \c rrtype parameters specify the
- /// type of RDATA to be encoded in the new session. Note that if the
- /// set of RDATA is signed, \c rrtype always specifies the "signed" type;
- /// it must not be RRSIG.
- ///
- /// \throw BadValue RRSIG is specified for rrtype.
- ///
- /// \param rrclass The RR class of RDATA to be encoded in the session.
- /// \param rrtype The RR type of RDATA to be encoded in the session.
- void start(dns::RRClass rrclass, dns::RRType rrtype);
-
- /// \brief Add an RDATA for encoding.
- ///
- /// This method updates internal state of the \c RdataEncoder() with the
- /// given RDATA so it will be part of the encoded data in a subsequent
- /// call to \c encode().
- ///
- /// The given \c rdata must be of the RR class and type specified at
- /// the prior call to \c start(). This method checks the assumption
- /// to some extent, but the check is not complete; this is generally
- /// the responsibility of the caller.
- ///
- /// The caller can destroy \c rdata after this call is completed.
- ///
- /// \note This implementation does not support RDATA (or any subfield of
- /// it) whose size exceeds 65535 bytes (max uint16_t value). Such RDATA
- /// may not necessarily be considered invalid in terms of protocol
- /// specification, but in practice it's mostly useless because the
- /// corresponding RR won't fit in any valid DNS message.
- ///
- /// As long as the \c rdata is of the correct type and its size is normal,
- /// this method should normally be exception free. If it throws, however,
- /// it doesn't always provide the strong exception guarantee. In general,
- /// the caller needs to either destroy the encoder object or restart a
- /// new session from \c start() should this method throws an exception.
- ///
- /// \throw InvalidOperation called before start().
- /// \throw BadValue inconsistent data found.
- /// \throw RdataEncodingError A very unusual case, such as over 64KB RDATA.
- /// \throw std::bad_alloc Internal memory allocation failure.
- ///
- /// \param rdata An RDATA to be encoded in the session.
- void addRdata(const dns::rdata::Rdata& rdata);
-
- /// \brief Add an RRSIG RDATA for encoding.
- ///
- /// This method updates internal state of the \c RdataEncoder() with the
- /// given RDATA, which is assumed to be of type RRSIG that covers the
- /// type specified at the time of \c start() for the encoding session.
- /// The corresponding data for the RRSIG RDATA will be encoded in a
- /// subsequent call to \c encode().
- ///
- /// The passed \c sig_rdata is expected to be of type RRSIG and cover
- /// the RR type specified at the call to \c start() to this encoding
- /// session. But this method does not check if it is the case at all;
- /// it could even accept any type of RDATA as opaque data. It's caller's
- /// responsibility to ensure the assumption.
- ///
- /// The caller can destroy \c rdata after this call is completed.
- ///
- /// \note Like addRdata(), this implementation does not support
- /// RRSIG RDATA whose size (in the form of wire format) exceeds 65535
- /// bytes.
- ///
- /// The same note about exception safety as \c addRdata() applies.
- ///
- /// \throw InvalidOperation called before start().
- /// \throw RdataEncodingError A very unusual case, such as over 64KB RDATA.
- /// \throw std::bad_alloc Internal memory allocation failure.
- ///
- /// \param sig_rdata An RDATA to be encoded in the session. Supposed to
- /// be of type RRSIG.
- void addSIGRdata(const dns::rdata::Rdata& sig_rdata);
-
- /// \brief Return the length of space for encoding for the session.
- ///
- /// It returns the size of the encoded data that would be generated for
- /// the set of RDATA (and RRSIGs) in the encoder at the call of this
- /// method. It's ensured that a buffer of that size can be safely passed
- /// to \c encode() unless there's no other "add" method is called by then.
- ///
- /// As long as this method is called after start(), it never throws.
- ///
- /// \throw InvalidOperation called before start().
- ///
- /// \return The expected size of the encoded data at the time of the call.
- size_t getStorageLength() const;
-
- /// \brief Encode RDATAs of the session to a buffer.
- ///
- /// This method dumps encoded data for the stored set of RDATA and
- /// their RRSIGs to a given buffer. The buffer must have a size
- /// at least as large as the return value of a prior call to
- /// \c getStorageLength() (it may be larger than that).
- ///
- /// The given buffer must be aligned at the natural boundary for
- /// 16-bit integers. The method doesn't check this condition; it's
- /// caller's responsibility to ensure that. Note: the alignment
- /// requirement may change in a future version of this implementation.
- ///
- /// As long as this method is called after start() and the buffer is
- /// valid with a sufficient size, this method never throws.
- ///
- /// \throw InvalidOperation called before start().
- /// \throw BadValue buffer is NULL or it's too short for the encoded data.
- ///
- /// \param buf A pointer to the buffer to which encoded data are to be
- /// dumped.
- /// \param buf_len The size of the buffer in bytes.
- void encode(void* buf, size_t buf_len) const;
-
-private:
- struct RdataEncoderImpl;
- RdataEncoderImpl* impl_;
-};
-
-// We use the following quick-hack version of "foreach"
-// operators until we implement the complete versions. The plan is to
-// update the test cases that use these functions with the complete
-// functions/classes, and then remove the entire namespace.
-namespace testing {
-// Callbacks used in foreachRdataField.
-typedef boost::function<void(const dns::LabelSequence&,
- RdataNameAttributes)> NameCallback;
-typedef boost::function<void(const uint8_t*, size_t)> DataCallback;
-
-// Iterate over each field (in terms of the internal encoding) of each
-// RDATA stored in encoded_data, and call the given callback for each
-// data (for domain name fields, name_callback will be called; for
-// normal data fields data_callback will be called). rdata_count is
-// the number of RDATAs. If the encoded data contain variable-length
-// data fields, varlen_list should store a sequence of their lengths,
-// in the order of the appearance.
-void foreachRdataField(dns::RRClass rrclass, dns::RRType rrtype,
- size_t rdata_count,
- const std::vector<uint8_t>& encoded_data,
- const std::vector<uint16_t>& varlen_list,
- NameCallback name_callback, DataCallback data_callback);
-
-// Iterate over each RRSIG stored in encoded_data, and call the given
-// callback for each. rrsiglen_list should store a sequence of their lengths,
-// in the order of the appearance. Its size is the number of RRSIGs.
-// The list can be empty, in which case this function does nothing.
-void foreachRRSig(const std::vector<uint8_t>& encoded_data,
- const std::vector<uint16_t>& rrsiglen_list,
- DataCallback data_callback);
-}
-
-} // namespace memory
-} // namespace datasrc
-} // namespace isc
-
-#endif // DATASRC_MEMORY_RDATA_ENCODER_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/memory/rdata_serialization.cc b/src/lib/datasrc/memory/rdata_serialization.cc
new file mode 100644
index 0000000..9c020b2
--- /dev/null
+++ b/src/lib/datasrc/memory/rdata_serialization.cc
@@ -0,0 +1,635 @@
+// 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 "rdata_serialization.h"
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <cassert>
+#include <cstring>
+#include <vector>
+#include <boost/static_assert.hpp>
+
+using namespace isc::dns;
+using std::vector;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+#include "rdata_serialization_priv.cc"
+
+namespace {
+
+// Many types of RDATA can be treated as a single-field, variable length
+// field (in terms of our encoding). The following define such most general
+// form of field spec.
+const RdataFieldSpec generic_data_fields[] = {
+ {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE}
+};
+const uint16_t n_generic_data_fields =
+ sizeof(generic_data_fields) / sizeof(RdataFieldSpec);
+const RdataEncodeSpec generic_data_spec = {
+ n_generic_data_fields, 0, 1, generic_data_fields
+};
+
+// RDATA consist of a single IPv4 address field.
+const RdataFieldSpec single_ipv4_fields[] = {
+ {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint32_t), NAMEATTR_NONE}
+};
+const uint16_t n_ipv4_fields =
+ sizeof(single_ipv4_fields) / sizeof(RdataFieldSpec);
+
+// RDATA consist of a single IPv6 address field.
+const RdataFieldSpec single_ipv6_fields[] = {
+ {RdataFieldSpec::FIXEDLEN_DATA, 16, NAMEATTR_NONE} // 128bits = 16 bytes
+};
+const uint16_t n_ipv6_fields =
+ sizeof(single_ipv6_fields) / sizeof(RdataFieldSpec);
+
+// There are several RR types that consist of a single domain name.
+const RdataFieldSpec single_noattr_name_fields[] = {
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
+};
+const RdataFieldSpec single_compressible_name_fields[] = {
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE}
+};
+const RdataFieldSpec single_compadditional_name_fields[] = {
+ {RdataFieldSpec::DOMAIN_NAME, 0,
+ static_cast<RdataNameAttributes>(
+ static_cast<unsigned int>(NAMEATTR_COMPRESSIBLE) |
+ static_cast<unsigned int>(NAMEATTR_ADDITIONAL))}
+};
+const uint16_t n_single_name_fields =
+ sizeof(single_noattr_name_fields) / sizeof(RdataFieldSpec);
+
+// RDATA consisting of two names. There are some of this type.
+const RdataFieldSpec double_compressible_name_fields[] = {
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE}
+};
+const RdataFieldSpec double_noattr_name_fields[] = {
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE},
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
+};
+const uint16_t n_double_name_fields =
+ sizeof(double_compressible_name_fields) / sizeof(RdataFieldSpec);
+
+// SOA specific: two compressible names + 5*32-bit data
+const RdataFieldSpec soa_fields[] = {
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_COMPRESSIBLE},
+ {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint32_t) * 5, NAMEATTR_NONE}
+};
+const uint16_t n_soa_fields = sizeof(soa_fields) / sizeof(RdataFieldSpec);
+
+// MX specific: 16-bit data + compressible/additional name
+const RdataFieldSpec mx_fields[] = {
+ {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t), NAMEATTR_NONE},
+ {RdataFieldSpec::DOMAIN_NAME, 0,
+ static_cast<RdataNameAttributes>(
+ static_cast<unsigned int>(NAMEATTR_COMPRESSIBLE) |
+ static_cast<unsigned int>(NAMEATTR_ADDITIONAL))}
+};
+const uint16_t n_mx_fields = sizeof(mx_fields) / sizeof(RdataFieldSpec);
+
+// AFSDB specific: 16-bit data + no-attribute name
+const RdataFieldSpec afsdb_fields[] = {
+ {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t), NAMEATTR_NONE},
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
+};
+const uint16_t n_afsdb_fields = sizeof(afsdb_fields) / sizeof(RdataFieldSpec);
+
+// SRV specific: 3*16-bit data + additional name
+const RdataFieldSpec srv_fields[] = {
+ {RdataFieldSpec::FIXEDLEN_DATA, sizeof(uint16_t) * 3, NAMEATTR_NONE},
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_ADDITIONAL}
+};
+const uint16_t n_srv_fields = sizeof(srv_fields) / sizeof(RdataFieldSpec);
+
+// NAPTR specific: (multi-field) variable data + (additional) name
+// NAPTR requires complicated additional section handling; for now, we skip
+// the additional handling completely.
+const RdataFieldSpec naptr_fields[] = {
+ {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE},
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE}
+};
+const uint16_t n_naptr_fields = sizeof(naptr_fields) / sizeof(RdataFieldSpec);
+
+// NSEC specific: no-attribute name + varlen data
+const RdataFieldSpec nsec_fields[] = {
+ {RdataFieldSpec::DOMAIN_NAME, 0, NAMEATTR_NONE},
+ {RdataFieldSpec::VARLEN_DATA, 0, NAMEATTR_NONE}
+};
+const uint16_t n_nsec_fields = sizeof(nsec_fields) / sizeof(RdataFieldSpec);
+
+// Class IN encode specs. This gives a shortcut to the encode spec for
+// some well-known types of RDATA specific to class IN (most of which are
+// generic and can be used for other classes). The array index is the
+// RR type code.
+const RdataEncodeSpec encode_spec_list_in[] = {
+ generic_data_spec, // #0: (NONE)
+ {n_ipv4_fields, 0, 0, single_ipv4_fields}, // #1: A
+ {n_single_name_fields, 1, 0, single_compadditional_name_fields}, // #2: NS
+ generic_data_spec, // #3
+ generic_data_spec, // #4
+ {n_single_name_fields, 1, 0, single_compressible_name_fields}, // #5: CNAME
+ {n_soa_fields, 2, 0, soa_fields}, // #6: SOA
+ generic_data_spec, // #7
+ generic_data_spec, // #8
+ generic_data_spec, // #9
+ generic_data_spec, // #10
+ generic_data_spec, // #11
+ {n_single_name_fields, 1, 0, single_compressible_name_fields}, // #12: PTR
+ generic_data_spec, // #13: HINFO
+ {n_double_name_fields, 2, 0, double_compressible_name_fields}, // #14:HINFO
+ {n_mx_fields, 1, 0, mx_fields}, // #15: MX
+ generic_data_spec, // #16: TXT
+ {n_double_name_fields, 2, 0, double_noattr_name_fields}, // 17: RP
+ {n_afsdb_fields, 1, 0, afsdb_fields}, // #18: AFSDB
+ // #19-#26
+ generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
+ generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
+ generic_data_spec, // #27
+ {n_ipv6_fields, 0, 0, single_ipv6_fields}, // #28: AAAA
+ // #29-#32
+ generic_data_spec, generic_data_spec, generic_data_spec, generic_data_spec,
+ {n_srv_fields, 1, 0, srv_fields}, // #33: SRV
+ generic_data_spec, // #34
+ {n_naptr_fields, 1, 1, naptr_fields}, // #35: NAPTR
+ generic_data_spec, // #36
+ generic_data_spec, // #37
+ generic_data_spec, // #38
+ {n_single_name_fields, 1, 0, single_noattr_name_fields}, // #39 DNAME
+ generic_data_spec, // #40
+ generic_data_spec, // #41 (OPT)
+ generic_data_spec, // #42
+ generic_data_spec, // #43: DS (this is opaque for encoding purposes)
+ generic_data_spec, // #44: SSHFP (this is opaque for encoding purposes)
+ generic_data_spec, // #45
+ generic_data_spec, // #46: RRSIG (this is opaque for encoding purposes)
+ {n_nsec_fields, 1, 1, nsec_fields} // #47: NSEC
+
+ // All others can be treated as single-field variable length data, at
+ // least for currently supported RR types.
+};
+
+// # of entries in encode_spec_list_in
+const size_t encode_spec_list_in_size =
+ sizeof(encode_spec_list_in) / sizeof(encode_spec_list_in[0]);
+BOOST_STATIC_ASSERT(encode_spec_list_in_size == 48);
+
+}
+
+/// \brief Get the spec for given class and type
+const RdataEncodeSpec&
+getRdataEncodeSpec(const RRClass& rrclass, const RRType& rrtype) {
+ // Special case: for classes other than IN, we treat RDATA of RR types
+ // that are class-IN specific as generic opaque data.
+ if (rrclass != RRClass::IN() &&
+ (rrtype == RRType::A() || rrtype == RRType::AAAA() ||
+ rrtype == RRType::SRV())) {
+ return (generic_data_spec);
+ }
+
+ // Otherwise, if the type is in the pre-defined range, we use the defined
+ // spec; otherwise we treat it as opaque data.
+ const uint16_t typecode = rrtype.getCode();
+ if (typecode < encode_spec_list_in_size) {
+ return (encode_spec_list_in[rrtype.getCode()]);
+ }
+ return (generic_data_spec);
+}
+
+namespace {
+
+// This class is a helper for RdataEncoder to divide the content of RDATA
+// fields for encoding by "abusing" the message rendering logic.
+// The idea is to identify domain name fields in the writeName() method,
+// while keeping track of the size and position of other types of data
+// around the names.
+//
+// Technically, this use of inheritance may be considered a violation of
+// Liskov Substitution Principle in that it doesn't actually compress domain
+// names, and some of the methods are not expected to be used.
+// In fact, skip() or trim() may not be make much sense in this context.
+// Nevertheless we keep this idea at the moment. Since the usage is limited
+// (it's only used within this file, and only used with \c Rdata variants),
+// it's hopefully an acceptable practice.
+class RdataFieldComposer : public AbstractMessageRenderer {
+public:
+ RdataFieldComposer() : last_data_pos_(0), encode_spec_(NULL),
+ current_field_(0)
+ {}
+ virtual ~RdataFieldComposer() {}
+ virtual bool isTruncated() const { return (false); }
+ virtual size_t getLengthLimit() const { return (65535); }
+ virtual CompressMode getCompressMode() const { return (CASE_INSENSITIVE); }
+ virtual void setTruncated() {}
+ virtual void setLengthLimit(size_t) {}
+ virtual void setCompressMode(CompressMode) {}
+
+ // Called for each domain name in the RDATA, from the RDATA's toWire()
+ // implementation.
+ virtual void writeName(const Name& name, bool compress) {
+ // First, see if we have other data already stored in the renderer's
+ // buffer, and handle it appropriately.
+ updateOtherData();
+
+ // Then, we should still have a field in the spec, and it must be a
+ // domain name field.
+ if (current_field_ >= encode_spec_->field_count) {
+ isc_throw(BadValue,
+ "RDATA encoder encounters an unexpected name data: " <<
+ name);
+ }
+ const RdataFieldSpec& field =
+ encode_spec_->fields[current_field_++];
+ // Since we know we've passed any prior data field, the next field
+ // must be a domain name as long as it exists; otherwise it's a bug
+ // in the spec (not a bogus input). So we assert() that condition.
+ assert(field.type == RdataFieldSpec::DOMAIN_NAME);
+
+ // It would be compressed iff the field has that attribute.
+ if (compress !=
+ ((field.name_attributes & NAMEATTR_COMPRESSIBLE) != 0)) {
+ isc_throw(BadValue, "RDATA encoder error, inconsistent name "
+ "compression policy: " << name);
+ }
+
+ const LabelSequence labels(name);
+ labels.serialize(labels_placeholder_, sizeof(labels_placeholder_));
+ writeData(labels_placeholder_, labels.getSerializedLength());
+
+ last_data_pos_ += labels.getSerializedLength();
+ }
+ // Clear all internal states and resources for a new set of RDATA.
+ void clearLocal(const RdataEncodeSpec* encode_spec) {
+ AbstractMessageRenderer::clear();
+ encode_spec_ = encode_spec;
+ data_lengths_.clear();
+ last_data_pos_ = 0;
+ }
+ // Called at the beginning of an RDATA.
+ void startRdata() {
+ current_field_ = 0;
+ }
+ // Called at the end of an RDATA.
+ void endRdata() {
+ // Handle any remaining data (there should be no more name). Then
+ // we should reach the end of the fields.
+ updateOtherData();
+ if (current_field_ != encode_spec_->field_count) {
+ isc_throw(BadValue,
+ "RDATA encoder didn't find all expected fields");
+ }
+ }
+
+ // Hold the lengths of variable length fields, in the order of their
+ // appearance. For convenience, allow the encoder to refer to it
+ // directly.
+ vector<uint16_t> data_lengths_;
+
+private:
+ // We use generict write* methods, with the exception of writeName.
+ // So new data can arrive without us knowing it, this considers all new
+ // data to be just data, checking consistency with the field spec, and
+ // if it contains variable-length field, record its length.
+ size_t last_data_pos_;
+ void updateOtherData() {
+ // If we've reached the end of the fields or we are expecting a
+ // domain name, there's nothing to do here.
+ if (current_field_ >= encode_spec_->field_count ||
+ encode_spec_->fields[current_field_].type ==
+ RdataFieldSpec::DOMAIN_NAME) {
+ return;
+ }
+
+ const size_t cur_pos = getLength();
+ const size_t data_len = cur_pos - last_data_pos_;
+
+ const RdataFieldSpec& field = encode_spec_->fields[current_field_];
+ if (field.type == RdataFieldSpec::FIXEDLEN_DATA) {
+ // The data length of a fixed length field must be the one
+ // specified in the field spec.
+ if (data_len != field.fixeddata_len) {
+ isc_throw(BadValue,
+ "RDATA encoding: available data too short for the "
+ "type");
+ }
+ } else {
+ // For encoding purposes, a variable-length data field is
+ // a single field covering all data, even if it may
+ // consist of multiple fields as DNS RDATA (e.g. TXT).
+ if (data_len > 0xffff) {
+ isc_throw(RdataEncodingError, "RDATA field is too large: "
+ << data_len << " bytes");
+ }
+ data_lengths_.push_back(data_len);
+ }
+
+ ++current_field_;
+ last_data_pos_ = cur_pos;
+ }
+
+ // The RDATA field spec of the current session. Set at the beginning of
+ // each session.
+ const RdataEncodeSpec* encode_spec_;
+ // the RDATA field (for encoding) currently handled. Reset to 0 for
+ // each RDATA of the session.
+ size_t current_field_;
+ // Placeholder to convert a name object to a label sequence.
+ uint8_t labels_placeholder_[LabelSequence::MAX_SERIALIZED_LENGTH];
+};
+
+} // end of unnamed namespace
+
+struct RdataEncoder::RdataEncoderImpl {
+ RdataEncoderImpl() : encode_spec_(NULL), rrsig_buffer_(0),
+ rdata_count_(0)
+ {}
+
+ const RdataEncodeSpec* encode_spec_; // encode spec of current RDATA set
+ RdataFieldComposer field_composer_;
+ util::OutputBuffer rrsig_buffer_;
+ size_t rdata_count_;
+ vector<uint16_t> rrsig_lengths_;
+};
+
+RdataEncoder::RdataEncoder() :
+ impl_(new RdataEncoderImpl)
+{}
+
+RdataEncoder::~RdataEncoder() {
+ delete impl_;
+}
+
+void
+RdataEncoder::start(RRClass rrclass, RRType rrtype) {
+ if (rrtype == RRType::RRSIG()) {
+ isc_throw(BadValue, "RRSIG cannot be encoded as main RDATA type");
+ }
+
+ impl_->encode_spec_ = &getRdataEncodeSpec(rrclass, rrtype);
+ impl_->field_composer_.clearLocal(impl_->encode_spec_);
+ impl_->rrsig_buffer_.clear();
+ impl_->rdata_count_ = 0;
+ impl_->rrsig_lengths_.clear();
+}
+
+void
+RdataEncoder::addRdata(const rdata::Rdata& rdata) {
+ if (impl_->encode_spec_ == NULL) {
+ isc_throw(InvalidOperation,
+ "RdataEncoder::addRdata performed before start");
+ }
+
+ impl_->field_composer_.startRdata();
+ rdata.toWire(impl_->field_composer_);
+ impl_->field_composer_.endRdata();
+ ++impl_->rdata_count_;
+}
+
+void
+RdataEncoder::addSIGRdata(const rdata::Rdata& sig_rdata) {
+ if (impl_->encode_spec_ == NULL) {
+ isc_throw(InvalidOperation,
+ "RdataEncoder::addSIGRdata performed before start");
+ }
+ const size_t cur_pos = impl_->rrsig_buffer_.getLength();
+ sig_rdata.toWire(impl_->rrsig_buffer_);
+ const size_t rrsig_datalen = impl_->rrsig_buffer_.getLength() - cur_pos;
+ if (rrsig_datalen > 0xffff) {
+ isc_throw(RdataEncodingError, "RRSIG is too large: "
+ << rrsig_datalen << " bytes");
+ }
+ impl_->rrsig_lengths_.push_back(rrsig_datalen);
+}
+
+size_t
+RdataEncoder::getStorageLength() const {
+ if (impl_->encode_spec_ == NULL) {
+ isc_throw(InvalidOperation,
+ "RdataEncoder::getStorageLength performed before start");
+ }
+
+ return (sizeof(uint16_t) * impl_->field_composer_.data_lengths_.size() +
+ sizeof(uint16_t) * impl_->rrsig_lengths_.size() +
+ impl_->rrsig_buffer_.getLength() +
+ impl_->field_composer_.getLength());
+}
+
+void
+RdataEncoder::encode(void* buf, size_t buf_len) const {
+ if (impl_->encode_spec_ == NULL) {
+ isc_throw(InvalidOperation,
+ "RdataEncoder::encode performed before start");
+ }
+ if (buf == NULL) {
+ isc_throw(BadValue,
+ "RdataEncoder::encode NULL buffer is given");
+ }
+ if (getStorageLength() > buf_len) {
+ isc_throw(BadValue, "RdataEncoder::encode short buffer given");
+ }
+
+ uint8_t* const dp_beg = reinterpret_cast<uint8_t*>(buf);
+ uint8_t* dp = dp_beg;
+ uint16_t* lenp = reinterpret_cast<uint16_t*>(buf);
+
+ // Encode list of lengths for variable length fields (if any)
+ if (!impl_->field_composer_.data_lengths_.empty()) {
+ const size_t varlen_fields_len =
+ impl_->field_composer_.data_lengths_.size() * sizeof(uint16_t);
+ std::memcpy(lenp, &impl_->field_composer_.data_lengths_[0],
+ varlen_fields_len);
+ lenp += impl_->field_composer_.data_lengths_.size();
+ dp += varlen_fields_len;
+ }
+ // Encode list of lengths for RRSIGs (if any)
+ if (!impl_->rrsig_lengths_.empty()) {
+ const size_t rrsigs_len =
+ impl_->rrsig_lengths_.size() * sizeof(uint16_t);
+ std::memcpy(lenp, &impl_->rrsig_lengths_[0], rrsigs_len);
+ dp += rrsigs_len;
+ }
+ // Encode main RDATA
+ std::memcpy(dp, impl_->field_composer_.getData(),
+ impl_->field_composer_.getLength());
+ dp += impl_->field_composer_.getLength();
+ // Encode RRSIGs, if any
+ std::memcpy(dp, impl_->rrsig_buffer_.getData(),
+ impl_->rrsig_buffer_.getLength());
+ dp += impl_->rrsig_buffer_.getLength();
+
+ // The validation at the entrance must ensure this
+ assert(buf_len >= dp - dp_beg);
+}
+
+RdataReader::RdataReader(const RRClass& rrclass, const RRType& rrtype,
+ const void* data,
+ size_t rdata_count, size_t sig_count,
+ const NameAction& name_action,
+ const DataAction& data_action) :
+ name_action_(name_action),
+ data_action_(data_action),
+ spec_(getRdataEncodeSpec(rrclass, rrtype)),
+ var_count_total_(spec_.varlen_count * rdata_count),
+ sig_count_(sig_count),
+ spec_count_(spec_.field_count * rdata_count),
+ // The lenghts are stored first
+ lengths_(reinterpret_cast<const uint16_t*>(data)),
+ // And the data just after all the lengths
+ data_(reinterpret_cast<const uint8_t*>(data) +
+ (var_count_total_ + sig_count_) * sizeof(uint16_t)),
+ sigs_(NULL)
+{
+ rewind();
+}
+
+void
+RdataReader::rewind() {
+ data_pos_ = 0;
+ spec_pos_ = 0;
+ length_pos_ = 0;
+ sig_data_pos_ = 0;
+ sig_pos_ = 0;
+}
+
+RdataReader::Boundary
+RdataReader::nextInternal(const NameAction& name_action,
+ const DataAction& data_action)
+{
+ if (spec_pos_ < spec_count_) {
+ const RdataFieldSpec& spec(spec_.fields[(spec_pos_++) %
+ spec_.field_count]);
+ if (spec.type == RdataFieldSpec::DOMAIN_NAME) {
+ const LabelSequence sequence(data_ + data_pos_);
+ data_pos_ += sequence.getSerializedLength();
+ name_action(sequence, spec.name_attributes);
+ } else {
+ const size_t length(spec.type == RdataFieldSpec::FIXEDLEN_DATA ?
+ spec.fixeddata_len : lengths_[length_pos_++]);
+ const uint8_t* const pos = data_ + data_pos_;
+ data_pos_ += length;
+ data_action(pos, length);
+ }
+ return (spec_pos_ % spec_.field_count == 0 ?
+ RDATA_BOUNDARY : NO_BOUNDARY);
+ } else {
+ sigs_ = data_ + data_pos_;
+ return (RRSET_BOUNDARY);
+ }
+}
+
+RdataReader::Boundary
+RdataReader::next() {
+ return (nextInternal(name_action_, data_action_));
+}
+
+namespace {
+
+void
+emptyNameAction(const LabelSequence&, unsigned) {
+ // Do nothing here.
+}
+
+void
+emptyDataAction(const void*, size_t) {
+ // Do nothing here.
+}
+
+}
+
+RdataReader::Boundary
+RdataReader::nextSig() {
+ if (sig_pos_ < sig_count_) {
+ if (sigs_ == NULL) {
+ // We didn't find where the signatures start yet. We do it
+ // by iterating the whole data and then returning the state
+ // back.
+ const size_t data_pos = data_pos_;
+ const size_t spec_pos = spec_pos_;
+ const size_t length_pos = length_pos_;
+ // When the next() gets to the last item, it sets the sigs_
+ while (nextInternal(emptyNameAction, emptyDataAction) !=
+ RRSET_BOUNDARY) {}
+ assert(sigs_ != NULL);
+ // Return the state
+ data_pos_ = data_pos;
+ spec_pos_ = spec_pos;
+ length_pos_ = length_pos;
+ }
+ // Extract the result
+ const size_t length = lengths_[var_count_total_ + sig_pos_];
+ const uint8_t* const pos = sigs_ + sig_data_pos_;
+ // Move the position of iterator.
+ sig_data_pos_ += lengths_[var_count_total_ + sig_pos_];
+ ++sig_pos_;
+ // Call the callback
+ data_action_(pos, length);
+ return (RDATA_BOUNDARY);
+ } else {
+ return (RRSET_BOUNDARY);
+ }
+}
+
+size_t
+RdataReader::getSize() const {
+ size_t storage_size = 0; // this will be the end result
+ size_t data_pos = 0;
+ size_t length_pos = 0;
+
+ // Go over all data fields, adding their lengths to storage_size
+ for (size_t spec_pos = 0; spec_pos < spec_count_; ++spec_pos) {
+ const RdataFieldSpec& spec =
+ spec_.fields[spec_pos % spec_.field_count];
+ if (spec.type == RdataFieldSpec::DOMAIN_NAME) {
+ const size_t seq_len =
+ LabelSequence(data_ + data_pos).getSerializedLength();
+ data_pos += seq_len;
+ storage_size += seq_len;
+ } else {
+ const size_t data_len =
+ (spec.type == RdataFieldSpec::FIXEDLEN_DATA ?
+ spec.fixeddata_len : lengths_[length_pos++]);
+ data_pos += data_len;
+ storage_size += data_len;
+ }
+ }
+ // Same for all RRSIG data
+ for (size_t sig_pos = 0; sig_pos < sig_count_; ++sig_pos) {
+ const size_t sig_data_len = lengths_[length_pos++];
+ storage_size += sig_data_len;
+ }
+
+ // Finally, add the size for 16-bit length fields
+ storage_size += (var_count_total_ * sizeof(uint16_t) +
+ sig_count_ * sizeof(uint16_t));
+
+ return (storage_size);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/datasrc/memory/rdata_serialization.h b/src/lib/datasrc/memory/rdata_serialization.h
new file mode 100644
index 0000000..a039e60
--- /dev/null
+++ b/src/lib/datasrc/memory/rdata_serialization.h
@@ -0,0 +1,512 @@
+// 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_RDATA_ENCODER_H
+#define DATASRC_MEMORY_RDATA_ENCODER_H 1
+
+#include <exceptions/exceptions.h>
+
+#include <dns/labelsequence.h>
+#include <dns/rdata.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <vector>
+
+/// \file rdata_serialization.h
+///
+/// This file defines a set of interfaces (classes, types, constants) to
+/// manipulate a given set of RDATA of the same type (normally associated with
+/// an RRset) that may be accompanied with RRSIGs in a memory efficient way.
+///
+/// The entire set of RDATA is stored in a packed form in a contiguous
+/// memory region. It's opaque data, without containing non trivial
+/// data structures, so it can be located anywhere in the memory or even
+/// dumped to a file.
+///
+/// Two main classes are provided: one is
+/// \c isc::datasrc::memory::RdataEncoder, which allows
+/// the application to create encoded data for a set of RDATA;
+/// the isc::datasrc::memory::RdataReader provides an interface to iterate
+/// over encoded set of RDATA for purposes such as data lookups or rendering
+/// the data into the wire format to create a DNS message.
+///
+/// The actual encoding detail is private information to the implementation,
+/// and the application shouldn't assume anything about that except that
+/// each RDATA is considered to consist of one or more generic fields,
+/// and each field is typed as either opaque data or a domain name.
+/// A domain name field has additional attributes
+/// (see \c isc::datasrc::memory::RdataNameAttributes)
+/// so the application can change how the name should be handled in terms
+/// of the DNS protocol (e.g., whether it's subject to name compression).
+///
+/// The following are the current implementation of internal encoding, shown
+/// only for reference. Applications must not assume this particular form
+/// for the encoded data; in fact, it can change in a future version of the
+/// implementation.
+/// \verbatim
+// The encoded data begin with a series of 16-bit length fields (values are
+// stored in the host byte order). The sequence may be empty.
+// uint16_t n1_1: size of 1st variable len field (if any) of 1st RDATA
+// uint16_t n1_2: size of 2nd variable len field of 1st RDATA
+// ...
+// uint16_t nN_M: size of last (Mth) variable len field of last (Nth) RDATA
+// uint16_t ns1: size of 1st RRSIG (if any) data
+// ...
+// uint16_t nsL: size of last (Lth) RRSIG data
+// A sequence of packed data fields follows:
+// uint8_t[]: data field value, length specified by nI_J (in case it's
+// variable-length) or by the per type field spec (in case it's
+// fixed-length).
+// or
+// opaque data, LabelSequence::getSerializedLength() bytes: data for a name
+// uint8_t[ns1]: 1st RRSIG data
+// ...
+// uint8_t[nsL]: last RRSIG data
+// \endverbatim
+///
+/// As described above, this implementation treats RRSIGs as opaque data
+/// that don't contain any domain names. Technically, it has a "signer"
+/// domain name field in the sense of RFC4034. In practice, however, this
+/// field is essentially mere data; it's not subject to name compression,
+/// and since it's very likely to be a subdomain of (or equal to) the
+/// owner name of the corresponding RR (or, if used in a DNS message,
+/// some domain name that already appears before this field), so it won't
+/// be a target of name compression either. By treating the entire RRSIG
+/// as single-field data we can make the implementation simpler, and probably
+/// make it faster in rendering it into a DNS message.
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief General error in RDATA encoding.
+///
+/// This is thrown when \c RdataEncoder encounters a rare, unsupported
+/// situation. a method is called for a name or RRset which
+/// is not in or below the zone.
+class RdataEncodingError : public Exception {
+public:
+ RdataEncodingError(const char* file, size_t line, const char* what) :
+ Exception(file, line, what) {}
+};
+
+/// \brief RDATA encoder.
+///
+/// This class provides interfaces to encode a set of RDATA of a specific
+/// RR class and type, possibly with their RRSIG RDATAs, in a memory-efficient
+/// format. In many cases these sets of RDATA come from a specific (signed
+/// or unsigned) RRset.
+///
+/// It is expected for a single \c RdataEncoder object to be used multiple
+/// times for different sets of RDATA, such as in loading an entire zone
+/// into memory. Each encoding session begins with the \c start() method,
+/// which sets the context for the specific RR class and type to be encoded.
+/// Any number of calls to \c addRdata() or \c addSIGRdata() follow, each
+/// of which updates the internal state of the encoder with the encoding
+/// information for the given RDATA or RRSIG RDATA, respectively.
+/// The \c addRdata() is expected to be called with an
+/// \c isc::dns::rdata::Rdata object
+/// of the specified class and type, and \c addRdata() checks the consistency
+/// for the purpose of encoding (but it's not completely type safe; for
+/// example, it wouldn't distinguish TXT RDATA and HINFO RDATA.
+/// Likewise, an \c isc::dns::rdata::Rdata given to \c addSIGRdata() is
+/// expected to be of RRSIG, but the method does not check the assumption).
+///
+/// After passing the complete set of RDATA and their RRSIG, the application
+/// is expected to call \c getStorageLength() to know the size of storage
+/// that is sufficient to store all encoded data. Normally the application
+/// would allocate a memory region of that size, and then call \c encode()
+/// with the prepared region. The \c encode() method dumps encoded data
+/// to the given memory region.
+///
+/// The caller can reuse the \c RdataEncoder object for another set of RDATA
+/// by repeating the session from \c start().
+class RdataEncoder : boost::noncopyable {
+public:
+ /// \brief Default constructor.
+ RdataEncoder();
+
+ /// \brief The destrcutor.
+ ~RdataEncoder();
+
+ /// \brief Start the encoding session.
+ ///
+ /// It re-initializes the internal encoder state for a new encoding
+ /// session. The \c rrclass and \c rrtype parameters specify the
+ /// type of RDATA to be encoded in the new session. Note that if the
+ /// set of RDATA is signed, \c rrtype always specifies the "signed" type;
+ /// it must not be RRSIG.
+ ///
+ /// \throw BadValue RRSIG is specified for rrtype.
+ ///
+ /// \param rrclass The RR class of RDATA to be encoded in the session.
+ /// \param rrtype The RR type of RDATA to be encoded in the session.
+ void start(dns::RRClass rrclass, dns::RRType rrtype);
+
+ /// \brief Add an RDATA for encoding.
+ ///
+ /// This method updates internal state of the \c RdataEncoder() with the
+ /// given RDATA so it will be part of the encoded data in a subsequent
+ /// call to \c encode().
+ ///
+ /// The given \c rdata must be of the RR class and type specified at
+ /// the prior call to \c start(). This method checks the assumption
+ /// to some extent, but the check is not complete; this is generally
+ /// the responsibility of the caller.
+ ///
+ /// The caller can destroy \c rdata after this call is completed.
+ ///
+ /// \note This implementation does not support RDATA (or any subfield of
+ /// it) whose size exceeds 65535 bytes (max uint16_t value). Such RDATA
+ /// may not necessarily be considered invalid in terms of protocol
+ /// specification, but in practice it's mostly useless because the
+ /// corresponding RR won't fit in any valid DNS message.
+ ///
+ /// As long as the \c rdata is of the correct type and its size is normal,
+ /// this method should normally be exception free. If it throws, however,
+ /// it doesn't always provide the strong exception guarantee. In general,
+ /// the caller needs to either destroy the encoder object or restart a
+ /// new session from \c start() should this method throws an exception.
+ ///
+ /// \throw InvalidOperation called before start().
+ /// \throw BadValue inconsistent data found.
+ /// \throw RdataEncodingError A very unusual case, such as over 64KB RDATA.
+ /// \throw std::bad_alloc Internal memory allocation failure.
+ ///
+ /// \param rdata An RDATA to be encoded in the session.
+ void addRdata(const dns::rdata::Rdata& rdata);
+
+ /// \brief Add an RRSIG RDATA for encoding.
+ ///
+ /// This method updates internal state of the \c RdataEncoder() with the
+ /// given RDATA, which is assumed to be of type RRSIG that covers the
+ /// type specified at the time of \c start() for the encoding session.
+ /// The corresponding data for the RRSIG RDATA will be encoded in a
+ /// subsequent call to \c encode().
+ ///
+ /// The passed \c sig_rdata is expected to be of type RRSIG and cover
+ /// the RR type specified at the call to \c start() to this encoding
+ /// session. But this method does not check if it is the case at all;
+ /// it could even accept any type of RDATA as opaque data. It's caller's
+ /// responsibility to ensure the assumption.
+ ///
+ /// The caller can destroy \c rdata after this call is completed.
+ ///
+ /// \note Like addRdata(), this implementation does not support
+ /// RRSIG RDATA whose size (in the form of wire format) exceeds 65535
+ /// bytes.
+ ///
+ /// The same note about exception safety as \c addRdata() applies.
+ ///
+ /// \throw InvalidOperation called before start().
+ /// \throw RdataEncodingError A very unusual case, such as over 64KB RDATA.
+ /// \throw std::bad_alloc Internal memory allocation failure.
+ ///
+ /// \param sig_rdata An RDATA to be encoded in the session. Supposed to
+ /// be of type RRSIG.
+ void addSIGRdata(const dns::rdata::Rdata& sig_rdata);
+
+ /// \brief Return the length of space for encoding for the session.
+ ///
+ /// It returns the size of the encoded data that would be generated for
+ /// the set of RDATA (and RRSIGs) in the encoder at the call of this
+ /// method. It's ensured that a buffer of that size can be safely passed
+ /// to \c encode() unless there's no other "add" method is called by then.
+ ///
+ /// As long as this method is called after start(), it never throws.
+ ///
+ /// \throw InvalidOperation called before start().
+ ///
+ /// \return The expected size of the encoded data at the time of the call.
+ size_t getStorageLength() const;
+
+ /// \brief Encode RDATAs of the session to a buffer.
+ ///
+ /// This method dumps encoded data for the stored set of RDATA and
+ /// their RRSIGs to a given buffer. The buffer must have a size
+ /// at least as large as the return value of a prior call to
+ /// \c getStorageLength() (it may be larger than that).
+ ///
+ /// The given buffer must be aligned at the natural boundary for
+ /// 16-bit integers. The method doesn't check this condition; it's
+ /// caller's responsibility to ensure that. Note: the alignment
+ /// requirement may change in a future version of this implementation.
+ ///
+ /// As long as this method is called after start() and the buffer is
+ /// valid with a sufficient size, this method never throws.
+ ///
+ /// \throw InvalidOperation called before start().
+ /// \throw BadValue buffer is NULL or it's too short for the encoded data.
+ ///
+ /// \param buf A pointer to the buffer to which encoded data are to be
+ /// dumped.
+ /// \param buf_len The size of the buffer in bytes.
+ void encode(void* buf, size_t buf_len) const;
+
+private:
+ struct RdataEncoderImpl;
+ RdataEncoderImpl* impl_;
+};
+
+/// \brief Attributes of domain name fields of encoded RDATA.
+///
+/// The enum values define special traits of the name that can affect how
+/// it should be handled in rendering or query processing.
+enum RdataNameAttributes {
+ NAMEATTR_NONE = 0, ///< No special attributes
+ NAMEATTR_COMPRESSIBLE = 1, ///< Name should be compressed when rendered
+ NAMEATTR_ADDITIONAL = (NAMEATTR_COMPRESSIBLE << 1) ///< Name requires
+ ///< Additional section
+ ///< handling
+};
+
+// forward declaration, defined in a private implementation file.
+struct RdataEncodeSpec;
+
+/// \brief Class to read serialized rdata
+///
+/// This class allows you to read the data encoded by RdataEncoder.
+/// It is rather low-level -- it provides sequence of data fields.
+/// Each field is either opaque data, passed as a pointer and length,
+/// or a name, in the form of dns::LabelSequence (which is always
+/// absolute) and attributes.
+///
+/// Conceptually, these fields correspond to consecutive regions in
+/// wire-format representation of the RDATA, varying the type of above
+/// two cases depending on whether the region corresponds to a domain
+/// name or other data. For example, for an MX RDATA the field
+/// sequence will be
+/// - 2 bytes of opaque data (which corresponds to the MX preference)
+/// - a domain name (which corresponds to the MX name)
+///
+/// If the encoded data contain multiple MX RDATAs, the same type of
+/// sequence continues for the number of RDATAs. Note that the opaque
+/// data field does not always corresponds to a specific RDATA field
+/// as is the 2-byte preference field of MX. For example, the field
+/// sequence for an SOA RDATA in terms of RdataEncoder will be:
+/// - a domain name (which corresponds to the SOA MNAME)
+/// - a domain name (which corresponds to the SOA RNAME)
+/// - 20 bytes of opaque data (for the rest of fields)
+///
+/// So, if you want to construct a general purpose dns::Rdata object
+/// from the field sequence, you'll need to build the complete
+/// wire-format data, and then construct a dns::Rdata object from it.
+///
+/// To use it, contstruct it with the data you got from RDataEncoder,
+/// provide it with callbacks and then iterate through the data.
+/// The callbacks are called with the data fields contained in the
+/// data.
+///
+/// \code
+/// void handleName(const dns::LabelSequence& labels, unsigned int flags) {
+/// ...
+/// }
+/// void handleData(const void* data, size_t size) {
+/// ...
+/// }
+///
+/// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data,
+/// rdata_count, sig_count, &handleName, &handleData);
+/// reader.iterate();
+/// \endcode
+///
+/// If you need to do the iteration per RDATA basis rather than per data field
+/// basis, you can use \c iterateRdata() as follows:
+///
+/// \code
+/// for (size_t i = 0; i < rdata_count; ++i)
+/// // maybe do something related to this RDATA
+/// reader.iterateRdata(); // specified actions called for this RDATA
+/// // maybe do some other thing related to this RDATA
+/// }
+/// if (reader.iterateRdata()) {
+/// isc_throw(Unexpected, "Inconsistent data");
+/// }
+/// \endcode
+///
+/// The check after the loop is primarily for consistency
+/// validation, but it would also help a possible subsequent call
+/// to \c iterateAllSigs() if you also want to iterate over RRSIGs;
+/// the final call to \c iterateRdata() updates the internal state of the
+/// reader object so \c iterateAllSigs() can find the RRSIG data more
+/// efficiently. \c iterateAllSigs() will work correctly even with out
+/// this small optimization, but checking the consistency is a good practice
+/// anyway, and the optimization is an additional bonus.
+///
+/// \note It is caller's responsibility to pass valid data here. This means
+/// the data returned by RdataEncoder and the corresponding class and type.
+/// If this is not the case, all the kinds of pointer hell might get loose.
+class RdataReader {
+public:
+ /// \brief Function called on each name encountered in the data.
+ typedef boost::function<void(const dns::LabelSequence&,
+ RdataNameAttributes)> NameAction;
+ /// \brief Function called on each data field in the data.
+ typedef boost::function<void(const void*, size_t)> DataAction;
+
+ /// \brief Constructor
+ ///
+ /// This constructs the reader on top of some serialized data.
+ /// It does not copy the data, you have to make sure the data
+ /// is valid for the whole life of this object and that they
+ /// don't change.
+ ///
+ /// \param rrclass The class the encoded rdata belongs to.
+ /// \param rrtype The type of the encode rdata.
+ /// \param data The actual data.
+ /// \param rdata_count The number of Rdata encoded in the data.
+ /// \param sig_count The number of RRSig rdata bundled with the data.
+ /// \param name_action The callback to be called on each encountered name.
+ /// \param data_action The callback to be called on each data chunk.
+ RdataReader(const dns::RRClass& rrclass, const dns::RRType& rrtype,
+ const void* data, size_t rdata_count, size_t sig_count,
+ const NameAction& name_action, const DataAction& data_action);
+
+ /// \brief Result of next() and nextSig()
+ ///
+ /// This specifies if there's any boundary in the data at the
+ /// place where the corresponding call to next() or nextSig()
+ /// finished.
+ enum Boundary {
+ NO_BOUNDARY, ///< It is in the middle of Rdata
+ RDATA_BOUNDARY, ///< At the end of single Rdata
+ RRSET_BOUNDARY ///< At the end of the RRset (past the end)
+ };
+
+ /// \brief Step to next data field.
+ ///
+ /// Iterate over the next field and call appropriate hook (name_action
+ /// or data_action, depending on the type) as passed to the constructor.
+ ///
+ /// \return It returns NO_BOUNDARY if the next call to next() will process
+ /// data of the same rdata as this one. RDATA_BOUNDARY is returned when
+ /// this field is the last of the current rdata. If there are no more
+ /// data to process, no hook is called and RRSET_BOUNDARY is returned.
+ /// Therefore, at the end of the whole data, once it processes the last
+ /// field and returns RDATA_BOUNDARY and then it returns RRSET_BOUNDARY
+ /// on the next call.
+ Boundary next();
+
+ /// \brief Call next() until the end.
+ ///
+ /// This is just convenience method to iterate through all the data.
+ /// It calls next until it reaches the end (it does not rewind beforehand,
+ /// therefore if you already called next() yourself, it does not start
+ /// at the beginning).
+ void iterate() {
+ while (nextInternal(name_action_, data_action_) != RRSET_BOUNDARY) {}
+ }
+
+ /// \brief Call next() until the end of current rdata.
+ ///
+ /// This is a convenience method to iterate until the end of current
+ /// rdata. Notice this may cause more than one field being processed,
+ /// as some rrtypes are more complex.
+ ///
+ /// \return If there was Rdata to iterate through.
+ bool iterateRdata() {
+ while (true) {
+ switch (nextInternal(name_action_, data_action_)) {
+ case NO_BOUNDARY: break;
+ case RDATA_BOUNDARY: return (true);
+ case RRSET_BOUNDARY: return (false);
+ }
+ }
+ }
+
+ /// \brief Step to next field of RRSig data.
+ ///
+ /// This is almost the same as next(), but it iterates through the
+ /// associated RRSig data, not the data for the given RRType.
+ Boundary nextSig();
+
+ /// \brief Iterate through all RRSig data.
+ ///
+ /// This is almost the same as iterate(), but it iterates through the
+ /// RRSig data instead.
+ void iterateAllSigs() {
+ while (nextSig() != RRSET_BOUNDARY) {}
+ }
+
+ /// \brief Iterate through the current RRSig Rdata.
+ ///
+ /// This is almote the same as iterateRdata, except it is for single
+ /// signature Rdata.
+ ///
+ /// In practice, this should process one DATA field.
+ bool iterateSingleSig() {
+ while (true) {
+ switch (nextSig()) {
+ case NO_BOUNDARY:
+ isc_throw(isc::Unexpected, "NO_BOUNDARY inside an RRSig. "
+ "Data corruption? Bug inside RdataReader?");
+ case RDATA_BOUNDARY: return (true);
+ case RRSET_BOUNDARY: return (false);
+ }
+ }
+ }
+
+ /// \brief Rewind the iterator to the beginnig of data.
+ ///
+ /// The following next() and nextSig() will start iterating from the
+ /// beginning again.
+ void rewind();
+
+ /// \brief Returns the size of associated data.
+ ///
+ /// This should be the same as the return value of
+ /// RdataEncoder::getStorageLength() for the same set of data.
+ /// The intended use of this method is to tell the caller the size of
+ /// data that were possibly dynamically allocated so that the caller can
+ /// use it for deallocation.
+ ///
+ /// This method only uses the parameters given at the construction of the
+ /// object, and does not rely on or modify other mutable states.
+ /// In practice, when the caller wants to call this method, that would be
+ /// the only purpose of that RdataReader object (although it doesn't have
+ /// to be so).
+ size_t getSize() const;
+private:
+ const NameAction name_action_;
+ const DataAction data_action_;
+ const RdataEncodeSpec& spec_;
+ // Total number of var-length fields, count of signatures
+ const size_t var_count_total_, sig_count_, spec_count_;
+ // Pointer to the beginning of length fields
+ const uint16_t* const lengths_;
+ // Pointer to the beginning of the data (after the lengths)
+ const uint8_t* const data_;
+ // Pointer to the first data signature
+ // Will be computed during the normal RR iteration
+ const uint8_t* sigs_;
+ // The positions in data.
+ size_t data_pos_, spec_pos_, length_pos_;
+ size_t sig_pos_, sig_data_pos_;
+ Boundary nextInternal(const NameAction& name_action,
+ const DataAction& data_action);
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_RDATA_ENCODER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/rdata_serialization_priv.cc b/src/lib/datasrc/memory/rdata_serialization_priv.cc
new file mode 100644
index 0000000..2974079
--- /dev/null
+++ b/src/lib/datasrc/memory/rdata_serialization_priv.cc
@@ -0,0 +1,65 @@
+// 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.
+
+// This file is directly included from the rdata_serialization.cc. It would
+// be part of the file if we didn't need to steal some definitions from here
+// for the tests (which include it too). To make the intent clear, it is
+// named .cc, not .h.
+
+struct RdataFieldSpec {
+ enum FieldType {
+ FIXEDLEN_DATA = 0, // fixed-length data field
+ VARLEN_DATA, // variable-length data field
+ DOMAIN_NAME // domain name
+ };
+
+ const FieldType type; // field type
+
+ // The length of fixed-length data field. Only valid for FIXEDLEN_DATA.
+ // For type DOMAIN_NAME, set it to 0.
+ const uint16_t fixeddata_len;
+
+ // Attributes of the name. Only valid for DOMAIN_NAME.
+ // For type _DATA, set it to NAMEATTR_NONE.
+ const RdataNameAttributes name_attributes;
+};
+
+/// Specification of RDATA in terms of internal encoding.
+///
+/// The fields must be a sequence of:
+/// <0 or 1 fixed/var-len data field>,
+/// <1 or more domain name fields>,
+/// <1 fixed/var-len data field>,
+/// <1 or more domain name fields>,
+/// <1 fixed/var-len data field>,
+/// ...and so on.
+/// There must not be more than one consecutive data fields (i.e., without
+/// interleaved by a domain name); it would just be inefficient in terms of
+/// memory footprint and iterating over the fields, and it would break
+/// some assumption within the encoder implementation. For consecutive
+/// data fields in the DNS protocol, if all fields have fixed lengths, they
+/// should be combined into a single fixed-length field (like the last 20
+/// bytes of SOA RDATA). If there's a variable length field, they should be
+/// combined into a single variable-length field (such as DNSKEY, which has
+/// 3 fixed-length fields followed by one variable-length field).
+struct RdataEncodeSpec {
+ const uint16_t field_count; // total number of fields (# of fields member)
+ const uint16_t name_count; // number of domain name fields
+ const uint16_t varlen_count; // number of variable-length data fields
+ const RdataFieldSpec* const fields; // list of field specs
+};
+
+/// \brief Get the spec for given class and type
+const RdataEncodeSpec&
+getRdataEncodeSpec(const RRClass& rrclass, const RRType& rrtype);
diff --git a/src/lib/datasrc/memory/tests/Makefile.am b/src/lib/datasrc/memory/tests/Makefile.am
index 2275d84..6195cc7 100644
--- a/src/lib/datasrc/memory/tests/Makefile.am
+++ b/src/lib/datasrc/memory/tests/Makefile.am
@@ -18,7 +18,7 @@ if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += rdata_encoder_unittest.cc
+run_unittests_SOURCES += rdata_serialization_unittest.cc
run_unittests_SOURCES += domaintree_unittest.cc
run_unittests_SOURCES += zone_table_unittest.cc
diff --git a/src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc b/src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc
deleted file mode 100644
index 2854c37..0000000
--- a/src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc
+++ /dev/null
@@ -1,481 +0,0 @@
-// 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/name.h>
-#include <dns/labelsequence.h>
-#include <dns/messagerenderer.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include <datasrc/memory/rdata_encoder.h>
-
-#include <util/unittests/wiredata.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
-
-#include <cstring>
-#include <set>
-#include <string>
-#include <vector>
-
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc::memory;
-using namespace isc::datasrc::memory::testing;
-
-using isc::util::unittests::matchWireData;
-using std::string;
-using std::vector;
-
-namespace {
-// This defines a tuple of test data used in test_rdata_list below.
-struct TestRdata {
- const char* const rrclass; // RR class, textual form
- const char* const rrtype; // RR type, textual form
- const char* const rdata; // textual RDATA
- const size_t n_varlen_fields; // expected # of variable-len fields
-};
-
-// This test data consist of (almost) all supported types of RDATA (+ some
-// unusual and corner cases).
-const TestRdata test_rdata_list[] = {
- {"IN", "A", "192.0.2.1", 0},
- {"IN", "NS", "ns.example.com", 0},
- {"IN", "CNAME", "cname.example.com", 0},
- {"IN", "SOA", "ns.example.com root.example.com 0 0 0 0 0", 0},
- {"IN", "PTR", "reverse.example.com", 0},
- {"IN", "HINFO", "\"cpu-info\" \"OS-info\"", 1},
- {"IN", "MINFO", "root.example.com mbox.example.com", 0},
- {"IN", "MX", "10 mx.example.com", 0},
- {"IN", "TXT", "\"test1\" \"test 2\"", 1},
- {"IN", "RP", "root.example.com. rp-text.example.com", 0},
- {"IN", "AFSDB", "1 afsdb.example.com", 0},
- {"IN", "AAAA", "2001:db8::1", 0},
- {"IN", "SRV", "1 0 10 target.example.com", 0},
- {"IN", "NAPTR", "100 50 \"s\" \"http\" \"\" _http._tcp.example.com", 1},
- {"IN", "DNAME", "dname.example.com", 0},
- {"IN", "DS", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
- {"IN", "SSHFP", "1 1 dd465c09cfa51fb45020cc83316fff", 1},
- // We handle RRSIG separately, so it's excluded from the list
- {"IN", "NSEC", "next.example.com. A AAAA NSEC RRSIG", 1},
- {"IN", "DNSKEY", "256 3 5 FAKEFAKE", 1},
- {"IN", "DHCID", "FAKEFAKE", 1},
- {"IN", "NSEC3", "1 1 12 AABBCCDD FAKEFAKE A RRSIG", 1},
- {"IN", "NSEC3PARAM", "1 0 12 AABBCCDD", 1},
- {"IN", "SPF", "v=spf1 +mx a:colo.example.com/28 -all", 1},
- {"IN", "DLV", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
- {"IN", "TYPE65000", "\\# 3 010203", 1}, // some "custom" type
- {"IN", "TYPE65535", "\\# 0", 1}, // max RR type, 0-length RDATA
- {"CH", "A", "\\# 2 0102", 1}, // A RR for non-IN class; varlen data
- {"CH", "NS", "ns.example.com", 0}, // class CH, generic data
- {"CH", "TXT", "BIND10", 1}, // ditto
- {"HS", "A", "\\# 5 0102030405", 1}, // A RR for non-IN class; varlen data
- {NULL, NULL, NULL, 0}
-};
-
-// The following two functions will be used to generate wire format data
-// from encoded representation of each RDATA.
-void
-renderNameField(MessageRenderer* renderer, bool additional_required,
- const LabelSequence& labels, RdataNameAttributes attributes)
-{
- EXPECT_EQ(additional_required,
- (attributes & NAMEATTR_ADDITIONAL) != 0);
- renderer->writeName(labels, (attributes & NAMEATTR_COMPRESSIBLE) != 0);
-}
-
-void
-renderDataField(MessageRenderer* renderer, const uint8_t* data,
- size_t data_len)
-{
- renderer->writeData(data, data_len);
-}
-
-class RdataEncoderTest : public ::testing::Test {
-protected:
- RdataEncoderTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
- "192.0.2.53")),
- aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
- "2001:db8::53")),
- rrsig_rdata_(createRdata(
- RRType::RRSIG(), RRClass::IN(),
- "A 5 2 3600 20120814220826 "
- "20120715220826 12345 com. FAKE"))
- {}
-
- // This helper test method constructs encodes the given list of RDATAs
- // (in rdata_list), and then iterates over the data, rendering the fields
- // in the wire format. It then compares the wire data with the one
- // generated by the normal libdns++ interface to see the encoding/decoding
- // works as intended.
- void checkEncode(RRClass rrclass, RRType rrtype,
- const vector<ConstRdataPtr>& rdata_list,
- size_t expected_varlen_fields,
- const vector<ConstRdataPtr>& rrsig_list);
- // A wraper for RdataEncoder::encode() with buffer overrun check.
- void encodeWrapper(size_t data_len);
-
- void addRdataCommon(const vector<ConstRdataPtr>& rrsigs);
- void addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs);
-
- // Some commonly used RDATA
- const ConstRdataPtr a_rdata_;
- const ConstRdataPtr aaaa_rdata_;
- const ConstRdataPtr rrsig_rdata_;
-
- RdataEncoder encoder_;
- vector<uint8_t> encoded_data_;
- MessageRenderer expected_renderer_;
- MessageRenderer actual_renderer_;
- vector<ConstRdataPtr> rdata_list_;
-};
-
-
-void
-RdataEncoderTest::encodeWrapper(size_t data_len) {
- // make sure the data buffer is large enough for the canary
- encoded_data_.resize(data_len + 2);
- // set the canary data
- encoded_data_.at(data_len) = 0xde;
- encoded_data_.at(data_len + 1) = 0xad;
- // encode, then check the canary is intact
- encoder_.encode(&encoded_data_[0], data_len);
- EXPECT_EQ(0xde, encoded_data_.at(data_len));
- EXPECT_EQ(0xad, encoded_data_.at(data_len + 1));
- // shrink the data buffer to the originally expected size (some tests
- // expect that). the actual encoded data should be intact.
- encoded_data_.resize(data_len);
-}
-
-void
-RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
- const vector<ConstRdataPtr>& rdata_list,
- size_t expected_varlen_fields,
- const vector<ConstRdataPtr>& rrsig_list =
- vector<ConstRdataPtr>())
-{
- // These two names will be rendered before and after the test RDATA,
- // to check in case the RDATA contain a domain name whether it's
- // compressed or not correctly. The names in the RDATA should basically
- // a subdomain of example.com, so it can be compressed due to dummy_name.
- // Likewise, dummy_name2 should be able to be fully compressed due to
- // the name in the RDATA.
- const Name dummy_name("com");
- const Name dummy_name2("example.com");
-
- // The set of RR types that require additional section processing.
- // We'll pass it to renderNameField to check the stored attribute matches
- // our expectation.
- std::set<RRType> need_additionals;
- need_additionals.insert(RRType::NS());
- need_additionals.insert(RRType::MX());
- need_additionals.insert(RRType::SRV());
- expected_renderer_.clear();
- actual_renderer_.clear();
- encoded_data_.clear();
-
- const bool additional_required =
- (need_additionals.find(rrtype) != need_additionals.end());
-
- // Build expected wire-format data
- expected_renderer_.writeName(dummy_name);
- BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
- rdata->toWire(expected_renderer_);
- }
- expected_renderer_.writeName(dummy_name2);
- BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
- rdata->toWire(expected_renderer_);
- }
-
- // Then build wire format data using the encoded data.
- // 1st dummy name
- actual_renderer_.writeName(dummy_name);
-
- // Create encoded data
- encoder_.start(rrclass, rrtype);
- BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
- encoder_.addRdata(*rdata);
- }
- BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
- encoder_.addSIGRdata(*rdata);
- }
- encodeWrapper(encoder_.getStorageLength());
-
- // If this type of RDATA is expected to contain variable-length fields,
- // we brute force the encoded data, exploiting our knowledge of actual
- // encoding, then adjust the encoded data excluding the list of length
- // fields. This is ugly, but we should be able to eliminate this hack
- // at #2096.
- vector<uint16_t> varlen_list;
- if (expected_varlen_fields > 0) {
- const size_t varlen_list_size =
- rdata_list.size() * expected_varlen_fields * sizeof(uint16_t);
- ASSERT_LE(varlen_list_size, encoded_data_.size());
- varlen_list.resize(rdata_list.size() * expected_varlen_fields);
- std::memcpy(&varlen_list[0], &encoded_data_[0], varlen_list_size);
- encoded_data_.assign(encoded_data_.begin() + varlen_list_size,
- encoded_data_.end());
- }
-
- // If RRSIGs are given, we need to extract the list of the RRSIG lengths
- // and adjust encoded_data_ further (this will be unnecessary at #2096,
- // too).
- vector<uint16_t> rrsiglen_list;
- if (rrsig_list.size() > 0) {
- const size_t rrsig_len_size = rrsig_list.size() * sizeof(uint16_t);
- ASSERT_LE(rrsig_len_size, encoded_data_.size());
- rrsiglen_list.resize(rrsig_list.size() * rrsig_len_size);
- std::memcpy(&rrsiglen_list[0], &encoded_data_[0], rrsig_len_size);
- encoded_data_.assign(encoded_data_.begin() + rrsig_len_size,
- encoded_data_.end());
- }
-
- // Create wire-format data from the encoded data
- foreachRdataField(rrclass, rrtype, rdata_list.size(), encoded_data_,
- varlen_list,
- boost::bind(renderNameField, &actual_renderer_,
- additional_required, _1, _2),
- boost::bind(renderDataField, &actual_renderer_, _1, _2));
- // 2nd dummy name
- actual_renderer_.writeName(dummy_name2);
- // Finally, dump any RRSIGs in wire format.
- foreachRRSig(encoded_data_, rrsiglen_list,
- boost::bind(renderDataField, &actual_renderer_, _1, _2));
-
- // Two sets of wire-format data should be identical.
- matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
- actual_renderer_.getData(), actual_renderer_.getLength());
-}
-
-void
-RdataEncoderTest::addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
- // Basic check on the encoded data for (most of) all supported RR types,
- // in a comprehensive manner.
- for (size_t i = 0; test_rdata_list[i].rrclass != NULL; ++i) {
- SCOPED_TRACE(string(test_rdata_list[i].rrclass) + "/" +
- test_rdata_list[i].rrtype);
- const RRClass rrclass(test_rdata_list[i].rrclass);
- const RRType rrtype(test_rdata_list[i].rrtype);
- const ConstRdataPtr rdata = createRdata(rrtype, rrclass,
- test_rdata_list[i].rdata);
- rdata_list_.clear();
- rdata_list_.push_back(rdata);
- checkEncode(rrclass, rrtype, rdata_list_,
- test_rdata_list[i].n_varlen_fields, rrsigs);
- }
-}
-
-TEST_F(RdataEncoderTest, addRdata) {
- vector<ConstRdataPtr> rrsigs;
- addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
-
- // Test with RRSIGs (covered type doesn't always match, but the encoder
- // doesn't check that)
- rrsigs.push_back(rrsig_rdata_);
- addRdataCommon(rrsigs);
-}
-
-void
-RdataEncoderTest::addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
- // Similar to addRdata(), but test with multiple RDATAs.
- // Four different cases are tested: a single fixed-len RDATA (A),
- // fixed-len data + domain name (MX), variable-len data only (TXT),
- // variable-len data + domain name (NAPTR).
- ConstRdataPtr a_rdata2 = createRdata(RRType::A(), RRClass::IN(),
- "192.0.2.54");
- rdata_list_.clear();
- rdata_list_.push_back(a_rdata_);
- rdata_list_.push_back(a_rdata2);
- checkEncode(RRClass::IN(), RRType::A(), rdata_list_, 0, rrsigs);
-
- ConstRdataPtr mx_rdata1 = createRdata(RRType::MX(), RRClass::IN(),
- "5 mx1.example.com");
- ConstRdataPtr mx_rdata2 = createRdata(RRType::MX(), RRClass::IN(),
- "10 mx2.example.com");
- rdata_list_.clear();
- rdata_list_.push_back(mx_rdata1);
- rdata_list_.push_back(mx_rdata2);
- checkEncode(RRClass::IN(), RRType::MX(), rdata_list_, 0, rrsigs);
-
- ConstRdataPtr txt_rdata1 = createRdata(RRType::TXT(), RRClass::IN(),
- "foo bar baz");
- ConstRdataPtr txt_rdata2 = createRdata(RRType::TXT(), RRClass::IN(),
- "another text data");
- rdata_list_.clear();
- rdata_list_.push_back(txt_rdata1);
- rdata_list_.push_back(txt_rdata2);
- checkEncode(RRClass::IN(), RRType::TXT(), rdata_list_, 1, rrsigs);
-
- ConstRdataPtr naptr_rdata1 =
- createRdata(RRType::NAPTR(), RRClass::IN(),
- "100 50 \"s\" \"http\" \"\" _http._tcp.example.com");
- ConstRdataPtr naptr_rdata2 =
- createRdata(RRType::NAPTR(), RRClass::IN(),
- "200 100 \"s\" \"http\" \"\" _http._tcp.example.com");
- rdata_list_.clear();
- rdata_list_.push_back(naptr_rdata1);
- rdata_list_.push_back(naptr_rdata2);
- checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
-}
-
-TEST_F(RdataEncoderTest, encodeLargeRdata) {
- // There should be no reason for a large RDATA to fail in encoding,
- // but we check such a case explicitly.
-
- encoded_data_.resize(65535); // max unsigned 16-bit int
- isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
- const in::DHCID large_dhcid(buffer, encoded_data_.size());
-
- encoder_.start(RRClass::IN(), RRType::DHCID());
- encoder_.addRdata(large_dhcid);
- encodeWrapper(encoder_.getStorageLength());
-
- // The encoded data should be identical to the original one.
- ASSERT_LT(sizeof(uint16_t), encoder_.getStorageLength());
- isc::util::InputBuffer ib(&encoded_data_[2], encoded_data_.size() - 2);
- const in::DHCID encoded_dhcid(ib, ib.getLength());
- EXPECT_EQ(0, encoded_dhcid.compare(large_dhcid));
-}
-
-TEST_F(RdataEncoderTest, addRdataMulti) {
- vector<ConstRdataPtr> rrsigs;
- addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
-
- // Tests with two RRSIGs
- rrsigs.push_back(rrsig_rdata_);
- rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
- "A 5 2 3600 20120814220826 "
- "20120715220826 54321 com. FAKE"));
- addRdataMultiCommon(rrsigs);
-}
-
-TEST_F(RdataEncoderTest, badAddRdata) {
- // Some operations must follow start().
- EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::InvalidOperation);
- EXPECT_THROW(encoder_.getStorageLength(), isc::InvalidOperation);
- // will allocate space of some arbitrary size (256 bytes)
- EXPECT_THROW(encodeWrapper(256), isc::InvalidOperation);
-
- // Bad buffer for encode
- encoder_.start(RRClass::IN(), RRType::A());
- encoder_.addRdata(*a_rdata_);
- const size_t buf_len = encoder_.getStorageLength();
- // NULL buffer for encode
- EXPECT_THROW(encoder_.encode(NULL, buf_len), isc::BadValue);
- // buffer length is too short (we don't use the wrraper because we don't
- // like to tweak the length arg to encode()).
- encoded_data_.resize(buf_len - 1);
- EXPECT_THROW(encoder_.encode(&encoded_data_[0], buf_len - 1),
- isc::BadValue);
-
- // Type of RDATA and the specified RR type don't match. addRdata() should
- // detect this inconsistency.
- encoder_.start(RRClass::IN(), RRType::AAAA());
- EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::BadValue);
-
- // Likewise.
- encoder_.start(RRClass::IN(), RRType::A());
- EXPECT_THROW(encoder_.addRdata(*aaaa_rdata_), isc::BadValue);
-
- // Likewise. The encoder expects the first name completes the data, and
- // throws on the second due as an unexpected name field.
- const ConstRdataPtr rp_rdata =
- createRdata(RRType::RP(), RRClass::IN(), "a.example. b.example");
- encoder_.start(RRClass::IN(), RRType::NS());
- EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
-
- // Likewise. The encoder considers the name data a variable length data
- // field, and throws on the first name.
- encoder_.start(RRClass::IN(), RRType::DHCID());
- EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
-
- // Likewise. The text RDATA (2 bytes) will be treated as MX preference,
- // and the encoder will still expect to see a domain name.
- const ConstRdataPtr txt_rdata = createRdata(RRType::TXT(), RRClass::IN(),
- "a");
- encoder_.start(RRClass::IN(), RRType::MX());
- EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
-
- // Similar to the previous one, but in this case there's no data field
- // in the spec.
- encoder_.start(RRClass::IN(), RRType::NS());
- EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
-
- // Likewise. Inconsistent name compression policy.
- const ConstRdataPtr ns_rdata =
- createRdata(RRType::NS(), RRClass::IN(), "ns.example");
- encoder_.start(RRClass::IN(), RRType::DNAME());
- EXPECT_THROW(encoder_.addRdata(*ns_rdata), isc::BadValue);
-
- // Same as the previous one, opposite inconsistency.
- const ConstRdataPtr dname_rdata =
- createRdata(RRType::DNAME(), RRClass::IN(), "dname.example");
- encoder_.start(RRClass::IN(), RRType::NS());
- EXPECT_THROW(encoder_.addRdata(*dname_rdata), isc::BadValue);
-
- // RDATA len exceeds the 16-bit range. Technically not invalid, but
- // we don't support that (and it's practically useless anyway).
- encoded_data_.resize(65536); // use encoded_data_ for placeholder
- isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
- encoder_.start(RRClass::IN(), RRType::DHCID());
- EXPECT_THROW(encoder_.addRdata(in::DHCID(buffer, encoded_data_.size())),
- RdataEncodingError);
-
- // RRSIG cannot be used as the main RDATA type (can only be added as
- // a signature for some other type of RDATAs).
- EXPECT_THROW(encoder_.start(RRClass::IN(), RRType::RRSIG()),
- isc::BadValue);
-}
-
-TEST_F(RdataEncoderTest, addSIGRdataOnly) {
- // Encoded data that only contain RRSIGs. Mostly useless, but can happen
- // (in a partially broken zone) and it's accepted.
- encoder_.start(RRClass::IN(), RRType::A());
- encoder_.addSIGRdata(*rrsig_rdata_);
- encodeWrapper(encoder_.getStorageLength());
- ASSERT_LT(sizeof(uint16_t), encoder_.getStorageLength());
-
- // The encoded data should be identical to the given one.
- isc::util::InputBuffer ib(&encoded_data_[2], encoded_data_.size() - 2);
- const generic::RRSIG encoded_sig(ib, ib.getLength());
- EXPECT_EQ(0, encoded_sig.compare(*rrsig_rdata_));
-}
-
-TEST_F(RdataEncoderTest, badAddSIGRdata) {
- // try adding SIG before start
- EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);
-
- // Very big RRSIG. This implementation rejects it.
- isc::util::OutputBuffer ob(0);
- rrsig_rdata_->toWire(ob);
- // append dummy trailing signature to make it too big
- vector<uint8_t> dummy_sig(65536 - ob.getLength());
- ob.writeData(&dummy_sig[0], dummy_sig.size());
- ASSERT_EQ(65536, ob.getLength());
-
- isc::util::InputBuffer ib(ob.getData(), ob.getLength());
- const generic::RRSIG big_sigrdata(ib, ob.getLength());
- encoder_.start(RRClass::IN(), RRType::A());
- EXPECT_THROW(encoder_.addSIGRdata(big_sigrdata), RdataEncodingError);
-}
-}
diff --git a/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc b/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
new file mode 100644
index 0000000..12b613a
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
@@ -0,0 +1,839 @@
+// 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/name.h>
+#include <dns/labelsequence.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+
+#include <util/unittests/wiredata.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+#include <cstring>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+
+using isc::util::unittests::matchWireData;
+using std::string;
+using std::vector;
+
+// A trick to steal some private definitions of the implementation we use here
+
+namespace isc {
+namespace datasrc{
+namespace memory {
+
+#include "../rdata_serialization_priv.cc"
+
+}
+}
+}
+
+namespace {
+// This defines a tuple of test data used in test_rdata_list below.
+struct TestRdata {
+ const char* const rrclass; // RR class, textual form
+ const char* const rrtype; // RR type, textual form
+ const char* const rdata; // textual RDATA
+ const size_t n_varlen_fields; // expected # of variable-len fields
+};
+
+// This test data consist of (almost) all supported types of RDATA (+ some
+// unusual and corner cases).
+const TestRdata test_rdata_list[] = {
+ {"IN", "A", "192.0.2.1", 0},
+ {"IN", "NS", "ns.example.com", 0},
+ {"IN", "CNAME", "cname.example.com", 0},
+ {"IN", "SOA", "ns.example.com root.example.com 0 0 0 0 0", 0},
+ {"IN", "PTR", "reverse.example.com", 0},
+ {"IN", "HINFO", "\"cpu-info\" \"OS-info\"", 1},
+ {"IN", "MINFO", "root.example.com mbox.example.com", 0},
+ {"IN", "MX", "10 mx.example.com", 0},
+ {"IN", "TXT", "\"test1\" \"test 2\"", 1},
+ {"IN", "RP", "root.example.com. rp-text.example.com", 0},
+ {"IN", "AFSDB", "1 afsdb.example.com", 0},
+ {"IN", "AAAA", "2001:db8::1", 0},
+ {"IN", "SRV", "1 0 10 target.example.com", 0},
+ {"IN", "NAPTR", "100 50 \"s\" \"http\" \"\" _http._tcp.example.com", 1},
+ {"IN", "DNAME", "dname.example.com", 0},
+ {"IN", "DS", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
+ {"IN", "SSHFP", "1 1 dd465c09cfa51fb45020cc83316fff", 1},
+ // We handle RRSIG separately, so it's excluded from the list
+ {"IN", "NSEC", "next.example.com. A AAAA NSEC RRSIG", 1},
+ {"IN", "DNSKEY", "256 3 5 FAKEFAKE", 1},
+ {"IN", "DHCID", "FAKEFAKE", 1},
+ {"IN", "NSEC3", "1 1 12 AABBCCDD FAKEFAKE A RRSIG", 1},
+ {"IN", "NSEC3PARAM", "1 0 12 AABBCCDD", 1},
+ {"IN", "SPF", "v=spf1 +mx a:colo.example.com/28 -all", 1},
+ {"IN", "DLV", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
+ {"IN", "TYPE65000", "\\# 3 010203", 1}, // some "custom" type
+ {"IN", "TYPE65535", "\\# 0", 1}, // max RR type, 0-length RDATA
+ {"CH", "A", "\\# 2 0102", 1}, // A RR for non-IN class; varlen data
+ {"CH", "NS", "ns.example.com", 0}, // class CH, generic data
+ {"CH", "TXT", "BIND10", 1}, // ditto
+ {"HS", "A", "\\# 5 0102030405", 1}, // A RR for non-IN class; varlen data
+ {NULL, NULL, NULL, 0}
+};
+
+// The following two functions will be used to generate wire format data
+// from encoded representation of each RDATA.
+void
+renderNameField(MessageRenderer* renderer, bool additional_required,
+ const LabelSequence& labels, RdataNameAttributes attributes)
+{
+ EXPECT_EQ(additional_required, (attributes & NAMEATTR_ADDITIONAL) != 0);
+ renderer->writeName(labels, (attributes & NAMEATTR_COMPRESSIBLE) != 0);
+}
+
+void
+renderDataField(MessageRenderer* renderer, const void* data, size_t data_len) {
+ renderer->writeData(data, data_len);
+}
+
+class RdataSerializationTest : public ::testing::Test {
+protected:
+ RdataSerializationTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
+ "192.0.2.53")),
+ aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
+ "2001:db8::53")),
+ rrsig_rdata_(createRdata(
+ RRType::RRSIG(), RRClass::IN(),
+ "A 5 2 3600 20120814220826 "
+ "20120715220826 12345 com. FAKE"))
+ {}
+
+ // A wraper for RdataEncoder::encode() with buffer overrun check.
+ void encodeWrapper(size_t data_len);
+
+ // Some commonly used RDATA
+ const ConstRdataPtr a_rdata_;
+ const ConstRdataPtr aaaa_rdata_;
+ const ConstRdataPtr rrsig_rdata_;
+
+ RdataEncoder encoder_;
+ vector<uint8_t> encoded_data_;
+ MessageRenderer expected_renderer_;
+ MessageRenderer actual_renderer_;
+ vector<ConstRdataPtr> rdata_list_;
+};
+
+// There are several ways to decode the data. For one, there are
+// more interfaces uses for RdataReader, and we use our own decoder,
+// to check the actual encoded data.
+//
+// These decoding ways are provided by the template parameter.
+template<class DecoderStyle>
+class RdataEncodeDecodeTest : public RdataSerializationTest {
+public:
+ // This helper test method encodes the given list of RDATAs
+ // (in rdata_list), and then iterates over the data, rendering the fields
+ // in the wire format. It then compares the wire data with the one
+ // generated by the normal libdns++ interface to see the encoding/decoding
+ // works as intended.
+ void checkEncode(RRClass rrclass, RRType rrtype,
+ const vector<ConstRdataPtr>& rdata_list,
+ size_t expected_varlen_fields,
+ const vector<ConstRdataPtr>& rrsig_list =
+ vector<ConstRdataPtr>());
+
+ void addRdataCommon(const vector<ConstRdataPtr>& rrsigs);
+ void addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs);
+};
+
+// Used across more classes and scopes. But it's just uninteresting
+// constant.
+const Name& dummyName2() {
+ static const Name result("example.com");
+ return (result);
+}
+
+bool
+additionalRequired(const RRType& type) {
+ // The set of RR types that require additional section processing.
+ // We'll use it to determine what value should the renderNameField get
+ // and, if the stored attributes are as expected.
+ static std::set<RRType> need_additionals;
+ if (need_additionals.empty()) {
+ need_additionals.insert(RRType::NS());
+ need_additionals.insert(RRType::MX());
+ need_additionals.insert(RRType::SRV());
+ }
+
+ return (need_additionals.find(type) != need_additionals.end());
+}
+
+// A decoder that does not use RdataReader. Not recommended for use,
+// but it allows the tests to check the internals of the data.
+class ManualDecoderStyle {
+public:
+ static void foreachRdataField(RRClass rrclass, RRType rrtype,
+ size_t rdata_count,
+ const vector<uint8_t>& encoded_data,
+ const vector<uint16_t>& varlen_list,
+ RdataReader::NameAction name_callback,
+ RdataReader::DataAction data_callback)
+ {
+ const RdataEncodeSpec& encode_spec = getRdataEncodeSpec(rrclass,
+ rrtype);
+
+ size_t off = 0;
+ size_t varlen_count = 0;
+ size_t name_count = 0;
+ for (size_t count = 0; count < rdata_count; ++count) {
+ for (size_t i = 0; i < encode_spec.field_count; ++i) {
+ const RdataFieldSpec& field_spec = encode_spec.fields[i];
+ switch (field_spec.type) {
+ case RdataFieldSpec::FIXEDLEN_DATA:
+ if (data_callback) {
+ data_callback(&encoded_data.at(off),
+ field_spec.fixeddata_len);
+ }
+ off += field_spec.fixeddata_len;
+ break;
+ case RdataFieldSpec::VARLEN_DATA:
+ {
+ const size_t varlen = varlen_list.at(varlen_count);
+ if (data_callback && varlen > 0) {
+ data_callback(&encoded_data.at(off), varlen);
+ }
+ off += varlen;
+ ++varlen_count;
+ break;
+ }
+ case RdataFieldSpec::DOMAIN_NAME:
+ {
+ ++name_count;
+ const LabelSequence labels(&encoded_data.at(off));
+ if (name_callback) {
+ name_callback(labels,
+ field_spec.name_attributes);
+ }
+ off += labels.getSerializedLength();
+ break;
+ }
+ }
+ }
+ }
+ assert(name_count == encode_spec.name_count * rdata_count);
+ assert(varlen_count == encode_spec.varlen_count * rdata_count);
+ }
+
+ static void foreachRRSig(const vector<uint8_t>& encoded_data,
+ const vector<uint16_t>& rrsiglen_list,
+ RdataReader::DataAction data_callback)
+ {
+ size_t rrsig_totallen = 0;
+ for (vector<uint16_t>::const_iterator it = rrsiglen_list.begin();
+ it != rrsiglen_list.end();
+ ++it) {
+ rrsig_totallen += *it;
+ }
+ assert(encoded_data.size() >= rrsig_totallen);
+
+ const uint8_t* dp = &encoded_data[encoded_data.size() -
+ rrsig_totallen];
+ for (size_t i = 0; i < rrsiglen_list.size(); ++i) {
+ data_callback(dp, rrsiglen_list[i]);
+ dp += rrsiglen_list[i];
+ }
+ }
+
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count,
+ size_t rrsig_count,
+ size_t expected_varlen_fields,
+ // Warning: this test actualy might change the
+ // encoded_data !
+ vector<uint8_t>& encoded_data, size_t,
+ MessageRenderer& renderer)
+ {
+ // If this type of RDATA is expected to contain variable-length fields,
+ // we brute force the encoded data, exploiting our knowledge of actual
+ // encoding, then adjust the encoded data excluding the list of length
+ // fields. This is ugly, but for tests only.
+ vector<uint16_t> varlen_list;
+ if (expected_varlen_fields > 0) {
+ const size_t varlen_list_size =
+ rdata_count * expected_varlen_fields * sizeof(uint16_t);
+ ASSERT_LE(varlen_list_size, encoded_data.size());
+ varlen_list.resize(rdata_count * expected_varlen_fields);
+ std::memcpy(&varlen_list[0], &encoded_data[0], varlen_list_size);
+ encoded_data.assign(encoded_data.begin() + varlen_list_size,
+ encoded_data.end());
+ }
+
+ // If RRSIGs are given, we need to extract the list of the RRSIG
+ // lengths and adjust encoded_data_ further.
+ vector<uint16_t> rrsiglen_list;
+ if (rrsig_count > 0) {
+ const size_t rrsig_len_size = rrsig_count * sizeof(uint16_t);
+ ASSERT_LE(rrsig_len_size, encoded_data.size());
+ rrsiglen_list.resize(rrsig_count * rrsig_len_size);
+ std::memcpy(&rrsiglen_list[0], &encoded_data[0], rrsig_len_size);
+ encoded_data.assign(encoded_data.begin() + rrsig_len_size,
+ encoded_data.end());
+ }
+
+ // Create wire-format data from the encoded data
+ foreachRdataField(rrclass, rrtype, rdata_count, encoded_data,
+ varlen_list,
+ boost::bind(renderNameField, &renderer,
+ additionalRequired(rrtype), _1, _2),
+ boost::bind(renderDataField, &renderer, _1, _2));
+
+ // 2nd dummy name
+ renderer.writeName(dummyName2());
+ // Finally, dump any RRSIGs in wire format.
+ foreachRRSig(encoded_data, rrsiglen_list,
+ boost::bind(renderDataField, &renderer, _1, _2));
+ }
+};
+
+// Check using callbacks and calling next until the end.
+class CallbackDecoder {
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count, size_t sig_count, size_t,
+ const vector<uint8_t>& encoded_data, size_t,
+ MessageRenderer& renderer)
+ {
+ RdataReader reader(rrclass, rrtype, &encoded_data[0], rdata_count,
+ sig_count,
+ boost::bind(renderNameField, &renderer,
+ additionalRequired(rrtype), _1, _2),
+ boost::bind(renderDataField, &renderer, _1, _2));
+ while (reader.next() != RdataReader::RRSET_BOUNDARY) {}
+ renderer.writeName(dummyName2());
+ while (reader.nextSig() != RdataReader::RRSET_BOUNDARY) {}
+ }
+};
+
+// Check using callbacks and calling iterate.
+class IterateDecoder {
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count, size_t sig_count, size_t,
+ const vector<uint8_t>& encoded_data, size_t,
+ MessageRenderer& renderer)
+ {
+ RdataReader reader(rrclass, rrtype, &encoded_data[0],
+ rdata_count, sig_count,
+ boost::bind(renderNameField, &renderer,
+ additionalRequired(rrtype), _1, _2),
+ boost::bind(renderDataField, &renderer, _1, _2));
+ reader.iterate();
+ renderer.writeName(dummyName2());
+ reader.iterateAllSigs();
+ }
+};
+
+namespace {
+
+// Render the data to renderer, if one is set, or put it inside
+// a data buffer.
+void
+appendOrRenderData(vector<uint8_t>* where, MessageRenderer** renderer,
+ const void* data, size_t size)
+{
+ if (*renderer != NULL) {
+ (*renderer)->writeData(data, size);
+ } else {
+ where->insert(where->end(), reinterpret_cast<const uint8_t*>(data),
+ reinterpret_cast<const uint8_t*>(data) + size);
+ }
+}
+
+}
+
+// Similar to IterateDecoder, but it first iterates a little and rewinds
+// before actual rendering.
+class RewindAndDecode {
+private:
+ static void writeName(MessageRenderer** renderer,
+ const LabelSequence& labels,
+ RdataNameAttributes attributes)
+ {
+ (*renderer)->writeName(labels,
+ (attributes & NAMEATTR_COMPRESSIBLE) != 0);
+ }
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count, size_t sig_count, size_t,
+ const vector<uint8_t>& encoded_data, size_t,
+ MessageRenderer& renderer)
+ {
+ MessageRenderer dump; // A place to dump the extra data from before
+ // actual rendering.
+ MessageRenderer* current = &dump;
+ vector<uint8_t> placeholder; // boost::bind does not like NULL
+ RdataReader reader(rrclass, rrtype, &encoded_data[0],
+ rdata_count, sig_count,
+ boost::bind(writeName, ¤t, _1, _2),
+ boost::bind(appendOrRenderData, &placeholder,
+ ¤t, _1, _2));
+ // Iterate a little and rewind
+ reader.next();
+ reader.nextSig();
+ reader.rewind();
+ // Do the actual rendering
+ current = &renderer;
+ reader.iterate();
+ renderer.writeName(dummyName2());
+ reader.iterateAllSigs();
+ }
+};
+
+// Decode using the iteration over one rdata each time.
+// We also count there's the correct count of Rdatas.
+class SingleIterateDecoder {
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count, size_t sig_count, size_t,
+ const vector<uint8_t>& encoded_data, size_t,
+ MessageRenderer& renderer)
+ {
+ RdataReader reader(rrclass, rrtype, &encoded_data[0],
+ rdata_count, sig_count,
+ boost::bind(renderNameField, &renderer,
+ additionalRequired(rrtype), _1, _2),
+ boost::bind(renderDataField, &renderer, _1, _2));
+ size_t actual_count = 0;
+ while (reader.iterateRdata()) {
+ ++actual_count;
+ }
+ EXPECT_EQ(rdata_count, actual_count);
+ actual_count = 0;
+ renderer.writeName(dummyName2());
+ while (reader.iterateSingleSig()) {
+ ++actual_count;
+ }
+ EXPECT_EQ(sig_count, actual_count);
+ }
+};
+
+// This one does not adhere to the usual way the reader is used, trying
+// to confuse it. It iterates part of the data manually and then reads
+// the rest through iterate. It also reads the signatures in the middle
+// of rendering.
+template<bool start_data, bool start_sig>
+class HybridDecoder {
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count, size_t sig_count, size_t,
+ const vector<uint8_t>& encoded_data,
+ size_t encoded_data_len,
+ MessageRenderer& renderer)
+ {
+ vector<uint8_t> data;
+ MessageRenderer* current;
+ RdataReader reader(rrclass, rrtype, &encoded_data[0],
+ rdata_count, sig_count,
+ boost::bind(renderNameField, &renderer,
+ additionalRequired(rrtype), _1, _2),
+ boost::bind(appendOrRenderData, &data, ¤t, _1,
+ _2));
+ // The size matches
+ EXPECT_EQ(encoded_data_len, reader.getSize());
+ if (start_sig) {
+ current = NULL;
+ reader.nextSig();
+ }
+ // Render first part of data. If there's none, return empty Result and
+ // do nothing.
+ if (start_data) {
+ current = &renderer;
+ reader.next();
+ }
+ // Now, we let all sigs to be copied to data. We disable the
+ // renderer for this.
+ current = NULL;
+ reader.iterateAllSigs();
+ // Now return the renderer and render the rest of the data
+ current = &renderer;
+ reader.iterate();
+ // Now, this should not break anything and should be valid, but should
+ // return ends.
+ EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.next());
+ EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.nextSig());
+ // Render the name and the sigs
+ renderer.writeName(dummyName2());
+ renderer.writeData(&data[0], data.size());
+ // The size matches even after use
+ EXPECT_EQ(encoded_data_len, reader.getSize());
+ }
+};
+
+typedef ::testing::Types<ManualDecoderStyle,
+ CallbackDecoder, IterateDecoder, SingleIterateDecoder,
+ HybridDecoder<true, true>, HybridDecoder<true, false>,
+ HybridDecoder<false, true>,
+ HybridDecoder<false, false> >
+ DecoderStyles;
+// Each decoder style must contain a decode() method. Such method is expected
+// to decode the passed data, first render the Rdata into the passed renderer,
+// then write the dummyName2() there and write the RRSig data after that.
+// It may do other checks too.
+//
+// There are some slight differences to how to do the decoding, that's why we
+// have the typed test.
+TYPED_TEST_CASE(RdataEncodeDecodeTest, DecoderStyles);
+
+void
+RdataSerializationTest::encodeWrapper(size_t data_len) {
+ // make sure the data buffer is large enough for the canary
+ encoded_data_.resize(data_len + 2);
+ // set the canary data
+ encoded_data_.at(data_len) = 0xde;
+ encoded_data_.at(data_len + 1) = 0xad;
+ // encode, then check the canary is intact
+ encoder_.encode(&encoded_data_[0], data_len);
+ EXPECT_EQ(0xde, encoded_data_.at(data_len));
+ EXPECT_EQ(0xad, encoded_data_.at(data_len + 1));
+ // shrink the data buffer to the originally expected size (some tests
+ // expect that). the actual encoded data should be intact.
+ encoded_data_.resize(data_len);
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+checkEncode(RRClass rrclass, RRType rrtype,
+ const vector<ConstRdataPtr>& rdata_list,
+ size_t expected_varlen_fields,
+ const vector<ConstRdataPtr>& rrsig_list)
+{
+ // These two names will be rendered before and after the test RDATA,
+ // to check in case the RDATA contain a domain name whether it's
+ // compressed or not correctly. The names in the RDATA should basically
+ // a subdomain of example.com, so it can be compressed due to dummyName2().
+ // Likewise, dummyName2() should be able to be fully compressed due to
+ // the name in the RDATA.
+ const Name dummy_name("com");
+
+ expected_renderer_.clear();
+ actual_renderer_.clear();
+ encoded_data_.clear();
+
+ // Build expected wire-format data
+ expected_renderer_.writeName(dummy_name);
+ BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
+ rdata->toWire(expected_renderer_);
+ }
+ expected_renderer_.writeName(dummyName2());
+ BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
+ rdata->toWire(expected_renderer_);
+ }
+
+ // Then build wire format data using the encoded data.
+ // 1st dummy name
+ actual_renderer_.writeName(dummy_name);
+
+ // Create encoded data
+ encoder_.start(rrclass, rrtype);
+ BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
+ encoder_.addRdata(*rdata);
+ }
+ BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
+ encoder_.addSIGRdata(*rdata);
+ }
+ const size_t storage_len = encoder_.getStorageLength();
+ encodeWrapper(storage_len);
+
+ DecoderStyle::decode(rrclass, rrtype, rdata_list.size(), rrsig_list.size(),
+ expected_varlen_fields, encoded_data_, storage_len,
+ actual_renderer_);
+
+ // Two sets of wire-format data should be identical.
+ matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
+ actual_renderer_.getData(), actual_renderer_.getLength());
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
+ // Basic check on the encoded data for (most of) all supported RR types,
+ // in a comprehensive manner.
+ for (size_t i = 0; test_rdata_list[i].rrclass != NULL; ++i) {
+ SCOPED_TRACE(string(test_rdata_list[i].rrclass) + "/" +
+ test_rdata_list[i].rrtype);
+ const RRClass rrclass(test_rdata_list[i].rrclass);
+ const RRType rrtype(test_rdata_list[i].rrtype);
+ const ConstRdataPtr rdata = createRdata(rrtype, rrclass,
+ test_rdata_list[i].rdata);
+ rdata_list_.clear();
+ rdata_list_.push_back(rdata);
+ checkEncode(rrclass, rrtype, rdata_list_,
+ test_rdata_list[i].n_varlen_fields, rrsigs);
+ }
+}
+
+TYPED_TEST(RdataEncodeDecodeTest, addRdata) {
+ vector<ConstRdataPtr> rrsigs;
+ this->addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
+
+ // Test with RRSIGs (covered type doesn't always match, but the encoder
+ // doesn't check that)
+ rrsigs.push_back(this->rrsig_rdata_);
+ this->addRdataCommon(rrsigs);
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
+ // Similar to addRdata(), but test with multiple RDATAs.
+ // Four different cases are tested: a single fixed-len RDATA (A),
+ // fixed-len data + domain name (MX), variable-len data only (TXT),
+ // variable-len data + domain name (NAPTR).
+ ConstRdataPtr a_rdata2 = createRdata(RRType::A(), RRClass::IN(),
+ "192.0.2.54");
+ rdata_list_.clear();
+ rdata_list_.push_back(a_rdata_);
+ rdata_list_.push_back(a_rdata2);
+ checkEncode(RRClass::IN(), RRType::A(), rdata_list_, 0, rrsigs);
+
+ ConstRdataPtr mx_rdata1 = createRdata(RRType::MX(), RRClass::IN(),
+ "5 mx1.example.com");
+ ConstRdataPtr mx_rdata2 = createRdata(RRType::MX(), RRClass::IN(),
+ "10 mx2.example.com");
+ rdata_list_.clear();
+ rdata_list_.push_back(mx_rdata1);
+ rdata_list_.push_back(mx_rdata2);
+ checkEncode(RRClass::IN(), RRType::MX(), rdata_list_, 0, rrsigs);
+
+ ConstRdataPtr txt_rdata1 = createRdata(RRType::TXT(), RRClass::IN(),
+ "foo bar baz");
+ ConstRdataPtr txt_rdata2 = createRdata(RRType::TXT(), RRClass::IN(),
+ "another text data");
+ rdata_list_.clear();
+ rdata_list_.push_back(txt_rdata1);
+ rdata_list_.push_back(txt_rdata2);
+ checkEncode(RRClass::IN(), RRType::TXT(), rdata_list_, 1, rrsigs);
+
+ ConstRdataPtr naptr_rdata1 =
+ createRdata(RRType::NAPTR(), RRClass::IN(),
+ "100 50 \"s\" \"http\" \"\" _http._tcp.example.com");
+ ConstRdataPtr naptr_rdata2 =
+ createRdata(RRType::NAPTR(), RRClass::IN(),
+ "200 100 \"s\" \"http\" \"\" _http._tcp.example.com");
+ rdata_list_.clear();
+ rdata_list_.push_back(naptr_rdata1);
+ rdata_list_.push_back(naptr_rdata2);
+ checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
+}
+
+void ignoreName(const LabelSequence&, unsigned) {
+}
+
+void
+checkLargeData(const in::DHCID* decoded, bool* called, const void* encoded,
+ size_t length)
+{
+ EXPECT_FALSE(*called); // Called exactly once
+ *called = true;
+
+ // Reconstruct the Rdata and check it.
+ isc::util::InputBuffer ib(encoded, length);
+ const in::DHCID reconstructed(ib, ib.getLength());
+ EXPECT_EQ(0, reconstructed.compare(*decoded));
+}
+
+TEST_F(RdataSerializationTest, encodeLargeRdata) {
+ // There should be no reason for a large RDATA to fail in encoding,
+ // but we check such a case explicitly.
+
+ encoded_data_.resize(65535); // max unsigned 16-bit int
+ isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
+ const in::DHCID large_dhcid(buffer, encoded_data_.size());
+
+ encoder_.start(RRClass::IN(), RRType::DHCID());
+ encoder_.addRdata(large_dhcid);
+ encodeWrapper(encoder_.getStorageLength());
+
+ // The encoded data should be identical to the original one.
+ bool called = false;
+ RdataReader reader(RRClass::IN(), RRType::DHCID(), &encoded_data_[0], 1, 0,
+ ignoreName, boost::bind(checkLargeData, &large_dhcid,
+ &called, _1, _2));
+ reader.iterate();
+ EXPECT_TRUE(called);
+ called = false;
+ reader.iterateAllSigs();
+ EXPECT_FALSE(called);
+}
+
+TYPED_TEST(RdataEncodeDecodeTest, addRdataMulti) {
+ vector<ConstRdataPtr> rrsigs;
+ this->addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
+
+ // Tests with two RRSIGs
+ rrsigs.push_back(this->rrsig_rdata_);
+ rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
+ "A 5 2 3600 20120814220826 "
+ "20120715220826 54321 com. FAKE"));
+ this->addRdataMultiCommon(rrsigs);
+}
+
+TEST_F(RdataSerializationTest, badAddRdata) {
+ // Some operations must follow start().
+ EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::InvalidOperation);
+ EXPECT_THROW(encoder_.getStorageLength(), isc::InvalidOperation);
+ // will allocate space of some arbitrary size (256 bytes)
+ EXPECT_THROW(encodeWrapper(256), isc::InvalidOperation);
+
+ // Bad buffer for encode
+ encoder_.start(RRClass::IN(), RRType::A());
+ encoder_.addRdata(*a_rdata_);
+ const size_t buf_len = encoder_.getStorageLength();
+ // NULL buffer for encode
+ EXPECT_THROW(encoder_.encode(NULL, buf_len), isc::BadValue);
+ // buffer length is too short (we don't use the wrraper because we don't
+ // like to tweak the length arg to encode()).
+ encoded_data_.resize(buf_len - 1);
+ EXPECT_THROW(encoder_.encode(&encoded_data_[0], buf_len - 1),
+ isc::BadValue);
+
+ // Type of RDATA and the specified RR type don't match. addRdata() should
+ // detect this inconsistency.
+ encoder_.start(RRClass::IN(), RRType::AAAA());
+ EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::BadValue);
+
+ // Likewise.
+ encoder_.start(RRClass::IN(), RRType::A());
+ EXPECT_THROW(encoder_.addRdata(*aaaa_rdata_), isc::BadValue);
+
+ // Likewise. The encoder expects the first name completes the data, and
+ // throws on the second due as an unexpected name field.
+ const ConstRdataPtr rp_rdata =
+ createRdata(RRType::RP(), RRClass::IN(), "a.example. b.example");
+ encoder_.start(RRClass::IN(), RRType::NS());
+ EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
+
+ // Likewise. The encoder considers the name data a variable length data
+ // field, and throws on the first name.
+ encoder_.start(RRClass::IN(), RRType::DHCID());
+ EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
+
+ // Likewise. The text RDATA (2 bytes) will be treated as MX preference,
+ // and the encoder will still expect to see a domain name.
+ const ConstRdataPtr txt_rdata = createRdata(RRType::TXT(), RRClass::IN(),
+ "a");
+ encoder_.start(RRClass::IN(), RRType::MX());
+ EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
+
+ // Similar to the previous one, but in this case there's no data field
+ // in the spec.
+ encoder_.start(RRClass::IN(), RRType::NS());
+ EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
+
+ // Likewise. Inconsistent name compression policy.
+ const ConstRdataPtr ns_rdata =
+ createRdata(RRType::NS(), RRClass::IN(), "ns.example");
+ encoder_.start(RRClass::IN(), RRType::DNAME());
+ EXPECT_THROW(encoder_.addRdata(*ns_rdata), isc::BadValue);
+
+ // Same as the previous one, opposite inconsistency.
+ const ConstRdataPtr dname_rdata =
+ createRdata(RRType::DNAME(), RRClass::IN(), "dname.example");
+ encoder_.start(RRClass::IN(), RRType::NS());
+ EXPECT_THROW(encoder_.addRdata(*dname_rdata), isc::BadValue);
+
+ // RDATA len exceeds the 16-bit range. Technically not invalid, but
+ // we don't support that (and it's practically useless anyway).
+ encoded_data_.resize(65536); // use encoded_data_ for placeholder
+ isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
+ encoder_.start(RRClass::IN(), RRType::DHCID());
+ EXPECT_THROW(encoder_.addRdata(in::DHCID(buffer, encoded_data_.size())),
+ RdataEncodingError);
+
+ // RRSIG cannot be used as the main RDATA type (can only be added as
+ // a signature for some other type of RDATAs).
+ EXPECT_THROW(encoder_.start(RRClass::IN(), RRType::RRSIG()),
+ isc::BadValue);
+}
+
+void
+checkSigData(const ConstRdataPtr& decoded, bool* called, const void* encoded,
+ size_t length)
+{
+ EXPECT_FALSE(*called); // Called exactly once
+ *called = true;
+
+ // Reconstruct the RRSig and check it.
+ isc::util::InputBuffer ib(encoded, length);
+ const generic::RRSIG reconstructed(ib, ib.getLength());
+ EXPECT_EQ(0, reconstructed.compare(*decoded));
+}
+
+TEST_F(RdataSerializationTest, addSIGRdataOnly) {
+ // Encoded data that only contain RRSIGs. Mostly useless, but can happen
+ // (in a partially broken zone) and it's accepted.
+ encoder_.start(RRClass::IN(), RRType::A());
+ encoder_.addSIGRdata(*rrsig_rdata_);
+ encodeWrapper(encoder_.getStorageLength());
+ ASSERT_LT(sizeof(uint16_t), encoder_.getStorageLength());
+
+ bool called = false;
+ RdataReader reader(RRClass::IN(), RRType::A(), &encoded_data_[0], 0, 1,
+ ignoreName, boost::bind(checkSigData, rrsig_rdata_,
+ &called, _1, _2));
+ reader.iterate();
+ EXPECT_FALSE(called);
+ reader.iterateAllSigs();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(RdataSerializationTest, badAddSIGRdata) {
+ // try adding SIG before start
+ EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);
+
+ // Very big RRSIG. This implementation rejects it.
+ isc::util::OutputBuffer ob(0);
+ rrsig_rdata_->toWire(ob);
+ // append dummy trailing signature to make it too big
+ vector<uint8_t> dummy_sig(65536 - ob.getLength());
+ ob.writeData(&dummy_sig[0], dummy_sig.size());
+ ASSERT_EQ(65536, ob.getLength());
+
+ isc::util::InputBuffer ib(ob.getData(), ob.getLength());
+ const generic::RRSIG big_sigrdata(ib, ob.getLength());
+ encoder_.start(RRClass::IN(), RRType::A());
+ EXPECT_THROW(encoder_.addSIGRdata(big_sigrdata), RdataEncodingError);
+}
+}
diff --git a/src/lib/dns/benchmarks/message_renderer_bench.cc b/src/lib/dns/benchmarks/message_renderer_bench.cc
index 33cd65b..cc2fc88 100644
--- a/src/lib/dns/benchmarks/message_renderer_bench.cc
+++ b/src/lib/dns/benchmarks/message_renderer_bench.cc
@@ -35,21 +35,28 @@ template <typename T>
class MessageRendererBenchMark {
public:
MessageRendererBenchMark(const vector<Name>& names) :
+ renderer_(NULL),
names_(names)
{}
+ MessageRendererBenchMark() {
+ delete renderer_;
+ }
unsigned int run() {
- renderer_.clear();
+ if (renderer_ == NULL) {
+ renderer_ = new T();
+ }
+ renderer_->clear();
vector<Name>::const_iterator it = names_.begin();
const vector<Name>::const_iterator it_end = names_.end();
for (; it != it_end; ++it) {
- renderer_.writeName(*it);
+ renderer_->writeName(*it);
}
// Make sure truncation didn't accidentally happen.
- assert(!renderer_.isTruncated());
+ assert(!renderer_->isTruncated());
return (names_.size());
}
private:
- T renderer_;
+ T* renderer_; // It's pointer, so we won't need to copy it.
const vector<Name>& names_;
};
diff --git a/src/lib/dns/benchmarks/rdatarender_bench.cc b/src/lib/dns/benchmarks/rdatarender_bench.cc
index 368ea6a..38ee2ac 100644
--- a/src/lib/dns/benchmarks/rdatarender_bench.cc
+++ b/src/lib/dns/benchmarks/rdatarender_bench.cc
@@ -44,18 +44,25 @@ public:
RdataRenderBenchMark(const vector<T>& dataset) :
dataset_(dataset)
{}
+ RdataRenderBenchMark() {
+ delete renderer_;
+ }
unsigned int run() {
+ if (renderer_ == NULL) {
+ renderer_ = new MessageRenderer();
+ }
typename vector<T>::const_iterator data;
typename vector<T>::const_iterator data_end = dataset_.end();
for (data = dataset_.begin(); data != data_end; ++data) {
- renderer_.clear();
- (*data)->toWire(renderer_);
+ renderer_->clear();
+ (*data)->toWire(*renderer_);
}
return (dataset_.size());
}
private:
const vector<T>& dataset_;
- MessageRenderer renderer_;
+ // Just-in-time initialized pointer, so no copy
+ MessageRenderer* renderer_;
};
// This supplemental class emulates an RRset like class that internally
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index 5a81eb2..d8f4634 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -17,6 +17,8 @@
#include <util/buffer.h>
+#include <boost/noncopyable.hpp>
+
namespace isc {
namespace dns {
@@ -346,7 +348,8 @@ public:
/// end of the buffer at the time of construction. However, if the
/// pre-existing portion of the buffer contains DNS names, these names won't
/// be considered for name compression.
-class MessageRenderer : public AbstractMessageRenderer {
+class MessageRenderer : public AbstractMessageRenderer,
+ public boost::noncopyable { // Can crash if copied
public:
using AbstractMessageRenderer::CASE_INSENSITIVE;
using AbstractMessageRenderer::CASE_SENSITIVE;
More information about the bind10-changes
mailing list