Kea  2.5.2
d2_client_mgr.cc
Go to the documentation of this file.
1 // Copyright (C) 2014-2021 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  bool suffix_present = true;
190  if (!suffix.empty()) {
191  std::string str = gen_name.str();
192  auto suffix_rit = suffix.rbegin();
193  if (*suffix_rit == '.') {
194  ++suffix_rit;
195  }
196 
197  auto gen_rit = str.rbegin();
198  if (*gen_rit == '.') {
199  ++gen_rit;
200  }
201 
202  while (suffix_rit != suffix.rend()) {
203  if ((gen_rit == str.rend()) || (*suffix_rit != *gen_rit)) {
204  // They don't match.
205  suffix_present = false;
206  break;
207  }
208 
209  ++suffix_rit;
210  ++gen_rit;
211  }
212 
213  // Catch the case where name has suffix embedded.
214  // input: foo.barexample.com suffix: example.com
215  if ((suffix_present) && (suffix_rit == suffix.rend())) {
216  if ((gen_rit != str.rend()) && (*gen_rit != '.')) {
217  suffix_present = false;
218  }
219  }
220 
221  if (!suffix_present) {
222  size_t len = str.length();
223  if ((len > 0) && (str[len - 1] != '.')) {
224  gen_name << ".";
225  }
226 
227  gen_name << suffix;
228  }
229  }
230 
231  std::string str = gen_name.str();
232  size_t len = str.length();
233 
234  if (trailing_dot) {
235  // If trailing dot should be added but there is no trailing dot,
236  // append it.
237  if ((len > 0) && (str[len - 1] != '.')) {
238  gen_name << ".";
239  }
240 
241  } else {
242  // If the trailing dot should not be appended but it is present,
243  // remove it.
244  if ((len > 0) && (str[len - 1] == '.')) {
245  gen_name.str(str.substr(0,len-1));
246  }
247 
248  }
249 
250  return (gen_name.str());
251 }
252 
253 void
255  if (amSending()) {
256  return;
257  }
258 
259  // Create a our own service instance when we are not being multiplexed
260  // into an external service..
261  private_io_service_.reset(new asiolink::IOService());
262  startSender(error_handler, *private_io_service_);
264  .arg(d2_client_config_->toText());
265 }
266 
267 void
269  isc::asiolink::IOService& io_service) {
270  if (amSending()) {
271  return;
272  }
273 
274  if (!name_change_sender_) {
275  isc_throw(D2ClientError, "D2ClientMgr::startSender sender is null");
276  }
277 
278  if (!error_handler) {
279  isc_throw(D2ClientError, "D2ClientMgr::startSender handler is null");
280  }
281 
282  // Set the error handler.
283  client_error_handler_ = error_handler;
284 
285  // Start the sender on the given service.
286  name_change_sender_->startSending(io_service);
287 
288  // Register sender's select-fd with IfaceMgr.
289  // We need to remember the fd that is registered so we can unregister later.
290  // IO error handling in the sender may alter its select-fd.
291  registered_select_fd_ = name_change_sender_->getSelectFd();
292  IfaceMgr::instance().addExternalSocket(registered_select_fd_,
293  std::bind(&D2ClientMgr::runReadyIO,
294  this));
295 }
296 
297 bool
299  return (name_change_sender_ && name_change_sender_->amSending());
300 }
301 
302 void
305  if (registered_select_fd_ != util::WatchSocket::SOCKET_NOT_VALID) {
306  IfaceMgr::instance().deleteExternalSocket(registered_select_fd_);
307  registered_select_fd_ = util::WatchSocket::SOCKET_NOT_VALID;
308  }
309 
310  // If its not null, call stop.
311  if (amSending()) {
312  name_change_sender_->stopSending();
314  }
315 }
316 
317 void
319  if (!amSending()) {
320  // This is programmatic error so bust them for it.
321  isc_throw(D2ClientError, "D2ClientMgr::sendRequest not in send mode");
322  }
323 
324  try {
325  name_change_sender_->sendRequest(ncr);
326  } catch (const std::exception& ex) {
328  .arg(ex.what()).arg((ncr ? ncr->toText() : " NULL "));
330  }
331 }
332 
333 void
335  Result result,
337  // Handler is mandatory to enter send mode but test it just to be safe.
338  if (!client_error_handler_) {
340  } else {
341  // Handler is not supposed to throw, but catch just in case.
342  try {
343  (client_error_handler_)(result, ncr);
344  } catch (const std::exception& ex) {
346  .arg(ex.what());
347  }
348  }
349 }
350 
351 size_t
353  if (!name_change_sender_) {
354  isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null");
355  }
356 
357  return(name_change_sender_->getQueueSize());
358 }
359 
360 size_t
362  if (!name_change_sender_) {
363  isc_throw(D2ClientError, "D2ClientMgr::getQueueMaxSize sender is null");
364  }
365 
366  return(name_change_sender_->getQueueMaxSize());
367 }
368 
369 
370 
372 D2ClientMgr::peekAt(const size_t index) const {
373  if (!name_change_sender_) {
374  isc_throw(D2ClientError, "D2ClientMgr::peekAt sender is null");
375  }
376 
377  return (name_change_sender_->peekAt(index));
378 }
379 
380 void
382  if (!name_change_sender_) {
383  isc_throw(D2ClientError, "D2ClientMgr::clearQueue sender is null");
384  }
385 
386  name_change_sender_->clearSendQueue();
387 }
388 
389 void
392  if (result == dhcp_ddns::NameChangeSender::SUCCESS) {
394  DHCPSRV_DHCP_DDNS_NCR_SENT).arg(ncr->toText());
395  } else {
396  invokeClientErrorHandler(result, ncr);
397  }
398 }
399 
400 int
402  if (!amSending()) {
403  isc_throw (D2ClientError, "D2ClientMgr::getSelectFd "
404  " not in send mode");
405  }
406 
407  return (name_change_sender_->getSelectFd());
408 }
409 
410 void
412  if (!name_change_sender_) {
413  // This should never happen.
414  isc_throw(D2ClientError, "D2ClientMgr::runReadyIO"
415  " name_change_sender is null");
416  }
417 
418  name_change_sender_->runReadyIO();
419 }
420 
421 }; // namespace dhcp
422 
423 }; // 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:57
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.
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.
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.
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.
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.
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 DdnsParams &ddns_params, const bool trailing_dot) const
Adds a qualifying suffix to a given domain name.
Convenience container for conveying DDNS behavioral parameters It is intended to be created per Packe...
Definition: srv_config.h:48
std::string getGeneratedPrefix() const
Returns the Prefix Kea should use when generating domain-names.
Definition: srv_config.cc:962
std::string getQualifyingSuffix() const
Returns the suffix Kea should use when to qualify partial domain-names.
Definition: srv_config.cc:971
bool getOverrideNoUpdate() const
Returns whether or not Kea should perform updates, even if client requested no updates.
Definition: srv_config.cc:936
bool getEnableUpdates() const
Returns whether or not DHCP DDNS updating is enabled.
Definition: srv_config.cc:927
bool getOverrideClientUpdate() const
Returns whether or not Kea should perform updates, even if client requested delegation.
Definition: srv_config.cc:944
void deleteExternalSocket(int socketfd)
Deletes external socket.
Definition: iface_mgr.cc:348
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:325
Abstract interface for sending NameChangeRequests.
Definition: ncr_io.h:466
Result
Defines the outcome of an asynchronous NCR send.
Definition: ncr_io.h:476
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:848
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition: ncr_msg.h:240
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
const isc::log::MessageID DHCPSRV_DHCP_DDNS_NCR_SENT
boost::shared_ptr< D2ClientConfig > D2ClientConfigPtr
Defines a pointer for D2ClientConfig instances.
const isc::log::MessageID DHCPSRV_DHCP_DDNS_SENDER_STOPPED
const isc::log::MessageID DHCPSRV_DHCP_DDNS_SUSPEND_UPDATES
const isc::log::MessageID DHCPSRV_CFGMGR_CFG_DHCP_DDNS
const isc::log::MessageID DHCPSRV_DHCP_DDNS_SENDER_STARTED
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
const isc::log::MessageID DHCPSRV_DHCP_DDNS_NCR_REJECTED
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
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition: dhcpsrv_log.h:26
const isc::log::MessageID DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION
const isc::log::MessageID DHCPSRV_DHCP_DDNS_HANDLER_NULL
Defines the logger used by the top-level component of kea-lfc.
This file provides UDP socket based implementation for sending and receiving NameChangeRequests.