]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2536] Implementing DNRv4 Option
authorPiotrek Zadroga <piotrek@isc.org>
Tue, 18 Apr 2023 17:59:51 +0000 (19:59 +0200)
committerPiotrek Zadroga <piotrek@isc.org>
Thu, 4 May 2023 21:17:18 +0000 (23:17 +0200)
src/lib/dhcp/Makefile.am
src/lib/dhcp/option4_dnr.cc [new file with mode: 0644]
src/lib/dhcp/option4_dnr.h [moved from src/lib/dhcp/option_dnr.h with 58% similarity]
src/lib/dhcp/option6_dnr.cc [new file with mode: 0644]
src/lib/dhcp/option6_dnr.h [new file with mode: 0644]
src/lib/dhcp/option_definition.cc
src/lib/dhcp/option_dnr.cc [deleted file]
src/lib/dhcp/tests/option_dnr_unittest.cc

index 1a2c5b418138bff330e90e5fe26431afcf7cdf3f..414363aa25f427b2474234e45c9cfe063622f5ba 100644 (file)
@@ -35,7 +35,8 @@ libkea_dhcp___la_SOURCES += option.cc option.h
 libkea_dhcp___la_SOURCES += option_custom.cc option_custom.h
 libkea_dhcp___la_SOURCES += option_data_types.cc option_data_types.h
 libkea_dhcp___la_SOURCES += option_definition.cc option_definition.h
-libkea_dhcp___la_SOURCES += option_dnr.cc option_dnr.h
+libkea_dhcp___la_SOURCES += option4_dnr.cc option4_dnr.h
+libkea_dhcp___la_SOURCES += option6_dnr.cc option6_dnr.h
 libkea_dhcp___la_SOURCES += option_int.h
 libkea_dhcp___la_SOURCES += option_int_array.h
 libkea_dhcp___la_SOURCES += option_opaque_data_tuples.cc option_opaque_data_tuples.h
