]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2536] Implementing DNRv6 Option with TDD
authorPiotrek Zadroga <piotrek@isc.org>
Thu, 6 Apr 2023 13:12:04 +0000 (15:12 +0200)
committerPiotrek Zadroga <piotrek@isc.org>
Thu, 4 May 2023 21:17:18 +0000 (23:17 +0200)
src/lib/dhcp/option_definition.cc
src/lib/dhcp/option_dnr.cc
src/lib/dhcp/option_dnr.h
src/lib/dhcp/std_option_defs.h
src/lib/dhcp/tests/Makefile.am
src/lib/dhcp/tests/option_dnr_unittest.cc [new file with mode: 0644]

index 0b20cf7d64cf80c2219bb0012be52ea26d8d6f24..835a09e9cb5c1390384d27531eb36071e25d5510 100644 (file)
@@ -868,7 +868,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 OptionDnr6(begin, end)));
 
         default:
             break;
@@ -899,7 +899,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 OptionDnr4(begin, end)));
 
         default:
             break;
index ca9af55151376052034e544a1cb712937419e5ec..427bf0feef687a6d021533a319d4a704c758b285 100644 (file)
 namespace isc {
 namespace dhcp {
 
-OptionDNR6::OptionDNR6(OptionBufferConstIter begin, OptionBufferConstIter end)
-    : Option(V6, D6O_V6_DNR) {
+OptionDnr6::OptionDnr6(OptionBufferConstIter begin, OptionBufferConstIter end)
+    : Option(V6, D6O_V6_DNR), addr_length_(0) {
     unpack(begin, end);
 }
 
 OptionPtr
-OptionDNR6::clone() const {
+OptionDnr6::clone() const {
     return Option::clone();
 }
 
 void
-OptionDNR6::pack(util::OutputBuffer& buf, bool check) const {
+OptionDnr6::pack(util::OutputBuffer& buf, bool check) const {
     packHeader(buf, check);
 
     buf.writeUint16(service_priority_);
@@ -33,7 +33,7 @@ OptionDNR6::pack(util::OutputBuffer& buf, bool check) const {
 }
 
 void
-OptionDNR6::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
+OptionDnr6::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
     if (std::distance(begin, end) < getMinimalLength()) {
         isc_throw(OutOfRange, "parsed DHCPv6 Encrypted DNS Option ("
                               << D6O_V6_DNR << ") data truncated to"
@@ -50,13 +50,13 @@ OptionDNR6::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
     adn_length_ = adn_tuple.getLength();
     if (adn_length_ > 0) {
         // Let's try to extract ADN FQDN data
-        isc::util::InputBuffer name_buf(&adn_tuple.getData(),
-                                        adn_length_);
+        isc::util::InputBuffer name_buf(&adn_tuple.getData()[0], adn_length_);
         try {
             adn_.reset(new isc::dns::Name(name_buf, true));
-        } catch (const Exception&) {
-            isc_throw(InvalidOptionDNR6DomainName, "failed to parse "
-                                                    "fully qualified domain-name from wire format");
+        } catch (const Exception& ex) {
+            isc_throw(InvalidOptionDnr6DomainName, "failed to parse "
+                                                   "fully qualified domain-name from wire format "
+                                                   "- " << ex.what());
         }
     }
     begin += adn_tuple.getTotalLength();
@@ -74,45 +74,53 @@ OptionDNR6::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
 }
 
 std::string
-OptionDNR6::toText(int indent) const {
+OptionDnr6::toText(int indent) const {
     return Option::toText(indent);
 }
 
 uint16_t
-OptionDNR6::len() const {
+OptionDnr6::len() const {
     return Option::len();
 }
 
-OptionDNR4::OptionDNR4() : Option(V4, DHO_V4_DNR) {
+std::string
+OptionDnr6::getAdn() const {
+    if (adn_) {
+        return adn_->toText();
+    }
+    return "";
+}
+
+OptionDnr4::OptionDnr4() : Option(V4, DHO_V4_DNR) {
 }
 
-OptionDNR4::OptionDNR4(OptionBufferConstIter begin, OptionBufferConstIter end)
+OptionDnr4::OptionDnr4(OptionBufferConstIter begin, OptionBufferConstIter end)
     : Option(V4, DHO_V4_DNR) {
     unpack(begin, end);
 }
 
 OptionPtr
-OptionDNR4::clone() const {
+OptionDnr4::clone() const {
     return Option::clone();
 }
 
 void
-OptionDNR4::pack(util::OutputBuffer& buf, bool check) const {
+OptionDnr4::pack(util::OutputBuffer& buf, bool check) const {
     Option::pack(buf, check);
 }
 
 void
-OptionDNR4::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
+OptionDnr4::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
     Option::unpack(begin, end);
 }
 
 std::string
-OptionDNR4::toText(int indent) const {
+OptionDnr4::toText(int indent) const {
     return Option::toText(indent);
 }
 
 uint16_t
-OptionDNR4::len() const {
+OptionDnr4::len() const {
     return Option::len();
 }
 
index f2c6b2a1aae79b2faaa44e3a4603306e344941aa..45f3807c4269c90781e6222b159552cfd4616d65 100644 (file)
@@ -14,16 +14,15 @@ namespace isc {
 namespace dhcp {
 
 /// @brief Exception thrown when invalid domain name is specified.
-class InvalidOptionDNR6DomainName : public Exception {
+class InvalidOptionDnr6DomainName : public Exception {
 public:
-    InvalidOptionDNR6DomainName(const char* file, size_t line,
-                                 const char* what) :
-          isc::Exception(file, line, what) {}
+    InvalidOptionDnr6DomainName(const char* file, size_t line, const char* what)
+        : isc::Exception(file, line, what) {
+    }
 };
 
-class OptionDNR6 : public Option {
+class OptionDnr6 : public Option {
 public:
-
     /// @brief Size in octets of Service Priority field
     static const uint8_t SERVICE_PRIORITY_SIZE = 2;
 
@@ -33,23 +32,49 @@ public:
     /// @brief Size in octets of Addr Length field
     static const uint8_t ADDR_LENGTH_SIZE = 2;
 
-    OptionDNR6() : Option(V6, D6O_V6_DNR) {
-          };
-    OptionDNR6(OptionBufferConstIter begin, OptionBufferConstIter end);
+    OptionDnr6(OptionBufferConstIter begin, OptionBufferConstIter end);
     virtual OptionPtr clone() const;
     virtual void pack(util::OutputBuffer& buf, bool check) const;
     virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
     virtual std::string toText(int indent) const;
     virtual uint16_t len() const;
 
+    /// @brief Getter of the @c service_priority_
+    ///
+    /// @return The priority of this OPTION_V6_DNR instance compared to other instances.
+    uint16_t getServicePriority() const {
+        return service_priority_;
+    }
+
+    /// @brief Getter of the @c adn_length_
+    ///
+    /// @return Length of the authentication-domain-name data in octets.
+    uint16_t getAdnLength() const {
+        return adn_length_;
+    }
+
+    /// @brief Returns the Authentication domain name in the text format.
+    ///
+    /// 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;
+
+    /// @brief Getter of the @c addr_length_
+    ///
+    /// @return  Length of enclosed IPv6 addresses in octets.
+    uint16_t getAddrLength() const {
+        return addr_length_;
+    }
+
 private:
     /// @brief The priority of this OPTION_V6_DNR instance compared to other instances.
     uint16_t service_priority_;
 
-    /// @brief Length of the authentication-domain-name field in octets.
+    /// @brief Length of the authentication-domain-name data in octets.
     uint16_t adn_length_;
 
-    /// @brief Authentication domain name field of variable 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.
@@ -72,10 +97,10 @@ private:
     };
 };
 
-class OptionDNR4 : public Option {
+class OptionDnr4 : public Option {
 public:
-    OptionDNR4();
-    OptionDNR4(OptionBufferConstIter begin, OptionBufferConstIter end);
+    OptionDnr4();
+    OptionDnr4(OptionBufferConstIter begin, OptionBufferConstIter end);
     virtual OptionPtr clone() const;
     virtual void pack(util::OutputBuffer& buf, bool check) const;
     virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
@@ -83,6 +108,12 @@ public:
     virtual uint16_t len() const;
 };
 
+/// A pointer to the @c OptionDnr6 object.
+typedef boost::shared_ptr<OptionDnr6> OptionDnr6Ptr;
+
+/// A pointer to the @c OptionDnr4 object.
+typedef boost::shared_ptr<OptionDnr4> OptionDnr4Ptr;
+
 }  // namespace dhcp
 }  // namespace isc
 
index bcd1a3b3714deb05c3fd36023072a7800fe93d38..5669ea24dbf77c6b381000221155cca0f2e8c38f 100644 (file)
@@ -367,7 +367,7 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = {
     { "v4-portparams", DHO_V4_PORTPARAMS, DHCP4_OPTION_SPACE, OPT_RECORD_TYPE,
       false, RECORD_DEF(V4_PORTPARAMS_RECORDS), "" },
     { "v4-dnr", DHO_V4_DNR, DHCP4_OPTION_SPACE, OPT_BINARY_TYPE,
-     true, NO_RECORD_DEF, "" },
+     false, NO_RECORD_DEF, "" },
     { "option-6rd", DHO_6RD, DHCP4_OPTION_SPACE, OPT_RECORD_TYPE, true,
       RECORD_DEF(OPT_6RD_RECORDS), "" },
     { "v4-access-domain", DHO_V4_ACCESS_DOMAIN, DHCP4_OPTION_SPACE,
index 259ddff03fef899e51a9ed6d09ecc2fa7aef9193..6b52279b5e235a17d56f245ee87140214138eedc 100644 (file)
@@ -63,6 +63,7 @@ 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/option_dnr_unittest.cc b/src/lib/dhcp/tests/option_dnr_unittest.cc
new file mode 100644 (file)
index 0000000..5ac3eb6
--- /dev/null
@@ -0,0 +1,80 @@
+// 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 <dhcp/dhcp6.h>
+#include <dhcp/option_dnr.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+using boost::scoped_ptr;
+
+namespace {
+
+// This test verifies option constructor from wire data.
+// Provided wire data is in the ADN only mode i.e. only
+// Service priority and Authentication domain name FQDN
+// fields are present.
+TEST(OptionDnr6Test, constructorAdnOnlyMode) {
+    // Prepare data to decode - ADN only mode.
+    const uint8_t buf_data[] = {
+        0x80, 0x01,                                      // Service priority is 32769 dec
+        0x00, 0x14,                                      // ADN Length is 20 dec
+        0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74,        // FQDN: Myhost.
+        0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65,  // Example.
+        0x03, 0x43, 0x6F, 0x6D, 0x00                     // Com.
+    };
+
+    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())));
+    ASSERT_TRUE(option);
+
+    // Check if member variables were correctly set by ctor.
+    EXPECT_EQ(Option::V6, option->getUniverse());
+    EXPECT_EQ(D6O_V6_DNR, option->getType());
+
+    // 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(0, option->getAddrLength());
+}
+
+TEST(OptionDnr6Test, constructorDataTruncated) {
+    // Prepare data to decode - data too short.
+    const uint8_t buf_data[] = {
+        0x80, 0x01  // Service priority is 32769 dec, other data is missing
+    };
+
+    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);
+    ASSERT_FALSE(option);
+}
+
+TEST(OptionDnr6Test, onlyWhitespaceFqdn) {
+    // Prepare data to decode - ADN only mode.
+    const uint8_t buf_data[] = {
+        0x80, 0x01,  // Service priority is 32769 dec
+        0x00, 0x02,  // ADN Length is 2 dec
+        0x01, 0x20   // FQDN consists only of whitespace
+    };
+
+    OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+    // Create option instance. Check that constructor throws InvalidOptionDnr6DomainName exception.
+    scoped_ptr<OptionDnr6> option;
+    EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), InvalidOptionDnr6DomainName);
+    ASSERT_FALSE(option);
+}
+
+}  // namespace
\ No newline at end of file