Kea 1.5.0
d2_client_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2014-2015 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 <dhcp/iface_mgr.h>
10#include <dhcp_ddns/ncr_udp.h>
12#include <dhcpsrv/dhcpsrv_log.h>
13
14#include <boost/bind.hpp>
15
16#include <string>
17
18using namespace std;
19
20namespace isc {
21namespace dhcp {
22
23D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()),
24 name_change_sender_(), private_io_service_(),
25 registered_select_fd_(util::WatchSocket::SOCKET_NOT_VALID) {
26 // Default constructor initializes with a disabled configuration.
27}
28
30 stopSender();
31}
32
33void
35 if (ddnsEnabled()) {
38 LOG_WARN(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SUSPEND_UPDATES);
39 d2_client_config_->enableUpdates(false);
40 if (name_change_sender_) {
41 stopSender();
42 }
43 }
44}
45
46void
48 if (!new_config) {
50 "D2ClientMgr cannot set DHCP-DDNS configuration to NULL.");
51 }
52
53 // Don't do anything unless configuration values are actually different.
54 if (*d2_client_config_ != *new_config) {
55 // Make sure we stop sending first.
56 stopSender();
57 if (!new_config->getEnableUpdates()) {
58 // Updating has been turned off.
59 // Destroy current sender (any queued requests are tossed).
60 name_change_sender_.reset();
61 } else {
63 switch (new_config->getNcrProtocol()) {
64 case dhcp_ddns::NCR_UDP: {
65 // Instantiate a new sender.
66 new_sender.reset(new dhcp_ddns::NameChangeUDPSender(
67 new_config->getSenderIp(),
68 new_config->getSenderPort(),
69 new_config->getServerIp(),
70 new_config->getServerPort(),
71 new_config->getNcrFormat(),
72 *this,
73 new_config->getMaxQueueSize()));
74 break;
75 }
76 default:
77 // In theory you can't get here.
78 isc_throw(D2ClientError, "Invalid sender Protocol: "
79 << new_config->getNcrProtocol());
80 break;
81 }
82
83 // Transfer queued requests from previous sender to the new one.
89 if (name_change_sender_) {
90 new_sender->assumeQueue(*name_change_sender_);
91 }
92
93 // Replace the old sender with the new one.
94 name_change_sender_ = new_sender;
95 }
96 }
97
98 // Update the configuration.
99 d2_client_config_ = new_config;
100 LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_CFG_DHCP_DDNS)
101 .arg(!ddnsEnabled() ? "DHCP-DDNS updates disabled" :
102 "DHCP_DDNS updates enabled");
103}
104
105bool
107 return (d2_client_config_->getEnableUpdates());
108}
109
112 return (d2_client_config_);
113}
114
115void
116D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
117 bool& server_s, bool& server_n) const {
118 // Per RFC 4702 & 4704, the client N and S flags allow the client to
119 // request one of three options:
120 //
121 // N flag S flag Option
122 // ------------------------------------------------------------------
123 // 0 0 client wants to do forward updates (section 3.2)
124 // 0 1 client wants server to do forward updates (section 3.3)
125 // 1 0 client wants no one to do updates (section 3.4)
126 // 1 1 invalid combination
127 // (Note section numbers cited are for 4702, for 4704 see 5.1, 5.2, and 5.3)
128 //
129 // Make a bit mask from the client's flags and use it to set the response
130 // flags accordingly.
131 const uint8_t mask = ((client_n ? 2 : 0) + (client_s ? 1 : 0));
132
133 switch (mask) {
134 case 0:
135 if (!d2_client_config_->getEnableUpdates()) {
136 server_s = false;
137 server_n = true;
138 } else {
139 // If updates are enabled and we are overriding client delegation
140 // then S flag should be true. N-flag should be false.
141 server_s = d2_client_config_->getOverrideClientUpdate();
142 server_n = false;
143 }
144 break;
145
146 case 1:
147 server_s = d2_client_config_->getEnableUpdates();
148 server_n = !server_s;
149 break;
150
151 case 2:
152 // If updates are enabled and we are overriding "no updates" then
153 // S flag should be true.
154 server_s = (d2_client_config_->getEnableUpdates() &&
155 d2_client_config_->getOverrideNoUpdate());
156 server_n = !server_s;
157 break;
158
159 default:
160 // RFCs declare this an invalid combination.
162 "Invalid client FQDN - N and S cannot both be 1");
163 break;
164 }
165}
166
167std::string
169 const bool trailing_dot) const {
170 std::string hostname = address.toText();
171 std::replace(hostname.begin(), hostname.end(),
172 (address.isV4() ? '.' : ':'), '-');
173
174 std::ostringstream gen_name;
175 gen_name << d2_client_config_->getGeneratedPrefix() << "-" << hostname;
176 return (qualifyName(gen_name.str(), trailing_dot));
177}
178
179
180std::string
181D2ClientMgr::qualifyName(const std::string& partial_name,
182 const bool trailing_dot) const {
183 std::ostringstream gen_name;
184
185 gen_name << partial_name;
186 if (!d2_client_config_->getQualifyingSuffix().empty()) {
187 std::string str = gen_name.str();
188 size_t len = str.length();
189 if ((len > 0) && (str[len - 1] != '.')) {
190 gen_name << ".";
191 }
192
193 gen_name << d2_client_config_->getQualifyingSuffix();
194 }
195
196 std::string str = gen_name.str();
197 size_t len = str.length();
198
199 if (trailing_dot) {
200 // If trailing dot should be added but there is no trailing dot,
201 // append it.
202 if ((len > 0) && (str[len - 1] != '.')) {
203 gen_name << ".";
204 }
205
206 } else {
207 // If the trailing dot should not be appended but it is present,
208 // remove it.
209 if ((len > 0) && (str[len - 1] == '.')) {
210 gen_name.str(str.substr(0,len-1));
211 }
212
213 }
214
215 return (gen_name.str());
216}
217
218void
220 if (amSending()) {
221 return;
222 }
223
224 // Create a our own service instance when we are not being multiplexed
225 // into an external service..
226 private_io_service_.reset(new asiolink::IOService());
227 startSender(error_handler, *private_io_service_);
228 LOG_INFO(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SENDER_STARTED)
229 .arg(d2_client_config_->toText());
230}
231
232void
234 isc::asiolink::IOService& io_service) {
235 if (amSending()) {
236 return;
237 }
238
239 if (!name_change_sender_) {
240 isc_throw(D2ClientError, "D2ClientMgr::startSender sender is null");
241 }
242
243 if (!error_handler) {
244 isc_throw(D2ClientError, "D2ClientMgr::startSender handler is null");
245 }
246
247 // Set the error handler.
248 client_error_handler_ = error_handler;
249
250 // Start the sender on the given service.
251 name_change_sender_->startSending(io_service);
252
253 // Register sender's select-fd with IfaceMgr.
254 // We need to remember the fd that is registered so we can unregister later.
255 // IO error handling in the sender may alter its select-fd.
256 registered_select_fd_ = name_change_sender_->getSelectFd();
257 IfaceMgr::instance().addExternalSocket(registered_select_fd_,
258 boost::bind(&D2ClientMgr::runReadyIO,
259 this));
260}
261
262bool
264 return (name_change_sender_ && name_change_sender_->amSending());
265}
266
267void
270 if (registered_select_fd_ != util::WatchSocket::SOCKET_NOT_VALID) {
271 IfaceMgr::instance().deleteExternalSocket(registered_select_fd_);
272 registered_select_fd_ = util::WatchSocket::SOCKET_NOT_VALID;
273 }
274
275 // If its not null, call stop.
276 if (amSending()) {
277 name_change_sender_->stopSending();
278 LOG_INFO(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SENDER_STOPPED);
279 }
280}
281
282void
284 if (!amSending()) {
285 // This is programmatic error so bust them for it.
286 isc_throw(D2ClientError, "D2ClientMgr::sendRequest not in send mode");
287 }
288
289 try {
290 name_change_sender_->sendRequest(ncr);
291 } catch (const std::exception& ex) {
292 LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_NCR_REJECTED)
293 .arg(ex.what()).arg((ncr ? ncr->toText() : " NULL "));
295 }
296}
297
298void
300 Result result,
302 // Handler is mandatory to enter send mode but test it just to be safe.
303 if (!client_error_handler_) {
304 LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_HANDLER_NULL);
305 } else {
306 // Handler is not supposed to throw, but catch just in case.
307 try {
308 (client_error_handler_)(result, ncr);
309 } catch (const std::exception& ex) {
310 LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION)
311 .arg(ex.what());
312 }
313 }
314}
315
316size_t
318 if (!name_change_sender_) {
319 isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null");
320 }
321
322 return(name_change_sender_->getQueueSize());
323}
324
325size_t
327 if (!name_change_sender_) {
328 isc_throw(D2ClientError, "D2ClientMgr::getQueueMaxSize sender is null");
329 }
330
331 return(name_change_sender_->getQueueMaxSize());
332}
333
334
335
337D2ClientMgr::peekAt(const size_t index) const {
338 if (!name_change_sender_) {
339 isc_throw(D2ClientError, "D2ClientMgr::peekAt sender is null");
340 }
341
342 return (name_change_sender_->peekAt(index));
343}
344
345void
347 if (!name_change_sender_) {
348 isc_throw(D2ClientError, "D2ClientMgr::clearQueue sender is null");
349 }
350
351 name_change_sender_->clearSendQueue();
352}
353
354void
359 DHCPSRV_DHCP_DDNS_NCR_SENT).arg(ncr->toText());
360 } else {
361 invokeClientErrorHandler(result, ncr);
362 }
363}
364
365int
367 if (!amSending()) {
368 isc_throw (D2ClientError, "D2ClientMgr::getSelectFd "
369 " not in send mode");
370 }
371
372 return (name_change_sender_->getSelectFd());
373}
374
375void
377 if (!name_change_sender_) {
378 // This should never happen.
379 isc_throw(D2ClientError, "D2ClientMgr::runReadyIO"
380 " name_change_sender is null");
381 }
382
383 name_change_sender_->runReadyIO();
384}
385
386}; // namespace dhcp
387
388}; // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Acts as a storage vault for D2 client configuration.
Definition: d2_client_cfg.h:53
An exception that is thrown if an error occurs while configuring the D2 DHCP DDNS client.
Definition: d2_client_cfg.h:34
void invokeClientErrorHandler(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Calls the client's error handler.
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
const D2ClientConfigPtr & getD2ClientConfig() const
Fetches the DHCP-DDNS configuration pointer.
void startSender(D2ClientErrorHandler error_handler, isc::asiolink::IOService &io_service)
Enables sending NameChangeRequests to kea-dhcp-ddns.
void suspendUpdates()
Suspends sending requests.
void sendRequest(dhcp_ddns::NameChangeRequestPtr &ncr)
Send the given NameChangeRequests to kea-dhcp-ddns.
size_t getQueueSize() const
Returns the number of NCRs queued for transmission.
void clearQueue()
Removes all NCRs queued for transmission.
void stopSender()
Disables sending NameChangeRequests to kea-dhcp-ddns.
void setD2ClientConfig(D2ClientConfigPtr &new_config)
Updates the DHCP-DDNS client configuration to the given value.
std::string generateFqdn(const asiolink::IOAddress &address, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
bool amSending() const
Returns true if the sender is in send mode, false otherwise.
int getSelectFd()
Fetches the sender's select-fd.
void runReadyIO()
Processes sender IO events.
~D2ClientMgr()
Destructor.
D2ClientMgr()
Constructor.
size_t getQueueMaxSize() const
Returns the maximum number of NCRs allowed in the queue.
virtual void operator()(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Function operator implementing the NCR sender callback.
const dhcp_ddns::NameChangeRequestPtr & peekAt(const size_t index) const
Returns the nth NCR queued for transmission.
std::string qualifyName(const std::string &partial_name, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
void analyzeFqdn(const bool client_s, const bool client_n, bool &server_s, bool &server_n) const
Determines server flags based on configuration and client flags.
void deleteExternalSocket(int socketfd)
Deletes external socket.
Definition: iface_mgr.cc:339
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
void addExternalSocket(int socketfd, SocketCallback callback)
Adds external socket and a callback.
Definition: iface_mgr.cc:317
Abstract interface for sending NameChangeRequests.
Definition: ncr_io.h:457
Result
Defines the outcome of an asynchronous NCR send.
Definition: ncr_io.h:467
Provides the ability to send NameChangeRequests via UDP socket.
Definition: ncr_udp.h:441
static const int SOCKET_NOT_VALID
Value used to signify an invalid descriptor.
Definition: watch_socket.h:50
Defines the D2ClientMgr class.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
boost::shared_ptr< NameChangeSender > NameChangeSenderPtr
Defines a smart pointer to an instance of a sender.
Definition: ncr_io.h:777
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:214
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
boost::shared_ptr< D2ClientConfig > D2ClientConfigPtr
Defines a pointer for D2ClientConfig instances.
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
boost::function< void(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)> D2ClientErrorHandler
Defines the type for D2 IO error handler.
Definition: d2_client_mgr.h:43
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition: dhcpsrv_log.h:26
Defines the logger used by the top-level component of kea-dhcp-ddns.
This file provides UDP socket based implementation for sending and receiving NameChangeRequests.