diff --git a/src/lib/dhcp/option4_dnr.cc b/src/lib/dhcp/option4_dnr.cc
new file mode 100644 (file)
index 0000000..f8d196d
--- /dev/null
@@ -0,0 +1,350 @@
+// Copyright (C) 2018-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 <config.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/opaque_data_tuple.h>
+#include <dhcp/option4_dnr.h>
+#include <dhcp/option6_dnr.h>
+#include <dns/labelsequence.h>
+#include <util/strutil.h>
+#include <set>
+
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+Option4Dnr::Option4Dnr(OptionBufferConstIter begin, OptionBufferConstIter end)
+    : Option(V4, DHO_V4_DNR) {
+    unpack(begin, end);
+}
+
+OptionPtr
+Option4Dnr::clone() const {
+    return (cloneInternal<Option4Dnr>());
+}
+
+void
+Option4Dnr::pack(util::OutputBuffer& buf, bool check) const {
+    packHeader(buf, check);
+    for (const DnrInstance& dnr_instance : dnr_instances_) {
+        buf.writeUint16(dnr_instance.getDnrInstanceDataLength());
+        buf.writeUint16(dnr_instance.getServicePriority());
+        buf.writeUint8(dnr_instance.getAdnLength());
+        dnr_instance.packAdn(buf);
+        if (dnr_instance.isAdnOnlyMode()) {
+            return;
+        }
+        buf.writeUint8(dnr_instance.getAddrLength());
+        dnr_instance.packAddresses(buf);
+        dnr_instance.packSvcParams(buf);
+    }
+}
+
+void
+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()) {
+            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));
+        OptionBufferConstIter dnr_instance_end = begin + offset +
+                                                 dnr_instance.getDnrInstanceDataLength();
+        offset += DNR_INSTANCE_DATA_LENGTH_SIZE;
+        dnr_instance.setServicePriority(
+            readUint16(&(*(begin + offset)), SERVICE_PRIORITY_SIZE));
+        offset += 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");
+        }
+
+        // Let's try to extract ADN FQDN data.
+        InputBuffer name_buf(&(*adn_tuple.getData().begin()), adn_length);
+        try {
+            auto adn = dnr_instance.getAdn();
+            adn.reset(new isc::dns::Name(name_buf, true));
+        } catch (const Exception& ex) {
+            isc_throw(InvalidOptionDnrDomainName, "failed to parse "
+                                                  "fully qualified domain-name from wire format "
+                                                  "- " << ex.what());
+        }
+
+        offset += adn_tuple.getTotalLength();
+
+        if (begin + offset == 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 += ADDR_LENGTH_SIZE;
+        OptionBufferConstIter addr_begin = begin + offset;
+        OptionBufferConstIter addr_end = addr_begin + addr_length;
+        auto ip_addresses = dnr_instance.getIpAddresses();
+
+        while (addr_begin != addr_end) {
+            const uint8_t* ptr = &(*addr_begin);
+            ip_addresses.push_back(IOAddress(readUint32(ptr, std::distance(addr_begin, addr_end))));
+            addr_begin += V4ADDRESS_LEN;
+            offset += V4ADDRESS_LEN;
+        }
+
+        // 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();
+            svc_params.assign(begin + offset, dnr_instance_end);
+            dnr_instance.checkSvcParams();
+        }
+
+        addDnrInstance(dnr_instance);
+    }
+}
+
+std::string
+Option4Dnr::toText(int indent) const {
+    return Option::toText(indent);
+}
+
+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;
+    }
+    return (len);
+}
+
+void
+Option4Dnr::addDnrInstance(DnrInstance& dnr_instance) {
+    dnr_instances_.push_back(dnr_instance);
+}
+
+void
+DnrInstance::packAdn(util::OutputBuffer& buf) const {
+    if (!adn_) {
+        // This should not happen since Encrypted DNS options are designed
+        // to always include an authentication domain name.
+        isc_throw(InvalidOptionDnrDomainName, "Mandatory Authentication Domain Name fully "
+                                              "qualified domain-name is missing");
+    }
+    isc::dns::LabelSequence label_sequence(*adn_);
+    if (label_sequence.getDataLength() > 0) {
+        size_t data_length = 0;
+        const uint8_t* data = label_sequence.getData(&data_length);
+        buf.writeData(data, data_length);
+    }
+}
+
+void
+DnrInstance::packAddresses(util::OutputBuffer& buf) const {
+    AddressContainer::const_iterator address = ip_addresses_.begin();
+    while (address != ip_addresses_.end()) {
+        buf.writeUint32(address->toUint32());
+        ++address;
+    }
+}
+
+void
+DnrInstance::packSvcParams(util::OutputBuffer& buf) const {
+    if (svc_params_length_ > 0) {
+        buf.writeData(&(*svc_params_.begin()), svc_params_length_);
+    }
+}
+
+std::string
+DnrInstance::getAdnAsText() const {
+    if (adn_) {
+        return (adn_->toText());
+    }
+    return ("");
+}
+
+void
+DnrInstance::setAdn(const std::string& adn) {
+    std::string trimmed_adn = isc::util::str::trim(adn);
+    if (trimmed_adn.empty()) {
+        isc_throw(InvalidOptionDnrDomainName, "Mandatory Authentication Domain Name fully "
+                                              "qualified domain-name must not be empty");
+    }
+    try {
+        adn_.reset(new isc::dns::Name(trimmed_adn, true));
+    } catch (const Exception& ex) {
+        isc_throw(InvalidOptionDnrDomainName, "Failed to parse "
+                                              "fully qualified domain-name from string "
+                                              "- " << ex.what());
+    }
+    size_t adn_len = 0;
+    isc::dns::LabelSequence label_sequence(*adn_);
+    label_sequence.getData(&adn_len);
+    if (adn_len > std::numeric_limits<uint16_t>::max()) {
+        isc_throw(InvalidOptionDnrDomainName,
+                  "Given ADN FQDN length " << adn_len << " is bigger than uint_16 MAX");
+    }
+
+    adn_length_ = adn_len;
+}
+
+void
+DnrInstance::checkSvcParams(bool from_wire_data) {
+    std::string svc_params = isc::util::str::trim(svc_params_);
+    if (svc_params.empty()) {
+        isc_throw(InvalidOptionDnrSvcParams, "Provided Svc Params field is empty");
+    }
+    if (!from_wire_data) {
+        // If Service Params field was not parsed from on-wire data,
+        // but actually was provided with ctor, let's calculate svc_params_length_.
+        auto svc_params_len = svc_params.length();
+        if (svc_params_len > std::numeric_limits<uint16_t>::max()) {
+            isc_throw(OutOfRange, "Given Svc Params length " << svc_params_len
+                                                             << " is bigger than uint_16 MAX");
+        }
+        svc_params_length_ = svc_params_len;
+        // If Service Params field was not parsed from on-wire data,
+        // but actually was provided with ctor, let's replace it with trimmed value.
+        svc_params_ = svc_params;
+    }
+
+    // SvcParams are a whitespace-separated list, with each SvcParam
+    // consisting of a SvcParamKey=SvcParamValue pair or a standalone SvcParamKey.
+    // SvcParams in presentation format MAY appear in any order, but keys MUST NOT be repeated.
+
+    // Let's put all elements of a whitespace-separated list into a vector.
+    std::vector<std::string> tokens = isc::util::str::tokens(svc_params, " ");
+
+    // Set of keys used to check if a key is not repeated.
+    std::set<std::string> keys;
+    // String sanitizer is used to check keys syntax.
+    util::str::StringSanitizerPtr sanitizer;
+    // SvcParamKeys are lower-case alphanumeric strings. Key names
+    // contain 1-63 characters from the ranges "a"-"z", "0"-"9", and "-".
+    std::string regex = "[^a-z0-9-]";
+    sanitizer.reset(new util::str::StringSanitizer(regex, ""));
+    // The service parameters MUST NOT include
+    // "ipv4hint" or "ipv6hint" SvcParams as they are superseded by the
+    // included IP addresses.
+    std::set<std::string> forbidden_keys = {"ipv4hint", "ipv6hint"};
+
+    // Now let's check each SvcParamKey=SvcParamValue pair.
+    for (const std::string& token : tokens) {
+        std::vector<std::string> key_val = isc::util::str::tokens(token, "=");
+        if (key_val.size() > 2) {
+            isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - more than one "
+                                                 "equals sign found in SvcParamKey=SvcParamValue "
+                                                 "pair");
+        }
+
+        // SvcParam Key related checks come below.
+        std::string key = key_val[0];
+        if (forbidden_keys.find(key) != forbidden_keys.end()) {
+            isc_throw(InvalidOptionDnrSvcParams,
+                      "Wrong Svc Params syntax - key " << key << " must not be used");
+        }
+
+        auto insert_res = keys.insert(key);
+        if (!insert_res.second) {
+            isc_throw(InvalidOptionDnrSvcParams,
+                      "Wrong Svc Params syntax - key " << key << " was duplicated");
+        }
+
+        if (key.length() > 63) {
+            isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - key had more than 63 "
+                                                 "characters - "
+                                                     << key);
+        }
+
+        std::string sanitized_key = sanitizer->scrub(key);
+        if (sanitized_key.size() < key.size()) {
+            isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - invalid character "
+                                                 "used in key - "
+                                                     << key);
+        }
+
+        if (key_val.size() == 2) {
+            // tbd Check value syntax
+            std::string value = key_val[1];
+        }
+    }
+}
+
+void
+DnrInstance::checkFields() {
+    if (svc_params_.empty() && ip_addresses_.empty()) {
+        // ADN only mode, nothing more to do.
+        return;
+    }
+    if (!svc_params_.empty() && ip_addresses_.empty()) {
+        // 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.
+        isc_throw(OutOfRange,
+                  getLogPrefix()
+                      << " malformed: No IP address given. Since this is not "
+                         "ADN only mode, at least one valid IP address must be included");
+    }
+    if (!svc_params_.empty()) {
+        checkSvcParams(false);
+    }
+    adn_only_mode_ = false;
+    auto addr_len = ip_addresses_.size() * V6ADDRESS_LEN;
+    if (addr_len > std::numeric_limits<uint16_t>::max()) {
+        isc_throw(OutOfRange,
+                  "Given IPv6 addresses length " << addr_len << " is bigger than uint_16 MAX");
+    }
+    addr_length_ = addr_len;
+}
+
+std::string
+DnrInstance::getLogPrefix() const {
+    return (universe_ == Option::V4) ?
+               ("DHCPv4 Encrypted DNS Option (" + std::to_string(DHO_V4_DNR) + ")") :
+               ("DHCPv6 Encrypted DNS Option (" + std::to_string(D6O_V6_DNR) + ")");
+}
+
+}  // namespace dhcp
+}  // namespace isc
similarity index 58%
rename from src/lib/dhcp/option_dnr.h
rename to src/lib/dhcp/option4_dnr.h
index f343854e21f1e5f214097f606def17c2b48dcd4b..0848dce318d85a3385a29482cc600d07dcb3e65c 100644 (file)
@@ -4,8 +4,8 @@
 // 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/.
 
