|
@@ -132,6 +132,35 @@ OptionCustom::checkIndex(const uint32_t index) const {
|
|
|
}
|
|
|
|
|
|
void
|
|
|
+OptionCustom::createBuffer(OptionBuffer& buffer,
|
|
|
+ const OptionDataType data_type) const {
|
|
|
+ // For data types that have a fixed size we can use the
|
|
|
+ // utility function to get the buffer's size.
|
|
|
+ size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
|
|
|
+
|
|
|
+ // For variable data sizes the utility function returns zero.
|
|
|
+ // It is ok for string values because the default string
|
|
|
+ // is 'empty'. However for FQDN the empty value is not valid
|
|
|
+ // so we initialize it to '.'. For prefix there is a prefix
|
|
|
+ // length fixed field.
|
|
|
+ if (data_size == 0) {
|
|
|
+ if (data_type == OPT_FQDN_TYPE) {
|
|
|
+ OptionDataTypeUtil::writeFqdn(".", buffer);
|
|
|
+
|
|
|
+ } else if (data_type == OPT_IPV6_PREFIX_TYPE) {
|
|
|
+ OptionDataTypeUtil::writePrefix(PrefixLen(0),
|
|
|
+ IOAddress::IPV6_ZERO_ADDRESS(),
|
|
|
+ buffer);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // At this point we can resize the buffer. Note that
|
|
|
+ // for string values we are setting the empty buffer
|
|
|
+ // here.
|
|
|
+ buffer.resize(data_size);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
OptionCustom::createBuffers() {
|
|
|
definition_.validate();
|
|
|
|
|
@@ -154,31 +183,7 @@ OptionCustom::createBuffers() {
|
|
|
for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
|
|
|
field != fields.end(); ++field) {
|
|
|
OptionBuffer buf;
|
|
|
-
|
|
|
- // For data types that have a fixed size we can use the
|
|
|
- // utility function to get the buffer's size.
|
|
|
- size_t data_size = OptionDataTypeUtil::getDataTypeLen(*field);
|
|
|
-
|
|
|
- // For variable data sizes the utility function returns zero.
|
|
|
- // It is ok for string values because the default string
|
|
|
- // is 'empty'. However for FQDN the empty value is not valid
|
|
|
- // so we initialize it to '.'. For prefix there is a prefix
|
|
|
- // length fixed field.
|
|
|
- if (data_size == 0) {
|
|
|
- if (*field == OPT_FQDN_TYPE) {
|
|
|
- OptionDataTypeUtil::writeFqdn(".", buf);
|
|
|
-
|
|
|
- } else if (*field == OPT_IPV6_PREFIX_TYPE) {
|
|
|
- OptionDataTypeUtil::writePrefix(PrefixLen(0),
|
|
|
- IOAddress::IPV6_ZERO_ADDRESS(),
|
|
|
- buf);
|
|
|
- }
|
|
|
- } else {
|
|
|
- // At this point we can resize the buffer. Note that
|
|
|
- // for string values we are setting the empty buffer
|
|
|
- // here.
|
|
|
- buf.resize(data_size);
|
|
|
- }
|
|
|
+ createBuffer(buf, *field);
|
|
|
// We have the buffer with default value prepared so we
|
|
|
// add it to the set of buffers.
|
|
|
buffers.push_back(buf);
|
|
@@ -193,21 +198,7 @@ OptionCustom::createBuffers() {
|
|
|
// For non-arrays we have a single value being held by the option
|
|
|
// so we have to allocate exactly one buffer.
|
|
|
OptionBuffer buf;
|
|
|
- size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
|
|
|
- if (data_size == 0) {
|
|
|
- if (data_type == OPT_FQDN_TYPE) {
|
|
|
- OptionDataTypeUtil::writeFqdn(".", buf);
|
|
|
-
|
|
|
- } else if (data_type == OPT_IPV6_PREFIX_TYPE) {
|
|
|
- OptionDataTypeUtil::writePrefix(PrefixLen(0),
|
|
|
- IOAddress::IPV6_ZERO_ADDRESS(),
|
|
|
- buf);
|
|
|
- }
|
|
|
- } else {
|
|
|
- // Note that if our option holds a string value then
|
|
|
- // we are making empty buffer here.
|
|
|
- buf.resize(data_size);
|
|
|
- }
|
|
|
+ createBuffer(buf, data_type);
|
|
|
// Add a buffer that we have created and leave.
|
|
|
buffers.push_back(buf);
|
|
|
}
|
|
@@ -217,6 +208,73 @@ OptionCustom::createBuffers() {
|
|
|
std::swap(buffers, buffers_);
|
|
|
}
|
|
|
|
|
|
+size_t
|
|
|
+OptionCustom::bufferLength(const OptionDataType data_type, bool in_array,
|
|
|
+ OptionBuffer::const_iterator begin,
|
|
|
+ OptionBuffer::const_iterator end) const {
|
|
|
+ // For fixed-size data type such as boolean, integer, even
|
|
|
+ // IP address we can use the utility function to get the required
|
|
|
+ // buffer size.
|
|
|
+ size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
|
|
|
+
|
|
|
+ // For variable size types (e.g. string) the function above will
|
|
|
+ // return 0 so we need to do a runtime check of the length.
|
|
|
+ if (data_size == 0) {
|
|
|
+ // FQDN is a special data type as it stores variable length data
|
|
|
+ // but the data length is encoded in the buffer. The easiest way
|
|
|
+ // to obtain the length of the data is to read the FQDN. The
|
|
|
+ // utility function will return the size of the buffer on success.
|
|
|
+ if (data_type == OPT_FQDN_TYPE) {
|
|
|
+ std::string fqdn =
|
|
|
+ OptionDataTypeUtil::readFqdn(OptionBuffer(begin, end));
|
|
|
+ // The size of the buffer holding an FQDN is always
|
|
|
+ // 1 byte larger than the size of the string
|
|
|
+ // representation of this FQDN.
|
|
|
+ data_size = fqdn.size() + 1;
|
|
|
+ } else if (!definition_.getArrayType() &&
|
|
|
+ ((data_type == OPT_BINARY_TYPE) ||
|
|
|
+ (data_type == OPT_STRING_TYPE))) {
|
|
|
+ // In other case we are dealing with string or binary value
|
|
|
+ // which size can't be determined. Thus we consume the
|
|
|
+ // remaining part of the buffer for it. Note that variable
|
|
|
+ // size data can be laid at the end of the option only and
|
|
|
+ // that the validate() function in OptionDefinition object
|
|
|
+ // should have checked wheter it is a case for this option.
|
|
|
+ data_size = std::distance(begin, end);
|
|
|
+ } else if (data_type == OPT_IPV6_PREFIX_TYPE) {
|
|
|
+ // The size of the IPV6 prefix type is determined as
|
|
|
+ // one byte (which is the size of the prefix in bits)
|
|
|
+ // followed by the prefix bits (right-padded with
|
|
|
+ // zeros to the nearest octet boundary)
|
|
|
+ if ((begin == end) && !in_array)
|
|
|
+ return 0;
|
|
|
+ PrefixTuple prefix =
|
|
|
+ OptionDataTypeUtil::readPrefix(OptionBuffer(begin, end));
|
|
|
+ // Data size comprises 1 byte holding a prefix length and the
|
|
|
+ // prefix length (in bytes) rounded to the nearest byte boundary.
|
|
|
+ data_size = sizeof(uint8_t) + (prefix.first.asUint8() + 7) / 8;
|
|
|
+ } else if (data_type == OPT_TUPLE_TYPE) {
|
|
|
+ OpaqueDataTuple::LengthFieldType lft =
|
|
|
+ getUniverse() == Option::V4 ?
|
|
|
+ OpaqueDataTuple::LENGTH_1_BYTE :
|
|
|
+ OpaqueDataTuple::LENGTH_2_BYTES;
|
|
|
+ std::string value =
|
|
|
+ OptionDataTypeUtil::readTuple(OptionBuffer(begin, end), lft);
|
|
|
+ data_size = value.size();
|
|
|
+ // The size of the buffer holding a tuple is always
|
|
|
+ // 1 or 2 byte larger than the size of the string
|
|
|
+ data_size += getUniverse() == Option::V4 ? 1 : 2;
|
|
|
+ } else {
|
|
|
+ // 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.
|
|
|
+ isc_throw(OutOfRange, "option buffer truncated");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return data_size;
|
|
|
+}
|
|
|
+
|
|
|
void
|
|
|
OptionCustom::createBuffers(const OptionBuffer& data_buf) {
|
|
|
// Check that the option definition is correct as we are going
|
|
@@ -237,60 +295,8 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
|
|
|
// 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.
|
|
|
- size_t data_size = OptionDataTypeUtil::getDataTypeLen(*field);
|
|
|
-
|
|
|
- // For variable size types (e.g. string) the function above will
|
|
|
- // return 0 so we need to do a runtime check of the length.
|
|
|
- if (data_size == 0) {
|
|
|
- // FQDN is a special data type as it stores variable length data
|
|
|
- // but the data length is encoded in the buffer. The easiest way
|
|
|
- // to obtain the length of the data is to read the FQDN. The
|
|
|
- // utility function will return the size of the buffer on success.
|
|
|
- if (*field == OPT_FQDN_TYPE) {
|
|
|
- std::string fqdn =
|
|
|
- OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_buf.end()));
|
|
|
- // The size of the buffer holding an FQDN is always
|
|
|
- // 1 byte larger than the size of the string
|
|
|
- // representation of this FQDN.
|
|
|
- data_size = fqdn.size() + 1;
|
|
|
- } else if ((*field == OPT_BINARY_TYPE) || (*field == OPT_STRING_TYPE)) {
|
|
|
- // In other case we are dealing with string or binary value
|
|
|
- // which size can't be determined. Thus we consume the
|
|
|
- // remaining part of the buffer for it. Note that variable
|
|
|
- // size data can be laid at the end of the option only and
|
|
|
- // that the validate() function in OptionDefinition object
|
|
|
- // should have checked wheter it is a case for this option.
|
|
|
- data_size = std::distance(data, data_buf.end());
|
|
|
- } else if (*field == OPT_IPV6_PREFIX_TYPE) {
|
|
|
- // The size of the IPV6 prefix type is determined as
|
|
|
- // one byte (which is the size of the prefix in bits)
|
|
|
- // followed by the prefix bits (right-padded with
|
|
|
- // zeros to the nearest octet boundary).
|
|
|
- if (std::distance(data, data_buf.end()) > 0) {
|
|
|
- data_size = static_cast<size_t>(sizeof(uint8_t) + (*data + 7) / 8);
|
|
|
- }
|
|
|
- } else if (*field == OPT_TUPLE_TYPE) {
|
|
|
- OpaqueDataTuple::LengthFieldType lft =
|
|
|
- getUniverse() == Option::V4 ?
|
|
|
- OpaqueDataTuple::LENGTH_1_BYTE :
|
|
|
- OpaqueDataTuple::LENGTH_2_BYTES;
|
|
|
- std::string value =
|
|
|
- OptionDataTypeUtil::readTuple(OptionBuffer(data, data_buf.end()),
|
|
|
- lft);
|
|
|
- data_size = value.size();
|
|
|
- // The size of the buffer holding a tuple is always
|
|
|
- // 1 or 2 byte larger than the size of the string
|
|
|
- data_size += getUniverse() == Option::V4 ? 1 : 2;
|
|
|
- } else {
|
|
|
- // 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.
|
|
|
- isc_throw(OutOfRange, "option buffer truncated");
|
|
|
- }
|
|
|
- }
|
|
|
+ size_t data_size = bufferLength(*field, false,
|
|
|
+ data, data_buf.end());
|
|
|
|
|
|
// Our data field requires that there is a certain chunk of
|
|
|
// data left in the buffer. If not, option is truncated.
|
|
@@ -304,8 +310,23 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
|
|
|
data += data_size;
|
|
|
}
|
|
|
|
|
|
+ // Get extra buffers when the last field is an array.
|
|
|
+ if (definition_.getArrayType()) {
|
|
|
+ while (data != data_buf.end()) {
|
|
|
+ // Code copied from the standard array case
|
|
|
+ size_t data_size = bufferLength(fields.back(), true,
|
|
|
+ data, data_buf.end());
|
|
|
+ assert(data_size > 0);
|
|
|
+ if (std::distance(data, data_buf.end()) < data_size) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ buffers.push_back(OptionBuffer(data, data + data_size));
|
|
|
+ data += data_size;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Unpack suboptions if any.
|
|
|
- if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
|
|
|
+ else if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
|
|
|
unpackOptions(OptionBuffer(data, data_buf.end()));
|
|
|
}
|
|
|
|
|
@@ -328,38 +349,7 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
|
|
|
// we have to handle multiple buffers.
|
|
|
if (definition_.getArrayType()) {
|
|
|
while (data != data_buf.end()) {
|
|
|
- // FQDN is a special case because it is of a variable length.
|
|
|
- // The actual length for a particular FQDN is encoded within
|
|
|
- // a buffer so we have to actually read the FQDN from a buffer
|
|
|
- // to get it.
|
|
|
- if (data_type == OPT_FQDN_TYPE) {
|
|
|
- std::string fqdn =
|
|
|
- OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_buf.end()));
|
|
|
- // The size of the buffer holding an FQDN is always
|
|
|
- // 1 byte larger than the size of the string
|
|
|
- // representation of this FQDN.
|
|
|
- data_size = fqdn.size() + 1;
|
|
|
-
|
|
|
- } else if (data_type == OPT_IPV6_PREFIX_TYPE) {
|
|
|
- PrefixTuple prefix =
|
|
|
- OptionDataTypeUtil::readPrefix(OptionBuffer(data, data_buf.end()));
|
|
|
- // Data size comprises 1 byte holding a prefix length and the
|
|
|
- // prefix length (in bytes) rounded to the nearest byte boundary.
|
|
|
- data_size = sizeof(uint8_t) + (prefix.first.asUint8() + 7) / 8;
|
|
|
- } else if (data_type == OPT_TUPLE_TYPE) {
|
|
|
- OpaqueDataTuple::LengthFieldType lft =
|
|
|
- getUniverse() == Option::V4 ?
|
|
|
- OpaqueDataTuple::LENGTH_1_BYTE :
|
|
|
- OpaqueDataTuple::LENGTH_2_BYTES;
|
|
|
- std::string value =
|
|
|
- OptionDataTypeUtil::readTuple(OptionBuffer(data, data_buf.end()),
|
|
|
- lft);
|
|
|
- data_size = value.size();
|
|
|
- // The size of the buffer holding a tuple is always
|
|
|
- // 1 or 2 byte larger than the size of the string
|
|
|
- data_size += getUniverse() == Option::V4 ? 1 : 2;
|
|
|
-
|
|
|
- }
|
|
|
+ data_size = bufferLength(data_type, true, data, data_buf.end());
|
|
|
// 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
|
|
@@ -381,38 +371,7 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
|
|
|
// 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) {
|
|
|
- // For FQDN we get the size by actually reading the FQDN.
|
|
|
- if (data_type == OPT_FQDN_TYPE) {
|
|
|
- std::string fqdn =
|
|
|
- OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_buf.end()));
|
|
|
- // The size of the buffer holding an FQDN is always
|
|
|
- // 1 bytes larger than the size of the string
|
|
|
- // representation of this FQDN.
|
|
|
- data_size = fqdn.size() + 1;
|
|
|
-
|
|
|
- } else if (data_type == OPT_IPV6_PREFIX_TYPE) {
|
|
|
- if (!data_buf.empty()) {
|
|
|
- data_size = static_cast<size_t>
|
|
|
- (sizeof(uint8_t) + (data_buf[0] + 7) / 8);
|
|
|
- }
|
|
|
- } else if (data_type == OPT_TUPLE_TYPE) {
|
|
|
- OpaqueDataTuple::LengthFieldType lft =
|
|
|
- getUniverse() == Option::V4 ?
|
|
|
- OpaqueDataTuple::LENGTH_1_BYTE :
|
|
|
- OpaqueDataTuple::LENGTH_2_BYTES;
|
|
|
- std::string value =
|
|
|
- OptionDataTypeUtil::readTuple(OptionBuffer(data, data_buf.end()),
|
|
|
- lft);
|
|
|
- data_size = value.size();
|
|
|
- // The size of the buffer holding a tuple is always
|
|
|
- // 1 or 2 byte larger than the size of the string
|
|
|
- data_size += getUniverse() == Option::V4 ? 1 : 2;
|
|
|
-
|
|
|
- } else {
|
|
|
- data_size = std::distance(data, data_buf.end());
|
|
|
- }
|
|
|
- }
|
|
|
+ data_size = bufferLength(data_type, false, data, data_buf.end());
|
|
|
if ((data_size > 0) && (std::distance(data, data_buf.end()) >= data_size)) {
|
|
|
buffers.push_back(OptionBuffer(data, data + data_size));
|
|
|
data += data_size;
|
|
@@ -744,6 +703,13 @@ std::string OptionCustom::toText(int indent) const {
|
|
|
output << " " << dataFieldToText(*field, std::distance(fields.begin(),
|
|
|
field));
|
|
|
}
|
|
|
+
|
|
|
+ // If the last record field is an array iterate on extra buffers
|
|
|
+ if (definition_.getArrayType()) {
|
|
|
+ for (unsigned int i = fields.size(); i < getDataFieldsNum(); ++i) {
|
|
|
+ output << " " << dataFieldToText(fields.back(), i);
|
|
|
+ }
|
|
|
+ }
|
|
|
} else {
|
|
|
// For non-record types we iterate over all buffers
|
|
|
// and print the data type set globally for an option
|