Commit 307eb1cc authored by Thomas Markwalder's avatar Thomas Markwalder

[3059] Added D2UpdateMgr to DHCP_DDNS

Added initial implemenation of D2UpdateMgr class to src/bin/d2.
parent 72bcad54
......@@ -56,6 +56,7 @@ b10_dhcp_ddns_SOURCES += d2_config.cc d2_config.h
b10_dhcp_ddns_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h
b10_dhcp_ddns_SOURCES += d2_queue_mgr.cc d2_queue_mgr.h
b10_dhcp_ddns_SOURCES += d2_update_message.cc d2_update_message.h
b10_dhcp_ddns_SOURCES += d2_update_mgr.cc d2_update_mgr.h
b10_dhcp_ddns_SOURCES += d2_zone.cc d2_zone.h
b10_dhcp_ddns_SOURCES += dns_client.cc dns_client.h
......@@ -69,7 +70,7 @@ b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
......
......@@ -14,6 +14,7 @@
#include <d2/d2_log.h>
#include <d2/d2_cfg_mgr.h>
#include <util/encode/hex.h>
#include <boost/foreach.hpp>
......@@ -39,7 +40,7 @@ D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
reverse_mgr_->setDomains(rhs.reverse_mgr_->getDomains());
}
keys_ = rhs.keys_;
keys_ = rhs.keys_;
}
D2CfgContext::~D2CfgContext() {
......@@ -47,6 +48,10 @@ D2CfgContext::~D2CfgContext() {
// *********************** D2CfgMgr *************************
const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa.";
const char* D2CfgMgr::IPV6_REV_ZONE_SUFFIX = "ip6.arpa.";
D2CfgMgr::D2CfgMgr() : DCfgMgrBase(DCfgContextBasePtr(new D2CfgContext())) {
// TSIG keys need to parse before the Domains, so we can catch Domains
// that specify undefined keys. Create the necessary parsing order now.
......@@ -76,17 +81,77 @@ D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) {
}
bool
D2CfgMgr::matchReverse(const std::string& fqdn, DdnsDomainPtr& domain) {
if (fqdn.empty()) {
// This is a programmatic error and should not happen.
isc_throw(D2CfgError, "matchReverse passed a null or empty fqdn");
}
D2CfgMgr::matchReverse(const std::string& ip_address, DdnsDomainPtr& domain) {
// Note, reverseIpAddress will throw if the ip_address is invalid.
std::string reverse_address = reverseIpAddress(ip_address);
// Fetch the reverse manager from the D2 context.
DdnsDomainListMgrPtr mgr = getD2CfgContext()->getReverseMgr();
// Call the manager's match method and return the result.
return (mgr->matchDomain(fqdn, domain));
return (mgr->matchDomain(reverse_address, domain));
}
std::string
D2CfgMgr::reverseIpAddress(const std::string& address) {
try {
// Convert string address into an IOAddress and invoke the
// appropriate reverse method.
isc::asiolink::IOAddress ioaddr(address);
if (ioaddr.getFamily() == AF_INET) {
return (reverseV4Address(ioaddr));
}
return (reverseV6Address(ioaddr));
} catch (const isc::Exception& ex) {
isc_throw(D2CfgError, "D2CfgMgr cannot reverse address :"
<< address << " : " << ex.what());
}
}
std::string
D2CfgMgr::reverseV4Address(const isc::asiolink::IOAddress& ioaddr) {
if (ioaddr.getFamily() != AF_INET) {
isc_throw(D2CfgError, "D2CfgMgr address is not IPv4 address :"
<< ioaddr.toText());
}
// Get the address in byte vector form.
std::vector<uint8_t> bytes = ioaddr.toBytes();
// Walk backwards through vector outputting each octet and a dot.
std::ostringstream stream;
for (int i = 3; i >= 0; i--) {
stream << (unsigned int)(bytes[i]) << ".";
}
// Tack on the suffix and we're done.
stream << IPV4_REV_ZONE_SUFFIX;
return(stream.str());
}
std::string
D2CfgMgr::reverseV6Address(const isc::asiolink::IOAddress& ioaddr) {
if (ioaddr.getFamily() != AF_INET6) {
isc_throw(D2CfgError, "D2Cfg address is not IPv6 address :"
<< ioaddr.toText());
}
// Turn the address into a string of digits.
std::vector<uint8_t> bytes = ioaddr.toBytes();
std::string digits;
digits = isc::util::encode::encodeHex(bytes);
// Walk backwards through string outputting each digits and a dot.
std::ostringstream stream;
std::string::const_reverse_iterator rit;
for (rit = digits.rbegin(); rit != digits.rend(); ++rit) {
stream << (char)(*rit) << ".";
}
// Tack on the suffix and we're done.
stream << IPV6_REV_ZONE_SUFFIX;
return(stream.str());
}
......@@ -99,10 +164,10 @@ D2CfgMgr::createConfigParser(const std::string& config_id) {
isc::dhcp::DhcpConfigParser* parser = NULL;
if ((config_id == "interface") ||
(config_id == "ip_address")) {
parser = new isc::dhcp::StringParser(config_id,
parser = new isc::dhcp::StringParser(config_id,
context->getStringStorage());
} else if (config_id == "port") {
parser = new isc::dhcp::Uint32Parser(config_id,
parser = new isc::dhcp::Uint32Parser(config_id,
context->getUint32Storage());
} else if (config_id == "forward_ddns") {
parser = new DdnsDomainListMgrParser("forward_mgr",
......
......@@ -105,6 +105,14 @@ typedef boost::shared_ptr<DdnsDomainListMgr> DdnsDomainListMgrPtr;
/// and retrieving the information on demand.
class D2CfgMgr : public DCfgMgrBase {
public:
/// @brief Reverse zone suffix added to IPv4 addresses for reverse lookups
/// @todo This should be configurable.
static const char* IPV4_REV_ZONE_SUFFIX;
/// @brief Reverse zone suffix added to IPv6 addresses for reverse lookups
/// @todo This should be configurable.
static const char* IPV6_REV_ZONE_SUFFIX;
/// @brief Constructor
D2CfgMgr();
......@@ -119,30 +127,84 @@ public:
}
/// @brief Matches a given FQDN to a forward domain.
///
///
/// This calls the matchDomain method of the forward domain manager to
/// match the given FQDN to a forward domain.
/// match the given FQDN to a forward domain.
///
/// @param fqdn is the name for which to look.
/// @param domain receives the matching domain. Note that it will be reset
/// upon entry and only set if a match is subsequently found.
///
/// @return returns true if a match is found, false otherwise.
/// @throw throws D2CfgError if given an invalid fqdn.
bool matchForward(const std::string& fqdn, DdnsDomainPtr &domain);
/// @throw throws D2CfgError if given an invalid fqdn.
bool matchForward(const std::string& fqdn, DdnsDomainPtr& domain);
/// @brief Matches a given FQDN to a reverse domain.
/// @brief Matches a given IP address to a reverse domain.
///
/// This calls the matchDomain method of the reverse domain manager to
/// match the given FQDN to a forward domain.
/// match the given IPv4 or IPv6 address to a reverse domain.
///
/// @param fqdn is the name for which to look.
/// @param ip_address is the name for which to look.
/// @param domain receives the matching domain. Note that it will be reset
/// upon entry and only set if a match is subsequently found.
///
/// @return returns true if a match is found, false otherwise.
/// @throw throws D2CfgError if given an invalid fqdn.
bool matchReverse(const std::string& fqdn, DdnsDomainPtr &domain);
/// @throw throws D2CfgError if given an invalid fqdn.
bool matchReverse(const std::string& ip_address, DdnsDomainPtr& domain);
/// @brief Generate a reverse order string for the given IP address
///
/// This method creates a string containing the given IP address
/// contents in reverse order. This format is used for matching
/// against reverse DDNS domains in DHCP_DDNS configuration.
/// After reversing the syllables of the address, it appends the
/// appropriate suffix.
///
/// @param address string containing a valid IPv4 or IPv6 address.
///
/// @return a std::string containing the reverse order address.
///
/// @throw D2CfgError if given an invalid address.
std::string reverseIpAddress(const std::string& address);
/// @brief Generate a reverse order string for the given IP address
///
/// This method creates a string containing the given IP address
/// contents in reverse order. This format is used for matching
/// against reverse DDNS domains in DHCP_DDNS configuration.
/// After reversing the syllables of the address, it appends the
/// appropriate suffix.
///
/// Example:
/// input: 192.168.1.15
/// output: 15.1.168.192.in-addr.arpa.
///
/// @param ioaddr is the IPv4 IOaddress to convert
///
/// @return a std::string containing the reverse order address.
///
/// @throw D2CfgError if not given an IPv4 address.
std::string reverseV4Address(const isc::asiolink::IOAddress& ioaddr);
/// @brief Generate a reverse order string for the given IP address
///
/// This method creates a string containing the given IPv6 address
/// contents in reverse order. This format is used for matching
/// against reverse DDNS domains in DHCP_DDNS configuration.
/// After reversing the syllables of the address, it appends the
/// appropriate suffix.
///
/// IPv6 example:
/// input: 2001:db8:302:99::
/// output:
///0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.9.0.0.2.0.3.0.8.B.D.0.1.0.0.2.ip6.arpa.
///
/// @param address string containing a valid IPv6 address.
///
/// @return a std::string containing the reverse order address.
///
/// @throw D2CfgError if not given an IPv6 address.
std::string reverseV6Address(const isc::asiolink::IOAddress& ioaddr);
protected:
/// @brief Given an element_id returns an instance of the appropriate
......
......@@ -100,35 +100,60 @@ DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
return (true);
}
// Start with the longest version of the fqdn and search the list.
// Continue looking for shorter versions of fqdn so long as no match is
// found.
// @todo This can surely be optimized, time permitting.
std::string match_name = fqdn;
std::size_t start_pos = 0;
while (start_pos != std::string::npos) {
match_name = match_name.substr(start_pos, std::string::npos);
DdnsDomainMap::iterator gotit = domains_->find(match_name);
if (gotit != domains_->end()) {
domain = gotit->second;
return (true);
// Iterate over the domain map looking for the domain which matches
// the longest portion of the given fqdn.
const char* req_name = fqdn.c_str();
size_t req_len = fqdn.size();
size_t match_len = 0;
DdnsDomainMapPair map_pair;
BOOST_FOREACH (map_pair, *domains_) {
std::string domain_name = map_pair.first;
size_t dom_len = domain_name.size();
// If the domain name is longer than the fqdn, then it cant be match.
if (req_len < dom_len) {
continue;
}
start_pos = match_name.find_first_of(".");
if (start_pos != std::string::npos) {
++start_pos;
// If the lengths are identical and the names match we're done.
if (req_len == dom_len) {
if (memcmp(req_name, domain_name.c_str(), req_len) == 0) {
// exact match, done
domain = map_pair.second;
return (true);
}
} else {
// The fqdn is longer than the domain name. Adjust the start
// point of comparison by the excess in length. Only do the
// comparison if the adjustment lands on a boundary. This
// prevents "onetwo.net" from matching "two.net".
size_t offset = req_len - dom_len;
if ((req_name[offset - 1] == '.') &&
(memcmp(&req_name[offset], domain_name.c_str(), dom_len) == 0)) {
// Fqdn contains domain name, keep it if its better than
// any we have matched so far.
if (dom_len > match_len) {
match_len = dom_len;
domain = map_pair.second;
}
}
}
}
// There's no match. If they specified a wild card domain use it
// otherwise there's no domain for this entry.
if (wildcard_domain_) {
domain = wildcard_domain_;
return (true);
if (!domain) {
// There's no match. If they specified a wild card domain use it
// otherwise there's no domain for this entry.
if (wildcard_domain_) {
domain = wildcard_domain_;
return (true);
}
LOG_WARN(dctl_logger, DHCP_DDNS_NO_MATCH).arg(fqdn);
return (false);
}
LOG_WARN(dctl_logger, DHCP_DDNS_NO_MATCH).arg(fqdn);
return (false);
return (true);
}
// *************************** PARSERS ***********************************
......
......@@ -162,3 +162,26 @@ in event loop.
% DHCP_DDNS_SHUTDOWN application is performing a normal shut down
This is a debug message issued when the application has been instructed
to shut down by the controller.
% DHCP_DDNS_AT_MAX_TRANSACTIONS application has: %1 queued requests but has reached maximum number of: %2 concurrent transactions
This is a debug message that indicates that the application has DHCP_DDNS
requests in the queue but is working as many concurrent requests as allowed.
% DHCP_DDNS_NO_ELIGIBLE_JOBS although there are queued requests, there are pending transactions for each Queue count: %1 Transaction count: %2
This is a debug messge issued when all of the queued requests represent clients
for which there is a an update already in progress. This may occur under
normal operations but should be temporary situation.
% DHCP_DDNS_NO_FWD_MATCH_ERROR the configured list of forward DDNS domains does not contain a match for FQDN: %1 The request has been discarded.
This is an error message that indicates that DHCP_DDNS received a request to
update a the forward DNS information for the given FQDN but for which there are
no configured DDNS domains in the DHCP_DDNS configuration. Either the DHCP_DDNS
configuration needs to be updated or the source of the FQDN itself should be
investigated.
% DHCP_DDNS_NO_REV_MATCH_ERROR the configured list of reverse DDNS domains does not contain a match for FQDN: %1 The request has been discarded.
This is an error message that indicates that DHCP_DDNS received a request to
update a the reverse DNS information for the given FQDN but for which there are
no configured DDNS domains in the DHCP_DDNS configuration. Either the DHCP_DDNS
configuration needs to be updated or the source of the FQDN itself should be
investigated.
// Copyright (C) 2013 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 <d2/d2_update_mgr.h>
#include <sstream>
#include <iostream>
#include <vector>
namespace isc {
namespace d2 {
const size_t D2UpdateMgr::MAX_TRANSACTIONS_DEFAULT;
D2UpdateMgr::D2UpdateMgr(D2QueueMgrPtr& queue_mgr, D2CfgMgrPtr& cfg_mgr,
isc::asiolink::IOService& io_service,
const size_t max_transactions)
:queue_mgr_(queue_mgr), cfg_mgr_(cfg_mgr), io_service_(io_service) {
if (!queue_mgr_) {
isc_throw(D2UpdateMgrError, "D2UpdateMgr queue manager cannot be null");
}
if (!cfg_mgr_) {
isc_throw(D2UpdateMgrError,
"D2UpdateMgr configuration manager cannot be null");
}
// Use setter to do validation.
setMaxTransactions(max_transactions);
}
D2UpdateMgr::~D2UpdateMgr() {
transaction_list_.clear();
}
void D2UpdateMgr::sweep() {
// cleanup finished transactions;
checkFinishedTransactions();
// if the queue isn't empty, find the next suitable job and
// start a transaction for it.
// @todo - Do we want to queue max transactions? The logic here will only
// start one new transaction per invocation. On the other hand a busy
// system will generate many IO events and this method will be called
// frequently. It will likely achieve max transactions quickly on its own.
if (getQueueCount() > 0) {
if (getTransactionCount() >= max_transactions_) {
LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL_DATA,
DHCP_DDNS_AT_MAX_TRANSACTIONS).arg(getQueueCount())
.arg(getMaxTransactions());
return;
}
// We are not at maximum transactions, so pick and start the next job.
pickNextJob();
}
}
void
D2UpdateMgr::checkFinishedTransactions() {
// Cycle through transaction list and do whatever needs to be done
// for finished transactions.
// At the moment all we do is remove them from the list. This is likely
// to expand as DHCP_DDNS matures.
TransactionList::iterator it = transaction_list_.begin();
while (it != transaction_list_.end()) {
NameChangeTransactionPtr trans = (*it).second;
switch (trans->getNcrStatus()) {
case dhcp_ddns::ST_COMPLETED:
transaction_list_.erase(it);
break;
case dhcp_ddns::ST_FAILED:
transaction_list_.erase(it);
break;
default:
break;
}
++it;
}
}
void D2UpdateMgr::pickNextJob() {
// Start at the front of the queue, looking for the first entry for
// which no transaction is in progress. If we find an eligible entry
// remove it from the queue and make a transaction for it.
// Requests and transactions are associated by DHCID. If a request has
// the same DHCID as a transaction, they are presumed to be for the same
// "end user".
size_t queue_count = getQueueCount();
for (size_t index = 0; index < queue_count; index++) {
dhcp_ddns::NameChangeRequestPtr found_ncr = queue_mgr_->peekAt(index);
if (!hasTransaction(found_ncr->getDhcid())) {
queue_mgr_->dequeueAt(index);
makeTransaction(found_ncr);
return;
}
}
// There were no eligible jobs. All of the current DHCIDs already have
// transactions pending.
LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL_DATA, DHCP_DDNS_NO_ELIGIBLE_JOBS)
.arg(getQueueCount()).arg(getTransactionCount());
}
void
D2UpdateMgr::makeTransaction(dhcp_ddns::NameChangeRequestPtr& next_ncr) {
// First lets ensure there is not a transaction in progress for this
// DHCID. (pickNextJob should ensure this, as it is the only real caller
// but for safety's sake we'll check).
const TransactionKey& key = next_ncr->getDhcid();
if (findTransaction(key) != transactionListEnd()) {
// This is programmatic error. Caller(s) should be checking this.
isc_throw(D2UpdateMgrError, "Transaction already in progress for: "
<< key.toStr());
}
// If forward change is enabled, match to forward servers.
DdnsDomainPtr forward_domain;
if (next_ncr->isForwardChange()) {
bool matched = cfg_mgr_->matchForward(next_ncr->getFqdn(),
forward_domain);
// Could not find a match for forward DNS server. Log it and get out.
// This has the net affect of dropping the request on the floor.
if (!matched) {
LOG_ERROR(dctl_logger, DHCP_DDNS_NO_FWD_MATCH_ERROR)
.arg(next_ncr->getFqdn());
return;
}
}
// If reverse change is enabled, match to reverse servers.
DdnsDomainPtr reverse_domain;
if (next_ncr->isReverseChange()) {
bool matched = cfg_mgr_->matchReverse(next_ncr->getIpAddress(),
reverse_domain);
// Could not find a match for reverse DNS server. Log it and get out.
// This has the net affect of dropping the request on the floor.
if (!matched) {
LOG_ERROR(dctl_logger, DHCP_DDNS_NO_REV_MATCH_ERROR)
.arg(next_ncr->getIpAddress());
return;
}
}
// We matched to the required servers, so construct the transaction.
NameChangeTransactionPtr trans(new NameChangeTransaction(io_service_,
next_ncr,
forward_domain,
reverse_domain));
// Add the new transaction to the list.
transaction_list_[key] = trans;
}
TransactionList::iterator
D2UpdateMgr::findTransaction(const TransactionKey& key) {
return (transaction_list_.find(key));
}
bool
D2UpdateMgr::hasTransaction(const TransactionKey& key) {
return (findTransaction(key) != transactionListEnd());
}
void
D2UpdateMgr::removeTransaction(const TransactionKey& key) {
TransactionList::iterator pos = findTransaction(key);
if (pos != transactionListEnd()) {
transaction_list_.erase(pos);
}
}
TransactionList::iterator
D2UpdateMgr::transactionListEnd() {
return (transaction_list_.end());
}
void
D2UpdateMgr::clearTransactionList() {
// @todo for now this just wipes them out. We might need something
// more elegant, that allows a cancel first.
transaction_list_.clear();
}
void
D2UpdateMgr::setMaxTransactions(const size_t new_trans_max) {
// Obviously we need at room for at least one transaction.
if (new_trans_max < 1) {
isc_throw(D2UpdateMgrError, "D2UpdateMgr"
" maximum transactions limit must be greater than zero");
}
// Do not allow the list maximum to be set to less then current list size.
if (new_trans_max < getTransactionCount()) {
isc_throw(D2UpdateMgrError, "D2UpdateMgr maximum transaction limit "
"cannot be less than the current transaction count :"
<< getTransactionCount());
}
max_transactions_ = new_trans_max;
}
size_t
D2UpdateMgr::getQueueCount() {
return (queue_mgr_->getQueueSize());
}
size_t
D2UpdateMgr::getTransactionCount() {
return (transaction_list_.size());
}
} // namespace isc::d2
} // namespace isc
// Copyright (C) 2013 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_UPDATE_MGR_H
#define D2_UPDATE_MGR_H
/// @file d2_update_mgr.h This file defines the class D2UpdateMgr.
#include <asiolink/io_service.h>
#include <exceptions/exceptions.h>
#include <d2/d2_log.h>
#include <d2/d2_queue_mgr.h>
#include <d2/d2_cfg_mgr.h>
#include <boost/shared_ptr.hpp>
#include <map>
namespace isc {
namespace d2 {
/// @brief Thrown if the update manager encounters an general error.
class D2UpdateMgrError : public isc::Exception {
public:
D2UpdateMgrError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
//@{
/// @todo This is a stub implementation of NameChangeTransaction that is here
/// strictly to facilitate development of D2UpdateMgr. It will move to its own
/// source file(s) once NameChangeTransaction class development begins.
/// @brief Defines the key for transactions.
typedef isc::dhcp_ddns::D2Dhcid TransactionKey;
class NameChangeTransaction {
public:
NameChangeTransaction(isc::asiolink::IOService& io_service,
dhcp_ddns::NameChangeRequestPtr& ncr,
DdnsDomainPtr forward_domain,
DdnsDomainPtr reverse_domain)
: io_service_(io_service), ncr_(ncr), forward_domain_(forward_domain),
reverse_domain_(reverse_domain) {
}
~NameChangeTransaction(){
}
const dhcp_ddns::NameChangeRequestPtr& getNcr() const {
return (ncr_);
}