Kea  2.3.7
dbaccess_parser.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2023 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 
10 #include <database/db_exceptions.h>
12 
13 #include <boost/lexical_cast.hpp>
14 
15 #include <map>
16 #include <string>
17 #include <utility>
18 
19 using namespace std;
20 using namespace isc::data;
21 
22 namespace isc {
23 namespace db {
24 
25 
26 // Factory function to build the parser
27 DbAccessParser::DbAccessParser()
28  : values_() {
29 }
30 
31 // Parse the configuration and check that the various keywords are consistent.
32 void
33 DbAccessParser::parse(std::string& access_string,
34  ConstElementPtr database_config) {
35 
36  // To cope with incremental updates, the strategy is:
37  // 1. Take a copy of the stored keyword/value pairs.
38  // 2. Update the copy with the passed keywords.
39  // 3. Perform validation checks on the updated keyword/value pairs.
40  // 4. If all is OK, update the stored keyword/value pairs.
41  // 5. Save resulting database access string in the Configuration
42  // Manager.
43 
44  // Note only range checks can fail with a database_config from
45  // a flex/bison parser.
46 
47  // 1. Take a copy of the stored keyword/value pairs.
48  DatabaseConnection::ParameterMap values_copy = values_;
49 
50  int64_t lfc_interval = 0;
51  int64_t connect_timeout = 0;
52  int64_t read_timeout = 0;
53  int64_t write_timeout = 0;
54  int64_t tcp_user_timeout = 0;
55  int64_t port = 0;
56  int64_t max_reconnect_tries = 0;
57  int64_t reconnect_wait_time = 0;
58  int64_t max_row_errors = 0;
59 
60  // 2. Update the copy with the passed keywords.
61  for (std::pair<std::string, ConstElementPtr> param : database_config->mapValue()) {
62  try {
63  if ((param.first == "persist") ||
64  (param.first == "readonly")) {
65  values_copy[param.first] = (param.second->boolValue() ?
66  "true" : "false");
67 
68  } else if (param.first == "lfc-interval") {
69  lfc_interval = param.second->intValue();
70  values_copy[param.first] =
71  boost::lexical_cast<std::string>(lfc_interval);
72 
73  } else if (param.first == "connect-timeout") {
74  connect_timeout = param.second->intValue();
75  values_copy[param.first] =
76  boost::lexical_cast<std::string>(connect_timeout);
77 
78  } else if (param.first == "read-timeout") {
79  read_timeout = param.second->intValue();
80  values_copy[param.first] =
81  boost::lexical_cast<std::string>(read_timeout);
82 
83  } else if (param.first == "write-timeout") {
84  write_timeout = param.second->intValue();
85  values_copy[param.first] =
86  boost::lexical_cast<std::string>(write_timeout);
87 
88  } else if (param.first == "tcp-user-timeout") {
89  tcp_user_timeout = param.second->intValue();
90  values_copy[param.first] =
91  boost::lexical_cast<std::string>(tcp_user_timeout);
92 
93  } else if (param.first == "max-reconnect-tries") {
94  max_reconnect_tries = param.second->intValue();
95  values_copy[param.first] =
96  boost::lexical_cast<std::string>(max_reconnect_tries);
97 
98  } else if (param.first == "reconnect-wait-time") {
99  reconnect_wait_time = param.second->intValue();
100  values_copy[param.first] =
101  boost::lexical_cast<std::string>(reconnect_wait_time);
102 
103  } else if (param.first == "port") {
104  port = param.second->intValue();
105  values_copy[param.first] =
106  boost::lexical_cast<std::string>(port);
107 
108  } else if (param.first == "max-row-errors") {
109  max_row_errors = param.second->intValue();
110  values_copy[param.first] =
111  boost::lexical_cast<std::string>(max_row_errors);
112  } else {
113 
114  // all remaining string parameters
115  // type
116  // user
117  // password
118  // host
119  // name
120  // on-fail
121  // trust-anchor
122  // cert-file
123  // key-file
124  // cipher-list
125  values_copy[param.first] = param.second->stringValue();
126  }
127  } catch (const isc::data::TypeError& ex) {
128  // Append position of the element.
129  isc_throw(DbConfigError, "invalid value type specified for "
130  "parameter '" << param.first << "' ("
131  << param.second->getPosition() << ")");
132  }
133  }
134 
135  // 3. Perform validation checks on the updated set of keyword/values.
136  //
137  // a. Check if the "type" keyword exists and thrown an exception if not.
138  auto type_ptr = values_copy.find("type");
139  if (type_ptr == values_copy.end()) {
141  "database access parameters must "
142  "include the keyword 'type' to determine type of database "
143  "to be accessed (" << database_config->getPosition() << ")");
144  }
145 
146  // b. Check if the 'type' keyword known and throw an exception if not.
147  //
148  // Please note when you add a new database backend you have to add
149  // the new type here and in server grammars.
150  string dbtype = type_ptr->second;
151  if ((dbtype != "memfile") &&
152  (dbtype != "mysql") &&
153  (dbtype != "postgresql")) {
154  ConstElementPtr value = database_config->get("type");
155  isc_throw(DbConfigError, "unknown backend database type: " << dbtype
156  << " (" << value->getPosition() << ")");
157  }
158 
159  // c. Check that the lfc-interval is within a reasonable range.
160  if ((lfc_interval < 0) ||
161  (lfc_interval > std::numeric_limits<uint32_t>::max())) {
162  ConstElementPtr value = database_config->get("lfc-interval");
163  isc_throw(DbConfigError, "lfc-interval value: " << lfc_interval
164  << " is out of range, expected value: 0.."
165  << std::numeric_limits<uint32_t>::max()
166  << " (" << value->getPosition() << ")");
167  }
168 
169  // d. Check that the timeouts are within a reasonable range.
170  if ((connect_timeout < 0) ||
171  (connect_timeout > std::numeric_limits<uint32_t>::max())) {
172  ConstElementPtr value = database_config->get("connect-timeout");
173  isc_throw(DbConfigError, "connect-timeout value: " << connect_timeout
174  << " is out of range, expected value: 0.."
175  << std::numeric_limits<uint32_t>::max()
176  << " (" << value->getPosition() << ")");
177  }
178  if ((read_timeout < 0) ||
179  (read_timeout > std::numeric_limits<uint32_t>::max())) {
180  ConstElementPtr value = database_config->get("read-timeout");
181  isc_throw(DbConfigError, "read-timeout value: " << read_timeout
182  << " is out of range, expected value: 0.."
183  << std::numeric_limits<uint32_t>::max()
184  << " (" << value->getPosition() << ")");
185  }
186  if (read_timeout > 0 && (dbtype != "mysql")) {
187  ConstElementPtr value = database_config->get("read-timeout");
188  isc_throw(DbConfigError, "read-timeout value is only supported by the mysql backend"
189  << " (" << value->getPosition() << ")");
190  }
191  if ((write_timeout < 0) ||
192  (write_timeout > std::numeric_limits<uint32_t>::max())) {
193  ConstElementPtr value = database_config->get("write-timeout");
194  isc_throw(DbConfigError, "write-timeout value: " << write_timeout
195  << " is out of range, expected value: 0.."
196  << std::numeric_limits<uint32_t>::max()
197  << " (" << value->getPosition() << ")");
198  }
199  if (write_timeout > 0 && (dbtype != "mysql")) {
200  ConstElementPtr value = database_config->get("write-timeout");
201  isc_throw(DbConfigError, "write-timeout value is only supported by the mysql backend"
202  << " (" << value->getPosition() << ")");
203  }
204  if ((tcp_user_timeout < 0) ||
205  (tcp_user_timeout > std::numeric_limits<uint32_t>::max())) {
206  ConstElementPtr value = database_config->get("tcp-user-timeout");
207  isc_throw(DbConfigError, "tcp-user-timeout value: " << tcp_user_timeout
208  << " is out of range, expected value: 0.."
209  << std::numeric_limits<uint32_t>::max()
210  << " (" << value->getPosition() << ")");
211  }
212  if (tcp_user_timeout > 0 && (dbtype != "postgresql")) {
213  ConstElementPtr value = database_config->get("tcp-user-timeout");
214  isc_throw(DbConfigError, "tcp-user-timeout value is only supported by the postgresql backend"
215  << " (" << value->getPosition() << ")");
216  }
217 
218  // e. Check that the port is within a reasonable range.
219  if ((port < 0) ||
220  (port > std::numeric_limits<uint16_t>::max())) {
221  ConstElementPtr value = database_config->get("port");
222  isc_throw(DbConfigError, "port value: " << port
223  << " is out of range, expected value: 0.."
224  << std::numeric_limits<uint16_t>::max()
225  << " (" << value->getPosition() << ")");
226  }
227 
228  // f. Check that the max-row-errors is within a reasonable range.
229  if ((max_row_errors < 0) ||
230  (max_row_errors > std::numeric_limits<uint32_t>::max())) {
231  ConstElementPtr value = database_config->get("max-row-errors");
232  isc_throw(DbConfigError, "max-row-errors value: " << max_row_errors
233  << " is out of range, expected value: 0.."
234  << std::numeric_limits<uint32_t>::max()
235  << " (" << value->getPosition() << ")");
236  }
237 
238  // Check that the max-reconnect-tries is reasonable.
239  if (max_reconnect_tries < 0) {
240  ConstElementPtr value = database_config->get("max-reconnect-tries");
242  "max-reconnect-tries cannot be less than zero: ("
243  << value->getPosition() << ")");
244  }
245 
246  // Check that the reconnect-wait-time is reasonable.
247  if ((reconnect_wait_time < 0) ||
248  (reconnect_wait_time > std::numeric_limits<uint32_t>::max())) {
249  ConstElementPtr value = database_config->get("reconnect-wait-time");
250  isc_throw(DbConfigError, "reconnect-wait-time " << reconnect_wait_time
251  << " must be in range 0...MAX_UINT32 (4294967295) "
252  << "(" << value->getPosition() << ")");
253  }
254 
255  // 4. If all is OK, update the stored keyword/value pairs. We do this by
256  // swapping contents - values_copy is destroyed immediately after the
257  // operation (when the method exits), so we are not interested in its new
258  // value.
259  values_.swap(values_copy);
260 
261  // 5. Save the database access string in the Configuration Manager.
262  access_string = getDbAccessString();
263 }
264 
265 // Create the database access string
266 std::string
268 
269  // Construct the database access string from all keywords and values in the
270  // parameter map where the value is not null.
271  string dbaccess;
272  for (auto keyval : values_) {
273  if (!keyval.second.empty()) {
274 
275  // Separate keyword/value pair from predecessor (if there is one).
276  if (!dbaccess.empty()) {
277  dbaccess += std::string(" ");
278  }
279 
280  // Add the keyword/value pair to the access string.
281  auto val = keyval.second;
282  if (val.find_first_of("\t ") != string::npos){
283  val = "'" + val + "'";
284  }
285  dbaccess += (keyval.first + std::string("=") + val);
286  }
287  }
288 
289  return (dbaccess);
290 }
291 
292 } // namespace db
293 } // namespace isc
A standard Data module exception that is thrown if a function is called for an Element that has a wro...
Definition: data.h:34
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
void parse(std::string &access_string, isc::data::ConstElementPtr database_config)
Parse configuration value.
std::string getDbAccessString() const
Construct database access string.
Error detected in the database configuration.
Definition: db_exceptions.h:66
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
Defines the logger used by the top-level component of kea-lfc.