]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2536] Implementing DNRv4 Option with TDD
authorPiotrek Zadroga <piotrek@isc.org>
Wed, 19 Apr 2023 12:32:57 +0000 (14:32 +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/Makefile.am
src/lib/dhcp/tests/option4_dnr_unittest.cc [new file with mode: 0644]
src/lib/dhcp/tests/option6_dnr_unittest.cc [moved from src/lib/dhcp/tests/option_dnr_unittest.cc with 96% similarity]

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