Browse Source

[2107] Merge branch 'trac2097' into trac2107

JINMEI Tatuya 12 years ago
parent
commit
6ca7b0d6bc

+ 4 - 2
src/lib/datasrc/memory/Makefile.am

@@ -11,6 +11,8 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
 noinst_LTLIBRARIES = libdatasrc_memory.la
 
 libdatasrc_memory_la_SOURCES = \
-	rdata_encoder.h \
-	rdata_encoder.cc \
+	rdata_encoder.h rdata_encoder.cc \
+	rdata_field.h rdata_field.cc \
+	rdata_reader.h rdata_reader.cc \
+	rdataset.h rdataset.cc \
 	domaintree.h

+ 1 - 1
src/lib/datasrc/memory/domaintree.h

@@ -930,7 +930,7 @@ private:
     /// \brief The destructor.
     ///
     /// An object of this class is always expected to be destroyed explicitly
-    /// by \c destroy(), so the constructor is hidden as private.
+    /// by \c destroy(), so the destructor is hidden as private.
     ///
     /// \note DomainTree is not intended to be inherited so the destructor
     /// is not virtual

+ 2 - 298
src/lib/datasrc/memory/rdata_encoder.cc

@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include "rdata_encoder.h"
+
 #include <exceptions/exceptions.h>
 
 #include <util/buffer.h>
@@ -23,16 +25,10 @@
 #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;
 
@@ -41,227 +37,6 @@ 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.
@@ -525,77 +300,6 @@ RdataEncoder::encode(void* buf, size_t buf_len) const {
     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

+ 3 - 46
src/lib/datasrc/memory/rdata_encoder.h

@@ -15,6 +15,8 @@
 #ifndef DATASRC_MEMORY_RDATA_ENCODER_H
 #define DATASRC_MEMORY_RDATA_ENCODER_H 1
 
+#include "rdata_field.h"
+
 #include <exceptions/exceptions.h>
 
 #include <dns/labelsequence.h>
@@ -98,26 +100,13 @@ 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.
+/// situation.
 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
@@ -276,38 +265,6 @@ private:
     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

+ 211 - 0
src/lib/datasrc/memory/rdata_field.cc

@@ -0,0 +1,211 @@
+// 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_field.h"
+
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <boost/static_assert.hpp>
+
+using namespace isc::dns;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+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);
+
+}
+
+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);
+}
+
+}
+}
+}

+ 99 - 0
src/lib/datasrc/memory/rdata_field.h

@@ -0,0 +1,99 @@
+// 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_FIELD
+#define DATASRC_MEMORY_RDATA_FIELD
+
+#include <stdint.h>
+
+/// \file rdata_field.h
+///
+/// This header should be considered private to the implementation and should
+/// not be used from outside.
+///
+/// It is used to share the definition of encoding for RRtypes.
+
+namespace isc {
+namespace dns {
+class RRType;
+class RRClass;
+}
+namespace datasrc {
+namespace memory {
+
+/// \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
+};
+
+/// 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
+};
+
+/// \brief Get the spec for given class and type
+const RdataEncodeSpec&
+getRdataEncodeSpec(const dns::RRClass& rrclass, const dns::RRType& rrtype);
+
+}
+}
+}
+
+#endif

+ 185 - 0
src/lib/datasrc/memory/rdata_reader.cc

@@ -0,0 +1,185 @@
+// 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_reader.h"
+
+using namespace isc::dns;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+void
+RdataReader::emptyNameAction(const LabelSequence&, unsigned) {
+    // Do nothing here. On purpose, it's not unfinished.
+}
+
+void
+RdataReader::emptyDataAction(const uint8_t*, size_t) {
+    // Do nothing here. On purpose, it's not unfinished.
+}
+
+RdataReader::Result::Result(const LabelSequence& label,
+                            unsigned attributes) :
+    label_(label),
+    data_(NULL),
+    size_(0),
+    type_(NAME),
+    compressible_((attributes & NAMEATTR_COMPRESSIBLE) != 0),
+    additional_((attributes & NAMEATTR_ADDITIONAL) != 0)
+{}
+
+RdataReader::Result::Result(const uint8_t* data, size_t size) :
+    label_(Name::ROOT_NAME()),
+    data_(data),
+    size_(size),
+    type_(DATA),
+    compressible_(false),
+    additional_(false)
+{}
+
+RdataReader::RdataReader(const RRClass& rrclass, const RRType& rrtype,
+                         const uint8_t* 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 casts, well, C++ decided it doesn't like completely valid
+    // and explicitly allowed cast in C, so we need to fool it through
+    // void.
+    lengths_(static_cast<const uint16_t*>(
+             static_cast<const void*>(data))), // The lenghts are stored first
+    // And the data just after all the lengths
+    data_(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::Result
+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);
+            return (Result(sequence, spec.name_attributes));
+        } else {
+            const size_t length(spec.type == RdataFieldSpec::FIXEDLEN_DATA ?
+                                spec.fixeddata_len : lengths_[length_pos_ ++]);
+            Result result(data_ + data_pos_, length);
+            data_pos_ += length;
+            data_action(result.data(), result.size());
+            return (result);
+        }
+    } else {
+        sigs_ = data_ + data_pos_;
+        return (Result());
+    }
+}
+
+RdataReader::Result
+RdataReader::next() {
+    return (nextInternal(name_action_, data_action_));
+}
+
+RdataReader::Result
+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.
+            size_t data_pos = data_pos_;
+            size_t spec_pos = spec_pos_;
+            size_t length_pos = length_pos_;
+            // When the next() gets to the last item, it sets the sigs_
+            while (nextInternal(emptyNameAction, emptyDataAction)) {}
+            assert(sigs_ != NULL);
+            // Return the state
+            data_pos_ = data_pos;
+            spec_pos_ = spec_pos;
+            length_pos_ = length_pos;
+        }
+        // Extract the result
+        Result result(sigs_ + sig_data_pos_, lengths_[var_count_total_ +
+                      sig_pos_]);
+        // Move the position of iterator.
+        sig_data_pos_ += lengths_[var_count_total_ + sig_pos_];
+        sig_pos_ ++;
+        // Call the callback
+        data_action_(result.data(), result.size());
+        return (result);
+    } else {
+        return (Result());
+    }
+}
+
+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);
+}
+
+}
+}
+}

