]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3141] config parser wip
authorPiotrek Zadroga <piotrek@isc.org>
Tue, 13 Feb 2024 18:02:17 +0000 (19:02 +0100)
committerPiotrek Zadroga <piotrek@isc.org>
Fri, 23 Feb 2024 16:14:05 +0000 (17:14 +0100)
src/lib/dhcp/option4_dnr.cc
src/lib/dhcp/option4_dnr.h
src/lib/dhcp/option6_dnr.cc

index f3985324c600171f470e7009edc4779359f84846..d5dab27d09d94006f082a30a903454656502bedd 100644 (file)
@@ -132,6 +132,8 @@ Option4Dnr::parseConfigData(const std::string& config_txt) {
 
 const std::unordered_set<std::string> DnrInstance::FORBIDDEN_SVC_PARAMS = {"ipv4hint", "ipv6hint"};
 
+const std::unordered_set<uint8_t> DnrInstance::SUPPORTED_SVC_PARAMS = {1, 3, 7};
+
 DnrInstance::DnrInstance(Option::Universe universe)
     : universe_(universe), dnr_instance_data_length_(0), service_priority_(0),
       adn_length_(0), addr_length_(0), svc_params_length_(0),
index 9ef1bdc786cde1b2a7a16a9bf5e136b66f6a3384..1f0ef4088a158a690c0050a2ba12a96843c5ca88 100644 (file)
@@ -13,6 +13,7 @@
 #include <dhcp/option.h>
 #include <dhcp/option_data_types.h>
 #include <dns/name.h>
+#include <util/strutil.h>
 
 #include <unordered_set>
 #include <string>
@@ -34,7 +35,7 @@ const std::map<std::string, uint16_t> SVC_PARAMS =
     { "ipv4hint", 4},        // RFC 9460, Section 14.3.2, forbidden in DNR
     { "ech", 5},             // RFC 9460, Section 14.3.2, not used in DNR
     { "ipv6hint", 6},        // RFC 9460, Section 14.3.2, forbidden in DNR
-    { "dotpath", 7},         // RFC 9461, optional in DNR
+    { "dohpath", 7},         // RFC 9461, optional in DNR
     { "ohttp", 8}            // https://datatracker.ietf.org/doc/draft-ietf-ohai-svcb-config,
                              // not used in DNR
 };
@@ -122,6 +123,8 @@ public:
     /// included IP addresses.
     static const std::unordered_set<std::string> FORBIDDEN_SVC_PARAMS;
 
+    static const std::unordered_set<uint8_t> SUPPORTED_SVC_PARAMS;
+
     /// @brief Constructor of the empty DNR Instance.
     ///
     /// @param universe either V4 or V6 Option universe
index e4daefce534de9a06380f6d129fd29be66f8032b..2a6ac713002b634f508b79c34e664e798ed8d671 100644 (file)
@@ -9,6 +9,7 @@
 #include <dhcp/option6_dnr.h>
 
 using namespace isc::asiolink;
