zone_entry.cc 22 KB

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