diff --git a/src/lib/dhcp/tests/opaque_data_tuple_unittest.cc b/src/lib/dhcp/tests/opaque_data_tuple_unittest.cc index 85cbe868b2..fc406ab57b 100644 --- a/src/lib/dhcp/tests/opaque_data_tuple_unittest.cc +++ b/src/lib/dhcp/tests/opaque_data_tuple_unittest.cc @@ -5,9 +5,12 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include + #include #include + #include + #include #include #include @@ -18,6 +21,20 @@ using namespace isc::util; namespace { +struct OpaqueDataTupleLenientParsing : ::testing::Test { + OpaqueDataTupleLenientParsing() : previous_(Option::lenient_parsing_) { + // Enable lenient parsing. + Option::lenient_parsing_ = true; + } + + ~OpaqueDataTupleLenientParsing() { + // Restore. + Option::lenient_parsing_ = previous_; + } + + bool previous_; +}; + // This test checks that when the default constructor is called, the data buffer // is empty. TEST(OpaqueDataTuple, constructor) { @@ -42,7 +59,6 @@ TEST(OpaqueDataTuple, constructorParse1Byte) { EXPECT_EQ(11, tuple.getLength()); EXPECT_EQ("Hello world", tuple.getText()); - } // Test that the constructor which takes the buffer as argument parses the @@ -59,7 +75,6 @@ TEST(OpaqueDataTuple, constructorParse2Bytes) { EXPECT_EQ(11, tuple.getLength()); EXPECT_EQ("Hello world", tuple.getText()); - } @@ -225,7 +240,6 @@ TEST(OpaqueDataTuple, operatorOutputStream) { EXPECT_NO_THROW(tuple = " and some other text"); EXPECT_NO_THROW(s << tuple); EXPECT_EQ(s.str(), "Some text and some other text"); - } // This test verifies that the value of the tuple can be initialized from the @@ -457,7 +471,7 @@ TEST(OpaqueDataTuple, unpack2ByteEmptyBuffer) { EXPECT_THROW(tuple.unpack(wire_data, wire_data), OpaqueDataTupleError); } -// This test verifies that exception if thrown when parsing truncated buffer. +// This test verifies that exception is thrown when parsing truncated buffer. TEST(OpaqueDataTuple, unpack2ByteTruncatedBuffer) { OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES); // Specify the data with the length of 10, but limit the buffer size to @@ -470,5 +484,15 @@ TEST(OpaqueDataTuple, unpack2ByteTruncatedBuffer) { OpaqueDataTupleError); } +// Test that an exception is not thrown when parsing in lenient mode. +TEST_F(OpaqueDataTupleLenientParsing, unpack) { + OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES); + // Specify the data with the length of 10, but limit the buffer size to 2. + const char wire_data[] = { + 0, 10, 2, 3 + }; + EXPECT_NO_THROW(tuple.unpack(wire_data, wire_data + sizeof(wire_data))); + EXPECT_EQ(tuple.getData(), OpaqueDataTuple::Buffer({2, 3})); +} } // anonymous namespace diff --git a/src/lib/dhcp/tests/option_vendor_class_unittest.cc b/src/lib/dhcp/tests/option_vendor_class_unittest.cc index e8d1af3a53..f5b16705de 100644 --- a/src/lib/dhcp/tests/option_vendor_class_unittest.cc +++ b/src/lib/dhcp/tests/option_vendor_class_unittest.cc @@ -9,6 +9,7 @@ #include #include #include + #include using namespace isc; @@ -17,6 +18,20 @@ using namespace isc::util; namespace { +struct OptionVendorClassLenientParsing : ::testing::Test { + OptionVendorClassLenientParsing() : previous_(Option::lenient_parsing_) { + // Enable lenient parsing. + Option::lenient_parsing_ = true; + } + + ~OptionVendorClassLenientParsing() { + // 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 @@ -248,7 +263,7 @@ TEST(OptionVendorClass, unpack4) { EXPECT_EQ("foo", vendor_class->getTuple(1).getText()); } -// This function checks that the DHCPv4 option with two opaque data tuples +// This function checks that the DHCPv6 option with two opaque data tuples // is parsed correctly. TEST(OptionVendorClass, unpack6) { // Prepare data to decode. @@ -276,7 +291,7 @@ TEST(OptionVendorClass, unpack6) { } -// This test checks that the DHCPv4 option with opaque data of size 0 +// This test checks that the DHCPv6 option with opaque data of size 0 // is correctly parsed. TEST(OptionVendorClass, unpack4EmptyTuple) { // Prepare data to decode. @@ -418,7 +433,7 @@ TEST(OptionVendorClass, toText4) { vendor_class.toText(3)); } -// Verifies correctness of the text representation of the DHCPv4 option. +// 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()); @@ -443,5 +458,128 @@ TEST(OptionVendorClass, toText6) { vendor_class.toText(2)); } -} // end of anonymous namespace +// 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, unpack6) { + // 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 + 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 + 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 + 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 + 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