recursive_query.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  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 <netinet/in.h>
  15. #include <stdlib.h>
  16. #include <sys/socket.h>
  17. #include <unistd.h> // for some IPC/network system calls
  18. #include <boost/lexical_cast.hpp>
  19. #include <boost/bind.hpp>
  20. #include <config.h>
  21. #include <log/dummylog.h>
  22. #include <dns/question.h>
  23. #include <dns/message.h>
  24. #include <dns/opcode.h>
  25. #include <resolve/resolve.h>
  26. #include <cache/resolver_cache.h>
  27. #include <asio.hpp>
  28. #include <asiolink/dns_service.h>
  29. #include <asiolink/io_fetch.h>
  30. #include <asiolink/io_service.h>
  31. #include <asiolink/recursive_query.h>
  32. using isc::log::dlog;
  33. using namespace isc::dns;
  34. namespace asiolink {
  35. typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
  36. // Here we do not use the typedef above, as the SunStudio compiler
  37. // mishandles this in its name mangling, and wouldn't compile.
  38. // We can probably use a typedef, but need to move it to a central
  39. // location and use it consistently.
  40. RecursiveQuery::RecursiveQuery(DNSService& dns_service,
  41. const std::vector<std::pair<std::string, uint16_t> >& upstream,
  42. const std::vector<std::pair<std::string, uint16_t> >& upstream_root,
  43. int query_timeout, int client_timeout, int lookup_timeout,
  44. unsigned retries) :
  45. dns_service_(dns_service), upstream_(new AddressVector(upstream)),
  46. upstream_root_(new AddressVector(upstream_root)),
  47. test_server_("", 0),
  48. query_timeout_(query_timeout), client_timeout_(client_timeout),
  49. lookup_timeout_(lookup_timeout), retries_(retries)
  50. {}
  51. // Set the test server - only used for unit testing.
  52. void
  53. RecursiveQuery::setTestServer(const std::string& address, uint16_t port) {
  54. dlog("Setting test server to " + address + "(" +
  55. boost::lexical_cast<std::string>(port) + ")");
  56. test_server_.first = address;
  57. test_server_.second = port;
  58. }
  59. namespace {
  60. typedef std::pair<std::string, uint16_t> addr_t;
  61. /*
  62. * This is a query in progress. When a new query is made, this one holds
  63. * the context information about it, like how many times we are allowed
  64. * to retry on failure, what to do when we succeed, etc.
  65. *
  66. * Used by RecursiveQuery::sendQuery.
  67. */
  68. class RunningQuery : public IOFetch::Callback {
  69. private:
  70. // The io service to handle async calls
  71. IOService& io_;
  72. // Info for (re)sending the query (the question and destination)
  73. Question question_;
  74. // This is where we build and store our final answer
  75. MessagePtr answer_message_;
  76. // currently we use upstream as the current list of NS records
  77. // we should differentiate between forwarding and resolving
  78. boost::shared_ptr<AddressVector> upstream_;
  79. // root servers...just copied over to the zone_servers_
  80. boost::shared_ptr<AddressVector> upstream_root_;
  81. // Test server - only used for testing. This takes precedence over all
  82. // other servers if the port is non-zero.
  83. std::pair<std::string, uint16_t> test_server_;
  84. // Buffer to store the result.
  85. OutputBufferPtr buffer_;
  86. // Server to notify when we succeed or fail
  87. //shared_ptr<DNSServer> server_;
  88. isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
  89. // Protocol used for the last query. This is set to IOFetch::UDP when a
  90. // new upstream query is initiated, and changed to IOFetch::TCP if a
  91. // packet is returned with the TC bit set. It is stored here to detect the
  92. // case of a TCP packet being returned with the TC bit set.
  93. IOFetch::Protocol protocol_;
  94. // To prevent both unreasonably long cname chains and cname loops,
  95. // we simply keep a counter of the number of CNAMEs we have
  96. // followed so far (and error if it exceeds RESOLVER_MAX_CNAME_CHAIN
  97. // from lib/resolve/response_classifier.h)
  98. unsigned cname_count_;
  99. /*
  100. * TODO Do something more clever with timeouts. In the long term, some
  101. * computation of average RTT, increase with each retry, etc.
  102. */
  103. // Timeout information
  104. int query_timeout_;
  105. unsigned retries_;
  106. // normal query state
  107. // Not using NSAS at this moment, so we keep a list
  108. // of 'current' zone servers
  109. std::vector<addr_t> zone_servers_;
  110. // Update the question that will be sent to the server
  111. void setQuestion(const Question& new_question) {
  112. question_ = new_question;
  113. }
  114. // TODO: replace by our wrapper
  115. asio::deadline_timer client_timer;
  116. asio::deadline_timer lookup_timer;
  117. size_t queries_out_;
  118. // If we timed out ourselves (lookup timeout), stop issuing queries
  119. bool done_;
  120. // If we have a client timeout, we send back an answer, but don't
  121. // stop. We use this variable to make sure we don't send another
  122. // answer if we do find one later (or if we have a lookup_timeout)
  123. bool answer_sent_;
  124. // Reference to our cache
  125. isc::cache::ResolverCache& cache_;
  126. // perform a single lookup; first we check the cache to see
  127. // if we have a response for our query stored already. if
  128. // so, call handlerecursiveresponse(), if not, we call send()
  129. void doLookup() {
  130. dlog("doLookup: try cache");
  131. Message cached_message(Message::RENDER);
  132. isc::resolve::initResponseMessage(question_, cached_message);
  133. if (cache_.lookup(question_.getName(), question_.getType(),
  134. question_.getClass(), cached_message)) {
  135. dlog("Message found in cache, returning that");
  136. handleRecursiveAnswer(cached_message);
  137. } else {
  138. send();
  139. }
  140. }
  141. // (re)send the query to the server.
  142. //
  143. // \param protocol Protocol to use for the fetch (default is UDP)
  144. void send(IOFetch::Protocol protocol = IOFetch::UDP) {
  145. const int uc = upstream_->size();
  146. const int zs = zone_servers_.size();
  147. protocol_ = protocol; // Store protocol being used for this
  148. buffer_->clear();
  149. if (test_server_.second != 0) {
  150. dlog("Sending upstream query (" + question_.toText() +
  151. ") to test server at " + test_server_.first);
  152. IOFetch query(protocol, io_, question_,
  153. test_server_.first,
  154. test_server_.second, buffer_, this,
  155. query_timeout_);
  156. ++queries_out_;
  157. io_.get_io_service().post(query);
  158. } else if (uc > 0) {
  159. int serverIndex = rand() % uc;
  160. dlog("Sending upstream query (" + question_.toText() +
  161. ") to " + upstream_->at(serverIndex).first);
  162. IOFetch query(protocol, io_, question_,
  163. upstream_->at(serverIndex).first,
  164. upstream_->at(serverIndex).second, buffer_, this,
  165. query_timeout_);
  166. ++queries_out_;
  167. io_.get_io_service().post(query);
  168. } else if (zs > 0) {
  169. int serverIndex = rand() % zs;
  170. dlog("Sending query to zone server (" + question_.toText() +
  171. ") to " + zone_servers_.at(serverIndex).first);
  172. IOFetch query(protocol, io_, question_,
  173. zone_servers_.at(serverIndex).first,
  174. zone_servers_.at(serverIndex).second, buffer_, this,
  175. query_timeout_);
  176. ++queries_out_;
  177. io_.get_io_service().post(query);
  178. } else {
  179. dlog("Error, no upstream servers to send to.");
  180. }
  181. }
  182. // This function is called by operator() if there is an actual
  183. // answer from a server and we are in recursive mode
  184. // depending on the contents, we go on recursing or return
  185. //
  186. // Note that the footprint may change as this function may
  187. // need to append data to the answer we are building later.
  188. //
  189. // returns true if we are done (either we have an answer or an
  190. // error message)
  191. // returns false if we are not done
  192. bool handleRecursiveAnswer(const Message& incoming) {
  193. dlog("Handle response");
  194. // In case we get a CNAME, we store the target
  195. // here (classify() will set it when it walks through
  196. // the cname chain to verify it).
  197. Name cname_target(question_.getName());
  198. isc::resolve::ResponseClassifier::Category category =
  199. isc::resolve::ResponseClassifier::classify(
  200. question_, incoming, cname_target, cname_count_, true);
  201. bool found_ns_address = false;
  202. // If the packet is OK, store it in the cache
  203. if (!isc::resolve::ResponseClassifier::error(category)) {
  204. cache_.update(incoming);
  205. }
  206. switch (category) {
  207. case isc::resolve::ResponseClassifier::ANSWER:
  208. case isc::resolve::ResponseClassifier::ANSWERCNAME:
  209. // Done. copy and return.
  210. isc::resolve::copyResponseMessage(incoming, answer_message_);
  211. return true;
  212. break;
  213. case isc::resolve::ResponseClassifier::CNAME:
  214. dlog("Response is CNAME!");
  215. // (unfinished) CNAME. We set our question_ to the CNAME
  216. // target, then start over at the beginning (for now, that
  217. // is, we reset our 'current servers' to the root servers).
  218. if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
  219. // just give up
  220. dlog("CNAME chain too long");
  221. isc::resolve::makeErrorMessage(answer_message_,
  222. Rcode::SERVFAIL());
  223. return true;
  224. }
  225. answer_message_->appendSection(Message::SECTION_ANSWER,
  226. incoming);
  227. setZoneServersToRoot();
  228. question_ = Question(cname_target, question_.getClass(),
  229. question_.getType());
  230. dlog("Following CNAME chain to " + question_.toText());
  231. doLookup();
  232. return false;
  233. break;
  234. case isc::resolve::ResponseClassifier::NXDOMAIN:
  235. // NXDOMAIN, just copy and return.
  236. isc::resolve::copyResponseMessage(incoming, answer_message_);
  237. return true;
  238. break;
  239. case isc::resolve::ResponseClassifier::REFERRAL:
  240. // Referral. For now we just take the first glue address
  241. // we find and continue with that
  242. zone_servers_.clear();
  243. for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_ADDITIONAL);
  244. rrsi != incoming.endSection(Message::SECTION_ADDITIONAL) && !found_ns_address;
  245. rrsi++) {
  246. ConstRRsetPtr rrs = *rrsi;
  247. if (rrs->getType() == RRType::A()) {
  248. // found address
  249. RdataIteratorPtr rdi = rrs->getRdataIterator();
  250. // just use the first for now
  251. if (!rdi->isLast()) {
  252. std::string addr_str = rdi->getCurrent().toText();
  253. dlog("[XX] first address found: " + addr_str);
  254. // now we have one address, simply
  255. // resend that exact same query
  256. // to that address and yield, when it
  257. // returns, loop again.
  258. // TODO should use NSAS
  259. zone_servers_.push_back(addr_t(addr_str, 53));
  260. found_ns_address = true;
  261. break;
  262. }
  263. }
  264. }
  265. if (found_ns_address) {
  266. // next resolver round
  267. // we do NOT use doLookup() here, but send() (i.e. we
  268. // skip the cache), since if we had the final answer
  269. // instead of a delegation cached, we would have been
  270. // there by now.
  271. send();
  272. return false;
  273. } else {
  274. dlog("[XX] no ready-made addresses in additional. need nsas.");
  275. // TODO this will result in answering with the delegation. oh well
  276. isc::resolve::copyResponseMessage(incoming, answer_message_);
  277. return true;
  278. }
  279. break;
  280. case isc::resolve::ResponseClassifier::TRUNCATED:
  281. // Truncated packet. If the protocol we used for the last one is
  282. // UDP, re-query using TCP. Otherwise regard it as an error.
  283. if (protocol_ == IOFetch::UDP) {
  284. dlog("Response truncated, re-querying over TCP");
  285. send(IOFetch::TCP);
  286. break;
  287. }
  288. // Was a TCP query so we have received a packet over TCP with the TC
  289. // bit set: drop through to common error processing.
  290. // TODO: Can we use what we have received instead of discarding it?
  291. case isc::resolve::ResponseClassifier::EMPTY:
  292. case isc::resolve::ResponseClassifier::EXTRADATA:
  293. case isc::resolve::ResponseClassifier::INVNAMCLASS:
  294. case isc::resolve::ResponseClassifier::INVTYPE:
  295. case isc::resolve::ResponseClassifier::MISMATQUEST:
  296. case isc::resolve::ResponseClassifier::MULTICLASS:
  297. case isc::resolve::ResponseClassifier::NOTONEQUEST:
  298. case isc::resolve::ResponseClassifier::NOTRESPONSE:
  299. case isc::resolve::ResponseClassifier::NOTSINGLE:
  300. case isc::resolve::ResponseClassifier::OPCODE:
  301. case isc::resolve::ResponseClassifier::RCODE:
  302. // Should we try a different server rather than SERVFAIL?
  303. isc::resolve::makeErrorMessage(answer_message_,
  304. Rcode::SERVFAIL());
  305. return true;
  306. break;
  307. }
  308. // should not be reached. assert here?
  309. dlog("[FATAL] unreachable code");
  310. return true;
  311. }
  312. public:
  313. RunningQuery(IOService& io,
  314. const Question &question,
  315. MessagePtr answer_message,
  316. boost::shared_ptr<AddressVector> upstream,
  317. boost::shared_ptr<AddressVector> upstream_root,
  318. std::pair<std::string, uint16_t>& test_server,
  319. OutputBufferPtr buffer,
  320. isc::resolve::ResolverInterface::CallbackPtr cb,
  321. int query_timeout, int client_timeout, int lookup_timeout,
  322. unsigned retries,
  323. isc::cache::ResolverCache& cache) :
  324. io_(io),
  325. question_(question),
  326. answer_message_(answer_message),
  327. upstream_(upstream),
  328. upstream_root_(upstream_root),
  329. test_server_(test_server),
  330. buffer_(buffer),
  331. resolvercallback_(cb),
  332. protocol_(IOFetch::UDP),
  333. cname_count_(0),
  334. query_timeout_(query_timeout),
  335. retries_(retries),
  336. client_timer(io.get_io_service()),
  337. lookup_timer(io.get_io_service()),
  338. queries_out_(0),
  339. done_(false),
  340. answer_sent_(false),
  341. cache_(cache)
  342. {
  343. // Setup the timer to stop trying (lookup_timeout)
  344. if (lookup_timeout >= 0) {
  345. lookup_timer.expires_from_now(
  346. boost::posix_time::milliseconds(lookup_timeout));
  347. lookup_timer.async_wait(boost::bind(&RunningQuery::stop, this, false));
  348. }
  349. // Setup the timer to send an answer (client_timeout)
  350. if (client_timeout >= 0) {
  351. client_timer.expires_from_now(
  352. boost::posix_time::milliseconds(client_timeout));
  353. client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
  354. }
  355. // should use NSAS for root servers
  356. // Adding root servers if not a forwarder
  357. if (upstream_->empty()) {
  358. setZoneServersToRoot();
  359. }
  360. doLookup();
  361. }
  362. void setZoneServersToRoot() {
  363. zone_servers_.clear();
  364. if (upstream_root_->empty()) { //if no root ips given, use this
  365. zone_servers_.push_back(addr_t("192.5.5.241", 53));
  366. } else {
  367. // copy the list
  368. dlog("Size is " +
  369. boost::lexical_cast<std::string>(upstream_root_->size()) +
  370. "\n");
  371. for(AddressVector::iterator it = upstream_root_->begin();
  372. it < upstream_root_->end(); ++it) {
  373. zone_servers_.push_back(addr_t(it->first,it->second));
  374. dlog("Put " + zone_servers_.back().first + "into root list\n");
  375. }
  376. }
  377. }
  378. virtual void clientTimeout() {
  379. // Return a SERVFAIL, but do not stop until
  380. // we have an answer or timeout ourselves
  381. isc::resolve::makeErrorMessage(answer_message_,
  382. Rcode::SERVFAIL());
  383. if (!answer_sent_) {
  384. answer_sent_ = true;
  385. resolvercallback_->success(answer_message_);
  386. }
  387. }
  388. virtual void stop(bool resume) {
  389. // if we cancel our timers, we will still get an event for
  390. // that, so we cannot delete ourselves just yet (those events
  391. // would be bound to a deleted object)
  392. // cancel them one by one, both cancels should get us back
  393. // here again.
  394. // same goes if we have an outstanding query (can't delete
  395. // until that one comes back to us)
  396. done_ = true;
  397. if (resume && !answer_sent_) {
  398. answer_sent_ = true;
  399. // There are two types of messages we could store in the
  400. // cache;
  401. // 1. answers to our fetches from authoritative servers,
  402. // exactly as we receive them, and
  403. // 2. answers to queries we received from clients, which
  404. // have received additional processing (following CNAME
  405. // chains, for instance)
  406. //
  407. // Doing only the first would mean we would have to re-do
  408. // processing when we get data from our cache, and doing
  409. // only the second would miss out on the side-effect of
  410. // having nameserver data in our cache.
  411. //
  412. // So right now we do both. Since the cache (currently)
  413. // stores Messages on their question section only, this
  414. // does mean that we overwrite the messages we stored in
  415. // the previous iteration if we are following a delegation.
  416. cache_.update(*answer_message_);
  417. resolvercallback_->success(answer_message_);
  418. } else {
  419. resolvercallback_->failure();
  420. }
  421. if (lookup_timer.cancel() != 0) {
  422. return;
  423. }
  424. if (client_timer.cancel() != 0) {
  425. return;
  426. }
  427. if (queries_out_ > 0) {
  428. return;
  429. }
  430. delete this;
  431. }
  432. // This function is used as callback from DNSQuery.
  433. virtual void operator()(IOFetch::Result result) {
  434. --queries_out_;
  435. if (!done_ && result != IOFetch::TIME_OUT) {
  436. // we got an answer
  437. Message incoming(Message::PARSE);
  438. InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
  439. incoming.fromWire(ibuf);
  440. if (upstream_->size() == 0 &&
  441. incoming.getRcode() == Rcode::NOERROR()) {
  442. done_ = handleRecursiveAnswer(incoming);
  443. } else {
  444. isc::resolve::copyResponseMessage(incoming, answer_message_);
  445. done_ = true;
  446. }
  447. if (done_) {
  448. stop(true);
  449. }
  450. } else if (!done_ && retries_--) {
  451. // We timed out, but we have some retries, so send again
  452. dlog("Timeout, resending query");
  453. send();
  454. } else {
  455. // out of retries, give up for now
  456. stop(false);
  457. }
  458. }
  459. };
  460. }
  461. void
  462. RecursiveQuery::resolve(const QuestionPtr& question,
  463. const isc::resolve::ResolverInterface::CallbackPtr callback)
  464. {
  465. IOService& io = dns_service_.getIOService();
  466. MessagePtr answer_message(new Message(Message::RENDER));
  467. isc::resolve::initResponseMessage(*question, *answer_message);
  468. OutputBufferPtr buffer(new OutputBuffer(0));
  469. dlog("Try out cache first (direct call to resolve)");
  470. // First try to see if we have something cached in the messagecache
  471. if (cache_.lookup(question->getName(), question->getType(),
  472. question->getClass(), *answer_message)) {
  473. dlog("Message found in cache, returning that");
  474. // TODO: err, should cache set rcode as well?
  475. answer_message->setRcode(Rcode::NOERROR());
  476. callback->success(answer_message);
  477. } else {
  478. dlog("Message not found in cache, starting recursive query");
  479. // It will delete itself when it is done
  480. new RunningQuery(io, *question, answer_message, upstream_,
  481. upstream_root_, test_server_,
  482. buffer, callback, query_timeout_,
  483. client_timeout_, lookup_timeout_, retries_,
  484. cache_);
  485. }
  486. }
  487. void
  488. RecursiveQuery::resolve(const Question& question,
  489. MessagePtr answer_message,
  490. OutputBufferPtr buffer,
  491. DNSServer* server)
  492. {
  493. // XXX: eventually we will need to be able to determine whether
  494. // the message should be sent via TCP or UDP, or sent initially via
  495. // UDP and then fall back to TCP on failure, but for the moment
  496. // we're only going to handle UDP.
  497. IOService& io = dns_service_.getIOService();
  498. isc::resolve::ResolverInterface::CallbackPtr crs(
  499. new isc::resolve::ResolverCallbackServer(server));
  500. // TODO: general 'prepareinitialanswer'
  501. answer_message->setOpcode(isc::dns::Opcode::QUERY());
  502. answer_message->addQuestion(question);
  503. // First try to see if we have something cached in the messagecache
  504. dlog("Try out cache first (started by incoming event)");
  505. if (cache_.lookup(question.getName(), question.getType(),
  506. question.getClass(), *answer_message)) {
  507. dlog("Message found in cache, returning that");
  508. // TODO: err, should cache set rcode as well?
  509. answer_message->setRcode(Rcode::NOERROR());
  510. crs->success(answer_message);
  511. } else {
  512. dlog("Message not found in cache, starting recursive query");
  513. // It will delete itself when it is done
  514. new RunningQuery(io, question, answer_message, upstream_, upstream_root_,
  515. test_server_,
  516. buffer, crs, query_timeout_, client_timeout_,
  517. lookup_timeout_, retries_, cache_);
  518. }
  519. }
  520. } // namespace asiolink