// 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_INT_ARRAY_H #define OPTION_INT_ARRAY_H #include #include #include #include #include #include #include namespace isc { namespace dhcp { /// Forward declaration of OptionIntArray. template class OptionIntArray; /// @defgroup option_int_array_defs Typedefs for OptionIntArray class. /// /// @brief Classes that represent options comprising array of integers. /// /// @{ typedef OptionIntArray OptionUint8Array; typedef boost::shared_ptr OptionUint8ArrayPtr; typedef OptionIntArray OptionUint16Array; typedef boost::shared_ptr OptionUint16ArrayPtr; typedef OptionIntArray OptionUint32Array; typedef boost::shared_ptr OptionUint32ArrayPtr; /// @} /// This template class represents DHCP (v4 or v6) option with an /// array of integer values. The type of the elements in the array /// can be any of the following: /// - uint8_t, /// - uint16_t, /// - uint32_t, /// - int8_t, /// - int16_t, /// - int32_t. /// /// @warning Since this option may convey variable number of integer /// values, sub-options are should not be added in this option as /// there is no way to distinguish them from other data. The API will /// allow addition of sub-options but they will be ignored during /// packing and unpacking option data. /// /// @tparam T data field type (see above). template class OptionIntArray : public Option { private: /// @brief Pointer to the option type for the specified T. typedef boost::shared_ptr > OptionIntArrayTypePtr; public: /// @brief Constructor. /// /// Creates option with empty values vector. /// /// @param u universe (V4 or V6). /// @param type option type. /// /// @throw isc::dhcp::InvalidDataType if data field type provided /// as template parameter is not a supported integer type. OptionIntArray(const Option::Universe u, const uint16_t type) : Option(u, type), values_(0) { if (!OptionDataTypeTraits::integer_type) { isc_throw(dhcp::InvalidDataType, "non-integer type"); } } /// @brief Constructor. /// /// @param u universe (V4 or V6). /// @param type option type. /// @param buf buffer with option data (must not be empty). /// /// @throw isc::OutOfRange if provided buffer is empty or its length /// is not multiple of size of the data type in bytes. /// @throw isc::dhcp::InvalidDataType if data field type provided /// as template parameter is not a supported integer type. OptionIntArray(const Option::Universe u, const uint16_t type, const OptionBuffer& buf) : Option(u, type) { if (!OptionDataTypeTraits::integer_type) { isc_throw(dhcp::InvalidDataType, "non-integer type"); } unpack(buf.begin(), buf.end()); } /// @brief Constructor. /// /// This constructor creates option from a buffer. This constructor /// may throw exception if \ref unpack function throws during buffer /// parsing. /// /// @param u universe (V4 or V6). /// @param type option type. /// @param begin iterator to first byte of option data. /// @param end iterator to end of option data (first byte after option end). /// /// @throw isc::OutOfRange if provided buffer is empty or its length /// is not multiple of size of the data type in bytes. /// @throw isc::dhcp::InvalidDataType if data field type provided /// as template parameter is not a supported integer type. OptionIntArray(const Option::Universe u, const uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end) : Option(u, type) { if (!OptionDataTypeTraits::integer_type) { isc_throw(dhcp::InvalidDataType, "non-integer type"); } unpack(begin, end); } /// @brief Copies this option and returns a pointer to the copy. virtual OptionPtr clone() const { return (cloneInternal >()); } /// @brief Adds a new value to the array. /// /// @param value a value being added. void addValue(const T value) { values_.push_back(value); } /// Writes option in wire-format to buf, returns pointer to first unused /// byte after stored option. /// /// @param [out] buf buffer (option will be stored here) /// @param check if set to false, allows options larger than 255 for v4 /// /// @throw isc::dhcp::InvalidDataType if size of a data fields type is not /// equal to 1, 2 or 4 bytes. The data type is not checked in this function /// because it is checked in a constructor. virtual void pack(isc::util::OutputBuffer& buf, bool check = true) const { // Pack option header. packHeader(buf, check); // Pack option data. for (size_t i = 0; i < values_.size(); ++i) { // Depending on the data type length we use different utility functions // writeUint16 or writeUint32 which write the data in the network byte // order to the provided buffer. The same functions can be safely used // for either unsigned or signed integers so there is not need to create // special cases for intX_t types. switch (OptionDataTypeTraits::len) { case 1: buf.writeUint8(values_[i]); break; case 2: buf.writeUint16(values_[i]); break; case 4: buf.writeUint32(values_[i]); break; default: isc_throw(dhcp::InvalidDataType, "non-integer type"); } } // We don't pack sub-options here because we have array-type option. // We don't allow sub-options in array-type options as there is no // way to distinguish them from the data fields on option reception. } /// @brief Parses received buffer /// /// Parses received buffer and returns offset to the first unused byte after /// parsed option. /// /// @param begin iterator to first byte of option data /// @param end iterator to end of option data (first byte after option end) /// /// @throw isc::dhcp::InvalidDataType if size of a data fields type is not /// equal to 1, 2 or 4 bytes. The data type is not checked in this function /// because it is checked in a constructor. virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { if (distance(begin, end) == 0) { isc_throw(OutOfRange, "option " << getType() << " empty"); } if (distance(begin, end) % sizeof(T) != 0) { isc_throw(OutOfRange, "OptionIntArray " << getType() << " truncated"); } // @todo consider what to do if buffer is longer than data type. values_.clear(); while (begin != end) { // Depending on the data type length we use different utility functions // readUint16 or readUint32 which read the data laid in the network byte // order from the provided buffer. The same functions can be safely used // for either unsigned or signed integers so there is not need to create // special cases for intX_t types. int data_size_len = OptionDataTypeTraits::len; switch (data_size_len) { case 1: values_.push_back(*begin); break; case 2: values_.push_back(isc::util::readUint16(&(*begin), std::distance(begin, end))); break; case 4: values_.push_back(isc::util::readUint32(&(*begin), std::distance(begin, end))); break; default: isc_throw(dhcp::InvalidDataType, "non-integer type"); } // Use local variable to set a new value for this iterator. // When using OptionDataTypeTraits::len directly some versions // of clang complain about unresolved reference to // OptionDataTypeTraits structure during linking. begin += data_size_len; } // We do not unpack sub-options here because we have array-type option. // Such option have variable number of data fields, thus there is no // way to assess where sub-options start. } /// @brief Return collection of option values. /// /// @return collection of values. const std::vector& getValues() const { return (values_); } /// @brief Set option values. /// /// @param values collection of values to be set for option. void setValues(const std::vector& values) { values_ = values; } /// @brief returns complete length of option /// /// Returns length of this option, including option header and suboptions /// /// @return length of this option virtual uint16_t len() const { uint16_t length = (getUniverse() == Option::V4) ? OPTION4_HDR_LEN : OPTION6_HDR_LEN; length += values_.size() * sizeof(T); // length of all suboptions for (OptionCollection::const_iterator it = options_.begin(); it != options_.end(); ++it) { length += (*it).second->len(); } return (length); } /// @brief Returns textual representation of the option. /// /// @param indent Number of space characters to be inserted before /// the text. /// /// @return textual representation of the option. virtual std::string toText(int indent = 0) const { std::stringstream output; output << headerToText(indent) << ":"; std::string data_type = OptionDataTypeUtil::getDataTypeName(OptionDataTypeTraits::type); for (typename std::vector::const_iterator value = values_.begin(); value != values_.end(); ++value) { output << " "; // For 1 byte long data types we need to cast to the integer // because they are usually implemented as "char" types, in // which case the character rather than number would be printed. if (OptionDataTypeTraits::len == 1) { output << static_cast(*value); } else { output << *value; } // Append data type. output << "(" << data_type << ")"; } return (output.str()); } private: std::vector values_; }; } // isc::dhcp namespace } // isc namespace #endif // OPTION_INT_ARRAY_H