Commit 28d885b4 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac2312'

parents d3c5e8d9 11e65849
......@@ -21,8 +21,9 @@ libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
libb10_dhcp___la_SOURCES += iface_mgr_sun.cc
libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
libb10_dhcp___la_SOURCES += option.cc option.h
libb10_dhcp___la_SOURCES += option_data_types.h
libb10_dhcp___la_SOURCES += option_data_types.cc option_data_types.h
libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h
libb10_dhcp___la_SOURCES += option_custom.cc option_custom.h
libb10_dhcp___la_SOURCES += option6_ia.cc option6_ia.h
libb10_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
......
......@@ -89,7 +89,7 @@ public:
// Depending on the data type length we use different utility functions
// writeUint16 or writeUint32 which write the data in the network byte
// order to the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// for either unsigned or signed integers so there is not need to create
// special cases for intX_t types.
switch (OptionDataTypeTraits<T>::len) {
case 1:
......@@ -128,7 +128,7 @@ public:
// Depending on the data type length we use different utility functions
// readUint16 or readUint32 which read the data laid in the network byte
// order from the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// for either unsigned or signed integers so there is not need to create
// special cases for intX_t types.
int data_size_len = OptionDataTypeTraits<T>::len;
switch (data_size_len) {
......
......@@ -118,7 +118,7 @@ public:
// Depending on the data type length we use different utility functions
// writeUint16 or writeUint32 which write the data in the network byte
// order to the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// for either unsigned or signed integers so there is not need to create
// special cases for intX_t types.
switch (OptionDataTypeTraits<T>::len) {
case 1:
......@@ -164,7 +164,7 @@ public:
// Depending on the data type length we use different utility functions
// readUint16 or readUint32 which read the data laid in the network byte
// order from the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// for either unsigned or signed integers so there is not need to create
// special cases for intX_t types.
int data_size_len = OptionDataTypeTraits<T>::len;
switch (data_size_len) {
......
// 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
// 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.
#ifndef OPTION_CUSTOM_H
#define OPTION_CUSTOM_H
#include <dhcp/option.h>
#include <dhcp/option_definition.h>
#include <util/io_utilities.h>
namespace isc {
namespace dhcp {
/// @brief Option with defined data fields represented as buffers that can
/// be accessed using data field index.
///
/// This class represents an option which has defined structure: data fields
/// of specific types and order. Those fields can be accessed using indexes,
/// where index 0 represents first data field within an option. The last
/// field can be accessed using index equal to 'number of fields' - 1.
/// Internally, the option data is stored as a collection of OptionBuffer
/// objects, each representing data for a particular data field. This data
/// can be converted to the actual data type using methods implemented
/// within this class. This class is used to represent those options that
/// can't be represented by any other specialized class (this excludes the
/// Option class which is generic and can be used to represent any option).
class OptionCustom : public Option {
public:
/// @brief Constructor, used for options to be sent.
///
/// This constructor creates an instance of an option from the whole
/// supplied buffer. This constructor is mainly used to create an
/// instances of options to be stored in outgoing DHCP packets.
/// The buffer used to create the instance of an option can be
/// created from the option data specified in server's configuration.
///
/// @param def option definition.
/// @param u specifies universe (V4 or V6).
/// @param data content of the option.
///
/// @throw OutOfRange if option buffer is truncated.
///
/// @todo list all exceptions thrown by ctor.
OptionCustom(const OptionDefinition& def, Universe u, const OptionBuffer& data);
/// @brief Constructor, used for received options.
///
/// This constructor creates an instance an option from the portion
/// of the buffer specified by iterators. This is mainly useful when
/// parsing received packets. Such packets are represented by a single
/// buffer holding option data and all sub options. Methods that are
/// parsing a packet, supply relevant portions of the packet buffer
/// to this constructor to create option instances out of it.
///
/// @param def option definition.
/// @param u specifies universe (V4 or V6).
/// @param first iterator to the first element that should be copied.
/// @param last iterator to the next element after the last one
/// to be copied.
///
/// @throw OutOfRange if option buffer is truncated.
///
/// @todo list all exceptions thrown by ctor.
OptionCustom(const OptionDefinition& def, Universe u,
OptionBufferConstIter first, OptionBufferConstIter last);
/// @brief Return a number of the data fields.
///
/// @return number of data fields held by the option.
uint32_t getDataFieldsNum() const { return (buffers_.size()); }
/// @brief Read a buffer as IP address.
///
/// @param index buffer index.
///
/// @return IP address read from a buffer.
/// @throw isc::OutOfRange if index is out of range.
asiolink::IOAddress readAddress(const uint32_t index) const;
/// @brief Read a buffer as binary data.
///
/// @param index buffer index.
///
/// @throw isc::OutOfRange if index is out of range.
/// @return read buffer holding binary data.
const OptionBuffer& readBinary(const uint32_t index) const;
/// @brief Read a buffer as boolean value.
///
/// @param index buffer index.
///
/// @throw isc::OutOfRange if index is out of range.
/// @return read boolean value.
bool readBoolean(const uint32_t index) const;
/// @brief Read a buffer as integer value.
///
/// @param index buffer index.
/// @tparam integer type of a value being returned.
///
/// @throw isc::OutOfRange if index is out of range.
/// @return read integer value.
template<typename T>
T readInteger(const uint32_t index) const {
checkIndex(index);
// Check that the requested return type is a supported integer.
if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(isc::dhcp::InvalidDataType, "specified data type to be returned"
" by readInteger is not supported integer type");
}
// Get the option definition type.
OptionDataType data_type = definition_.getType();
if (data_type == OPT_RECORD_TYPE) {
const OptionDefinition::RecordFieldsCollection& record_fields =
definition_.getRecordFields();
// When we initialized buffers we have already checked that
// the number of these buffers is equal to number of option
// fields in the record so the condition below should be met.
assert(index < record_fields.size());
// Get the data type to be returned.
data_type = record_fields[index];
}
// Requested data type must match the data type in a record.
if (OptionDataTypeTraits<T>::type != data_type) {
isc_throw(isc::dhcp::InvalidDataType,
"unable to read option field with index " << index
<< " as integer value. The field's data type"
<< data_type << " does not match the integer type"
<< "returned by the readInteger function.");
}
// When we created the buffer we have checked that it has a
// valid size so this condition here should be always fulfiled.
assert(buffers_[index].size() == OptionDataTypeTraits<T>::len);
// Read an integer value.
return (OptionDataTypeUtil::readInt<T>(buffers_[index]));
}
/// @brief Read a buffer as string value.
///
/// @param index buffer index.
///
/// @return string value read from buffer.
/// @throw isc::OutOfRange if index is out of range.
std::string readString(const uint32_t index) const;
/// @brief Parses received buffer.
///
/// @param begin iterator to first byte of option data
/// @param end iterator to end of option data (first byte after option end)
virtual void unpack(OptionBufferConstIter begin,
OptionBufferConstIter end);
/// @brief Returns string representation of the option.
///
/// @param indent number of spaces before printed text.
///
/// @return string with text representation.
virtual std::string toText(int indent = 0);
/// @brief Returns length of the complete option (data length +
/// DHCPv4/DHCPv6 option header)
///
/// @return length of the option
virtual uint16_t len();
/// @brief Sets content of this option from buffer.
///
/// Option will be resized to length of buffer.
///
/// @param first iterator pointing begining of buffer to copy.
/// @param last iterator pointing to end of buffer to copy.
void setData(const OptionBufferConstIter first,
const OptionBufferConstIter last);
protected:
/// @brief Writes DHCPv4 option in a wire format to a buffer.
///
/// @param buf output buffer (option will be stored there).
virtual void pack4(isc::util::OutputBuffer& buf);
/// @brief Writes DHCPv6 option in a wire format to a buffer.
///