-#ifndef OPTION_DNR_H
-#define OPTION_DNR_H
+#ifndef OPTION4_DNR_H
+#define OPTION4_DNR_H
 
 #include <asiolink/io_address.h>
 #include <dhcp/option.h>
 namespace isc {
 namespace dhcp {
 
-/// @brief Exception thrown when invalid domain name is specified.
-class InvalidOptionDnrDomainName : public Exception {
+class DnrInstance {
 public:
-    InvalidOptionDnrDomainName(const char* file, size_t line, const char* what)
-        : isc::Exception(file, line, what) {
-    }
-};
+    DnrInstance(Option::Universe universe) : universe_(universe) {}
 
-/// @brief Exception thrown when Service parameters have wrong format.
-class InvalidOptionDnrSvcParams : public Exception {
-public:
-    InvalidOptionDnrSvcParams(const char* file, size_t line, const char* what)
-        : isc::Exception(file, line, what) {
-    }
-};
+    virtual ~DnrInstance() {}
 
-/// @brief Represents DHCPv6 Encrypted DNS %Option (code 144).
-///
-/// This option has been defined in the draft-ietf-add-dnr-15 (to be replaced
-/// with published RFC) and it has a following structure:
-/// - option-code = 144 (2 octets)
-/// - option-len (2 octets)
-/// - Service Priority (2 octets)
-/// - ADN Length (2 octets)
-/// - Authentication Domain Name (variable length)
-/// - Addr Length (2 octets)
-/// - IPv6 Address(es) (variable length)
-/// - Service Parameters (variable length).
-class OptionDnr6 : public Option {
-public:
-    /// @brief A container for (IPv6) addresses.
+    /// @brief A Type defined for container holding IP addresses.
     typedef std::vector<isc::asiolink::IOAddress> AddressContainer;
 
-    /// @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
-    /// on-wire data. It may throw an exception if the @c unpack method throws.
-    ///
-    /// @param begin Iterator pointing to the beginning of the buffer holding an
-    /// option.
-    /// @param end Iterator pointing to the end of the buffer holding an option.
-    OptionDnr6(OptionBufferConstIter begin, OptionBufferConstIter end);
-
-    OptionDnr6(const uint16_t service_priority, const std::string& adn, const AddressContainer& ipv6_addresses, const std::string& svc_params);
+    const AddressContainer& getIpAddresses() const {
+        return ip_addresses_;
+    }
 
-    OptionDnr6(const uint16_t service_priority, const std::string& adn);
+    const boost::shared_ptr<isc::dns::Name>& getAdn() const {
+        return 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;
+    uint16_t getDnrInstanceDataLength() const {
+        return (dnr_instance_data_length_);
+    }
 
     /// @brief Getter of the @c service_priority_.
     ///
-    /// @return The priority of this OPTION_V6_DNR instance compared to other instances.
+    /// @return The priority of this DNR instance compared to other instances.
     uint16_t getServicePriority() const {
         return (service_priority_);
     }
@@ -95,11 +54,11 @@ public:
     /// 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;
+    std::string getAdnAsText() const;
 
     /// @brief Getter of the @c addr_length_.
     ///
-    /// @return  Length of enclosed IPv6 addresses in octets.
+    /// @return  Length of enclosed IP addresses in octets.
     uint16_t getAddrLength() const {
         return (addr_length_);
     }
@@ -120,66 +79,51 @@ public:
     ///
     /// @return Address container with addresses.
     AddressContainer getAddresses() const {
-        return (ipv6_addresses_);
+        return (ip_addresses_);
     }
 
     /// @brief Getter of the @c svc_params_ field.
     ///
     /// @return Returns Service Parameters as a string.
-    std::string getSvcParams() const {
-        return (svc_params_);
+    const std::string& getSvcParams() const {
+        return svc_params_;
     }
 
-protected:
-    /// @brief The priority of this OPTION_V6_DNR instance compared to other instances.
-    uint16_t service_priority_;
-
-    /// @brief Length of the authentication-domain-name data 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<isc::dns::Name> adn_;
-
-    /// @brief Length of enclosed IPv6 addresses in octets.
-    uint16_t addr_length_ = 0;
-
-    /// @brief Vector container holding one or more IPv6 addresses.
-    ///
-    /// One or more IPv6 addresses to reach the encrypted DNS resolver.
-    /// An address can be link-local, ULA, or GUA.
-    AddressContainer ipv6_addresses_;
-
-    /// @brief Length of Service Parameters field in octets.
-    uint16_t svc_params_length_ = 0;
+    bool isAdnOnlyMode() const {
+        return adn_only_mode_;
+    }
 
-    /// @brief Flag stating whether ADN only mode is used or not.
+    /// @brief Sets Authentication domain name from given string.
     ///
-    /// "Addr Length", "ipv6-address(es)", and "Service Parameters (SvcParams)"
-    /// fields are not present if the ADN-only mode is used.
-    bool adn_only_mode_ = true;
-
-    /// @brief Service Parameters (SvcParams) (variable length).
+    /// Sets FQDN of the encrypted DNS resolver from given string.
+    /// It may throw an exception if parsing of the FQDN fails or if
+    /// provided FQDN length is bigger than uint16_t Max.
+    /// It also calculates and sets value of Addr length field.
     ///
-    /// Specifies a set of service parameters that are encoded
-    /// following the rules in Section 2.1 of [I-D.ietf-dnsop-svcb-https].
-    std::string svc_params_;
+    /// @param adn string representation of ADN FQDN
+    void setAdn(const std::string& adn);
 
-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);
-    };
+    void setDnrInstanceDataLength(uint16_t dnr_instance_data_length) {
+        dnr_instance_data_length_ = dnr_instance_data_length;
+    }
+    void setServicePriority(uint16_t service_priority) {
+        service_priority_ = service_priority;
+    }
+    void setAdnLength(uint16_t adn_length) {
+        adn_length_ = adn_length;
+    }
+    void setAddrLength(uint16_t addr_length) {
+        addr_length_ = addr_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;
+    }
+    void setSvcParams(const std::string& svc_params) {
+        svc_params_ = svc_params;
+    }
 
     /// @brief Writes the ADN FQDN in the wire format into a buffer.
     ///
@@ -189,13 +133,13 @@ private:
     /// @param [out] buf buffer where ADN FQDN will be written.
     void packAdn(isc::util::OutputBuffer& buf) const;
 
-    /// @brief Writes the IPv6 address(es) in the wire format into a buffer.
+    /// @brief Writes the IP address(es) in the wire format into a buffer.
     ///
-    /// The IPv6 address(es) (@c ipv6_addresses_) data is appended at the end
+    /// The IP address(es) (@c ip_addresses_) data is appended at the end
     /// of the buffer.
     ///
-    /// @param [out] buf buffer where IPv6 address(es) will be written.
-    void packAddresses(isc::util::OutputBuffer& buf) const;
+    /// @param [out] buf buffer where IP address(es) will be written.
+    virtual void packAddresses(isc::util::OutputBuffer& buf) const;
 
     /// @brief Writes the Service Parameters in the wire format into a buffer.
     ///
@@ -211,36 +155,106 @@ private:
     /// Section 2.1 of [I-D.ietf-dnsop-svcb-https].
     void checkSvcParams(bool from_wire_data = true);
 
-    /// @brief Sets Authentication domain name from given string.
+    void checkFields();
+
+protected:
+    Option::Universe universe_;
+
+    /// @brief Authentication domain name field of variable length.
     ///
-    /// Sets FQDN of the encrypted DNS resolver from given string.
-    /// It may throw an exception if parsing of the FQDN fails or if
-    /// provided FQDN length is bigger than uint16_t Max.
-    /// It also calculates and sets value of Addr length field.
+    /// 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<isc::dns::Name> adn_;
+
+    /// @brief Length of all following data in octets.
     ///
-    /// @param adn string representation of ADN FQDN
-    void setAdn(const std::string& adn);
-    void checkFields();
+    /// This field is only used for DHCPv4 Encrypted DNS %Option.
+    uint16_t dnr_instance_data_length_;
+
+    /// @brief The priority of this instance compared to other DNR instances.
+    uint16_t service_priority_;
+
+    /// @brief Length of the authentication-domain-name data in octets.
+    uint16_t adn_length_;
+
+    /// @brief Length of included IP addresses in octets.
+    uint16_t addr_length_ = 0;
+
+    /// @brief Vector container holding one or more IP addresses.
+    ///
+    /// One or more IP addresses to reach the encrypted DNS resolver.
+    /// In case of DHCPv4, both private and public IPv4 addresses can
+    /// be included in this field.
+    /// In case of DHCPv6, an address can be link-local, ULA, or GUA.
+    AddressContainer ip_addresses_;
+
+    /// @brief Length of Service Parameters field in octets.
+    uint16_t svc_params_length_ = 0;
+
+    /// @brief Flag stating whether ADN only mode is used or not.
+    ///
+    /// "Addr Length", "IP(v4/v6) Address(es)", and "Service Parameters (SvcParams)"
+    /// fields are not present if the ADN-only mode is used.
+    bool adn_only_mode_ = true;
+
+    /// @brief Service Parameters (SvcParams) (variable length).
+    ///
+    /// Specifies a set of service parameters that are encoded
+    /// following the rules in Section 2.1 of [I-D.ietf-dnsop-svcb-https].
+    std::string svc_params_;
+
+private:
+    std::string getLogPrefix() const;
 };
 
-class OptionDnr4 : public Option {
+class Option4Dnr : public Option {
 public:
-    OptionDnr4();
-    OptionDnr4(OptionBufferConstIter begin, OptionBufferConstIter end);
+    /// @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;
+
+    /// @brief Size in octets of ADN Length field.
+    static const uint8_t ADN_LENGTH_SIZE = 1;
+
+    /// @brief Size in octets of Addr Length field.
+    static const uint8_t ADDR_LENGTH_SIZE = 1;
+
+    typedef std::vector<DnrInstance> DnrInstanceContainer;
+
+    Option4Dnr(OptionBufferConstIter begin, OptionBufferConstIter end);
+
     virtual OptionPtr clone() const;
-    virtual void pack(util::OutputBuffer& buf, bool check) const;
+    virtual void pack(util::OutputBuffer& buf, bool check = true) const;
     virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
-    virtual std::string toText(int indent) const;
+    virtual std::string toText(int indent = 0) const;
     virtual uint16_t len() const;
-};
 
-/// A pointer to the @c OptionDnr6 object.
-typedef boost::shared_ptr<OptionDnr6> OptionDnr6Ptr;
+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.
-typedef boost::shared_ptr<OptionDnr4> OptionDnr4Ptr;
+typedef boost::shared_ptr<Option4Dnr> Option4DnrPtr;
 
 }  // namespace dhcp
 }  // namespace isc
 
-#endif  // OPTION_DNR_H
+#endif  // OPTION4_DNR_H
diff --git a/src/lib/dhcp/option6_dnr.cc b/src/lib/dhcp/option6_dnr.cc
new file mode 100644 (file)
index 0000000..8926685
--- /dev/null
@@ -0,0 +1,197 @@
+// Copyright (C) 2018-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 <config.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/opaque_data_tuple.h>
+#include <dhcp/option6_dnr.h>
+#include <dns/labelsequence.h>
+#include <set>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+Option6Dnr::Option6Dnr(OptionBufferConstIter begin, OptionBufferConstIter end)
+    : Option(V6, D6O_V6_DNR), DnrInstance(V6) {
+    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<Option6Dnr>());
+}
+
+void
+Option6Dnr::pack(util::OutputBuffer& buf, bool check) const {
+    packHeader(buf, check);
+
+    buf.writeUint16(service_priority_);
+    buf.writeUint16(adn_length_);
+    packAdn(buf);
+    if (adn_only_mode_) {
+        return;
+    }
+    buf.writeUint16(addr_length_);
+    packAddresses(buf);
+    packSvcParams(buf);
+}
+
+void
+Option6Dnr::packAddresses(util::OutputBuffer& buf) const {
+    for (const auto& address : ip_addresses_) {
+        if (!address.isV6()) {
+            isc_throw(isc::BadValue, address.toText() << " is not an IPv6 address");
+        }
+        buf.writeData(&address.toBytes()[0], V6ADDRESS_LEN);
+    }
+}
+
+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));
+    }
+    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;
+
+    // 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.
+    isc::util::InputBuffer name_buf(&(*adn_tuple.getData().begin()), adn_length_);
+    try {
+        adn_.reset(new isc::dns::Name(name_buf, true));
+    } catch (const Exception& ex) {
+        isc_throw(InvalidOptionDnrDomainName, "failed to parse "
+                                              "fully qualified domain-name from wire format "
+                                              "- " << ex.what());
+    }
+
+    begin += adn_tuple.getTotalLength();
+
+    if (begin == end) {
+        // ADN only mode, other fields are not included.
+        return;
+    }
+    adn_only_mode_ = false;
+    if (std::distance(begin, end) < ADDR_LENGTH_SIZE) {
+        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;
+    // 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");
+    }
+
+    // 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");
+    }
+
+    // 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));
+    }
+
+    // Let's unpack the ipv6-address(es).
+    auto addr_end = begin + addr_length_;
+    while (begin != addr_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()) << ", "
+           << "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_ << "'";
+        }
+    }
+
+    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);
+}
+
+}  // namespace dhcp
+}  // namespace isc
diff --git a/src/lib/dhcp/option6_dnr.h b/src/lib/dhcp/option6_dnr.h
new file mode 100644 (file)
index 0000000..9c3be4a
--- /dev/null
@@ -0,0 +1,100 @@
+// Copyright (C) 2018-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/.
+
+#ifndef OPTION6_DNR_H
+#define OPTION6_DNR_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+#include <dhcp/option4_dnr.h>
+#include <dns/name.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Exception thrown when invalid domain name is specified.
+class InvalidOptionDnrDomainName : public Exception {
+public:
+    InvalidOptionDnrDomainName(const char* file, size_t line, const char* what)
+        : isc::Exception(file, line, what) {
+    }
+};
+
+/// @brief Exception thrown when Service parameters have wrong format.
+class InvalidOptionDnrSvcParams : public Exception {
+public:
+    InvalidOptionDnrSvcParams(const char* file, size_t line, const char* what)
+        : isc::Exception(file, line, what) {
+    }
+};
+
+/// @brief Represents DHCPv6 Encrypted DNS %Option (code 144).
+///
+/// This option has been defined in the draft-ietf-add-dnr-15 (to be replaced
+/// with published RFC) and it has a following structure:
+/// - option-code = 144 (2 octets)
+/// - option-len (2 octets)
+/// - Service Priority (2 octets)
+/// - ADN Length (2 octets)
+/// - Authentication Domain Name (variable length)
+/// - Addr Length (2 octets)
+/// - IPv6 Address(es) (variable length)
+/// - Service Parameters (variable length).
+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
+    /// on-wire data. It may throw an exception if the @c unpack method throws.
+    ///
+    /// @param begin Iterator pointing to the beginning of the buffer holding an
+    /// option.
+    /// @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);
+
+    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;
+
+    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.
+typedef boost::shared_ptr<Option6Dnr> Option6DnrPtr;
+
+}  // namespace dhcp
+}  // namespace isc
+
+#endif  // OPTION6_DNR_H
index 835a09e9cb5c1390384d27531eb36071e25d5510..847e4b0d4a76670b1a690745983d3899da9baca0 100644 (file)
 #include <dhcp/dhcp6.h>
 #include <dhcp/option4_addrlst.h>
 #include <dhcp/option4_client_fqdn.h>
