option_custom.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. // Copyright (C) 2012-2013 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. : Option(u, def.getCode(), OptionBuffer()),
  23. definition_(def) {
  24. setEncapsulatedSpace(def.getEncapsulatedSpace());
  25. createBuffers();
  26. }
  27. OptionCustom::OptionCustom(const OptionDefinition& def,
  28. Universe u,
  29. const OptionBuffer& data)
  30. : Option(u, def.getCode(), data.begin(), data.end()),
  31. definition_(def) {
  32. setEncapsulatedSpace(def.getEncapsulatedSpace());
  33. createBuffers(getData());
  34. }
  35. OptionCustom::OptionCustom(const OptionDefinition& def,
  36. Universe u,
  37. OptionBufferConstIter first,
  38. OptionBufferConstIter last)
  39. : Option(u, def.getCode(), first, last),
  40. definition_(def) {
  41. setEncapsulatedSpace(def.getEncapsulatedSpace());
  42. createBuffers(getData());
  43. }
  44. void
  45. OptionCustom::addArrayDataField(const asiolink::IOAddress& address) {
  46. checkArrayType();
  47. if ((address.isV4() && definition_.getType() != OPT_IPV4_ADDRESS_TYPE) ||
  48. (address.isV6() && definition_.getType() != OPT_IPV6_ADDRESS_TYPE)) {
  49. isc_throw(BadDataTypeCast, "invalid address specified "
  50. << address.toText() << ". Expected a valid IPv"
  51. << (definition_.getType() == OPT_IPV4_ADDRESS_TYPE ?
  52. "4" : "6") << " address.");
  53. }
  54. OptionBuffer buf;
  55. OptionDataTypeUtil::writeAddress(address, buf);
  56. buffers_.push_back(buf);
  57. }
  58. void
  59. OptionCustom::addArrayDataField(const bool value) {
  60. checkArrayType();
  61. OptionBuffer buf;
  62. OptionDataTypeUtil::writeBool(value, buf);
  63. buffers_.push_back(buf);
  64. }
  65. void
  66. OptionCustom::checkIndex(const uint32_t index) const {
  67. if (index >= buffers_.size()) {
  68. isc_throw(isc::OutOfRange, "specified data field index " << index
  69. << " is out of range.");
  70. }
  71. }
  72. template<typename T>
  73. void
  74. OptionCustom::checkDataType(const uint32_t index) const {
  75. // Check that the requested return type is a supported integer.
  76. if (!OptionDataTypeTraits<T>::integer_type) {
  77. isc_throw(isc::dhcp::InvalidDataType, "specified data type"
  78. " is not a supported integer type.");
  79. }
  80. // Get the option definition type.
  81. OptionDataType data_type = definition_.getType();
  82. if (data_type == OPT_RECORD_TYPE) {
  83. const OptionDefinition::RecordFieldsCollection& record_fields =
  84. definition_.getRecordFields();
  85. // When we initialized buffers we have already checked that
  86. // the number of these buffers is equal to number of option
  87. // fields in the record so the condition below should be met.
  88. assert(index < record_fields.size());
  89. // Get the data type to be returned.
  90. data_type = record_fields[index];
  91. }
  92. if (OptionDataTypeTraits<T>::type != data_type) {
  93. isc_throw(isc::dhcp::InvalidDataType,
  94. "specified data type " << data_type << " does not"
  95. " match the data type in an option definition for field"
  96. " index " << index);
  97. }
  98. }
  99. void
  100. OptionCustom::createBuffers() {
  101. definition_.validate();
  102. std::vector<OptionBuffer> buffers;
  103. OptionDataType data_type = definition_.getType();
  104. // This function is called when an empty data buffer has been
  105. // passed to the constructor. In such cases values for particular
  106. // data fields will be set using modifier functions but for now
  107. // we need to initialize a set of buffers that are specified
  108. // for an option by its definition. Since there is no data yet,
  109. // we are going to fill these buffers with default values.
  110. if (data_type == OPT_RECORD_TYPE) {
  111. // For record types we need to iterate over all data fields
  112. // specified in option definition and create corresponding
  113. // buffers for each of them.
  114. const OptionDefinition::RecordFieldsCollection fields =
  115. definition_.getRecordFields();
  116. for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
  117. field != fields.end(); ++field) {
  118. OptionBuffer buf;
  119. // For data types that have a fixed size we can use the
  120. // utility function to get the buffer's size.
  121. size_t data_size = OptionDataTypeUtil::getDataTypeLen(*field);
  122. // For variable data sizes the utility function returns zero.
  123. // It is ok for string values because the default string
  124. // is 'empty'. However for FQDN the empty value is not valid
  125. // so we initialize it to '.'.
  126. if (data_size == 0 &&
  127. *field == OPT_FQDN_TYPE) {
  128. OptionDataTypeUtil::writeFqdn(".", buf);
  129. } else {
  130. // At this point we can resize the buffer. Note that
  131. // for string values we are setting the empty buffer
  132. // here.
  133. buf.resize(data_size);
  134. }
  135. // We have the buffer with default value prepared so we
  136. // add it to the set of buffers.
  137. buffers.push_back(buf);
  138. }
  139. } else if (!definition_.getArrayType() &&
  140. data_type != OPT_EMPTY_TYPE) {
  141. // For either 'empty' options we don't have to create any buffers
  142. // for obvious reason. For arrays we also don't create any buffers
  143. // yet because the set of fields that belong to the array is open
  144. // ended so we can't allocate required buffers until we know how
  145. // many of them are needed.
  146. // For non-arrays we have a single value being held by the option
  147. // so we have to allocate exactly one buffer.
  148. OptionBuffer buf;
  149. size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
  150. if (data_size == 0 &&
  151. data_type == OPT_FQDN_TYPE) {
  152. OptionDataTypeUtil::writeFqdn(".", buf);
  153. } else {
  154. // Note that if our option holds a string value then
  155. // we are making empty buffer here.
  156. buf.resize(data_size);
  157. }
  158. // Add a buffer that we have created and leave.
  159. buffers.push_back(buf);
  160. }
  161. // The 'swap' is used here because we want to make sure that we
  162. // don't touch buffers_ until we successfully allocate all
  163. // buffers to be stored there.
  164. std::swap(buffers, buffers_);
  165. }
  166. void
  167. OptionCustom::createBuffers(const OptionBuffer& data_buf) {
  168. // Check that the option definition is correct as we are going
  169. // to use it to split the data_ buffer into set of sub buffers.
  170. definition_.validate();
  171. std::vector<OptionBuffer> buffers;
  172. OptionBuffer::const_iterator data = data_buf.begin();
  173. OptionDataType data_type = definition_.getType();
  174. if (data_type == OPT_RECORD_TYPE) {
  175. // An option comprises a record of data fields. We need to
  176. // get types of these data fields to allocate enough space
  177. // for each buffer.
  178. const OptionDefinition::RecordFieldsCollection& fields =
  179. definition_.getRecordFields();
  180. // Go over all data fields within a record.
  181. for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
  182. field != fields.end(); ++field) {
  183. // For fixed-size data type such as boolean, integer, even
  184. // IP address we can use the utility function to get the required
  185. // buffer size.
  186. size_t data_size = OptionDataTypeUtil::getDataTypeLen(*field);
  187. // For variable size types (e.g. string) the function above will
  188. // return 0 so we need to do a runtime check of the length.
  189. if (data_size == 0) {
  190. // FQDN is a special data type as it stores variable length data
  191. // but the data length is encoded in the buffer. The easiest way
  192. // to obtain the length of the data is to read the FQDN. The
  193. // utility function will return the size of the buffer on success.
  194. if (*field == OPT_FQDN_TYPE) {
  195. std::string fqdn =
  196. OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_buf.end()));
  197. // The size of the buffer holding an FQDN is always
  198. // 1 byte larger than the size of the string
  199. // representation of this FQDN.
  200. data_size = fqdn.size() + 1;
  201. } else if ( (*field == OPT_BINARY_TYPE) || (*field == OPT_STRING_TYPE) ) {
  202. // In other case we are dealing with string or binary value
  203. // which size can't be determined. Thus we consume the
  204. // remaining part of the buffer for it. Note that variable
  205. // size data can be laid at the end of the option only and
  206. // that the validate() function in OptionDefinition object
  207. // should have checked wheter it is a case for this option.
  208. data_size = std::distance(data, data_buf.end());
  209. } else {
  210. // If we reached the end of buffer we assume that this option is
  211. // truncated because there is no remaining data to initialize
  212. // an option field.
  213. isc_throw(OutOfRange, "option buffer truncated");
  214. }
  215. } else {
  216. // Our data field requires that there is a certain chunk of
  217. // data left in the buffer. If not, option is truncated.
  218. if (std::distance(data, data_buf.end()) < data_size) {
  219. isc_throw(OutOfRange, "option buffer truncated");
  220. }
  221. }
  222. // Store the created buffer.
  223. buffers.push_back(OptionBuffer(data, data + data_size));
  224. // Proceed to the next data field.
  225. data += data_size;
  226. }
  227. } else if (data_type != OPT_EMPTY_TYPE) {
  228. // If data_type value is other than OPT_RECORD_TYPE, our option is
  229. // empty (have no data at all) or it comprises one or more
  230. // data fields of the same type. The type of those fields
  231. // is held in the data_type variable so let's use it to determine
  232. // a size of buffers.
  233. size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
  234. // The check below will fail if the input buffer is too short
  235. // for the data size being held by this option.
  236. // Note that data_size returned by getDataTypeLen may be zero
  237. // if variable length data is being held by the option but
  238. // this will not cause this check to throw exception.
  239. if (std::distance(data, data_buf.end()) < data_size) {
  240. isc_throw(OutOfRange, "option buffer truncated");
  241. }
  242. // For an array of values we are taking different path because
  243. // we have to handle multiple buffers.
  244. if (definition_.getArrayType()) {
  245. while (data != data_buf.end()) {
  246. // FQDN is a special case because it is of a variable length.
  247. // The actual length for a particular FQDN is encoded within
  248. // a buffer so we have to actually read the FQDN from a buffer
  249. // to get it.
  250. if (data_type == OPT_FQDN_TYPE) {
  251. std::string fqdn =
  252. OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_buf.end()));
  253. // The size of the buffer holding an FQDN is always
  254. // 1 byte larger than the size of the string
  255. // representation of this FQDN.
  256. data_size = fqdn.size() + 1;
  257. }
  258. // We don't perform other checks for data types that can't be
  259. // used together with array indicator such as strings, empty field
  260. // etc. This is because OptionDefinition::validate function should
  261. // have checked this already. Thus data_size must be greater than
  262. // zero.
  263. assert(data_size > 0);
  264. // Get chunks of data and store as a collection of buffers.
  265. // Truncate any remaining part which length is not divisible by
  266. // data_size. Note that it is ok to truncate the data if and only
  267. // if the data buffer is long enough to keep at least one value.
  268. // This has been checked above already.
  269. if (std::distance(data, data_buf.end()) < data_size) {
  270. break;
  271. }
  272. buffers.push_back(OptionBuffer(data, data + data_size));
  273. data += data_size;
  274. }
  275. } else {
  276. // For non-arrays the data_size can be zero because
  277. // getDataTypeLen returns zero for variable size data types
  278. // such as strings. Simply take whole buffer.
  279. if (data_size == 0) {
  280. // For FQDN we get the size by actually reading the FQDN.
  281. if (data_type == OPT_FQDN_TYPE) {
  282. std::string fqdn =
  283. OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_buf.end()));
  284. // The size of the buffer holding an FQDN is always
  285. // 1 bytes larger than the size of the string
  286. // representation of this FQDN.
  287. data_size = fqdn.size() + 1;
  288. } else {
  289. data_size = std::distance(data, data_buf.end());
  290. }
  291. }
  292. if (data_size > 0) {
  293. buffers.push_back(OptionBuffer(data, data + data_size));
  294. } else {
  295. isc_throw(OutOfRange, "option buffer truncated");
  296. }
  297. }
  298. }
  299. // If everything went ok we can replace old buffer set with new ones.
  300. std::swap(buffers_, buffers);
  301. }
  302. std::string
  303. OptionCustom::dataFieldToText(const OptionDataType data_type,
  304. const uint32_t index) const {
  305. std::ostringstream text;
  306. // Get the value of the data field.
  307. switch (data_type) {
  308. case OPT_BINARY_TYPE:
  309. text << util::encode::encodeHex(readBinary(index));
  310. break;
  311. case OPT_BOOLEAN_TYPE:
  312. text << (readBoolean(index) ? "true" : "false");
  313. break;
  314. case OPT_INT8_TYPE:
  315. text << readInteger<int8_t>(index);
  316. break;
  317. case OPT_INT16_TYPE:
  318. text << readInteger<int16_t>(index);
  319. break;
  320. case OPT_INT32_TYPE:
  321. text << readInteger<int32_t>(index);
  322. break;
  323. case OPT_UINT8_TYPE:
  324. text << readInteger<uint8_t>(index);
  325. break;
  326. case OPT_UINT16_TYPE:
  327. text << readInteger<uint16_t>(index);
  328. break;
  329. case OPT_UINT32_TYPE:
  330. text << readInteger<uint32_t>(index);
  331. break;
  332. case OPT_IPV4_ADDRESS_TYPE:
  333. case OPT_IPV6_ADDRESS_TYPE:
  334. text << readAddress(index).toText();
  335. break;
  336. case OPT_FQDN_TYPE:
  337. text << readFqdn(index);
  338. break;
  339. case OPT_STRING_TYPE:
  340. text << readString(index);
  341. break;
  342. default:
  343. ;
  344. }
  345. // Append data field type in brackets.
  346. text << " ( " << OptionDataTypeUtil::getDataTypeName(data_type) << " ) ";
  347. return (text.str());
  348. }
  349. void
  350. OptionCustom::pack(isc::util::OutputBuffer& buf) {
  351. // Pack DHCP header (V4 or V6).
  352. packHeader(buf);
  353. // Write data from buffers.
  354. for (std::vector<OptionBuffer>::const_iterator it = buffers_.begin();
  355. it != buffers_.end(); ++it) {
  356. // In theory the createBuffers function should have taken
  357. // care that there are no empty buffers added to the
  358. // collection but it is almost always good to make sure.
  359. if (!it->empty()) {
  360. buf.writeData(&(*it)[0], it->size());
  361. }
  362. }
  363. // Write suboptions.
  364. packOptions(buf);
  365. }
  366. asiolink::IOAddress
  367. OptionCustom::readAddress(const uint32_t index) const {
  368. checkIndex(index);
  369. // The address being read can be either IPv4 or IPv6. The decision
  370. // is made based on the buffer length. If it holds 4 bytes it is IPv4
  371. // address, if it holds 16 bytes it is IPv6.
  372. if (buffers_[index].size() == asiolink::V4ADDRESS_LEN) {
  373. return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET));
  374. } else if (buffers_[index].size() == asiolink::V6ADDRESS_LEN) {
  375. return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET6));
  376. } else {
  377. isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
  378. << " IP address. Invalid buffer length "
  379. << buffers_[index].size() << ".");
  380. }
  381. }
  382. void
  383. OptionCustom::writeAddress(const asiolink::IOAddress& address,
  384. const uint32_t index) {
  385. using namespace isc::asiolink;
  386. checkIndex(index);
  387. if ((address.isV4() && buffers_[index].size() != V4ADDRESS_LEN) ||
  388. (address.isV6() && buffers_[index].size() != V6ADDRESS_LEN)) {
  389. isc_throw(BadDataTypeCast, "invalid address specified "
  390. << address.toText() << ". Expected a valid IPv"
  391. << (buffers_[index].size() == V4ADDRESS_LEN ? "4" : "6")
  392. << " address.");
  393. }
  394. OptionBuffer buf;
  395. OptionDataTypeUtil::writeAddress(address, buf);
  396. std::swap(buf, buffers_[index]);
  397. }
  398. const OptionBuffer&
  399. OptionCustom::readBinary(const uint32_t index) const {
  400. checkIndex(index);
  401. return (buffers_[index]);
  402. }
  403. void
  404. OptionCustom::writeBinary(const OptionBuffer& buf,
  405. const uint32_t index) {
  406. checkIndex(index);
  407. buffers_[index] = buf;
  408. }
  409. bool
  410. OptionCustom::readBoolean(const uint32_t index) const {
  411. checkIndex(index);
  412. return (OptionDataTypeUtil::readBool(buffers_[index]));
  413. }
  414. void
  415. OptionCustom::writeBoolean(const bool value, const uint32_t index) {
  416. checkIndex(index);
  417. buffers_[index].clear();
  418. OptionDataTypeUtil::writeBool(value, buffers_[index]);
  419. }
  420. std::string
  421. OptionCustom::readFqdn(const uint32_t index) const {
  422. checkIndex(index);
  423. return (OptionDataTypeUtil::readFqdn(buffers_[index]));
  424. }
  425. void
  426. OptionCustom::writeFqdn(const std::string& fqdn, const uint32_t index) {
  427. checkIndex(index);
  428. // Create a temporay buffer where the FQDN will be written.
  429. OptionBuffer buf;
  430. // Try to write to the temporary buffer rather than to the
  431. // buffers_ member directly guarantees that we don't modify
  432. // (clear) buffers_ until we are sure that the provided FQDN
  433. // is valid.
  434. OptionDataTypeUtil::writeFqdn(fqdn, buf);
  435. // If we got to this point it means that the FQDN is valid.
  436. // We can move the contents of the teporary buffer to the
  437. // target buffer.
  438. std::swap(buffers_[index], buf);
  439. }
  440. std::string
  441. OptionCustom::readString(const uint32_t index) const {
  442. checkIndex(index);
  443. return (OptionDataTypeUtil::readString(buffers_[index]));
  444. }
  445. void
  446. OptionCustom::writeString(const std::string& text, const uint32_t index) {
  447. checkIndex(index);
  448. // Let's clear a buffer as we want to replace the value of the
  449. // whole buffer. If we fail to clear the buffer the data will
  450. // be appended.
  451. buffers_[index].clear();
  452. // If the text value is empty we can leave because the buffer
  453. // is already empty.
  454. if (!text.empty()) {
  455. OptionDataTypeUtil::writeString(text, buffers_[index]);
  456. }
  457. }
  458. void
  459. OptionCustom::unpack(OptionBufferConstIter begin,
  460. OptionBufferConstIter end) {
  461. initialize(begin, end);
  462. }
  463. uint16_t
  464. OptionCustom::len() {
  465. // The length of the option is a sum of option header ...
  466. int length = getHeaderLen();
  467. // ... lengths of all buffers that hold option data ...
  468. for (std::vector<OptionBuffer>::const_iterator buf = buffers_.begin();
  469. buf != buffers_.end(); ++buf) {
  470. length += buf->size();
  471. }
  472. // ... and lengths of all suboptions
  473. for (OptionCollection::iterator it = options_.begin();
  474. it != options_.end();
  475. ++it) {
  476. length += (*it).second->len();
  477. }
  478. return (length);
  479. }
  480. void OptionCustom::initialize(const OptionBufferConstIter first,
  481. const OptionBufferConstIter last) {
  482. setData(first, last);
  483. // Chop the data_ buffer into set of buffers that represent
  484. // option fields data.
  485. createBuffers(getData());
  486. }
  487. std::string OptionCustom::toText(int indent) {
  488. std::stringstream tmp;
  489. for (int i = 0; i < indent; ++i)
  490. tmp << " ";
  491. tmp << "type=" << type_ << ", len=" << len()-getHeaderLen()
  492. << ", data fields:" << std::endl;
  493. OptionDataType data_type = definition_.getType();
  494. if (data_type == OPT_RECORD_TYPE) {
  495. const OptionDefinition::RecordFieldsCollection& fields =
  496. definition_.getRecordFields();
  497. // For record types we iterate over fields defined in
  498. // option definition and match the appropriate buffer
  499. // with them.
  500. for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
  501. field != fields.end(); ++field) {
  502. for (int j = 0; j < indent + 2; ++j) {
  503. tmp << " ";
  504. }
  505. tmp << "#" << std::distance(fields.begin(), field) << " "
  506. << dataFieldToText(*field, std::distance(fields.begin(),
  507. field))
  508. << std::endl;
  509. }
  510. } else {
  511. // For non-record types we iterate over all buffers
  512. // and print the data type set globally for an option
  513. // definition. We take the same code path for arrays
  514. // and non-arrays as they only differ in such a way that
  515. // non-arrays have just single data field.
  516. for (unsigned int i = 0; i < getDataFieldsNum(); ++i) {
  517. for (int j = 0; j < indent + 2; ++j) {
  518. tmp << " ";
  519. }
  520. tmp << "#" << i << " "
  521. << dataFieldToText(definition_.getType(), i)
  522. << std::endl;
  523. }
  524. }
  525. // print suboptions
  526. for (OptionCollection::const_iterator opt = options_.begin();
  527. opt != options_.end();
  528. ++opt) {
  529. tmp << (*opt).second->toText(indent+2);
  530. }
  531. return tmp.str();
  532. }
  533. } // end of isc::dhcp namespace
  534. } // end of isc namespace