Kea 3.3.1
database_connection.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2026 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <cc/cfg_to_element.h>
13#include <database/db_log.h>
16#include <util/str.h>
17
18#include <boost/algorithm/string.hpp>
19#include <vector>
20
21using namespace isc::asiolink;
22using namespace isc::data;
23using namespace isc::util;
24using namespace std;
25
26namespace isc {
27namespace db {
28
29const time_t DatabaseConnection::MAX_DB_TIME = 2147483647;
30
31std::string
32DatabaseConnection::getParameter(const std::string& name) const {
33 ParameterMap::const_iterator param = parameters_.find(name);
34 if (param == parameters_.end()) {
35 isc_throw(BadValue, "Parameter " << name << " not found");
36 }
37 return (param->second);
38}
39
41DatabaseConnection::parse(const std::string& dbaccess) {
43 std::string dba = dbaccess;
44
45 try {
46 // Handle the special case of a password which is enclosed in apostrophes.
47 // Such password may include whitespace.
48 std::string password_prefix = "password='";
49 auto password_pos = dba.find(password_prefix);
50 if (password_pos != string::npos) {
51 // Password starts with apostrophe, so let's find ending apostrophe.
52 auto password_end_pos = dba.find('\'', password_pos + password_prefix.length());
53 if (password_end_pos == string::npos) {
54 // No ending apostrophe. This is wrong.
55 isc_throw(InvalidParameter, "Apostrophe (') expected at the end of password");
56 }
57 // Extract the password value. It starts after the password=' prefix and ends
58 // at the position of ending apostrophe.
59 auto password = dba.substr(password_pos + password_prefix.length(),
60 password_end_pos - password_pos - password_prefix.length());
61 // Refuse default passwords.
63 mapped_tokens.insert(make_pair("password", password));
64
65 // We need to erase the password from the access string because the generic
66 // algorithm parsing other parameters requires that there are no whitespaces
67 // within the parameter values.
68 dba.erase(password_pos, password_prefix.length() + password.length() + 2);
69 // Leading or trailing whitespace may remain after the password removal.
70 dba = util::str::trim(dba);
71 }
72 splitDbAccessString(dba, mapped_tokens);
73 } catch (const std::exception& ex) {
74 if (mapped_tokens.empty()) {
75 // The split likely didn't get to happen.
76 // Let's try again disregarding any exception so we don't shadow the current exception.
77 try {
78 splitDbAccessString(dba, mapped_tokens);
79 } catch (const std::exception&) {
80 }
81 }
83 throw;
84 }
85
86 return (mapped_tokens);
87}
88
89std::string
91 // Reconstruct the access string: start of with an empty string, then
92 // work through all the parameters in the original string and add them.
93 std::string access;
94 for (auto const& i : parameters) {
95
96 // Separate second and subsequent tokens are preceded by a space.
97 if (!access.empty()) {
98 access += " ";
99 }
100
101 // Append name of parameter...
102 access += i.first;
103 access += "=";
104
105 // ... and the value, except in the case of the password, where a
106 // redacted value is appended.
107 if (i.first == std::string("password")) {
108 access += "*****";
109 } else {
110 access += i.second;
111 }
112 }
113
114 return (access);
115}
116
117bool
119 std::string readonly_value = "false";
120 try {
121 readonly_value = getParameter("readonly");
122 boost::algorithm::to_lower(readonly_value);
123 } catch (...) {
124 // Parameter "readonly" hasn't been specified so we simply use
125 // the default value of "false".
126 }
127
128 if ((readonly_value != "false") && (readonly_value != "true")) {
129 isc_throw(DbInvalidReadOnly, "invalid value '" << readonly_value
130 << "' specified for boolean parameter 'readonly'");
131 }
132
133 return (readonly_value == "true");
134}
135
136void
137DatabaseConnection::makeReconnectCtl(const std::string& timer_name, unsigned int id) {
138 string type = "unknown";
139 unsigned int retries = 0;
140 unsigned int interval = 0;
141
142 // Assumes that parsing ensures only valid values are present
143 try {
144 type = getParameter("type");
145 } catch (...) {
146 // Wasn't specified so we'll use default of "unknown".
147 }
148
149 std::string parm_str;
150 try {
151 parm_str = getParameter("max-reconnect-tries");
152 retries = boost::lexical_cast<unsigned int>(parm_str);
153 } catch (...) {
154 // Wasn't specified so we'll use default of 0;
155 }
156
157 try {
158 parm_str = getParameter("reconnect-wait-time");
159 interval = boost::lexical_cast<unsigned int>(parm_str);
160 } catch (...) {
161 // Wasn't specified so we'll use default of 0;
162 }
163
165 try {
166 parm_str = getParameter("on-fail");
167 action = ReconnectCtl::onFailActionFromText(parm_str);
168 } catch (...) {
169 // Wasn't specified so we'll use default of "stop-retry-exit";
170 }
171
172 reconnect_ctl_ = boost::make_shared<ReconnectCtl>(type, timer_name, retries,
173 interval, action, id);
174}
175
176bool
179 return (DatabaseConnection::db_lost_callback_(db_reconnect_ctl));
180 }
181
182 return (false);
183}
184
185bool
188 return (DatabaseConnection::db_recovered_callback_(db_reconnect_ctl));
189 }
190
191 return (false);
192}
193
194bool
197 return (DatabaseConnection::db_failed_callback_(db_reconnect_ctl));
198 }
199
200 return (false);
201}
202
206
207 for (auto const& param : params) {
208 std::string keyword = param.first;
209 std::string value = param.second;
210
211 if ((keyword == "lfc-interval") ||
212 (keyword == "connect-timeout") ||
213 (keyword == "read-timeout") ||
214 (keyword == "write-timeout") ||
215 (keyword == "tcp-user-timeout") ||
216 (keyword == "reconnect-wait-time") ||
217 (keyword == "max-reconnect-tries") ||
218 (keyword == "port") ||
219 (keyword == "max-row-errors")) {
220 // integer parameters
221 int64_t int_value;
222 try {
223 int_value = boost::lexical_cast<int64_t>(value);
224 result->set(keyword, isc::data::Element::create(int_value));
225 } catch (...) {
227 .arg(keyword).arg(value);
228 }
229 } else if ((keyword == "persist") ||
230 (keyword == "readonly") ||
231 (keyword == "retry-on-startup")) {
232 if (value == "true") {
233 result->set(keyword, isc::data::Element::create(true));
234 } else if (value == "false") {
235 result->set(keyword, isc::data::Element::create(false));
236 } else {
238 .arg(keyword).arg(value);
239 }
240 } else if ((keyword == "type") ||
241 (keyword == "user") ||
242 (keyword == "password") ||
243 (keyword == "password-file") ||
244 (keyword == "host") ||
245 (keyword == "name") ||
246 (keyword == "on-fail") ||
247 (keyword == "trust-anchor") ||
248 (keyword == "cert-file") ||
249 (keyword == "key-file") ||
250 (keyword == "ssl-mode") ||
251 (keyword == "cipher-list")) {
252 result->set(keyword, isc::data::Element::create(value));
253 } else {
255 .arg(keyword).arg(value);
256 }
257 }
258
259 return (result);
260}
261
264 ParameterMap params = parse(dbaccess);
265 return (toElement(params));
266}
267
268void
269DatabaseConnection::splitDbAccessString(const string& dbaccess, ParameterMap& mapped_tokens) {
270 if (dbaccess.empty()) {
271 return;
272 }
273 vector<string> tokens;
274 string bad_token;
275 bool error(false);
276
277 // We need to pass a string to is_any_of, not just char*. Otherwise
278 // there are cryptic warnings on Debian6 running g++ 4.4 in
279 // /usr/include/c++/4.4/bits/stl_algo.h:2178 "array subscript is above
280 // array bounds"
281 boost::split(tokens, dbaccess, boost::is_any_of(string("\t ")));
282 for (auto const& token : tokens) {
283 size_t pos = token.find("=");
284 if (pos == string::npos) {
285 if (!error) {
286 // Keep first only.
287 bad_token = token;
288 error = true;
289 }
290 continue;
291 }
292 string name = token.substr(0, pos);
293 string value = token.substr(pos + 1);
294 mapped_tokens.insert(make_pair(name, value));
295 }
296 if (error) {
298 "Cannot parse " << bad_token << ", expected format is name=value");
299 }
300}
301
305bool DatabaseConnection::retry_ = false;
306IOServicePtr DatabaseConnection::io_service_ = IOServicePtr();
307
309
310} // namespace db
311} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
static ElementPtr create(const Position &pos=ZERO_POSITION())
Create a NullElement.
Definition data.cc:300
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:355
bool configuredReadOnly() const
Convenience method checking if database should be opened with read only access.
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
static bool invokeDbLostCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's lost connectivity callback.
virtual void makeReconnectCtl(const std::string &timer_name, unsigned int id)
Instantiates a ReconnectCtl based on the connection's reconnect parameters.
static std::string redactedAccessString(const ParameterMap &parameters)
Redact database access string.
static bool invokeDbFailedCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restore failed connectivity callback.
static isc::data::ElementPtr toElement(const ParameterMap &params)
Unparse a parameter map.
static isc::data::ElementPtr toElementDbAccessString(const std::string &dbaccess)
Unparse an access string.
static DbCallback db_recovered_callback_
Optional callback function to invoke if an opened connection recovery succeeded.
static ParameterMap parse(const std::string &dbaccess)
Parse database access string.
static bool test_mode_
Test mode flag (default false).
static bool retry_
Flag which indicates if the database connection should be retried on fail.
static bool invokeDbRecoveredCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restored connectivity callback.
static void splitDbAccessString(const std::string &dbaccess, ParameterMap &mapped_tokens)
Split a database access string into a parameter map.
static DbCallback db_failed_callback_
Optional callback function to invoke if an opened connection recovery failed.
static DbCallback db_lost_callback_
Optional callback function to invoke if an opened connection is lost.
static const time_t MAX_DB_TIME
Defines maximum value for time that can be reliably stored.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Invalid 'readonly' value specification.
static OnFailAction onFailActionFromText(const std::string &text)
Convert string to action.
We want to reuse the database backend connection and exchange code for other uses,...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
boost::shared_ptr< Element > ElementPtr
Definition data.h:29
isc::log::Logger database_logger("database")
Common database library logger.
Definition db_log.h:46
@ DB_INVALID_ACCESS
Definition db_log.h:52
const isc::log::MessageID DATABASE_TO_JSON_UNKNOWN_TYPE_ERROR
Definition db_messages.h:37
@ error
Definition db_log.h:126
const isc::log::MessageID DATABASE_TO_JSON_INTEGER_ERROR
Definition db_messages.h:36
std::function< bool(util::ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
const isc::log::MessageID DATABASE_TO_JSON_BOOLEAN_ERROR
Definition db_messages.h:35
string trim(const string &input)
Trim leading and trailing spaces.
Definition str.cc:32
OnFailAction
Type of action to take on connection loss.
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
Defines the logger used by the top-level component of kea-lfc.
static void check(const std::string &value)
Check if the value is a default credential.
DB_LOG & arg(T first, Args... args)
Pass parameters to replace logger placeholders.
Definition db_log.h:152