+ 287 - 0
src/lib/datasrc/memory/rdata_reader.h

@@ -0,0 +1,287 @@
+// 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_READER_H
+#define DATASRC_MEMORY_RDATA_READER_H 1
+
+#include "rdata_field.h"
+
+#include <boost/function.hpp>
+
+#include <dns/labelsequence.h>
+#include <dns/name.h>
+
+namespace isc {
+// Some forward declarations
+namespace dns{
+class RRClass;
+class RRType;
+}
+
+namespace datasrc {
+namespace memory {
+
+/// \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
+/// and names. It does not give you convenient Rdata or RRset class.
+///
+/// It allows two types of operation. First one is by providing callbacks
+/// to the constructor and then iterating by repeatedly calling next, or
+/// calling iterate once. The callbacks are then called with each part of
+/// the data.
+///
+/// \code
+/// void handleLabel(const dns::LabelSequence& label, unsigned int flags) {
+///     ...
+/// }
+/// void handleData(const uint8_t* data, size_t size) {
+///     ...
+/// }
+///
+/// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data,
+///                    &handleLabel, handleData);
+/// reader.iterate();
+/// \endcode
+///
+/// The other way is to use the return value of next() and loop through
+/// it manually. It has the inconvenience of having the type condition, but
+/// the code is in one place. The used way is matter of personal preferrence,
+/// there's no much difference on the technical side.
+///
+/// \code
+/// RdataReader reader(RRClass::IN(), RRType::AAAA(), size, data,
+///                    &handleLabel, handleData);
+/// RdataReader::Result data;
+/// while (data = reader.next()) {
+///     switch(data.type()) {
+///         case RdataReader::NAME:
+///             ...
+///             break;
+///         case RdataReader::DATA:
+///             ...
+///             break;
+///         default: assert(0); // Can not happen
+///     }
+/// }
+/// \endcode
+///
+/// \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&, unsigned)>
+        NameAction;
+    /// \brief Function called on each data field in the data.
+    typedef boost::function<void(const uint8_t*, size_t)> DataAction;
+
+    /// \brief a NameAction that does nothing.
+    ///
+    /// This is a NameAction function that does nothing. It is used
+    /// as a default in the constructor.
+    static void emptyNameAction(const dns::LabelSequence& label,
+                                unsigned attributes);
+    /// \brief a DataAction that does nothing.
+    ///
+    /// This is a DataAction function that does nothing. It is used
+    /// as a default in the constructor.
+    static void emptyDataAction(const uint8_t* data, size_t size);
+
+    /// \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 uint8_t* data, size_t rdata_count, size_t sig_count,
+                const NameAction& name_action = &emptyNameAction,
+                const DataAction& data_action = &emptyDataAction);
+
+    /// \brief The type of data returned from this iteration.
+    enum DataType {
+        NAME, ///< This iteration returns domain label
+        DATA, ///< This iteration returns unstructuder data
+        END   ///< No more data to return
+    };
+
+    /// \brief Data from one iteration
+    ///
+    /// Each time you call next() or nextSig(), it returns some data.
+    /// This holds the data.
+    ///
+    /// It is valid only for as long as the RdataReader that returned it.
+    ///
+    /// All the methods can be called under any circumstances. However,
+    /// if the required property is not valid for the given type (eg.
+    /// when calling size() on type() == NAME), it returns some "empty"
+    /// value (0, NULL, or the like).
+    class Result {
+    public:
+        /// \brief Default constructor
+        ///
+        /// It creates an empty result (with no data) of type END.
+        Result() :
+            // TODO: Do we maybe want to have a static one to copy
+            // instead of constructing new one from the root Name?
+            label_(dns::Name::ROOT_NAME()),
+            data_(NULL),
+            size_(0),
+            type_(END),
+            compressible_(false),
+            additional_(false)
+        {}
+        /// \brief Constructor from a domain label
+        ///
+        /// Creates the NAME type result. Used internally from RdataReader.
+        ///
+        /// \param label The label to hold
+        /// \param attributes The attributes, as stored by the serialized
+        ///     data.
+        Result(const dns::LabelSequence& label, unsigned attributes);
+        /// \brief Constructor from data
+        ///
+        /// Creates the DATA type result. Used internally from RdataReader.
+        ///
+        /// \param data The data pointer to hold.
+        /// \param size The size to hold.
+        Result(const uint8_t* data, size_t size);
+        /// \brief The type of data returned.
+        DataType type() const { return (type_); }
+        /// \brief The raw data.
+        ///
+        /// This is only valid if type() == DATA.
+        const uint8_t* data() const { return (data_); }
+        /// \brief The size of the raw data.
+        ///
+        /// This is the number of bytes the data takes. It is valid only
+        /// if type() == DATA.
+        size_t size() const { return (size_); }
+        /// \brief The domain label.
+        ///
+        /// This holds the domain label. It is only valid if type() == NAME.
+        const dns::LabelSequence& label() const { return (label_); }
+        /// \brief Is the name in label() compressible?
+        ///
+        /// This is valid only if type() == NAME.
+        bool compressible() const { return (compressible_); }
+        /// \brief Does the name expect additional processing?
+        ///
+        /// This is valid only if type() == NAME.
+        bool additional() const { return (additional_); }
+        /// \brief If there are data returned.
+        ///
+        /// This returns if there are any data at all returned. This is
+        /// equivalent to action != END, but it allows for more convenient
+        /// code of a loop through the data.
+        operator bool() const {
+            return (type() != END);
+        }
+    private:
+        dns::LabelSequence label_;
+        const uint8_t* data_;
+        size_t size_;
+        DataType type_;
+        bool compressible_;
+        bool additional_;
+    };
+
+    /// \brief Step to next piece of data.
+    ///
+    /// This returns the next available data. Also, the apropriate hook
+    /// (name_action or data_action, depending on the data type) as passed
+    /// to the constructor is called.
+    ///
+    /// If there are no more data, a Result with type END is returned and
+    /// no callback is called.
+    Result 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 revind before,
+    /// therefore if you already called next() yourself, it does not start
+    /// at the beginning).
+    ///
+    /// The method only makes sense if you set the callbacks in constructor.
+    void iterate() {
+        while (next()) { }
+    }
+    /// \brief Step to next piece 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.
+    Result nextSig();
+    /// \brief Iterate through all RRSig data.
+    ///
+    /// This is almost the same as iterate(), but it iterates through the
+    /// RRSig data instead.
+    void iterateSig() {
+        while (nextSig()) { }
+    }
+    /// \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_;
+    Result nextInternal(const NameAction& name_action,
+                        const DataAction& data_action);
+};
+
+}
+}
+}
+
+#endif

