Browse Source

[2091a] added revised version of LabelSequence (de)serialization

JINMEI Tatuya 12 years ago
parent
commit
5b7ac017e3
3 changed files with 253 additions and 8 deletions
  1. 58 0
      src/lib/dns/labelsequence.cc
  2. 64 0
      src/lib/dns/labelsequence.h
  3. 131 8
      src/lib/dns/tests/labelsequence_unittest.cc

+ 58 - 0
src/lib/dns/labelsequence.cc

@@ -51,6 +51,37 @@ LabelSequence::LabelSequence(const uint8_t* data,
     }
 }
 
+LabelSequence::LabelSequence(const void* buf) {
+    if (buf == NULL) {
+        isc_throw(BadValue,
+                  "Null pointer passed to LabelSequence constructor");
+    }
+
+    const uint8_t* bp = reinterpret_cast<const uint8_t*>(buf);
+
+    first_label_ = 0;
+    const uint8_t offsets_len = *bp++;
+    if (offsets_len == 0 || offsets_len > Name::MAX_LABELS) {
+        isc_throw(BadValue,
+                  "Bad offsets len in serialized LabelSequence data: "
+                  << static_cast<unsigned int>(offsets_len));
+    }
+    last_label_ = offsets_len - 1;
+    offsets_ = bp;
+    data_ = bp + offsets_len;
+
+    // Check the integrity on the offsets and the name data
+    const uint8_t* dp = data_;
+    for (size_t cur_offset = 0; cur_offset < offsets_len; ++cur_offset) {
+        if (offsets_[cur_offset] > Name::MAX_LABELLEN ||
+            dp - data_ != offsets_[cur_offset]) {
+            isc_throw(BadValue,
+                      "Broken offset or name data in serialized "
+                      "LabelSequence data");
+        }
+        dp += (1 + *dp);
+    }
+}
 
 const uint8_t*
 LabelSequence::getData(size_t *len) const {
@@ -74,6 +105,33 @@ LabelSequence::getDataLength() const {
     return (offsets_[last_label_] - offsets_[first_label_] + last_label_len);
 }
 
+size_t
+LabelSequence::getSerializedLength() const {
+    return (1 + getLabelCount() + getDataLength());
+}
+
+void
+LabelSequence::serialize(void* buf, size_t buf_len) const {
+    const size_t expected_size = getSerializedLength();
+    if (expected_size > buf_len) {
+        isc_throw(BadValue, "buffer too short for LabelSequence::serialize");
+    }
+
+    const size_t offsets_len = getLabelCount();
+    assert(offsets_len < 256);  // should be in the 8-bit range
+
+    uint8_t* bp = reinterpret_cast<uint8_t*>(buf);
+    *bp++ = offsets_len;
+    for (size_t i = 0; i < offsets_len; ++i) {
+        *bp++ = offsets_[first_label_ + i] - offsets_[first_label_];
+    }
+    const size_t ndata_len = getDataLength();
+    memcpy(bp, &data_[offsets_[first_label_]], ndata_len);
+    bp += ndata_len;
+
+    assert(bp - reinterpret_cast<const uint8_t*>(buf) == expected_size);
+}
+
 bool
 LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
     size_t len, other_len;

+ 64 - 0
src/lib/dns/labelsequence.h

@@ -79,6 +79,24 @@ public:
                   const uint8_t* offsets,
                   size_t offsets_size);
 
+    /// \brief Constructor from serialized image.
+    ///
+    /// This constructor restores a \c LabelSequence object from a serialized
+    /// binary image previously generated by \c serialize().  Any other input
+    /// to this constructor will result in undefined behavior.
+    ///
+    /// The binary data passed to this constructor MUST remain in scope and
+    /// MUST NOT be modified during the lifetime of this LabelSequence.
+    ///
+    /// As long as the data were previously generated by a call to
+    /// \c serialize() on a valid \c LabelSequence object, this constructor
+    /// should succeed.  While any other case is undefined, this constructor
+    /// may perform some validity checks internally for safety.  Nevertheless,
+    /// applications must not rely on such checks.
+    ///
+    /// \param buf Pointer to the serialized image generated by \c serialize().
+    explicit LabelSequence(const void* buf);
+
     /// \brief Copy constructor.
     ///
     /// \note The associated data MUST remain in scope during the lifetime
@@ -136,6 +154,52 @@ public:
     /// \return The length of the data of the label sequence in octets.
     size_t getDataLength() const;
 
