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