+ 178 - 0
src/lib/datasrc/memory/rdataset.cc

@@ -0,0 +1,178 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+
+#include "rdataset.h"
+#include "rdata_encoder.h"
+#include "rdata_reader.h"
+
+#include <boost/static_assert.hpp>
+
+#include <stdint.h>
+#include <cstring>
+#include <typeinfo>             // for bad_cast
+#include <new>                  // for the placement new
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+namespace {
+RRType
+getCoveredType(const Rdata& rdata) {
+    try {
+        const generic::RRSIG& rrsig_rdata =
+            dynamic_cast<const generic::RRSIG&>(rdata);
+        return (rrsig_rdata.typeCovered());
+    } catch (const std::bad_cast&) {
+        isc_throw(BadValue, "Non RRSIG is given where it's expected");
+    }
+}
+}
+
+RdataSet*
+RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
+                 ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset)
+{
+    // Check basic validity
+    if (!rrset && !sig_rrset) {
+        isc_throw(BadValue, "Both RRset and RRSIG are NULL");
+    }
+    if (rrset && rrset->getRdataCount() == 0) {
+        isc_throw(BadValue, "Empty RRset");
+    }
+    if (sig_rrset && sig_rrset->getRdataCount() == 0) {
+        isc_throw(BadValue, "Empty SIG RRset");
+    }
+    if (rrset && sig_rrset && rrset->getClass() != sig_rrset->getClass()) {
+        isc_throw(BadValue, "RR class doesn't match between RRset and RRSIG");
+    }
+
+    // Check assumptions on the number of RDATAs
+    if (rrset && rrset->getRdataCount() > MAX_RDATA_COUNT) {
+        isc_throw(RdataSetError, "Too many RDATAs for RdataSet: "
+                  << rrset->getRdataCount() << ", must be <= "
+                  << MAX_RDATA_COUNT);
+    }
+    if (sig_rrset && sig_rrset->getRdataCount() > MAX_RRSIG_COUNT) {
+        isc_throw(RdataSetError, "Too many RRSIGs for RdataSet: "
+                  << sig_rrset->getRdataCount() << ", must be <= "
+                  << MAX_RRSIG_COUNT);
+    }
+
+    const RRClass rrclass = rrset ? rrset->getClass() : sig_rrset->getClass();
+    const RRType rrtype = rrset ? rrset->getType() :
+        getCoveredType(sig_rrset->getRdataIterator()->getCurrent());
+    const RRTTL rrttl = rrset ? rrset->getTTL() : sig_rrset->getTTL();
+
+    encoder.start(rrclass, rrtype);
+    if (rrset) {
+        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())
+        {
+            if (getCoveredType(it->getCurrent()) != rrtype) {
+                isc_throw(BadValue, "Type covered doesn't match");
+            }
+            encoder.addSIGRdata(it->getCurrent());
+        }
+    }
+
+    const size_t rrsig_count = sig_rrset ? sig_rrset->getRdataCount() : 0;
+    const size_t ext_rrsig_count_len =
+        rrsig_count >= MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
+    const size_t data_len = encoder.getStorageLength();
+    void* p = mem_sgmt.allocate(sizeof(RdataSet) + ext_rrsig_count_len +
+                                data_len);
+    RdataSet* rdataset = new(p) RdataSet(rrtype,
+                                         rrset ? rrset->getRdataCount() : 0,
+                                         rrsig_count, rrttl);
+    if (rrsig_count >= MANY_RRSIG_COUNT) {
+        *rdataset->getExtSIGCountBuf() = rrsig_count;
+    }
+    encoder.encode(rdataset->getDataBuf(), data_len);
+    return (rdataset);
+}
+
+void
+RdataSet::destroy(util::MemorySegment& mem_sgmt, RRClass rrclass,
+                  RdataSet* rdataset)
+{
+    const size_t data_len =
+        RdataReader(rrclass, rdataset->type,
+                    reinterpret_cast<const uint8_t*>(rdataset->getDataBuf()),
+                    rdataset->getRdataCount(),
+                    rdataset->getSigRdataCount()).getSize();
+    const size_t ext_rrsig_count_len =
+        rdataset->sig_rdata_count_ == MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
+    rdataset->~RdataSet();
+    mem_sgmt.deallocate(rdataset,
+                        sizeof(RdataSet) + ext_rrsig_count_len + data_len);
+}
+
+namespace {
+// Convert the given RRTTL into the corresponding 32-bit unsigned integer,
+// in the network byte order.  We do not use htonl() to be as portable as
+// possible.
+uint32_t
+convertTTL(RRTTL ttl) {
+    const uint32_t ttl_val = ttl.getValue();
+    uint8_t buf[4];
+    buf[0] = (ttl_val & 0xff000000) >> 24;
+    buf[1] = (ttl_val & 0x00ff0000) >> 16;
+    buf[2] = (ttl_val & 0x0000ff00) >> 8;
+    buf[3] = (ttl_val & 0x000000ff);
+    uint32_t ret;
+    std::memcpy(&ret, buf, sizeof(ret));
+    return (ret);
+}
+}
+
+RdataSet::RdataSet(RRType type_param, size_t rdata_count,
+                   size_t sig_rdata_count, RRTTL ttl) :
+    type(type_param),
+    sig_rdata_count_(sig_rdata_count >= MANY_RRSIG_COUNT ?
+                     MANY_RRSIG_COUNT : sig_rdata_count),
+    rdata_count_(rdata_count), ttl_(convertTTL(ttl))
+{
+    // Make sure an RRType object is essentially a plain 16-bit value, so
+    // our assumption of the size of RdataSet holds.  If it's not the case
+    // we should use the bare value instead of the class object.
+    BOOST_STATIC_ASSERT(sizeof(type) == sizeof(uint16_t));
+
+    // Confirm we meet the alignment requirement for RdataEncoder
+    // ("this + 1" should be safely passed to the encoder).
+    BOOST_STATIC_ASSERT(sizeof(RdataSet) % sizeof(uint16_t) == 0);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc

+ 323 - 0
src/lib/datasrc/memory/rdataset.h

@@ -0,0 +1,323 @@
+// 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_RDATASET_H
+#define DATASRC_MEMORY_RDATASET_H 1
+
+#include <util/memory_segment.h>
+
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+
+#include <boost/interprocess/offset_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+class RdataEncoder;
+
+/// \brief General error on creating RdataSet.
+///
+/// This is thrown when creating \c RdataSet encounters a rare, unsupported
+/// situation.
+class RdataSetError : public Exception {
+public:
+    RdataSetError(const char* file, size_t line, const char* what) :
+        Exception(file, line, what) {}
+};
+
+/// \brief Memory-efficient representation of RRset data with RRSIGs.
+///
+/// This class provides memory-efficient and lightweight interface to various
+/// attributes of an RRset, which may or may not be signed with RRSIGs.
+///
+/// This class is primarily intended to be used in the in-memory data source
+/// implementation, and is not supposed to be used by general applications.
+/// The major design goals is to keep required memory footprint for the given
+/// amount of data as small as possible, while providing a reasonably
+/// efficient interface to examine the data, focusing on zone lookup and DNS
+/// message rendering.
+///
+/// It encodes a specific set of RRset and (when signed) its RRSIGs, excluding
+/// the owner name and the RR class.  The owner name is supposed to be
+/// maintained by the application outside this class (the intended place to
+/// store this information is a \c DomainTree node, although this
+/// implementation does not rely on that intent).  The RR class must be the
+/// same in a single zone, and it's simply a waste if we have it with each
+/// RRset.  The RR class information is therefore expected to be maintained
+/// outside this class.
+///
+/// This class imposes some limitations on the number of RDATAs of the RRset
+/// and RRSIG: a (non RRSIG) RRset containing more than 8191 (2^13 - 1)
+/// or an RRSIG containing more than 65535 (2^16 - 1) RDATAs cannot be
+/// maintained in this class.  The former restriction comes from the
+/// following observation: any RR in wire format in a DNS message must at
+/// least contain 10 bytes of data (for RR type, class, TTL and RDATA length),
+/// and since a valid DNS message must not be larger than 65535 bytes,
+/// no valid DNS response can contain more than 6554 RRs.  So, in practice,
+/// it should be reasonable even if we reject very large RRsets that would
+/// not fit in a DNS message.  For the same reason we restrict the number of
+/// RRSIGs, although due to internal implementation details the limitation
+/// is more relaxed for RRSIGs.
+///
+/// \note (This is pure implementation details) By limiting the number of
+/// RDATAs so it will fit in a 13-bit integer, we can use 3 more bits in a
+/// 2-byte integer for other purposes.  We use this additional field to
+/// represent the number of RRSIGs up to 6, while using the value of 7 to mean
+/// there are more than 6 RRSIGs.  In the vast majority of real world
+/// deployment, an RRset should normally have only a few RRSIGs, and 6 should
+/// normally be more than sufficient.  So we can cover most practical cases
+/// regarding the number of records with this 2-byte field.
+///
+/// A set of objects of this class (which would be \c RdataSets of various
+/// types of the same owner name) will often be maintained in a single linked
+/// list.  The class has a member variable to make the link.
+///
+/// This class is designed so an instance can be stored in a shared
+/// memory region.  So it only contains straightforward data (e.g., it
+/// doesn't hold a pointer to an object of some base class that
+/// contains virtual methods), and the pointer member (see the
+/// previous paragraph) is represented as an offset pointer.  For the
+/// same reason this class should never have virtual methods (and as a
+/// result, should never be inherited in practice).  When this class
+/// is extended these properties must be preserved.
+///
+/// The \c RdataSet class itself only contains a subset of attributes that
+/// it is conceptually expected to contain.  The rest of the attributes
+/// are encoded in a consecutive memory region immediately following the main
+/// \c RdataSet object.  The memory layout would be as follows:
+/// \verbatim
+/// RdataSet object
+/// (optional) uint16_t: number of RRSIGs, if it's larger than 6 (see above)
+/// encoded RDATA (generated by RdataEncoder) \endverbatim
+///
+/// This is shown here only for reference purposes.  The application must not
+/// assume any particular format of data in this region directly; it must
+/// get access to it via public interfaces provided in the main \c RdataSet
+/// class.
+class RdataSet : boost::noncopyable {
+public:
+    /// \brief Allocate and construct \c RdataSet
+    ///
+    /// This static method allocates memory for a new \c RdataSet
+    /// object for the set of an RRset and (if it's supposed to be signed)
+    /// RRSIG from the given memory segment, constructs the object, and
+    /// returns a pointer to it.
+    ///
+    /// Normally the (non RRSIG) RRset is given (\c rrset is not NULL) while
+    /// its RRSIG (\c sig_rrset) may or may not be provided.  But it's also
+    /// expected that in some rare (mostly broken) cases there can be an RRSIG
+    /// RR in a zone without having the covered RRset in the zone.  To handle
+    /// such cases, this class allows to only hold RRSIG, in which case
+    /// \c rrset can be NULL.  At least \c rrset or \c sig_rrset must be
+    /// non NULL, however.  Also, if non NULL, the RRset must not be empty,
+    /// that is, it must contain at least one RDATA.
+    ///
+    /// The RR type of \c rrset must not be RRSIG; the RR type of \c sig_rrset
+    /// must be RRSIG.
+    ///
+    /// When both \c rrset and \c sig_rrset are provided (both are non
+    /// NULL), the latter must validly cover the former: the RR class
+    /// must be identical; the type covered field of any RDATA of \c
+    /// sig_rrset must be identical to the RR type of \c rrset.  The owner
+    /// name of these RRsets must also be identical, but this implementation
+    /// doesn't require it because \c RdataSet itself does not rely on the
+    /// owner name, and it should be pretty likely that this condition is met
+    /// in the context where this class is used (and, name comparison is
+    /// relatively expensive, and if we end up comparing them twice the
+    /// overhead can be non negligible).
+    ///
+    /// If any of the above conditions isn't met, an isc::BadValue exception
+    /// will be thrown; basically, there should be a bug in the caller if this
+    /// happens.
+    ///
+    /// Due to implementation limitations, this class cannot contain more than
+    /// 8191 RDATAs for the non RRISG RRset; also, it cannot contain more than
+    /// 65535 RRSIGs.  If the given RRset(s) fail to meet this condition,
+    /// an \c RdataSetError exception will be thrown.
+    ///
+    /// \throw isc::BadValue Given RRset(s) are invalid (see the description)
+    /// \throw RdataSetError Number of RDATAs exceed the limits
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c RdataSet is allocated.
+    /// \param encoder The RDATA encoder to encode \c rrset and \c sig_rrset
+    /// with the \c RdataSet to be created.
+    /// \param rrset A (non RRSIG) RRset from which the \c RdataSet is to be
+    /// created.  Can be NULL if sig_rrset is not.
+    /// \param sig_rrset An RRSIG RRset from which the \c RdataSet is to be
+    /// created.  Can be NULL if rrset is not.
+    ///
+    /// \return A pointer to the created \c RdataSet.
+    static RdataSet* create(util::MemorySegment& mem_sgmt,
+                            RdataEncoder& encoder,
+                            dns::ConstRRsetPtr rrset,
+                            dns::ConstRRsetPtr sig_rrset);
+
+    /// \brief Destruct and deallocate \c RdataSet
+    ///
+    /// Note that this method needs to know the expected RR class of the
+    /// \c RdataSet.  This is because the destruction may depend on the
+    /// internal data encoding that only \c RdataEncoder and \c RdataReader
+    /// know, and they need to know the corresponding RR class and type to
+    /// identify the internal data representation.  Since \c RdataSet itself
+    /// does not hold the class information, the caller needs to provide it.
+    /// Obviously, this RR class must be identical to the RR class of \c rrset
+    /// (when given) or of \c sig_rrset (when \c rrset isn't given) at the
+    /// \c create() time.
+    ///
+    /// \throw none
+    ///
+    /// \param mem_sgmt The \c MemorySegment that allocated memory for
+    /// \c node.
+    /// \param rrclass The RR class of the \c RdataSet to be destroyed.
+    /// \param rdataset A non NULL pointer to a valid \c RdataSet object
+    /// that was originally created by the \c create() method (the behavior
+    /// is undefined if this condition isn't met).
+    static void destroy(util::MemorySegment& mem_sgmt, dns::RRClass rrclass,
+                        RdataSet* rdataset);
+
+    typedef boost::interprocess::offset_ptr<RdataSet> RdataSetPtr;
+    typedef boost::interprocess::offset_ptr<const RdataSet> ConstRdataSetPtr;
+
+    // Note: the size and order of the members are carefully chosen to
+    // maximize efficiency.  Don't change them unless there's strong reason
+    // for that and the consequences are considered.
+    // For convenience (and since this class is mostly intended to be an
+    // internal definition for the in-memory data source implementation),
+    // we allow the application to get access to some members directly.
+    // Some others require some conversion to use in a meaningful way,
+    // for which we force the application to use accessor methods in order
+    // to prevent misuse.
+
+    RdataSetPtr next; ///< Pointer to the next \c RdataSet (when linked)
+    const dns::RRType type;     ///< The RR type of the \c RdataSet
+
+private:
+    const uint16_t sig_rdata_count_ : 3; // # of RRSIGs, up to 6 (7 means many)
+    const uint16_t rdata_count_ : 13; // # of RDATAs, up to 8191
+    const uint32_t ttl_;       // TTL of the RdataSet, net byte order
+
+    // Max number of normal RDATAs that can be stored in \c RdataSet.
+    // It's 2^13 - 1 = 8191.
+    static const size_t MAX_RDATA_COUNT = (1 << 13) - 1;
+
+    // Max number of RRSIGs that can be stored in \c RdataSet.
+    // It's 2^16 - 1 = 65535.
+    static const size_t MAX_RRSIG_COUNT = (1 << 16) - 1;
+
+    // Indicate the \c RdataSet contains many RRSIGs that require an additional
+    // field for the real number of RRSIGs.  It's 2^3 - 1 = 7.
+    static const size_t MANY_RRSIG_COUNT = (1 << 3) - 1;
+
+public:
+    /// \brief Return the number of RDATAs stored in the \c RdataSet.
+    size_t getRdataCount() const { return (rdata_count_); }
+
+    /// \brief Return the number of RRSIG RDATAs stored in the \c RdataSet.
+    size_t getSigRdataCount() const {
+        if (sig_rdata_count_ < MANY_RRSIG_COUNT) {
+            return (sig_rdata_count_);
+        } else {
+            return (*getExtSIGCountBuf());
+        }
+    }
+
+    /// \brief Return a pointer to the TTL data of the \c RdataSet.
+    ///
+    /// The returned pointer points to a memory region that is valid at least
+    /// for 32 bits, storing the TTL of the \c RdataSet in the network byte
+    /// order.  It returns opaque data to make it clear that unless the wire
+    /// format data is necessary (e.g., when rendering it in a DNS message),
+    /// it should be converted to, e.g., an \c RRTTL object explicitly.
+    ///
+    /// \throw none
+    const void* getTTLData() const { return (&ttl_); }
+
+    /// \brief Accessor to the memory region for encoded RDATAs.
+    ///
+    /// The only valid usage of the returned pointer is to pass it to
+    /// the constructor of \c RdataReader.
+    ///
+    /// \throw none
+    const void* getDataBuf() const {
+        return (getDataBuf<const void, const RdataSet>(this));
+    }
+
+private:
+    /// \brief Accessor to the memory region for encoded RDATAs, mutable
+    /// version.
+    ///
+    /// This version is only used within the class implementation, so it's
+    /// defined as private.
+    void* getDataBuf() {
+        return (getDataBuf<void, RdataSet>(this));
+    }
+
+    // Implementation of getDataBuf().  Templated to unify the mutable and
+    // immutable versions.
+    template <typename RetType, typename ThisType>
+    static RetType* getDataBuf(ThisType* rdataset) {
+        if (rdataset->sig_rdata_count_ < MANY_RRSIG_COUNT) {
+            return (rdataset + 1);
+        } else {
+            return (rdataset->getExtSIGCountBuf() + 1);
+        }
+    }
+
+    /// \brief Accessor to the memory region for the RRSIG count field for
+    /// a large number of RRSIGs.
+    ///
+    /// These are used only internally and defined as private.
+    const uint16_t* getExtSIGCountBuf() const {
+        return (reinterpret_cast<const uint16_t*>(this + 1));
+    }
+    uint16_t* getExtSIGCountBuf() {
+        return (reinterpret_cast<uint16_t*>(this + 1));
+    }
+
+    /// \brief The constructor.
+    ///
+    /// An object of this class is always expected to be created by the
+    /// allocator (\c create()), so the constructor is hidden as private.
+    ///
+    /// It never throws an exception.
+    RdataSet(dns::RRType type, size_t rdata_count, size_t sig_rdata_count,
+             dns::RRTTL ttl);
+
+    /// \brief The destructor.
+    ///
+    /// An object of this class is always expected to be destroyed explicitly
+    /// by \c destroy(), so the destructor is hidden as private.
+    ///
+    /// This currently does nothing, but is explicitly defined to clarify
+    /// it's intentionally defined as private.
+    ~RdataSet() {}
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_RDATASET_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 4 - 1
src/lib/datasrc/memory/tests/Makefile.am

@@ -18,7 +18,8 @@ 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 += rdataset_unittest.cc
 run_unittests_SOURCES += domaintree_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -27,6 +28,8 @@ run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(builddir)/../libdatasrc_memory.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 run_unittests_LDADD += $(GTEST_LDADD)
 endif

+ 0 - 481
src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc

@@ -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);
-}
-}

