From: Thomas Markwalder Date: Wed, 10 Apr 2013 21:02:10 +0000 (-0400) Subject: [2355] Initial commit of refactoring bin/dchp4 and bin/dchp6 X-Git-Tag: bind10-1.2.0beta1-release~464^2~8 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=53ceb71e36d310a2a4cd5fef7ae9fa5d410f078a;p=thirdparty%2Fkea.git [2355] Initial commit of refactoring bin/dchp4 and bin/dchp6 configuration parsing classes into a common set of classes in lib/dhcpsrv. Moved most of the parsers classes into new files dhcp_parsers.h/cc. Eliminated use of global variables within common parsers. --- diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc index 2391dff085..e5317451ba 100644 --- a/src/bin/dhcp4/config_parser.cc +++ b/src/bin/dhcp4/config_parser.cc @@ -13,19 +13,21 @@ // PERFORMANCE OF THIS SOFTWARE. #include -#include #include #include #include #include +#include #include -#include +#include #include #include #include + #include #include #include + #include #include #include @@ -39,37 +41,17 @@ using namespace isc::asiolink; 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 BooleanParserPtr; typedef boost::shared_ptr StringParserPtr; typedef boost::shared_ptr 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 FactoryMap; - -/// @brief Storage for option definitions. -typedef OptionSpaceContainer 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 PoolStorage; -/// Collection of containers holding option spaces. Each container within -/// a particular option space holds so-called option descriptors. -typedef OptionSpaceContainer OptionStorage; /// @brief Global uint32 parameters that will be used as defaults. Uint32Storage uint32_defaults; @@ -83,365 +65,6 @@ OptionStorage option_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(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::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(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 interfaces_; -}; - /// @brief parser for pool definition /// /// This parser handles pool definitions, i.e. a list of entries of one @@ -461,6 +84,12 @@ public: // 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. @@ -575,675 +204,6 @@ private: 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 - name_parser(dynamic_cast(StringParser::factory(param.first))); - if (name_parser) { - name_parser->setStorage(&string_values_); - parser = name_parser; - } - } else if (param.first == "code") { - boost::shared_ptr - code_parser(dynamic_cast(Uint32Parser::factory(param.first))); - if (code_parser) { - code_parser->setStorage(&uint32_values_); - parser = code_parser; - } - } else if (param.first == "csv-format") { - boost::shared_ptr - value_parser(dynamic_cast(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::max()) { - isc_throw(DhcpConfigError, "invalid option code '" << option_code - << "', it must not exceed '" - << std::numeric_limits::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 binary; - std::vector 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(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 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::factory(entry))); - if (str_parser) { - str_parser->setStorage(&string_values_); - parser = str_parser; - } - } else if (entry == "code") { - Uint32ParserPtr - code_parser(dynamic_cast(Uint32Parser::factory(entry))); - if (code_parser) { - code_parser->setStorage(&uint32_values_); - parser = code_parser; - } - } else if (entry == "array") { - BooleanParserPtr - array_parser(dynamic_cast(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 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 - 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 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 @@ -1264,30 +224,10 @@ public: 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(parser, uint32_values_, - param.second) && - // Try string type parser. - !buildParser(parser, string_values_, - param.second) && - // Try pool parser. - !buildParser(parser, pools_, - param.second) && - // Try option data parser. - !buildParser(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 @@ -1319,40 +259,6 @@ public: 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 - 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 cast_parser = boost::dynamic_pointer_cast(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. @@ -1418,7 +324,7 @@ private: 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"); } @@ -1534,24 +440,27 @@ private: /// @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) @@ -1622,16 +531,11 @@ public: /// /// @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. @@ -1668,6 +572,34 @@ public: 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. @@ -1677,28 +609,39 @@ namespace dhcp { /// @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 @@ -1790,6 +733,7 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) { } 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()); @@ -1834,6 +778,13 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) { // 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); @@ -1854,3 +805,4 @@ const Uint32Storage& getUint32Defaults() { }; // end of isc::dhcp namespace }; // end of isc namespace + diff --git a/src/bin/dhcp4/config_parser.h b/src/bin/dhcp4/config_parser.h index 51a7556204..578b637ce4 100644 --- a/src/bin/dhcp4/config_parser.h +++ b/src/bin/dhcp4/config_parser.h @@ -12,9 +12,10 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include -#include #include +#include +#include + #include #include @@ -69,6 +70,49 @@ configureDhcp4Server(Dhcpv4Srv&, /// @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 diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc index e99deeb7ff..8e746076d7 100644 --- a/src/bin/dhcp6/config_parser.cc +++ b/src/bin/dhcp6/config_parser.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -49,38 +50,17 @@ using namespace isc::asiolink; 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 BooleanParserPtr; typedef boost::shared_ptr StringParserPtr; typedef boost::shared_ptr 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 FactoryMap; - -/// @brief Storage for option definitions. -typedef OptionSpaceContainer 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 PoolStorage; -/// Collection of containers holding option spaces. Each container within -/// a particular option space holds so-called option descriptors. -typedef OptionSpaceContainer OptionStorage; - /// @brief Global uint32 parameters that will be used as defaults. Uint32Storage uint32_defaults; @@ -93,381 +73,6 @@ OptionStorage option_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(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::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(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 interfaces_; -}; - /// @brief parser for pool definition /// /// This parser handles pool definitions, i.e. a list of entries of one @@ -487,6 +92,12 @@ public: // 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. @@ -602,674 +213,6 @@ private: 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 - name_parser(dynamic_cast(StringParser::factory(param.first))); - if (name_parser) { - name_parser->setStorage(&string_values_); - parser = name_parser; - } - } else if (param.first == "code") { - boost::shared_ptr - code_parser(dynamic_cast(Uint32Parser::factory(param.first))); - if (code_parser) { - code_parser->setStorage(&uint32_values_); - parser = code_parser; - } - } else if (param.first == "csv-format") { - boost::shared_ptr - value_parser(dynamic_cast(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::max()) { - isc_throw(DhcpConfigError, "invalid option code '" << option_code - << "', it must not exceed '" - << std::numeric_limits::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 binary; - std::vector 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(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 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::factory(entry))); - if (str_parser) { - str_parser->setStorage(&string_values_); - parser = str_parser; - } - } else if (entry == "code") { - Uint32ParserPtr - code_parser(dynamic_cast(Uint32Parser::factory(entry))); - if (code_parser) { - code_parser->setStorage(&uint32_values_); - parser = code_parser; - } - } else if (entry == "array") { - BooleanParserPtr - array_parser(dynamic_cast(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 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 - 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 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 @@ -1292,29 +235,8 @@ public: 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(parser, uint32_values_, - param.second) && - // Try string type parser. - !buildParser(parser, string_values_, - param.second) && - // Try pool parser. - !buildParser(parser, pools_, - param.second) && - // Try option data parser. - !buildParser(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 @@ -1342,40 +264,6 @@ public: 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 - 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 cast_parser = boost::dynamic_pointer_cast(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. @@ -1581,27 +469,30 @@ private: /// @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) @@ -1718,6 +609,34 @@ public: 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. @@ -1727,29 +646,40 @@ namespace dhcp { /// @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 diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h index 6d7a807b80..6840ecce8a 100644 --- a/src/bin/dhcp6/config_parser.h +++ b/src/bin/dhcp6/config_parser.h @@ -20,6 +20,8 @@ #include #include +#include + #include namespace isc { @@ -47,6 +49,48 @@ class Dhcpv6Srv; 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 diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index db82513f52..77a3a122af 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -37,6 +37,7 @@ libb10_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h 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 @@ -57,6 +58,7 @@ libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES) 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) diff --git a/src/lib/dhcpsrv/dhcp_parsers.cc b/src/lib/dhcpsrv/dhcp_parsers.cc new file mode 100644 index 0000000000..940cf069b3 --- /dev/null +++ b/src/lib/dhcpsrv/dhcp_parsers.cc @@ -0,0 +1,633 @@ +// 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace isc::data; + +namespace isc { +namespace dhcp { + +// Pointers to various parser objects. +typedef boost::shared_ptr BooleanParserPtr; +typedef boost::shared_ptr StringParserPtr; +typedef boost::shared_ptr 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(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::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(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::max()) { + isc_throw(DhcpConfigError, "invalid option code '" << option_code + << "', it must not exceed '" + << std::numeric_limits::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 binary; + std::vector 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(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 + 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 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 + 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 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 diff --git a/src/lib/dhcpsrv/dhcp_parsers.h b/src/lib/dhcpsrv/dhcp_parsers.h new file mode 100644 index 0000000000..76c6719daf --- /dev/null +++ b/src/lib/dhcpsrv/dhcp_parsers.h @@ -0,0 +1,504 @@ +// 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Storage for option definitions. +typedef OptionSpaceContainer OptionDefStorage; + +/// Collection of containers holding option spaces. Each container within +/// a particular option space holds so-called option descriptors. +typedef OptionSpaceContainer 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 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 +