// Copyright (C) 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 OPTION4_DNR_H #define OPTION4_DNR_H #include #include #include #include #include #include #include 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 DNR Instance which is used both in DHCPv4 /// and DHCPv6 Encrypted DNS %Option. /// /// DNR Instance includes the configuration data of an encrypted DNS resolver. /// It is used to build OPTION_V4_DNR (code 162). There may be multiple DNR Instances /// in one OPTION_V4_DNR %Option. OPTION_V6_DNR (code 144) is using very similar structure, /// only that there must be only one DNR Instance per one OPTION_V6_DNR %Option. That's why /// @c Option6Dnr class can derive from this @c DnrInstance class, whereas @c Option4Dnr class /// should have a container of @c DnrInstance's. /// /// DNR Instance Data Format has been defined in the @c draft-ietf-add-dnr (to be replaced /// with published RFC). class DnrInstance { public: /// @brief A Type defined for container holding IP addresses. typedef std::vector AddressContainer; /// @brief Size in octets of Service Priority field. static const uint8_t SERVICE_PRIORITY_SIZE = 2; /// @brief Set of forbidden SvcParams. /// /// The service parameters MUST NOT include /// "ipv4hint" or "ipv6hint" SvcParams as they are superseded by the /// included IP addresses. static const std::unordered_set FORBIDDEN_SVC_PARAMS; /// @brief Constructor of the empty DNR Instance. /// /// @param universe either V4 or V6 Option universe explicit DnrInstance(Option::Universe universe); /// @brief Constructor of the DNR Instance with all fields from params. /// /// Constructor of the DNR Instance where all fields /// i.e. Service priority, ADN, IP address(es) and Service params /// are provided as ctor parameters. /// /// @param universe either V4 or V6 Option universe /// @param service_priority Service priority /// @param adn ADN FQDN /// @param ip_addresses Container of IP addresses /// @param svc_params Service Parameters /// /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN /// @throw InvalidOptionDnrSvcParams Thrown when @c checkSvcParams(from_wire_data) throws /// @throw OutOfRange Thrown in case of no IP addresses found or when IP addresses length /// is too big DnrInstance(Option::Universe universe, uint16_t service_priority, const std::string& adn, const AddressContainer& ip_addresses, const std::string& svc_params); /// @brief Constructor of the DNR Instance in ADN only mode. /// /// Constructor of the DNR Instance in ADN only mode /// i.e. only Service priority and ADN FQDN /// are provided as ctor parameters. /// /// @param universe either V4 or V6 Option universe /// @param service_priority Service priority /// @param adn ADN FQDN /// /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN DnrInstance(Option::Universe universe, uint16_t service_priority, const std::string& adn); /// @brief Default destructor. virtual ~DnrInstance() = default; /// @brief Getter of the @c dnr_instance_data_length_. /// /// @return Length of all following data inside this DNR instance in octets. uint16_t getDnrInstanceDataLength() const { return (dnr_instance_data_length_); } /// @brief Getter of the @c service_priority_. /// /// @return The priority of this 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 getAdnAsText() const; /// @brief Returns string representation of the DNR instance. /// /// @return String with text representation. std::string getDnrInstanceAsText() const; /// @brief Getter of the @c addr_length_. /// /// @return Length of enclosed IP addresses in octets. uint16_t getAddrLength() const { return (addr_length_); } /// @brief Getter of the @c svc_params_length_. /// /// @return Length of Service Parameters field in octets. uint16_t getSvcParamsLength() const { return (svc_params_length_); } /// @brief Returns vector with addresses. /// /// We return a copy of our list. Although this includes overhead, /// it also makes this list safe to use after this option object /// is no longer available. As options are expected to hold only /// a few (1-3) addresses, the overhead is not that big. /// /// @return Address container with addresses. AddressContainer getAddresses() const { return (ip_addresses_); } /// @brief Getter of the @c svc_params_ field. /// /// @return Returns Service Parameters as a string. const std::string& getSvcParams() const { return (svc_params_); } /// @brief Returns minimal length of the DNR instance data (without headers) in octets. /// /// @return Minimal length of the DNR instance data (without headers) in octets. uint8_t getMinimalLength() const { return (minimal_length_); } /// @brief Returns size in octets of Addr Length field. uint8_t getAddrLengthSize() const { return (addr_length_size_); } /// @brief Returns size in octets of DNR Instance Data Length field. uint8_t getDnrInstanceDataLengthSize() const { return (dnr_instance_data_length_size_); } /// @brief Returns size in octets of ADN Length field. uint8_t getAdnLengthSize() const { return (adn_length_size_); } /// @brief Returns Log prefix depending on V4/V6 Option universe. /// /// @return Log prefix as a string which can be used for prints when throwing an exception. std::string getLogPrefix() const { return (log_prefix_); } /// @brief Returns whether ADN only mode is enabled or disabled. bool isAdnOnlyMode() const { return (adn_only_mode_); } /// @brief Sets Authentication domain name from given string. /// /// 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. /// /// @param adn string representation of ADN FQDN /// /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN /// from given string. void setAdn(const std::string& adn); /// @brief Setter of the @c adn_only_mode_ field. /// /// @param adn_only_mode enabled/disabled setting void setAdnOnlyMode(bool adn_only_mode) { adn_only_mode_ = adn_only_mode; } /// @brief Writes the ADN FQDN in the wire format into a buffer. /// /// The Authentication Domain Name - fully qualified domain name of the encrypted /// DNS resolver data is appended at the end of the buffer. /// /// @param [out] buf buffer where ADN FQDN will be written. /// /// @throw InvalidOptionDnrDomainName Thrown when mandatory field ADN is empty. void packAdn(isc::util::OutputBuffer& buf) const; /// @brief Writes the IP address(es) in the wire format into a buffer. /// /// The IP address(es) (@c ip_addresses_) data is appended at the end /// of the buffer. /// /// @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. /// /// The Service Parameters (@c svc_params_) data is appended at the end /// of the buffer. /// /// @param [out] buf buffer where SvcParams will be written. void packSvcParams(isc::util::OutputBuffer& buf) const; /// @brief Unpacks DNR Instance Data Length from wire data buffer and stores /// it in @c dnr_instance_data_length_. /// /// It may throw in case of malformed data detected during parsing. /// /// @param begin beginning of the buffer from which the field will be read /// @param end end of the buffer from which the field will be read /// /// @throw OutOfRange Thrown in case of truncated data detected. void unpackDnrInstanceDataLength(OptionBufferConstIter& begin, OptionBufferConstIter end); /// @brief Unpacks Service Priority from wire data buffer and stores it in @c service_priority_. /// /// @param begin beginning of the buffer from which the field will be read void unpackServicePriority(OptionBufferConstIter& begin); /// @brief Unpacks the ADN from given wire data buffer and stores it in @c adn_ field. /// /// It may throw in case of malformed data detected during parsing. /// /// @param begin beginning of the buffer from which the ADN will be read /// @param end end of the buffer from which the ADN will be read /// /// @throw BadValue Thrown in case of any issue with unpacking opaque data of the ADN. /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN /// from given wire data. void unpackAdn(OptionBufferConstIter& begin, OptionBufferConstIter end); /// @brief Unpacks IP address(es) from wire data and stores it/them in @c ip_addresses_. /// /// It may throw in case of malformed data detected during parsing. /// /// @param begin beginning of the buffer from which the field will be read /// @param end end of the buffer from which the field will be read /// /// @throw BadValue Thrown in case of any issue with unpacking opaque data of the IP addresses. /// @throw OutOfRange Thrown in case of malformed data detected during parsing e.g. /// Addr Len not divisible by 4, Addr Len is 0. virtual void unpackAddresses(OptionBufferConstIter& begin, OptionBufferConstIter end); /// @brief Unpacks Service Parameters from wire data buffer and stores it in @c svc_params_. /// /// It may throw in case of malformed data detected during parsing. /// /// @param begin beginning of the buffer from which the field will be read /// @param end end of the buffer from which the field will be read void unpackSvcParams(OptionBufferConstIter& begin, OptionBufferConstIter end); /// @brief Checks SvcParams field if encoded correctly and throws in case of issue found. /// /// The field should be encoded following the rules in /// Section 2.1 of [I-D.ietf-dnsop-svcb-https]. SvcParams are /// a whitespace-separated list, with each SvcParam consisting of /// a SvcParamKey=SvcParamValue pair or a standalone SvcParamKey. /// /// @note It is user's responsibility to provide correct configuration /// of @c SvcParams as described in Section 2.1 of [I-D.ietf-dnsop-svcb-https]. /// Currently, SvcParamValue is not verified. Proper syntax of SvcParamValue /// is described in Appendix A of [I-D.ietf-dnsop-svcb-https]. /// /// @param from_wire_data used to determine whether SvcParams data comes /// from unpacked wire data or from ctor param /// /// @throw InvalidOptionDnrSvcParams Thrown in case of any issue found when checking /// @c ServiceParams field syntax void checkSvcParams(bool from_wire_data = true); /// @brief Checks IP address(es) field if data is correct and throws in case of issue found. /// /// Fields lengths are also calculated and saved to member variables. /// /// @throw OutOfRange Thrown in case of no IP addresses found or when IP addresses length /// is too big /// @throw InvalidOptionDnrSvcParams Thrown when @c checkSvcParams(from_wire_data) throws void checkFields(); /// @brief Adds IP address to @c ip_addresses_ container. /// /// @param ip_address IP address to be added void addIpAddress(const asiolink::IOAddress& ip_address); protected: /// @brief Either V4 or V6 Option universe. Option::Universe universe_; /// @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 adn_; /// @brief Length of all following data inside this DNR instance in octets. /// /// 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_; /// @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_; /// @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_; /// @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_; /// @brief Calculates and returns length of DNR Instance data in octets. /// @return length of DNR Instance data in octets. uint16_t dnrInstanceLen() const; private: /// @brief Size in octets of DNR Instance Data Length field. /// /// @note This field is used only in case of V4 DNR option. uint8_t dnr_instance_data_length_size_; /// @brief Size in octets of ADN Length field. uint8_t adn_length_size_; /// @brief Size in octets of Addr Length field. uint8_t addr_length_size_; /// @brief Minimal length of the DNR instance data (without headers) in octets. /// /// @note If the ADN-only mode is used, then "Addr Length", "ip(v4/v6)-address(es)", /// and "Service Parameters (SvcParams)" fields are not present. /// So minimal length of data is calculated by adding 2 octets for Service Priority, /// octets needed for ADN Length and octets needed for DNR Instance Data Length /// (only in case of DHCPv4). uint8_t minimal_length_; /// @brief Log prefix as a string which can be used for prints when throwing an exception. std::string log_prefix_; /// @brief Initializes private member variables basing on option's V4/V6 Universe. /// /// @note It must be called in all types of constructors of class @c DnrInstance . void initMembers(); }; /// @brief Represents DHCPv4 Encrypted DNS %Option (code 162). /// /// This option has been defined in the @c draft-ietf-add-dnr (to be replaced /// with published RFC) and it has a following structure: /// - option-code = 162 (1 octet) /// - option-len (1 octet) /// - multiple (one or more) DNR Instance Data /// /// DNR Instance Data structure: /// - DNR Instance Data Length (2 octets) /// - Service Priority (2 octets) /// - ADN Length (1 octet) /// - Authentication Domain Name (variable length) /// - Addr Length (1 octet) /// - IPv4 Address(es) (variable length) /// - Service Parameters (variable length). class Option4Dnr : public Option { public: /// @brief A Type defined for container holding DNR Instances. typedef std::vector DnrInstanceContainer; /// @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. /// /// @throw OutOfRange Thrown in case of truncated data. May be also thrown when /// @c DnrInstance::unpackDnrInstanceDataLength(begin,end) throws. /// @throw BadValue Thrown when @c DnrInstance::unpackAdn(begin,end) throws. /// @throw InvalidOptionDnrDomainName Thrown when @c DnrInstance::unpackAdn(begin,end) throws. Option4Dnr(OptionBufferConstIter begin, OptionBufferConstIter end); /// @brief Constructor of the empty %Option. /// /// No DNR instances are included in the %Option when using this ctor. /// They must be added afterwards. /// No fields data included in the %Option when using this ctor. /// They must be added afterwards. Option4Dnr() : Option(V4, DHO_V4_DNR) {} /// @brief Adds given DNR instance to Option's DNR Instance container. /// @param dnr_instance DNR instance to be added void addDnrInstance(DnrInstance& dnr_instance); /// @brief Getter of the @c dnr_instances_ field. /// @return Reference to Option's DNR Instance container const DnrInstanceContainer& getDnrInstances() const { return (dnr_instances_); } /// @brief Copies this option and returns a pointer to the copy. /// /// @return Pointer to the copy of the option. OptionPtr clone() const override; /// @brief Writes option in wire-format to a buffer. /// /// Writes option in wire-format to buffer, returns pointer to first unused /// byte after stored option (that is useful for writing options one after /// another). /// /// @param buf pointer to a buffer /// @param check flag which indicates if checking the option length is /// required (used only in V4) /// /// @throw InvalidOptionDnrDomainName Thrown when Option's mandatory field ADN is empty. /// @throw OutOfRange Thrown when @c check param set to @c true and /// @c Option::packHeader(buf,check) throws. void pack(util::OutputBuffer& buf, bool check = true) const override; /// @brief Parses received wire data buffer. /// /// @param begin iterator to first byte of option data /// @param end iterator to end of option data (first byte after option end) /// /// @throw OutOfRange Thrown in case of truncated data. May be also thrown when /// @c DnrInstance::unpackDnrInstanceDataLength(begin,end) throws. /// @throw BadValue Thrown when @c DnrInstance::unpackAdn(begin,end) throws. /// @throw InvalidOptionDnrDomainName Thrown when @c DnrInstance::unpackAdn(begin,end) throws. void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) override; /// @brief Returns string representation of the option. /// /// @param indent number of spaces before printing text /// /// @return string with text representation. std::string toText(int indent = 0) const override; /// @brief Returns length of the complete option (data length + DHCPv4/DHCPv6 /// option header) /// /// @return length of the option uint16_t len() const override; protected: /// @brief Container holding DNR Instances. DnrInstanceContainer dnr_instances_; }; /// A pointer to the @c OptionDnr4 object. typedef boost::shared_ptr Option4DnrPtr; } // namespace dhcp } // namespace isc #endif // OPTION4_DNR_H