BIND 10 trac3329, updated. 45cd4d8881742dbd6bba9c965e01d2c5d206971f [3329] dhcpsrv::D2ClientMgr now registers IfaceMgr
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Feb 11 20:53:54 UTC 2014
The branch, trac3329 has been updated
via 45cd4d8881742dbd6bba9c965e01d2c5d206971f (commit)
via 74341d7cf0b8d7d2c3454b6be29b83006e67d85b (commit)
via 14b17e299221f6df1c7fbd2948b34c8cdbcd41f0 (commit)
from e200b6ca369bd2b49e7dd444c7845e06e66e4e94 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 45cd4d8881742dbd6bba9c965e01d2c5d206971f
Author: Thomas Markwalder <tmark at isc.org>
Date: Tue Feb 11 15:50:50 2014 -0500
[3329] dhcpsrv::D2ClientMgr now registers IfaceMgr
D2ClientMgr now registers and unregisters its runReadyIO method and sender's
select-fd with IfaceMgr's external socket monitoring.
commit 74341d7cf0b8d7d2c3454b6be29b83006e67d85b
Author: Thomas Markwalder <tmark at isc.org>
Date: Tue Feb 11 15:43:08 2014 -0500
[3329] dhcp_ddns::NameChangeSender extended to support running ready IO
Rememebering the io_service in use and how to run ready IO handlers was
pushed down from D2ClientMgr into NameChangeSender. NameChangeSender stop
logic was altered so it will now cleanly complete the last send as well
as interrupting the daisy-chain of instigating the next send upon
completion of the current send.
commit 14b17e299221f6df1c7fbd2948b34c8cdbcd41f0
Author: Thomas Markwalder <tmark at isc.org>
Date: Mon Feb 10 16:01:49 2014 -0500
[3329] Moved dhcp::D2ClientMgr into its own header and source.
d2_client.h and .cc was split into four files:
D2ClientConfig class is now in d2_client_cfg.h/cc
D2ClientMgr class is now in d2_client_mgr.h/cc
-----------------------------------------------------------------------
Summary of changes:
src/lib/dhcp_ddns/dhcp_ddns_messages.mes | 6 +
src/lib/dhcp_ddns/ncr_io.cc | 45 +++-
src/lib/dhcp_ddns/ncr_io.h | 16 ++
src/lib/dhcp_ddns/ncr_udp.cc | 10 +
src/lib/dhcp_ddns/ncr_udp.h | 2 +
src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc | 48 ++--
src/lib/dhcpsrv/Makefile.am | 3 +-
src/lib/dhcpsrv/cfgmgr.h | 2 +-
src/lib/dhcpsrv/d2_client_cfg.cc | 141 ++++++++++++
src/lib/dhcpsrv/d2_client_cfg.h | 217 ++++++++++++++++++
src/lib/dhcpsrv/{d2_client.cc => d2_client_mgr.cc} | 170 +++-----------
src/lib/dhcpsrv/{d2_client.h => d2_client_mgr.h} | 231 +++-----------------
src/lib/dhcpsrv/dhcp_parsers.h | 2 +-
src/lib/dhcpsrv/tests/d2_client_unittest.cc | 2 +-
src/lib/dhcpsrv/tests/d2_udp_unittest.cc | 44 +++-
15 files changed, 568 insertions(+), 371 deletions(-)
create mode 100644 src/lib/dhcpsrv/d2_client_cfg.cc
create mode 100644 src/lib/dhcpsrv/d2_client_cfg.h
rename src/lib/dhcpsrv/{d2_client.cc => d2_client_mgr.cc} (65%)
rename src/lib/dhcpsrv/{d2_client.h => d2_client_mgr.h} (65%)
-----------------------------------------------------------------------
diff --git a/src/lib/dhcp_ddns/dhcp_ddns_messages.mes b/src/lib/dhcp_ddns/dhcp_ddns_messages.mes
index 554139f..aa6dec8 100644
--- a/src/lib/dhcp_ddns/dhcp_ddns_messages.mes
+++ b/src/lib/dhcp_ddns/dhcp_ddns_messages.mes
@@ -19,6 +19,12 @@ This is an error message that indicates that an invalid request to update
a DNS entry was received by the application. Either the format or the content
of the request is incorrect. The request will be ignored.
+% DHCP_DDNS_NCR_FLUSH_IO_ERROR DHCP-DDNS Last send before stopping did not complete successfully: %1
+This is an error message that indicates the DHCP-DDNS client was unable to
+complete the last send prior to exiting send mode. This is a programmatic
+error, highly unlikely to occur, and should not impair the application's to
+process requests.
+
% DHCP_DDNS_NCR_LISTEN_CLOSE_ERROR application encountered an error while closing the listener used to receive NameChangeRequests : %1
This is an error message that indicates the application was unable to close the
listener connection used to receive NameChangeRequests. Closure may occur
diff --git a/src/lib/dhcp_ddns/ncr_io.cc b/src/lib/dhcp_ddns/ncr_io.cc
index 52a72c9..3c8ef5a 100644
--- a/src/lib/dhcp_ddns/ncr_io.cc
+++ b/src/lib/dhcp_ddns/ncr_io.cc
@@ -15,6 +15,7 @@
#include <dhcp_ddns/dhcp_ddns_log.h>
#include <dhcp_ddns/ncr_io.h>
+#include <asio.hpp>
#include <boost/algorithm/string/predicate.hpp>
namespace isc {
@@ -159,7 +160,7 @@ NameChangeListener::invokeRecvHandler(const Result result,
NameChangeSender::NameChangeSender(RequestSendHandler& send_handler,
size_t send_queue_max)
: sending_(false), send_handler_(send_handler),
- send_queue_max_(send_queue_max) {
+ send_queue_max_(send_queue_max), io_service_(NULL) {
// Queue size must be big enough to hold at least 1 entry.
setQueueMaxSize(send_queue_max);
@@ -177,6 +178,8 @@ NameChangeSender::startSending(isc::asiolink::IOService& io_service) {
// Call implementation dependent open.
try {
+ // Remember io service we're given.
+ io_service_ = &io_service;
open(io_service);
} catch (const isc::Exception& ex) {
stopSending();
@@ -189,6 +192,23 @@ NameChangeSender::startSending(isc::asiolink::IOService& io_service) {
void
NameChangeSender::stopSending() {
+ // Set it send indicator to false, no matter what. This allows us to at
+ // least try to re-open via startSending(). Also, setting it false now,
+ // allows us to break sendNext() chain in invokeSendHandler.
+ setSending(false);
+
+ // If there is an outstanding IO to complete, attempt to process it.
+ if (ioReady() && io_service_ != NULL) {
+ try {
+ runReadyIO();
+ } catch (const std::exception& ex) {
+ // Swallow exceptions. If we have some sort of error we'll log
+ // it but we won't propagate the throw.
+ LOG_ERROR(dhcp_ddns_logger,
+ DHCP_DDNS_NCR_FLUSH_IO_ERROR).arg(ex.what());
+ }
+ }
+
try {
// Call implementation dependent close.
close();
@@ -199,9 +219,7 @@ NameChangeSender::stopSending() {
DHCP_DDNS_NCR_SEND_CLOSE_ERROR).arg(ex.what());
}
- // Set it false, no matter what. This allows us to at least try to
- // re-open via startSending().
- setSending(false);
+ io_service_ = NULL;
}
void
@@ -274,7 +292,9 @@ NameChangeSender::invokeSendHandler(const NameChangeSender::Result result) {
// Set up the next send
try {
- sendNext();
+ if (amSending()) {
+ sendNext();
+ }
} catch (const isc::Exception& ex) {
// It is possible though unlikely, for sendNext to fail without
// scheduling the send. While, unlikely, it does mean the callback
@@ -367,5 +387,20 @@ NameChangeSender::getSelectFd() {
isc_throw(NotImplemented, "NameChangeSender::getSelectFd is not supported");
}
+void
+NameChangeSender::runReadyIO() {
+ if (!io_service_) {
+ isc_throw(NcrSenderError, "NameChangeSender::runReadyIO"
+ " sender io service is null");
+ }
+
+ // We shouldn't be here if IO isn't ready to execute.
+ // By running poll we're gauranteed not to hang.
+ /// @todo Trac# 3325 requests that asiolink::IOService provide a
+ /// wrapper for poll().
+ io_service_->get_io_service().poll_one();
+}
+
+
} // namespace isc::dhcp_ddns
} // namespace isc
diff --git a/src/lib/dhcp_ddns/ncr_io.h b/src/lib/dhcp_ddns/ncr_io.h
index 65b8929..1946383 100644
--- a/src/lib/dhcp_ddns/ncr_io.h
+++ b/src/lib/dhcp_ddns/ncr_io.h
@@ -576,6 +576,8 @@ public:
/// @throw NcrSenderError if the sender is not in send mode,
virtual int getSelectFd() = 0;
+ virtual bool ioReady() = 0;
+
protected:
/// @brief Dequeues and sends the next request on the send queue.
///
@@ -715,6 +717,14 @@ public:
/// end of the queue.
const NameChangeRequestPtr& peekAt(const size_t index) const;
+ /// @brief Processes sender IO events
+ ///
+ /// Executes at most one ready handler on the sender's IO service. If
+ /// no handlers are ready it returns immediately.
+ /// @warning - Running all ready handlers, in theory, could process all
+ /// messages currently queued.
+ virtual void runReadyIO();
+
protected:
/// @brief Returns a reference to the send queue.
SendQueue& getSendQueue() {
@@ -746,6 +756,12 @@ private:
/// @brief Pointer to the request which is in the process of being sent.
NameChangeRequestPtr ncr_to_send_;
+
+ /// @brief Pointer to the IOService currently being used by the sender.
+ /// @note We need to remember the io_service but we receive it by
+ /// reference. Use a raw pointer to store it. This value should never be
+ /// exposed and is only valid while in send mode.
+ asiolink::IOService* io_service_;
};
/// @brief Defines a smart pointer to an instance of a sender.
diff --git a/src/lib/dhcp_ddns/ncr_udp.cc b/src/lib/dhcp_ddns/ncr_udp.cc
index 6207bc3..54901f2 100644
--- a/src/lib/dhcp_ddns/ncr_udp.cc
+++ b/src/lib/dhcp_ddns/ncr_udp.cc
@@ -359,6 +359,16 @@ NameChangeUDPSender::getSelectFd() {
return(watch_socket_->getSelectFd());
}
+bool
+NameChangeUDPSender::ioReady() {
+ if (watch_socket_) {
+ return (watch_socket_->isReady());
+ }
+
+ return (false);
+}
+
+
}; // end of isc::dhcp_ddns namespace
}; // end of isc namespace
diff --git a/src/lib/dhcp_ddns/ncr_udp.h b/src/lib/dhcp_ddns/ncr_udp.h
index 97f8316..461589d 100644
--- a/src/lib/dhcp_ddns/ncr_udp.h
+++ b/src/lib/dhcp_ddns/ncr_udp.h
@@ -542,6 +542,8 @@ public:
/// @throw NcrSenderError if the sender is not in send mode,
virtual int getSelectFd();
+ virtual bool ioReady();
+
private:
/// @brief IP address from which to send.
isc::asiolink::IOAddress ip_address_;
diff --git a/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc b/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc
index 835afaf..6a9f104 100644
--- a/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc
+++ b/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc
@@ -361,7 +361,11 @@ TEST(NameChangeUDPSenderBasicTest, basicSendTests) {
// Verify select_fd is valid and currently shows no ready to read.
ASSERT_NE(dhcp_ddns::WatchSocket::INVALID_SOCKET, select_fd);
+
+ // Make sure select_fd does evaluates to not ready via select and
+ // that ioReady() method agrees.
ASSERT_EQ(0, selectCheck(select_fd));
+ ASSERT_FALSE(sender.ioReady());
// Iterate over a series of messages, sending each one. Since we
// do not invoke IOService::run, then the messages should accumulate
@@ -392,18 +396,22 @@ TEST(NameChangeUDPSenderBasicTest, basicSendTests) {
// IOService::run_one. This should complete the send of exactly one
// message and the queue count should decrement accordingly.
for (int i = num_msgs; i > 0; i--) {
- // Verify that sender shows IO ready.
+ // Make sure select_fd does evaluates to ready via select and
+ // that ioReady() method agrees.
ASSERT_TRUE(selectCheck(select_fd) > 0);
+ ASSERT_TRUE(sender.ioReady());
// Execute at one ready handler.
- io_service.run_one();
+ ASSERT_NO_THROW(sender.runReadyIO());
// Verify that the queue count decrements in step with each run.
EXPECT_EQ(i-1, sender.getQueueSize());
}
- // Verify that sender shows no IO ready.
- EXPECT_EQ(0, selectCheck(select_fd));
+ // Make sure select_fd does evaluates to not ready via select and
+ // that ioReady() method agrees.
+ ASSERT_EQ(0, selectCheck(select_fd));
+ ASSERT_FALSE(sender.ioReady());
// Verify that the queue is empty.
EXPECT_EQ(0, sender.getQueueSize());
@@ -419,16 +427,21 @@ TEST(NameChangeUDPSenderBasicTest, basicSendTests) {
// Verify that flushing the queue is not allowed in sending state.
EXPECT_THROW(sender.clearSendQueue(), NcrSenderError);
- // Put a message on the queue.
- EXPECT_NO_THROW(sender.sendRequest(ncr));
- EXPECT_EQ(1, sender.getQueueSize());
+ // Put num_msgs messages on the queue.
+ for (int i = 0; i < num_msgs; i++) {
+ ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
+ EXPECT_NO_THROW(sender.sendRequest(ncr));
+ }
+
+ // Make sure we have number of messages expected.
+ EXPECT_EQ(num_msgs, sender.getQueueSize());
// Verify that we can gracefully stop sending.
EXPECT_NO_THROW(sender.stopSending());
EXPECT_FALSE(sender.amSending());
// Verify that the queue is preserved after leaving sending state.
- EXPECT_EQ(1, sender.getQueueSize());
+ EXPECT_EQ(num_msgs - 1, sender.getQueueSize());
// Verify that flushing the queue works when not sending.
EXPECT_NO_THROW(sender.clearSendQueue());
@@ -454,23 +467,19 @@ TEST(NameChangeUDPSenderBasicTest, anyAddressSend) {
ASSERT_NO_THROW(sender.startSending(io_service));
EXPECT_TRUE(sender.amSending());
- // Fetch the sender's select-fd.
- int select_fd = sender.getSelectFd();
-
// Create and queue up a message.
NameChangeRequestPtr ncr;
ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[0]));
EXPECT_NO_THROW(sender.sendRequest(ncr));
EXPECT_EQ(1, sender.getQueueSize());
- // message and the queue count should decrement accordingly.
- // Execute at one ready handler.
- ASSERT_TRUE(selectCheck(select_fd) > 0);
- ASSERT_NO_THROW(io_service.run_one());
+ // Verify we have a ready IO, then execute at one ready handler.
+ ASSERT_TRUE(sender.ioReady());
+ ASSERT_NO_THROW(sender.runReadyIO());
// Verify that sender shows no IO ready.
// and that the queue is empty.
- EXPECT_EQ(0, selectCheck(select_fd));
+ ASSERT_FALSE(sender.ioReady());
EXPECT_EQ(0, sender.getQueueSize());
}
@@ -514,6 +523,9 @@ TEST(NameChangeSender, assumeQueue) {
// Take sender1 out of send mode.
ASSERT_NO_THROW(sender1.stopSending());
ASSERT_FALSE(sender1.amSending());
+ // Stopping should have completed the first message.
+ --num_msgs;
+ EXPECT_EQ(num_msgs, sender1.getQueueSize());
// Transfer should succeed. Verify sender1 has none,
// and sender2 has num_msgs queued.
@@ -719,7 +731,7 @@ TEST(NameChangeUDPSenderBasicTest, watchClosedAfterSendRequest) {
// Run one handler. This should execute the send completion handler
// after sending the first message. Duing completion handling, we will
// attempt to queue the second message which should fail.
- ASSERT_NO_THROW(io_service.run_one());
+ ASSERT_NO_THROW(sender.runReadyIO());
// Verify handler got called twice. First request should have be sent
// without error, second call should have failed to send due to watch
@@ -767,7 +779,7 @@ TEST(NameChangeUDPSenderBasicTest, watchSocketBadRead) {
// after sending the message. Duing completion handling clearing the
// watch socket should fail, which will close the socket, but not
// result in a throw.
- ASSERT_NO_THROW(io_service.run_one());
+ ASSERT_NO_THROW(sender.runReadyIO());
// Verify handler got called twice. First request should have be sent
// without error, second call should have failed to send due to watch
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index 69b14b2..1c8a18a 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -39,7 +39,8 @@ libb10_dhcpsrv_la_SOURCES =
libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
libb10_dhcpsrv_la_SOURCES += callout_handle_store.h
-libb10_dhcpsrv_la_SOURCES += d2_client.cc d2_client.h
+libb10_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h
+libb10_dhcpsrv_la_SOURCES += d2_client_mgr.cc d2_client_mgr.h
libb10_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h
libb10_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index 20c162d..ca854d6 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -20,7 +20,7 @@
#include <dhcp/option_definition.h>
#include <dhcp/option_space.h>
#include <dhcp/classify.h>
-#include <dhcpsrv/d2_client.h>
+#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
diff --git a/src/lib/dhcpsrv/d2_client.cc b/src/lib/dhcpsrv/d2_client.cc
deleted file mode 100644
index f38d332..0000000
--- a/src/lib/dhcpsrv/d2_client.cc
+++ /dev/null
@@ -1,446 +0,0 @@
-// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <dhcp_ddns/ncr_udp.h>
-#include <dhcpsrv/d2_client.h>
-#include <dhcpsrv/dhcpsrv_log.h>
-
-#include <string>
-
-using namespace std;
-
-namespace isc {
-namespace dhcp {
-
-//***************************** D2ClientConfig ********************************
-
-D2ClientConfig::D2ClientConfig(const bool enable_updates,
- const isc::asiolink::IOAddress& server_ip,
- const size_t server_port,
- const dhcp_ddns::
- NameChangeProtocol& ncr_protocol,
- const dhcp_ddns::
- NameChangeFormat& ncr_format,
- const bool always_include_fqdn,
- const bool override_no_update,
- const bool override_client_update,
- const bool replace_client_name,
- const std::string& generated_prefix,
- const std::string& qualifying_suffix)
- : enable_updates_(enable_updates),
- server_ip_(server_ip),
- server_port_(server_port),
- ncr_protocol_(ncr_protocol),
- ncr_format_(ncr_format),
- always_include_fqdn_(always_include_fqdn),
- override_no_update_(override_no_update),
- override_client_update_(override_client_update),
- replace_client_name_(replace_client_name),
- generated_prefix_(generated_prefix),
- qualifying_suffix_(qualifying_suffix) {
- validateContents();
-}
-
-D2ClientConfig::D2ClientConfig()
- : enable_updates_(false),
- server_ip_(isc::asiolink::IOAddress("0.0.0.0")),
- server_port_(0),
- ncr_protocol_(dhcp_ddns::NCR_UDP),
- ncr_format_(dhcp_ddns::FMT_JSON),
- always_include_fqdn_(false),
- override_no_update_(false),
- override_client_update_(false),
- replace_client_name_(false),
- generated_prefix_("myhost"),
- qualifying_suffix_("example.com") {
- validateContents();
-}
-
-D2ClientConfig::~D2ClientConfig(){};
-
-void
-D2ClientConfig::validateContents() {
- if (ncr_format_ != dhcp_ddns::FMT_JSON) {
- isc_throw(D2ClientError, "D2ClientConfig: NCR Format:"
- << dhcp_ddns::ncrFormatToString(ncr_format_)
- << " is not yet supported");
- }
-
- if (ncr_protocol_ != dhcp_ddns::NCR_UDP) {
- isc_throw(D2ClientError, "D2ClientConfig: NCR Protocol:"
- << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
- << " is not yet supported");
- }
-
- /// @todo perhaps more validation we should do yet?
- /// Are there any invalid combinations of options we need to test against?
-}
-
-bool
-D2ClientConfig::operator == (const D2ClientConfig& other) const {
- return ((enable_updates_ == other.enable_updates_) &&
- (server_ip_ == other.server_ip_) &&
- (server_port_ == other.server_port_) &&
- (ncr_protocol_ == other.ncr_protocol_) &&
- (ncr_format_ == other.ncr_format_) &&
- (always_include_fqdn_ == other.always_include_fqdn_) &&
- (override_no_update_ == other.override_no_update_) &&
- (override_client_update_ == other.override_client_update_) &&
- (replace_client_name_ == other.replace_client_name_) &&
- (generated_prefix_ == other.generated_prefix_) &&
- (qualifying_suffix_ == other.qualifying_suffix_));
-}
-
-bool
-D2ClientConfig::operator != (const D2ClientConfig& other) const {
- return (!(*this == other));
-}
-
-std::string
-D2ClientConfig::toText() const {
- std::ostringstream stream;
-
- stream << "enable_updates: " << (enable_updates_ ? "yes" : "no");
- if (enable_updates_) {
- stream << ", server_ip: " << server_ip_.toText()
- << ", server_port: " << server_port_
- << ", ncr_protocol: " << ncr_protocol_
- << ", ncr_format: " << ncr_format_
- << ", always_include_fqdn: " << (always_include_fqdn_ ?
- "yes" : "no")
- << ", override_no_update: " << (override_no_update_ ?
- "yes" : "no")
- << ", override_client_update: " << (override_client_update_ ?
- "yes" : "no")
- << ", replace_client_name: " << (replace_client_name_ ?
- "yes" : "no")
- << ", generated_prefix: [" << generated_prefix_ << "]"
- << ", qualifying_suffix: [" << qualifying_suffix_ << "]";
- }
-
- return (stream.str());
-}
-
-std::ostream&
-operator<<(std::ostream& os, const D2ClientConfig& config) {
- os << config.toText();
- return (os);
-}
-
-
-//******************************** D2ClientMgr ********************************
-
-
-D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()),
- name_change_sender_(), private_io_service_(), sender_io_service_(NULL) {
- // Default constructor initializes with a disabled configuration.
-}
-
-D2ClientMgr::~D2ClientMgr(){
-}
-
-void
-D2ClientMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
- if (!new_config) {
- isc_throw(D2ClientError,
- "D2ClientMgr cannot set DHCP-DDNS configuration to NULL.");
- }
-
- // Don't do anything unless configuration values are actually different.
- if (*d2_client_config_ != *new_config) {
- if (!new_config->getEnableUpdates()) {
- // Updating has been turned off, destroy current sender.
- // Any queued requests are tossed.
- name_change_sender_.reset();
- } else {
- dhcp_ddns::NameChangeSenderPtr new_sender;
- switch (new_config->getNcrProtocol()) {
- case dhcp_ddns::NCR_UDP: {
- /// @todo Should we be able to configure a sender's client
- /// side ip and port? We should certainly be able to
- /// configure a maximum queue size. These were overlooked
- /// but are covered in Trac# 3328.
- isc::asiolink::IOAddress any_addr("0.0.0.0");
- uint32_t any_port = 0;
- uint32_t queue_max = 1024;
-
- // Instantiate a new sender.
- new_sender.reset(new dhcp_ddns::NameChangeUDPSender(
- any_addr, any_port,
- new_config->getServerIp(),
- new_config->getServerPort(),
- new_config->getNcrFormat(),
- *this, queue_max));
- break;
- }
- default:
- // In theory you can't get here.
- isc_throw(D2ClientError, "Invalid sender Protocol: "
- << new_config->getNcrProtocol());
- break;
- }
-
- // Transfer queued requests from previous sender to the new one.
- /// @todo - Should we consider anything queued to be wrong?
- /// If only server values changed content might still be right but
- /// if content values changed (e.g. suffix or an override flag)
- /// then the queued contents might now be invalid. There is
- /// no way to regenerate them if they are wrong.
- if (name_change_sender_) {
- name_change_sender_->stopSending();
- new_sender->assumeQueue(*name_change_sender_);
- }
-
- // Replace the old sender with the new one.
- name_change_sender_ = new_sender;
- }
- }
-
- // Update the configuration.
- d2_client_config_ = new_config;
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_CFG_DHCP_DDNS)
- .arg(!ddnsEnabled() ? "DHCP-DDNS updates disabled" :
- "DHCP_DDNS updates enabled");
-}
-
-bool
-D2ClientMgr::ddnsEnabled() {
- return (d2_client_config_->getEnableUpdates());
-}
-
-const D2ClientConfigPtr&
-D2ClientMgr::getD2ClientConfig() const {
- return (d2_client_config_);
-}
-
-void
-D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
- bool& server_s, bool& server_n) const {
- // Per RFC 4702 & 4704, the client N and S flags allow the client to
- // request one of three options:
- //
- // N flag S flag Option
- // ------------------------------------------------------------------
- // 0 0 client wants to do forward updates (section 3.2)
- // 0 1 client wants server to do forward updates (section 3.3)
- // 1 0 client wants no one to do updates (section 3.4)
- // 1 1 invalid combination
- // (Note section numbers cited are for 4702, for 4704 see 5.1, 5.2, and 5.3)
- //
- // Make a bit mask from the client's flags and use it to set the response
- // flags accordingly.
- const uint8_t mask = ((client_n ? 2 : 0) + (client_s ? 1 : 0));
-
- switch (mask) {
- case 0:
- // If updates are enabled and we are overriding client delegation
- // then S flag should be true.
- server_s = (d2_client_config_->getEnableUpdates() &&
- d2_client_config_->getOverrideClientUpdate());
- break;
-
- case 1:
- server_s = d2_client_config_->getEnableUpdates();
- break;
-
- case 2:
- // If updates are enabled and we are overriding "no updates" then
- // S flag should be true.
- server_s = (d2_client_config_->getEnableUpdates() &&
- d2_client_config_->getOverrideNoUpdate());
- break;
-
- default:
- // RFCs declare this an invalid combination.
- isc_throw(isc::BadValue,
- "Invalid client FQDN - N and S cannot both be 1");
- break;
- }
-
- /// @todo Currently we are operating under the premise that N should be 1
- /// if the server is not doing updates nor do we have configuration
- /// controls to govern forward and reverse updates independently.
- /// In addition, the client FQDN flags cannot explicitly suggest what to
- /// do with reverse updates. They request either forward updates or no
- /// updates. In other words, the client cannot request the server do or
- /// not do reverse updates. For now, we are either going to do updates in
- /// both directions or none at all. If and when additional configuration
- /// parameters are added this logic will have to be reassessed.
- server_n = !server_s;
-}
-
-std::string
-D2ClientMgr::generateFqdn(const asiolink::IOAddress& address) const {
- std::string hostname = address.toText();
- std::replace(hostname.begin(), hostname.end(),
- (address.isV4() ? '.' : ':'), '-');
-
- std::ostringstream gen_name;
- gen_name << d2_client_config_->getGeneratedPrefix() << "-" << hostname;
- return (qualifyName(gen_name.str()));
-}
-
-std::string
-D2ClientMgr::qualifyName(const std::string& partial_name) const {
- std::ostringstream gen_name;
- gen_name << partial_name << "." << d2_client_config_->getQualifyingSuffix();
-
- // Tack on a trailing dot in case suffix doesn't have one.
- std::string str = gen_name.str();
- size_t len = str.length();
- if ((len > 0) && (str[len - 1] != '.')) {
- gen_name << ".";
- }
-
- return (gen_name.str());
-}
-
-void
-D2ClientMgr::startSender(D2ClientErrorHandler error_handler) {
- // Create a our own service instance when we are not being multiplexed
- // into an external service..
- private_io_service_.reset(new asiolink::IOService());
- startSender(error_handler, *private_io_service_);
-}
-
-void
-D2ClientMgr::startSender(D2ClientErrorHandler error_handler,
- isc::asiolink::IOService& io_service) {
- if (!name_change_sender_) {
- isc_throw(D2ClientError, "D2ClientMgr::startSender sender is null");
- }
-
- if (!error_handler) {
- isc_throw(D2ClientError, "D2ClientMgr::startSender handler is null");
- }
-
- // Set the error handler.
- client_error_handler_ = error_handler;
-
- // Remember the io service being used.
- sender_io_service_ = &io_service;
-
- // Start the sender on the given service.
- name_change_sender_->startSending(*sender_io_service_);
-
- /// @todo need to register sender's select-fd with IfaceMgr once 3315 is
- /// done.
-}
-
-bool
-D2ClientMgr::amSending() const {
- return (name_change_sender_ && name_change_sender_->amSending());
-}
-
-void
-D2ClientMgr::stopSender() {
- if (!name_change_sender_) {
- isc_throw(D2ClientError, "D2ClientMgr::stopSender sender is null");
- }
-
- /// @todo need to unregister sender's select-fd with IfaceMgr once 3315 is
- /// done.
-
- name_change_sender_->stopSending();
-}
-
-void
-D2ClientMgr::sendRequest(dhcp_ddns::NameChangeRequestPtr& ncr) {
- if (!name_change_sender_) {
- isc_throw(D2ClientError, "D2ClientMgr::sendRequest sender is null");
- }
-
- name_change_sender_->sendRequest(ncr);
-}
-
-size_t
-D2ClientMgr::getQueueSize() const {
- if (!name_change_sender_) {
- isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null");
- }
-
- return(name_change_sender_->getQueueSize());
-}
-
-
-const dhcp_ddns::NameChangeRequestPtr&
-D2ClientMgr::peekAt(const size_t index) const {
- if (!name_change_sender_) {
- isc_throw(D2ClientError, "D2ClientMgr::peekAt sender is null");
- }
-
- return (name_change_sender_->peekAt(index));
-}
-
-void
-D2ClientMgr::clearQueue() {
- if (!name_change_sender_) {
- isc_throw(D2ClientError, "D2ClientMgr::clearQueue sender is null");
- }
-
- name_change_sender_->clearSendQueue();
-}
-
-void
-D2ClientMgr::operator()(const dhcp_ddns::NameChangeSender::Result result,
- dhcp_ddns::NameChangeRequestPtr& ncr) {
- if (result == dhcp_ddns::NameChangeSender::SUCCESS) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_DHCP_DDNS_NCR_SENT).arg(ncr->toText());
- } else {
- // Handler is mandatory but test it just to be safe.
- /// @todo Until we have a better feel for how errors need to be
- /// handled we farm it out to the application layer.
- if (client_error_handler_) {
- // Handler is not supposed to throw, but catch just in case.
- try {
- (client_error_handler_)(result, ncr);
- } catch (const std::exception& ex) {
- LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION)
- .arg(ex.what());
- }
- } else {
- LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_HANDLER_NULL);
- }
- }
-}
-
-int
-D2ClientMgr::getSelectFd() {
- if (!amSending()) {
- isc_throw (D2ClientError, "D2ClientMgr::getSelectFd "
- " not in send mode");
- }
-
- return (name_change_sender_->getSelectFd());
-}
-
-void
-D2ClientMgr::runReadyIO() {
- if (!sender_io_service_) {
- // This should never happen.
- isc_throw(D2ClientError, "D2ClientMgr::runReadyIO"
- " sender io service is null");
- }
-
- // We shouldn't be here if IO isn't ready to execute.
- // By running poll we're gauranteed not to hang.
- /// @todo Trac# 3325 requests that asiolink::IOService provide a
- /// wrapper for poll().
- sender_io_service_->get_io_service().poll();
-}
-
-}; // namespace dhcp
-
-}; // namespace isc
diff --git a/src/lib/dhcpsrv/d2_client.h b/src/lib/dhcpsrv/d2_client.h
deleted file mode 100644
index 639747a..0000000
--- a/src/lib/dhcpsrv/d2_client.h
+++ /dev/null
@@ -1,554 +0,0 @@
-// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef D2_CLIENT_H
-#define D2_CLIENT_H
-
-/// @file d2_client.h Defines the D2ClientConfig and D2ClientMgr classes.
-/// This file defines the classes Kea uses to act as a client of the b10-
-/// dhcp-ddns module (aka D2).
-///
-#include <asiolink/io_address.h>
-#include <dhcp_ddns/ncr_io.h>
-#include <exceptions/exceptions.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <stdint.h>
-#include <string>
-#include <vector>
-
-namespace isc {
-namespace dhcp {
-
-
-/// An exception that is thrown if an error occurs while configuring
-/// the D2 DHCP DDNS client.
-class D2ClientError : public isc::Exception {
-public:
-
- /// @brief constructor
- ///
- /// @param file name of the file, where exception occurred
- /// @param line line of the file, where exception occurred
- /// @param what text description of the issue that caused exception
- D2ClientError(const char* file, size_t line, const char* what)
- : isc::Exception(file, line, what) {}
-};
-
-/// @brief Acts as a storage vault for D2 client configuration
-///
-/// A simple container class for storing and retrieving the configuration
-/// parameters associated with DHCP-DDNS and acting as a client of D2.
-/// Instances of this class may be constructed through configuration parsing.
-///
-class D2ClientConfig {
-public:
- /// @brief Constructor
- ///
- /// @param enable_updates Enables DHCP-DDNS updates
- /// @param server_ip IP address of the b10-dhcp-ddns server (IPv4 or IPv6)
- /// @param server_port IP port of the b10-dhcp-ddns server
- /// @param ncr_protocol Socket protocol to use with b10-dhcp-ddns
- /// Currently only UDP is supported.
- /// @param ncr_format Format of the b10-dhcp-ddns requests.
- /// Currently only JSON format is supported.
- /// @param always_include_fqdn Enables always including the FQDN option in
- /// DHCP responses.
- /// @param override_no_update Enables updates, even if clients request no
- /// updates.
- /// @param override_client_update Perform updates, even if client requested
- /// delegation.
- /// @param replace_client_name enables replacement of the domain-name
- /// supplied by the client with a generated name.
- /// @param generated_prefix Prefix to use when generating domain-names.
- /// @param qualifying_suffix Suffix to use to qualify partial domain-names.
- ///
- /// @throw D2ClientError if given an invalid protocol or format.
- D2ClientConfig(const bool enable_updates,
- const isc::asiolink::IOAddress& server_ip,
- const size_t server_port,
- const dhcp_ddns::NameChangeProtocol& ncr_protocol,
- const dhcp_ddns::NameChangeFormat& ncr_format,
- const bool always_include_fqdn,
- const bool override_no_update,
- const bool override_client_update,
- const bool replace_client_name,
- const std::string& generated_prefix,
- const std::string& qualifying_suffix);
-
- /// @brief Default constructor
- /// The default constructor creates an instance that has updates disabled.
- D2ClientConfig();
-
- /// @brief Destructor
- virtual ~D2ClientConfig();
-
- /// @brief Return whether or not DHCP-DDNS updating is enabled.
- bool getEnableUpdates() const {
- return(enable_updates_);
- }
-
- /// @brief Return the IP address of b10-dhcp-ddns (IPv4 or IPv6).
- const isc::asiolink::IOAddress& getServerIp() const {
- return(server_ip_);
- }
-
- /// @brief Return the IP port of b10-dhcp-ddns.
- size_t getServerPort() const {
- return(server_port_);
- }
-
- /// @brief Return the socket protocol to use with b10-dhcp-ddns.
- const dhcp_ddns::NameChangeProtocol& getNcrProtocol() const {
- return(ncr_protocol_);
- }
-
- /// @brief Return the b10-dhcp-ddns request format.
- const dhcp_ddns::NameChangeFormat& getNcrFormat() const {
- return(ncr_format_);
- }
-
- /// @brief Return whether or not FQDN is always included in DHCP responses.
- bool getAlwaysIncludeFqdn() const {
- return(always_include_fqdn_);
- }
-
- /// @brief Return if updates are done even if clients request no updates.
- bool getOverrideNoUpdate() const {
- return(override_no_update_);
- }
-
- /// @brief Return if updates are done even when clients request delegation.
- bool getOverrideClientUpdate() const {
- return(override_client_update_);
- }
-
- /// @brief Return whether or not client's domain-name is always replaced.
- bool getReplaceClientName() const {
- return(replace_client_name_);
- }
-
- /// @brief Return the prefix to use when generating domain-names.
- const std::string& getGeneratedPrefix() const {
- return(generated_prefix_);
- }
-
- /// @brief Return the suffix to use to qualify partial domain-names.
- const std::string& getQualifyingSuffix() const {
- return(qualifying_suffix_);
- }
-
- /// @brief Compares two D2ClientConfigs for equality
- bool operator == (const D2ClientConfig& other) const;
-
- /// @brief Compares two D2ClientConfigs for inequality
- bool operator != (const D2ClientConfig& other) const;
-
- /// @brief Generates a string representation of the class contents.
- std::string toText() const;
-
-protected:
- /// @brief Validates member values.
- ///
- /// Method is used by the constructor to validate member contents.
- ///
- /// @throw D2ClientError if given an invalid protocol or format.
- virtual void validateContents();
-
-private:
- /// @brief Indicates whether or not DHCP DDNS updating is enabled.
- bool enable_updates_;
-
- /// @brief IP address of the b10-dhcp-ddns server (IPv4 or IPv6).
- isc::asiolink::IOAddress server_ip_;
-
- /// @brief IP port of the b10-dhcp-ddns server.
- size_t server_port_;
-
- /// @brief The socket protocol to use with b10-dhcp-ddns.
- /// Currently only UDP is supported.
- dhcp_ddns::NameChangeProtocol ncr_protocol_;
-
- /// @brief Format of the b10-dhcp-ddns requests.
- /// Currently only JSON format is supported.
- dhcp_ddns::NameChangeFormat ncr_format_;
-
- /// @brief Should Kea always include the FQDN option in its response.
- bool always_include_fqdn_;
-
- /// @brief Should Kea perform updates, even if client requested no updates.
- /// Overrides the client request for no updates via the N flag.
- bool override_no_update_;
-
- /// @brief Should Kea perform updates, even if client requested delegation.
- bool override_client_update_;
-
- /// @brief Should Kea replace the domain-name supplied by the client.
- bool replace_client_name_;
-
- /// @brief Prefix Kea should use when generating domain-names.
- std::string generated_prefix_;
-
- /// @brief Suffix Kea should use when to qualify partial domain-names.
- std::string qualifying_suffix_;
-};
-
-std::ostream&
-operator<<(std::ostream& os, const D2ClientConfig& config);
-
-/// @brief Defines a pointer for D2ClientConfig instances.
-typedef boost::shared_ptr<D2ClientConfig> D2ClientConfigPtr;
-
-/// @brief Defines the type for D2 IO error handler.
-/// This callback is invoked when a send to b10-dhcp-ddns completes with a
-/// failed status. This provides the application layer (Kea) with a means to
-/// handle the error appropriately.
-///
-/// @param result Result code of the send operation.
-/// @param ncr NameChangeRequest which failed to send.
-///
-/// @note Handlers are expected not to throw. In the event a hanlder does
-/// throw invoking code logs the exception and then swallows it.
-typedef
-boost::function<void(const dhcp_ddns::NameChangeSender::Result result,
- dhcp_ddns::NameChangeRequestPtr& ncr)> D2ClientErrorHandler;
-
-/// @brief D2ClientMgr isolates Kea from the details of being a D2 client.
-///
-/// Provides services for managing the current dhcp-ddns configuration and
-/// as well as communications with b10-dhcp-ddns. Regarding configuration it
-/// provides services to store, update, and access the current dhcp-ddns
-/// configuration. As for b10-dhcp-ddns communications, D2ClientMgr creates
-/// maintains a NameChangeSender appropriate to the current configuration and
-/// provides services to start, stop, and post NCRs to the sender. Additionally
-/// there are methods to examine the queue of requests currently waiting for
-/// transmission.
-///
-/// The manager also provides the mechanics to integrate the ASIO-based IO
-/// used by the NCR IPC with the select-driven IO used by Kea. Senders expose
-/// a file descriptor, the "select-fd" that can monitored for read-readiness
-/// with the select() function (or variants). D2ClientMgr provides a method,
-/// runReadyIO(), that will process all ready events on a sender's
-/// IOservice. Track# 3315 is extending Kea's IfaceMgr to support the
-/// registration of multiple external sockets with callbacks that are then
-/// monitored with IO readiness via select().
-/// @todo D2ClientMgr will be modified to register the sender's select-fd and
-/// runReadyIO() with IfaceMgr when entering the send mode and will
-/// unregister when exiting send mode.
-///
-/// To place the manager in send mode, the calling layer must supply an error
-/// handler and optionally an IOService instance. The error handler is invoked
-/// if a send completes with a failed status. This provides the calling layer
-/// an opportunity act upon the error.
-///
-/// If the caller supplies an IOService, that service will be used to process
-/// the sender's IO. If not supplied, D2ClientMgr pass a private IOService
-/// into the sender. Using a private service isolates the sender's IO from
-/// any other services.
-///
-class D2ClientMgr : public dhcp_ddns::NameChangeSender::RequestSendHandler {
-public:
- /// @brief Constructor
- ///
- /// Default constructor which constructs an instance which has DHCP-DDNS
- /// updates disabled.
- D2ClientMgr();
-
- /// @brief Destructor.
- ~D2ClientMgr();
-
- /// @brief Updates the DHCP-DDNS client configuration to the given value.
- ///
- /// @param new_config pointer to the new client configuration.
- /// @throw D2ClientError if passed an empty pointer.
- void setD2ClientConfig(D2ClientConfigPtr& new_config);
-
- /// @brief Convenience method for checking if DHCP-DDNS is enabled.
- ///
- /// @return True if the D2 configuration is enabled.
- bool ddnsEnabled();
-
- /// @brief Fetches the DHCP-DDNS configuration pointer.
- ///
- /// @return a reference to the current configuration pointer.
- const D2ClientConfigPtr& getD2ClientConfig() const;
-
- /// @brief Determines server flags based on configuration and client flags.
- ///
- /// This method uses input values for the client's FQDN S and N flags, in
- /// conjunction with the configuration parameters updates-enabled, override-
- /// no-updates, and override-client-updates to determine the values that
- /// should be used for the server's FQDN S and N flags.
- /// The logic in this method is based upon RFCs 4702 and 4704.
- ///
- /// @param client_s S Flag from the client's FQDN
- /// @param client_n N Flag from the client's FQDN
- /// @param server_s [out] S Flag for the server's FQDN
- /// @param server_n [out] N Flag for the server's FQDN
- ///
- /// @throw isc::BadValue if client_s and client_n are both 1 as this is
- /// an invalid combination per RFCs.
- void analyzeFqdn(const bool client_s, const bool client_n, bool& server_s,
- bool& server_n) const;
-
- /// @brief Builds a FQDN based on the configuration and given IP address.
- ///
- /// Using the current values for generated-prefix, qualifying-suffix and
- /// an IP address, this method constructs a fully qualified domain name.
- /// It supports both IPv4 and IPv6 addresses. The format of the name
- /// is as follows:
- ///
- /// <generated-prefix>-<ip address>.<qualifying-suffix>.
- ///
- /// <ip-address> is the result of IOAddress.toText() with the delimiters
- /// ('.' for IPv4 or ':' for IPv6) replaced with a hyphen, '-'.
- ///
- /// @param address IP address from which to derive the name (IPv4 or IPv6)
- ///
- /// @return std::string containing the generated name.
- std::string generateFqdn(const asiolink::IOAddress& address) const;
-
- /// @brief Adds a qualifying suffix to a given domain name
- ///
- /// Constructs a FQDN based on the configured qualifying-suffix and
- /// a partial domain name as follows:
- ///
- /// <partial_name>.<qualifying-suffix>.
- /// Note it will add a trailing '.' should qualifying-suffix not end with
- /// one.
- ///
- /// @param partial_name domain name to qualify
- ///
- /// @return std::string containing the qualified name.
- std::string qualifyName(const std::string& partial_name) const;
-
- /// @brief Set server FQDN flags based on configuration and a given FQDN
- ///
- /// Templated wrapper around the analyzeFqdn() allowing that method to
- /// be used for either IPv4 or IPv6 processing. This methods resets all
- /// of the flags in the response to zero and then sets the S,N, and O
- /// flags. Any other flags are the responsiblity of the invoking layer.
- ///
- /// @param fqdn FQDN option from which to read client (inbound) flags
- /// @param fqdn_resp FQDN option to update with the server (outbound) flags
- /// @tparam T FQDN Option class containing the FQDN data such as
- /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
- template <class T>
- void adjustFqdnFlags(const T& fqdn, T& fqdn_resp);
-
- /// @brief Set server FQDN name based on configuration and a given FQDN
- ///
- /// Templated method which adjusts the domain name value and type in
- /// a server FQDN from a client (inbound) FQDN and the current
- /// configuration. The logic is as follows:
- ///
- /// If replace-client-name is true or the supplied name is empty, the
- /// server FQDN is set to ""/PARTIAL.
- ///
- /// If replace-client-name is false and the supplied name is a partial
- /// name the server FQDN is set to the supplied name qualified by
- /// appending the qualifying-suffix.
- ///
- /// If replace-client-name is false and the supplied name is a fully
- /// qualified name, set the server FQDN to the supplied name.
- ///
- /// @param fqdn FQDN option from which to get client (inbound) name
- /// @param fqdn_resp FQDN option to update with the adjusted name
- /// @tparam T FQDN Option class containing the FQDN data such as
- /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
- template <class T>
- void adjustDomainName(const T& fqdn, T& fqdn_resp);
-
- /// @brief Enables sending NameChangeRequests to b10-dhcp-ddns
- ///
- /// Places the NameChangeSender into send mode. This instructs the
- /// sender to begin dequeuing and transmitting requests and to accept
- /// additional requests via the sendRequest() method.
- ///
- /// @param error_handler application level error handler to cope with
- /// sends that complete with a failed status. A valid function must be
- /// supplied as the manager cannot know how an application should deal
- /// with send failures.
- /// @param io_service IOService to be used for sender IO event processing
- ///
- /// @throw D2ClientError if sender instance is null. Underlying layer
- /// may throw NCRSenderExceptions exceptions.
- void startSender(D2ClientErrorHandler error_handler,
- isc::asiolink::IOService& io_service);
-
- /// @brief Enables sending NameChangeRequests to b10-dhcp-ddns
- ///
- /// Places the NameChangeSender into send mode. This instructs the
- /// sender to begin dequeuing and transmitting requests and to accept
- /// additional requests via the sendRequest() method. The manager
- /// will create a new, private instance of an IOService for the sender
- /// to use for IO event processing.
- ///
- /// @param error_handler application level error handler to cope with
- /// sends that complete with a failed status. A valid function must be
- /// supplied as the manager cannot know how an application should deal
- /// with send failures.
- ///
- /// @throw D2ClientError if sender instance is null. Underlying layer
- /// may throw NCRSenderExceptions exceptions.
- void startSender(D2ClientErrorHandler error_handler);
-
- /// @brief Returns true if the sender is in send mode, false otherwise.
- ///
- /// A true value indicates that the sender is present and in accepting
- /// messages for transmission, false otherwise.
- bool amSending() const;
-
- /// @brief Disables sending NameChangeRequests to b10-dhcp-ddns
- ///
- /// Takes the NameChangeSender out of send mode. The sender will stop
- /// transmitting requests, though any queued requests remain queued.
- /// Attempts to queue additional requests via sendRequest will fail.
- ///
- /// @throw D2ClientError if sender instance is null. Underlying layer
- /// may throw NCRSenderExceptions exceptions.
- void stopSender();
-
- /// @brief Send the given NameChangeRequests to b10-dhcp-ddns
- ///
- /// Passes NameChangeRequests to the NCR sender for transmission to
- /// b10-dhcp-ddns.
- ///
- /// @param ncr NameChangeRequest to send
- ///
- /// @throw D2ClientError if sender instance is null. Underlying layer
- /// may throw NCRSenderExceptions exceptions.
- void sendRequest(dhcp_ddns::NameChangeRequestPtr& ncr);
-
- /// @brief Returns the number of NCRs queued for transmission.
- size_t getQueueSize() const;
-
- /// @brief Returns the nth NCR queued for transmission.
- ///
- /// Note that the entry is not removed from the queue.
- /// @param index the index of the entry in the queue to fetch.
- /// Valid values are 0 (front of the queue) to (queue size - 1).
- /// @note This method is for test purposes only.
- ///
- /// @return Pointer reference to the queue entry.
- ///
- /// @throw D2ClientError if sender instance is null. Underlying layer
- /// may throw NCRSenderExceptions exceptions.
- const dhcp_ddns::NameChangeRequestPtr& peekAt(const size_t index) const;
-
- /// @brief Removes all NCRs queued for transmission.
- ///
- /// @throw D2ClientError if sender instance is null. Underlying layer
- /// may throw NCRSenderExceptions exceptions.
- void clearQueue();
-
- /// @brief Processes sender IO events
- ///
- /// Runs all handlers ready for execution on the sender's IO service.
- void runReadyIO();
-
-protected:
- /// @brief Function operator implementing the NCR sender callback.
- ///
- /// This method is invoked each time the NameChangeSender completes
- /// an asychronous send.
- ///
- /// @param result contains that send outcome status.
- /// @param ncr is a pointer to the NameChangeRequest that was
- /// delivered (or attempted).
- ///
- /// @throw This method MUST NOT throw.
- virtual void operator ()(const dhcp_ddns::NameChangeSender::Result result,
- dhcp_ddns::NameChangeRequestPtr& ncr);
-
- /// @brief Fetches the sender's select-fd.
- ///
- /// The select-fd may be used with select() or poll(). If the sender has
- /// IO waiting to process, the fd will evaluate as !EWOULDBLOCK.
- /// @note This is only exposed for testing purposes.
- ///
- /// @return The sender's select-fd
- ///
- /// @throw D2ClientError if the sender does not exist or is not in send
- /// mode.
- int getSelectFd();
-
-private:
- /// @brief Container class for DHCP-DDNS configuration parameters.
- D2ClientConfigPtr d2_client_config_;
-
- /// @brief Pointer to the current interface to DHCP-DDNS.
- dhcp_ddns::NameChangeSenderPtr name_change_sender_;
-
- /// @brief Private IOService to use if calling layer doesn't wish to
- /// supply one.
- boost::shared_ptr<asiolink::IOService> private_io_service_;
-
- /// @brief Application supplied error handler invoked when a send
- /// completes with a failed status.
- D2ClientErrorHandler client_error_handler_;
-
- /// @brief Pointer to the IOService currently being used by the sender.
- /// @note We need to remember the io_service given to the sender however
- /// we may have received only a referenece to it from the calling layer.
- /// Use a raw pointer to store it. This value should never be exposed
- /// and is only valid while in send mode.
- asiolink::IOService* sender_io_service_;
-};
-
-template <class T>
-void
-D2ClientMgr::adjustFqdnFlags(const T& fqdn, T& fqdn_resp) {
- bool server_s = false;
- bool server_n = false;
- analyzeFqdn(fqdn.getFlag(T::FLAG_S), fqdn.getFlag(T::FLAG_N),
- server_s, server_n);
-
- // Reset the flags to zero to avoid triggering N and S both 1 check.
- fqdn_resp.resetFlags();
-
- // Set S and N flags.
- fqdn_resp.setFlag(T::FLAG_S, server_s);
- fqdn_resp.setFlag(T::FLAG_N, server_n);
-
- // Set O flag true if server S overrides client S.
- fqdn_resp.setFlag(T::FLAG_O, (fqdn.getFlag(T::FLAG_S) != server_s));
-}
-
-
-template <class T>
-void
-D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp) {
- // If we're configured to replace it or the supplied name is blank
- // set the response name to blank.
- if (d2_client_config_->getReplaceClientName() ||
- fqdn.getDomainName().empty()) {
- fqdn_resp.setDomainName("", T::PARTIAL);
- } else {
- // If the supplied name is partial, qualify it by adding the suffix.
- if (fqdn.getDomainNameType() == T::PARTIAL) {
- fqdn_resp.setDomainName(qualifyName(fqdn.getDomainName()), T::FULL);
- }
- }
-}
-
-/// @brief Defines a pointer for D2ClientMgr instances.
-typedef boost::shared_ptr<D2ClientMgr> D2ClientMgrPtr;
-
-
-} // namespace isc
-} // namespace dhcp
-
-#endif
diff --git a/src/lib/dhcpsrv/d2_client_cfg.cc b/src/lib/dhcpsrv/d2_client_cfg.cc
new file mode 100644
index 0000000..cb04eb1
--- /dev/null
+++ b/src/lib/dhcpsrv/d2_client_cfg.cc
@@ -0,0 +1,141 @@
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp_ddns/ncr_udp.h>
+#include <dhcpsrv/d2_client_cfg.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+
+#include <string>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+D2ClientConfig::D2ClientConfig(const bool enable_updates,
+ const isc::asiolink::IOAddress& server_ip,
+ const size_t server_port,
+ const dhcp_ddns::
+ NameChangeProtocol& ncr_protocol,
+ const dhcp_ddns::
+ NameChangeFormat& ncr_format,
+ const bool always_include_fqdn,
+ const bool override_no_update,
+ const bool override_client_update,
+ const bool replace_client_name,
+ const std::string& generated_prefix,
+ const std::string& qualifying_suffix)
+ : enable_updates_(enable_updates),
+ server_ip_(server_ip),
+ server_port_(server_port),
+ ncr_protocol_(ncr_protocol),
+ ncr_format_(ncr_format),
+ always_include_fqdn_(always_include_fqdn),
+ override_no_update_(override_no_update),
+ override_client_update_(override_client_update),
+ replace_client_name_(replace_client_name),
+ generated_prefix_(generated_prefix),
+ qualifying_suffix_(qualifying_suffix) {
+ validateContents();
+}
+
+D2ClientConfig::D2ClientConfig()
+ : enable_updates_(false),
+ server_ip_(isc::asiolink::IOAddress("0.0.0.0")),
+ server_port_(0),
+ ncr_protocol_(dhcp_ddns::NCR_UDP),
+ ncr_format_(dhcp_ddns::FMT_JSON),
+ always_include_fqdn_(false),
+ override_no_update_(false),
+ override_client_update_(false),
+ replace_client_name_(false),
+ generated_prefix_("myhost"),
+ qualifying_suffix_("example.com") {
+ validateContents();
+}
+
+D2ClientConfig::~D2ClientConfig(){};
+
+void
+D2ClientConfig::validateContents() {
+ if (ncr_format_ != dhcp_ddns::FMT_JSON) {
+ isc_throw(D2ClientError, "D2ClientConfig: NCR Format:"
+ << dhcp_ddns::ncrFormatToString(ncr_format_)
+ << " is not yet supported");
+ }
+
+ if (ncr_protocol_ != dhcp_ddns::NCR_UDP) {
+ isc_throw(D2ClientError, "D2ClientConfig: NCR Protocol:"
+ << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
+ << " is not yet supported");
+ }
+
+ /// @todo perhaps more validation we should do yet?
+ /// Are there any invalid combinations of options we need to test against?
+}
+
+bool
+D2ClientConfig::operator == (const D2ClientConfig& other) const {
+ return ((enable_updates_ == other.enable_updates_) &&
+ (server_ip_ == other.server_ip_) &&
+ (server_port_ == other.server_port_) &&
+ (ncr_protocol_ == other.ncr_protocol_) &&
+ (ncr_format_ == other.ncr_format_) &&
+ (always_include_fqdn_ == other.always_include_fqdn_) &&
+ (override_no_update_ == other.override_no_update_) &&
+ (override_client_update_ == other.override_client_update_) &&
+ (replace_client_name_ == other.replace_client_name_) &&
+ (generated_prefix_ == other.generated_prefix_) &&
+ (qualifying_suffix_ == other.qualifying_suffix_));
+}
+
+bool
+D2ClientConfig::operator != (const D2ClientConfig& other) const {
+ return (!(*this == other));
+}
+
+std::string
+D2ClientConfig::toText() const {
+ std::ostringstream stream;
+
+ stream << "enable_updates: " << (enable_updates_ ? "yes" : "no");
+ if (enable_updates_) {
+ stream << ", server_ip: " << server_ip_.toText()
+ << ", server_port: " << server_port_
+ << ", ncr_protocol: " << ncr_protocol_
+ << ", ncr_format: " << ncr_format_
+ << ", always_include_fqdn: " << (always_include_fqdn_ ?
+ "yes" : "no")
+ << ", override_no_update: " << (override_no_update_ ?
+ "yes" : "no")
+ << ", override_client_update: " << (override_client_update_ ?
+ "yes" : "no")
+ << ", replace_client_name: " << (replace_client_name_ ?
+ "yes" : "no")
+ << ", generated_prefix: [" << generated_prefix_ << "]"
+ << ", qualifying_suffix: [" << qualifying_suffix_ << "]";
+ }
+
+ return (stream.str());
+}
+
+std::ostream&
+operator<<(std::ostream& os, const D2ClientConfig& config) {
+ os << config.toText();
+ return (os);
+}
+
+}; // namespace dhcp
+
+}; // namespace isc
diff --git a/src/lib/dhcpsrv/d2_client_cfg.h b/src/lib/dhcpsrv/d2_client_cfg.h
new file mode 100644
index 0000000..fd9d242
--- /dev/null
+++ b/src/lib/dhcpsrv/d2_client_cfg.h
@@ -0,0 +1,217 @@
+// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef D2_CLIENT_CFG_H
+#define D2_CLIENT_CFG_H
+
+/// @file d2_client_cfg.h Defines the D2ClientConfig class.
+/// This file defines the classes Kea uses to manage configuration needed to
+/// act as a client of the b10-dhcp-ddns module (aka D2).
+///
+#include <asiolink/io_address.h>
+#include <dhcp_ddns/ncr_io.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+
+/// An exception that is thrown if an error occurs while configuring
+/// the D2 DHCP DDNS client.
+class D2ClientError : public isc::Exception {
+public:
+
+ /// @brief constructor
+ ///
+ /// @param file name of the file, where exception occurred
+ /// @param line line of the file, where exception occurred
+ /// @param what text description of the issue that caused exception
+ D2ClientError(const char* file, size_t line, const char* what)
+ : isc::Exception(file, line, what) {}
+};
+
+/// @brief Acts as a storage vault for D2 client configuration
+///
+/// A simple container class for storing and retrieving the configuration
+/// parameters associated with DHCP-DDNS and acting as a client of D2.
+/// Instances of this class may be constructed through configuration parsing.
+///
+class D2ClientConfig {
+public:
+ /// @brief Constructor
+ ///
+ /// @param enable_updates Enables DHCP-DDNS updates
+ /// @param server_ip IP address of the b10-dhcp-ddns server (IPv4 or IPv6)
+ /// @param server_port IP port of the b10-dhcp-ddns server
+ /// @param ncr_protocol Socket protocol to use with b10-dhcp-ddns
+ /// Currently only UDP is supported.
+ /// @param ncr_format Format of the b10-dhcp-ddns requests.
+ /// Currently only JSON format is supported.
+ /// @param always_include_fqdn Enables always including the FQDN option in
+ /// DHCP responses.
+ /// @param override_no_update Enables updates, even if clients request no
+ /// updates.
+ /// @param override_client_update Perform updates, even if client requested
+ /// delegation.
+ /// @param replace_client_name enables replacement of the domain-name
+ /// supplied by the client with a generated name.
+ /// @param generated_prefix Prefix to use when generating domain-names.
+ /// @param qualifying_suffix Suffix to use to qualify partial domain-names.
+ ///
+ /// @throw D2ClientError if given an invalid protocol or format.
+ D2ClientConfig(const bool enable_updates,
+ const isc::asiolink::IOAddress& server_ip,
+ const size_t server_port,
+ const dhcp_ddns::NameChangeProtocol& ncr_protocol,
+ const dhcp_ddns::NameChangeFormat& ncr_format,
+ const bool always_include_fqdn,
+ const bool override_no_update,
+ const bool override_client_update,
+ const bool replace_client_name,
+ const std::string& generated_prefix,
+ const std::string& qualifying_suffix);
+
+ /// @brief Default constructor
+ /// The default constructor creates an instance that has updates disabled.
+ D2ClientConfig();
+
+ /// @brief Destructor
+ virtual ~D2ClientConfig();
+
+ /// @brief Return whether or not DHCP-DDNS updating is enabled.
+ bool getEnableUpdates() const {
+ return(enable_updates_);
+ }
+
+ /// @brief Return the IP address of b10-dhcp-ddns (IPv4 or IPv6).
+ const isc::asiolink::IOAddress& getServerIp() const {
+ return(server_ip_);
+ }
+
+ /// @brief Return the IP port of b10-dhcp-ddns.
+ size_t getServerPort() const {
+ return(server_port_);
+ }
+
+ /// @brief Return the socket protocol to use with b10-dhcp-ddns.
+ const dhcp_ddns::NameChangeProtocol& getNcrProtocol() const {
+ return(ncr_protocol_);
+ }
+
+ /// @brief Return the b10-dhcp-ddns request format.
+ const dhcp_ddns::NameChangeFormat& getNcrFormat() const {
+ return(ncr_format_);
+ }
+
+ /// @brief Return whether or not FQDN is always included in DHCP responses.
+ bool getAlwaysIncludeFqdn() const {
+ return(always_include_fqdn_);
+ }
+
+ /// @brief Return if updates are done even if clients request no updates.
+ bool getOverrideNoUpdate() const {
+ return(override_no_update_);
+ }
+
+ /// @brief Return if updates are done even when clients request delegation.
+ bool getOverrideClientUpdate() const {
+ return(override_client_update_);
+ }
+
+ /// @brief Return whether or not client's domain-name is always replaced.
+ bool getReplaceClientName() const {
+ return(replace_client_name_);
+ }
+
+ /// @brief Return the prefix to use when generating domain-names.
+ const std::string& getGeneratedPrefix() const {
+ return(generated_prefix_);
+ }
+
+ /// @brief Return the suffix to use to qualify partial domain-names.
+ const std::string& getQualifyingSuffix() const {
+ return(qualifying_suffix_);
+ }
+
+ /// @brief Compares two D2ClientConfigs for equality
+ bool operator == (const D2ClientConfig& other) const;
+
+ /// @brief Compares two D2ClientConfigs for inequality
+ bool operator != (const D2ClientConfig& other) const;
+
+ /// @brief Generates a string representation of the class contents.
+ std::string toText() const;
+
+protected:
+ /// @brief Validates member values.
+ ///
+ /// Method is used by the constructor to validate member contents.
+ ///
+ /// @throw D2ClientError if given an invalid protocol or format.
+ virtual void validateContents();
+
+private:
+ /// @brief Indicates whether or not DHCP DDNS updating is enabled.
+ bool enable_updates_;
+
+ /// @brief IP address of the b10-dhcp-ddns server (IPv4 or IPv6).
+ isc::asiolink::IOAddress server_ip_;
+
+ /// @brief IP port of the b10-dhcp-ddns server.
+ size_t server_port_;
+
+ /// @brief The socket protocol to use with b10-dhcp-ddns.
+ /// Currently only UDP is supported.
+ dhcp_ddns::NameChangeProtocol ncr_protocol_;
+
+ /// @brief Format of the b10-dhcp-ddns requests.
+ /// Currently only JSON format is supported.
+ dhcp_ddns::NameChangeFormat ncr_format_;
+
+ /// @brief Should Kea always include the FQDN option in its response.
+ bool always_include_fqdn_;
+
+ /// @brief Should Kea perform updates, even if client requested no updates.
+ /// Overrides the client request for no updates via the N flag.
+ bool override_no_update_;
+
+ /// @brief Should Kea perform updates, even if client requested delegation.
+ bool override_client_update_;
+
+ /// @brief Should Kea replace the domain-name supplied by the client.
+ bool replace_client_name_;
+
+ /// @brief Prefix Kea should use when generating domain-names.
+ std::string generated_prefix_;
+
+ /// @brief Suffix Kea should use when to qualify partial domain-names.
+ std::string qualifying_suffix_;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const D2ClientConfig& config);
+
+/// @brief Defines a pointer for D2ClientConfig instances.
+typedef boost::shared_ptr<D2ClientConfig> D2ClientConfigPtr;
+
+} // namespace isc
+} // namespace dhcp
+
+#endif
diff --git a/src/lib/dhcpsrv/d2_client_mgr.cc b/src/lib/dhcpsrv/d2_client_mgr.cc
new file mode 100644
index 0000000..e43bbdb
--- /dev/null
+++ b/src/lib/dhcpsrv/d2_client_mgr.cc
@@ -0,0 +1,332 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/iface_mgr.h>
+#include <dhcp_ddns/ncr_udp.h>
+#include <dhcpsrv/d2_client_mgr.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+
+#include <boost/bind.hpp>
+
+#include <string>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()),
+ name_change_sender_(), private_io_service_(),
+ registered_select_fd_(dhcp_ddns::WatchSocket::INVALID_SOCKET) {
+ // Default constructor initializes with a disabled configuration.
+}
+
+D2ClientMgr::~D2ClientMgr(){
+ stopSender();
+}
+
+void
+D2ClientMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
+ if (!new_config) {
+ isc_throw(D2ClientError,
+ "D2ClientMgr cannot set DHCP-DDNS configuration to NULL.");
+ }
+
+ // Don't do anything unless configuration values are actually different.
+ if (*d2_client_config_ != *new_config) {
+ if (!new_config->getEnableUpdates()) {
+ // Updating has been turned off, destroy current sender.
+ // Any queued requests are tossed.
+ name_change_sender_.reset();
+ } else {
+ dhcp_ddns::NameChangeSenderPtr new_sender;
+ switch (new_config->getNcrProtocol()) {
+ case dhcp_ddns::NCR_UDP: {
+ /// @todo Should we be able to configure a sender's client
+ /// side ip and port? We should certainly be able to
+ /// configure a maximum queue size. These were overlooked
+ /// but are covered in Trac# 3328.
+ isc::asiolink::IOAddress any_addr("0.0.0.0");
+ uint32_t any_port = 0;
+ uint32_t queue_max = 1024;
+
+ // Instantiate a new sender.
+ new_sender.reset(new dhcp_ddns::NameChangeUDPSender(
+ any_addr, any_port,
+ new_config->getServerIp(),
+ new_config->getServerPort(),
+ new_config->getNcrFormat(),
+ *this, queue_max));
+ break;
+ }
+ default:
+ // In theory you can't get here.
+ isc_throw(D2ClientError, "Invalid sender Protocol: "
+ << new_config->getNcrProtocol());
+ break;
+ }
+
+ // Transfer queued requests from previous sender to the new one.
+ /// @todo - Should we consider anything queued to be wrong?
+ /// If only server values changed content might still be right but
+ /// if content values changed (e.g. suffix or an override flag)
+ /// then the queued contents might now be invalid. There is
+ /// no way to regenerate them if they are wrong.
+ if (name_change_sender_) {
+ name_change_sender_->stopSending();
+ new_sender->assumeQueue(*name_change_sender_);
+ }
+
+ // Replace the old sender with the new one.
+ name_change_sender_ = new_sender;
+ }
+ }
+
+ // Update the configuration.
+ d2_client_config_ = new_config;
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_CFG_DHCP_DDNS)
+ .arg(!ddnsEnabled() ? "DHCP-DDNS updates disabled" :
+ "DHCP_DDNS updates enabled");
+}
+
+bool
+D2ClientMgr::ddnsEnabled() {
+ return (d2_client_config_->getEnableUpdates());
+}
+
+const D2ClientConfigPtr&
+D2ClientMgr::getD2ClientConfig() const {
+ return (d2_client_config_);
+}
+
+void
+D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
+ bool& server_s, bool& server_n) const {
+ // Per RFC 4702 & 4704, the client N and S flags allow the client to
+ // request one of three options:
+ //
+ // N flag S flag Option
+ // ------------------------------------------------------------------
+ // 0 0 client wants to do forward updates (section 3.2)
+ // 0 1 client wants server to do forward updates (section 3.3)
+ // 1 0 client wants no one to do updates (section 3.4)
+ // 1 1 invalid combination
+ // (Note section numbers cited are for 4702, for 4704 see 5.1, 5.2, and 5.3)
+ //
+ // Make a bit mask from the client's flags and use it to set the response
+ // flags accordingly.
+ const uint8_t mask = ((client_n ? 2 : 0) + (client_s ? 1 : 0));
+
+ switch (mask) {
+ case 0:
+ // If updates are enabled and we are overriding client delegation
+ // then S flag should be true.
+ server_s = (d2_client_config_->getEnableUpdates() &&
+ d2_client_config_->getOverrideClientUpdate());
+ break;
+
+ case 1:
+ server_s = d2_client_config_->getEnableUpdates();
+ break;
+
+ case 2:
+ // If updates are enabled and we are overriding "no updates" then
+ // S flag should be true.
+ server_s = (d2_client_config_->getEnableUpdates() &&
+ d2_client_config_->getOverrideNoUpdate());
+ break;
+
+ default:
+ // RFCs declare this an invalid combination.
+ isc_throw(isc::BadValue,
+ "Invalid client FQDN - N and S cannot both be 1");
+ break;
+ }
+
+ /// @todo Currently we are operating under the premise that N should be 1
+ /// if the server is not doing updates nor do we have configuration
+ /// controls to govern forward and reverse updates independently.
+ /// In addition, the client FQDN flags cannot explicitly suggest what to
+ /// do with reverse updates. They request either forward updates or no
+ /// updates. In other words, the client cannot request the server do or
+ /// not do reverse updates. For now, we are either going to do updates in
+ /// both directions or none at all. If and when additional configuration
+ /// parameters are added this logic will have to be reassessed.
+ server_n = !server_s;
+}
+
+std::string
+D2ClientMgr::generateFqdn(const asiolink::IOAddress& address) const {
+ std::string hostname = address.toText();
+ std::replace(hostname.begin(), hostname.end(),
+ (address.isV4() ? '.' : ':'), '-');
+
+ std::ostringstream gen_name;
+ gen_name << d2_client_config_->getGeneratedPrefix() << "-" << hostname;
+ return (qualifyName(gen_name.str()));
+}
+
+std::string
+D2ClientMgr::qualifyName(const std::string& partial_name) const {
+ std::ostringstream gen_name;
+ gen_name << partial_name << "." << d2_client_config_->getQualifyingSuffix();
+
+ // Tack on a trailing dot in case suffix doesn't have one.
+ std::string str = gen_name.str();
+ size_t len = str.length();
+ if ((len > 0) && (str[len - 1] != '.')) {
+ gen_name << ".";
+ }
+
+ return (gen_name.str());
+}
+
+void
+D2ClientMgr::startSender(D2ClientErrorHandler error_handler) {
+ // Create a our own service instance when we are not being multiplexed
+ // into an external service..
+ private_io_service_.reset(new asiolink::IOService());
+ startSender(error_handler, *private_io_service_);
+}
+
+void
+D2ClientMgr::startSender(D2ClientErrorHandler error_handler,
+ isc::asiolink::IOService& io_service) {
+ if (!name_change_sender_) {
+ isc_throw(D2ClientError, "D2ClientMgr::startSender sender is null");
+ }
+
+ if (!error_handler) {
+ isc_throw(D2ClientError, "D2ClientMgr::startSender handler is null");
+ }
+
+ // Set the error handler.
+ client_error_handler_ = error_handler;
+
+ // Start the sender on the given service.
+ name_change_sender_->startSending(io_service);
+
+ // Register sender's select-fd with IfaceMgr.
+ // We need to remember the fd that is registered so we can unregister later.
+ // IO error handling in the sender may alter its select-fd.
+ registered_select_fd_ = name_change_sender_->getSelectFd();
+ IfaceMgr::instance().addExternalSocket(registered_select_fd_,
+ boost::bind(&D2ClientMgr::runReadyIO,
+ this));
+}
+
+bool
+D2ClientMgr::amSending() const {
+ return (name_change_sender_ && name_change_sender_->amSending());
+}
+
+void
+D2ClientMgr::stopSender() {
+ /// Unregister sender's select-fd.
+ if (registered_select_fd_ != dhcp_ddns::WatchSocket::INVALID_SOCKET) {
+ IfaceMgr::instance().deleteExternalSocket(registered_select_fd_);
+ registered_select_fd_ = dhcp_ddns::WatchSocket::INVALID_SOCKET;
+ }
+
+ // If its not null, call stop.
+ if (name_change_sender_) {
+ name_change_sender_->stopSending();
+ }
+}
+
+void
+D2ClientMgr::sendRequest(dhcp_ddns::NameChangeRequestPtr& ncr) {
+ if (!name_change_sender_) {
+ isc_throw(D2ClientError, "D2ClientMgr::sendRequest sender is null");
+ }
+
+ name_change_sender_->sendRequest(ncr);
+}
+
+size_t
+D2ClientMgr::getQueueSize() const {
+ if (!name_change_sender_) {
+ isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null");
+ }
+
+ return(name_change_sender_->getQueueSize());
+}
+
+
+const dhcp_ddns::NameChangeRequestPtr&
+D2ClientMgr::peekAt(const size_t index) const {
+ if (!name_change_sender_) {
+ isc_throw(D2ClientError, "D2ClientMgr::peekAt sender is null");
+ }
+
+ return (name_change_sender_->peekAt(index));
+}
+
+void
+D2ClientMgr::clearQueue() {
+ if (!name_change_sender_) {
+ isc_throw(D2ClientError, "D2ClientMgr::clearQueue sender is null");
+ }
+
+ name_change_sender_->clearSendQueue();
+}
+
+void
+D2ClientMgr::operator()(const dhcp_ddns::NameChangeSender::Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr) {
+ if (result == dhcp_ddns::NameChangeSender::SUCCESS) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_DHCP_DDNS_NCR_SENT).arg(ncr->toText());
+ } else {
+ // Handler is mandatory but test it just to be safe.
+ /// @todo Until we have a better feel for how errors need to be
+ /// handled we farm it out to the application layer.
+ if (client_error_handler_) {
+ // Handler is not supposed to throw, but catch just in case.
+ try {
+ (client_error_handler_)(result, ncr);
+ } catch (const std::exception& ex) {
+ LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION)
+ .arg(ex.what());
+ }
+ } else {
+ LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_HANDLER_NULL);
+ }
+ }
+}
+
+int
+D2ClientMgr::getSelectFd() {
+ if (!amSending()) {
+ isc_throw (D2ClientError, "D2ClientMgr::getSelectFd "
+ " not in send mode");
+ }
+
+ return (name_change_sender_->getSelectFd());
+}
+
+void
+D2ClientMgr::runReadyIO() {
+ if (!name_change_sender_) {
+ // This should never happen.
+ isc_throw(D2ClientError, "D2ClientMgr::runReadyIO"
+ " name_change_sender is null");
+ }
+
+ name_change_sender_->runReadyIO();
+}
+
+}; // namespace dhcp
+
+}; // namespace isc
diff --git a/src/lib/dhcpsrv/d2_client_mgr.h b/src/lib/dhcpsrv/d2_client_mgr.h
new file mode 100644
index 0000000..de47c12
--- /dev/null
+++ b/src/lib/dhcpsrv/d2_client_mgr.h
@@ -0,0 +1,383 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef D2_CLIENT_MGR_H
+#define D2_CLIENT_MGR_H
+
+/// @file d2_client_mgr.h Defines the D2ClientMgr class.
+/// This file defines the class Kea uses to act as a client of the
+/// b10-dhcp-ddns module (aka D2).
+///
+#include <asiolink/io_address.h>
+#include <dhcp_ddns/ncr_io.h>
+#include <dhcpsrv/d2_client_cfg.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Defines the type for D2 IO error handler.
+/// This callback is invoked when a send to b10-dhcp-ddns completes with a
+/// failed status. This provides the application layer (Kea) with a means to
+/// handle the error appropriately.
+///
+/// @param result Result code of the send operation.
+/// @param ncr NameChangeRequest which failed to send.
+///
+/// @note Handlers are expected not to throw. In the event a hanlder does
+/// throw invoking code logs the exception and then swallows it.
+typedef
+boost::function<void(const dhcp_ddns::NameChangeSender::Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr)> D2ClientErrorHandler;
+
+/// @brief D2ClientMgr isolates Kea from the details of being a D2 client.
+///
+/// Provides services for managing the current dhcp-ddns configuration and
+/// as well as communications with b10-dhcp-ddns. Regarding configuration it
+/// provides services to store, update, and access the current dhcp-ddns
+/// configuration. As for b10-dhcp-ddns communications, D2ClientMgr creates
+/// maintains a NameChangeSender appropriate to the current configuration and
+/// provides services to start, stop, and post NCRs to the sender. Additionally
+/// there are methods to examine the queue of requests currently waiting for
+/// transmission.
+///
+/// The manager also provides the mechanics to integrate the ASIO-based IO
+/// used by the NCR IPC with the select-driven IO used by Kea. Senders expose
+/// a file descriptor, the "select-fd" that can monitored for read-readiness
+/// with the select() function (or variants). D2ClientMgr provides a method,
+/// runReadyIO(), that will instructs the sender to process the next ready
+/// ready IO handler on the sender's IOservice. Track# 3315 extended
+/// Kea's IfaceMgr to support the registration of multiple external sockets
+/// with callbacks that are then monitored with IO readiness via select().
+/// D2ClientMgr registers the sender's select-fd and runReadyIO() with
+/// IfaceMgr when entering the send mode and unregisters it when exiting send
+/// mode.
+///
+/// To place the manager in send mode, the calling layer must supply an error
+/// handler and optionally an IOService instance. The error handler is invoked
+/// if a send completes with a failed status. This provides the calling layer
+/// an opportunity act upon the error.
+///
+/// If the caller supplies an IOService, that service will be used to process
+/// the sender's IO. If not supplied, D2ClientMgr pass a private IOService
+/// into the sender. Using a private service isolates the sender's IO from
+/// any other services.
+///
+class D2ClientMgr : public dhcp_ddns::NameChangeSender::RequestSendHandler {
+public:
+ /// @brief Constructor
+ ///
+ /// Default constructor which constructs an instance which has DHCP-DDNS
+ /// updates disabled.
+ D2ClientMgr();
+
+ /// @brief Destructor.
+ ~D2ClientMgr();
+
+ /// @brief Updates the DHCP-DDNS client configuration to the given value.
+ ///
+ /// @param new_config pointer to the new client configuration.
+ /// @throw D2ClientError if passed an empty pointer.
+ void setD2ClientConfig(D2ClientConfigPtr& new_config);
+
+ /// @brief Convenience method for checking if DHCP-DDNS is enabled.
+ ///
+ /// @return True if the D2 configuration is enabled.
+ bool ddnsEnabled();
+
+ /// @brief Fetches the DHCP-DDNS configuration pointer.
+ ///
+ /// @return a reference to the current configuration pointer.
+ const D2ClientConfigPtr& getD2ClientConfig() const;
+
+ /// @brief Determines server flags based on configuration and client flags.
+ ///
+ /// This method uses input values for the client's FQDN S and N flags, in
+ /// conjunction with the configuration parameters updates-enabled, override-
+ /// no-updates, and override-client-updates to determine the values that
+ /// should be used for the server's FQDN S and N flags.
+ /// The logic in this method is based upon RFCs 4702 and 4704.
+ ///
+ /// @param client_s S Flag from the client's FQDN
+ /// @param client_n N Flag from the client's FQDN
+ /// @param server_s [out] S Flag for the server's FQDN
+ /// @param server_n [out] N Flag for the server's FQDN
+ ///
+ /// @throw isc::BadValue if client_s and client_n are both 1 as this is
+ /// an invalid combination per RFCs.
+ void analyzeFqdn(const bool client_s, const bool client_n, bool& server_s,
+ bool& server_n) const;
+
+ /// @brief Builds a FQDN based on the configuration and given IP address.
+ ///
+ /// Using the current values for generated-prefix, qualifying-suffix and
+ /// an IP address, this method constructs a fully qualified domain name.
+ /// It supports both IPv4 and IPv6 addresses. The format of the name
+ /// is as follows:
+ ///
+ /// <generated-prefix>-<ip address>.<qualifying-suffix>.
+ ///
+ /// <ip-address> is the result of IOAddress.toText() with the delimiters
+ /// ('.' for IPv4 or ':' for IPv6) replaced with a hyphen, '-'.
+ ///
+ /// @param address IP address from which to derive the name (IPv4 or IPv6)
+ ///
+ /// @return std::string containing the generated name.
+ std::string generateFqdn(const asiolink::IOAddress& address) const;
+
+ /// @brief Adds a qualifying suffix to a given domain name
+ ///
+ /// Constructs a FQDN based on the configured qualifying-suffix and
+ /// a partial domain name as follows:
+ ///
+ /// <partial_name>.<qualifying-suffix>.
+ /// Note it will add a trailing '.' should qualifying-suffix not end with
+ /// one.
+ ///
+ /// @param partial_name domain name to qualify
+ ///
+ /// @return std::string containing the qualified name.
+ std::string qualifyName(const std::string& partial_name) const;
+
+ /// @brief Set server FQDN flags based on configuration and a given FQDN
+ ///
+ /// Templated wrapper around the analyzeFqdn() allowing that method to
+ /// be used for either IPv4 or IPv6 processing. This methods resets all
+ /// of the flags in the response to zero and then sets the S,N, and O
+ /// flags. Any other flags are the responsibility of the invoking layer.
+ ///
+ /// @param fqdn FQDN option from which to read client (inbound) flags
+ /// @param fqdn_resp FQDN option to update with the server (outbound) flags
+ /// @tparam T FQDN Option class containing the FQDN data such as
+ /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
+ template <class T>
+ void adjustFqdnFlags(const T& fqdn, T& fqdn_resp);
+
+ /// @brief Set server FQDN name based on configuration and a given FQDN
+ ///
+ /// Templated method which adjusts the domain name value and type in
+ /// a server FQDN from a client (inbound) FQDN and the current
+ /// configuration. The logic is as follows:
+ ///
+ /// If replace-client-name is true or the supplied name is empty, the
+ /// server FQDN is set to ""/PARTIAL.
+ ///
+ /// If replace-client-name is false and the supplied name is a partial
+ /// name the server FQDN is set to the supplied name qualified by
+ /// appending the qualifying-suffix.
+ ///
+ /// If replace-client-name is false and the supplied name is a fully
+ /// qualified name, set the server FQDN to the supplied name.
+ ///
+ /// @param fqdn FQDN option from which to get client (inbound) name
+ /// @param fqdn_resp FQDN option to update with the adjusted name
+ /// @tparam T FQDN Option class containing the FQDN data such as
+ /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
+ template <class T>
+ void adjustDomainName(const T& fqdn, T& fqdn_resp);
+
+ /// @brief Enables sending NameChangeRequests to b10-dhcp-ddns
+ ///
+ /// Places the NameChangeSender into send mode. This instructs the
+ /// sender to begin dequeuing and transmitting requests and to accept
+ /// additional requests via the sendRequest() method.
+ ///
+ /// @param error_handler application level error handler to cope with
+ /// sends that complete with a failed status. A valid function must be
+ /// supplied as the manager cannot know how an application should deal
+ /// with send failures.
+ /// @param io_service IOService to be used for sender IO event processing
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ void startSender(D2ClientErrorHandler error_handler,
+ isc::asiolink::IOService& io_service);
+
+ /// @brief Enables sending NameChangeRequests to b10-dhcp-ddns
+ ///
+ /// Places the NameChangeSender into send mode. This instructs the
+ /// sender to begin dequeuing and transmitting requests and to accept
+ /// additional requests via the sendRequest() method. The manager
+ /// will create a new, private instance of an IOService for the sender
+ /// to use for IO event processing.
+ ///
+ /// @param error_handler application level error handler to cope with
+ /// sends that complete with a failed status. A valid function must be
+ /// supplied as the manager cannot know how an application should deal
+ /// with send failures.
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ void startSender(D2ClientErrorHandler error_handler);
+
+ /// @brief Returns true if the sender is in send mode, false otherwise.
+ ///
+ /// A true value indicates that the sender is present and in accepting
+ /// messages for transmission, false otherwise.
+ bool amSending() const;
+
+ /// @brief Disables sending NameChangeRequests to b10-dhcp-ddns
+ ///
+ /// Takes the NameChangeSender out of send mode. The sender will stop
+ /// transmitting requests, though any queued requests remain queued.
+ /// Attempts to queue additional requests via sendRequest will fail.
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ void stopSender();
+
+ /// @brief Send the given NameChangeRequests to b10-dhcp-ddns
+ ///
+ /// Passes NameChangeRequests to the NCR sender for transmission to
+ /// b10-dhcp-ddns.
+ ///
+ /// @param ncr NameChangeRequest to send
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ void sendRequest(dhcp_ddns::NameChangeRequestPtr& ncr);
+
+ /// @brief Returns the number of NCRs queued for transmission.
+ size_t getQueueSize() const;
+
+ /// @brief Returns the nth NCR queued for transmission.
+ ///
+ /// Note that the entry is not removed from the queue.
+ /// @param index the index of the entry in the queue to fetch.
+ /// Valid values are 0 (front of the queue) to (queue size - 1).
+ /// @note This method is for test purposes only.
+ ///
+ /// @return Pointer reference to the queue entry.
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ const dhcp_ddns::NameChangeRequestPtr& peekAt(const size_t index) const;
+
+ /// @brief Removes all NCRs queued for transmission.
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ void clearQueue();
+
+ /// @brief Processes sender IO events
+ ///
+ /// Serves as callback registered for the sender's select-fd with IfaceMgr.
+ /// It instructs the sender to execute the next ready IO handler.
+ /// It provides an instance method that can be bound via boost::bind, as
+ /// NameChangeSender is abstract.
+ void runReadyIO();
+
+protected:
+ /// @brief Function operator implementing the NCR sender callback.
+ ///
+ /// This method is invoked each time the NameChangeSender completes
+ /// an asynchronous send.
+ ///
+ /// @param result contains that send outcome status.
+ /// @param ncr is a pointer to the NameChangeRequest that was
+ /// delivered (or attempted).
+ ///
+ /// @throw This method MUST NOT throw.
+ virtual void operator ()(const dhcp_ddns::NameChangeSender::Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr);
+
+ /// @brief Fetches the sender's select-fd.
+ ///
+ /// The select-fd may be used with select() or poll(). If the sender has
+ /// IO waiting to process, the fd will evaluate as !EWOULDBLOCK.
+ /// @note This is only exposed for testing purposes.
+ ///
+ /// @return The sender's select-fd
+ ///
+ /// @throw D2ClientError if the sender does not exist or is not in send
+ /// mode.
+ int getSelectFd();
+
+ /// @brief Fetches the select-fd that is currently registered.
+ ///
+ /// @return The currently registered select-fd or
+ /// dhcp_ddns::WatchSocket::INVALID_SOCKET.
+ ///
+ /// @note This is only exposed for testing purposes.
+ int getRegisteredSelectFd();
+
+private:
+ /// @brief Container class for DHCP-DDNS configuration parameters.
+ D2ClientConfigPtr d2_client_config_;
+
+ /// @brief Pointer to the current interface to DHCP-DDNS.
+ dhcp_ddns::NameChangeSenderPtr name_change_sender_;
+
+ /// @brief Private IOService to use if calling layer doesn't wish to
+ /// supply one.
+ boost::shared_ptr<asiolink::IOService> private_io_service_;
+
+ /// @brief Application supplied error handler invoked when a send
+ /// completes with a failed status.
+ D2ClientErrorHandler client_error_handler_;
+
+ /// @brief Remembers the select-fd registered with IfaceMgr.
+ int registered_select_fd_;
+};
+
+template <class T>
+void
+D2ClientMgr::adjustFqdnFlags(const T& fqdn, T& fqdn_resp) {
+ bool server_s = false;
+ bool server_n = false;
+ analyzeFqdn(fqdn.getFlag(T::FLAG_S), fqdn.getFlag(T::FLAG_N),
+ server_s, server_n);
+
+ // Reset the flags to zero to avoid triggering N and S both 1 check.
+ fqdn_resp.resetFlags();
+
+ // Set S and N flags.
+ fqdn_resp.setFlag(T::FLAG_S, server_s);
+ fqdn_resp.setFlag(T::FLAG_N, server_n);
+
+ // Set O flag true if server S overrides client S.
+ fqdn_resp.setFlag(T::FLAG_O, (fqdn.getFlag(T::FLAG_S) != server_s));
+}
+
+
+template <class T>
+void
+D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp) {
+ // If we're configured to replace it or the supplied name is blank
+ // set the response name to blank.
+ if (d2_client_config_->getReplaceClientName() ||
+ fqdn.getDomainName().empty()) {
+ fqdn_resp.setDomainName("", T::PARTIAL);
+ } else {
+ // If the supplied name is partial, qualify it by adding the suffix.
+ if (fqdn.getDomainNameType() == T::PARTIAL) {
+ fqdn_resp.setDomainName(qualifyName(fqdn.getDomainName()), T::FULL);
+ }
+ }
+}
+
+/// @brief Defines a pointer for D2ClientMgr instances.
+typedef boost::shared_ptr<D2ClientMgr> D2ClientMgrPtr;
+
+
+} // namespace isc
+} // namespace dhcp
+
+#endif
diff --git a/src/lib/dhcpsrv/dhcp_parsers.h b/src/lib/dhcpsrv/dhcp_parsers.h
index 0f8f8f9..eefc76e 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.h
+++ b/src/lib/dhcpsrv/dhcp_parsers.h
@@ -18,7 +18,7 @@
#include <asiolink/io_address.h>
#include <cc/data.h>
#include <dhcp/option_definition.h>
-#include <dhcpsrv/d2_client.h>
+#include <dhcpsrv/d2_client_cfg.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/subnet.h>
diff --git a/src/lib/dhcpsrv/tests/d2_client_unittest.cc b/src/lib/dhcpsrv/tests/d2_client_unittest.cc
index 8a8550f..cefb31e 100644
--- a/src/lib/dhcpsrv/tests/d2_client_unittest.cc
+++ b/src/lib/dhcpsrv/tests/d2_client_unittest.cc
@@ -15,7 +15,7 @@
#include <config.h>
#include <dhcp/option4_client_fqdn.h>
#include <dhcp/option6_client_fqdn.h>
-#include <dhcpsrv/d2_client.h>
+#include <dhcpsrv/d2_client_mgr.h>
#include <exceptions/exceptions.h>
#include <gtest/gtest.h>
diff --git a/src/lib/dhcpsrv/tests/d2_udp_unittest.cc b/src/lib/dhcpsrv/tests/d2_udp_unittest.cc
index 66d2125..5b51530 100644
--- a/src/lib/dhcpsrv/tests/d2_udp_unittest.cc
+++ b/src/lib/dhcpsrv/tests/d2_udp_unittest.cc
@@ -19,7 +19,8 @@
#include <asio.hpp>
#include <asiolink/io_service.h>
#include <config.h>
-#include <dhcpsrv/d2_client.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcpsrv/d2_client_mgr.h>
#include <exceptions/exceptions.h>
#include <boost/function.hpp>
@@ -376,4 +377,45 @@ TEST_F(D2ClientMgrTest, udpSendErrorHandler) {
ASSERT_EQ(1, error_handler_count_);
}
+TEST_F(D2ClientMgrTest, ifaceRegister) {
+ // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP.
+ enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP);
+
+ // Place sender in send mode.
+ ASSERT_NO_THROW(startSender(getErrorHandler()));
+
+ // Queue three messages.
+ for (int i = 0; i < 3; ++i) {
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
+ ASSERT_NO_THROW(sendRequest(ncr));
+ }
+
+ EXPECT_EQ(3, getQueueSize());
+
+ // select_fd should evaluate to ready to read.
+ selectCheck(true);
+
+ // Calling receive should complete the first message and start the second.
+ IfaceMgr::instance().receive4(0, 0);
+
+ // Verify the callback hander was invoked, no errors counted.
+ EXPECT_EQ(2, getQueueSize());
+ ASSERT_EQ(1, callback_count_);
+ ASSERT_EQ(0, error_handler_count_);
+
+ // Stop the sender. This should complete the second message but leave
+ // the third in the queue.
+ ASSERT_NO_THROW(stopSender());
+ EXPECT_EQ(1, getQueueSize());
+ ASSERT_EQ(2, callback_count_);
+ ASSERT_EQ(0, error_handler_count_);
+
+ // Calling recevie again should have no affect.
+ IfaceMgr::instance().receive4(0, 0);
+ EXPECT_EQ(1, getQueueSize());
+ ASSERT_EQ(2, callback_count_);
+ ASSERT_EQ(0, error_handler_count_);
+}
+
+
} // end of anonymous namespace
More information about the bind10-changes
mailing list