2013-07-17 16:34:33 -04:00
|
|
|
// 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.
|
|
|
|
|
2013-07-25 08:12:29 -04:00
|
|
|
#include <dhcp_ddns/dhcp_ddns_log.h>
|
|
|
|
#include <dhcp_ddns/ncr_io.h>
|
2013-07-17 16:34:33 -04:00
|
|
|
|
|
|
|
namespace isc {
|
2013-07-25 08:12:29 -04:00
|
|
|
namespace dhcp_ddns {
|
2013-07-17 16:34:33 -04:00
|
|
|
|
|
|
|
//************************** NameChangeListener ***************************
|
|
|
|
|
2013-07-23 11:31:07 -04:00
|
|
|
NameChangeListener::NameChangeListener(RequestReceiveHandler&
|
2013-07-17 16:34:33 -04:00
|
|
|
recv_handler)
|
|
|
|
: listening_(false), recv_handler_(recv_handler) {
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
NameChangeListener::startListening(isc::asiolink::IOService& io_service) {
|
|
|
|
if (amListening()) {
|
|
|
|
// This amounts to a programmatic error.
|
|
|
|
isc_throw(NcrListenerError, "NameChangeListener is already listening");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call implementation dependent open.
|
|
|
|
try {
|
|
|
|
open(io_service);
|
|
|
|
} catch (const isc::Exception& ex) {
|
|
|
|
stopListening();
|
|
|
|
isc_throw(NcrListenerOpenError, "Open failed:" << ex.what());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set our status to listening.
|
|
|
|
setListening(true);
|
|
|
|
|
|
|
|
// Start the first asynchronous receive.
|
|
|
|
try {
|
|
|
|
doReceive();
|
|
|
|
} catch (const isc::Exception& ex) {
|
|
|
|
stopListening();
|
|
|
|
isc_throw(NcrListenerReceiveError, "doReceive failed:" << ex.what());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
NameChangeListener::stopListening() {
|
|
|
|
try {
|
|
|
|
// Call implementation dependent close.
|
|
|
|
close();
|
|
|
|
} catch (const isc::Exception &ex) {
|
|
|
|
// Swallow exceptions. If we have some sort of error we'll log
|
|
|
|
// it but we won't propagate the throw.
|
2013-07-25 08:12:29 -04:00
|
|
|
LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_LISTEN_CLOSE_ERROR)
|
|
|
|
.arg(ex.what());
|
2013-07-17 16:34:33 -04:00
|
|
|
}
|
|
|
|
|
2013-07-23 11:31:07 -04:00
|
|
|
// Set it false, no matter what. This allows us to at least try to
|
|
|
|
// re-open via startListening().
|
2013-07-17 16:34:33 -04:00
|
|
|
setListening(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-07-23 11:36:09 -04:00
|
|
|
NameChangeListener::invokeRecvHandler(const Result result,
|
2013-07-23 11:31:07 -04:00
|
|
|
NameChangeRequestPtr& ncr) {
|
2013-07-17 16:34:33 -04:00
|
|
|
// Call the registered application layer handler.
|
2013-07-24 08:46:47 -04:00
|
|
|
// Surround the invocation with a try-catch. The invoked handler is
|
|
|
|
// not supposed to throw, but in the event it does we will at least
|
|
|
|
// report it.
|
|
|
|
try {
|
|
|
|
recv_handler_(result, ncr);
|
|
|
|
} catch (const std::exception& ex) {
|
2013-07-25 08:12:29 -04:00
|
|
|
LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_UNCAUGHT_NCR_RECV_HANDLER_ERROR)
|
2013-07-24 08:46:47 -04:00
|
|
|
.arg(ex.what());
|
|
|
|
}
|
2013-07-17 16:34:33 -04:00
|
|
|
|
|
|
|
// Start the next IO layer asynchronous receive.
|
2013-07-23 11:31:07 -04:00
|
|
|
// In the event the handler above intervened and decided to stop listening
|
|
|
|
// we need to check that first.
|
|
|
|
if (amListening()) {
|
|
|
|
try {
|
|
|
|
doReceive();
|
|
|
|
} catch (const isc::Exception& ex) {
|
|
|
|
// It is possible though unlikely, for doReceive to fail without
|
|
|
|
// scheduling the read. While, unlikely, it does mean the callback
|
|
|
|
// will not get called with a failure. A throw here would surface
|
|
|
|
// at the IOService::run (or run variant) invocation. So we will
|
|
|
|
// close the window by invoking the application handler with
|
|
|
|
// a failed result, and let the application layer sort it out.
|
2013-07-25 08:12:29 -04:00
|
|
|
LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_RECV_NEXT_ERROR)
|
2013-07-24 08:46:47 -04:00
|
|
|
.arg(ex.what());
|
|
|
|
|
|
|
|
// Call the registered application layer handler.
|
|
|
|
// Surround the invocation with a try-catch. The invoked handler is
|
|
|
|
// not supposed to throw, but in the event it does we will at least
|
|
|
|
// report it.
|
2013-07-23 11:31:07 -04:00
|
|
|
NameChangeRequestPtr empty;
|
2013-07-24 08:46:47 -04:00
|
|
|
try {
|
|
|
|
recv_handler_(ERROR, empty);
|
|
|
|
} catch (const std::exception& ex) {
|
2013-07-25 08:12:29 -04:00
|
|
|
LOG_ERROR(dhcp_ddns_logger,
|
2013-07-24 08:46:47 -04:00
|
|
|
DHCP_DDNS_UNCAUGHT_NCR_RECV_HANDLER_ERROR)
|
|
|
|
.arg(ex.what());
|
|
|
|
}
|
2013-07-23 11:31:07 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-07-17 16:34:33 -04:00
|
|
|
|
|
|
|
//************************* NameChangeSender ******************************
|
|
|
|
|
2013-07-23 11:31:07 -04:00
|
|
|
NameChangeSender::NameChangeSender(RequestSendHandler& send_handler,
|
|
|
|
size_t send_queue_max)
|
2013-07-17 16:34:33 -04:00
|
|
|
: sending_(false), send_handler_(send_handler),
|
2013-07-23 11:31:07 -04:00
|
|
|
send_queue_max_(send_queue_max) {
|
2013-07-17 16:34:33 -04:00
|
|
|
|
|
|
|
// Queue size must be big enough to hold at least 1 entry.
|
2013-07-23 11:31:07 -04:00
|
|
|
if (send_queue_max == 0) {
|
|
|
|
isc_throw(NcrSenderError, "NameChangeSender constructor"
|
2013-07-17 16:34:33 -04:00
|
|
|
" queue size must be greater than zero");
|
|
|
|
}
|
2013-07-23 11:31:07 -04:00
|
|
|
}
|
2013-07-17 16:34:33 -04:00
|
|
|
|
|
|
|
void
|
2013-07-23 11:31:07 -04:00
|
|
|
NameChangeSender::startSending(isc::asiolink::IOService& io_service) {
|
2013-07-17 16:34:33 -04:00
|
|
|
if (amSending()) {
|
|
|
|
// This amounts to a programmatic error.
|
|
|
|
isc_throw(NcrSenderError, "NameChangeSender is already sending");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear send marker.
|
|
|
|
ncr_to_send_.reset();
|
|
|
|
|
|
|
|
// Call implementation dependent open.
|
|
|
|
try {
|
|
|
|
open(io_service);
|
|
|
|
} catch (const isc::Exception& ex) {
|
|
|
|
stopSending();
|
2013-07-23 11:31:07 -04:00
|
|
|
isc_throw(NcrSenderOpenError, "Open failed: " << ex.what());
|
2013-07-17 16:34:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set our status to sending.
|
|
|
|
setSending(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
NameChangeSender::stopSending() {
|
|
|
|
try {
|
|
|
|
// Call implementation dependent close.
|
|
|
|
close();
|
|
|
|
} catch (const isc::Exception &ex) {
|
|
|
|
// Swallow exceptions. If we have some sort of error we'll log
|
|
|
|
// it but we won't propagate the throw.
|
2013-07-25 08:12:29 -04:00
|
|
|
LOG_ERROR(dhcp_ddns_logger,
|
|
|
|
DHCP_DDNS_NCR_SEND_CLOSE_ERROR).arg(ex.what());
|
2013-07-17 16:34:33 -04:00
|
|
|
}
|
2013-07-23 11:31:07 -04:00
|
|
|
|
|
|
|
// Set it false, no matter what. This allows us to at least try to
|
|
|
|
// re-open via startSending().
|
2013-07-17 16:34:33 -04:00
|
|
|
setSending(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-07-23 11:31:07 -04:00
|
|
|
NameChangeSender::sendRequest(NameChangeRequestPtr& ncr) {
|
2013-07-17 16:34:33 -04:00
|
|
|
if (!amSending()) {
|
|
|
|
isc_throw(NcrSenderError, "sender is not ready to send");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ncr) {
|
|
|
|
isc_throw(NcrSenderError, "request to send is empty");
|
|
|
|
}
|
|
|
|
|
2013-07-23 11:31:07 -04:00
|
|
|
if (send_queue_.size() >= send_queue_max_) {
|
|
|
|
isc_throw(NcrSenderQueueFull, "send queue has reached maximum capacity:"
|
|
|
|
<< send_queue_max_ );
|
2013-07-17 16:34:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Put it on the queue.
|
2013-07-23 11:31:07 -04:00
|
|
|
send_queue_.push_back(ncr);
|
2013-07-17 16:34:33 -04:00
|
|
|
|
|
|
|
// Call sendNext to schedule the next one to go.
|
|
|
|
sendNext();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
NameChangeSender::sendNext() {
|
|
|
|
if (ncr_to_send_) {
|
|
|
|
// @todo Not sure if there is any risk of getting stuck here but
|
|
|
|
// an interval timer to defend would be good.
|
|
|
|
// In reality, the derivation should ensure they timeout themselves
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If queue isn't empty, then get one from the front. Note we leave
|
|
|
|
// it on the front of the queue until we successfully send it.
|
2013-07-25 08:12:29 -04:00
|
|
|
if (!send_queue_.empty()) {
|
2013-07-23 11:31:07 -04:00
|
|
|
ncr_to_send_ = send_queue_.front();
|
2013-07-17 16:34:33 -04:00
|
|
|
|
|
|
|
// @todo start defense timer
|
|
|
|
// If a send were to hang and we timed it out, then timeout
|
|
|
|
// handler need to cycle thru open/close ?
|
|
|
|
|
|
|
|
// Call implementation dependent send.
|
|
|
|
doSend(ncr_to_send_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-07-23 11:31:07 -04:00
|
|
|
NameChangeSender::invokeSendHandler(const NameChangeSender::Result result) {
|
2013-07-17 16:34:33 -04:00
|
|
|
// @todo reset defense timer
|
|
|
|
if (result == SUCCESS) {
|
|
|
|
// It shipped so pull it off the queue.
|
2013-07-23 11:31:07 -04:00
|
|
|
send_queue_.pop_front();
|
2013-07-17 16:34:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Invoke the completion handler passing in the result and a pointer
|
|
|
|
// the request involved.
|
2013-07-24 08:46:47 -04:00
|
|
|
// Surround the invocation with a try-catch. The invoked handler is
|
|
|
|
// not supposed to throw, but in the event it does we will at least
|
|
|
|
// report it.
|
|
|
|
try {
|
|
|
|
send_handler_(result, ncr_to_send_);
|
|
|
|
} catch (const std::exception& ex) {
|
2013-07-25 08:12:29 -04:00
|
|
|
LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_UNCAUGHT_NCR_SEND_HANDLER_ERROR)
|
2013-07-24 08:46:47 -04:00
|
|
|
.arg(ex.what());
|
|
|
|
}
|
2013-07-17 16:34:33 -04:00
|
|
|
|
|
|
|
// Clear the pending ncr pointer.
|
|
|
|
ncr_to_send_.reset();
|
|
|
|
|
|
|
|
// Set up the next send
|
2013-07-23 11:31:07 -04:00
|
|
|
try {
|
|
|
|
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
|
|
|
|
// will not get called with a failure. A throw here would surface
|
|
|
|
// at the IOService::run (or run variant) invocation. So we will
|
|
|
|
// close the window by invoking the application handler with
|
|
|
|
// a failed result, and let the application layer sort it out.
|
2013-07-25 08:12:29 -04:00
|
|
|
LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_SEND_NEXT_ERROR)
|
2013-07-24 08:46:47 -04:00
|
|
|
.arg(ex.what());
|
|
|
|
|
|
|
|
// Invoke the completion handler passing in failed result.
|
|
|
|
// Surround the invocation with a try-catch. The invoked handler is
|
|
|
|
// not supposed to throw, but in the event it does we will at least
|
|
|
|
// report it.
|
|
|
|
try {
|
|
|
|
send_handler_(ERROR, ncr_to_send_);
|
|
|
|
} catch (const std::exception& ex) {
|
2013-07-25 08:12:29 -04:00
|
|
|
LOG_ERROR(dhcp_ddns_logger,
|
|
|
|
DHCP_DDNS_UNCAUGHT_NCR_SEND_HANDLER_ERROR).arg(ex.what());
|
2013-07-24 08:46:47 -04:00
|
|
|
}
|
2013-07-23 11:31:07 -04:00
|
|
|
}
|
|
|
|
}
|
2013-07-17 16:34:33 -04:00
|
|
|
|
|
|
|
void
|
|
|
|
NameChangeSender::skipNext() {
|
2013-07-25 08:12:29 -04:00
|
|
|
if (!send_queue_.empty()) {
|
2013-07-17 16:34:33 -04:00
|
|
|
// Discards the request at the front of the queue.
|
2013-07-23 11:31:07 -04:00
|
|
|
send_queue_.pop_front();
|
2013-07-17 16:34:33 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-07-23 11:31:07 -04:00
|
|
|
NameChangeSender::clearSendQueue() {
|
2013-07-17 16:34:33 -04:00
|
|
|
if (amSending()) {
|
2013-07-23 11:31:07 -04:00
|
|
|
isc_throw(NcrSenderError, "Cannot clear queue while sending");
|
2013-07-17 16:34:33 -04:00
|
|
|
}
|
|
|
|
|
2013-07-23 11:31:07 -04:00
|
|
|
send_queue_.clear();
|
2013-07-17 16:34:33 -04:00
|
|
|
}
|
|
|
|
|
2013-07-25 08:12:29 -04:00
|
|
|
} // namespace isc::dhcp_ddns
|
2013-07-17 16:34:33 -04:00
|
|
|
} // namespace isc
|