|
@@ -249,6 +249,7 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
|
|
size_t* relay_msg_len /* = 0 */) {
|
|
|
size_t offset = 0;
|
|
|
size_t length = buf.size();
|
|
|
+ size_t last_offset = 0;
|
|
|
|
|
|
// Get the list of standard option definitions.
|
|
|
OptionDefContainer option_defs;
|
|
@@ -265,7 +266,17 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
|
|
|
|
|
// The buffer being read comprises a set of options, each starting with
|
|
|
// a two-byte type code and a two-byte length field.
|
|
|
- while (offset + 4 <= length) {
|
|
|
+ while (offset < length) {
|
|
|
+ // Save the current offset for backtracking
|
|
|
+ last_offset = offset;
|
|
|
+
|
|
|
+ // Check if there is room for another option
|
|
|
+ if (offset + 4 > length) {
|
|
|
+ // Still something but smaller than an option
|
|
|
+ return (last_offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parse the option header
|
|
|
uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
|
|
|
offset += 2;
|
|
|
|
|
@@ -273,13 +284,15 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
|
|
offset += 2;
|
|
|
|
|
|
if (offset + opt_len > length) {
|
|
|
- // @todo: consider throwing exception here.
|
|
|
-
|
|
|
- // We peeked at the option header of the next option, but discovered
|
|
|
- // that it would end up beyond buffer end, so the option is
|
|
|
- // truncated. Hence we can't parse it. Therefore we revert
|
|
|
- // back by those four bytes (as if we never parsed them).
|
|
|
- return (offset - 4);
|
|
|
+ // We peeked at the option header of the next option, but
|
|
|
+ // discovered that it would end up beyond buffer end, so
|
|
|
+ // the option is truncated. Hence we can't parse
|
|
|
+ // it. Therefore we revert back by those bytes (as if
|
|
|
+ // we never parsed them).
|
|
|
+ //
|
|
|
+ // @note it is the responsibility of the caller to throw
|
|
|
+ // an exception on partial parsing
|
|
|
+ return (last_offset);
|
|
|
}
|
|
|
|
|
|
if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
|
|
@@ -294,10 +307,10 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
|
|
|
|
|
if (opt_type == D6O_VENDOR_OPTS) {
|
|
|
if (offset + 4 > length) {
|
|
|
- // Truncated vendor-option. There is expected at least 4 bytes
|
|
|
- // long enterprise-id field. Let's roll back option code + option
|
|
|
- // length (4 bytes) and return.
|
|
|
- return (offset - 4);
|
|
|
+ // Truncated vendor-option. We expect at least
|
|
|
+ // 4 bytes for the enterprise-id field. Let's roll back
|
|
|
+ // option code + option length (4 bytes) and return.
|
|
|
+ return (last_offset);
|
|
|
}
|
|
|
|
|
|
// Parse this as vendor option
|
|
@@ -309,11 +322,11 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- // Get all definitions with the particular option code. Note that option
|
|
|
- // code is non-unique within this container however at this point we
|
|
|
- // expect to get one option definition with the particular code. If more
|
|
|
- // are returned we report an error.
|
|
|
+ // Get all definitions with the particular option code. Note
|
|
|
+ // that option code is non-unique within this container
|
|
|
+ // however at this point we expect to get one option
|
|
|
+ // definition with the particular code. If more are returned
|
|
|
+ // we report an error.
|
|
|
const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
|
|
|
// Get the number of returned option definitions for the option code.
|
|
|
size_t num_defs = distance(range.first, range.second);
|
|
@@ -321,16 +334,18 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
|
|
OptionPtr opt;
|
|
|
if (num_defs > 1) {
|
|
|
// Multiple options of the same code are not supported right now!
|
|
|
- isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
|
|
|
- " for option type " << opt_type << " returned. Currently it is not"
|
|
|
- " supported to initialize multiple option definitions"
|
|
|
- " for the same option code. This will be supported once"
|
|
|
- " support for option spaces is implemented");
|
|
|
+ isc_throw(isc::Unexpected, "Internal error: multiple option"
|
|
|
+ " definitions for option type " << opt_type <<
|
|
|
+ " returned. Currently it is not supported to initialize"
|
|
|
+ " multiple option definitions for the same option code."
|
|
|
+ " This will be supported once support for option spaces"
|
|
|
+ " is implemented");
|
|
|
} else if (num_defs == 0) {
|
|
|
- // @todo Don't crash if definition does not exist because only a few
|
|
|
- // option definitions are initialized right now. In the future
|
|
|
- // we will initialize definitions for all options and we will
|
|
|
- // remove this elseif. For now, return generic option.
|
|
|
+ // @todo Don't crash if definition does not exist because
|
|
|
+ // only a few option definitions are initialized right
|
|
|
+ // now. In the future we will initialize definitions for
|
|
|
+ // all options and we will remove this elseif. For now,
|
|
|
+ // return generic option.
|
|
|
opt = OptionPtr(new Option(Option::V6, opt_type,
|
|
|
buf.begin() + offset,
|
|
|
buf.begin() + offset + opt_len));
|
|
@@ -355,8 +370,9 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
|
|
|
const std::string& option_space,
|
|
|
isc::dhcp::OptionCollection& options) {
|
|
|
size_t offset = 0;
|
|
|
+ size_t last_offset = 0;
|
|
|
|
|
|
- // Get the list of stdandard option definitions.
|
|
|
+ // Get the list of standard option definitions.
|
|
|
OptionDefContainer option_defs;
|
|
|
if (option_space == "dhcp4") {
|
|
|
option_defs = LibDHCP::getOptionDefs(Option::V4);
|
|
@@ -371,12 +387,20 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
|
|
|
|
|
|
// The buffer being read comprises a set of options, each starting with
|
|
|
// a one-byte type code and a one-byte length field.
|
|
|
- while (offset + 1 <= buf.size()) {
|
|
|
+ while (offset < buf.size()) {
|
|
|
+ // Save the current offset for backtracking
|
|
|
+ last_offset = offset;
|
|
|
+
|
|
|
+ // Get the option type
|
|
|
uint8_t opt_type = buf[offset++];
|
|
|
|
|
|
// DHO_END is a special, one octet long option
|
|
|
- if (opt_type == DHO_END)
|
|
|
- return (offset); // just return. Don't need to add DHO_END option
|
|
|
+ if (opt_type == DHO_END) {
|
|
|
+ // just return. Don't need to add DHO_END option
|
|
|
+ // Don't return offset because it makes this condition
|
|
|
+ // and partial parsing impossible to recognize.
|
|
|
+ return (last_offset);
|
|
|
+ }
|
|
|
|
|
|
// DHO_PAD is just a padding after DHO_END. Let's continue parsing
|
|
|
// in case we receive a message without DHO_END.
|
|
@@ -384,30 +408,30 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
|
|
|
continue;
|
|
|
|
|
|
if (offset + 1 > buf.size()) {
|
|
|
- // opt_type must be cast to integer so as it is not treated as
|
|
|
- // unsigned char value (a number is presented in error message).
|
|
|
- isc_throw(OutOfRange, "Attempt to parse truncated option "
|
|
|
- << static_cast<int>(opt_type));
|
|
|
+ // We peeked at the option header of the next option, but
|
|
|
+ // discovered that it would end up beyond buffer end, so
|
|
|
+ // the option is truncated. Hence we can't parse
|
|
|
+ // it. Therefore we revert back (as if we never parsed it).
|
|
|
+ //
|
|
|
+ // @note it is the responsibility of the caller to throw
|
|
|
+ // an exception on partial parsing
|
|
|
+ return (last_offset);
|
|
|
}
|
|
|
|
|
|
uint8_t opt_len = buf[offset++];
|
|
|
if (offset + opt_len > buf.size()) {
|
|
|
-
|
|
|
- // We peeked at the option header of the next option, but discovered
|
|
|
- // that it would end up beyond buffer end, so the option is
|
|
|
- // truncated. Hence we can't parse it. Therefore we revert
|
|
|
- // back by two bytes (as if we never parsed them).
|
|
|
- return (offset - 2);
|
|
|
-
|
|
|
- // isc_throw(OutOfRange, "Option parse failed. Tried to parse "
|
|
|
- // << offset + opt_len << " bytes from " << buf.size()
|
|
|
- // << "-byte long buffer.");
|
|
|
+ // We peeked at the option header of the next option, but
|
|
|
+ // discovered that it would end up beyond buffer end, so
|
|
|
+ // the option is truncated. Hence we can't parse
|
|
|
+ // it. Therefore we revert back (as if we never parsed it).
|
|
|
+ return (last_offset);
|
|
|
}
|
|
|
|
|
|
- // Get all definitions with the particular option code. Note that option code
|
|
|
- // is non-unique within this container however at this point we expect
|
|
|
- // to get one option definition with the particular code. If more are
|
|
|
- // returned we report an error.
|
|
|
+ // Get all definitions with the particular option code. Note
|
|
|
+ // that option code is non-unique within this container
|
|
|
+ // however at this point we expect to get one option
|
|
|
+ // definition with the particular code. If more are returned
|
|
|
+ // we report an error.
|
|
|
const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
|
|
|
// Get the number of returned option definitions for the option code.
|
|
|
size_t num_defs = distance(range.first, range.second);
|
|
@@ -415,12 +439,13 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
|
|
|
OptionPtr opt;
|
|
|
if (num_defs > 1) {
|
|
|
// Multiple options of the same code are not supported right now!
|
|
|
- isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
|
|
|
- " for option type " << static_cast<int>(opt_type)
|
|
|
- << " returned. Currently it is not supported to initialize"
|
|
|
- << " multiple option definitions for the same option code."
|
|
|
- << " This will be supported once support for option spaces"
|
|
|
- << " is implemented");
|
|
|
+ isc_throw(isc::Unexpected, "Internal error: multiple option"
|
|
|
+ " definitions for option type " <<
|
|
|
+ static_cast<int>(opt_type) <<
|
|
|
+ " returned. Currently it is not supported to initialize"
|
|
|
+ " multiple option definitions for the same option code."
|
|
|
+ " This will be supported once support for option spaces"
|
|
|
+ " is implemented");
|
|
|
} else if (num_defs == 0) {
|
|
|
opt = OptionPtr(new Option(Option::V4, opt_type,
|
|
|
buf.begin() + offset,
|
|
@@ -448,7 +473,8 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
|
|
|
size_t length = buf.size();
|
|
|
|
|
|
// Get the list of option definitions for this particular vendor-id
|
|
|
- const OptionDefContainer* option_defs = LibDHCP::getVendorOption6Defs(vendor_id);
|
|
|
+ const OptionDefContainer* option_defs =
|
|
|
+ LibDHCP::getVendorOption6Defs(vendor_id);
|
|
|
|
|
|
// Get the search index #1. It allows to search for option definitions
|
|
|
// using option code. If there's no such vendor-id space, we're out of luck
|
|
@@ -460,7 +486,12 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
|
|
|
|
|
|
// The buffer being read comprises a set of options, each starting with
|
|
|
// a two-byte type code and a two-byte length field.
|
|
|
- while (offset + 4 <= length) {
|
|
|
+ while (offset < length) {
|
|
|
+ if (offset + 4 > length) {
|
|
|
+ isc_throw(OutOfRange,
|
|
|
+ "Vendor option parse failed: truncated header");
|
|
|
+ }
|
|
|
+
|
|
|
uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
|
|
|
offset += 2;
|
|
|
|
|
@@ -468,13 +499,9 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
|
|
|
offset += 2;
|
|
|
|
|
|
if (offset + opt_len > length) {
|
|
|
- // @todo: consider throwing exception here.
|
|
|
-
|
|
|
- // We peeked at the option header of the next option, but discovered
|
|
|
- // that it would end up beyond buffer end, so the option is
|
|
|
- // truncated. Hence we can't parse it. Therefore we revert
|
|
|
- // back by those four bytes (as if we never parsed them).
|
|
|
- return (offset - 4);
|
|
|
+ isc_throw(OutOfRange, "Vendor option parse failed. Tried to parse "
|
|
|
+ << offset + opt_len << " bytes from " << length
|
|
|
+ << "-byte long buffer.");
|
|
|
}
|
|
|
|
|
|
OptionPtr opt;
|
|
@@ -482,20 +509,25 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
|
|
|
|
|
|
// If there is a definition for such a vendor option...
|
|
|
if (idx) {
|
|
|
- // Get all definitions with the particular option code. Note that option
|
|
|
- // code is non-unique within this container however at this point we
|
|
|
- // expect to get one option definition with the particular code. If more
|
|
|
- // are returned we report an error.
|
|
|
- const OptionDefContainerTypeRange& range = idx->equal_range(opt_type);
|
|
|
- // Get the number of returned option definitions for the option code.
|
|
|
+ // Get all definitions with the particular option
|
|
|
+ // code. Note that option code is non-unique within this
|
|
|
+ // container however at this point we expect to get one
|
|
|
+ // option definition with the particular code. If more are
|
|
|
+ // returned we report an error.
|
|
|
+ const OptionDefContainerTypeRange& range =
|
|
|
+ idx->equal_range(opt_type);
|
|
|
+ // Get the number of returned option definitions for the
|
|
|
+ // option code.
|
|
|
size_t num_defs = distance(range.first, range.second);
|
|
|
|
|
|
if (num_defs > 1) {
|
|
|
- // Multiple options of the same code are not supported right now!
|
|
|
- isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
|
|
|
- " for option type " << opt_type << " returned. Currently it is not"
|
|
|
- " supported to initialize multiple option definitions"
|
|
|
- " for the same option code. This will be supported once"
|
|
|
+ // Multiple options of the same code are not supported
|
|
|
+ // right now!
|
|
|
+ isc_throw(isc::Unexpected, "Internal error: multiple option"
|
|
|
+ " definitions for option type " << opt_type <<
|
|
|
+ " returned. Currently it is not supported to"
|
|
|
+ " initialize multiple option definitions for the"
|
|
|
+ " same option code. This will be supported once"
|
|
|
" support for option spaces is implemented");
|
|
|
} else if (num_defs == 1) {
|
|
|
// The option definition has been found. Use it to create
|
|
@@ -510,7 +542,9 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
|
|
|
|
|
|
// This can happen in one of 2 cases:
|
|
|
// 1. we do not have definitions for that vendor-space
|
|
|
- // 2. we do have definitions, but that particular option was not defined
|
|
|
+ // 2. we do have definitions, but that particular option was
|
|
|
+ // not defined
|
|
|
+
|
|
|
if (!opt) {
|
|
|
opt = OptionPtr(new Option(Option::V6, opt_type,
|
|
|
buf.begin() + offset,
|
|
@@ -532,7 +566,8 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
|
|
|
size_t offset = 0;
|
|
|
|
|
|
// Get the list of stdandard option definitions.
|
|
|
- const OptionDefContainer* option_defs = LibDHCP::getVendorOption4Defs(vendor_id);
|
|
|
+ const OptionDefContainer* option_defs =
|
|
|
+ LibDHCP::getVendorOption4Defs(vendor_id);
|
|
|
// Get the search index #1. It allows to search for option definitions
|
|
|
// using option code.
|
|
|
const OptionDefContainerTypeIndex* idx = NULL;
|
|
@@ -542,47 +577,36 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
|
|
|
|
|
|
// The buffer being read comprises a set of options, each starting with
|
|
|
// a one-byte type code and a one-byte length field.
|
|
|
- while (offset + 1 <= buf.size()) {
|
|
|
-
|
|
|
- // Note that Vendor-Specific info option (RFC3925) has a different option
|
|
|
- // format than Vendor-Spec info for DHCPv6. (there's additional layer of
|
|
|
- // data-length
|
|
|
+ while (offset < buf.size()) {
|
|
|
+ // Note that Vendor-Specific info option (RFC3925) has a
|
|
|
+ // different option format than Vendor-Spec info for
|
|
|
+ // DHCPv6. (there's additional layer of data-length)
|
|
|
uint8_t data_len = buf[offset++];
|
|
|
|
|
|
if (offset + data_len > buf.size()) {
|
|
|
// The option is truncated.
|
|
|
-
|
|
|
- // We peeked at the data_len, but discovered that it would end up
|
|
|
- // beyond buffer end, so the data block is truncated. Hence we can't
|
|
|
- // parse it. Therefore we revert back by one byte (as if we never
|
|
|
- // parsed it).
|
|
|
- return (offset - 1);
|
|
|
+ isc_throw(OutOfRange, "Attempt to parse truncated vendor option");
|
|
|
}
|
|
|
|
|
|
uint8_t offset_end = offset + data_len;
|
|
|
|
|
|
// beginning of data-chunk parser
|
|
|
- while (offset + 1 <= offset_end) {
|
|
|
+ while (offset < offset_end) {
|
|
|
uint8_t opt_type = buf[offset++];
|
|
|
|
|
|
- // DHO_END is a special, one octet long option
|
|
|
- if (opt_type == DHO_END)
|
|
|
- return (offset); // just return. Don't need to add DHO_END option
|
|
|
-
|
|
|
- // DHO_PAD is just a padding after DHO_END. Let's continue parsing
|
|
|
- // in case we receive a message without DHO_END.
|
|
|
- if (opt_type == DHO_PAD)
|
|
|
- continue;
|
|
|
+ // No DHO_END or DHO_PAD in vendor options
|
|
|
|
|
|
- if (offset + 1 > buf.size()) {
|
|
|
- // opt_type must be cast to integer so as it is not treated as
|
|
|
- // unsigned char value (a number is presented in error message).
|
|
|
- isc_throw(OutOfRange, "Attempt to parse truncated vendor option "
|
|
|
+ if (offset + 1 > offset_end) {
|
|
|
+ // opt_type must be cast to integer so as it is not
|
|
|
+ // treated as unsigned char value (a number is
|
|
|
+ // presented in error message).
|
|
|
+ isc_throw(OutOfRange,
|
|
|
+ "Attempt to parse truncated vendor option "
|
|
|
<< static_cast<int>(opt_type));
|
|
|
}
|
|
|
|
|
|
uint8_t opt_len = buf[offset++];
|
|
|
- if (offset + opt_len > buf.size()) {
|
|
|
+ if (offset + opt_len > offset_end) {
|
|
|
isc_throw(OutOfRange, "Option parse failed. Tried to parse "
|
|
|
<< offset + opt_len << " bytes from " << buf.size()
|
|
|
<< "-byte long buffer.");
|
|
@@ -592,22 +616,27 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
|
|
|
opt.reset();
|
|
|
|
|
|
if (idx) {
|
|
|
- // Get all definitions with the particular option code. Note that option code
|
|
|
- // is non-unique within this container however at this point we expect
|
|
|
- // to get one option definition with the particular code. If more are
|
|
|
- // returned we report an error.
|
|
|
- const OptionDefContainerTypeRange& range = idx->equal_range(opt_type);
|
|
|
- // Get the number of returned option definitions for the option code.
|
|
|
+ // Get all definitions with the particular option
|
|
|
+ // code. Note that option code is non-unique within
|
|
|
+ // this container however at this point we expect to
|
|
|
+ // get one option definition with the particular
|
|
|
+ // code. If more are returned we report an error.
|
|
|
+ const OptionDefContainerTypeRange& range =
|
|
|
+ idx->equal_range(opt_type);
|
|
|
+ // Get the number of returned option definitions for
|
|
|
+ // the option code.
|
|
|
size_t num_defs = distance(range.first, range.second);
|
|
|
|
|
|
if (num_defs > 1) {
|
|
|
- // Multiple options of the same code are not supported right now!
|
|
|
- isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
|
|
|
- " for option type " << static_cast<int>(opt_type)
|
|
|
- << " returned. Currently it is not supported to initialize"
|
|
|
- << " multiple option definitions for the same option code."
|
|
|
- << " This will be supported once support for option spaces"
|
|
|
- << " is implemented");
|
|
|
+ // Multiple options of the same code are not
|
|
|
+ // supported right now!
|
|
|
+ isc_throw(isc::Unexpected, "Internal error: multiple"
|
|
|
+ " option definitions for option type "
|
|
|
+ << opt_type << " returned. Currently it is"
|
|
|
+ " not supported to initialize multiple option"
|
|
|
+ " definitions for the same option code."
|
|
|
+ " This will be supported once support for"
|
|
|
+ " option spaces is implemented");
|
|
|
} else if (num_defs == 1) {
|
|
|
// The option definition has been found. Use it to create
|
|
|
// the option instance from the provided buffer chunk.
|
|
@@ -630,6 +659,7 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
|
|
|
|
|
|
} // end of data-chunk
|
|
|
|
|
|
+ break; // end of the vendor block.
|
|
|
}
|
|
|
return (offset);
|
|
|
}
|