+    /// \brief Max possible size of serialized image generated by \c serialize
+    ///
+    /// A fixed length buffer of this size can be always passed to
+    /// \c serialize() safely.  (But the application shouldn't use the
+    /// specific size value; it must use this constant variable).
+    static const size_t MAX_SERIALIZED_LENGTH =
+        Name::MAX_WIRE + Name::MAX_LABELS + 1;
+
+    /// \brief Return the size of serialized image of the \c LabelSequence.
+    ///
+    /// This method calculates the size of necessary storage to store
+    /// serialized image of this \c LabelSequence (which would be dumped by
+    /// \c serialize()) and returns it.  The size is in bytes.
+    ///
+    /// \throw none.
+    ///
+    /// \return The size of serialized image of the \c LabelSequence.
+    size_t getSerializedLength() const;
+
+    /// \brief Serialize the \c LabelSequence object in to a buffer.
+    ///
+    /// This method dumps a serialized image of this \c LabelSequence
+    /// that would be restored by the corresponding constructor into the
+    /// given buffer.  The buffer size must be at least equal to
+    /// the value returned by getSerializedLength() (it can be larger than
+    /// that).
+    ///
+    /// The serialized image would be as follows:
+    /// - olen: number of offsets (1 byte)
+    /// - binary sequence of offsets (olen bytes, verbatim copy of offsets_
+    ///   of this size)
+    /// - binary sequence of name data (length determined by itself, verbatim
+    ///   copy of data_ of the corresponding size)
+    ///
+    /// Applications must use the resulting image opaque value and must not
+    /// use it for other purposes than input to the corresponding constructor
+    /// to restore it.  Application behavior that assumes the specific
+    /// organization of the image is not guaranteed.
+    ///
+    /// \throw isc::BadValue buf_len is too short (this method never throws
+    /// otherwise)
+    ///
+    /// \param buf Pointer to the placeholder to dump the serialized image
+    /// \param buf_len The size of available region in \c buf
+    void serialize(void* buf, size_t buf_len) const;
+
     /// \brief Compares two label sequences for equality.
     ///
     /// Performs a (optionally case-insensitive) comparison between this

+ 131 - 8
src/lib/dns/tests/labelsequence_unittest.cc

@@ -21,11 +21,17 @@
 #include <boost/functional/hash.hpp>
 
 #include <string>
+#include <vector>
+#include <utility>
 #include <set>
 
 using namespace isc::dns;
 using namespace std;
 
