libkea_dhcp___la_SOURCES += option_custom.cc option_custom.h
libkea_dhcp___la_SOURCES += option_data_types.cc option_data_types.h
libkea_dhcp___la_SOURCES += option_definition.cc option_definition.h
-libkea_dhcp___la_SOURCES += option_dnr.cc option_dnr.h
+libkea_dhcp___la_SOURCES += option4_dnr.cc option4_dnr.h
+libkea_dhcp___la_SOURCES += option6_dnr.cc option6_dnr.h
libkea_dhcp___la_SOURCES += option_int.h
libkea_dhcp___la_SOURCES += option_int_array.h
libkea_dhcp___la_SOURCES += option_opaque_data_tuples.cc option_opaque_data_tuples.h
--- /dev/null
+// Copyright (C) 2018-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/dhcp6.h>
+#include <dhcp/opaque_data_tuple.h>
+#include <dhcp/option4_dnr.h>
+#include <dhcp/option6_dnr.h>
+#include <dns/labelsequence.h>
+#include <util/strutil.h>
+#include <set>
+
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+Option4Dnr::Option4Dnr(OptionBufferConstIter begin, OptionBufferConstIter end)
+ : Option(V4, DHO_V4_DNR) {
+ unpack(begin, end);
+}
+
+OptionPtr
+Option4Dnr::clone() const {
+ return (cloneInternal<Option4Dnr>());
+}
+
+void
+Option4Dnr::pack(util::OutputBuffer& buf, bool check) const {
+ packHeader(buf, check);
+ for (const DnrInstance& dnr_instance : dnr_instances_) {
+ buf.writeUint16(dnr_instance.getDnrInstanceDataLength());
+ buf.writeUint16(dnr_instance.getServicePriority());
+ buf.writeUint8(dnr_instance.getAdnLength());
+ dnr_instance.packAdn(buf);
+ if (dnr_instance.isAdnOnlyMode()) {
+ return;
+ }
+ buf.writeUint8(dnr_instance.getAddrLength());
+ dnr_instance.packAddresses(buf);
+ dnr_instance.packSvcParams(buf);
+ }
+}
+
+void
+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()) {
+ 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));
+ OptionBufferConstIter dnr_instance_end = begin + offset +
+ dnr_instance.getDnrInstanceDataLength();
+ offset += DNR_INSTANCE_DATA_LENGTH_SIZE;
+ dnr_instance.setServicePriority(
+ readUint16(&(*(begin + offset)), SERVICE_PRIORITY_SIZE));
+ offset += 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");
+ }
+
+ // Let's try to extract ADN FQDN data.
+ InputBuffer name_buf(&(*adn_tuple.getData().begin()), adn_length);
+ try {
+ auto adn = dnr_instance.getAdn();
+ adn.reset(new isc::dns::Name(name_buf, true));
+ } catch (const Exception& ex) {
+ isc_throw(InvalidOptionDnrDomainName, "failed to parse "
+ "fully qualified domain-name from wire format "
+ "- " << ex.what());
+ }
+
+ offset += adn_tuple.getTotalLength();
+
+ if (begin + offset == 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 += ADDR_LENGTH_SIZE;
+ OptionBufferConstIter addr_begin = begin + offset;
+ OptionBufferConstIter addr_end = addr_begin + addr_length;
+ auto ip_addresses = dnr_instance.getIpAddresses();
+
+ while (addr_begin != addr_end) {
+ const uint8_t* ptr = &(*addr_begin);
+ ip_addresses.push_back(IOAddress(readUint32(ptr, std::distance(addr_begin, addr_end))));
+ addr_begin += V4ADDRESS_LEN;
+ offset += V4ADDRESS_LEN;
+ }
+
+ // 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();
+ svc_params.assign(begin + offset, dnr_instance_end);
+ dnr_instance.checkSvcParams();
+ }
+
+ addDnrInstance(dnr_instance);
+ }
+}
+
+std::string
+Option4Dnr::toText(int indent) const {
+ return Option::toText(indent);
+}
+
+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;
+ }
+ return (len);
+}
+
+void
+Option4Dnr::addDnrInstance(DnrInstance& dnr_instance) {
+ dnr_instances_.push_back(dnr_instance);
+}
+
+void
+DnrInstance::packAdn(util::OutputBuffer& buf) const {
+ if (!adn_) {
+ // This should not happen since Encrypted DNS options are designed
+ // to always include an authentication domain name.
+ isc_throw(InvalidOptionDnrDomainName, "Mandatory Authentication Domain Name fully "
+ "qualified domain-name is missing");
+ }
+ isc::dns::LabelSequence label_sequence(*adn_);
+ if (label_sequence.getDataLength() > 0) {
+ size_t data_length = 0;
+ const uint8_t* data = label_sequence.getData(&data_length);
+ buf.writeData(data, data_length);
+ }
+}
+
+void
+DnrInstance::packAddresses(util::OutputBuffer& buf) const {
+ AddressContainer::const_iterator address = ip_addresses_.begin();
+ while (address != ip_addresses_.end()) {
+ buf.writeUint32(address->toUint32());
+ ++address;
+ }
+}
+
+void
+DnrInstance::packSvcParams(util::OutputBuffer& buf) const {
+ if (svc_params_length_ > 0) {
+ buf.writeData(&(*svc_params_.begin()), svc_params_length_);
+ }
+}
+
+std::string
+DnrInstance::getAdnAsText() const {
+ if (adn_) {
+ return (adn_->toText());
+ }
+ return ("");
+}
+
+void
+DnrInstance::setAdn(const std::string& adn) {
+ std::string trimmed_adn = isc::util::str::trim(adn);
+ if (trimmed_adn.empty()) {
+ isc_throw(InvalidOptionDnrDomainName, "Mandatory Authentication Domain Name fully "
+ "qualified domain-name must not be empty");
+ }
+ try {
+ adn_.reset(new isc::dns::Name(trimmed_adn, true));
+ } catch (const Exception& ex) {
+ isc_throw(InvalidOptionDnrDomainName, "Failed to parse "
+ "fully qualified domain-name from string "
+ "- " << ex.what());
+ }
+ size_t adn_len = 0;
+ isc::dns::LabelSequence label_sequence(*adn_);
+ label_sequence.getData(&adn_len);
+ if (adn_len > std::numeric_limits<uint16_t>::max()) {
+ isc_throw(InvalidOptionDnrDomainName,
+ "Given ADN FQDN length " << adn_len << " is bigger than uint_16 MAX");
+ }
+
+ adn_length_ = adn_len;
+}
+
+void
+DnrInstance::checkSvcParams(bool from_wire_data) {
+ std::string svc_params = isc::util::str::trim(svc_params_);
+ if (svc_params.empty()) {
+ isc_throw(InvalidOptionDnrSvcParams, "Provided Svc Params field is empty");
+ }
+ if (!from_wire_data) {
+ // If Service Params field was not parsed from on-wire data,
+ // but actually was provided with ctor, let's calculate svc_params_length_.
+ auto svc_params_len = svc_params.length();
+ if (svc_params_len > std::numeric_limits<uint16_t>::max()) {
+ isc_throw(OutOfRange, "Given Svc Params length " << svc_params_len
+ << " is bigger than uint_16 MAX");
+ }
+ svc_params_length_ = svc_params_len;
+ // If Service Params field was not parsed from on-wire data,
+ // but actually was provided with ctor, let's replace it with trimmed value.
+ svc_params_ = svc_params;
+ }
+
+ // SvcParams are a whitespace-separated list, with each SvcParam
+ // consisting of a SvcParamKey=SvcParamValue pair or a standalone SvcParamKey.
+ // SvcParams in presentation format MAY appear in any order, but keys MUST NOT be repeated.
+
+ // Let's put all elements of a whitespace-separated list into a vector.
+ std::vector<std::string> tokens = isc::util::str::tokens(svc_params, " ");
+
+ // Set of keys used to check if a key is not repeated.
+ std::set<std::string> keys;
+ // String sanitizer is used to check keys syntax.
+ util::str::StringSanitizerPtr sanitizer;
+ // SvcParamKeys are lower-case alphanumeric strings. Key names
+ // contain 1-63 characters from the ranges "a"-"z", "0"-"9", and "-".
+ std::string regex = "[^a-z0-9-]";
+ sanitizer.reset(new util::str::StringSanitizer(regex, ""));
+ // The service parameters MUST NOT include
+ // "ipv4hint" or "ipv6hint" SvcParams as they are superseded by the
+ // included IP addresses.
+ std::set<std::string> forbidden_keys = {"ipv4hint", "ipv6hint"};
+
+ // Now let's check each SvcParamKey=SvcParamValue pair.
+ for (const std::string& token : tokens) {
+ std::vector<std::string> key_val = isc::util::str::tokens(token, "=");
+ if (key_val.size() > 2) {
+ isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - more than one "
+ "equals sign found in SvcParamKey=SvcParamValue "
+ "pair");
+ }
+
+ // SvcParam Key related checks come below.
+ std::string key = key_val[0];
+ if (forbidden_keys.find(key) != forbidden_keys.end()) {
+ isc_throw(InvalidOptionDnrSvcParams,
+ "Wrong Svc Params syntax - key " << key << " must not be used");
+ }
+
+ auto insert_res = keys.insert(key);
+ if (!insert_res.second) {
+ isc_throw(InvalidOptionDnrSvcParams,
+ "Wrong Svc Params syntax - key " << key << " was duplicated");
+ }
+
+ if (key.length() > 63) {
+ isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - key had more than 63 "
+ "characters - "
+ << key);
+ }
+
+ std::string sanitized_key = sanitizer->scrub(key);
+ if (sanitized_key.size() < key.size()) {
+ isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - invalid character "
+ "used in key - "
+ << key);
+ }
+
+ if (key_val.size() == 2) {
+ // tbd Check value syntax
+ std::string value = key_val[1];
+ }
+ }
+}
+
+void
+DnrInstance::checkFields() {
+ if (svc_params_.empty() && ip_addresses_.empty()) {
+ // ADN only mode, nothing more to do.
+ return;
+ }
+ if (!svc_params_.empty() && ip_addresses_.empty()) {
+ // 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.
+ isc_throw(OutOfRange,
+ getLogPrefix()
+ << " malformed: No IP address given. Since this is not "
+ "ADN only mode, at least one valid IP address must be included");
+ }
+ if (!svc_params_.empty()) {
+ checkSvcParams(false);
+ }
+ adn_only_mode_ = false;
+ auto addr_len = ip_addresses_.size() * V6ADDRESS_LEN;
+ if (addr_len > std::numeric_limits<uint16_t>::max()) {
+ isc_throw(OutOfRange,
+ "Given IPv6 addresses length " << addr_len << " is bigger than uint_16 MAX");
+ }
+ addr_length_ = addr_len;
+}
+
+std::string
+DnrInstance::getLogPrefix() const {
+ return (universe_ == Option::V4) ?
+ ("DHCPv4 Encrypted DNS Option (" + std::to_string(DHO_V4_DNR) + ")") :
+ ("DHCPv6 Encrypted DNS Option (" + std::to_string(D6O_V6_DNR) + ")");
+}
+
+} // namespace dhcp
+} // namespace isc
// 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/.
-#ifndef OPTION_DNR_H
-#define OPTION_DNR_H
+#ifndef OPTION4_DNR_H
+#define OPTION4_DNR_H
#include <asiolink/io_address.h>
#include <dhcp/option.h>
namespace isc {
namespace dhcp {
-/// @brief Exception thrown when invalid domain name is specified.
-class InvalidOptionDnrDomainName : public Exception {
+class DnrInstance {
public:
- InvalidOptionDnrDomainName(const char* file, size_t line, const char* what)
- : isc::Exception(file, line, what) {
- }
-};
+ DnrInstance(Option::Universe universe) : universe_(universe) {}
-/// @brief Exception thrown when Service parameters have wrong format.
-class InvalidOptionDnrSvcParams : public Exception {
-public:
- InvalidOptionDnrSvcParams(const char* file, size_t line, const char* what)
- : isc::Exception(file, line, what) {
- }
-};
+ virtual ~DnrInstance() {}
-/// @brief Represents DHCPv6 Encrypted DNS %Option (code 144).
-///
-/// This option has been defined in the draft-ietf-add-dnr-15 (to be replaced
-/// with published RFC) and it has a following structure:
-/// - option-code = 144 (2 octets)
-/// - option-len (2 octets)
-/// - Service Priority (2 octets)
-/// - ADN Length (2 octets)
-/// - Authentication Domain Name (variable length)
-/// - Addr Length (2 octets)
-/// - IPv6 Address(es) (variable length)
-/// - Service Parameters (variable length).
-class OptionDnr6 : public Option {
-public:
- /// @brief A container for (IPv6) addresses.
+ /// @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;
-
- /// @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
- /// on-wire data. It may throw an exception if the @c unpack method throws.
- ///
- /// @param begin Iterator pointing to the beginning of the buffer holding an
- /// option.
- /// @param end Iterator pointing to the end of the buffer holding an option.
- OptionDnr6(OptionBufferConstIter begin, OptionBufferConstIter end);
-
- OptionDnr6(const uint16_t service_priority, const std::string& adn, const AddressContainer& ipv6_addresses, const std::string& svc_params);
+ const AddressContainer& getIpAddresses() const {
+ return ip_addresses_;
+ }
- OptionDnr6(const uint16_t service_priority, const std::string& adn);
+ const boost::shared_ptr<isc::dns::Name>& getAdn() const {
+ return 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;
+ uint16_t getDnrInstanceDataLength() const {
+ return (dnr_instance_data_length_);
+ }
/// @brief Getter of the @c service_priority_.
///
- /// @return The priority of this OPTION_V6_DNR instance compared to other instances.
+ /// @return The priority of this DNR instance compared to other instances.
uint16_t getServicePriority() const {
return (service_priority_);
}
/// 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;
+ std::string getAdnAsText() const;
/// @brief Getter of the @c addr_length_.
///
- /// @return Length of enclosed IPv6 addresses in octets.
+ /// @return Length of enclosed IP addresses in octets.
uint16_t getAddrLength() const {
return (addr_length_);
}
///
/// @return Address container with addresses.
AddressContainer getAddresses() const {
- return (ipv6_addresses_);
+ return (ip_addresses_);
}
/// @brief Getter of the @c svc_params_ field.
///
/// @return Returns Service Parameters as a string.
- std::string getSvcParams() const {
- return (svc_params_);
+ const std::string& getSvcParams() const {
+ return svc_params_;
}
-protected:
- /// @brief The priority of this OPTION_V6_DNR instance compared to other instances.
- uint16_t service_priority_;
-
- /// @brief Length of the authentication-domain-name data 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_ = 0;
-
- /// @brief Vector container holding one or more IPv6 addresses.
- ///
- /// One or more IPv6 addresses to reach the encrypted DNS resolver.
- /// An address can be link-local, ULA, or GUA.
- AddressContainer ipv6_addresses_;
-
- /// @brief Length of Service Parameters field in octets.
- uint16_t svc_params_length_ = 0;
+ bool isAdnOnlyMode() const {
+ return adn_only_mode_;
+ }
- /// @brief Flag stating whether ADN only mode is used or not.
+ /// @brief Sets Authentication domain name from given string.
///
- /// "Addr Length", "ipv6-address(es)", and "Service Parameters (SvcParams)"
- /// fields are not present if the ADN-only mode is used.
- bool adn_only_mode_ = true;
-
- /// @brief Service Parameters (SvcParams) (variable length).
+ /// Sets FQDN of the encrypted DNS resolver from given string.
+ /// It may throw an exception if parsing of the FQDN fails or if
+ /// provided FQDN length is bigger than uint16_t Max.
+ /// It also calculates and sets value of Addr length field.
///
- /// Specifies a set of service parameters that are encoded
- /// following the rules in Section 2.1 of [I-D.ietf-dnsop-svcb-https].
- std::string svc_params_;
+ /// @param adn string representation of ADN FQDN
+ void setAdn(const std::string& adn);
-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);
- };
+ void setDnrInstanceDataLength(uint16_t dnr_instance_data_length) {
+ dnr_instance_data_length_ = dnr_instance_data_length;
+ }
+ void setServicePriority(uint16_t service_priority) {
+ service_priority_ = service_priority;
+ }
+ void setAdnLength(uint16_t adn_length) {
+ adn_length_ = adn_length;
+ }
+ void setAddrLength(uint16_t addr_length) {
+ addr_length_ = addr_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;
+ }
+ void setSvcParams(const std::string& svc_params) {
+ svc_params_ = svc_params;
+ }
/// @brief Writes the ADN FQDN in the wire format into a buffer.
///
/// @param [out] buf buffer where ADN FQDN will be written.
void packAdn(isc::util::OutputBuffer& buf) const;
- /// @brief Writes the IPv6 address(es) in the wire format into a buffer.
+ /// @brief Writes the IP address(es) in the wire format into a buffer.
///
- /// The IPv6 address(es) (@c ipv6_addresses_) data is appended at the end
+ /// The IP address(es) (@c ip_addresses_) data is appended at the end
/// of the buffer.
///
- /// @param [out] buf buffer where IPv6 address(es) will be written.
- void packAddresses(isc::util::OutputBuffer& buf) const;
+ /// @param [out] buf buffer where IP address(es) will be written.
+ virtual void packAddresses(isc::util::OutputBuffer& buf) const;
/// @brief Writes the Service Parameters in the wire format into a buffer.
///
/// Section 2.1 of [I-D.ietf-dnsop-svcb-https].
void checkSvcParams(bool from_wire_data = true);
- /// @brief Sets Authentication domain name from given string.
+ void checkFields();
+
+protected:
+ Option::Universe universe_;
+
+ /// @brief Authentication domain name field of variable length.
///
- /// Sets FQDN of the encrypted DNS resolver from given string.
- /// It may throw an exception if parsing of the FQDN fails or if
- /// provided FQDN length is bigger than uint16_t Max.
- /// It also calculates and sets value of Addr length field.
+ /// 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 all following data in octets.
///
- /// @param adn string representation of ADN FQDN
- void setAdn(const std::string& adn);
- void checkFields();
+ /// This field is only used for DHCPv4 Encrypted DNS %Option.
+ uint16_t dnr_instance_data_length_;
+
+ /// @brief The priority of this instance compared to other DNR instances.
+ uint16_t service_priority_;
+
+ /// @brief Length of the authentication-domain-name data in octets.
+ uint16_t adn_length_;
+
+ /// @brief Length of included IP addresses in octets.
+ uint16_t addr_length_ = 0;
+
+ /// @brief Vector container holding one or more IP addresses.
+ ///
+ /// One or more IP addresses to reach the encrypted DNS resolver.
+ /// In case of DHCPv4, both private and public IPv4 addresses can
+ /// be included in this field.
+ /// In case of DHCPv6, an address can be link-local, ULA, or GUA.
+ AddressContainer ip_addresses_;
+
+ /// @brief Length of Service Parameters field in octets.
+ uint16_t svc_params_length_ = 0;
+
+ /// @brief Flag stating whether ADN only mode is used or not.
+ ///
+ /// "Addr Length", "IP(v4/v6) Address(es)", and "Service Parameters (SvcParams)"
+ /// fields are not present if the ADN-only mode is used.
+ bool adn_only_mode_ = true;
+
+ /// @brief Service Parameters (SvcParams) (variable length).
+ ///
+ /// Specifies a set of service parameters that are encoded
+ /// following the rules in Section 2.1 of [I-D.ietf-dnsop-svcb-https].
+ std::string svc_params_;
+
+private:
+ std::string getLogPrefix() const;
};
-class OptionDnr4 : public Option {
+class Option4Dnr : public Option {
public:
- OptionDnr4();
- OptionDnr4(OptionBufferConstIter begin, OptionBufferConstIter end);
+ /// @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;
+
+ /// @brief Size in octets of ADN Length field.
+ static const uint8_t ADN_LENGTH_SIZE = 1;
+
+ /// @brief Size in octets of Addr Length field.
+ static const uint8_t ADDR_LENGTH_SIZE = 1;
+
+ typedef std::vector<DnrInstance> DnrInstanceContainer;
+
+ Option4Dnr(OptionBufferConstIter begin, OptionBufferConstIter end);
+
virtual OptionPtr clone() const;
- virtual void pack(util::OutputBuffer& buf, bool check) const;
+ virtual void pack(util::OutputBuffer& buf, bool check = true) const;
virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
- virtual std::string toText(int indent) const;
+ virtual std::string toText(int indent = 0) const;
virtual uint16_t len() const;
-};
-/// A pointer to the @c OptionDnr6 object.
-typedef boost::shared_ptr<OptionDnr6> OptionDnr6Ptr;
+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.
-typedef boost::shared_ptr<OptionDnr4> OptionDnr4Ptr;
+typedef boost::shared_ptr<Option4Dnr> Option4DnrPtr;
} // namespace dhcp
} // namespace isc
-#endif // OPTION_DNR_H
+#endif // OPTION4_DNR_H
--- /dev/null
+// Copyright (C) 2018-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/dhcp6.h>
+#include <dhcp/opaque_data_tuple.h>
+#include <dhcp/option6_dnr.h>
+#include <dns/labelsequence.h>
+#include <set>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+Option6Dnr::Option6Dnr(OptionBufferConstIter begin, OptionBufferConstIter end)
+ : Option(V6, D6O_V6_DNR), DnrInstance(V6) {
+ 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>());
+}
+
+void
+Option6Dnr::pack(util::OutputBuffer& buf, bool check) const {
+ packHeader(buf, check);
+
+ buf.writeUint16(service_priority_);
+ buf.writeUint16(adn_length_);
+ packAdn(buf);
+ if (adn_only_mode_) {
+ return;
+ }
+ buf.writeUint16(addr_length_);
+ packAddresses(buf);
+ packSvcParams(buf);
+}
+
+void
+Option6Dnr::packAddresses(util::OutputBuffer& buf) const {
+ for (const auto& address : ip_addresses_) {
+ if (!address.isV6()) {
+ isc_throw(isc::BadValue, address.toText() << " is not an IPv6 address");
+ }
+ buf.writeData(&address.toBytes()[0], V6ADDRESS_LEN);
+ }
+}
+
+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));
+ }
+ 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;
+
+ // 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.
+ isc::util::InputBuffer name_buf(&(*adn_tuple.getData().begin()), adn_length_);
+ try {
+ adn_.reset(new isc::dns::Name(name_buf, true));
+ } catch (const Exception& ex) {
+ isc_throw(InvalidOptionDnrDomainName, "failed to parse "
+ "fully qualified domain-name from wire format "
+ "- " << ex.what());
+ }
+
+ begin += adn_tuple.getTotalLength();
+
+ if (begin == end) {
+ // ADN only mode, other fields are not included.
+ return;
+ }
+ adn_only_mode_ = false;
+ if (std::distance(begin, end) < ADDR_LENGTH_SIZE) {
+ 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;
+ // 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");
+ }
+
+ // 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");
+ }
+
+ // 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));
+ }
+
+ // Let's unpack the ipv6-address(es).
+ auto addr_end = begin + addr_length_;
+ while (begin != addr_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()) << ", "
+ << "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_ << "'";
+ }
+ }
+
+ 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);
+}
+
+} // namespace dhcp
+} // namespace isc
--- /dev/null
+// Copyright (C) 2018-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/.
+
+#ifndef OPTION6_DNR_H
+#define OPTION6_DNR_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+#include <dhcp/option4_dnr.h>
+#include <dns/name.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Exception thrown when invalid domain name is specified.
+class InvalidOptionDnrDomainName : public Exception {
+public:
+ InvalidOptionDnrDomainName(const char* file, size_t line, const char* what)
+ : isc::Exception(file, line, what) {
+ }
+};
+
+/// @brief Exception thrown when Service parameters have wrong format.
+class InvalidOptionDnrSvcParams : public Exception {
+public:
+ InvalidOptionDnrSvcParams(const char* file, size_t line, const char* what)
+ : isc::Exception(file, line, what) {
+ }
+};
+
+/// @brief Represents DHCPv6 Encrypted DNS %Option (code 144).
+///
+/// This option has been defined in the draft-ietf-add-dnr-15 (to be replaced
+/// with published RFC) and it has a following structure:
+/// - option-code = 144 (2 octets)
+/// - option-len (2 octets)
+/// - Service Priority (2 octets)
+/// - ADN Length (2 octets)
+/// - Authentication Domain Name (variable length)
+/// - Addr Length (2 octets)
+/// - IPv6 Address(es) (variable length)
+/// - Service Parameters (variable length).
+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
+ /// on-wire data. It may throw an exception if the @c unpack method throws.
+ ///
+ /// @param begin Iterator pointing to the beginning of the buffer holding an
+ /// option.
+ /// @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);
+
+ 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;
+
+ 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.
+typedef boost::shared_ptr<Option6Dnr> Option6DnrPtr;
+
+} // namespace dhcp
+} // namespace isc
+
+#endif // OPTION6_DNR_H
#include <dhcp/dhcp6.h>
#include <dhcp/option4_addrlst.h>
#include <dhcp/option4_client_fqdn.h>
+#include <dhcp/option4_dnr.h>
#include <dhcp/option6_addrlst.h>
#include <dhcp/option6_client_fqdn.h>
+#include <dhcp/option6_dnr.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_iaprefix.h>
#include <dhcp/option6_status_code.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_definition.h>
-#include <dhcp/option_dnr.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <dhcp/option_opaque_data_tuples.h>
return (OptionPtr(new Option6PDExclude(begin, end)));
case D6O_V6_DNR:
- return (OptionPtr(new OptionDnr6(begin, end)));
+ return (OptionPtr(new Option6Dnr(begin, end)));
default:
break;
return (factoryOpaqueDataTuples(Option::V4, getCode(), begin, end, OpaqueDataTuple::LENGTH_2_BYTES));
case DHO_V4_DNR:
- return (OptionPtr(new OptionDnr4(begin, end)));
+ return (OptionPtr(new Option4Dnr(begin, end)));
default:
break;
+++ /dev/null
-// Copyright (C) 2018-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/dhcp6.h>
-#include <dhcp/opaque_data_tuple.h>
-#include <dhcp/option_dnr.h>
-#include <dns/labelsequence.h>
-#include <util/strutil.h>
-#include <set>
-
-using namespace isc::asiolink;
-
-namespace isc {
-namespace dhcp {
-
-OptionDnr6::OptionDnr6(OptionBufferConstIter begin, OptionBufferConstIter end)
- : Option(V6, D6O_V6_DNR) {
- unpack(begin, end);
-}
-
-OptionDnr6::OptionDnr6(const uint16_t service_priority,
- const std::string& adn,
- const OptionDnr6::AddressContainer& ipv6_addresses,
- const std::string& svc_params)
- : Option(V6, D6O_V6_DNR), service_priority_(service_priority),
- ipv6_addresses_(ipv6_addresses), svc_params_(svc_params) {
- setAdn(adn);
- checkFields();
-}
-
-OptionDnr6::OptionDnr6(const uint16_t service_priority, const std::string& adn)
- : Option(V6, D6O_V6_DNR), service_priority_(service_priority) {
- setAdn(adn);
-}
-
-OptionPtr
-OptionDnr6::clone() const {
- return (cloneInternal<OptionDnr6>());
-}
-
-void
-OptionDnr6::pack(util::OutputBuffer& buf, bool check) const {
- packHeader(buf, check);
-
- buf.writeUint16(service_priority_);
- buf.writeUint16(adn_length_);
- packAdn(buf);
- if (adn_only_mode_) {
- return;
- }
- buf.writeUint16(addr_length_);
- packAddresses(buf);
- packSvcParams(buf);
-}
-
-void
-OptionDnr6::packAdn(util::OutputBuffer& buf) const {
- if (!adn_) {
- // This should not happen since Encrypted DNS options are designed
- // to always include an authentication domain name.
- return;
- }
- isc::dns::LabelSequence label_sequence(*adn_);
- if (label_sequence.getDataLength() > 0) {
- size_t data_length = 0;
- const uint8_t* data = label_sequence.getData(&data_length);
- buf.writeData(data, data_length);
- }
-}
-
-void
-OptionDnr6::packAddresses(util::OutputBuffer& buf) const {
- for (const auto& address : ipv6_addresses_) {
- if (!address.isV6()) {
- isc_throw(isc::BadValue, address.toText() << " is not an IPv6 address");
- }
- buf.writeData(&address.toBytes()[0], V6ADDRESS_LEN);
- }
-}
-
-void
-OptionDnr6::packSvcParams(util::OutputBuffer& buf) const {
- if (svc_params_length_ > 0) {
- buf.writeData(&(*svc_params_.begin()), svc_params_length_);
- }
-}
-
-void
-OptionDnr6::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));
- }
- 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;
-
- // 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.
- isc::util::InputBuffer name_buf(&(*adn_tuple.getData().begin()), adn_length_);
- try {
- adn_.reset(new isc::dns::Name(name_buf, true));
- } catch (const Exception& ex) {
- isc_throw(InvalidOptionDnrDomainName, "failed to parse "
- "fully qualified domain-name from wire format "
- "- " << ex.what());
- }
-
- begin += adn_tuple.getTotalLength();
-
- if (begin == end) {
- // ADN only mode, other fields are not included.
- return;
- }
- adn_only_mode_ = false;
- if (std::distance(begin, end) < ADDR_LENGTH_SIZE) {
- 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;
- // 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");
- }
-
- // 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");
- }
-
- // 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));
- }
-
- // Let's unpack the ipv6-address(es).
- auto addr_end = begin + addr_length_;
- while (begin != addr_end) {
- ipv6_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
-OptionDnr6::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='" << getAdn() << "'";
- if (!adn_only_mode_) {
- stream << ", addr_length=" << addr_length_
- << ", address(es):";
- for (const auto& address : ipv6_addresses_) {
- stream << " " << address.toText();
- }
-
- if (svc_params_length_ > 0) {
- stream << ", svc_params='" << svc_params_ << "'";
- }
- }
-
- return (stream.str());
-}
-
-uint16_t
-OptionDnr6::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);
-}
-
-std::string
-OptionDnr6::getAdn() const {
- if (adn_) {
- return (adn_->toText());
- }
- return ("");
-}
-
-void
-OptionDnr6::checkSvcParams(bool from_wire_data) {
- std::string svc_params = isc::util::str::trim(svc_params_);
- if (svc_params.empty()) {
- isc_throw(InvalidOptionDnrSvcParams, "Provided Svc Params field is empty");
- }
- if (!from_wire_data) {
- // If Service Params field was not parsed from on-wire data,
- // but actually was provided with ctor, let's calculate svc_params_length_.
- auto svc_params_len = svc_params.length();
- if (svc_params_len > std::numeric_limits<uint16_t>::max()) {
- isc_throw(OutOfRange, "Given Svc Params length "
- << svc_params_len << " is bigger than uint_16 MAX");
- }
- svc_params_length_ = svc_params_len;
- // If Service Params field was not parsed from on-wire data,
- // but actually was provided with ctor, let's replace it with trimmed value.
- svc_params_ = svc_params;
- }
-
- // SvcParams are a whitespace-separated list, with each SvcParam
- // consisting of a SvcParamKey=SvcParamValue pair or a standalone SvcParamKey.
- // SvcParams in presentation format MAY appear in any order, but keys MUST NOT be repeated.
-
- // Let's put all elements of a whitespace-separated list into a vector.
- std::vector<std::string> tokens = isc::util::str::tokens(svc_params, " ");
-
- // Set of keys used to check if a key is not repeated.
- std::set<std::string> keys;
- // String sanitizer is used to check keys syntax.
- util::str::StringSanitizerPtr sanitizer;
- // SvcParamKeys are lower-case alphanumeric strings. Key names
- // contain 1-63 characters from the ranges "a"-"z", "0"-"9", and "-".
- std::string regex = "[^a-z0-9-]";
- sanitizer.reset(new util::str::StringSanitizer(regex, ""));
- // The service parameters MUST NOT include
- // "ipv4hint" or "ipv6hint" SvcParams as they are superseded by the
- // included IP addresses.
- std::set<std::string> forbidden_keys = {"ipv4hint", "ipv6hint"};
-
- // Now let's check each SvcParamKey=SvcParamValue pair.
- for (const std::string& token : tokens) {
- std::vector<std::string> key_val = isc::util::str::tokens(token, "=");
- if (key_val.size() > 2) {
- isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - more than one "
- "equals sign found in SvcParamKey=SvcParamValue "
- "pair");
- }
-
- // SvcParam Key related checks come below.
- std::string key = key_val[0];
- if (forbidden_keys.find(key) != forbidden_keys.end()) {
- isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - key "
- << key << " must not be used");
- }
-
- auto insert_res = keys.insert(key);
- if (!insert_res.second) {
- isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - key "
- << key << " was duplicated");
- }
-
- if (key.length() > 63) {
- isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - key had more than 63 "
- "characters - " << key);
- }
-
- std::string sanitized_key = sanitizer->scrub(key);
- if (sanitized_key.size() < key.size()) {
- isc_throw(InvalidOptionDnrSvcParams, "Wrong Svc Params syntax - invalid character "
- "used in key - " << key);
- }
-
- if (key_val.size() == 2) {
- // tbd Check value syntax
- std::string value = key_val[1];
- }
- }
-
-}
-
-void
-OptionDnr6::setAdn(const std::string& adn) {
- std::string trimmed_adn = isc::util::str::trim(adn);
- if (trimmed_adn.empty()) {
- isc_throw(InvalidOptionDnrDomainName, "Mandatory Authentication Domain Name fully "
- "qualified domain-name must not be empty");
- }
- try {
- adn_.reset(new isc::dns::Name(trimmed_adn, true));
- } catch (const Exception& ex) {
- isc_throw(InvalidOptionDnrDomainName, "Failed to parse "
- "fully qualified domain-name from string "
- "- " << ex.what());
- }
- size_t adn_len = 0;
- isc::dns::LabelSequence label_sequence(*adn_);
- label_sequence.getData(&adn_len);
- if (adn_len > std::numeric_limits<uint16_t>::max()) {
- isc_throw(InvalidOptionDnrDomainName, "Given ADN FQDN length "
- << adn_len << " is bigger than uint_16 MAX");
- }
-
- adn_length_ = adn_len;
-}
-
-void
-OptionDnr6::checkFields() {
- if (svc_params_.empty() && ipv6_addresses_.empty()) {
- // ADN only mode, nothing more to do.
- return;
- }
- if(!svc_params_.empty() && ipv6_addresses_.empty()) {
- // 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.
- isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option ("
- << type_ << ")"
- << " malformed: No IPv6 address given. Since this is not "
- "ADN only mode, at least one valid IP address must be included");
-
- }
- if(!svc_params_.empty()) {
- checkSvcParams(false);
- }
- adn_only_mode_ = false;
- auto addr_len = ipv6_addresses_.size() * V6ADDRESS_LEN;
- if (addr_len > std::numeric_limits<uint16_t>::max()) {
- isc_throw(OutOfRange, "Given IPv6 addresses length "
- << addr_len << " is bigger than uint_16 MAX");
- }
- addr_length_ = addr_len;
-}
-
-OptionDnr4::OptionDnr4() : Option(V4, DHO_V4_DNR) {
-}
-
-OptionDnr4::OptionDnr4(OptionBufferConstIter begin, OptionBufferConstIter end)
- : Option(V4, DHO_V4_DNR) {
- unpack(begin, end);
-}
-
-OptionPtr
-OptionDnr4::clone() const {
- return Option::clone();
-}
-
-void
-OptionDnr4::pack(util::OutputBuffer& buf, bool check) const {
- Option::pack(buf, check);
-}
-
-void
-OptionDnr4::unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
- Option::unpack(begin, end);
-}
-
-std::string
-OptionDnr4::toText(int indent) const {
- return Option::toText(indent);
-}
-
-uint16_t
-OptionDnr4::len() const {
- return Option::len();
-}
-
-} // namespace dhcp
-} // namespace isc
#include <asiolink/io_address.h>
#include <dhcp/dhcp6.h>
#include <dhcp/opaque_data_tuple.h>
-#include <dhcp/option_dnr.h>
+#include <dhcp/option6_dnr.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
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())));
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_NO_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())));
ASSERT_TRUE(option);
// Check if member variables were correctly set by ctor.
// 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("myhost.example.com.", option->getAdnAsText());
// This is ADN only mode, so Addr Length and SvcParams Length
// are both expected to be zero.
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);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange);
ASSERT_FALSE(option);
}
OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
// Create option instance. Check that constructor throws InvalidOptionDnrDomainName exception.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), InvalidOptionDnrDomainName);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrDomainName);
ASSERT_FALSE(option);
}
// Create option instance. Encrypted DNS options are designed to ALWAYS include
// an authentication domain name, so check that constructor throws
// InvalidOptionDnrDomainName exception.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), InvalidOptionDnrDomainName);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrDomainName);
ASSERT_FALSE(option);
}
OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
// Create option instance. Check that constructor throws OpaqueDataTupleError exception.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OpaqueDataTupleError);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OpaqueDataTupleError);
ASSERT_FALSE(option);
}
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);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange);
ASSERT_FALSE(option);
}
// Create option instance. Check that constructor throws OutOfRange exception.
// If additional data is supplied (i.e. not ADN only mode),
// the option includes at least one valid IP address.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OutOfRange);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange);
ASSERT_FALSE(option);
}
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);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange);
ASSERT_FALSE(option);
}
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())));
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_NO_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())));
ASSERT_TRUE(option);
// Check if member variables were correctly set by ctor.
// 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("myhost.example.com.", option->getAdnAsText());
EXPECT_EQ(48, option->getAddrLength());
- const OptionDnr6::AddressContainer& addresses = option->getAddresses();
+ const Option6Dnr::AddressContainer& addresses = option->getAddresses();
EXPECT_EQ(3, addresses.size());
EXPECT_EQ("2001:db8:1::dead:beef", addresses[0].toText());
EXPECT_EQ("ff02::face:b00c", addresses[1].toText());
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);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), OutOfRange);
ASSERT_FALSE(option);
}
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())));
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_NO_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())));
ASSERT_TRUE(option);
// Check if member variables were correctly set by ctor.
// 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("myhost.example.com.", option->getAdnAsText());
EXPECT_EQ(16, option->getAddrLength());
- const OptionDnr6::AddressContainer& addresses = option->getAddresses();
+ const Option6Dnr::AddressContainer& addresses = option->getAddresses();
EXPECT_EQ(1, addresses.size());
EXPECT_EQ("2001:db8:1::dead:beef", addresses[0].toText());
EXPECT_EQ(3, option->getSvcParamsLength());
OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
// Create option instance. Check that constructor throws InvalidOptionDnrSvcParams exception.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), InvalidOptionDnrSvcParams);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(buf.begin(), buf.end())), InvalidOptionDnrSvcParams);
ASSERT_FALSE(option);
}
const std::string adn = "myhost.example.com.";
// Create option instance. Check that constructor doesn't throw.
- scoped_ptr<OptionDnr6> option;
- EXPECT_NO_THROW(option.reset(new OptionDnr6(service_priority, adn)));
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_NO_THROW(option.reset(new Option6Dnr(service_priority, adn)));
ASSERT_TRUE(option);
// Check if member variables were correctly set by ctor.
EXPECT_EQ(D6O_V6_DNR, option->getType());
EXPECT_EQ(service_priority, option->getServicePriority());
EXPECT_EQ(20, option->getAdnLength());
- EXPECT_EQ(adn, option->getAdn());
+ EXPECT_EQ(adn, option->getAdnAsText());
// This is ADN only mode, so Addr Length and SvcParams Length
// are both expected to be zero.
const std::string adn; // invalid empty ADN
// Create option instance. Check that constructor throws.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn)), InvalidOptionDnrDomainName);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn)), InvalidOptionDnrDomainName);
ASSERT_FALSE(option);
}
// Prepare example parameters
const uint16_t service_priority = 9;
const std::string adn = "myhost.example.com.";
- OptionDnr6::AddressContainer addresses;
+ Option6Dnr::AddressContainer addresses;
addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca"));
const std::string svc_params = "alpn";
// Create option instance. Check that constructor doesn't throw.
- scoped_ptr<OptionDnr6> option;
- EXPECT_NO_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)));
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_NO_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)));
ASSERT_TRUE(option);
// Check if member variables were correctly set by ctor.
EXPECT_EQ(D6O_V6_DNR, option->getType());
EXPECT_EQ(service_priority, option->getServicePriority());
EXPECT_EQ(20, option->getAdnLength());
- EXPECT_EQ(adn, option->getAdn());
+ EXPECT_EQ(adn, option->getAdnAsText());
EXPECT_EQ(16, option->getAddrLength());
EXPECT_EQ(4, option->getSvcParamsLength());
EXPECT_EQ(svc_params, option->getSvcParams());
// Prepare example parameters
const uint16_t service_priority = 9;
const std::string adn = "myhost.example.com.";
- const OptionDnr6::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<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), OutOfRange);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), OutOfRange);
ASSERT_FALSE(option);
}
// Prepare example parameters.
const uint16_t service_priority = 9;
const std::string adn = "myhost.example.com.";
- OptionDnr6::AddressContainer addresses;
+ 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
// Create option instance. Check that constructor throws.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
ASSERT_FALSE(option);
}
// Prepare example parameters.
const uint16_t service_priority = 9;
const std::string adn = "myhost.example.com.";
- OptionDnr6::AddressContainer addresses;
+ 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
// Create option instance. Check that constructor throws.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
ASSERT_FALSE(option);
}
// Prepare example parameters.
const uint16_t service_priority = 9;
const std::string adn = "myhost.example.com.";
- OptionDnr6::AddressContainer addresses;
+ 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
// Create option instance. Check that constructor throws.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
ASSERT_FALSE(option);
}
// Prepare example parameters.
const uint16_t service_priority = 9;
const std::string adn = "myhost.example.com.";
- OptionDnr6::AddressContainer addresses;
+ Option6Dnr::AddressContainer addresses;
addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca"));
const std::string svc_params = "thisisveryveryveryvery"
"veryveryveryveryveryvery"
"veryveryveryveryvlongkey"; // svc param key longer than 63
// Create option instance. Check that constructor throws.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
ASSERT_FALSE(option);
}
// Prepare example parameters.
const uint16_t service_priority = 9;
const std::string adn = "myhost.example.com.";
- OptionDnr6::AddressContainer addresses;
+ Option6Dnr::AddressContainer addresses;
addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca"));
const std::string svc_params = "alpn=h2 NOT_ALLOWED_CHARS_KEY=123"; // svc param key has forbidden chars
// Create option instance. Check that constructor throws.
- scoped_ptr<OptionDnr6> option;
- EXPECT_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)), InvalidOptionDnrSvcParams);
ASSERT_FALSE(option);
}
// Prepare example parameters.
const uint16_t service_priority = 9;
const std::string adn = "myhost.example.com.";
- OptionDnr6::AddressContainer addresses;
+ Option6Dnr::AddressContainer addresses;
addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::baca"));
const std::string svc_params = "alpn";
// Create option instance. Check that constructor doesn't throw.
- scoped_ptr<OptionDnr6> option;
- EXPECT_NO_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)));
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_NO_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)));
ASSERT_TRUE(option);
const int indent = 4;
const std::string adn = "myhost.example.com.";
// Create option instance. Check that constructor doesn't throw.
- scoped_ptr<OptionDnr6> option;
- EXPECT_NO_THROW(option.reset(new OptionDnr6(service_priority, adn)));
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_NO_THROW(option.reset(new Option6Dnr(service_priority, adn)));
ASSERT_TRUE(option);
// Prepare on-wire format of the option.
// Prepare example parameters.
const uint16_t service_priority = 9;
const std::string adn = "myhost.example.com.";
- OptionDnr6::AddressContainer addresses;
+ Option6Dnr::AddressContainer addresses;
addresses.push_back(isc::asiolink::IOAddress("2001:db8:1::dead:beef"));
addresses.push_back(isc::asiolink::IOAddress("ff02::face:b00c"));
const std::string svc_params = "alpn";
// Create option instance. Check that constructor doesn't throw.
- scoped_ptr<OptionDnr6> option;
- EXPECT_NO_THROW(option.reset(new OptionDnr6(service_priority, adn, addresses, svc_params)));
+ scoped_ptr<Option6Dnr> option;
+ EXPECT_NO_THROW(option.reset(new Option6Dnr(service_priority, adn, addresses, svc_params)));
ASSERT_TRUE(option);
// Prepare on-wire format of the option.