Browse Source

[2096] Code reorganisation

The foreach functions to manually iterate over the data are used in
tests only, so moved to the tests.

The specifications are used by the tests and encoding and will be used
by the decoding machinery too, probably, so we are making them
accessible and reside in a separate file.
Michal 'vorner' Vaner 12 years ago
parent
commit
fdb51ca68f

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

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

+ 3 - 296
src/lib/datasrc/memory/rdata_encoder.cc

@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include "rdata_encoder.h"
+#include "rdata_field.h"
+
 #include <exceptions/exceptions.h>
 
 #include <util/buffer.h>
@@ -23,10 +26,6 @@
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 
-#include "rdata_encoder.h"
-
-#include <boost/static_assert.hpp>
-
 #include <cassert>
 #include <cstring>
 #include <vector>
@@ -41,227 +40,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 +303,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

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

@@ -0,0 +1,208 @@
+// 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 <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);
+}
+
+}
+}
+}

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

@@ -0,0 +1,87 @@
+// 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 "rdata_encoder.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 {
+
+/// 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

+ 73 - 0
src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc

@@ -29,6 +29,7 @@
 #include <dns/rrtype.h>
 
 #include <datasrc/memory/rdata_encoder.h>
+#include <datasrc/memory/rdata_field.h>
 
 #include <util/unittests/wiredata.h>
 
@@ -172,6 +173,78 @@ const Name dummy_name2("example.com");
 // 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,
+                                  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);
+    }
+
+    static 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];
+        }
+    }
+
     static void decode(const isc::dns::RRClass& rrclass,
                        const isc::dns::RRType& rrtype,
                        size_t rdata_count,