LCOV - code coverage report
Current view: top level - resolve - response_classifier.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 60 60 100.0 %
Date: 2012-05-15 Functions: 3 3 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 109 154 70.8 %

           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                 :            : // $Id$
      16                 :            : 
      17                 :            : #include <cstddef>
      18                 :            : #include <vector>
      19                 :            : 
      20                 :            : #include <resolve/response_classifier.h>
      21                 :            : #include <dns/name.h>
      22                 :            : #include <dns/opcode.h>
      23                 :            : #include <dns/rcode.h>
      24                 :            : #include <dns/rrset.h>
      25                 :            : 
      26                 :            : using namespace isc::dns;
      27                 :            : using namespace std;
      28                 :            : 
      29                 :            : namespace isc {
      30                 :            : namespace resolve {
      31                 :            : 
      32                 :            : // Classify the response in the "message" object.
      33                 :            : 
      34                 :         84 : ResponseClassifier::Category ResponseClassifier::classify(
      35                 :            :     const Question& question, const Message& message, 
      36                 :            :     Name& cname_target, unsigned int& cname_count, bool tcignore
      37                 :            :     )
      38                 :            : {
      39                 :            :     // Check header bits
      40         [ +  + ]:         84 :     if (!message.getHeaderFlag(Message::HEADERFLAG_QR)) {
      41                 :            :         return (NOTRESPONSE);   // Query-response bit not set in the response
      42                 :            :     }
      43                 :            : 
      44                 :            :     // We only recognise responses to queries here
      45         [ +  + ]:         83 :     if (message.getOpcode() != Opcode::QUERY()) {
      46                 :            :         return (OPCODE);
      47                 :            :     }
      48                 :            : 
      49                 :            :     // Apparently have a response.  There must be a single question in it...
      50         [ +  - ]:         68 :     const vector<QuestionPtr> msgquestion(message.beginQuestion(),
      51 [ +  - ][ +  - ]:        220 :             message.endQuestion());
      52         [ +  + ]:         68 :     if (msgquestion.size() != 1) {
      53                 :            :         return (NOTONEQUEST); // Not one question in response question section
      54                 :            :     }
      55                 :            : 
      56                 :            :     // ... and the question should be equal to the question given.
      57                 :            :     // XXX: This means that "question" may not be the question sent by the
      58                 :            :     // client.  In the case of a CNAME response, the qname of subsequent
      59                 :            :     // questions needs to be altered.
      60         [ +  + ]:         65 :     if (question != *(msgquestion[0])) {
      61                 :            :         return (MISMATQUEST);
      62                 :            :     }
      63                 :            : 
      64                 :            :     // Check for Rcode-related errors.
      65         [ +  - ]:         64 :     const Rcode& rcode = message.getRcode();
      66         [ +  + ]:         64 :     if (rcode != Rcode::NOERROR()) {
      67         [ +  + ]:         31 :         if (rcode == Rcode::NXDOMAIN()) {
      68                 :            : 
      69                 :            :             // No such domain.  According to RFC2308, the domain referred to by
      70                 :            :             // the QNAME does not exist, although there may be a CNAME in the
      71                 :            :             // answer section and there may be an SOA and/or NS RRs in the
      72                 :            :             // authority section (ignoring any DNSSEC RRs for now).
      73                 :            :             //
      74                 :            :             // Note the "may".  There may not be anything.  Also, note that if
      75                 :            :             // there is a CNAME in the answer section, the authoritative server
      76                 :            :             // has verified that the name given in the CNAME's RDATA field does
      77                 :            :             // not exist. And that if a CNAME is returned in the answer, then
      78                 :            :             // the QNAME of the RRs in the authority section will refer to the
      79                 :            :             // authority for the CNAME's RDATA and not to the original question.
      80                 :            :             //
      81                 :            :             // Without doing further classification, it is sufficient to say
      82                 :            :             // that if an NXDOMAIN is received, there was no translation of the
      83                 :            :             // QNAME available.
      84                 :            :             return (NXDOMAIN);  // Received NXDOMAIN from parent.
      85                 :            : 
      86                 :            :         } else {
      87                 :            : 
      88                 :            :             // Not NXDOMAIN but not NOERROR either.  Must be an RCODE-related
      89                 :            :             // error.
      90                 :            :             return (RCODE);
      91                 :            :         }
      92                 :            :     }
      93                 :            : 
      94                 :            :     // All seems OK and we can start looking at the content.  However, one
      95                 :            :     // more header check remains - was the response truncated?  If so, we'll
      96                 :            :     // probably want to re-query over TCP.  However, in some circumstances we
      97                 :            :     // might want to go with what we have.  So give the caller the option of
      98                 :            :     // ignoring the TC bit.
      99 [ +  - ][ +  + ]:         33 :     if (message.getHeaderFlag(Message::HEADERFLAG_TC) && (!tcignore)) {
         [ +  + ][ +  + ]
     100                 :            :         return (TRUNCATED);
     101                 :            :     }
     102                 :            : 
     103                 :            :     // By the time we get here, we're assured that the packet format is correct.
     104                 :            :     // We now need to decide as to whether it is an answer, a CNAME, or a
     105                 :            :     // referral.  For this, we need to inspect the contents of the answer
     106                 :            :     // and authority sections.
     107                 :            :     const vector<RRsetPtr> answer(
     108         [ +  - ]:         31 :             message.beginSection(Message::SECTION_ANSWER),
     109         [ +  - ]:         31 :             message.endSection(Message::SECTION_ANSWER)
     110 [ +  - ][ +  - ]:         62 :             );
         [ +  - ][ +  - ]
     111                 :            :     const vector<RRsetPtr> authority(
     112         [ +  - ]:         31 :             message.beginSection(Message::SECTION_AUTHORITY),
     113         [ +  - ]:         31 :             message.endSection(Message::SECTION_AUTHORITY)
     114 [ +  - ][ +  - ]:         62 :             );
         [ +  - ][ +  - ]
     115                 :            : 
     116                 :            :     // If there is nothing in the answer section, it is a referral - unless
     117                 :            :     // there is no NS in the authority section
     118         [ +  + ]:         31 :     if (answer.empty()) {
     119         [ +  + ]:         13 :         if (authority.empty()) {
     120                 :            :             return (EMPTY);
     121                 :            :         }
     122         [ +  + ]:          5 :         for (vector<RRsetPtr>::size_type i = 0; i < authority.size(); ++i) {
     123 [ +  - ][ +  + ]:          4 :             if (authority[i]->getType() == RRType::NS()) {
     124                 :            :                 return (REFERRAL);
     125                 :            :             }
     126                 :            :         }
     127                 :            :         return (NXRRSET);
     128                 :            :     }
     129                 :            : 
     130                 :            :     // Look at two cases - one RRset in the answer and multiple RRsets in
     131                 :            :     // the answer.
     132         [ +  + ]:         18 :     if (answer.size() == 1) {
     133                 :            : 
     134                 :            :         // Does the name and class of the answer match that of the question?
     135 [ +  - ][ +  + ]:         26 :         if ((answer[0]->getName() == question.getName()) &&
         [ +  + ][ +  + ]
     136         [ +  - ]:          8 :             (answer[0]->getClass() == question.getClass())) {
     137                 :            : 
     138                 :            :             // It does.  How about the type of the response?  The response
     139                 :            :             // is an answer if the type matches that of the question, or if the
     140                 :            :             // question was for type ANY.  It is a CNAME reply if the answer
     141                 :            :             // type is CNAME.  And it is an error for anything else.
     142         [ +  - ]:         11 :             if ((answer[0]->getType() == question.getType()) ||
           [ +  +  +  + ]
                 [ +  + ]
     143                 :          4 :                 (question.getType() == RRType::ANY())) {
     144                 :            :                 return (ANSWER);
     145 [ +  - ][ +  + ]:          3 :             } else if (answer[0]->getType() == RRType::CNAME()) {
     146         [ +  - ]:          2 :                 RdataIteratorPtr it = answer[0]->getRdataIterator();
     147 [ +  - ][ +  - ]:          2 :                 cname_target = Name(it->getCurrent().toText());
         [ +  - ][ +  - ]
     148                 :          2 :                 ++cname_count;
     149                 :          2 :                 return (CNAME);
     150                 :            :             } else {
     151                 :            :                 return (INVTYPE);
     152                 :            :             }
     153                 :            :         }
     154                 :            :         else {
     155                 :            : 
     156                 :            :             // Either the name and/or class of the reply don't match that of
     157                 :            :             // the question.
     158                 :            :             return (INVNAMCLASS);
     159                 :            :         }
     160                 :            :     }
     161                 :            : 
     162                 :            :     // There are multiple RRsets in the answer. They should all have the same
     163                 :            :     // QCLASS, else there is some error in the response.
     164         [ +  + ]:         22 :     for (vector<RRsetPtr>::size_type i = 1; i < answer.size(); ++i) {
     165 [ +  - ][ +  - ]:         14 :         if (answer[0]->getClass() != answer[i]->getClass()) {
                 [ +  + ]
     166                 :            :             return (MULTICLASS);
     167                 :            :         }
     168                 :            :     }
     169                 :            : 
     170                 :            :     // If the request type was ANY and they all have the same QNAME, we have
     171                 :            :     // an answer.  But if they don't have the same QNAME, we must have an error;
     172                 :            :     // the only way we could get different QNAMES in an answer is if one were a
     173                 :            :     // CNAME - in which case there should no other record types at that QNAME.
     174         [ +  + ]:          8 :     if (question.getType() == RRType::ANY()) {
     175                 :            :         bool all_same = true;
     176 [ +  + ][ -  + ]:          5 :         for (vector<RRsetPtr>::size_type i = 1; (i < answer.size()) && all_same;
                 [ +  + ]
     177                 :            :              ++i) {
     178 [ +  - ][ +  - ]:          3 :             all_same = (answer[0]->getName() == answer[i]->getName());
     179                 :            :         }
     180         [ +  + ]:          2 :         if (all_same) {
     181                 :            :             return (ANSWER);
     182                 :            :         } else {
     183                 :            :             return (EXTRADATA);
     184                 :            :         }
     185                 :            :     }
     186                 :            : 
     187                 :            :     // Multiple RRs in the answer, and not all the same QNAME.  This
     188                 :            :     // is either an answer, a CNAME (in either case, there could be multiple
     189                 :            :     // CNAMEs in the chain) or an error.
     190                 :            :     //
     191                 :            :     // So we need to follow the CNAME chain to resolve this.  For this to work:
     192                 :            :     //
     193                 :            :     // a) There must be one RR that matches the name, class and type of
     194                 :            :     //    the question, and this is a CNAME.
     195                 :            :     // b) The CNAME chain is followed until the end of the chain does not
     196                 :            :     //    exist (answer is a CNAME) or it is not of type CNAME (ANSWER).
     197                 :            :     //
     198                 :            :     // In the latter case, if there are additional RRs, it must be an error.
     199                 :            : 
     200         [ +  - ]:          6 :     vector<RRsetPtr> ansrrset(answer);
     201                 :         12 :     vector<int> present(ansrrset.size(), 1);
     202                 :          6 :     return cnameChase(question.getName(), question.getType(),
     203                 :            :         cname_target, cname_count,
     204         [ +  - ]:          6 :         ansrrset, present, ansrrset.size());
     205                 :            : }
     206                 :            : 
     207                 :            : // Search the CNAME chain.
     208                 :         13 : ResponseClassifier::Category ResponseClassifier::cnameChase(
     209                 :            :     const Name& qname, const RRType& qtype,
     210                 :            :     Name& cname_target, unsigned int& cname_count,
     211                 :            :     vector<RRsetPtr>& ansrrset, vector<int>& present, size_t size)
     212                 :            : {
     213                 :            :     // Search through the vector of RRset pointers until we find one with the
     214                 :            :     // right QNAME.
     215         [ +  - ]:         36 :     for (vector<RRsetPtr>::size_type i = 0; i < ansrrset.size(); ++i) {
     216         [ +  + ]:         23 :         if (present[i]) {
     217                 :            : 
     218                 :            :             // This entry has not been logically removed, so look at it.
     219         [ +  - ]:         26 :             if (ansrrset[i]->getName() == qname) {
     220                 :            : 
     221                 :            :                 // QNAME match.  If this RRset is a CNAME, remove it from
     222                 :            :                 // further consideration.  If nothing is left, the end of the
     223                 :            :                 // chain is a CNAME so this is a CNAME.  Otherwise replace
     224                 :            :                 // the name with the RDATA of the CNAME and call ourself
     225                 :            :                 // recursively.
     226         [ +  + ]:         13 :                 if (ansrrset[i]->getType() == RRType::CNAME()) {
     227                 :            : 
     228                 :            :                     // Don't consider it in the next iteration (although we
     229                 :            :                     // can still access it for now).
     230                 :          8 :                     present[i] = 0;
     231                 :          8 :                     --size;
     232         [ +  + ]:          8 :                     if (size == 0) {
     233                 :          1 :                         RdataIteratorPtr it = ansrrset[i]->getRdataIterator();
     234 [ +  - ][ +  - ]:          1 :                         cname_target = Name(it->getCurrent().toText());
         [ +  - ][ +  - ]
     235                 :          1 :                         return (CNAME);
     236                 :            :                     } else {
     237         [ +  - ]:          7 :                         if (ansrrset[i]->getRdataCount() != 1) {
     238                 :            : 
     239                 :            :                             // Multiple RDATA for a CNAME?  This is invalid.
     240                 :            : 
     241                 :            :                             return (NOTSINGLE);
     242                 :            :                         }
     243                 :          7 :                         RdataIteratorPtr it = ansrrset[i]->getRdataIterator();
     244 [ +  - ][ +  - ]:         14 :                         Name newname(it->getCurrent().toText());
                 [ +  - ]
     245                 :            : 
     246                 :            :                         // Increase CNAME count, and continue
     247                 :            :                         return cnameChase(newname, qtype, cname_target,
     248         [ +  - ]:          7 :                             ++cname_count, ansrrset, present, size);
     249                 :            :                     }
     250                 :            : 
     251                 :            :                 } else {
     252                 :            : 
     253                 :            :                     // We've got here because the element is not a CNAME.  If
     254                 :            :                     // this is the last element and the type is the one we are
     255                 :            :                     // after, we've found the answer, or it is an error.  If
     256                 :            :                     // there is more than one RRset left in the list we are
     257                 :            :                     // searching, we have extra data in the answer.
     258         [ +  + ]:          5 :                     if (ansrrset[i]->getType() == qtype) {
     259         [ +  + ]:          4 :                         if (size == 1) {
     260                 :            :                             return (ANSWERCNAME);
     261                 :            :                         } else {
     262                 :          3 :                             return (EXTRADATA);
     263                 :            :                         }
     264                 :            :                     }
     265                 :            :                     return (INVTYPE);
     266                 :            :                 }
     267                 :            :             }
     268                 :            :         }
     269                 :            :     }
     270                 :            : 
     271                 :            :     // We get here if we've dropped off the end of the list without finding the
     272                 :            :     // QNAME we are looking for.  This means that the CNAME chain has ended
     273                 :            :     // but there are additional RRsets in the data.
     274                 :            : 
     275                 :            :     return (EXTRADATA);
     276                 :            : }
     277                 :            : 
     278                 :            : } // namespace resolve
     279                 :          2 : } // namespace isc

Generated by: LCOV version 1.9