+#include <dhcp/option4_dnr.h>
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_client_fqdn.h>
+#include <dhcp/option6_dnr.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_iaprefix.h>
@@ -19,7 +21,6 @@
 #include <dhcp/option6_status_code.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_definition.h>
-#include <dhcp/option_dnr.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_opaque_data_tuples.h>
@@ -868,7 +869,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 Option6Dnr(begin, end)));
 
         default:
             break;
@@ -899,7 +900,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 Option4Dnr(begin, end)));
 
         default:
             break;
diff --git a/src/lib/dhcp/option_dnr.cc b/src/lib/dhcp/option_dnr.cc
deleted file mode 100644 (file)
index 6a1d6a9..0000000
+++ /dev/null
@@ -1,392 +0,0 @@
-// Copyright (C) 2018-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 <config.h>
-
-#include <asiolink/io_address.h>
-#include <dhcp/dhcp4.h>
-#include <dhcp/dhcp6.h>
-#include <dhcp/opaque_data_tuple.h>
-#include <dhcp/option_dnr.h>
-#include <dns/labelsequence.h>
-#include <util/strutil.h>
-#include <set>
-
-using namespace isc::asiolink;
-
-namespace isc {
-namespace dhcp {
-
-OptionDnr6::OptionDnr6(OptionBufferConstIter begin, OptionBufferConstIter end)
-    : Option(V6, D6O_V6_DNR) {
-    unpack(begin, end);
-}
-
-OptionDnr6::OptionDnr6(const uint16_t service_priority,
-                       const std::string& adn,
-                       const OptionDnr6::AddressContainer& ipv6_addresses,
-                       const std::string& svc_params)
-    : Option(V6, D6O_V6_DNR), service_priority_(service_priority),
-      ipv6_addresses_(ipv6_addresses), svc_params_(svc_params) {
-    setAdn(adn);
-    checkFields();
-}
-
-OptionDnr6::OptionDnr6(const uint16_t service_priority, const std::string& adn)
-    : Option(V6, D6O_V6_DNR), service_priority_(service_priority) {
-    setAdn(adn);
-}
-
-OptionPtr
-OptionDnr6::clone() const {
-    return (cloneInternal<OptionDnr6>());
-}
-
-void
-OptionDnr6::pack(util::OutputBuffer& buf, bool check) const {
-    packHeader(buf, check);
-
-    buf.writeUint16(service_priority_);
-    buf.writeUint16(adn_length_);
-    packAdn(buf);
-    if (adn_only_mode_) {
-        return;
-    }
-    buf.writeUint16(addr_length_);
-    packAddresses(buf);
-    packSvcParams(buf);
-}
-
-void
-OptionDnr6::packAdn(util::OutputBuffer& buf) const {
-    if (!adn_) {
-        // This should not happen since Encrypted DNS options are designed
-        // to always include an authentication domain name.
-        return;
-    }
-    isc::dns::LabelSequence label_sequence(*adn_);
-    if (label_sequence.getDataLength() > 0) {
-        size_t data_length = 0;
-        const uint8_t* data = label_sequence.getData(&data_length);
-        buf.writeData(data, data_length);
-    }
-}
-
-void
-OptionDnr6::packAddresses(util::OutputBuffer& buf) const {
-    for (const auto& address : ipv6_addresses_) {
-        if (!address.isV6()) {
-            isc_throw(isc::BadValue, address.toText() << " is not an IPv6 address");
-        }
-        buf.writeData(&address.toBytes()[0], V6ADDRESS_LEN);
-    }
-}
-
-void
-OptionDnr6::packSvcParams(util::OutputBuffer& buf) const {
-    if (svc_params_length_ > 0) {
-        buf.writeData(&(*svc_params_.begin()), svc_params_length_);
-    }
-}
-
-void
-OptionDnr6::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));
-    }
-    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;
-
-    // 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.
-    isc::util::InputBuffer name_buf(&(*adn_tuple.getData().begin()), adn_length_);
-    try {
-        adn_.reset(new isc::dns::Name(name_buf, true));
-    } catch (const Exception& ex) {
-        isc_throw(InvalidOptionDnrDomainName, "failed to parse "
-                                              "fully qualified domain-name from wire format "
-                                              "- " << ex.what());
-    }
-
-    begin += adn_tuple.getTotalLength();
-
-    if (begin == end) {
-        // ADN only mode, other fields are not included.
-        return;
-    }
-    adn_only_mode_ = false;
-    if (std::distance(begin, end) < ADDR_LENGTH_SIZE) {
-        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;
-    // 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");
-    }
-
-    // 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");
-    }
-
-    // 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));
-    }
-
-    // Let's unpack the ipv6-address(es).
-    auto addr_end = begin + addr_length_;
-    while (begin != addr_end) {
-        ipv6_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
-OptionDnr6::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='" << getAdn() << "'";
-    if (!adn_only_mode_) {
-        stream << ", addr_length=" << addr_length_
-               << ", address(es):";
-        for (const auto& address : ipv6_addresses_) {
-            stream << " " << address.toText();
-        }
-
-        if (svc_params_length_ > 0) {
-            stream << ", svc_params='" << svc_params_ << "'";
-        }
-    }
-
-    return (stream.str());
-}
-
-uint16_t
-OptionDnr6::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);
-}
-
-std::string
-OptionDnr6::getAdn() const {
-    if (adn_) {
-        return (adn_->toText());
-    }
-    return ("");
-}
-
-void
-OptionDnr6::checkSvcParams(bool from_wire_data) {
-    std::string svc_params = isc::util::str::trim(svc_params_);
-    if (svc_params.empty()) {
-        isc_throw(InvalidOptionDnrSvcParams, "Provided Svc Params field is empty");
-    }
-    if (!from_wire_data) {
-        // If Service Params field was not parsed from on-wire data,
-        // but actually was provided with ctor, let's calculate svc_params_length_.
-        auto svc_params_len = svc_params.length();
-        if (svc_params_len > std::numeric_limits<uint16_t>::max()) {
-            isc_throw(OutOfRange, "Given Svc Params length "
-                                  << svc_params_len << " is bigger than uint_16 MAX");
-        }
-        svc_params_length_ = svc_params_len;
-        // If Service Params field was not parsed from on-wire data,
-        // but actually was provided with ctor, let's replace it with trimmed value.
-        svc_params_ = svc_params;
-    }
-
-    // SvcParams are a whitespace-separated list, with each SvcParam
-    // consisting of a SvcParamKey=SvcParamValue pair or a standalone SvcParamKey.
-    // SvcParams in presentation format MAY appear in any order, but keys MUST NOT be repeated.
-
-    // Let's put all elements of a whitespace-separated list into a vector.
-    std::vector<std::string> tokens = isc::util::str::tokens(svc_params, " ");
-
-    // Set of keys used to check if a key is not repeated.
-    std::set<std::string> keys;
-    // String sanitizer is used to check keys syntax.
-    util::str::StringSanitizerPtr sanitizer;
-    // SvcParamKeys are lower-case alphanumeric strings. Key names
-    // contain 1-63 characters from the ranges "a"-"z", "0"-"9", and "-".
-    std::string regex = "[^a-z0-9-]";
-    sanitizer.reset(new util::str::StringSanitizer(regex, ""));
-    // The service parameters MUST NOT include
-    // "ipv4hint" or "ipv6hint" SvcParams as they are superseded by the
-    // included IP addresses.
-    std::set<std::string> forbidden_keys = {"ipv4hint", "ipv6hint"};
-
-    // Now let's check each SvcParamKey=SvcParamValue pair.
-    for (const std::string& token : tokens) {
-        std::vector<std::string> key_val = isc::util::str::tokens(token, "=");
-        if (key_val.size() > 2) {
-            isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - more than one "
-                                                 "equals sign found in SvcParamKey=SvcParamValue "
-                                                 "pair");
-        }
-
-        // SvcParam Key related checks come below.
-        std::string key = key_val[0];
-        if (forbidden_keys.find(key) != forbidden_keys.end()) {
-            isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - key "
-                                                 << key << " must not be used");
-        }
-
-        auto insert_res = keys.insert(key);
-        if (!insert_res.second) {
-            isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - key "
-                                                 << key << " was duplicated");
-        }
-
-        if (key.length() > 63) {
-            isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - key had more than 63 "
-                                                 "characters - " << key);
-        }
-
-        std::string sanitized_key = sanitizer->scrub(key);
-        if (sanitized_key.size() < key.size()) {
-            isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - invalid character "
-                                                 "used in key - " << key);
-        }
-
-        if (key_val.size() == 2) {
-            // tbd Check value syntax
-            std::string value = key_val[1];
-        }
-    }
-
-}
-
-void
-OptionDnr6::setAdn(const std::string& adn) {
-    std::string trimmed_adn = isc::util::str::trim(adn);
-    if (trimmed_adn.empty()) {
-        isc_throw(InvalidOptionDnrDomainName, "Mandatory Authentication Domain Name fully "
-                                              "qualified domain-name must not be empty");
-    }
-    try {
-        adn_.reset(new isc::dns::Name(trimmed_adn, true));
-    } catch (const Exception& ex) {
-        isc_throw(InvalidOptionDnrDomainName, "Failed to parse "
-                                              "fully qualified domain-name from string "
-                                              "- " << ex.what());
-    }
-    size_t adn_len = 0;
-    isc::dns::LabelSequence label_sequence(*adn_);
-    label_sequence.getData(&adn_len);
-    if (adn_len > std::numeric_limits<uint16_t>::max()) {
-        isc_throw(InvalidOptionDnrDomainName, "Given ADN FQDN length "
-                                              << adn_len << " is bigger than uint_16 MAX");
-    }
-
-    adn_length_ = adn_len;
-}
-
-void
-OptionDnr6::checkFields() {
-    if (svc_params_.empty() && ipv6_addresses_.empty()) {
-        // ADN only mode, nothing more to do.
-        return;
-    }
-    if(!svc_params_.empty() && ipv6_addresses_.empty()) {
-        // 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.
-        isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option ("
-                               << type_ << ")"
-                               << " malformed: No IPv6 address given. Since this is not "
-                                  "ADN only mode, at least one valid IP address must be included");
-
-    }
-    if(!svc_params_.empty()) {
-        checkSvcParams(false);
-    }
-    adn_only_mode_ = false;
-    auto addr_len = ipv6_addresses_.size() * V6ADDRESS_LEN;
-    if (addr_len > std::numeric_limits<uint16_t>::max()) {
-        isc_throw(OutOfRange, "Given IPv6 addresses length "
-                              << addr_len << " is bigger than uint_16 MAX");
-    }
-    addr_length_ = addr_len;
-}
-
-OptionDnr4::OptionDnr4() : Option(V4, DHO_V4_DNR) {
-}
-
-OptionDnr4::OptionDnr4(OptionBufferConstIter begin, OptionBufferConstIter end)
-    : Option(V4, DHO_V4_DNR) {
-    unpack(begin, end);
-}
-
-OptionPtr
-OptionDnr4::clone() const {
-    return Option::clone();
-}
-
-void
-OptionDnr4::pack(util::OutputBuffer& buf, bool check) const {
-    Option::pack(buf, check);
-}
-
-void
-OptionDnr4::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
-    Option::unpack(begin, end);
-}
-
-std::string
-OptionDnr4::toText(int indent) const {
-    return Option::toText(indent);
-}
-
-uint16_t
-OptionDnr4::len() const {
-    return Option::len();
-}
-
-}  // namespace dhcp
-}  // namespace isc
index 60d70bf9c9619e7a533124d8f6fb73ff6fbae1d1..f16ecf3db3cd9ce1a01f1d6a755b6a5398ae5ef6 100644 (file)
@@ -9,7 +9,7 @@
 #include <asiolink/io_address.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/opaque_data_tuple.h>
