Commit 285c2845 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[master] Merge branch 'trac2148'

Conflicts:
	src/lib/dns/labelsequence.h
parents 49e66448 1651ddfd
......@@ -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());
}
......
......@@ -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();
......
......@@ -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.
///
......
......@@ -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);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment