Browse Source

[master] Merge branch 'trac2094'

JINMEI Tatuya 12 years ago
parent
commit
df5d7b6c42

+ 2 - 0
configure.ac

@@ -1101,6 +1101,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/exceptions/Makefile
                  src/lib/exceptions/tests/Makefile
                  src/lib/datasrc/Makefile
+                 src/lib/datasrc/memory/Makefile
+                 src/lib/datasrc/memory/tests/Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/datasrc/tests/testdata/Makefile
                  src/lib/xfr/Makefile

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

@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = memory . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
@@ -64,6 +64,7 @@ libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 libdatasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la
 libdatasrc_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
 libdatasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
+libdatasrc_la_LIBADD += memory/libdatasrc_memory.la # convenience library
 libdatasrc_la_LIBADD += $(SQLITE_LIBS)
 
 BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc

+ 12 - 0
src/lib/datasrc/memory/Makefile.am

@@ -0,0 +1,12 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
+
+noinst_LTLIBRARIES = libdatasrc_memory.la
+libdatasrc_memory_la_SOURCES = rdata_encoder.h rdata_encoder.cc

+ 383 - 0
src/lib/datasrc/memory/rdata_encoder.cc

@@ -0,0 +1,383 @@
+// 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 <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>     // for a test function
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <util/buffer.h>        // for test functions
+
+#include "rdata_encoder.h"
+
+#include <boost/static_assert.hpp>
+
+#include <cassert>
+#include <vector>
+
+#include <stdint.h>
+
+using namespace isc::dns;
+using std::vector;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+namespace {
+/// Specification of a single RDATA field in terms of internal encoding.
+struct RdataFieldSpec {
+    enum FieldType {
+        FIXEDLEN_DATA = 0,      // fixed-length data field
+        VARLEN_DATA,            // variable-length data field
+        DOMAIN_NAME             // domain name
+    };
+
+    const FieldType type;       // field type
+
+    // type specific data.  We use a union so it'll be clear only one of them
+    // (determined by the type) is valid.  Since we want to make it as
+    // lightweight as possible, we use a relatively lower-level primitives
+    // here.
+    union {
+        // The length of fixed-length data field.  Only valid for FIXEDLEN_DATA
+        const uint16_t fixeddata_len;
+
+        // Attributes of the name.  Only valid for DOMAIN_NAME.
+        const RdataNameAttributes name_attributes;
+    };
+};
+
+/// Specification of RDATA in terms of internal encoding.
+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
+};
+
+// These constants are convenient shortcut to initialize the name_attributes
+// member of RdataFieldSpec (since it's a union, we can only directly
+// initialize fixeddata_len member, so we need to convert it to its type).
+// These are essentially small integers, so the cast should be safe.
+const uint16_t NAMEATTR_NOATTRIBUTE_INITIALIZER = static_cast<uint16_t>(0);
+const uint16_t NAMEATTR_COMPRESSIBLE_INITIALIZER =
+    static_cast<uint16_t>(NAMEATTR_COMPRESSIBLE);
+const uint16_t NAMEATTR_ADDITIONAL_INITIALIZER =
+    static_cast<uint16_t>(NAMEATTR_ADDITIONAL);
+const uint16_t NAMEATTR_COMPADDITIONAL_INITIALIZER =
+    static_cast<uint16_t>(NAMEATTR_COMPRESSIBLE | NAMEATTR_ADDITIONAL);
+
+// 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}}
+};
+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)}}
+};
+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}} // 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, {NAMEATTR_NOATTRIBUTE_INITIALIZER}}
+};
+const RdataFieldSpec single_compressible_name_fields[] = {
+    {RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_COMPRESSIBLE_INITIALIZER}}
+};
+const RdataFieldSpec single_compadditional_name_fields[] = {
+    {RdataFieldSpec::DOMAIN_NAME,
+     {NAMEATTR_COMPRESSIBLE_INITIALIZER|NAMEATTR_COMPADDITIONAL_INITIALIZER}}
+};
+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, {NAMEATTR_COMPRESSIBLE_INITIALIZER}},
+    {RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_COMPRESSIBLE_INITIALIZER}}
+};
+const RdataFieldSpec double_noattr_name_fields[] = {
+    {RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_NOATTRIBUTE_INITIALIZER}},
+    {RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_NOATTRIBUTE_INITIALIZER}}
+};
+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, {NAMEATTR_COMPRESSIBLE_INITIALIZER}},
+    {RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_COMPRESSIBLE_INITIALIZER}},
+    {RdataFieldSpec::FIXEDLEN_DATA, {sizeof(uint32_t) * 5}}
+};
+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)}},
+    {RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_COMPADDITIONAL_INITIALIZER}}
+};
+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)}},
+    {RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_NOATTRIBUTE_INITIALIZER}}
+};
+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}},
+    {RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_ADDITIONAL_INITIALIZER}}
+};
+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}},
+    {RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_NOATTRIBUTE_INITIALIZER}}
+};
+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, {NAMEATTR_NOATTRIBUTE_INITIALIZER}},
+    {RdataFieldSpec::VARLEN_DATA, {0}}
+};
+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);
+}
+
+// A temporary helper of temporary encodeRdata(): it calculates the length
+// of the data portion of a NAPTR RDATA (i.e., the RDATA fields before the
+// "replacement" name).
+size_t
+getNAPTRDataLen(const rdata::Rdata& rdata) {
+    const rdata::generic::NAPTR& naptr_rdata =
+        dynamic_cast<const rdata::generic::NAPTR&>(rdata);
+
+    util::OutputBuffer buffer(0);
+    rdata.toWire(buffer);
+    return (buffer.getLength() - naptr_rdata.getReplacement().getLength());
+}
+} // end of unnamed namespace
+
+namespace testing {
+void
+encodeRdata(const rdata::Rdata& rdata, RRClass rrclass, RRType rrtype,
+            vector<uint8_t>& data_result, vector<uint16_t>& len_result)
+{
+    util::OutputBuffer buffer(0);
+    rdata.toWire(buffer);
+    util::InputBuffer ibuffer(buffer.getData(), buffer.getLength());
+    vector<uint8_t> tmp;        // used as temporary placeholder below
+
+    const RdataEncodeSpec& encode_spec = getRdataEncodeSpec(rrclass, rrtype);
+    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:
+            tmp.resize(field_spec.fixeddata_len);
+            ibuffer.readData(&tmp[0], tmp.size());
+            data_result.insert(data_result.end(), tmp.begin(), tmp.end());
+            break;
+        case RdataFieldSpec::VARLEN_DATA:
+        {
+            // In the vast majority cases of our supported RR types,
+            // variable-length data fields are placed at the end of RDATA,
+            // so the length of the field should be the remaining length
+            // of the output buffer.  The only exception is NAPTR, for which
+            // we use an ad hoc workaround (remember this function is for
+            // initial testing only, and will be cleaned up eventually).
+            const size_t pos = ibuffer.getPosition();
+            const size_t data_len = rrtype == RRType::NAPTR() ?
+                getNAPTRDataLen(rdata) : (ibuffer.getLength() - pos);
+            tmp.resize(data_len);
+            ibuffer.readData(&tmp[0], tmp.size());
+            data_result.insert(data_result.end(), tmp.begin(), tmp.end());
+            len_result.push_back(data_len);
+            break;
+        }
+        case RdataFieldSpec::DOMAIN_NAME:
+        {
+            const Name name(ibuffer);
+            const LabelSequence labels(name);
+            size_t nlen;
+            const uint8_t* ndata = labels.getData(&nlen);
+            assert(nlen < 256); // nlen should fit in 8 bits
+            size_t olen;
+            uint8_t offset_holder[Name::MAX_LABELS];
+            labels.getOffsetData(&olen, offset_holder);
+            assert(olen < 256); // olen should fit in 8 bits
+            data_result.push_back(nlen);
+            data_result.push_back(olen);
+            data_result.insert(data_result.end(), ndata, ndata + nlen);
+            data_result.insert(data_result.end(), offset_holder,
+                               offset_holder + olen);
+            break;
+        }
+        }
+    }
+}
+
+void
+foreachRdataField(RRClass rrclass, RRType rrtype,
+                  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 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 uint8_t nlen = encoded_data.at(off);
+            const uint8_t olen = encoded_data.at(off + 1);
+            if (name_callback) {
+                const uint8_t* ndata = &encoded_data.at(off + 2);
+                const uint8_t* odata = &encoded_data.at(off + 2 + nlen);
+                name_callback(LabelSequence(ndata, odata, olen),
+                              field_spec.name_attributes);
+            }
+            off += (2 + nlen + olen);
+            break;
+        }
+        }
+    }
+
+    assert(name_count == encode_spec.name_count);
+    assert(varlen_count == encode_spec.varlen_count);
+}
+} // namespace testing
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc

