option_custom.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. // Copyright (C) 2012-2016 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. #ifndef OPTION_CUSTOM_H
  7. #define OPTION_CUSTOM_H
  8. #include <asiolink/io_address.h>
  9. #include <dhcp/option.h>
  10. #include <dhcp/option_definition.h>
  11. #include <util/io_utilities.h>
  12. namespace isc {
  13. namespace dhcp {
  14. /// @brief Option with defined data fields represented as buffers that can
  15. /// be accessed using data field index.
  16. ///
  17. /// This class represents an option which has defined structure: data fields
  18. /// of specific types and order. Those fields can be accessed using indexes,
  19. /// where index 0 represents first data field within an option. The last
  20. /// field can be accessed using index equal to 'number of fields' - 1.
  21. /// Internally, the option data is stored as a collection of OptionBuffer
  22. /// objects, each representing data for a particular data field. This data
  23. /// can be converted to the actual data type using methods implemented
  24. /// within this class. This class is used to represent those options that
  25. /// can't be represented by any other specialized class (this excludes the
  26. /// Option class which is generic and can be used to represent any option).
  27. class OptionCustom : public Option {
  28. public:
  29. /// @brief Constructor, used for options to be sent.
  30. ///
  31. /// This constructor creates an instance of an option with default
  32. /// data set for all data fields. The option buffers are allocated
  33. /// according to data size being stored in particular data fields.
  34. /// For variable size data empty buffers are created.
  35. ///
  36. /// @param def option definition.
  37. /// @param u specifies universe (V4 or V6)
  38. OptionCustom(const OptionDefinition& def, Universe u);
  39. /// @brief Constructor, used for options to be sent.
  40. ///
  41. /// This constructor creates an instance of an option from the whole
  42. /// supplied buffer. This constructor is mainly used to create an
  43. /// instances of options to be stored in outgoing DHCP packets.
  44. /// The buffer used to create the instance of an option can be
  45. /// created from the option data specified in server's configuration.
  46. ///
  47. /// @param def option definition.
  48. /// @param u specifies universe (V4 or V6).
  49. /// @param data content of the option.
  50. ///
  51. /// @throw OutOfRange if option buffer is truncated.
  52. ///
  53. /// @todo list all exceptions thrown by ctor.
  54. OptionCustom(const OptionDefinition& def, Universe u, const OptionBuffer& data);
  55. /// @brief Constructor, used for received options.
  56. ///
  57. /// This constructor creates an instance an option from the portion
  58. /// of the buffer specified by iterators. This is mainly useful when
  59. /// parsing received packets. Such packets are represented by a single
  60. /// buffer holding option data and all sub options. Methods that are
  61. /// parsing a packet, supply relevant portions of the packet buffer
  62. /// to this constructor to create option instances out of it.
  63. ///
  64. /// @param def option definition.
  65. /// @param u specifies universe (V4 or V6).
  66. /// @param first iterator to the first element that should be copied.
  67. /// @param last iterator to the next element after the last one
  68. /// to be copied.
  69. ///
  70. /// @throw OutOfRange if option buffer is truncated.
  71. ///
  72. /// @todo list all exceptions thrown by ctor.
  73. OptionCustom(const OptionDefinition& def, Universe u,
  74. OptionBufferConstIter first, OptionBufferConstIter last);
  75. /// @brief Copies this option and returns a pointer to the copy.
  76. virtual OptionPtr clone() const;
  77. /// @brief Create new buffer and set its value as an IP address.
  78. ///
  79. /// @param address IPv4 or IPv6 address to be written to
  80. /// a buffer being created.
  81. void addArrayDataField(const asiolink::IOAddress& address);
  82. /// @brief Create new buffer and store boolean value in it.
  83. ///
  84. /// @param value value to be stored in the created buffer.
  85. void addArrayDataField(const bool value);
  86. /// @brief Create new buffer and store integer value in it.
  87. ///
  88. /// @param value value to be stored in the created buffer.
  89. /// @tparam T integer type of the value being stored.
  90. template<typename T>
  91. void addArrayDataField(const T value) {
  92. checkArrayType();
  93. OptionDataType data_type = definition_.getType();
  94. if (OptionDataTypeTraits<T>::type != data_type) {
  95. isc_throw(isc::dhcp::InvalidDataType,
  96. "specified data type " << data_type << " does not"
  97. " match the data type in an option definition");
  98. }
  99. OptionBuffer buf;
  100. OptionDataTypeUtil::writeInt<T>(value, buf);
  101. buffers_.push_back(buf);
  102. }
  103. /// @brief Create new buffer and store variable length prefix in it.
  104. ///
  105. /// @param prefix_len Prefix length.
  106. /// @param prefix Prefix.
  107. void addArrayDataField(const PrefixLen& prefix_len,
  108. const asiolink::IOAddress& prefix);
  109. /// @brief Create new buffer and store PSID length / value in it.
  110. ///
  111. /// @param psid_len PSID length.
  112. /// @param psid PSID.
  113. void addArrayDataField(const PSIDLen& psid_len, const PSID& psid);
  114. /// @brief Return a number of the data fields.
  115. ///
  116. /// @return number of data fields held by the option.
  117. uint32_t getDataFieldsNum() const { return (buffers_.size()); }
  118. /// @brief Read a buffer as IP address.
  119. ///
  120. /// @param index buffer index.
  121. ///
  122. /// @return IP address read from a buffer.
  123. /// @throw isc::OutOfRange if index is out of range.
  124. asiolink::IOAddress readAddress(const uint32_t index = 0) const;
  125. /// @brief Write an IP address into a buffer.
  126. ///
  127. /// @param address IP address being written.
  128. /// @param index buffer index.
  129. ///
  130. /// @throw isc::OutOfRange if index is out of range.
  131. /// @throw isc::dhcp::BadDataTypeCast if IP address is invalid.
  132. void writeAddress(const asiolink::IOAddress& address,
  133. const uint32_t index = 0);
  134. /// @brief Read a buffer as binary data.
  135. ///
  136. /// @param index buffer index.
  137. ///
  138. /// @throw isc::OutOfRange if index is out of range.
  139. /// @return read buffer holding binary data.
  140. const OptionBuffer& readBinary(const uint32_t index = 0) const;
  141. /// @brief Write binary data into a buffer.
  142. ///
  143. /// @param buf buffer holding binary data to be written.
  144. /// @param index buffer index.
  145. void writeBinary(const OptionBuffer& buf, const uint32_t index = 0);
  146. /// @brief Read a buffer as boolean value.
  147. ///
  148. /// @param index buffer index.
  149. ///
  150. /// @throw isc::OutOfRange if index is out of range.
  151. /// @return read boolean value.
  152. bool readBoolean(const uint32_t index = 0) const;
  153. /// @brief Write a boolean value into a buffer.
  154. ///
  155. /// @param value boolean value to be written.
  156. /// @param index buffer index.
  157. ///
  158. /// @throw isc::OutOfRange if index is out of range.
  159. void writeBoolean(const bool value, const uint32_t index = 0);
  160. /// @brief Read a buffer as FQDN.
  161. ///
  162. /// @param index buffer index.
  163. ///
  164. /// @throw isc::OutOfRange if buffer index is out of range.
  165. /// @throw isc::dhcp::BadDataTypeCast if a buffer being read
  166. /// does not hold a valid FQDN.
  167. /// @return string representation if FQDN.
  168. std::string readFqdn(const uint32_t index = 0) const;
  169. /// @brief Write an FQDN into a buffer.
  170. ///
  171. /// @param fqdn text representation of FQDN.
  172. /// @param index buffer index.
  173. ///
  174. /// @throw isc::OutOfRange if index is out of range.
  175. void writeFqdn(const std::string& fqdn, const uint32_t index = 0);
  176. /// @brief Read a buffer as integer value.
  177. ///
  178. /// @param index buffer index.
  179. /// @tparam integer type of a value being returned.
  180. ///
  181. /// @throw isc::OutOfRange if index is out of range.
  182. /// @throw isc::dhcp::InvalidDataType if T is invalid.
  183. /// @return read integer value.
  184. template<typename T>
  185. T readInteger(const uint32_t index = 0) const {
  186. // Check that the index is not out of range.
  187. checkIndex(index);
  188. // Check that T points to a valid integer type and this type
  189. // is consistent with an option definition.
  190. checkDataType<T>(index);
  191. // When we created the buffer we have checked that it has a
  192. // valid size so this condition here should be always fulfilled.
  193. assert(buffers_[index].size() == OptionDataTypeTraits<T>::len);
  194. // Read an integer value.
  195. return (OptionDataTypeUtil::readInt<T>(buffers_[index]));
  196. }
  197. /// @brief Write an integer value into a buffer.
  198. ///
  199. /// @param value integer value to be written.
  200. /// @param index buffer index.
  201. /// @tparam T integer type of a value being written.
  202. ///
  203. /// @throw isc::OutOfRange if index is out of range.
  204. /// @throw isc::dhcp::InvalidDataType if T is invalid.
  205. template<typename T>
  206. void writeInteger(const T value, const uint32_t index = 0) {
  207. // Check that the index is not out of range.
  208. checkIndex(index);
  209. // Check that T points to a valid integer type and this type
  210. // is consistent with an option definition.
  211. checkDataType<T>(index);
  212. // Get some temporary buffer.
  213. OptionBuffer buf;
  214. // Try to write to the buffer.
  215. OptionDataTypeUtil::writeInt<T>(value, buf);
  216. // If successful, replace the old buffer with new one.
  217. std::swap(buffers_[index], buf);
  218. }
  219. /// @brief Read a buffer as variable length prefix.
  220. ///
  221. /// @param index buffer index.
  222. ///
  223. /// @return Prefix length / value tuple.
  224. /// @throw isc::OutOfRange of index is out of range.
  225. PrefixTuple readPrefix(const uint32_t index = 0) const;
  226. /// @brief Write prefix length and value into a buffer.
  227. ///
  228. /// @param prefix_len Prefix length.
  229. /// @param prefix Prefix value.
  230. /// @param index Buffer index.
  231. ///
  232. /// @throw isc::OutOfRange if index is out of range.
  233. void writePrefix(const PrefixLen& prefix_len,
  234. const asiolink::IOAddress& prefix,
  235. const uint32_t index = 0);
  236. /// @brief Read a buffer as a PSID length / value tuple.
  237. ///
  238. /// @param index buffer index.
  239. ///
  240. /// @return PSID length / value tuple.
  241. /// @throw isc::OutOfRange of index is out of range.
  242. PSIDTuple readPsid(const uint32_t index = 0) const;
  243. /// @brief Write PSID length / value into a buffer.
  244. ///
  245. /// @param psid_len PSID length value.
  246. /// @param psid PSID value in the range of 0 .. 2^(PSID length).
  247. /// @param index buffer index.
  248. ///
  249. /// @throw isc::dhcp::BadDataTypeCast if PSID length or value is
  250. /// invalid.
  251. /// @throw isc::OutOfRange if index is out of range.
  252. void writePsid(const PSIDLen& psid_len, const PSID& psid,
  253. const uint32_t index = 0);
  254. /// @brief Read a buffer as string value.
  255. ///
  256. /// @param index buffer index.
  257. ///
  258. /// @return string value read from buffer.
  259. /// @throw isc::OutOfRange if index is out of range.
  260. std::string readString(const uint32_t index = 0) const;
  261. /// @brief Write a string value into a buffer.
  262. ///
  263. /// @param text the string value to be written.
  264. /// @param index buffer index.
  265. void writeString(const std::string& text,
  266. const uint32_t index = 0);
  267. /// @brief Writes DHCP option in a wire format to a buffer.
  268. ///
  269. /// @param buf output buffer (option will be stored there).
  270. virtual void pack(isc::util::OutputBuffer& buf) const;
  271. /// @brief Parses received buffer.
  272. ///
  273. /// @param begin iterator to first byte of option data
  274. /// @param end iterator to end of option data (first byte after option end)
  275. virtual void unpack(OptionBufferConstIter begin,
  276. OptionBufferConstIter end);
  277. /// @brief Returns string representation of the option.
  278. ///
  279. /// @param indent number of spaces before printed text.
  280. ///
  281. /// @return string with text representation.
  282. virtual std::string toText(int indent = 0) const;
  283. /// @brief Returns length of the complete option (data length +
  284. /// DHCPv4/DHCPv6 option header)
  285. ///
  286. /// @return length of the option
  287. virtual uint16_t len() const;
  288. /// @brief Sets content of this option from buffer.
  289. ///
  290. /// Option will be resized to length of buffer.
  291. ///
  292. /// @param first iterator pointing to beginning of buffer to copy.
  293. /// @param last iterator pointing to end of buffer to copy.
  294. void initialize(const OptionBufferConstIter first,
  295. const OptionBufferConstIter last);
  296. private:
  297. /// @brief Verify that the option comprises an array of values.
  298. ///
  299. /// This helper function is used by createArrayEntry functions
  300. /// and throws an exception if the particular option is not
  301. /// an array.
  302. ///
  303. /// @throw isc::InvalidOperation if option is not an array.
  304. inline void checkArrayType() const {
  305. if (!definition_.getArrayType()) {
  306. isc_throw(InvalidOperation, "failed to add new array entry to an"
  307. << " option. The option is not an array.");
  308. }
  309. }
  310. /// @brief Verify that the integer type is consistent with option
  311. /// field type.
  312. ///
  313. /// This convenience function checks that the data type specified as T
  314. /// is consistent with a type of a data field identified by index.
  315. ///
  316. /// @param index data field index.
  317. /// @tparam data type to be validated.
  318. ///
  319. /// @throw isc::dhcp::InvalidDataType if the type is invalid.
  320. template<typename T>
  321. // cppcheck-suppress unusedPrivateFunction
  322. void checkDataType(const uint32_t index) const;
  323. /// @brief Check if data field index is valid.
  324. ///
  325. /// @param index Data field index to check.
  326. ///
  327. /// @throw isc::OutOfRange if index is out of range.
  328. void checkIndex(const uint32_t index) const;
  329. /// @brief Create a collection of non initialized buffers.
  330. void createBuffers();
  331. /// @brief Create collection of buffers representing data field values.
  332. ///
  333. /// @param data_buf a buffer to be parsed.
  334. void createBuffers(const OptionBuffer& data_buf);
  335. /// @brief Return a text representation of a data field.
  336. ///
  337. /// @param data_type data type of a field.
  338. /// @param index data field buffer index within a custom option.
  339. ///
  340. /// @return text representation of a data field.
  341. std::string dataFieldToText(const OptionDataType data_type,
  342. const uint32_t index) const;
  343. /// Make this function private as we don't want it to be invoked
  344. /// on OptionCustom object. We rather want that initialize to
  345. /// be called instead.
  346. using Option::setData;
  347. /// Option definition used to create an option.
  348. OptionDefinition definition_;
  349. /// The collection of buffers holding data for option fields.
  350. /// The order of buffers corresponds to the order of option
  351. /// fields.
  352. std::vector<OptionBuffer> buffers_;
  353. };
  354. /// A pointer to the OptionCustom object.
  355. typedef boost::shared_ptr<OptionCustom> OptionCustomPtr;
  356. template<typename T>
  357. void
  358. OptionCustom::checkDataType(const uint32_t index) const {
  359. // Check that the requested return type is a supported integer.
  360. if (!OptionDataTypeTraits<T>::integer_type) {
  361. isc_throw(isc::dhcp::InvalidDataType, "specified data type"
  362. " is not a supported integer type.");
  363. }
  364. // Get the option definition type.
  365. OptionDataType data_type = definition_.getType();
  366. if (data_type == OPT_RECORD_TYPE) {
  367. const OptionDefinition::RecordFieldsCollection& record_fields =
  368. definition_.getRecordFields();
  369. // When we initialized buffers we have already checked that
  370. // the number of these buffers is equal to number of option
  371. // fields in the record so the condition below should be met.
  372. assert(index < record_fields.size());
  373. // Get the data type to be returned.
  374. data_type = record_fields[index];
  375. }
  376. if (OptionDataTypeTraits<T>::type != data_type) {
  377. isc_throw(isc::dhcp::InvalidDataType,
  378. "specified data type " << data_type << " does not"
  379. " match the data type in an option definition for field"
  380. " index " << index);
  381. }
  382. }
  383. } // namespace isc::dhcp
  384. } // namespace isc
  385. #endif // OPTION_CUSTOM_H