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>
11 #include <dhcpsrv/d2_client_mgr.h>
12 #include <dhcpsrv/dhcpsrv_log.h>
13 
14 #include <boost/bind.hpp>
15 
16 #include <string>
17 
18 using namespace std;
19 
20 namespace isc {
21 namespace dhcp {
22 
23 D2ClientMgr::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 
33 void
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 
46 void
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 
105 bool
107  return (d2_client_config_->getEnableUpdates());
108 }
109 
110 const D2ClientConfigPtr&
112  return (d2_client_config_);
113 }
114 
115 void
116 D2ClientMgr::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 
167 std::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 
180 std::string
181 D2ClientMgr::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 
218 void
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 
232 void
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 
262 bool
264  return (name_change_sender_ && name_change_sender_->amSending());
265 }
266 
267 void
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 
282 void
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 
298 void
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 
316 size_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 
325 size_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 
337 D2ClientMgr::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 
345 void
347  if (!name_change_sender_) {
348  isc_throw(D2ClientError, "D2ClientMgr::clearQueue sender is null");
349  }
350 
351  name_change_sender_->clearSendQueue();
352 }
353 
354 void
357  if (result == dhcp_ddns::NameChangeSender::SUCCESS) {
359  DHCPSRV_DHCP_DDNS_NCR_SENT).arg(ncr->toText());
360  } else {
361  invokeClientErrorHandler(result, ncr);
362  }
363 }
364 
365 int
367  if (!amSending()) {
368  isc_throw (D2ClientError, "D2ClientMgr::getSelectFd "
369  " not in send mode");
370  }
371 
372  return (name_change_sender_->getSelectFd());
373 }
374 
375 void
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
std::string generateFqdn(const asiolink::IOAddress &address, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
bool amSending() const
Returns true if the sender is in send mode, false otherwise.
std::string qualifyName(const std::string &partial_name, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
void addExternalSocket(int socketfd, SocketCallback callback)
Adds external socket and a callback.
Definition: iface_mgr.cc:317
~D2ClientMgr()
Destructor.
void startSender(D2ClientErrorHandler error_handler, isc::asiolink::IOService &io_service)
Enables sending NameChangeRequests to kea-dhcp-ddns.
Result
Defines the outcome of an asynchronous NCR send.
Definition: ncr_io.h:467
virtual void operator()(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Function operator implementing the NCR sender callback.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
This file provides UDP socket based implementation for sending and receiving NameChangeRequests.
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:212
void sendRequest(dhcp_ddns::NameChangeRequestPtr &ncr)
Send the given NameChangeRequests to kea-dhcp-ddns.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
An exception that is thrown if an error occurs while configuring the D2 DHCP DDNS client.
Definition: d2_client_cfg.h:34
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
Acts as a storage vault for D2 client configuration.
Definition: d2_client_cfg.h:53
void deleteExternalSocket(int socketfd)
Deletes external socket.
Definition: iface_mgr.cc:339
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
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.
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
void clearQueue()
Removes all NCRs queued for transmission.
const D2ClientConfigPtr & getD2ClientConfig() const
Fetches the DHCP-DDNS configuration pointer.
const dhcp_ddns::NameChangeRequestPtr & peekAt(const size_t index) const
Returns the nth NCR queued for transmission.
void invokeClientErrorHandler(const dhcp_ddns::NameChangeSender::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Calls the client's error handler.
Provides the ability to send NameChangeRequests via UDP socket.
Definition: ncr_udp.h:441
boost::shared_ptr< NameChangeSender > NameChangeSenderPtr
Defines a smart pointer to an instance of a sender.
Definition: ncr_io.h:777
Defines the logger used by the top-level component of kea-dhcp-ddns.
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition: dhcpsrv_log.h:26
void suspendUpdates()
Suspends sending requests.
Abstract interface for sending NameChangeRequests.
Definition: ncr_io.h:457
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
int getSelectFd()
Fetches the sender's select-fd.
boost::shared_ptr< D2ClientConfig > D2ClientConfigPtr
Defines a pointer for D2ClientConfig instances.
Defines the D2ClientMgr class.
void runReadyIO()
Processes sender IO events.
size_t getQueueMaxSize() const
Returns the maximum number of NCRs allowed in the queue.
void setD2ClientConfig(D2ClientConfigPtr &new_config)
Updates the DHCP-DDNS client configuration to the given value.
static const int SOCKET_NOT_VALID
Value used to signify an invalid descriptor.
Definition: watch_socket.h:50
void stopSender()
Disables sending NameChangeRequests to kea-dhcp-ddns.
size_t getQueueSize() const
Returns the number of NCRs queued for transmission.