zone_entry.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  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 <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. boost::shared_ptr<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. 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. Lock lock(mutex_);
  210. bool ask(false);
  211. // Look at expiration time
  212. if (expiry_ && time(NULL) >= expiry_) {
  213. setState(EXPIRED);
  214. }
  215. // We need to ask (again)
  216. if (getState() == EXPIRED || getState() == NOT_ASKED) {
  217. ask = true;
  218. }
  219. // We do not have the answer right away, just queue the callback
  220. bool execute(!ask && getState() != IN_PROGRESS &&
  221. callbacks_[family].empty());
  222. callbacks_[family].push_back(callback);
  223. if (execute) {
  224. // Try to process it right away, store if not possible to handle
  225. process(family, NameserverPtr());
  226. return;
  227. }
  228. if (ask) {
  229. setState(IN_PROGRESS);
  230. // Our callback might be directly called from resolve, unlock now
  231. QuestionPtr question(new Question(Name(name_), class_code_,
  232. RRType::NS()));
  233. boost::shared_ptr<ResolverCallback> resolver_callback(
  234. new ResolverCallback(shared_from_this()));
  235. resolver_->resolve(question, resolver_callback);
  236. return;
  237. }
  238. }
  239. void
  240. ZoneEntry::removeCallback(const CallbackPtr& callback, AddressFamily family) {
  241. Lock lock(mutex_);
  242. std::vector<boost::shared_ptr<AddressRequestCallback> >::iterator i =
  243. callbacks_[family].begin();
  244. for (; i != callbacks_[family].end(); ++i) {
  245. if (*i == callback) {
  246. callbacks_[family].erase(i);
  247. // At this point, a callback should only be in the list
  248. // once (enforced by RunningQuery doing only one at a time)
  249. // If that changes, we need to revise this (can't delete
  250. // elements from a list we're looping over)
  251. return;
  252. }
  253. }
  254. }
  255. namespace {
  256. // This just moves items from one container to another
  257. template<class Container>
  258. void
  259. move(Container& into, Container& from) {
  260. into.insert(into.end(), from.begin(), from.end());
  261. from.clear();
  262. }
  263. // Update the address selector according to the RTTs
  264. //
  265. // Each address has a probability to be selected if multiple addresses are available
  266. // The weight factor is equal to 1/(rtt*rtt), then all the weight factors are normalized
  267. // to make the sum equal to 1.0
  268. void
  269. updateAddressSelector(std::vector<NameserverAddress>& addresses,
  270. WeightedRandomIntegerGenerator& selector)
  271. {
  272. vector<double> probabilities;
  273. BOOST_FOREACH(NameserverAddress& address, addresses) {
  274. uint32_t rtt = address.getAddressEntry().getRTT();
  275. if(rtt == 0) {
  276. isc_throw(RTTIsZero, "The RTT is 0");
  277. }
  278. if(rtt == AddressEntry::UNREACHABLE) {
  279. probabilities.push_back(0);
  280. } else {
  281. probabilities.push_back(1.0/(rtt*rtt));
  282. }
  283. }
  284. // Calculate the sum
  285. double sum = accumulate(probabilities.begin(), probabilities.end(), 0.0);
  286. if(sum != 0) {
  287. // Normalize the probabilities to make the sum equal to 1.0
  288. for(vector<double>::iterator it = probabilities.begin();
  289. it != probabilities.end(); ++it){
  290. (*it) /= sum;
  291. }
  292. } else if(probabilities.size() > 0){
  293. // If all the nameservers are unreachable, the sum will be 0
  294. // So give each server equal opportunity to be selected.
  295. for(vector<double>::iterator it = probabilities.begin();
  296. it != probabilities.end(); ++it){
  297. (*it) = 1.0/probabilities.size();
  298. }
  299. }
  300. selector.reset(probabilities);
  301. }
  302. }
  303. /**
  304. * \short Sets given boolean to false when destroyed.
  305. *
  306. * This is hack eliminating C++ missing finally. We need to make sure
  307. * the value gets set to false when we leave the function, so we use
  308. * a Guard object, that sets it when it gets out of scope.
  309. */
  310. class ZoneEntry::ProcessGuard {
  311. public:
  312. ProcessGuard(bool& guarded) :
  313. guarded_(guarded)
  314. { }
  315. ~ ProcessGuard() {
  316. guarded_ = false;
  317. }
  318. private:
  319. bool& guarded_;
  320. };
  321. /**
  322. * \short Callback from NameserverEntry to us.
  323. *
  324. * We registre object of this class whenever some ZoneEntry has a need to be
  325. * notified of a change (received data) inside its NameserverEntry.
  326. *
  327. * This is part of the ZoneEntry code (not visible from outside, accessing
  328. * private functions). It is here just because C++ does not know propper lambda
  329. * functions.
  330. */
  331. class ZoneEntry::NameserverCallback : public NameserverEntry::Callback {
  332. public:
  333. /**
  334. * \short Constructor.
  335. *
  336. * \param entry The ZoneEntry to be notified.
  337. * \param family For which address family this change is, so we
  338. * do not process all the nameserves and callbacks there.
  339. */
  340. NameserverCallback(boost::shared_ptr<ZoneEntry> entry, AddressFamily family) :
  341. entry_(entry),
  342. family_(family)
  343. { }
  344. /**
  345. * \short Callback method.
  346. *
  347. * This is called by NameserverEntry when the change happens.
  348. * We just call process to go trough relevant nameservers and call
  349. * any callbacks we can.
  350. */
  351. virtual void operator()(NameserverPtr ns) {
  352. entry_->process(family_, ns);
  353. }
  354. private:
  355. boost::shared_ptr<ZoneEntry> entry_;
  356. AddressFamily family_;
  357. };
  358. void
  359. ZoneEntry::dispatchFailures(AddressFamily family) {
  360. // We extract all the callbacks
  361. vector<CallbackPtr> callbacks;
  362. if (family == ADDR_REQ_MAX) {
  363. move(callbacks_[ANY_OK], callbacks_[V4_ONLY]);
  364. move(callbacks_[ANY_OK], callbacks_[V6_ONLY]);
  365. family = ANY_OK;
  366. }
  367. callbacks.swap(callbacks_[family]);
  368. BOOST_FOREACH(const CallbackPtr& callback, callbacks) {
  369. callback->unreachable();
  370. }
  371. }
  372. void
  373. ZoneEntry::process(AddressFamily family,
  374. const boost::shared_ptr<NameserverEntry>& nameserver)
  375. {
  376. Lock lock(mutex_);
  377. switch (getState()) {
  378. // These are not interesting, nothing to return now
  379. case NOT_ASKED:
  380. case IN_PROGRESS:
  381. case EXPIRED:
  382. break;
  383. case UNREACHABLE: {
  384. dispatchFailures(family);
  385. // And we do nothing more now
  386. break;
  387. }
  388. case READY:
  389. if (family == ADDR_REQ_MAX) {
  390. // Just process each one separately
  391. // TODO Think this over, is it safe, to unlock in the middle?
  392. process(ANY_OK, nameserver);
  393. process(V4_ONLY, nameserver);
  394. process(V6_ONLY, nameserver);
  395. } else {
  396. // Nothing to do anyway for this family, be dormant
  397. if (callbacks_[family].empty()) {
  398. return;
  399. }
  400. /*
  401. * If we have multiple nameservers and more than 1 of them
  402. * is in the cache, we want to choose from all their addresses.
  403. * So we ensure this instance of process is the only one on
  404. * the stack. If not, we terminate and let the outernmost
  405. * one handle it when we return to it.
  406. *
  407. * If we didn't do it, one instance would call "resolve". If it
  408. * was from cache, it would imediatelly recurse back to another
  409. * process (trough the nameserver callback, etc), which would
  410. * take that only one nameserver and trigger all callbacks.
  411. * Only then would resolve terminate and we could ask for the
  412. * second nameserver. This way, we first receive all the
  413. * nameservers that are already in cache and trigger the
  414. * callbacks only then.
  415. *
  416. * However, this does not wait for external fetches of
  417. * nameserver addresses, as the callback is called after
  418. * process terminates. Therefore this waits only for filling
  419. * of the nameservers which we already have in cache.
  420. */
  421. if (in_process_[family]) {
  422. return;
  423. }
  424. // Mark we are on the stack
  425. ProcessGuard guard(in_process_[family]);
  426. in_process_[family] = true;
  427. // Variables to store the data to
  428. NameserverEntry::AddressVector addresses;
  429. NameserverVector to_ask;
  430. bool pending(false);
  431. // Pick info from the nameservers
  432. BOOST_FOREACH(const NameserverPtr& ns, nameservers_) {
  433. Fetchable::State ns_state(ns->getAddresses(addresses,
  434. family, ns == nameserver));
  435. switch (ns_state) {
  436. case IN_PROGRESS:
  437. pending = true;
  438. // Someone asked it, but not us, we don't have
  439. // callback
  440. if (nameservers_not_asked_.find(ns) !=
  441. nameservers_not_asked_.end())
  442. {
  443. to_ask.push_back(ns);
  444. }
  445. break;
  446. case NOT_ASKED:
  447. case EXPIRED:
  448. to_ask.push_back(ns);
  449. break;
  450. case UNREACHABLE:
  451. case READY:
  452. // Not interested, but avoiding warning
  453. break;
  454. }
  455. }
  456. // We have someone to ask, so do it
  457. if (!to_ask.empty()) {
  458. // We ask everything that makes sense now
  459. nameservers_not_asked_.clear();
  460. /*
  461. * TODO: Possible place for an optimisation. We now ask
  462. * everything we can. We should limit this to something like
  463. * 2 concurrent NS fetches (and fetch cache first, then
  464. * fetch the remote ones). But fetching everything right
  465. * away is simpler.
  466. */
  467. BOOST_FOREACH(const NameserverPtr& ns, to_ask) {
  468. // Put all 3 callbacks there. If we put just the
  469. // current family, it might not work due to missing
  470. // callback for different one.
  471. // If they recurse back to us (call directly), we kill
  472. // it by the in_process_
  473. insertCallback(ns, ADDR_REQ_MAX);
  474. }
  475. // Retry with all the data that might have arrived
  476. in_process_[family] = false;
  477. // We do not provide the callback again
  478. process(family, nameserver);
  479. // And be done
  480. return;
  481. // We have some addresses to answer
  482. } else if (!addresses.empty()) {
  483. // Prepare the selector of addresses
  484. // TODO: Think of a way how to keep it for a while
  485. // (not update every time)
  486. updateAddressSelector(addresses, address_selector);
  487. // Extract the callbacks
  488. vector<CallbackPtr> to_execute;
  489. // FIXME: Think of a solution where we do not lose
  490. // any callbacks upon exception
  491. to_execute.swap(callbacks_[family]);
  492. // Run the callbacks
  493. BOOST_FOREACH(const CallbackPtr& callback, to_execute) {
  494. callback->success(addresses[address_selector()]);
  495. }
  496. return;
  497. } else if (!pending) {
  498. dispatchFailures(family);
  499. return;
  500. }
  501. }
  502. return;
  503. }
  504. }
  505. void
  506. ZoneEntry::insertCallback(NameserverPtr ns, AddressFamily family) {
  507. if (family == ADDR_REQ_MAX) {
  508. insertCallback(ns, ANY_OK);
  509. insertCallback(ns, V4_ONLY);
  510. insertCallback(ns, V6_ONLY);
  511. } else {
  512. boost::shared_ptr<NameserverCallback> callback(new NameserverCallback(
  513. shared_from_this(), family));
  514. ns->askIP(resolver_, callback, family);
  515. }
  516. }
  517. }; // namespace nsas
  518. }; // namespace isc