1 // Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC") 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, v. 2.0. If a copy of the MPL was not distributed with this 5 // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 #ifndef OPTION_INT_H 8 #define OPTION_INT_H 9 10 #include <dhcp/libdhcp++.h> 11 #include <dhcp/option.h> 12 #include <dhcp/option_data_types.h> 13 #include <dhcp/option_space.h> 14 #include <util/io_utilities.h> 15 16 #include <stdint.h> 17 #include <sstream> 18 19 namespace isc { 20 namespace dhcp { 21 22 template<typename T> 23 class OptionInt; 24 25 /// @defgroup option_int_defs Typedefs for OptionInt class. 26 /// 27 /// @brief Classes that represent options comprising an integer. 28 /// 29 /// @{ 30 typedef OptionInt<uint8_t> OptionUint8; 31 typedef boost::shared_ptr<OptionUint8> OptionUint8Ptr; 32 typedef OptionInt<uint16_t> OptionUint16; 33 typedef boost::shared_ptr<OptionUint16> OptionUint16Ptr; 34 typedef OptionInt<uint32_t> OptionUint32; 35 typedef boost::shared_ptr<OptionUint32> OptionUint32Ptr; 36 /// @} 37 38 /// This template class represents DHCP option with single value. 39 /// This value is of integer type and can be any of the following: 40 /// - uint8_t, 41 /// - uint16_t, 42 /// - uint32_t, 43 /// - int8_t, 44 /// - int16_t, 45 /// - int32_t. 46 /// 47 /// @param T data field type (see above). 48 template<typename T> 49 class OptionInt: public Option { 50 private: 51 52 /// @brief Pointer to the option object for a specified type T. 53 typedef boost::shared_ptr<OptionInt<T> > OptionIntTypePtr; 54 55 public: 56 /// @brief Constructor. 57 /// 58 /// @param u universe (V4 or V6) 59 /// @param type option type. 60 /// @param value option value. 61 /// 62 /// @throw isc::dhcp::InvalidDataType if data field type provided 63 /// as template parameter is not a supported integer type. 64 /// @todo Extend constructor to set encapsulated option space name. OptionInt(Option::Universe u,uint16_t type,T value)65 OptionInt(Option::Universe u, uint16_t type, T value) 66 : Option(u, type), value_(value) { 67 if (!OptionDataTypeTraits<T>::integer_type) { 68 isc_throw(dhcp::InvalidDataType, "non-integer type"); 69 } 70 setEncapsulatedSpace(u == Option::V4 ? DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE); 71 } 72 73 /// @brief Constructor. 74 /// 75 /// This constructor creates option from a buffer. This constructor 76 /// may throw exception if \ref unpack function throws during buffer 77 /// parsing. 78 /// 79 /// @param u universe (V4 or V6) 80 /// @param type option type. 81 /// @param begin iterator to first byte of option data. 82 /// @param end iterator to end of option data (first byte after option end). 83 /// 84 /// @throw isc::OutOfRange if provided buffer is shorter than data size. 85 /// @throw isc::dhcp::InvalidDataType if data field type provided 86 /// as template parameter is not a supported integer type. 87 /// @todo Extend constructor to set encapsulated option space name. OptionInt(Option::Universe u,uint16_t type,OptionBufferConstIter begin,OptionBufferConstIter end)88 OptionInt(Option::Universe u, uint16_t type, OptionBufferConstIter begin, 89 OptionBufferConstIter end) 90 : Option(u, type) { 91 if (!OptionDataTypeTraits<T>::integer_type) { 92 isc_throw(dhcp::InvalidDataType, "non-integer type"); 93 } 94 setEncapsulatedSpace(u == Option::V4 ? DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE); 95 unpack(begin, end); 96 } 97 98 /// @brief Copies this option and returns a pointer to the copy. clone()99 virtual OptionPtr clone() const { 100 return (cloneInternal<OptionInt<T> >()); 101 } 102 103 /// Writes option in wire-format to buf, returns pointer to first unused 104 /// byte after stored option. 105 /// 106 /// @param [out] buf buffer (option will be stored here) 107 /// 108 /// @throw isc::dhcp::InvalidDataType if size of a data field type is not 109 /// equal to 1, 2 or 4 bytes. The data type is not checked in this function 110 /// because it is checked in a constructor. pack(isc::util::OutputBuffer & buf)111 void pack(isc::util::OutputBuffer& buf) const { 112 // Pack option header. 113 packHeader(buf); 114 // Depending on the data type length we use different utility functions 115 // writeUint16 or writeUint32 which write the data in the network byte 116 // order to the provided buffer. The same functions can be safely used 117 // for either unsigned or signed integers so there is not need to create 118 // special cases for intX_t types. 119 switch (OptionDataTypeTraits<T>::len) { 120 case 1: 121 buf.writeUint8(value_); 122 break; 123 case 2: 124 buf.writeUint16(value_); 125 break; 126 case 4: 127 buf.writeUint32(value_); 128 break; 129 default: 130 isc_throw(dhcp::InvalidDataType, "non-integer type"); 131 } 132 packOptions(buf); 133 } 134 135 /// @brief Parses received buffer 136 /// 137 /// Parses received buffer and returns offset to the first unused byte after 138 /// parsed option. 139 /// 140 /// @param begin iterator to first byte of option data 141 /// @param end iterator to end of option data (first byte after option end) 142 /// 143 /// @throw isc::OutOfRange if provided buffer is shorter than data size. 144 /// @throw isc::dhcp::InvalidDataType if size of a data field type is not 145 /// equal to 1, 2 or 4 bytes. The data type is not checked in this function 146 /// because it is checked in a constructor. unpack(OptionBufferConstIter begin,OptionBufferConstIter end)147 virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) { 148 if (distance(begin, end) < sizeof(T)) { 149 isc_throw(OutOfRange, "OptionInt " << getType() << " truncated"); 150 } 151 // @todo consider what to do if buffer is longer than data type. 152 153 // Depending on the data type length we use different utility functions 154 // readUint16 or readUint32 which read the data laid in the network byte 155 // order from the provided buffer. The same functions can be safely used 156 // for either unsigned or signed integers so there is not need to create 157 // special cases for intX_t types. 158 int data_size_len = OptionDataTypeTraits<T>::len; 159 switch (data_size_len) { 160 case 1: 161 value_ = *begin; 162 break; 163 case 2: 164 value_ = isc::util::readUint16(&(*begin), 165 std::distance(begin, end)); 166 break; 167 case 4: 168 value_ = isc::util::readUint32(&(*begin), 169 std::distance(begin, end)); 170 break; 171 default: 172 isc_throw(dhcp::InvalidDataType, "non-integer type"); 173 } 174 // Use local variable to set a new value for this iterator. 175 // When using OptionDataTypeTraits<T>::len directly some versions 176 // of clang complain about unresolved reference to 177 // OptionDataTypeTraits structure during linking. 178 begin += data_size_len; 179 unpackOptions(OptionBuffer(begin, end)); 180 } 181 182 /// @brief Set option value. 183 /// 184 /// @param value new option value. setValue(T value)185 void setValue(T value) { value_ = value; } 186 187 /// @brief Return option value. 188 /// 189 /// @return option value. getValue()190 T getValue() const { return value_; } 191 192 /// @brief returns complete length of option 193 /// 194 /// Returns length of this option, including option header and suboptions 195 /// 196 /// @return length of this option len()197 virtual uint16_t len() const { 198 // Calculate the length of the header. 199 uint16_t length = (getUniverse() == Option::V4) ? OPTION4_HDR_LEN : OPTION6_HDR_LEN; 200 // The data length is equal to size of T. 201 length += sizeof(T);; 202 // length of all suboptions 203 for (OptionCollection::const_iterator it = options_.begin(); 204 it != options_.end(); 205 ++it) { 206 length += (*it).second->len(); 207 } 208 return (length); 209 } 210 211 /// @brief Returns option carrying an integer value in the textual 212 /// format. 213 /// 214 /// The returned value also includes the suboptions if present. 215 /// 216 /// @param indent Number of spaces to be inserted before the text. 217 virtual std::string toText(int indent = 0) const { 218 std::stringstream output; 219 output << headerToText(indent) << ": "; 220 221 // For 1 byte long data types we need to cast to the integer 222 // because they are usually implemented as "char" types, in 223 // which case the character rather than number would be printed. 224 if (OptionDataTypeTraits<T>::len == 1) { 225 output << static_cast<int>(getValue()); 226 } else { 227 output << getValue(); 228 } 229 230 // Append data type name. 231 output << " (" 232 << OptionDataTypeUtil::getDataTypeName(OptionDataTypeTraits<T>::type) 233 << ")"; 234 235 // Append suboptions. 236 output << suboptionsToText(indent + 2); 237 238 return (output.str()); 239 } 240 241 private: 242 243 T value_; ///< Value conveyed by the option. 244 }; 245 246 } // isc::dhcp namespace 247 } // isc namespace 248 249 #endif // OPTION_INT_H 250