+ 88 - 0
src/lib/datasrc/memory/rdata_encoder.h

@@ -0,0 +1,88 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_RDATA_ENCODER_H
+#define DATASRC_MEMORY_RDATA_ENCODER_H 1
+
+#include <dns/labelsequence.h>
+#include <dns/rdata.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <boost/function.hpp>
+
+#include <vector>
+
+namespace isc {
+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_COMPRESSIBLE = 1,  ///< Name should be compressed when rendered
+    NAMEATTR_ADDITIONAL = (NAMEATTR_COMPRESSIBLE << 1) ///< Name requires
+                                                      ///< Additional section
+                                                      ///< handling
+};
+
+// We use the following quick-hack version of encoder and "foreach"
+// operator 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 {
+// "Encode" given RDATA of given RR class and type.
+//
+// Fixed/variable-length data fields are encoded in their wire-format;
+// domain names are encoded in the form of:
+//  - nlen: name data length (1 byte)
+//  - olen: offset data length (1 byte)
+//  - name data (nlen bytes)
+//  - offset data (olen bytes)
+//
+// The encoded results are appended to data_result.
+// If the RDATA contain variable-length data fields, the lengths of the
+// these fields will be appended in len_result, in the order of appearance.
+void encodeRdata(const dns::rdata::Rdata& rdata, dns::RRClass rrclass,
+                 dns::RRType rrtype, std::vector<uint8_t>& data_result,
+                 std::vector<uint16_t>& len_result);
+
+// 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 RDATA field (in terms of the internal encoding) 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).  If the encoded data contain variable-length
+// data fields, varlen_list should store a sequence of their lengths, in the
+// of the appearance.
+void foreachRdataField(dns::RRClass rrclass, dns::RRType rrtype,
+                       const std::vector<uint8_t>& encoded_data,
+                       const std::vector<uint16_t>& varlen_list,
+                       NameCallback name_callback, DataCallback data_callback);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_RDATA_ENCODER_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 33 - 0
src/lib/datasrc/memory/tests/Makefile.am

