diff options
Diffstat (limited to 'src/lib/database/dbaccess_parser.cc')
-rw-r--r-- | src/lib/database/dbaccess_parser.cc | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/src/lib/database/dbaccess_parser.cc b/src/lib/database/dbaccess_parser.cc new file mode 100644 index 0000000..2048b3b --- /dev/null +++ b/src/lib/database/dbaccess_parser.cc @@ -0,0 +1,293 @@ +// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <database/database_connection.h> +#include <database/db_exceptions.h> +#include <database/dbaccess_parser.h> + +#include <boost/lexical_cast.hpp> + +#include <map> +#include <string> +#include <utility> + +using namespace std; +using namespace isc::data; + +namespace isc { +namespace db { + + +// Factory function to build the parser +DbAccessParser::DbAccessParser() + : values_() { +} + +// Parse the configuration and check that the various keywords are consistent. +void +DbAccessParser::parse(std::string& access_string, + ConstElementPtr database_config) { + + // To cope with incremental updates, the strategy is: + // 1. Take a copy of the stored keyword/value pairs. + // 2. Update the copy with the passed keywords. + // 3. Perform validation checks on the updated keyword/value pairs. + // 4. If all is OK, update the stored keyword/value pairs. + // 5. Save resulting database access string in the Configuration + // Manager. + + // Note only range checks can fail with a database_config from + // a flex/bison parser. + + // 1. Take a copy of the stored keyword/value pairs. + DatabaseConnection::ParameterMap values_copy = values_; + + int64_t lfc_interval = 0; + int64_t connect_timeout = 0; + int64_t read_timeout = 0; + int64_t write_timeout = 0; + int64_t tcp_user_timeout = 0; + int64_t port = 0; + int64_t max_reconnect_tries = 0; + int64_t reconnect_wait_time = 0; + int64_t max_row_errors = 0; + + // 2. Update the copy with the passed keywords. + for (std::pair<std::string, ConstElementPtr> param : database_config->mapValue()) { + try { + if ((param.first == "persist") || + (param.first == "readonly")) { + values_copy[param.first] = (param.second->boolValue() ? + "true" : "false"); + + } else if (param.first == "lfc-interval") { + lfc_interval = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(lfc_interval); + + } else if (param.first == "connect-timeout") { + connect_timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(connect_timeout); + + } else if (param.first == "read-timeout") { + read_timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(read_timeout); + + } else if (param.first == "write-timeout") { + write_timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(write_timeout); + + } else if (param.first == "tcp-user-timeout") { + tcp_user_timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(tcp_user_timeout); + + } else if (param.first == "max-reconnect-tries") { + max_reconnect_tries = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(max_reconnect_tries); + + } else if (param.first == "reconnect-wait-time") { + reconnect_wait_time = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(reconnect_wait_time); + + } else if (param.first == "port") { + port = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(port); + + } else if (param.first == "max-row-errors") { + max_row_errors = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(max_row_errors); + } else { + + // all remaining string parameters + // type + // user + // password + // host + // name + // on-fail + // trust-anchor + // cert-file + // key-file + // cipher-list + values_copy[param.first] = param.second->stringValue(); + } + } catch (const isc::data::TypeError& ex) { + // Append position of the element. + isc_throw(DbConfigError, "invalid value type specified for " + "parameter '" << param.first << "' (" + << param.second->getPosition() << ")"); + } + } + + // 3. Perform validation checks on the updated set of keyword/values. + // + // a. Check if the "type" keyword exists and thrown an exception if not. + auto type_ptr = values_copy.find("type"); + if (type_ptr == values_copy.end()) { + isc_throw(DbConfigError, + "database access parameters must " + "include the keyword 'type' to determine type of database " + "to be accessed (" << database_config->getPosition() << ")"); + } + + // b. Check if the 'type' keyword known and throw an exception if not. + // + // Please note when you add a new database backend you have to add + // the new type here and in server grammars. + string dbtype = type_ptr->second; + if ((dbtype != "memfile") && + (dbtype != "mysql") && + (dbtype != "postgresql")) { + ConstElementPtr value = database_config->get("type"); + isc_throw(DbConfigError, "unknown backend database type: " << dbtype + << " (" << value->getPosition() << ")"); + } + + // c. Check that the lfc-interval is within a reasonable range. + if ((lfc_interval < 0) || + (lfc_interval > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("lfc-interval"); + isc_throw(DbConfigError, "lfc-interval value: " << lfc_interval + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + + // d. Check that the timeouts are within a reasonable range. + if ((connect_timeout < 0) || + (connect_timeout > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("connect-timeout"); + isc_throw(DbConfigError, "connect-timeout value: " << connect_timeout + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + if ((read_timeout < 0) || + (read_timeout > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("read-timeout"); + isc_throw(DbConfigError, "read-timeout value: " << read_timeout + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + if (read_timeout > 0 && (dbtype != "mysql")) { + ConstElementPtr value = database_config->get("read-timeout"); + isc_throw(DbConfigError, "read-timeout value is only supported by the mysql backend" + << " (" << value->getPosition() << ")"); + } + if ((write_timeout < 0) || + (write_timeout > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("write-timeout"); + isc_throw(DbConfigError, "write-timeout value: " << write_timeout + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + if (write_timeout > 0 && (dbtype != "mysql")) { + ConstElementPtr value = database_config->get("write-timeout"); + isc_throw(DbConfigError, "write-timeout value is only supported by the mysql backend" + << " (" << value->getPosition() << ")"); + } + if ((tcp_user_timeout < 0) || + (tcp_user_timeout > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("tcp-user-timeout"); + isc_throw(DbConfigError, "tcp-user-timeout value: " << tcp_user_timeout + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + if (tcp_user_timeout > 0 && (dbtype != "postgresql")) { + ConstElementPtr value = database_config->get("tcp-user-timeout"); + isc_throw(DbConfigError, "tcp-user-timeout value is only supported by the postgresql backend" + << " (" << value->getPosition() << ")"); + } + + // e. Check that the port is within a reasonable range. + if ((port < 0) || + (port > std::numeric_limits<uint16_t>::max())) { + ConstElementPtr value = database_config->get("port"); + isc_throw(DbConfigError, "port value: " << port + << " is out of range, expected value: 0.." + << std::numeric_limits<uint16_t>::max() + << " (" << value->getPosition() << ")"); + } + + // f. Check that the max-row-errors is within a reasonable range. + if ((max_row_errors < 0) || + (max_row_errors > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("max-row-errors"); + isc_throw(DbConfigError, "max-row-errors value: " << max_row_errors + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + + // Check that the max-reconnect-tries is reasonable. + if (max_reconnect_tries < 0) { + ConstElementPtr value = database_config->get("max-reconnect-tries"); + isc_throw(DbConfigError, + "max-reconnect-tries cannot be less than zero: (" + << value->getPosition() << ")"); + } + + // Check that the reconnect-wait-time is reasonable. + if ((reconnect_wait_time < 0) || + (reconnect_wait_time > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("reconnect-wait-time"); + isc_throw(DbConfigError, "reconnect-wait-time " << reconnect_wait_time + << " must be in range 0...MAX_UINT32 (4294967295) " + << "(" << value->getPosition() << ")"); + } + + // 4. If all is OK, update the stored keyword/value pairs. We do this by + // swapping contents - values_copy is destroyed immediately after the + // operation (when the method exits), so we are not interested in its new + // value. + values_.swap(values_copy); + + // 5. Save the database access string in the Configuration Manager. + access_string = getDbAccessString(); +} + +// Create the database access string +std::string +DbAccessParser::getDbAccessString() const { + + // Construct the database access string from all keywords and values in the + // parameter map where the value is not null. + string dbaccess; + for (auto keyval : values_) { + if (!keyval.second.empty()) { + + // Separate keyword/value pair from predecessor (if there is one). + if (!dbaccess.empty()) { + dbaccess += std::string(" "); + } + + // Add the keyword/value pair to the access string. + auto val = keyval.second; + if (val.find_first_of("\t ") != string::npos){ + val = "'" + val + "'"; + } + dbaccess += (keyval.first + std::string("=") + val); + } + } + + return (dbaccess); +} + +} // namespace db +} // namespace isc |