Kea  2.1.7-git
database_connection.cc
Go to the documentation of this file.
1 // Copyright (C) 2015-2022 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>
11 #include <database/db_exceptions.h>
12 #include <database/db_log.h>
13 #include <database/db_messages.h>
14 #include <exceptions/exceptions.h>
15 #include <util/strutil.h>
16 
17 #include <boost/algorithm/string.hpp>
18 #include <boost/foreach.hpp>
19 #include <vector>
20 
21 using namespace isc::util;
22 using namespace std;
23 
24 namespace isc {
25 namespace db {
26 
27 const time_t DatabaseConnection::MAX_DB_TIME = 2147483647;
28 
29 std::string
30 DatabaseConnection::getParameter(const std::string& name) const {
31  ParameterMap::const_iterator param = parameters_.find(name);
32  if (param == parameters_.end()) {
33  isc_throw(BadValue, "Parameter " << name << " not found");
34  }
35  return (param->second);
36 }
37 
39 DatabaseConnection::parse(const std::string& dbaccess) {
41  std::string dba = dbaccess;
42 
43  if (!dba.empty()) {
44  try {
45  vector<string> tokens;
46 
47  // Handle the special case of a password which is enclosed in apostrophes.
48  // Such password may include whitespace.
49  std::string password_prefix = "password='";
50  auto password_pos = dba.find(password_prefix);
51  if (password_pos != string::npos) {
52  // Password starts with apostrophe, so let's find ending apostrophe.
53  auto password_end_pos = dba.find('\'', password_pos + password_prefix.length());
54  if (password_end_pos == string::npos) {
55  // No ending apostrophe. This is wrong.
56  isc_throw(InvalidParameter, "Apostrophe (') expected at the end of password");
57  }
58  // Extract the password value. It starts after the password=' prefix and ends
59  // at the position of ending apostrophe.
60  auto password = dba.substr(password_pos + password_prefix.length(),
61  password_end_pos - password_pos - password_prefix.length());
62  mapped_tokens.insert(make_pair("password", password));
63 
64  // We need to erase the password from the access string because the generic
65  // algorithm parsing other parameters requires that there are no whitespaces
66  // within the parameter values.
67  dba.erase(password_pos, password_prefix.length() + password.length() + 2);
68  // Leading or trailing whitespace may remain after the password removal.
69  dba = util::str::trim(dba);
70  // If the password was the only parameter in the access string, there is
71  // nothing more to do.
72  if (dba.empty()) {
73  return (mapped_tokens);
74  }
75  }
76 
77  // We need to pass a string to is_any_of, not just char*. Otherwise
78  // there are cryptic warnings on Debian6 running g++ 4.4 in
79  // /usr/include/c++/4.4/bits/stl_algo.h:2178 "array subscript is above
80  // array bounds"
81  boost::split(tokens, dba, boost::is_any_of(string("\t ")));
82  BOOST_FOREACH(std::string token, tokens) {
83  size_t pos = token.find("=");
84  if (pos != string::npos) {
85  string name = token.substr(0, pos);
86  string value = token.substr(pos + 1);
87  mapped_tokens.insert(make_pair(name, value));
88  } else {
89  isc_throw(InvalidParameter, "Cannot parse " << token
90  << ", expected format is name=value");
91  }
92  }
93  } catch (const std::exception& ex) {
94  // We'd obscure the password if we could parse the access string.
96  throw;
97  }
98  }
99 
100  return (mapped_tokens);
101 }
102 
103 std::string
104 DatabaseConnection::redactedAccessString(const ParameterMap& parameters) {
105  // Reconstruct the access string: start of with an empty string, then
106  // work through all the parameters in the original string and add them.
107  std::string access;
108  for (DatabaseConnection::ParameterMap::const_iterator i = parameters.begin();
109  i != parameters.end(); ++i) {
110 
111  // Separate second and subsequent tokens are preceded by a space.
112  if (!access.empty()) {
113  access += " ";
114  }
115 
116  // Append name of parameter...
117  access += i->first;
118  access += "=";
119 
120  // ... and the value, except in the case of the password, where a
121  // redacted value is appended.
122  if (i->first == std::string("password")) {
123  access += "*****";
124  } else {
125  access += i->second;
126  }
127  }
128 
129  return (access);
130 }
131 
132 bool
133 DatabaseConnection::configuredReadOnly() const {
134  std::string readonly_value = "false";
135  try {
136  readonly_value = getParameter("readonly");
137  boost::algorithm::to_lower(readonly_value);
138  } catch (...) {
139  // Parameter "readonly" hasn't been specified so we simply use
140  // the default value of "false".
141  }
142 
143  if ((readonly_value != "false") && (readonly_value != "true")) {
144  isc_throw(DbInvalidReadOnly, "invalid value '" << readonly_value
145  << "' specified for boolean parameter 'readonly'");
146  }
147 
148  return (readonly_value == "true");
149 }
150 
151 void
152 DatabaseConnection::makeReconnectCtl(const std::string& timer_name) {
153  string type = "unknown";
154  unsigned int retries = 0;
155  unsigned int interval = 0;
156 
157  // Assumes that parsing ensures only valid values are present
158  try {
159  type = getParameter("type");
160  } catch (...) {
161  // Wasn't specified so we'll use default of "unknown".
162  }
163 
164  std::string parm_str;
165  try {
166  parm_str = getParameter("max-reconnect-tries");
167  retries = boost::lexical_cast<unsigned int>(parm_str);
168  } catch (...) {
169  // Wasn't specified so we'll use default of 0;
170  }
171 
172  try {
173  parm_str = getParameter("reconnect-wait-time");
174  interval = boost::lexical_cast<unsigned int>(parm_str);
175  } catch (...) {
176  // Wasn't specified so we'll use default of 0;
177  }
178 
180  try {
181  parm_str = getParameter("on-fail");
182  action = ReconnectCtl::onFailActionFromText(parm_str);
183  } catch (...) {
184  // Wasn't specified so we'll use default of "stop-retry-exit";
185  }
186 
187  reconnect_ctl_ = boost::make_shared<ReconnectCtl>(type, timer_name, retries,
188  interval, action);
189 }
190 
191 bool
192 DatabaseConnection::invokeDbLostCallback(const ReconnectCtlPtr& db_reconnect_ctl) {
193  if (DatabaseConnection::db_lost_callback_) {
194  return (DatabaseConnection::db_lost_callback_(db_reconnect_ctl));
195  }
196 
197  return (false);
198 }
199 
200 bool
201 DatabaseConnection::invokeDbRecoveredCallback(const ReconnectCtlPtr& db_reconnect_ctl) {
202  if (DatabaseConnection::db_recovered_callback_) {
203  return (DatabaseConnection::db_recovered_callback_(db_reconnect_ctl));
204  }
205 
206  return (false);
207 }
208 
209 bool
210 DatabaseConnection::invokeDbFailedCallback(const ReconnectCtlPtr& db_reconnect_ctl) {
211  if (DatabaseConnection::db_failed_callback_) {
212  return (DatabaseConnection::db_failed_callback_(db_reconnect_ctl));
213  }
214 
215  return (false);
216 }
217 
219 DatabaseConnection::toElement(const ParameterMap& params) {
221 
222  for (auto param: params) {
223  std::string keyword = param.first;
224  std::string value = param.second;
225 
226  if ((keyword == "lfc-interval") ||
227  (keyword == "connect-timeout") ||
228  (keyword == "reconnect-wait-time") ||
229  (keyword == "max-reconnect-tries") ||
230  (keyword == "port") ||
231  (keyword == "max-row-errors")) {
232  // integer parameters
233  int64_t int_value;
234  try {
235  int_value = boost::lexical_cast<int64_t>(value);
236  result->set(keyword, isc::data::Element::create(int_value));
237  } catch (...) {
239  .arg("integer").arg(keyword).arg(value);
240  }
241  } else if ((keyword == "persist") ||
242  (keyword == "readonly")) {
243  if (value == "true") {
244  result->set(keyword, isc::data::Element::create(true));
245  } else if (value == "false") {
246  result->set(keyword, isc::data::Element::create(false));
247  } else {
249  .arg("boolean").arg(keyword).arg(value);
250  }
251  } else if ((keyword == "type") ||
252  (keyword == "user") ||
253  (keyword == "password") ||
254  (keyword == "host") ||
255  (keyword == "name") ||
256  (keyword == "on-fail") ||
257  (keyword == "trust-anchor") ||
258  (keyword == "cert-file") ||
259  (keyword == "key-file") ||
260  (keyword == "cipher-list")) {
261  result->set(keyword, isc::data::Element::create(value));
262  } else {
264  .arg("unknown").arg(keyword).arg(value);
265  }
266  }
267 
268  return (result);
269 }
270 
272 DatabaseConnection::toElementDbAccessString(const std::string& dbaccess) {
273  ParameterMap params = parse(dbaccess);
274  return (toElement(params));
275 }
276 
277 DbCallback DatabaseConnection::db_lost_callback_ = 0;
278 DbCallback DatabaseConnection::db_recovered_callback_ = 0;
279 DbCallback DatabaseConnection::db_failed_callback_ = 0;
280 
281 } // namespace db
282 } // namespace isc
We want to reuse the database backend connection and exchange code for other uses, in particular for hook libraries.
std::function< bool(util::ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
const isc::log::MessageID DATABASE_TO_JSON_ERROR
Definition: db_messages.h:23
#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:24
STL namespace.
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:286
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: edns.h:19
vector< string > tokens(const std::string &text, const std::string &delim, bool escape)
Split String into Tokens.
Definition: strutil.cc:77
OnFailAction
Type of action to take on connection loss.
Definition: reconnect_ctl.h:17
Defines the logger used by the top-level component of kea-lfc.
DB_LOG & arg(T first, Args... args)
Pass parameters to replace logger placeholders.
Definition: db_log.h:141
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:241
Invalid &#39;readonly&#39; value specification.
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
isc::log::Logger database_logger("database")
Common database library logger.
Definition: db_log.h:46
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
static OnFailAction onFailActionFromText(const std::string &text)
Convert string to action.