diff options
Diffstat (limited to 'src/lib/dhcp/tests/option6_dnr_unittest.cc')
-rw-r--r-- | src/lib/dhcp/tests/option6_dnr_unittest.cc | 717 |
1 files changed, 717 insertions, 0 deletions
diff --git a/src/lib/dhcp/tests/option6_dnr_unittest.cc b/src/lib/dhcp/tests/option6_dnr_unittest.cc new file mode 100644 index 0000000..055e805 --- /dev/null +++ b/src/lib/dhcp/tests/option6_dnr_unittest.cc @@ -0,0 +1,717 @@ +// Copyright (C) 2023-2024 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 <dhcp/option6_dnr.h> + +#include <gtest/gtest.h> + +using namespace isc; +using namespace isc::dhcp; + +namespace { + +// This test verifies option constructor from wire data. +// Provided wire data is in the ADN only mode i.e. only +// Service priority and Authentication domain name FQDN +// fields are present. +TEST(Option6DnrTest, onWireCtorAdnOnlyMode) { + // Prepare data to decode - ADN only mode. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00 // Com. + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor doesn't throw. + Option6DnrPtr option; + EXPECT_NO_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end()))); + ASSERT_TRUE(option); + + // Check if member variables were correctly set by ctor. + EXPECT_EQ(Option::V6, option->getUniverse()); + EXPECT_EQ(D6O_V6_DNR, option->getType()); + + // Check if data was unpacked correctly from wire data. + EXPECT_EQ(0x8001, option->getServicePriority()); + EXPECT_EQ(20, option->getAdnLength()); + EXPECT_EQ("myhost.example.com.", option->getAdnAsText()); + + // This is ADN only mode, so Addr Length and SvcParams Length + // are both expected to be zero. + EXPECT_EQ(0, option->getAddrLength()); + EXPECT_EQ(0, option->getSvcParamsLength()); + + // BTW let's check if len() works ok. + // expected len: 20 (FQDN) + 2 (ADN Len) + 2 (Service priority) + 4 (headers) = 28. + EXPECT_EQ(28, option->len()); + + // BTW let's check if toText() works ok. + // toText() len does not count in headers len. + EXPECT_EQ("type=144(V6_DNR), len=24, " + "service_priority=32769, adn_length=20, " + "adn='myhost.example.com.'", + option->toText()); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - mandatory fields are truncated. +TEST(Option6DnrTest, onWireCtorDataTruncated) { + // Prepare data to decode - data too short. + const uint8_t buf_data[] = { + 0x80, 0x01 // Service priority is 32769 dec, other data is missing + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws OutOfRange exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - ADN FQDN contains only whitespace - non-valid FQDN. +TEST(Option6DnrTest, onWireCtorOnlyWhitespaceFqdn) { + // Prepare data to decode - ADN only mode. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x02, // ADN Length is 2 dec + 0x01, 0x20 // FQDN consists only of whitespace + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws InvalidOptionDnrDomainName exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrDomainName); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - ADN Length is 0 and no ADN FQDN at all. +TEST(Option6DnrTest, onWireCtorNoAdnFqdn) { + // Prepare data to decode - ADN only mode. + const uint8_t buf_data[] = { + 0x00, 0x01, // Service priority is 1 dec + 0x00, 0x00 // ADN Length is 0 dec + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Encrypted DNS options are designed to ALWAYS include + // an authentication domain name, so check that constructor throws + // InvalidOptionDnrDomainName exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrDomainName); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - FQDN data is truncated. +TEST(Option6DnrTest, onWireCtorTruncatedFqdn) { + // Prepare data to decode - ADN only mode. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74 // FQDN data is truncated + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws BadValue exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), BadValue); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - Addr Length field truncated. +TEST(Option6DnrTest, onWireCtorAddrLenTruncated) { + // Prepare data to decode. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x10 // Truncated Addr Len field + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws OutOfRange exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - Addr length is 0 and no IPv6 addresses at all. +TEST(Option6DnrTest, onWireCtorAddrLenZero) { + // Prepare data to decode. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x00 // Addr Len field value = 0 + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws OutOfRange exception. + // If additional data is supplied (i.e. not ADN only mode), + // the option includes at least one valid IP address. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - Addr length is not a multiple of 16. +TEST(Option6DnrTest, onWireCtorAddrLenNot16Modulo) { + // Prepare data to decode. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0xFF, 0xFE // Addr Len is not a multiple of 16 + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws OutOfRange exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange); + ASSERT_FALSE(option); +} + +// This test verifies option constructor from wire data. +// Provided wire data contains also IPv6 addresses. +TEST(Option6DnrTest, onWireCtorValidIpV6Addresses) { + // Prepare data to decode + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x30, // Addr Len field value = 48 dec + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ff02::face:b00c + 0x00, 0x00, 0x00, 0x00, 0xfa, 0xce, 0xb0, 0x0c, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor doesn't throw. + Option6DnrPtr option; + EXPECT_NO_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end()))); + ASSERT_TRUE(option); + + // Check if member variables were correctly set by ctor. + EXPECT_EQ(Option::V6, option->getUniverse()); + EXPECT_EQ(D6O_V6_DNR, option->getType()); + + // Check if data was unpacked correctly from wire data. + EXPECT_EQ(0x8001, option->getServicePriority()); + EXPECT_EQ(20, option->getAdnLength()); + EXPECT_EQ("myhost.example.com.", option->getAdnAsText()); + EXPECT_EQ(48, option->getAddrLength()); + const Option6Dnr::AddressContainer& addresses = option->getAddresses(); + EXPECT_EQ(3, addresses.size()); + EXPECT_EQ("2001:db8:1::dead:beef", addresses[0].toText()); + EXPECT_EQ("ff02::face:b00c", addresses[1].toText()); + EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", addresses[2].toText()); + EXPECT_EQ(0, option->getSvcParamsLength()); + + // BTW let's check if len() works ok. + // expected len: 20 (FQDN) + 2 (ADN Len) + 2 (Service priority) + 4 (headers) = 28 + // + 48 (3 IP addresses) + 2 (Addr Len) = 78. + EXPECT_EQ(78, option->len()); + + // BTW let's check if toText() works ok. + // toText() len does not count in headers len. + EXPECT_EQ("type=144(V6_DNR), len=74, " + "service_priority=32769, adn_length=20, " + "adn='myhost.example.com.', " + "addr_length=48, " + "address(es): 2001:db8:1::dead:beef " + "ff02::face:b00c " + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + option->toText()); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - IPv6 addresses are truncated. +TEST(Option6DnrTest, onWireCtorTruncatedIpV6Addresses) { + // Prepare data to decode. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x30, // Addr Len field value = 48 dec + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, + 0xff, 0x02, 0x00 // IPv6 address truncated + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws OutOfRange exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange); + ASSERT_FALSE(option); +} + +// This test verifies option constructor from wire data. +// Provided wire data contains also IPv6 address and Svc Params. +TEST(Option6DnrTest, onWireCtorSvcParamsIncluded) { + // Prepare data to decode. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x10, // Addr Len field value = 16 dec + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, // + 0, 1, // SvcParamKey alpn + 0, 8, // SvcParamVal Len + 3, 'd', 'o', 't', // 3 octets long alpn-id dot + 3, 'd', 'o', 'q' // 3 octets long alpn-id doq + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor doesn't throw. + Option6DnrPtr option; + EXPECT_NO_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end()))); + ASSERT_TRUE(option); + + // Check if member variables were correctly set by ctor. + EXPECT_EQ(Option::V6, option->getUniverse()); + EXPECT_EQ(D6O_V6_DNR, option->getType()); + + // Check if data was unpacked correctly from wire data. + EXPECT_EQ(0x8001, option->getServicePriority()); + EXPECT_EQ(20, option->getAdnLength()); + EXPECT_EQ("myhost.example.com.", option->getAdnAsText()); + EXPECT_EQ(16, option->getAddrLength()); + const Option6Dnr::AddressContainer& addresses = option->getAddresses(); + EXPECT_EQ(1, addresses.size()); + EXPECT_EQ("2001:db8:1::dead:beef", addresses[0].toText()); + + // Reference SvcParams on-wire data buffer. + const OptionBuffer svc_params = { + 0, 1, // SvcParamKey alpn + 0, 8, // SvcParamVal Len + 3, 'd', 'o', 't', // 3 octets long alpn-id dot + 3, 'd', 'o', 'q' // 3 octets long alpn-id doq + }; + EXPECT_EQ(svc_params, option->getSvcParams()); + EXPECT_EQ(svc_params.size(), option->getSvcParamsLength()); + + // BTW let's check if len() works ok. + // expected len: 20 (FQDN) + 2 (ADN Len) + 2 (Service priority) + 4 (headers) = 28 + // + 16 (IP address) + 2 (Addr Len) + 12 (SvcParams) = 58. + EXPECT_EQ(58, option->len()); + + // BTW let's check if toText() works ok. + // toText() len does not count in headers len. + EXPECT_EQ("type=144(V6_DNR), len=54, " + "service_priority=32769, adn_length=20, " + "adn='myhost.example.com.', " + "addr_length=16, " + "address(es): 2001:db8:1::dead:beef, " + "svc_params='alpn=dot,doq'", + option->toText()); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - SvcParams Key contains truncated SvcParamVal Len data. +TEST(Option6DnrTest, onWireCtorSvcParamsTruncatedParamLen) { + // Prepare data to decode with invalid SvcParams. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x10, // Addr Len field value + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, // + 0, 1, // SvcParamKey alpn + 0, 8, // SvcParamVal Len + 3, 'd', 'o', 't', // 3 octets long alpn-id dot + 3, 'd', 'o', 'q', // 3 octets long alpn-id doq + 0, 3, // SvcParamKey port + 0 // truncated SvcParamVal Len + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - SvcParams Key contains truncated data. +TEST(Option6DnrTest, onWireCtorSvcParamsTruncatedParamData) { + // Prepare data to decode with invalid SvcParams. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x10, // Addr Len field value + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, // + 0, 1, // SvcParamKey alpn + 0, 8, // SvcParamVal Len + 3, 'd', 'o', 't', // 3 octets long alpn-id dot + 3, 'd', 'o', 'q', // 3 octets long alpn-id doq + 0, 3, // SvcParamKey port + 0, 2, // SvcParamVal Len + 1 // truncated data + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrSvcParams); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - SvcParams Key contains unknown key. +TEST(Option6DnrTest, onWireCtorSvcParamsUnknownKey) { + // Prepare data to decode with invalid SvcParams. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x10, // Addr Len field value + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, // + 0, 1, // SvcParamKey alpn + 0, 8, // SvcParamVal Len + 3, 'd', 'o', 't', // 3 octets long alpn-id dot + 3, 'd', 'o', 'q', // 3 octets long alpn-id doq + 0, 99, // unknown SvcParamKey + 0, 2, // SvcParamVal Len + 1, 2 // random data + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrSvcParams); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - SvcParams Key contains forbidden key. +TEST(Option6DnrTest, onWireCtorSvcParamsForbiddenKey) { + // Prepare data to decode with invalid SvcParams. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x10, // Addr Len field value + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, // + 0, 1, // SvcParamKey alpn + 0, 8, // SvcParamVal Len + 3, 'd', 'o', 't', // 3 octets long alpn-id dot + 3, 'd', 'o', 'q', // 3 octets long alpn-id doq + 0, 4, // SvcParamKey ipv4hint is forbidden + 0, 2, // SvcParamVal Len + 1, 2 // random data + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrSvcParams); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - SvcParams Key contains unsupported key. +TEST(Option6DnrTest, onWireCtorSvcParamsUnsupportedKey) { + // Prepare data to decode with invalid SvcParams. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x10, // Addr Len field value + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, // + 0, 1, // SvcParamKey alpn + 0, 8, // SvcParamVal Len + 3, 'd', 'o', 't', // 3 octets long alpn-id dot + 3, 'd', 'o', 'q', // 3 octets long alpn-id doq + 0, 5, // SvcParamKey ech is unsupported in DHCP DNR option + 0, 2, // SvcParamVal Len + 1, 2 // random data + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrSvcParams); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - SvcParams Key contains duplicated key. +TEST(Option6DnrTest, onWireCtorSvcParamsDuplicatedKey) { + // Prepare data to decode with invalid SvcParams. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x10, // Addr Len field value + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, // + 0, 1, // SvcParamKey alpn + 0, 8, // SvcParamVal Len + 3, 'd', 'o', 't', // 3 octets long alpn-id dot + 3, 'd', 'o', 'q', // 3 octets long alpn-id doq + 0, 1, // SvcParamKey alpn is duplicated + 0, 4, // SvcParamVal Len + 3, 'd', 'o', 't' // 3 octets long alpn-id dot + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrSvcParams); + ASSERT_FALSE(option); +} + +// Test checks that exception is thrown when trying to unpack malformed wire data +// - SvcParams Keys are in wrong order. +TEST(Option6DnrTest, onWireCtorSvcParamsWrongKeyOrder) { + // Prepare data to decode with invalid SvcParams. + const uint8_t buf_data[] = { + 0x80, 0x01, // Service priority is 32769 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost. + 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example. + 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com. + 0x00, 0x10, // Addr Len field value + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, // + 0, 3, // SvcParamKey port + 0, 2, // SvcParamVal Len + 23, 23, // 2 octets long port number + 0, 1, // SvcParamKey alpn is duplicated + 0, 4, // SvcParamVal Len + 3, 'd', 'o', 't' // 3 octets long alpn-id dot + }; + + OptionBuffer buf(buf_data, buf_data + sizeof(buf_data)); + // Create option instance. Check that constructor throws exception. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrSvcParams); + ASSERT_FALSE(option); +} + +// This test verifies that option constructor throws +// an exception when config provided via ctor is malformed +// - SvcParam key=val pair has 2 equal signs. +TEST(Option6DnrTest, fromConfigCtorSvcParamsTwoEqualSignsPerParam) { + // Prepare example config. + const std::string config = "100, dot1.example.org., 2001:db8::1 2001:db8::2, " + "alpn=dot=doq port=8530 dohpath=/q{?dns}"; + + OptionBuffer buf; + buf.assign(config.begin(), config.end()); + + // Create option instance. Check that constructor throws. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end(), true)), + InvalidOptionDnrSvcParams); + ASSERT_FALSE(option); +} + +// This test verifies that option constructor throws +// an exception when config provided via ctor is malformed +// - SvcParam has forbidden key. +TEST(Option6DnrTest, fromConfigCtorSvcParamsForbiddenKey) { + // Prepare example config. + const std::string config = "100, dot1.example.org., 2001:db8::1 2001:db8::2, " + "ipv6hint=something port=8530 dohpath=/q{?dns}"; + + OptionBuffer buf; + buf.assign(config.begin(), config.end()); + + // Create option instance. Check that constructor throws. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end(), true)), + InvalidOptionDnrSvcParams); + ASSERT_FALSE(option); +} + +// This test verifies that option constructor throws +// an exception when config provided via ctor is malformed +// - Svc Params key was repeated. +TEST(Option6DnrTest, fromConfigCtorSvcParamsKeyRepeated) { + // Prepare example config. + const std::string config = "100, dot1.example.org., 2001:db8::1 2001:db8::2, " + "port=8530 port=1234 dohpath=/q{?dns}"; + + OptionBuffer buf; + buf.assign(config.begin(), config.end()); + + // Create option instance. Check that constructor throws. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end(), true)), + InvalidOptionDnrSvcParams); + ASSERT_FALSE(option); +} + +// This test verifies that option constructor throws +// an exception when config provided via ctor is malformed +// - IPv4 address given. +TEST(Option6DnrTest, fromConfigCtorIPv4Address) { + // Prepare example config. + const std::string config = "100, dot1.example.org., 10.0.2.3"; + + OptionBuffer buf; + buf.assign(config.begin(), config.end()); + + // Create option instance. Check that constructor throws. + Option6DnrPtr option; + EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end(), true)), + BadValue); + ASSERT_FALSE(option); +} + +// This test verifies that string representation of the option returned by +// toText method is correctly formatted. +TEST(Option6DnrTest, toText) { + // Prepare example config. + const std::string config = "9, myhost.example.com., 2001:db8:1::baca, " + "alpn=h3 port=1234 dohpath=/q{?dns}"; + + OptionBuffer buf; + buf.assign(config.begin(), config.end()); + + // Create option instance. Check that constructor doesn't throw. + Option6DnrPtr option; + EXPECT_NO_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end(), true))); + ASSERT_TRUE(option); + + const int indent = 4; + std::string expected = " type=144(V6_DNR), len=67, " // the indentation of 4 spaces + "service_priority=9, adn_length=20, " + "adn='myhost.example.com.', addr_length=16, " + "address(es): 2001:db8:1::baca, svc_params='alpn=h3 port=1234 " + "dohpath=/q{?dns}'"; + EXPECT_EQ(expected, option->toText(indent)); +} + +// This test verifies that the option is correctly created in ADN only mode, +// when constructor from convenient string notation is used. +TEST(Option6DnrTest, fromConfigCtorPackAdnOnlyMode) { + // Prepare example config. + const std::string config = "9, myhost.example.com."; + + OptionBuffer in_buf; + in_buf.assign(config.begin(), config.end()); + + // Create option instance. Check that constructor doesn't throw. + Option6DnrPtr option; + EXPECT_NO_THROW(option.reset(new Option6Dnr(in_buf.begin(), in_buf.end(), true))); + ASSERT_TRUE(option); + + // Prepare on-wire format of the option. + isc::util::OutputBuffer buf(10); + ASSERT_NO_THROW(option->pack(buf)); + + // Prepare reference data. + const uint8_t ref_data[] = { + 0x00, D6O_V6_DNR, // Option code + 0x00, 24, // Option len=24 dec + 0x00, 0x09, // Service priority is 9 dec + 0x00, 20, // ADN Length is 20 dec + 0x06, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: myhost. + 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example. + 0x03, 0x63, 0x6F, 0x6D, 0x00 // com. + }; + + size_t ref_data_size = sizeof(ref_data) / sizeof(ref_data[0]); + + // Check if the buffer has the same length as the reference data, + // so as they can be compared directly. + ASSERT_EQ(ref_data_size, buf.getLength()); + EXPECT_EQ(0, memcmp(ref_data, buf.getData(), buf.getLength())); +} + +// This test verifies on-wire format of the option is correctly created with pack +// method when IP addresses and Svc Params are also included. +TEST(Option6DnrTest, pack) { + // Prepare example config. + const std::string config = "9, myhost.example.com., 2001:db8:1::dead:beef ff02::face:b00c," + " alpn=imap"; + + OptionBuffer in_buf; + in_buf.assign(config.begin(), config.end()); + + // Create option instance. Check that constructor doesn't throw. + Option6DnrPtr option; + EXPECT_NO_THROW(option.reset(new Option6Dnr(in_buf.begin(), in_buf.end(), true))); + ASSERT_TRUE(option); + + // Prepare on-wire format of the option. + isc::util::OutputBuffer buf(10); + ASSERT_NO_THROW(option->pack(buf)); + + // Prepare reference data. + const uint8_t ref_data[] = { + 0x00, D6O_V6_DNR, // Option code + 0x00, 67, // Option len=67 dec + 0x00, 0x09, // Service priority is 9 dec + 0x00, 0x14, // ADN Length is 20 dec + 0x06, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: myhost. + 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example. + 0x03, 0x63, 0x6F, 0x6D, 0x00, // com. + 0x00, 0x20, // Addr Len field value = 32 dec + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef + 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xff, 0x02, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ff02::face:b00c + 0x00, 0x00, 0x00, 0x00, 0xfa, 0xce, 0xb0, 0x0c, // + 0, 1, // SvcParamKey alpn + 0, 5, // SvcParamVal len + 4, 'i', 'm', 'a', 'p' // alpn-id + }; + + size_t ref_data_size = sizeof(ref_data) / sizeof(ref_data[0]); + + // Check if the buffer has the same length as the reference data, + // so as they can be compared directly. + ASSERT_EQ(ref_data_size, buf.getLength()); + EXPECT_EQ(0, memcmp(ref_data, buf.getData(), buf.getLength())); +} + +} // namespace
\ No newline at end of file |