Kea  1.9.9-git
d2_client_mgr.cc
Go to the documentation of this file.
1 // Copyright (C) 2014-2020 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 <functional>
15 #include <string>
16 
17 using namespace std;
18 
19 namespace isc {
20 namespace dhcp {
21 
22 D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()),
23  name_change_sender_(), private_io_service_(),
24  registered_select_fd_(util::WatchSocket::SOCKET_NOT_VALID) {
25  // Default constructor initializes with a disabled configuration.
26 }
27 
29  stopSender();
30 }
31 
32 void
34  if (ddnsEnabled()) {
38  d2_client_config_->enableUpdates(false);
39  if (name_change_sender_) {
40  stopSender();
41  }
42  }
43 }
44 
45 void
47  if (!new_config) {
49  "D2ClientMgr cannot set DHCP-DDNS configuration to NULL.");
50  }
51 
52  // Don't do anything unless configuration values are actually different.
53  if (*d2_client_config_ != *new_config) {
54  // Make sure we stop sending first.
55  stopSender();
56  if (!new_config->getEnableUpdates()) {
57  // Updating has been turned off.
58  // Destroy current sender (any queued requests are tossed).
59  name_change_sender_.reset();
60  } else {
62  switch (new_config->getNcrProtocol()) {
63  case dhcp_ddns::NCR_UDP: {
64  // Instantiate a new sender.
65  new_sender.reset(new dhcp_ddns::NameChangeUDPSender(
66  new_config->getSenderIp(),
67  new_config->getSenderPort(),
68  new_config->getServerIp(),
69  new_config->getServerPort(),
70  new_config->getNcrFormat(),
71  *this,
72  new_config->getMaxQueueSize()));
73  break;
74  }
75  default:
76  // In theory you can't get here.
77  isc_throw(D2ClientError, "Invalid sender Protocol: "
78  << new_config->getNcrProtocol());
79  break;
80  }
81 
82  // Transfer queued requests from previous sender to the new one.
88  if (name_change_sender_) {
89  new_sender->assumeQueue(*name_change_sender_);
90  }
91 
92  // Replace the old sender with the new one.
93  name_change_sender_ = new_sender;
94  }
95  }
96 
97  // Update the configuration.
98  d2_client_config_ = new_config;
100  .arg(!ddnsEnabled() ? "DHCP-DDNS updates disabled" :
101  "DHCP_DDNS updates enabled");
102 }
103 
104 bool
106  return (d2_client_config_->getEnableUpdates());
107 }
108 
109 const D2ClientConfigPtr&
111  return (d2_client_config_);
112 }
113 
114 void
115 D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
116  bool& server_s, bool& server_n,
117  const DdnsParams& ddns_params) 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 (!ddns_params.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 = ddns_params.getOverrideClientUpdate();
142  server_n = false;
143  }
144  break;
145 
146  case 1:
147  server_s = ddns_params.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 = (ddns_params.getEnableUpdates() &&
155  ddns_params.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 DdnsParams& ddns_params,
170  const bool trailing_dot) const {
171  std::string hostname = address.toText();
172  std::replace(hostname.begin(), hostname.end(),
173  (address.isV4() ? '.' : ':'), '-');
174 
175  std::ostringstream gen_name;
176  gen_name << ddns_params.getGeneratedPrefix() << "-" << hostname;
177  return (qualifyName(gen_name.str(), ddns_params, trailing_dot));
178 }
179 
180 
181 std::string
182 D2ClientMgr::qualifyName(const std::string& partial_name,
183  const DdnsParams& ddns_params,
184  const bool trailing_dot) const {
185  std::ostringstream gen_name;
186 
187  gen_name << partial_name;
188  std::string suffix = ddns_params.getQualifyingSuffix();
189  if (!suffix.empty()) {
190  std::string str = gen_name.str();
191  size_t len = str.length();
192  if ((len > 0) && (str[len - 1] != '.')) {
193  gen_name << ".";
194  }
195 
196  gen_name << suffix;
197  }
198 
199  std::string str = gen_name.str();
200  size_t len = str.length();
201 
202  if (trailing_dot) {
203  // If trailing dot should be added but there is no trailing dot,
204  // append it.
205  if ((len > 0) && (str[len - 1] != '.')) {
206  gen_name << ".";
207  }
208 
209  } else {
210  // If the trailing dot should not be appended but it is present,
211  // remove it.
212  if ((len > 0) && (str[len - 1] == '.')) {
213  gen_name.str(str.substr(0,len-1));
214  }
215 
216  }
217 
218  return (gen_name.str());
219 }
220 
221 void
223  if (amSending()) {
224  return;
225  }
226 
227  // Create a our own service instance when we are not being multiplexed
228  // into an external service..
229  private_io_service_.reset(new asiolink::IOService());
230  startSender(error_handler, *private_io_service_);
232  .arg(d2_client_config_->toText());
233 }
234 
235 void
237  isc::asiolink::IOService& io_service) {
238  if (amSending()) {
239  return;
240  }
241 
242  if (!name_change_sender_) {
243  isc_throw(D2ClientError, "D2ClientMgr::startSender sender is null");
244  }
245 
246  if (!error_handler) {
247  isc_throw(D2ClientError, "D2ClientMgr::startSender handler is null");
248  }
249 
250  // Set the error handler.
251  client_error_handler_ = error_handler;
252 
253  // Start the sender on the given service.
254  name_change_sender_->startSending(io_service);
255 
256  // Register sender's select-fd with IfaceMgr.
257  // We need to remember the fd that is registered so we can unregister later.
258  // IO error handling in the sender may alter its select-fd.
259  registered_select_fd_ = name_change_sender_->getSelectFd();
260  IfaceMgr::instance().addExternalSocket(registered_select_fd_,
261  std::bind(&D2ClientMgr::runReadyIO,
262  this));
263 }
264 
265 bool
267  return (name_change_sender_ && name_change_sender_->amSending());
268 }
269 
270 void
273  if (registered_select_fd_ != util::WatchSocket::SOCKET_NOT_VALID) {
274  IfaceMgr::instance().deleteExternalSocket(registered_select_fd_);
275  registered_select_fd_ = util::WatchSocket::SOCKET_NOT_VALID;
276  }
277 
278  // If its not null, call stop.
279  if (amSending()) {
280  name_change_sender_->stopSending();
282  }
283 }
284 
285 void
287  if (!amSending()) {
288  // This is programmatic error so bust them for it.
289  isc_throw(D2ClientError, "D2ClientMgr::sendRequest not in send mode");
290  }
291 
292  try {
293  name_change_sender_->sendRequest(ncr);
294  } catch (const std::exception& ex) {
296  .arg(ex.what()).arg((ncr ? ncr->toText() : " NULL "));
298  }
299 }
300 
301 void
303  Result result,
305  // Handler is mandatory to enter send mode but test it just to be safe.
306  if (!client_error_handler_) {
308  } else {
309  // Handler is not supposed to throw, but catch just in case.
310  try {
311  (client_error_handler_)(result, ncr);
312  } catch (const std::exception& ex) {
314  .arg(ex.what());
315  }
316  }
317 }
318 
319 size_t
321  if (!name_change_sender_) {
322  isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null");
323  }
324 
325  return(name_change_sender_->getQueueSize());
326 }
327 
328 size_t
330  if (!name_change_sender_) {
331  isc_throw(D2ClientError, "D2ClientMgr::getQueueMaxSize sender is null");
332  }
333 
334  return(name_change_sender_->getQueueMaxSize());
335 }
336 
337 
338 
340 D2ClientMgr::peekAt(const size_t index) const {
341  if (!name_change_sender_) {
342  isc_throw(D2ClientError, "D2ClientMgr::peekAt sender is null");
343  }
344 
345  return (name_change_sender_->peekAt(index));
346 }
347 
348 void
350  if (!name_change_sender_) {
351  isc_throw(D2ClientError, "D2ClientMgr::clearQueue sender is null");
352  }
353 
354  name_change_sender_->clearSendQueue();
355 }
356 
357 void
360  if (result == dhcp_ddns::NameChangeSender::SUCCESS) {
362  DHCPSRV_DHCP_DDNS_NCR_SENT).arg(ncr->toText());
363  } else {
364  invokeClientErrorHandler(result, ncr);
365  }
366 }
367 
368 int
370  if (!amSending()) {
371  isc_throw (D2ClientError, "D2ClientMgr::getSelectFd "
372  " not in send mode");
373  }
374 
375  return (name_change_sender_->getSelectFd());
376 }
377 
378 void
380  if (!name_change_sender_) {
381  // This should never happen.
382  isc_throw(D2ClientError, "D2ClientMgr::runReadyIO"
383  " name_change_sender is null");
384  }
385 
386  name_change_sender_->runReadyIO();
387 }
388 
389 }; // namespace dhcp
390 
391 }; // namespace isc
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
const isc::log::MessageID DHCPSRV_DHCP_DDNS_NCR_REJECTED
#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:324
~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:476
const isc::log::MessageID DHCPSRV_DHCP_DDNS_SENDER_STARTED
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
STL namespace.
const isc::log::MessageID DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION
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.
std::string generateFqdn(const asiolink::IOAddress &address, const DdnsParams &ddns_params, const bool trailing_dot=true) const
Builds a FQDN based on the configuration and given IP address.
const isc::log::MessageID DHCPSRV_DHCP_DDNS_NCR_SENT
#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...
const D2ClientConfigPtr & getD2ClientConfig() const
Fetches the DHCP-DDNS configuration pointer.
An exception that is thrown if an error occurs while configuring the D2 DHCP DDNS client...
Definition: d2_client_cfg.h:33
bool getEnableUpdates() const
Returns whether or not DHCP DDNS updating is enabled.
Definition: srv_config.cc:901
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:56
size_t getQueueMaxSize() const
Returns the maximum number of NCRs allowed in the queue.
void deleteExternalSocket(int socketfd)
Deletes external socket.
Definition: iface_mgr.cc:347
const dhcp_ddns::NameChangeRequestPtr & peekAt(const size_t index) const
Returns the nth NCR queued for transmission.
bool ddnsEnabled()
Convenience method for checking if DHCP-DDNS is enabled.
void clearQueue()
Removes all NCRs 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
void analyzeFqdn(const bool client_s, const bool client_n, bool &server_s, bool &server_n, const DdnsParams &ddns_params) const
Determines server flags based on configuration and client flags.
boost::shared_ptr< NameChangeSender > NameChangeSenderPtr
Defines a smart pointer to an instance of a sender.
Definition: ncr_io.h:848
Convenience container for conveying DDNS behavioral parameters It is intended to be created per Packe...
Definition: srv_config.h:46
Defines the logger used by the top-level component of kea-dhcp-ddns.
const isc::log::MessageID DHCPSRV_DHCP_DDNS_HANDLER_NULL
bool getOverrideClientUpdate() const
Returns whether or not Kea should perform updates, even if client requested delegation.
Definition: srv_config.cc:918
size_t getQueueSize() const
Returns the number of NCRs queued for transmission.
std::string qualifyName(const std::string &partial_name, const DdnsParams &ddns_params, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
const isc::log::MessageID DHCPSRV_DHCP_DDNS_SENDER_STOPPED
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
std::string getGeneratedPrefix() const
Returns the Prefix Kea should use when generating domain-names.
Definition: srv_config.cc:936
void suspendUpdates()
Suspends sending requests.
Abstract interface for sending NameChangeRequests.
Definition: ncr_io.h:466
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
bool getOverrideNoUpdate() const
Returns whether or not Kea should perform updates, even if client requested no updates.
Definition: srv_config.cc:910
int getSelectFd()
Fetches the sender's select-fd.
std::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:44
bool amSending() const
Returns true if the sender is in send mode, false otherwise.
const isc::log::MessageID DHCPSRV_DHCP_DDNS_SUSPEND_UPDATES
boost::shared_ptr< D2ClientConfig > D2ClientConfigPtr
Defines a pointer for D2ClientConfig instances.
std::string getQualifyingSuffix() const
Returns the suffix Kea should use when to qualify partial domain-names.
Definition: srv_config.cc:945
Defines the D2ClientMgr class.
const isc::log::MessageID DHCPSRV_CFGMGR_CFG_DHCP_DDNS
void runReadyIO()
Processes sender IO events.
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.