From: Piotrek Zadroga Date: Wed, 5 Apr 2023 16:43:08 +0000 (+0200) Subject: [#2536] Working on upack DNRv6 Option X-Git-Tag: Kea-2.3.8~186 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a6ed181a8ec12e20bebb70c8ecbae0fac1e0dbaa;p=thirdparty%2Fkea.git [#2536] Working on upack DNRv6 Option --- diff --git a/src/lib/dhcp/option_dnr.cc b/src/lib/dhcp/option_dnr.cc index c3e3701130..ca9af55151 100644 --- a/src/lib/dhcp/option_dnr.cc +++ b/src/lib/dhcp/option_dnr.cc @@ -8,14 +8,12 @@ #include #include +#include #include namespace isc { namespace dhcp { -OptionDNR6::OptionDNR6() : Option(V6, D6O_V6_DNR) { -} - OptionDNR6::OptionDNR6(OptionBufferConstIter begin, OptionBufferConstIter end) : Option(V6, D6O_V6_DNR) { unpack(begin, end); @@ -28,12 +26,51 @@ OptionDNR6::clone() const { void OptionDNR6::pack(util::OutputBuffer& buf, bool check) const { - Option::pack(buf, check); + packHeader(buf, check); + + buf.writeUint16(service_priority_); + // TBD } void OptionDNR6::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { - Option::unpack(begin, end); + if (std::distance(begin, end) < getMinimalLength()) { + isc_throw(OutOfRange, "parsed DHCPv6 Encrypted DNS Option (" + << D6O_V6_DNR << ") 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 += sizeof(service_priority_); + + // Next comes 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(); + if (adn_length_ > 0) { + // Let's try to extract ADN FQDN data + isc::util::InputBuffer name_buf(&adn_tuple.getData(), + 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"); + } + } + begin += adn_tuple.getTotalLength(); + + if (begin == end) { + // ADN only mode, other fields are not included + return; + } + if (std::distance(begin, end) < ADDR_LENGTH_SIZE) { + isc_throw(OutOfRange, "truncated DHCPv6 Encrypted DNS Option (" << D6O_V6_DNR << ") - after" + " ADN field, there should be at least 2 bytes long Addr Length field"); + } + addr_length_ = isc::util::readUint16((&*begin), ADDR_LENGTH_SIZE); + // TBD } std::string diff --git a/src/lib/dhcp/option_dnr.h b/src/lib/dhcp/option_dnr.h index 630947cc7e..f2c6b2a1aa 100644 --- a/src/lib/dhcp/option_dnr.h +++ b/src/lib/dhcp/option_dnr.h @@ -8,19 +8,68 @@ #define OPTION_DNR_H #include +#include namespace isc { namespace dhcp { +/// @brief Exception thrown when invalid domain name is specified. +class InvalidOptionDNR6DomainName : public Exception { +public: + InvalidOptionDNR6DomainName(const char* file, size_t line, + const char* what) : + isc::Exception(file, line, what) {} +}; + class OptionDNR6 : public Option { public: - OptionDNR6(); + + /// @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; + + OptionDNR6() : Option(V6, D6O_V6_DNR) { + }; 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; + +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. + uint16_t adn_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. + /// This field is formatted as specified in Section 10 of RFC8415. + boost::shared_ptr adn_; + + /// @brief Length of enclosed IPv6 addresses in octets. + uint16_t addr_length_; + + /// @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; + }; }; class OptionDNR4 : public Option {