+using namespace isc::util;
 
 namespace isc {
 namespace dhcp {
@@ -151,7 +152,179 @@ Option6Dnr::unpackAddresses(OptionBufferConstIter& begin, OptionBufferConstIter
 
 void
 Option6Dnr::parseConfigData(const std::string& config_txt){
-    // TBD
+    // This parses convenient option config notation.
+    // The config to be parsed may contain escaped characters like "\\," or "\\|".
+    // Example configs are below (first contains recommended resolvers' IP addresses, and SvcParams;
+    // second is an example of ADN-only mode):
+    //
+    // "name": "v6-dnr",
+    // "data": "100, dot1.example.org., 2001:db8::1 2001:db8::2, alpn=dot\\,doq\\,h2\\,h3 port=8530 dohpath=/q{?dns}"
+    //
+    // "name": "v6-dnr",
+    // "data": "200, resolver.example."
+
+    // get tokens using comma separator with double backslash escaping enabled
+    std::vector<std::string> tokens = str::tokens(config_txt, std::string(","), true);
+
+    if (tokens.size() < 2) {
+        isc_throw(BadValue, getLogPrefix() << "Option config requires at least comma separated "
+                                           << "Service Priority and ADN");
+    }
+
+    if (tokens.size() > 4) {
+        isc_throw(BadValue, getLogPrefix() << "Option config supports maximum 4 comma separated "
+                                           << "fields: Service Priority, ADN, resolver IP "
+                                           << "address/es and SvcParams");
+    }
+
+    // parse Service Priority
+    std::string txt_svc_priority = str::trim(tokens[0]);
+    try {
+        service_priority_ = boost::lexical_cast<uint16_t>(txt_svc_priority);
+    } catch (const std::exception& e) {
+        isc_throw(BadValue, getLogPrefix() << "Given service priority " + txt_svc_priority
+                                           << " cannot be parsed into uint_16 integer. "
+                                           << "Error: " << e.what());
+    }
+
+    // parse ADN
+    std::string txt_adn = str::trim(tokens[1]);
+    try {
+        adn_.reset(new isc::dns::Name(txt_adn, true));
+    } catch (const std::exception& e) {
+        isc_throw(InvalidOptionDnrDomainName, getLogPrefix()
+                                                  << "Given ADN " + txt_adn
+                                                  << " cannot be parsed into fully qualified "
+                                                  << "domain-name. "
+                                                  << "Error: " << e.what());
+    }
+
+    adn_length_ = adn_->getLength();
+    if (adn_length_ == 0) {
+        isc_throw(InvalidOptionDnrDomainName, getLogPrefix()
+                                                  << "Mandatory Authentication Domain Name fully "
+                                                     "qualified domain-name is missing");
+    }
+
+    if (tokens.size() > 2) {
+        setAdnOnlyMode(false);
+
+        // parse resolver IP address/es
+        std::string txt_addresses = str::trim(tokens[2]);
+
+        // IP addresses are separated with space
+        std::vector<std::string> addresses = str::tokens(txt_addresses, std::string(" "));
+        for (auto const& txt_addr : addresses) {
+            try {
+                ip_addresses_.push_back(IOAddress(str::trim(txt_addr)));
+            } catch (const Exception& e) {
+                isc_throw(BadValue, getLogPrefix() << "Given string " + txt_addr
+                                                   << " cannot be parsed into IPv6 address. "
+                                                   << "Error: " << e.what());
+            }
+        }
+
+        // As per RFC9463 section 3.1.8:
+        // (If ADN-only mode is not used)
+        // The option includes at least one valid IP address.
+        if (ip_addresses_.empty()) {
+            isc_throw(BadValue, getLogPrefix() << "Option config requires at least one valid IP "
+                                               << "address.");
+        }
+
+        addr_length_ = ip_addresses_.size() * V6ADDRESS_LEN;
+    }
+
+    if (tokens.size() == 4) {
+        // parse Service Parameters
+        std::string txt_svc_params = str::trim(tokens[3]);
+
+        // SvcParamKey=SvcParamValue pairs are separated with space
+        std::vector<std::string> svc_params = str::tokens(txt_svc_params, std::string(" "));
+        std::vector<std::string> alpn_ids;
+        std::vector<OpaqueDataTuple> alpn_ids_container;
+        for (auto const& txt_svc_param : svc_params) {
+            std::vector<std::string> key_val = str::tokens(str::trim(txt_svc_param), "=");
+            if (key_val.size() != 2) {
+                isc_throw(InvalidOptionDnrSvcParams,
+                          getLogPrefix() << "Wrong Svc Params syntax - SvcParamKey=SvcParamValue "
+                                         << "pair syntax must be used");
+            }
+
+            // SvcParam Key related checks come below.
+            std::string key = str::trim(key_val[0]);
+
+            if (FORBIDDEN_SVC_PARAMS.find(key) != FORBIDDEN_SVC_PARAMS.end()) {
+                isc_throw(InvalidOptionDnrSvcParams, getLogPrefix() << "Wrong Svc Params syntax - key "
+                                                                    << key << " must not be used");
+            }
+
+            auto svc_params_iterator = SVC_PARAMS.find(key);
+            if (svc_params_iterator == SVC_PARAMS.end()) {
+                isc_throw(InvalidOptionDnrSvcParams,
+                          getLogPrefix() << "Wrong Svc Params syntax - key "
+                                         << key
+                                         << " not found in SvcParamKeys registry");
+            }
+
+            uint8_t num_svc_param_key = svc_params_iterator->second;
+            if (SUPPORTED_SVC_PARAMS.find(num_svc_param_key) == SUPPORTED_SVC_PARAMS.end()) {
+                isc_throw(InvalidOptionDnrSvcParams,
+                          getLogPrefix() << "Wrong Svc Params syntax - key "
+                                         << key
+                                         << " not supported in DNR option SvcParams");
+            }
+
+            // SvcParam Val check.
+            std::string val = str::trim(key_val[1]);
+            if (val.empty()) {
+                isc_throw(InvalidOptionDnrSvcParams,
+                          getLogPrefix() << "Wrong Svc Params syntax - empty SvcParamValue for key "
+                                         << key);
+            }
+
+
+
+            switch (num_svc_param_key) {
+            case 1:
+                // alpn
+                // The wire-format value for "alpn" consists of at least one alpn-id prefixed by its
+                // length as a single octet, and these length-value pairs are concatenated to form
+                // the SvcParamValue.
+
+                alpn_ids = str::tokens(val, std::string(","));
+                for (auto const& alpn_id : alpn_ids) {
+                    if (ALPN_IDS.find(alpn_id) == ALPN_IDS.end()) {
+                        isc_throw(InvalidOptionDnrSvcParams,
+                                  getLogPrefix() << "Wrong Svc Params syntax - alpn-id "
+                                                 << alpn_id
+                                  << " not found in ALPN-IDs registry");
+                    }
+
+                    OpaqueDataTuple alpn_id_tuple(OpaqueDataTuple::LENGTH_1_BYTE);
+                    alpn_id_tuple.append(alpn_id);
+                    alpn_ids_container.push_back(alpn_id_tuple);
+                }
+
+                break;
+            case 3:
+                // port
+                break;
+            case 7:
+                // dohpath
+                break;
+            }
+        }
+
+        std::ostringstream stream;
+        for (auto const& t : alpn_ids_container) {
+            stream << t.getLength() << "-" << t.getText() << " ";
+        }
+        isc_throw(BadValue, getLogPrefix() << "SvcParams: " + txt_svc_params + ", parsed "
+                                                                               "alpn-ids "
+                  + stream.str());
+    }
+
 }
 
 }  // namespace dhcp