+// XXX: this is defined as class static constants, but some compilers
+// seemingly cannot find the symbols when used in the EXPECT_xxx macros.
+const size_t LabelSequence::MAX_SERIALIZED_LENGTH;
+
 namespace {
 
 class LabelSequenceTest : public ::testing::Test {
@@ -38,6 +44,13 @@ public:
                           n10("\\000xample.org"),
                           n11("\\000xample.com"),
                           n12("\\000xamplE.com"),
+                          n_maxlabel("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
+                                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
+                                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
+                                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
+                                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
+                                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
+                                     "0.1.2.3.4.5.6"),
                           ls1(n1), ls2(n2), ls3(n3), ls4(n4), ls5(n5),
                           ls6(n6), ls7(n7), ls8(n8),
                           ls9(n9), ls10(n10), ls11(n11), ls12(n12)
@@ -46,6 +59,7 @@ public:
     // the labelsequences
     Name n1, n2, n3, n4, n5, n6, n7, n8;
     Name n9, n10, n11, n12;
+    const Name n_maxlabel;
 
     LabelSequence ls1, ls2, ls3, ls4, ls5, ls6, ls7, ls8;
     LabelSequence ls9, ls10, ls11, ls12;
@@ -654,14 +668,7 @@ TEST_F(LabelSequenceTest, toText) {
               "012345678901234567890123456789"
               "0123456789012345678901234567890", ls_long1.toText());
 
-    Name n_long2("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
-                 "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
-                 "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
-                 "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
-                 "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
-                 "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
-                 "0.1.2.3.4.5.6");
-    LabelSequence ls_long2(n_long2);
+    LabelSequence ls_long2(n_maxlabel);
 
     EXPECT_EQ("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
               "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
@@ -843,4 +850,120 @@ TEST(LabelSequence, badRawConstruction) {
     EXPECT_THROW(LabelSequence(data, offsets_noincrease, 3), isc::BadValue);
 }
 
+TEST_F(LabelSequenceTest, serializedLength) {
+    // Initially, the labels are "example.org."
+    const size_t base_size = n1.getLength() + n1.getLabelCount() + 1;
+    EXPECT_EQ(base_size, ls1.getSerializedLength());
+
+    // Strip off the trailing dot.  We'll lose 1 label and 1-byte data
+    LabelSequence ls1_stripped = ls1;
+    ls1_stripped.stripRight(1);
+    EXPECT_EQ(base_size - 2, ls1_stripped.getSerializedLength());
+
+    // Strip off the leftmost label (example).  We'll lose 1 label and
+    // 8-byte data (1 + len('example')).
+    ls1_stripped = ls1;
+    ls1_stripped.stripLeft(1);
+    EXPECT_EQ(base_size - 9, ls1_stripped.getSerializedLength());
+
+    // Longest possible serialized length.  Confirm there's indeed such a case.
+    EXPECT_EQ(LabelSequence::MAX_SERIALIZED_LENGTH,
+              LabelSequence(n_maxlabel).getSerializedLength());
+}
+
+TEST_F(LabelSequenceTest, serialize) {
+    // placeholder for serialized data
+    uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+
+    // vector to store expected and actual data
+    vector<LabelSequence> actual_labelseqs;
+    typedef pair<size_t, const uint8_t*> DataPair;
+    vector<DataPair> expected;
+
+    // An absolute sequence directly constructed from a valid name.
+    // labels = 3, offset sequence = 0, 8, 12, data = "example.com."
+    actual_labelseqs.push_back(ls1);
+    const uint8_t const expected_data1[] = {
+        3, 0, 8, 12, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
+        3, 'o', 'r', 'g', 0 };
+    expected.push_back(DataPair(sizeof(expected_data1), expected_data1));
+
+    // Strip the original one from right.
+    // labels = 2, offset sequence = 0, 8, data = "example.com" (non absolute)
+    LabelSequence ls_rstripped = ls1;
+    ls_rstripped.stripRight(1);
+    actual_labelseqs.push_back(ls_rstripped);
+    const uint8_t const expected_data2[] = {
+        2, 0, 8, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
+        3, 'o', 'r', 'g'};
+    expected.push_back(DataPair(sizeof(expected_data2), expected_data2));
+
+    // Strip the original one from left.
+    // labels = 2, offset sequence = 0, 4, data = "com."
+    // Note that offsets are adjusted so that they begin with 0.
+    LabelSequence ls_lstripped = ls1;
+    ls_lstripped.stripLeft(1);
+    actual_labelseqs.push_back(ls_lstripped);
+    const uint8_t const expected_data3[] = {
+        2, 0, 4, 3, 'o', 'r', 'g', 0 };
+    expected.push_back(DataPair(sizeof(expected_data3), expected_data3));
+
+    // Root label.
+    LabelSequence ls_root(Name::ROOT_NAME());
+    actual_labelseqs.push_back(ls_root);
+    const uint8_t const expected_data4[] = { 1, 0, 0 };
+    expected.push_back(DataPair(sizeof(expected_data4), expected_data4));
+
+    // Non absolute single-label.
+    LabelSequence ls_single = ls_rstripped;
+    ls_single.stripRight(1);
+    actual_labelseqs.push_back(ls_single);
+    const uint8_t const expected_data5[] = {
+        1, 0, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e' };
+    expected.push_back(DataPair(sizeof(expected_data5), expected_data5));
+
+    // For each data set, serialize the labels and compare the data to the
+    // expected one.
+    vector<DataPair>::const_iterator it = expected.begin();
+    vector<LabelSequence>::const_iterator itl = actual_labelseqs.begin();
+    for (; it != expected.end(); ++it, ++itl) {
+        SCOPED_TRACE(itl->toText());
+
+        const size_t serialized_len = itl->getSerializedLength();
+
+        ASSERT_GE(LabelSequence::MAX_SERIALIZED_LENGTH, serialized_len);
+        itl->serialize(labels_buf, serialized_len);
+        EXPECT_EQ(it->first, serialized_len);
+        EXPECT_EQ(0, memcmp(it->second, labels_buf, serialized_len));
+
+        EXPECT_EQ(NameComparisonResult::EQUAL,
+                  LabelSequence(labels_buf).compare(*itl).getRelation());
+    }
+
+    EXPECT_THROW(ls1.serialize(labels_buf, ls1.getSerializedLength() - 1),
+                 isc::BadValue);
+}
+
+TEST_F(LabelSequenceTest, badDeserialize) {
+    EXPECT_THROW(LabelSequence(NULL), isc::BadValue);
+    const uint8_t const zero_offsets[] = { 0 };
+    EXPECT_THROW(LabelSequence ls(zero_offsets), isc::BadValue);
+    const uint8_t const toomany_offsets[] = { Name::MAX_LABELS + 1 };
+    EXPECT_THROW(LabelSequence ls(toomany_offsets), isc::BadValue);
+
+    // exceed MAX_LABEL_LEN
+    const uint8_t const offsets_toolonglabel[] = { 2, 0, 64 };
+    EXPECT_THROW(LabelSequence ls(offsets_toolonglabel), isc::BadValue);
+
+    // Inconsistent data: an offset is lower than the previous offset
+    const uint8_t const offsets_lower[] = { 3, // # of offsets
+                                            0, 2, 1, // offsets
+                                            1, 'a', 1, 'b', 0};
+    EXPECT_THROW(LabelSequence ls(offsets_lower), isc::BadValue);
+
+    // Inconsistent data: an offset is equal to the previous offset
+    const uint8_t const offsets_noincrease[] = { 2, 0, 0, 0, 0 };
+    EXPECT_THROW(LabelSequence ls(offsets_noincrease), isc::BadValue);
+}
+
 }