LCOV - code coverage report
Current view: top level - dns - masterload.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 73 73 100.0 %
Date: 2012-05-15 Functions: 4 4 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 156 230 67.8 %

           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 <istream>
      16                 :            : #include <fstream>
      17                 :            : #include <sstream>
      18                 :            : #include <string>
      19                 :            : #include <cctype>
      20                 :            : 
      21                 :            : #include <boost/scoped_ptr.hpp>
      22                 :            : 
      23                 :            : #include <exceptions/exceptions.h>
      24                 :            : 
      25                 :            : #include <dns/masterload.h>
      26                 :            : #include <dns/name.h>
      27                 :            : #include <dns/rdata.h>
      28                 :            : #include <dns/rdataclass.h>
      29                 :            : #include <dns/rrclass.h>
      30                 :            : #include <dns/rrset.h>
      31                 :            : #include <dns/rrttl.h>
      32                 :            : #include <dns/rrtype.h>
      33                 :            : 
      34                 :            : using namespace std;
      35                 :            : using namespace boost;
      36                 :            : using namespace isc::dns::rdata;
      37                 :            : 
      38                 :            : namespace isc {
      39                 :            : namespace dns {
      40                 :            : namespace {
      41                 :            : // A helper function that strips off any comment or whitespace at the end of
      42                 :            : // an RR.
      43                 :            : // This is an incomplete implementation, and cannot handle all such comments;
      44                 :            : // it's considered a short term workaround to deal with some real world
      45                 :            : // cases.
      46                 :            : string
      47                 :         36 : stripLine(string& s, const Exception& ex) {
      48                 :            :     // Find any ';' in the text data, and locate the position of the last
      49                 :            :     // occurrence.  Note that unless/until we support empty RDATA it
      50                 :            :     // shouldn't be placed at the beginning of the data.
      51                 :         36 :     const size_t pos_semicolon = s.rfind(';');
      52         [ +  + ]:         36 :     if (pos_semicolon == 0) {
      53                 :          2 :         throw ex;
      54         [ +  + ]:         35 :     } else if (pos_semicolon != string::npos) {
      55                 :         32 :         s.resize(pos_semicolon);
      56                 :            :     }
      57                 :            :     // Remove any trailing whitespace return the resulting text.
      58                 :         35 :     s.resize(s.find_last_not_of(" \t") + 1);
      59                 :         35 :     return (s);
      60                 :            : }
      61                 :            : }
      62                 :            : 
      63                 :            : void
      64                 :         61 : masterLoad(const char* const filename, const Name& origin,
      65                 :            :            const RRClass& zone_class, MasterLoadCallback callback)
      66                 :            : {
      67 [ +  + ][ -  + ]:         61 :     if ((filename == NULL) || (*filename == '\0')) {
      68 [ +  - ][ +  - ]:          2 :         isc_throw(MasterLoadError, "Name of master file must not be null");
      69                 :            :     }
      70                 :            : 
      71                 :        120 :     ifstream ifs;
      72         [ +  - ]:         60 :     ifs.open(filename, ios_base::in);
      73         [ +  + ]:         60 :     if (ifs.fail()) {
      74 [ +  - ][ +  - ]:          6 :         isc_throw(MasterLoadError, "Failed to open master file: " << filename);
         [ +  - ][ +  - ]
      75                 :            :     }
      76         [ +  + ]:         57 :     masterLoad(ifs, origin, zone_class, callback);
      77         [ +  - ]:         52 :     ifs.close();
      78                 :         52 : }
      79                 :            : 
      80                 :            : void
      81                 :        590 : masterLoad(istream& input, const Name& origin, const RRClass& zone_class,
      82                 :            :            MasterLoadCallback callback)
      83                 :            : {
      84                 :            :     RRsetPtr rrset;
      85                 :            :     ConstRdataPtr prev_rdata;   // placeholder for special case of RRSIGs
      86                 :        590 :     string line;
      87                 :            :     unsigned int line_count = 1;
      88                 :            : 
      89         [ +  + ]:      11781 :     do {
      90         [ +  - ]:      11799 :         getline(input, line);
      91 [ +  + ][ +  + ]:      11799 :         if (input.bad() || (input.fail() && !input.eof())) {
         [ +  - ][ +  + ]
      92 [ +  - ][ +  - ]:          2 :             isc_throw(MasterLoadError, "Unexpectedly failed to read a line");
                 [ +  - ]
      93                 :            :         }
      94                 :            : 
      95                 :            :         // blank/comment lines should be simply skipped.
      96 [ +  + ][ +  + ]:      22771 :         if (line.empty() || line[0] == ';') {
                 [ +  + ]
      97                 :       1370 :             continue;
      98                 :            :         }
      99                 :            : 
     100                 :            :         // The line shouldn't have leading space (which means omitting the
     101                 :            :         // owner name).
     102         [ +  + ]:      10428 :         if (isspace(line[0])) {
     103 [ +  - ][ +  - ]:          6 :             isc_throw(MasterLoadError, "Leading space at line " << line_count);
                 [ +  - ]
     104                 :            :         }
     105                 :            : 
     106                 :            :         // Parse a single RR
     107   [ +  -  +  - ]:      20852 :         istringstream iss(line);
     108                 :      10426 :         string owner_txt, ttl_txt, rrclass_txt, rrtype_txt;
     109                 :      20852 :         stringbuf rdatabuf;
     110 [ +  - ][ +  - ]:      10426 :         iss >> owner_txt >> ttl_txt >> rrclass_txt >> rrtype_txt >> &rdatabuf;
         [ +  - ][ +  - ]
                 [ +  - ]
     111 [ +  - ][ +  + ]:      10426 :         if (iss.bad() || iss.fail()) {
                 [ +  + ]
     112 [ +  - ][ +  - ]:          3 :             isc_throw(MasterLoadError, "Parse failure for a valid RR at line "
                 [ +  - ]
     113                 :            :                       << line_count);
     114                 :            :         }
     115                 :            : 
     116                 :            :         // This simple version doesn't support relative owner names with a
     117                 :            :         // separate origin.
     118 [ +  - ][ +  - ]:      10425 :         if (owner_txt.empty() || *(owner_txt.end() - 1) != '.') {
         [ +  + ][ +  + ]
     119 [ +  - ][ +  - ]:          3 :             isc_throw(MasterLoadError, "Owner name is not absolute at line "
                 [ +  - ]
     120                 :            :                       << line_count);
     121                 :            :         }
     122                 :            : 
     123                 :            :         // XXX: this part is a bit tricky (and less efficient).  We are going
     124                 :            :         // to validate the text for the RR parameters, and throw an exception
     125                 :            :         // if any of them is invalid by converting an underlying exception
     126                 :            :         // to MasterLoadError.  To do that, we need to define the corresponding
     127                 :            :         // variables used for RRset construction outside the try-catch block,
     128                 :            :         // but we don't like to use a temporary variable with a meaningless
     129                 :            :         // initial value.  So we define pointers outside the try block
     130                 :            :         // and allocate/initialize the actual objects within the block.
     131                 :            :         // To make it exception safe we use Boost.scoped_ptr.
     132                 :      10424 :         scoped_ptr<const Name> owner;
     133                 :            :         scoped_ptr<const RRTTL> ttl;
     134                 :            :         scoped_ptr<const RRClass> rrclass;
     135                 :            :         scoped_ptr<const RRType> rrtype;
     136                 :            :         ConstRdataPtr rdata;
     137                 :            :         try {
     138 [ +  - ][ +  + ]:      10424 :             owner.reset(new Name(owner_txt));
     139 [ +  - ][ +  + ]:      10423 :             ttl.reset(new RRTTL(ttl_txt));
     140 [ +  - ][ +  + ]:      10422 :             rrclass.reset(new RRClass(rrclass_txt));
     141 [ +  - ][ +  + ]:      10421 :             rrtype.reset(new RRType(rrtype_txt));
     142         [ +  - ]:      20840 :             string rdtext = rdatabuf.str();
     143                 :            :             try {
     144 [ +  + ][ +  - ]:      10420 :                 rdata = createRdata(*rrtype, *rrclass, rdtext);
     145         [ -  + ]:         72 :             } catch (const Exception& ex) {
     146                 :            :                 // If the parse for the RDATA fails, check if it has comments
     147                 :            :                 // or whitespace at the end, and if so, retry the conversion
     148                 :            :                 // after stripping off the comment or whitespace
     149 [ +  + ][ +  + ]:         70 :                 rdata = createRdata(*rrtype, *rrclass, stripLine(rdtext, ex));
                 [ -  + ]
     150                 :            :             }
     151         [ -  + ]:         12 :         } catch (const Exception& ex) {
     152 [ -  + ][ -  + ]:         18 :             isc_throw(MasterLoadError, "Invalid RR text at line " << line_count
         [ -  + ][ -  + ]
                 [ -  + ]
     153                 :            :                       << ": " << ex.what());
     154                 :            :         }
     155                 :            : 
     156                 :            :         // Origin related validation:
     157                 :            :         //  - reject out-of-zone data
     158                 :            :         //  - reject SOA whose owner is not at the top of zone
     159         [ +  - ]:      10418 :         const NameComparisonResult cmp_result = owner->compare(origin);
     160 [ +  + ][ +  + ]:      10418 :         if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
                 [ +  + ]
     161                 :       9454 :             cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
     162 [ +  - ][ +  - ]:         12 :             isc_throw(MasterLoadError, "Out-of-zone data at line "
                 [ +  - ]
     163                 :            :                       << line_count);
     164                 :            :         }
     165 [ +  + ][ +  + ]:      10414 :         if (*rrtype == RRType::SOA() &&
                 [ +  + ]
     166                 :        194 :             cmp_result.getRelation() != NameComparisonResult::EQUAL) {
     167 [ +  - ][ +  - ]:          6 :             isc_throw(MasterLoadError, "SOA not at top of zone at line "
                 [ +  - ]
     168                 :            :                       << line_count);
     169                 :            :         }
     170                 :            : 
     171                 :            :         // Reject RR class mismatching
     172         [ +  + ]:      10412 :         if (*rrclass != zone_class) {
     173 [ +  - ][ +  - ]:          3 :             isc_throw(MasterLoadError, "RR class (" << rrclass_txt
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
     174                 :            :                       << ") does not match the zone class (" << zone_class
     175                 :            :                       << ") at line " << line_count);
     176                 :            :         }
     177                 :            : 
     178                 :            :         // Everything is okay.  Now create/update RRset with the new RR.
     179                 :            :         // If this is the first RR or the RR type/name is new, we are seeing
     180                 :            :         // a new RRset.
     181                 :      10411 :         bool new_rrset = false;
     182 [ +  + ][ +  - ]:      13481 :         if (!rrset || rrset->getType() != *rrtype ||
         [ +  + ][ +  + ]
                 [ +  + ]
     183         [ +  - ]:       3070 :             rrset->getName() != *owner) {
     184                 :            :             new_rrset = true;
     185 [ +  - ][ +  + ]:       1067 :         } else if (rrset->getType() == RRType::RRSIG()) {
     186                 :            :             // We are seeing two consecutive RRSIGs of the same name.
     187                 :            :             // They can be combined iff they have the same type covered.
     188 [ +  - ][ +  - ]:          8 :             if (dynamic_cast<const generic::RRSIG&>(*rdata).typeCovered() !=
                 [ +  + ]
     189 [ +  - ][ +  - ]:          8 :                 dynamic_cast<const generic::RRSIG&>(*prev_rdata).typeCovered())
     190                 :            :             {
     191                 :          6 :                 new_rrset = true;
     192                 :            :             }
     193                 :            :         }
     194         [ +  + ]:      10411 :         if (new_rrset) {
     195                 :            :             // Commit the previous RRset, if any.
     196         [ +  + ]:       9350 :             if (rrset) {
     197         [ +  - ]:       8777 :                 callback(rrset);
     198                 :            :             }
     199 [ +  - ][ +  - ]:      18700 :             rrset = RRsetPtr(new RRset(*owner, *rrclass, *rrtype, *ttl));
         [ +  - ][ +  - ]
     200                 :            :         }
     201         [ +  - ]:      20822 :         rrset->addRdata(rdata);
     202                 :            :         prev_rdata = rdata;
     203                 :      11781 :     } while (++line_count, !input.eof());
     204                 :            : 
     205                 :            :     // Commit the last RRset, if any.
     206         [ +  + ]:        572 :     if (rrset) {
     207         [ +  + ]:        571 :         callback(rrset);
     208                 :            :     }
     209                 :        571 : }
     210                 :            : } // namespace dns
     211                 :        137 : } // namespace isc

Generated by: LCOV version 1.9