+ 839 - 0
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.
+
+// Note: This file tests both the rdata_encoder and rdata_reader. They are
+// tested together because they form kind the oposite sides of the same
+// functionality.
+
+#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 <datasrc/memory/rdata_field.h>
+#include <datasrc/memory/rdata_reader.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;
+
+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, unsigned 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 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 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 =
+                     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 dummy_name2("example.com");
+
+bool
+additionalRequired(const RRType& type) {
+    // The set of RR types that require additional section processing.
+    // We'll pass it to renderNameField to check the stored attribute matches
+    // our expectation.
+    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(dummy_name2);
+    // Finally, dump any RRSIGs in wire format.
+    foreachRRSig(encoded_data, rrsiglen_list,
+                 boost::bind(renderDataField, &renderer, _1, _2));
+    }
+};
+
+// Decode using reader with the return value of next
+class NextDecoder {
+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);
+        RdataReader::Result field;
+        while ((field = reader.next())) {
+            switch (field.type()) {
+                case RdataReader::DATA:
+                    renderer.writeData(field.data(), field.size());
+                    break;
+                case RdataReader::NAME:
+                    renderer.writeName(field.label(), field.compressible());
+                    break;
+                default:
+                    FAIL();
+            }
+        }
+
+        renderer.writeName(dummy_name2);
+
+        while ((field = reader.nextSig())) {
+            switch (field.type()) {
+                case RdataReader::DATA:
+                    renderer.writeData(field.data(), field.size());
+                    break;
+                default: // There are also no NAME fields in RRSigs
+                    FAIL();
+            }
+        }
+    }
+};
+
+// Decode using reader with the return value of next, but after the reader
+// was used and rewund.
+class RewundDecoder {
+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);
+        // Use the reader first and rewind it
+        reader.iterateSig();
+        reader.iterate();
+        reader.rewind();
+        RdataReader::Result field;
+        while ((field = reader.next())) {
+            switch (field.type()) {
+                case RdataReader::DATA:
+                    renderer.writeData(field.data(), field.size());
+                    break;
+                case RdataReader::NAME:
+                    renderer.writeName(field.label(), field.compressible());
+                    break;
+                default:
+                    FAIL();
+            }
+        }
+
+        renderer.writeName(dummy_name2);
+
+        while ((field = reader.nextSig())) {
+            switch (field.type()) {
+                case RdataReader::DATA:
+                    renderer.writeData(field.data(), field.size());
+                    break;
+                default: // There are also no NAME fields in RRSigs
+                    FAIL();
+            }
+        }
+    }
+};
+
+// 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()) { }
+        renderer.writeName(dummy_name2);
+        while (reader.nextSig()) { }
+    }
+};
+
+// 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(dummy_name2);
+        reader.iterateSig();
+    }
+};
+
+// 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 {
+private:
+    // Append data either to the renderer or to the vector passed.
+    // The double pointer is there so we can change the renderer to NULL
+    // and back during the test.
+    static void appendData(vector<uint8_t>* where, MessageRenderer** renderer,
+                           const uint8_t* data, size_t size)
+    {
+        if (*renderer != NULL) {
+            (*renderer)->writeData(data, size);
+        } else {
+            where->insert(where->end(), data, data + size);
+        }
+    }
+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(appendData, &data, &current, _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.iterateSig();
+        // 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_FALSE(reader.next());
+        EXPECT_FALSE(reader.nextSig());
+        // Render the name and the sigs
+        renderer.writeName(dummy_name2);
+        renderer.writeData(&data[0], data.size());
+        // The size matches even after use
+        EXPECT_EQ(encoded_data_len, reader.getSize());
+    }
+};
+
+typedef ::testing::Types<ManualDecoderStyle, NextDecoder, RewundDecoder,
+                         CallbackDecoder, IterateDecoder,
+                         HybridDecoder<true, true>, HybridDecoder<true, false>,
+                         HybridDecoder<false, true>,
+                         HybridDecoder<false, false> >
+    DecoderStyles;
+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 dummy_name.
+    // Likewise, dummy_name2 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(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);
+    }
+    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);
+}
+
+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.
+    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));
+}
+
+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);
+}
+
+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());
+
+    // 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(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);
+}
+
+// Test the result returns what it was constructed with.
+TEST_F(RdataSerializationTest, readerResult) {
+    // Default constructor
+    RdataReader::Result empty;
+    // Everything should be at the "empty" values, type END
+    EXPECT_EQ(RdataReader::END, empty.type());
+    EXPECT_EQ(NULL, empty.data());
+    EXPECT_EQ(0, empty.size());
+    EXPECT_TRUE(empty.label().equals(LabelSequence(Name::ROOT_NAME())));
+    EXPECT_FALSE(empty);
+    EXPECT_FALSE(empty.compressible());
+    EXPECT_FALSE(empty.additional());
+    // Constructor from label sequence
+    LabelSequence seq(Name("example.org"));
+    RdataReader::Result compressible(seq, NAMEATTR_COMPRESSIBLE);
+    EXPECT_EQ(RdataReader::NAME, compressible.type());
+    EXPECT_EQ(NULL, compressible.data());
+    EXPECT_EQ(0, compressible.size());
+    EXPECT_TRUE(compressible.label().equals(seq));
+    EXPECT_TRUE(compressible);
+    EXPECT_TRUE(compressible.compressible());
+    EXPECT_FALSE(compressible.additional());
+    RdataReader::Result incompressible(seq, NAMEATTR_ADDITIONAL);
+    EXPECT_EQ(RdataReader::NAME, incompressible.type());
+    EXPECT_EQ(NULL, incompressible.data());
+    EXPECT_EQ(0, incompressible.size());
+    EXPECT_TRUE(incompressible.label().equals(seq));
+    EXPECT_TRUE(incompressible);
+    EXPECT_FALSE(incompressible.compressible());
+    EXPECT_TRUE(incompressible.additional());
+    // Constructor from data
+    uint8_t byte;
+    RdataReader::Result data(&byte, 1);
+    EXPECT_EQ(RdataReader::DATA, data.type());
+    EXPECT_EQ(&byte, data.data());
+    EXPECT_EQ(1, data.size());
+    EXPECT_TRUE(data.label().equals(LabelSequence(Name::ROOT_NAME())));
+    EXPECT_TRUE(data);
+    EXPECT_FALSE(data.compressible());
+    EXPECT_FALSE(data.additional());
+}
+}