-#include <dhcp/option_dnr.h>
+#include <dhcp/option6_dnr.h>
 
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
@@ -36,8 +36,8 @@ TEST(OptionDnr6Test, onWireCtorAdnOnlyMode) {
 
     OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
     // Create option instance. Check that constructor doesn't throw.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_NO_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())));
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_NO_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())));
     ASSERT_TRUE(option);
 
     // Check if member variables were correctly set by ctor.
@@ -47,7 +47,7 @@ TEST(OptionDnr6Test, onWireCtorAdnOnlyMode) {
     // 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("myhost.example.com.", option->getAdnAsText());
 
     // This is ADN only mode, so Addr Length and SvcParams Length
     // are both expected to be zero.
@@ -75,8 +75,8 @@ TEST(OptionDnr6Test, onWireCtorDataTruncated) {
 
     OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
     // Create option instance. Check that constructor throws OutOfRange exception.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OutOfRange);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange);
     ASSERT_FALSE(option);
 }
 
@@ -92,8 +92,8 @@ TEST(OptionDnr6Test, onWireCtorOnlyWhitespaceFqdn) {
 
     OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
     // Create option instance. Check that constructor throws InvalidOptionDnrDomainName exception.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), InvalidOptionDnrDomainName);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrDomainName);
     ASSERT_FALSE(option);
 }
 
@@ -110,8 +110,8 @@ TEST(OptionDnr6Test, onWireCtorNoAdnFqdn) {
     // Create option instance. Encrypted DNS options are designed to ALWAYS include
     // an authentication domain name, so check that constructor throws
     // InvalidOptionDnrDomainName exception.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), InvalidOptionDnrDomainName);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrDomainName);
     ASSERT_FALSE(option);
 }
 
@@ -127,8 +127,8 @@ TEST(OptionDnr6Test, onWireCtorTruncatedFqdn) {
 
     OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
     // Create option instance. Check that constructor throws OpaqueDataTupleError exception.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OpaqueDataTupleError);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OpaqueDataTupleError);
     ASSERT_FALSE(option);
 }
 
@@ -147,8 +147,8 @@ TEST(OptionDnr6Test, onWireCtorAddrLenTruncated) {
 
     OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
     // Create option instance. Check that constructor throws OutOfRange exception.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OutOfRange);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange);
     ASSERT_FALSE(option);
 }
 
@@ -169,8 +169,8 @@ TEST(OptionDnr6Test, onWireCtorAddrLenZero) {
     // 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.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OutOfRange);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange);
     ASSERT_FALSE(option);
 }
 
@@ -189,8 +189,8 @@ TEST(OptionDnr6Test, onWireCtorAddrLenNot16Modulo) {
 
     OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
     // Create option instance. Check that constructor throws OutOfRange exception.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OutOfRange);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange);
     ASSERT_FALSE(option);
 }
 
@@ -215,8 +215,8 @@ TEST(OptionDnr6Test, onWireCtorValidIpV6Addresses) {
 
     OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
     // Create option instance. Check that constructor doesn't throw.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_NO_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())));
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_NO_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())));
     ASSERT_TRUE(option);
 
     // Check if member variables were correctly set by ctor.
