LCOV - code coverage report
Current view: top level - nsas - zone_entry.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 155 169 91.7 %
Date: 2012-05-15 Functions: 17 18 94.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 176 270 65.2 %

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

Generated by: LCOV version 1.9