+ 280 - 0
src/lib/datasrc/memory/tests/rdataset_unittest.cc

@@ -0,0 +1,280 @@
+// 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 <util/memory_segment_local.h>
+
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrset.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+#include <datasrc/memory/rdata_encoder.h>
+#include <datasrc/memory/rdata_reader.h>
+#include <datasrc/memory/rdataset.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+using boost::lexical_cast;
+
+namespace {
+
+class RdataSetTest : public ::testing::Test {
+protected:
+    RdataSetTest() :
+        // 1076895760 = 0x40302010.  Use this so we fill in all 8-bit "field"
+        // of the 32-bit TTL
+        a_rrset_(textToRRset("www.example.com. 1076895760 IN A 192.0.2.1")),
+        rrsig_rrset_(textToRRset("www.example.com. 1076895760 IN RRSIG "
+                                 "A 5 2 3600 20120814220826 20120715220826 "
+                                 "1234 example.com. FAKE"))
+    {}
+    void TearDown() {
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+    }
+
+    ConstRRsetPtr a_rrset_, rrsig_rrset_;
+    isc::util::MemorySegmentLocal mem_sgmt_;
+    RdataEncoder encoder_;
+};
+
+// Convert the given 32-bit integer (network byte order) to the corresponding
+// RRTTL object.
+RRTTL
+restoreTTL(const void* ttl_data) {
+    isc::util::InputBuffer b(ttl_data, sizeof(uint32_t));
+    return (RRTTL(b));
+}
+
+// A helper callback for checkRdataSet.  This confirms the given data
+// is the expected in::A RDATA (the value is taken from the RdataSetTest
+// constructor).
+void
+checkData(const uint8_t* data, size_t size) {
+    isc::util::InputBuffer b(data, size);
+    EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1")));
+}
+
+// This is a set of checks for an RdataSet created with some simple
+// conditions.  with_rrset/with_rrsig is true iff the RdataSet is supposed to
+// contain normal/RRSIG RDATA.
+void
+checkRdataSet(const RdataSet& rdataset, bool with_rrset, bool with_rrsig) {
+    EXPECT_FALSE(rdataset.next); // by default the next pointer should be NULL
+    EXPECT_EQ(RRType::A(), rdataset.type);
+    // See the RdataSetTest constructor for the magic number.
+    EXPECT_EQ(RRTTL(1076895760), restoreTTL(rdataset.getTTLData()));
+    EXPECT_EQ(with_rrset ? 1 : 0, rdataset.getRdataCount());
+    EXPECT_EQ(with_rrsig ? 1 : 0, rdataset.getSigRdataCount());
+
+    // A simple test for the data content.  Details tests for the encoder/
+    // reader should be basically sufficient for various cases of the data,
+    // and the fact that this test doesn't detect memory leak should be
+    // reasonably sufficient that the implementation handles the data region
+    // correctly.  Here we check one simple case for a simple form of RDATA,
+    // mainly for checking the behavior of getDataBuf().
+    RdataReader reader(RRClass::IN(), RRType::A(),
+                       reinterpret_cast<const uint8_t*>(
+                           rdataset.getDataBuf()),
+                       rdataset.getRdataCount(), rdataset.getSigRdataCount(),
+                       &RdataReader::emptyNameAction, checkData);
+    reader.iterate();
+}
+
+TEST_F(RdataSetTest, create) {
+    // A simple case of creating an RdataSet.  Confirming the resulting
+    // fields have the expected values, and then destroying it (TearDown()
+    // would detect any memory leak)
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          ConstRRsetPtr());
+    checkRdataSet(*rdataset, true, false);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+// A helper function to create an RRset containing the given number of
+// unique RDATAs.
+ConstRRsetPtr
+getRRsetWithRdataCount(size_t rdata_count) {
+    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::TXT(),
+                             RRTTL(3600)));
+    for (size_t i = 0; i < rdata_count; ++i) {
+        rrset->addRdata(rdata::createRdata(RRType::TXT(), RRClass::IN(),
+                                           lexical_cast<std::string>(i)));
+    }
+    return (rrset);
+}
+
+TEST_F(RdataSetTest, createManyRRs) {
+    // RRset with possible maximum number of RDATAs
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_,
+                                          getRRsetWithRdataCount(8191),
+                                          ConstRRsetPtr());
+    EXPECT_EQ(8191, rdataset->getRdataCount());
+    EXPECT_EQ(0, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Exceeding that will result in an exception.
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
+                                  getRRsetWithRdataCount(8192),
+                                  ConstRRsetPtr()),
+                 RdataSetError);
+    // To be very sure even try larger number than the threshold
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
+                                  getRRsetWithRdataCount(65535),
+                                  ConstRRsetPtr()),
+                 RdataSetError);
+}
+
+TEST_F(RdataSetTest, createWithRRSIG) {
+    // Normal case.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          rrsig_rrset_);
+    checkRdataSet(*rdataset, true, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Unusual case: TTL doesn't match.  This implementation accepts that,
+    // using the TTL of the covered RRset.
+    ConstRRsetPtr rrsig_badttl(textToRRset(
+                                   "www.example.com. 3600 IN RRSIG "
+                                   "A 5 2 3600 20120814220826 "
+                                   "20120715220826 1234 example.com. FAKE"));
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, rrsig_badttl);
+    checkRdataSet(*rdataset, true, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+// A helper function to create an RRSIG RRset containing the given number of
+// unique RDATAs.
+ConstRRsetPtr
+getRRSIGWithRdataCount(size_t sig_count) {
+    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(),
+                             RRType::RRSIG(), RRTTL(3600)));
+    // We use a base wire-format image and tweak the original TTL field to
+    // generate unique RDATAs in the loop.  (Creating them from corresponding
+    // text is simpler, but doing so for a large number of RRSIGs is
+    // relatively heavy and could be too long for unittests).
+    ConstRdataPtr rrsig_base =
+        rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
+                           "A 5 2 3600 20120814220826 20120715220826 1234 "
+                           "example.com. FAKE");
+    isc::util::OutputBuffer ob(0);
+    rrsig_base->toWire(ob);
+    for (size_t i = 0; i < sig_count; ++i) {
+        ob.writeUint16At((i >> 16) & 0xffff, 4);
+        ob.writeUint16At(i & 0xffff, 6);
+        isc::util::InputBuffer ib(ob.getData(), ob.getLength());
+        rrset->addRdata(rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
+                                           ib, ib.getLength()));
+    }
+    return (rrset);
+}
+
+TEST_F(RdataSetTest, createManyRRSIGs) {
+    // 7 has a special meaning in the implementation: if the number of the
+    // RRSIGs reaches this value, an extra 'sig count' field will be created.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          getRRSIGWithRdataCount(7));
+    EXPECT_EQ(7, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // 8 would cause overflow in the normal 3-bit field if there were no extra
+    // count field.
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                getRRSIGWithRdataCount(8));
+    EXPECT_EQ(8, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Up to 2^16-1 RRSIGs are allowed (although that would be useless
+    // in practice)
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                getRRSIGWithRdataCount(65535));
+    EXPECT_EQ(65535, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Exceeding this limit will result in an exception.
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  getRRSIGWithRdataCount(65536)),
+                 RdataSetError);
+    // To be very sure even try larger number than the threshold
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  getRRSIGWithRdataCount(70000)),
+                 RdataSetError);
+}
+
+TEST_F(RdataSetTest, createWithRRSIGOnly) {
+    // A rare, but allowed, case: RdataSet without the main RRset but with
+    // RRSIG.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                          rrsig_rrset_);
+    checkRdataSet(*rdataset, false, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+TEST_F(RdataSetTest, badCeate) {
+    // Neither the RRset nor RRSIG RRset is given
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                  ConstRRsetPtr()), isc::BadValue);
+
+    // Empty RRset (An RRset without RDATA)
+    ConstRRsetPtr empty_rrset(new RRset(Name("example.com"), RRClass::IN(),
+                                        RRType::A(), RRTTL(3600)));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset,
+                                  ConstRRsetPtr()), isc::BadValue);
+    ConstRRsetPtr empty_rrsig(new RRset(Name("example.com"), RRClass::IN(),
+                                        RRType::RRSIG(), RRTTL(3600)));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                  empty_rrsig), isc::BadValue);
+
+    // The RRset type and RRSIG's type covered don't match
+    ConstRRsetPtr bad_rrsig(textToRRset(
+                                "www.example.com. 1076895760 IN RRSIG "
+                                "NS 5 2 3600 20120814220826 20120715220826 "
+                                "1234 example.com. FAKE"));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, bad_rrsig),
+                 isc::BadValue);
+
+    // Pass non RRSIG for the sig parameter
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, a_rrset_),
+                 isc::BadValue);
+
+    // Pass RRSIG for normal RRset (the RdataEncoder will catch this and throw)
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, rrsig_rrset_,
+                                  rrsig_rrset_),
+                 isc::BadValue);
+
+    // RR class doesn't match between RRset and RRSIG
+    ConstRRsetPtr badclass_rrsig(textToRRset(
+                                     "www.example.com. 1076895760 CH RRSIG "
+                                     "A 5 2 3600 20120814220826 "
+                                     "20120715220826 1234 example.com. FAKE",
+                                     RRClass::CH()));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  badclass_rrsig),
+                 isc::BadValue);
+}
+}

+ 11 - 4
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_;
 };
 

+ 10 - 3
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

+ 4 - 1
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;