From: Piotrek Zadroga Date: Thu, 6 Apr 2023 13:12:04 +0000 (+0200) Subject: [#2536] Implementing DNRv6 Option with TDD X-Git-Tag: Kea-2.3.8~185 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=65a972e6b61551aec149091dce8fc3641f812e93;p=thirdparty%2Fkea.git [#2536] Implementing DNRv6 Option with TDD --- diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc index 0b20cf7d64..835a09e9cb 100644 --- a/src/lib/dhcp/option_definition.cc +++ b/src/lib/dhcp/option_definition.cc @@ -868,7 +868,7 @@ OptionDefinition::factorySpecialFormatOption(Option::Universe u, return (OptionPtr(new Option6PDExclude(begin, end))); case D6O_V6_DNR: - return (OptionPtr(new OptionDNR6(begin, end))); + return (OptionPtr(new OptionDnr6(begin, end))); default: break; @@ -899,7 +899,7 @@ OptionDefinition::factorySpecialFormatOption(Option::Universe u, return (factoryOpaqueDataTuples(Option::V4, getCode(), begin, end, OpaqueDataTuple::LENGTH_2_BYTES)); case DHO_V4_DNR: - return (OptionPtr(new OptionDNR4(begin, end))); + return (OptionPtr(new OptionDnr4(begin, end))); default: break; diff --git a/src/lib/dhcp/option_dnr.cc b/src/lib/dhcp/option_dnr.cc index ca9af55151..427bf0feef 100644 --- a/src/lib/dhcp/option_dnr.cc +++ b/src/lib/dhcp/option_dnr.cc @@ -14,18 +14,18 @@ namespace isc { namespace dhcp { -OptionDNR6::OptionDNR6(OptionBufferConstIter begin, OptionBufferConstIter end) - : Option(V6, D6O_V6_DNR) { +OptionDnr6::OptionDnr6(OptionBufferConstIter begin, OptionBufferConstIter end) + : Option(V6, D6O_V6_DNR), addr_length_(0) { unpack(begin, end); } OptionPtr -OptionDNR6::clone() const { +OptionDnr6::clone() const { return Option::clone(); } void -OptionDNR6::pack(util::OutputBuffer& buf, bool check) const { +OptionDnr6::pack(util::OutputBuffer& buf, bool check) const { packHeader(buf, check); buf.writeUint16(service_priority_); @@ -33,7 +33,7 @@ OptionDNR6::pack(util::OutputBuffer& buf, bool check) const { } void -OptionDNR6::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { +OptionDnr6::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { if (std::distance(begin, end) < getMinimalLength()) { isc_throw(OutOfRange, "parsed DHCPv6 Encrypted DNS Option (" << D6O_V6_DNR << ") data truncated to" @@ -50,13 +50,13 @@ OptionDNR6::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { adn_length_ = adn_tuple.getLength(); if (adn_length_ > 0) { // Let's try to extract ADN FQDN data - isc::util::InputBuffer name_buf(&adn_tuple.getData(), - adn_length_); + isc::util::InputBuffer name_buf(&adn_tuple.getData()[0], adn_length_); try { adn_.reset(new isc::dns::Name(name_buf, true)); - } catch (const Exception&) { - isc_throw(InvalidOptionDNR6DomainName, "failed to parse " - "fully qualified domain-name from wire format"); + } catch (const Exception& ex) { + isc_throw(InvalidOptionDnr6DomainName, "failed to parse " + "fully qualified domain-name from wire format " + "- " << ex.what()); } } begin += adn_tuple.getTotalLength(); @@ -74,45 +74,53 @@ OptionDNR6::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { } std::string -OptionDNR6::toText(int indent) const { +OptionDnr6::toText(int indent) const { return Option::toText(indent); } uint16_t -OptionDNR6::len() const { +OptionDnr6::len() const { return Option::len(); } -OptionDNR4::OptionDNR4() : Option(V4, DHO_V4_DNR) { +std::string +OptionDnr6::getAdn() const { + if (adn_) { + return adn_->toText(); + } + return ""; +} + +OptionDnr4::OptionDnr4() : Option(V4, DHO_V4_DNR) { } -OptionDNR4::OptionDNR4(OptionBufferConstIter begin, OptionBufferConstIter end) +OptionDnr4::OptionDnr4(OptionBufferConstIter begin, OptionBufferConstIter end) : Option(V4, DHO_V4_DNR) { unpack(begin, end); } OptionPtr -OptionDNR4::clone() const { +OptionDnr4::clone() const { return Option::clone(); } void -OptionDNR4::pack(util::OutputBuffer& buf, bool check) const { +OptionDnr4::pack(util::OutputBuffer& buf, bool check) const { Option::pack(buf, check); } void -OptionDNR4::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { +OptionDnr4::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { Option::unpack(begin, end); } std::string -OptionDNR4::toText(int indent) const { +OptionDnr4::toText(int indent) const { return Option::toText(indent); } uint16_t -OptionDNR4::len() const { +OptionDnr4::len() const { return Option::len(); } diff --git a/src/lib/dhcp/option_dnr.h b/src/lib/dhcp/option_dnr.h index f2c6b2a1aa..45f3807c42 100644 --- a/src/lib/dhcp/option_dnr.h +++ b/src/lib/dhcp/option_dnr.h @@ -14,16 +14,15 @@ namespace isc { namespace dhcp { /// @brief Exception thrown when invalid domain name is specified. -class InvalidOptionDNR6DomainName : public Exception { +class InvalidOptionDnr6DomainName : public Exception { public: - InvalidOptionDNR6DomainName(const char* file, size_t line, - const char* what) : - isc::Exception(file, line, what) {} + InvalidOptionDnr6DomainName(const char* file, size_t line, const char* what) + : isc::Exception(file, line, what) { + } }; -class OptionDNR6 : public Option { +class OptionDnr6 : public Option { public: - /// @brief Size in octets of Service Priority field static const uint8_t SERVICE_PRIORITY_SIZE = 2; @@ -33,23 +32,49 @@ public: /// @brief Size in octets of Addr Length field static const uint8_t ADDR_LENGTH_SIZE = 2; - OptionDNR6() : Option(V6, D6O_V6_DNR) { - }; - OptionDNR6(OptionBufferConstIter begin, OptionBufferConstIter end); + OptionDnr6(OptionBufferConstIter begin, OptionBufferConstIter end); virtual OptionPtr clone() const; virtual void pack(util::OutputBuffer& buf, bool check) const; virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end); virtual std::string toText(int indent) const; virtual uint16_t len() const; + /// @brief Getter of the @c service_priority_ + /// + /// @return The priority of this OPTION_V6_DNR instance compared to other instances. + uint16_t getServicePriority() const { + return service_priority_; + } + + /// @brief Getter of the @c adn_length_ + /// + /// @return Length of the authentication-domain-name data in octets. + uint16_t getAdnLength() const { + return adn_length_; + } + + /// @brief Returns the Authentication domain name in the text format. + /// + /// FQDN data stored in @c adn_ is converted into text format and returned. + /// + /// @return Authentication domain name in the text format. + std::string getAdn() const; + + /// @brief Getter of the @c addr_length_ + /// + /// @return Length of enclosed IPv6 addresses in octets. + uint16_t getAddrLength() const { + return addr_length_; + } + private: /// @brief The priority of this OPTION_V6_DNR instance compared to other instances. uint16_t service_priority_; - /// @brief Length of the authentication-domain-name field in octets. + /// @brief Length of the authentication-domain-name data in octets. uint16_t adn_length_; - /// @brief Authentication domain name field of variable length + /// @brief Authentication domain name field of variable length. /// /// Authentication domain name field of variable length holding /// a fully qualified domain name of the encrypted DNS resolver. @@ -72,10 +97,10 @@ private: }; }; -class OptionDNR4 : public Option { +class OptionDnr4 : public Option { public: - OptionDNR4(); - OptionDNR4(OptionBufferConstIter begin, OptionBufferConstIter end); + OptionDnr4(); + OptionDnr4(OptionBufferConstIter begin, OptionBufferConstIter end); virtual OptionPtr clone() const; virtual void pack(util::OutputBuffer& buf, bool check) const; virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end); @@ -83,6 +108,12 @@ public: virtual uint16_t len() const; }; +/// A pointer to the @c OptionDnr6 object. +typedef boost::shared_ptr OptionDnr6Ptr; + +/// A pointer to the @c OptionDnr4 object. +typedef boost::shared_ptr OptionDnr4Ptr; + } // namespace dhcp } // namespace isc diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h index bcd1a3b371..5669ea24db 100644 --- a/src/lib/dhcp/std_option_defs.h +++ b/src/lib/dhcp/std_option_defs.h @@ -367,7 +367,7 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = { { "v4-portparams", DHO_V4_PORTPARAMS, DHCP4_OPTION_SPACE, OPT_RECORD_TYPE, false, RECORD_DEF(V4_PORTPARAMS_RECORDS), "" }, { "v4-dnr", DHO_V4_DNR, DHCP4_OPTION_SPACE, OPT_BINARY_TYPE, - true, NO_RECORD_DEF, "" }, + false, NO_RECORD_DEF, "" }, { "option-6rd", DHO_6RD, DHCP4_OPTION_SPACE, OPT_RECORD_TYPE, true, RECORD_DEF(OPT_6RD_RECORDS), "" }, { "v4-access-domain", DHO_V4_ACCESS_DOMAIN, DHCP4_OPTION_SPACE, diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index 259ddff03f..6b52279b5e 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -63,6 +63,7 @@ libdhcp___unittests_SOURCES += option_data_types_unittest.cc libdhcp___unittests_SOURCES += option_definition_unittest.cc libdhcp___unittests_SOURCES += option_copy_unittest.cc libdhcp___unittests_SOURCES += option_custom_unittest.cc +libdhcp___unittests_SOURCES += option_dnr_unittest.cc libdhcp___unittests_SOURCES += option_opaque_data_tuples_unittest.cc libdhcp___unittests_SOURCES += option_unittest.cc libdhcp___unittests_SOURCES += option_space_unittest.cc diff --git a/src/lib/dhcp/tests/option_dnr_unittest.cc b/src/lib/dhcp/tests/option_dnr_unittest.cc new file mode 100644 index 0000000000..5ac3eb63f8 --- /dev/null +++ b/src/lib/dhcp/tests/option_dnr_unittest.cc @@ -0,0 +1,80 @@ +// Copyright (C) 2015-2023 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 + +#include +#include + +#include +#include + +using namespace isc; +using namespace isc::dhcp; +using boost::scoped_ptr; + +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(OptionDnr6Test, constructorAdnOnlyMode) { + // 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. + scoped_ptr option; + EXPECT_NO_THROW(option.reset(new OptionDnr6(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->getAdn()); + EXPECT_EQ(0, option->getAddrLength()); +} + +TEST(OptionDnr6Test, constructorDataTruncated) { + // 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. + scoped_ptr option; + EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OutOfRange); + ASSERT_FALSE(option); +} + +TEST(OptionDnr6Test, onlyWhitespaceFqdn) { + // 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 InvalidOptionDnr6DomainName exception. + scoped_ptr option; + EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), InvalidOptionDnr6DomainName); + ASSERT_FALSE(option); +} + +} // namespace \ No newline at end of file