LCOV - code coverage report
Current view: top level - home/jelte/repos/coverage_bind10/src/bin/auth - query.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 218 221 98.6 %
Date: 2012-05-15 Functions: 19 19 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 315 459 68.6 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
       2                 :            : //
       3                 :            : // Permission to use, copy, modify, and/or distribute this software for any
       4                 :            : // purpose with or without fee is hereby granted, provided that the above
       5                 :            : // copyright notice and this permission notice appear in all copies.
       6                 :            : //
       7                 :            : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
       8                 :            : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       9                 :            : // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
      10                 :            : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
      11                 :            : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
      12                 :            : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
      13                 :            : // PERFORMANCE OF THIS SOFTWARE.
      14                 :            : 
      15                 :            : #include <dns/message.h>
      16                 :            : #include <dns/rcode.h>
      17                 :            : #include <dns/rrtype.h>
      18                 :            : #include <dns/rrset.h>
      19                 :            : #include <dns/rdataclass.h>
      20                 :            : 
      21                 :            : #include <datasrc/client.h>
      22                 :            : 
      23                 :            : #include <auth/query.h>
      24                 :            : 
      25                 :            : #include <boost/foreach.hpp>
      26                 :            : #include <boost/bind.hpp>
      27                 :            : #include <boost/function.hpp>
      28                 :            : 
      29                 :            : #include <cassert>
      30                 :            : #include <algorithm>            // for std::max
      31                 :            : #include <functional>
      32                 :            : #include <vector>
      33                 :            : 
      34                 :            : using namespace std;
      35                 :            : using namespace isc::dns;
      36                 :            : using namespace isc::datasrc;
      37                 :            : using namespace isc::dns::rdata;
      38                 :            : 
      39                 :            : // This is a "constant" vector storing desired RR types for the additional
      40                 :            : // section.  The vector is filled first time it's used.
      41                 :            : namespace {
      42                 :            : const vector<RRType>&
      43                 :         73 : A_AND_AAAA() {
      44 [ +  + ][ +  - ]:         73 :     static vector<RRType> needed_types;
      45         [ +  + ]:         73 :     if (needed_types.empty()) {
      46                 :          1 :         needed_types.push_back(RRType::A());
      47                 :          1 :         needed_types.push_back(RRType::AAAA());
      48                 :            :     }
      49                 :         73 :     return (needed_types);
      50                 :            : }
      51                 :            : }
      52                 :            : 
      53                 :            : namespace isc {
      54                 :            : namespace auth {
      55                 :            : 
      56                 :            : void
      57                 :        245 : Query::ResponseCreator::addRRset(isc::dns::Message& message,
      58                 :            :                                  const isc::dns::Message::Section section,
      59                 :            :                                  const ConstRRsetPtr& rrset, const bool dnssec)
      60                 :            : {
      61                 :            :     /// Is this RRset already in the list of RRsets added to the message?
      62                 :            :     const std::vector<const AbstractRRset*>::const_iterator i =
      63                 :        490 :         std::find_if(added_.begin(), added_.end(),
      64                 :            :                      std::bind1st(Query::ResponseCreator::IsSameKind(),
      65                 :        245 :                                   rrset.get()));
      66         [ +  + ]:        245 :     if (i == added_.end()) {
      67                 :            :         // No - add it to both the message and the list of RRsets processed.
      68                 :            :         // The const-cast is wrong, but the message interface seems to insist.
      69                 :            :         message.addRRset(section,
      70                 :            :                          boost::const_pointer_cast<AbstractRRset>(rrset),
      71         [ +  - ]:        231 :                          dnssec);
      72                 :        231 :         added_.push_back(rrset.get());
      73                 :            :     }
      74                 :        245 : }
      75                 :            : 
      76                 :            : void
      77                 :         72 : Query::ResponseCreator::create(Message& response,
      78                 :            :                                const vector<ConstRRsetPtr>& answers,
      79                 :            :                                const vector<ConstRRsetPtr>& authorities,
      80                 :            :                                const vector<ConstRRsetPtr>& additionals,
      81                 :            :                                const bool dnssec)
      82                 :            : {
      83                 :            :     // Inserter should be reset each time the query is reset, so should be
      84                 :            :     // empty at this point.
      85         [ -  + ]:         72 :     assert(added_.empty());
      86                 :            : 
      87                 :            :     // Add the RRsets to the message.  The order of sections is important,
      88                 :            :     // as the ResponseCreator remembers RRsets added and will not add
      89                 :            :     // duplicates.  Adding in the order answer, authory, additional will
      90                 :            :     // guarantee that if there are duplicates, the single RRset added will
      91                 :            :     // appear in the most important section.
      92 [ +  + ][ +  - ]:        170 :     BOOST_FOREACH(const ConstRRsetPtr& rrset, answers) {
         [ +  - ][ +  + ]
                 [ +  + ]
      93                 :         49 :         addRRset(response, Message::SECTION_ANSWER, rrset, dnssec);
      94                 :            :     }
      95 [ +  + ][ +  - ]:        284 :     BOOST_FOREACH(const ConstRRsetPtr& rrset, authorities) {
         [ +  - ][ +  + ]
                 [ +  + ]
      96                 :        106 :         addRRset(response, Message::SECTION_AUTHORITY, rrset, dnssec);
      97                 :            :     }
      98 [ +  + ][ +  - ]:        252 :     BOOST_FOREACH(const ConstRRsetPtr& rrset, additionals) {
         [ +  - ][ +  + ]
                 [ +  + ]
      99                 :         90 :         addRRset(response, Message::SECTION_ADDITIONAL, rrset, dnssec);
     100                 :            :     }
     101                 :         72 : }
     102                 :            : 
     103                 :            : void
     104                 :         40 : Query::addSOA(ZoneFinder& finder) {
     105                 :         38 :     ZoneFinderContextPtr soa_ctx = finder.find(finder.getOrigin(),
     106         [ +  - ]:         40 :                                                RRType::SOA(), dnssec_opt_);
     107         [ +  + ]:         38 :     if (soa_ctx->code != ZoneFinder::SUCCESS) {
     108 [ +  - ][ +  - ]:          4 :         isc_throw(NoSOA, "There's no SOA record in zone " <<
         [ +  - ][ +  - ]
                 [ +  - ]
     109                 :            :             finder.getOrigin().toText());
     110                 :            :     } else {
     111         [ +  - ]:         36 :         authorities_.push_back(soa_ctx->rrset);
     112                 :            :     }
     113                 :         36 : }
     114                 :            : 
     115                 :            : // Note: unless the data source client implementation or the zone content
     116                 :            : // is broken, 'nsec' should be a valid NSEC RR.  Likewise, the call to
     117                 :            : // find() in this method should result in NXDOMAIN and an NSEC RR that proves
     118                 :            : // the non existent of matching wildcard.  If these assumptions aren't met
     119                 :            : // due to a buggy data source implementation or a broken zone, we'll let
     120                 :            : // underlying libdns++ modules throw an exception, which would result in
     121                 :            : // either an SERVFAIL response or just ignoring the query.  We at least prevent
     122                 :            : // a complete crash due to such broken behavior.
     123                 :            : void
     124                 :          9 : Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
     125         [ +  + ]:          9 :     if (nsec->getRdataCount() == 0) {
     126         [ +  - ]:          2 :         isc_throw(BadNSEC, "NSEC for NXDOMAIN is empty");
     127                 :            :     }
     128                 :            : 
     129                 :            :     // Add the NSEC proving NXDOMAIN to the authority section.
     130                 :          8 :     authorities_.push_back(nsec);
     131                 :            : 
     132                 :            :     // Next, identify the best possible wildcard name that would match
     133                 :            :     // the query name.  It's the longer common suffix with the qname
     134                 :            :     // between the owner or the next domain of the NSEC that proves NXDOMAIN,
     135                 :            :     // prefixed by the wildcard label, "*".  For example, for query name
     136                 :            :     // a.b.example.com, if the NXDOMAIN NSEC is
     137                 :            :     // b.example.com. NSEC c.example.com., the longer suffix is b.example.com.,
     138                 :            :     // and the best possible wildcard is *.b.example.com.  If the NXDOMAIN
     139                 :            :     // NSEC is a.example.com. NSEC c.b.example.com., the longer suffix
     140                 :            :     // is the next domain of the NSEC, and we get the same wildcard name.
     141                 :         16 :     const int qlabels = qname_->getLabelCount();
     142                 :          8 :     const int olabels = qname_->compare(nsec->getName()).getCommonLabels();
     143                 :            :     const int nlabels = qname_->compare(
     144                 :          8 :         dynamic_cast<const generic::NSEC&>(nsec->getRdataIterator()->
     145         [ +  - ]:          8 :                                            getCurrent()).
     146 [ +  + ][ +  - ]:          8 :         getNextName()).getCommonLabels();
                 [ +  - ]
     147                 :          7 :     const int common_labels = std::max(olabels, nlabels);
     148                 :          7 :     const Name wildname(Name("*").concatenate(qname_->split(qlabels -
     149 [ +  - ][ +  - ]:         21 :                                                            common_labels)));
                 [ +  - ]
     150                 :            : 
     151                 :            :     // Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
     152                 :            :     // otherwise we shouldn't have got NXDOMAIN for the original query in
     153                 :            :     // the first place).
     154                 :            :     ConstZoneFinderContextPtr fcontext =
     155         [ +  - ]:          7 :         finder.find(wildname, RRType::NSEC(), dnssec_opt_);
     156 [ +  + ][ +  + ]:         12 :     if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
         [ +  + ][ +  + ]
     157         [ +  - ]:          5 :         fcontext->rrset->getRdataCount() == 0) {
     158 [ +  - ][ +  - ]:          6 :         isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
     159                 :            :     }
     160                 :            : 
     161                 :            :     // Add the (no-) wildcard proof.  This can be the same NSEC we already
     162                 :            :     // added, but we'd add it here anyway; duplicate checks will take place
     163                 :            :     // later in a unified manner.
     164         [ +  - ]:          4 :     authorities_.push_back(fcontext->rrset);
     165                 :          4 : }
     166                 :            : 
     167                 :            : uint8_t
     168                 :         15 : Query::addClosestEncloserProof(ZoneFinder& finder, const Name& name,
     169                 :            :                                bool exact_ok, bool add_closest)
     170                 :            : {
     171                 :         28 :     const ZoneFinder::FindNSEC3Result result = finder.findNSEC3(name, true);
     172                 :            : 
     173                 :            :     // Validity check (see the method description).  Note that a completely
     174                 :            :     // broken findNSEC3 implementation could even return NULL RRset in
     175                 :            :     // closest_proof.  We don't explicitly check such case; addRRset() will
     176                 :            :     // throw an exception, and it will be converted to SERVFAIL at the caller.
     177 [ +  + ][ +  + ]:         13 :     if (!exact_ok && !result.next_proof) {
                 [ +  + ]
     178 [ +  - ][ +  - ]:          9 :         isc_throw(BadNSEC3, "Matching NSEC3 found for a non existent name: "
     179                 :            :                   << qname_);
     180                 :            :     }
     181                 :            : 
     182         [ +  + ]:         10 :     if (add_closest) {
     183         [ +  - ]:          8 :         authorities_.push_back(result.closest_proof);
     184                 :            :     }
     185         [ +  + ]:         10 :     if (result.next_proof) {
     186         [ +  - ]:          8 :         authorities_.push_back(result.next_proof);
     187                 :            :     }
     188                 :         10 :     return (result.closest_labels);
     189                 :            : }
     190                 :            : 
     191                 :            : void
     192                 :          6 : Query::addNSEC3ForName(ZoneFinder& finder, const Name& name, bool match) {
     193                 :         12 :     const ZoneFinder::FindNSEC3Result result = finder.findNSEC3(name, false);
     194                 :            : 
     195                 :            :     // See the comment for addClosestEncloserProof().  We don't check a
     196                 :            :     // totally bogus case where closest_proof is NULL here.
     197         [ +  + ]:          6 :     if (match != result.matched) {
     198 [ +  - ][ +  + ]:          6 :         isc_throw(BadNSEC3, "Unexpected "
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
     199                 :            :                   << (result.matched ? "matching" : "covering")
     200                 :            :                   << " NSEC3 found for " << name);
     201                 :            :     }
     202         [ +  - ]:          3 :     authorities_.push_back(result.closest_proof);
     203                 :          3 : }
     204                 :            : 
     205                 :            : void
     206                 :          5 : Query::addNXDOMAINProofByNSEC3(ZoneFinder& finder) {
     207                 :            :     // Firstly get the NSEC3 proves for Closest Encloser Proof
     208                 :            :     // See Section 7.2.1 of RFC 5155.
     209                 :            :     const uint8_t closest_labels =
     210                 :          5 :         addClosestEncloserProof(finder, *qname_, false);
     211                 :            : 
     212                 :            :     // Next, construct the wildcard name at the closest encloser, i.e.,
     213                 :            :     // '*' followed by the closest encloser, and add NSEC3 for it.
     214                 :          2 :     const Name wildname(Name("*").concatenate(
     215 [ +  - ][ +  - ]:          6 :                qname_->split(qname_->getLabelCount() - closest_labels)));
                 [ +  - ]
     216         [ +  + ]:          2 :     addNSEC3ForName(finder, wildname, false);
     217                 :          1 : }
     218                 :            : 
     219                 :            : void
     220                 :          8 : Query::addWildcardProof(ZoneFinder& finder,
     221                 :            :                         const ZoneFinder::Context& db_context)
     222                 :            : {
     223         [ +  + ]:          8 :     if (db_context.isNSECSigned()) {
     224                 :            :         // Case for RFC4035 Section 3.1.3.3.
     225                 :            :         //
     226                 :            :         // The query name shouldn't exist in the zone if there were no wildcard
     227                 :            :         // substitution.  Confirm that by specifying NO_WILDCARD.  It should
     228                 :            :         // result in NXDOMAIN and an NSEC RR that proves it should be returned.
     229                 :            :         ConstZoneFinderContextPtr fcontext =
     230                 :          5 :             finder.find(*qname_, RRType::NSEC(),
     231                 :          5 :                         dnssec_opt_ | ZoneFinder::NO_WILDCARD);
     232 [ +  + ][ +  + ]:          8 :         if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
         [ +  + ][ +  + ]
     233         [ +  - ]:          3 :             fcontext->rrset->getRdataCount() == 0) {
     234 [ +  - ][ +  - ]:          6 :             isc_throw(BadNSEC,
     235                 :            :                       "Unexpected NSEC result for wildcard proof");
     236                 :            :         }
     237         [ +  - ]:          2 :         authorities_.push_back(fcontext->rrset);
     238                 :            :     } else if (db_context.isNSEC3Signed()) {
     239                 :            :         // Case for RFC 5155 Section 7.2.6.
     240                 :            :         //
     241                 :            :         // Note that the closest encloser must be the immediate ancestor
     242                 :            :         // of the matching wildcard, so NSEC3 for its next closer (and only
     243                 :            :         // that NSEC3) is what we are expected to provided per the RFC (if
     244                 :            :         // this assumption isn't met the zone is broken anyway).
     245                 :          3 :         addClosestEncloserProof(finder, *qname_, false, false);
     246                 :            :     }
     247                 :          4 : }
     248                 :            : 
     249                 :            : void
     250                 :          3 : Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
     251                 :            :     // There should be one NSEC RR which was found in the zone to prove
     252                 :            :     // that there is not matched <QNAME,QTYPE> via wildcard expansion.
     253         [ -  + ]:          3 :     if (nsec->getRdataCount() == 0) {
     254         [ #  # ]:          0 :         isc_throw(BadNSEC, "NSEC for WILDCARD_NXRRSET is empty");
     255                 :            :     }
     256                 :            :     
     257                 :            :     ConstZoneFinderContextPtr fcontext =
     258                 :          3 :         finder.find(*qname_, RRType::NSEC(),
     259                 :          3 :                     dnssec_opt_ | ZoneFinder::NO_WILDCARD);
     260 [ +  - ][ +  - ]:          6 :     if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
         [ -  + ][ -  + ]
     261         [ +  - ]:          3 :         fcontext->rrset->getRdataCount() == 0) {
     262 [ #  # ][ #  # ]:          0 :         isc_throw(BadNSEC, "Unexpected result for no match QNAME proof");
     263                 :            :     }
     264                 :            : 
     265         [ +  - ]:          3 :     authorities_.push_back(fcontext->rrset);
     266                 :          3 : }
     267                 :            : 
     268                 :            : void
     269                 :          7 : Query::addDS(ZoneFinder& finder, const Name& dname) {
     270                 :            :     ConstZoneFinderContextPtr ds_context =
     271                 :          7 :         finder.find(dname, RRType::DS(), dnssec_opt_);
     272         [ +  + ]:          7 :     if (ds_context->code == ZoneFinder::SUCCESS) {
     273         [ +  - ]:          2 :         authorities_.push_back(ds_context->rrset);
     274   [ +  +  +  + ]:          9 :     } else if (ds_context->code == ZoneFinder::NXRRSET &&
                 [ +  + ]
     275                 :          4 :                ds_context->isNSECSigned()) {
     276         [ +  - ]:          1 :         addNXRRsetProof(finder, *ds_context);
     277   [ +  +  +  + ]:          7 :     } else if (ds_context->code == ZoneFinder::NXRRSET &&
                 [ +  + ]
     278                 :          3 :                ds_context->isNSEC3Signed()) {
     279                 :            :         // Add no DS proof with NSEC3 as specified in RFC 5155 Section 7.2.7.
     280         [ +  - ]:          2 :         addClosestEncloserProof(finder, dname, true);
     281         [ +  + ]:          2 :     } else if (ds_context->code != ZoneFinder::NXRRSET) {
     282                 :            :         // We know this domain should exist, so the result must be NXRRSET.
     283                 :            :         // If not, the zone is broken, so we'll return SERVFAIL by triggering
     284                 :            :         // an exception.
     285 [ +  - ][ +  - ]:          2 :         isc_throw(BadDS, "Unexpected result for DS lookup for delegation");
     286                 :            :     }
     287                 :          6 : }
     288                 :            : 
     289                 :            : void
     290                 :         18 : Query::addNXRRsetProof(ZoneFinder& finder,
     291                 :            :                        const ZoneFinder::Context& db_context)
     292                 :            : {
     293 [ +  + ][ -  + ]:         28 :     if (db_context.isNSECSigned() && db_context.rrset) {
                 [ +  + ]
     294                 :         10 :         authorities_.push_back(db_context.rrset);
     295         [ +  + ]:         10 :         if (db_context.isWildcard()) {
     296         [ +  - ]:          3 :             addWildcardNXRRSETProof(finder, db_context.rrset);
     297                 :            :         }
     298 [ +  + ][ +  + ]:          8 :     } else if (db_context.isNSEC3Signed() && !db_context.isWildcard()) {
                 [ +  + ]
     299         [ +  + ]:          4 :         if (*qtype_ == RRType::DS()) {
     300                 :            :             // RFC 5155, Section 7.2.4.  Add either NSEC3 for the qname or
     301                 :            :             // closest (provable) encloser proof in case of optout.
     302                 :          2 :             addClosestEncloserProof(finder, *qname_, true);
     303                 :            :         } else {
     304                 :            :             // RFC 5155, Section 7.2.3.  Just add NSEC3 for the qname.
     305                 :          2 :             addNSEC3ForName(finder, *qname_, true);
     306                 :            :         }
     307 [ +  + ][ -  + ]:          4 :     } else if (db_context.isNSEC3Signed() && db_context.isWildcard()) {
                 [ +  + ]
     308                 :            :         // Case for RFC 5155 Section 7.2.5: add closest encloser proof for the
     309                 :            :         // qname, construct the matched wildcard name and add NSEC3 for it.
     310                 :            :         const uint8_t closest_labels =
     311                 :          3 :             addClosestEncloserProof(finder, *qname_, false);
     312                 :          2 :         const Name wname = Name("*").concatenate(
     313 [ +  - ][ +  - ]:          6 :             qname_->split(qname_->getLabelCount() - closest_labels));
                 [ +  - ]
     314         [ +  + ]:          2 :         addNSEC3ForName(finder, wname, true);
     315                 :            :     }
     316                 :         15 : }
     317                 :            : 
     318                 :            : void
     319                 :         31 : Query::addAuthAdditional(ZoneFinder& finder,
     320                 :            :                          vector<ConstRRsetPtr>& additionals)
     321                 :            : {
     322                 :         62 :     const Name& origin = finder.getOrigin();
     323                 :            : 
     324                 :            :     // Fill in authority and addtional sections.
     325                 :         31 :     ConstZoneFinderContextPtr ns_context = finder.find(origin, RRType::NS(),
     326         [ +  - ]:         31 :                                                        dnssec_opt_);
     327                 :            : 
     328                 :            :     // zone origin name should have NS records
     329         [ +  + ]:         31 :     if (ns_context->code != ZoneFinder::SUCCESS) {
     330 [ +  - ][ +  - ]:          2 :         isc_throw(NoApexNS, "There's no apex NS records in zone " <<
         [ +  - ][ +  - ]
                 [ +  - ]
     331                 :            :                   finder.getOrigin().toText());
     332                 :            :     }
     333         [ +  - ]:         30 :     authorities_.push_back(ns_context->rrset);
     334         [ +  - ]:         30 :     ns_context->getAdditional(A_AND_AAAA(), additionals);
     335                 :         30 : }
     336                 :            : 
     337                 :            : namespace {
     338                 :            : // A simple wrapper for DataSourceClient::findZone().  Normally we can simply
     339                 :            : // check the closest zone to the qname, but for type DS query we need to
     340                 :            : // look into the parent zone.  Nevertheless, if there is no "parent" (i.e.,
     341                 :            : // the qname consists of a single label, which also means it's the root name),
     342                 :            : // we should search the deepest zone we have (which should be the root zone;
     343                 :            : // otherwise it's a query error).
     344                 :            : DataSourceClient::FindResult
     345                 :        101 : findZone(const DataSourceClient& client, const Name& qname, RRType qtype) {
     346 [ +  + ][ +  + ]:        101 :     if (qtype != RRType::DS() || qname.getLabelCount() == 1) {
                 [ +  + ]
     347                 :         92 :         return (client.findZone(qname));
     348                 :            :     }
     349         [ +  - ]:         99 :     return (client.findZone(qname.split(1)));
     350                 :            : }
     351                 :            : }
     352                 :            : 
     353                 :            : void
     354                 :        101 : Query::process(datasrc::DataSourceClient& datasrc_client,
     355                 :            :                const isc::dns::Name& qname, const isc::dns::RRType& qtype,
     356                 :            :                isc::dns::Message& response, bool dnssec)
     357                 :            : {
     358                 :            :     // Set up the cleaner object so internal pointers and vectors are
     359                 :            :     // always reset after scope leaves this method
     360                 :            :     QueryCleaner cleaner(*this);
     361                 :            : 
     362                 :            :     // Set up query parameters for the rest of the (internal) methods
     363         [ +  - ]:        101 :     initialize(datasrc_client, qname, qtype, response, dnssec);
     364                 :            : 
     365                 :            :     // Found a zone which is the nearest ancestor to QNAME
     366                 :            :     const DataSourceClient::FindResult result = findZone(*datasrc_client_,
     367         [ +  + ]:        101 :                                                          *qname_, *qtype_);
     368                 :            : 
     369                 :            :     // If we have no matching authoritative zone for the query name, return
     370                 :            :     // REFUSED.  In short, this is to be compatible with BIND 9, but the
     371                 :            :     // background discussion is not that simple.  See the relevant topic
     372                 :            :     // at the BIND 10 developers's ML:
     373                 :            :     // https://lists.isc.org/mailman/htdig/bind10-dev/2010-December/001633.html
     374         [ +  + ]:         99 :     if (result.code != result::SUCCESS &&
     375                 :            :         result.code != result::PARTIALMATCH) {
     376                 :            :         // If we tried to find a "parent zone" for a DS query and failed,
     377                 :            :         // we may still have authority at the child side.  If we do, the query
     378                 :            :         // has to be handled there.
     379 [ +  + ][ +  - ]:          9 :         if (*qtype_ == RRType::DS() && qname_->getLabelCount() > 1 &&
         [ +  + ][ +  + ]
     380         [ +  - ]:          3 :             processDSAtChild()) {
     381                 :            :             return;
     382                 :            :         }
     383         [ +  - ]:          4 :         response_->setHeaderFlag(Message::HEADERFLAG_AA, false);
     384         [ +  - ]:          4 :         response_->setRcode(Rcode::REFUSED());
     385                 :            :         return;
     386                 :            :     }
     387                 :         93 :     ZoneFinder& zfinder = *result.zone_finder;
     388                 :            : 
     389                 :            :     // We have authority for a zone that contain the query name (possibly
     390                 :            :     // indirectly via delegation).  Look into the zone.
     391         [ +  - ]:         93 :     response_->setHeaderFlag(Message::HEADERFLAG_AA);
     392 [ +  - ][ +  - ]:         93 :     response_->setRcode(Rcode::NOERROR());
     393                 :            :     boost::function0<ZoneFinderContextPtr> find;
     394                 :        186 :     const bool qtype_is_any = (*qtype_ == RRType::ANY());
     395         [ +  + ]:         93 :     if (qtype_is_any) {
     396                 :            :         find = boost::bind(&ZoneFinder::findAll, &zfinder, *qname_,
     397 [ +  - ][ +  - ]:         12 :                            boost::ref(answers_), dnssec_opt_);
     398                 :            :     } else {
     399                 :            :         find = boost::bind(&ZoneFinder::find, &zfinder, *qname_, *qtype_,
     400 [ +  - ][ +  - ]:        174 :                            dnssec_opt_);
     401                 :            :     }
     402                 :            :     ZoneFinderContextPtr db_context(find());
     403   [ +  +  +  +  :         91 :     switch (db_context->code) {
                +  +  - ]
     404                 :            :         case ZoneFinder::DNAME: {
     405                 :            :             // First, put the dname into the answer
     406         [ +  - ]:          4 :             answers_.push_back(db_context->rrset);
     407                 :            :             /*
     408                 :            :              * Empty DNAME should never get in, as it is impossible to
     409                 :            :              * create one in master file.
     410                 :            :              *
     411                 :            :              * FIXME: Other way to prevent this should be done
     412                 :            :              */
     413 [ +  - ][ -  + ]:          4 :             assert(db_context->rrset->getRdataCount() > 0);
     414                 :            :             // Get the data of DNAME
     415                 :            :             const rdata::generic::DNAME& dname(
     416                 :            :                 dynamic_cast<const rdata::generic::DNAME&>(
     417 [ +  - ][ +  - ]:          4 :                 db_context->rrset->getRdataIterator()->getCurrent()));
                 [ +  - ]
     418                 :            :             // The yet unmatched prefix dname
     419                 :          4 :             const Name prefix(qname_->split(0, qname_->getLabelCount() -
     420 [ +  - ][ +  - ]:          8 :                 db_context->rrset->getName().getLabelCount()));
     421                 :            :             // If we put it together, will it be too long?
     422                 :            :             // (The prefix contains trailing ., which will be removed
     423 [ +  - ][ +  + ]:          8 :             if (prefix.getLength() - Name::ROOT_NAME().getLength() +
     424         [ +  - ]:          4 :                 dname.getDname().getLength() > Name::MAX_WIRE) {
     425                 :            :                 /*
     426                 :            :                  * In case the synthesized name is too long, section 4.1
     427                 :            :                  * of RFC 2672 mandates we return YXDOMAIN.
     428                 :            :                  */
     429         [ +  - ]:          1 :                 response_->setRcode(Rcode::YXDOMAIN());
     430                 :            :                 break;
     431                 :            :             }
     432                 :            :             // The new CNAME we are creating (it will be unsigned even
     433                 :            :             // with DNSSEC, the DNAME is signed and it can be validated
     434                 :            :             // by that)
     435                 :          3 :             RRsetPtr cname(new RRset(*qname_, db_context->rrset->getClass(),
     436 [ +  - ][ +  - ]:          6 :                 RRType::CNAME(), db_context->rrset->getTTL()));
         [ +  - ][ +  - ]
                 [ +  - ]
     437                 :            :             // Construct the new target by replacing the end
     438                 :          3 :             cname->addRdata(rdata::generic::CNAME(qname_->split(0,
     439                 :          3 :                 qname_->getLabelCount() -
     440         [ +  - ]:          6 :                 db_context->rrset->getName().getLabelCount()).
     441 [ +  - ][ +  - ]:          9 :                 concatenate(dname.getDname())));
         [ +  - ][ +  - ]
                 [ +  - ]
     442         [ +  - ]:          3 :             answers_.push_back(cname);
     443                 :            :             break;
     444                 :            :         }
     445                 :            :         case ZoneFinder::CNAME:
     446                 :            :             /*
     447                 :            :              * We don't do chaining yet. Therefore handling a CNAME is
     448                 :            :              * mostly the same as handling SUCCESS, but we didn't get
     449                 :            :              * what we expected. It means no exceptions in ANY or NS
     450                 :            :              * on the origin (though CNAME in origin is probably
     451                 :            :              * forbidden anyway).
     452                 :            :              *
     453                 :            :              * So, just put it there.
     454                 :            :              */
     455         [ +  - ]:          6 :             answers_.push_back(db_context->rrset);
     456                 :            : 
     457                 :            :             // If the answer is a result of wildcard substitution,
     458                 :            :             // add a proof that there's no closer name.
     459 [ +  + ][ -  + ]:          6 :             if (dnssec_ && db_context->isWildcard()) {
                 [ +  + ]
     460         [ +  - ]:          2 :                 addWildcardProof(*result.zone_finder, *db_context);
     461                 :            :             }
     462                 :            :             break;
     463                 :            :         case ZoneFinder::SUCCESS:
     464                 :            :             // If query type is ANY, the rrs have already been added
     465         [ +  + ]:         33 :             if (!qtype_is_any) {
     466         [ +  - ]:         30 :                 answers_.push_back(db_context->rrset);
     467                 :            :             }
     468                 :            : 
     469                 :            :             // Retrieve additional records for the answer
     470         [ +  - ]:         33 :             db_context->getAdditional(A_AND_AAAA(), additionals_);
     471                 :            : 
     472                 :            :             // If apex NS records haven't been provided in the answer
     473                 :            :             // section, insert apex NS records into the authority section
     474                 :            :             // and AAAA/A RRS of each of the NS RDATA into the additional
     475                 :            :             // section.
     476                 :            :             // Checking the findZone() is a lightweight check to see if
     477                 :            :             // qname is the zone origin.
     478   [ +  +  +  -  :         41 :             if (result.code != result::SUCCESS ||
           +  + ][ +  + ]
                 [ +  + ]
     479                 :          4 :                 db_context->code != ZoneFinder::SUCCESS ||
     480                 :          4 :                 (*qtype_ != RRType::NS() && !qtype_is_any))
     481                 :            :             {
     482         [ +  + ]:         31 :                 addAuthAdditional(*result.zone_finder, additionals_);
     483                 :            :             }
     484                 :            : 
     485                 :            :             // If the answer is a result of wildcard substitution,
     486                 :            :             // add a proof that there's no closer name.
     487 [ +  + ][ +  + ]:         32 :             if (dnssec_ && db_context->isWildcard()) {
                 [ +  + ]
     488         [ +  + ]:          6 :                 addWildcardProof(*result.zone_finder, *db_context);
     489                 :            :             }
     490                 :            :             break;
     491                 :            :         case ZoneFinder::DELEGATION:
     492                 :            :             // If a DS query resulted in delegation, we also need to check
     493                 :            :             // if we are an authority of the child, too.  If so, we need to
     494                 :            :             // complete the process in the child as specified in Section
     495                 :            :             // 2.2.1.2. of RFC3658.
     496 [ +  + ][ +  - ]:         11 :             if (*qtype_ == RRType::DS() && processDSAtChild()) {
         [ +  + ][ +  + ]
     497                 :            :                 return;
     498                 :            :             }
     499                 :            : 
     500         [ +  - ]:         10 :             response_->setHeaderFlag(Message::HEADERFLAG_AA, false);
     501         [ +  - ]:         10 :             authorities_.push_back(db_context->rrset);
     502                 :            :             // Retrieve additional records for the name servers
     503         [ +  - ]:         10 :             db_context->getAdditional(A_AND_AAAA(), additionals_);
     504                 :            : 
     505                 :            :             // If DNSSEC is requested, see whether there is a DS
     506                 :            :             // record for this delegation.
     507         [ +  + ]:         10 :             if (dnssec_) {
     508 [ +  - ][ +  + ]:          7 :                 addDS(*result.zone_finder, db_context->rrset->getName());
     509                 :            :             }
     510                 :            :             break;
     511                 :            :         case ZoneFinder::NXDOMAIN:
     512         [ +  - ]:         20 :             response_->setRcode(Rcode::NXDOMAIN());
     513         [ +  + ]:         20 :             addSOA(*result.zone_finder);
     514         [ +  + ]:         16 :             if (dnssec_) {
     515 [ +  + ][ -  + ]:         23 :                 if (db_context->isNSECSigned() && db_context->rrset) {
                 [ +  + ]
     516         [ +  + ]:         18 :                     addNXDOMAINProofByNSEC(zfinder, db_context->rrset);
     517         [ +  - ]:          5 :                 } else if (db_context->isNSEC3Signed()) {
     518         [ +  + ]:          5 :                     addNXDOMAINProofByNSEC3(zfinder);
     519                 :            :                 }
     520                 :            :             }
     521                 :            :             break;
     522                 :            :         case ZoneFinder::NXRRSET:
     523         [ +  - ]:         17 :             addSOA(*result.zone_finder);
     524         [ +  + ]:         17 :             if (dnssec_) {
     525         [ +  + ]:         15 :                 addNXRRsetProof(zfinder, *db_context);
     526                 :            :             }
     527                 :            :             break;
     528                 :            :         default:
     529                 :            :             // This is basically a bug of the data source implementation,
     530                 :            :             // but could also happen in the middle of development where
     531                 :            :             // we try to add a new result code.
     532 [ #  # ][ #  # ]:          0 :             isc_throw(isc::NotImplemented, "Unknown result code");
                 [ #  # ]
     533                 :            :             break;
     534                 :            :     }
     535                 :            : 
     536                 :            :     response_creator_.create(*response_, answers_, authorities_, additionals_,
     537         [ +  - ]:         68 :                              dnssec_);
     538                 :            : }
     539                 :            : 
     540                 :            : void
     541                 :        101 : Query::initialize(datasrc::DataSourceClient& datasrc_client,
     542                 :            :                   const isc::dns::Name& qname, const isc::dns::RRType& qtype,
     543                 :            :                   isc::dns::Message& response, bool dnssec)
     544                 :            : {
     545                 :        101 :     datasrc_client_ = &datasrc_client;
     546                 :        101 :     qname_ = &qname;
     547                 :        101 :     qtype_ = &qtype;
     548                 :        101 :     response_ = &response;
     549                 :        101 :     dnssec_ = dnssec;
     550                 :            :     dnssec_opt_ = (dnssec ? isc::datasrc::ZoneFinder::FIND_DNSSEC :
     551         [ +  + ]:        101 :                    isc::datasrc::ZoneFinder::FIND_DEFAULT);
     552                 :        101 : }
     553                 :            : 
     554                 :            : void
     555                 :        101 : Query::reset() {
     556                 :        101 :     datasrc_client_ = NULL;
     557                 :        101 :     qname_ = NULL;
     558                 :        101 :     qtype_ = NULL;
     559                 :        101 :     response_ = NULL;
     560                 :        101 :     answers_.clear();
     561                 :        101 :     authorities_.clear();
     562                 :        101 :     additionals_.clear();
     563                 :        101 :     response_creator_.clear();
     564                 :        101 : }
     565                 :            : 
     566                 :            : bool
     567                 :          5 : Query::processDSAtChild() {
     568                 :            :     const DataSourceClient::FindResult zresult =
     569                 :          5 :         datasrc_client_->findZone(*qname_);
     570                 :            : 
     571         [ +  + ]:          5 :     if (zresult.code != result::SUCCESS) {
     572                 :            :         return (false);
     573                 :            :     }
     574                 :            : 
     575                 :            :     // We are receiving a DS query at the child side of the owner name,
     576                 :            :     // where the DS isn't supposed to belong.  We should return a "no data"
     577                 :            :     // response as described in Section 3.1.4.1 of RFC4035 and Section
     578                 :            :     // 2.2.1.1 of RFC 3658.  find(DS) should result in NXRRSET, in which
     579                 :            :     // case (and if DNSSEC is required) we also add the proof for that,
     580                 :            :     // but even if find() returns an unexpected result, we don't bother.
     581                 :            :     // The important point in this case is to return SOA so that the resolver
     582                 :            :     // that happens to contact us can hunt for the appropriate parent zone
     583                 :            :     // by seeing the SOA.
     584         [ +  - ]:          3 :     response_->setHeaderFlag(Message::HEADERFLAG_AA);
     585 [ +  - ][ +  - ]:          3 :     response_->setRcode(Rcode::NOERROR());
     586         [ +  - ]:          3 :     addSOA(*zresult.zone_finder);
     587                 :            :     ConstZoneFinderContextPtr ds_context =
     588         [ +  - ]:          3 :         zresult.zone_finder->find(*qname_, RRType::DS(), dnssec_opt_);
     589         [ +  + ]:          3 :     if (ds_context->code == ZoneFinder::NXRRSET) {
     590         [ +  - ]:          2 :         if (dnssec_) {
     591         [ +  - ]:          2 :             addNXRRsetProof(*zresult.zone_finder, *ds_context);
     592                 :            :         }
     593                 :            :     }
     594                 :            : 
     595                 :            :     response_creator_.create(*response_, answers_, authorities_, additionals_,
     596         [ +  - ]:          3 :                              dnssec_);
     597                 :          3 :     return (true);
     598                 :            : }
     599                 :            : 
     600                 :            : }
     601                 :          1 : }

Generated by: LCOV version 1.9