Kea: legal_log_mgr.cc Source File

| Kea 3.0.0

legal_log_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2020-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
9#include <legal_log_mgr.h>
10#include <legal_log_mgr_factory.h>
11
12#include <database/database_connection.h>
13#include <dhcpsrv/cfgmgr.h>
14#include <eval/eval_context.h>
15#include <util/reconnect_ctl.h>
16#include <util/filesystem.h>
17
18#include <boost/date_time/posix_time/posix_time.hpp>
19
20#include <errno.h>
21#include <iostream>
22#include <sstream>
23#include <time.h>
24
25namespace isc {
26namespace dhcp {
27
28using namespace isc::asiolink;
29using namespace isc::data;
30using namespace isc::db;
31using namespace isc::dhcp;
32using namespace isc::eval;
33using namespace isc::hooks;
34using namespace isc::util;
35using namespace std;
36
37namespace {
38 // Singleton PathChecker to set and hold valid legal log path.
39 file::PathCheckerPtr legal_log_path_checker_;
40};
41
42void
43LegalLogMgr::parseConfig(const ConstElementPtr& parameters, DatabaseConnection::ParameterMap& map) {
44 if (!parameters || !parameters->get("type") ||
45 parameters->get("type")->stringValue() == "logfile") {
46 parseFile(parameters, map);
47 } else if (parameters->get("type")->stringValue() == "syslog") {
48 parseSyslog(parameters, map);
49 } else {
50 parseDatabase(parameters, map);
51 }
52 parseExtraParameters(parameters, map);
53}
54
55void
56LegalLogMgr::parseDatabase(const ConstElementPtr& parameters, DatabaseConnection::ParameterMap& map) {
57 // Should never happen with the code flow at the time of writing, but
58 // let's get this check out of the way.
59 if (!parameters) {
60 isc_throw(BadValue, "no parameters specified for the hook library");
61 }
62
63 DatabaseConnection::ParameterMap db_parameters;
64
65 // Strings
66 for (char const* const& key : {
67 "type", "user", "password", "host", "name", "trust-anchor",
68 "cert-file", "key-file", "cipher-list" }) {
69 ConstElementPtr const value(parameters->get(key));
70 if (value) {
71 db_parameters.emplace(key, value->stringValue());
72 }
73 }
74
75 // uint32_t
76 for (char const* const& key : {
77 "connect-timeout", "reconnect-wait-time", "max-reconnect-tries",
78 "read-timeout", "write-timeout", "tcp-user-timeout"}) {
79 ConstElementPtr const value(parameters->get(key));
80 if (value) {
81 int64_t integer_value(value->intValue());
82 auto const max(numeric_limits<uint32_t>::max());
83 if (integer_value < 0 || max < integer_value) {
84 isc_throw(OutOfRange,
85 key << " value: " << integer_value
86 << " is out of range, expected value: 0.."
87 << max);
88 }
89 db_parameters[key] =
90 boost::lexical_cast<string>(integer_value);
91 }
92 }
93
94 string param_name = "tcp-nodelay";
95 ConstElementPtr param = parameters->get(param_name);
96 if (param) {
97 db_parameters.emplace(param_name,
98 param->boolValue() ? "true" : "false");
99 }
100
101 // Always set "on-fail" to "serve-retry-continue" if not explicitly
102 // configured.
103 string on_fail_action = ReconnectCtl::onFailActionToText(OnFailAction::SERVE_RETRY_CONTINUE);
104 param_name = "on-fail";
105 param = parameters->get(param_name);
106 if (param) {
107 on_fail_action = param->stringValue();
108 ReconnectCtl::onFailActionFromText(on_fail_action);
109 }
110 db_parameters.emplace(param_name, on_fail_action);
111
112 param_name = "retry-on-startup";
113 param = parameters->get(param_name);
114 if (param) {
115 db_parameters.emplace(param_name,
116 param->boolValue() ? "true" : "false");
117 }
118
119 int64_t port = 0;
120 param_name = "port";
121 param = parameters->get(param_name);
122 if (param) {
123 port = param->intValue();
124 if ((port < 0) || (port > numeric_limits<uint16_t>::max())) {
125 isc_throw(OutOfRange, param_name << " value: " << port
126 << " is out of range, expected value: 0.."
127 << numeric_limits<uint16_t>::max());
128 }
129 db_parameters.emplace(param_name,
130 boost::lexical_cast<string>(port));
131 }
132
133 string redacted =
134 DatabaseConnection::redactedAccessString(db_parameters);
135
136 string const db_type(db_parameters["type"]);
137 map = db_parameters;
138}
139
140void
141LegalLogMgr::parseSyslog(const ConstElementPtr& parameters, DatabaseConnection::ParameterMap& map) {
142 // Should never happen with the code flow at the time of writing, but
143 // let's get this check out of the way.
144 if (!parameters) {
145 isc_throw(BadValue, "no parameters specified for the hook library");
146 }
147
148 DatabaseConnection::ParameterMap syslog_parameters;
149
150 // Strings
151 for (char const* const& key : { "type", "pattern", "facility" }) {
152 ConstElementPtr const value(parameters->get(key));
153 if (value) {
154 syslog_parameters.emplace(key, value->stringValue());
155 }
156 }
157
158 map = syslog_parameters;
159}
160
161void
162LegalLogMgr::parseFile(const ConstElementPtr& parameters, DatabaseConnection::ParameterMap& map) {
163 DatabaseConnection::ParameterMap file_parameters;
164 file_parameters["type"] = "logfile";
165
166 if (!parameters) {
167 map = file_parameters;
168 return;
169 }
170
171 // Strings
172 for (char const* const& key : { "path", "base-name", "time-unit", "prerotate", "postrotate" }) {
173 ConstElementPtr const value(parameters->get(key));
174 if (value) {
175 if (key == std::string("path")) {
176 auto valid_path = validatePath(value->stringValue());
177 file_parameters.emplace(key, valid_path);
178 }
179
180 file_parameters.emplace(key, value->stringValue());
181 }
182 }
183
184 // uint32_t
185 for (char const* const& key : { "count" }) {
186 ConstElementPtr const value(parameters->get(key));
187 if (value) {
188 int64_t integer_value(value->intValue());
189 auto const max(numeric_limits<uint32_t>::max());
190 if (integer_value < 0 || max < integer_value) {
191 isc_throw(OutOfRange,
192 key << " value: " << integer_value
193 << " is out of range, expected value: 0.."
194 << max);
195 }
196 file_parameters[key] = boost::lexical_cast<string>(integer_value);
197 }
198 }
199 map = file_parameters;
200}
201
202void
203LegalLogMgr::parseExtraParameters(const ConstElementPtr& parameters, DatabaseConnection::ParameterMap& map) {
204 if (!parameters) {
205 return;
206 }
207
208 // Strings
209 for (char const* const& key : { "request-parser-format", "response-parser-format", "timestamp-format" }) {
210 ConstElementPtr const value(parameters->get(key));
211 if (value && !value->stringValue().empty()) {
212 map.emplace(key, value->stringValue());
213 }
214 }
215}
216
217struct tm
218 LegalLogMgr::currentTimeInfo() const {
219 struct tm time_info;
220 struct timespec timestamp = now();
221 localtime_r(&timestamp.tv_sec, &time_info);
222 return (time_info);
223}
224
225struct timespec
226 LegalLogMgr::now() const {
227 struct timespec now;
228 clock_gettime(CLOCK_REALTIME, &now);
229 return (now);
230}
231
232string
233LegalLogMgr::getNowString() const {
234 // Get a text representation of the current time.
235 return (getNowString(timestamp_format_));
236}
237
238string
239 LegalLogMgr::getNowString(const string& format) const {
240 // Get a text representation of the current time.
241 return (getTimeString(now(), format));
242}
243
244string
245LegalLogMgr::getTimeString(const struct timespec& time, const string& format) {
246 // Get a text representation of the requested time.
247
248 // First a quick and dirty support for fractional seconds: Replace any "%Q"
249 // tokens in the format string with the microsecond count from the timespec,
250 // before handing it off to strftime().
251 string tmp_format = format;
252 for (auto it = tmp_format.begin(); it < tmp_format.end(); ++it) {
253 if (*it == '%' && ((it + 1) < tmp_format.end())) {
254 if (*(it + 1) == 'Q') {
255 // Save the current position.
256 string::size_type pos = it - tmp_format.begin();
257 // Render the microsecond count.
258 ostringstream usec;
259 usec << setw(6) << setfill('0') << (time.tv_nsec / 1000);
260 string microseconds = usec.str();
261 microseconds.insert(3, 1, '.');
262 tmp_format.replace(it, it + 2, microseconds);
263 // Reinitialize the iterator after manipulating the string.
264 it = tmp_format.begin() + pos + microseconds.length() - 1;
265 } else {
266 ++it;
267 }
268 }
269 }
270
271 char buffer[128];
272 struct tm time_info;
273 localtime_r(&time.tv_sec, &time_info);
274
275 if (!strftime(buffer, sizeof(buffer), tmp_format.c_str(), &time_info)) {
276 isc_throw(LegalLogMgrError,
277 "strftime returned 0. Maybe the timestamp format '"
278 << tmp_format
279 << "' result is too long, maximum length allowed: "
280 << sizeof(buffer));
281 }
282 return (string(buffer));
283}
284
285string
286LegalLogMgr::genDurationString(const uint32_t secs) {
287 // Because Kea handles lease lifetimes as uint32_t and supports
288 // a value of 0xFFFFFFFF (infinite lifetime), we don't use things like
289 // boost:posix_time::time_duration as they work on longs. Therefore
290 // we'll figure it out ourselves. Besides, the math ain't that hard.
291 if (secs == 0xffffffff) {
292 return ("infinite duration");
293 }
294
295 uint32_t seconds = secs % 60;
296 uint32_t remainder = secs / 60;
297 uint32_t minutes = remainder % 60;
298 remainder /= 60;
299 uint32_t hours = remainder % 24;
300 uint32_t days = remainder / 24;
301
302 ostringstream os;
303 // Only spit out days if we have em.
304 if (days) {
305 os << days << " days ";
306 }
307
308 os << hours << " hrs "
309 << minutes << " mins "
310 << seconds << " secs";
311
312 return (os.str());
313}
314
315string
316LegalLogMgr::vectorHexDump(const vector<uint8_t>& bytes,
317 const string& delimiter) {
318 stringstream tmp;
319 tmp << hex;
320 bool delim = false;
321 for (auto const& it : bytes) {
322 if (delim) {
323 tmp << delimiter;
324 }
325 tmp << setw(2) << setfill('0') << static_cast<unsigned int>(it);
326 delim = true;
327 }
328 return (tmp.str());
329}
330
331string
332LegalLogMgr::vectorDump(const vector<uint8_t>& bytes) {
333 if (bytes.empty()) {
334 return (string());
335 }
336 return (string(bytes.cbegin(), bytes.cend()));
337}
338
339void
340LegalLogMgr::setRequestFormatExpression(const string& extended_format) {
341 Option::Universe universe;
342 if (CfgMgr::instance().getFamily() == AF_INET) {
343 universe = Option::V4;
344 } else {
345 universe = Option::V6;
346 }
347 EvalContext eval_ctx(universe);
348 eval_ctx.parseString(extended_format, EvalContext::PARSER_STRING);
349 request_expression_.reset(new Expression(eval_ctx.expression_));
350}
351
352void
353LegalLogMgr::setResponseFormatExpression(const string& extended_format) {
354 Option::Universe universe;
355 if (CfgMgr::instance().getFamily() == AF_INET) {
356 universe = Option::V4;
357 } else {
358 universe = Option::V6;
359 }
360 EvalContext eval_ctx(universe);
361 eval_ctx.parseString(extended_format, EvalContext::PARSER_STRING);
362 response_expression_.reset(new Expression(eval_ctx.expression_));
363}
364
365void
366LegalLogMgr::setTimestampFormat(const string& timestamp_format) {
367 timestamp_format_ = timestamp_format;
368}
369
370const string
371actionToVerb(Action action) {
372 switch (action) {
373 case Action::ASSIGN:
374 return ("assigned");
375 case Action::RELEASE:
376 return ("released");
377 default:
378 return ("unknown-action");
379 }
380}
381
382std::string
383LegalLogMgr::getLogPath(bool reset /* = false */, const std::string explicit_path /* = "" */) {
384 if (!legal_log_path_checker_ || reset) {
385 legal_log_path_checker_.reset(new file::PathChecker(LEGAL_LOG_DIR, "KEA_LEGAL_LOG_DIR"));
386 if (!explicit_path.empty()) {
387 legal_log_path_checker_->getPath(true, explicit_path);
388 }
389 }
390
391 return (legal_log_path_checker_->getPath());
392}
393
394std::string
395LegalLogMgr::validatePath(const std::string logpath) {
396 if (!legal_log_path_checker_) {
397 getLogPath();
398 }
399
400 return (legal_log_path_checker_->validateDirectory(logpath));
401}
402
403} // namespace dhcp
404} // namespace isc
cfgmgr.h
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition exceptions/exceptions.h:132
isc::OutOfRange
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
Definition exceptions/exceptions.h:115
isc::db::DatabaseConnection::redactedAccessString
static std::string redactedAccessString(const ParameterMap &parameters)
Redact database access string.
Definition database_connection.cc:108
isc::db::DatabaseConnection::ParameterMap
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Definition database_connection.h:137
isc::dhcp::CfgMgr::instance
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:29
isc::dhcp::LegalLogMgrError
Thrown if a LegalLogMgr encounters an error.
Definition legal_log_mgr.h:26
isc::dhcp::LegalLogMgr::currentTimeInfo
virtual struct tm currentTimeInfo() const
Returns the current local date and time.
Definition legal_log_mgr.cc:218
isc::dhcp::LegalLogMgr::setRequestFormatExpression
void setRequestFormatExpression(const std::string &extended_format)
Sets request extended format expression for custom logging.
Definition legal_log_mgr.cc:340
isc::dhcp::LegalLogMgr::parseSyslog
static void parseSyslog(const isc::data::ConstElementPtr &parameters, isc::db::DatabaseConnection::ParameterMap &map)
Parse syslog specification.
Definition legal_log_mgr.cc:141
isc::dhcp::LegalLogMgr::parseFile
static void parseFile(const isc::data::ConstElementPtr &parameters, isc::db::DatabaseConnection::ParameterMap &map)
Parse file specification.
Definition legal_log_mgr.cc:162
isc::dhcp::LegalLogMgr::vectorHexDump
static std::string vectorHexDump(const std::vector< uint8_t > &bytes, const std::string &delimiter=":")
Creates a string of hex digit pairs from a vector of bytes.
Definition legal_log_mgr.cc:316
isc::dhcp::LegalLogMgr::parseDatabase
static void parseDatabase(const isc::data::ConstElementPtr &parameters, isc::db::DatabaseConnection::ParameterMap &map)
Parse database specification.
Definition legal_log_mgr.cc:56
isc::dhcp::LegalLogMgr::setResponseFormatExpression
void setResponseFormatExpression(const std::string &extended_format)
Sets response extended format expression for custom logging.
Definition legal_log_mgr.cc:353
isc::dhcp::LegalLogMgr::genDurationString
static std::string genDurationString(const uint32_t secs)
Translates seconds into a text string of days, hours, minutes and seconds.
Definition legal_log_mgr.cc:286
isc::dhcp::LegalLogMgr::validatePath
static std::string validatePath(const std::string logpath)
Validates a log path against the supported path for legal log files.
Definition legal_log_mgr.cc:395
isc::dhcp::LegalLogMgr::now
virtual struct timespec now() const
Returns the current system time.
Definition legal_log_mgr.cc:226
isc::dhcp::LegalLogMgr::getTimeString
static std::string getTimeString(const struct timespec &time, const std::string &format)
Returns a time as string.
Definition legal_log_mgr.cc:245
isc::dhcp::LegalLogMgr::getLogPath
static std::string getLogPath(bool reset=false, const std::string explicit_path="")
Fetches the supported legal log file path.
Definition legal_log_mgr.cc:383
isc::dhcp::LegalLogMgr::vectorDump
static std::string vectorDump(const std::vector< uint8_t > &bytes)
Creates a string from a vector of printable bytes.
Definition legal_log_mgr.cc:332
isc::dhcp::LegalLogMgr::LegalLogMgr
LegalLogMgr(const isc::db::DatabaseConnection::ParameterMap parameters)
Constructor.
Definition legal_log_mgr.h:45
isc::dhcp::LegalLogMgr::parseConfig
static void parseConfig(const isc::data::ConstElementPtr &parameters, isc::db::DatabaseConnection::ParameterMap &map)
Parse database specification.
Definition legal_log_mgr.cc:43
isc::dhcp::LegalLogMgr::setTimestampFormat
void setTimestampFormat(const std::string &timestamp_format)
Sets the timestamp format used for logging.
Definition legal_log_mgr.cc:366
isc::dhcp::LegalLogMgr::parseExtraParameters
static void parseExtraParameters(const isc::data::ConstElementPtr &parameters, isc::db::DatabaseConnection::ParameterMap &map)
Parse extra parameters which are not related to backend connection.
Definition legal_log_mgr.cc:203
isc::dhcp::LegalLogMgr::getNowString
virtual std::string getNowString() const
Returns the current date and time as string.
Definition legal_log_mgr.cc:233
isc::dhcp::Option::Universe
Universe
defines option universe DHCPv4 or DHCPv6
Definition option.h:90
isc::dhcp::Option::V6
@ V6
Definition option.h:90
isc::dhcp::Option::V4
@ V4
Definition option.h:90
isc::eval::EvalContext
Evaluation context, an interface to the expression evaluation.
Definition eval_context.h:35
isc::eval::EvalContext::parseString
bool parseString(const std::string &str, ParserType type=PARSER_BOOL)
Run the parser on the string specified.
Definition eval_context.cc:37
isc::eval::EvalContext::PARSER_STRING
@ PARSER_STRING
expression is expected to evaluate to string
Definition eval_context.h:41
isc::eval::EvalContext::expression_
isc::dhcp::Expression expression_
Parsed expression (output tokens are stored here)
Definition eval_context.h:68
isc::util::ReconnectCtl::onFailActionToText
static std::string onFailActionToText(OnFailAction action)
Convert action to string.
Definition reconnect_ctl.cc:15
isc::util::ReconnectCtl::onFailActionFromText
static OnFailAction onFailActionFromText(const std::string &text)
Convert string to action.
Definition reconnect_ctl.cc:28
isc::util::file::PathChecker
Embodies a supported path against which file paths can be validated.
Definition filesystem.h:179
database_connection.h
eval_context.h
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
filesystem.h
legal_log_mgr.h
Defines the abstract class for backend stores.
legal_log_mgr_factory.h
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
isc::dhcp
Definition dhcp4/client_handler.cc:20
isc::dhcp::actionToVerb
const string actionToVerb(Action action)
Translates an Action into its corresponding verb.
Definition legal_log_mgr.cc:371
isc::dhcp::Expression
std::vector< TokenPtr > Expression
This is a structure that holds an expression converted to RPN.
Definition token.h:29
isc::dhcp::Action
Action
Describe what kind of event is being logged.
Definition legal_log_mgr.h:390
isc::dhcp::Action::RELEASE
@ RELEASE
Definition legal_log_mgr.h:390
isc::dhcp::Action::ASSIGN
@ ASSIGN
Definition legal_log_mgr.h:390
isc::util::file::PathCheckerPtr
boost::shared_ptr< PathChecker > PathCheckerPtr
Defines a pointer to a PathChecker.
Definition filesystem.h:298
isc::util::OnFailAction::SERVE_RETRY_CONTINUE
@ SERVE_RETRY_CONTINUE
Definition reconnect_ctl.h:20
isc
Defines the logger used by the top-level component of kea-lfc.
Definition agent_parser.cc:148
reconnect_ctl.h