From: Piotrek Zadroga Date: Wed, 19 Apr 2023 21:26:30 +0000 (+0200) Subject: [#2536] Implementing DNRv4 Option with TDD X-Git-Tag: Kea-2.3.8~171 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2b9f19ceae648fdd745fc61c4afa7579687bd1a8;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 ce59be639e..c1c9c0dee8 100644 --- a/src/lib/dhcp/option4_dnr.cc +++ b/src/lib/dhcp/option4_dnr.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -52,90 +53,38 @@ Option4Dnr::pack(util::OutputBuffer& buf, bool check) const { void Option4Dnr::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { setData(begin, end); - size_t offset = 0; - while (offset < std::distance(begin, end)) { + while (begin != end) { 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)); - } - dnr_instance.setDnrInstanceDataLength( - readUint16(&(*(begin + offset)), dnr_instance.getDnrInstanceDataLengthSize())); - offset += dnr_instance.getDnrInstanceDataLengthSize(); - OptionBufferConstIter dnr_instance_end = begin + offset + - dnr_instance.getDnrInstanceDataLength(); - - dnr_instance.setServicePriority( - 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(); - dnr_instance.setAdnLength(adn_length); - - // Encrypted DNS options are designed to always include an authentication domain name, - // so when there is no FQDN included, we shall throw an exception. - if (adn_length == 0) { - isc_throw(InvalidOptionDnrDomainName, "Mandatory Authentication Domain Name fully " - "qualified domain-name is missing"); + if (std::distance(begin, end) < dnr_instance.getMinimalLength()) { + isc_throw(OutOfRange, dnr_instance.getLogPrefix() + << " malformed: DNR instance data truncated to size " + << std::distance(begin, end)); } - // Let's try to extract ADN FQDN data. - dnr_instance.unpackAdn(adn_tuple.getData().begin(), adn_length); + // Unpack DnrInstanceDataLength. + dnr_instance.unpackDnrInstanceDataLength(begin); + + const OptionBufferConstIter dnr_instance_end = begin + + dnr_instance.getDnrInstanceDataLength(); + + // Unpack Service priority. + dnr_instance.unpackServicePriority(begin); - offset += adn_tuple.getTotalLength(); + // Unpack ADN len + ADN. + dnr_instance.unpackAdn(begin, dnr_instance_end); - if (begin + offset == dnr_instance_end) { + if (begin == dnr_instance_end) { // ADN only mode, other fields are not included. addDnrInstance(dnr_instance); continue; } dnr_instance.setAdnOnlyMode(false); - OpaqueDataTuple addr_tuple(OpaqueDataTuple::LENGTH_1_BYTE, begin + offset, - dnr_instance_end); - auto addr_length = addr_tuple.getLength(); - dnr_instance.setAddrLength(addr_length); - // It MUST be a multiple of 4. - if ((addr_length % V4ADDRESS_LEN) != 0) { - isc_throw(OutOfRange, "DHCPv4 Encrypted DNS Option (" - << type_ << ")" - << " malformed: Addr Len=" << addr_length - << " is not divisible by 4"); - } - - // As per draft-ietf-add-dnr 3.1.8: - // If additional data is supplied (i.e. not ADN only mode), - // the option includes at least one valid IP address. - if (addr_length == 0) { - isc_throw(OutOfRange, "DHCPv4 Encrypted DNS Option (" - << type_ << ")" - << " malformed: Addr Len=" << addr_length - << " is not greater than 0"); - } - - offset += dnr_instance.getAddrLengthSize(); - OptionBufferConstIter addr_begin = begin + offset; - OptionBufferConstIter addr_end = addr_begin + addr_length; - - while (addr_begin != addr_end) { - const uint8_t* ptr = &(*addr_begin); - dnr_instance.addIpAddress(IOAddress(readUint32(ptr, std::distance(addr_begin, addr_end)))); - addr_begin += V4ADDRESS_LEN; - offset += V4ADDRESS_LEN; - } + // Unpack Addr Len + IPv4 Address(es). + dnr_instance.unpackAddresses(begin, dnr_instance_end); // SvcParams (variable length) field is last. - auto svc_params_length = std::distance(begin + offset, dnr_instance_end); - dnr_instance.setSvcParamsLength(svc_params_length); - if (svc_params_length > 0) { - std::string svc_params; - svc_params.assign(begin + offset, dnr_instance_end); - dnr_instance.setSvcParams(svc_params); - dnr_instance.checkSvcParams(); - offset += svc_params_length; - } + dnr_instance.unpackSvcParams(begin, end); addDnrInstance(dnr_instance); } @@ -253,13 +202,23 @@ DnrInstance::setAdn(const std::string& adn) { adn_length_ = adn_len; if (universe_ == Option::V4) { - setDnrInstanceDataLength(dnrInstanceLen()); + dnr_instance_data_length_ = dnrInstanceLen(); } } void -DnrInstance::unpackAdn(OptionBufferConstIter begin, uint16_t adn_len) { - InputBuffer name_buf(&*begin, adn_len); +DnrInstance::unpackAdn(OptionBufferConstIter& begin, OptionBufferConstIter end) { + OpaqueDataTuple::LengthFieldType lft = OptionDataTypeUtil::getTupleLenFieldType(universe_); + OpaqueDataTuple adn_tuple(lft, begin, end); + adn_length_ = adn_tuple.getLength(); + + // Encrypted DNS options are designed to always include an authentication domain name, + // so when there is no FQDN included, we shall throw an exception. + if (adn_length_ == 0) { + isc_throw(InvalidOptionDnrDomainName, "Mandatory Authentication Domain Name fully " + "qualified domain-name is missing"); + } + InputBuffer name_buf(&*adn_tuple.getData().begin(), adn_length_); try { adn_.reset(new isc::dns::Name(name_buf, true)); } catch (const Exception& ex) { @@ -267,6 +226,7 @@ DnrInstance::unpackAdn(OptionBufferConstIter begin, uint16_t adn_len) { "fully qualified domain-name from wire format " "- " << ex.what()); } + begin += adn_length_ + getAdnLengthSize(); } void @@ -377,11 +337,11 @@ DnrInstance::checkFields() { auto addr_len = ip_addresses_.size() * addr_field_len; if (addr_len > max_addr_len) { isc_throw(OutOfRange, "Given IP addresses length " << addr_len << " is bigger than MAX " - << max_addr_len); + << max_addr_len); } addr_length_ = addr_len; if (universe_ == Option::V4) { - setDnrInstanceDataLength(dnrInstanceLen()); + dnr_instance_data_length_ = dnrInstanceLen(); } } @@ -406,7 +366,7 @@ DnrInstance::getDnrInstanceAsText() const { text += ", svc_params='" + svc_params_ + "'"; } } - return text; + return (text); } uint16_t @@ -452,5 +412,57 @@ DnrInstance::addIpAddress(const IOAddress& ip_address) { ip_addresses_.push_back(ip_address); } +void +DnrInstance::unpackDnrInstanceDataLength(OptionBufferConstIter& begin) { + dnr_instance_data_length_ = readUint16(&*begin, getDnrInstanceDataLengthSize()); + begin += getDnrInstanceDataLengthSize(); +} + +void +DnrInstance::unpackServicePriority(OptionBufferConstIter& begin) { + service_priority_ = readUint16(&*begin, SERVICE_PRIORITY_SIZE); + begin += SERVICE_PRIORITY_SIZE; +} + +void +DnrInstance::unpackAddresses(OptionBufferConstIter& begin, const OptionBufferConstIter end) { + OpaqueDataTuple addr_tuple(OpaqueDataTuple::LENGTH_1_BYTE, begin, end); + addr_length_ = addr_tuple.getLength(); + // It MUST be a multiple of 4. + if ((addr_length_ % V4ADDRESS_LEN) != 0) { + isc_throw(OutOfRange, getLogPrefix() << " malformed: Addr Len=" << addr_length_ + << " is not divisible by 4"); + } + + // As per draft-ietf-add-dnr 3.1.8: + // If additional data is supplied (i.e. not ADN only mode), + // the option includes at least one valid IP address. + if (addr_length_ == 0) { + isc_throw(OutOfRange, getLogPrefix() << " malformed: Addr Len=" << addr_length_ + << " is not greater than 0"); + } + + begin += getAddrLengthSize(); + OptionBufferConstIter addr_begin = begin; + OptionBufferConstIter addr_end = addr_begin + addr_length_; + + while (addr_begin != addr_end) { + const uint8_t* ptr = &(*addr_begin); + addIpAddress(IOAddress(readUint32(ptr, std::distance(addr_begin, addr_end)))); + addr_begin += V4ADDRESS_LEN; + begin += V4ADDRESS_LEN; + } +} + +void +DnrInstance::unpackSvcParams(OptionBufferConstIter& begin, OptionBufferConstIter end) { + svc_params_length_ = std::distance(begin, end); + if (svc_params_length_ > 0) { + svc_params_.assign(begin, end); + checkSvcParams(); + begin += svc_params_length_; + } +} + } // namespace dhcp } // namespace isc diff --git a/src/lib/dhcp/option4_dnr.h b/src/lib/dhcp/option4_dnr.h index 9c42d43b09..d2bcc2e879 100644 --- a/src/lib/dhcp/option4_dnr.h +++ b/src/lib/dhcp/option4_dnr.h @@ -20,10 +20,12 @@ namespace dhcp { /// /// DNR Instance includes the configuration data of an encrypted DNS resolver. /// It is used to build OPTION_V4_DNR (code 162). There may be multiple DNR Instances -/// in one OPTION_V4_DNR %Option. It can be also used to build OPTION_V6_DNR (code 144). -/// There must be only one DNR Instance in one OPTION_V6_DNR %Option. +/// in one OPTION_V4_DNR %Option. OPTION_V6_DNR (code 144) is using very similar structure, +/// only that there must be only one DNR Instance per one OPTION_V6_DNR %Option. That's why +/// @c Option6Dnr class can derive from this @c DnrInstance class, whereas @c Option4Dnr class +/// should have a container of @c DnrInstance's. /// -/// DNR Instance Data Format has been defined in the draft-ietf-add-dnr-15 (to be replaced +/// DNR Instance Data Format has been defined in the @c draft-ietf-add-dnr (to be replaced /// with published RFC). class DnrInstance { public: @@ -49,7 +51,8 @@ public: /// @param adn ADN FQDN /// @param ip_addresses Container of IP addresses /// @param svc_params Service Parameters - DnrInstance(Option::Universe universe, const uint16_t service_priority, + DnrInstance(Option::Universe universe, + uint16_t service_priority, const std::string& adn, const AddressContainer& ip_addresses, const std::string& svc_params); @@ -63,8 +66,7 @@ public: /// @param universe either V4 or V6 Option universe /// @param service_priority Service priority /// @param adn ADN FQDN - DnrInstance(Option::Universe universe, const uint16_t service_priority, - const std::string& adn); + DnrInstance(Option::Universe universe, uint16_t service_priority, const std::string& adn); /// @brief Default destructor. virtual ~DnrInstance() = default; @@ -132,7 +134,7 @@ public: /// /// @return Returns Service Parameters as a string. const std::string& getSvcParams() const { - return svc_params_; + return (svc_params_); } /// @brief Returns minimal length of the DNR instance data (without headers) in octets. @@ -152,9 +154,16 @@ public: /// @brief Returns size in octets of DNR Instance Data Length field. uint8_t getDnrInstanceDataLengthSize() const; + /// @brief Returns size in octets of ADN Length field. + uint8_t getAdnLengthSize() const; + + /// @brief Constructs Log prefix depending on V4/V6 Option universe. + /// @return Log prefix as a string which can be used for prints when throwing an exception. + std::string getLogPrefix() const; + /// @brief Returns whether ADN only mode is enabled or disabled. bool isAdnOnlyMode() const { - return adn_only_mode_; + return (adn_only_mode_); } /// @brief Sets Authentication domain name from given string. @@ -167,49 +176,12 @@ public: /// @param adn string representation of ADN FQDN void setAdn(const std::string& adn); - /// @brief Setter of the @c dnr_instance_data_length_ field. - /// - /// @param dnr_instance_data_length length to be set - void setDnrInstanceDataLength(uint16_t dnr_instance_data_length) { - dnr_instance_data_length_ = dnr_instance_data_length; - } - - /// @brief Setter of the @c service_priority_ field. - /// @param service_priority priority to be set - void setServicePriority(uint16_t service_priority) { - service_priority_ = service_priority; - } - - /// @brief Setter of the @c adn_length_ field. - /// @param adn_length length to be set - void setAdnLength(uint16_t adn_length) { - adn_length_ = adn_length; - } - - /// @brief Setter of the @c addr_length_ field. - /// @param addr_length length to be set - void setAddrLength(uint16_t addr_length) { - addr_length_ = addr_length; - } - /// @brief Setter of the @c adn_only_mode_ field. /// @param adn_only_mode enabled/disabled setting void setAdnOnlyMode(bool adn_only_mode) { adn_only_mode_ = adn_only_mode; } - /// @brief Setter of the @c svc_params_ field. - /// @param svc_params Svc Params to be set - void setSvcParams(const std::string& svc_params) { - svc_params_ = svc_params; - } - - /// @brief Setter of the @c svc_params_length_ field. - /// @param svc_params_length len to be set - void setSvcParamsLength(uint16_t svc_params_length) { - svc_params_length_ = svc_params_length; - } - /// @brief Writes the ADN FQDN in the wire format into a buffer. /// /// The Authentication Domain Name - fully qualified domain name of the encrypted @@ -234,10 +206,29 @@ public: /// @param [out] buf buffer where SvcParams will be written. void packSvcParams(isc::util::OutputBuffer& buf) const; + /// @brief Unpacks DNR Instance Data Length from wire data buffer and stores + /// it in @c dnr_instance_data_length_. + /// @param begin beginning of the buffer from which the field will be read + void unpackDnrInstanceDataLength(OptionBufferConstIter& begin); + + /// @brief Unpacks Service Priority from wire data buffer and stores it in @c service_priority_. + /// @param begin beginning of the buffer from which the field will be read + void unpackServicePriority(OptionBufferConstIter& begin); + /// @brief Unpacks the ADN from given wire data buffer and stores it in @c adn_ field. /// @param begin beginning of the buffer from which the ADN will be read - /// @param adn_len length of the ADN to be read - void unpackAdn(OptionBufferConstIter begin, uint16_t adn_len); + /// @param end end of the buffer from which the ADN will be read + void unpackAdn(OptionBufferConstIter& begin, OptionBufferConstIter end); + + /// @brief Unpacks IP address(es) from wire data and stores it/them in @c ip_addresses_. + /// @param begin beginning of the buffer from which the field will be read + /// @param end end of the buffer from which the field will be read + virtual void unpackAddresses(OptionBufferConstIter& begin, OptionBufferConstIter end); + + /// @brief Unpacks Service Parameters from wire data buffer and stores it in @c svc_params_. + /// @param begin beginning of the buffer from which the field will be read + /// @param end end of the buffer from which the field will be read + void unpackSvcParams(OptionBufferConstIter& begin, OptionBufferConstIter end); /// @brief Checks SvcParams field if encoded correctly and throws in case of issue found. /// @@ -305,19 +296,11 @@ protected: /// @brief Calculates and returns length of DNR Instance data in octets. /// @return length of DNR Instance data in octets. uint16_t dnrInstanceLen() const; - -private: - /// @brief Constructs Log prefix depending on V4/V6 Option universe. - /// @return Log prefix as a string which can be used for prints when throwing an exception. - std::string getLogPrefix() const; - - /// @brief Returns size in octets of ADN Length field. - uint8_t getAdnLengthSize() const; }; /// @brief Represents DHCPv4 Encrypted DNS %Option (code 162). /// -/// This option has been defined in the draft-ietf-add-dnr-15 (to be replaced +/// This option has been defined in the @c draft-ietf-add-dnr (to be replaced /// with published RFC) and it has a following structure: /// - option-code = 162 (1 octet) /// - option-len (1 octet) @@ -361,14 +344,14 @@ public: /// @brief Getter of the @c dnr_instances_ field. /// @return Reference to Option's DNR Instance container const DnrInstanceContainer& getDnrInstances() const { - return dnr_instances_; + return (dnr_instances_); } - virtual OptionPtr clone() const; - virtual void pack(util::OutputBuffer& buf, bool check = true) const; - virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end); - virtual std::string toText(int indent = 0) const; - virtual uint16_t len() const; + OptionPtr clone() const override; + void pack(util::OutputBuffer& buf, bool check = true) const override; + void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) override; + std::string toText(int indent = 0) const override; + uint16_t len() const override; protected: /// @brief Container holding DNR Instances. diff --git a/src/lib/dhcp/option6_dnr.cc b/src/lib/dhcp/option6_dnr.cc index 3e20d6bcdd..ca48bd761e 100644 --- a/src/lib/dhcp/option6_dnr.cc +++ b/src/lib/dhcp/option6_dnr.cc @@ -53,67 +53,73 @@ Option6Dnr::packAddresses(util::OutputBuffer& buf) const { void Option6Dnr::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { if (std::distance(begin, end) < getMinimalLength()) { - isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option (" << type_ << ") malformed: " - "data truncated to" - " size " << std::distance(begin, end)); + isc_throw(OutOfRange, getLogPrefix() << " malformed: data truncated to size " + << std::distance(begin, end)); } setData(begin, end); + // First two octets of Option data is Service Priority - this is mandatory field. - service_priority_ = isc::util::readUint16(&(*begin), SERVICE_PRIORITY_SIZE); - begin += SERVICE_PRIORITY_SIZE; + unpackServicePriority(begin); // Next come two octets of ADN Length plus the ADN data itself (variable length). // This is Opaque Data Tuple so let's use this class to retrieve the ADN data. - OpaqueDataTuple adn_tuple(OpaqueDataTuple::LENGTH_2_BYTES, begin, end); - adn_length_ = adn_tuple.getLength(); - - // Encrypted DNS options are designed to always include an authentication domain name, - // so when there is no FQDN included, we shall throw an exception. - if (adn_length_ == 0) { - isc_throw(InvalidOptionDnrDomainName, "Mandatory Authentication Domain Name fully " - "qualified domain-name is missing"); - } - - // Let's try to extract ADN FQDN data. - unpackAdn(adn_tuple.getData().begin(), adn_length_); - - begin += adn_tuple.getTotalLength(); + unpackAdn(begin, end); if (begin == end) { // ADN only mode, other fields are not included. return; } adn_only_mode_ = false; + + unpackAddresses(begin, end); + + // SvcParams (variable length) field is last. + unpackSvcParams(begin, end); +} + +std::string +Option6Dnr::toText(int indent) const { + std::ostringstream stream; + std::string in(indent, ' '); // base indentation + stream << in << "type=" << type_ << "(V6_DNR), " + << "len=" << (len() - getHeaderLen()) << ", " << getDnrInstanceAsText(); + return (stream.str()); +} + +uint16_t +Option6Dnr::len() const { + return (OPTION6_HDR_LEN + dnrInstanceLen()); +} + +void +Option6Dnr::unpackAddresses(OptionBufferConstIter& begin, OptionBufferConstIter end) { 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"); + isc_throw(OutOfRange, getLogPrefix() << " 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), getAddrLengthSize()); begin += getAddrLengthSize(); // It MUST be a multiple of 16. if ((addr_length_ % V6ADDRESS_LEN) != 0) { - isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option (" << type_ << ")" - << " malformed: Addr Len=" << addr_length_ - << " is not divisible by 16"); + isc_throw(OutOfRange, getLogPrefix() << " malformed: Addr Len=" << addr_length_ + << " is not divisible by 16"); } // As per draft-ietf-add-dnr 3.1.8: // If additional data is supplied (i.e. not ADN only mode), // the option includes at least one valid IP address. if (addr_length_ == 0) { - isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option (" << type_ << ")" - << " malformed: Addr Len=" << addr_length_ - << " is not greater than 0"); + isc_throw(OutOfRange, getLogPrefix() << " malformed: Addr Len=" << addr_length_ + << " is not greater than 0"); } // Check if IPv6 Address(es) field is not truncated. if (std::distance(begin, end) < addr_length_) { - isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option (" << type_ << ")" - << " malformed: Addr Len=" << addr_length_ - << " but IPv6 address(es) are truncated to len=" - << std::distance(begin, end)); + isc_throw(OutOfRange, getLogPrefix() << " malformed: Addr Len=" << addr_length_ + << " but IPv6 address(es) are truncated to len=" + << std::distance(begin, end)); } // Let's unpack the ipv6-address(es). @@ -122,27 +128,6 @@ Option6Dnr::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { ip_addresses_.push_back(IOAddress::fromBytes(AF_INET6, &(*begin))); begin += V6ADDRESS_LEN; } - - // SvcParams (variable length) field is last. - svc_params_length_ = std::distance(begin, end); - if (svc_params_length_ > 0) { - svc_params_.assign(begin, end); - checkSvcParams(); - } -} - -std::string -Option6Dnr::toText(int indent) const { - std::ostringstream stream; - std::string in(indent, ' '); // base indentation - stream << in << "type=" << type_ << "(V6_DNR), " - << "len=" << (len() - getHeaderLen()) << ", " << getDnrInstanceAsText(); - return (stream.str()); -} - -uint16_t -Option6Dnr::len() const { - return (OPTION6_HDR_LEN + dnrInstanceLen()); } } // namespace dhcp diff --git a/src/lib/dhcp/option6_dnr.h b/src/lib/dhcp/option6_dnr.h index 75ceaf143b..5a55cb36c5 100644 --- a/src/lib/dhcp/option6_dnr.h +++ b/src/lib/dhcp/option6_dnr.h @@ -31,7 +31,7 @@ public: /// @brief Represents DHCPv6 Encrypted DNS %Option (code 144). /// -/// This option has been defined in the draft-ietf-add-dnr-15 (to be replaced +/// This option has been defined in the @c draft-ietf-add-dnr (to be replaced /// with published RFC) and it has a following structure: /// - option-code = 144 (2 octets) /// - option-len (2 octets) @@ -43,7 +43,6 @@ public: /// - Service Parameters (variable length). class Option6Dnr : public Option, public DnrInstance { public: - /// @brief Constructor of the %Option from on-wire data. /// /// This constructor creates an instance of the option using a buffer with @@ -65,11 +64,11 @@ public: /// @param ip_addresses Container of IP addresses /// @param svc_params Service Parameters 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) {} + 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) { + } /// @brief Constructor of the %Option in ADN only mode. /// @@ -82,13 +81,14 @@ public: 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; - virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end); - virtual std::string toText(int indent = 0) const; - virtual uint16_t len() const; + OptionPtr clone() const override; + void pack(util::OutputBuffer& buf, bool check = false) const override; + void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) override; + std::string toText(int indent = 0) const override; + uint16_t len() const override; - virtual void packAddresses(isc::util::OutputBuffer& buf) const; + void packAddresses(isc::util::OutputBuffer& buf) const override; + void unpackAddresses(OptionBufferConstIter& begin, OptionBufferConstIter end) override; }; /// A pointer to the @c Option6Dnr object. diff --git a/src/lib/dhcp/tests/option4_dnr_unittest.cc b/src/lib/dhcp/tests/option4_dnr_unittest.cc index 20e644f629..93c5650b8d 100644 --- a/src/lib/dhcp/tests/option4_dnr_unittest.cc +++ b/src/lib/dhcp/tests/option4_dnr_unittest.cc @@ -438,4 +438,61 @@ TEST(Option4DnrTest, unpackOneDnrInstance) { EXPECT_EQ(66, option->len()); } +TEST(Option4DnrTest, unpackMixedDnrInstances) { + // Prepare data to decode - 2 DNR instances. + const uint8_t buf_data[] = { + 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, 62, // 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: myhost2. + 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example. + 0x03, 0x63, 0x6F, 0x6D, 0x00, // com. + 8, // Addr Len + 192, 168, 0, 1, // IP address 1 + 192, 168, 0, 2, // IP address 2 + 'k', 'e', 'y', '1', '2', '3', '=', 'v', // Svc Params + 'a', 'l', ' ', 'k', 'e', 'y', '2', '3', // Svc Params + '4', '=', 'v', 'a', 'l', '2', ' ', 'k', // Svc Params + 'e', 'y', '3', '4', '5' // Svc Params + }; + 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 Option4Dnr(buf.begin(), buf.end()))); + 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()); + + // Check if data was unpacked correctly from wire data. + const DnrInstance& dnr_1 = option->getDnrInstances()[0]; + EXPECT_EQ(24, dnr_1.getDnrInstanceDataLength()); + EXPECT_EQ(1, dnr_1.getServicePriority()); + EXPECT_EQ(21, dnr_1.getAdnLength()); + EXPECT_EQ("myhost1.example.com.", dnr_1.getAdnAsText()); + EXPECT_EQ(0, dnr_1.getAddrLength()); + EXPECT_EQ(0, dnr_1.getSvcParamsLength()); + + const DnrInstance& dnr_2 = option->getDnrInstances()[1]; + EXPECT_EQ(62, dnr_2.getDnrInstanceDataLength()); + EXPECT_EQ(2, dnr_2.getServicePriority()); + EXPECT_EQ(21, dnr_2.getAdnLength()); + EXPECT_EQ("myhost2.example.com.", dnr_2.getAdnAsText()); + EXPECT_EQ(8, dnr_2.getAddrLength()); + EXPECT_EQ(29, dnr_2.getSvcParamsLength()); + EXPECT_EQ(2, dnr_2.getAddresses().size()); + EXPECT_EQ("192.168.0.1", dnr_2.getAddresses()[0].toText()); + EXPECT_EQ("192.168.0.2", dnr_2.getAddresses()[1].toText()); + EXPECT_EQ("key123=val key234=val2 key345", dnr_2.getSvcParams()); + + EXPECT_EQ(92, option->len()); +} + } // namespace \ No newline at end of file diff --git a/src/lib/dhcp/tests/option6_dnr_unittest.cc b/src/lib/dhcp/tests/option6_dnr_unittest.cc index c94bfdf046..d13eaebfd8 100644 --- a/src/lib/dhcp/tests/option6_dnr_unittest.cc +++ b/src/lib/dhcp/tests/option6_dnr_unittest.cc @@ -62,7 +62,8 @@ TEST(Option6DnrTest, onWireCtorAdnOnlyMode) { // 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()); + "adn='myhost.example.com.'", + option->toText()); } // Test checks that exception is thrown when trying to unpack malformed wire data @@ -248,7 +249,8 @@ TEST(Option6DnrTest, onWireCtorValidIpV6Addresses) { "addr_length=48, " "address(es): 2001:db8:1::dead:beef " "ff02::face:b00c " - "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", option->toText()); + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + option->toText()); } // Test checks that exception is thrown when trying to unpack malformed wire data @@ -323,7 +325,8 @@ TEST(Option6DnrTest, onWireCtorSvcParamsIncluded) { "adn='myhost.example.com.', " "addr_length=16, " "address(es): 2001:db8:1::dead:beef, " - "svc_params='abc'", option->toText()); + "svc_params='abc'", + option->toText()); } // Test checks that exception is thrown when trying to unpack malformed wire data @@ -381,7 +384,8 @@ TEST(Option6DnrTest, adnOnlyModeCtor) { // toText() len does not count in headers len. EXPECT_EQ("type=144(V6_DNR), len=24, " "service_priority=9, adn_length=20, " - "adn='myhost.example.com.'", option->toText()); + "adn='myhost.example.com.'", + option->toText()); } // This test verifies that option constructor in ADN only mode throws @@ -389,7 +393,7 @@ TEST(Option6DnrTest, adnOnlyModeCtor) { TEST(Option6DnrTest, adnOnlyModeCtorNoFqdn) { // Prepare example parameters. const uint16_t service_priority = 9; - const std::string adn; // invalid empty ADN + const std::string adn; // invalid empty ADN // Create option instance. Check that constructor throws. scoped_ptr option; @@ -433,7 +437,8 @@ TEST(Option6DnrTest, allFieldsCtor) { EXPECT_EQ("type=144(V6_DNR), len=46, " "service_priority=9, adn_length=20, " "adn='myhost.example.com.', addr_length=16, " - "address(es): 2001:db8:1::baca, svc_params='alpn'", option->toText()); + "address(es): 2001:db8:1::baca, svc_params='alpn'", + option->toText()); } // This test verifies that option constructor throws @@ -443,12 +448,13 @@ TEST(Option6DnrTest, allFieldsCtorNoIpAddress) { // Prepare example parameters const uint16_t service_priority = 9; const std::string adn = "myhost.example.com."; - const Option6Dnr::AddressContainer addresses; // no IPv6 address in here + const Option6Dnr::AddressContainer addresses; // no IPv6 address in here const std::string svc_params = "alpn"; // Create option instance. Check that constructor throws. scoped_ptr option; - EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), OutOfRange); + EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), + OutOfRange); ASSERT_FALSE(option); } @@ -461,11 +467,12 @@ TEST(Option6DnrTest, svcParamsTwoEqualSignsPerParam) { const std::string adn = "myhost.example.com."; Option6Dnr::AddressContainer addresses; addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca")); - const std::string svc_params = "key123=val1=val2 key234"; // invalid svc param - 2 equal signs + const std::string svc_params = "key123=val1=val2 key234"; // invalid svc param - 2 equal signs // Create option instance. Check that constructor throws. scoped_ptr option; - EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams); + EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), + InvalidOptionDnrSvcParams); ASSERT_FALSE(option); } @@ -478,11 +485,12 @@ TEST(Option6DnrTest, svcParamsForbiddenKey) { const std::string adn = "myhost.example.com."; Option6Dnr::AddressContainer addresses; addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca")); - const std::string svc_params = "key123=val1 ipv6hint"; // forbidden svc param key - ipv6hint + const std::string svc_params = "key123=val1 ipv6hint"; // forbidden svc param key - ipv6hint // Create option instance. Check that constructor throws. scoped_ptr option; - EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams); + EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), + InvalidOptionDnrSvcParams); ASSERT_FALSE(option); } @@ -495,11 +503,12 @@ TEST(Option6DnrTest, svcParamsKeyRepeated) { const std::string adn = "myhost.example.com."; Option6Dnr::AddressContainer addresses; addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca")); - const std::string svc_params = "key123=val1 key234 key123"; // svc param key key123 repeated + const std::string svc_params = "key123=val1 key234 key123"; // svc param key key123 repeated // Create option instance. Check that constructor throws. scoped_ptr option; - EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams); + EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), + InvalidOptionDnrSvcParams); ASSERT_FALSE(option); } @@ -514,11 +523,12 @@ TEST(Option6DnrTest, svcParamsKeyTooLong) { addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca")); const std::string svc_params = "thisisveryveryveryvery" "veryveryveryveryveryvery" - "veryveryveryveryvlongkey"; // svc param key longer than 63 + "veryveryveryveryvlongkey"; // svc param key longer than 63 // Create option instance. Check that constructor throws. scoped_ptr option; - EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams); + EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), + InvalidOptionDnrSvcParams); ASSERT_FALSE(option); } @@ -535,7 +545,8 @@ TEST(Option6DnrTest, svcParamsKeyHasInvalidChar) { // Create option instance. Check that constructor throws. scoped_ptr option; - EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams); + EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), + InvalidOptionDnrSvcParams); ASSERT_FALSE(option); } @@ -555,7 +566,7 @@ TEST(Option6DnrTest, toText) { ASSERT_TRUE(option); const int indent = 4; - std::string expected = " type=144(V6_DNR), len=46, " // the indentation of 4 spaces + std::string expected = " type=144(V6_DNR), len=46, " // 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'";