Browse Source

[master] Merge branch 'trac2148'

Conflicts:
	src/lib/dns/labelsequence.h
Jelte Jansen 12 years ago
parent
commit
285c2845ca

+ 5 - 3
src/lib/datasrc/memory_datasrc.cc

@@ -147,9 +147,11 @@ struct ZoneData {
 
         // The assert may be too harsh, but we assume we'll discard (rewrite)
         // this code soon enough.  Until then this would be a good way to
-        // detect any memory leak.  Also, at that point we shouldn't use
-        // a single separate memory segment for each zone tree; normally
-        // zone data for multiple zones will be managed in a single segment.
+        // detect any memory leak.  Also, at that point we should use
+        // a single separate memory segment for each zone tree; eventually
+        // zone data for multiple zones will be managed in a single segment,
+        // at which point the segment will be passed from outside of this
+        // module.
         assert(local_mem_sgmt_.allMemoryDeallocated());
     }
 

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

@@ -55,6 +55,25 @@ LabelSequence::LabelSequence(const void* buf) {
     }
 }
 
+LabelSequence::LabelSequence(const LabelSequence& src,
+                             uint8_t buf[MAX_SERIALIZED_LENGTH])
+{
+    size_t data_len;
+    const uint8_t *data = src.getData(&data_len);
+    memcpy(buf, data, data_len);
+
+    for (size_t i = 0; i < src.getLabelCount(); ++i) {
+        buf[Name::MAX_WIRE + i] = src.offsets_[i + src.first_label_] -
+                                  src.offsets_[src.first_label_];
+    }
+
+    first_label_ = 0;
+    last_label_ = src.last_label_ - src.first_label_;
+    data_ = buf;
+    offsets_ = &buf[Name::MAX_WIRE];
+}
+
+
 const uint8_t*
 LabelSequence::getData(size_t *len) const {
     *len = getDataLength();
@@ -318,6 +337,51 @@ LabelSequence::toText() const {
     return (toText(!isAbsolute()));
 }
 
+void
+LabelSequence::extend(const LabelSequence& labels,
+                      uint8_t buf[MAX_SERIALIZED_LENGTH])
+{
+    // collect data to perform steps before anything is changed
+    size_t label_count = last_label_ + 1;
+    // Since we may have been stripped, do not use getDataLength(), but
+    // calculate actual data size this labelsequence currently uses
+    size_t data_pos = offsets_[last_label_] + data_[offsets_[last_label_]] + 1;
+
+    // If this labelsequence is absolute, virtually strip the root label.
+    if (isAbsolute()) {
+        data_pos--;
+        label_count--;
+    }
+    const size_t append_label_count = labels.getLabelCount();
+    size_t data_len;
+    const uint8_t *data = labels.getData(&data_len);
+
+    // Sanity checks
+    if (data_ != buf || offsets_ != &buf[Name::MAX_WIRE]) {
+        isc_throw(BadValue,
+                  "extend() called with unrelated buffer");
+    }
+    if (data_pos + data_len > Name::MAX_WIRE) {
+        isc_throw(BadValue,
+                  "extend() would exceed maximum wire length");
+    }
+    if (label_count + append_label_count > Name::MAX_LABELS) {
+        isc_throw(BadValue,
+                  "extend() would exceed maximum number of labels");
+    }
+
+    // All seems to be reasonably ok, let's proceed.
+    memmove(&buf[data_pos], data, data_len);
+
+    for (size_t i = 0; i < append_label_count; ++i) {
+        buf[Name::MAX_WIRE + label_count + i] =
+            offsets_[label_count] +
+            labels.offsets_[i + labels.first_label_] -
+            labels.offsets_[labels.first_label_];
+    }
+    last_label_ = label_count + append_label_count - 1;
+}
+
 std::ostream&
 operator<<(std::ostream& os, const LabelSequence& label_sequence) {
     os << label_sequence.toText();

+ 83 - 8
src/lib/dns/labelsequence.h

@@ -45,6 +45,14 @@ class LabelSequence {
     friend std::string Name::toText(bool) const;
 
 public:
+    /// \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 Constructs a LabelSequence for the given name
     ///
     /// \note The associated Name MUST remain in scope during the lifetime
@@ -78,6 +86,49 @@ public:
     /// \param buf Pointer to the serialized image generated by \c serialize().
     explicit LabelSequence(const void* buf);
 
+    /// \brief Construct 'extendable' LabelSequence
+    ///
+    /// This form of LabelSequence copies the data from the given
+    /// labelsequence into the given external buffer, which is subsequently
+    /// extendable by calling extend()
+    ///
+    /// The data is placed into the given buffer as follows:
+    /// - binary sequence of name data, starting at position 0,
+    ///   length determined by source LabelSequence
+    /// - offsets, starting at position Name::MAX_WIRE, length
+    ///   determined by source LabelSequence
+    /// The offsets are updated to be correct for the potentially partial
+    /// name data (as stripLeft() and stripRight may have been called on
+    /// the source LabelSequence).
+    ///
+    /// \note The given buf MUST remain in scope during the lifetime of
+    /// the LabelSequence created here.
+    /// \note The buffer should never be modified except through
+    /// calls to extend().
+    /// \note Also, only associate the buffer with at most one
+    /// LabelSequence. Behaviour is undefined if two LabelSequences are
+    /// using the same buffer.
+    ///
+    /// \param src LabelSequence to copy the initial data from
+    /// \param buf external buffer to store this labelsequence's data in
+    LabelSequence(const LabelSequence& src, uint8_t buf[MAX_SERIALIZED_LENGTH]);
+
+    /// \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_),
+        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
@@ -109,14 +160,6 @@ 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
@@ -223,6 +266,38 @@ public:
     /// \return a string representation of the <code>LabelSequence</code>.
     std::string toText() const;
 
+    /// \brief Extend this LabelSequence with the given labelsequence
+    ///
+    /// The given labels are appended to the name data, and internal
+    /// offset data is updated accordingly.
+    ///
+    /// The data from the given LabelSequence is copied into the buffer
+    /// associated with this LabelSequence; the appended LabelSequence
+    /// (the 'labels' argument) can be released if it is not needed for
+    /// other operations anymore.
+    ///
+    /// If this LabelSequence is absolute, its root label will be stripped
+    /// before the given LabelSequence is appended; after extend(),
+    /// this LabelSequence will be absolute if, and only if, the appended
+    /// LabelSequence was. A side-effect of this property is that adding
+    /// the root label to an absolute LabelSequence has no effect (the
+    /// root label is stripped, then added again).
+    ///
+    /// Some minimal checking is done on the data, but internal integrity
+    /// is not assumed. Do NOT modify the given buffer except through calls
+    /// to this method, and do NOT call this method if the buffer is
+    /// associated to another LabelSequence (behaviour of the other
+    /// LabelSequence is undefined in that scenario).
+    ///
+    /// \exception BadValue If the buffer does not appear to be associated
+    /// with this LabelSequence, or if the maximum wire length or maximum
+    /// number of labels would be exceeded by this operation
+    ///
+    /// \param labels The labels to append to this LabelSequence
+    /// \param buf The buffer associated with this LabelSequence
+    void extend(const LabelSequence& labels,
+                uint8_t buf[MAX_SERIALIZED_LENGTH]);
+
 private:
     /// \brief Convert the LabelSequence to a string.
     ///

+ 353 - 0
src/lib/dns/tests/labelsequence_unittest.cc

@@ -34,6 +34,27 @@ const size_t LabelSequence::MAX_SERIALIZED_LENGTH;
 
 namespace {
 
+// Common check that two labelsequences are equal
+void check_equal(const LabelSequence& ls1, const LabelSequence& ls2) {
+    NameComparisonResult result = ls1.compare(ls2);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation()) << ls1.toText() << " != " << ls2.toText();
+    EXPECT_EQ(0, result.getOrder()) << ls1.toText() << " != " << ls2.toText();
+    EXPECT_EQ(ls1.getLabelCount(), result.getCommonLabels());
+}
+
+// Common check for general comparison of two labelsequences
+void check_compare(const LabelSequence& ls1, const LabelSequence& ls2,
+                   isc::dns::NameComparisonResult::NameRelation relation,
+                   size_t common_labels, bool check_order, int order=0) {
+    NameComparisonResult result = ls1.compare(ls2);
+    EXPECT_EQ(relation, result.getRelation());
+    EXPECT_EQ(common_labels, result.getCommonLabels());
+    if (check_order) {
+        EXPECT_EQ(order, result.getOrder());
+    }
+}
+
 class LabelSequenceTest : public ::testing::Test {
 public:
     LabelSequenceTest() : n1("example.org"), n2("example.com"),
@@ -784,4 +805,336 @@ TEST_F(LabelSequenceTest, badDeserialize) {
     EXPECT_THROW(LabelSequence ls(offsets_noincrease), isc::BadValue);
 }
 
+namespace {
+
+// Helper function; repeatedly calls
+// - Initially, all three labelsequences should be the same
+// - repeatedly performs:
+//   - checks all three are equal
+//   - stripLeft on ls1
+//   - checks ls1 and ls2 are different, and ls2 and ls3 are equal
+//   - stripLeft on ls2
+//   - checks ls1 and ls2 are equal, and ls2 and ls3 are different
+//   - stripLeft on ls3
+//
+// (this test makes sure the stripLeft of one has no effect on the other
+// two, and that the strip properties hold regardless of how they were
+// constructed)
+//
+void stripLeftCheck(LabelSequence ls1, LabelSequence ls2, LabelSequence ls3) {
+    ASSERT_LT(1, ls1.getLabelCount());
+    while (ls1.getLabelCount() > 1) {
+        check_equal(ls1, ls2);
+        check_equal(ls2, ls3);
+
+        ls1.stripLeft(1);
+        check_compare(ls1, ls2, isc::dns::NameComparisonResult::SUPERDOMAIN,
+                      ls1.getLabelCount(), true, -1);
+        check_equal(ls2, ls3);
+
+        ls2.stripLeft(1);
+        check_equal(ls1, ls2);
+        check_compare(ls2, ls3, isc::dns::NameComparisonResult::SUPERDOMAIN,
+                      ls1.getLabelCount(), true, -1);
+
+        ls3.stripLeft(1);
+    }
+}
+
+// Similar to stripLeftCheck, but using stripRight()
+void stripRightCheck(LabelSequence ls1, LabelSequence ls2, LabelSequence ls3) {
+    ASSERT_LT(1, ls1.getLabelCount());
+    while (ls1.getLabelCount() > 1) {
+        check_equal(ls1, ls2);
+        check_equal(ls2, ls3);
+
+        ls1.stripRight(1);
+        check_compare(ls1, ls2, isc::dns::NameComparisonResult::NONE, 0,
+                      false);
+        check_equal(ls2, ls3);
+
+        ls2.stripRight(1);
+        check_equal(ls1, ls2);
+        check_compare(ls2, ls3, isc::dns::NameComparisonResult::NONE, 0,
+                      false);
+
+        ls3.stripRight(1);
+    }
+}
+
+} // end anonymous namespace
+
+class ExtendableLabelSequenceTest : public ::testing::Test {
+public:
+    ExtendableLabelSequenceTest() : bar("bar."),
+                                    example_org("example.org"),
+                                    foo("foo."),
+                                    foo_bar("foo.bar."),
+                                    foo_bar_example_org("foo.bar.example.org."),
+                                    foo_bar_foo_bar("foo.bar.foo.bar."),
+                                    foo_example("foo.example."),
+                                    org("org")
+    {
+        // explicitely set to non-zero data, to make sure
+        // we don't try to use data we don't set
+        memset(buf, 0xff, LabelSequence::MAX_SERIALIZED_LENGTH);
+    }
+
+    Name bar;
+    Name example_org;
+    Name foo;
+    Name foo_bar;
+    Name foo_bar_example_org;
+    Name foo_bar_foo_bar;
+    Name foo_example;
+    Name org;
+
+    uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+};
+
+// Test that 'extendable' labelsequences behave correctly when using
+// stripLeft() and stripRight()
+TEST_F(ExtendableLabelSequenceTest, extendableLabelSequence) {
+    LabelSequence ls1(example_org);
+    LabelSequence ls2(example_org);
+
+    LabelSequence els(ls1, buf);
+    // ls1 is absolute, so els should be too
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(ls1, els);
+
+    ASSERT_EQ(ls1.getDataLength(), els.getDataLength());
+    stripLeftCheck(ls1, els, ls2);
+    stripRightCheck(ls1, els, ls2);
+
+    // Creating an extendable labelsequence from a non-absolute
+    // label sequence should result in a non-absolute label sequence
+    ls1.stripRight(1);
+    els = LabelSequence(ls1, buf);
+    EXPECT_FALSE(els.isAbsolute());
+    check_equal(ls1, els);
+
+    // and extending with the root label should make it absolute again
+    els.extend(LabelSequence(Name(".")), buf);
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(ls2, els);
+}
+
+// Test that 'extendable' LabelSequences behave correctly when initialized
+// with a stripped source LabelSequence
+TEST_F(ExtendableLabelSequenceTest, extendableLabelSequenceLeftStrippedSource) {
+    LabelSequence ls1(foo_bar_example_org);
+    LabelSequence ls2(foo_bar_example_org);
+
+    while (ls1.getLabelCount() > 2) {
+        ls1.stripLeft(1);
+        ls2.stripLeft(1);
+
+        LabelSequence els(ls1, buf);
+
+        ASSERT_EQ(ls1.getDataLength(), els.getDataLength());
+        stripLeftCheck(ls1, els, ls2);
+        stripRightCheck(ls1, els, ls2);
+    }
+}
+
+TEST_F(ExtendableLabelSequenceTest, extendableLabelSequenceRightStrippedSource) {
+    LabelSequence ls1(foo_bar_example_org);
+    LabelSequence ls2(foo_bar_example_org);
+
+    while (ls1.getLabelCount() > 2) {
+        ls1.stripRight(1);
+        ls2.stripRight(1);
+
+        LabelSequence els(ls1, buf);
+
+        ASSERT_EQ(ls1.getDataLength(), els.getDataLength());
+        stripLeftCheck(ls1, els, ls2);
+        stripRightCheck(ls1, els, ls2);
+    }
+}
+
+// Check some basic 'extend' functionality
+TEST_F(ExtendableLabelSequenceTest, extend) {
+    LabelSequence ls1(foo_bar);
+    LabelSequence ls2(foo);
+    LabelSequence ls3(bar);
+    LabelSequence ls4(foo_bar);
+
+    LabelSequence els(ls2, buf);
+
+    check_compare(ls1, els, isc::dns::NameComparisonResult::COMMONANCESTOR, 1,
+                  true, -4);
+    els.extend(ls3, buf);
+    EXPECT_TRUE(els.isAbsolute());
+
+    check_equal(ls1, els);
+    stripLeftCheck(ls1, els, ls4);
+    stripRightCheck(ls1, els, ls4);
+
+    // strip, then extend again
+    els.stripRight(2); // (2, 1 for root label, 1 for last label)
+    els.extend(ls3, buf);
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(ls1, els);
+
+    // Extending again should make it different
+    els.extend(ls3, buf);
+    EXPECT_TRUE(els.isAbsolute());
+    check_compare(ls1, els, isc::dns::NameComparisonResult::COMMONANCESTOR, 2,
+                  true, 4);
+
+    // Extending with a non-absolute name should make it non-absolute as well
+    ls3.stripRight(1);
+    els.extend(ls3, buf);
+    EXPECT_FALSE(els.isAbsolute());
+
+    Name check_name("foo.bar.bar.bar");
+    LabelSequence check_ls(check_name);
+    check_ls.stripRight(1);
+    check_equal(check_ls, els);
+
+    // And try extending when both are not absolute
+    els.stripRight(3);
+    ls1.stripRight(1);
+    EXPECT_FALSE(els.isAbsolute());
+    els.extend(ls3, buf);
+    EXPECT_FALSE(els.isAbsolute());
+    check_equal(ls1, els);
+
+    // Extending non-absolute with absolute should make it absolute again
+    EXPECT_FALSE(els.isAbsolute());
+    els.extend(LabelSequence(Name("absolute.")), buf);
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(LabelSequence(Name("foo.bar.absolute")), els);
+}
+
+TEST_F(ExtendableLabelSequenceTest, extendLeftStripped) {
+    LabelSequence ls1(foo_example);
+    LabelSequence ls2(example_org);
+    LabelSequence ls3(org);
+
+    LabelSequence els(ls1, buf);
+
+    els.stripLeft(1);
+    els.extend(ls3, buf);
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(ls2, els);
+}
+
+// Check that when extending with itself, it does not cause horrible failures
+TEST_F(ExtendableLabelSequenceTest, extendWithItself) {
+    LabelSequence ls1(foo_bar);
+    LabelSequence ls2(foo_bar_foo_bar);
+
+    LabelSequence els(ls1, buf);
+
+    els.extend(els, buf);
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(ls2, els);
+
+    // Also try for non-absolute names
+    ls2.stripRight(1);
+    els = LabelSequence(ls1, buf);
+    els.stripRight(1);
+    els.extend(els, buf);
+    EXPECT_FALSE(els.isAbsolute());
+    check_equal(ls2, els);
+
+    // Once more, now start out with non-absolute labelsequence
+    ls1.stripRight(1);
+    els = LabelSequence(ls1, buf);
+    els.extend(els, buf);
+    EXPECT_FALSE(els.isAbsolute());
+    check_equal(ls2, els);
+}
+
+// Test that 'extending' with just a root label is a no-op, iff the original
+// was already absolute
+TEST_F(ExtendableLabelSequenceTest, extendWithRoot) {
+    LabelSequence ls1(example_org);
+
+    LabelSequence els(LabelSequence(ls1, buf));
+    check_equal(ls1, els);
+    els.extend(LabelSequence(Name(".")), buf);
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(ls1, els);
+
+    // but not if the original was not absolute (it will be equal to
+    // the original labelsequence used above, but not the one it was based
+    // on).
+    LabelSequence ls2(example_org);
+    ls2.stripRight(1);
+    els = LabelSequence(ls2, buf);
+    EXPECT_FALSE(els.isAbsolute());
+    els.extend(LabelSequence(Name(".")), buf);
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(ls1, els);
+    check_compare(ls2, els, isc::dns::NameComparisonResult::NONE, 0, true, 3);
+}
+
+// Check possible failure modes of extend()
+TEST_F(ExtendableLabelSequenceTest, extendBadData) {
+    LabelSequence ls1(example_org);
+
+    LabelSequence els(ls1, buf);
+
+    // try use with unrelated labelsequence
+    EXPECT_THROW(ls1.extend(ls1, buf), isc::BadValue);
+
+    // Create a long name, but so that we can still extend once
+    Name longlabel("1234567890123456789012345678901234567890"
+                   "12345678901234567890");
+    LabelSequence long_ls(longlabel);
+    els = LabelSequence(long_ls, buf);
+    els.extend(els, buf);
+    els.extend(long_ls, buf);
+    els.extend(long_ls, buf);
+    ASSERT_EQ(245, els.getDataLength());
+    // Extending once more with 10 bytes should still work
+    els.extend(LabelSequence(Name("123456789")), buf);
+    EXPECT_TRUE(els.isAbsolute());
+
+    // Extended label sequence should now look like
+    const Name full_name(
+        "123456789012345678901234567890123456789012345678901234567890."
+        "123456789012345678901234567890123456789012345678901234567890."
+        "123456789012345678901234567890123456789012345678901234567890."
+        "123456789012345678901234567890123456789012345678901234567890."
+        "123456789.");
+    const LabelSequence full_ls(full_name);
+    check_equal(full_ls, els);
+
+    // But now, even the shortest extension should fail
+    EXPECT_THROW(els.extend(LabelSequence(Name("1")), buf), isc::BadValue);
+
+    // Check it hasn't been changed
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(full_ls, els);
+
+    // Also check that extending past MAX_LABELS is not possible
+    Name shortname("1.");
+    LabelSequence short_ls(shortname);
+    els = LabelSequence(short_ls, buf);
+    for (size_t i=0; i < 126; ++i) {
+        els.extend(short_ls, buf);
+    }
+
+    // Should now look like this
+    const Name full_name2(
+        "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1."
+        "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1."
+        "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1."
+        "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1."
+        "1.1.1.1.1.1.1.");
+    const LabelSequence full_ls2(full_name2);
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(full_ls2, els);
+
+    EXPECT_THROW(els.extend(short_ls, buf), isc::BadValue);
+
+    EXPECT_TRUE(els.isAbsolute());
+    check_equal(full_ls2, els);
+}
+
 }