Browse Source

[2094] complete the spec data and test cases

JINMEI Tatuya 12 years ago
parent
commit
2d291f69d8

+ 192 - 25
src/lib/datasrc/memory/rdata_encoder.cc

@@ -15,6 +15,7 @@
 #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>
 
@@ -22,6 +23,9 @@
 
 #include "rdata_encoder.h"
 
+#include <boost/static_assert.hpp>
+
+#include <cassert>
 #include <vector>
 
 #include <stdint.h>
@@ -34,14 +38,6 @@ namespace datasrc {
 namespace memory {
 
 namespace {
-#ifdef notyet
-inline RdataNameAttributes
-operator&(RdataNameAttributes a1, RdataNameAttributes a2) {
-    return (static_cast<RdataNameAttributes>(
-                static_cast<unsigned int>(a1) &
-                static_cast<unsigned int>(a2)));
-}
-#endif
 struct RdataFieldSpec {
     enum FieldType {
         FIXEDLEN_DATA = 0,
@@ -75,43 +71,185 @@ const uint16_t NAMEATTR_COMPADDITIONAL_INITIALIZER =
 const RdataFieldSpec generic_data_fields[] = {
     {RdataFieldSpec::VARLEN_DATA, {0}}
 };
-const uint16_t generic_data_fields_count =
+const uint16_t n_generic_data_fields =
     sizeof(generic_data_fields) / sizeof(RdataFieldSpec);
 
+// Most general form of field spec: that consisting of single variable-len
+// data field.
+const RdataEncodeSpec generic_data_spec = {
+    n_generic_data_fields, 0, 1, generic_data_fields
+};
+
 // TBD
 const RdataFieldSpec single_ipv4_fields[] = {
     {RdataFieldSpec::FIXEDLEN_DATA, {sizeof(uint32_t)}}
 };
-const uint16_t single_ipv4_fields_count =
+const uint16_t n_ipv4_fields =
     sizeof(single_ipv4_fields) / sizeof(RdataFieldSpec);
 
+const RdataFieldSpec single_ipv6_fields[] = {
+    {RdataFieldSpec::FIXEDLEN_DATA, {16}} // 128bits = 16 bytes
+};
+const uint16_t n_ipv6_fields =
+    sizeof(single_ipv6_fields) / sizeof(RdataFieldSpec);
+
 // TBD
-const RdataFieldSpec generic_single_name_fields[] = {
+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 + 20-byte 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 generic_single_name_fields_count =
-    sizeof(generic_single_name_fields) / sizeof(RdataFieldSpec);
+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
 const RdataEncodeSpec encode_spec_list_in[] = {
-    {generic_data_fields_count, 0, 0, generic_data_fields}, // #0
-    {single_ipv4_fields_count, 0, 0, single_ipv4_fields},   // #1: A
-    {generic_single_name_fields_count, 0, 0, generic_single_name_fields} // NS
+    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.
 };
 
+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) {
-    if (rrclass == RRClass::IN()) {
+    if (rrclass != RRClass::IN() &&
+        (rrtype == RRType::A() || rrtype == RRType::AAAA() ||
+         rrtype == RRType::SRV())) {
+        return (generic_data_spec);
+    }
+
+    const uint16_t typecode = rrtype.getCode();
+    if (typecode < encode_spec_list_in_size) {
         return (encode_spec_list_in[rrtype.getCode()]);
     }
-    return (encode_spec_list_in[0]);
+    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
 
 void
 encodeRdata(const rdata::Rdata& rdata, RRClass rrclass, RRType rrtype,
-            vector<uint8_t>& data_result, vector<uint16_t>& /*len_result*/)
+            vector<uint8_t>& data_result, vector<uint16_t>& len_result)
 {
     util::OutputBuffer buffer(0);
     rdata.toWire(buffer);
@@ -127,6 +265,23 @@ encodeRdata(const rdata::Rdata& rdata, RRClass rrclass, RRType rrtype,
             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);
@@ -143,8 +298,6 @@ encodeRdata(const rdata::Rdata& rdata, RRClass rrclass, RRType rrtype,
                                offset_holder + olen);
             break;
         }
-        default:
-            assert(false);
         }
     }
 }
@@ -152,23 +305,36 @@ encodeRdata(const rdata::Rdata& rdata, RRClass rrclass, RRType rrtype,
 void
 foreachRdataField(RRClass rrclass, RRType rrtype,
                   const vector<uint8_t>& encoded_data,
-                  const vector<uint16_t>& /*varlen_list*/,
+                  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[off], field_spec.fixeddata_len);
+                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) {
@@ -180,10 +346,11 @@ foreachRdataField(RRClass rrclass, RRType rrtype,
             off += (2 + nlen + olen);
             break;
         }
-        default:
-            assert(false);
         }
     }
+
+    assert(name_count == encode_spec.name_count);
+    assert(varlen_count == encode_spec.varlen_count);
 }
 
 } // namespace memory

+ 46 - 6
src/lib/datasrc/memory/tests/rdata_encoder_unittest.cc

@@ -44,11 +44,45 @@ struct TestRdata {
     const char* const rrclass;
     const char* const rrtype;
     const char* const rdata;
+    const size_t n_varlen_fields; // expected # of variable-len fields
 };
 const TestRdata test_rdata_list[] = {
-    {"IN", "A", "192.0.2.1"},
-    {"IN", "NS", "ns.example.com"},
-    {NULL, NULL, NULL}
+    {"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}
 };
 
 void
@@ -68,7 +102,8 @@ renderDataField(MessageRenderer* renderer, const uint8_t* data,
 }
 
 TEST(RdataFieldSpec, checkData) {
-    const Name dummy_name("example.com");
+    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;
@@ -79,30 +114,35 @@ TEST(RdataFieldSpec, checkData) {
     need_additionals.insert(RRType::SRV());
 
     for (size_t i = 1; test_rdata_list[i].rrclass != NULL; ++i) {
-        SCOPED_TRACE(string(test_rdata_list->rrclass) + "/" +
-                     test_rdata_list->rrtype);
+        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(),