ncr_io.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. // Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <dhcp_ddns/dhcp_ddns_log.h>
  15. #include <dhcp_ddns/ncr_io.h>
  16. #include <boost/algorithm/string/predicate.hpp>
  17. namespace isc {
  18. namespace dhcp_ddns {
  19. NameChangeProtocol stringToNcrProtocol(const std::string& protocol_str) {
  20. if (boost::iequals(protocol_str, "UDP")) {
  21. return (NCR_UDP);
  22. }
  23. if (boost::iequals(protocol_str, "TCP")) {
  24. return (NCR_TCP);
  25. }
  26. isc_throw(BadValue, "Invalid NameChangeRequest protocol:" << protocol_str);
  27. }
  28. std::string ncrProtocolToString(NameChangeProtocol protocol) {
  29. switch (protocol) {
  30. case NCR_UDP:
  31. return ("UDP");
  32. case NCR_TCP:
  33. return ("TCP");
  34. default:
  35. break;
  36. }
  37. std::ostringstream stream;
  38. stream << "UNKNOWN(" << protocol << ")";
  39. return (stream.str());
  40. }
  41. //************************** NameChangeListener ***************************
  42. NameChangeListener::NameChangeListener(RequestReceiveHandler&
  43. recv_handler)
  44. : listening_(false), io_pending_(false), recv_handler_(recv_handler) {
  45. };
  46. void
  47. NameChangeListener::startListening(isc::asiolink::IOService& io_service) {
  48. if (amListening()) {
  49. // This amounts to a programmatic error.
  50. isc_throw(NcrListenerError, "NameChangeListener is already listening");
  51. }
  52. // Call implementation dependent open.
  53. try {
  54. open(io_service);
  55. } catch (const isc::Exception& ex) {
  56. stopListening();
  57. isc_throw(NcrListenerOpenError, "Open failed:" << ex.what());
  58. }
  59. // Set our status to listening.
  60. setListening(true);
  61. // Start the first asynchronous receive.
  62. try {
  63. receiveNext();
  64. } catch (const isc::Exception& ex) {
  65. stopListening();
  66. isc_throw(NcrListenerReceiveError, "doReceive failed:" << ex.what());
  67. }
  68. }
  69. void
  70. NameChangeListener::receiveNext() {
  71. io_pending_ = true;
  72. doReceive();
  73. }
  74. void
  75. NameChangeListener::stopListening() {
  76. try {
  77. // Call implementation dependent close.
  78. close();
  79. } catch (const isc::Exception &ex) {
  80. // Swallow exceptions. If we have some sort of error we'll log
  81. // it but we won't propagate the throw.
  82. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_LISTEN_CLOSE_ERROR)
  83. .arg(ex.what());
  84. }
  85. // Set it false, no matter what. This allows us to at least try to
  86. // re-open via startListening().
  87. setListening(false);
  88. }
  89. void
  90. NameChangeListener::invokeRecvHandler(const Result result,
  91. NameChangeRequestPtr& ncr) {
  92. // Call the registered application layer handler.
  93. // Surround the invocation with a try-catch. The invoked handler is
  94. // not supposed to throw, but in the event it does we will at least
  95. // report it.
  96. try {
  97. io_pending_ = false;
  98. recv_handler_(result, ncr);
  99. } catch (const std::exception& ex) {
  100. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_UNCAUGHT_NCR_RECV_HANDLER_ERROR)
  101. .arg(ex.what());
  102. }
  103. // Start the next IO layer asynchronous receive.
  104. // In the event the handler above intervened and decided to stop listening
  105. // we need to check that first.
  106. if (amListening()) {
  107. try {
  108. receiveNext();
  109. } catch (const isc::Exception& ex) {
  110. // It is possible though unlikely, for doReceive to fail without
  111. // scheduling the read. While, unlikely, it does mean the callback
  112. // will not get called with a failure. A throw here would surface
  113. // at the IOService::run (or run variant) invocation. So we will
  114. // close the window by invoking the application handler with
  115. // a failed result, and let the application layer sort it out.
  116. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_RECV_NEXT_ERROR)
  117. .arg(ex.what());
  118. // Call the registered application layer handler.
  119. // Surround the invocation with a try-catch. The invoked handler is
  120. // not supposed to throw, but in the event it does we will at least
  121. // report it.
  122. NameChangeRequestPtr empty;
  123. try {
  124. io_pending_ = false;
  125. recv_handler_(ERROR, empty);
  126. } catch (const std::exception& ex) {
  127. LOG_ERROR(dhcp_ddns_logger,
  128. DHCP_DDNS_UNCAUGHT_NCR_RECV_HANDLER_ERROR)
  129. .arg(ex.what());
  130. }
  131. }
  132. }
  133. }
  134. //************************* NameChangeSender ******************************
  135. NameChangeSender::NameChangeSender(RequestSendHandler& send_handler,
  136. size_t send_queue_max)
  137. : sending_(false), send_handler_(send_handler),
  138. send_queue_max_(send_queue_max) {
  139. // Queue size must be big enough to hold at least 1 entry.
  140. if (send_queue_max == 0) {
  141. isc_throw(NcrSenderError, "NameChangeSender constructor"
  142. " queue size must be greater than zero");
  143. }
  144. }
  145. void
  146. NameChangeSender::startSending(isc::asiolink::IOService& io_service) {
  147. if (amSending()) {
  148. // This amounts to a programmatic error.
  149. isc_throw(NcrSenderError, "NameChangeSender is already sending");
  150. }
  151. // Clear send marker.
  152. ncr_to_send_.reset();
  153. // Call implementation dependent open.
  154. try {
  155. open(io_service);
  156. } catch (const isc::Exception& ex) {
  157. stopSending();
  158. isc_throw(NcrSenderOpenError, "Open failed: " << ex.what());
  159. }
  160. // Set our status to sending.
  161. setSending(true);
  162. }
  163. void
  164. NameChangeSender::stopSending() {
  165. try {
  166. // Call implementation dependent close.
  167. close();
  168. } catch (const isc::Exception &ex) {
  169. // Swallow exceptions. If we have some sort of error we'll log
  170. // it but we won't propagate the throw.
  171. LOG_ERROR(dhcp_ddns_logger,
  172. DHCP_DDNS_NCR_SEND_CLOSE_ERROR).arg(ex.what());
  173. }
  174. // Set it false, no matter what. This allows us to at least try to
  175. // re-open via startSending().
  176. setSending(false);
  177. }
  178. void
  179. NameChangeSender::sendRequest(NameChangeRequestPtr& ncr) {
  180. if (!amSending()) {
  181. isc_throw(NcrSenderError, "sender is not ready to send");
  182. }
  183. if (!ncr) {
  184. isc_throw(NcrSenderError, "request to send is empty");
  185. }
  186. if (send_queue_.size() >= send_queue_max_) {
  187. isc_throw(NcrSenderQueueFull, "send queue has reached maximum capacity:"
  188. << send_queue_max_ );
  189. }
  190. // Put it on the queue.
  191. send_queue_.push_back(ncr);
  192. // Call sendNext to schedule the next one to go.
  193. sendNext();
  194. }
  195. void
  196. NameChangeSender::sendNext() {
  197. if (ncr_to_send_) {
  198. // @todo Not sure if there is any risk of getting stuck here but
  199. // an interval timer to defend would be good.
  200. // In reality, the derivation should ensure they timeout themselves
  201. return;
  202. }
  203. // If queue isn't empty, then get one from the front. Note we leave
  204. // it on the front of the queue until we successfully send it.
  205. if (!send_queue_.empty()) {
  206. ncr_to_send_ = send_queue_.front();
  207. // @todo start defense timer
  208. // If a send were to hang and we timed it out, then timeout
  209. // handler need to cycle thru open/close ?
  210. // Call implementation dependent send.
  211. doSend(ncr_to_send_);
  212. }
  213. }
  214. void
  215. NameChangeSender::invokeSendHandler(const NameChangeSender::Result result) {
  216. // @todo reset defense timer
  217. if (result == SUCCESS) {
  218. // It shipped so pull it off the queue.
  219. send_queue_.pop_front();
  220. }
  221. // Invoke the completion handler passing in the result and a pointer
  222. // the request involved.
  223. // Surround the invocation with a try-catch. The invoked handler is
  224. // not supposed to throw, but in the event it does we will at least
  225. // report it.
  226. try {
  227. send_handler_(result, ncr_to_send_);
  228. } catch (const std::exception& ex) {
  229. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_UNCAUGHT_NCR_SEND_HANDLER_ERROR)
  230. .arg(ex.what());
  231. }
  232. // Clear the pending ncr pointer.
  233. ncr_to_send_.reset();
  234. // Set up the next send
  235. try {
  236. sendNext();
  237. } catch (const isc::Exception& ex) {
  238. // It is possible though unlikely, for sendNext to fail without
  239. // scheduling the send. While, unlikely, it does mean the callback
  240. // will not get called with a failure. A throw here would surface
  241. // at the IOService::run (or run variant) invocation. So we will
  242. // close the window by invoking the application handler with
  243. // a failed result, and let the application layer sort it out.
  244. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_SEND_NEXT_ERROR)
  245. .arg(ex.what());
  246. // Invoke the completion handler passing in failed result.
  247. // Surround the invocation with a try-catch. The invoked handler is
  248. // not supposed to throw, but in the event it does we will at least
  249. // report it.
  250. try {
  251. send_handler_(ERROR, ncr_to_send_);
  252. } catch (const std::exception& ex) {
  253. LOG_ERROR(dhcp_ddns_logger,
  254. DHCP_DDNS_UNCAUGHT_NCR_SEND_HANDLER_ERROR).arg(ex.what());
  255. }
  256. }
  257. }
  258. void
  259. NameChangeSender::skipNext() {
  260. if (!send_queue_.empty()) {
  261. // Discards the request at the front of the queue.
  262. send_queue_.pop_front();
  263. }
  264. }
  265. void
  266. NameChangeSender::clearSendQueue() {
  267. if (amSending()) {
  268. isc_throw(NcrSenderError, "Cannot clear queue while sending");
  269. }
  270. send_queue_.clear();
  271. }
  272. } // namespace isc::dhcp_ddns
  273. } // namespace isc