summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/option_data_types.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcp/option_data_types.h')
-rw-r--r--src/lib/dhcp/option_data_types.h668
1 files changed, 668 insertions, 0 deletions
diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h
new file mode 100644
index 0000000..3b35dea
--- /dev/null
+++ b/src/lib/dhcp/option_data_types.h
@@ -0,0 +1,668 @@
+// Copyright (C) 2012-2022 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 OPTION_DATA_TYPES_H
+#define OPTION_DATA_TYPES_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/opaque_data_tuple.h>
+#include <dhcp/option.h>
+#include <exceptions/exceptions.h>
+#include <util/io_utilities.h>
+
+#include <stdint.h>
+#include <utility>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Exception to be thrown when invalid type specified as template parameter.
+class InvalidDataType : public Exception {
+public:
+ InvalidDataType(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception to be thrown when cast to the data type was unsuccessful.
+class BadDataTypeCast : public Exception {
+public:
+ BadDataTypeCast(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Data types of DHCP option fields.
+///
+/// @warning The order of data types matters: OPT_UNKNOWN_TYPE
+/// must always be the last position. Also, OPT_RECORD_TYPE
+/// must be at last but one position. This is because some
+/// functions perform sanity checks on data type values using
+/// '>' operators, assuming that all values beyond the
+/// OPT_RECORD_TYPE are invalid.
+enum OptionDataType {
+ OPT_EMPTY_TYPE,
+ OPT_BINARY_TYPE,
+ OPT_BOOLEAN_TYPE,
+ OPT_INT8_TYPE,
+ OPT_INT16_TYPE,
+ OPT_INT32_TYPE,
+ OPT_UINT8_TYPE,
+ OPT_UINT16_TYPE,
+ OPT_UINT32_TYPE,
+ OPT_ANY_ADDRESS_TYPE,
+ OPT_IPV4_ADDRESS_TYPE,
+ OPT_IPV6_ADDRESS_TYPE,
+ OPT_IPV6_PREFIX_TYPE,
+ OPT_PSID_TYPE,
+ OPT_STRING_TYPE,
+ OPT_TUPLE_TYPE,
+ OPT_FQDN_TYPE,
+ OPT_RECORD_TYPE,
+ OPT_UNKNOWN_TYPE
+};
+
+/// @brief Parameters being used to make up an option definition.
+struct OptionDefParams {
+ const char* name; // option name
+ uint16_t code; // option code
+ const char* space; // option space
+ OptionDataType type; // data type
+ bool array; // is array
+ const OptionDataType* records; // record fields
+ size_t records_size; // number of fields in a record
+ const char* encapsulates; // option space encapsulated by the
+ // particular option.
+};
+
+/// @brief Encapsulation of option definition parameters and the structure size.
+struct OptionDefParamsEncapsulation {
+ const struct OptionDefParams* optionDefParams; // parameters structure
+ const int size; // structure size
+ const char* space; // option space
+};
+
+/// @brief Trait class for data types supported in DHCP option definitions.
+///
+/// This is useful to check whether the type specified as template parameter
+/// is supported by classes like OptionInt, OptionIntArray and some template
+/// factory functions in OptionDefinition class.
+template<typename T>
+struct OptionDataTypeTraits {
+ static const bool valid = false;
+ static const int len = 0;
+ static const bool integer_type = false;
+ static const OptionDataType type = OPT_UNKNOWN_TYPE;
+};
+
+/// binary type is supported
+template<>
+struct OptionDataTypeTraits<OptionBuffer> {
+ static const bool valid = true;
+ static const int len = 0;
+ static const bool integer_type = false;
+ static const OptionDataType type = OPT_BINARY_TYPE;
+};
+
+/// bool type is supported
+template<>
+struct OptionDataTypeTraits<bool> {
+ static const bool valid = true;
+ static const int len = sizeof(uint8_t);
+ static const bool integer_type = false;
+ static const OptionDataType type = OPT_BOOLEAN_TYPE;
+};
+
+/// int8_t type is supported.
+template<>
+struct OptionDataTypeTraits<int8_t> {
+ static const bool valid = true;
+ static const int len = 1;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_INT8_TYPE;
+};
+
+/// int16_t type is supported.
+template<>
+struct OptionDataTypeTraits<int16_t> {
+ static const bool valid = true;
+ static const int len = 2;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_INT16_TYPE;
+};
+
+/// int32_t type is supported.
+template<>
+struct OptionDataTypeTraits<int32_t> {
+ static const bool valid = true;
+ static const int len = 4;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_INT32_TYPE;
+};
+
+/// uint8_t type is supported.
+template<>
+struct OptionDataTypeTraits<uint8_t> {
+ static const bool valid = true;
+ static const int len = 1;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_UINT8_TYPE;
+};
+
+/// uint16_t type is supported.
+template<>
+struct OptionDataTypeTraits<uint16_t> {
+ static const bool valid = true;
+ static const int len = 2;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_UINT16_TYPE;
+};
+
+/// uint32_t type is supported.
+template<>
+struct OptionDataTypeTraits<uint32_t> {
+ static const bool valid = true;
+ static const int len = 4;
+ static const bool integer_type = true;
+ static const OptionDataType type = OPT_UINT32_TYPE;
+};
+
+/// IPv4 and IPv6 address type is supported
+template<>
+struct OptionDataTypeTraits<asiolink::IOAddress> {
+ static const bool valid = true;
+ // The len value is used to determine the size of the data
+ // to be written to an option buffer. IOAddress object may
+ // either represent an IPv4 or IPv6 addresses which have
+ // different lengths. Thus we can't put fixed value here.
+ // The length of a data to be written into an option buffer
+ // have to be determined in the runtime for a particular
+ // IOAddress object. Thus setting len to zero.
+ static const int len = 0;
+ static const bool integer_type = false;
+ static const OptionDataType type = OPT_ANY_ADDRESS_TYPE;
+};
+
+/// string type is supported
+template<>
+struct OptionDataTypeTraits<std::string> {
+ static const bool valid = true;
+ // The len value is used to determine the size of the data
+ // to be written to an option buffer. For strings this
+ // size is unknown until we actually deal with the particular
+ // string to be written. Thus setting it to zero.
+ static const int len = 0;
+ static const bool integer_type = false;
+ static const OptionDataType type = OPT_STRING_TYPE;
+};
+
+/// @brief Encapsulates PSID length.
+class PSIDLen {
+public:
+
+ /// @brief Default constructor.
+ PSIDLen() : psid_len_(0) { }
+
+ /// @brief Constructor.
+ ///
+ /// It checks that the specified value is not greater than
+ /// 16, which is a maximum value for the PSID length.
+ ///
+ /// @param psid_len PSID length.
+ /// @throw isc::OutOfRange If specified PSID length is greater than 16.
+ explicit PSIDLen(const uint8_t psid_len)
+ : psid_len_(psid_len) {
+ if (psid_len_ > sizeof(uint16_t) * 8) {
+ isc_throw(isc::OutOfRange, "invalid value "
+ << asUnsigned() << " of PSID length");
+ }
+ }
+
+ /// @brief Returns PSID length as uint8_t value.
+ uint8_t asUint8() const {
+ return (psid_len_);
+ }
+
+ /// @brief Returns PSID length as unsigned int.
+ ///
+ /// This is useful to convert the value to a numeric type which
+ /// can be logged directly. Note that the uint8_t value has to
+ /// be cast to an integer value to be logged as a number. This
+ /// is because the uint8_t is often implemented as char, in which
+ /// case directly logging an uint8_t value prints a character rather
+ /// than a number.
+ unsigned int asUnsigned() const {
+ return (static_cast<unsigned>(psid_len_));
+ }
+
+private:
+
+ /// @brief PSID length.
+ uint8_t psid_len_;
+};
+
+/// @brief Encapsulates PSID value.
+class PSID {
+public:
+
+ /// @brief Default constructor.
+ PSID() : psid_(0) { }
+
+ /// @brief Constructor.
+ ///
+ /// This constructor doesn't perform any checks on the input data.
+ ///
+ /// @param psid PSID value.
+ explicit PSID(const uint16_t psid)
+ : psid_(psid) {
+ }
+
+ /// @brief Returns PSID value as a number.
+ uint16_t asUint16() const {
+ return (psid_);
+ }
+
+private:
+
+ /// @brief PSID value.
+ uint16_t psid_;
+
+};
+
+/// @brief Defines a pair of PSID length / value.
+typedef std::pair<PSIDLen, PSID> PSIDTuple;
+
+/// @brief Encapsulates prefix length.
+class PrefixLen {
+public:
+
+ /// @brief Default constructor.
+ PrefixLen() : prefix_len_(0) { }
+
+ /// @brief Constructor.
+ ///
+ /// This constructor checks if the specified prefix length is
+ /// in the range of 0 to 128.
+ ///
+ /// @param prefix_len Prefix length value.
+ /// @throw isc::OutOfRange If specified prefix length is greater than 128.
+ explicit PrefixLen(const uint8_t prefix_len)
+ : prefix_len_(prefix_len) {
+ }
+
+ /// @brief Returns prefix length as uint8_t value.
+ uint8_t asUint8() const {
+ return (prefix_len_);
+ }
+
+ /// @brief Returns prefix length as unsigned int.
+ ///
+ /// This is useful to convert the value to a numeric type which
+ /// can be logged directly. See @ref PSIDLen::asUnsigned for the
+ /// use cases of this accessor.
+ unsigned int asUnsigned() const {
+ return (static_cast<unsigned>(prefix_len_));
+ }
+
+private:
+
+ /// @brief Prefix length.
+ uint8_t prefix_len_;
+};
+
+/// @brief Defines a pair of prefix length / value.
+typedef std::pair<PrefixLen, asiolink::IOAddress> PrefixTuple;
+
+/// @brief Utility class for option data types.
+///
+/// This class provides a set of utility functions to operate on
+/// supported DHCP option data types. It includes conversion
+/// between enumerator values representing data types and data
+/// type names. It also includes a set of functions that write
+/// data into option buffers and read data from option buffers.
+/// The data being written and read are converted from/to actual
+/// data types.
+/// @note This is a singleton class but it can be accessed via
+/// static methods only.
+class OptionDataTypeUtil {
+public:
+
+ /// @brief Return option data type from its name.
+ ///
+ /// @param data_type data type name.
+ /// @return option data type.
+ static OptionDataType getDataType(const std::string& data_type);
+
+ /// @brief Return option data type name from the data type enumerator.
+ ///
+ /// @param data_type option data type.
+ /// @return option data type name.
+ static const std::string& getDataTypeName(const OptionDataType data_type);
+
+ /// @brief Get data type buffer length.
+ ///
+ /// This function returns the size of a particular data type.
+ /// Values returned by this function correspond to the data type
+ /// sizes defined in OptionDataTypeTraits (IPV4_ADDRESS_TYPE and
+ /// IPV6_ADDRESS_TYPE are exceptions here) so they rather indicate
+ /// the fixed length of the data being written into the buffer,
+ /// not the size of the particular data type. Thus for data types
+ /// such as string, binary etc. for which the buffer length can't
+ /// be determined this function returns 0.
+ /// In addition, this function returns the data sizes for
+ /// IPV4_ADDRESS_TYPE and IPV6_ADDRESS_TYPE as their buffer
+ /// representations have fixed data lengths: 4 and 16 respectively.
+ ///
+ /// @param data_type data type which size is to be returned.
+ /// @return data type size or zero for variable length types.
+ static int getDataTypeLen(const OptionDataType data_type);
+
+ /// @brief Read IPv4 or IPv6 address from a buffer.
+ ///
+ /// @param buf input buffer.
+ /// @param family address family: AF_INET or AF_INET6.
+ ///
+ /// @throw isc::dhcp::BadDataTypeCast when the data being read
+ /// is truncated.
+ /// @return address being read.
+ static asiolink::IOAddress readAddress(const std::vector<uint8_t>& buf,
+ const short family);
+
+ /// @brief Append IPv4 or IPv6 address to a buffer.
+ ///
+ /// @param address IPv4 or IPv6 address.
+ /// @param [out] buf output buffer.
+ static void writeAddress(const asiolink::IOAddress& address,
+ std::vector<uint8_t>& buf);
+
+ /// @brief Append hex-encoded binary values to a buffer.
+ ///
+ /// @param hex_str string representing a binary value encoded
+ /// with hexadecimal digits (without 0x prefix).
+ /// @param [out] buf output buffer.
+ static void writeBinary(const std::string& hex_str,
+ std::vector<uint8_t>& buf);
+
+ /// @brief Read length and string tuple from a buffer.
+ ///
+ /// @param buf input buffer.
+ /// @param lengthfieldtype LENGTH_1_BYTE (DHCPv4) or LENGTH_2_BYTES (DHCPv6)
+ /// @throw isc::dhcp::BadDataTypeCast when the data being read
+ /// is truncated.
+ /// @return string being read.
+ static std::string readTuple(const std::vector<uint8_t>& buf,
+ OpaqueDataTuple::LengthFieldType lengthfieldtype);
+
+ /// @brief Read length and string tuple from a buffer.
+ ///
+ /// @param buf input buffer.
+ /// @param tuple reference of the tuple to read into
+ /// @throw isc::dhcp::BadDataTypeCast when the data being read
+ /// is truncated.
+ static void readTuple(const std::vector<uint8_t>& buf,
+ OpaqueDataTuple& tuple);
+
+ /// @brief Append length and string tuple to a buffer
+ ///
+ /// @param value length and string tuple
+ /// @param lengthfieldtype LENGTH_1_BYTE (DHCPv4) or LENGTH_2_BYTES (DHCPv6)
+ /// @param [out] buf output buffer.
+ static void writeTuple(const std::string& value,
+ OpaqueDataTuple::LengthFieldType lengthfieldtype,
+ std::vector<uint8_t>& buf);
+
+ /// @brief Append length and string tuple to a buffer
+ ///
+ /// @param tuple length and string tuple
+ /// @param [out] buf output buffer.
+ static void writeTuple(const OpaqueDataTuple& tuple,
+ std::vector<uint8_t>& buf);
+
+ /// @brief Read boolean value from a buffer.
+ ///
+ /// @param buf input buffer.
+ ///
+ /// @throw isc::dhcp::BadDataTypeCast when the data being read
+ /// is truncated or the value is invalid (neither 1 nor 0).
+ /// @return boolean value read from a buffer.
+ static bool readBool(const std::vector<uint8_t>& buf);
+
+ /// @brief Append boolean value into a buffer.
+ ///
+ /// The bool value is encoded in a buffer in such a way that
+ /// "1" means "true" and "0" means "false".
+ ///
+ /// @param value boolean value to be written.
+ /// @param [out] buf output buffer.
+ static void writeBool(const bool value, std::vector<uint8_t>& buf);
+
+ /// @brief Read integer value from a buffer.
+ ///
+ /// @param buf input buffer.
+ /// @tparam integer type of the returned value.
+ ///
+ /// @throw isc::dhcp::BadDataTypeCast when the data in the buffer
+ /// is truncated.
+ /// @return integer value being read.
+ template<typename T>
+ static T readInt(const std::vector<uint8_t>& buf) {
+ if (!OptionDataTypeTraits<T>::integer_type) {
+ isc_throw(isc::dhcp::InvalidDataType, "specified data type to be returned"
+ " by readInteger is unsupported integer type");
+ }
+
+ if (buf.size() < OptionDataTypeTraits<T>::len) {
+ isc_throw(isc::dhcp::BadDataTypeCast,
+ "failed to read an integer value from a buffer"
+ << " - buffer is truncated.");
+ }
+
+ T value;
+ switch (OptionDataTypeTraits<T>::len) {
+ case 1:
+ value = *(buf.begin());
+ break;
+ case 2:
+ // Calling readUint16 works either for unsigned
+ // or signed types.
+ value = isc::util::readUint16(&(*buf.begin()), buf.size());
+ break;
+ case 4:
+ // Calling readUint32 works either for unsigned
+ // or signed types.
+ value = isc::util::readUint32(&(*buf.begin()), buf.size());
+ break;
+ default:
+ // This should not happen because we made checks on data types
+ // but it does not hurt to keep throw statement here.
+ isc_throw(isc::dhcp::InvalidDataType,
+ "invalid size of the data type to be read as integer.");
+ }
+ return (value);
+ }
+
+ /// @brief Append integer or unsigned integer value to a buffer.
+ ///
+ /// @param value an integer value to be written into a buffer.
+ /// @param [out] buf output buffer.
+ /// @tparam data type of the value.
+ template<typename T>
+ static void writeInt(const T value,
+ std::vector<uint8_t>& buf) {
+ if (!OptionDataTypeTraits<T>::integer_type) {
+ isc_throw(InvalidDataType, "provided data type is not the supported.");
+ }
+ switch (OptionDataTypeTraits<T>::len) {
+ case 1:
+ buf.push_back(static_cast<uint8_t>(value));
+ break;
+ case 2:
+ buf.resize(buf.size() + 2);
+ isc::util::writeUint16(static_cast<uint16_t>(value), &buf[buf.size() - 2], 2);
+ break;
+ case 4:
+ buf.resize(buf.size() + 4);
+ isc::util::writeUint32(static_cast<uint32_t>(value), &buf[buf.size() - 4], 4);
+ break;
+ default:
+ // The cases above cover whole range of possible data lengths because
+ // we check at the beginning of this function that given data type is
+ // a supported integer type which can be only 1,2 or 4 bytes long.
+ ;
+ }
+ }
+
+ /// @brief Read FQDN from a buffer as a string value.
+ ///
+ /// The format of an FQDN within a buffer complies with RFC1035,
+ /// section 3.1.
+ ///
+ /// @param buf input buffer holding a FQDN.
+ ///
+ /// @throw BadDataTypeCast if a FQDN stored within a buffer is
+ /// invalid (e.g. empty, contains invalid characters, truncated).
+ /// @return fully qualified domain name in a text form.
+ static std::string readFqdn(const std::vector<uint8_t>& buf);
+
+ /// @brief Append FQDN into a buffer.
+ ///
+ /// This method appends the Fully Qualified Domain Name (FQDN)
+ /// represented as string value into a buffer. The format of
+ /// the FQDN being stored into a buffer complies with RFC1035,
+ /// section 3.1.
+ ///
+ /// @param fqdn fully qualified domain name to be written.
+ /// @param [out] buf output buffer.
+ /// @param downcase indicates if the FQDN should be converted to lower
+ /// case (if true). By default it is not converted.
+ ///
+ /// @throw isc::dhcp::BadDataTypeCast if provided FQDN
+ /// is invalid.
+ static void writeFqdn(const std::string& fqdn,
+ std::vector<uint8_t>& buf,
+ const bool downcase = false);
+
+ /// @brief Return the number of labels in the Name.
+ ///
+ /// If the specified name is empty the 0 is returned.
+ ///
+ /// @param text_name A text representation of the name.
+ ///
+ /// @return A number of labels in the provided name or 0 if the
+ /// name string is empty.
+ /// @throw isc::dhcp::BadDataTypeCast if provided name is malformed.
+ static unsigned int getLabelCount(const std::string& text_name);
+
+ /// @brief Read prefix from a buffer.
+ ///
+ /// This method reads prefix length and a prefix value from a buffer.
+ /// The prefix value has variable length and this length is determined
+ /// from the first byte of the buffer. If the length is not divisible
+ /// by 8, the prefix is padded with zeros to the next byte boundary.
+ ///
+ /// @param buf input buffer holding a prefix length / prefix tuple.
+ ///
+ /// @return Prefix length and value.
+ static PrefixTuple readPrefix(const std::vector<uint8_t>& buf);
+
+ /// @brief Append prefix into a buffer.
+ ///
+ /// This method writes prefix length (1 byte) followed by a variable
+ /// length prefix.
+ ///
+ /// @param prefix_len Prefix length in bits (0 to 128).
+ /// @param prefix Prefix value.
+ /// @param [out] buf Output buffer.
+ static void writePrefix(const PrefixLen& prefix_len,
+ const asiolink::IOAddress& prefix,
+ std::vector<uint8_t>& buf);
+
+ /// @brief Read PSID length / value tuple from a buffer.
+ ///
+ /// This method reads three bytes from a buffer. The first byte
+ /// holds a PSID length value. The remaining two bytes contain a
+ /// zero padded PSID value.
+ ///
+ /// @return PSID length / value tuple.
+ /// @throw isc::dhcp::BadDataTypeCast if PSID length or value held
+ /// in the buffer is incorrect or the buffer is truncated.
+ static PSIDTuple readPsid(const std::vector<uint8_t>& buf);
+
+ /// @brief Append PSID length/value into a buffer.
+ ///
+ /// This method appends 1 byte of PSID length and 2 bytes of PSID
+ /// value into a buffer. The PSID value contains a PSID length
+ /// number of significant bits, followed by 16 - PSID length
+ /// zero bits.
+ ///
+ /// @param psid_len PSID length in the range of 0 to 16 holding the
+ /// number of significant bits within the PSID value.
+ /// @param psid PSID value, where the lowest value is 0, and the
+ /// highest value is 2^(PSID length)-1.
+ /// @param [out] buf output buffer.
+ ///
+ /// @throw isc::dhcp::BadDataTypeCast if specified psid_len or
+ /// psid value is incorrect.
+ static void writePsid(const PSIDLen& psid_len, const PSID& psid,
+ std::vector<uint8_t>& buf);
+
+ /// @brief Read string value from a buffer.
+ ///
+ /// To be compliant with RFC 2132, Sec. 2, trailing NULLs are trimmed.
+ /// @param buf input buffer.
+ ///
+ /// @return string value being read.
+ /// @throw isc::dhcp::OutOfRange is the payload contains only NULLs.
+ static std::string readString(const std::vector<uint8_t>& buf);
+
+ /// @brief Write UTF8-encoded string into a buffer.
+ ///
+ /// @param value string value to be written into a buffer.
+ /// @param [out] buf output buffer.
+ static void writeString(const std::string& value,
+ std::vector<uint8_t>& buf);
+private:
+
+ /// The container holding mapping of data type names to
+ /// data types enumerator.
+ std::map<std::string, OptionDataType> data_types_;
+
+ /// The container holding mapping of data types to data
+ /// type names.
+ std::map<OptionDataType, std::string> data_type_names_;
+
+ /// @brief Private constructor.
+ ///
+ /// This constructor is private because this class should
+ /// be used as singleton (through static public functions).
+ OptionDataTypeUtil();
+
+ /// @brief Return instance of OptionDataTypeUtil
+ ///
+ /// This function is used by some of the public static functions
+ /// to create an instance of OptionDataTypeUtil class.
+ /// When this instance is called it calls the classes constructor
+ /// and initializes some of the private data members.
+ ///
+ /// @return instance of OptionDataTypeUtil singleton.
+ static OptionDataTypeUtil& instance();
+
+ /// @brief Return option data type from its name.
+ ///
+ /// @param data_type data type name.
+ /// @return option data type.
+ OptionDataType getDataTypeImpl(const std::string& data_type) const;
+
+ /// @brief Return option data type name from the data type enumerator.
+ ///
+ /// @param data_type option data type.
+ /// @return option data type name.
+ const std::string& getDataTypeNameImpl(const OptionDataType data_type) const;
+};
+
+
+} // isc::dhcp namespace
+} // isc namespace
+
+#endif // OPTION_DATA_TYPES_H