zone_entry.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  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. #include <map>
  15. #include <config.h>
  16. #include "zone_entry.h"
  17. #include "address_request_callback.h"
  18. #include "nameserver_entry.h"
  19. #include <algorithm>
  20. #include <boost/foreach.hpp>
  21. #include <boost/bind.hpp>
  22. #include <dns/rrttl.h>
  23. #include <dns/rcode.h>
  24. #include <dns/rdataclass.h>
  25. using namespace std;
  26. namespace isc {
  27. using namespace dns;
  28. namespace nsas {
  29. ZoneEntry::ZoneEntry(
  30. isc::resolve::ResolverInterface* resolver,
  31. const std::string& name, const isc::dns::RRClass& class_code,
  32. boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
  33. boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru) :
  34. expiry_(0),
  35. name_(name), class_code_(class_code), resolver_(resolver),
  36. nameserver_table_(nameserver_table), nameserver_lru_(nameserver_lru)
  37. {
  38. in_process_[ANY_OK] = false;
  39. in_process_[V4_ONLY] = false;
  40. in_process_[V6_ONLY] = false;
  41. }
  42. namespace {
  43. // Shorter aliases for frequently used types
  44. typedef isc::locks::scoped_lock<isc::locks::recursive_mutex> Lock; // Local lock, nameservers not locked
  45. typedef boost::shared_ptr<AddressRequestCallback> CallbackPtr;
  46. /*
  47. * Create a nameserver.
  48. * Called inside a mutex so it is filled in atomically.
  49. */
  50. boost::shared_ptr<NameserverEntry>
  51. newNs(const std::string* name, const RRClass* class_code) {
  52. return (boost::shared_ptr<NameserverEntry>(new NameserverEntry(*name,
  53. *class_code)));
  54. }
  55. }
  56. /**
  57. * \short Callback class that ZoneEntry passes to a resolver.
  58. *
  59. * We need to ask for the list of nameservers. So we pass ResolverCallback
  60. * object to it, when it knows the answer, method of this thing will be
  61. * called.
  62. *
  63. * It is a nested friend class and should be considered as a part of ZoneEntry
  64. * code. It manipulates directly ZoneEntry's data members, locks it and like
  65. * that. Mostly eliminates C++ bad design of missing lambda functions.
  66. */
  67. class ZoneEntry::ResolverCallback :
  68. public isc::resolve::ResolverInterface::Callback {
  69. public:
  70. /// \short Constructor. Pass "this" zone entry
  71. ResolverCallback(boost::shared_ptr<ZoneEntry> entry) :
  72. entry_(entry)
  73. { }
  74. /**
  75. * \short It successfully received nameserver list.
  76. *
  77. * It fills the nameservers into the ZoneEntry whose callback this is.
  78. * If there are in the hash table, it is used. If not, they are
  79. * created. This might still fail, if the list is empty.
  80. *
  81. * It then calls process, to go trough the list of nameservers,
  82. * examining them and seeing if some addresses are already there
  83. * and to ask for the rest of them.
  84. */
  85. virtual void success(MessagePtr response_message) {
  86. Lock lock(entry_->mutex_);
  87. // TODO: find the correct RRset, not simply the first
  88. if (!response_message ||
  89. response_message->getRcode() != isc::dns::Rcode::NOERROR() ||
  90. response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
  91. // todo: define this
  92. failureInternal(300);
  93. }
  94. isc::dns::RRsetIterator rrsi =
  95. response_message->beginSection(isc::dns::Message::SECTION_ANSWER);
  96. const isc::dns::RRsetPtr answer = *rrsi;
  97. RdataIteratorPtr iterator(answer->getRdataIterator());
  98. // If there are no data
  99. if (iterator->isLast()) {
  100. failureInternal(answer->getTTL().getValue());
  101. return;
  102. } else {
  103. /*
  104. * We store the nameservers we have currently (we might have
  105. * none, at startup, but when we time out and ask again, we
  106. * do), so we can just reuse them instead of looking them up in
  107. * the table or creating them.
  108. */
  109. std::map<string, NameserverPtr> old;
  110. BOOST_FOREACH(const NameserverPtr& ptr, entry_->nameservers_) {
  111. old[ptr->getName()] = ptr;
  112. }
  113. /*
  114. * List of original nameservers we did not ask for IP address
  115. * yet.
  116. */
  117. set<NameserverPtr> old_not_asked;
  118. old_not_asked.swap(entry_->nameservers_not_asked_);
  119. // Once we have them put aside, remove the original set
  120. // of nameservers from the entry
  121. entry_->nameservers_.clear();
  122. // And put the ones from the answer them, reusing if possible
  123. for (; !iterator->isLast(); iterator->next()) {
  124. try {
  125. // Get the name from there
  126. Name ns_name(dynamic_cast<const rdata::generic::NS&>(
  127. iterator->getCurrent()).getNSName());
  128. // Try to find it in the old ones
  129. std::map<string, NameserverPtr>::iterator old_ns(old.find(
  130. ns_name.toText()));
  131. /*
  132. * We didn't have this nameserver before. So we just
  133. * look it up in the hash table or create it.
  134. */
  135. if (old_ns == old.end()) {
  136. // Look it up or create it
  137. string ns_name_str(ns_name.toText());
  138. pair<bool, NameserverPtr> from_hash(
  139. entry_->nameserver_table_->getOrAdd(HashKey(
  140. ns_name_str, entry_->class_code_), boost::bind(
  141. newNs, &ns_name_str, &entry_->class_code_)));
  142. // Make it at the front of the list
  143. if (from_hash.first) {
  144. entry_->nameserver_lru_->add(from_hash.second);
  145. } else {
  146. entry_->nameserver_lru_->touch(
  147. from_hash.second);
  148. }
  149. // And add it at last to the entry
  150. entry_->nameservers_.push_back(from_hash.second);
  151. entry_->nameservers_not_asked_.insert(
  152. from_hash.second);
  153. } else {
  154. // We had it before, reuse it
  155. entry_->nameservers_.push_back(old_ns->second);
  156. // Did we ask it already? If not, it is still not
  157. // asked (the one designing std interface must
  158. // have been mad)
  159. if (old_not_asked.find(old_ns->second) !=
  160. old_not_asked.end())
  161. {
  162. entry_->nameservers_not_asked_.insert(
  163. old_ns->second);
  164. }
  165. }
  166. }
  167. // OK, we skip this one as it is not NS (log?)
  168. catch (bad_cast&) { }
  169. }
  170. // It is unbelievable, but we found no nameservers there
  171. if (entry_->nameservers_.empty()) {
  172. // So we fail the same way as if we got empty list
  173. failureInternal(answer->getTTL().getValue());
  174. return;
  175. } else {
  176. // Ok, we have them. So set us as ready, set our
  177. // expiration time and try to answer what we can, ask
  178. // if there's still someone to ask.
  179. entry_->setState(READY);
  180. entry_->expiry_ = answer->getTTL().getValue() + time(NULL);
  181. entry_->process(ADDR_REQ_MAX, NameserverPtr());
  182. return;
  183. }
  184. }
  185. }
  186. /// \short Failed to receive answer.
  187. virtual void failure() {
  188. failureInternal(300);
  189. }
  190. private:
  191. /**
  192. * \short Common function called when "it did not work"
  193. *
  194. * It marks the ZoneEntry as unreachable and processes callbacks (by
  195. * calling process).
  196. */
  197. void failureInternal(time_t ttl) {
  198. Lock lock(entry_->mutex_);
  199. entry_->setState(UNREACHABLE);
  200. entry_->expiry_ = ttl + time(NULL);
  201. // Process all three callback lists and tell them KO
  202. entry_->process(ADDR_REQ_MAX, NameserverPtr());
  203. }
  204. /// \short The entry we are callback of
  205. boost::shared_ptr<ZoneEntry> entry_;
  206. };
  207. void
  208. ZoneEntry::addCallback(CallbackPtr callback, AddressFamily family,
  209. const GlueHints& glue_hints) {
  210. Lock lock(mutex_);
  211. bool ask(false);
  212. // Look at expiration time
  213. if (expiry_ && time(NULL) >= expiry_) {
  214. setState(EXPIRED);
  215. }
  216. // We need to ask (again)
  217. if (getState() == EXPIRED || getState() == NOT_ASKED) {
  218. ask = true;
  219. }
  220. // We do not have the answer right away, just queue the callback
  221. bool execute(!ask && getState() != IN_PROGRESS &&
  222. callbacks_[family].empty());
  223. // Unless there was glue
  224. if (ask && glue_hints.hasGlue(family)) {
  225. callback->success(glue_hints.getGlue(family));
  226. } else {
  227. callbacks_[family].push_back(callback);
  228. }
  229. if (execute) {
  230. // Try to process it right away, store if not possible to handle
  231. process(family, NameserverPtr());
  232. return;
  233. }
  234. if (ask) {
  235. setState(IN_PROGRESS);
  236. // Our callback might be directly called from resolve, unlock now
  237. QuestionPtr question(new Question(Name(name_), class_code_,
  238. RRType::NS()));
  239. boost::shared_ptr<ResolverCallback> resolver_callback(
  240. new ResolverCallback(shared_from_this()));
  241. resolver_->resolve(question, resolver_callback);
  242. return;
  243. }
  244. }
  245. void
  246. ZoneEntry::removeCallback(const CallbackPtr& callback, AddressFamily family) {
  247. Lock lock(mutex_);
  248. std::vector<boost::shared_ptr<AddressRequestCallback> >::iterator i =
  249. callbacks_[family].begin();
  250. for (; i != callbacks_[family].end(); ++i) {
  251. if (*i == callback) {
  252. callbacks_[family].erase(i);
  253. // At this point, a callback should only be in the list
  254. // once (enforced by RunningQuery doing only one at a time)
  255. // If that changes, we need to revise this (can't delete
  256. // elements from a list we're looping over)
  257. return;
  258. }
  259. }
  260. }
  261. namespace {
  262. // This just moves items from one container to another
  263. template<class Container>
  264. void
  265. move(Container& into, Container& from) {
  266. into.insert(into.end(), from.begin(), from.end());
  267. from.clear();
  268. }
  269. // Update the address selector according to the RTTs
  270. //
  271. // Each address has a probability to be selected if multiple addresses are available
  272. // The weight factor is equal to 1/(rtt*rtt), then all the weight factors are normalized
  273. // to make the sum equal to 1.0
  274. void
  275. updateAddressSelector(std::vector<NameserverAddress>& addresses,
  276. WeightedRandomIntegerGenerator& selector)
  277. {
  278. vector<double> probabilities;
  279. BOOST_FOREACH(NameserverAddress& address, addresses) {
  280. uint32_t rtt = address.getAddressEntry().getRTT();
  281. if(rtt == 0) {
  282. isc_throw(RTTIsZero, "The RTT is 0");
  283. }
  284. if(rtt == AddressEntry::UNREACHABLE) {
  285. probabilities.push_back(0);
  286. } else {
  287. probabilities.push_back(1.0/(rtt*rtt));
  288. }
  289. }
  290. // Calculate the sum
  291. double sum = accumulate(probabilities.begin(), probabilities.end(), 0.0);
  292. if(sum != 0) {
  293. // Normalize the probabilities to make the sum equal to 1.0
  294. for(vector<double>::iterator it = probabilities.begin();
  295. it != probabilities.end(); ++it){
  296. (*it) /= sum;
  297. }
  298. } else if(probabilities.size() > 0){
  299. // If all the nameservers are unreachable, the sum will be 0
  300. // So give each server equal opportunity to be selected.
  301. for(vector<double>::iterator it = probabilities.begin();
  302. it != probabilities.end(); ++it){
  303. (*it) = 1.0/probabilities.size();
  304. }
  305. }
  306. selector.reset(probabilities);
  307. }
  308. }
  309. /**
  310. * \short Sets given boolean to false when destroyed.
  311. *
  312. * This is hack eliminating C++ missing finally. We need to make sure
  313. * the value gets set to false when we leave the function, so we use
  314. * a Guard object, that sets it when it gets out of scope.
  315. */
  316. class ZoneEntry::ProcessGuard {
  317. public:
  318. ProcessGuard(bool& guarded) :
  319. guarded_(guarded)
  320. { }
  321. ~ ProcessGuard() {
  322. guarded_ = false;
  323. }
  324. private:
  325. bool& guarded_;
  326. };
  327. /**
  328. * \short Callback from NameserverEntry to us.
  329. *
  330. * We registre object of this class whenever some ZoneEntry has a need to be
  331. * notified of a change (received data) inside its NameserverEntry.
  332. *
  333. * This is part of the ZoneEntry code (not visible from outside, accessing
  334. * private functions). It is here just because C++ does not know propper lambda
  335. * functions.
  336. */
  337. class ZoneEntry::NameserverCallback : public NameserverEntry::Callback {
  338. public:
  339. /**
  340. * \short Constructor.
  341. *
  342. * \param entry The ZoneEntry to be notified.
  343. * \param family For which address family this change is, so we
  344. * do not process all the nameserves and callbacks there.
  345. */
  346. NameserverCallback(boost::shared_ptr<ZoneEntry> entry, AddressFamily family) :
  347. entry_(entry),
  348. family_(family)
  349. { }
  350. /**
  351. * \short Callback method.
  352. *
  353. * This is called by NameserverEntry when the change happens.
  354. * We just call process to go trough relevant nameservers and call
  355. * any callbacks we can.
  356. */
  357. virtual void operator()(NameserverPtr ns) {
  358. entry_->process(family_, ns);
  359. }
  360. private:
  361. boost::shared_ptr<ZoneEntry> entry_;
  362. AddressFamily family_;
  363. };
  364. void
  365. ZoneEntry::dispatchFailures(AddressFamily family) {
  366. // We extract all the callbacks
  367. vector<CallbackPtr> callbacks;
  368. if (family == ADDR_REQ_MAX) {
  369. move(callbacks_[ANY_OK], callbacks_[V4_ONLY]);
  370. move(callbacks_[ANY_OK], callbacks_[V6_ONLY]);
  371. family = ANY_OK;
  372. }
  373. callbacks.swap(callbacks_[family]);
  374. BOOST_FOREACH(const CallbackPtr& callback, callbacks) {
  375. callback->unreachable();
  376. }
  377. }
  378. void
  379. ZoneEntry::process(AddressFamily family,
  380. const boost::shared_ptr<NameserverEntry>& nameserver)
  381. {
  382. Lock lock(mutex_);
  383. switch (getState()) {
  384. // These are not interesting, nothing to return now
  385. case NOT_ASKED:
  386. case IN_PROGRESS:
  387. case EXPIRED:
  388. break;
  389. case UNREACHABLE: {
  390. dispatchFailures(family);
  391. // And we do nothing more now
  392. break;
  393. }
  394. case READY:
  395. if (family == ADDR_REQ_MAX) {
  396. // Just process each one separately
  397. // TODO Think this over, is it safe, to unlock in the middle?
  398. process(ANY_OK, nameserver);
  399. process(V4_ONLY, nameserver);
  400. process(V6_ONLY, nameserver);
  401. } else {
  402. // Nothing to do anyway for this family, be dormant
  403. if (callbacks_[family].empty()) {
  404. return;
  405. }
  406. /*
  407. * If we have multiple nameservers and more than 1 of them
  408. * is in the cache, we want to choose from all their addresses.
  409. * So we ensure this instance of process is the only one on
  410. * the stack. If not, we terminate and let the outernmost
  411. * one handle it when we return to it.
  412. *
  413. * If we didn't do it, one instance would call "resolve". If it
  414. * was from cache, it would imediatelly recurse back to another
  415. * process (trough the nameserver callback, etc), which would
  416. * take that only one nameserver and trigger all callbacks.
  417. * Only then would resolve terminate and we could ask for the
  418. * second nameserver. This way, we first receive all the
  419. * nameservers that are already in cache and trigger the
  420. * callbacks only then.
  421. *
  422. * However, this does not wait for external fetches of
  423. * nameserver addresses, as the callback is called after
  424. * process terminates. Therefore this waits only for filling
  425. * of the nameservers which we already have in cache.
  426. */
  427. if (in_process_[family]) {
  428. return;
  429. }
  430. // Mark we are on the stack
  431. ProcessGuard guard(in_process_[family]);
  432. in_process_[family] = true;
  433. // Variables to store the data to
  434. NameserverEntry::AddressVector addresses;
  435. NameserverVector to_ask;
  436. bool pending(false);
  437. // Pick info from the nameservers
  438. BOOST_FOREACH(const NameserverPtr& ns, nameservers_) {
  439. Fetchable::State ns_state(ns->getAddresses(addresses,
  440. family, ns == nameserver));
  441. switch (ns_state) {
  442. case IN_PROGRESS:
  443. pending = true;
  444. // Someone asked it, but not us, we don't have
  445. // callback
  446. if (nameservers_not_asked_.find(ns) !=
  447. nameservers_not_asked_.end())
  448. {
  449. to_ask.push_back(ns);
  450. }
  451. break;
  452. case NOT_ASKED:
  453. case EXPIRED:
  454. to_ask.push_back(ns);
  455. break;
  456. case UNREACHABLE:
  457. case READY:
  458. // Not interested, but avoiding warning
  459. break;
  460. }
  461. }
  462. // We have someone to ask, so do it
  463. if (!to_ask.empty()) {
  464. // We ask everything that makes sense now
  465. nameservers_not_asked_.clear();
  466. /*
  467. * TODO: Possible place for an optimisation. We now ask
  468. * everything we can. We should limit this to something like
  469. * 2 concurrent NS fetches (and fetch cache first, then
  470. * fetch the remote ones). But fetching everything right
  471. * away is simpler.
  472. */
  473. BOOST_FOREACH(const NameserverPtr& ns, to_ask) {
  474. // Put all 3 callbacks there. If we put just the
  475. // current family, it might not work due to missing
  476. // callback for different one.
  477. // If they recurse back to us (call directly), we kill
  478. // it by the in_process_
  479. insertCallback(ns, ADDR_REQ_MAX);
  480. }
  481. // Retry with all the data that might have arrived
  482. in_process_[family] = false;
  483. // We do not provide the callback again
  484. process(family, nameserver);
  485. // And be done
  486. return;
  487. // We have some addresses to answer
  488. } else if (!addresses.empty()) {
  489. // Prepare the selector of addresses
  490. // TODO: Think of a way how to keep it for a while
  491. // (not update every time)
  492. updateAddressSelector(addresses, address_selector);
  493. // Extract the callbacks
  494. vector<CallbackPtr> to_execute;
  495. // FIXME: Think of a solution where we do not lose
  496. // any callbacks upon exception
  497. to_execute.swap(callbacks_[family]);
  498. // Run the callbacks
  499. BOOST_FOREACH(const CallbackPtr& callback, to_execute) {
  500. callback->success(addresses[address_selector()]);
  501. }
  502. return;
  503. } else if (!pending) {
  504. dispatchFailures(family);
  505. return;
  506. }
  507. }
  508. return;
  509. }
  510. }
  511. void
  512. ZoneEntry::insertCallback(NameserverPtr ns, AddressFamily family) {
  513. if (family == ADDR_REQ_MAX) {
  514. insertCallback(ns, ANY_OK);
  515. insertCallback(ns, V4_ONLY);
  516. insertCallback(ns, V6_ONLY);
  517. } else {
  518. boost::shared_ptr<NameserverCallback> callback(new NameserverCallback(
  519. shared_from_this(), family));
  520. ns->askIP(resolver_, callback, family);
  521. }
  522. }
  523. }; // namespace nsas
  524. }; // namespace isc