@@ -0,0 +1,33 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += rdata_encoder_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(builddir)/../libdatasrc_memory.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(GTEST_LDADD)
+endif
+
+noinst_PROGRAMS = $(TESTS)

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

@@ -0,0 +1,172 @@
+// 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 <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.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 <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 all supported types of RDATA (+ some
+// unusual and corner cases).  We'll construct corresponding Rdata
+// object from this, and compare its wire format data both generated
+// by normal libdns++ interface and via encoding conversion.
+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},
+    // Note: in our implementation RRSIG is treated as opaque data (including
+    // the signer name).  We use "com" for signer so it won't be a compress
+    // target in the test.
+    {"IN", "RRSIG", "SOA 5 2 3600 20120814220826 20120715220826 12345 "
+     "com. FAKEFAKEFAKE", 1},
+    {"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);
+}
+
+TEST(RdataFieldSpec, checkData) {
+    // 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");
+
+    MessageRenderer expected_renderer, actual_renderer;
+    vector<uint8_t> encoded_data;
+    vector<uint16_t> varlen_list;
+
+    // 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());
+
+    for (size_t i = 1; test_rdata_list[i].rrclass != NULL; ++i) {
+        SCOPED_TRACE(string(test_rdata_list[i].rrclass) + "/" +
+                     test_rdata_list[i].rrtype);
+
+        expected_renderer.clear();
+        actual_renderer.clear();
+        expected_renderer.writeName(dummy_name);
+        actual_renderer.writeName(dummy_name);
+        encoded_data.clear();
+        varlen_list.clear();
+
+        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->toWire(expected_renderer);
+        expected_renderer.writeName(dummy_name2);
+
+        const bool additional_required =
+            (need_additionals.find(rrtype) != need_additionals.end());
+
+        encodeRdata(*rdata, rrclass, rrtype, encoded_data, varlen_list);
+        EXPECT_EQ(varlen_list.size(), test_rdata_list[i].n_varlen_fields);
+        foreachRdataField(rrclass, rrtype, encoded_data, varlen_list,
+                          boost::bind(renderNameField, &actual_renderer,
+                                      additional_required, _1, _2),
+                          boost::bind(renderDataField, &actual_renderer,
+                                      _1, _2));
+
+        actual_renderer.writeName(dummy_name2);
+        matchWireData(expected_renderer.getData(),
+                      expected_renderer.getLength(),
+                      actual_renderer.getData(),
+                      actual_renderer.getLength());
+    }
+}
+
+}

+ 23 - 0
src/lib/datasrc/memory/tests/run_unittests.cc

@@ -0,0 +1,23 @@
+// 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 <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    return (isc::util::unittests::run_all());
+}

+ 17 - 17
src/lib/dns/labelsequence.h

