]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[master] Merge branch 'trac2312'
authorMarcin Siodelski <marcin@isc.org>
Thu, 29 Nov 2012 10:03:05 +0000 (11:03 +0100)
committerMarcin Siodelski <marcin@isc.org>
Thu, 29 Nov 2012 10:03:05 +0000 (11:03 +0100)
1  2 
src/lib/dhcp/option6_int.h
src/lib/dhcp/option_custom.cc

Simple merge
index 0000000000000000000000000000000000000000,3bf99b7817ff93aa3bd2f6cb4bf175e001179b2e..8b3ef11111ff6086c7a195a07ff4b7c0f18ad8fa
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,370 +1,370 @@@
 -    LibDHCP::packOptions(buf, options_);
+ // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+ //
+ // Permission to use, copy, modify, and/or distribute this software for any
+ // purpose with or without fee is hereby granted, provided that the above
+ // copyright notice and this permission notice appear in all copies.
+ //
+ // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ // PERFORMANCE OF THIS SOFTWARE.
+ #include <dhcp/libdhcp++.h>
+ #include <dhcp/option_data_types.h>
+ #include <dhcp/option_custom.h>
+ #include <util/encode/hex.h>
+ namespace isc {
+ namespace dhcp {
+ OptionCustom::OptionCustom(const OptionDefinition& def,
+                              Universe u,
+                              const OptionBuffer& data)
+     : Option(u, def.getCode(), data.begin(), data.end()),
+       definition_(def) {
+     createBuffers();
+ }
+ OptionCustom::OptionCustom(const OptionDefinition& def,
+                              Universe u,
+                              OptionBufferConstIter first,
+                              OptionBufferConstIter last)
+     : Option(u, def.getCode(), first, last),
+       definition_(def) {
+     createBuffers();
+ }
+ void
+ OptionCustom::checkIndex(const uint32_t index) const {
+     if (index >= buffers_.size()) {
+         isc_throw(isc::OutOfRange, "specified data field index " << index
+                   << " is out of rangex.");
+     }
+ }
+ void
+ OptionCustom::createBuffers() {
+     // Check that the option definition is correct as we are going
+     // to use it to split the data_ buffer into set of sub buffers.
+     definition_.validate();
+     std::vector<OptionBuffer> buffers;
+     OptionBuffer::iterator data = data_.begin();
+     OptionDataType data_type = definition_.getType();
+     if (data_type == OPT_RECORD_TYPE) {
+         // An option comprises a record of data fields. We need to
+         // get types of these data fields to allocate enough space
+         // for each buffer.
+         const OptionDefinition::RecordFieldsCollection& fields =
+             definition_.getRecordFields();
+         // Go over all data fields within a record.
+         for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
+              field != fields.end(); ++field) {
+             // For fixed-size data type such as boolean, integer, even
+             // IP address we can use the utility function to get the required
+             // buffer size.
+             int data_size = OptionDataTypeUtil::getDataTypeLen(*field);
+             // For variable size types (such as string) the function above
+             // will return 0 so we need to do a runtime check. Since variable
+             // length data fields may be laid only at the end of an option we
+             // consume the rest of this option. Note that validate() function
+             // in OptionDefinition object should have checked whether the
+             // data fields layout is correct (that the variable string fields
+             // are laid at the end).
+             if (data_size == 0) {
+                 data_size = std::distance(data, data_.end());
+                 if (data_size == 0) {
+                     // If we reached the end of buffer we assume that this option is
+                     // truncated because there is no remaining data to initialize
+                     // an option field.
+                     if (data_size == 0) {
+                         isc_throw(OutOfRange, "option buffer truncated");
+                     }
+                 }
+             } else {
+                 // Our data field requires that there is a certain chunk of
+                 // data left in the buffer. If not, option is truncated.
+                 if (std::distance(data, data_.end()) < data_size) {
+                     isc_throw(OutOfRange, "option buffer truncated");
+                 }
+             }
+             // Store the created buffer.
+             buffers.push_back(OptionBuffer(data, data + data_size));
+             // Proceed to the next data field.
+             data += data_size;
+         }
+     } else if (data_type != OPT_EMPTY_TYPE) {
+         // If data_type value is other than OPT_RECORD_TYPE, our option is
+         // empty (have no data at all) or it comprises one or more
+         // data fields of the same type. The type of those fields
+         // is held in the data_type variable so let's use it to determine
+         // a size of buffers.
+         int data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
+         // The check below will fail if the input buffer is too short
+         // for the data size being held by this option.
+         // Note that data_size returned by getDataTypeLen may be zero
+         // if variable length data is being held by the option but
+         // this will not cause this check to throw exception.
+         if (std::distance(data, data_.end()) < data_size) {
+             isc_throw(OutOfRange, "option buffer truncated");
+         }
+         // For an array of values we are taking different path because
+         // we have to handle multiple buffers.
+         if (definition_.getArrayType()) {
+             // We don't perform other checks for data types that can't be
+             // used together with array indicator such as strings, empty field
+             // etc. This is because OptionDefinition::validate function should
+             // have checked this already. Thus data_size must be greater than
+             // zero.
+             assert(data_size > 0);
+             // Get equal chunks of data and store as collection of buffers.
+             // Truncate any remaining part which length is not divisible by
+             // data_size. Note that it is ok to truncate the data if and only
+             // if the data buffer is long enough to keep at least one value.
+             // This has been checked above already.
+             do {
+                 buffers.push_back(OptionBuffer(data, data + data_size));
+                 data += data_size;
+             } while (std::distance(data, data_.end()) >= data_size);
+         } else {
+             // For non-arrays the data_size can be zero because
+             // getDataTypeLen returns zero for variable size data types
+             // such as strings. Simply take whole buffer.
+             if (data_size == 0) {
+                 data_size = std::distance(data, data_.end());
+             }
+             if (data_size > 0) {
+                 buffers.push_back(OptionBuffer(data, data + data_size));
+             } else {
+                 isc_throw(OutOfRange, "option buffer truncated");
+             }
+         }
+     }
+     // If everything went ok we can replace old buffer set with new ones.
+     std::swap(buffers_, buffers);
+ }
+ std::string
+ OptionCustom::dataFieldToText(const OptionDataType data_type,
+                               const uint32_t index) const {
+     std::ostringstream text;
+     // Get the value of the data field.
+     switch (data_type) {
+     case OPT_BINARY_TYPE:
+         text << util::encode::encodeHex(readBinary(index));
+         break;
+     case OPT_BOOLEAN_TYPE:
+         text << (readBoolean(index) ? "true" : "false");
+         break;
+     case OPT_INT8_TYPE:
+         text << readInteger<int8_t>(index);
+         break;
+     case OPT_INT16_TYPE:
+         text << readInteger<int16_t>(index);
+         break;
+     case OPT_INT32_TYPE:
+         text << readInteger<int32_t>(index);
+         break;
+     case OPT_UINT8_TYPE:
+         text << readInteger<uint8_t>(index);
+         break;
+     case OPT_UINT16_TYPE:
+         text << readInteger<uint16_t>(index);
+         break;
+     case OPT_UINT32_TYPE:
+         text << readInteger<uint32_t>(index);
+         break;
+     case OPT_IPV4_ADDRESS_TYPE:
+     case OPT_IPV6_ADDRESS_TYPE:
+         text << readAddress(index).toText();
+         break;
+     case OPT_STRING_TYPE:
+         text << readString(index);
+         break;
+     default:
+         ;
+     }
+     // Append data field type in brackets.
+     text << " ( " << OptionDataTypeUtil::getDataTypeName(data_type) << " ) ";
+     return (text.str());
+ }
+ void
+ OptionCustom::pack4(isc::util::OutputBuffer& buf) {
+     if (len() > 255) {
+         isc_throw(OutOfRange, "DHCPv4 Option " << type_
+                   << " value is too high. At most 255 is supported.");
+     }
+     buf.writeUint8(type_);
+     buf.writeUint8(len() - getHeaderLen());
+     // Write data from buffers.
+     for (std::vector<OptionBuffer>::const_iterator it = buffers_.begin();
+          it != buffers_.end(); ++it) {
+         // In theory the createBuffers function should have taken
+         // care that there are no empty buffers added to the
+         // collection but it is almost always good to make sure.
+         if (!it->empty()) {
+             buf.writeData(&(*it)[0], it->size());
+         }
+     }
+     // Write suboptions.
 -    LibDHCP::packOptions(buf, options_);
++    packOptions(buf);
+ }
+ void
+ OptionCustom::pack6(isc::util::OutputBuffer& buf) {
+     buf.writeUint16(type_);
+     buf.writeUint16(len() - getHeaderLen());
+     // Write data from buffers.
+     for (std::vector<OptionBuffer>::const_iterator it = buffers_.begin();
+          it != buffers_.end(); ++it) {
+         if (!it->empty()) {
+             buf.writeData(&(*it)[0], it->size());
+         }
+     }
++    packOptions(buf);
+ }
+ asiolink::IOAddress
+ OptionCustom::readAddress(const uint32_t index) const {
+     checkIndex(index);
+     // The address being read can be either IPv4 or IPv6. The decision
+     // is made based on the buffer length. If it holds 4 bytes it is IPv4
+     // address, if it holds 16 bytes it is IPv6.
+     if (buffers_[index].size() == asiolink::V4ADDRESS_LEN) {
+         return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET));
+     } else if (buffers_[index].size() == asiolink::V6ADDRESS_LEN) {
+         return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET6));
+     } else {
+         isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
+                   << " IP address. Invalid buffer length " << buffers_[index].size());
+     }
+ }
+ const OptionBuffer&
+ OptionCustom::readBinary(const uint32_t index) const {
+     checkIndex(index);
+     return (buffers_[index]);
+ }
+ bool
+ OptionCustom::readBoolean(const uint32_t index) const {
+     checkIndex(index);
+     return (OptionDataTypeUtil::readBool(buffers_[index]));
+ }
+ std::string
+ OptionCustom::readString(const uint32_t index) const {
+     checkIndex(index);
+     return (OptionDataTypeUtil::readString(buffers_[index]));
+ }
+ void
+ OptionCustom::unpack(OptionBufferConstIter begin,
+                      OptionBufferConstIter end) {
+     data_ = OptionBuffer(begin, end);
+     // Chop the buffer stored in data_ into set of sub buffers.
+     createBuffers();
+ }
+ uint16_t
+ OptionCustom::len() {
+     // The length of the option is a sum of option header ...
+     int length = getHeaderLen();
+     // ... lengths of all buffers that hold option data ...
+     for (std::vector<OptionBuffer>::const_iterator buf = buffers_.begin();
+          buf != buffers_.end(); ++buf) {
+         length += buf->size();
+     }
+     // ... and lengths of all suboptions
+     for (OptionCustom::OptionCollection::iterator it = options_.begin();
+          it != options_.end();
+          ++it) {
+         length += (*it).second->len();
+     }
+     return (length);
+ }
+ void OptionCustom::setData(const OptionBufferConstIter first,
+                      const OptionBufferConstIter last) {
+     // We will copy entire option buffer, so we have to resize data_.
+     data_.resize(std::distance(first, last));
+     std::copy(first, last, data_.begin());
+     // Chop the data_ buffer into set of buffers that represent
+     // option fields data.
+     createBuffers();
+ }
+ std::string OptionCustom::toText(int indent) {
+     std::stringstream tmp;
+     for (int i = 0; i < indent; ++i)
+         tmp << " ";
+     tmp << "type=" << type_ << ", len=" << len()-getHeaderLen()
+         << ", data fields:" << std::endl;
+     OptionDataType data_type = definition_.getType();
+     if (data_type == OPT_RECORD_TYPE) {
+         const OptionDefinition::RecordFieldsCollection& fields =
+             definition_.getRecordFields();
+         // For record types we iterate over fields defined in
+         // option definition and match the appropriate buffer
+         // with them.
+         for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
+              field != fields.end(); ++field) {
+             for (int j = 0; j < indent + 2; ++j) {
+                 tmp << " ";
+             }
+             tmp << "#" << std::distance(fields.begin(), field) << " "
+                 << dataFieldToText(*field, std::distance(fields.begin(),
+                                                          field))
+                 << std::endl;
+         }
+     } else {
+         // For non-record types we iterate over all buffers
+         // and print the data type set globally for an option
+         // definition. We take the same code path for arrays
+         // and non-arrays as they only differ in such a way that
+         // non-arrays have just single data field.
+         for (unsigned int i = 0; i < getDataFieldsNum(); ++i) {
+             for (int j = 0; j < indent + 2; ++j) {
+                 tmp << " ";
+             }
+             tmp << "#" << i << " "
+                 << dataFieldToText(definition_.getType(), i)
+                 << std::endl;
+         }
+     }
+     // print suboptions
+     for (OptionCollection::const_iterator opt = options_.begin();
+          opt != options_.end();
+          ++opt) {
+         tmp << (*opt).second->toText(indent+2);
+     }
+     return tmp.str();
+ }
+ } // end of isc::dhcp namespace
+ } // end of isc namespace