// PERFORMANCE OF THIS SOFTWARE.
#include <config/ccsession.h>
-#include <dhcp4/config_parser.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/cfgmgr.h>
+#include <dhcp4/config_parser.h>
#include <dhcpsrv/dbaccess_parser.h>
-#include <dhcpsrv/dhcp_config_parser.h>
+#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/option_space_container.h>
#include <util/encode/hex.h>
#include <util/strutil.h>
+
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
+
#include <limits>
#include <iostream>
#include <vector>
namespace {
-// Forward declarations of some of the parser classes.
-// They are used to define pointer types for these classes.
-class BooleanParser;
-class StringParser;
-class Uint32Parser;
-
// Pointers to various parser objects.
typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
typedef boost::shared_ptr<StringParser> StringParserPtr;
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
-/// @brief a factory method that will create a parser for a given element name
-typedef isc::dhcp::DhcpConfigParser* ParserFactory(const std::string& config_id);
-
-/// @brief a collection of factories that creates parsers for specified element names
-typedef std::map<std::string, ParserFactory*> FactoryMap;
-
-/// @brief Storage for option definitions.
-typedef OptionSpaceContainer<OptionDefContainer,
- OptionDefinitionPtr> OptionDefStorage;
-
/// @brief a collection of pools
///
/// That type is used as intermediate storage, when pools are parsed, but there is
/// no subnet object created yet to store them.
typedef std::vector<Pool4Ptr> PoolStorage;
-/// Collection of containers holding option spaces. Each container within
-/// a particular option space holds so-called option descriptors.
-typedef OptionSpaceContainer<Subnet::OptionContainer,
- Subnet::OptionDescriptor> OptionStorage;
/// @brief Global uint32 parameters that will be used as defaults.
Uint32Storage uint32_defaults;
/// @brief Global storage for option definitions.
OptionDefStorage option_def_intermediate;
-/// @brief a dummy configuration parser
-///
-/// It is a debugging parser. It does not configure anything,
-/// will accept any configuration and will just print it out
-/// on commit. Useful for debugging existing configurations and
-/// adding new ones.
-class DebugParser : public DhcpConfigParser {
-public:
-
- /// @brief Constructor
- ///
- /// See @ref DhcpConfigParser class for details.
- ///
- /// @param param_name name of the parsed parameter
- DebugParser(const std::string& param_name)
- :param_name_(param_name) {
- }
-
- /// @brief builds parameter value
- ///
- /// See @ref DhcpConfigParser class for details.
- ///
- /// @param new_config pointer to the new configuration
- virtual void build(ConstElementPtr new_config) {
- std::cout << "Build for token: [" << param_name_ << "] = ["
- << value_->str() << "]" << std::endl;
- value_ = new_config;
- }
-
- /// @brief pretends to apply the configuration
- ///
- /// This is a method required by base class. It pretends to apply the
- /// configuration, but in fact it only prints the parameter out.
- ///
- /// See @ref DhcpConfigParser class for details.
- virtual void commit() {
- // Debug message. The whole DebugParser class is used only for parser
- // debugging, and is not used in production code. It is very convenient
- // to keep it around. Please do not turn this cout into logger calls.
- std::cout << "Commit for token: [" << param_name_ << "] = ["
- << value_->str() << "]" << std::endl;
- }
-
- /// @brief factory that constructs DebugParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* Factory(const std::string& param_name) {
- return (new DebugParser(param_name));
- }
-
-private:
- /// name of the parsed parameter
- std::string param_name_;
-
- /// pointer to the actual value of the parameter
- ConstElementPtr value_;
-};
-
-/// @brief A boolean value parser.
-///
-/// This parser handles configuration values of the boolean type.
-/// Parsed values are stored in a provided storage. If no storage
-/// is provided then the build function throws an exception.
-class BooleanParser : public DhcpConfigParser {
-public:
- /// @brief Constructor.
- ///
- /// @param param_name name of the parameter.
- BooleanParser(const std::string& param_name)
- : storage_(NULL),
- param_name_(param_name),
- value_(false) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief Parse a boolean value.
- ///
- /// @param value a value to be parsed.
- ///
- /// @throw isc::InvalidOperation if a storage has not been set
- /// prior to calling this function
- /// @throw isc::dhcp::DhcpConfigError if a provided parameter's
- /// name is empty.
- virtual void build(ConstElementPtr value) {
- if (storage_ == NULL) {
- isc_throw(isc::InvalidOperation, "parser logic error:"
- << " storage for the " << param_name_
- << " value has not been set");
- } else if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- // The Config Manager checks if user specified a
- // valid value for a boolean parameter: True or False.
- // It is then ok to assume that if str() does not return
- // 'true' the value is 'false'.
- value_ = (value->str() == "true") ? true : false;
- }
-
- /// @brief Put a parsed value to the storage.
- virtual void commit() {
- if (storage_ != NULL && !param_name_.empty()) {
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief Create an instance of the boolean parser.
- ///
- /// @param param_name name of the parameter for which the
- /// parser is created.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new BooleanParser(param_name));
- }
-
- /// @brief Set the storage for parsed value.
- ///
- /// This function must be called prior to calling build.
- ///
- /// @param storage a pointer to the storage where parsed data
- /// is to be stored.
- void setStorage(BooleanStorage* storage) {
- storage_ = storage;
- }
-
-private:
- /// Pointer to the storage where parsed value is stored.
- BooleanStorage* storage_;
- /// Name of the parameter which value is parsed with this parser.
- std::string param_name_;
- /// Parsed value.
- bool value_;
-};
-
-/// @brief Configuration parser for uint32 parameters
-///
-/// This class is a generic parser that is able to handle any uint32 integer
-/// type. By default it stores the value in external global container
-/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
-/// in subnet config), it can be pointed to a different storage, using
-/// setStorage() method. This class follows the parser interface, laid out
-/// in its base class, @ref DhcpConfigParser.
-///
-/// For overview of usability of this generic purpose parser, see
-/// @ref dhcpv4ConfigInherit page.
-class Uint32Parser : public DhcpConfigParser {
-public:
-
- /// @brief constructor for Uint32Parser
- /// @param param_name name of the configuration parameter being parsed
- Uint32Parser(const std::string& param_name)
- : storage_(&uint32_defaults),
- param_name_(param_name) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief Parses configuration configuration parameter as uint32_t.
- ///
- /// @param value pointer to the content of parsed values
- /// @throw BadValue if supplied value could not be base to uint32_t
- /// or the parameter name is empty.
- virtual void build(ConstElementPtr value) {
- if (param_name_.empty()) {
- isc_throw(DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
-
- int64_t check;
- string x = value->str();
- try {
- check = boost::lexical_cast<int64_t>(x);
- } catch (const boost::bad_lexical_cast &) {
- isc_throw(BadValue, "Failed to parse value " << value->str()
- << " as unsigned 32-bit integer.");
- }
- if (check > std::numeric_limits<uint32_t>::max()) {
- isc_throw(BadValue, "Value " << value->str() << "is too large"
- << " for unsigned 32-bit integer.");
- }
- if (check < 0) {
- isc_throw(BadValue, "Value " << value->str() << "is negative."
- << " Only 0 or larger are allowed for unsigned 32-bit integer.");
- }
-
- // value is small enough to fit
- value_ = static_cast<uint32_t>(check);
- }
-
- /// @brief Stores the parsed uint32_t value in a storage.
- virtual void commit() {
- if (storage_ != NULL && !param_name_.empty()) {
- // If a given parameter already exists in the storage we override
- // its value. If it doesn't we insert a new element.
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief factory that constructs Uint32Parser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new Uint32Parser(param_name));
- }
-
- /// @brief sets storage for value of this parameter
- ///
- /// See @ref dhcpv4ConfigInherit for details.
- ///
- /// @param storage pointer to the storage container
- void setStorage(Uint32Storage* storage) {
- storage_ = storage;
- }
-
-private:
- /// pointer to the storage, where parsed value will be stored
- Uint32Storage* storage_;
-
- /// name of the parameter to be parsed
- std::string param_name_;
-
- /// the actual parsed value
- uint32_t value_;
-};
-
-/// @brief Configuration parser for string parameters
-///
-/// This class is a generic parser that is able to handle any string
-/// parameter. By default it stores the value in external global container
-/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
-/// in subnet config), it can be pointed to a different storage, using
-/// setStorage() method. This class follows the parser interface, laid out
-/// in its base class, @ref DhcpConfigParser.
-///
-/// For overview of usability of this generic purpose parser, see
-/// @ref dhcpv4ConfigInherit page.
-class StringParser : public DhcpConfigParser {
-public:
-
- /// @brief constructor for StringParser
- /// @param param_name name of the configuration parameter being parsed
- StringParser(const std::string& param_name)
- :storage_(&string_defaults), param_name_(param_name) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief parses parameter value
- ///
- /// Parses configuration entry and stores it in storage. See
- /// @ref setStorage() for details.
- ///
- /// @param value pointer to the content of parsed values
- virtual void build(ConstElementPtr value) {
- value_ = value->str();
- boost::erase_all(value_, "\"");
- }
-
- /// @brief Stores the parsed value in a storage.
- virtual void commit() {
- if (storage_ != NULL && !param_name_.empty()) {
- // If a given parameter already exists in the storage we override
- // its value. If it doesn't we insert a new element.
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief factory that constructs StringParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new StringParser(param_name));
- }
-
- /// @brief sets storage for value of this parameter
- ///
- /// See \ref dhcpv4ConfigInherit for details.
- ///
- /// @param storage pointer to the storage container
- void setStorage(StringStorage* storage) {
- storage_ = storage;
- }
-
-private:
- /// pointer to the storage, where parsed value will be stored
- StringStorage* storage_;
-
- /// name of the parameter to be parsed
- std::string param_name_;
-
- /// the actual parsed value
- std::string value_;
-};
-
-
-/// @brief parser for interface list definition
-///
-/// This parser handles Dhcp4/interface entry.
-/// It contains a list of network interfaces that the server listens on.
-/// In particular, it can contain an entry called "all" or "any" that
-/// designates all interfaces.
-///
-/// It is useful for parsing Dhcp4/interface parameter.
-class InterfaceListConfigParser : public DhcpConfigParser {
-public:
-
- /// @brief constructor
- ///
- /// As this is a dedicated parser, it must be used to parse
- /// "interface" parameter only. All other types will throw exception.
- ///
- /// @param param_name name of the configuration parameter being parsed
- /// @throw BadValue if supplied parameter name is not "interface"
- InterfaceListConfigParser(const std::string& param_name) {
- if (param_name != "interface") {
- isc_throw(BadValue, "Internal error. Interface configuration "
- "parser called for the wrong parameter: " << param_name);
- }
- }
-
- /// @brief parses parameters value
- ///
- /// Parses configuration entry (list of parameters) and adds each element
- /// to the interfaces list.
- ///
- /// @param value pointer to the content of parsed values
- virtual void build(ConstElementPtr value) {
- BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
- interfaces_.push_back(iface->str());
- }
- }
-
- /// @brief commits interfaces list configuration
- virtual void commit() {
- /// @todo: Implement per interface listening. Currently always listening
- /// on all interfaces.
- }
-
- /// @brief factory that constructs InterfaceListConfigParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new InterfaceListConfigParser(param_name));
- }
-
-private:
- /// contains list of network interfaces
- vector<string> interfaces_;
-};
-
/// @brief parser for pool definition
///
/// This parser handles pool definitions, i.e. a list of entries of one
// ignore parameter name, it is always Dhcp4/subnet4[X]/pool
}
+ /// @brief constructor.
+ PoolParser(const std::string& /*param_name*/, PoolStorage* pools)
+ :pools_(pools) {
+ // ignore parameter name, it is always Dhcp4/subnet4[X]/pool
+ }
+
/// @brief parses the actual list
///
/// This method parses the actual list of interfaces.
PoolStorage local_pools_;
};
-/// @brief Parser for option data value.
-///
-/// This parser parses configuration entries that specify value of
-/// a single option. These entries include option name, option code
-/// and data carried by the option. The option data can be specified
-/// in one of the two available formats: binary value represented as
-/// a string of hexadecimal digits or a list of comma separated values.
-/// The format being used is controlled by csv-format configuration
-/// parameter. When setting this value to True, the latter format is
-/// used. The subsequent values in the CSV format apply to relevant
-/// option data fields in the configured option. For example the
-/// configuration: "data" : "192.168.2.0, 56, hello world" can be
-/// used to set values for the option comprising IPv4 address,
-/// integer and string data field. Note that order matters. If the
-/// order of values does not match the order of data fields within
-/// an option the configuration will not be accepted. If parsing
-/// is successful then an instance of an option is created and
-/// added to the storage provided by the calling class.
-class OptionDataParser : public DhcpConfigParser {
-public:
-
- /// @brief Constructor.
- ///
- /// Class constructor.
- OptionDataParser(const std::string&)
- : options_(NULL),
- // initialize option to NULL ptr
- option_descriptor_(false) { }
-
- /// @brief Parses the single option data.
- ///
- /// This method parses the data of a single option from the configuration.
- /// The option data includes option name, option code and data being
- /// carried by this option. Eventually it creates the instance of the
- /// option.
- ///
- /// @warning setStorage must be called with valid storage pointer prior
- /// to calling this method.
- ///
- /// @param option_data_entries collection of entries that define value
- /// for a particular option.
- /// @throw DhcpConfigError if invalid parameter specified in
- /// the configuration.
- /// @throw isc::InvalidOperation if failed to set storage prior to
- /// calling build.
- virtual void build(ConstElementPtr option_data_entries) {
- if (options_ == NULL) {
- isc_throw(isc::InvalidOperation, "Parser logic error: storage must be set before "
- "parsing option data.");
- }
- BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
- ParserPtr parser;
- if (param.first == "name" || param.first == "data" ||
- param.first == "space") {
- boost::shared_ptr<StringParser>
- name_parser(dynamic_cast<StringParser*>(StringParser::factory(param.first)));
- if (name_parser) {
- name_parser->setStorage(&string_values_);
- parser = name_parser;
- }
- } else if (param.first == "code") {
- boost::shared_ptr<Uint32Parser>
- code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(param.first)));
- if (code_parser) {
- code_parser->setStorage(&uint32_values_);
- parser = code_parser;
- }
- } else if (param.first == "csv-format") {
- boost::shared_ptr<BooleanParser>
- value_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(param.first)));
- if (value_parser) {
- value_parser->setStorage(&boolean_values_);
- parser = value_parser;
- }
- } else {
- isc_throw(DhcpConfigError,
- "Parser error: option-data parameter not supported: "
- << param.first);
- }
- parser->build(param.second);
- // Before we can create an option we need to get the data from
- // the child parsers. The only way to do it is to invoke commit
- // on them so as they store the values in appropriate storages
- // that this class provided to them. Note that this will not
- // modify values stored in the global storages so the configuration
- // will remain consistent even parsing fails somewhere further on.
- parser->commit();
- }
- // Try to create the option instance.
- createOption();
- }
-
- /// @brief Commits option value.
- ///
- /// This function adds a new option to the storage or replaces an existing option
- /// with the same code.
- ///
- /// @throw isc::InvalidOperation if failed to set pointer to storage or failed
- /// to call build() prior to commit. If that happens data in the storage
- /// remain un-modified.
- virtual void commit() {
- if (options_ == NULL) {
- isc_throw(isc::InvalidOperation, "parser logic error: storage must be set before "
- "committing option data.");
- } else if (!option_descriptor_.option) {
- // Before we can commit the new option should be configured. If it is not
- // than somebody must have called commit() before build().
- isc_throw(isc::InvalidOperation, "parser logic error: no option has been configured and"
- " thus there is nothing to commit. Has build() been called?");
- }
- uint16_t opt_type = option_descriptor_.option->getType();
- Subnet::OptionContainerPtr options = options_->getItems(option_space_);
- // The getItems() should never return NULL pointer. If there are no
- // options configured for the particular option space a pointer
- // to an empty container should be returned.
- assert(options);
- Subnet::OptionContainerTypeIndex& idx = options->get<1>();
- // Try to find options with the particular option code in the main
- // storage. If found, remove these options because they will be
- // replaced with new one.
- Subnet::OptionContainerTypeRange range =
- idx.equal_range(opt_type);
- if (std::distance(range.first, range.second) > 0) {
- idx.erase(range.first, range.second);
- }
- // Append new option to the main storage.
- options_->addItem(option_descriptor_, option_space_);
- }
-
- /// @brief Set storage for the parser.
- ///
- /// Sets storage for the parser. This storage points to the
- /// vector of options and is used by multiple instances of
- /// OptionDataParser. Each instance creates exactly one object
- /// of dhcp::Option or derived type and appends it to this
- /// storage.
- ///
- /// @param storage pointer to the options storage
- void setStorage(OptionStorage* storage) {
- options_ = storage;
- }
-
-private:
-
- /// @brief Create option instance.
- ///
- /// Creates an instance of an option and adds it to the provided
- /// options storage. If the option data parsed by \ref build function
- /// are invalid or insufficient this function emits an exception.
- ///
- /// @warning this function does not check if options_ storage pointer
- /// is intitialized but this check is not needed here because it is done
- /// in the \ref build function.
- ///
- /// @throw DhcpConfigError if parameters provided in the configuration
- /// are invalid.
- void createOption() {
- // 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 and is not zero.
- uint32_t option_code = uint32_values_.getParam("code");
- if (option_code == 0) {
- isc_throw(DhcpConfigError, "option code must not be zero."
- << " Option code '0' is reserved in DHCPv4.");
- } else if (option_code > std::numeric_limits<uint8_t>::max()) {
- isc_throw(DhcpConfigError, "invalid option code '" << option_code
- << "', it must not exceed '"
- << std::numeric_limits<uint8_t>::max() << "'");
- }
-
- // Check that the option name has been specified, is non-empty and does not
- // contain spaces
- std::string option_name = string_values_.getParam("name");
- if (option_name.empty()) {
- isc_throw(DhcpConfigError, "name of the option with code '"
- << option_code << "' is empty");
- } else if (option_name.find(" ") != std::string::npos) {
- isc_throw(DhcpConfigError, "invalid option name '" << option_name
- << "', space character is not allowed");
- }
-
- std::string option_space = string_values_.getParam("space");
- if (!OptionSpace::validateName(option_space)) {
- isc_throw(DhcpConfigError, "invalid option space name '"
- << option_space << "' specified for option '"
- << option_name << "' (code '" << option_code
- << "')");
- }
-
- OptionDefinitionPtr def;
- if (option_space == "dhcp4" &&
- LibDHCP::isStandardOption(Option::V4, option_code)) {
- def = LibDHCP::getOptionDef(Option::V4, option_code);
-
- } else if (option_space == "dhcp6") {
- isc_throw(DhcpConfigError, "'dhcp6' option space name is reserved"
- << " for DHCPv6 server");
- } else {
- // If we are not dealing with a standard option then we
- // need to search for its definition among user-configured
- // options. They are expected to be in the global storage
- // already.
- OptionDefContainerPtr defs = option_def_intermediate.getItems(option_space);
- // The getItems() should never return the NULL pointer. If there are
- // no option definitions for the particular option space a pointer
- // to an empty container should be returned.
- assert(defs);
- const OptionDefContainerTypeIndex& idx = defs->get<1>();
- OptionDefContainerTypeRange range = idx.equal_range(option_code);
- if (std::distance(range.first, range.second) > 0) {
- def = *range.first;
- }
- if (!def) {
- isc_throw(DhcpConfigError, "definition for the option '"
- << option_space << "." << option_name
- << "' having code '" << option_code
- << "' does not exist");
- }
-
- }
-
- // Get option data from the configuration database ('data' field).
- const std::string option_data = string_values_.getParam("data");
- const bool csv_format = boolean_values_.getParam("csv-format");
-
- // Transform string of hexadecimal digits into binary format.
- std::vector<uint8_t> binary;
- std::vector<std::string> data_tokens;
-
- if (csv_format) {
- // 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(option_data, ",");
- } else {
- // Otherwise, the option data is specified as a string of
- // hexadecimal digits that we have to turn into binary format.
- try {
- util::encode::decodeHex(option_data, binary);
- } catch (...) {
- isc_throw(DhcpConfigError, "option data is not a valid"
- << " string of hexadecimal digits: " << option_data);
- }
- }
-
- OptionPtr option;
- if (!def) {
- if (csv_format) {
- 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 " << option_code
- << " does not have a definition.");
- }
-
- // @todo We have a limited set of option definitions intiialized at the moment.
- // In the future we want to initialize option definitions for all options.
- // Consequently an error will be issued if an option 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(Option::V4, static_cast<uint16_t>(option_code),
- 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
- // may seem to be redundant but in the future we may want
- // 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() != option_name) {
- isc_throw(DhcpConfigError, "specified option name '"
- << option_name << "' does not match the "
- << "option definition: '" << option_space
- << "." << def->getName() << "'");
- }
-
- // Option definition has been found so let's use it to create
- // an instance of our option.
- try {
- OptionPtr option = csv_format ?
- def->optionFactory(Option::V4, option_code, data_tokens) :
- def->optionFactory(Option::V4, option_code, binary);
- Subnet::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: " << option_space
- << ", code: " << option_code << "): "
- << ex.what());
- }
-
- }
- // All went good, so we can set the option space name.
- option_space_ = option_space;
- }
-
- /// Storage for uint32 values (e.g. option code).
- Uint32Storage uint32_values_;
- /// Storage for string values (e.g. option name or data).
- StringStorage string_values_;
- /// Storage for boolean values.
- BooleanStorage boolean_values_;
- /// Pointer to options storage. This storage is provided by
- /// the calling class and is shared by all OptionDataParser objects.
- OptionStorage* options_;
- /// Option descriptor holds newly configured option.
- Subnet::OptionDescriptor option_descriptor_;
- /// Option space name where the option belongs to.
- std::string option_space_;
-};
-
-/// @brief Parser for option data values within a subnet.
-///
-/// This parser iterates over all entries that define options
-/// data for a particular subnet and creates a collection of options.
-/// If parsing is successful, all these options are added to the Subnet
-/// object.
-class OptionDataListParser : public DhcpConfigParser {
-public:
-
- /// @brief Constructor.
- ///
- /// Unless otherwise specified, parsed options will be stored in
- /// a global option container (option_default). That storage location
- /// is overriden on a subnet basis.
- OptionDataListParser(const std::string&)
- : options_(&option_defaults), local_options_() { }
-
- /// @brief Parses entries that define options' data for a subnet.
- ///
- /// This method iterates over all entries that define option data
- /// for options within a single subnet and creates options' instances.
- ///
- /// @param option_data_list pointer to a list of options' data sets.
- /// @throw DhcpConfigError if option parsing failed.
- void build(ConstElementPtr option_data_list) {
- BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
- boost::shared_ptr<OptionDataParser> parser(new OptionDataParser("option-data"));
- // options_ member will hold instances of all options thus
- // each OptionDataParser takes it as a storage.
- parser->setStorage(&local_options_);
- // Build the instance of a single option.
- parser->build(option_value);
- // Store a parser as it will be used to commit.
- parsers_.push_back(parser);
- }
- }
-
- /// @brief Set storage for option instances.
- ///
- /// @param storage pointer to options storage.
- void setStorage(OptionStorage* storage) {
- options_ = storage;
- }
-
-
- /// @brief Commit all option values.
- ///
- /// This function invokes commit for all option values.
- void commit() {
- BOOST_FOREACH(ParserPtr parser, parsers_) {
- parser->commit();
- }
- // Parsing was successful and we have all configured
- // options in local storage. We can now replace old values
- // with new values.
- std::swap(local_options_, *options_);
- }
-
- /// @brief Create DhcpDataListParser object
- ///
- /// @param param_name param name.
- ///
- /// @return DhcpConfigParser object.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new OptionDataListParser(param_name));
- }
-
- /// Pointer to options instances storage.
- OptionStorage* options_;
- /// Intermediate option storage. This storage is used by
- /// lower level parsers to add new options. Values held
- /// in this storage are assigned to main storage (options_)
- /// if overall parsing was successful.
- OptionStorage local_options_;
- /// Collection of parsers;
- ParserCollection parsers_;
-};
-
-/// @brief Parser for a single option definition.
-///
-/// This parser creates an instance of a single option definition.
-class OptionDefParser : public DhcpConfigParser {
-public:
-
- /// @brief Constructor.
- ///
- /// This constructor sets the pointer to the option definitions
- /// storage to NULL. It must be set to point to the actual storage
- /// before \ref build is called.
- OptionDefParser(const std::string&)
- : storage_(NULL) {
- }
-
- /// @brief Parses an entry that describes single option definition.
- ///
- /// @param option_def a configuration entry to be parsed.
- ///
- /// @throw DhcpConfigError if parsing was unsuccessful.
- void build(ConstElementPtr option_def) {
- if (storage_ == NULL) {
- isc_throw(DhcpConfigError, "parser logic error: storage must be set"
- " before parsing option definition data");
- }
- // Parse the elements that make up the option definition.
- BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
- std::string entry(param.first);
- ParserPtr parser;
- if (entry == "name" || entry == "type" ||
- entry == "record-types" || entry == "space" ||
- entry == "encapsulate") {
- StringParserPtr
- str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
- if (str_parser) {
- str_parser->setStorage(&string_values_);
- parser = str_parser;
- }
- } else if (entry == "code") {
- Uint32ParserPtr
- code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(entry)));
- if (code_parser) {
- code_parser->setStorage(&uint32_values_);
- parser = code_parser;
- }
- } else if (entry == "array") {
- BooleanParserPtr
- array_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(entry)));
- if (array_parser) {
- array_parser->setStorage(&boolean_values_);
- parser = array_parser;
- }
- } else {
- isc_throw(DhcpConfigError, "invalid parameter: " << entry);
- }
-
- parser->build(param.second);
- parser->commit();
- }
-
- // Create an instance of option definition.
- createOptionDef();
-
- // Get all items we collected so far for the particular option space.
- OptionDefContainerPtr defs = storage_->getItems(option_space_name_);
- // Check if there are any items with option code the same as the
- // one specified for the definition we are now creating.
- const OptionDefContainerTypeIndex& idx = defs->get<1>();
- const OptionDefContainerTypeRange& range =
- idx.equal_range(option_definition_->getCode());
- // If there are any items with this option code already we need
- // to issue an error because we don't allow duplicates for
- // option definitions within an option space.
- if (std::distance(range.first, range.second) > 0) {
- isc_throw(DhcpConfigError, "duplicated option definition for"
- << " code '" << option_definition_->getCode() << "'");
- }
- }
-
- /// @brief Stores the parsed option definition in a storage.
- void commit() {
- if (storage_ && option_definition_ &&
- OptionSpace::validateName(option_space_name_)) {
- storage_->addItem(option_definition_, option_space_name_);
- }
- }
-
- /// @brief Sets a pointer to the data store.
- ///
- /// The newly created instance of an option definition will be
- /// added to the data store given by the argument.
- ///
- /// @param storage pointer to the data store where the option definition
- /// will be added to.
- void setStorage(OptionDefStorage* storage) {
- storage_ = storage;
- }
-
-private:
-
- /// @brief Create option definition from the parsed parameters.
- void createOptionDef() {
-
- // Get the option space name and validate it.
- std::string space = string_values_.getParam("space");
- if (!OptionSpace::validateName(space)) {
- isc_throw(DhcpConfigError, "invalid option space name '"
- << space << "'");
- }
-
- // Get other parameters that are needed to create the
- // option definition.
- std::string name = string_values_.getParam("name");
- uint32_t code = uint32_values_.getParam("code");
- std::string type = string_values_.getParam("type");
- bool array_type = boolean_values_.getParam("array");
- std::string encapsulates = string_values_.getParam("encapsulate");
-
- // Create option definition.
- OptionDefinitionPtr def;
- // We need to check if user has set encapsulated option space
- // name. If so, different constructor will be used.
- if (!encapsulates.empty()) {
- // Arrays can't be used together with sub-options.
- if (array_type) {
- isc_throw(DhcpConfigError, "option '" << space << "."
- << "name" << "', comprising an array of data"
- << " fields may not encapsulate any option space");
-
- } else if (encapsulates == space) {
- isc_throw(DhcpConfigError, "option must not encapsulate"
- << " an option space it belongs to: '"
- << space << "." << name << "' is set to"
- << " encapsulate '" << space << "'");
-
- } else {
- def.reset(new OptionDefinition(name, code, type,
- encapsulates.c_str()));
- }
-
- } else {
- def.reset(new OptionDefinition(name, code, type, array_type));
-
- }
- // The record-types field may carry a list of comma separated names
- // of data types that form a record.
- std::string record_types = string_values_.getParam("record-types");
-
- // Split the list of record types into tokens.
- std::vector<std::string> record_tokens =
- isc::util::str::tokens(record_types, ",");
- // Iterate over each token and add a record type into
- // option definition.
- BOOST_FOREACH(std::string record_type, record_tokens) {
- try {
- boost::trim(record_type);
- if (!record_type.empty()) {
- def->addRecordField(record_type);
- }
- } catch (const Exception& ex) {
- isc_throw(DhcpConfigError, "invalid record type values"
- << " specified for the option definition: "
- << ex.what());
- }
- }
-
- // Check the option definition parameters are valid.
- try {
- def->validate();
- } catch (const isc::Exception& ex) {
- isc_throw(DhcpConfigError, "invalid option definition"
- << " parameters: " << ex.what());
- }
- // Option definition has been created successfully.
- option_space_name_ = space;
- option_definition_ = def;
- }
-
- /// Instance of option definition being created by this parser.
- OptionDefinitionPtr option_definition_;
- /// Name of the space the option definition belongs to.
- std::string option_space_name_;
-
- /// Pointer to a storage where the option definition will be
- /// added when \ref commit is called.
- OptionDefStorage* storage_;
-
- /// Storage for boolean values.
- BooleanStorage boolean_values_;
- /// Storage for string values.
- StringStorage string_values_;
- /// Storage for uint32 values.
- Uint32Storage uint32_values_;
-};
-
-/// @brief Parser for a list of option definitions.
-///
-/// This parser iterates over all configuration entries that define
-/// option definitions and creates instances of these definitions.
-/// If the parsing is successful, the collection of created definitions
-/// is put into the provided storage.
-class OptionDefListParser : DhcpConfigParser {
-public:
-
- /// @brief Constructor.
- ///
- /// This constructor initializes the pointer to option definitions
- /// storage to NULL value. This pointer has to be set to point to
- /// the actual storage before the \ref build function is called.
- OptionDefListParser(const std::string&) {
- }
-
- /// @brief Parse configuration entries.
- ///
- /// This function parses configuration entries and creates instances
- /// of option definitions.
- ///
- /// @param option_def_list pointer to an element that holds entries
- /// that define option definitions.
- /// @throw DhcpConfigError if configuration parsing fails.
- void build(ConstElementPtr option_def_list) {
- // Clear existing items in the global storage.
- // We are going to replace all of them.
- option_def_intermediate.clearItems();
-
- if (!option_def_list) {
- isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
- << " option definitions is NULL");
- }
-
- BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
- boost::shared_ptr<OptionDefParser>
- parser(new OptionDefParser("single-option-def"));
- parser->setStorage(&option_def_intermediate);
- parser->build(option_def);
- parser->commit();
- }
- }
-
- /// @brief Stores option definitions in the CfgMgr.
- void commit() {
-
- CfgMgr& cfg_mgr = CfgMgr::instance();
-
- cfg_mgr.deleteOptionDefs();
-
- // We need to move option definitions from the temporary
- // storage to the global storage.
- std::list<std::string> space_names =
- option_def_intermediate.getOptionSpaceNames();
- BOOST_FOREACH(std::string space_name, space_names) {
-
- BOOST_FOREACH(OptionDefinitionPtr def,
- *option_def_intermediate.getItems(space_name)) {
- // All option definitions should be initialized to non-NULL
- // values. The validation is expected to be made by the
- // OptionDefParser when creating an option definition.
- assert(def);
- cfg_mgr.addOptionDef(def, space_name);
- }
- }
- }
-
- /// @brief Create an OptionDefListParser object.
- ///
- /// @param param_name configuration entry holding option definitions.
- ///
- /// @return OptionDefListParser object.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new OptionDefListParser(param_name));
- }
-
-};
-
/// @brief this class parses a single subnet
///
/// This class parses the whole subnet definition. It creates parsers
BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
ParserPtr parser(createSubnet4ConfigParser(param.first));
- // The actual type of the parser is unknown here. We have to discover
- // the parser type here to invoke the corresponding setStorage function
- // on it. We discover parser type by trying to cast the parser to various
- // parser types and checking which one was successful. For this one
- // a setStorage and build methods are invoked.
-
- // Try uint32 type parser.
- if (!buildParser<Uint32Parser, Uint32Storage >(parser, uint32_values_,
- param.second) &&
- // Try string type parser.
- !buildParser<StringParser, StringStorage >(parser, string_values_,
- param.second) &&
- // Try pool parser.
- !buildParser<PoolParser, PoolStorage >(parser, pools_,
- param.second) &&
- // Try option data parser.
- !buildParser<OptionDataListParser, OptionStorage >(parser, options_,
- param.second)) {
- // Appropriate parsers are created in the createSubnet6ConfigParser
- // and they should be limited to those that we check here for. Thus,
- // if we fail to find a matching parser here it is a programming error.
- isc_throw(DhcpConfigError, "failed to find suitable parser");
- }
+ parser->build(param.second);
+ parsers_.push_back(parser);
}
+
// In order to create new subnet we need to get the data out
// of the child parsers first. The only way to do it is to
// invoke commit on them because it will make them write
private:
- /// @brief Set storage for a parser and invoke build.
- ///
- /// This helper method casts the provided parser pointer to the specified
- /// type. If the cast is successful it sets the corresponding storage for
- /// this parser, invokes build on it and saves the parser.
- ///
- /// @tparam T parser type to which parser argument should be cast.
- /// @tparam Y storage type for the specified parser type.
- /// @param parser parser on which build must be invoked.
- /// @param storage reference to a storage that will be set for a parser.
- /// @param subnet subnet element read from the configuration and being parsed.
- /// @return true if parser pointer was successfully cast to specialized
- /// parser type provided as Y.
- template<typename T, typename Y>
- bool buildParser(const ParserPtr& parser, Y& storage, const ConstElementPtr& subnet) {
- // We need to cast to T in order to set storage for the parser.
- boost::shared_ptr<T> cast_parser = boost::dynamic_pointer_cast<T>(parser);
- // It is common that this cast is not successful because we try to cast to all
- // supported parser types as we don't know the type of a parser in advance.
- if (cast_parser) {
- // Cast, successful so we go ahead with setting storage and actual parse.
- cast_parser->setStorage(&storage);
- parser->build(subnet);
- parsers_.push_back(parser);
- // We indicate that cast was successful so as the calling function
- // may skip attempts to cast to other parser types and proceed to
- // next element.
- return (true);
- }
- // It was not successful. Indicate that another parser type
- // should be tried.
- return (false);
- }
-
/// @brief Append sub-options to an option.
///
/// @param option_space a name of the encapsulated option space.
try {
subnet_txt = string_values_.getParam("subnet");
} catch (DhcpConfigError) {
- // Rethrow with precise error.
+ // rethrow with precise error
isc_throw(DhcpConfigError,
"Mandatory subnet definition in subnet missing");
}
/// @return parser object for specified entry name
/// @throw NotImplemented if trying to create a parser for unknown config element
DhcpConfigParser* createSubnet4ConfigParser(const std::string& config_id) {
- FactoryMap factories;
- factories["valid-lifetime"] = Uint32Parser::factory;
- factories["renew-timer"] = Uint32Parser::factory;
- factories["rebind-timer"] = Uint32Parser::factory;
- factories["subnet"] = StringParser::factory;
- factories["pool"] = PoolParser::factory;
- factories["option-data"] = OptionDataListParser::factory;
-
- FactoryMap::iterator f = factories.find(config_id);
- if (f == factories.end()) {
- // Used for debugging only.
- // return new DebugParser(config_id);
-
+ DhcpConfigParser *parser = NULL;
+ if ((config_id.compare("valid-lifetime") == 0) ||
+ (config_id.compare("renew-timer") == 0) ||
+ (config_id.compare("rebind-timer") == 0)) {
+ parser = new Uint32Parser(config_id, &uint32_values_);
+ }
+ else if (config_id.compare("subnet") == 0) {
+ parser = new StringParser(config_id, &string_values_);
+ }
+ else if (config_id.compare("pool") == 0) {
+ parser = new PoolParser(config_id, &pools_);
+ }
+ else if (config_id.compare("option-data") == 0) {
+ parser = new OptionDataListParser(config_id, &options_, &option_def_intermediate,
+ Dhcp4OptionDataParser::factory);
+ } else {
isc_throw(NotImplemented,
- "parser error: Subnet4 parameter not supported: "
- << config_id);
+ "parser error: Subnet4 parameter not supported: " << config_id);
}
- return (f->second(config_id));
+
+ return (parser);
}
/// @brief Returns value for a given parameter (after using inheritance)
///
/// @param subnets_list pointer to a list of IPv4 subnets
void build(ConstElementPtr subnets_list) {
-
- // No need to define FactoryMap here. There's only one type
- // used: Subnet4ConfigParser
-
BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
ParserPtr parser(new Subnet4ConfigParser("subnet"));
parser->build(subnet);
subnets_.push_back(parser);
}
-
}
/// @brief commits subnets definitions.
namespace isc {
namespace dhcp {
+//************** Dhcp4OptionDataParser methods *******************************
+
+Dhcp4OptionDataParser::Dhcp4OptionDataParser(const std::string& param_name,
+ OptionStorage *options, OptionDefStorage *option_defs)
+ :OptionDataParser(param_name, options, option_defs, Option::V4) {
+}
+
+OptionDataParser* Dhcp4OptionDataParser::factory(const std::string& param_name,
+ OptionStorage *options, OptionDefStorage *option_defs) {
+ return new Dhcp4OptionDataParser(param_name, options, option_defs);
+}
+
+OptionDefinitionPtr Dhcp4OptionDataParser::findServerSpaceOptionDefinition (
+ std::string& option_space, uint32_t option_code) {
+ OptionDefinitionPtr def;
+
+ if (option_space == "dhcp4" &&
+ LibDHCP::isStandardOption(Option::V4, option_code)) {
+ def = LibDHCP::getOptionDef(Option::V4, option_code);
+
+ } else if (option_space == "dhcp6") {
+ isc_throw(DhcpConfigError, "'dhcp6' option space name is reserved"
+ << " for DHCPv6 server");
+ }
+
+ return def;
+}
+
/// @brief creates global parsers
///
/// This method creates global parsers that parse global parameters, i.e.
/// @return parser for specified global DHCPv4 parameter
/// @throw NotImplemented if trying to create a parser for unknown config element
DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
- FactoryMap factories;
-
- factories["valid-lifetime"] = Uint32Parser::factory;
- factories["renew-timer"] = Uint32Parser::factory;
- factories["rebind-timer"] = Uint32Parser::factory;
- factories["interface"] = InterfaceListConfigParser::factory;
- factories["subnet4"] = Subnets4ListConfigParser::factory;
- factories["option-data"] = OptionDataListParser::factory;
- factories["option-def"] = OptionDefListParser::factory;
- factories["version"] = StringParser::factory;
- factories["lease-database"] = DbAccessParser::factory;
-
- FactoryMap::iterator f = factories.find(config_id);
- if (f == factories.end()) {
- // Used for debugging only.
- // return new DebugParser(config_id);
-
+ DhcpConfigParser *parser = NULL;
+ if ((config_id.compare("valid-lifetime") == 0) ||
+ (config_id.compare("renew-timer") == 0) ||
+ (config_id.compare("rebind-timer") == 0)) {
+ parser = new Uint32Parser(config_id, &uint32_defaults);
+ }
+ else if (config_id.compare("interface") == 0) {
+ parser = new InterfaceListConfigParser(config_id);
+ }
+ else if (config_id.compare("subnet4") == 0) {
+ parser = new Subnets4ListConfigParser(config_id);
+ }
+ else if (config_id.compare("option-data") == 0) {
+ parser = new OptionDataListParser(config_id, &option_defaults,
+ &option_def_intermediate,
+ Dhcp4OptionDataParser::factory);
+ }
+ else if (config_id.compare("option-def") == 0) {
+ parser = new OptionDefListParser(config_id, &option_def_intermediate);
+ }
+ else if (config_id.compare("version") == 0) {
+ parser = new StringParser(config_id, &string_defaults);
+ }
+ else if (config_id.compare("lease-database") == 0) {
+ parser = new DbAccessParser(config_id);
+ }
+ else {
isc_throw(NotImplemented,
- "Parser error: Global configuration parameter not supported: "
- << config_id);
+ "Parser error: Global configuration parameter not supported: "
+ << config_id);
}
- return (f->second(config_id));
+
+ return (parser);
}
isc::data::ConstElementPtr
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
.arg(config_pair.first).arg(ex.what());
+ std::cout << "parse failed on:" << config_pair.first << std::endl;
answer = isc::config::createAnswer(1,
string("Configuration parsing failed: ") + ex.what());
// Rollback changes as the configuration parsing failed.
if (rollback) {
+
+ // TKM - take this out, its just here for diagnostics
+ std::cout << "***************" << std::endl;
+ std::cout << "answer is:" << std::endl;
+ answer->toJSON(std::cout);
+ std::cout << std::endl << "***************" << std::endl;
+
std::swap(uint32_defaults, uint32_local);
std::swap(string_defaults, string_local);
std::swap(option_defaults, option_local);
}; // end of isc::dhcp namespace
}; // end of isc namespace
+
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <exceptions/exceptions.h>
-#include <dhcpsrv/dhcp_config_parser.h>
#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <dhcpsrv/dhcp_parsers.h>
+
#include <stdint.h>
#include <string>
/// @return a reference to a global uint32 values storage.
const Uint32Storage& getUint32Defaults();
+
+/// @brief Parser for DHCP4 option data value.
+///
+/// This parser parses configuration entries that specify value of
+/// a single option specific to DHCP4. It provides the DHCP4-specific
+/// implementation of the abstract class OptionDataParser.
+class Dhcp4OptionDataParser : public OptionDataParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// Class constructor.
+ Dhcp4OptionDataParser(const std::string&, OptionStorage *options,
+ OptionDefStorage *option_defs);
+
+ /// @brief static factory method for instantiating Dhcp4OptionDataParsers
+ ///
+ /// @param param_name name fo the parameter to be parsed.
+ /// @param options storage where the parameter value is to be stored.
+ /// @param global_option_defs global option definitions storage
+ static OptionDataParser* factory(const std::string& param_name, OptionStorage *options,
+ OptionDefStorage *global_option_defs);
+
+protected:
+ /// @brief Finds an option definition within the server's option space
+ ///
+ /// Given an option space and an option code, find the correpsonding
+ /// option defintion within the server's option defintion storage.
+ ///
+ /// @param option_space name of the parameter option space
+ /// @param option_code numeric value of the parameter to find
+ /// @return OptionDefintionPtr of the option defintion or an
+ /// empty OptionDefinitionPtr if not found.
+ /// @throw DhcpConfigError if the option space requested is not valid
+ /// for this server.
+ virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
+ std::string& option_space, uint32_t option_code);
+
+private:
+ // Private default Constructor declared for safety.
+ Dhcp4OptionDataParser() :OptionDataParser("",NULL,NULL,Option::V4) {}
+};
+
+
}; // end of isc::dhcp namespace
}; // end of isc namespace
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dbaccess_parser.h>
#include <dhcpsrv/dhcp_config_parser.h>
+#include <dhcpsrv/dhcp_parsers.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/triplet.h>
namespace {
-// Forward declarations of some of the parser classes.
-// They are used to define pointer types for these classes.
-class BooleanParser;
-class StringParser;
-class Uint32Parser;
-
// Pointers to various parser objects.
typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
typedef boost::shared_ptr<StringParser> StringParserPtr;
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
-/// @brief Factory method that will create a parser for a given element name
-typedef isc::dhcp::DhcpConfigParser* ParserFactory(const std::string& config_id);
-
-/// @brief Collection of factories that create parsers for specified element names
-typedef std::map<std::string, ParserFactory*> FactoryMap;
-
-/// @brief Storage for option definitions.
-typedef OptionSpaceContainer<OptionDefContainer,
- OptionDefinitionPtr> OptionDefStorage;
-
/// @brief Collection of address pools.
///
/// This type is used as intermediate storage, when pools are parsed, but there is
/// no subnet object created yet to store them.
typedef std::vector<isc::dhcp::Pool6Ptr> PoolStorage;
-/// Collection of containers holding option spaces. Each container within
-/// a particular option space holds so-called option descriptors.
-typedef OptionSpaceContainer<Subnet::OptionContainer,
- Subnet::OptionDescriptor> OptionStorage;
-
/// @brief Global uint32 parameters that will be used as defaults.
Uint32Storage uint32_defaults;
/// @brief Global storage for option definitions.
OptionDefStorage option_def_intermediate;
-/// @brief a dummy configuration parser
-///
-/// This is a debugging parser. It does not configure anything,
-/// will accept any configuration and will just print it out
-/// on commit. Useful for debugging existing configurations and
-/// adding new ones.
-class DebugParser : public DhcpConfigParser {
-public:
-
- /// @brief Constructor
- ///
- /// See @ref DhcpConfigParser class for details.
- ///
- /// @param param_name name of the parsed parameter
- DebugParser(const std::string& param_name)
- :param_name_(param_name) {
- }
-
- /// @brief builds parameter value
- ///
- /// See @ref DhcpConfigParser class for details.
- ///
- /// @param new_config pointer to the new configuration
- virtual void build(ConstElementPtr new_config) {
- std::cout << "Build for token: [" << param_name_ << "] = ["
- << value_->str() << "]" << std::endl;
- value_ = new_config;
- }
-
- /// @brief Pretends to apply the configuration.
- ///
- /// This is a method required by the base class. It pretends to apply the
- /// configuration, but in fact it only prints the parameter out.
- ///
- /// See @ref DhcpConfigParser class for details.
- virtual void commit() {
- // Debug message. The whole DebugParser class is used only for parser
- // debugging, and is not used in production code. It is very convenient
- // to keep it around. Please do not turn this cout into logger calls.
- std::cout << "Commit for token: [" << param_name_ << "] = ["
- << value_->str() << "]" << std::endl;
- }
-
- /// @brief factory that constructs DebugParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new DebugParser(param_name));
- }
-
-private:
- /// name of the parsed parameter
- std::string param_name_;
-
- /// pointer to the actual value of the parameter
- ConstElementPtr value_;
-};
-
-
-/// @brief A boolean value parser.
-///
-/// This parser handles configuration values of the boolean type.
-/// Parsed values are stored in a provided storage. If no storage
-/// is provided then the build function throws an exception.
-class BooleanParser : public DhcpConfigParser {
-public:
- /// @brief Constructor.
- ///
- /// @param param_name name of the parameter.
- BooleanParser(const std::string& param_name)
- : storage_(NULL),
- param_name_(param_name),
- value_(false) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief Parse a boolean value.
- ///
- /// @param value a value to be parsed.
- ///
- /// @throw isc::InvalidOperation if a storage has not been set
- /// prior to calling this function
- /// @throw isc::dhcp::DhcpConfigError if a provided parameter's
- /// name is empty.
- virtual void build(ConstElementPtr value) {
- if (storage_ == NULL) {
- isc_throw(isc::InvalidOperation, "parser logic error:"
- << " storage for the " << param_name_
- << " value has not been set");
- } else if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- // The Config Manager checks if user specified a
- // valid value for a boolean parameter: True or False.
- // It is then ok to assume that if str() does not return
- // 'true' the value is 'false'.
- value_ = (value->str() == "true") ? true : false;
- }
-
- /// @brief Put a parsed value to the storage.
- virtual void commit() {
- if (storage_ != NULL && !param_name_.empty()) {
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief Create an instance of the boolean parser.
- ///
- /// @param param_name name of the parameter for which the
- /// parser is created.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new BooleanParser(param_name));
- }
-
- /// @brief Set the storage for parsed value.
- ///
- /// This function must be called prior to calling build.
- ///
- /// @param storage a pointer to the storage where parsed data
- /// is to be stored.
- void setStorage(BooleanStorage* storage) {
- storage_ = storage;
- }
-
-private:
- /// Pointer to the storage where parsed value is stored.
- BooleanStorage* storage_;
- /// Name of the parameter which value is parsed with this parser.
- std::string param_name_;
- /// Parsed value.
- bool value_;
-};
-
-/// @brief Configuration parser for uint32 parameters
-///
-/// This class is a generic parser that is able to handle any uint32 integer
-/// type. By default it stores the value in external global container
-/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
-/// in subnet config), it can be pointed to a different storage, using
-/// setStorage() method. This class follows the parser interface, laid out
-/// in its base class, @ref DhcpConfigParser.
-///
-/// For overview of usability of this generic purpose parser, see
-/// @ref dhcpv6ConfigInherit page.
-///
-/// @todo this class should be turned into the template class which
-/// will handle all uintX_types of data (see ticket #2415).
-class Uint32Parser : public DhcpConfigParser {
-public:
-
- /// @brief constructor for Uint32Parser
- ///
- /// @param param_name name of the configuration parameter being parsed
- Uint32Parser(const std::string& param_name)
- : storage_(&uint32_defaults),
- param_name_(param_name) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief Parses configuration configuration parameter as uint32_t.
- ///
- /// @param value pointer to the content of parsed values
- /// @throw isc::dhcp::DhcpConfigError if failed to parse
- /// the configuration parameter as uint32_t value.
- virtual void build(ConstElementPtr value) {
- if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
-
- bool parse_error = false;
- // Cast the provided value to int64 value to check.
- int64_t int64value = 0;
- try {
- // Parsing the value as a int64 value allows to
- // check if the provided value is within the range
- // of uint32_t (is not negative or greater than
- // maximal uint32_t value).
- int64value = boost::lexical_cast<int64_t>(value->str());
- } catch (const boost::bad_lexical_cast&) {
- parse_error = true;
- }
- if (!parse_error) {
- // Check that the value is not out of bounds.
- if ((int64value < 0) ||
- (int64value > std::numeric_limits<uint32_t>::max())) {
- parse_error = true;
- } else {
- // A value is not out of bounds so let's cast it to
- // the uint32_t type.
- value_ = static_cast<uint32_t>(int64value);
- }
-
- }
- // Invalid value provided.
- if (parse_error) {
- isc_throw(isc::dhcp::DhcpConfigError, "Failed to parse value " << value->str()
- << " as unsigned 32-bit integer.");
- }
- }
-
- /// @brief Stores the parsed uint32_t value in a storage.
- virtual void commit() {
- if (storage_ != NULL) {
- // If a given parameter already exists in the storage we override
- // its value. If it doesn't we insert a new element.
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief Factory that constructs Uint32Parser objects.
- ///
- /// @param param_name name of the parameter to be parsed.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new Uint32Parser(param_name));
- }
-
- /// @brief Sets storage for value of this parameter.
- ///
- /// See @ref dhcpv6ConfigInherit for details.
- ///
- /// @param storage pointer to the storage container.
- void setStorage(Uint32Storage* storage) {
- storage_ = storage;
- }
-
-private:
- /// pointer to the storage, where parsed value will be stored
- Uint32Storage* storage_;
- /// name of the parameter to be parsed
- std::string param_name_;
- /// the actual parsed value
- uint32_t value_;
-};
-
-/// @brief Configuration parser for string parameters
-///
-/// This class is a generic parser that is able to handle any string
-/// parameter. By default it stores the value in an external global container
-/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
-/// in subnet config), it can be pointed to a different storage, using the
-/// setStorage() method. This class follows the parser interface, laid out
-/// in its base class, @ref DhcpConfigParser.
-///
-/// For overview of usability of this generic purpose parser, see
-/// @ref dhcpv6ConfigInherit page.
-class StringParser : public DhcpConfigParser {
-public:
-
- /// @brief constructor for StringParser
- ///
- /// @param param_name name of the configuration parameter being parsed
- StringParser(const std::string& param_name)
- : storage_(&string_defaults),
- param_name_(param_name) {
- // Empty parameter name is invalid.
- if (param_name_.empty()) {
- isc_throw(DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- }
-
- /// @brief parses parameter value
- ///
- /// Parses configuration parameter's value as string.
- ///
- /// @param value pointer to the content of parsed values
- /// @throws DhcpConfigError if the parsed parameter's name is empty.
- virtual void build(ConstElementPtr value) {
- if (param_name_.empty()) {
- isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
- << "empty parameter name provided");
- }
- value_ = value->str();
- boost::erase_all(value_, "\"");
- }
-
- /// @brief Stores the parsed value in a storage.
- virtual void commit() {
- if (storage_ != NULL && !param_name_.empty()) {
- // If a given parameter already exists in the storage we override
- // its value. If it doesn't we insert a new element.
- storage_->setParam(param_name_, value_);
- }
- }
-
- /// @brief Factory that constructs StringParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new StringParser(param_name));
- }
-
- /// @brief Sets storage for value of this parameter.
- ///
- /// See @ref dhcpv6ConfigInherit for details.
- ///
- /// @param storage pointer to the storage container
- void setStorage(StringStorage* storage) {
- storage_ = storage;
- }
-
-private:
- /// Pointer to the storage, where parsed value will be stored
- StringStorage* storage_;
- /// Name of the parameter to be parsed
- std::string param_name_;
- /// The actual parsed value
- std::string value_;
-};
-
-/// @brief parser for interface list definition
-///
-/// This parser handles Dhcp6/interface entry.
-/// It contains a list of network interfaces that the server listens on.
-/// In particular, it can contain an entry called "all" or "any" that
-/// designates all interfaces.
-///
-/// It is useful for parsing Dhcp6/interface parameter.
-class InterfaceListConfigParser : public DhcpConfigParser {
-public:
-
- /// @brief constructor
- ///
- /// As this is a dedicated parser, it must be used to parse
- /// "interface" parameter only. All other types will throw exception.
- ///
- /// @param param_name name of the configuration parameter being parsed
- /// @throw BadValue if supplied parameter name is not "interface"
- InterfaceListConfigParser(const std::string& param_name) {
- if (param_name != "interface") {
- isc_throw(isc::BadValue, "Internal error. Interface configuration "
- "parser called for the wrong parameter: " << param_name);
- }
- }
-
- /// @brief parses parameters value
- ///
- /// Parses configuration entry (list of parameters) and stores it in
- /// storage.
- ///
- /// @param value pointer to the content of parsed values
- virtual void build(ConstElementPtr value) {
- BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
- interfaces_.push_back(iface->str());
- }
- }
-
- /// @brief commits interfaces list configuration
- virtual void commit() {
- /// @todo: Implement per interface listening. Currently always listening
- /// on all interfaces.
- }
-
- /// @brief factory that constructs InterfaceListConfigParser objects
- ///
- /// @param param_name name of the parameter to be parsed
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new InterfaceListConfigParser(param_name));
- }
-
-private:
- /// contains list of network interfaces
- vector<string> interfaces_;
-};
-
/// @brief parser for pool definition
///
/// This parser handles pool definitions, i.e. a list of entries of one
// ignore parameter name, it is always Dhcp6/subnet6[X]/pool
}
+ /// @brief constructor.
+ PoolParser(const std::string& /*param_name*/, PoolStorage* pools)
+ :pools_(pools) {
+ // ignore parameter name, it is always Dhcp6/subnet6[X]/pool
+ }
+
/// @brief parses the actual list
///
/// This method parses the actual list of interfaces.
PoolStorage local_pools_;
};
-
-/// @brief Parser for option data value.
-///
-/// This parser parses configuration entries that specify value of
-/// a single option. These entries include option name, option code
-/// and data carried by the option. The option data can be specified
-/// in one of the two available formats: binary value represented as
-/// a string of hexadecimal digits or a list of comma separated values.
-/// The format being used is controlled by csv-format configuration
-/// parameter. When setting this value to True, the latter format is
-/// used. The subsequent values in the CSV format apply to relevant
-/// option data fields in the configured option. For example the
-/// configuration: "data" : "192.168.2.0, 56, hello world" can be
-/// used to set values for the option comprising IPv4 address,
-/// integer and string data field. Note that order matters. If the
-/// order of values does not match the order of data fields within
-/// an option the configuration will not be accepted. If parsing
-/// is successful then an instance of an option is created and
-/// added to the storage provided by the calling class.
-class OptionDataParser : public DhcpConfigParser {
-public:
-
- /// @brief Constructor.
- ///
- /// Class constructor.
- OptionDataParser(const std::string&)
- : options_(NULL),
- // initialize option to NULL ptr
- option_descriptor_(false) { }
-
- /// @brief Parses the single option data.
- ///
- /// This method parses the data of a single option from the configuration.
- /// The option data includes option name, option code and data being
- /// carried by this option. Eventually it creates the instance of the
- /// option.
- ///
- /// @warning setStorage must be called with valid storage pointer prior
- /// to calling this method.
- ///
- /// @param option_data_entries collection of entries that define value
- /// for a particular option.
- /// @throw DhcpConfigError if invalid parameter specified in
- /// the configuration.
- /// @throw isc::InvalidOperation if failed to set storage prior to
- /// calling build.
- virtual void build(ConstElementPtr option_data_entries) {
-
- if (options_ == NULL) {
- isc_throw(isc::InvalidOperation, "Parser logic error: storage must be set before "
- "parsing option data.");
- }
- BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
- ParserPtr parser;
- if (param.first == "name" || param.first == "data" ||
- param.first == "space") {
- boost::shared_ptr<StringParser>
- name_parser(dynamic_cast<StringParser*>(StringParser::factory(param.first)));
- if (name_parser) {
- name_parser->setStorage(&string_values_);
- parser = name_parser;
- }
- } else if (param.first == "code") {
- boost::shared_ptr<Uint32Parser>
- code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(param.first)));
- if (code_parser) {
- code_parser->setStorage(&uint32_values_);
- parser = code_parser;
- }
- } else if (param.first == "csv-format") {
- boost::shared_ptr<BooleanParser>
- value_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(param.first)));
- if (value_parser) {
- value_parser->setStorage(&boolean_values_);
- parser = value_parser;
- }
- } else {
- isc_throw(DhcpConfigError,
- "parser error: option-data parameter not supported: "
- << param.first);
- }
- parser->build(param.second);
- // Before we can create an option we need to get the data from
- // the child parsers. The only way to do it is to invoke commit
- // on them so as they store the values in appropriate storages
- // that this class provided to them. Note that this will not
- // modify values stored in the global storages so the configuration
- // will remain consistent even parsing fails somewhere further on.
- parser->commit();
- }
- // Try to create the option instance.
- createOption();
- }
-
- /// @brief Commits option value.
- ///
- /// This function adds a new option to the storage or replaces an existing option
- /// with the same code.
- ///
- /// @throw isc::InvalidOperation if failed to set pointer to storage or failed
- /// to call build() prior to commit. If that happens data in the storage
- /// remain un-modified.
- virtual void commit() {
- if (options_ == NULL) {
- isc_throw(isc::InvalidOperation, "parser logic error: storage must be set before "
- "committing option data.");
- } else if (!option_descriptor_.option) {
- // Before we can commit the new option should be configured. If it is not
- // than somebody must have called commit() before build().
- isc_throw(isc::InvalidOperation, "parser logic error: no option has been configured and"
- " thus there is nothing to commit. Has build() been called?");
- }
- uint16_t opt_type = option_descriptor_.option->getType();
- Subnet::OptionContainerPtr options = options_->getItems(option_space_);
- // The getItems() should never return NULL pointer. If there are no
- // options configured for the particular option space a pointer
- // to an empty container should be returned.
- assert(options);
- Subnet::OptionContainerTypeIndex& idx = options->get<1>();
- // Try to find options with the particular option code in the main
- // storage. If found, remove these options because they will be
- // replaced with new one.
- Subnet::OptionContainerTypeRange range =
- idx.equal_range(opt_type);
- if (std::distance(range.first, range.second) > 0) {
- idx.erase(range.first, range.second);
- }
- // Append new option to the main storage.
- options_->addItem(option_descriptor_, option_space_);
- }
-
- /// @brief Set storage for the parser.
- ///
- /// Sets storage for the parser. This storage points to the
- /// vector of options and is used by multiple instances of
- /// OptionDataParser. Each instance creates exactly one object
- /// of dhcp::Option or derived type and appends it to this
- /// storage.
- ///
- /// @param storage pointer to the options storage
- void setStorage(OptionStorage* storage) {
- options_ = storage;
- }
-
-private:
-
- /// @brief Create option instance.
- ///
- /// Creates an instance of an option and adds it to the provided
- /// options storage. If the option data parsed by \ref build function
- /// are invalid or insufficient this function emits an exception.
- ///
- /// @warning this function does not check if options_ storage pointer
- /// is intitialized but this check is not needed here because it is done
- /// in the \ref build function.
- ///
- /// @throw DhcpConfigError if parameters provided in the configuration
- /// are invalid.
- void createOption() {
-
- // 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 uint16_t and is not zero.
- uint32_t option_code = uint32_values_.getParam("code");
- if (option_code == 0) {
- isc_throw(DhcpConfigError, "option code must not be zero."
- << " Option code '0' is reserved in DHCPv6.");
- } else if (option_code > std::numeric_limits<uint16_t>::max()) {
- isc_throw(DhcpConfigError, "invalid option code '" << option_code
- << "', it must not exceed '"
- << std::numeric_limits<uint16_t>::max() << "'");
- }
- // Check that the option name has been specified, is non-empty and does not
- // contain spaces.
- std::string option_name = string_values_.getParam("name");
- if (option_name.empty()) {
- isc_throw(DhcpConfigError, "name of the option with code '"
- << option_code << "' is empty");
- } else if (option_name.find(" ") != std::string::npos) {
- isc_throw(DhcpConfigError, "invalid option name '" << option_name
- << "', space character is not allowed");
- }
-
- std::string option_space = string_values_.getParam("space");
- if (!OptionSpace::validateName(option_space)) {
- isc_throw(DhcpConfigError, "invalid option space name '"
- << option_space << "' specified for option '"
- << option_name << "' (code '" << option_code
- << "')");
- }
-
- OptionDefinitionPtr def;
- if (option_space == "dhcp6" &&
- LibDHCP::isStandardOption(Option::V6, option_code)) {
- def = LibDHCP::getOptionDef(Option::V6, option_code);
-
- } else if (option_space == "dhcp4") {
- isc_throw(DhcpConfigError, "'dhcp4' option space name is reserved"
- << " for DHCPv4 server");
- } else {
- // If we are not dealing with a standard option then we
- // need to search for its definition among user-configured
- // options. They are expected to be in the global storage
- // already.
- OptionDefContainerPtr defs = option_def_intermediate.getItems(option_space);
- // The getItems() should never return the NULL pointer. If there are
- // no option definitions for the particular option space a pointer
- // to an empty container should be returned.
- assert(defs);
- const OptionDefContainerTypeIndex& idx = defs->get<1>();
- OptionDefContainerTypeRange range = idx.equal_range(option_code);
- if (std::distance(range.first, range.second) > 0) {
- def = *range.first;
- }
- if (!def) {
- isc_throw(DhcpConfigError, "definition for the option '"
- << option_space << "." << option_name
- << "' having code '" << option_code
- << "' does not exist");
- }
-
- }
-
- // Get option data from the configuration database ('data' field).
- const std::string option_data = string_values_.getParam("data");
- const bool csv_format = boolean_values_.getParam("csv-format");
-
- // Transform string of hexadecimal digits into binary format.
- std::vector<uint8_t> binary;
- std::vector<std::string> data_tokens;
-
- if (csv_format) {
- // 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(option_data, ",");
- } else {
- // Otherwise, the option data is specified as a string of
- // hexadecimal digits that we have to turn into binary format.
- try {
- util::encode::decodeHex(option_data, binary);
- } catch (...) {
- isc_throw(DhcpConfigError, "Parser error: option data is not a valid"
- << " string of hexadecimal digits: " << option_data);
- }
- }
-
- OptionPtr option;
- if (!def) {
- if (csv_format) {
- 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 " << option_code
- << " does not have a definition.");
- }
-
- // @todo We have a limited set of option definitions intiialized at the moment.
- // In the future we want to initialize option definitions for all options.
- // Consequently an error will be issued if an option 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(Option::V6, static_cast<uint16_t>(option_code),
- 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
- // may seem to be redundant but in the future we may want
- // 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() != option_name) {
- isc_throw(DhcpConfigError, "specified option name '"
- << option_name << "' does not match the "
- << "option definition: '" << option_space
- << "." << def->getName() << "'");
- }
-
- // Option definition has been found so let's use it to create
- // an instance of our option.
- try {
- OptionPtr option = csv_format ?
- def->optionFactory(Option::V6, option_code, data_tokens) :
- def->optionFactory(Option::V6, option_code, binary);
- Subnet::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: " << option_space
- << ", code: " << option_code << "): "
- << ex.what());
- }
- }
- // All went good, so we can set the option space name.
- option_space_ = option_space;
- }
-
- /// Storage for uint32 values (e.g. option code).
- Uint32Storage uint32_values_;
- /// Storage for string values (e.g. option name or data).
- StringStorage string_values_;
- /// Storage for boolean values.
- BooleanStorage boolean_values_;
- /// Pointer to options storage. This storage is provided by
- /// the calling class and is shared by all OptionDataParser objects.
- OptionStorage* options_;
- /// Option descriptor holds newly configured option.
- isc::dhcp::Subnet::OptionDescriptor option_descriptor_;
- /// Option space name where the option belongs to.
- std::string option_space_;
-};
-
-/// @brief Parser for option data values within a subnet.
-///
-/// This parser iterates over all entries that define options
-/// data for a particular subnet and creates a collection of options.
-/// If parsing is successful, all these options are added to the Subnet
-/// object.
-class OptionDataListParser : public DhcpConfigParser {
-public:
-
- /// @brief Constructor.
- ///
- /// Unless otherwise specified, parsed options will be stored in
- /// a global option container (option_default). That storage location
- /// is overriden on a subnet basis.
- OptionDataListParser(const std::string&)
- : options_(&option_defaults), local_options_() { }
-
- /// @brief Parses entries that define options' data for a subnet.
- ///
- /// This method iterates over all entries that define option data
- /// for options within a single subnet and creates options' instances.
- ///
- /// @param option_data_list pointer to a list of options' data sets.
- /// @throw DhcpConfigError if option parsing failed.
- void build(ConstElementPtr option_data_list) {
- BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
- boost::shared_ptr<OptionDataParser> parser(new OptionDataParser("option-data"));
- // options_ member will hold instances of all options thus
- // each OptionDataParser takes it as a storage.
- parser->setStorage(&local_options_);
- // Build the instance of a single option.
- parser->build(option_value);
- // Store a parser as it will be used to commit.
- parsers_.push_back(parser);
- }
- }
-
- /// @brief Set storage for option instances.
- ///
- /// @param storage pointer to options storage.
- void setStorage(OptionStorage* storage) {
- options_ = storage;
- }
-
-
- /// @brief Commit all option values.
- ///
- /// This function invokes commit for all option values.
- void commit() {
- BOOST_FOREACH(ParserPtr parser, parsers_) {
- parser->commit();
- }
- // Parsing was successful and we have all configured
- // options in local storage. We can now replace old values
- // with new values.
- std::swap(local_options_, *options_);
- }
-
- /// @brief Create DhcpDataListParser object
- ///
- /// @param param_name param name.
- ///
- /// @return DhcpConfigParser object.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new OptionDataListParser(param_name));
- }
-
- /// Pointer to options instances storage.
- OptionStorage* options_;
- /// Intermediate option storage. This storage is used by
- /// lower level parsers to add new options. Values held
- /// in this storage are assigned to main storage (options_)
- /// if overall parsing was successful.
- OptionStorage local_options_;
- /// Collection of parsers;
- ParserCollection parsers_;
-};
-
-/// @brief Parser for a single option definition.
-///
-/// This parser creates an instance of a single option definition.
-class OptionDefParser: DhcpConfigParser {
-public:
-
- /// @brief Constructor.
- ///
- /// This constructor sets the pointer to the option definitions
- /// storage to NULL. It must be set to point to the actual storage
- /// before \ref build is called.
- OptionDefParser(const std::string&)
- : storage_(NULL) {
- }
-
- /// @brief Parses an entry that describes single option definition.
- ///
- /// @param option_def a configuration entry to be parsed.
- ///
- /// @throw DhcpConfigError if parsing was unsuccessful.
- void build(ConstElementPtr option_def) {
- if (storage_ == NULL) {
- isc_throw(DhcpConfigError, "parser logic error: storage must be set"
- " before parsing option definition data");
- }
- // Parse the elements that make up the option definition.
- BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
- std::string entry(param.first);
- ParserPtr parser;
- if (entry == "name" || entry == "type" || entry == "record-types" ||
- entry == "space" || entry == "encapsulate") {
- StringParserPtr
- str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
- if (str_parser) {
- str_parser->setStorage(&string_values_);
- parser = str_parser;
- }
- } else if (entry == "code") {
- Uint32ParserPtr
- code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(entry)));
- if (code_parser) {
- code_parser->setStorage(&uint32_values_);
- parser = code_parser;
- }
- } else if (entry == "array") {
- BooleanParserPtr
- array_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(entry)));
- if (array_parser) {
- array_parser->setStorage(&boolean_values_);
- parser = array_parser;
- }
- } else {
- isc_throw(DhcpConfigError, "invalid parameter: " << entry);
- }
-
- parser->build(param.second);
- parser->commit();
- }
-
- // Create an instance of option definition.
- createOptionDef();
-
- // Get all items we collected so far for the particular option space.
- OptionDefContainerPtr defs = storage_->getItems(option_space_name_);
- // Check if there are any items with option code the same as the
- // one specified for the definition we are now creating.
- const OptionDefContainerTypeIndex& idx = defs->get<1>();
- const OptionDefContainerTypeRange& range =
- idx.equal_range(option_definition_->getCode());
- // If there are any items with this option code already we need
- // to issue an error because we don't allow duplicates for
- // option definitions within an option space.
- if (std::distance(range.first, range.second) > 0) {
- isc_throw(DhcpConfigError, "duplicated option definition for"
- << " code '" << option_definition_->getCode() << "'");
- }
- }
-
- /// @brief Stores the parsed option definition in the data store.
- void commit() {
- if (storage_ && option_definition_ &&
- OptionSpace::validateName(option_space_name_)) {
- storage_->addItem(option_definition_, option_space_name_);
- }
- }
-
- /// @brief Sets a pointer to the data store.
- ///
- /// The newly created instance of an option definition will be
- /// added to the data store given by the argument.
- ///
- /// @param storage pointer to the data store where the option definition
- /// will be added to.
- void setStorage(OptionDefStorage* storage) {
- storage_ = storage;
- }
-
-private:
-
- /// @brief Create option definition from the parsed parameters.
- void createOptionDef() {
- // Get the option space name and validate it.
- std::string space = string_values_.getParam("space");
- if (!OptionSpace::validateName(space)) {
- isc_throw(DhcpConfigError, "invalid option space name '"
- << space << "'");
- }
-
- // Get other parameters that are needed to create the
- // option definition.
- std::string name = string_values_.getParam("name");
- uint32_t code = uint32_values_.getParam("code");
- std::string type = string_values_.getParam("type");
- bool array_type = boolean_values_.getParam("array");
- std::string encapsulates = string_values_.getParam("encapsulate");
-
- // Create option definition.
- OptionDefinitionPtr def;
- // We need to check if user has set encapsulated option space
- // name. If so, different constructor will be used.
- if (!encapsulates.empty()) {
- // Arrays can't be used together with sub-options.
- if (array_type) {
- isc_throw(DhcpConfigError, "option '" << space << "."
- << "name" << "', comprising an array of data"
- << " fields may not encapsulate any option space");
-
- } else if (encapsulates == space) {
- isc_throw(DhcpConfigError, "option must not encapsulate"
- << " an option space it belongs to: '"
- << space << "." << name << "' is set to"
- << " encapsulate '" << space << "'");
-
- } else {
- def.reset(new OptionDefinition(name, code, type,
- encapsulates.c_str()));
- }
-
- } else {
- def.reset(new OptionDefinition(name, code, type, array_type));
-
- }
-
- // The record-types field may carry a list of comma separated names
- // of data types that form a record.
- std::string record_types = string_values_.getParam("record-types");
- // Split the list of record types into tokens.
- std::vector<std::string> record_tokens =
- isc::util::str::tokens(record_types, ",");
- // Iterate over each token and add a record type into
- // option definition.
- BOOST_FOREACH(std::string record_type, record_tokens) {
- try {
- boost::trim(record_type);
- if (!record_type.empty()) {
- def->addRecordField(record_type);
- }
- } catch (const Exception& ex) {
- isc_throw(DhcpConfigError, "invalid record type values"
- << " specified for the option definition: "
- << ex.what());
- }
- }
-
- // Check the option definition parameters are valid.
- try {
- def->validate();
- } catch (const isc::Exception& ex) {
- isc_throw(DhcpConfigError, "invalid option definition"
- << " parameters: " << ex.what());
- }
- // Option definition has been created successfully.
- option_space_name_ = space;
- option_definition_ = def;
- }
-
- /// Instance of option definition being created by this parser.
- OptionDefinitionPtr option_definition_;
- /// Name of the space the option definition belongs to.
- std::string option_space_name_;
-
- /// Pointer to a storage where the option definition will be
- /// added when \ref commit is called.
- OptionDefStorage* storage_;
-
- /// Storage for boolean values.
- BooleanStorage boolean_values_;
- /// Storage for string values.
- StringStorage string_values_;
- /// Storage for uint32 values.
- Uint32Storage uint32_values_;
-};
-
-/// @brief Parser for a list of option definitions.
-///
-/// This parser iterates over all configuration entries that define
-/// option definitions and creates instances of these definitions.
-/// If the parsing is successful, the collection of created definitions
-/// is put into the provided storage.
-class OptionDefListParser : DhcpConfigParser {
-public:
-
- /// @brief Constructor.
- ///
- /// This constructor initializes the pointer to option definitions
- /// storage to NULL value. This pointer has to be set to point to
- /// the actual storage before the \ref build function is called.
- OptionDefListParser(const std::string&) {
- }
-
- /// @brief Parse configuration entries.
- ///
- /// This function parses configuration entries and creates instances
- /// of option definitions.
- ///
- /// @param option_def_list pointer to an element that holds entries
- /// that define option definitions.
- /// @throw DhcpConfigError if configuration parsing fails.
- void build(ConstElementPtr option_def_list) {
- // Clear existing items in the global storage.
- // We are going to replace all of them.
- option_def_intermediate.clearItems();
-
- if (!option_def_list) {
- isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
- << " option definitions is NULL");
- }
-
- BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
- boost::shared_ptr<OptionDefParser>
- parser(new OptionDefParser("single-option-def"));
- parser->setStorage(&option_def_intermediate);
- parser->build(option_def);
- parser->commit();
- }
- }
-
- /// @brief Stores option definitions in the CfgMgr.
- void commit() {
-
- CfgMgr& cfg_mgr = CfgMgr::instance();
-
- cfg_mgr.deleteOptionDefs();
-
- // We need to move option definitions from the temporary
- // storage to the global storage.
- std::list<std::string> space_names =
- option_def_intermediate.getOptionSpaceNames();
- BOOST_FOREACH(std::string space_name, space_names) {
-
- BOOST_FOREACH(OptionDefinitionPtr def,
- *option_def_intermediate.getItems(space_name)) {
- // All option definitions should be initialized to non-NULL
- // values. The validation is expected to be made by the
- // OptionDefParser when creating an option definition.
- assert(def);
- cfg_mgr.addOptionDef(def, space_name);
- }
- }
- }
-
- /// @brief Create an OptionDefListParser object.
- ///
- /// @param param_name configuration entry holding option definitions.
- ///
- /// @return OptionDefListParser object.
- static DhcpConfigParser* factory(const std::string& param_name) {
- return (new OptionDefListParser(param_name));
- }
-
-};
-
/// @brief this class parses a single subnet
///
/// This class parses the whole subnet definition. It creates parsers
BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
ParserPtr parser(createSubnet6ConfigParser(param.first));
- // The actual type of the parser is unknown here. We have to discover
- // the parser type here to invoke the corresponding setStorage function
- // on it. We discover parser type by trying to cast the parser to various
- // parser types and checking which one was successful. For this one
- // a setStorage and build methods are invoked.
-
- // Try uint32 type parser.
- if (!buildParser<Uint32Parser, Uint32Storage >(parser, uint32_values_,
- param.second) &&
- // Try string type parser.
- !buildParser<StringParser, StringStorage >(parser, string_values_,
- param.second) &&
- // Try pool parser.
- !buildParser<PoolParser, PoolStorage >(parser, pools_,
- param.second) &&
- // Try option data parser.
- !buildParser<OptionDataListParser, OptionStorage >(parser, options_,
- param.second)) {
- // Appropriate parsers are created in the createSubnet6ConfigParser
- // and they should be limited to those that we check here for. Thus,
- // if we fail to find a matching parser here it is a programming error.
- isc_throw(DhcpConfigError, "failed to find suitable parser");
- }
+ parser->build(param.second);
+ parsers_.push_back(parser);
}
// In order to create new subnet we need to get the data out
private:
- /// @brief Set storage for a parser and invoke build.
- ///
- /// This helper method casts the provided parser pointer to the specified
- /// type. If the cast is successful it sets the corresponding storage for
- /// this parser, invokes build on it and saves the parser.
- ///
- /// @tparam T parser type to which parser argument should be cast.
- /// @tparam Y storage type for the specified parser type.
- /// @param parser parser on which build must be invoked.
- /// @param storage reference to a storage that will be set for a parser.
- /// @param subnet subnet element read from the configuration and being parsed.
- /// @return true if parser pointer was successfully cast to specialized
- /// parser type provided as Y.
- template<typename T, typename Y>
- bool buildParser(const ParserPtr& parser, Y& storage, const ConstElementPtr& subnet) {
- // We need to cast to T in order to set storage for the parser.
- boost::shared_ptr<T> cast_parser = boost::dynamic_pointer_cast<T>(parser);
- // It is common that this cast is not successful because we try to cast to all
- // supported parser types as we don't know the type of a parser in advance.
- if (cast_parser) {
- // Cast, successful so we go ahead with setting storage and actual parse.
- cast_parser->setStorage(&storage);
- parser->build(subnet);
- parsers_.push_back(parser);
- // We indicate that cast was successful so as the calling function
- // may skip attempts to cast to other parser types and proceed to
- // next element.
- return (true);
- }
- // It was not successful. Indicate that another parser type
- // should be tried.
- return (false);
- }
-
/// @brief Append sub-options to an option.
///
/// @param option_space a name of the encapsulated option space.
/// @throw isc::dhcp::DhcpConfigError if trying to create a parser
/// for unknown config element
DhcpConfigParser* createSubnet6ConfigParser(const std::string& config_id) {
- FactoryMap factories;
-
- factories["preferred-lifetime"] = Uint32Parser::factory;
- factories["valid-lifetime"] = Uint32Parser::factory;
- factories["renew-timer"] = Uint32Parser::factory;
- factories["rebind-timer"] = Uint32Parser::factory;
- factories["subnet"] = StringParser::factory;
- factories["pool"] = PoolParser::factory;
- factories["option-data"] = OptionDataListParser::factory;
- factories["interface"] = StringParser::factory;
-
- FactoryMap::iterator f = factories.find(config_id);
- if (f == factories.end()) {
- // Used for debugging only.
- // return new DebugParser(config_id);
-
- isc_throw(isc::dhcp::DhcpConfigError,
- "parser error: subnet6 parameter not supported: "
- << config_id);
+ DhcpConfigParser *parser = NULL;
+ if ((config_id.compare("preferred-lifetime") == 0) ||
+ (config_id.compare("valid-lifetime") == 0) ||
+ (config_id.compare("renew-timer") == 0) ||
+ (config_id.compare("rebind-timer") == 0)) {
+ parser = new Uint32Parser(config_id, &uint32_values_);
+ }
+ else if ((config_id.compare("subnet") == 0) ||
+ (config_id.compare("interface") == 0)) {
+ parser = new StringParser(config_id, &string_values_);
+ }
+ else if (config_id.compare("pool") == 0) {
+ parser = new PoolParser(config_id, &pools_);
+ }
+ else if (config_id.compare("option-data") == 0) {
+ parser = new OptionDataListParser(config_id, &options_,
+ &option_def_intermediate,
+ Dhcp6OptionDataParser::factory);
+ } else {
+ isc_throw(NotImplemented,
+ "parser error: Subnet6 parameter not supported: " << config_id);
}
- return (f->second(config_id));
+
+ return (parser);
}
/// @brief Returns value for a given parameter (after using inheritance)
namespace isc {
namespace dhcp {
+//************** Dhcp6OptionDataParser methods ****************************
+
+Dhcp6OptionDataParser::Dhcp6OptionDataParser(const std::string& param_name,
+ OptionStorage *options, OptionDefStorage *option_defs)
+ :OptionDataParser(param_name, options, option_defs, Option::V6) {
+}
+
+OptionDataParser* Dhcp6OptionDataParser::factory(const std::string& param_name,
+ OptionStorage *options, OptionDefStorage *option_defs) {
+ return new Dhcp6OptionDataParser(param_name, options, option_defs);
+}
+
+OptionDefinitionPtr Dhcp6OptionDataParser::findServerSpaceOptionDefinition (
+ std::string& option_space, uint32_t option_code) {
+ OptionDefinitionPtr def;
+
+ if (option_space == "dhcp6" &&
+ LibDHCP::isStandardOption(Option::V6, option_code)) {
+ def = LibDHCP::getOptionDef(Option::V6, option_code);
+
+ } else if (option_space == "dhcp4") {
+ isc_throw(DhcpConfigError, "'dhcp4' option space name is reserved"
+ << " for DHCPv4 server");
+ }
+
+ return def;
+}
+
/// @brief creates global parsers
///
/// This method creates global parsers that parse global parameters, i.e.
/// @return parser for specified global DHCPv6 parameter
/// @throw NotImplemented if trying to create a parser for unknown config element
DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
- FactoryMap factories;
-
- factories["preferred-lifetime"] = Uint32Parser::factory;
- factories["valid-lifetime"] = Uint32Parser::factory;
- factories["renew-timer"] = Uint32Parser::factory;
- factories["rebind-timer"] = Uint32Parser::factory;
- factories["interface"] = InterfaceListConfigParser::factory;
- factories["subnet6"] = Subnets6ListConfigParser::factory;
- factories["option-data"] = OptionDataListParser::factory;
- factories["option-def"] = OptionDefListParser::factory;
- factories["version"] = StringParser::factory;
- factories["lease-database"] = DbAccessParser::factory;
-
- FactoryMap::iterator f = factories.find(config_id);
- if (f == factories.end()) {
- // Used for debugging only.
- // return new DebugParser(config_id);
-
+ DhcpConfigParser *parser = NULL;
+ if ((config_id.compare("preferred-lifetime") == 0) ||
+ (config_id.compare("valid-lifetime") == 0) ||
+ (config_id.compare("renew-timer") == 0) ||
+ (config_id.compare("rebind-timer") == 0)) {
+ parser = new Uint32Parser(config_id, &uint32_defaults);
+ }
+ else if (config_id.compare("interface") == 0) {
+ parser = new InterfaceListConfigParser(config_id);
+ }
+ else if (config_id.compare("subnet6") == 0) {
+ parser = new Subnets6ListConfigParser(config_id);
+ }
+ else if (config_id.compare("option-data") == 0) {
+ parser = new OptionDataListParser(config_id, &option_defaults,
+ &option_def_intermediate,
+ Dhcp6OptionDataParser::factory);
+ }
+ else if (config_id.compare("option-def") == 0) {
+ parser = new OptionDefListParser(config_id, &option_def_intermediate);
+ }
+ else if (config_id.compare("version") == 0) {
+ parser = new StringParser(config_id, &string_defaults);
+ }
+ else if (config_id.compare("lease-database") == 0) {
+ parser = new DbAccessParser(config_id);
+ }
+ else {
isc_throw(NotImplemented,
- "Parser error: Global configuration parameter not supported: "
- << config_id);
+ "Parser error: Global configuration parameter not supported: "
+ << config_id);
}
- return (f->second(config_id));
+
+ return (parser);
}
ConstElementPtr
#include <cc/data.h>
#include <exceptions/exceptions.h>
+#include <dhcpsrv/dhcp_parsers.h>
+
#include <string>
namespace isc {
isc::data::ConstElementPtr
configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set);
+/// @brief Parser for DHCP6 option data value.
+///
+/// This parser parses configuration entries that specify value of
+/// a single option specific to DHCP6. It provides the DHCP6-specific
+/// implementation of the abstract class OptionDataParser.
+class Dhcp6OptionDataParser : public OptionDataParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// Class constructor.
+ Dhcp6OptionDataParser(const std::string&, OptionStorage *options,
+ OptionDefStorage *option_defs);
+
+ /// @brief static factory method for instantiating Dhcp4OptionDataParsers
+ ///
+ /// @param param_name name fo the parameter to be parsed.
+ /// @param options storage where the parameter value is to be stored.
+ /// @param global_option_defs global option definitions storage
+ static OptionDataParser* factory(const std::string& param_name, OptionStorage *options,
+ OptionDefStorage *global_option_defs);
+
+protected:
+ /// @brief Finds an option definition within the server's option space
+ ///
+ /// Given an option space and an option code, find the correpsonding
+ /// option defintion within the server's option defintion storage.
+ ///
+ /// @param option_space name of the parameter option space
+ /// @param option_code numeric value of the parameter to find
+ /// @return OptionDefintionPtr of the option defintion or an
+ /// empty OptionDefinitionPtr if not found.
+ /// @throw DhcpConfigError if the option space requested is not valid
+ /// for this server.
+ virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
+ std::string& option_space, uint32_t option_code);
+
+private:
+ // Private default Constructor declared for safety.
+ Dhcp6OptionDataParser() :OptionDataParser("",NULL,NULL,Option::V6) {}
+};
+
+
}; // end of isc::dhcp namespace
}; // end of isc namespace
libb10_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
libb10_dhcpsrv_la_SOURCES += dhcp_config_parser.h
+libb10_dhcpsrv_la_SOURCES += dhcp_parsers.cc dhcp_parsers.h
libb10_dhcpsrv_la_SOURCES += key_from_key.h
libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
libb10_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
libb10_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
+libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
libb10_dhcpsrv_la_LDFLAGS = -no-undefined -version-info 3:0:0
if HAVE_MYSQL
libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
--- /dev/null
+// Copyright (C) 2013 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 <dhcpsrv/dhcp_parsers.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <util/encode/hex.h>
+#include <util/strutil.h>
+
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+#include <map>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+// Pointers to various parser objects.
+typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
+typedef boost::shared_ptr<StringParser> StringParserPtr;
+typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
+
+// **************************** DebugParser *************************
+
+DebugParser::DebugParser(const std::string& param_name)
+ :param_name_(param_name) {
+}
+
+void DebugParser::build(ConstElementPtr new_config) {
+ std::cout << "Build for token: [" << param_name_ << "] = ["
+ << value_->str() << "]" << std::endl;
+ value_ = new_config;
+}
+
+void DebugParser::commit() {
+ // Debug message. The whole DebugParser class is used only for parser
+ // debugging, and is not used in production code. It is very convenient
+ // to keep it around. Please do not turn this cout into logger calls.
+ std::cout << "Commit for token: [" << param_name_ << "] = ["
+ << value_->str() << "]" << std::endl;
+}
+
+// **************************** BooleanParser *************************
+BooleanParser::BooleanParser(const std::string& param_name, BooleanStorage *storage)
+ : storage_(storage), param_name_(param_name), value_(false) {
+ // Empty parameter name is invalid.
+ if (param_name_.empty()) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "empty parameter name provided");
+ }
+
+ // NUll storage is invalid.
+ if (!storage_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "storage may not be NULL");
+ }
+}
+
+void BooleanParser::build(ConstElementPtr value) {
+ // The Config Manager checks if user specified a
+ // valid value for a boolean parameter: True or False.
+ // It is then ok to assume that if str() does not return
+ // 'true' the value is 'false'.
+ value_ = (value->str() == "true") ? true : false;
+}
+
+void BooleanParser::commit() {
+ // If a given parameter already exists in the storage we override
+ // its value. If it doesn't we insert a new element.
+ storage_->setParam(param_name_, value_);
+}
+
+// **************************** Uin32Parser *************************
+
+Uint32Parser::Uint32Parser(const std::string& param_name, Uint32Storage *storage)
+ : storage_(storage), param_name_(param_name) {
+ // Empty parameter name is invalid.
+ if (param_name_.empty()) {
+ isc_throw(DhcpConfigError, "parser logic error:"
+ << "empty parameter name provided");
+ }
+
+ // NULL storage is invalid.
+ if (!storage_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "storage may not be NULL");
+ }
+}
+
+void Uint32Parser::build(ConstElementPtr value) {
+ int64_t check;
+ string x = value->str();
+ try {
+ check = boost::lexical_cast<int64_t>(x);
+ } catch (const boost::bad_lexical_cast &) {
+ isc_throw(BadValue, "Failed to parse value " << value->str()
+ << " as unsigned 32-bit integer.");
+ }
+ if (check > std::numeric_limits<uint32_t>::max()) {
+ isc_throw(BadValue, "Value " << value->str() << "is too large"
+ << " for unsigned 32-bit integer.");
+ }
+ if (check < 0) {
+ isc_throw(BadValue, "Value " << value->str() << "is negative."
+ << " Only 0 or larger are allowed for unsigned 32-bit integer.");
+ }
+
+ // value is small enough to fit
+ value_ = static_cast<uint32_t>(check);
+}
+
+void Uint32Parser::commit() {
+ // If a given parameter already exists in the storage we override
+ // its value. If it doesn't we insert a new element.
+ storage_->setParam(param_name_, value_);
+}
+
+// **************************** StringParser *************************
+
+StringParser::StringParser(const std::string& param_name, StringStorage *storage)
+ :storage_(storage), param_name_(param_name) {
+ // Empty parameter name is invalid.
+ if (param_name_.empty()) {
+ isc_throw(DhcpConfigError, "parser logic error:"
+ << "empty parameter name provided");
+ }
+
+ // NULL storage is invalid.
+ if (!storage_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "storage may not be NULL");
+ }
+}
+
+void StringParser::build(ConstElementPtr value) {
+ value_ = value->str();
+ boost::erase_all(value_, "\"");
+}
+
+void StringParser::commit() {
+ // If a given parameter already exists in the storage we override
+ // its value. If it doesn't we insert a new element.
+ storage_->setParam(param_name_, value_);
+}
+
+// **************************** InterfaceListConfigParser *************************
+
+InterfaceListConfigParser::InterfaceListConfigParser(const std::string& param_name) {
+ if (param_name != "interface") {
+ isc_throw(BadValue, "Internal error. Interface configuration "
+ "parser called for the wrong parameter: " << param_name);
+ }
+}
+
+void InterfaceListConfigParser::build(ConstElementPtr value) {
+ BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
+ interfaces_.push_back(iface->str());
+ }
+}
+
+void InterfaceListConfigParser::commit() {
+ /// @todo: Implement per interface listening. Currently always listening
+ /// on all interfaces.
+}
+
+// **************************** OptionDataParser *************************
+
+OptionDataParser::OptionDataParser(const std::string&, OptionStorage *options,
+ OptionDefStorage *option_defs, Option::Universe universe)
+ : options_(options),
+ // initialize option to NULL ptr
+ option_descriptor_(false), global_option_defs_(option_defs),
+ universe_(universe) {
+ if (!options_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "options storage may not be NULL");
+ }
+}
+
+void OptionDataParser::build(ConstElementPtr option_data_entries) {
+ BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
+ ParserPtr parser;
+ if (param.first == "name" || param.first == "data" ||
+ param.first == "space") {
+ StringParserPtr name_parser(new StringParser(param.first, &string_values_));
+ parser = name_parser;
+ } else if (param.first == "code") {
+ Uint32ParserPtr code_parser(new Uint32Parser(param.first, &uint32_values_));
+ parser = code_parser;
+ } else if (param.first == "csv-format") {
+ BooleanParserPtr value_parser(new BooleanParser(param.first, &boolean_values_));
+ parser = value_parser;
+ } else {
+ isc_throw(DhcpConfigError,
+ "Parser error: option-data parameter not supported: "
+ << param.first);
+ }
+
+ parser->build(param.second);
+ // Before we can create an option we need to get the data from
+ // the child parsers. The only way to do it is to invoke commit
+ // on them so as they store the values in appropriate storages
+ // that this class provided to them. Note that this will not
+ // modify values stored in the global storages so the configuration
+ // will remain consistent even parsing fails somewhere further on.
+ parser->commit();
+ }
+
+ // Try to create the option instance.
+ createOption();
+}
+
+void OptionDataParser::commit() {
+ if (!option_descriptor_.option) {
+ // Before we can commit the new option should be configured. If it is not
+ // than somebody must have called commit() before build().
+ isc_throw(isc::InvalidOperation,
+ "parser logic error: no option has been configured and"
+ " thus there is nothing to commit. Has build() been called?");
+ }
+
+ uint16_t opt_type = option_descriptor_.option->getType();
+ Subnet::OptionContainerPtr options = options_->getItems(option_space_);
+ // The getItems() should never return NULL pointer. If there are no
+ // options configured for the particular option space a pointer
+ // to an empty container should be returned.
+ assert(options);
+ Subnet::OptionContainerTypeIndex& idx = options->get<1>();
+ // Try to find options with the particular option code in the main
+ // storage. If found, remove these options because they will be
+ // replaced with new one.
+ Subnet::OptionContainerTypeRange range = idx.equal_range(opt_type);
+ if (std::distance(range.first, range.second) > 0) {
+ idx.erase(range.first, range.second);
+ }
+
+ // Append new option to the main storage.
+ options_->addItem(option_descriptor_, option_space_);
+}
+
+void OptionDataParser::createOption() {
+ // 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 and is not zero.
+ uint32_t option_code = uint32_values_.getParam("code");
+ if (option_code == 0) {
+ isc_throw(DhcpConfigError, "option code must not be zero."
+ << " Option code '0' is reserved in DHCPv4.");
+ } else if (option_code > std::numeric_limits<uint8_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << option_code
+ << "', it must not exceed '"
+ << std::numeric_limits<uint8_t>::max() << "'");
+ }
+
+ // Check that the option name has been specified, is non-empty and does not
+ // contain spaces
+ std::string option_name = string_values_.getParam("name");
+ if (option_name.empty()) {
+ isc_throw(DhcpConfigError, "name of the option with code '"
+ << option_code << "' is empty");
+ } else if (option_name.find(" ") != std::string::npos) {
+ isc_throw(DhcpConfigError, "invalid option name '" << option_name
+ << "', space character is not allowed");
+ }
+
+ std::string option_space = string_values_.getParam("space");
+ if (!OptionSpace::validateName(option_space)) {
+ isc_throw(DhcpConfigError, "invalid option space name '"
+ << option_space << "' specified for option '"
+ << option_name << "' (code '" << option_code
+ << "')");
+ }
+
+ // Find the Option Definition for the option by its option code.
+ // findOptionDefinition will throw if not found, no need to test.
+ OptionDefinitionPtr def;
+ if (!(def = findServerSpaceOptionDefinition(option_space, option_code))) {
+ // If we are not dealing with a standard option then we
+ // need to search for its definition among user-configured
+ // options. They are expected to be in the global storage
+ // already.
+ OptionDefContainerPtr defs = global_option_defs_->getItems(option_space);
+
+ // The getItems() should never return the NULL pointer. If there are
+ // no option definitions for the particular option space a pointer
+ // to an empty container should be returned.
+ assert(defs);
+ const OptionDefContainerTypeIndex& idx = defs->get<1>();
+ OptionDefContainerTypeRange range = idx.equal_range(option_code);
+ if (std::distance(range.first, range.second) > 0) {
+ def = *range.first;
+ }
+ if (!def) {
+ isc_throw(DhcpConfigError, "definition for the option '"
+ << option_space << "." << option_name
+ << "' having code '" << option_code
+ << "' does not exist");
+ }
+ }
+
+ // Get option data from the configuration database ('data' field).
+ const std::string option_data = string_values_.getParam("data");
+ const bool csv_format = boolean_values_.getParam("csv-format");
+
+ // Transform string of hexadecimal digits into binary format.
+ std::vector<uint8_t> binary;
+ std::vector<std::string> data_tokens;
+
+ if (csv_format) {
+ // 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(option_data, ",");
+ } else {
+ // Otherwise, the option data is specified as a string of
+ // hexadecimal digits that we have to turn into binary format.
+ try {
+ util::encode::decodeHex(option_data, binary);
+ } catch (...) {
+ isc_throw(DhcpConfigError, "option data is not a valid"
+ << " string of hexadecimal digits: " << option_data);
+ }
+ }
+
+ OptionPtr option;
+ if (!def) {
+ if (csv_format) {
+ 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 " << option_code
+ << " does not have a definition.");
+ }
+
+ // @todo We have a limited set of option definitions intiialized at the moment.
+ // In the future we want to initialize option definitions for all options.
+ // Consequently an error will be issued if an option 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>(option_code),
+ 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
+ // may seem to be redundant but in the future we may want
+ // 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() != option_name) {
+ isc_throw(DhcpConfigError, "specified option name '"
+ << option_name << "' does not match the "
+ << "option definition: '" << option_space
+ << "." << def->getName() << "'");
+ }
+
+ // 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_, option_code, data_tokens) :
+ def->optionFactory(universe_, option_code, binary);
+ Subnet::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: " << option_space
+ << ", code: " << option_code << "): "
+ << ex.what());
+ }
+ }
+
+ // All went good, so we can set the option space name.
+ option_space_ = option_space;
+}
+
+// **************************** OptionDataListParser *************************
+
+OptionDataListParser::OptionDataListParser(const std::string&,
+ OptionStorage *storage, OptionDefStorage *global_option_defs,
+ OptionDataParserFactory *optionDataParserFactory)
+ : options_(storage), local_options_(),
+ global_option_defs_(global_option_defs),
+ optionDataParserFactory_(optionDataParserFactory) {
+ if (!options_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "options storage may not be NULL");
+ }
+
+ if (!optionDataParserFactory_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "option data parser factory may not be NULL");
+ }
+}
+
+
+void OptionDataListParser::build(ConstElementPtr option_data_list) {
+ BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
+ boost::shared_ptr<OptionDataParser>
+ parser((*optionDataParserFactory_)("option-data", &local_options_, global_option_defs_));
+
+ // options_ member will hold instances of all options thus
+ // each OptionDataParser takes it as a storage.
+ // Build the instance of a single option.
+ parser->build(option_value);
+ // Store a parser as it will be used to commit.
+ parsers_.push_back(parser);
+ }
+}
+
+void OptionDataListParser::commit() {
+ BOOST_FOREACH(ParserPtr parser, parsers_) {
+ parser->commit();
+ }
+
+ // Parsing was successful and we have all configured
+ // options in local storage. We can now replace old values
+ // with new values.
+ std::swap(local_options_, *options_);
+}
+
+// ******************************** OptionDefParser ****************************
+
+OptionDefParser::OptionDefParser(const std::string&, OptionDefStorage *storage)
+ : storage_(storage) {
+ if (!storage_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "options storage may not be NULL");
+ }
+}
+
+void OptionDefParser::build(ConstElementPtr option_def) {
+ // Parse the elements that make up the option definition.
+ BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
+ std::string entry(param.first);
+ ParserPtr parser;
+ if (entry == "name" || entry == "type" || entry == "record-types"
+ || entry == "space" || entry == "encapsulate") {
+ StringParserPtr str_parser(new StringParser(entry, &string_values_));
+ parser = str_parser;
+ } else if (entry == "code") {
+ Uint32ParserPtr code_parser(new Uint32Parser(entry, &uint32_values_));
+ parser = code_parser;
+ } else if (entry == "array") {
+ BooleanParserPtr array_parser(new BooleanParser(entry, &boolean_values_));
+ parser = array_parser;
+ } else {
+ isc_throw(DhcpConfigError, "invalid parameter: " << entry);
+ }
+
+ parser->build(param.second);
+ parser->commit();
+ }
+
+ // Create an instance of option definition.
+ createOptionDef();
+
+ // Get all items we collected so far for the particular option space.
+ OptionDefContainerPtr defs = storage_->getItems(option_space_name_);
+
+ // Check if there are any items with option code the same as the
+ // one specified for the definition we are now creating.
+ const OptionDefContainerTypeIndex& idx = defs->get<1>();
+ const OptionDefContainerTypeRange& range =
+ idx.equal_range(option_definition_->getCode());
+
+ // If there are any items with this option code already we need
+ // to issue an error because we don't allow duplicates for
+ // option definitions within an option space.
+ if (std::distance(range.first, range.second) > 0) {
+ isc_throw(DhcpConfigError, "duplicated option definition for"
+ << " code '" << option_definition_->getCode() << "'");
+ }
+}
+
+void OptionDefParser::commit() {
+ if (storage_ && option_definition_ &&
+ OptionSpace::validateName(option_space_name_)) {
+ storage_->addItem(option_definition_, option_space_name_);
+ }
+}
+
+void OptionDefParser::createOptionDef() {
+ // Get the option space name and validate it.
+ std::string space = string_values_.getParam("space");
+ if (!OptionSpace::validateName(space)) {
+ isc_throw(DhcpConfigError, "invalid option space name '"
+ << space << "'");
+ }
+
+ // Get other parameters that are needed to create the
+ // option definition.
+ std::string name = string_values_.getParam("name");
+ uint32_t code = uint32_values_.getParam("code");
+ std::string type = string_values_.getParam("type");
+ bool array_type = boolean_values_.getParam("array");
+ std::string encapsulates = string_values_.getParam("encapsulate");
+
+ // Create option definition.
+ OptionDefinitionPtr def;
+ // We need to check if user has set encapsulated option space
+ // name. If so, different constructor will be used.
+ if (!encapsulates.empty()) {
+ // Arrays can't be used together with sub-options.
+ if (array_type) {
+ isc_throw(DhcpConfigError, "option '" << space << "."
+ << "name" << "', comprising an array of data"
+ << " fields may not encapsulate any option space");
+
+ } else if (encapsulates == space) {
+ isc_throw(DhcpConfigError, "option must not encapsulate"
+ << " an option space it belongs to: '"
+ << space << "." << name << "' is set to"
+ << " encapsulate '" << space << "'");
+
+ } else {
+ def.reset(new OptionDefinition(name, code, type,
+ encapsulates.c_str()));
+ }
+
+ } else {
+ def.reset(new OptionDefinition(name, code, type, array_type));
+
+ }
+
+ // The record-types field may carry a list of comma separated names
+ // of data types that form a record.
+ std::string record_types = string_values_.getParam("record-types");
+
+ // Split the list of record types into tokens.
+ std::vector<std::string> record_tokens =
+ isc::util::str::tokens(record_types, ",");
+ // Iterate over each token and add a record type into
+ // option definition.
+ BOOST_FOREACH(std::string record_type, record_tokens) {
+ try {
+ boost::trim(record_type);
+ if (!record_type.empty()) {
+ def->addRecordField(record_type);
+ }
+ } catch (const Exception& ex) {
+ isc_throw(DhcpConfigError, "invalid record type values"
+ << " specified for the option definition: "
+ << ex.what());
+ }
+ }
+
+ // Check the option definition parameters are valid.
+ try {
+ def->validate();
+ } catch (const isc::Exception& ex) {
+ isc_throw(DhcpConfigError, "invalid option definition"
+ << " parameters: " << ex.what());
+ }
+
+ // Option definition has been created successfully.
+ option_space_name_ = space;
+ option_definition_ = def;
+}
+
+// ******************************** OptionDefListParser ************************
+
+OptionDefListParser::OptionDefListParser(const std::string&,
+ OptionDefStorage *storage) :storage_(storage) {
+ if (!storage_) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "storage may not be NULL");
+ }
+}
+
+void OptionDefListParser::build(ConstElementPtr option_def_list) {
+ // Clear existing items in the global storage.
+ // We are going to replace all of them.
+ storage_->clearItems();
+
+ if (!option_def_list) {
+ isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
+ << " option definitions is NULL");
+ }
+
+ BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
+ boost::shared_ptr<OptionDefParser>
+ parser(new OptionDefParser("single-option-def", storage_));
+ parser->build(option_def);
+ parser->commit();
+ }
+}
+
+void OptionDefListParser::commit() {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ cfg_mgr.deleteOptionDefs();
+
+ // We need to move option definitions from the temporary
+ // storage to the global storage.
+ std::list<std::string> space_names =
+ storage_->getOptionSpaceNames();
+
+ BOOST_FOREACH(std::string space_name, space_names) {
+ BOOST_FOREACH(OptionDefinitionPtr def,
+ *(storage_->getItems(space_name))) {
+ // All option definitions should be initialized to non-NULL
+ // values. The validation is expected to be made by the
+ // OptionDefParser when creating an option definition.
+ assert(def);
+ cfg_mgr.addOptionDef(def, space_name);
+ }
+ }
+}
+
+}; // namespace dhcp
+}; // namespace isc
--- /dev/null
+// Copyright (C) 2013 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 DHCP_PARSERS_H
+#define DHCP_PARSERS_H
+
+#include <cc/data.h>
+#include <dhcp/option_definition.h>
+#include <dhcpsrv/dhcp_config_parser.h>
+#include <dhcpsrv/option_space_container.h>
+#include <dhcpsrv/subnet.h>
+#include <exceptions/exceptions.h>
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Storage for option definitions.
+typedef OptionSpaceContainer<OptionDefContainer,
+ OptionDefinitionPtr> OptionDefStorage;
+
+/// Collection of containers holding option spaces. Each container within
+/// a particular option space holds so-called option descriptors.
+typedef OptionSpaceContainer<Subnet::OptionContainer,
+ Subnet::OptionDescriptor> OptionStorage;
+
+/// @brief a dummy configuration parser
+///
+/// It is a debugging parser. It does not configure anything,
+/// will accept any configuration and will just print it out
+/// on commit. Useful for debugging existing configurations and
+/// adding new ones.
+class DebugParser : public DhcpConfigParser {
+public:
+
+ /// @brief Constructor
+ ///
+ /// See @ref DhcpConfigParser class for details.
+ ///
+ /// @param param_name name of the parsed parameter
+ DebugParser(const std::string& param_name);
+
+ /// @brief builds parameter value
+ ///
+ /// See @ref DhcpConfigParser class for details.
+ ///
+ /// @param new_config pointer to the new configuration
+ virtual void build(isc::data::ConstElementPtr new_config);
+
+ /// @brief pretends to apply the configuration
+ ///
+ /// This is a method required by base class. It pretends to apply the
+ /// configuration, but in fact it only prints the parameter out.
+ ///
+ /// See @ref DhcpConfigParser class for details.
+ virtual void commit();
+
+private:
+ /// name of the parsed parameter
+ std::string param_name_;
+
+ /// pointer to the actual value of the parameter
+ isc::data::ConstElementPtr value_;
+
+};
+
+/// @brief A boolean value parser.
+///
+/// This parser handles configuration values of the boolean type.
+/// Parsed values are stored in a provided storage. If no storage
+/// is provided then the build function throws an exception.
+class BooleanParser : public DhcpConfigParser {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param param_name name of the parameter.
+ BooleanParser(const std::string& param_name, BooleanStorage *storage);
+
+ /// @brief Parse a boolean value.
+ ///
+ /// @param value a value to be parsed.
+ ///
+ /// @throw isc::InvalidOperation if a storage has not been set
+ /// prior to calling this function
+ /// @throw isc::dhcp::DhcpConfigError if a provided parameter's
+ /// name is empty.
+ virtual void build(isc::data::ConstElementPtr value);
+
+ /// @brief Put a parsed value to the storage.
+ virtual void commit();
+
+private:
+ /// Pointer to the storage where parsed value is stored.
+ BooleanStorage* storage_;
+ /// Name of the parameter which value is parsed with this parser.
+ std::string param_name_;
+ /// Parsed value.
+ bool value_;
+
+ /// Default constructor is private for safety.
+ BooleanParser(){};
+};
+
+/// @brief Configuration parser for uint32 parameters
+///
+/// This class is a generic parser that is able to handle any uint32 integer
+/// type. By default it stores the value in external global container
+/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
+/// in subnet config), it can be pointed to a different storage, using
+/// setStorage() method. This class follows the parser interface, laid out
+/// in its base class, @ref DhcpConfigParser.
+///
+/// For overview of usability of this generic purpose parser, see
+/// @ref dhcpv4ConfigInherit page.
+class Uint32Parser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor for Uint32Parser
+ /// @param param_name name of the configuration parameter being parsed
+ Uint32Parser(const std::string& param_name, Uint32Storage *storage);
+
+ /// @brief Parses configuration configuration parameter as uint32_t.
+ ///
+ /// @param value pointer to the content of parsed values
+ /// @throw BadValue if supplied value could not be base to uint32_t
+ /// or the parameter name is empty.
+ virtual void build(isc::data::ConstElementPtr value);
+
+ /// @brief Stores the parsed uint32_t value in a storage.
+ virtual void commit();
+
+private:
+ /// pointer to the storage, where parsed value will be stored
+ Uint32Storage* storage_;
+
+ /// name of the parameter to be parsed
+ std::string param_name_;
+
+ /// the actual parsed value
+ uint32_t value_;
+
+ /// Default constructor is private for safety.
+ Uint32Parser(){};
+};
+
+/// @brief Configuration parser for string parameters
+///
+/// This class is a generic parser that is able to handle any string
+/// parameter. By default it stores the value in external global container
+/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
+/// in subnet config), it can be pointed to a different storage, using
+/// setStorage() method. This class follows the parser interface, laid out
+/// in its base class, @ref DhcpConfigParser.
+///
+/// For overview of usability of this generic purpose parser, see
+/// @ref dhcpv4ConfigInherit page.
+class StringParser : public DhcpConfigParser {
+public:
+ /// @brief constructor for StringParser
+ /// @param param_name name of the configuration parameter being parsed
+ StringParser(const std::string& param_name, StringStorage *storage);
+
+ /// @brief parses parameter value
+ ///
+ /// Parses configuration entry and stores it in storage. See
+ /// @ref setStorage() for details.
+ ///
+ /// @param value pointer to the content of parsed values
+ virtual void build(isc::data::ConstElementPtr value);
+
+ /// @brief Stores the parsed value in a storage.
+ virtual void commit();
+
+private:
+ /// pointer to the storage, where parsed value will be stored
+ StringStorage* storage_;
+
+ /// name of the parameter to be parsed
+ std::string param_name_;
+
+ /// the actual parsed value
+ std::string value_;
+
+ /// Default constructor is private for safety.
+ StringParser(){};
+};
+
+/// @brief parser for interface list definition
+///
+/// This parser handles Dhcp4/interface entry.
+/// It contains a list of network interfaces that the server listens on.
+/// In particular, it can contain an entry called "all" or "any" that
+/// designates all interfaces.
+///
+/// It is useful for parsing Dhcp4/interface parameter.
+class InterfaceListConfigParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor
+ ///
+ /// As this is a dedicated parser, it must be used to parse
+ /// "interface" parameter only. All other types will throw exception.
+ ///
+ /// @param param_name name of the configuration parameter being parsed
+ /// @throw BadValue if supplied parameter name is not "interface"
+ InterfaceListConfigParser(const std::string& param_name);
+
+ /// @brief parses parameters value
+ ///
+ /// Parses configuration entry (list of parameters) and adds each element
+ /// to the interfaces list.
+ ///
+ /// @param value pointer to the content of parsed values
+ virtual void build(isc::data::ConstElementPtr value);
+
+ /// @brief commits interfaces list configuration
+ virtual void commit();
+
+private:
+ /// contains list of network interfaces
+ std::vector<std::string> interfaces_;
+
+ /// Default constructor is private for safety.
+ InterfaceListConfigParser(){};
+};
+
+
+/// @brief Parser for option data value.
+///
+/// This parser parses configuration entries that specify value of
+/// a single option. These entries include option name, option code
+/// and data carried by the option. The option data can be specified
+/// in one of the two available formats: binary value represented as
+/// a string of hexadecimal digits or a list of comma separated values.
+/// The format being used is controlled by csv-format configuration
+/// parameter. When setting this value to True, the latter format is
+/// used. The subsequent values in the CSV format apply to relevant
+/// option data fields in the configured option. For example the
+/// configuration: "data" : "192.168.2.0, 56, hello world" can be
+/// used to set values for the option comprising IPv4 address,
+/// integer and string data field. Note that order matters. If the
+/// order of values does not match the order of data fields within
+/// an option the configuration will not be accepted. If parsing
+/// is successful then an instance of an option is created and
+/// added to the storage provided by the calling class.
+class OptionDataParser : public DhcpConfigParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// Class constructor.
+ OptionDataParser(const std::string&, OptionStorage *options,
+ OptionDefStorage *option_defs, Option::Universe universe);
+
+ /// @brief Parses the single option data.
+ ///
+ /// This method parses the data of a single option from the configuration.
+ /// The option data includes option name, option code and data being
+ /// carried by this option. Eventually it creates the instance of the
+ /// option.
+ ///
+ /// @warning setStorage must be called with valid storage pointer prior
+ /// to calling this method.
+ ///
+ /// @param option_data_entries collection of entries that define value
+ /// for a particular option.
+ /// @throw DhcpConfigError if invalid parameter specified in
+ /// the configuration.
+ /// @throw isc::InvalidOperation if failed to set storage prior to
+ /// calling build.
+ virtual void build(isc::data::ConstElementPtr option_data_entries);
+
+ /// @brief Commits option value.
+ ///
+ /// This function adds a new option to the storage or replaces an existing option
+ /// with the same code.
+ ///
+ /// @throw isc::InvalidOperation if failed to set pointer to storage or failed
+ /// to call build() prior to commit. If that happens data in the storage
+ /// remain un-modified.
+ virtual void commit();
+
+ /// @brief virtual destructor to ensure orderly destruction of derivations.
+ virtual ~OptionDataParser(){};
+
+protected:
+ /// @brief Finds an option definition within the server's option space
+ ///
+ /// Given an option space and an option code, find the correpsonding
+ /// option defintion within the server's option defintion storage. This
+ /// method is pure virtual requiring derivations to manage which option
+ /// space(s) is valid for search.
+ ///
+ /// @param option_space name of the parameter option space
+ /// @param option_code numeric value of the parameter to find
+ /// @return OptionDefintionPtr of the option defintion or an
+ /// empty OptionDefinitionPtr if not found.
+ /// @throw DhcpConfigError if the option space requested is not valid
+ /// for this server.
+ virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
+ std::string& option_space, uint32_t option_code) = 0;
+
+private:
+
+ /// @brief Create option instance.
+ ///
+ /// Creates an instance of an option and adds it to the provided
+ /// options storage. If the option data parsed by \ref build function
+ /// are invalid or insufficient this function emits an exception.
+ ///
+ /// @warning this function does not check if options_ storage pointer
+ /// is intitialized but this check is not needed here because it is done
+ /// in the \ref build function.
+ ///
+ /// @throw DhcpConfigError if parameters provided in the configuration
+ /// are invalid.
+ void createOption();
+
+ /// Storage for uint32 values (e.g. option code).
+ Uint32Storage uint32_values_;
+ /// Storage for string values (e.g. option name or data).
+ StringStorage string_values_;
+ /// Storage for boolean values.
+ BooleanStorage boolean_values_;
+ /// Pointer to options storage. This storage is provided by
+ /// the calling class and is shared by all OptionDataParser objects.
+ OptionStorage* options_;
+ /// Option descriptor holds newly configured option.
+ Subnet::OptionDescriptor option_descriptor_;
+ /// Option space name where the option belongs to.
+ std::string option_space_;
+
+ /// Option definition storage
+ OptionDefStorage *global_option_defs_;
+
+ /// Option::Universe for this parser's option
+ Option::Universe universe_;
+
+ /// Default constructor is private for safety.
+ OptionDataParser():option_descriptor_(false){};
+};
+
+///@brief Function pointer for OptionDataParser factory methods
+typedef OptionDataParser *OptionDataParserFactory(const std::string&, OptionStorage *options,
+ OptionDefStorage *option_defs);
+
+/// @brief Parser for option data values within a subnet.
+///
+/// This parser iterates over all entries that define options
+/// data for a particular subnet and creates a collection of options.
+/// If parsing is successful, all these options are added to the Subnet
+/// object.
+class OptionDataListParser : public DhcpConfigParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param string& nominally would be param name, this is always ignored.
+ /// @param storage parsed option storage for options in this list
+ /// @param global_option_defs global set of option definitions to reference
+ /// @param optionDataParserFactory factory method for creating individual option
+ /// parsers
+ OptionDataListParser(const std::string&, OptionStorage *storage,
+ OptionDefStorage *global_option_defs,
+ OptionDataParserFactory *optionDataParserFactory);
+
+ /// @brief Parses entries that define options' data for a subnet.
+ ///
+ /// This method iterates over all entries that define option data
+ /// for options within a single subnet and creates options' instances.
+ ///
+ /// @param option_data_list pointer to a list of options' data sets.
+ /// @throw DhcpConfigError if option parsing failed.
+ void build(isc::data::ConstElementPtr option_data_list);
+
+ /// @brief Commit all option values.
+ ///
+ /// This function invokes commit for all option values.
+ void commit();
+
+private:
+ /// Pointer to options instances storage.
+ OptionStorage* options_;
+ /// Intermediate option storage. This storage is used by
+ /// lower level parsers to add new options. Values held
+ /// in this storage are assigned to main storage (options_)
+ /// if overall parsing was successful.
+ OptionStorage local_options_;
+ /// Collection of parsers;
+ ParserCollection parsers_;
+
+ /// Global option definitions
+ OptionDefStorage *global_option_defs_;
+
+ /// Factory to create server-specific option data parsers
+ OptionDataParserFactory *optionDataParserFactory_;
+
+ /// Default constructor is private for safety.
+ OptionDataListParser(){};
+};
+
+
+/// @brief Parser for a single option definition.
+///
+/// This parser creates an instance of a single option definition.
+class OptionDefParser : public DhcpConfigParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// This constructor sets the pointer to the option definitions
+ /// storage to NULL. It must be set to point to the actual storage
+ /// before \ref build is called.
+ OptionDefParser(const std::string&, OptionDefStorage *storage);
+
+ /// @brief Parses an entry that describes single option definition.
+ ///
+ /// @param option_def a configuration entry to be parsed.
+ ///
+ /// @throw DhcpConfigError if parsing was unsuccessful.
+ void build(isc::data::ConstElementPtr option_def);
+
+ /// @brief Stores the parsed option definition in a storage.
+ void commit();
+
+private:
+
+ /// @brief Create option definition from the parsed parameters.
+ void createOptionDef();
+
+ /// Instance of option definition being created by this parser.
+ OptionDefinitionPtr option_definition_;
+ /// Name of the space the option definition belongs to.
+ std::string option_space_name_;
+
+ /// Pointer to a storage where the option definition will be
+ /// added when \ref commit is called.
+ OptionDefStorage* storage_;
+
+ /// Storage for boolean values.
+ BooleanStorage boolean_values_;
+ /// Storage for string values.
+ StringStorage string_values_;
+ /// Storage for uint32 values.
+ Uint32Storage uint32_values_;
+
+ // Default constructor is private for safety.
+ OptionDefParser(){};
+};
+
+/// @brief Parser for a list of option definitions.
+///
+/// This parser iterates over all configuration entries that define
+/// option definitions and creates instances of these definitions.
+/// If the parsing is successful, the collection of created definitions
+/// is put into the provided storage.
+class OptionDefListParser : public DhcpConfigParser {
+public:
+ /// @brief Constructor.
+ ///
+ /// This constructor initializes the pointer to option definitions
+ /// storage to NULL value. This pointer has to be set to point to
+ /// the actual storage before the \ref build function is called.
+ OptionDefListParser(const std::string&, OptionDefStorage *storage);
+
+ /// @brief Parse configuration entries.
+ ///
+ /// This function parses configuration entries and creates instances
+ /// of option definitions.
+ ///
+ /// @param option_def_list pointer to an element that holds entries
+ /// that define option definitions.
+ /// @throw DhcpConfigError if configuration parsing fails.
+ void build(isc::data::ConstElementPtr option_def_list);
+
+ /// @brief Stores option definitions in the CfgMgr.
+ void commit();
+
+private:
+ /// @brief storage for option definitions.
+ OptionDefStorage *storage_;
+
+ // Default constructor is private for safety.
+ OptionDefListParser(){};
+
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DHCP_PARSERS_H
+