@@ -46,23 +46,6 @@ class LabelSequence {
     friend std::string Name::toText(bool) const;
 
 public:
-    /// \brief Constructs a LabelSequence for the given label sequence
-    ///
-    /// \note The associated data MUST remain in scope during the lifetime
-    /// of this LabelSequence, since only the pointers are copied.
-    ///
-    /// \note No validation is done on the given data upon construction;
-    ///       use with care.
-    ///
-    /// \param ls The LabelSequence to construct a LabelSequence from
-    explicit LabelSequence(const LabelSequence& ls):
-                                     data_(ls.data_),
-                                     offsets_(ls.offsets_),
-                                     offsets_size_(ls.offsets_size_),
-                                     first_label_(ls.first_label_),
-                                     last_label_(ls.last_label_)
-    {}
-
     /// \brief Constructs a LabelSequence for the given name
     ///
     /// \note The associated Name MUST remain in scope during the lifetime
@@ -98,6 +81,23 @@ public:
                   const uint8_t* offsets,
                   size_t offsets_size);
 
+    /// \brief Copy constructor.
+    ///
+    /// \note The associated data MUST remain in scope during the lifetime
+    /// of this LabelSequence, since only the pointers are copied.
+    ///
+    /// \note No validation is done on the given data upon construction;
+    ///       use with care.
+    ///
+    /// \param ls The LabelSequence to construct a LabelSequence from
+    LabelSequence(const LabelSequence& ls):
+                                     data_(ls.data_),
+                                     offsets_(ls.offsets_),
+                                     offsets_size_(ls.offsets_size_),
+                                     first_label_(ls.first_label_),
+                                     last_label_(ls.last_label_)
+    {}
+
     /// \brief Return the wire-format data for this LabelSequence
     ///
     /// The data is returned as a pointer to (the part of) the original

+ 3 - 1
src/lib/dns/rdata/generic/dname_39.cc

@@ -54,7 +54,9 @@ DNAME::toWire(OutputBuffer& buffer) const {
 
 void
 DNAME::toWire(AbstractMessageRenderer& renderer) const {
-    renderer.writeName(dname_);
+    // Type DNAME is not "well-known", and name compression must be disabled
+    // per RFC3597.
+    renderer.writeName(dname_, false);
 }
 
 string

+ 5 - 0
src/lib/dns/rdata/generic/naptr_35.cc

@@ -125,11 +125,16 @@ NAPTR::NAPTR(const NAPTR& naptr):
 void
 NAPTR::toWire(OutputBuffer& buffer) const {
     toWireHelper(buffer);
+    replacement_.toWire(buffer);
+
 }
 
 void
 NAPTR::toWire(AbstractMessageRenderer& renderer) const {
     toWireHelper(renderer);
+    // Type NAPTR is not "well-known", and name compression must be disabled
+    // per RFC3597.
+    renderer.writeName(replacement_, false);
 }
 
 string

+ 0 - 2
src/lib/dns/rdata/generic/naptr_35.h

@@ -62,8 +62,6 @@ private:
 
         outputer.writeUint8(regexp_.size());
         outputer.writeData(regexp_.c_str(), regexp_.size());
-
-        replacement_.toWire(outputer);
     }
 
     uint16_t order_;

+ 3 - 1
src/lib/dns/rdata/generic/nsec_47.cc

