udp_server.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. // Copyright (C) 2011 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 <boost/shared_array.hpp>
  16. #include <asio.hpp>
  17. #include <log/dummylog.h>
  18. #include <asiolink/io_completion_cb.h>
  19. #include <asiolink/udp_endpoint.h>
  20. #include <asiolink/udp_server.h>
  21. #include <asiolink/udp_socket.h>
  22. using namespace asio;
  23. using asio::ip::udp;
  24. using asio::ip::tcp;
  25. using isc::log::dlog;
  26. using namespace std;
  27. using namespace isc::dns;
  28. namespace asiolink {
  29. /*
  30. * Some of the member variables here are shared_ptrs and some are
  31. * auto_ptrs. There will be one instance of Data for the lifetime
  32. * of packet. The variables that are state only for a single packet
  33. * use auto_ptr, as it is more lightweight. In the case of shared
  34. * configuration (eg. the callbacks, socket), we use shared_ptrs.
  35. */
  36. struct UDPServer::Data {
  37. /*
  38. * Constructor from parameters passed to UDPServer constructor.
  39. * This instance will not be used to retrieve and answer the actual
  40. * query, it will only hold parameters until we wait for the
  41. * first packet. But we do initialize the socket in here.
  42. */
  43. Data(io_service& io_service, const ip::address& addr, const uint16_t port,
  44. SimpleCallback* checkin, DNSLookup* lookup, DNSAnswer* answer) :
  45. io_(io_service), done_(false), checkin_callback_(checkin),
  46. lookup_callback_(lookup), answer_callback_(answer)
  47. {
  48. // We must use different instantiations for v4 and v6;
  49. // otherwise ASIO will bind to both
  50. udp proto = addr.is_v4() ? udp::v4() : udp::v6();
  51. socket_.reset(new udp::socket(io_service, proto));
  52. socket_->set_option(socket_base::reuse_address(true));
  53. if (addr.is_v6()) {
  54. socket_->set_option(asio::ip::v6_only(true));
  55. }
  56. socket_->bind(udp::endpoint(addr, port));
  57. }
  58. /*
  59. * Copy constructor. Default one would probably do, but it is unnecessary
  60. * to copy many of the member variables every time we fork to handle
  61. * another packet.
  62. *
  63. * We also allocate data for receiving the packet here.
  64. */
  65. Data(const Data& other) :
  66. io_(other.io_), socket_(other.socket_), done_(false),
  67. checkin_callback_(other.checkin_callback_),
  68. lookup_callback_(other.lookup_callback_),
  69. answer_callback_(other.answer_callback_)
  70. {
  71. // Instantiate the data buffer and endpoint that will
  72. // be used by the asynchronous receive call.
  73. data_.reset(new char[MAX_LENGTH]);
  74. sender_.reset(new udp::endpoint());
  75. }
  76. // The ASIO service object
  77. asio::io_service& io_;
  78. // Class member variables which are dynamic, and changes to which
  79. // need to accessible from both sides of a coroutine fork or from
  80. // outside of the coroutine (i.e., from an asynchronous I/O call),
  81. // should be declared here as pointers and allocated in the
  82. // constructor or in the coroutine. This allows state information
  83. // to persist when an individual copy of the coroutine falls out
  84. // scope while waiting for an event, *so long as* there is another
  85. // object that is referencing the same data. As a side-benefit, using
  86. // pointers also reduces copy overhead for coroutine objects.
  87. //
  88. // Note: Currently these objects are allocated by "new" in the
  89. // constructor, or in the function operator while processing a query.
  90. // Repeated allocations from the heap for every incoming query is
  91. // clearly a performance issue; this must be optimized in the future.
  92. // The plan is to have a structure pre-allocate several "Data"
  93. // objects which can be pulled off a free list and placed on an in-use
  94. // list whenever a query comes in. This will serve the dual purpose
  95. // of improving performance and guaranteeing that state information
  96. // will *not* be destroyed when any one instance of the coroutine
  97. // falls out of scope while waiting for an event.
  98. //
  99. // Socket used to for listen for queries. Created in the
  100. // constructor and stored in a shared_ptr because socket objects
  101. // are not copyable.
  102. boost::shared_ptr<asio::ip::udp::socket> socket_;
  103. // The ASIO-internal endpoint object representing the client
  104. std::auto_ptr<asio::ip::udp::endpoint> sender_;
  105. // \c IOMessage and \c Message objects to be passed to the
  106. // DNS lookup and answer providers
  107. std::auto_ptr<asiolink::IOMessage> io_message_;
  108. // The original query as sent by the client
  109. isc::dns::MessagePtr query_message_;
  110. // The response message we are building
  111. isc::dns::MessagePtr answer_message_;
  112. // The buffer into which the response is written
  113. isc::dns::OutputBufferPtr respbuf_;
  114. // The buffer into which the query packet is written
  115. boost::shared_array<char> data_;
  116. // State information that is entirely internal to a given instance
  117. // of the coroutine can be declared here.
  118. size_t bytes_;
  119. bool done_;
  120. // Callback functions provided by the caller
  121. const SimpleCallback* checkin_callback_;
  122. const DNSLookup* lookup_callback_;
  123. const DNSAnswer* answer_callback_;
  124. std::auto_ptr<IOEndpoint> peer_;
  125. std::auto_ptr<IOSocket> iosock_;
  126. };
  127. /// The following functions implement the \c UDPServer class.
  128. ///
  129. /// The constructor. It just creates new internal state object
  130. /// and lets it handle the initialization.
  131. UDPServer::UDPServer(io_service& io_service, const ip::address& addr,
  132. const uint16_t port, SimpleCallback* checkin, DNSLookup* lookup,
  133. DNSAnswer* answer) :
  134. data_(new Data(io_service, addr, port, checkin, lookup, answer))
  135. { }
  136. /// The function operator is implemented with the "stackless coroutine"
  137. /// pattern; see internal/coroutine.h for details.
  138. void
  139. UDPServer::operator()(error_code ec, size_t length) {
  140. /// Because the coroutine reeentry block is implemented as
  141. /// a switch statement, inline variable declarations are not
  142. /// permitted. Certain variables used below can be declared here.
  143. CORO_REENTER (this) {
  144. do {
  145. /*
  146. * This is preparation for receiving a packet. We get a new
  147. * state object for the lifetime of the next packet to come.
  148. * It allocates the buffers to receive data into.
  149. */
  150. data_.reset(new Data(*data_));
  151. do {
  152. // Begin an asynchronous receive, then yield.
  153. // When the receive event is posted, the coroutine
  154. // will resume immediately after this point.
  155. CORO_YIELD data_->socket_->async_receive_from(
  156. buffer(data_->data_.get(), MAX_LENGTH), *data_->sender_,
  157. *this);
  158. } while (ec || length == 0);
  159. data_->bytes_ = length;
  160. /*
  161. * We fork the coroutine now. One (the child) will keep
  162. * the current state and handle the packet, then die and
  163. * drop ownership of the state. The other (parent) will just
  164. * go into the loop again and replace the current state with
  165. * a new one for a new object.
  166. *
  167. * Actually, both of the coroutines will be a copy of this
  168. * one, but that's just internal implementation detail.
  169. */
  170. CORO_FORK data_->io_.post(UDPServer(*this));
  171. } while (is_parent());
  172. // Create an \c IOMessage object to store the query.
  173. //
  174. // (XXX: It would be good to write a factory function
  175. // that would quickly generate an IOMessage object without
  176. // all these calls to "new".)
  177. data_->peer_.reset(new UDPEndpoint(*data_->sender_));
  178. // The TCP socket class has been extended with asynchronous functions
  179. // and takes as a template parameter a completion callback class. As
  180. // TCPServer does not use these extended functions (only those defined
  181. // in the IOSocket base class) - but needs a TCPSocket to get hold of
  182. // the underlying Boost TCP socket - use "IOCompletionCallback" -
  183. // a basic callback class: it is not used but provides the appropriate
  184. // signature.
  185. data_->iosock_.reset(
  186. new UDPSocket<IOCompletionCallback>(*data_->socket_));
  187. data_->io_message_.reset(new IOMessage(data_->data_.get(),
  188. data_->bytes_, *data_->iosock_, *data_->peer_));
  189. // Perform any necessary operations prior to processing an incoming
  190. // query (e.g., checking for queued configuration messages).
  191. //
  192. // (XXX: it may be a performance issue to check in for every single
  193. // incoming query; we may wish to throttle this in the future.)
  194. if (data_->checkin_callback_ != NULL) {
  195. (*data_->checkin_callback_)(*data_->io_message_);
  196. }
  197. // If we don't have a DNS Lookup provider, there's no point in
  198. // continuing; we exit the coroutine permanently.
  199. if (data_->lookup_callback_ == NULL) {
  200. CORO_YIELD return;
  201. }
  202. // Instantiate objects that will be needed by the
  203. // asynchronous DNS lookup and/or by the send call.
  204. data_->respbuf_.reset(new OutputBuffer(0));
  205. data_->query_message_.reset(new Message(Message::PARSE));
  206. data_->answer_message_.reset(new Message(Message::RENDER));
  207. // Schedule a DNS lookup, and yield. When the lookup is
  208. // finished, the coroutine will resume immediately after
  209. // this point.
  210. CORO_YIELD data_->io_.post(AsyncLookup<UDPServer>(*this));
  211. dlog("[XX] got an answer");
  212. // The 'done_' flag indicates whether we have an answer
  213. // to send back. If not, exit the coroutine permanently.
  214. if (!data_->done_) {
  215. CORO_YIELD return;
  216. }
  217. // Call the DNS answer provider to render the answer into
  218. // wire format
  219. (*data_->answer_callback_)(*data_->io_message_, data_->query_message_,
  220. data_->answer_message_, data_->respbuf_);
  221. // Begin an asynchronous send, and then yield. When the
  222. // send completes, we will resume immediately after this point
  223. // (though we have nothing further to do, so the coroutine
  224. // will simply exit at that time).
  225. CORO_YIELD data_->socket_->async_send_to(
  226. buffer(data_->respbuf_->getData(), data_->respbuf_->getLength()),
  227. *data_->sender_, *this);
  228. }
  229. }
  230. /// Call the DNS lookup provider. (Expected to be called by the
  231. /// AsyncLookup<UDPServer> handler.)
  232. void
  233. UDPServer::asyncLookup() {
  234. (*data_->lookup_callback_)(*data_->io_message_,
  235. data_->query_message_, data_->answer_message_, data_->respbuf_, this);
  236. }
  237. /// Post this coroutine on the ASIO service queue so that it will
  238. /// resume processing where it left off. The 'done' parameter indicates
  239. /// whether there is an answer to return to the client.
  240. void
  241. UDPServer::resume(const bool done) {
  242. data_->done_ = done;
  243. data_->io_.post(*this);
  244. }
  245. bool
  246. UDPServer::hasAnswer() {
  247. return (data_->done_);
  248. }
  249. } // namespace asiolink