asiolink.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. // Copyright (C) 2010 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. // $Id$
  15. #include <config.h>
  16. #include <cstdlib> // For random(), temporary until better forwarding is done
  17. #include <unistd.h> // for some IPC/network system calls
  18. #include <sys/socket.h>
  19. #include <netinet/in.h>
  20. #include <vector>
  21. #include <asio.hpp>
  22. #include <boost/lexical_cast.hpp>
  23. #include <boost/bind.hpp>
  24. #include <boost/shared_ptr.hpp>
  25. #include <dns/buffer.h>
  26. #include <dns/message.h>
  27. #include <asiolink/asiolink.h>
  28. #include <asiolink/internal/tcpdns.h>
  29. #include <asiolink/internal/udpdns.h>
  30. #include <log/dummylog.h>
  31. using namespace asio;
  32. using asio::ip::udp;
  33. using asio::ip::tcp;
  34. using namespace std;
  35. using namespace isc::dns;
  36. using isc::log::dlog;
  37. using namespace boost;
  38. namespace asiolink {
  39. class IOServiceImpl {
  40. private:
  41. IOServiceImpl(const IOService& source);
  42. IOServiceImpl& operator=(const IOService& source);
  43. public:
  44. /// \brief The constructor
  45. IOServiceImpl() :
  46. io_service_(),
  47. work_(io_service_)
  48. {};
  49. /// \brief The destructor.
  50. ~IOServiceImpl() {};
  51. //@}
  52. /// \brief Start the underlying event loop.
  53. ///
  54. /// This method does not return control to the caller until
  55. /// the \c stop() method is called via some handler.
  56. void run() { io_service_.run(); };
  57. /// \brief Run the underlying event loop for a single event.
  58. ///
  59. /// This method return control to the caller as soon as the
  60. /// first handler has completed. (If no handlers are ready when
  61. /// it is run, it will block until one is.)
  62. void run_one() { io_service_.run_one();} ;
  63. /// \brief Stop the underlying event loop.
  64. ///
  65. /// This will return the control to the caller of the \c run() method.
  66. void stop() { io_service_.stop();} ;
  67. /// \brief Return the native \c io_service object used in this wrapper.
  68. ///
  69. /// This is a short term work around to support other BIND 10 modules
  70. /// that share the same \c io_service with the authoritative server.
  71. /// It will eventually be removed once the wrapper interface is
  72. /// generalized.
  73. asio::io_service& get_io_service() { return io_service_; };
  74. private:
  75. asio::io_service io_service_;
  76. asio::io_service::work work_;
  77. };
  78. IOService::IOService() {
  79. io_impl_ = new IOServiceImpl();
  80. }
  81. IOService::~IOService() {
  82. delete io_impl_;
  83. }
  84. void
  85. IOService::run() {
  86. io_impl_->run();
  87. }
  88. void
  89. IOService::run_one() {
  90. io_impl_->run_one();
  91. }
  92. void
  93. IOService::stop() {
  94. io_impl_->stop();
  95. }
  96. asio::io_service&
  97. IOService::get_io_service() {
  98. return (io_impl_->get_io_service());
  99. }
  100. class DNSServiceImpl {
  101. public:
  102. DNSServiceImpl(IOService& io_service, const char& port,
  103. const ip::address* v4addr, const ip::address* v6addr,
  104. SimpleCallback* checkin, DNSLookup* lookup,
  105. DNSAnswer* answer);
  106. IOService& io_service_;
  107. typedef boost::shared_ptr<UDPServer> UDPServerPtr;
  108. typedef boost::shared_ptr<TCPServer> TCPServerPtr;
  109. typedef boost::shared_ptr<DNSServer> DNSServerPtr;
  110. vector<DNSServerPtr> servers_;
  111. SimpleCallback *checkin_;
  112. DNSLookup *lookup_;
  113. DNSAnswer *answer_;
  114. void addServer(uint16_t port, const ip::address& address) {
  115. try {
  116. dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<string>(port));
  117. TCPServerPtr tcpServer(new TCPServer(io_service_.get_io_service(),
  118. address, port, checkin_, lookup_, answer_));
  119. (*tcpServer)();
  120. servers_.push_back(tcpServer);
  121. dlog(std::string("Initialize UDP server at ") + address.to_string() + ":" + boost::lexical_cast<string>(port));
  122. UDPServerPtr udpServer(new UDPServer(io_service_.get_io_service(),
  123. address, port, checkin_, lookup_, answer_));
  124. (*udpServer)();
  125. servers_.push_back(udpServer);
  126. }
  127. catch (const asio::system_error& err) {
  128. // We need to catch and convert any ASIO level exceptions.
  129. // This can happen for unavailable address, binding a privilege port
  130. // without the privilege, etc.
  131. isc_throw(IOError, "Failed to initialize network servers: " <<
  132. err.what());
  133. }
  134. }
  135. void addServer(const char& port, const ip::address& address) {
  136. uint16_t portnum;
  137. try {
  138. // XXX: SunStudio with stlport4 doesn't reject some invalid
  139. // representation such as "-1" by lexical_cast<uint16_t>, so
  140. // we convert it into a signed integer of a larger size and perform
  141. // range check ourselves.
  142. const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
  143. if (portnum32 < 0 || portnum32 > 65535) {
  144. isc_throw(IOError, "Invalid port number '" << &port);
  145. }
  146. portnum = portnum32;
  147. } catch (const boost::bad_lexical_cast& ex) {
  148. isc_throw(IOError, "Invalid port number '" << &port << "': " <<
  149. ex.what());
  150. }
  151. addServer(portnum, address);
  152. }
  153. };
  154. DNSServiceImpl::DNSServiceImpl(IOService& io_service,
  155. const char& port,
  156. const ip::address* const v4addr,
  157. const ip::address* const v6addr,
  158. SimpleCallback* checkin,
  159. DNSLookup* lookup,
  160. DNSAnswer* answer) :
  161. io_service_(io_service),
  162. checkin_(checkin),
  163. lookup_(lookup),
  164. answer_(answer)
  165. {
  166. if (v4addr) {
  167. addServer(port, *v4addr);
  168. }
  169. if (v6addr) {
  170. addServer(port, *v6addr);
  171. }
  172. }
  173. DNSService::DNSService(IOService& io_service,
  174. const char& port, const char& address,
  175. SimpleCallback* checkin,
  176. DNSLookup* lookup,
  177. DNSAnswer* answer) :
  178. impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
  179. answer)), io_service_(io_service)
  180. {
  181. addServer(port, &address);
  182. }
  183. DNSService::DNSService(IOService& io_service,
  184. const char& port,
  185. const bool use_ipv4, const bool use_ipv6,
  186. SimpleCallback* checkin,
  187. DNSLookup* lookup,
  188. DNSAnswer* answer) :
  189. impl_(NULL), io_service_(io_service)
  190. {
  191. const ip::address v4addr_any = ip::address(ip::address_v4::any());
  192. const ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL;
  193. const ip::address v6addr_any = ip::address(ip::address_v6::any());
  194. const ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
  195. impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
  196. }
  197. DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
  198. DNSLookup* lookup, DNSAnswer *answer) :
  199. impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
  200. answer)), io_service_(io_service)
  201. {
  202. }
  203. DNSService::~DNSService() {
  204. delete impl_;
  205. }
  206. namespace {
  207. typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
  208. }
  209. RecursiveQuery::RecursiveQuery(DNSService& dns_service,
  210. const AddressVector& upstream, int timeout, unsigned retries) :
  211. dns_service_(dns_service), upstream_(new AddressVector(upstream)),
  212. timeout_(timeout), retries_(retries)
  213. {}
  214. namespace {
  215. ip::address
  216. convertAddr(const string& address) {
  217. error_code err;
  218. ip::address addr = ip::address::from_string(address, err);
  219. if (err) {
  220. isc_throw(IOError, "Invalid IP address '" << &address << "': "
  221. << err.message());
  222. }
  223. return addr;
  224. }
  225. }
  226. void
  227. DNSService::addServer(const char& port, const string& address) {
  228. impl_->addServer(port, convertAddr(address));
  229. }
  230. void
  231. DNSService::addServer(uint16_t port, const string& address) {
  232. impl_->addServer(port, convertAddr(address));
  233. }
  234. void
  235. DNSService::clearServers() {
  236. // FIXME: This does not work, it does not close the socket.
  237. // How is it done?
  238. impl_->servers_.clear();
  239. }
  240. namespace {
  241. /*
  242. * This is a query in progress. When a new query is made, this one holds
  243. * the context information about it, like how many times we are allowed
  244. * to retry on failure, what to do when we succeed, etc.
  245. *
  246. * Used by RecursiveQuery::sendQuery.
  247. */
  248. class RunningQuery : public UDPQuery::Callback {
  249. private:
  250. // The io service to handle async calls
  251. asio::io_service& io_;
  252. // Info for (re)sending the query (the question and destination)
  253. Question question_;
  254. shared_ptr<AddressVector> upstream_;
  255. // Buffer to store the result.
  256. OutputBufferPtr buffer_;
  257. // Server to notify when we succeed or fail
  258. shared_ptr<DNSServer> server_;
  259. /*
  260. * TODO Do something more clever with timeouts. In the long term, some
  261. * computation of average RTT, increase with each retry, etc.
  262. */
  263. // Timeout information
  264. int timeout_;
  265. unsigned retries_;
  266. // (re)send the query to the server.
  267. void send() {
  268. const int uc = upstream_->size();
  269. if (uc > 0) {
  270. int serverIndex(random() % uc);
  271. dlog("Sending upstream query (" + question_.toText() +
  272. ") to " + upstream_->at(serverIndex).first);
  273. UDPQuery query(io_, question_,
  274. upstream_->at(serverIndex).first,
  275. upstream_->at(serverIndex).second, buffer_, this,
  276. timeout_);
  277. io_.post(query);
  278. } else {
  279. dlog("Error, no upstream servers to send to.");
  280. }
  281. }
  282. public:
  283. RunningQuery(asio::io_service& io, const Question &question,
  284. shared_ptr<AddressVector> upstream,
  285. OutputBufferPtr buffer, DNSServer* server, int timeout,
  286. unsigned retries) :
  287. io_(io),
  288. question_(question),
  289. upstream_(upstream),
  290. buffer_(buffer),
  291. server_(server->clone()),
  292. timeout_(timeout),
  293. retries_(retries)
  294. {
  295. send();
  296. }
  297. // This function is used as callback from DNSQuery.
  298. virtual void operator()(UDPQuery::Result result) {
  299. if (result == UDPQuery::TIME_OUT && retries_ --) {
  300. dlog("Resending query");
  301. // We timed out, but we have some retries, so send again
  302. send();
  303. } else {
  304. server_->resume(result == UDPQuery::SUCCESS);
  305. delete this;
  306. }
  307. }
  308. };
  309. }
  310. void
  311. RecursiveQuery::sendQuery(const Question& question, OutputBufferPtr buffer,
  312. DNSServer* server)
  313. {
  314. // XXX: eventually we will need to be able to determine whether
  315. // the message should be sent via TCP or UDP, or sent initially via
  316. // UDP and then fall back to TCP on failure, but for the moment
  317. // we're only going to handle UDP.
  318. asio::io_service& io = dns_service_.get_io_service();
  319. // It will delete itself when it is done
  320. new RunningQuery(io, question, upstream_, buffer, server,
  321. timeout_, retries_);
  322. }
  323. }