zone_entry.cc 21 KB

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