ncr_udp.cc 14 KB

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