From: Piotrek Zadroga Date: Wed, 19 Apr 2023 12:32:57 +0000 (+0200) Subject: [#2536] Implementing DNRv4 Option with TDD X-Git-Tag: Kea-2.3.8~175 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7a2ab16bbd39345835cf4f75d76b07cbbdcbd6a6;p=thirdparty%2Fkea.git [#2536] Implementing DNRv4 Option with TDD --- diff --git a/src/lib/dhcp/option4_dnr.cc b/src/lib/dhcp/option4_dnr.cc index d7afbdb601..d1c4232f6d 100644 --- a/src/lib/dhcp/option4_dnr.cc +++ b/src/lib/dhcp/option4_dnr.cc @@ -41,7 +41,7 @@ Option4Dnr::pack(util::OutputBuffer& buf, bool check) const { buf.writeUint8(dnr_instance.getAdnLength()); dnr_instance.packAdn(buf); if (dnr_instance.isAdnOnlyMode()) { - return; + continue; } buf.writeUint8(dnr_instance.getAddrLength()); dnr_instance.packAddresses(buf); @@ -54,20 +54,20 @@ Option4Dnr::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { setData(begin, end); size_t offset = 0; while (offset < std::distance(begin, end)) { - if (std::distance(begin + offset, end) < getMinimalLength()) { + DnrInstance dnr_instance(V4); + if (std::distance(begin + offset, end) < dnr_instance.getMinimalLength()) { isc_throw(OutOfRange, "DHCPv4 Encrypted DNS Option (" << type_ << ") malformed: " "DNR instance data truncated to size " << std::distance(begin + offset, end)); } - DnrInstance dnr_instance(V4); dnr_instance.setDnrInstanceDataLength( - readUint16(&(*(begin + offset)), DNR_INSTANCE_DATA_LENGTH_SIZE)); + readUint16(&(*(begin + offset)), dnr_instance.getDnrInstanceDataLengthSize())); OptionBufferConstIter dnr_instance_end = begin + offset + dnr_instance.getDnrInstanceDataLength(); - offset += DNR_INSTANCE_DATA_LENGTH_SIZE; + offset += dnr_instance.getDnrInstanceDataLengthSize(); dnr_instance.setServicePriority( - readUint16(&(*(begin + offset)), SERVICE_PRIORITY_SIZE)); - offset += SERVICE_PRIORITY_SIZE; + readUint16(&(*(begin + offset)), dnr_instance.SERVICE_PRIORITY_SIZE)); + offset += dnr_instance.SERVICE_PRIORITY_SIZE; OpaqueDataTuple adn_tuple(OpaqueDataTuple::LENGTH_1_BYTE, begin + offset, dnr_instance_end); auto adn_length = adn_tuple.getLength(); @@ -122,7 +122,7 @@ Option4Dnr::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { << " is not greater than 0"); } - offset += ADDR_LENGTH_SIZE; + offset += dnr_instance.getAddrLengthSize(); OptionBufferConstIter addr_begin = begin + offset; OptionBufferConstIter addr_end = addr_begin + addr_length; auto ip_addresses = dnr_instance.getIpAddresses(); @@ -137,8 +137,9 @@ Option4Dnr::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { // SvcParams (variable length) field is last. auto svc_params_length = std::distance(begin + offset, dnr_instance_end); if (svc_params_length > 0) { - std::string svc_params = dnr_instance.getSvcParams(); + std::string svc_params; svc_params.assign(begin + offset, dnr_instance_end); + dnr_instance.setSvcParams(svc_params); dnr_instance.checkSvcParams(); offset += svc_params_length; } @@ -149,14 +150,24 @@ Option4Dnr::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { std::string Option4Dnr::toText(int indent) const { - return Option::toText(indent); + std::ostringstream stream; + std::string in(indent, ' '); // base indentation + stream << in << "type=" << type_ << "(V4_DNR), " + << "len=" << (len() - getHeaderLen()); + int i = 0; + for(const DnrInstance& dnr_instance : dnr_instances_) { + stream << ", DNR Instance " << ++i << "(Instance len=" + << dnr_instance.getDnrInstanceDataLength() << ", " + << dnr_instance.getDnrInstanceAsText() << ")"; + } + return (stream.str()); } uint16_t Option4Dnr::len() const { uint16_t len = OPTION4_HDR_LEN; for (const DnrInstance& dnr_instance : dnr_instances_) { - len += dnr_instance.getDnrInstanceDataLength() + DNR_INSTANCE_DATA_LENGTH_SIZE; + len += dnr_instance.getDnrInstanceDataLength() + dnr_instance.getDnrInstanceDataLengthSize(); } return (len); } @@ -229,6 +240,9 @@ DnrInstance::setAdn(const std::string& adn) { } adn_length_ = adn_len; + if (universe_ == Option::V4) { + setDnrInstanceDataLength(dnrInstanceLen()); + } } void @@ -332,12 +346,19 @@ DnrInstance::checkFields() { checkSvcParams(false); } adn_only_mode_ = false; - auto addr_len = ip_addresses_.size() * V6ADDRESS_LEN; - if (addr_len > std::numeric_limits::max()) { + const uint8_t addr_field_len = (universe_ == Option::V4) ? V4ADDRESS_LEN : V6ADDRESS_LEN; + const uint16_t max_addr_len = (universe_ == Option::V4) + ? std::numeric_limits::max() + : std::numeric_limits::max(); + auto addr_len = ip_addresses_.size() * addr_field_len; + if (addr_len > max_addr_len) { isc_throw(OutOfRange, - "Given IPv6 addresses length " << addr_len << " is bigger than uint_16 MAX"); + "Given IP addresses length " << addr_len << " is bigger than MAX " << max_addr_len); } addr_length_ = addr_len; + if (universe_ == Option::V4) { + setDnrInstanceDataLength(dnrInstanceLen()); + } } std::string @@ -347,5 +368,78 @@ DnrInstance::getLogPrefix() const { ("DHCPv6 Encrypted DNS Option (" + std::to_string(D6O_V6_DNR) + ")"); } +DnrInstance::DnrInstance(Option::Universe universe, + const uint16_t service_priority, + const std::string& adn, + const DnrInstance::AddressContainer& ip_addresses, + const std::string& svc_params) + : universe_(universe), service_priority_(service_priority), + ip_addresses_(ip_addresses), svc_params_(svc_params) { + setAdn(adn); + checkFields(); +} + +DnrInstance::DnrInstance(Option::Universe universe, + const uint16_t service_priority, + const std::string& adn) + : universe_(universe), service_priority_(service_priority){ + setAdn(adn); +} + +std::string +DnrInstance::getDnrInstanceAsText() const { + std::string text = "service_priority=" + std::to_string(service_priority_) + ", " + + "adn_length=" + std::to_string(adn_length_) + ", " + + "adn='" + getAdnAsText() + "'"; + if (!adn_only_mode_) { + text += ", addr_length=" + std::to_string(addr_length_) + ", address(es):"; + for (const auto& address : ip_addresses_) { + text += " " + address.toText(); + } + if (svc_params_length_ > 0) { + text += ", svc_params='" + svc_params_ + "'"; + } + } + return text; +} + +uint16_t +DnrInstance::dnrInstanceLen() const { + uint16_t len = SERVICE_PRIORITY_SIZE + adn_length_ + getAdnLengthSize(); + if (!adn_only_mode_) { + len += addr_length_ + getAddrLengthSize() + svc_params_length_; + } + return (len); +} + +uint8_t +DnrInstance::getDnrInstanceDataLengthSize() const { + if (universe_ == Option::V6) { + return (0); + } + return (2); +} + +uint8_t +DnrInstance::getAdnLengthSize() const { + if (universe_ == Option::V6) { + return (2); + } + return (1); +} + +uint8_t +DnrInstance::getAddrLengthSize() const { + if (universe_ == Option::V6) { + return (2); + } + return (1); +} + +uint8_t +DnrInstance::getMinimalLength() const { + return (getDnrInstanceDataLengthSize() + SERVICE_PRIORITY_SIZE + getAdnLengthSize()); +} + } // namespace dhcp } // namespace isc diff --git a/src/lib/dhcp/option4_dnr.h b/src/lib/dhcp/option4_dnr.h index 0848dce318..e2cff27639 100644 --- a/src/lib/dhcp/option4_dnr.h +++ b/src/lib/dhcp/option4_dnr.h @@ -8,6 +8,7 @@ #define OPTION4_DNR_H #include +#include #include #include @@ -16,13 +17,24 @@ namespace dhcp { class DnrInstance { public: - DnrInstance(Option::Universe universe) : universe_(universe) {} - - virtual ~DnrInstance() {} - /// @brief A Type defined for container holding IP addresses. typedef std::vector AddressContainer; + /// @brief Size in octets of Service Priority field. + static const uint8_t SERVICE_PRIORITY_SIZE = 2; + + explicit DnrInstance(Option::Universe universe) : universe_(universe) {} + + DnrInstance(Option::Universe universe, const uint16_t service_priority, + const std::string& adn, + const AddressContainer& ip_addresses, + const std::string& svc_params); + + DnrInstance(Option::Universe universe, const uint16_t service_priority, + const std::string& adn); + + virtual ~DnrInstance() = default; + const AddressContainer& getIpAddresses() const { return ip_addresses_; } @@ -56,6 +68,8 @@ public: /// @return Authentication domain name in the text format. std::string getAdnAsText() const; + std::string getDnrInstanceAsText() const; + /// @brief Getter of the @c addr_length_. /// /// @return Length of enclosed IP addresses in octets. @@ -89,6 +103,23 @@ public: return svc_params_; } + /// @brief Returns minimal length of the DNR instance data (without headers) in octets. + /// + /// If the ADN-only mode is used, then "Addr Length", "ip(v4/v6)-address(es)", + /// and "Service Parameters (SvcParams)" fields are not present. + /// So minimal length of data is calculated by adding 2 octets for Service Priority, + /// octets needed for ADN Length and octets needed for DNR Instance Data Length + /// (only in case of DHCPv4). + /// + /// @return Minimal length of the DNR instance data (without headers) in octets. + uint8_t getMinimalLength() const; + + /// @brief Returns size in octets of Addr Length field. + uint8_t getAddrLengthSize() const; + + /// @brief Returns size in octets of DNR Instance Data Length field. + uint8_t getDnrInstanceDataLengthSize() const; + bool isAdnOnlyMode() const { return adn_only_mode_; } @@ -115,9 +146,9 @@ public: void setAddrLength(uint16_t addr_length) { addr_length_ = addr_length; } - void setSvcParamsLength(uint16_t svc_params_length) { - svc_params_length_ = svc_params_length; - } +// void setSvcParamsLength(uint16_t svc_params_length) { +// svc_params_length_ = svc_params_length; +// } void setAdnOnlyMode(bool adn_only_mode) { adn_only_mode_ = adn_only_mode; } @@ -204,27 +235,28 @@ protected: /// following the rules in Section 2.1 of [I-D.ietf-dnsop-svcb-https]. std::string svc_params_; + uint16_t dnrInstanceLen() const; + private: std::string getLogPrefix() const; + + /// @brief Returns size in octets of ADN Length field. + uint8_t getAdnLengthSize() const; }; class Option4Dnr : public Option { public: - /// @brief Size in octets of Service Priority field. - static const uint8_t DNR_INSTANCE_DATA_LENGTH_SIZE = 2; - - /// @brief Size in octets of Service Priority field. - static const uint8_t SERVICE_PRIORITY_SIZE = 2; + typedef std::vector DnrInstanceContainer; - /// @brief Size in octets of ADN Length field. - static const uint8_t ADN_LENGTH_SIZE = 1; + Option4Dnr(OptionBufferConstIter begin, OptionBufferConstIter end); - /// @brief Size in octets of Addr Length field. - static const uint8_t ADDR_LENGTH_SIZE = 1; + Option4Dnr() : Option(V4, DHO_V4_DNR) {} - typedef std::vector DnrInstanceContainer; + void addDnrInstance(DnrInstance& dnr_instance); - Option4Dnr(OptionBufferConstIter begin, OptionBufferConstIter end); + const DnrInstanceContainer& getDnrInstances() const { + return dnr_instances_; + } virtual OptionPtr clone() const; virtual void pack(util::OutputBuffer& buf, bool check = true) const; @@ -234,21 +266,6 @@ public: protected: DnrInstanceContainer dnr_instances_; - -private: - /// @brief Returns minimal length of the option data (without headers) in octets. - /// - /// If the ADN-only mode is used, then "Addr Length", "IPv4 Address(es)", - /// and "Service Parameters (SvcParams)" fields are not present. In this - /// case minimal length of data is 2 octets for Service Priority plus 1 octet - /// for ADN Length plus 2 octets for DNR Instance Data Length. - /// - /// @return Minimal length of the option data (without headers) in octets. - static uint8_t getMinimalLength() { - return (DNR_INSTANCE_DATA_LENGTH_SIZE + SERVICE_PRIORITY_SIZE + ADN_LENGTH_SIZE); - }; - - void addDnrInstance(DnrInstance& dnr_instance); }; /// A pointer to the @c OptionDnr4 object. diff --git a/src/lib/dhcp/option6_dnr.cc b/src/lib/dhcp/option6_dnr.cc index 6eca714f3b..77bcd19d85 100644 --- a/src/lib/dhcp/option6_dnr.cc +++ b/src/lib/dhcp/option6_dnr.cc @@ -6,8 +6,6 @@ #include -#include -#include #include #include #include @@ -22,24 +20,6 @@ Option6Dnr::Option6Dnr(OptionBufferConstIter begin, OptionBufferConstIter end) unpack(begin, end); } -Option6Dnr::Option6Dnr(const uint16_t service_priority, - const std::string& adn, - const Option6Dnr::AddressContainer& ip_addresses, - const std::string& svc_params) - : Option(V6, D6O_V6_DNR), DnrInstance(V6) { - service_priority_ = service_priority; - ip_addresses_ = ip_addresses; - svc_params_ = svc_params; - setAdn(adn); - checkFields(); -} - -Option6Dnr::Option6Dnr(const uint16_t service_priority, const std::string& adn) - : Option(V6, D6O_V6_DNR), DnrInstance(V6) { - service_priority_ = service_priority; - setAdn(adn); -} - OptionPtr Option6Dnr::clone() const { return (cloneInternal()); @@ -111,14 +91,14 @@ Option6Dnr::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { return; } adn_only_mode_ = false; - if (std::distance(begin, end) < ADDR_LENGTH_SIZE) { + if (std::distance(begin, end) < getAddrLengthSize()) { isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option (" << type_ << ") malformed: after" " ADN field, there should be at least " "2 bytes long Addr Length field"); } // Next come two octets of Addr Length. - addr_length_ = isc::util::readUint16(&(*begin), ADDR_LENGTH_SIZE); - begin += ADDR_LENGTH_SIZE; + addr_length_ = isc::util::readUint16(&(*begin), getAddrLengthSize()); + begin += getAddrLengthSize(); // It MUST be a multiple of 16. if ((addr_length_ % V6ADDRESS_LEN) != 0) { isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option (" << type_ << ")" @@ -163,32 +143,13 @@ Option6Dnr::toText(int indent) const { std::ostringstream stream; std::string in(indent, ' '); // base indentation stream << in << "type=" << type_ << "(V6_DNR), " - << "len=" << (len() - getHeaderLen()) << ", " - << "service_priority=" << service_priority_ << ", " - << "adn_length=" << adn_length_ << ", " - << "adn='" << getAdnAsText() << "'"; - if (!adn_only_mode_) { - stream << ", addr_length=" << addr_length_ - << ", address(es):"; - for (const auto& address : ip_addresses_) { - stream << " " << address.toText(); - } - - if (svc_params_length_ > 0) { - stream << ", svc_params='" << svc_params_ << "'"; - } - } - + << "len=" << (len() - getHeaderLen()) << ", " << getDnrInstanceAsText(); return (stream.str()); } uint16_t Option6Dnr::len() const { - uint16_t len = OPTION6_HDR_LEN + SERVICE_PRIORITY_SIZE + adn_length_ + ADN_LENGTH_SIZE; - if (!adn_only_mode_) { - len += addr_length_ + ADDR_LENGTH_SIZE + svc_params_length_; - } - return (len); + return (OPTION6_HDR_LEN + dnrInstanceLen()); } } // namespace dhcp diff --git a/src/lib/dhcp/option6_dnr.h b/src/lib/dhcp/option6_dnr.h index 8df2c0a5a5..b91354fe03 100644 --- a/src/lib/dhcp/option6_dnr.h +++ b/src/lib/dhcp/option6_dnr.h @@ -44,15 +44,6 @@ public: class Option6Dnr : public Option, public DnrInstance { public: - /// @brief Size in octets of Service Priority field. - static const uint8_t SERVICE_PRIORITY_SIZE = 2; - - /// @brief Size in octets of ADN Length field. - static const uint8_t ADN_LENGTH_SIZE = 2; - - /// @brief Size in octets of Addr Length field. - static const uint8_t ADDR_LENGTH_SIZE = 2; - /// @brief Constructor of the %Option from on-wire data. /// /// This constructor creates an instance of the option using a buffer with @@ -63,9 +54,15 @@ public: /// @param end Iterator pointing to the end of the buffer holding an option. Option6Dnr(OptionBufferConstIter begin, OptionBufferConstIter end); - Option6Dnr(const uint16_t service_priority, const std::string& adn, const AddressContainer& ip_addresses, const std::string& svc_params); + Option6Dnr(const uint16_t service_priority, + const std::string& adn, + const Option6Dnr::AddressContainer& ip_addresses, + const std::string& svc_params) + : Option(V6, D6O_V6_DNR), + DnrInstance(V6, service_priority, adn, ip_addresses, svc_params) {} - Option6Dnr(const uint16_t service_priority, const std::string& adn); + Option6Dnr(const uint16_t service_priority, const std::string& adn) + : Option(V6, D6O_V6_DNR), DnrInstance(V6, service_priority, adn) {} virtual OptionPtr clone() const; virtual void pack(util::OutputBuffer& buf, bool check = false) const; @@ -74,19 +71,6 @@ public: virtual uint16_t len() const; virtual void packAddresses(isc::util::OutputBuffer& buf) const; - -private: - /// @brief Returns minimal length of the option data (without headers) in octets. - /// - /// If the ADN-only mode is used, then "Addr Length", "ipv6-address(es)", - /// and "Service Parameters (SvcParams)" fields are not present. In this - /// case minimal length of data is 2 octets for Service Priority plus 2 octets - /// for ADN Length. - /// - /// @return Minimal length of the option data (without headers) in octets. - static uint8_t getMinimalLength() { - return (SERVICE_PRIORITY_SIZE + ADN_LENGTH_SIZE); - }; }; /// A pointer to the @c Option6Dnr object. diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index 6b52279b5e..23d2f42275 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -49,9 +49,11 @@ libdhcp___unittests_SOURCES += libdhcp++_unittest.cc libdhcp___unittests_SOURCES += opaque_data_tuple_unittest.cc libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc libdhcp___unittests_SOURCES += option4_client_fqdn_unittest.cc +libdhcp___unittests_SOURCES += option4_dnr_unittest.cc libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc libdhcp___unittests_SOURCES += option6_client_fqdn_unittest.cc libdhcp___unittests_SOURCES += option6_auth_unittest.cc +libdhcp___unittests_SOURCES += option6_dnr_unittest.cc libdhcp___unittests_SOURCES += option6_ia_unittest.cc libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc libdhcp___unittests_SOURCES += option6_iaprefix_unittest.cc @@ -63,7 +65,6 @@ 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/option4_dnr_unittest.cc b/src/lib/dhcp/tests/option4_dnr_unittest.cc new file mode 100644 index 0000000000..c1615a25c3 --- /dev/null +++ b/src/lib/dhcp/tests/option4_dnr_unittest.cc @@ -0,0 +1,223 @@ +// 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 + +#include + +#include + +using namespace isc; +using namespace isc::dhcp; +using boost::scoped_ptr; + +namespace { + +TEST(Option4DnrTest, emptyCtor) { + // Create option instance. Check that constructor doesn't throw. + scoped_ptr option; + EXPECT_NO_THROW(option.reset(new Option4Dnr())); + ASSERT_TRUE(option); + + // Check if member variables were correctly set by ctor. + EXPECT_EQ(Option::V4, option->getUniverse()); + EXPECT_EQ(DHO_V4_DNR, option->getType()); +} + +TEST(Option4DnrTest, oneDnrOnlyModeInstance) { + // Create option instance. Check that constructor doesn't throw. + scoped_ptr option; + EXPECT_NO_THROW(option.reset(new Option4Dnr())); + ASSERT_TRUE(option); + + // Prepare example DNR instance to add. + DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com."); + + // Add DNR instance. + option->addDnrInstance(dnr_1); + + // Check if member variables were correctly set inside DNR instances. + EXPECT_EQ(1, option->getDnrInstances().size()); + EXPECT_EQ(1, option->getDnrInstances()[0].getServicePriority()); + EXPECT_EQ(21, option->getDnrInstances()[0].getAdnLength()); + EXPECT_EQ("myhost1.example.com.", option->getDnrInstances()[0].getAdnAsText()); + + // This is ADN only mode, so Addr Length and SvcParams Length + // are both expected to be zero. + EXPECT_EQ(0, option->getDnrInstances()[0].getAddrLength()); + EXPECT_EQ(0, option->getDnrInstances()[0].getSvcParamsLength()); + + // BTW let's check if len() works ok. In ADN only mode, DNR Instance Data Len + // is set to ADN Len (21) + 3 = 24. + // expected len: 1x(24 (ADN+ADN Len+Service priority) + 2 (DNR Instance Data Len)) + 2 (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=162(V4_DNR), len=26, " + "DNR Instance 1(Instance len=24, service_priority=1, " + "adn_length=21, adn='myhost1.example.com.')", + option->toText()); +} + +TEST(Option4DnrTest, multipleDnrOnlyModeInstances) { + // Create option instance. Check that constructor doesn't throw. + scoped_ptr option; + EXPECT_NO_THROW(option.reset(new Option4Dnr())); + ASSERT_TRUE(option); + + // Check if member variables were correctly set by ctor. + EXPECT_EQ(Option::V4, option->getUniverse()); + EXPECT_EQ(DHO_V4_DNR, option->getType()); + + // Prepare example DNR instances to add. + DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com."); + DnrInstance dnr_2 = DnrInstance(Option::V4, 2, "myhost2.example.com."); + DnrInstance dnr_3 = DnrInstance(Option::V4, 3, "myhost3.example.com."); + + // Add DNR instances. + option->addDnrInstance(dnr_1); + option->addDnrInstance(dnr_2); + option->addDnrInstance(dnr_3); + + // Check if member variables were correctly set inside DNR instances. + EXPECT_EQ(3, option->getDnrInstances().size()); + EXPECT_EQ(1, option->getDnrInstances()[0].getServicePriority()); + EXPECT_EQ(2, option->getDnrInstances()[1].getServicePriority()); + EXPECT_EQ(3, option->getDnrInstances()[2].getServicePriority()); + EXPECT_EQ(21, option->getDnrInstances()[0].getAdnLength()); + EXPECT_EQ(21, option->getDnrInstances()[1].getAdnLength()); + EXPECT_EQ(21, option->getDnrInstances()[2].getAdnLength()); + EXPECT_EQ("myhost1.example.com.", option->getDnrInstances()[0].getAdnAsText()); + EXPECT_EQ("myhost2.example.com.", option->getDnrInstances()[1].getAdnAsText()); + EXPECT_EQ("myhost3.example.com.", option->getDnrInstances()[2].getAdnAsText()); + + // This is ADN only mode, so Addr Length and SvcParams Length + // are both expected to be zero. + EXPECT_EQ(0, option->getDnrInstances()[0].getAddrLength()); + EXPECT_EQ(0, option->getDnrInstances()[0].getSvcParamsLength()); + EXPECT_EQ(0, option->getDnrInstances()[1].getAddrLength()); + EXPECT_EQ(0, option->getDnrInstances()[1].getSvcParamsLength()); + EXPECT_EQ(0, option->getDnrInstances()[2].getAddrLength()); + EXPECT_EQ(0, option->getDnrInstances()[2].getSvcParamsLength()); + + // BTW let's check if len() works ok. In ADN only mode, DNR Instance Data Len + // is set to ADN Len (21) + 3 = 24. + // expected len: 3x(24 (ADN+ADN Len+Service priority) + 2 (DNR Instance Data Len)) + 2 (headers) + // = 78 + 2 = 80 + EXPECT_EQ(80, option->len()); + + // BTW let's check if toText() works ok. + // toText() len does not count in headers len. + EXPECT_EQ("type=162(V4_DNR), len=78, " + "DNR Instance 1(Instance len=24, service_priority=1, " + "adn_length=21, adn='myhost1.example.com.'), " + "DNR Instance 2(Instance len=24, service_priority=2, " + "adn_length=21, adn='myhost2.example.com.'), " + "DNR Instance 3(Instance len=24, service_priority=3, " + "adn_length=21, adn='myhost3.example.com.')", + option->toText()); +} + +TEST(Option4DnrTest, packOneDnrOnlyModeInstance) { + // Create option instance. Check that constructor doesn't throw. + scoped_ptr option; + EXPECT_NO_THROW(option.reset(new Option4Dnr())); + ASSERT_TRUE(option); + + // Prepare example DNR instance to add. + DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com."); + + // Add DNR instance. + option->addDnrInstance(dnr_1); + + // 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[] = { + DHO_V4_DNR, // Option code + 26, // Option len=26 dec + 0x00, 24, // DNR Instance Data Len + 0x00, 0x01, // Service priority is 1 dec + 21, // ADN Length is 21 dec + 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '1', // FQDN: myhost1. + 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())); +} + +TEST(Option4DnrTest, packMultipleDnrOnlyModeInstances) { + // Create option instance. Check that constructor doesn't throw. + scoped_ptr option; + EXPECT_NO_THROW(option.reset(new Option4Dnr())); + ASSERT_TRUE(option); + + // Check if member variables were correctly set by ctor. + EXPECT_EQ(Option::V4, option->getUniverse()); + EXPECT_EQ(DHO_V4_DNR, option->getType()); + + // Prepare example DNR instances to add. + DnrInstance dnr_1 = DnrInstance(Option::V4, 1, "myhost1.example.com."); + DnrInstance dnr_2 = DnrInstance(Option::V4, 2, "myhost2.example.com."); + DnrInstance dnr_3 = DnrInstance(Option::V4, 3, "myhost3.example.com."); + + // Add DNR instances. + option->addDnrInstance(dnr_1); + option->addDnrInstance(dnr_2); + option->addDnrInstance(dnr_3); + + // 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[] = { + DHO_V4_DNR, // Option code + 78, // Option len=78 dec + 0x00, 24, // DNR Instance Data Len + 0x00, 0x01, // Service priority is 1 dec + 21, // ADN Length is 21 dec + 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '1', // FQDN: myhost1. + 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example. + 0x03, 0x63, 0x6F, 0x6D, 0x00, // com. + 0x00, 24, // DNR Instance Data Len + 0x00, 0x02, // Service priority is 2 dec + 21, // ADN Length is 21 dec + 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '2', // FQDN: myhost1. + 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example. + 0x03, 0x63, 0x6F, 0x6D, 0x00, // com. + 0x00, 24, // DNR Instance Data Len + 0x00, 0x03, // Service priority is 3 dec + 21, // ADN Length is 21 dec + 0x07, 0x6D, 0x79, 0x68, 0x6F, 0x73, 0x74, '3', // FQDN: myhost1. + 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())); +} + +} // namespace \ No newline at end of file diff --git a/src/lib/dhcp/tests/option_dnr_unittest.cc b/src/lib/dhcp/tests/option6_dnr_unittest.cc similarity index 96% rename from src/lib/dhcp/tests/option_dnr_unittest.cc rename to src/lib/dhcp/tests/option6_dnr_unittest.cc index f16ecf3db3..c94bfdf046 100644 --- a/src/lib/dhcp/tests/option_dnr_unittest.cc +++ b/src/lib/dhcp/tests/option6_dnr_unittest.cc @@ -24,7 +24,7 @@ namespace { // Provided wire data is in the ADN only mode i.e. only // Service priority and Authentication domain name FQDN // fields are present. -TEST(OptionDnr6Test, onWireCtorAdnOnlyMode) { +TEST(Option6DnrTest, onWireCtorAdnOnlyMode) { // Prepare data to decode - ADN only mode. const uint8_t buf_data[] = { 0x80, 0x01, // Service priority is 32769 dec @@ -67,7 +67,7 @@ TEST(OptionDnr6Test, onWireCtorAdnOnlyMode) { // Test checks that exception is thrown when trying to unpack malformed wire data // - mandatory fields are truncated. -TEST(OptionDnr6Test, onWireCtorDataTruncated) { +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 @@ -82,7 +82,7 @@ TEST(OptionDnr6Test, onWireCtorDataTruncated) { // Test checks that exception is thrown when trying to unpack malformed wire data // - ADN FQDN contains only whitespace - non valid FQDN. -TEST(OptionDnr6Test, onWireCtorOnlyWhitespaceFqdn) { +TEST(Option6DnrTest, onWireCtorOnlyWhitespaceFqdn) { // Prepare data to decode - ADN only mode. const uint8_t buf_data[] = { 0x80, 0x01, // Service priority is 32769 dec @@ -99,7 +99,7 @@ TEST(OptionDnr6Test, onWireCtorOnlyWhitespaceFqdn) { // Test checks that exception is thrown when trying to unpack malformed wire data // - ADN Length is 0 and no ADN FQDN at all. -TEST(OptionDnr6Test, onWireCtorNoAdnFqdn) { +TEST(Option6DnrTest, onWireCtorNoAdnFqdn) { // Prepare data to decode - ADN only mode. const uint8_t buf_data[] = { 0x00, 0x01, // Service priority is 1 dec @@ -117,7 +117,7 @@ TEST(OptionDnr6Test, onWireCtorNoAdnFqdn) { // Test checks that exception is thrown when trying to unpack malformed wire data // - FQDN data is truncated. -TEST(OptionDnr6Test, onWireCtorTruncatedFqdn) { +TEST(Option6DnrTest, onWireCtorTruncatedFqdn) { // Prepare data to decode - ADN only mode. const uint8_t buf_data[] = { 0x80, 0x01, // Service priority is 32769 dec @@ -134,7 +134,7 @@ TEST(OptionDnr6Test, onWireCtorTruncatedFqdn) { // Test checks that exception is thrown when trying to unpack malformed wire data // - Addr Length field truncated. -TEST(OptionDnr6Test, onWireCtorAddrLenTruncated) { +TEST(Option6DnrTest, onWireCtorAddrLenTruncated) { // Prepare data to decode. const uint8_t buf_data[] = { 0x80, 0x01, // Service priority is 32769 dec @@ -154,7 +154,7 @@ TEST(OptionDnr6Test, onWireCtorAddrLenTruncated) { // Test checks that exception is thrown when trying to unpack malformed wire data // - Addr length is 0 and no IPv6 addresses at all. -TEST(OptionDnr6Test, onWireCtorAddrLenZero) { +TEST(Option6DnrTest, onWireCtorAddrLenZero) { // Prepare data to decode. const uint8_t buf_data[] = { 0x80, 0x01, // Service priority is 32769 dec @@ -176,7 +176,7 @@ TEST(OptionDnr6Test, onWireCtorAddrLenZero) { // Test checks that exception is thrown when trying to unpack malformed wire data // - Addr length is not a multiple of 16. -TEST(OptionDnr6Test, onWireCtorAddrLenNot16Modulo) { +TEST(Option6DnrTest, onWireCtorAddrLenNot16Modulo) { // Prepare data to decode. const uint8_t buf_data[] = { 0x80, 0x01, // Service priority is 32769 dec @@ -196,7 +196,7 @@ TEST(OptionDnr6Test, onWireCtorAddrLenNot16Modulo) { // This test verifies option constructor from wire data. // Provided wire data contains also IPv6 addresses. -TEST(OptionDnr6Test, onWireCtorValidIpV6Addresses) { +TEST(Option6DnrTest, onWireCtorValidIpV6Addresses) { // Prepare data to decode const uint8_t buf_data[] = { 0x80, 0x01, // Service priority is 32769 dec @@ -253,7 +253,7 @@ TEST(OptionDnr6Test, onWireCtorValidIpV6Addresses) { // Test checks that exception is thrown when trying to unpack malformed wire data // - IPv6 addresses are truncated. -TEST(OptionDnr6Test, onWireCtorTruncatedIpV6Addresses) { +TEST(Option6DnrTest, onWireCtorTruncatedIpV6Addresses) { // Prepare data to decode. const uint8_t buf_data[] = { 0x80, 0x01, // Service priority is 32769 dec @@ -276,7 +276,7 @@ TEST(OptionDnr6Test, onWireCtorTruncatedIpV6Addresses) { // This test verifies option constructor from wire data. // Provided wire data contains also IPv6 address and Svc Params. -TEST(OptionDnr6Test, onWireCtorSvcParamsIncluded) { +TEST(Option6DnrTest, onWireCtorSvcParamsIncluded) { // Prepare data to decode. const uint8_t buf_data[] = { 0x80, 0x01, // Service priority is 32769 dec @@ -328,7 +328,7 @@ TEST(OptionDnr6Test, onWireCtorSvcParamsIncluded) { // Test checks that exception is thrown when trying to unpack malformed wire data // - SvcParams Key contains char that is not allowed. -TEST(OptionDnr6Test, onWireCtorSvcParamsInvalidCharKey) { +TEST(Option6DnrTest, onWireCtorSvcParamsInvalidCharKey) { // Prepare data to decode with invalid SvcParams. const uint8_t buf_data[] = { 0x80, 0x01, // Service priority is 32769 dec @@ -351,7 +351,7 @@ TEST(OptionDnr6Test, onWireCtorSvcParamsInvalidCharKey) { // This test verifies option constructor in ADN only mode. // Service priority and ADN are provided via ctor. -TEST(OptionDnr6Test, adnOnlyModeCtor) { +TEST(Option6DnrTest, adnOnlyModeCtor) { // Prepare example parameters. const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; @@ -386,7 +386,7 @@ TEST(OptionDnr6Test, adnOnlyModeCtor) { // This test verifies that option constructor in ADN only mode throws // an exception when mandatory ADN is empty. -TEST(OptionDnr6Test, adnOnlyModeCtorNoFqdn) { +TEST(Option6DnrTest, adnOnlyModeCtorNoFqdn) { // Prepare example parameters. const uint16_t service_priority = 9; const std::string adn; // invalid empty ADN @@ -400,7 +400,7 @@ TEST(OptionDnr6Test, adnOnlyModeCtorNoFqdn) { // This test verifies option constructor where all fields // i.e. Service priority, ADN, IP address(es) and Service params // are provided as ctor parameters. -TEST(OptionDnr6Test, allFieldsCtor) { +TEST(Option6DnrTest, allFieldsCtor) { // Prepare example parameters const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; @@ -439,7 +439,7 @@ TEST(OptionDnr6Test, allFieldsCtor) { // This test verifies that option constructor throws // an exception when option fields provided via ctor are malformed // - no IPv6 address provided. -TEST(OptionDnr6Test, allFieldsCtorNoIpAddress) { +TEST(Option6DnrTest, allFieldsCtorNoIpAddress) { // Prepare example parameters const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; @@ -455,7 +455,7 @@ TEST(OptionDnr6Test, allFieldsCtorNoIpAddress) { // This test verifies that option constructor throws // an exception when option fields provided via ctor are malformed // - Svc Params key=val pair has 2 equal signs. -TEST(OptionDnr6Test, svcParamsTwoEqualSignsPerParam) { +TEST(Option6DnrTest, svcParamsTwoEqualSignsPerParam) { // Prepare example parameters. const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; @@ -472,7 +472,7 @@ TEST(OptionDnr6Test, svcParamsTwoEqualSignsPerParam) { // This test verifies that option constructor throws // an exception when option fields provided via ctor are malformed // - Svc Params forbidden key provided. -TEST(OptionDnr6Test, svcParamsForbiddenKey) { +TEST(Option6DnrTest, svcParamsForbiddenKey) { // Prepare example parameters. const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; @@ -489,7 +489,7 @@ TEST(OptionDnr6Test, svcParamsForbiddenKey) { // This test verifies that option constructor throws // an exception when option fields provided via ctor are malformed // - Svc Params key was repeated. -TEST(OptionDnr6Test, svcParamsKeyRepeated) { +TEST(Option6DnrTest, svcParamsKeyRepeated) { // Prepare example parameters. const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; @@ -506,7 +506,7 @@ TEST(OptionDnr6Test, svcParamsKeyRepeated) { // This test verifies that option constructor throws // an exception when option fields provided via ctor are malformed // - Svc Params key is too long. -TEST(OptionDnr6Test, svcParamsKeyTooLong) { +TEST(Option6DnrTest, svcParamsKeyTooLong) { // Prepare example parameters. const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; @@ -525,7 +525,7 @@ TEST(OptionDnr6Test, svcParamsKeyTooLong) { // This test verifies that option constructor throws // an exception when option fields provided via ctor are malformed // - Svc Params key has chars that are not allowed. -TEST(OptionDnr6Test, svcParamsKeyHasInvalidChar) { +TEST(Option6DnrTest, svcParamsKeyHasInvalidChar) { // Prepare example parameters. const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; @@ -541,7 +541,7 @@ TEST(OptionDnr6Test, svcParamsKeyHasInvalidChar) { // This test verifies that string representation of the option returned by // toText method is correctly formatted. -TEST(OptionDnr6Test, toText) { +TEST(Option6DnrTest, toText) { // Prepare example parameters. const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; @@ -563,7 +563,7 @@ TEST(OptionDnr6Test, toText) { } // This test verifies on-wire format of the option is correctly created in ADN only mode. -TEST(OptionDnr6Test, packAdnOnlyMode) { +TEST(Option6DnrTest, packAdnOnlyMode) { // Prepare example parameters. const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; @@ -598,7 +598,7 @@ TEST(OptionDnr6Test, packAdnOnlyMode) { // This test verifies on-wire format of the option is correctly created when // IP addresses and Svc Params are also included. -TEST(OptionDnr6Test, pack) { +TEST(Option6DnrTest, pack) { // Prepare example parameters. const uint16_t service_priority = 9; const std::string adn = "myhost.example.com.";