recursive_query.cc 27 KB

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