recursive_query.cc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  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 <nsas/address_request_callback.h>
  28. #include <nsas/nameserver_address.h>
  29. #include <asio.hpp>
  30. #include <asiolink/dns_service.h>
  31. #include <asiolink/io_fetch.h>
  32. #include <asiolink/io_service.h>
  33. #include <resolve/recursive_query.h>
  34. using isc::log::dlog;
  35. using namespace isc::dns;
  36. namespace asiolink {
  37. typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
  38. // Here we do not use the typedef above, as the SunStudio compiler
  39. // mishandles this in its name mangling, and wouldn't compile.
  40. // We can probably use a typedef, but need to move it to a central
  41. // location and use it consistently.
  42. RecursiveQuery::RecursiveQuery(DNSService& dns_service,
  43. isc::nsas::NameserverAddressStore& nsas,
  44. isc::cache::ResolverCache& cache,
  45. const std::vector<std::pair<std::string, uint16_t> >& upstream,
  46. const std::vector<std::pair<std::string, uint16_t> >& upstream_root,
  47. int query_timeout, int client_timeout, int lookup_timeout,
  48. unsigned retries) :
  49. dns_service_(dns_service),
  50. nsas_(nsas), cache_(cache),
  51. upstream_(new AddressVector(upstream)),
  52. upstream_root_(new AddressVector(upstream_root)),
  53. test_server_("", 0),
  54. query_timeout_(query_timeout), client_timeout_(client_timeout),
  55. lookup_timeout_(lookup_timeout), retries_(retries)
  56. {
  57. }
  58. // Set the test server - only used for unit testing.
  59. void
  60. RecursiveQuery::setTestServer(const std::string& address, uint16_t port) {
  61. dlog("Setting test server to " + address + "(" +
  62. boost::lexical_cast<std::string>(port) + ")");
  63. test_server_.first = address;
  64. test_server_.second = port;
  65. }
  66. namespace {
  67. typedef std::pair<std::string, uint16_t> addr_t;
  68. /*
  69. * This is a query in progress. When a new query is made, this one holds
  70. * the context information about it, like how many times we are allowed
  71. * to retry on failure, what to do when we succeed, etc.
  72. *
  73. * Used by RecursiveQuery::sendQuery.
  74. */
  75. class RunningQuery : public IOFetch::Callback {
  76. class ResolverNSASCallback : public isc::nsas::AddressRequestCallback {
  77. public:
  78. ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
  79. void success(const isc::nsas::NameserverAddress& address) {
  80. dlog("Found a nameserver, sending query to " + address.getAddress().toText());
  81. rq_->nsasCallbackCalled();
  82. rq_->sendTo(address);
  83. }
  84. void unreachable() {
  85. dlog("Nameservers unreachable");
  86. // Drop query or send servfail?
  87. rq_->nsasCallbackCalled();
  88. rq_->makeSERVFAIL();
  89. rq_->callCallback(true);
  90. rq_->stop();
  91. }
  92. private:
  93. RunningQuery* rq_;
  94. };
  95. private:
  96. // The io service to handle async calls
  97. IOService& io_;
  98. // Info for (re)sending the query (the question and destination)
  99. Question question_;
  100. // This is where we build and store our final answer
  101. MessagePtr answer_message_;
  102. // currently we use upstream as the current list of NS records
  103. // we should differentiate between forwarding and resolving
  104. boost::shared_ptr<AddressVector> upstream_;
  105. // Test server - only used for testing. This takes precedence over all
  106. // other servers if the port is non-zero.
  107. std::pair<std::string, uint16_t> test_server_;
  108. // Buffer to store the intermediate results.
  109. OutputBufferPtr buffer_;
  110. // The callback will be called when we have either decided we
  111. // are done, or when we give up
  112. isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
  113. // Protocol used for the last query. This is set to IOFetch::UDP when a
  114. // new upstream query is initiated, and changed to IOFetch::TCP if a
  115. // packet is returned with the TC bit set. It is stored here to detect the
  116. // case of a TCP packet being returned with the TC bit set.
  117. IOFetch::Protocol protocol_;
  118. // To prevent both unreasonably long cname chains and cname loops,
  119. // we simply keep a counter of the number of CNAMEs we have
  120. // followed so far (and error if it exceeds RESOLVER_MAX_CNAME_CHAIN
  121. // from lib/resolve/response_classifier.h)
  122. unsigned cname_count_;
  123. /*
  124. * TODO Do something more clever with timeouts. In the long term, some
  125. * computation of average RTT, increase with each retry, etc.
  126. */
  127. // Timeout information for outgoing queries
  128. int query_timeout_;
  129. unsigned retries_;
  130. // normal query state
  131. // Update the question that will be sent to the server
  132. void setQuestion(const Question& new_question) {
  133. question_ = new_question;
  134. }
  135. // TODO: replace by our wrapper
  136. asio::deadline_timer client_timer;
  137. asio::deadline_timer lookup_timer;
  138. // If we timed out ourselves (lookup timeout), stop issuing queries
  139. bool done_;
  140. // If we have a client timeout, we call back with a failure message,
  141. // but we do not stop yet. We use this variable to make sure we
  142. // don't call back a second time later
  143. bool callback_called_;
  144. // Reference to our NSAS
  145. isc::nsas::NameserverAddressStore& nsas_;
  146. // Reference to our cache
  147. isc::cache::ResolverCache& cache_;
  148. // the 'current' zone we are in (i.e.) we start out at the root,
  149. // and for each delegation this gets updated with the zone the
  150. // delegation points to.
  151. // TODO: make this a Name (it is a string right now because most
  152. // of the call we use it in take a string, we need update those
  153. // too).
  154. std::string cur_zone_;
  155. // This is the handler we pass on to the NSAS; it is called when
  156. // the NSAS has an address for us to query
  157. boost::shared_ptr<ResolverNSASCallback> nsas_callback_;
  158. // this is set to true if we have asked the nsas to give us
  159. // an address and we are waiting for it to call us back.
  160. // We use is to cancel the outstanding callback in case we
  161. // have a lookup timeout and decide to give up
  162. bool nsas_callback_out_;
  163. // This is the nameserver we have an outstanding query to.
  164. // It is used to update the RTT once the query returns
  165. isc::nsas::NameserverAddress current_ns_address;
  166. // The moment in time we sent a query to the nameserver above.
  167. struct timeval current_ns_qsent_time;
  168. // RunningQuery deletes itself when it is done. In order for us
  169. // to do this safely, we must make sure that there are no events
  170. // that might call back to it. There are two types of events in
  171. // this sense; the timers we set ourselves (lookup and client),
  172. // and outstanding queries to nameservers. When each of these is
  173. // started, we increase this value. When they fire, it is decreased
  174. // again. We cannot delete ourselves until this value is back to 0.
  175. //
  176. // Note that the NSAS callback is *not* seen as an outstanding
  177. // event; we can cancel the NSAS callback safely.
  178. size_t outstanding_events_;
  179. // perform a single lookup; first we check the cache to see
  180. // if we have a response for our query stored already. if
  181. // so, call handlerecursiveresponse(), if not, we call send()
  182. void doLookup() {
  183. dlog("doLookup: try cache");
  184. Message cached_message(Message::RENDER);
  185. isc::resolve::initResponseMessage(question_, cached_message);
  186. if (cache_.lookup(question_.getName(), question_.getType(),
  187. question_.getClass(), cached_message)) {
  188. dlog("Message found in cache, continuing with that");
  189. // Should these be set by the cache too?
  190. cached_message.setOpcode(Opcode::QUERY());
  191. cached_message.setRcode(Rcode::NOERROR());
  192. cached_message.setHeaderFlag(Message::HEADERFLAG_QR);
  193. if (handleRecursiveAnswer(cached_message)) {
  194. callCallback(true);
  195. stop();
  196. }
  197. } else {
  198. cur_zone_ = ".";
  199. send();
  200. }
  201. }
  202. // Send the current question to the given nameserver address
  203. void sendTo(const isc::nsas::NameserverAddress& address) {
  204. // We need to keep track of the Address, so that we can update
  205. // the RTT
  206. current_ns_address = address;
  207. gettimeofday(&current_ns_qsent_time, NULL);
  208. ++outstanding_events_;
  209. if (test_server_.second != 0) {
  210. IOFetch query(protocol_, io_, question_,
  211. test_server_.first,
  212. test_server_.second, buffer_, this,
  213. query_timeout_);
  214. io_.get_io_service().post(query);
  215. } else {
  216. IOFetch query(protocol_, io_, question_,
  217. current_ns_address.getAddress(),
  218. 53, buffer_, this,
  219. query_timeout_);
  220. io_.get_io_service().post(query);
  221. }
  222. }
  223. // 'general' send; if we are in forwarder mode, send a query to
  224. // a random nameserver in our forwarders list. If we are in
  225. // recursive mode, ask the NSAS to give us an address.
  226. void send(IOFetch::Protocol protocol = IOFetch::UDP) {
  227. // If are in forwarder mode, send it to a random
  228. // forwarder. If not, ask the NSAS for an address
  229. const int uc = upstream_->size();
  230. protocol_ = protocol; // Store protocol being used for this
  231. if (test_server_.second != 0) {
  232. dlog("Sending upstream query (" + question_.toText() +
  233. ") to test server at " + test_server_.first);
  234. ++outstanding_events_;
  235. IOFetch query(protocol, io_, question_,
  236. test_server_.first,
  237. test_server_.second, buffer_, this,
  238. query_timeout_);
  239. io_.get_io_service().post(query);
  240. } else if (uc > 0) {
  241. // TODO: use boost, or rand()-utility function we provide
  242. int serverIndex = rand() % uc;
  243. dlog("Sending upstream query (" + question_.toText() +
  244. ") to " + upstream_->at(serverIndex).first);
  245. ++outstanding_events_;
  246. IOFetch query(protocol, io_, question_,
  247. upstream_->at(serverIndex).first,
  248. upstream_->at(serverIndex).second, buffer_, this,
  249. query_timeout_);
  250. io_.get_io_service().post(query);
  251. } else {
  252. // Ask the NSAS for an address for the current zone,
  253. // the callback will call the actual sendTo()
  254. dlog("Look up nameserver for " + cur_zone_ + " in NSAS");
  255. // Can we have multiple calls to nsas_out? Let's assume not
  256. // for now
  257. assert(!nsas_callback_out_);
  258. nsas_callback_out_ = true;
  259. nsas_.lookup(cur_zone_, question_.getClass(), nsas_callback_);
  260. }
  261. }
  262. // Called by our NSAS callback handler so we know we do not have
  263. // an outstanding NSAS call anymore.
  264. void nsasCallbackCalled() {
  265. nsas_callback_out_ = false;
  266. }
  267. // This function is called by operator() and lookup();
  268. // We have an answer either from a nameserver or the cache, and
  269. // we do not know yet if this is a final answer we can send back or
  270. // that more recursive processing needs to be done.
  271. // Depending on the content, we go on recursing or return
  272. //
  273. // This method also updates the cache, depending on the content
  274. // of the message
  275. //
  276. // returns true if we are done (either we have an answer or an
  277. // error message)
  278. // returns false if we are not done
  279. bool handleRecursiveAnswer(const Message& incoming) {
  280. dlog("Handle response");
  281. // In case we get a CNAME, we store the target
  282. // here (classify() will set it when it walks through
  283. // the cname chain to verify it).
  284. Name cname_target(question_.getName());
  285. isc::resolve::ResponseClassifier::Category category =
  286. isc::resolve::ResponseClassifier::classify(
  287. question_, incoming, cname_target, cname_count_);
  288. bool found_ns = false;
  289. switch (category) {
  290. case isc::resolve::ResponseClassifier::ANSWER:
  291. case isc::resolve::ResponseClassifier::ANSWERCNAME:
  292. // Done. copy and return.
  293. dlog("Response is an answer");
  294. isc::resolve::copyResponseMessage(incoming, answer_message_);
  295. cache_.update(*answer_message_);
  296. return true;
  297. break;
  298. case isc::resolve::ResponseClassifier::CNAME:
  299. dlog("Response is CNAME!");
  300. // (unfinished) CNAME. We set our question_ to the CNAME
  301. // target, then start over at the beginning (for now, that
  302. // is, we reset our 'current servers' to the root servers).
  303. if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
  304. // just give up
  305. dlog("CNAME chain too long");
  306. makeSERVFAIL();
  307. return true;
  308. }
  309. answer_message_->appendSection(Message::SECTION_ANSWER,
  310. incoming);
  311. question_ = Question(cname_target, question_.getClass(),
  312. question_.getType());
  313. dlog("Following CNAME chain to " + question_.toText());
  314. doLookup();
  315. return false;
  316. break;
  317. case isc::resolve::ResponseClassifier::NXDOMAIN:
  318. case isc::resolve::ResponseClassifier::NXRRSET:
  319. dlog("Response is NXDOMAIN or NXRRSET");
  320. // NXDOMAIN, just copy and return.
  321. dlog(incoming.toText());
  322. isc::resolve::copyResponseMessage(incoming, answer_message_);
  323. // no negcache yet
  324. //cache_.update(*answer_message_);
  325. return true;
  326. break;
  327. case isc::resolve::ResponseClassifier::REFERRAL:
  328. dlog("Response is referral");
  329. cache_.update(incoming);
  330. // Referral. For now we just take the first glue address
  331. // we find and continue with that
  332. // auth section should have at least one RRset
  333. // and one of them should be an NS (otherwise
  334. // classifier should have error'd)
  335. // TODO: should we check if it really is subzone?
  336. for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_AUTHORITY);
  337. rrsi != incoming.endSection(Message::SECTION_AUTHORITY) && !found_ns;
  338. ++rrsi) {
  339. ConstRRsetPtr rrs = *rrsi;
  340. if (rrs->getType() == RRType::NS()) {
  341. // TODO: make cur_zone_ a Name instead of a string
  342. // (this requires a few API changes in related
  343. // libraries, so as not to need many conversions)
  344. cur_zone_ = rrs->getName().toText();
  345. dlog("Referred to zone " + cur_zone_);
  346. found_ns = true;
  347. break;
  348. }
  349. }
  350. if (found_ns) {
  351. // next resolver round
  352. // we do NOT use doLookup() here, but send() (i.e. we
  353. // skip the cache), since if we had the final answer
  354. // instead of a delegation cached, we would have been
  355. // there by now.
  356. GlueHints glue_hints(cur_zone_, incoming);
  357. // Ask the NSAS for an address, or glue.
  358. // This will eventually result in either sendTo()
  359. // or stop() being called by nsas_callback_
  360. assert(!nsas_callback_out_);
  361. nsas_callback_out_ = true;
  362. nsas_.lookup(cur_zone_, question_.getClass(),
  363. nsas_callback_, ANY_OK, glue_hints);
  364. return false;
  365. } else {
  366. dlog("No NS RRset in referral?");
  367. // TODO this will result in answering with the delegation. oh well
  368. isc::resolve::copyResponseMessage(incoming, answer_message_);
  369. return true;
  370. }
  371. break;
  372. case isc::resolve::ResponseClassifier::TRUNCATED:
  373. // Truncated packet. If the protocol we used for the last one is
  374. // UDP, re-query using TCP. Otherwise regard it as an error.
  375. if (protocol_ == IOFetch::UDP) {
  376. dlog("Response truncated, re-querying over TCP");
  377. send(IOFetch::TCP);
  378. return false;
  379. }
  380. // Was a TCP query so we have received a packet over TCP with the TC
  381. // bit set: drop through to common error processing.
  382. // TODO: Can we use what we have received instead of discarding it?
  383. case isc::resolve::ResponseClassifier::EMPTY:
  384. case isc::resolve::ResponseClassifier::EXTRADATA:
  385. case isc::resolve::ResponseClassifier::INVNAMCLASS:
  386. case isc::resolve::ResponseClassifier::INVTYPE:
  387. case isc::resolve::ResponseClassifier::MISMATQUEST:
  388. case isc::resolve::ResponseClassifier::MULTICLASS:
  389. case isc::resolve::ResponseClassifier::NOTONEQUEST:
  390. case isc::resolve::ResponseClassifier::NOTRESPONSE:
  391. case isc::resolve::ResponseClassifier::NOTSINGLE:
  392. case isc::resolve::ResponseClassifier::OPCODE:
  393. case isc::resolve::ResponseClassifier::RCODE:
  394. // Should we try a different server rather than SERVFAIL?
  395. makeSERVFAIL();
  396. return true;
  397. break;
  398. }
  399. // Since we do not have a default in the switch above,
  400. // the compiler should have errored on any missing case
  401. // statements.
  402. assert(false);
  403. return true;
  404. }
  405. public:
  406. RunningQuery(IOService& io,
  407. const Question& question,
  408. MessagePtr answer_message,
  409. boost::shared_ptr<AddressVector> upstream,
  410. std::pair<std::string, uint16_t>& test_server,
  411. OutputBufferPtr buffer,
  412. isc::resolve::ResolverInterface::CallbackPtr cb,
  413. int query_timeout, int client_timeout, int lookup_timeout,
  414. unsigned retries,
  415. isc::nsas::NameserverAddressStore& nsas,
  416. isc::cache::ResolverCache& cache) :
  417. io_(io),
  418. question_(question),
  419. answer_message_(answer_message),
  420. upstream_(upstream),
  421. test_server_(test_server),
  422. buffer_(buffer),
  423. resolvercallback_(cb),
  424. protocol_(IOFetch::UDP),
  425. cname_count_(0),
  426. query_timeout_(query_timeout),
  427. retries_(retries),
  428. client_timer(io.get_io_service()),
  429. lookup_timer(io.get_io_service()),
  430. done_(false),
  431. callback_called_(false),
  432. nsas_(nsas),
  433. cache_(cache),
  434. nsas_callback_(new ResolverNSASCallback(this)),
  435. nsas_callback_out_(false),
  436. outstanding_events_(0)
  437. {
  438. // Setup the timer to stop trying (lookup_timeout)
  439. if (lookup_timeout >= 0) {
  440. lookup_timer.expires_from_now(
  441. boost::posix_time::milliseconds(lookup_timeout));
  442. ++outstanding_events_;
  443. lookup_timer.async_wait(boost::bind(&RunningQuery::lookupTimeout, this));
  444. }
  445. // Setup the timer to send an answer (client_timeout)
  446. if (client_timeout >= 0) {
  447. client_timer.expires_from_now(
  448. boost::posix_time::milliseconds(client_timeout));
  449. ++outstanding_events_;
  450. client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
  451. }
  452. doLookup();
  453. }
  454. // called if we have a lookup timeout; if our callback has
  455. // not been called, call it now. Then stop.
  456. void lookupTimeout() {
  457. if (!callback_called_) {
  458. makeSERVFAIL();
  459. callCallback(true);
  460. }
  461. assert(outstanding_events_ > 0);
  462. --outstanding_events_;
  463. stop();
  464. }
  465. // called if we have a client timeout; if our callback has
  466. // not been called, call it now. But do not stop.
  467. void clientTimeout() {
  468. if (!callback_called_) {
  469. makeSERVFAIL();
  470. callCallback(true);
  471. }
  472. assert(outstanding_events_ > 0);
  473. --outstanding_events_;
  474. if (outstanding_events_ == 0) {
  475. stop();
  476. }
  477. }
  478. // If the callback has not been called yet, call it now
  479. // If success is true, we call 'success' with our answer_message
  480. // If it is false, we call failure()
  481. void callCallback(bool success) {
  482. if (!callback_called_) {
  483. callback_called_ = true;
  484. // There are two types of messages we could store in the
  485. // cache;
  486. // 1. answers to our fetches from authoritative servers,
  487. // exactly as we receive them, and
  488. // 2. answers to queries we received from clients, which
  489. // have received additional processing (following CNAME
  490. // chains, for instance)
  491. //
  492. // Doing only the first would mean we would have to re-do
  493. // processing when we get data from our cache, and doing
  494. // only the second would miss out on the side-effect of
  495. // having nameserver data in our cache.
  496. //
  497. // So right now we do both. Since the cache (currently)
  498. // stores Messages on their question section only, this
  499. // does mean that we overwrite the messages we stored in
  500. // the previous iteration if we are following a delegation.
  501. if (success) {
  502. resolvercallback_->success(answer_message_);
  503. } else {
  504. resolvercallback_->failure();
  505. }
  506. }
  507. }
  508. // We are done. If there are no more outstanding events, we delete
  509. // ourselves. If there are any, we do not.
  510. void stop() {
  511. done_ = true;
  512. if (nsas_callback_out_) {
  513. nsas_.cancel(cur_zone_, question_.getClass(), nsas_callback_);
  514. nsas_callback_out_ = false;
  515. }
  516. client_timer.cancel();
  517. lookup_timer.cancel();
  518. if (outstanding_events_ > 0) {
  519. return;
  520. } else {
  521. delete this;
  522. }
  523. }
  524. // This function is used as callback from DNSQuery.
  525. virtual void operator()(IOFetch::Result result) {
  526. // XXX is this the place for TCP retry?
  527. assert(outstanding_events_ > 0);
  528. --outstanding_events_;
  529. if (!done_ && result != IOFetch::TIME_OUT) {
  530. // we got an answer
  531. // Update the NSAS with the time it took
  532. struct timeval cur_time;
  533. gettimeofday(&cur_time, NULL);
  534. uint32_t rtt;
  535. if (cur_time.tv_sec >= current_ns_qsent_time.tv_sec ||
  536. cur_time.tv_usec > current_ns_qsent_time.tv_usec) {
  537. rtt = 1000 * (cur_time.tv_sec - current_ns_qsent_time.tv_sec);
  538. rtt += (cur_time.tv_usec - current_ns_qsent_time.tv_usec) / 1000;
  539. } else {
  540. rtt = 1;
  541. }
  542. dlog("RTT: " + boost::lexical_cast<std::string>(rtt));
  543. current_ns_address.updateRTT(rtt);
  544. Message incoming(Message::PARSE);
  545. InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
  546. incoming.fromWire(ibuf);
  547. buffer_->clear();
  548. if (recursive_mode() &&
  549. incoming.getRcode() == Rcode::NOERROR()) {
  550. done_ = handleRecursiveAnswer(incoming);
  551. } else {
  552. isc::resolve::copyResponseMessage(incoming, answer_message_);
  553. done_ = true;
  554. }
  555. if (done_) {
  556. callCallback(true);
  557. stop();
  558. }
  559. } else if (!done_ && retries_--) {
  560. // Query timed out, but we have some retries, so send again
  561. dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", resending query");
  562. if (recursive_mode()) {
  563. current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
  564. }
  565. send();
  566. } else {
  567. // We are either already done, or out of retries
  568. if (recursive_mode() && result == IOFetch::TIME_OUT) {
  569. dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", giving up");
  570. current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
  571. }
  572. if (!callback_called_) {
  573. makeSERVFAIL();
  574. callCallback(true);
  575. }
  576. stop();
  577. }
  578. }
  579. // Clear the answer parts of answer_message, and set the rcode
  580. // to servfail
  581. void makeSERVFAIL() {
  582. isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
  583. }
  584. // Returns true if we are in 'recursive' mode
  585. // Returns false if we are in 'forwarding' mode
  586. // (i.e. if we have anything in upstream_)
  587. bool recursive_mode() const {
  588. return upstream_->empty();
  589. }
  590. };
  591. }
  592. void
  593. RecursiveQuery::resolve(const QuestionPtr& question,
  594. const isc::resolve::ResolverInterface::CallbackPtr callback)
  595. {
  596. IOService& io = dns_service_.getIOService();
  597. MessagePtr answer_message(new Message(Message::RENDER));
  598. isc::resolve::initResponseMessage(*question, *answer_message);
  599. OutputBufferPtr buffer(new OutputBuffer(0));
  600. dlog("Asked to resolve: " + question->toText());
  601. dlog("Try out cache first (direct call to resolve)");
  602. // First try to see if we have something cached in the messagecache
  603. if (cache_.lookup(question->getName(), question->getType(),
  604. question->getClass(), *answer_message) &&
  605. answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
  606. dlog("Message found in cache, returning that");
  607. // TODO: err, should cache set rcode as well?
  608. answer_message->setRcode(Rcode::NOERROR());
  609. callback->success(answer_message);
  610. } else {
  611. // Perhaps we only have the one RRset?
  612. // TODO: can we do this? should we check for specific types only?
  613. RRsetPtr cached_rrset = cache_.lookup(question->getName(),
  614. question->getType(),
  615. question->getClass());
  616. if (cached_rrset) {
  617. dlog("Found single RRset in cache");
  618. answer_message->addRRset(Message::SECTION_ANSWER,
  619. cached_rrset);
  620. answer_message->setRcode(Rcode::NOERROR());
  621. callback->success(answer_message);
  622. } else {
  623. dlog("Message not found in cache, starting recursive query");
  624. // It will delete itself when it is done
  625. new RunningQuery(io, *question, answer_message, upstream_,
  626. test_server_, buffer, callback,
  627. query_timeout_, client_timeout_,
  628. lookup_timeout_, retries_, nsas_, cache_);
  629. }
  630. }
  631. }
  632. void
  633. RecursiveQuery::resolve(const Question& question,
  634. MessagePtr answer_message,
  635. OutputBufferPtr buffer,
  636. DNSServer* server)
  637. {
  638. // XXX: eventually we will need to be able to determine whether
  639. // the message should be sent via TCP or UDP, or sent initially via
  640. // UDP and then fall back to TCP on failure, but for the moment
  641. // we're only going to handle UDP.
  642. IOService& io = dns_service_.getIOService();
  643. isc::resolve::ResolverInterface::CallbackPtr crs(
  644. new isc::resolve::ResolverCallbackServer(server));
  645. // TODO: general 'prepareinitialanswer'
  646. answer_message->setOpcode(isc::dns::Opcode::QUERY());
  647. answer_message->addQuestion(question);
  648. dlog("Asked to resolve: " + question.toText());
  649. // First try to see if we have something cached in the messagecache
  650. dlog("Try out cache first (started by incoming event)");
  651. if (cache_.lookup(question.getName(), question.getType(),
  652. question.getClass(), *answer_message) &&
  653. answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
  654. dlog("Message found in cache, returning that");
  655. // TODO: err, should cache set rcode as well?
  656. answer_message->setRcode(Rcode::NOERROR());
  657. crs->success(answer_message);
  658. } else {
  659. // Perhaps we only have the one RRset?
  660. // TODO: can we do this? should we check for specific types only?
  661. RRsetPtr cached_rrset = cache_.lookup(question.getName(),
  662. question.getType(),
  663. question.getClass());
  664. if (cached_rrset) {
  665. dlog("Found single RRset in cache");
  666. answer_message->addRRset(Message::SECTION_ANSWER,
  667. cached_rrset);
  668. answer_message->setRcode(Rcode::NOERROR());
  669. crs->success(answer_message);
  670. } else {
  671. dlog("Message not found in cache, starting recursive query");
  672. // It will delete itself when it is done
  673. new RunningQuery(io, question, answer_message, upstream_,
  674. test_server_, buffer, crs, query_timeout_,
  675. client_timeout_, lookup_timeout_, retries_,
  676. nsas_, cache_);
  677. }
  678. }
  679. }
  680. } // namespace asiolink