summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/tests/option_vendor_class_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcp/tests/option_vendor_class_unittest.cc')
-rw-r--r--src/lib/dhcp/tests/option_vendor_class_unittest.cc588
1 files changed, 588 insertions, 0 deletions
diff --git a/src/lib/dhcp/tests/option_vendor_class_unittest.cc b/src/lib/dhcp/tests/option_vendor_class_unittest.cc
new file mode 100644
index 0000000..1c6d446
--- /dev/null
+++ b/src/lib/dhcp/tests/option_vendor_class_unittest.cc
@@ -0,0 +1,588 @@
+// Copyright (C) 2014-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <dhcp/option_vendor_class.h>
+#include <util/buffer.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+namespace {
+
+struct OptionVendorClassLenientParsing : ::testing::Test {
+ void SetUp() final override {
+ // Retain the current setting for future restoration.
+ previous_ = Option::lenient_parsing_;
+
+ // Enable lenient parsing.
+ Option::lenient_parsing_ = true;
+ }
+
+ void TearDown() final override {
+ // Restore.
+ Option::lenient_parsing_ = previous_;
+ }
+
+ bool previous_;
+};
+
+// This test checks that the DHCPv4 option constructor sets the default
+// properties to the expected values. This constructor should add an
+// empty opaque data tuple (it is essentially the same as adding a 1-byte
+// long field which carries a value of 0).
+TEST(OptionVendorClass, constructor4) {
+ OptionVendorClass vendor_class(Option::V4, 1234);
+ EXPECT_EQ(1234, vendor_class.getVendorId());
+ // Option length is 1 byte for header + 1 byte for option size +
+ // 4 bytes of enterprise id + 1 byte for opaque data.
+ EXPECT_EQ(7, vendor_class.len());
+ // There should be one empty tuple.
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ EXPECT_EQ(0, vendor_class.getTuple(0).getLength());
+}
+
+// This test checks that the DHCPv6 option constructor sets the default
+// properties to the expected values.
+TEST(OptionVendorClass, constructor6) {
+ OptionVendorClass vendor_class(Option::V6, 2345);
+ EXPECT_EQ(2345, vendor_class.getVendorId());
+ // Option length is 2 bytes for option code + 2 bytes for option size +
+ // 4 bytes of enterprise id.
+ EXPECT_EQ(8, vendor_class.len());
+ // There should be no tuples.
+ EXPECT_EQ(0, vendor_class.getTuplesNum());
+}
+
+// This test verifies that it is possible to append the opaque data tuple
+// to the option and then retrieve it.
+TEST(OptionVendorClass, addTuple) {
+ OptionVendorClass vendor_class(Option::V6, 2345);
+ // Initially there should be no tuples (for DHCPv6).
+ ASSERT_EQ(0, vendor_class.getTuplesNum());
+ // Create a new tuple and add it to the option.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "xyz";
+ vendor_class.addTuple(tuple);
+ // The option should now hold one tuple.
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ EXPECT_EQ("xyz", vendor_class.getTuple(0).getText());
+ // Add another tuple.
+ tuple = "abc";
+ vendor_class.addTuple(tuple);
+ // The option should now hold exactly two tuples in the order in which
+ // they were added.
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
+ EXPECT_EQ("xyz", vendor_class.getTuple(0).getText());
+ EXPECT_EQ("abc", vendor_class.getTuple(1).getText());
+
+ // Check that hasTuple correctly identifies existing tuples.
+ EXPECT_TRUE(vendor_class.hasTuple("xyz"));
+ EXPECT_TRUE(vendor_class.hasTuple("abc"));
+ EXPECT_FALSE(vendor_class.hasTuple("other"));
+
+ // Attempt to add the tuple with 1 byte long length field should fail
+ // for DHCPv6 option.
+ OpaqueDataTuple tuple2(OpaqueDataTuple::LENGTH_1_BYTE);
+ EXPECT_THROW(vendor_class.addTuple(tuple2), isc::BadValue);
+}
+
+// This test checks that it is possible to replace existing tuple.
+TEST(OptionVendorClass, setTuple) {
+ OptionVendorClass vendor_class(Option::V4, 1234);
+ // The DHCPv4 option should carry one empty tuple.
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ ASSERT_TRUE(vendor_class.getTuple(0).getText().empty());
+ // Replace the empty tuple with non-empty one.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE);
+ tuple = "xyz";
+ ASSERT_NO_THROW(vendor_class.setTuple(0, tuple));
+ // There should be one tuple with updated data.
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ EXPECT_EQ("xyz", vendor_class.getTuple(0).getText());
+
+ // Add another one.
+ tuple = "abc";
+ vendor_class.addTuple(tuple);
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
+ ASSERT_EQ("abc", vendor_class.getTuple(1).getText());
+
+ // Try to replace them with new tuples.
+ tuple = "new_xyz";
+ ASSERT_NO_THROW(vendor_class.setTuple(0, tuple));
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
+ EXPECT_EQ("new_xyz", vendor_class.getTuple(0).getText());
+
+ tuple = "new_abc";
+ ASSERT_NO_THROW(vendor_class.setTuple(1, tuple));
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
+ EXPECT_EQ("new_abc", vendor_class.getTuple(1).getText());
+
+ // For out of range position, exception should be thrown.
+ tuple = "foo";
+ EXPECT_THROW(vendor_class.setTuple(2, tuple), isc::OutOfRange);
+
+ // Attempt to add the tuple with 2 byte long length field should fail
+ // for DHCPv4 option.
+ OpaqueDataTuple tuple2(OpaqueDataTuple::LENGTH_2_BYTES);
+ EXPECT_THROW(vendor_class.addTuple(tuple2), isc::BadValue);
+}
+
+// Check that the returned length of the DHCPv4 option is correct.
+TEST(OptionVendorClass, len4) {
+ OptionVendorClass vendor_class(Option::V4, 1234);
+ ASSERT_EQ(7, vendor_class.len());
+ // Replace the default empty tuple.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE);
+ tuple = "xyz";
+ ASSERT_NO_THROW(vendor_class.setTuple(0, tuple));
+ // The total length should get increased by the size of 'xyz'.
+ EXPECT_EQ(10, vendor_class.len());
+ // Add another tuple.
+ tuple = "abc";
+ vendor_class.addTuple(tuple);
+ // The total size now grows by the additional enterprise id and the
+ // 1 byte of the tuple length field and 3 bytes of 'abc'.
+ EXPECT_EQ(18, vendor_class.len());
+}
+
+// Check that the returned length of the DHCPv6 option is correct.
+TEST(OptionVendorClass, len6) {
+ OptionVendorClass vendor_class(Option::V6, 1234);
+ ASSERT_EQ(8, vendor_class.len());
+ // Add first tuple.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "xyz";
+ ASSERT_NO_THROW(vendor_class.addTuple(tuple));
+ // The total length grows by 2 bytes of the length field and 3 bytes
+ // consumed by 'xyz'.
+ EXPECT_EQ(13, vendor_class.len());
+ // Add another tuple and check that the total size gets increased.
+ tuple = "abc";
+ vendor_class.addTuple(tuple);
+ EXPECT_EQ(18, vendor_class.len());
+}
+
+// Check that the option is rendered to the buffer in wire format.
+TEST(OptionVendorClass, pack4) {
+ OptionVendorClass vendor_class(Option::V4, 1234);
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ // By default, there is an empty tuple in the option. Let's replace
+ // it with the tuple with some data.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE);
+ tuple = "Hello world";
+ vendor_class.setTuple(0, tuple);
+ // And add another tuple so as resulting option is a bit more complex.
+ tuple = "foo";
+ vendor_class.addTuple(tuple);
+
+ // Render the data to the buffer.
+ OutputBuffer buf(10);
+ ASSERT_NO_THROW(vendor_class.pack(buf));
+ ASSERT_EQ(26, buf.getLength());
+
+ // Prepare reference data.
+ const uint8_t ref[] = {
+ 0x7C, 0x18, // option 124, length 24
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 3, // tuple length is 3
+ 0x66, 0x6F, 0x6F // foo
+ };
+ // Compare the buffer with reference data.
+ EXPECT_EQ(0, memcmp(static_cast<const void*>(ref),
+ static_cast<const void*>(buf.getData()), 26));
+}
+
+// Check that the DHCPv6 option is rendered to the buffer in wire format.
+TEST(OptionVendorClass, pack6) {
+ OptionVendorClass vendor_class(Option::V6, 1234);
+ ASSERT_EQ(0, vendor_class.getTuplesNum());
+ // Add tuple.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "Hello world";
+ vendor_class.addTuple(tuple);
+ // And add another tuple so as resulting option is a bit more complex.
+ tuple = "foo";
+ vendor_class.addTuple(tuple);
+
+ // Render the data to the buffer.
+ OutputBuffer buf(10);
+ ASSERT_NO_THROW(vendor_class.pack(buf));
+ ASSERT_EQ(26, buf.getLength());
+
+ // Prepare reference data.
+ const uint8_t ref[] = {
+ 0x00, 0x10, 0x00, 0x16, // option 16, length 22
+ 0x00, 0x00, 0x04, 0xD2, // enterprise id 1234
+ 0x00, 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0x00, 0x03, // tuple length is 3
+ 0x66, 0x6F, 0x6F // foo
+ };
+ // Compare the buffer with reference data.
+ EXPECT_EQ(0, memcmp(static_cast<const void*>(ref),
+ static_cast<const void*>(buf.getData()),
+ buf.getLength()));
+}
+
+// This function checks that the DHCPv4 option with two opaque data tuples
+// is parsed correctly.
+TEST(OptionVendorClass, unpack4) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 3, // tuple length is 3
+ 0x66, 0x6F, 0x6F // foo
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V4,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(DHO_VIVCO_SUBOPTIONS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(2, vendor_class->getTuplesNum());
+ EXPECT_EQ("Hello world", vendor_class->getTuple(0).getText());
+ EXPECT_EQ("foo", vendor_class->getTuple(1).getText());
+}
+
+// This function checks that the DHCPv6 option with two opaque data tuples
+// is parsed correctly.
+TEST(OptionVendorClass, unpack6) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x00, 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0x00, 0x03, // tuple length is 3
+ 0x66, 0x6F, 0x6F // foo
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V6,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(2, vendor_class->getTuplesNum());
+ EXPECT_EQ("Hello world", vendor_class->getTuple(0).getText());
+ EXPECT_EQ("foo", vendor_class->getTuple(1).getText());
+}
+
+
+// This test checks that the DHCPv6 option with opaque data of size 0
+// is correctly parsed.
+TEST(OptionVendorClass, unpack4EmptyTuple) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x00, // tuple length is 0
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V4,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(DHO_VIVCO_SUBOPTIONS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(1, vendor_class->getTuplesNum());
+ EXPECT_TRUE(vendor_class->getTuple(0).getText().empty());
+}
+
+// This test checks that the DHCPv6 option with opaque data of size 0
+// is correctly parsed.
+TEST(OptionVendorClass, unpack6EmptyTuple) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x00, 0x00 // tuple length is 0
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V6,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(1, vendor_class->getTuplesNum());
+ EXPECT_TRUE(vendor_class->getTuple(0).getText().empty());
+}
+
+// This test checks that the DHCPv4 option without opaque data is
+// correctly parsed.
+TEST(OptionVendorClass, unpack4NoTuple) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2 // enterprise id 1234
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V4,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(DHO_VIVCO_SUBOPTIONS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ EXPECT_EQ(0, vendor_class->getTuplesNum());
+}
+
+// This test checks that the DHCPv6 option without opaque data is
+// correctly parsed.
+TEST(OptionVendorClass, unpack6NoTuple) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2 // enterprise id 1234
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V6,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ EXPECT_EQ(0, vendor_class->getTuplesNum());
+}
+
+// This test checks that exception is thrown when parsing truncated DHCPv4
+// V-I Vendor Class option.
+TEST(OptionVendorClass, unpack4Truncated) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ EXPECT_THROW(OptionVendorClass (Option::V4, buf.begin(), buf.end()),
+ isc::OutOfRange);
+}
+
+// This test checks that exception is thrown when parsing truncated DHCPv6
+// Vendor Class option.
+TEST(OptionVendorClass, unpack6Truncated) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x00, 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C // worl (truncated d!)
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ EXPECT_THROW(OptionVendorClass (Option::V6, buf.begin(), buf.end()),
+ isc::dhcp::OpaqueDataTupleError);
+}
+
+// Verifies correctness of the text representation of the DHCPv4 option.
+TEST(OptionVendorClass, toText4) {
+ OptionVendorClass vendor_class(Option::V4, 1234);
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ // By default, there is an empty tuple in the option. Let's replace
+ // it with the tuple with some data.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE);
+ tuple = "Hello world";
+ vendor_class.setTuple(0, tuple);
+ // And add another tuple so as resulting option is a bit more complex.
+ tuple = "foo";
+ vendor_class.addTuple(tuple);
+ // Check that the text representation of the option is as expected.
+ EXPECT_EQ("type=124, len=24, enterprise id=0x4d2,"
+ " data-len0=11, vendor-class-data0='Hello world',"
+ " enterprise id=0x4d2, data-len1=3, vendor-class-data1='foo'",
+ vendor_class.toText());
+
+ // Check that indentation works.
+ EXPECT_EQ(" type=124, len=24, enterprise id=0x4d2,"
+ " data-len0=11, vendor-class-data0='Hello world',"
+ " enterprise id=0x4d2, data-len1=3, vendor-class-data1='foo'",
+ vendor_class.toText(3));
+}
+
+// Verifies correctness of the text representation of the DHCPv6 option.
+TEST(OptionVendorClass, toText6) {
+ OptionVendorClass vendor_class(Option::V6, 1234);
+ ASSERT_EQ(0, vendor_class.getTuplesNum());
+ // By default, there is an empty tuple in the option. Let's replace
+ // it with the tuple with some data.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "Hello world";
+ vendor_class.addTuple(tuple);
+ // And add another tuple so as resulting option is a bit more complex.
+ tuple = "foo";
+ vendor_class.addTuple(tuple);
+ // Check that the text representation of the option is as expected.
+ EXPECT_EQ("type=16, len=22, enterprise id=0x4d2,"
+ " data-len0=11, vendor-class-data0='Hello world',"
+ " data-len1=3, vendor-class-data1='foo'",
+ vendor_class.toText());
+
+ // Check that indentation works.
+ EXPECT_EQ(" type=16, len=22, enterprise id=0x4d2,"
+ " data-len0=11, vendor-class-data0='Hello world',"
+ " data-len1=3, vendor-class-data1='foo'",
+ vendor_class.toText(2));
+}
+
+// Test that a well formed DHCPv6 option with two opaque data tuples is parsed
+// correctly when lenient mode is enabled.
+TEST_F(OptionVendorClassLenientParsing, unpack6WellFormed) {
+ // Enable lenient parsing.
+ bool const previous(Option::lenient_parsing_);
+ Option::lenient_parsing_ = true;
+
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x00, 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0x00, 0x03, // tuple length is 3
+ 0x66, 0x6F, 0x6F // foo
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(
+ new OptionVendorClass(Option::V6, buf.begin(), buf.end())););
+
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(2, vendor_class->getTuplesNum());
+ EXPECT_EQ("Hello world", vendor_class->getTuple(0).getText());
+ EXPECT_EQ("foo", vendor_class->getTuple(1).getText());
+
+ // Restore.
+ Option::lenient_parsing_ = previous;
+}
+
+// Test that the DHCPv6 option with truncated or over-extending (depends on
+// perspective) buffers is parsed correctly when lenient mode is enabled.
+TEST_F(OptionVendorClassLenientParsing, unpack6FirstLengthIsBad) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x00, 0x0C, // tuple length is 12 (should be 11)
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0x00, 0x03, // tuple length is 3
+ 0x66, 0x6F, 0x6F // foo
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(
+ new OptionVendorClass(Option::V6, buf.begin(), buf.end())););
+
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(2, vendor_class->getTuplesNum());
+ // The first value will have one extra byte.
+ EXPECT_EQ(std::string("Hello world") + '\0',
+ vendor_class->getTuple(0).getText());
+ // The length would have internally been interpreted as {0x03, 0x66} == 870,
+ // but the parser would have stopped at the end of the option, so the second
+ // value should be "oo".
+ EXPECT_EQ("oo", vendor_class->getTuple(1).getText());
+}
+
+// Test that the DHCPv6 option with truncated or over-extending (depends on
+// perspective) buffers is parsed correctly when lenient mode is enabled.
+TEST_F(OptionVendorClassLenientParsing, unpack6SecondLengthIsBad) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x00, 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0x00, 0x04, // tuple length is 4 (should be 3)
+ 0x66, 0x6F, 0x6F // foo
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(
+ new OptionVendorClass(Option::V6, buf.begin(), buf.end())););
+
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(2, vendor_class->getTuplesNum());
+ EXPECT_EQ("Hello world", vendor_class->getTuple(0).getText());
+ // The length would have internally been interpreted as {0x00, 0x04} == 4,
+ // but the parser would have stopped at the end of the option, so the second
+ // value should be "foo" just like normal.
+ EXPECT_EQ("foo", vendor_class->getTuple(1).getText());
+}
+
+// Test that the DHCPv6 option with truncated or over-extending (depends on
+// perspective) buffers is parsed correctly when lenient mode is enabled.
+TEST_F(OptionVendorClassLenientParsing, unpack6BothLengthsAreBad) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x00, 0x0C, // tuple length is 12 (should be 11)
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0x00, 0x04, // tuple length is 4 (should be 3)
+ 0x66, 0x6F, 0x6F // foo
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(
+ new OptionVendorClass(Option::V6, buf.begin(), buf.end())););
+
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(2, vendor_class->getTuplesNum());
+ // The first value will have one extra byte.
+ EXPECT_EQ(std::string("Hello world") + '\0',
+ vendor_class->getTuple(0).getText());
+ // The length would have internally been interpreted as {0x04, 0x66} == 1126,
+ // but the parser would have stopped at the end of the option, so the second
+ // value should be "oo".
+ EXPECT_EQ("oo", vendor_class->getTuple(1).getText());
+}
+
+} // end of anonymous namespace