123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- // Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at http://mozilla.org/MPL/2.0/.
- #include <config.h>
- #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_(util::WatchSocket::SOCKET_NOT_VALID) {
- // Default constructor initializes with a disabled configuration.
- }
- D2ClientMgr::~D2ClientMgr(){
- stopSender();
- }
- void
- D2ClientMgr::suspendUpdates() {
- if (ddnsEnabled()) {
- /// @todo For now we will disable updates and stop sending.
- /// This at least provides a means to shut it off if there are errors.
- LOG_WARN(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SUSPEND_UPDATES);
- d2_client_config_->enableUpdates(false);
- if (name_change_sender_) {
- 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) {
- // Make sure we stop sending first.
- stopSender();
- 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: {
- // Instantiate a new sender.
- new_sender.reset(new dhcp_ddns::NameChangeUDPSender(
- new_config->getSenderIp(),
- new_config->getSenderPort(),
- new_config->getServerIp(),
- new_config->getServerPort(),
- new_config->getNcrFormat(),
- *this,
- new_config->getMaxQueueSize()));
- 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_) {
- 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 (!d2_client_config_->getEnableUpdates()) {
- server_s = false;
- server_n = true;
- } else {
- // If updates are enabled and we are overriding client delegation
- // then S flag should be true. N-flag should be false.
- server_s = d2_client_config_->getOverrideClientUpdate();
- server_n = false;
- }
- break;
- case 1:
- server_s = d2_client_config_->getEnableUpdates();
- server_n = !server_s;
- 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());
- server_n = !server_s;
- break;
- default:
- // RFCs declare this an invalid combination.
- isc_throw(isc::BadValue,
- "Invalid client FQDN - N and S cannot both be 1");
- break;
- }
- }
- std::string
- D2ClientMgr::generateFqdn(const asiolink::IOAddress& address,
- const bool trailing_dot) 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(), trailing_dot));
- }
- std::string
- D2ClientMgr::qualifyName(const std::string& partial_name,
- const bool trailing_dot) const {
- std::ostringstream gen_name;
- gen_name << partial_name;
- if (!d2_client_config_->getQualifyingSuffix().empty()) {
- std::string str = gen_name.str();
- size_t len = str.length();
- if ((len > 0) && (str[len - 1] != '.')) {
- gen_name << ".";
- }
- gen_name << d2_client_config_->getQualifyingSuffix();
- }
- std::string str = gen_name.str();
- size_t len = str.length();
- if (trailing_dot) {
- // If trailing dot should be added but there is no trailing dot,
- // append it.
- if ((len > 0) && (str[len - 1] != '.')) {
- gen_name << ".";
- }
- } else {
- // If the trailing dot should not be appended but it is present,
- // remove it.
- if ((len > 0) && (str[len - 1] == '.')) {
- gen_name.str(str.substr(0,len-1));
- }
- }
- return (gen_name.str());
- }
- void
- D2ClientMgr::startSender(D2ClientErrorHandler error_handler) {
- if (amSending()) {
- return;
- }
- // 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_);
- LOG_INFO(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SENDER_STARTED)
- .arg(d2_client_config_->toText());
- }
- void
- D2ClientMgr::startSender(D2ClientErrorHandler error_handler,
- isc::asiolink::IOService& io_service) {
- if (amSending()) {
- return;
- }
- 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_ != util::WatchSocket::SOCKET_NOT_VALID) {
- IfaceMgr::instance().deleteExternalSocket(registered_select_fd_);
- registered_select_fd_ = util::WatchSocket::SOCKET_NOT_VALID;
- }
- // If its not null, call stop.
- if (amSending()) {
- name_change_sender_->stopSending();
- LOG_INFO(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SENDER_STOPPED);
- }
- }
- void
- D2ClientMgr::sendRequest(dhcp_ddns::NameChangeRequestPtr& ncr) {
- if (!amSending()) {
- // This is programmatic error so bust them for it.
- isc_throw(D2ClientError, "D2ClientMgr::sendRequest not in send mode");
- }
- try {
- name_change_sender_->sendRequest(ncr);
- } catch (const std::exception& ex) {
- LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_NCR_REJECTED)
- .arg(ex.what()).arg((ncr ? ncr->toText() : " NULL "));
- invokeClientErrorHandler(dhcp_ddns::NameChangeSender::ERROR, ncr);
- }
- }
- void
- D2ClientMgr::invokeClientErrorHandler(const dhcp_ddns::NameChangeSender::
- Result result,
- dhcp_ddns::NameChangeRequestPtr& ncr) {
- // Handler is mandatory to enter send mode but test it just to be safe.
- if (!client_error_handler_) {
- LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_HANDLER_NULL);
- } else {
- // 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());
- }
- }
- }
- size_t
- D2ClientMgr::getQueueSize() const {
- if (!name_change_sender_) {
- isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null");
- }
- return(name_change_sender_->getQueueSize());
- }
- size_t
- D2ClientMgr::getQueueMaxSize() const {
- if (!name_change_sender_) {
- isc_throw(D2ClientError, "D2ClientMgr::getQueueMaxSize sender is null");
- }
- return(name_change_sender_->getQueueMaxSize());
- }
- 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 {
- invokeClientErrorHandler(result, ncr);
- }
- }
- 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
|