summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/option_custom.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcp/option_custom.h')
-rw-r--r--src/lib/dhcp/option_custom.h511
1 files changed, 511 insertions, 0 deletions
diff --git a/src/lib/dhcp/option_custom.h b/src/lib/dhcp/option_custom.h
new file mode 100644
index 0000000..9208651
--- /dev/null
+++ b/src/lib/dhcp/option_custom.h
@@ -0,0 +1,511 @@
+// 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_CUSTOM_H
+#define OPTION_CUSTOM_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+#include <dhcp/option_definition.h>
+#include <exceptions/isc_assert.h>
+#include <util/io_utilities.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Option with defined data fields represented as buffers that can
+/// be accessed using data field index.
+///
+/// This class represents an option which has defined structure: data fields
+/// of specific types and order. Those fields can be accessed using indexes,
+/// where index 0 represents first data field within an option. The last
+/// field can be accessed using index equal to 'number of fields' - 1.
+/// Internally, the option data is stored as a collection of OptionBuffer
+/// objects, each representing data for a particular data field. This data
+/// can be converted to the actual data type using methods implemented
+/// within this class. This class is used to represent those options that
+/// can't be represented by any other specialized class (this excludes the
+/// Option class which is generic and can be used to represent any option).
+class OptionCustom : public Option {
+public:
+
+ /// @brief Constructor, used for options to be sent.
+ ///
+ /// This constructor creates an instance of an option with default
+ /// data set for all data fields. The option buffers are allocated
+ /// according to data size being stored in particular data fields.
+ /// For variable size data empty buffers are created.
+ ///
+ /// @param def option definition.
+ /// @param u specifies universe (V4 or V6)
+ OptionCustom(const OptionDefinition& def, Universe u);
+
+ /// @brief Constructor, used for options to be sent.
+ ///
+ /// This constructor creates an instance of an option from the whole
+ /// supplied buffer. This constructor is mainly used to create an
+ /// instances of options to be stored in outgoing DHCP packets.
+ /// The buffer used to create the instance of an option can be
+ /// created from the option data specified in server's configuration.
+ ///
+ /// @param def option definition.
+ /// @param u specifies universe (V4 or V6).
+ /// @param data content of the option.
+ ///
+ /// @throw OutOfRange if option buffer is truncated.
+ ///
+ /// @todo list all exceptions thrown by ctor.
+ OptionCustom(const OptionDefinition& def, Universe u, const OptionBuffer& data);
+
+ /// @brief Constructor, used for received options.
+ ///
+ /// This constructor creates an instance an option from the portion
+ /// of the buffer specified by iterators. This is mainly useful when
+ /// parsing received packets. Such packets are represented by a single
+ /// buffer holding option data and all sub options. Methods that are
+ /// parsing a packet, supply relevant portions of the packet buffer
+ /// to this constructor to create option instances out of it.
+ ///
+ /// @param def option definition.
+ /// @param u specifies universe (V4 or V6).
+ /// @param first iterator to the first element that should be copied.
+ /// @param last iterator to the next element after the last one
+ /// to be copied.
+ ///
+ /// @throw OutOfRange if option buffer is truncated.
+ ///
+ /// @todo list all exceptions thrown by ctor.
+ OptionCustom(const OptionDefinition& def, Universe u,
+ OptionBufferConstIter first, OptionBufferConstIter last);
+
+ /// @brief Copies this option and returns a pointer to the copy.
+ virtual OptionPtr clone() const;
+
+ /// @brief Create new buffer and set its value as an IP address.
+ ///
+ /// @param address IPv4 or IPv6 address to be written to
+ /// a buffer being created.
+ void addArrayDataField(const asiolink::IOAddress& address);
+
+ /// @brief Create new buffer and store boolean value in it.
+ ///
+ /// @param value value to be stored in the created buffer.
+ void addArrayDataField(const bool value);
+
+ /// @brief Create new buffer and store integer value in it.
+ ///
+ /// @param value value to be stored in the created buffer.
+ /// @tparam T integer type of the value being stored.
+ template<typename T>
+ void addArrayDataField(const T value) {
+ checkArrayType();
+ OptionDataType data_type = definition_.getType();
+ // Handle record last field.
+ if (data_type == OPT_RECORD_TYPE) {
+ data_type = definition_.getRecordFields().back();
+ }
+ if (OptionDataTypeTraits<T>::type != data_type) {
+ isc_throw(isc::dhcp::InvalidDataType,
+ "specified data type " << data_type << " does not"
+ " match the data type in an option definition");
+ }
+
+ OptionBuffer buf;
+ OptionDataTypeUtil::writeInt<T>(value, buf);
+ buffers_.push_back(buf);
+ }
+
+ /// @brief Create new buffer and store tuple value in it
+ ///
+ /// @param value value to be stored as a tuple in the created buffer.
+ void addArrayDataField(const std::string& value);
+
+ /// @brief Create new buffer and store tuple value in it
+ ///
+ /// @param value value to be stored as a tuple in the created buffer.
+ void addArrayDataField(const OpaqueDataTuple& value);
+
+ /// @brief Create new buffer and store variable length prefix in it.
+ ///
+ /// @param prefix_len Prefix length.
+ /// @param prefix Prefix.
+ void addArrayDataField(const PrefixLen& prefix_len,
+ const asiolink::IOAddress& prefix);
+
+ /// @brief Create new buffer and store PSID length / value in it.
+ ///
+ /// @param psid_len PSID length.
+ /// @param psid PSID.
+ void addArrayDataField(const PSIDLen& psid_len, const PSID& psid);
+
+ /// @brief Return a number of the data fields.
+ ///
+ /// @return number of data fields held by the option.
+ uint32_t getDataFieldsNum() const { return (buffers_.size()); }
+
+ /// @brief Read a buffer as IP address.
+ ///
+ /// @param index buffer index.
+ ///
+ /// @return IP address read from a buffer.
+ /// @throw isc::OutOfRange if index is out of range.
+ asiolink::IOAddress readAddress(const uint32_t index = 0) const;
+
+ /// @brief Write an IP address into a buffer.
+ ///
+ /// @param address IP address being written.
+ /// @param index buffer index.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ /// @throw isc::dhcp::BadDataTypeCast if IP address is invalid.
+ void writeAddress(const asiolink::IOAddress& address,
+ const uint32_t index = 0);
+
+ /// @brief Read a buffer as binary data.
+ ///
+ /// @param index buffer index.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ /// @return read buffer holding binary data.
+ const OptionBuffer& readBinary(const uint32_t index = 0) const;
+
+ /// @brief Write binary data into a buffer.
+ ///
+ /// @param buf buffer holding binary data to be written.
+ /// @param index buffer index.
+ void writeBinary(const OptionBuffer& buf, const uint32_t index = 0);
+
+ /// @brief Read a buffer as length and string tuple.
+ ///
+ /// @param index buffer index.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ /// @return string read from a buffer.
+ std::string readTuple(const uint32_t index = 0) const;
+
+ /// @brief Read a buffer into a length and string tuple.
+ ///
+ /// @param tuple tuple to fill.
+ /// @param index buffer index.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ void readTuple(OpaqueDataTuple& tuple, const uint32_t index = 0) const;
+
+ /// @brief Write a length and string tuple into a buffer.
+ ///
+ /// @param value value to be written.
+ /// @param index buffer index.
+ void writeTuple(const std::string& value, const uint32_t index = 0);
+
+ /// @brief Write a length and string tuple into a buffer.
+ ///
+ /// @param value value to be written.
+ /// @param index buffer index.
+ void writeTuple(const OpaqueDataTuple& value, const uint32_t index = 0);
+
+ /// @brief Read a buffer as boolean value.
+ ///
+ /// @param index buffer index.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ /// @return read boolean value.
+ bool readBoolean(const uint32_t index = 0) const;
+
+ /// @brief Write a boolean value into a buffer.
+ ///
+ /// @param value boolean value to be written.
+ /// @param index buffer index.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ void writeBoolean(const bool value, const uint32_t index = 0);
+
+ /// @brief Read a buffer as FQDN.
+ ///
+ /// @param index buffer index.
+ ///
+ /// @throw isc::OutOfRange if buffer index is out of range.
+ /// @throw isc::dhcp::BadDataTypeCast if a buffer being read
+ /// does not hold a valid FQDN.
+ /// @return string representation if FQDN.
+ std::string readFqdn(const uint32_t index = 0) const;
+
+ /// @brief Write an FQDN into a buffer.
+ ///
+ /// @param fqdn text representation of FQDN.
+ /// @param index buffer index.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ void writeFqdn(const std::string& fqdn, const uint32_t index = 0);
+
+ /// @brief Read a buffer as integer value.
+ ///
+ /// @param index buffer index.
+ /// @tparam integer type of a value being returned.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ /// @throw isc::dhcp::InvalidDataType if T is invalid.
+ /// @return read integer value.
+ template<typename T>
+ T readInteger(const uint32_t index = 0) const {
+ // Check that the index is not out of range.
+ checkIndex(index);
+ // Check that T points to a valid integer type and this type
+ // is consistent with an option definition.
+ checkDataType<T>(index);
+ // When we created the buffer we have checked that it has a
+ // valid size so this condition here should be always fulfilled.
+ isc_throw_assert(buffers_[index].size() == OptionDataTypeTraits<T>::len);
+ // Read an integer value.
+ return (OptionDataTypeUtil::readInt<T>(buffers_[index]));
+ }
+
+ /// @brief Write an integer value into a buffer.
+ ///
+ /// @param value integer value to be written.
+ /// @param index buffer index.
+ /// @tparam T integer type of a value being written.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ /// @throw isc::dhcp::InvalidDataType if T is invalid.
+ template<typename T>
+ void writeInteger(const T value, const uint32_t index = 0) {
+ // Check that the index is not out of range.
+ checkIndex(index);
+ // Check that T points to a valid integer type and this type
+ // is consistent with an option definition.
+ checkDataType<T>(index);
+ // Get some temporary buffer.
+ OptionBuffer buf;
+ // Try to write to the buffer.
+ OptionDataTypeUtil::writeInt<T>(value, buf);
+ // If successful, replace the old buffer with new one.
+ std::swap(buffers_[index], buf);
+ }
+
+ /// @brief Read a buffer as variable length prefix.
+ ///
+ /// @param index buffer index.
+ ///
+ /// @return Prefix length / value tuple.
+ /// @throw isc::OutOfRange of index is out of range.
+ PrefixTuple readPrefix(const uint32_t index = 0) const;
+
+ /// @brief Write prefix length and value into a buffer.
+ ///
+ /// @param prefix_len Prefix length.
+ /// @param prefix Prefix value.
+ /// @param index Buffer index.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ void writePrefix(const PrefixLen& prefix_len,
+ const asiolink::IOAddress& prefix,
+ const uint32_t index = 0);
+
+ /// @brief Read a buffer as a PSID length / value tuple.
+ ///
+ /// @param index buffer index.
+ ///
+ /// @return PSID length / value tuple.
+ /// @throw isc::OutOfRange of index is out of range.
+ PSIDTuple readPsid(const uint32_t index = 0) const;
+
+ /// @brief Write PSID length / value into a buffer.
+ ///
+ /// @param psid_len PSID length value.
+ /// @param psid PSID value in the range of 0 .. 2^(PSID length).
+ /// @param index buffer index.
+ ///
+ /// @throw isc::dhcp::BadDataTypeCast if PSID length or value is
+ /// invalid.
+ /// @throw isc::OutOfRange if index is out of range.
+ void writePsid(const PSIDLen& psid_len, const PSID& psid,
+ const uint32_t index = 0);
+
+ /// @brief Read a buffer as string value.
+ ///
+ /// @param index buffer index.
+ ///
+ /// @return string value read from buffer.
+ /// @throw isc::OutOfRange if index is out of range.
+ std::string readString(const uint32_t index = 0) const;
+
+ /// @brief Write a string value into a buffer.
+ ///
+ /// @param text the string value to be written.
+ /// @param index buffer index.
+ void writeString(const std::string& text,
+ const uint32_t index = 0);
+
+ /// @brief Writes DHCP option in a wire format to a buffer.
+ ///
+ /// @param buf output buffer (option will be stored there).
+ /// @param check if set to false, allows options larger than 255 for v4
+ virtual void pack(isc::util::OutputBuffer& buf, bool check = true) const;
+
+ /// @brief Parses received buffer.
+ ///
+ /// @param begin iterator to first byte of option data
+ /// @param end iterator to end of option data (first byte after option end)
+ virtual void unpack(OptionBufferConstIter begin,
+ OptionBufferConstIter end);
+
+ /// @brief Returns string representation of the option.
+ ///
+ /// @param indent number of spaces before printed text.
+ ///
+ /// @return string with text representation.
+ virtual std::string toText(int indent = 0) const;
+
+ /// @brief Returns length of the complete option (data length +
+ /// DHCPv4/DHCPv6 option header)
+ ///
+ /// @return length of the option
+ virtual uint16_t len() const;
+
+ /// @brief Sets content of this option from buffer.
+ ///
+ /// Option will be resized to length of buffer.
+ ///
+ /// @param first iterator pointing to beginning of buffer to copy.
+ /// @param last iterator pointing to end of buffer to copy.
+ void initialize(const OptionBufferConstIter first,
+ const OptionBufferConstIter last);
+
+private:
+
+ /// @brief Verify that the option comprises an array of values.
+ ///
+ /// This helper function is used by createArrayEntry functions
+ /// and throws an exception if the particular option is not
+ /// an array.
+ ///
+ /// @throw isc::InvalidOperation if option is not an array.
+ inline void checkArrayType() const {
+ if (!definition_.getArrayType()) {
+ isc_throw(InvalidOperation, "failed to add new array entry to an"
+ << " option. The option is not an array.");
+ }
+ }
+
+ /// @brief Verify that the integer type is consistent with option
+ /// field type.
+ ///
+ /// This convenience function checks that the data type specified as T
+ /// is consistent with a type of a data field identified by index.
+ ///
+ /// @param index data field index.
+ /// @tparam data type to be validated.
+ ///
+ /// @throw isc::dhcp::InvalidDataType if the type is invalid.
+ template<typename T>
+ // cppcheck-suppress unusedPrivateFunction
+ void checkDataType(const uint32_t index) const;
+
+ /// @brief Check if data field index is valid.
+ ///
+ /// @param index Data field index to check.
+ ///
+ /// @throw isc::OutOfRange if index is out of range.
+ void checkIndex(const uint32_t index) const;
+
+ /// @brief Create a non initialized buffer.
+ ///
+ /// @param buffer buffer to update.
+ /// @param data_type data type of buffer.
+ void createBuffer(OptionBuffer& buffer,
+ const OptionDataType data_type) const;
+
+ /// @brief Create a collection of non initialized buffers.
+ void createBuffers();
+
+ /// @brief Return length of a buffer.
+ ///
+ /// @param data_type data type of buffer.
+ /// @param in_array true is called from the array case
+ /// @param begin iterator to first byte of input data.
+ /// @param end iterator to end of input data.
+ ///
+ /// @return size of data to copy to the buffer.
+ /// @throw isc::OutOfRange if option buffer is truncated.
+ size_t bufferLength(const OptionDataType data_type, bool in_array,
+ OptionBuffer::const_iterator begin,
+ OptionBuffer::const_iterator end) const;
+
+ /// @brief Create collection of buffers representing data field values.
+ ///
+ /// @param data_buf a buffer to be parsed.
+ void createBuffers(const OptionBuffer& data_buf);
+
+ /// @brief Return a text representation of a data field.
+ ///
+ /// @param data_type data type of a field.
+ /// @param index data field buffer index within a custom option.
+ ///
+ /// @return text representation of a data field.
+ std::string dataFieldToText(const OptionDataType data_type,
+ const uint32_t index) const;
+
+ /// Make this function private as we don't want it to be invoked
+ /// on OptionCustom object. We rather want that initialize to
+ /// be called instead.
+ using Option::setData;
+
+ /// Option definition used to create an option.
+ OptionDefinition definition_;
+
+ /// The collection of buffers holding data for option fields.
+ /// The order of buffers corresponds to the order of option
+ /// fields.
+ std::vector<OptionBuffer> buffers_;
+};
+
+/// A pointer to the OptionCustom object.
+typedef boost::shared_ptr<OptionCustom> OptionCustomPtr;
+
+template<typename T>
+void
+OptionCustom::checkDataType(const uint32_t index) const {
+ // Check that the requested return type is a supported integer.
+ if (!OptionDataTypeTraits<T>::integer_type) {
+ isc_throw(isc::dhcp::InvalidDataType, "specified data type"
+ " is not a supported integer type.");
+ }
+
+ // Get the option definition type.
+ OptionDataType data_type = definition_.getType();
+ if (data_type == OPT_RECORD_TYPE) {
+ const OptionDefinition::RecordFieldsCollection& record_fields =
+ definition_.getRecordFields();
+ if (definition_.getArrayType()) {
+ // If the array flag is set the last record field is an array.
+ if (index < record_fields.size()) {
+ // Get the data type to be returned.
+ data_type = record_fields[index];
+ } else {
+ // Get the data type to be returned from the last record field.
+ data_type = record_fields.back();
+ }
+ } else {
+ // When we initialized buffers we have already checked that
+ // the number of these buffers is equal to number of option
+ // fields in the record so the condition below should be met.
+ isc_throw_assert(index < record_fields.size());
+ // Get the data type to be returned.
+ data_type = record_fields[index];
+ }
+ }
+
+ if (OptionDataTypeTraits<T>::type != data_type) {
+ isc_throw(isc::dhcp::InvalidDataType,
+ "specified data type " << data_type << " does not"
+ " match the data type in an option definition for field"
+ " index " << index);
+ }
+}
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif // OPTION_CUSTOM_H