LCOV - code coverage report
Current view: top level - resolve - recursive_query.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 285 402 70.9 %
Date: 2012-05-15 Functions: 27 36 75.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 298 901 33.1 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (C) 2011  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 <config.h>
      16                 :            : 
      17                 :            : #include <stdlib.h>
      18                 :            : #include <netinet/in.h>
      19                 :            : #include <sys/socket.h>
      20                 :            : #include <unistd.h>             // for some IPC/network system calls
      21                 :            : #include <string>
      22                 :            : 
      23                 :            : #include <boost/lexical_cast.hpp>
      24                 :            : #include <boost/bind.hpp>
      25                 :            : 
      26                 :            : #include <dns/question.h>
      27                 :            : #include <dns/message.h>
      28                 :            : #include <dns/opcode.h>
      29                 :            : #include <dns/exceptions.h>
      30                 :            : #include <dns/rdataclass.h>
      31                 :            : #include <resolve/resolve.h>
      32                 :            : #include <resolve/resolve_log.h>
      33                 :            : #include <resolve/resolve_messages.h>
      34                 :            : #include <cache/resolver_cache.h>
      35                 :            : #include <nsas/address_request_callback.h>
      36                 :            : #include <nsas/nameserver_address.h>
      37                 :            : 
      38                 :            : #include <asio.hpp>
      39                 :            : #include <asiodns/dns_service.h>
      40                 :            : #include <asiodns/io_fetch.h>
      41                 :            : #include <asiolink/io_service.h>
      42                 :            : #include <resolve/response_classifier.h>
      43                 :            : #include <resolve/recursive_query.h>
      44                 :            : 
      45                 :            : using namespace isc::dns;
      46                 :            : using namespace isc::util;
      47                 :            : using namespace isc::asiolink;
      48                 :            : using namespace isc::resolve;
      49                 :            : 
      50                 :            : namespace isc {
      51                 :            : namespace asiodns {
      52                 :            : 
      53                 :            : namespace {
      54                 :            : // Function to check if the given name/class has any address in the cache
      55                 :            : bool
      56                 :          8 : hasAddress(const Name& name, const RRClass& rrClass,
      57                 :            :       const isc::cache::ResolverCache& cache)
      58                 :            : {
      59                 :            :     // FIXME: If we are single-stack and we get only the other type of
      60                 :            :     // address, what should we do? In that case, it will be considered
      61                 :            :     // unreachable, which is most probably true, because A and AAAA will
      62                 :            :     // usually have the same RTT, so we should have both or none from the
      63                 :            :     // glue.
      64 [ +  - ][ #  # ]:          8 :     return (cache.lookup(name, RRType::A(), rrClass) != RRsetPtr() ||
      65 [ +  + ][ +  - ]:         28 :             cache.lookup(name, RRType::AAAA(), rrClass) != RRsetPtr());
         [ -  + ][ +  + ]
         [ +  + ][ #  # ]
      66                 :            : }
      67                 :            : 
      68                 :            : // Convenience function for debug messages.  Question::toText() includes
      69                 :            : // a trailing newline in its output, which makes it awkward to embed in a
      70                 :            : // message.  This just strips that newline from it.
      71                 :            : std::string
      72                 :         30 : questionText(const isc::dns::Question& question) {
      73                 :         30 :     std::string text = question.toText();
      74         [ +  - ]:         30 :     if (!text.empty()) {
      75         [ +  - ]:         30 :         text.erase(text.size() - 1);
      76                 :            :     }
      77                 :         30 :     return (text);
      78                 :            : }
      79                 :            : 
      80                 :            : } // anonymous namespace
      81                 :            : 
      82                 :            : /// \brief Find deepest usable delegation in the cache
      83                 :            : ///
      84                 :            : /// This finds the deepest delegation we have in cache and is safe to use.
      85                 :            : /// It is not public function, therefore it's not in header. But it's not
      86                 :            : /// in anonymous namespace, so we can call it from unittests.
      87                 :            : /// \param name The name we want to delegate to.
      88                 :            : /// \param rrclass The class.
      89                 :            : /// \param cache The place too look for known delegations.
      90                 :            : std::string
      91                 :          5 : deepestDelegation(Name name, RRClass rrclass,
      92                 :            :                   isc::cache::ResolverCache& cache)
      93                 :            : {
      94                 :            :     RRsetPtr cachedNS;
      95                 :            :     // Look for delegation point from bottom, until we find one with
      96                 :            :     // IP address or get to root.
      97                 :            :     //
      98                 :            :     // We need delegation with IP address so we can ask it right away.
      99                 :            :     // If we don't have the IP address, we would need to ask above it
     100                 :            :     // anyway in the best case, and the NS could be inside the zone,
     101                 :            :     // and we could get all loopy with the NSAS in the worst case.
     102 [ +  - ][ +  + ]:         27 :     while (name.getLabelCount() > 1 &&
                 [ +  + ]
     103 [ +  - ][ +  - ]:         27 :            (cachedNS = cache.lookupDeepestNS(name, rrclass)) != RRsetPtr()) {
         [ +  - ][ +  - ]
         [ #  # ][ #  # ]
     104                 :            :         // Look if we have an IP address for the NS
     105 [ +  - ][ +  + ]:         18 :         for (RdataIteratorPtr ns(cachedNS->getRdataIterator());
     106 [ +  - ][ +  - ]:         12 :              !ns->isLast(); ns->next()) {
     107                 :            :             // Do we have IP for this specific NS?
     108 [ +  - ][ +  + ]:          8 :             if (hasAddress(dynamic_cast<const rdata::generic::NS&>(
     109 [ +  - ][ +  - ]:          8 :                                ns->getCurrent()).getNSName(), rrclass,
                 [ +  - ]
     110                 :            :                            cache)) {
     111                 :            :                 // Found one, stop checking and use this zone
     112                 :            :                 // (there may be more addresses, that's only better)
     113 [ +  - ][ +  - ]:          2 :                 return (cachedNS->getName().toText());
     114                 :            :             }
     115                 :            :         }
     116                 :            :         // We don't have anything for this one, so try something higher
     117         [ +  - ]:          4 :         if (name.getLabelCount() > 1) {
     118 [ +  - ][ +  - ]:          9 :             name = name.split(1);
     119                 :            :         }
     120                 :            :     }
     121                 :            :     // Fallback, nothing found, start at root
     122         [ +  - ]:          3 :     return (".");
     123                 :            : }
     124                 :            : 
     125                 :            : typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
     126                 :            : 
     127                 :            : // Here we do not use the typedef above, as the SunStudio compiler
     128                 :            : // mishandles this in its name mangling, and wouldn't compile.
     129                 :            : // We can probably use a typedef, but need to move it to a central
     130                 :            : // location and use it consistently.
     131                 :         17 : RecursiveQuery::RecursiveQuery(DNSServiceBase& dns_service,
     132                 :            :     isc::nsas::NameserverAddressStore& nsas,
     133                 :            :     isc::cache::ResolverCache& cache,
     134                 :            :     const std::vector<std::pair<std::string, uint16_t> >& upstream,
     135                 :            :     const std::vector<std::pair<std::string, uint16_t> >& upstream_root,
     136                 :            :     int query_timeout, int client_timeout, int lookup_timeout,
     137                 :            :     unsigned retries)
     138                 :            :     :
     139                 :            :     dns_service_(dns_service),
     140                 :            :     nsas_(nsas), cache_(cache),
     141         [ +  - ]:         17 :     upstream_(new AddressVector(upstream)),
     142         [ +  - ]:         17 :     upstream_root_(new AddressVector(upstream_root)),
     143                 :            :     test_server_("", 0),
     144                 :            :     query_timeout_(query_timeout), client_timeout_(client_timeout),
     145 [ +  - ][ +  - ]:         68 :     lookup_timeout_(lookup_timeout), retries_(retries), rtt_recorder_()
                 [ +  - ]
     146                 :            : {
     147                 :         17 : }
     148                 :            : 
     149                 :            : // Set the test server - only used for unit testing.
     150                 :            : void
     151                 :          2 : RecursiveQuery::setTestServer(const std::string& address, uint16_t port) {
     152 [ +  - ][ +  - ]:          2 :     LOG_WARN(isc::resolve::logger, RESLIB_TEST_SERVER).arg(address).arg(port);
                 [ +  - ]
     153                 :          2 :     test_server_.first = address;
     154                 :          2 :     test_server_.second = port;
     155                 :          2 : }
     156                 :            : 
     157                 :            : // Set the RTT recorder - only used for testing
     158                 :            : void
     159                 :          2 : RecursiveQuery::setRttRecorder(boost::shared_ptr<RttRecorder>& recorder) {
     160                 :          2 :     rtt_recorder_ = recorder;
     161                 :          2 : }
     162                 :            : 
     163                 :            : namespace {
     164                 :            : 
     165                 :            : typedef std::pair<std::string, uint16_t> addr_t;
     166                 :            : 
     167                 :            : /*
     168                 :            :  * This is a query in progress. When a new query is made, this one holds
     169                 :            :  * the context information about it, like how many times we are allowed
     170                 :            :  * to retry on failure, what to do when we succeed, etc.
     171                 :            :  *
     172                 :            :  * Used by RecursiveQuery::sendQuery.
     173                 :            :  */
     174 [ #  # ][ #  # ]:          0 : class RunningQuery : public IOFetch::Callback {
     175                 :            : 
     176                 :          0 : class ResolverNSASCallback : public isc::nsas::AddressRequestCallback {
     177                 :            : public:
     178                 :          6 :     ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
     179                 :            :     
     180                 :          2 :     void success(const isc::nsas::NameserverAddress& address) {
     181                 :            :         // Success callback, send query to found namesever
     182 [ +  - ][ +  - ]:          4 :         LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_SUCCESS)
     183 [ +  - ][ +  - ]:          4 :                   .arg(address.getAddress().toText());
     184                 :          2 :         rq_->nsasCallbackCalled();
     185                 :          2 :         rq_->sendTo(address);
     186                 :          2 :     }
     187                 :            :     
     188                 :          1 :     void unreachable() {
     189                 :            :         // Nameservers unreachable: drop query or send servfail?
     190         [ +  - ]:          1 :         LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_FAIL);
     191                 :          1 :         rq_->nsasCallbackCalled();
     192                 :          1 :         rq_->makeSERVFAIL();
     193                 :          1 :         rq_->callCallback(true);
     194                 :          1 :         rq_->stop();
     195                 :          1 :     }
     196                 :            : 
     197                 :            : private:
     198                 :            :     RunningQuery* rq_;
     199                 :            : };
     200                 :            : 
     201                 :            : 
     202                 :            : private:
     203                 :            :     // The io service to handle async calls
     204                 :            :     IOService& io_;
     205                 :            : 
     206                 :            :     // Info for (re)sending the query (the question and destination)
     207                 :            :     Question question_;
     208                 :            : 
     209                 :            :     // This is the query message got from client
     210                 :            :     ConstMessagePtr query_message_;
     211                 :            : 
     212                 :            :     // This is where we build and store our final answer
     213                 :            :     MessagePtr answer_message_;
     214                 :            : 
     215                 :            :     // Test server - only used for testing.  This takes precedence over all
     216                 :            :     // other servers if the port is non-zero.
     217                 :            :     std::pair<std::string, uint16_t> test_server_;
     218                 :            : 
     219                 :            :     // Buffer to store the intermediate results.
     220                 :            :     OutputBufferPtr buffer_;
     221                 :            : 
     222                 :            :     // The callback will be called when we have either decided we
     223                 :            :     // are done, or when we give up
     224                 :            :     isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
     225                 :            : 
     226                 :            :     // Protocol used for the last query.  This is set to IOFetch::UDP when a
     227                 :            :     // new upstream query is initiated, and changed to IOFetch::TCP if a
     228                 :            :     // packet is returned with the TC bit set.  It is stored here to detect the
     229                 :            :     // case of a TCP packet being returned with the TC bit set.
     230                 :            :     IOFetch::Protocol protocol_;
     231                 :            : 
     232                 :            :     // EDNS flag
     233                 :            :     bool edns_;
     234                 :            : 
     235                 :            :     // To prevent both unreasonably long cname chains and cname loops,
     236                 :            :     // we simply keep a counter of the number of CNAMEs we have
     237                 :            :     // followed so far (and error if it exceeds RESOLVER_MAX_CNAME_CHAIN
     238                 :            :     // from lib/resolve/response_classifier.h)
     239                 :            :     unsigned cname_count_;
     240                 :            : 
     241                 :            :     /*
     242                 :            :      * TODO Do something more clever with timeouts. In the long term, some
     243                 :            :      *     computation of average RTT, increase with each retry, etc.
     244                 :            :      */
     245                 :            :     // Timeout information for outgoing queries
     246                 :            :     int query_timeout_;
     247                 :            :     unsigned retries_;
     248                 :            : 
     249                 :            :     // normal query state
     250                 :            : 
     251                 :            :     // Update the question that will be sent to the server
     252                 :            :     void setQuestion(const Question& new_question) {
     253                 :            :         question_ = new_question;
     254                 :            :     }
     255                 :            : 
     256                 :            :     // TODO: replace by our wrapper
     257                 :            :     asio::deadline_timer client_timer;
     258                 :            :     asio::deadline_timer lookup_timer;
     259                 :            : 
     260                 :            :     // If we timed out ourselves (lookup timeout), stop issuing queries
     261                 :            :     bool done_;
     262                 :            : 
     263                 :            :     // If we have a client timeout, we call back with a failure message,
     264                 :            :     // but we do not stop yet. We use this variable to make sure we 
     265                 :            :     // don't call back a second time later
     266                 :            :     bool callback_called_;
     267                 :            : 
     268                 :            :     // Reference to our NSAS
     269                 :            :     isc::nsas::NameserverAddressStore& nsas_;
     270                 :            : 
     271                 :            :     // Reference to our cache
     272                 :            :     isc::cache::ResolverCache& cache_;
     273                 :            :     
     274                 :            :     // the 'current' zone we are in (i.e.) we start out at the root,
     275                 :            :     // and for each delegation this gets updated with the zone the
     276                 :            :     // delegation points to.
     277                 :            :     // TODO: make this a Name (it is a string right now because most
     278                 :            :     // of the call we use it in take a string, we need update those
     279                 :            :     // too).
     280                 :            :     std::string cur_zone_;
     281                 :            :     
     282                 :            :     // This is the handler we pass on to the NSAS; it is called when
     283                 :            :     // the NSAS has an address for us to query
     284                 :            :     boost::shared_ptr<ResolverNSASCallback> nsas_callback_;
     285                 :            : 
     286                 :            :     // this is set to true if we have asked the nsas to give us
     287                 :            :     // an address and we are waiting for it to call us back.
     288                 :            :     // We use is to cancel the outstanding callback in case we
     289                 :            :     // have a lookup timeout and decide to give up
     290                 :            :     bool nsas_callback_out_;
     291                 :            : 
     292                 :            :     // This is the nameserver we have an outstanding query to.
     293                 :            :     // It is used to update the RTT once the query returns
     294                 :            :     isc::nsas::NameserverAddress current_ns_address;
     295                 :            : 
     296                 :            :     // The moment in time we sent a query to the nameserver above.
     297                 :            :     struct timeval current_ns_qsent_time;
     298                 :            :     
     299                 :            :     // RunningQuery deletes itself when it is done. In order for us
     300                 :            :     // to do this safely, we must make sure that there are no events
     301                 :            :     // that might call back to it. There are two types of events in
     302                 :            :     // this sense; the timers we set ourselves (lookup and client),
     303                 :            :     // and outstanding queries to nameservers. When each of these is
     304                 :            :     // started, we increase this value. When they fire, it is decreased
     305                 :            :     // again. We cannot delete ourselves until this value is back to 0.
     306                 :            :     //
     307                 :            :     // Note that the NSAS callback is *not* seen as an outstanding
     308                 :            :     // event; we can cancel the NSAS callback safely.
     309                 :            :     size_t outstanding_events_;
     310                 :            : 
     311                 :            :     // RTT Recorder.  Used for testing, the RTTs of queries are
     312                 :            :     // sent to this object as well as being used to update the NSAS.
     313                 :            :     boost::shared_ptr<RttRecorder> rtt_recorder_;
     314                 :            : 
     315                 :            :     // perform a single lookup; first we check the cache to see
     316                 :            :     // if we have a response for our query stored already. if
     317                 :            :     // so, call handlerecursiveresponse(), if not, we call send()
     318                 :          3 :     void doLookup() {
     319 [ +  - ][ +  - ]:          6 :         LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNQ_CACHE_LOOKUP)
     320 [ +  - ][ +  - ]:          6 :                   .arg(questionText(question_));
     321                 :            : 
     322                 :          6 :         Message cached_message(Message::RENDER);
     323         [ +  - ]:          3 :         isc::resolve::initResponseMessage(question_, cached_message);
     324 [ +  - ][ -  + ]:          3 :         if (cache_.lookup(question_.getName(), question_.getType(),
     325                 :          6 :                           question_.getClass(), cached_message)) {
     326                 :            : 
     327 [ #  # ][ #  # ]:          0 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNQ_CACHE_FIND)
                 [ #  # ]
     328 [ #  # ][ #  # ]:          0 :                       .arg(questionText(question_));
                 [ #  # ]
     329                 :            :             // Should these be set by the cache too?
     330 [ #  # ][ #  # ]:          0 :             cached_message.setOpcode(Opcode::QUERY());
     331 [ #  # ][ #  # ]:          0 :             cached_message.setRcode(Rcode::NOERROR());
     332         [ #  # ]:          0 :             cached_message.setHeaderFlag(Message::HEADERFLAG_QR);
     333 [ #  # ][ #  # ]:          0 :             if (handleRecursiveAnswer(cached_message)) {
     334         [ #  # ]:          0 :                 callCallback(true);
     335         [ #  # ]:          0 :                 stop();
     336                 :            :             }
     337                 :            :         } else {
     338                 :          3 :             cur_zone_ = deepestDelegation(question_.getName(),
     339 [ +  - ][ +  - ]:          6 :                                           question_.getClass(), cache_);
     340 [ +  - ][ +  - ]:          6 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_DEEPEST)
                 [ +  - ]
     341 [ +  - ][ +  - ]:          6 :                       .arg(questionText(question_)).arg(cur_zone_);
         [ +  - ][ +  - ]
     342         [ +  - ]:          3 :             send();
     343                 :            :         }
     344                 :            : 
     345                 :          3 :     }
     346                 :            : 
     347                 :            :     // Send the current question to the given nameserver address
     348                 :            :     void sendTo(const isc::nsas::NameserverAddress& address) {
     349                 :            :         // We need to keep track of the Address, so that we can update
     350                 :            :         // the RTT
     351                 :          2 :         current_ns_address = address;
     352                 :          2 :         gettimeofday(&current_ns_qsent_time, NULL);
     353                 :          2 :         ++outstanding_events_;
     354         [ +  - ]:          2 :         if (test_server_.second != 0) {
     355                 :            :             IOFetch query(protocol_, io_, question_,
     356                 :            :                 test_server_.first,
     357                 :            :                 test_server_.second, buffer_, this,
     358                 :          4 :                 query_timeout_, edns_);
     359 [ +  - ][ +  - ]:          2 :             io_.get_io_service().post(query);
                 [ +  - ]
     360                 :            :         } else {
     361                 :            :             IOFetch query(protocol_, io_, question_,
     362                 :            :                 current_ns_address.getAddress(),
     363                 :            :                 53, buffer_, this,
     364                 :          0 :                 query_timeout_, edns_);
     365 [ #  # ][ #  # ]:          0 :             io_.get_io_service().post(query);
                 [ #  # ]
     366                 :            :         }
     367                 :            :     }
     368                 :            :     
     369                 :            :     // 'general' send, ask the NSAS to give us an address.
     370                 :          7 :     void send(IOFetch::Protocol protocol = IOFetch::UDP, bool edns = true) {
     371                 :          7 :         protocol_ = protocol;   // Store protocol being used for this
     372                 :          7 :         edns_ = edns;
     373         [ +  + ]:          7 :         if (test_server_.second != 0) {
     374                 :            :             // Send query to test server
     375 [ +  - ][ +  - ]:         12 :             LOG_DEBUG(isc::resolve::logger,
     376                 :            :                       RESLIB_DBG_TRACE, RESLIB_TEST_UPSTREAM)
     377 [ +  - ][ +  - ]:         12 :                 .arg(questionText(question_)).arg(test_server_.first);
                 [ +  - ]
     378                 :          6 :             gettimeofday(&current_ns_qsent_time, NULL);
     379                 :          6 :             ++outstanding_events_;
     380                 :            :             IOFetch query(protocol, io_, question_,
     381                 :            :                 test_server_.first,
     382                 :            :                 test_server_.second, buffer_, this,
     383                 :         12 :                 query_timeout_, edns_);
     384 [ +  - ][ +  - ]:          6 :             io_.get_io_service().post(query);
                 [ +  - ]
     385                 :            : 
     386                 :            :         } else {
     387                 :            :             // Ask the NSAS for an address for the current zone,
     388                 :            :             // the callback will call the actual sendTo()
     389         [ +  - ]:          2 :             LOG_DEBUG(isc::resolve::logger,
     390                 :            :                       RESLIB_DBG_TRACE, RESLIB_NSAS_LOOKUP)
     391         [ +  - ]:          1 :                 .arg(cur_zone_);
     392                 :            : 
     393                 :            :             // Can we have multiple calls to nsas_out? Let's assume not
     394                 :            :             // for now
     395         [ -  + ]:          1 :             assert(!nsas_callback_out_);
     396                 :          1 :             nsas_callback_out_ = true;
     397         [ +  - ]:          2 :             nsas_.lookup(cur_zone_, question_.getClass(), nsas_callback_);
     398                 :            :         }
     399                 :          7 :     }
     400                 :            :     
     401                 :            :     // Called by our NSAS callback handler so we know we do not have
     402                 :            :     // an outstanding NSAS call anymore.
     403                 :          0 :     void nsasCallbackCalled() {
     404                 :          3 :         nsas_callback_out_ = false;
     405                 :          0 :     }
     406                 :            : 
     407                 :            :     // This function is called by operator() and lookup();
     408                 :            :     // We have an answer either from a nameserver or the cache, and
     409                 :            :     // we do not know yet if this is a final answer we can send back or
     410                 :            :     // that more recursive processing needs to be done.
     411                 :            :     // Depending on the content, we go on recursing or return
     412                 :            :     //
     413                 :            :     // This method also updates the cache, depending on the content
     414                 :            :     // of the message
     415                 :            :     //
     416                 :            :     // returns true if we are done (either we have an answer or an
     417                 :            :     //              error message)
     418                 :            :     // returns false if we are not done
     419                 :          6 :     bool handleRecursiveAnswer(const Message& incoming) {
     420                 :            : 
     421                 :            :         // In case we get a CNAME, we store the target
     422                 :            :         // here (classify() will set it when it walks through
     423                 :            :         // the cname chain to verify it).
     424                 :         12 :         Name cname_target(question_.getName());
     425                 :            :         
     426                 :            :         isc::resolve::ResponseClassifier::Category category =
     427                 :            :             isc::resolve::ResponseClassifier::classify(
     428         [ +  - ]:          6 :                 question_, incoming, cname_target, cname_count_);
     429                 :            : 
     430                 :          6 :         bool found_ns = false;
     431                 :            :             
     432   [ +  -  -  +  :          6 :         switch (category) {
                +  +  - ]
     433                 :            :         case isc::resolve::ResponseClassifier::ANSWER:
     434                 :            :         case isc::resolve::ResponseClassifier::ANSWERCNAME:
     435                 :            :             // Answer received - copy and return.
     436 [ +  - ][ +  - ]:          4 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_ANSWER)
                 [ +  - ]
     437 [ +  - ][ +  - ]:          4 :                       .arg(questionText(question_));
                 [ +  - ]
     438         [ +  - ]:          2 :             isc::resolve::copyResponseMessage(incoming, answer_message_);
     439         [ +  - ]:          2 :             cache_.update(*answer_message_);
     440                 :            :             return (true);
     441                 :            :             break;
     442                 :            : 
     443                 :            :         case isc::resolve::ResponseClassifier::CNAME:
     444                 :            :             // CNAME received.
     445                 :            : 
     446                 :            :             // (unfinished) CNAME. We set our question_ to the CNAME
     447                 :            :             // target, then start over at the beginning (for now, that
     448                 :            :             // is, we reset our 'current servers' to the root servers).
     449         [ #  # ]:          0 :             if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
     450                 :            :                 // CNAME chain too long - just give up
     451 [ #  # ][ #  # ]:          0 :                 LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_LONG_CHAIN)
                 [ #  # ]
     452 [ #  # ][ #  # ]:          0 :                           .arg(questionText(question_));
                 [ #  # ]
     453         [ #  # ]:          0 :                 makeSERVFAIL();
     454                 :            :                 return (true);
     455                 :            :             }
     456                 :            : 
     457 [ #  # ][ #  # ]:          0 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_CNAME)
                 [ #  # ]
     458 [ #  # ][ #  # ]:          0 :                       .arg(questionText(question_));
                 [ #  # ]
     459                 :            : 
     460                 :            :             answer_message_->appendSection(Message::SECTION_ANSWER,
     461         [ #  # ]:          0 :                                            incoming);
     462                 :            : 
     463                 :          0 :             question_ = Question(cname_target, question_.getClass(),
     464                 :          0 :                                  question_.getType());
     465                 :            : 
     466                 :            :             // Follow CNAME chain.
     467 [ #  # ][ #  # ]:          0 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_FOLLOW_CNAME)
                 [ #  # ]
     468 [ #  # ][ #  # ]:          0 :                       .arg(questionText(question_));
                 [ #  # ]
     469         [ #  # ]:          0 :             doLookup();
     470                 :            :             return (false);
     471                 :            :             break;
     472                 :            : 
     473                 :            :         case isc::resolve::ResponseClassifier::NXDOMAIN:
     474                 :            :         case isc::resolve::ResponseClassifier::NXRRSET:
     475                 :            :             // Received NXDOMAIN or NXRRSET, just copy and return
     476 [ #  # ][ #  # ]:          0 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NXDOM_NXRR)
                 [ #  # ]
     477 [ #  # ][ #  # ]:          0 :                       .arg(questionText(question_));
                 [ #  # ]
     478         [ #  # ]:          0 :             isc::resolve::copyResponseMessage(incoming, answer_message_);
     479                 :            :             // no negcache yet
     480                 :            :             //cache_.update(*answer_message_);
     481                 :            :             return (true);
     482                 :            :             break;
     483                 :            : 
     484                 :            :         case isc::resolve::ResponseClassifier::REFERRAL:
     485                 :            :             // Response is a referral
     486 [ +  - ][ +  - ]:          4 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFERRAL)
                 [ +  - ]
     487 [ +  - ][ +  - ]:          4 :                       .arg(questionText(question_));
                 [ +  - ]
     488                 :            : 
     489         [ +  - ]:          2 :             cache_.update(incoming);
     490                 :            :             // Referral. For now we just take the first glue address
     491                 :            :             // we find and continue with that
     492                 :            : 
     493                 :            :             // auth section should have at least one RRset
     494                 :            :             // and one of them should be an NS (otherwise
     495                 :            :             // classifier should have error'd) to a subdomain
     496 [ +  - ][ #  # ]:          8 :             for (RRsetIterator rrsi = incoming.beginSection(Message::SECTION_AUTHORITY);
         [ -  + ][ +  - ]
                 [ +  - ]
     497 [ +  - ][ +  - ]:          4 :                  rrsi != incoming.endSection(Message::SECTION_AUTHORITY) && !found_ns;
         [ +  - ][ #  # ]
     498                 :            :                  ++rrsi) {
     499         [ +  - ]:          2 :                 ConstRRsetPtr rrs = *rrsi;
     500 [ +  - ][ +  - ]:          2 :                 if (rrs->getType() == RRType::NS()) {
     501 [ +  - ][ +  - ]:          2 :                     NameComparisonResult compare(Name(cur_zone_).compare(rrs->getName()));
                 [ +  - ]
     502         [ +  - ]:          2 :                     if (compare.getRelation() == NameComparisonResult::SUPERDOMAIN) {
     503                 :            :                         // TODO: make cur_zone_ a Name instead of a string
     504                 :            :                         // (this requires a few API changes in related
     505                 :            :                         // libraries, so as not to need many conversions)
     506 [ +  - ][ +  - ]:          4 :                         cur_zone_ = rrs->getName().toText();
     507 [ +  - ][ +  - ]:          4 :                         LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFER_ZONE)
                 [ +  - ]
     508 [ +  - ][ +  - ]:          2 :                                   .arg(cur_zone_);
     509                 :          2 :                         found_ns = true;
     510                 :            :                         break;
     511                 :            :                     }
     512                 :            :                 }
     513                 :            :             }
     514                 :            : 
     515         [ +  - ]:          2 :             if (found_ns) {
     516                 :            :                 // next resolver round
     517                 :            :                 // we do NOT use doLookup() here, but send() (i.e. we
     518                 :            :                 // skip the cache), since if we had the final answer
     519                 :            :                 // instead of a delegation cached, we would have been
     520                 :            :                 // there by now.
     521 [ +  - ][ +  - ]:          4 :                 GlueHints glue_hints(cur_zone_, incoming);
     522                 :            : 
     523                 :            :                 // Ask the NSAS for an address, or glue.
     524                 :            :                 // This will eventually result in either sendTo()
     525                 :            :                 // or stop() being called by nsas_callback_
     526         [ -  + ]:          2 :                 assert(!nsas_callback_out_);
     527                 :          2 :                 nsas_callback_out_ = true;
     528                 :          2 :                 nsas_.lookup(cur_zone_, question_.getClass(),
     529         [ +  - ]:          2 :                              nsas_callback_, ANY_OK, glue_hints);
     530                 :            :                 return (false);
     531                 :            :             } else {
     532                 :            :                 // Referral was received but did not contain an NS RRset.
     533 [ #  # ][ #  # ]:          0 :                 LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NO_NS_RRSET)
                 [ #  # ]
     534 [ #  # ][ #  # ]:          0 :                           .arg(questionText(question_));
                 [ #  # ]
     535                 :            : 
     536                 :            :                 // TODO this will result in answering with the delegation. oh well
     537         [ #  # ]:          0 :                 isc::resolve::copyResponseMessage(incoming, answer_message_);
     538                 :            :                 return (true);
     539                 :            :             }
     540                 :            :             break;
     541                 :            : 
     542                 :            :         case isc::resolve::ResponseClassifier::TRUNCATED:
     543                 :            :             // Truncated packet.  If the protocol we used for the last one is
     544                 :            :             // UDP, re-query using TCP.  Otherwise regard it as an error.
     545         [ +  - ]:          1 :             if (protocol_ == IOFetch::UDP) {
     546 [ +  - ][ +  - ]:          2 :                 LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
                 [ +  - ]
     547 [ +  - ][ +  - ]:          2 :                           RESLIB_TRUNCATED).arg(questionText(question_));
                 [ +  - ]
     548         [ +  - ]:          1 :                 send(IOFetch::TCP);
     549                 :            :                 return (false);
     550                 :            :             }
     551                 :            : 
     552                 :            :             // Was a TCP query so we have received a packet over TCP with the
     553                 :            :             // TC bit set: report an error by going to the common
     554                 :            :             // error code.
     555                 :            :             goto SERVFAIL;
     556                 :            : 
     557                 :            :         case isc::resolve::ResponseClassifier::RCODE:
     558                 :            :             // see if it's a FORMERR and a potential EDNS problem
     559 [ +  - ][ +  - ]:          1 :             if (incoming.getRcode() == Rcode::FORMERR()) {
     560 [ +  - ][ +  - ]:          1 :                 if (protocol_ == IOFetch::UDP && edns_) {
     561                 :            :                     // TODO: in case we absolutely need EDNS (i.e. for DNSSEC
     562                 :            :                     // aware queries), we might want to try TCP before we give
     563                 :            :                     // up. For now, just try UDP, no EDNS
     564         [ +  - ]:          1 :                     send(IOFetch::UDP, false);
     565                 :            :                     return (false);
     566                 :            :                 }
     567                 :            : 
     568                 :            :                 // TC should take care of non-EDNS over UDP, fall through to
     569                 :            :                 // SERVFAIL if we get FORMERR instead
     570                 :            :             }
     571                 :            :             goto SERVFAIL;
     572                 :            :             
     573                 :            :         default:
     574                 :            : SERVFAIL:
     575                 :            :             // Some error in received packet it.  Report it and return SERVFAIL
     576                 :            :             // to the caller.
     577 [ #  # ][ #  # ]:          0 :             if (logger.isDebugEnabled()) {
     578         [ #  # ]:          0 :                 reportResponseClassifierError(category, incoming.getRcode());
     579                 :            :             }
     580         [ #  # ]:          0 :             makeSERVFAIL();
     581                 :            :             return (true);
     582                 :            :         }
     583                 :            : 
     584                 :            :         // If we get here, there is some serious logic error (or a missing
     585                 :            :         // "return").
     586                 :            :         assert(false);
     587                 :            :         return (true);  // To keep the compiler happy
     588                 :            :     }
     589                 :            : 
     590                 :            :     /// \brief Report classification-detected error
     591                 :            :     ///
     592                 :            :     /// When the response classifier has detected an error in the response from
     593                 :            :     /// an upstream query, this method is called to log a debug message giving
     594                 :            :     /// information about the problem.
     595                 :            :     ///
     596                 :            :     /// \param category Classification code for the packet
     597                 :            :     /// \param rcode RCODE value in the packet
     598                 :            :     void reportResponseClassifierError(ResponseClassifier::Category category,
     599                 :            :                                        const Rcode& rcode)
     600                 :            :     {
     601                 :            :         // We could set up a table of response classifications to message
     602                 :            :         // IDs here and index into that table.  But given that (a) C++ does
     603                 :            :         // not have C's named initializers, (b) the codes for the
     604                 :            :         // response classifier are in another module and (c) not all messages
     605                 :            :         // have the same number of arguments, the setup of the table would be
     606                 :            :         // almost as long as the code here: it would need to include a number
     607                 :            :         // of assertions to ensure that any change to the the response
     608                 :            :         // classifier codes was detected, and the checking logic would need to
     609                 :            :         // check that the numeric value of the code lay within the defined
     610                 :            :         // limits of the table.
     611                 :            : 
     612         [ #  # ]:          0 :         if (category == ResponseClassifier::RCODE) {
     613                 :            : 
     614                 :            :             // Special case as this message takes two arguments.
     615 [ #  # ][ #  # ]:          0 :             LOG_DEBUG(logger, RESLIB_DBG_RESULTS, RESLIB_RCODE_ERROR).
                 [ #  # ]
     616 [ #  # ][ #  # ]:          0 :                       arg(questionText(question_)).arg(rcode);
         [ #  # ][ #  # ]
     617                 :            : 
     618                 :            :         } else {
     619                 :            : 
     620                 :            :             isc::log::MessageID message_id;
     621   [ #  #  #  #  :          0 :             switch (category) {
          #  #  #  #  #  
                #  #  # ]
     622                 :            :             case ResponseClassifier::TRUNCATED:
     623                 :          0 :                 message_id = RESLIB_TCP_TRUNCATED;
     624                 :            :                 break;
     625                 :            : 
     626                 :            :             case ResponseClassifier::EMPTY:
     627                 :          0 :                 message_id = RESLIB_EMPTY_RESPONSE;
     628                 :            :                 break;
     629                 :            : 
     630                 :            :             case ResponseClassifier::EXTRADATA:
     631                 :          0 :                 message_id = RESLIB_EXTRADATA_RESPONSE;
     632                 :            :                 break;
     633                 :            : 
     634                 :            :             case ResponseClassifier::INVNAMCLASS:
     635                 :          0 :                 message_id = RESLIB_INVALID_NAMECLASS_RESPONSE;
     636                 :            :                 break;
     637                 :            : 
     638                 :            :             case ResponseClassifier::INVTYPE:
     639                 :          0 :                 message_id = RESLIB_INVALID_TYPE_RESPONSE;
     640                 :            :                 break;
     641                 :            : 
     642                 :            :             case ResponseClassifier::MISMATQUEST:
     643                 :          0 :                 message_id = RESLIB_INVALID_QNAME_RESPONSE;
     644                 :            :                 break;
     645                 :            : 
     646                 :            :             case ResponseClassifier::MULTICLASS:
     647                 :          0 :                 message_id = RESLIB_MULTIPLE_CLASS_RESPONSE;
     648                 :            :                 break;
     649                 :            : 
     650                 :            :             case ResponseClassifier::NOTONEQUEST:
     651                 :          0 :                 message_id = RESLIB_NOT_ONE_QNAME_RESPONSE;
     652                 :            :                 break;
     653                 :            : 
     654                 :            :             case ResponseClassifier::NOTRESPONSE:
     655                 :          0 :                 message_id = RESLIB_NOT_RESPONSE;
     656                 :            :                 break;
     657                 :            : 
     658                 :            :             case ResponseClassifier::NOTSINGLE:
     659                 :          0 :                 message_id = RESLIB_NOTSINGLE_RESPONSE;
     660                 :            :                 break;
     661                 :            : 
     662                 :            :             case ResponseClassifier::OPCODE:
     663                 :          0 :                 message_id = RESLIB_OPCODE_RESPONSE;
     664                 :            :                 break;
     665                 :            : 
     666                 :            :             default:
     667                 :          0 :                 message_id = RESLIB_ERROR_RESPONSE;
     668                 :            :                 break;
     669                 :            :             }
     670 [ #  # ][ #  # ]:          0 :             LOG_DEBUG(logger, RESLIB_DBG_RESULTS, message_id).
                 [ #  # ]
     671 [ #  # ][ #  # ]:          0 :                       arg(questionText(question_));
                 [ #  # ]
     672                 :            :         }
     673                 :            :     }
     674                 :            : 
     675                 :            : public:
     676                 :          3 :     RunningQuery(IOService& io,
     677                 :            :         const Question& question,
     678                 :            :         MessagePtr answer_message,
     679                 :            :         std::pair<std::string, uint16_t>& test_server,
     680                 :            :         OutputBufferPtr buffer,
     681                 :            :         isc::resolve::ResolverInterface::CallbackPtr cb,
     682                 :            :         int query_timeout, int client_timeout, int lookup_timeout,
     683                 :            :         unsigned retries,
     684                 :            :         isc::nsas::NameserverAddressStore& nsas,
     685                 :            :         isc::cache::ResolverCache& cache,
     686                 :            :         boost::shared_ptr<RttRecorder>& recorder)
     687                 :            :         :
     688                 :            :         io_(io),
     689                 :            :         question_(question),
     690                 :            :         query_message_(),
     691                 :            :         answer_message_(answer_message),
     692                 :            :         test_server_(test_server),
     693                 :            :         buffer_(buffer),
     694                 :            :         resolvercallback_(cb),
     695                 :            :         protocol_(IOFetch::UDP),
     696                 :            :         cname_count_(0),
     697                 :            :         query_timeout_(query_timeout),
     698                 :            :         retries_(retries),
     699         [ +  - ]:          3 :         client_timer(io.get_io_service()),
     700         [ +  - ]:          3 :         lookup_timer(io.get_io_service()),
     701                 :            :         done_(false),
     702                 :            :         callback_called_(false),
     703                 :            :         nsas_(nsas),
     704                 :            :         cache_(cache),
     705                 :            :         cur_zone_("."),
     706                 :            :         nsas_callback_(),
     707                 :            :         nsas_callback_out_(false),
     708                 :            :         outstanding_events_(0),
     709 [ +  - ][ +  - ]:          6 :         rtt_recorder_(recorder)
                 [ +  - ]
     710                 :            :     {
     711                 :            :         // Set here to avoid using "this" in initializer list.
     712         [ +  - ]:          3 :         nsas_callback_.reset(new ResolverNSASCallback(this));
     713                 :            : 
     714                 :            :         // Setup the timer to stop trying (lookup_timeout)
     715         [ +  - ]:          3 :         if (lookup_timeout >= 0) {
     716                 :            :             lookup_timer.expires_from_now(
     717         [ +  - ]:          3 :                 boost::posix_time::milliseconds(lookup_timeout));
     718                 :          3 :             ++outstanding_events_;
     719         [ +  - ]:          3 :             lookup_timer.async_wait(boost::bind(&RunningQuery::lookupTimeout, this));
     720                 :            :         }
     721                 :            :         
     722                 :            :         // Setup the timer to send an answer (client_timeout)
     723         [ +  - ]:          3 :         if (client_timeout >= 0) {
     724                 :            :             client_timer.expires_from_now(
     725         [ +  - ]:          3 :                 boost::posix_time::milliseconds(client_timeout));
     726                 :          3 :             ++outstanding_events_;
     727         [ +  - ]:          3 :             client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
     728                 :            :         }
     729                 :            :         
     730         [ +  - ]:          3 :         doLookup();
     731                 :          3 :     }
     732                 :            : 
     733                 :            :     // called if we have a lookup timeout; if our callback has
     734                 :            :     // not been called, call it now. Then stop.
     735                 :          0 :     void lookupTimeout() {
     736         [ #  # ]:          0 :         if (!callback_called_) {
     737                 :          0 :             makeSERVFAIL();
     738                 :          0 :             callCallback(true);
     739                 :            :         }
     740         [ #  # ]:          0 :         assert(outstanding_events_ > 0);
     741                 :          0 :         --outstanding_events_;
     742                 :          0 :         stop();
     743                 :          0 :     }
     744                 :            :     
     745                 :            :     // called if we have a client timeout; if our callback has
     746                 :            :     // not been called, call it now. But do not stop.
     747                 :          0 :     void clientTimeout() {
     748         [ #  # ]:          0 :         if (!callback_called_) {
     749                 :          0 :             makeSERVFAIL();
     750                 :          0 :             callCallback(true);
     751                 :            :         }
     752         [ #  # ]:          0 :         assert(outstanding_events_ > 0);
     753                 :          0 :         --outstanding_events_;
     754         [ #  # ]:          0 :         if (outstanding_events_ == 0) {
     755                 :          0 :             stop();
     756                 :            :         }
     757                 :          0 :     }
     758                 :            : 
     759                 :            :     // If the callback has not been called yet, call it now
     760                 :            :     // If success is true, we call 'success' with our answer_message
     761                 :            :     // If it is false, we call failure()
     762                 :          3 :     void callCallback(bool success) {
     763         [ +  - ]:          3 :         if (!callback_called_) {
     764                 :          3 :             callback_called_ = true;
     765                 :            : 
     766                 :            :             // There are two types of messages we could store in the
     767                 :            :             // cache;
     768                 :            :             // 1. answers to our fetches from authoritative servers,
     769                 :            :             //    exactly as we receive them, and
     770                 :            :             // 2. answers to queries we received from clients, which
     771                 :            :             //    have received additional processing (following CNAME
     772                 :            :             //    chains, for instance)
     773                 :            :             //
     774                 :            :             // Doing only the first would mean we would have to re-do
     775                 :            :             // processing when we get data from our cache, and doing
     776                 :            :             // only the second would miss out on the side-effect of
     777                 :            :             // having nameserver data in our cache.
     778                 :            :             //
     779                 :            :             // So right now we do both. Since the cache (currently)
     780                 :            :             // stores Messages on their question section only, this
     781                 :            :             // does mean that we overwrite the messages we stored in
     782                 :            :             // the previous iteration if we are following a delegation.
     783         [ +  - ]:          3 :             if (success) {
     784         [ +  - ]:          6 :                 resolvercallback_->success(answer_message_);
     785                 :            :             } else {
     786                 :          0 :                 resolvercallback_->failure();
     787                 :            :             }
     788                 :            :         }
     789                 :          3 :     }
     790                 :            : 
     791                 :            :     // We are done. If there are no more outstanding events, we delete
     792                 :            :     // ourselves. If there are any, we do not.
     793                 :          3 :     void stop() {
     794                 :          3 :         done_ = true;
     795         [ -  + ]:          3 :         if (nsas_callback_out_) {
     796         [ #  # ]:          0 :             nsas_.cancel(cur_zone_, question_.getClass(), nsas_callback_);
     797                 :          0 :             nsas_callback_out_ = false;
     798                 :            :         }
     799                 :          3 :         client_timer.cancel();
     800                 :          3 :         lookup_timer.cancel();
     801         [ -  + ]:          3 :         if (outstanding_events_ > 0) {
     802                 :          3 :             return;
     803                 :            :         } else {
     804         [ #  # ]:          0 :             delete this;
     805                 :            :         }
     806                 :            :     }
     807                 :            : 
     808                 :            :     // This function is used as callback from DNSQuery.
     809                 :          8 :     virtual void operator()(IOFetch::Result result) {
     810                 :            :         // XXX is this the place for TCP retry?
     811         [ -  + ]:          8 :         assert(outstanding_events_ > 0);
     812                 :          8 :         --outstanding_events_;
     813                 :            :         
     814 [ +  - ][ +  + ]:          8 :         if (!done_ && result != IOFetch::TIME_OUT) {
     815                 :            :             // we got an answer
     816                 :            : 
     817                 :            :             // Update the NSAS with the time it took
     818                 :            :             struct timeval cur_time;
     819                 :          7 :             gettimeofday(&cur_time, NULL);
     820                 :          7 :             uint32_t rtt = 0;
     821                 :            : 
     822                 :            :             // Only calculate RTT if it is positive
     823 [ +  - ][ +  - ]:          7 :             if (cur_time.tv_sec > current_ns_qsent_time.tv_sec ||
                 [ +  - ]
     824                 :            :                 (cur_time.tv_sec == current_ns_qsent_time.tv_sec &&
     825                 :            :                  cur_time.tv_usec > current_ns_qsent_time.tv_usec)) {
     826                 :          7 :                 rtt = 1000 * (cur_time.tv_sec - current_ns_qsent_time.tv_sec);
     827                 :          7 :                 rtt += (cur_time.tv_usec - current_ns_qsent_time.tv_usec) / 1000;
     828                 :            :             }
     829 [ +  - ][ +  - ]:          7 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_RTT).arg(rtt);
     830                 :          7 :             current_ns_address.updateRTT(rtt);
     831         [ +  - ]:          7 :             if (rtt_recorder_) {
     832                 :         14 :                 rtt_recorder_->addRtt(rtt);
     833                 :            :             }
     834                 :            : 
     835                 :            :             try {
     836 [ +  - ][ +  - ]:         14 :                 Message incoming(Message::PARSE);
     837                 :          7 :                 InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
     838                 :            : 
     839         [ +  + ]:          7 :                 incoming.fromWire(ibuf);
     840                 :            : 
     841                 :          6 :                 buffer_->clear();
     842         [ +  - ]:          6 :                 done_ = handleRecursiveAnswer(incoming);
     843         [ +  + ]:          6 :                 if (done_) {
     844         [ +  - ]:          2 :                     callCallback(true);
     845         [ +  - ]:          2 :                     stop();
     846                 :            :                 }
     847         [ -  + ]:          2 :             } catch (const isc::dns::DNSProtocolError& dpe) {
     848                 :            :                 // Right now, we treat this similar to timeouts
     849                 :            :                 // (except we don't store RTT)
     850                 :            :                 // We probably want to make this an integral part
     851                 :            :                 // of the fetch data process. (TODO)
     852         [ +  - ]:          1 :                 if (retries_--) {
     853                 :            :                     // Retry
     854 [ -  + ][ +  - ]:          2 :                     LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
                 [ -  + ]
     855                 :            :                               RESLIB_PROTOCOL_RETRY)
     856 [ -  + ][ -  + ]:          2 :                               .arg(questionText(question_)).arg(dpe.what())
         [ -  + ][ -  + ]
     857         [ -  + ]:          1 :                               .arg(retries_);
     858         [ -  + ]:          1 :                     send();
     859                 :            :                 } else {
     860                 :            :                     // Give up
     861 [ #  # ][ #  # ]:          0 :                     LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
                 [ #  # ]
     862                 :            :                               RESLIB_PROTOCOL)
     863 [ #  # ][ #  # ]:          0 :                               .arg(questionText(question_)).arg(dpe.what());
         [ #  # ][ #  # ]
     864         [ #  # ]:          0 :                     if (!callback_called_) {
     865         [ #  # ]:          0 :                         makeSERVFAIL();
     866         [ #  # ]:          0 :                         callCallback(true);
     867                 :            :                     }
     868         [ #  # ]:          0 :                     stop();
     869                 :            :                 }
     870                 :            :             }
     871 [ +  - ][ -  + ]:          1 :         } else if (!done_ && retries_--) {
                 [ +  - ]
     872                 :            :             // Query timed out, but we have some retries, so send again
     873 [ +  - ][ +  - ]:          2 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUT_RETRY)
     874 [ +  - ][ +  - ]:          2 :                       .arg(questionText(question_))
                 [ +  - ]
     875 [ +  - ][ +  - ]:          3 :                       .arg(current_ns_address.getAddress().toText()).arg(retries_);
     876                 :          1 :             current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
     877                 :          1 :             send();
     878                 :            :         } else {
     879                 :            :             // We are either already done, or out of retries
     880         [ #  # ]:          0 :             if (result == IOFetch::TIME_OUT) {
     881 [ #  # ][ #  # ]:          0 :                 LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUT)
     882 [ #  # ][ #  # ]:          0 :                           .arg(questionText(question_))
                 [ #  # ]
     883         [ #  # ]:          0 :                           .arg(current_ns_address.getAddress().toText());
     884                 :          0 :                 current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
     885                 :            :             }
     886         [ #  # ]:          0 :             if (!callback_called_) {
     887                 :          0 :                 makeSERVFAIL();
     888                 :          0 :                 callCallback(true);
     889                 :            :             }
     890                 :          0 :             stop();
     891                 :            :         }
     892                 :          8 :     }
     893                 :            :     
     894                 :            :     // Clear the answer parts of answer_message, and set the rcode
     895                 :            :     // to servfail
     896                 :          1 :     void makeSERVFAIL() {
     897         [ +  - ]:          2 :         isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
     898                 :          1 :     }
     899                 :            : };
     900                 :            : 
     901 [ #  # ][ #  # ]:          0 : class ForwardQuery : public IOFetch::Callback {
     902                 :            : private:
     903                 :            :     // The io service to handle async calls
     904                 :            :     IOService& io_;
     905                 :            : 
     906                 :            :     // This is the query message got from client
     907                 :            :     ConstMessagePtr query_message_;
     908                 :            : 
     909                 :            :     // This is where we build and store our final answer
     910                 :            :     MessagePtr answer_message_;
     911                 :            : 
     912                 :            :     // List of nameservers to forward to
     913                 :            :     boost::shared_ptr<AddressVector> upstream_;
     914                 :            : 
     915                 :            :     // Buffer to store the result.
     916                 :            :     OutputBufferPtr buffer_;
     917                 :            : 
     918                 :            :     // This will be notified when we succeed or fail
     919                 :            :     isc::resolve::ResolverInterface::CallbackPtr resolvercallback_;
     920                 :            : 
     921                 :            :     /*
     922                 :            :      * TODO Do something more clever with timeouts. In the long term, some
     923                 :            :      *     computation of average RTT, increase with each retry, etc.
     924                 :            :      */
     925                 :            :     // Timeout information
     926                 :            :     int query_timeout_;
     927                 :            : 
     928                 :            :     // TODO: replace by our wrapper
     929                 :            :     asio::deadline_timer client_timer;
     930                 :            :     asio::deadline_timer lookup_timer;
     931                 :            : 
     932                 :            :     // Make FowardQuery deletes itself safely. for more information see
     933                 :            :     // the comments of outstanding_events in RunningQuery.
     934                 :            :     size_t outstanding_events_;
     935                 :            : 
     936                 :            :     // If we have a client timeout, we call back with a failure message,
     937                 :            :     // but we do not stop yet. We use this variable to make sure we
     938                 :            :     // don't call back a second time later
     939                 :            :     bool callback_called_;
     940                 :            : 
     941                 :            :     // send the query to the server.
     942                 :            :     void send(IOFetch::Protocol protocol = IOFetch::UDP) {
     943                 :          5 :         const int uc = upstream_->size();
     944                 :          5 :         buffer_->clear();
     945                 :          5 :         int serverIndex = rand() % uc;
     946 [ +  - ][ +  - ]:         10 :         ConstQuestionPtr question = *(query_message_->beginQuestion());
                 [ +  - ]
     947 [ +  - ][ +  - ]:         10 :         LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_UPSTREAM)
                 [ +  - ]
     948 [ +  - ][ +  - ]:         10 :             .arg(questionText(*question))
                 [ +  - ]
     949 [ +  - ][ +  - ]:         10 :             .arg(upstream_->at(serverIndex).first);
     950                 :            : 
     951                 :          5 :         ++outstanding_events_;
     952                 :            :         // Forward the query, create the IOFetch with
     953                 :            :         // query message, so that query flags can be forwarded
     954                 :            :         // together.
     955                 :            :         IOFetch query(protocol, io_, query_message_,
     956         [ +  - ]:          5 :             upstream_->at(serverIndex).first,
     957         [ +  - ]:          5 :             upstream_->at(serverIndex).second,
     958 [ +  - ][ +  - ]:         15 :             buffer_, this, query_timeout_);
                 [ +  - ]
     959                 :            : 
     960 [ +  - ][ +  - ]:          5 :         io_.get_io_service().post(query);
                 [ +  - ]
     961                 :            :     }
     962                 :            : 
     963                 :            : public:
     964                 :            :     ForwardQuery(IOService& io,
     965                 :            :         ConstMessagePtr query_message,
     966                 :            :         MessagePtr answer_message,
     967                 :            :         boost::shared_ptr<AddressVector> upstream,
     968                 :            :         OutputBufferPtr buffer,
     969                 :            :         isc::resolve::ResolverInterface::CallbackPtr cb,
     970                 :            :         int query_timeout, int client_timeout, int lookup_timeout) :
     971                 :            :         io_(io),
     972                 :            :         query_message_(query_message),
     973                 :            :         answer_message_(answer_message),
     974                 :            :         upstream_(upstream),
     975                 :            :         buffer_(buffer),
     976                 :            :         resolvercallback_(cb),
     977                 :            :         query_timeout_(query_timeout),
     978         [ +  - ]:          5 :         client_timer(io.get_io_service()),
     979         [ +  - ]:          5 :         lookup_timer(io.get_io_service()),
     980                 :            :         outstanding_events_(0),
     981 [ +  - ][ +  - ]:         10 :         callback_called_(false)
     982                 :            :     {
     983                 :            :         // Setup the timer to stop trying (lookup_timeout)
     984         [ +  - ]:          5 :         if (lookup_timeout >= 0) {
     985                 :            :             lookup_timer.expires_from_now(
     986         [ +  - ]:          5 :                 boost::posix_time::milliseconds(lookup_timeout));
     987                 :          5 :             ++outstanding_events_;
     988         [ +  - ]:          5 :             lookup_timer.async_wait(boost::bind(&ForwardQuery::lookupTimeout, this));
     989                 :            :         }
     990                 :            : 
     991                 :            :         // Setup the timer to send an answer (client_timeout)
     992         [ +  - ]:          5 :         if (client_timeout >= 0) {
     993                 :            :             client_timer.expires_from_now(
     994         [ +  - ]:          5 :                 boost::posix_time::milliseconds(client_timeout));
     995                 :          5 :             ++outstanding_events_;
     996         [ +  - ]:          5 :             client_timer.async_wait(boost::bind(&ForwardQuery::clientTimeout, this));
     997                 :            :         }
     998                 :            : 
     999                 :            :         send();
    1000                 :            :     }
    1001                 :            : 
    1002                 :          3 :     virtual void lookupTimeout() {
    1003         [ +  - ]:          3 :         if (!callback_called_) {
    1004                 :          3 :             makeSERVFAIL();
    1005                 :          3 :             callCallback(false);
    1006                 :            :         }
    1007         [ -  + ]:          3 :         assert(outstanding_events_ > 0);
    1008                 :          3 :         --outstanding_events_;
    1009                 :          3 :         stop();
    1010                 :          3 :     }
    1011                 :            : 
    1012                 :          1 :     virtual void clientTimeout() {
    1013         [ +  - ]:          1 :         if (!callback_called_) {
    1014                 :          1 :             makeSERVFAIL();
    1015                 :          1 :             callCallback(false);
    1016                 :            :         }
    1017         [ -  + ]:          1 :         assert(outstanding_events_ > 0);
    1018                 :          1 :         --outstanding_events_;
    1019                 :          1 :         stop();
    1020                 :          1 :     }
    1021                 :            : 
    1022                 :            :     // If the callback has not been called yet, call it now
    1023                 :            :     // If success is true, we call 'success' with our answer_message
    1024                 :            :     // If it is false, we call failure()
    1025                 :          4 :     void callCallback(bool success) {
    1026         [ +  - ]:          4 :         if (!callback_called_) {
    1027                 :          4 :             callback_called_ = true;
    1028         [ -  + ]:          4 :             if (success) {
    1029         [ #  # ]:          0 :                 resolvercallback_->success(answer_message_);
    1030                 :            :             } else {
    1031                 :          4 :                 resolvercallback_->failure();
    1032                 :            :             }
    1033                 :            :         }
    1034                 :          4 :     }
    1035                 :            : 
    1036                 :          5 :     virtual void stop() {
    1037                 :            :         // if we cancel our timers, we will still get an event for
    1038                 :            :         // that, so we cannot delete ourselves just yet (those events
    1039                 :            :         // would be bound to a deleted object)
    1040                 :            :         // cancel them one by one, both cancels should get us back
    1041                 :            :         // here again.
    1042                 :            :         // same goes if we have an outstanding query (can't delete
    1043                 :            :         // until that one comes back to us)
    1044                 :          5 :         lookup_timer.cancel();
    1045                 :          5 :         client_timer.cancel();
    1046         [ -  + ]:          5 :         if (outstanding_events_ > 0) {
    1047                 :          5 :             return;
    1048                 :            :         } else {
    1049         [ #  # ]:          0 :             delete this;
    1050                 :            :         }
    1051                 :            :     }
    1052                 :            : 
    1053                 :            :     // This function is used as callback from DNSQuery.
    1054                 :          1 :     virtual void operator()(IOFetch::Result result) {
    1055                 :            :         // XXX is this the place for TCP retry?
    1056         [ -  + ]:          1 :         assert(outstanding_events_ > 0);
    1057                 :          1 :         --outstanding_events_;
    1058         [ -  + ]:          1 :         if (result != IOFetch::TIME_OUT) {
    1059                 :            :             // we got an answer
    1060                 :          0 :             Message incoming(Message::PARSE);
    1061                 :          0 :             InputBuffer ibuf(buffer_->getData(), buffer_->getLength());
    1062         [ #  # ]:          0 :             incoming.fromWire(ibuf);
    1063         [ #  # ]:          0 :             isc::resolve::copyResponseMessage(incoming, answer_message_);
    1064         [ #  # ]:          0 :             callCallback(true);
    1065                 :            :         }
    1066                 :            : 
    1067                 :          1 :         stop();
    1068                 :          1 :     }
    1069                 :            : 
    1070                 :            :     // Clear the answer parts of answer_message, and set the rcode
    1071                 :            :     // to servfail
    1072                 :          4 :     void makeSERVFAIL() {
    1073         [ +  - ]:          8 :         isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
    1074                 :          4 :     }
    1075                 :            : };
    1076                 :            : 
    1077                 :            : }
    1078                 :            : 
    1079                 :            : void
    1080                 :          2 : RecursiveQuery::resolve(const QuestionPtr& question,
    1081                 :            :     const isc::resolve::ResolverInterface::CallbackPtr callback)
    1082                 :            : {
    1083                 :          2 :     IOService& io = dns_service_.getIOService();
    1084                 :            : 
    1085         [ +  - ]:          2 :     MessagePtr answer_message(new Message(Message::RENDER));
    1086         [ +  - ]:          2 :     isc::resolve::initResponseMessage(*question, *answer_message);
    1087                 :            : 
    1088         [ +  - ]:          2 :     OutputBufferPtr buffer(new OutputBuffer(0));
    1089                 :            : 
    1090                 :            :     // First try to see if we have something cached in the messagecache
    1091 [ +  - ][ +  - ]:          4 :     LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
                 [ +  - ]
    1092 [ +  - ][ +  - ]:          4 :               .arg(questionText(*question)).arg(1);
         [ +  - ][ +  - ]
    1093 [ -  + ][ #  # ]:          4 :     if (cache_.lookup(question->getName(), question->getType(),
                 [ -  + ]
    1094         [ +  - ]:          4 :                       question->getClass(), *answer_message) &&
    1095         [ #  # ]:          0 :         answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
    1096                 :            :         // Message found, return that
    1097 [ #  # ][ #  # ]:          0 :         LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND)
                 [ #  # ]
    1098 [ #  # ][ #  # ]:          0 :                   .arg(questionText(*question)).arg(1);
         [ #  # ][ #  # ]
    1099                 :            :         
    1100                 :            :         // TODO: err, should cache set rcode as well?
    1101 [ #  # ][ #  # ]:          0 :         answer_message->setRcode(Rcode::NOERROR());
    1102         [ #  # ]:          0 :         callback->success(answer_message);
    1103                 :            :     } else {
    1104                 :            :         // Perhaps we only have the one RRset?
    1105                 :            :         // TODO: can we do this? should we check for specific types only?
    1106                 :          2 :         RRsetPtr cached_rrset = cache_.lookup(question->getName(),
    1107                 :          2 :                                               question->getType(),
    1108         [ +  - ]:          4 :                                               question->getClass());
    1109         [ -  + ]:          2 :         if (cached_rrset) {
    1110                 :            :             // Found single RRset in cache
    1111 [ #  # ][ #  # ]:          0 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSET_FOUND)
                 [ #  # ]
    1112 [ #  # ][ #  # ]:          0 :                       .arg(questionText(*question)).arg(1);
         [ #  # ][ #  # ]
    1113                 :            :             answer_message->addRRset(Message::SECTION_ANSWER,
    1114         [ #  # ]:          0 :                                      cached_rrset);
    1115 [ #  # ][ #  # ]:          0 :             answer_message->setRcode(Rcode::NOERROR());
    1116         [ #  # ]:          0 :             callback->success(answer_message);
    1117                 :            :         } else {
    1118                 :            :             // Message not found in cache, start recursive query.  It will
    1119                 :            :             // delete itself when it is done
    1120 [ +  - ][ +  - ]:          4 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
                 [ +  - ]
    1121 [ +  - ][ +  - ]:          4 :                       .arg(questionText(*question)).arg(1);
         [ +  - ][ +  - ]
    1122                 :            :             new RunningQuery(io, *question, answer_message,
    1123                 :            :                              test_server_, buffer, callback,
    1124                 :            :                              query_timeout_, client_timeout_,
    1125                 :            :                              lookup_timeout_, retries_, nsas_,
    1126 [ +  - ][ +  - ]:          4 :                              cache_, rtt_recorder_);
    1127                 :            :         }
    1128                 :            :     }
    1129                 :          2 : }
    1130                 :            : 
    1131                 :            : void
    1132                 :          1 : RecursiveQuery::resolve(const Question& question,
    1133                 :            :                         MessagePtr answer_message,
    1134                 :            :                         OutputBufferPtr buffer,
    1135                 :            :                         DNSServer* server)
    1136                 :            : {
    1137                 :            :     // XXX: eventually we will need to be able to determine whether
    1138                 :            :     // the message should be sent via TCP or UDP, or sent initially via
    1139                 :            :     // UDP and then fall back to TCP on failure, but for the moment
    1140                 :            :     // we're only going to handle UDP.
    1141                 :          1 :     IOService& io = dns_service_.getIOService();
    1142                 :            : 
    1143                 :            :     isc::resolve::ResolverInterface::CallbackPtr crs(
    1144                 :          2 :         new isc::resolve::ResolverCallbackServer(server));
    1145                 :            : 
    1146                 :            :     // TODO: general 'prepareinitialanswer'
    1147 [ +  - ][ +  - ]:          1 :     answer_message->setOpcode(isc::dns::Opcode::QUERY());
    1148         [ +  - ]:          1 :     answer_message->addQuestion(question);
    1149                 :            :     
    1150                 :            :     // First try to see if we have something cached in the messagecache
    1151 [ +  - ][ +  - ]:          2 :     LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
                 [ +  - ]
    1152 [ +  - ][ +  - ]:          2 :               .arg(questionText(question)).arg(2);
         [ +  - ][ +  - ]
    1153                 :            :     
    1154 [ -  + ][ #  # ]:          1 :     if (cache_.lookup(question.getName(), question.getType(),
                 [ -  + ]
    1155         [ +  - ]:          1 :                       question.getClass(), *answer_message) &&
    1156         [ #  # ]:          0 :         answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
    1157                 :            : 
    1158                 :            :         // Message found, return that
    1159 [ #  # ][ #  # ]:          0 :         LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND)
                 [ #  # ]
    1160 [ #  # ][ #  # ]:          0 :                   .arg(questionText(question)).arg(2);
         [ #  # ][ #  # ]
    1161                 :            :         // TODO: err, should cache set rcode as well?
    1162 [ #  # ][ #  # ]:          0 :         answer_message->setRcode(Rcode::NOERROR());
    1163         [ #  # ]:          0 :         crs->success(answer_message);
    1164                 :            :     } else {
    1165                 :            :         // Perhaps we only have the one RRset?
    1166                 :            :         // TODO: can we do this? should we check for specific types only?
    1167                 :          1 :         RRsetPtr cached_rrset = cache_.lookup(question.getName(),
    1168                 :          1 :                                               question.getType(),
    1169         [ +  - ]:          1 :                                               question.getClass());
    1170         [ -  + ]:          1 :         if (cached_rrset) {
    1171                 :            :             // Found single RRset in cache
    1172 [ #  # ][ #  # ]:          0 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSET_FOUND)
                 [ #  # ]
    1173 [ #  # ][ #  # ]:          0 :                       .arg(questionText(question)).arg(2);
         [ #  # ][ #  # ]
    1174                 :            :             answer_message->addRRset(Message::SECTION_ANSWER,
    1175         [ #  # ]:          0 :                                      cached_rrset);
    1176 [ #  # ][ #  # ]:          0 :             answer_message->setRcode(Rcode::NOERROR());
    1177         [ #  # ]:          0 :             crs->success(answer_message);
    1178                 :            : 
    1179                 :            :         } else {
    1180                 :            :             // Message not found in cache, start recursive query.  It will
    1181                 :            :             // delete itself when it is done
    1182 [ +  - ][ +  - ]:          2 :             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
                 [ +  - ]
    1183 [ +  - ][ +  - ]:          2 :                       .arg(questionText(question)).arg(2);
         [ +  - ][ +  - ]
    1184                 :            :             new RunningQuery(io, question, answer_message, 
    1185                 :            :                              test_server_, buffer, crs, query_timeout_,
    1186                 :            :                              client_timeout_, lookup_timeout_, retries_,
    1187 [ +  - ][ +  - ]:          1 :                              nsas_, cache_, rtt_recorder_);
    1188                 :            :         }
    1189                 :            :     }
    1190                 :          1 : }
    1191                 :            : 
    1192                 :            : void
    1193                 :          5 : RecursiveQuery::forward(ConstMessagePtr query_message,
    1194                 :            :     MessagePtr answer_message,
    1195                 :            :     OutputBufferPtr buffer,
    1196                 :            :     DNSServer* server,
    1197                 :            :     isc::resolve::ResolverInterface::CallbackPtr callback)
    1198                 :            : {
    1199                 :            :     // XXX: eventually we will need to be able to determine whether
    1200                 :            :     // the message should be sent via TCP or UDP, or sent initially via
    1201                 :            :     // UDP and then fall back to TCP on failure, but for the moment
    1202                 :            :     // we're only going to handle UDP.
    1203                 :          5 :     IOService& io = dns_service_.getIOService();
    1204                 :            : 
    1205         [ +  + ]:          5 :     if (!callback) {
    1206                 :          1 :         callback.reset(new isc::resolve::ResolverCallbackServer(server));
    1207                 :            :     }
    1208                 :            : 
    1209                 :            :     // TODO: general 'prepareinitialanswer'
    1210                 :          5 :     answer_message->setOpcode(isc::dns::Opcode::QUERY());
    1211 [ +  - ][ +  - ]:         10 :     ConstQuestionPtr question = *query_message->beginQuestion();
    1212         [ +  - ]:          5 :     answer_message->addQuestion(*question);
    1213                 :            : 
    1214                 :            :     // implement the simplest forwarder, which will pass
    1215                 :            :     // everything throught without interpretation, except
    1216                 :            :     // QID, port number. The response will not be cached.
    1217                 :            :     // It will delete itself when it is done
    1218                 :            :     new ForwardQuery(io, query_message, answer_message,
    1219                 :            :                       upstream_, buffer, callback, query_timeout_,
    1220         [ +  - ]:          5 :                       client_timeout_, lookup_timeout_);
    1221                 :          5 : }
    1222                 :            : 
    1223                 :            : } // namespace asiodns
    1224 [ +  - ][ +  - ]:          6 : } // namespace isc

Generated by: LCOV version 1.9