@@ -226,9 +226,9 @@ TEST(OptionDnr6Test, onWireCtorValidIpV6Addresses) {
     // 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("myhost.example.com.", option->getAdnAsText());
     EXPECT_EQ(48, option->getAddrLength());
-    const OptionDnr6::AddressContainer& addresses = option->getAddresses();
+    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());
@@ -269,8 +269,8 @@ TEST(OptionDnr6Test, onWireCtorTruncatedIpV6Addresses) {
 
     OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
     // Create option instance. Check that constructor throws OutOfRange exception.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OutOfRange);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange);
     ASSERT_FALSE(option);
 }
 
@@ -292,8 +292,8 @@ TEST(OptionDnr6Test, onWireCtorSvcParamsIncluded) {
 
     OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
     // Create option instance. Check that constructor doesn't throw.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_NO_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())));
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_NO_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())));
     ASSERT_TRUE(option);
 
     // Check if member variables were correctly set by ctor.
@@ -303,9 +303,9 @@ TEST(OptionDnr6Test, onWireCtorSvcParamsIncluded) {
     // 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("myhost.example.com.", option->getAdnAsText());
     EXPECT_EQ(16, option->getAddrLength());
-    const OptionDnr6::AddressContainer& addresses = option->getAddresses();
+    const Option6Dnr::AddressContainer& addresses = option->getAddresses();
     EXPECT_EQ(1, addresses.size());
     EXPECT_EQ("2001:db8:1::dead:beef", addresses[0].toText());
     EXPECT_EQ(3, option->getSvcParamsLength());
@@ -344,8 +344,8 @@ TEST(OptionDnr6Test, onWireCtorSvcParamsInvalidCharKey) {
 
     OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
     // Create option instance. Check that constructor throws InvalidOptionDnrSvcParams exception.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), InvalidOptionDnrSvcParams);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrSvcParams);
     ASSERT_FALSE(option);
 }
 
@@ -357,8 +357,8 @@ TEST(OptionDnr6Test, adnOnlyModeCtor) {
     const std::string adn = "myhost.example.com.";
 
     // Create option instance. Check that constructor doesn't throw.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_NO_THROW(option.reset(new OptionDnr6(service_priority, adn)));
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_NO_THROW(option.reset(new Option6Dnr(service_priority, adn)));
     ASSERT_TRUE(option);
 
     // Check if member variables were correctly set by ctor.
@@ -366,7 +366,7 @@ TEST(OptionDnr6Test, adnOnlyModeCtor) {
     EXPECT_EQ(D6O_V6_DNR, option->getType());
     EXPECT_EQ(service_priority, option->getServicePriority());
     EXPECT_EQ(20, option->getAdnLength());
-    EXPECT_EQ(adn, option->getAdn());
+    EXPECT_EQ(adn, option->getAdnAsText());
 
     // This is ADN only mode, so Addr Length and SvcParams Length
     // are both expected to be zero.
@@ -392,8 +392,8 @@ TEST(OptionDnr6Test, adnOnlyModeCtorNoFqdn) {
     const std::string adn; // invalid empty ADN
 
     // Create option instance. Check that constructor throws.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn)), InvalidOptionDnrDomainName);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn)), InvalidOptionDnrDomainName);
     ASSERT_FALSE(option);
 }
 
@@ -404,13 +404,13 @@ TEST(OptionDnr6Test, allFieldsCtor) {
     // Prepare example parameters
     const uint16_t service_priority = 9;
     const std::string adn = "myhost.example.com.";
-    OptionDnr6::AddressContainer addresses;
+    Option6Dnr::AddressContainer addresses;
     addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca"));
     const std::string svc_params = "alpn";
 
     // Create option instance. Check that constructor doesn't throw.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_NO_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)));
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_NO_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)));
     ASSERT_TRUE(option);
 
     // Check if member variables were correctly set by ctor.
@@ -418,7 +418,7 @@ TEST(OptionDnr6Test, allFieldsCtor) {
     EXPECT_EQ(D6O_V6_DNR, option->getType());
     EXPECT_EQ(service_priority, option->getServicePriority());
     EXPECT_EQ(20, option->getAdnLength());
-    EXPECT_EQ(adn, option->getAdn());
+    EXPECT_EQ(adn, option->getAdnAsText());
     EXPECT_EQ(16, option->getAddrLength());
     EXPECT_EQ(4, option->getSvcParamsLength());
     EXPECT_EQ(svc_params, option->getSvcParams());
@@ -443,12 +443,12 @@ TEST(OptionDnr6Test, allFieldsCtorNoIpAddress) {
     // Prepare example parameters
     const uint16_t service_priority = 9;
     const std::string adn = "myhost.example.com.";
-    const OptionDnr6::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<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), OutOfRange);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), OutOfRange);
     ASSERT_FALSE(option);
 }
 
