return (it->second(u, type, buf));
}
+size_t
+LibDHCP::MAX_RECUSION_LEVEL = 10;
+
size_t
LibDHCP::unpackOptions6(const OptionBuffer& buf, const string& option_space,
OptionCollection& options,
size_t* relay_msg_offset /* = 0 */,
- size_t* relay_msg_len /* = 0 */) {
+ size_t* relay_msg_len /* = 0 */,
+ size_t rec_level /* = 0 */) {
+ ++rec_level;
+ if (rec_level >= MAX_RECUSION_LEVEL) {
+ isc_throw(isc::Unexpected, "Too deep recursion in unpacking options");
+ }
size_t offset = 0;
size_t length = buf.size();
size_t last_offset = 0;
isc_throw_assert(def);
opt = def->optionFactory(Option::V6, opt_type,
buf.begin() + offset,
- buf.begin() + offset + opt_len);
+ buf.begin() + offset + opt_len,
+ rec_level);
} catch (const SkipThisOptionError&) {
opt.reset();
} catch (const SkipRemainingOptionsError&) {
/// offset to beginning of relay_msg option will be stored in it.
/// @param relay_msg_len reference to a size_t structure. If specified,
/// length of the relay_msg option will be stored in it.
+ /// @param rec_level recursion level.
/// @return offset to the first byte after the last successfully
/// parsed option
///
const std::string& option_space,
isc::dhcp::OptionCollection& options,
size_t* relay_msg_offset = 0,
- size_t* relay_msg_len = 0);
+ size_t* relay_msg_len = 0,
+ size_t rec_level = 0);
/// @brief Extend vendor options from fused options in multiple OptionVendor
/// or OptionVendorClass options and add respective suboptions or values.
/// @brief Get definition of D6O_IAADDR option.
static const OptionDefinition& D6O_IAADDR_DEF();
+ /// @brief Maximum level of recursion unpacking options.
+ ///
+ /// @note: avoid to blow the stack when unpacking recursive
+ /// embedded v6 space options from a thread (so not using
+ /// system stack) on a TCP (so possibly large) received packet.
+ static size_t MAX_RECUSION_LEVEL;
+
private:
/// Initialize DHCP option definitions.
}
void
-Option::unpackOptions(const OptionBuffer& buf) {
+Option::unpackOptions(const OptionBuffer& buf, size_t rec_level /* = 0 */) {
list<uint16_t> deferred;
switch (universe_) {
case V4:
getType() == DHO_VENDOR_ENCAPSULATED_OPTIONS);
return;
case V6:
- LibDHCP::unpackOptions6(buf, getEncapsulatedSpace(), options_);
+ LibDHCP::unpackOptions6(buf, getEncapsulatedSpace(), options_,
+ 0, 0, rec_level);
return;
default:
isc_throw(isc::BadValue, "Invalid universe type " << universe_);
/// different exceptions when option assembly fails.
///
/// @param buf buffer to be parsed.
+ /// @param rec_level recursion level.
///
/// @todo The set of exceptions thrown by this function depend on
/// exceptions thrown by unpack methods invoked on objects
/// representing sub options. We should consider whether to aggregate
/// those into one exception which can be documented here.
- void unpackOptions(const OptionBuffer& buf);
+ void unpackOptions(const OptionBuffer& buf, size_t rec_level = 0);
/// @brief Returns option header in the textual format.
///
createBuffers(getData());
}
+OptionCustom::OptionCustom(const OptionDefinition& def,
+ Universe u,
+ OptionBufferConstIter first,
+ OptionBufferConstIter last,
+ size_t rec_level)
+ : Option(u, def.getCode(), first, last),
+ definition_(def) {
+ setEncapsulatedSpace(def.getEncapsulatedSpace());
+ createBuffers(getData(), rec_level);
+}
+
OptionPtr
OptionCustom::clone() const {
return (cloneInternal<OptionCustom>());
}
void
-OptionCustom::createBuffers(const OptionBuffer& data_buf) {
+OptionCustom::createBuffers(const OptionBuffer& data_buf, size_t rec_level) {
// 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();
// Unpack suboptions if any.
else if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
- unpackOptions(OptionBuffer(data, data_buf.end()));
+ unpackOptions(OptionBuffer(data, data_buf.end()), rec_level);
}
} else if (data_type != OPT_EMPTY_TYPE) {
// Unpack suboptions if any.
if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
- unpackOptions(OptionBuffer(data, data_buf.end()));
+ unpackOptions(OptionBuffer(data, data_buf.end()), rec_level);
}
}
} else {
// Unpack suboptions if any.
if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
- unpackOptions(OptionBuffer(data, data_buf.end()));
+ unpackOptions(OptionBuffer(data, data_buf.end()), rec_level);
}
}
// If everything went ok we can replace old buffer set with new ones.
OptionCustom(const OptionDefinition& def, Universe u,
OptionBufferConstIter first, OptionBufferConstIter last);
+ /// @brief Constructor, used for received options with limited recursion.
+ ///
+ /// @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.
+ /// @param rec_level recursion level.
+ ///
+ /// @throw OutOfRange if option buffer is truncated.
+ ///
+ /// @todo list all exceptions thrown by ctor.
+ OptionCustom(const OptionDefinition& def, Universe u,
+ OptionBufferConstIter first, OptionBufferConstIter last,
+ size_t rec_level);
+
/// @brief Copies this option and returns a pointer to the copy.
virtual OptionPtr clone() const;
/// @brief Create collection of buffers representing data field values.
///
/// @param data_buf a buffer to be parsed.
- void createBuffers(const OptionBuffer& data_buf);
+ /// @param rec_level recursion level.
+ void createBuffers(const OptionBuffer& data_buf, size_t rec_level = 0);
/// @brief Return a text representation of a data field.
///
uint16_t type,
OptionBufferConstIter begin,
OptionBufferConstIter end,
- bool convenient_notation) const {
+ bool convenient_notation,
+ size_t rec_level) const {
try {
// Some of the options are represented by the specialized classes derived
if (getEncapsulatedSpace().empty()) {
return (factoryEmpty(u, type));
} else {
- return (OptionPtr(new OptionCustom(*this, u, begin, end)));
+ return (OptionPtr(new OptionCustom(*this, u, begin, end,
+ rec_level)));
}
case OPT_BINARY_TYPE:
// Do nothing. We will return generic option a few lines down.
;
}
- return (OptionPtr(new OptionCustom(*this, u, begin, end)));
+ return (OptionPtr(new OptionCustom(*this, u, begin, end, rec_level)));
} catch (const SkipThisOptionError&) {
// We need to throw this one as is.
throw;
/// as a string formatted in user-friendly, convenient way.
/// The flag is propagated to the option constructor, so that
/// the data could be parsed properly. Defaults to false.
+ /// @param rec_level recursion level.
///
/// @return instance of the DHCP option.
/// @throw InvalidOptionValue if data for the option is invalid.
uint16_t type,
OptionBufferConstIter begin,
OptionBufferConstIter end,
- bool convenient_notation = false) const;
+ bool convenient_notation = false,
+ size_t rec_level = 0) const;
/// @brief Option factory.
///