sync_udp_server.cc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Copyright (C) 2012 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 <asio.hpp>
  16. #include <asio/error.hpp>
  17. #include "sync_udp_server.h"
  18. #include "logger.h"
  19. #include <asiolink/dummy_io_cb.h>
  20. #include <asiolink/udp_endpoint.h>
  21. #include <asiolink/udp_socket.h>
  22. #include <boost/bind.hpp>
  23. #include <sys/types.h>
  24. #include <netinet/in.h>
  25. #include <sys/socket.h>
  26. #include <unistd.h> // for some IPC/network system calls
  27. #include <errno.h>
  28. using namespace std;
  29. using namespace isc::asiolink;
  30. namespace isc {
  31. namespace asiodns {
  32. SyncUDPServer::SyncUDPServer(asio::io_service& io_service, const int fd,
  33. const int af, asiolink::SimpleCallback* checkin,
  34. DNSLookup* lookup, DNSAnswer* answer) :
  35. output_buffer_(new isc::util::OutputBuffer(0)),
  36. query_(new isc::dns::Message(isc::dns::Message::PARSE)),
  37. answer_(new isc::dns::Message(isc::dns::Message::RENDER)),
  38. io_(io_service), checkin_callback_(checkin), lookup_callback_(lookup),
  39. answer_callback_(answer), stopped_(false)
  40. {
  41. if (af != AF_INET && af != AF_INET6) {
  42. isc_throw(InvalidParameter, "Address family must be either AF_INET "
  43. "or AF_INET6, not " << af);
  44. }
  45. LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, ASIODNS_FD_ADD_UDP).arg(fd);
  46. try {
  47. socket_.reset(new asio::ip::udp::socket(io_service));
  48. socket_->assign(af == AF_INET6 ? asio::ip::udp::v6() :
  49. asio::ip::udp::v4(), fd);
  50. } catch (const std::exception& exception) {
  51. // Whatever the thing throws, it is something from ASIO and we
  52. // convert it
  53. isc_throw(IOError, exception.what());
  54. }
  55. }
  56. void
  57. SyncUDPServer::scheduleRead() {
  58. socket_->async_receive_from(asio::buffer(data_, MAX_LENGTH), sender_,
  59. boost::bind(&SyncUDPServer::handleRead, this,
  60. _1, _2));
  61. }
  62. void
  63. SyncUDPServer::handleRead(const asio::error_code& ec, const size_t length) {
  64. // Abort on fatal errors
  65. if (ec) {
  66. using namespace asio::error;
  67. if (ec.value() != would_block && ec.value() != try_again &&
  68. ec.value() != interrupted) {
  69. return;
  70. }
  71. }
  72. // Some kind of interrupt, spurious wakeup, or like that. Just try reading
  73. // again.
  74. if (ec || length == 0) {
  75. scheduleRead();
  76. return;
  77. }
  78. // OK, we have a real packet of data. Let's dig into it!
  79. // XXX: This is taken (and ported) from UDPSocket class. What the hell does
  80. // it really mean?
  81. // The UDP socket class has been extended with asynchronous functions
  82. // and takes as a template parameter a completion callback class. As
  83. // UDPServer does not use these extended functions (only those defined
  84. // in the IOSocket base class) - but needs a UDPSocket to get hold of
  85. // the underlying Boost UDP socket - DummyIOCallback is used. This
  86. // provides the appropriate operator() but is otherwise functionless.
  87. UDPSocket<DummyIOCallback> socket(*socket_);
  88. UDPEndpoint endpoint(sender_);
  89. IOMessage message(data_, length, socket, endpoint);
  90. if (checkin_callback_ != NULL) {
  91. (*checkin_callback_)(message);
  92. if (stopped_) {
  93. return;
  94. }
  95. }
  96. // If we don't have a DNS Lookup provider, there's no point in
  97. // continuing; we exit the coroutine permanently.
  98. if (lookup_callback_ == NULL) {
  99. scheduleRead();
  100. return;
  101. }
  102. // Make sure the buffers are fresh. Note that we don't touch query_
  103. // because it's supposed to be cleared in lookup_callback_. We should
  104. // eventually even remove this member variable (and remove it from
  105. // the lookup_callback_ interface, but until then, any callback
  106. // implementation should be careful that it's the responsibility of
  107. // the callback implementation. See also #2239).
  108. output_buffer_->clear();
  109. answer_->clear(isc::dns::Message::RENDER);
  110. // Mark that we don't have an answer yet.
  111. done_ = false;
  112. resume_called_ = false;
  113. // Call the actual lookup
  114. (*lookup_callback_)(message, query_, answer_, output_buffer_, this);
  115. if (!resume_called_) {
  116. isc_throw(isc::Unexpected,
  117. "No resume called from the lookup callback");
  118. }
  119. if (stopped_) {
  120. return;
  121. }
  122. if (done_) {
  123. // Good, there's an answer.
  124. // Call the answer callback to render it.
  125. (*answer_callback_)(message, query_, answer_, output_buffer_);
  126. if (stopped_) {
  127. return;
  128. }
  129. socket_->send_to(asio::buffer(output_buffer_->getData(),
  130. output_buffer_->getLength()),
  131. sender_);
  132. }
  133. // And schedule handling another socket.
  134. scheduleRead();
  135. }
  136. void
  137. SyncUDPServer::operator()(asio::error_code, size_t) {
  138. // To start the server, we just schedule reading of data when they
  139. // arrive.
  140. scheduleRead();
  141. }
  142. /// Stop the UDPServer
  143. void
  144. SyncUDPServer::stop() {
  145. /// Using close instead of cancel, because cancel
  146. /// will only cancel the asynchornized event already submitted
  147. /// to io service, the events post to io service after
  148. /// cancel still can be scheduled by io service, if
  149. /// the socket is cloesed, all the asynchronized event
  150. /// for it won't be scheduled by io service not matter it is
  151. /// submit to io serice before or after close call. And we will
  152. //. get bad_descriptor error
  153. socket_->close();
  154. stopped_ = true;
  155. }
  156. /// Post this coroutine on the ASIO service queue so that it will
  157. /// resume processing where it left off. The 'done' parameter indicates
  158. /// whether there is an answer to return to the client.
  159. void
  160. SyncUDPServer::resume(const bool done) {
  161. resume_called_ = true;
  162. done_ = done;
  163. }
  164. bool
  165. SyncUDPServer::hasAnswer() {
  166. return (done_);
  167. }
  168. } // namespace asiodns
  169. } // namespace isc