@@ -459,13 +459,13 @@ TEST(OptionDnr6Test, svcParamsTwoEqualSignsPerParam) {
     // Prepare example parameters.
     const uint16_t service_priority = 9;
     const std::string adn = "myhost.example.com.";
-    OptionDnr6::AddressContainer addresses;
+    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
 
     // Create option instance. Check that constructor throws.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
     ASSERT_FALSE(option);
 }
 
@@ -476,13 +476,13 @@ TEST(OptionDnr6Test, svcParamsForbiddenKey) {
     // Prepare example parameters.
     const uint16_t service_priority = 9;
     const std::string adn = "myhost.example.com.";
-    OptionDnr6::AddressContainer addresses;
+    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
 
     // Create option instance. Check that constructor throws.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
     ASSERT_FALSE(option);
 }
 
@@ -493,13 +493,13 @@ TEST(OptionDnr6Test, svcParamsKeyRepeated) {
     // Prepare example parameters.
     const uint16_t service_priority = 9;
     const std::string adn = "myhost.example.com.";
-    OptionDnr6::AddressContainer addresses;
+    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
 
     // Create option instance. Check that constructor throws.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
     ASSERT_FALSE(option);
 }
 
@@ -510,15 +510,15 @@ TEST(OptionDnr6Test, svcParamsKeyTooLong) {
     // Prepare example parameters.
     const uint16_t service_priority = 9;
     const std::string adn = "myhost.example.com.";
-    OptionDnr6::AddressContainer addresses;
+    Option6Dnr::AddressContainer addresses;
     addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca"));
     const std::string svc_params = "thisisveryveryveryvery"
                                    "veryveryveryveryveryvery"
                                    "veryveryveryveryvlongkey"; // svc param key longer than 63
 
     // Create option instance. Check that constructor throws.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
     ASSERT_FALSE(option);
 }
 
