]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2536] Implementing DNRv4 Option with TDD
authorPiotrek Zadroga <piotrek@isc.org>
Wed, 19 Apr 2023 21:26:30 +0000 (23:26 +0200)
committerPiotrek Zadroga <piotrek@isc.org>
Thu, 4 May 2023 21:17:18 +0000 (23:17 +0200)
src/lib/dhcp/option4_dnr.cc
src/lib/dhcp/option4_dnr.h
src/lib/dhcp/option6_dnr.cc
src/lib/dhcp/option6_dnr.h
src/lib/dhcp/tests/option4_dnr_unittest.cc
src/lib/dhcp/tests/option6_dnr_unittest.cc

index ce59be639e4027d6d511ac8a5b43cd332895bb19..c1c9c0dee86ac5dd8cd417c6ce1bf64c45c3852b 100644 (file)
@@ -12,6 +12,7 @@
 #include <dhcp/opaque_data_tuple.h>
 #include <dhcp/option4_dnr.h>
 #include <dhcp/option6_dnr.h>
+#include <dhcp/option_data_types.h>
 #include <dns/labelsequence.h>
 #include <util/strutil.h>
 #include <set>
@@ -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
index 9c42d43b09b84442e0b2c97b787ad437a307dec9..d2bcc2e8794c0697fdb7ebe88ad655e2a8ed3a50 100644 (file)
@@ -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.
index 3e20d6bcdd638d2e6cf40bbeacb898a5e731553d..ca48bd761e05096e108b67d4c490658b107fee46 100644 (file)
@@ -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
index 75ceaf143b6357ebd0d76b5f079aee4ccff9bbd9..5a55cb36c52897130090f2e76de642400a8f1b4a 100644 (file)
@@ -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.
index 20e644f629407beff89ea5f0f77ef33703af0cbf..93c5650b8df83f9dc4d32c4018a07e8493090840 100644 (file)
@@ -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<Option4Dnr> 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
index c94bfdf0465091a977ff373b8c4f18493490e1e8..d13eaebfd8141408bdc4517408f6e4c7e6dd1790 100644 (file)
@@ -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<Option6Dnr> 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<Option6Dnr> 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<Option6Dnr> 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<Option6Dnr> 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<Option6Dnr> 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<Option6Dnr> 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<Option6Dnr> 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'";