]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2536] Working on upack DNRv6 Option
authorPiotrek Zadroga <piotrek@isc.org>
Wed, 5 Apr 2023 16:43:08 +0000 (18:43 +0200)
committerPiotrek Zadroga <piotrek@isc.org>
Thu, 4 May 2023 21:17:18 +0000 (23:17 +0200)
src/lib/dhcp/option_dnr.cc
src/lib/dhcp/option_dnr.h

index c3e37011301f5280b72104952ff5732299989194..ca9af55151376052034e544a1cb712937419e5ec 100644 (file)
@@ -8,14 +8,12 @@
 
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
+#include <dhcp/opaque_data_tuple.h>
 #include <dhcp/option_dnr.h>
 
 namespace isc {
 namespace dhcp {
 
-OptionDNR6::OptionDNR6() : Option(V6, D6O_V6_DNR) {
-}
-
 OptionDNR6::OptionDNR6(OptionBufferConstIter begin, OptionBufferConstIter end)
     : Option(V6, D6O_V6_DNR) {
     unpack(begin, end);
@@ -28,12 +26,51 @@ OptionDNR6::clone() const {
 
 void
 OptionDNR6::pack(util::OutputBuffer& buf, bool check) const {
-    Option::pack(buf, check);
+    packHeader(buf, check);
+
+    buf.writeUint16(service_priority_);
+    // TBD
 }
 
 void
 OptionDNR6::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
-    Option::unpack(begin, end);
+    if (std::distance(begin, end) < getMinimalLength()) {
+        isc_throw(OutOfRange, "parsed DHCPv6 Encrypted DNS Option ("
+                              << D6O_V6_DNR << ") data truncated to"
+                              " size " << std::distance(begin, end));
+    }
+    setData(begin, end);
+    // First two octets of Option data is Service Priority - this is mandatory field.
+    service_priority_ = isc::util::readUint16((&*begin), SERVICE_PRIORITY_SIZE);
+    begin += sizeof(service_priority_);
+
+    // Next comes two octets of ADN Length plus the ADN data itself (variable length).
+    // This is Opaque Data Tuple so let's use this class to retrieve the ADN data.
+    OpaqueDataTuple adn_tuple(OpaqueDataTuple::LENGTH_2_BYTES, begin, end);
+    adn_length_ = adn_tuple.getLength();
+    if (adn_length_ > 0) {
+        // Let's try to extract ADN FQDN data
+        isc::util::InputBuffer name_buf(&adn_tuple.getData(),
+                                        adn_length_);
+        try {
+            adn_.reset(new isc::dns::Name(name_buf, true));
+        } catch (const Exception&) {
+            isc_throw(InvalidOptionDNR6DomainName, "failed to parse "
+                                                    "fully qualified domain-name from wire format");
+        }
+    }
+    begin += adn_tuple.getTotalLength();
+
+    if (begin == end) {
+        // ADN only mode, other fields are not included
+        return;
+    }
+    if (std::distance(begin, end) < ADDR_LENGTH_SIZE) {
+        isc_throw(OutOfRange, "truncated DHCPv6 Encrypted DNS Option (" << D6O_V6_DNR << ") - after"
+                              " ADN field, there should be at least 2 bytes long Addr Length field");
+    }
+    addr_length_ = isc::util::readUint16((&*begin), ADDR_LENGTH_SIZE);
+    // TBD
 }
 
 std::string
index 630947cc7e40450ebcd0798e94497f0b7c83bb3a..f2c6b2a1aae79b2faaa44e3a4603306e344941aa 100644 (file)
@@ -8,19 +8,68 @@
 #define OPTION_DNR_H
 
 #include <dhcp/option.h>
+#include <dns/name.h>
 
 namespace isc {
 namespace dhcp {
 
+/// @brief Exception thrown when invalid domain name is specified.
+class InvalidOptionDNR6DomainName : public Exception {
+public:
+    InvalidOptionDNR6DomainName(const char* file, size_t line,
+                                 const char* what) :
+          isc::Exception(file, line, what) {}
+};
+
 class OptionDNR6 : public Option {
 public:
-    OptionDNR6();
+
+    /// @brief Size in octets of Service Priority field
+    static const uint8_t SERVICE_PRIORITY_SIZE = 2;
+
+    /// @brief Size in octets of ADN Length field
+    static const uint8_t ADN_LENGTH_SIZE = 2;
+
+    /// @brief Size in octets of Addr Length field
+    static const uint8_t ADDR_LENGTH_SIZE = 2;
+
+    OptionDNR6() : Option(V6, D6O_V6_DNR) {
+          };
     OptionDNR6(OptionBufferConstIter begin, OptionBufferConstIter end);
     virtual OptionPtr clone() const;
     virtual void pack(util::OutputBuffer& buf, bool check) const;
     virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
     virtual std::string toText(int indent) const;
     virtual uint16_t len() const;
+
+private:
+    /// @brief The priority of this OPTION_V6_DNR instance compared to other instances.
+    uint16_t service_priority_;
+
+    /// @brief Length of the authentication-domain-name field in octets.
+    uint16_t adn_length_;
+
+    /// @brief Authentication domain name field of variable length
+    ///
+    /// Authentication domain name field of variable length holding
+    /// a fully qualified domain name of the encrypted DNS resolver.
+    /// This field is formatted as specified in Section 10 of RFC8415.
+    boost::shared_ptr<isc::dns::Name> adn_;
+
+    /// @brief Length of enclosed IPv6 addresses in octets.
+    uint16_t addr_length_;
+
+    /// @brief Returns minimal length of the option data (without headers) in octets.
+    ///
+    /// If the ADN-only mode is used, then "Addr Length", "ipv6-address(es)",
+    /// and "Service Parameters (SvcParams)" fields are not present. In this
+    /// case minimal length of data is 2 octets for Service Priority plus 2 octets
+    /// for ADN Length.
+    ///
+    /// @return Minimal length of the option data (without headers) in octets.
+    static uint8_t getMinimalLength() {
+        return SERVICE_PRIORITY_SIZE + ADN_LENGTH_SIZE;
+    };
 };
 
 class OptionDNR4 : public Option {