123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- // 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.
- 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
|