option_custom.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <dhcp/libdhcp++.h>
  15. #include <dhcp/option_data_types.h>
  16. #include <dhcp/option_custom.h>
  17. #include <util/encode/hex.h>
  18. namespace isc {
  19. namespace dhcp {
  20. OptionCustom::OptionCustom(const OptionDefinition& def,
  21. Universe u,
  22. const OptionBuffer& data)
  23. : Option(u, def.getCode(), data.begin(), data.end()),
  24. definition_(def) {
  25. createBuffers();
  26. }
  27. OptionCustom::OptionCustom(const OptionDefinition& def,
  28. Universe u,
  29. OptionBufferConstIter first,
  30. OptionBufferConstIter last)
  31. : Option(u, def.getCode(), first, last),
  32. definition_(def) {
  33. createBuffers();
  34. }
  35. void
  36. OptionCustom::checkIndex(const uint32_t index) const {
  37. if (index >= buffers_.size()) {
  38. isc_throw(isc::OutOfRange, "specified data field index " << index
  39. << " is out of rangex.");
  40. }
  41. }
  42. void
  43. OptionCustom::createBuffers() {
  44. // Check that the option definition is correct as we are going
  45. // to use it to split the data_ buffer into set of sub buffers.
  46. definition_.validate();
  47. std::vector<OptionBuffer> buffers;
  48. OptionBuffer::iterator data = data_.begin();
  49. OptionDataType data_type = definition_.getType();
  50. if (data_type == OPT_RECORD_TYPE) {
  51. // An option comprises a record of data fields. We need to
  52. // get types of these data fields to allocate enough space
  53. // for each buffer.
  54. const OptionDefinition::RecordFieldsCollection& fields =
  55. definition_.getRecordFields();
  56. // Go over all data fields within a record.
  57. for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
  58. field != fields.end(); ++field) {
  59. // For fixed-size data type such as boolean, integer, even
  60. // IP address we can use the utility function to get the required
  61. // buffer size.
  62. int data_size = OptionDataTypeUtil::getDataTypeLen(*field);
  63. // For variable size types (such as string) the function above
  64. // will return 0 so we need to do a runtime check. Since variable
  65. // length data fields may be laid only at the end of an option we
  66. // consume the rest of this option. Note that validate() function
  67. // in OptionDefinition object should have checked whether the
  68. // data fields layout is correct (that the variable string fields
  69. // are laid at the end).
  70. if (data_size == 0) {
  71. data_size = std::distance(data, data_.end());
  72. if (data_size == 0) {
  73. // If we reached the end of buffer we assume that this option is
  74. // truncated because there is no remaining data to initialize
  75. // an option field.
  76. if (data_size == 0) {
  77. isc_throw(OutOfRange, "option buffer truncated");
  78. }
  79. }
  80. } else {
  81. // Our data field requires that there is a certain chunk of
  82. // data left in the buffer. If not, option is truncated.
  83. if (std::distance(data, data_.end()) < data_size) {
  84. isc_throw(OutOfRange, "option buffer truncated");
  85. }
  86. }
  87. // Store the created buffer.
  88. buffers.push_back(OptionBuffer(data, data + data_size));
  89. // Proceed to the next data field.
  90. data += data_size;
  91. }
  92. } else if (data_type != OPT_EMPTY_TYPE) {
  93. // If data_type value is other than OPT_RECORD_TYPE, our option is
  94. // empty (have no data at all) or it comprises one or more
  95. // data fields of the same type. The type of those fields
  96. // is held in the data_type variable so let's use it to determine
  97. // a size of buffers.
  98. int data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
  99. // The check below will fail if the input buffer is too short
  100. // for the data size being held by this option.
  101. // Note that data_size returned by getDataTypeLen may be zero
  102. // if variable length data is being held by the option but
  103. // this will not cause this check to throw exception.
  104. if (std::distance(data, data_.end()) < data_size) {
  105. isc_throw(OutOfRange, "option buffer truncated");
  106. }
  107. // For an array of values we are taking different path because
  108. // we have to handle multiple buffers.
  109. if (definition_.getArrayType()) {
  110. // We don't perform other checks for data types that can't be
  111. // used together with array indicator such as strings, empty field
  112. // etc. This is because OptionDefinition::validate function should
  113. // have checked this already. Thus data_size must be greater than
  114. // zero.
  115. assert(data_size > 0);
  116. // Get equal chunks of data and store as collection of buffers.
  117. // Truncate any remaining part which length is not divisible by
  118. // data_size. Note that it is ok to truncate the data if and only
  119. // if the data buffer is long enough to keep at least one value.
  120. // This has been checked above already.
  121. do {
  122. buffers.push_back(OptionBuffer(data, data + data_size));
  123. data += data_size;
  124. } while (std::distance(data, data_.end()) >= data_size);
  125. } else {
  126. // For non-arrays the data_size can be zero because
  127. // getDataTypeLen returns zero for variable size data types
  128. // such as strings. Simply take whole buffer.
  129. if (data_size == 0) {
  130. data_size = std::distance(data, data_.end());
  131. }
  132. if (data_size > 0) {
  133. buffers.push_back(OptionBuffer(data, data + data_size));
  134. } else {
  135. isc_throw(OutOfRange, "option buffer truncated");
  136. }
  137. }
  138. }
  139. // If everything went ok we can replace old buffer set with new ones.
  140. std::swap(buffers_, buffers);
  141. }
  142. std::string
  143. OptionCustom::dataFieldToText(const OptionDataType data_type,
  144. const uint32_t index) const {
  145. std::ostringstream text;
  146. // Get the value of the data field.
  147. switch (data_type) {
  148. case OPT_BINARY_TYPE:
  149. text << util::encode::encodeHex(readBinary(index));
  150. break;
  151. case OPT_BOOLEAN_TYPE:
  152. text << (readBoolean(index) ? "true" : "false");
  153. break;
  154. case OPT_INT8_TYPE:
  155. text << readInteger<int8_t>(index);
  156. break;
  157. case OPT_INT16_TYPE:
  158. text << readInteger<int16_t>(index);
  159. break;
  160. case OPT_INT32_TYPE:
  161. text << readInteger<int32_t>(index);
  162. break;
  163. case OPT_UINT8_TYPE:
  164. text << readInteger<uint8_t>(index);
  165. break;
  166. case OPT_UINT16_TYPE:
  167. text << readInteger<uint16_t>(index);
  168. break;
  169. case OPT_UINT32_TYPE:
  170. text << readInteger<uint32_t>(index);
  171. break;
  172. case OPT_IPV4_ADDRESS_TYPE:
  173. case OPT_IPV6_ADDRESS_TYPE:
  174. text << readAddress(index).toText();
  175. break;
  176. case OPT_STRING_TYPE:
  177. text << readString(index);
  178. break;
  179. default:
  180. ;
  181. }
  182. // Append data field type in brackets.
  183. text << " ( " << OptionDataTypeUtil::getDataTypeName(data_type) << " ) ";
  184. return (text.str());
  185. }
  186. void
  187. OptionCustom::pack4(isc::util::OutputBuffer& buf) {
  188. if (len() > 255) {
  189. isc_throw(OutOfRange, "DHCPv4 Option " << type_
  190. << " value is too high. At most 255 is supported.");
  191. }
  192. buf.writeUint8(type_);
  193. buf.writeUint8(len() - getHeaderLen());
  194. // Write data from buffers.
  195. for (std::vector<OptionBuffer>::const_iterator it = buffers_.begin();
  196. it != buffers_.end(); ++it) {
  197. // In theory the createBuffers function should have taken
  198. // care that there are no empty buffers added to the
  199. // collection but it is almost always good to make sure.
  200. if (!it->empty()) {
  201. buf.writeData(&(*it)[0], it->size());
  202. }
  203. }
  204. // Write suboptions.
  205. packOptions(buf);
  206. }
  207. void
  208. OptionCustom::pack6(isc::util::OutputBuffer& buf) {
  209. buf.writeUint16(type_);
  210. buf.writeUint16(len() - getHeaderLen());
  211. // Write data from buffers.
  212. for (std::vector<OptionBuffer>::const_iterator it = buffers_.begin();
  213. it != buffers_.end(); ++it) {
  214. if (!it->empty()) {
  215. buf.writeData(&(*it)[0], it->size());
  216. }
  217. }
  218. packOptions(buf);
  219. }
  220. asiolink::IOAddress
  221. OptionCustom::readAddress(const uint32_t index) const {
  222. checkIndex(index);
  223. // The address being read can be either IPv4 or IPv6. The decision
  224. // is made based on the buffer length. If it holds 4 bytes it is IPv4
  225. // address, if it holds 16 bytes it is IPv6.
  226. if (buffers_[index].size() == asiolink::V4ADDRESS_LEN) {
  227. return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET));
  228. } else if (buffers_[index].size() == asiolink::V6ADDRESS_LEN) {
  229. return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET6));
  230. } else {
  231. isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
  232. << " IP address. Invalid buffer length " << buffers_[index].size());
  233. }
  234. }
  235. const OptionBuffer&
  236. OptionCustom::readBinary(const uint32_t index) const {
  237. checkIndex(index);
  238. return (buffers_[index]);
  239. }
  240. bool
  241. OptionCustom::readBoolean(const uint32_t index) const {
  242. checkIndex(index);
  243. return (OptionDataTypeUtil::readBool(buffers_[index]));
  244. }
  245. std::string
  246. OptionCustom::readString(const uint32_t index) const {
  247. checkIndex(index);
  248. return (OptionDataTypeUtil::readString(buffers_[index]));
  249. }
  250. void
  251. OptionCustom::unpack(OptionBufferConstIter begin,
  252. OptionBufferConstIter end) {
  253. data_ = OptionBuffer(begin, end);
  254. // Chop the buffer stored in data_ into set of sub buffers.
  255. createBuffers();
  256. }
  257. uint16_t
  258. OptionCustom::len() {
  259. // The length of the option is a sum of option header ...
  260. int length = getHeaderLen();
  261. // ... lengths of all buffers that hold option data ...
  262. for (std::vector<OptionBuffer>::const_iterator buf = buffers_.begin();
  263. buf != buffers_.end(); ++buf) {
  264. length += buf->size();
  265. }
  266. // ... and lengths of all suboptions
  267. for (OptionCustom::OptionCollection::iterator it = options_.begin();
  268. it != options_.end();
  269. ++it) {
  270. length += (*it).second->len();
  271. }
  272. return (length);
  273. }
  274. void OptionCustom::setData(const OptionBufferConstIter first,
  275. const OptionBufferConstIter last) {
  276. // We will copy entire option buffer, so we have to resize data_.
  277. data_.resize(std::distance(first, last));
  278. std::copy(first, last, data_.begin());
  279. // Chop the data_ buffer into set of buffers that represent
  280. // option fields data.
  281. createBuffers();
  282. }
  283. std::string OptionCustom::toText(int indent) {
  284. std::stringstream tmp;
  285. for (int i = 0; i < indent; ++i)
  286. tmp << " ";
  287. tmp << "type=" << type_ << ", len=" << len()-getHeaderLen()
  288. << ", data fields:" << std::endl;
  289. OptionDataType data_type = definition_.getType();
  290. if (data_type == OPT_RECORD_TYPE) {
  291. const OptionDefinition::RecordFieldsCollection& fields =
  292. definition_.getRecordFields();
  293. // For record types we iterate over fields defined in
  294. // option definition and match the appropriate buffer
  295. // with them.
  296. for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
  297. field != fields.end(); ++field) {
  298. for (int j = 0; j < indent + 2; ++j) {
  299. tmp << " ";
  300. }
  301. tmp << "#" << std::distance(fields.begin(), field) << " "
  302. << dataFieldToText(*field, std::distance(fields.begin(),
  303. field))
  304. << std::endl;
  305. }
  306. } else {
  307. // For non-record types we iterate over all buffers
  308. // and print the data type set globally for an option
  309. // definition. We take the same code path for arrays
  310. // and non-arrays as they only differ in such a way that
  311. // non-arrays have just single data field.
  312. for (unsigned int i = 0; i < getDataFieldsNum(); ++i) {
  313. for (int j = 0; j < indent + 2; ++j) {
  314. tmp << " ";
  315. }
  316. tmp << "#" << i << " "
  317. << dataFieldToText(definition_.getType(), i)
  318. << std::endl;
  319. }
  320. }
  321. // print suboptions
  322. for (OptionCollection::const_iterator opt = options_.begin();
  323. opt != options_.end();
  324. ++opt) {
  325. tmp << (*opt).second->toText(indent+2);
  326. }
  327. return tmp.str();
  328. }
  329. } // end of isc::dhcp namespace
  330. } // end of isc namespace