option_vendor_class.cc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // Copyright (C) 2014-2017 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. #include <exceptions/exceptions.h>
  7. #include <dhcp/opaque_data_tuple.h>
  8. #include <dhcp/option_vendor_class.h>
  9. #include <sstream>
  10. namespace isc {
  11. namespace dhcp {
  12. OptionVendorClass::OptionVendorClass(Option::Universe u,
  13. const uint32_t vendor_id)
  14. : Option(u, getOptionCode(u)), vendor_id_(vendor_id) {
  15. if (u == Option::V4) {
  16. addTuple(OpaqueDataTuple(OpaqueDataTuple::LENGTH_1_BYTE));
  17. }
  18. }
  19. OptionVendorClass::OptionVendorClass(Option::Universe u,
  20. OptionBufferConstIter begin,
  21. OptionBufferConstIter end)
  22. : Option(u, getOptionCode(u)) {
  23. unpack(begin, end);
  24. }
  25. OptionPtr
  26. OptionVendorClass::clone() const {
  27. return (cloneInternal<OptionVendorClass>());
  28. }
  29. void
  30. OptionVendorClass::pack(isc::util::OutputBuffer& buf) const {
  31. packHeader(buf);
  32. buf.writeUint32(getVendorId());
  33. for (TuplesCollection::const_iterator it = tuples_.begin();
  34. it != tuples_.end(); ++it) {
  35. // For DHCPv4 V-I Vendor Class option, there is enterprise id before
  36. // every tuple.
  37. if ((getUniverse() == V4) && (it != tuples_.begin())) {
  38. buf.writeUint32(getVendorId());
  39. }
  40. it->pack(buf);
  41. }
  42. }
  43. void
  44. OptionVendorClass::unpack(OptionBufferConstIter begin,
  45. OptionBufferConstIter end) {
  46. if (std::distance(begin, end) < getMinimalLength() - getHeaderLen()) {
  47. isc_throw(OutOfRange, "parsed Vendor Class option data truncated to"
  48. " size " << std::distance(begin, end));
  49. }
  50. // Option must contain at least one enterprise id. It is ok to read 4-byte
  51. // value here because we have checked that the buffer he minimal length.
  52. vendor_id_ = isc::util::readUint32(&(*begin), distance(begin, end));
  53. begin += sizeof(vendor_id_);
  54. // Start reading opaque data.
  55. size_t offset = 0;
  56. while (offset < std::distance(begin, end)) {
  57. // Parse a tuple.
  58. OpaqueDataTuple tuple(getLengthFieldType(), begin + offset, end);
  59. addTuple(tuple);
  60. // The tuple has been parsed correctly which implies that it is safe to
  61. // advance the offset by its total length.
  62. offset += tuple.getTotalLength();
  63. // For DHCPv4 option, there is enterprise id before every opaque data
  64. // tuple. Let's read it, unless we have already reached the end of
  65. // buffer.
  66. if ((getUniverse() == V4) && (begin + offset != end)) {
  67. // Advance the offset by the size of enterprise id.
  68. offset += sizeof(vendor_id_);
  69. // If the offset already ran over the buffer length or there is
  70. // no space left for the empty tuple (thus we add 1), we have
  71. // to signal the option truncation.
  72. if (offset + 1 >= std::distance(begin, end)) {
  73. isc_throw(isc::OutOfRange, "truncated DHCPv4 V-I Vendor Class"
  74. " option - it should contain enterprise id followed"
  75. " by opaque data field tuple");
  76. }
  77. }
  78. }
  79. }
  80. void
  81. OptionVendorClass::addTuple(const OpaqueDataTuple& tuple) {
  82. if (tuple.getLengthFieldType() != getLengthFieldType()) {
  83. isc_throw(isc::BadValue, "attempted to add opaque data tuple having"
  84. " invalid size of the length field "
  85. << tuple.getDataFieldSize() << " to Vendor Class option");
  86. }
  87. tuples_.push_back(tuple);
  88. }
  89. void
  90. OptionVendorClass::setTuple(const size_t at, const OpaqueDataTuple& tuple) {
  91. if (at >= getTuplesNum()) {
  92. isc_throw(isc::OutOfRange, "attempted to set an opaque data for the"
  93. " vendor option at position " << at << " which is out of"
  94. " range");
  95. } else if (tuple.getLengthFieldType() != getLengthFieldType()) {
  96. isc_throw(isc::BadValue, "attempted to set opaque data tuple having"
  97. " invalid size of the length field "
  98. << tuple.getDataFieldSize() << " to Vendor Class option");
  99. }
  100. tuples_[at] = tuple;
  101. }
  102. OpaqueDataTuple
  103. OptionVendorClass::getTuple(const size_t at) const {
  104. if (at >= getTuplesNum()) {
  105. isc_throw(isc::OutOfRange, "attempted to get an opaque data for the"
  106. " vendor option at position " << at << " which is out of"
  107. " range. There are only " << getTuplesNum() << " tuples");
  108. }
  109. return (tuples_[at]);
  110. }
  111. bool
  112. OptionVendorClass::hasTuple(const std::string& tuple_str) const {
  113. // Iterate over existing tuples (there shouldn't be many of them),
  114. // and try to match the searched one.
  115. for (TuplesCollection::const_iterator it = tuples_.begin();
  116. it != tuples_.end(); ++it) {
  117. if (*it == tuple_str) {
  118. return (true);
  119. }
  120. }
  121. return (false);
  122. }
  123. uint16_t
  124. OptionVendorClass::len() const {
  125. // The option starts with the header and enterprise id.
  126. uint16_t length = getHeaderLen() + sizeof(uint32_t);
  127. // Now iterate over existing tuples and add their size.
  128. for (TuplesCollection::const_iterator it = tuples_.begin();
  129. it != tuples_.end(); ++it) {
  130. // For DHCPv4 V-I Vendor Class option, there is enterprise id before
  131. // every tuple.
  132. if ((getUniverse() == V4) && (it != tuples_.begin())) {
  133. length += sizeof(uint32_t);
  134. }
  135. length += it->getTotalLength();
  136. }
  137. return (length);
  138. }
  139. std::string
  140. OptionVendorClass::toText(int indent) const {
  141. std::ostringstream s;
  142. // Apply indentation
  143. s << std::string(indent, ' ');
  144. // Print type, length and first occurrence of enterprise id.
  145. s << "type=" << getType() << ", len=" << len() - getHeaderLen() << ", "
  146. " enterprise id=0x" << std::hex << getVendorId() << std::dec;
  147. // Iterate over all tuples and print their size and contents.
  148. for (unsigned i = 0; i < getTuplesNum(); ++i) {
  149. // The DHCPv4 V-I Vendor Class has enterprise id before every tuple.
  150. if ((getUniverse() == V4) && (i > 0)) {
  151. s << ", enterprise id=0x" << std::hex << getVendorId() << std::dec;
  152. }
  153. // Print the tuple.
  154. s << ", data-len" << i << "=" << getTuple(i).getLength();
  155. s << ", vendor-class-data" << i << "='" << getTuple(i) << "'";
  156. }
  157. return (s.str());
  158. }
  159. } // namespace isc::dhcp
  160. } // namespace isc