iofetch.cc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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 <unistd.h> // for some IPC/network system calls
  16. #include <sys/socket.h>
  17. #include <netinet/in.h>
  18. #include <boost/bind.hpp>
  19. #include <asio.hpp>
  20. #include <asio/deadline_timer.hpp>
  21. #include <asio/ip/address.hpp>
  22. #include <boost/shared_ptr.hpp>
  23. #include <boost/date_time/posix_time/posix_time_types.hpp>
  24. #include <dns/buffer.h>
  25. #include <dns/message.h>
  26. #include <dns/messagerenderer.h>
  27. #include <log/dummylog.h>
  28. #include <dns/opcode.h>
  29. #include <dns/rcode.h>
  30. #include <asiolink.h>
  31. #include <internal/coroutine.h>
  32. #include <internal/udpdns.h>
  33. #include <internal/tcpdns.h>
  34. #include <internal/iofetch.h>
  35. using namespace asio;
  36. using asio::ip::udp;
  37. using asio::ip::tcp;
  38. using isc::log::dlog;
  39. using namespace std;
  40. using namespace isc::dns;
  41. namespace asiolink {
  42. // Private UDPQuery data (see internal/udpdns.h for reasons)
  43. struct UDPQuery::PrivateData {
  44. // UDP Socket we send query to and expect reply from there
  45. udp::socket socket;
  46. // Where was the query sent
  47. udp::endpoint remote;
  48. // TCP Socket
  49. //tcp::socket tsocket;
  50. // tcp endpoint
  51. //tcp::endpoint tremote;
  52. // What we ask the server
  53. Question question;
  54. // We will store the answer here
  55. OutputBufferPtr buffer;
  56. OutputBufferPtr msgbuf;
  57. // Temporary buffer for answer
  58. boost::shared_array<char> data;
  59. // This will be called when the data arrive or timeouts
  60. Callback* callback;
  61. // Did we already stop operating (data arrived, we timed out, someone
  62. // called stop). This can be so when we are cleaning up/there are
  63. // still pointers to us.
  64. bool stopped;
  65. // Timer to measure timeouts.
  66. deadline_timer timer;
  67. // How many milliseconds are we willing to wait for answer?
  68. int timeout;
  69. PrivateData(io_service& service,
  70. const udp::socket::protocol_type& protocol, const Question &q,
  71. OutputBufferPtr b, Callback *c) :
  72. socket(service, protocol),
  73. question(q),
  74. buffer(b),
  75. msgbuf(new OutputBuffer(512)),
  76. callback(c),
  77. stopped(false),
  78. timer(service)
  79. { }
  80. };
  81. /// The following functions implement the \c UDPQuery class.
  82. ///
  83. /// The constructor
  84. UDPQuery::UDPQuery(io_service& io_service,
  85. const Question& q, const IOAddress& addr, uint16_t port,
  86. OutputBufferPtr buffer, Callback *callback, int timeout) :
  87. data_(new PrivateData(io_service,
  88. addr.getFamily() == AF_INET ? udp::v4() : udp::v6(), q, buffer,
  89. callback))
  90. {
  91. data_->remote = UDPEndpoint(addr, port).getASIOEndpoint();
  92. data_->timeout = timeout;
  93. }
  94. /// The function operator is implemented with the "stackless coroutine"
  95. /// pattern; see internal/coroutine.h for details.
  96. void
  97. UDPQuery::operator()(error_code ec, size_t length) {
  98. if (ec || data_->stopped) {
  99. return;
  100. }
  101. CORO_REENTER (this) {
  102. /// Generate the upstream query and render it to wire format
  103. /// This is done in a different scope to allow inline variable
  104. /// declarations.
  105. {
  106. Message msg(Message::RENDER);
  107. // XXX: replace with boost::random or some other suitable PRNG
  108. msg.setQid(0);
  109. msg.setOpcode(Opcode::QUERY());
  110. msg.setRcode(Rcode::NOERROR());
  111. msg.setHeaderFlag(Message::HEADERFLAG_RD);
  112. msg.addQuestion(data_->question);
  113. MessageRenderer renderer(*data_->msgbuf);
  114. msg.toWire(renderer);
  115. dlog("Sending " + msg.toText() + " to " +
  116. data_->remote.address().to_string());
  117. }
  118. // If we timeout, we stop, which will shutdown everything and
  119. // cancel all other attempts to run inside the coroutine
  120. if (data_->timeout != -1) {
  121. data_->timer.expires_from_now(boost::posix_time::milliseconds(
  122. data_->timeout));
  123. data_->timer.async_wait(boost::bind(&UDPQuery::stop, *this,
  124. TIME_OUT));
  125. }
  126. // Begin an asynchronous send, and then yield. When the
  127. // send completes, we will resume immediately after this point.
  128. CORO_YIELD data_->socket.async_send_to(buffer(data_->msgbuf->getData(),
  129. data_->msgbuf->getLength()), data_->remote, *this);
  130. /// Allocate space for the response. (XXX: This should be
  131. /// optimized by maintaining a free list of pre-allocated blocks)
  132. data_->data.reset(new char[MAX_LENGTH]);
  133. /// Begin an asynchronous receive, and yield. When the receive
  134. /// completes, we will resume immediately after this point.
  135. CORO_YIELD data_->socket.async_receive_from(buffer(data_->data.get(),
  136. MAX_LENGTH), data_->remote, *this);
  137. // The message is not rendered yet, so we can't print it easilly
  138. dlog("Received response from " + data_->remote.address().to_string());
  139. /// Copy the answer into the response buffer. (XXX: If the
  140. /// OutputBuffer object were made to meet the requirements of
  141. /// a MutableBufferSequence, then it could be written to directly
  142. /// by async_recieve_from() and this additional copy step would
  143. /// be unnecessary.)
  144. data_->buffer->writeData(data_->data.get(), length);
  145. /// We are done
  146. stop(SUCCESS);
  147. }
  148. }
  149. void
  150. UDPQuery::stop(Result result) {
  151. if (!data_->stopped) {
  152. switch (result) {
  153. case TIME_OUT:
  154. dlog("Query timed out");
  155. break;
  156. case STOPPED:
  157. dlog("Query stopped");
  158. break;
  159. default:;
  160. }
  161. data_->stopped = true;
  162. data_->socket.cancel();
  163. data_->socket.close();
  164. data_->timer.cancel();
  165. if (data_->callback) {
  166. (*data_->callback)(result);
  167. }
  168. }
  169. }
  170. }