@@ -529,13 +529,13 @@ TEST(OptionDnr6Test, svcParamsKeyHasInvalidChar) {
     // Prepare example parameters.
     const uint16_t service_priority = 9;
     const std::string adn = "myhost.example.com.";
-    OptionDnr6::AddressContainer addresses;
+    Option6Dnr::AddressContainer addresses;
     addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca"));
     const std::string svc_params = "alpn=h2 NOT_ALLOWED_CHARS_KEY=123"; // svc param key has forbidden chars
 
     // Create option instance. Check that constructor throws.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
     ASSERT_FALSE(option);
 }
 
@@ -545,13 +545,13 @@ TEST(OptionDnr6Test, toText) {
     // Prepare example parameters.
     const uint16_t service_priority = 9;
     const std::string adn = "myhost.example.com.";
-    OptionDnr6::AddressContainer addresses;
+    Option6Dnr::AddressContainer addresses;
     addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca"));
     const std::string svc_params = "alpn";
 
     // Create option instance. Check that constructor doesn't throw.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_NO_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)));
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_NO_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)));
     ASSERT_TRUE(option);
 
     const int indent = 4;
@@ -569,8 +569,8 @@ TEST(OptionDnr6Test, packAdnOnlyMode) {
     const std::string adn = "myhost.example.com.";
 
     // Create option instance. Check that constructor doesn't throw.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_NO_THROW(option.reset(new OptionDnr6(service_priority, adn)));
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_NO_THROW(option.reset(new Option6Dnr(service_priority, adn)));
     ASSERT_TRUE(option);
 
     // Prepare on-wire format of the option.
@@ -602,14 +602,14 @@ TEST(OptionDnr6Test, pack) {
     // Prepare example parameters.
     const uint16_t service_priority = 9;
     const std::string adn = "myhost.example.com.";
-    OptionDnr6::AddressContainer addresses;
+    Option6Dnr::AddressContainer addresses;
     addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::dead:beef"));
     addresses.push_back(isc::asiolink::IOAddress("ff02::face:b00c"));
     const std::string svc_params = "alpn";
 
     // Create option instance. Check that constructor doesn't throw.
-    scoped_ptr<OptionDnr6> option;
-    EXPECT_NO_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)));
+    scoped_ptr<Option6Dnr> option;
+    EXPECT_NO_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)));
     ASSERT_TRUE(option);
 
     // Prepare on-wire format of the option.