zone_entry.cc 20 KB

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