summaryrefslogtreecommitdiffstats
path: root/src/lib/nsas/zone_entry.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/nsas/zone_entry.cc')
-rw-r--r--src/lib/nsas/zone_entry.cc570
1 files changed, 0 insertions, 570 deletions
diff --git a/src/lib/nsas/zone_entry.cc b/src/lib/nsas/zone_entry.cc
deleted file mode 100644
index 6d15533bf1..0000000000
--- a/src/lib/nsas/zone_entry.cc
+++ /dev/null
@@ -1,570 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <map>
-
-#include <config.h>
-
-#include "zone_entry.h"
-#include "address_request_callback.h"
-#include "nameserver_entry.h"
-
-#include <algorithm>
-#include <boost/foreach.hpp>
-#include <boost/bind.hpp>
-#include <dns/rrttl.h>
-#include <dns/rcode.h>
-#include <dns/rdataclass.h>
-
-using namespace std;
-
-namespace isc {
-
-using namespace isc::dns;
-using namespace isc::util;
-using namespace isc::util::random;
-
-namespace nsas {
-
-ZoneEntry::ZoneEntry(
- isc::resolve::ResolverInterface* resolver,
- const std::string& name, const isc::dns::RRClass& class_code,
- boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
- boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru) :
- expiry_(0),
- name_(name), class_code_(class_code), resolver_(resolver),
- nameserver_table_(nameserver_table), nameserver_lru_(nameserver_lru)
-{
- in_process_[ANY_OK] = false;
- in_process_[V4_ONLY] = false;
- in_process_[V6_ONLY] = false;
-}
-
-namespace {
-// Shorter aliases for frequently used types
-typedef isc::util::locks::scoped_lock<isc::util::locks::recursive_mutex> Lock; // Local lock, nameservers not locked
-typedef boost::shared_ptr<AddressRequestCallback> CallbackPtr;
-
-/*
- * Create a nameserver.
- * Called inside a mutex so it is filled in atomically.
- */
-boost::shared_ptr<NameserverEntry>
-newNs(const std::string* name, const RRClass* class_code) {
- return (boost::shared_ptr<NameserverEntry>(new NameserverEntry(*name,
- *class_code)));
-}
-
-}
-
-/**
- * \short Callback class that ZoneEntry passes to a resolver.
- *
- * We need to ask for the list of nameservers. So we pass ResolverCallback
- * object to it, when it knows the answer, method of this thing will be
- * called.
- *
- * It is a nested friend class and should be considered as a part of ZoneEntry
- * code. It manipulates directly ZoneEntry's data members, locks it and like
- * that. Mostly eliminates C++ bad design of missing lambda functions.
- */
-class ZoneEntry::ResolverCallback :
- public isc::resolve::ResolverInterface::Callback {
- public:
- /// \short Constructor. Pass "this" zone entry
- ResolverCallback(boost::shared_ptr<ZoneEntry> entry) :
- entry_(entry)
- { }
- /**
- * \short It successfully received nameserver list.
- *
- * It fills the nameservers into the ZoneEntry whose callback this is.
- * If there are in the hash table, it is used. If not, they are
- * created. This might still fail, if the list is empty.
- *
- * It then calls process, to go through the list of nameservers,
- * examining them and seeing if some addresses are already there
- * and to ask for the rest of them.
- */
- virtual void success(MessagePtr response_message) {
- Lock lock(entry_->mutex_);
-
- // TODO: find the correct RRset, not simply the first
- if (!response_message ||
- response_message->getRcode() != isc::dns::Rcode::NOERROR() ||
- response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) {
- // todo: define this
- failureInternal(300);
- }
-
- isc::dns::RRsetIterator rrsi =
- response_message->beginSection(isc::dns::Message::SECTION_ANSWER);
- const isc::dns::RRsetPtr answer = *rrsi;
-
- RdataIteratorPtr iterator(answer->getRdataIterator());
- // If there are no data
- if (iterator->isLast()) {
- failureInternal(answer->getTTL().getValue());
- return;
- } else {
- /*
- * We store the nameservers we have currently (we might have
- * none, at startup, but when we time out and ask again, we
- * do), so we can just reuse them instead of looking them up in
- * the table or creating them.
- */
- std::map<string, NameserverPtr> old;
- BOOST_FOREACH(const NameserverPtr& ptr, entry_->nameservers_) {
- old[ptr->getName()] = ptr;
- }
- /*
- * List of original nameservers we did not ask for IP address
- * yet.
- */
- set<NameserverPtr> old_not_asked;
- old_not_asked.swap(entry_->nameservers_not_asked_);
-
- // Once we have them put aside, remove the original set
- // of nameservers from the entry
- entry_->nameservers_.clear();
- // And put the ones from the answer them, reusing if possible
- for (; !iterator->isLast(); iterator->next()) {
- try {
- // Get the name from there
- Name ns_name(dynamic_cast<const rdata::generic::NS&>(
- iterator->getCurrent()).getNSName());
- // Try to find it in the old ones
- std::map<string, NameserverPtr>::iterator old_ns(old.find(
- ns_name.toText()));
- /*
- * We didn't have this nameserver before. So we just
- * look it up in the hash table or create it.
- */
- if (old_ns == old.end()) {
- // Look it up or create it
- string ns_name_str(ns_name.toText());
- pair<bool, NameserverPtr> from_hash(
- entry_->nameserver_table_->getOrAdd(HashKey(
- ns_name_str, entry_->class_code_), boost::bind(
- newNs, &ns_name_str, &entry_->class_code_)));
- // Make it at the front of the list
- if (from_hash.first) {
- entry_->nameserver_lru_->add(from_hash.second);
- } else {
- entry_->nameserver_lru_->touch(
- from_hash.second);
- }
- // And add it at last to the entry
- entry_->nameservers_.push_back(from_hash.second);
- entry_->nameservers_not_asked_.insert(
- from_hash.second);
- } else {
- // We had it before, reuse it
- entry_->nameservers_.push_back(old_ns->second);
- // Did we ask it already? If not, it is still not
- // asked (the one designing std interface must
- // have been mad)
- if (old_not_asked.find(old_ns->second) !=
- old_not_asked.end())
- {
- entry_->nameservers_not_asked_.insert(
- old_ns->second);
- }
- }
- }
- // OK, we skip this one as it is not NS (log?)
- catch (bad_cast&) { }
- }
-
- // It is unbelievable, but we found no nameservers there
- if (entry_->nameservers_.empty()) {
- // So we fail the same way as if we got empty list
- failureInternal(answer->getTTL().getValue());
- return;
- } else {
- // Ok, we have them. So set us as ready, set our
- // expiration time and try to answer what we can, ask
- // if there's still someone to ask.
- entry_->setState(READY);
- entry_->expiry_ = answer->getTTL().getValue() + time(NULL);
- entry_->process(ADDR_REQ_MAX, NameserverPtr());
- return;
- }
- }
- }
- /// \short Failed to receive answer.
- virtual void failure() {
- failureInternal(300);
- }
- private:
- /**
- * \short Common function called when "it did not work"
- *
- * It marks the ZoneEntry as unreachable and processes callbacks (by
- * calling process).
- */
- void failureInternal(time_t ttl) {
- Lock lock(entry_->mutex_);
- entry_->setState(UNREACHABLE);
- entry_->expiry_ = ttl + time(NULL);
- // Process all three callback lists and tell them KO
- entry_->process(ADDR_REQ_MAX, NameserverPtr());
- }
- /// \short The entry we are callback of
- boost::shared_ptr<ZoneEntry> entry_;
-};
-
-void
-ZoneEntry::addCallback(CallbackPtr callback, AddressFamily family,
- const GlueHints& glue_hints) {
- Lock lock(mutex_);
-
- bool ask(false);
-
- // Look at expiration time
- if (expiry_ && time(NULL) >= expiry_) {
- setState(EXPIRED);
- }
-
- // We need to ask (again)
- if (getState() == EXPIRED || getState() == NOT_ASKED) {
- ask = true;
- }
-
- // We do not have the answer right away, just queue the callback
- bool execute(!ask && getState() != IN_PROGRESS &&
- callbacks_[family].empty());
-
- // Unless there was glue
- if (ask && glue_hints.hasGlue(family)) {
- callback->success(glue_hints.getGlue(family));
- } else {
- callbacks_[family].push_back(callback);
- }
-
- if (execute) {
- // Try to process it right away, store if not possible to handle
- process(family, NameserverPtr());
- return;
- }
-
- if (ask) {
- setState(IN_PROGRESS);
- // Our callback might be directly called from resolve, unlock now
- QuestionPtr question(new Question(Name(name_), class_code_,
- RRType::NS()));
- boost::shared_ptr<ResolverCallback> resolver_callback(
- new ResolverCallback(shared_from_this()));
- resolver_->resolve(question, resolver_callback);
- return;
- }
-}
-
-void
-ZoneEntry::removeCallback(const CallbackPtr& callback, AddressFamily family) {
- Lock lock(mutex_);
- std::vector<boost::shared_ptr<AddressRequestCallback> >::iterator i =
- callbacks_[family].begin();
- for (; i != callbacks_[family].end(); ++i) {
- if (*i == callback) {
- callbacks_[family].erase(i);
- // At this point, a callback should only be in the list
- // once (enforced by RunningQuery doing only one at a time)
- // If that changes, we need to revise this (can't delete
- // elements from a list we're looping over)
- return;
- }
- }
-}
-
-namespace {
-
-// This just moves items from one container to another
-template<class Container>
-void
-move(Container& into, Container& from) {
- into.insert(into.end(), from.begin(), from.end());
- from.clear();
-}
-
-// Update the address selector according to the RTTs
-//
-// Each address has a probability to be selected if multiple addresses are available
-// The weight factor is equal to 1/(rtt*rtt), then all the weight factors are normalized
-// to make the sum equal to 1.0
-void
-updateAddressSelector(std::vector<NameserverAddress>& addresses,
- WeightedRandomIntegerGenerator& selector)
-{
- vector<double> probabilities;
- BOOST_FOREACH(NameserverAddress& address, addresses) {
- uint32_t rtt = address.getAddressEntry().getRTT();
- if(rtt == 0) {
- isc_throw(RTTIsZero, "The RTT is 0");
- }
-
- if(rtt == AddressEntry::UNREACHABLE) {
- probabilities.push_back(0);
- } else {
- probabilities.push_back(1.0/(rtt*rtt));
- }
- }
- // Calculate the sum
- double sum = accumulate(probabilities.begin(), probabilities.end(), 0.0);
-
- if(sum != 0) {
- // Normalize the probabilities to make the sum equal to 1.0
- for(vector<double>::iterator it = probabilities.begin();
- it != probabilities.end(); ++it){
- (*it) /= sum;
- }
- } else if(!probabilities.empty()){
- // If all the nameservers are unreachable, the sum will be 0
- // So give each server equal opportunity to be selected.
- for(vector<double>::iterator it = probabilities.begin();
- it != probabilities.end(); ++it){
- (*it) = 1.0/probabilities.size();
- }
- }
-
- selector.reset(probabilities);
-}
-
-}
-
-/**
- * \short Sets given boolean to false when destroyed.
- *
- * This is hack eliminating C++ missing finally. We need to make sure
- * the value gets set to false when we leave the function, so we use
- * a Guard object, that sets it when it gets out of scope.
- */
-class ZoneEntry::ProcessGuard {
- public:
- ProcessGuard(bool& guarded) :
- guarded_(guarded)
- { }
- ~ ProcessGuard() {
- guarded_ = false;
- }
- private:
- bool& guarded_;
-};
-
-/**
- * \short Callback from NameserverEntry to us.
- *
- * We registre object of this class whenever some ZoneEntry has a need to be
- * notified of a change (received data) inside its NameserverEntry.
- *
- * This is part of the ZoneEntry code (not visible from outside, accessing
- * private functions). It is here just because C++ does not know propper lambda
- * functions.
- */
-class ZoneEntry::NameserverCallback : public NameserverEntry::Callback {
- public:
- /**
- * \short Constructor.
- *
- * \param entry The ZoneEntry to be notified.
- * \param family For which address family this change is, so we
- * do not process all the nameserves and callbacks there.
- */
- NameserverCallback(boost::shared_ptr<ZoneEntry> entry, AddressFamily family) :
- entry_(entry),
- family_(family)
- { }
- /**
- * \short Callback method.
- *
- * This is called by NameserverEntry when the change happens.
- * We just call process to go through relevant nameservers and call
- * any callbacks we can.
- */
- virtual void operator()(NameserverPtr ns) {
- entry_->process(family_, ns);
- }
- private:
- boost::shared_ptr<ZoneEntry> entry_;
- AddressFamily family_;
-};
-
-void
-ZoneEntry::dispatchFailures(AddressFamily family) {
- // We extract all the callbacks
- vector<CallbackPtr> callbacks;
- if (family == ADDR_REQ_MAX) {
- move(callbacks_[ANY_OK], callbacks_[V4_ONLY]);
- move(callbacks_[ANY_OK], callbacks_[V6_ONLY]);
- family = ANY_OK;
- }
- callbacks.swap(callbacks_[family]);
- BOOST_FOREACH(const CallbackPtr& callback, callbacks) {
- callback->unreachable();
- }
-}
-
-void
-ZoneEntry::process(AddressFamily family,
- const boost::shared_ptr<NameserverEntry>& nameserver)
-{
- Lock lock(mutex_);
- switch (getState()) {
- // These are not interesting, nothing to return now
- case NOT_ASKED:
- case IN_PROGRESS:
- case EXPIRED:
- break;
- case UNREACHABLE: {
- dispatchFailures(family);
- // And we do nothing more now
- break;
- }
- case READY:
- if (family == ADDR_REQ_MAX) {
- // Just process each one separately
- // TODO Think this over, is it safe, to unlock in the middle?
- process(ANY_OK, nameserver);
- process(V4_ONLY, nameserver);
- process(V6_ONLY, nameserver);
- } else {
- // Nothing to do anyway for this family, be dormant
- if (callbacks_[family].empty()) {
- return;
- }
- /*
- * If we have multiple nameservers and more than 1 of them
- * is in the cache, we want to choose from all their addresses.
- * So we ensure this instance of process is the only one on
- * the stack. If not, we terminate and let the outernmost
- * one handle it when we return to it.
- *
- * If we didn't do it, one instance would call "resolve". If it
- * was from cache, it would immediately recurse back to another
- * process (through the nameserver callback, etc), which would
- * take that only one nameserver and trigger all callbacks.
- * Only then would resolve terminate and we could ask for the
- * second nameserver. This way, we first receive all the
- * nameservers that are already in cache and trigger the
- * callbacks only then.
- *
- * However, this does not wait for external fetches of
- * nameserver addresses, as the callback is called after
- * process terminates. Therefore this waits only for filling
- * of the nameservers which we already have in cache.
- */
- if (in_process_[family]) {
- return;
- }
- // Mark we are on the stack
- ProcessGuard guard(in_process_[family]);
- in_process_[family] = true;
- // Variables to store the data to
- NameserverEntry::AddressVector addresses;
- NameserverVector to_ask;
- bool pending(false);
-
- // Pick info from the nameservers
- BOOST_FOREACH(const NameserverPtr& ns, nameservers_) {
- Fetchable::State ns_state(ns->getAddresses(addresses,
- family, ns == nameserver));
- switch (ns_state) {
- case IN_PROGRESS:
- pending = true;
- // Someone asked it, but not us, we don't have
- // callback
- if (nameservers_not_asked_.find(ns) !=
- nameservers_not_asked_.end())
- {
- to_ask.push_back(ns);
- }
- break;
- case NOT_ASKED:
- case EXPIRED:
- to_ask.push_back(ns);
- break;
- case UNREACHABLE:
- case READY:
- // Not interested, but avoiding warning
- break;
- }
- }
-
- // We have someone to ask, so do it
- if (!to_ask.empty()) {
- // We ask everything that makes sense now
- nameservers_not_asked_.clear();
- /*
- * TODO: Possible place for an optimisation. We now ask
- * everything we can. We should limit this to something like
- * 2 concurrent NS fetches (and fetch cache first, then
- * fetch the remote ones). But fetching everything right
- * away is simpler.
- */
- BOOST_FOREACH(const NameserverPtr& ns, to_ask) {
- // Put all 3 callbacks there. If we put just the
- // current family, it might not work due to missing
- // callback for different one.
- // If they recurse back to us (call directly), we kill
- // it by the in_process_
- insertCallback(ns, ADDR_REQ_MAX);
- }
- // Retry with all the data that might have arrived
- in_process_[family] = false;
- // We do not provide the callback again
- process(family, nameserver);
- // And be done
- return;
- // We have some addresses to answer
- } else if (!addresses.empty()) {
- // Prepare the selector of addresses
- // TODO: Think of a way how to keep it for a while
- // (not update every time)
- updateAddressSelector(addresses, address_selector);
-
- // Extract the callbacks
- vector<CallbackPtr> to_execute;
- // FIXME: Think of a solution where we do not lose
- // any callbacks upon exception
- to_execute.swap(callbacks_[family]);
-
- // Run the callbacks
- BOOST_FOREACH(const CallbackPtr& callback, to_execute) {
- callback->success(addresses[address_selector()]);
- }
- return;
- } else if (!pending) {
- dispatchFailures(family);
- return;
- }
- }
- return;
- }
-}
-
-void
-ZoneEntry::insertCallback(NameserverPtr ns, AddressFamily family) {
- if (family == ADDR_REQ_MAX) {
- insertCallback(ns, ANY_OK);
- insertCallback(ns, V4_ONLY);
- insertCallback(ns, V6_ONLY);
- } else {
- boost::shared_ptr<NameserverCallback> callback(new NameserverCallback(
- shared_from_this(), family));
- ns->askIP(resolver_, callback, family);
- }
-}
-
-}; // namespace nsas
-}; // namespace isc