123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- // 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 <dhcp_ddns/dhcp_ddns_log.h>
- #include <dhcp_ddns/ncr_io.h>
- #include <boost/algorithm/string/predicate.hpp>
- namespace isc {
- namespace dhcp_ddns {
- NameChangeProtocol stringToNcrProtocol(const std::string& protocol_str) {
- if (boost::iequals(protocol_str, "UDP")) {
- return (NCR_UDP);
- }
- if (boost::iequals(protocol_str, "TCP")) {
- return (NCR_TCP);
- }
- isc_throw(BadValue, "Invalid NameChangeRequest protocol:" << protocol_str);
- }
- std::string ncrProtocolToString(NameChangeProtocol protocol) {
- switch (protocol) {
- case NCR_UDP:
- return ("UDP");
- case NCR_TCP:
- return ("TCP");
- default:
- break;
- }
- std::ostringstream stream;
- stream << "UNKNOWN(" << protocol << ")";
- return (stream.str());
- }
- //************************** NameChangeListener ***************************
- NameChangeListener::NameChangeListener(RequestReceiveHandler&
- recv_handler)
- : listening_(false), io_pending_(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 {
- receiveNext();
- } catch (const isc::Exception& ex) {
- stopListening();
- isc_throw(NcrListenerReceiveError, "doReceive failed:" << ex.what());
- }
- }
- void
- NameChangeListener::receiveNext() {
- io_pending_ = true;
- doReceive();
- }
- 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.
- LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_LISTEN_CLOSE_ERROR)
- .arg(ex.what());
- }
- // Set it false, no matter what. This allows us to at least try to
- // re-open via startListening().
- setListening(false);
- }
- void
- NameChangeListener::invokeRecvHandler(const Result result,
- NameChangeRequestPtr& ncr) {
- // 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.
- try {
- io_pending_ = false;
- recv_handler_(result, ncr);
- } catch (const std::exception& ex) {
- LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_UNCAUGHT_NCR_RECV_HANDLER_ERROR)
- .arg(ex.what());
- }
- // Start the next IO layer asynchronous receive.
- // In the event the handler above intervened and decided to stop listening
- // we need to check that first.
- if (amListening()) {
- try {
- receiveNext();
- } 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.
- LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_RECV_NEXT_ERROR)
- .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.
- NameChangeRequestPtr empty;
- try {
- io_pending_ = false;
- recv_handler_(ERROR, empty);
- } catch (const std::exception& ex) {
- LOG_ERROR(dhcp_ddns_logger,
- DHCP_DDNS_UNCAUGHT_NCR_RECV_HANDLER_ERROR)
- .arg(ex.what());
- }
- }
- }
- }
- //************************* NameChangeSender ******************************
- NameChangeSender::NameChangeSender(RequestSendHandler& send_handler,
- size_t send_queue_max)
- : sending_(false), send_handler_(send_handler),
- send_queue_max_(send_queue_max) {
- // Queue size must be big enough to hold at least 1 entry.
- if (send_queue_max == 0) {
- isc_throw(NcrSenderError, "NameChangeSender constructor"
- " queue size must be greater than zero");
- }
- }
- void
- NameChangeSender::startSending(isc::asiolink::IOService& io_service) {
- 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();
- isc_throw(NcrSenderOpenError, "Open failed: " << ex.what());
- }
- // 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.
- LOG_ERROR(dhcp_ddns_logger,
- 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);
- }
- void
- NameChangeSender::sendRequest(NameChangeRequestPtr& ncr) {
- if (!amSending()) {
- isc_throw(NcrSenderError, "sender is not ready to send");
- }
- if (!ncr) {
- isc_throw(NcrSenderError, "request to send is empty");
- }
- if (send_queue_.size() >= send_queue_max_) {
- isc_throw(NcrSenderQueueFull, "send queue has reached maximum capacity:"
- << send_queue_max_ );
- }
- // Put it on the queue.
- send_queue_.push_back(ncr);
- // 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.
- if (!send_queue_.empty()) {
- ncr_to_send_ = send_queue_.front();
- // @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
- NameChangeSender::invokeSendHandler(const NameChangeSender::Result result) {
- // @todo reset defense timer
- if (result == SUCCESS) {
- // It shipped so pull it off the queue.
- send_queue_.pop_front();
- }
- // Invoke the completion handler passing in the result and a pointer
- // the request involved.
- // 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) {
- LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_UNCAUGHT_NCR_SEND_HANDLER_ERROR)
- .arg(ex.what());
- }
- // Clear the pending ncr pointer.
- ncr_to_send_.reset();
- // Set up the next send
- 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.
- LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_SEND_NEXT_ERROR)
- .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) {
- LOG_ERROR(dhcp_ddns_logger,
- DHCP_DDNS_UNCAUGHT_NCR_SEND_HANDLER_ERROR).arg(ex.what());
- }
- }
- }
- void
- NameChangeSender::skipNext() {
- if (!send_queue_.empty()) {
- // Discards the request at the front of the queue.
- send_queue_.pop_front();
- }
- }
- void
- NameChangeSender::clearSendQueue() {
- if (amSending()) {
- isc_throw(NcrSenderError, "Cannot clear queue while sending");
- }
- send_queue_.clear();
- }
- } // namespace isc::dhcp_ddns
- } // namespace isc
|