option_custom.cc 22 KB

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