using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::hooks;
+using namespace isc::util;
namespace isc {
namespace dhcp {
// Does nothing
}
+OptionalValue<uint32_t>
+OptionDataParser::extractCode() const {
+ uint32_t code;
+ try {
+ code = uint32_values_->getParam("code");
+
+ } catch (const exception& ex) {
+ return (OptionalValue<uint32_t>(code));
+ }
+
+ if (code == 0) {
+ isc_throw(DhcpConfigError, "option code must not be zero "
+ "(" << uint32_values_->getPosition("code") << ")");
+
+ } else if (address_family_ == AF_INET &&
+ code > std::numeric_limits<uint8_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << code
+ << "', it must not be greater than '"
+ << static_cast<int>(std::numeric_limits<uint8_t>::max())
+ << "' (" << uint32_values_->getPosition("code") << ")");
+
+ } else if (address_family_ == AF_INET6 &&
+ code > std::numeric_limits<uint16_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << code
+ << "', it must not exceed '"
+ << std::numeric_limits<uint16_t>::max()
+ << "' (" << uint32_values_->getPosition("code") << ")");
+
+ }
+
+ return (OptionalValue<uint32_t>(code, OptionalValueState(true)));
+}
+
+OptionalValue<std::string>
+OptionDataParser::extractName() const {
+ std::string name;
+ try {
+ name = string_values_->getParam("name");
+
+ } catch (...) {
+ return (OptionalValue<std::string>(name));
+ }
+
+ if (name.empty()) {
+ isc_throw(DhcpConfigError, "option name is empty ("
+ << string_values_->getPosition("name") << ")");
+
+ } else if (name.find(" ") != std::string::npos) {
+ isc_throw(DhcpConfigError, "invalid option name '" << name
+ << "', space character is not allowed ("
+ << string_values_->getPosition("name") << ")");
+ }
+
+ return (OptionalValue<std::string>(name, OptionalValueState(true)));
+}
+
+std::string
+OptionDataParser::extractData() const {
+ std::string data;
+ try {
+ data = string_values_->getParam("data");
+
+ } catch (...) {
+ return (data);
+ }
+
+ return (data);
+}
+
+OptionalValue<bool>
+OptionDataParser::extractCSVFormat() const {
+ bool csv_format = true;
+ try {
+ csv_format = boolean_values_->getParam("csv-format");
+
+ } catch (...) {
+ return (OptionalValue<bool>(csv_format));
+ }
+
+ return (OptionalValue<bool>(csv_format, OptionalValueState(true)));
+}
+
+std::string
+OptionDataParser::extractSpace() const {
+ std::string space = address_family_ == AF_INET ? "dhcp4" : "dhcp6";
+ try {
+ space = string_values_->getParam("space");
+
+ } catch (...) {
+ return (space);
+ }
+
+ if (!OptionSpace::validateName(space)) {
+ isc_throw(DhcpConfigError, "invalid option space name '"
+ << space << "' ("
+ << string_values_->getPosition("space") << ")");
+ }
+
+ return (space);
+}
+
+
OptionDefinitionPtr
OptionDataParser::findServerSpaceOptionDefinition(const std::string& option_space,
const uint32_t option_code) const {
OptionDataParser::createOption(ConstElementPtr option_data) {
const Option::Universe universe = address_family_ == AF_INET ?
Option::V4 : Option::V6;
- // Check if mandatory parameters are specified.
- uint32_t code;
- std::string name;
- std::string data;
- try {
- code = uint32_values_->getParam("code");
- name = string_values_->getParam("name");
- data = string_values_->getParam("data");
- } catch (const std::exception& ex) {
- isc_throw(DhcpConfigError,
- ex.what() << "(" << option_data->getPosition() << ")");
- }
- // Check parameters having default values.
- std::string space = string_values_->getOptionalParam("space", universe == Option::V4 ?
- "dhcp4" : "dhcp6");
- bool csv_format = boolean_values_->getOptionalParam("csv-format", false);
-
- // Option code is held in the uint32_t storage but is supposed to
- // be uint16_t value. We need to check that value in the configuration
- // does not exceed range of uint8_t for DHCPv4, uint16_t for DHCPv6 and
- // is not zero.
- if (code == 0) {
- isc_throw(DhcpConfigError, "option code must not be zero "
- "(" << uint32_values_->getPosition("code") << ")");
-
- } else if (universe == Option::V4 &&
- code > std::numeric_limits<uint8_t>::max()) {
- isc_throw(DhcpConfigError, "invalid option code '" << code
- << "', it must not exceed '"
- << static_cast<int>(std::numeric_limits<uint8_t>::max())
- << "' (" << uint32_values_->getPosition("code") << ")");
- } else if (universe == Option::V6 &&
- code > std::numeric_limits<uint16_t>::max()) {
- isc_throw(DhcpConfigError, "invalid option code '" << code
- << "', it must not exceed '"
- << std::numeric_limits<uint16_t>::max()
- << "' (" << uint32_values_->getPosition("code") << ")");
-
- }
-
- // Check that the option name is non-empty and does not contain spaces
- if (name.empty()) {
- isc_throw(DhcpConfigError, "name of the option with code '"
- << code << "' is empty ("
- << string_values_->getPosition("name") << ")");
- } else if (name.find(" ") != std::string::npos) {
- isc_throw(DhcpConfigError, "invalid option name '" << name
- << "', space character is not allowed ("
- << string_values_->getPosition("name") << ")");
- }
-
- if (!OptionSpace::validateName(space)) {
- isc_throw(DhcpConfigError, "invalid option space name '"
- << space << "' specified for option '"
- << name << "', code '" << code
- << "' (" << string_values_->getPosition("space") << ")");
- }
+ OptionalValue<uint32_t> code_param = extractCode();
+ OptionalValue<std::string> name_param = extractName();
+ OptionalValue<bool> csv_format_param = extractCSVFormat();
+ std::string data_param = extractData();
+ std::string space_param = extractSpace();
// Find the Option Definition for the option by its option code.
// findOptionDefinition will throw if not found, no need to test.
// configuration.
OptionDefinitionPtr def;
try {
- def = findServerSpaceOptionDefinition(space, code);
+ def = findServerSpaceOptionDefinition(space_param, code_param);
} catch (const std::exception& ex) {
isc_throw(DhcpConfigError, ex.what()
// need to search for its definition among user-configured
// options. They are expected to be in the global storage
// already.
- def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get(space, code);
+ def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef()
+ ->get(space_param, code_param);
// It's ok if we don't have option format if the option is
// specified as hex
- if (!def && csv_format) {
+ if (!def && csv_format_param) {
isc_throw(DhcpConfigError, "definition for the option '"
- << space << "." << name
- << "' having code '" << code
+ << space_param << "." << name_param
+ << "' having code '" << code_param
<< "' does not exist ("
<< string_values_->getPosition("name") << ")");
}
std::vector<uint8_t> binary;
std::vector<std::string> data_tokens;
- if (csv_format) {
+ if (!csv_format_param.isSpecified() || csv_format_param) {
// If the option data is specified as a string of comma
// separated values then we need to split this string into
// individual values - each value will be used to initialize
// one data field of an option.
- data_tokens = isc::util::str::tokens(data, ",");
+ data_tokens = isc::util::str::tokens(data_param, ",");
} else {
// Otherwise, the option data is specified as a string of
// hexadecimal digits that we have to turn into binary format.
// The decodeHex function expects that the string contains an
// even number of digits. If we don't meet this requirement,
// we have to insert a leading 0.
- if (!data.empty() && data.length() % 2) {
- data = data.insert(0, "0");
+ if (!data_param.empty() && data_param.length() % 2) {
+ data_param = data_param.insert(0, "0");
}
- util::encode::decodeHex(data, binary);
+ util::encode::decodeHex(data_param, binary);
} catch (...) {
isc_throw(DhcpConfigError, "option data is not a valid"
- << " string of hexadecimal digits: " << data
+ << " string of hexadecimal digits: " << data_param
<< " (" << string_values_->getPosition("data") << ")");
}
}
OptionPtr option;
if (!def) {
- if (csv_format) {
+ if (csv_format_param.isSpecified() && csv_format_param) {
isc_throw(DhcpConfigError, "the CSV option data format can be"
" used to specify values for an option that has a"
- " definition. The option with code " << code
+ " definition. The option with code " << code_param
<< " does not have a definition ("
<< boolean_values_->getPosition("csv-format") << ")");
}
// definition does not exist for a particular option code. For now it is
// ok to create generic option if definition does not exist.
OptionPtr option(new Option(universe,
- static_cast<uint16_t>(code), binary));
+ static_cast<uint16_t>(code_param), binary));
// The created option is stored in option_descriptor_ class member
// until the commit stage when it is inserted into the main storage.
// If an option with the same code exists in main storage already the
// old option is replaced.
option_descriptor_.option_ = option;
option_descriptor_.persistent_ = false;
+
} else {
// Option name should match the definition. The option name
// to reference options and definitions using their names
// and/or option codes so keeping the option name in the
// definition of option value makes sense.
- if (def->getName() != name) {
+ if (def->getName() != name_param.get()) {
isc_throw(DhcpConfigError, "specified option name '"
- << name << "' does not match the "
- << "option definition: '" << space
+ << name_param << "' does not match the "
+ << "option definition: '" << space_param
<< "." << def->getName() << "' ("
<< string_values_->getPosition("name") << ")");
}
// Option definition has been found so let's use it to create
// an instance of our option.
try {
- OptionPtr option = csv_format ?
- def->optionFactory(universe, code, data_tokens) :
- def->optionFactory(universe, code, binary);
+ OptionPtr option = csv_format_param ?
+ def->optionFactory(universe, code_param, data_tokens) :
+ def->optionFactory(universe, code_param, binary);
OptionDescriptor desc(option, false);
option_descriptor_.option_ = option;
option_descriptor_.persistent_ = false;
} catch (const isc::Exception& ex) {
isc_throw(DhcpConfigError, "option data does not match"
- << " option definition (space: " << space
- << ", code: " << code << "): "
+ << " option definition (space: " << space_param
+ << ", code: " << code_param << "): "
<< ex.what() << " ("
<< string_values_->getPosition("data") << ")");
}
}
// All went good, so we can set the option space name.
- option_space_ = space;
+ option_space_ = space_param;
}
// **************************** OptionDataListParser *************************
#ifndef OPTIONAL_VALUE_H
#define OPTIONAL_VALUE_H
+#include <ostream>
+
namespace isc {
namespace util {
+/// @brief Indicate if an @c OptionalValue is is specified or not.
+///
+/// This is a simple wrapper class which holds a boolean value to indicate
+/// if the @c OptionalValue is specified or not. By using this class in the
+/// @c OptionalValue class constructor we avoid the ambiguity when the
+/// @c OptionalValue encapsulates a bool type.
+struct OptionalValueState {
+
+ /// @brief Constructor.
+ ///
+ /// @param specified A boolean value to be assigned.
+ OptionalValueState(const bool specified)
+ : specified_(specified) {
+ }
+ /// @brief A bool value encapsulated by this structure.
+ bool specified_;
+};
+
/// @brief Simple class representing an optional value.
///
/// This template class encapsulates a value of any type. An additional flag
class OptionalValue {
public:
+ /// @brief Default constructor.
+ ///
+ /// Note that the type @c T must have a default constructor to use this
+ /// constructor.
+ OptionalValue()
+ : value_(T()), specified_(false) {
+ }
+
/// @brief Constructor
///
/// Creates optional value. The value defaults to "unspecified".
///
/// @param value Default explicit value.
- /// @param specified Boolean value which determines if the value is
- /// initially specified or not (default is false).
- OptionalValue(const T& value, const bool specified = false)
- : value_(value),
- specified_(specified) {
+ /// @param specified Value which determines if the value is initially
+ // specified or not (default is false).
+ explicit OptionalValue(const T& value, const OptionalValueState& state =
+ OptionalValueState(false))
+ : value_(value), specified_(state.specified_) {
}
/// @brief Retrieves the actual value.
specify(true);
}
+ void specify(const OptionalValueState& state) {
+ specified_ = state.specified_;
+ }
+
/// @brief Sets the value to "specified" or "unspecified".
///
/// It does not alter the actual value. It only marks it "specified" or
bool specified_; ///< Flag which indicates if the value is specified.
};
+/// @brief Inserts an optional value to a stream.
+///
+/// This function overloads the global operator<< to behave as in
+/// @c ostream::operator<< but applied to @c OptionalValue objects.
+///
+/// @param os A @c std::ostream object to which the value is inserted.
+/// @param optional_value An @c OptionalValue object to be inserted into
+/// a stream.
+/// @tparam Type of the value encapsulated by the @c OptionalValue object.
+///
+/// @return A reference to the stream after insertion.
+template<typename T>
+std::ostream&
+operator<<(std::ostream& os, const OptionalValue<T>& optional_value) {
+ os << optional_value.get();
+ return (os);
+}
+
+
} // end of namespace isc::util
} // end of namespace isc