@@ -124,7 +124,9 @@ NSEC::toWire(OutputBuffer& buffer) const {
 
 void
 NSEC::toWire(AbstractMessageRenderer& renderer) const {
-    impl_->nextname_.toWire(renderer);
+    // Type NSEC is not "well-known", and name compression must be disabled
+    // per RFC3597.
+    renderer.writeName(impl_->nextname_, false);
     renderer.writeData(&impl_->typebits_[0], impl_->typebits_.size());
 }
 

+ 5 - 3
src/lib/dns/tests/rdata_dname_unittest.cc

@@ -45,9 +45,11 @@ const uint8_t wiredata_dname2[] = {
     // first name: dn.example.com.
     0x02, 0x64, 0x6e, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
     0x63, 0x6f, 0x6d, 0x00,
-    // second name: dn2.example.com.  all labels except the first should be
-    // compressed.
-    0x03, 0x64, 0x6e, 0x32, 0xc0, 0x03 };
+    // second name: dn2.example.com.  The "example.com" shouldn't be compressed
+    // because DNAME is not a well know type per RFC3597.
+    0x03, 0x64, 0x6e, 0x32,
+    0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03,
+    0x63, 0x6f, 0x6d, 0x00 };
 
 TEST_F(Rdata_DNAME_Test, createFromText) {
     EXPECT_EQ(0, rdata_dname.compare(generic::DNAME("dn.example.com")));

+ 1 - 0
src/lib/util/unittests/Makefile.am

@@ -9,6 +9,7 @@ if HAVE_GTEST
 libutil_unittests_la_SOURCES += resource.h resource.cc
 libutil_unittests_la_SOURCES += run_all.h run_all.cc
 libutil_unittests_la_SOURCES += textdata.h
+libutil_unittests_la_SOURCES += wiredata.h wiredata.cc
 endif
 
 # For now, this isn't needed for libutil_unittests

+ 2 - 2
src/lib/util/unittests/fork.h

@@ -18,8 +18,8 @@
 #include <unistd.h>
 
 /**
- * @file fork.h
- * @short Help functions to fork the test case process.
+ * \file fork.h
+ * \brief Help functions to fork the test case process.
  * Various functions to fork a process and feed some data to pipe, check
  * its output and such lives here.
  */

+ 2 - 2
src/lib/util/unittests/newhook.h

@@ -16,8 +16,8 @@
 #define __UTIL_UNITTESTS_NEWHOOK_H 1
 
 /**
- * @file newhook.h
- * @short Enable the use of special operator new that throws for testing.
+ * \file newhook.h
+ * \brief Enable the use of special operator new that throws for testing.
  *
  * This small utility allows a test case to force the global operator new
  * to throw for a given size to test a case where memory allocation fails

+ 2 - 2
src/lib/util/unittests/testdata.h

@@ -16,8 +16,8 @@
 #define __UTIL_UNITTESTS_TESTDATA_H 1
 
 /**
- * @file testdata.h
- * @short Manipulating test data files.
+ * \file testdata.h
+ * \brief Manipulating test data files.
  *
  * This utility defines functions that help test case handle test data
  * stored in a file.

+ 2 - 2
src/lib/util/unittests/textdata.h

@@ -22,8 +22,8 @@
 #define __UTIL_UNITTESTS_TEXTDATA_H 1
 
 /**
- * @file textdata.h
- * @short Utilities for tests with text data.
+ * \file textdata.h
+ * \brief Utilities for tests with text data.
  *
  * This utility provides convenient helper functions for unit tests using
  * textual data.

+ 52 - 0
src/lib/util/unittests/wiredata.cc

@@ -0,0 +1,52 @@
+// 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 "wiredata.h"
+
+#include <gtest/gtest.h>
+
+#include <algorithm>            // for std::min
+#include <stdint.h>
+
+using namespace std;
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+void
+matchWireData(const void* expected_data, size_t expected_len,
+              const void* actual_data, size_t actual_len)
+{
+    const size_t cmplen = std::min(expected_len, actual_len);
+
+    for (size_t i = 0; i < cmplen; ++i) {
+        const int ebyte = static_cast<const uint8_t*>(expected_data)[i];
+        const int abyte = static_cast<const uint8_t*>(actual_data)[i];
+        // Once we find a mismatch, it's quite likely that there will be many
+        // mismatches after this point.  So we stop here by using ASSERT not
+        // to be too noisy.
+        ASSERT_EQ(ebyte, abyte) << "Wire data mismatch at " << i << "th byte\n"
+                                << "  Actual: " << abyte << "\n"
+                                << "Expected: " << ebyte << "\n";
+    }
+    EXPECT_EQ(expected_len, actual_len)
+        << "Wire data mismatch in length:\n"
+        << "  Actual: " << actual_len << "\n"
+        << "Expected: " << expected_len << "\n";
+}
+
+} // unittests
+} // util
+} // isc

+ 45 - 0
src/lib/util/unittests/wiredata.h

@@ -0,0 +1,45 @@
+// 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 __UTIL_UNITTESTS_WIREDATA_H
+#define __UTIL_UNITTESTS_WIREDATA_H 1
+
+#include <cstddef>
+
+/// \file wiredata.h
+/// \brief Utilities for tests with wire data.
+///
+/// This utility provides convenient helper functions for unit tests using
+/// wire (binary) data.
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// \brief Compare two sets of binary data in a google test.
+///
+/// This method checks if the expected and actual data have the same length
+/// and all bytes are the same.  If not, it reports the point of mismatch in
+/// the google test format.
+void matchWireData(const void* expected_data, size_t expected_len,
+                   const void* actual_data, size_t actual_len);
+}
+}
+}
+
+#endif // __UTIL_UNITTESTS_WIREDATA_H
+
+// Local Variables:
+// mode: c++
+// End: