ncr_udp.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // Copyright (C) 2013-2015 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 <config.h>
  15. #include <dhcp_ddns/dhcp_ddns_log.h>
  16. #include <dhcp_ddns/ncr_udp.h>
  17. #include <boost/asio/ip/udp.hpp>
  18. #include <boost/system/error_code.hpp>
  19. #include <boost/bind.hpp>
  20. namespace isc {
  21. namespace dhcp_ddns {
  22. //*************************** UDPCallback ***********************
  23. UDPCallback::UDPCallback (RawBufferPtr& buffer, const size_t buf_size,
  24. UDPEndpointPtr& data_source,
  25. const UDPCompletionHandler& handler)
  26. : handler_(handler), data_(new Data(buffer, buf_size, data_source)) {
  27. if (handler.empty()) {
  28. isc_throw(NcrUDPError, "UDPCallback - handler can't be null");
  29. }
  30. if (!buffer) {
  31. isc_throw(NcrUDPError, "UDPCallback - buffer can't be null");
  32. }
  33. }
  34. void
  35. UDPCallback::operator ()(const boost::system::error_code error_code,
  36. const size_t bytes_transferred) {
  37. // Save the result state and number of bytes transferred.
  38. setErrorCode(error_code);
  39. setBytesTransferred(bytes_transferred);
  40. // Invoke the NameChangeRequest layer completion handler.
  41. // First argument is a boolean indicating success or failure.
  42. // The second is a pointer to "this" callback object. By passing
  43. // ourself in, we make all of the service related data available
  44. // to the completion handler.
  45. handler_(!error_code, this);
  46. }
  47. void
  48. UDPCallback::putData(const uint8_t* src, size_t len) {
  49. if (!src) {
  50. isc_throw(NcrUDPError, "UDPCallback putData, data source is NULL");
  51. }
  52. if (len > data_->buf_size_) {
  53. isc_throw(NcrUDPError, "UDPCallback putData, data length too large");
  54. }
  55. memcpy (data_->buffer_.get(), src, len);
  56. data_->put_len_ = len;
  57. }
  58. //*************************** NameChangeUDPListener ***********************
  59. NameChangeUDPListener::
  60. NameChangeUDPListener(const isc::asiolink::IOAddress& ip_address,
  61. const uint32_t port, const NameChangeFormat format,
  62. RequestReceiveHandler& ncr_recv_handler,
  63. const bool reuse_address)
  64. : NameChangeListener(ncr_recv_handler), ip_address_(ip_address),
  65. port_(port), format_(format), reuse_address_(reuse_address) {
  66. // Instantiate the receive callback. This gets passed into each receive.
  67. // Note that the callback constructor is passed an instance method
  68. // pointer to our completion handler method, receiveCompletionHandler.
  69. RawBufferPtr buffer(new uint8_t[RECV_BUF_MAX]);
  70. UDPEndpointPtr data_source(new asiolink::UDPEndpoint());
  71. recv_callback_.reset(new
  72. UDPCallback(buffer, RECV_BUF_MAX, data_source,
  73. boost::bind(&NameChangeUDPListener::
  74. receiveCompletionHandler, this, _1, _2)));
  75. }
  76. NameChangeUDPListener::~NameChangeUDPListener() {
  77. // Clean up.
  78. stopListening();
  79. }
  80. void
  81. NameChangeUDPListener::open(isc::asiolink::IOService& io_service) {
  82. // create our endpoint and bind the the low level socket to it.
  83. isc::asiolink::UDPEndpoint endpoint(ip_address_, port_);
  84. // Create the low level socket.
  85. try {
  86. asio_socket_.reset(new boost::asio::ip::udp::
  87. socket(io_service.get_io_service(),
  88. (ip_address_.isV4() ? boost::asio::ip::udp::v4() :
  89. boost::asio::ip::udp::v6())));
  90. // Set the socket option to reuse addresses if it is enabled.
  91. if (reuse_address_) {
  92. asio_socket_->set_option(boost::asio::socket_base::reuse_address(true));
  93. }
  94. // Bind the low level socket to our endpoint.
  95. asio_socket_->bind(endpoint.getASIOEndpoint());
  96. } catch (boost::system::system_error& ex) {
  97. asio_socket_.reset();
  98. isc_throw (NcrUDPError, ex.code().message());
  99. }
  100. // Create the asiolink socket from the low level socket.
  101. socket_.reset(new NameChangeUDPSocket(*asio_socket_));
  102. }
  103. void
  104. NameChangeUDPListener::doReceive() {
  105. // Call the socket's asychronous receiving, passing ourself in as callback.
  106. RawBufferPtr recv_buffer = recv_callback_->getBuffer();
  107. socket_->asyncReceive(recv_buffer.get(), recv_callback_->getBufferSize(),
  108. 0, recv_callback_->getDataSource().get(),
  109. *recv_callback_);
  110. }
  111. void
  112. NameChangeUDPListener::close() {
  113. // Whether we think we are listening or not, make sure we aren't.
  114. // Since we are managing our own socket, we need to close it ourselves.
  115. // NOTE that if there is a pending receive, it will be canceled, which
  116. // WILL generate an invocation of the callback with error code of
  117. // "operation aborted".
  118. if (asio_socket_) {
  119. if (asio_socket_->is_open()) {
  120. try {
  121. asio_socket_->close();
  122. } catch (boost::system::system_error& ex) {
  123. // It is really unlikely that this will occur.
  124. // If we do reopen later it will be with a new socket
  125. // instance. Repackage exception as one that is conformant
  126. // with the interface.
  127. isc_throw (NcrUDPError, ex.code().message());
  128. }
  129. }
  130. asio_socket_.reset();
  131. }
  132. socket_.reset();
  133. }
  134. void
  135. NameChangeUDPListener::receiveCompletionHandler(const bool successful,
  136. const UDPCallback *callback) {
  137. NameChangeRequestPtr ncr;
  138. Result result = SUCCESS;
  139. if (successful) {
  140. // Make an InputBuffer from our internal array
  141. isc::util::InputBuffer input_buffer(callback->getData(),
  142. callback->getBytesTransferred());
  143. try {
  144. ncr = NameChangeRequest::fromFormat(format_, input_buffer);
  145. } catch (const NcrMessageError& ex) {
  146. // log it and go back to listening
  147. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_INVALID_NCR).arg(ex.what());
  148. // Queue up the next receive.
  149. // NOTE: We must call the base class, NEVER doReceive
  150. receiveNext();
  151. return;
  152. }
  153. } else {
  154. boost::system::error_code error_code = callback->getErrorCode();
  155. if (error_code.value() == boost::asio::error::operation_aborted) {
  156. // A shutdown cancels all outstanding reads. For this reason,
  157. // it can be an expected event, so log it as a debug message.
  158. LOG_DEBUG(dhcp_ddns_logger, DBGLVL_TRACE_BASIC,
  159. DHCP_DDNS_NCR_UDP_RECV_CANCELED);
  160. result = STOPPED;
  161. } else {
  162. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_UDP_RECV_ERROR)
  163. .arg(error_code.message());
  164. result = ERROR;
  165. }
  166. }
  167. // Call the application's registered request receive handler.
  168. invokeRecvHandler(result, ncr);
  169. }
  170. //*************************** NameChangeUDPSender ***********************
  171. NameChangeUDPSender::
  172. NameChangeUDPSender(const isc::asiolink::IOAddress& ip_address,
  173. const uint32_t port,
  174. const isc::asiolink::IOAddress& server_address,
  175. const uint32_t server_port, const NameChangeFormat format,
  176. RequestSendHandler& ncr_send_handler,
  177. const size_t send_que_max, const bool reuse_address)
  178. : NameChangeSender(ncr_send_handler, send_que_max),
  179. ip_address_(ip_address), port_(port), server_address_(server_address),
  180. server_port_(server_port), format_(format),
  181. reuse_address_(reuse_address) {
  182. // Instantiate the send callback. This gets passed into each send.
  183. // Note that the callback constructor is passed the an instance method
  184. // pointer to our completion handler, sendCompletionHandler.
  185. RawBufferPtr buffer(new uint8_t[SEND_BUF_MAX]);
  186. UDPEndpointPtr data_source(new asiolink::UDPEndpoint());
  187. send_callback_.reset(new UDPCallback(buffer, SEND_BUF_MAX, data_source,
  188. boost::bind(&NameChangeUDPSender::
  189. sendCompletionHandler, this,
  190. _1, _2)));
  191. }
  192. NameChangeUDPSender::~NameChangeUDPSender() {
  193. // Clean up.
  194. stopSending();
  195. }
  196. void
  197. NameChangeUDPSender::open(isc::asiolink::IOService& io_service) {
  198. // create our endpoint and bind the the low level socket to it.
  199. isc::asiolink::UDPEndpoint endpoint(ip_address_, port_);
  200. // Create the low level socket.
  201. try {
  202. asio_socket_.reset(new boost::asio::ip::udp::
  203. socket(io_service.get_io_service(),
  204. (ip_address_.isV4() ? boost::asio::ip::udp::v4() :
  205. boost::asio::ip::udp::v6())));
  206. // Set the socket option to reuse addresses if it is enabled.
  207. if (reuse_address_) {
  208. asio_socket_->set_option(boost::asio::socket_base::reuse_address(true));
  209. }
  210. // Bind the low leve socket to our endpoint.
  211. asio_socket_->bind(endpoint.getASIOEndpoint());
  212. } catch (boost::system::system_error& ex) {
  213. isc_throw (NcrUDPError, ex.code().message());
  214. }
  215. // Create the asiolink socket from the low level socket.
  216. socket_.reset(new NameChangeUDPSocket(*asio_socket_));
  217. // Create the server endpoint
  218. server_endpoint_.reset(new isc::asiolink::
  219. UDPEndpoint(server_address_, server_port_));
  220. send_callback_->setDataSource(server_endpoint_);
  221. closeWatchSocket();
  222. watch_socket_.reset(new util::WatchSocket());
  223. }
  224. void
  225. NameChangeUDPSender::close() {
  226. // Whether we think we are sending or not, make sure we aren't.
  227. // Since we are managing our own socket, we need to close it ourselves.
  228. // NOTE that if there is a pending send, it will be canceled, which
  229. // WILL generate an invocation of the callback with error code of
  230. // "operation aborted".
  231. if (asio_socket_) {
  232. if (asio_socket_->is_open()) {
  233. try {
  234. asio_socket_->close();
  235. } catch (boost::system::system_error& ex) {
  236. // It is really unlikely that this will occur.
  237. // If we do reopen later it will be with a new socket
  238. // instance. Repackage exception as one that is conformant
  239. // with the interface.
  240. isc_throw (NcrUDPError, ex.code().message());
  241. }
  242. }
  243. asio_socket_.reset();
  244. }
  245. socket_.reset();
  246. closeWatchSocket();
  247. watch_socket_.reset();
  248. }
  249. void
  250. NameChangeUDPSender::doSend(NameChangeRequestPtr& ncr) {
  251. // Now use the NCR to write JSON to an output buffer.
  252. isc::util::OutputBuffer ncr_buffer(SEND_BUF_MAX);
  253. ncr->toFormat(format_, ncr_buffer);
  254. // Copy the wire-ized request to callback. This way we know after
  255. // send completes what we sent (or attempted to send).
  256. send_callback_->putData(static_cast<const uint8_t*>(ncr_buffer.getData()),
  257. ncr_buffer.getLength());
  258. // Call the socket's asychronous send, passing our callback
  259. socket_->asyncSend(send_callback_->getData(), send_callback_->getPutLen(),
  260. send_callback_->getDataSource().get(), *send_callback_);
  261. // Set IO ready marker so sender activity is visible to select() or poll().
  262. // Note, if this call throws it will manifest itself as a throw from
  263. // from sendRequest() which the application calls directly and is documented
  264. // as throwing exceptions; or caught inside invokeSendHandler() which
  265. // will invoke the application's send_handler with an error status.
  266. watch_socket_->markReady();
  267. }
  268. void
  269. NameChangeUDPSender::sendCompletionHandler(const bool successful,
  270. const UDPCallback *send_callback) {
  271. // Clear the IO ready marker.
  272. try {
  273. watch_socket_->clearReady();
  274. } catch (const std::exception& ex) {
  275. // This can only happen if the WatchSocket's select_fd has been
  276. // compromised which is a programmatic error. We'll log the error
  277. // here, then continue on and process the IO result we were given.
  278. // WatchSocket issue will resurface on the next send as a closed
  279. // fd in markReady(). This allows application's handler to deal
  280. // with watch errors more uniformly.
  281. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_UDP_CLEAR_READY_ERROR)
  282. .arg(ex.what());
  283. }
  284. Result result;
  285. if (successful) {
  286. result = SUCCESS;
  287. }
  288. else {
  289. // On a failure, log the error and set the result to ERROR.
  290. boost::system::error_code error_code = send_callback->getErrorCode();
  291. if (error_code.value() == boost::asio::error::operation_aborted) {
  292. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_UDP_SEND_CANCELED)
  293. .arg(error_code.message());
  294. result = STOPPED;
  295. } else {
  296. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_UDP_SEND_ERROR)
  297. .arg(error_code.message());
  298. result = ERROR;
  299. }
  300. }
  301. // Call the application's registered request send handler.
  302. invokeSendHandler(result);
  303. }
  304. int
  305. NameChangeUDPSender::getSelectFd() {
  306. if (!amSending()) {
  307. isc_throw(NotImplemented, "NameChangeUDPSender::getSelectFd"
  308. " not in send mode");
  309. }
  310. return(watch_socket_->getSelectFd());
  311. }
  312. bool
  313. NameChangeUDPSender::ioReady() {
  314. if (watch_socket_) {
  315. return (watch_socket_->isReady());
  316. }
  317. return (false);
  318. }
  319. void
  320. NameChangeUDPSender::closeWatchSocket() {
  321. if (watch_socket_) {
  322. std::string error_string;
  323. watch_socket_->closeSocket(error_string);
  324. if (!error_string.empty()) {
  325. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_UDP_SENDER_WATCH_SOCKET_CLOSE_ERROR)
  326. .arg(error_string);
  327. }
  328. }
  329. }
  330. }; // end of isc::dhcp_ddns namespace
  331. }; // end of isc namespace