1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344 | // Copyright (C) 2024-2025 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <asiolink/asio_wrapper.h>
#include <config/command_mgr.h>
#include <config/config_log.h>
#include <config/http_command_mgr.h>
#include <config/http_command_response_creator_factory.h>
#include <config/http_command_response_creator.h>
#include <config/timeouts.h>
#include <sstream>
#include <vector>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::config;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::http;
using namespace std;
namespace isc {
namespace config {
/// @brief Implementation of the @c HttpCommandMgr.
class HttpCommandMgrImpl {
public:
/// @brief Constructor.
HttpCommandMgrImpl()
: io_service_(), timeout_(TIMEOUT_AGENT_RECEIVE_COMMAND),
idle_timeout_(TIMEOUT_AGENT_IDLE_CONNECTION_TIMEOUT),
use_external_(true) {
}
/// @brief Open http control sockets using configuration.
///
/// @param config Configuration information for the http control sockets.
void openCommandSockets(const isc::data::ConstElementPtr config);
/// @brief Open http control socket using configuration.
///
/// Creates http/https listener, or reuses the existing one reapplying
/// changes.
///
/// @param config Configuration information for the http control socket.
void openCommandSocket(const isc::data::ConstElementPtr config);
/// @brief Close control socket.
///
/// @param info Configuration information for the http control socket.
/// @param remove When true remove the listeners immediately.
void closeCommandSocket(HttpSocketInfoPtr info, bool remove);
/// @brief Close control socket.
///
/// @param remove When true remove the listeners immediately.
void closeCommandSockets(bool remove = true);
/// @brief Returns a const pointer to the HTTP listener.
///
/// @param info Configuration information for the http control socket.
///
/// @return Const pointer to the currently used listener or null pointer if
/// there is no listener.
ConstHttpListenerPtr getHttpListener(HttpSocketInfoPtr info) const;
/// @brief Pointer to the IO service.
IOServicePtr io_service_;
/// @brief Connection timeout.
long timeout_;
/// @brief Idle connection timeout.
long idle_timeout_;
/// @brief The HTTP/HTTPS socket data (configuration, listener, etc.).
std::map<std::pair<IOAddress, uint16_t>, HttpSocketInfoPtr> sockets_;
/// @brief Use external sockets flag.
bool use_external_;
};
void
HttpCommandMgrImpl::openCommandSockets(const isc::data::ConstElementPtr config) {
if (!config) {
isc_throw(BadSocketInfo, "Missing config parameters, can't create socket.");
}
if (config->getType() != Element::list) {
isc_throw(DhcpConfigError, "expected list type ("
<< config->getPosition() << ")");
}
for (auto const& socket : config->listValue()) {
openCommandSocket(socket);
}
auto copy = sockets_;
for (auto const& data : copy) {
if (data.second->usable_) {
// If the connection can be used (just created) or reused, keep it
// in the list and clear the flag. It will be marked again on next
// configuration event if needed.
data.second->usable_ = false;
} else {
// If the connection can not be reused, stop it and remove it from the list.
closeCommandSocket(data.second, true);
}
}
}
void
HttpCommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr config) {
if (!config) {
isc_throw(BadSocketInfo, "Missing config parameters, can't create socket.");
}
HttpCommandConfigPtr cmd_config(new HttpCommandConfig(config));
IOAddress server_address = cmd_config->getSocketAddress();
uint16_t server_port = cmd_config->getSocketPort();
// Search for the specific connection and reuse the existing one if found.
auto it = sockets_.find(std::make_pair(server_address, server_port));
if (it != sockets_.end()) {
auto listener = it->second->listener_;
if (listener) {
// Reconfig keeping the same address and port.
if (listener->getTlsContext()) {
if (cmd_config->getTrustAnchor().empty()) {
// Can not switch from HTTPS to HTTP
LOG_ERROR(command_logger, HTTP_COMMAND_MGR_HTTPS_SERVICE_REUSE_FAILED)
.arg(server_address.toText())
.arg(server_port);
isc_throw(BadValue,
"Can not switch from HTTPS to HTTP sockets using the same address and port.");
} else {
// Apply TLS settings each time.
TlsContextPtr tls_context;
TlsContext::configure(tls_context,
TlsRole::SERVER,
cmd_config->getTrustAnchor(),
cmd_config->getCertFile(),
cmd_config->getKeyFile(),
cmd_config->getCertRequired());
// Overwrite the authentication setup, the http headers and the emulation flag
// in the response creator config.
it->second->config_->setAuthConfig(cmd_config->getAuthConfig());
it->second->config_->setHttpHeaders(cmd_config->getHttpHeaders());
it->second->config_->setEmulateAgentResponse(cmd_config->getEmulateAgentResponse());
listener->setTlsContext(tls_context);
LOG_INFO(command_logger, HTTP_COMMAND_MGR_HTTPS_SERVICE_UPDATED)
.arg(server_address.toText())
.arg(server_port);
}
} else {
if (!cmd_config->getTrustAnchor().empty()) {
// Can not switch from HTTP to HTTPS
LOG_ERROR(command_logger, HTTP_COMMAND_MGR_HTTP_SERVICE_REUSE_FAILED)
.arg(server_address.toText())
.arg(server_port);
isc_throw(BadValue,
"Can not switch from HTTP to HTTPS sockets using the same address and port.");
} else {
// Overwrite the authentication setup, the http headers and the emulation flag
// in the response creator config.
it->second->config_->setAuthConfig(cmd_config->getAuthConfig());
it->second->config_->setHttpHeaders(cmd_config->getHttpHeaders());
it->second->config_->setEmulateAgentResponse(cmd_config->getEmulateAgentResponse());
LOG_INFO(command_logger, HTTP_COMMAND_MGR_HTTP_SERVICE_UPDATED)
.arg(server_address.toText())
.arg(server_port);
}
}
}
// If the connection can be reused, mark it as usable.
it->second->usable_ = true;
return;
}
// Connection not found so it needs to be created.
// When TLS is enabled configure it.
bool use_https = false;
TlsContextPtr tls_context;
if (!cmd_config->getCertFile().empty()) {
TlsContext::configure(tls_context,
TlsRole::SERVER,
cmd_config->getTrustAnchor(),
cmd_config->getCertFile(),
cmd_config->getKeyFile(),
cmd_config->getCertRequired());
use_https = true;
}
// Create response creator factory first. It will be used to
// generate response creators. Each response creator will be used
// to generate answer to specific request.
HttpResponseCreatorFactoryPtr rfc(new HttpCommandResponseCreatorFactory(cmd_config));
// Create HTTP listener. It will open up a TCP socket and be
// prepared to accept incoming connection.
HttpListenerPtr http_listener
(new HttpListener(io_service_,
server_address,
server_port,
tls_context,
rfc,
HttpListener::RequestTimeout(timeout_),
HttpListener::IdleTimeout(idle_timeout_)));
// Pass the use external socket flag.
http_listener->addExternalSockets(use_external_);
// Instruct the HTTP listener to actually open socket, install
// callback and start listening.
http_listener->start();
HttpSocketInfoPtr socket_info(new HttpSocketInfo());
socket_info->config_ = cmd_config;
socket_info->listener_ = http_listener;
sockets_[std::make_pair(server_address, server_port)] = socket_info;
// Ok, seems we're good to go.
LOG_INFO(command_logger, HTTP_COMMAND_MGR_SERVICE_STARTED)
.arg(use_https ? "HTTPS" : "HTTP")
.arg(server_address.toText())
.arg(server_port);
}
void
HttpCommandMgrImpl::closeCommandSocket(HttpSocketInfoPtr info, bool remove) {
bool use_https = false;
if (info) {
ostringstream ep;
use_https = !info->config_->getCertFile().empty();
ep << "bound to address " << info->config_->getSocketAddress()
<< " port " << info->config_->getSocketPort();
LOG_INFO(command_logger, HTTP_COMMAND_MGR_SERVICE_STOPPING)
.arg(use_https ? "HTTPS" : "HTTP")
.arg(ep.str());
info->listener_->stop();
if (remove) {
auto it = sockets_.find(std::make_pair(info->config_->getSocketAddress(), info->config_->getSocketPort()));
if (it != sockets_.end()) {
sockets_.erase(it);
}
}
// We have stopped listeners but there may be some pending handlers
// related to these listeners. Need to invoke these handlers.
try {
io_service_->pollOne();
} catch (...) {
}
} else {
closeCommandSockets(remove);
}
}
void
HttpCommandMgrImpl::closeCommandSockets(bool remove) {
auto copy = sockets_;
for (auto const& data : copy) {
closeCommandSocket(data.second, remove);
}
}
ConstHttpListenerPtr
HttpCommandMgrImpl::getHttpListener(HttpSocketInfoPtr info) const {
// Return the most recent listener or null.
if (info) {
auto it = sockets_.find(std::make_pair(info->config_->getSocketAddress(), info->config_->getSocketPort()));
if (it != sockets_.end()) {
return (it->second->listener_);
}
} else if (sockets_.size()) {
return (sockets_.begin()->second->listener_);
}
return (ConstHttpListenerPtr());
}
HttpCommandMgr&
HttpCommandMgr::instance() {
static HttpCommandMgr http_cmd_mgr;
return (http_cmd_mgr);
}
HttpCommandMgr::HttpCommandMgr() : impl_(new HttpCommandMgrImpl()) {
}
void
HttpCommandMgr::setIOService(const IOServicePtr& io_service) {
impl_->io_service_ = io_service;
}
void
HttpCommandMgr::setConnectionTimeout(const long timeout) {
impl_->timeout_ = timeout;
}
void
HttpCommandMgr::setIdleConnectionTimeout(const long timeout) {
impl_->idle_timeout_ = timeout;
}
void
HttpCommandMgr::addExternalSockets(bool use_external) {
impl_->use_external_ = use_external;
}
void
HttpCommandMgr::openCommandSocket(const isc::data::ConstElementPtr config) {
impl_->openCommandSocket(config);
}
void
HttpCommandMgr::openCommandSockets(const isc::data::ConstElementPtr config) {
impl_->openCommandSockets(config);
}
void
HttpCommandMgr::closeCommandSocket(HttpSocketInfoPtr info, bool remove) {
impl_->closeCommandSocket(info, remove);
}
void
HttpCommandMgr::closeCommandSockets() {
impl_->closeCommandSockets();
}
ConstHttpListenerPtr
HttpCommandMgr::getHttpListener(HttpSocketInfoPtr info) const {
return (impl_->getHttpListener(info));
}
} // end of isc::config
} // end of isc
|