LCOV - code coverage report
Current view: top level - datasrc - database.cc (source / functions) Hit Total Coverage
Test: report.info Lines: 597 605 98.7 %
Date: 2012-05-15 Functions: 57 57 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 1051 1786 58.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                 :            : #include <string>
      16                 :            : #include <utility>
      17                 :            : #include <vector>
      18                 :            : 
      19                 :            : #include <datasrc/database.h>
      20                 :            : #include <datasrc/data_source.h>
      21                 :            : #include <datasrc/iterator.h>
      22                 :            : 
      23                 :            : #include <exceptions/exceptions.h>
      24                 :            : #include <dns/name.h>
      25                 :            : #include <dns/rrclass.h>
      26                 :            : #include <dns/rrttl.h>
      27                 :            : #include <dns/rrset.h>
      28                 :            : #include <dns/rdata.h>
      29                 :            : #include <dns/rdataclass.h>
      30                 :            : #include <dns/nsec3hash.h>
      31                 :            : 
      32                 :            : #include <datasrc/data_source.h>
      33                 :            : #include <datasrc/logger.h>
      34                 :            : 
      35                 :            : #include <boost/foreach.hpp>
      36                 :            : #include <boost/scoped_ptr.hpp>
      37                 :            : 
      38                 :            : using namespace isc::dns;
      39                 :            : using namespace std;
      40                 :            : using namespace isc::dns::rdata;
      41                 :            : using namespace boost;
      42                 :            : 
      43                 :            : namespace isc {
      44                 :            : namespace datasrc {
      45                 :            : 
      46                 :        247 : DatabaseClient::DatabaseClient(RRClass rrclass,
      47                 :            :                                boost::shared_ptr<DatabaseAccessor>
      48                 :            :                                accessor) :
      49                 :        495 :     rrclass_(rrclass), accessor_(accessor)
      50                 :            : {
      51         [ +  + ]:        247 :     if (!accessor_) {
      52 [ +  - ][ +  - ]:          2 :         isc_throw(isc::InvalidParameter,
      53                 :            :                   "No database provided to DatabaseClient");
      54                 :            :     }
      55                 :        246 : }
      56                 :            : 
      57                 :            : DataSourceClient::FindResult
      58                 :        383 : DatabaseClient::findZone(const Name& name) const {
      59         [ +  - ]:        383 :     std::pair<bool, int> zone(accessor_->getZone(name.toText()));
      60                 :            :     // Try exact first
      61         [ +  + ]:        383 :     if (zone.first) {
      62                 :            :         return (FindResult(result::SUCCESS,
      63                 :            :                            ZoneFinderPtr(new Finder(accessor_,
      64 [ +  - ][ +  - ]:        278 :                                                     zone.second, name))));
                 [ +  - ]
      65                 :            :     }
      66                 :            :     // Then super domains
      67                 :            :     // Start from 1, as 0 is covered above
      68         [ +  + ]:        280 :     for (size_t i(1); i < name.getLabelCount(); ++i) {
      69                 :        482 :         isc::dns::Name superdomain(name.split(i));
      70 [ +  - ][ +  - ]:        241 :         zone = accessor_->getZone(superdomain.toText());
      71         [ +  + ]:        241 :         if (zone.first) {
      72                 :            :             return (FindResult(result::PARTIALMATCH,
      73                 :            :                                ZoneFinderPtr(new Finder(accessor_,
      74                 :            :                                                         zone.second,
      75 [ +  - ][ +  - ]:         66 :                                                         superdomain))));
                 [ +  - ]
      76                 :            :         }
      77                 :            :     }
      78                 :            :     // No, really nothing
      79                 :            :     return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
      80                 :            : }
      81                 :            : 
      82                 :        528 : DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseAccessor> accessor,
      83                 :            :                                int zone_id, const isc::dns::Name& origin) :
      84                 :            :     accessor_(accessor),
      85                 :            :     zone_id_(zone_id),
      86         [ +  - ]:        528 :     origin_(origin)
      87                 :        528 : { }
      88                 :            : 
      89                 :            : namespace {
      90                 :            : // Adds the given Rdata to the given RRset
      91                 :            : // If the rrset is an empty pointer, a new one is
      92                 :            : // created with the given name, class, type and ttl
      93                 :            : // The type is checked if the rrset exists, but the
      94                 :            : // name is not.
      95                 :            : //
      96                 :            : // Then adds the given rdata to the set
      97                 :            : //
      98                 :            : // Raises a DataSourceError if the type does not
      99                 :            : // match, or if the given rdata string does not
     100                 :            : // parse correctly for the given type and class
     101                 :            : //
     102                 :            : // The DatabaseAccessor is passed to print the
     103                 :            : // database name in the log message if the TTL is
     104                 :            : // modified
     105                 :       2331 : void addOrCreate(isc::dns::RRsetPtr& rrset,
     106                 :            :                     const isc::dns::Name& name,
     107                 :            :                     const isc::dns::RRClass& cls,
     108                 :            :                     const isc::dns::RRType& type,
     109                 :            :                     const isc::dns::RRTTL& ttl,
     110                 :            :                     const std::string& rdata_str,
     111                 :            :                     const DatabaseAccessor& db
     112                 :            :                 )
     113                 :            : {
     114         [ +  + ]:       2331 :     if (!rrset) {
     115         [ +  - ]:       1453 :         rrset.reset(new isc::dns::RRset(name, cls, type, ttl));
     116                 :            :     } else {
     117                 :            :         // This is a check to make sure find() is not messing things up
     118         [ -  + ]:        878 :         assert(type == rrset->getType());
     119         [ +  + ]:        878 :         if (ttl != rrset->getTTL()) {
     120         [ +  + ]:         58 :             if (ttl < rrset->getTTL()) {
     121                 :          4 :                 rrset->setTTL(ttl);
     122                 :            :             }
     123                 :         58 :             logger.warn(DATASRC_DATABASE_FIND_TTL_MISMATCH)
     124 [ +  - ][ +  - ]:         58 :                 .arg(db.getDBName()).arg(name).arg(cls)
                 [ +  - ]
     125 [ +  - ][ +  - ]:        116 :                 .arg(type).arg(rrset->getTTL());
     126                 :            :         }
     127                 :            :     }
     128                 :            :     try {
     129 [ +  + ][ +  - ]:       4658 :         rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str));
     130         [ -  + ]:          8 :     } catch (const isc::dns::rdata::InvalidRdataText& ivrt) {
     131                 :            :         // at this point, rrset may have been initialised for no reason,
     132                 :            :         // and won't be used. But the caller would drop the shared_ptr
     133                 :            :         // on such an error anyway, so we don't care.
     134 [ -  + ][ -  + ]:          8 :         isc_throw(DataSourceError,
         [ -  + ][ -  + ]
         [ -  + ][ -  + ]
         [ -  + ][ -  + ]
     135                 :            :                     "bad rdata in database for " << name << " "
     136                 :            :                     << type << ": " << ivrt.what());
     137                 :            :     }
     138                 :       2327 : }
     139                 :            : 
     140                 :            : // This class keeps a short-lived store of RRSIG records encountered
     141                 :            : // during a call to find(). If the backend happens to return signatures
     142                 :            : // before the actual data, we might not know which signatures we will need
     143                 :            : // So if they may be relevant, we store the in this class.
     144                 :            : //
     145                 :            : // (If this class seems useful in other places, we might want to move
     146                 :            : // it to util. That would also provide an opportunity to add unit tests)
     147                 :       4342 : class RRsigStore {
     148                 :            : public:
     149                 :            :     // Adds the given signature Rdata to the store
     150                 :            :     // The signature rdata MUST be of the RRSIG rdata type
     151                 :            :     // (the caller must make sure of this).
     152                 :            :     // NOTE: if we move this class to a public namespace,
     153                 :            :     // we should add a type_covered argument, so as not
     154                 :            :     // to have to do this cast here.
     155                 :            :     void addSig(isc::dns::rdata::RdataPtr sig_rdata) {
     156                 :            :         const isc::dns::RRType& type_covered =
     157                 :            :             static_cast<isc::dns::rdata::generic::RRSIG*>(
     158         [ +  - ]:       2110 :                 sig_rdata.get())->typeCovered();
     159                 :       4220 :         sigs[type_covered].push_back(sig_rdata);
     160                 :            :     }
     161                 :            : 
     162                 :            :     // If the store contains signatures for the type of the given
     163                 :            :     // rrset, they are appended to it.
     164                 :            :     void appendSignatures(isc::dns::RRsetPtr& rrset) const {
     165                 :            :         std::map<isc::dns::RRType,
     166                 :            :                  std::vector<isc::dns::rdata::RdataPtr> >::const_iterator
     167         [ +  - ]:       1431 :             found = sigs.find(rrset->getType());
     168         [ +  + ]:       1431 :         if (found != sigs.end()) {
     169 [ +  + ][ +  - ]:       3636 :             BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, found->second) {
         [ +  - ][ +  + ]
                 [ +  + ]
     170         [ +  - ]:        728 :                 rrset->addRRsig(sig);
     171                 :            :             }
     172                 :            :         }
     173                 :            :     }
     174                 :            : 
     175                 :            : private:
     176                 :            :     std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs;
     177                 :            : };
     178                 :            : }
     179                 :            : 
     180                 :            : DatabaseClient::Finder::FoundRRsets
     181                 :       2171 : DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
     182                 :            :                                   bool check_ns, const string* construct_name,
     183                 :            :                                   bool any,
     184                 :            :                                   DatabaseAccessor::IteratorContextPtr context)
     185                 :            : {
     186                 :            :     RRsigStore sig_store;
     187                 :       2171 :     bool records_found = false;
     188                 :            :     std::map<RRType, RRsetPtr> result;
     189                 :            : 
     190                 :            :     // Request the context in case we didn't get one
     191         [ +  + ]:       2171 :     if (!context) {
     192 [ +  + ][ +  - ]:       2155 :         context = accessor_->getRecords(name, zone_id_);
     193                 :            :     }
     194                 :            :     // It must not return NULL, that's a bug of the implementation
     195         [ +  - ]:       2167 :     if (!context) {
     196 [ #  # ][ #  # ]:          0 :         isc_throw(isc::Unexpected, "Iterator context null at " + name);
                 [ #  # ]
     197                 :            :     }
     198                 :            : 
     199         [ +  + ]:      23837 :     std::string columns[DatabaseAccessor::COLUMN_COUNT];
     200         [ +  + ]:       2167 :     if (construct_name == NULL) {
     201                 :       1899 :         construct_name = &name;
     202                 :            :     }
     203                 :            : 
     204         [ +  - ]:       4334 :     const Name construct_name_object(*construct_name);
     205                 :            : 
     206                 :            :     bool seen_cname(false);
     207                 :            :     bool seen_ds(false);
     208                 :            :     bool seen_other(false);
     209                 :            :     bool seen_ns(false);
     210                 :            : 
     211 [ +  + ][ +  + ]:       9353 :     while (context->getNext(columns)) {
     212                 :            :         // The domain is not empty
     213                 :       7198 :         records_found = true;
     214                 :            : 
     215                 :            :         try {
     216         [ +  + ]:       7198 :             const RRType cur_type(columns[DatabaseAccessor::TYPE_COLUMN]);
     217                 :            : 
     218         [ +  + ]:       7196 :             if (cur_type == RRType::RRSIG()) {
     219                 :            :                 // If we get signatures before we get the actual data, we
     220                 :            :                 // can't know which ones to keep and which to drop...
     221                 :            :                 // So we keep a separate store of any signature that may be
     222                 :            :                 // relevant and add them to the final RRset when we are
     223                 :            :                 // done.
     224                 :            :                 // A possible optimization here is to not store them for
     225                 :            :                 // types we are certain we don't need
     226                 :       2112 :                 sig_store.addSig(rdata::createRdata(cur_type, getClass(),
     227 [ +  - ][ +  + ]:       2112 :                      columns[DatabaseAccessor::RDATA_COLUMN]));
     228                 :            :             }
     229                 :            : 
     230 [ +  + ][ +  + ]:       7194 :             if (types.find(cur_type) != types.end() || any) {
                 [ +  + ]
     231                 :            :                 // This type is requested, so put it into result
     232         [ +  + ]:       2335 :                 const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
     233                 :            :                 // Ths sigtype column was an optimization for finding the
     234                 :            :                 // relevant RRSIG RRs for a lookup. Currently this column is
     235                 :            :                 // not used in this revised datasource implementation. We
     236                 :            :                 // should either start using it again, or remove it from use
     237                 :            :                 // completely (i.e. also remove it from the schema and the
     238                 :            :                 // backend implementation).
     239                 :            :                 // Note that because we don't use it now, we also won't notice
     240                 :            :                 // it if the value is wrong (i.e. if the sigtype column
     241                 :            :                 // contains an rrtype that is different from the actual value
     242                 :            :                 // of the 'type covered' field in the RRSIG Rdata).
     243                 :            :                 //cur_sigtype(columns[SIGTYPE_COLUMN]);
     244         [ +  - ]:       2331 :                 addOrCreate(result[cur_type], construct_name_object,
     245                 :       2331 :                             getClass(), cur_type, cur_ttl,
     246                 :            :                             columns[DatabaseAccessor::RDATA_COLUMN],
     247 [ +  - ][ +  + ]:       4662 :                             *accessor_);
     248                 :            :             }
     249                 :            : 
     250         [ +  + ]:       7186 :             if (cur_type == RRType::CNAME()) {
     251                 :            :                 seen_cname = true;
     252         [ +  + ]:       7162 :             } else if (cur_type == RRType::NS()) {
     253                 :            :                 seen_ns = true;
     254         [ +  + ]:       5453 :             } else if (cur_type == RRType::DS()) {
     255                 :            :                 seen_ds = true;
     256   [ +  +  +  +  :      12051 :             } else if (cur_type != RRType::RRSIG() &&
           +  + ][ +  + ]
     257                 :       3317 :                        cur_type != RRType::NSEC3() &&
     258                 :       3307 :                        cur_type != RRType::NSEC()) {
     259                 :            :                 // NSEC and RRSIG can coexist with anything, otherwise
     260                 :            :                 // we've seen something that can't live together with potential
     261                 :            :                 // CNAME or NS
     262                 :            :                 //
     263                 :            :                 // NSEC3 lives in separate namespace from everything, therefore
     264                 :            :                 // we just ignore it here for these checks as well.
     265                 :       9329 :                 seen_other = true;
     266                 :            :             }
     267                 :          4 :         } catch (const InvalidRRType&) {
     268 [ -  + ][ -  + ]:          4 :             isc_throw(DataSourceError, "Invalid RRType in database for " <<
         [ -  + ][ -  + ]
         [ -  + ][ -  + ]
     269                 :            :                       name << ": " << columns[DatabaseAccessor::
     270                 :            :                       TYPE_COLUMN]);
     271                 :          8 :         } catch (const InvalidRRTTL&) {
     272 [ -  + ][ -  + ]:          8 :             isc_throw(DataSourceError, "Invalid TTL in database for " <<
         [ -  + ][ -  + ]
         [ -  + ][ -  + ]
     273                 :            :                       name << ": " << columns[DatabaseAccessor::
     274                 :            :                       TTL_COLUMN]);
     275   [ +  +  +  + ]:         14 :         } catch (const rdata::InvalidRdataText&) {
     276 [ -  + ][ -  + ]:          4 :             isc_throw(DataSourceError, "Invalid rdata in database for " <<
         [ -  + ][ -  + ]
         [ -  + ][ -  + ]
     277                 :            :                       name << ": " << columns[DatabaseAccessor::
     278                 :            :                       RDATA_COLUMN]);
     279                 :            :         }
     280                 :            :     }
     281 [ +  + ][ +  + ]:       2152 :     if (seen_cname && (seen_other || seen_ns || seen_ds)) {
         [ +  - ][ -  + ]
     282 [ +  - ][ +  - ]:          8 :         isc_throw(DataSourceError, "CNAME shares domain " << name <<
         [ +  - ][ +  - ]
                 [ +  - ]
     283                 :            :                   " with something else");
     284                 :            :     }
     285 [ +  + ][ +  + ]:       2148 :     if (check_ns && seen_ns && seen_other) {
                 [ +  + ]
     286 [ +  - ][ +  - ]:          8 :         isc_throw(DataSourceError, "NS shares domain " << name <<
         [ +  - ][ +  - ]
                 [ +  - ]
     287                 :            :                   " with something else");
     288                 :            :     }
     289                 :            :     // Add signatures to all found RRsets
     290         [ +  + ]:       3575 :     for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
     291                 :       3575 :          i != result.end(); ++ i) {
     292                 :       1431 :         sig_store.appendSignatures(i->second);
     293                 :            :     }
     294 [ +  + ][ +  + ]:       2144 :     if (records_found && any) {
     295 [ +  - ][ +  - ]:         17 :         result[RRType::ANY()] = RRsetPtr();
     296                 :            :         // These will be sitting on the other RRsets.
     297                 :         17 :         result.erase(RRType::RRSIG());
     298                 :            :     }
     299 [ +  + ][ +  + ]:      15146 :     return (FoundRRsets(records_found, result));
     300                 :            : }
     301                 :            : 
     302                 :            : bool
     303                 :        487 : DatabaseClient::Finder::hasSubdomains(const std::string& name) {
     304                 :            :     // Request the context
     305                 :            :     DatabaseAccessor::IteratorContextPtr
     306                 :        487 :         context(accessor_->getRecords(name, zone_id_, true));
     307                 :            :     // It must not return NULL, that's a bug of the implementation
     308         [ +  - ]:        487 :     if (!context) {
     309 [ #  # ][ #  # ]:          0 :         isc_throw(isc::Unexpected, "Iterator context null at " + name);
                 [ #  # ]
     310                 :            :     }
     311                 :            : 
     312         [ +  + ]:       5357 :     std::string columns[DatabaseAccessor::COLUMN_COUNT];
     313 [ +  - ][ +  + ]:       3409 :     return (context->getNext(columns));
                 [ #  # ]
     314                 :            : }
     315                 :            : 
     316                 :            : // Some manipulation with RRType sets
     317                 :            : namespace {
     318                 :            : 
     319                 :            : // Bunch of functions to construct specific sets of RRTypes we will
     320                 :            : // ask from it.
     321                 :            : typedef std::set<RRType> WantedTypes;
     322                 :            : 
     323                 :            : const WantedTypes&
     324                 :         16 : NSEC3_TYPES() {
     325                 :            :     static bool initialized(false);
     326 [ +  + ][ +  - ]:         16 :     static WantedTypes result;
     327                 :            : 
     328         [ +  + ]:         16 :     if (!initialized) {
     329                 :          1 :         result.insert(RRType::NSEC3());
     330                 :          1 :         initialized = true;
     331                 :            :     }
     332                 :         16 :     return (result);
     333                 :            : }
     334                 :            : 
     335                 :            : const WantedTypes&
     336                 :        115 : NSEC3PARAM_TYPES() {
     337                 :            :     static bool initialized(false);
     338 [ +  + ][ +  - ]:        115 :     static WantedTypes result;
     339                 :            : 
     340         [ +  + ]:        115 :     if (!initialized) {
     341                 :          1 :         result.insert(RRType::NSEC3PARAM());
     342                 :          1 :         initialized = true;
     343                 :            :     }
     344                 :        115 :     return (result);
     345                 :            : }
     346                 :            : 
     347                 :            : const WantedTypes&
     348                 :        101 : NSEC_TYPES() {
     349                 :            :     static bool initialized(false);
     350 [ +  + ][ +  - ]:        101 :     static WantedTypes result;
     351                 :            : 
     352         [ +  + ]:        101 :     if (!initialized) {
     353                 :          1 :         result.insert(RRType::NSEC());
     354                 :          1 :         initialized = true;
     355                 :            :     }
     356                 :        101 :     return (result);
     357                 :            : }
     358                 :            : 
     359                 :            : const WantedTypes&
     360                 :        984 : DELEGATION_TYPES() {
     361                 :            :     static bool initialized(false);
     362 [ +  + ][ +  - ]:        984 :     static WantedTypes result;
     363                 :            : 
     364         [ +  + ]:        984 :     if (!initialized) {
     365                 :          4 :         result.insert(RRType::DNAME());
     366                 :          4 :         result.insert(RRType::NS());
     367                 :          4 :         initialized = true;
     368                 :            :     }
     369                 :        984 :     return (result);
     370                 :            : }
     371                 :            : 
     372                 :            : const WantedTypes&
     373                 :        873 : FINAL_TYPES() {
     374                 :            :     static bool initialized(false);
     375 [ +  + ][ +  - ]:        873 :     static WantedTypes result;
     376                 :            : 
     377         [ +  + ]:        873 :     if (!initialized) {
     378                 :          7 :         result.insert(RRType::CNAME());
     379                 :          7 :         result.insert(RRType::NS());
     380                 :          7 :         result.insert(RRType::NSEC());
     381                 :          7 :         initialized = true;
     382                 :            :     }
     383                 :        873 :     return (result);
     384                 :            : }
     385                 :            : }
     386                 :            : 
     387                 :            : ZoneFinderContextPtr
     388                 :         64 : DatabaseClient::Finder::findAll(const isc::dns::Name& name,
     389                 :            :                                 std::vector<isc::dns::ConstRRsetPtr>& target,
     390                 :            :                                 const FindOptions options)
     391                 :            : {
     392                 :            :     return (ZoneFinderContextPtr(new Context(*this, options,
     393                 :         64 :                                              findInternal(name, RRType::ANY(),
     394                 :            :                                                           &target, options),
     395 [ +  - ][ +  - ]:        172 :                                              target)));
     396                 :            : }
     397                 :            : 
     398                 :            : ZoneFinderContextPtr
     399                 :        689 : DatabaseClient::Finder::find(const isc::dns::Name& name,
     400                 :            :                              const isc::dns::RRType& type,
     401                 :            :                              const FindOptions options)
     402                 :            : {
     403         [ +  + ]:        689 :     if (type == RRType::ANY()) {
     404         [ +  - ]:          4 :         isc_throw(isc::Unexpected, "Use findAll to answer ANY");
     405                 :            :     }
     406                 :            :     return (ZoneFinderContextPtr(new Context(*this, options,
     407                 :            :                                              findInternal(name, type, NULL,
     408 [ +  - ][ +  - ]:       1973 :                                                           options))));
     409                 :            : }
     410                 :            : 
     411                 :            : DatabaseClient::Finder::DelegationSearchResult
     412                 :        728 : DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
     413                 :            :                                             const FindOptions options)
     414                 :            : {
     415                 :            :     // Result of search
     416                 :            :     isc::dns::ConstRRsetPtr result_rrset;
     417                 :        728 :     ZoneFinder::Result result_status = SUCCESS;
     418                 :            : 
     419                 :            :     // Are we searching for glue?
     420                 :        728 :     const bool glue_ok = ((options & FIND_GLUE_OK) != 0);
     421                 :            : 
     422                 :            :     // This next declaration is an optimisation.  When we search the database
     423                 :            :     // for glue records, we generally ignore delegations. (This allows for
     424                 :            :     // the case where e.g. the delegation to zone example.com refers to
     425                 :            :     // nameservers within the zone, e.g. ns1.example.com.  When conducting the
     426                 :            :     // search for ns1.example.com, we have to search past the NS records at
     427                 :            :     // example.com.)
     428                 :            :     //
     429                 :            :     // The one case where this is forbidden is when we search past the zone
     430                 :            :     // cut but the match we find for the glue is a wildcard match.  In that
     431                 :            :     // case, we return the delegation instead (see RFC 1034, section 4.3.3).
     432                 :            :     // To save a new search, we record the location of the delegation cut when
     433                 :            :     // we encounter it here.
     434                 :            :     isc::dns::ConstRRsetPtr first_ns;
     435                 :            : 
     436                 :            :     // We want to search from the apex down.  We are given the full domain
     437                 :            :     // name so we have to do some manipulation to ensure that when we start
     438                 :            :     // checking superdomains, we start from the the domain name of the zone
     439                 :            :     // (e.g. if the name is b.a.example.com. and we are in the example.com.
     440                 :            :     // zone, we check example.com., a.example.com. and b.a.example.com.  We
     441                 :            :     // don't need to check com. or .).
     442                 :            :     //
     443                 :            :     // Set the number of labels in the origin (i.e. apex of the zone) and in
     444                 :            :     // the last known non-empty domain (which, at this point, is the origin).
     445         [ +  - ]:        728 :     const size_t origin_label_count = getOrigin().getLabelCount();
     446                 :        728 :     size_t last_known = origin_label_count;
     447                 :            : 
     448                 :            :     // Set how many labels we remove to get origin: this is the number of
     449                 :            :     // labels we have to process in our search.
     450                 :        728 :     const size_t remove_labels = name.getLabelCount() - origin_label_count;
     451                 :            : 
     452                 :            :     // Go through all superdomains from the origin down searching for nodes
     453                 :            :     // that indicate a delegation (.e. NS or DNAME).
     454         [ +  + ]:       1671 :     for (int i = remove_labels; i > 0; --i) {
     455         [ +  - ]:       1968 :         const Name superdomain(name.split(i));
     456                 :            : 
     457                 :            :         // Note if this is the origin. (We don't count NS records at the origin
     458                 :            :         // as a delegation so this controls whether NS RRs are included in
     459                 :            :         // the results of some searches.)
     460                 :        984 :         const bool not_origin = (i != remove_labels);
     461                 :            : 
     462                 :            :         // Look if there's NS or DNAME at this point of the tree, but ignore
     463                 :            :         // the NS RRs at the apex of the zone.
     464                 :        984 :         const FoundRRsets found = getRRsets(superdomain.toText(),
     465 [ +  - ][ +  - ]:       1968 :                                             DELEGATION_TYPES(), not_origin);
         [ +  - ][ +  - ]
                 [ +  - ]
     466         [ +  + ]:        984 :         if (found.first) {
     467                 :            :             // This node contains either NS or DNAME RRs so it does exist.
     468                 :        640 :             const FoundIterator nsi(found.second.find(RRType::NS()));
     469                 :        640 :             const FoundIterator dni(found.second.find(RRType::DNAME()));
     470                 :            : 
     471                 :            :             // An optimisation.  We know that there is an exact match for
     472                 :            :             // something at this point in the tree so remember it.  If we have
     473                 :            :             // to do a wildcard search, as we search upwards through the tree
     474                 :            :             // we don't need to pass this point, which is an exact match for
     475                 :            :             // the domain name.
     476                 :        640 :             last_known = superdomain.getLabelCount();
     477                 :            : 
     478 [ +  + ][ +  - ]:        640 :             if (glue_ok && !first_ns && not_origin &&
         [ +  + ][ +  + ]
                 [ +  + ]
     479                 :         26 :                     nsi != found.second.end()) {
     480                 :            :                 // If we are searching for glue ("glue OK" mode), store the
     481                 :            :                 // highest NS record that we find that is not the apex.  This
     482                 :            :                 // is another optimisation for later, where we need the
     483                 :            :                 // information if the domain we are looking for matches through
     484                 :            :                 // a wildcard.
     485         [ +  - ]:         20 :                 first_ns = nsi->second;
     486                 :            : 
     487 [ +  + ][ +  + ]:        620 :             } else if (!glue_ok && not_origin && nsi != found.second.end()) {
         [ +  + ][ +  + ]
     488                 :            :                 // Not searching for glue and we have found an NS RRset that is
     489                 :            :                 // not at the apex.  We have found a delegation - return that
     490                 :            :                 // fact, there is no need to search further down the tree.
     491 [ +  - ][ +  - ]:         48 :                 LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                 [ +  - ]
     492                 :            :                           DATASRC_DATABASE_FOUND_DELEGATION).
     493 [ +  - ][ +  - ]:         24 :                     arg(accessor_->getDBName()).arg(superdomain);
         [ +  - ][ +  - ]
     494         [ +  - ]:         24 :                 result_rrset = nsi->second;
     495                 :            :                 result_status = DELEGATION;
     496                 :            :                 break;
     497                 :            : 
     498         [ +  + ]:        596 :             } else if (dni != found.second.end()) {
     499                 :            :                 // We have found a DNAME so again stop searching down the tree
     500                 :            :                 // and return the information.
     501 [ +  - ][ +  - ]:         34 :                 LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                 [ +  - ]
     502                 :            :                           DATASRC_DATABASE_FOUND_DNAME).
     503 [ +  - ][ +  - ]:         17 :                     arg(accessor_->getDBName()).arg(superdomain);
         [ +  - ][ +  - ]
     504         [ +  - ]:         17 :                 result_rrset = dni->second;
     505                 :         17 :                 result_status = DNAME;
     506 [ +  - ][ +  + ]:         17 :                 if (result_rrset->getRdataCount() != 1) {
     507 [ +  - ][ +  - ]:          6 :                     isc_throw(DataSourceError, "DNAME at " << superdomain <<
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
     508                 :            :                               " has " << result_rrset->getRdataCount() <<
     509                 :            :                               " rdata, 1 expected");
     510                 :            :                 }
     511                 :            :                 break;
     512                 :            :             }
     513                 :            :         }
     514                 :            :     }
     515                 :            :     return (DelegationSearchResult(result_status, result_rrset, first_ns,
     516                 :        726 :                                    last_known));
     517                 :            : }
     518                 :            : 
     519                 :            : // This method is called when we have not found an exact match and when we
     520                 :            : // know that the name is not an empty non-terminal.  So the only way that
     521                 :            : // the name can match something in the zone is through a wildcard match.
     522                 :            : //
     523                 :            : // During an earlier stage in the search for this name, we made a record of
     524                 :            : // the lowest superdomain for which we know an RR exists. (Note the "we
     525                 :            : // know" qualification - there may be lower superdomains (ones with more
     526                 :            : // labels) that hold an RR, but as we weren't searching for them, we don't
     527                 :            : // know about them.)
     528                 :            : //
     529                 :            : // In the search for a wildcard match (which starts at the given domain
     530                 :            : // name and goes up the tree to successive superdomains), this is the level
     531                 :            : // at which we can stop - there can't be a wildcard at or beyond that
     532                 :            : // point.
     533                 :            : //
     534                 :            : // At each level that can stop the search, we should consider several cases:
     535                 :            : //
     536                 :            : // - If we found a wildcard match for a glue record below a
     537                 :            : // delegation point, we don't return the match,
     538                 :            : // instead we return the delegation.  (Note that if we didn't
     539                 :            : // a wildcard match at all, we would return NXDOMAIN, not the
     540                 :            : // the delegation.)
     541                 :            : //
     542                 :            : // - If we found a wildcard match and we are sure that the match
     543                 :            : // is not an empty non-terminal, return the result taking into account CNAME,
     544                 :            : // on a zone cut, and NXRRSET.
     545                 :            : // (E.g. searching for a match
     546                 :            : // for c.b.a.example.com, we found that b.a.example.com did
     547                 :            : // not exist but that *.a.example.com. did. Checking
     548                 :            : // b.a.example.com revealed no subdomains, so we can use the
     549                 :            : // wilcard match we found.)
     550                 :            : //
     551                 :            : // - If we found a more specified match, the wildcard search
     552                 :            : // is canceled, resulting in NXDOMAIN.  (E.g. searching for a match
     553                 :            : // for c.b.a.example.com, we found that b.a.example.com did
     554                 :            : // not exist but that *.a.example.com. did. Checking
     555                 :            : // b.a.example.com found subdomains.  So b.example.com is
     556                 :            : // an empty non-terminal and so should not be returned in
     557                 :            : // the wildcard matching process.  In other words,
     558                 :            : // b.example.com does exist in the DNS space, it just doesn't
     559                 :            : // have any RRs associated with it.)
     560                 :            : //
     561                 :            : // - If we found a match, but it is an empty non-terminal asterisk (E.g.#
     562                 :            : // subdomain.*.example.com.  is present, but there is nothing at
     563                 :            : // *.example.com.),  return an NXRRSET indication;
     564                 :            : // the wildcard exists in the DNS space, there's just nothing
     565                 :            : // associated with it.  If DNSSEC data is required, return the
     566                 :            : // covering NSEC record.
     567                 :            : //
     568                 :            : // If none of the above applies in any level, the search fails with NXDOMAIN.
     569                 :            : ZoneFinder::ResultContext
     570                 :        186 : DatabaseClient::Finder::findWildcardMatch(
     571                 :            :     const Name& name, const RRType& type, const FindOptions options,
     572                 :            :     const DelegationSearchResult& dresult, vector<ConstRRsetPtr>* target,
     573                 :            :     FindDNSSECContext& dnssec_ctx)
     574                 :            : {
     575                 :            :     // Note that during the search we are going to search not only for the
     576                 :            :     // requested type, but also for types that indicate a delegation -
     577                 :            :     // NS and DNAME.
     578                 :        372 :     WantedTypes final_types(FINAL_TYPES());
     579         [ +  - ]:        186 :     final_types.insert(type);
     580                 :            : 
     581                 :        186 :     const size_t remove_labels = name.getLabelCount() - dresult.last_known;
     582         [ +  + ]:        368 :     for (size_t i = 1; i <= remove_labels; ++i) {
     583                 :            : 
     584                 :            :         // Strip off the left-more label(s) in the name and replace with a "*".
     585         [ +  - ]:        536 :         const Name superdomain(name.split(i));
     586 [ +  - ][ +  - ]:        536 :         const string wildcard("*." + superdomain.toText());
     587         [ +  - ]:        536 :         const string construct_name(name.toText());
     588                 :            : 
     589                 :            :         // TODO Add a check for DNAME, as DNAME wildcards are discouraged (see
     590                 :            :         // RFC 4592 section 4.4).
     591                 :            :         // Search for a match.  The types are the same as with original query.
     592                 :            :         FoundRRsets found = getRRsets(wildcard, final_types, true,
     593 [ +  - ][ +  - ]:        536 :                                       &construct_name, type == RRType::ANY());
                 [ +  - ]
     594         [ +  + ]:        268 :         if (found.first) {
     595                 :            :             // Found something - but what?
     596                 :            : 
     597         [ +  + ]:         52 :             if (dresult.first_ns) {
     598                 :            :                 // About to use first_ns.  The only way this can be set is if
     599                 :            :                 // we are searching for glue, so do a sanity check.
     600         [ -  + ]:          2 :                 if ((options & FIND_GLUE_OK) == 0) {
     601 [ #  # ][ #  # ]:          0 :                     isc_throw(Unexpected, "Inconsistent conditions during "
         [ #  # ][ #  # ]
                 [ #  # ]
     602                 :            :                               "cancel of wilcard search for " <<
     603                 :            :                               name.toText() << ": find_ns non-null when not "
     604                 :            :                               "processing glue request");
     605                 :            :                 }
     606                 :            : 
     607                 :            :                 // Wildcard match for a glue below a delegation point
     608 [ +  - ][ +  - ]:          4 :                 LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                 [ +  - ]
     609                 :            :                           DATASRC_DATABASE_WILDCARD_CANCEL_NS).
     610 [ +  - ][ +  - ]:          2 :                     arg(accessor_->getDBName()).arg(wildcard).
         [ +  - ][ +  - ]
     611 [ +  - ][ +  - ]:          4 :                     arg(dresult.first_ns->getName());
     612                 :          2 :                 return (ResultContext(DELEGATION, dresult.first_ns));
     613 [ +  - ][ +  - ]:         50 :             } else if (!hasSubdomains(name.split(i - 1).toText())) {
         [ +  - ][ +  + ]
     614                 :            :                 // The wildcard match is the best one, find the final result
     615                 :            :                 // at it.  Note that wildcard should never be the zone origin.
     616                 :            :                 return (findOnNameResult(name, type, options, false, found,
     617         [ +  - ]:         48 :                                          &wildcard, target, dnssec_ctx));
     618                 :            :             } else {
     619                 :            : 
     620                 :            :                 // more specified match found, cancel wildcard match
     621 [ +  - ][ +  - ]:          4 :                 LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                 [ +  - ]
     622                 :            :                           DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
     623 [ +  - ][ +  - ]:          2 :                     arg(accessor_->getDBName()).arg(wildcard).
         [ +  - ][ +  - ]
     624 [ +  - ][ +  - ]:          2 :                     arg(name).arg(superdomain);
     625                 :            :                 return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
     626                 :            :             }
     627                 :            : 
     628 [ +  - ][ +  + ]:        216 :         } else if (hasSubdomains(wildcard)) {
     629                 :            :             // an empty non-terminal asterisk
     630 [ +  - ][ +  - ]:         68 :             LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                 [ +  - ]
     631                 :            :                       DATASRC_DATABASE_WILDCARD_EMPTY).
     632 [ +  - ][ +  - ]:         34 :                 arg(accessor_->getDBName()).arg(wildcard).arg(name);
         [ +  - ][ +  - ]
                 [ +  - ]
     633                 :            :             const FindResultFlags flags = (RESULT_WILDCARD |
     634         [ +  - ]:         34 :                                            dnssec_ctx.getResultFlags());
     635                 :            :             return (ResultContext(NXRRSET,
     636                 :         34 :                                   dnssec_ctx.getDNSSECRRset(Name(wildcard),
     637 [ +  - ][ +  - ]:         34 :                                                             true), flags));
     638                 :            :         }
     639                 :            :     }
     640                 :            : 
     641                 :            :     // Nothing found at any level.
     642                 :            :     return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
     643                 :            : }
     644                 :            : 
     645                 :            : ZoneFinder::ResultContext
     646                 :        485 : DatabaseClient::Finder::logAndCreateResult(
     647                 :            :     const Name& name, const string* wildname, const RRType& type,
     648                 :            :     ZoneFinder::Result code, ConstRRsetPtr rrset,
     649                 :            :     const isc::log::MessageID& log_id, FindResultFlags flags) const
     650                 :            : {
     651         [ +  + ]:        485 :     if (rrset) {
     652         [ +  + ]:        406 :         if (wildname == NULL) {
     653         [ +  - ]:        772 :             LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
     654 [ +  - ][ +  - ]:        386 :                 arg(accessor_->getDBName()).arg(name).arg(type).
                 [ +  - ]
     655 [ +  - ][ +  - ]:        772 :                 arg(getClass()).arg(*rrset);
     656                 :            :         } else {
     657         [ +  - ]:         40 :             LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
     658 [ +  - ][ +  - ]:         20 :                 arg(accessor_->getDBName()).arg(name).arg(type).
                 [ +  - ]
     659 [ +  - ][ +  - ]:         40 :                 arg(getClass()).arg(*wildname).arg(*rrset);
                 [ +  - ]
     660                 :            :         }
     661                 :            :     } else {
     662         [ +  + ]:         79 :         if (wildname == NULL) {
     663         [ +  - ]:        110 :             LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
     664 [ +  - ][ +  - ]:         55 :                 arg(accessor_->getDBName()).arg(name).arg(type).
                 [ +  - ]
     665         [ +  - ]:        110 :                 arg(getClass());
     666                 :            :         } else {
     667         [ +  - ]:         48 :             LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
     668 [ +  - ][ +  - ]:         24 :                 arg(accessor_->getDBName()).arg(name).arg(type).
                 [ +  - ]
     669 [ +  - ][ +  - ]:         48 :                 arg(getClass()).arg(*wildname);
     670                 :            :         }
     671                 :            :     }
     672                 :        485 :     return (ResultContext(code, rrset, flags));
     673                 :            : }
     674                 :            : 
     675                 :        660 : DatabaseClient::Finder::FindDNSSECContext::FindDNSSECContext(
     676                 :            :     DatabaseClient::Finder& finder,
     677                 :            :     const FindOptions options) :
     678                 :            :     finder_(finder),
     679                 :            :     need_dnssec_((options & FIND_DNSSEC) != 0),
     680                 :            :     is_nsec3_(false),
     681                 :            :     is_nsec_(false),
     682                 :        660 :     probed_(false)
     683                 :        660 : {}
     684                 :            : 
     685                 :            : void
     686                 :        273 : DatabaseClient::Finder::FindDNSSECContext::probe() {
     687         [ +  - ]:        273 :     if (!probed_) {
     688                 :        273 :         probed_ = true;
     689         [ +  + ]:        273 :         if (need_dnssec_) {
     690                 :            :             // If an NSEC3PARAM RR exists at the zone apex, it's quite likely
     691                 :            :             // that the zone is signed with NSEC3.  (If not the zone is more
     692                 :            :             // or less broken, but it's caller's responsibility how to handle
     693                 :            :             // such cases).
     694         [ +  - ]:        214 :             const string origin = finder_.getOrigin().toText();
     695                 :            :             const FoundRRsets nsec3_found =
     696 [ +  - ][ +  - ]:        214 :                 finder_.getRRsets(origin, NSEC3PARAM_TYPES(), false);
                 [ +  - ]
     697                 :            :             const FoundIterator nfi=
     698                 :        107 :                 nsec3_found.second.find(RRType::NSEC3PARAM());
     699                 :        214 :             is_nsec3_ = (nfi != nsec3_found.second.end());
     700                 :            : 
     701                 :            :             // Likewise for NSEC, depending on the apex has an NSEC RR.
     702                 :            :             // If we know the zone is NSEC3-signed, however, we don't bother
     703                 :            :             // to check that.  This is aligned with the transition guideline
     704                 :            :             // described in Section 10.4 of RFC 5155.
     705         [ +  + ]:        107 :             if (!is_nsec3_) {
     706                 :            :                 const FoundRRsets nsec_found =
     707         [ +  - ]:        134 :                     finder_.getRRsets(origin, NSEC_TYPES(), false);
           [ +  -  +  - ]
     708                 :            :                 const FoundIterator nfi =
     709                 :         67 :                     nsec_found.second.find(RRType::NSEC());
     710                 :        134 :                 is_nsec_ = (nfi != nsec_found.second.end());
     711                 :            :             }
     712                 :            :         }
     713                 :            :     }
     714                 :        273 : }
     715                 :            : 
     716                 :            : bool
     717                 :        283 : DatabaseClient::Finder::FindDNSSECContext::isNSEC3() {
     718         [ +  + ]:        283 :     if (!probed_) {
     719                 :        219 :         probe();
     720                 :            :     }
     721                 :        283 :     return (is_nsec3_);
     722                 :            : }
     723                 :            : 
     724                 :            : bool
     725                 :        482 : DatabaseClient::Finder::FindDNSSECContext::isNSEC() {
     726         [ +  + ]:        482 :     if (!probed_) {
     727                 :         54 :         probe();
     728                 :            :     }
     729                 :        482 :     return (is_nsec_);
     730                 :            : }
     731                 :            : 
     732                 :            : isc::dns::ConstRRsetPtr
     733                 :         54 : DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(
     734                 :            :     const FoundRRsets& found_set)
     735                 :            : {
     736         [ +  + ]:         54 :     if (!isNSEC()) {
     737                 :            :         return (ConstRRsetPtr());
     738                 :            :     }
     739                 :            : 
     740                 :          4 :     const FoundIterator nci = found_set.second.find(RRType::NSEC());
     741         [ +  - ]:          4 :     if (nci != found_set.second.end()) {
     742                 :          4 :         return (nci->second);
     743                 :            :     } else {
     744                 :            :         return (ConstRRsetPtr());
     745                 :            :     }
     746                 :            : }
     747                 :            : 
     748                 :            : isc::dns::ConstRRsetPtr
     749                 :        189 : DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(const Name &name,
     750                 :            :                                                           bool covering)
     751                 :            : {
     752         [ +  + ]:        189 :     if (!isNSEC()) {
     753                 :            :         return (ConstRRsetPtr());
     754                 :            :     }
     755                 :            : 
     756                 :            :     try {
     757                 :            :         const Name& nsec_name =
     758 [ +  + ][ +  + ]:         70 :             covering ? finder_.findPreviousName(name) : name;
                 [ +  - ]
     759         [ +  - ]:         68 :         const bool need_nscheck = (nsec_name != finder_.getOrigin());
     760                 :         34 :         const FoundRRsets found = finder_.getRRsets(nsec_name.toText(),
     761         [ +  - ]:         34 :                                                     NSEC_TYPES(),
     762 [ +  - ][ +  - ]:         68 :                                                     need_nscheck);
         [ +  - ][ #  # ]
     763                 :         34 :         const FoundIterator nci = found.second.find(RRType::NSEC());
     764         [ +  - ]:         34 :         if (nci != found.second.end()) {
     765                 :         34 :             return (nci->second);
     766                 :            :         }
     767         [ -  + ]:          4 :     } catch (const isc::NotImplemented&) {
     768                 :            :         // This happens when the underlying database accessor doesn't support
     769                 :            :         // findPreviousName() (it probably doesn't support DNSSEC at all) but
     770                 :            :         // there is somehow an NSEC RR at the zone apex.  We log the fact but
     771                 :            :         // otherwise let the caller decide what to do (so, for example, a
     772                 :            :         // higher level query processing won't completely fail but can return
     773                 :            :         // anything it can get).
     774 [ -  + ][ +  - ]:          4 :         LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED).
                 [ -  + ]
     775 [ -  + ][ -  + ]:          2 :             arg(finder_.accessor_->getDBName()).arg(name);
         [ -  + ][ -  + ]
     776                 :            :     }
     777                 :            :     return (ConstRRsetPtr());
     778                 :            : }
     779                 :            : 
     780                 :            : ZoneFinder::FindResultFlags
     781                 :        283 : DatabaseClient::Finder::FindDNSSECContext::getResultFlags() {
     782         [ +  + ]:        283 :     if (isNSEC3()) {
     783                 :            :         return (RESULT_NSEC3_SIGNED);
     784         [ +  + ]:        239 :     } else if (isNSEC()) {
     785                 :            :         return (RESULT_NSEC_SIGNED);
     786                 :            :     }
     787                 :        283 :     return (RESULT_DEFAULT);
     788                 :            : }
     789                 :            : 
     790                 :            : ZoneFinder::ResultContext
     791                 :        487 : DatabaseClient::Finder::findOnNameResult(const Name& name,
     792                 :            :                                          const RRType& type,
     793                 :            :                                          const FindOptions options,
     794                 :            :                                          const bool is_origin,
     795                 :            :                                          const FoundRRsets& found,
     796                 :            :                                          const string* wildname,
     797                 :            :                                          std::vector<isc::dns::ConstRRsetPtr>*
     798                 :            :                                          target, FindDNSSECContext& dnssec_ctx)
     799                 :            : {
     800                 :        487 :     const bool wild = (wildname != NULL);
     801                 :            :     // For wildcard case with DNSSEC required, the caller would need to
     802                 :            :     // know whether it's NSEC or NSEC3 signed.  getResultFlags returns
     803                 :            :     // appropriate flag based on the query context and zone status.
     804                 :            :     const FindResultFlags flags =
     805         [ +  + ]:        487 :         wild ? (RESULT_WILDCARD | dnssec_ctx.getResultFlags()) : RESULT_DEFAULT;
     806                 :            : 
     807                 :            :     // Get iterators for the different types of records we are interested in -
     808                 :            :     // CNAME, NS and Wanted types.
     809                 :        487 :     const FoundIterator nsi(found.second.find(RRType::NS()));
     810                 :        487 :     const FoundIterator cni(found.second.find(RRType::CNAME()));
     811                 :        487 :     const FoundIterator wti(found.second.find(type));
     812                 :            : 
     813 [ +  + ][ +  + ]:        487 :     if (!is_origin && (options & FIND_GLUE_OK) == 0 &&
         [ +  + ][ +  + ]
     814                 :        626 :         nsi != found.second.end()) {
     815                 :            :         // A NS RRset was found at the domain we were searching for.  As it is
     816                 :            :         // not at the origin of the zone, it is a delegation and indicates that
     817                 :            :         // this zone is not authoritative for the data. Just return the
     818                 :            :         // delegation information.
     819                 :            :         return (logAndCreateResult(name, wildname, type, DELEGATION,
     820                 :            :                                    nsi->second,
     821                 :            :                                    wild ? DATASRC_DATABASE_WILDCARD_NS :
     822                 :            :                                    DATASRC_DATABASE_FOUND_DELEGATION_EXACT,
     823 [ +  + ][ +  - ]:         12 :                                    flags));
     824                 :            : 
     825 [ +  + ][ +  + ]:        481 :     } else if (type != RRType::CNAME() && cni != found.second.end()) {
                 [ +  + ]
     826                 :            :         // We are not searching for a CNAME but nevertheless we have found one
     827                 :            :         // at the name we are searching so we return it. (The caller may
     828                 :            :         // want to continue the lookup by replacing the query name with the
     829                 :            :         // canonical name and the original RR type.) First though, do a sanity
     830                 :            :         // check to ensure that there is only one RR in the CNAME RRset.
     831         [ +  + ]:         16 :         if (cni->second->getRdataCount() != 1) {
     832 [ +  - ][ +  - ]:          6 :             isc_throw(DataSourceError, "CNAME with " <<
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
     833                 :            :                       cni->second->getRdataCount() << " rdata at " << name <<
     834                 :            :                       ", expected 1");
     835                 :            :         }
     836                 :            :         return (logAndCreateResult(name, wildname, type, CNAME, cni->second,
     837                 :            :                                    wild ? DATASRC_DATABASE_WILDCARD_CNAME :
     838                 :            :                                    DATASRC_DATABASE_FOUND_CNAME,
     839 [ +  + ][ +  - ]:         28 :                                    flags));
     840         [ +  + ]:        465 :     } else if (wti != found.second.end()) {
     841                 :        393 :         bool any(type == RRType::ANY());
     842                 :            :         isc::log::MessageID lid(wild ? DATASRC_DATABASE_WILDCARD_MATCH :
     843         [ +  + ]:        393 :                                 DATASRC_DATABASE_FOUND_RRSET);
     844         [ +  + ]:        393 :         if (any) {
     845                 :            :             // An ANY query, copy everything to the target instead of returning
     846                 :            :             // directly.
     847         [ +  + ]:         61 :             for (FoundIterator it(found.second.begin());
     848                 :        122 :                  it != found.second.end(); ++it) {
     849         [ +  + ]:         46 :                 if (it->second) {
     850                 :            :                     // Skip over the empty ANY
     851                 :         31 :                     target->push_back(it->second);
     852                 :            :                 }
     853                 :            :             }
     854                 :            :             lid = wild ? DATASRC_DATABASE_WILDCARD_ANY :
     855         [ +  + ]:         15 :                 DATASRC_DATABASE_FOUND_ANY;
     856                 :            :         }
     857                 :            :         // Found an RR matching the query, so return it.  (Note that this
     858                 :            :         // includes the case where we were explicitly querying for a CNAME and
     859                 :            :         // found it.  It also includes the case where we were querying for an
     860                 :            :         // NS RRset and found it at the apex of the zone.)
     861                 :            :         return (logAndCreateResult(name, wildname, type, SUCCESS,
     862         [ +  - ]:        393 :                                    wti->second, lid, flags));
     863                 :            :     }
     864                 :            : 
     865                 :            :     // If we get here, we have found something at the requested name but not
     866                 :            :     // one of the RR types we were interested in. This is the NXRRSET case so
     867                 :            :     // return the appropriate status.  If DNSSEC information was requested,
     868                 :            :     // provide the NSEC records.  If it's for wildcard, we need to get the
     869                 :            :     // NSEC records in the name of the wildcard, not the substituted one,
     870                 :            :     // so we need to search the tree again.
     871                 :            :     const ConstRRsetPtr dnssec_rrset =
     872                 :         18 :         wild ? dnssec_ctx.getDNSSECRRset(Name(*wildname), false) :
     873 [ +  + ][ +  - ]:         72 :         dnssec_ctx.getDNSSECRRset(found);
         [ +  - ][ +  - ]
         [ +  + ][ #  # ]
     874         [ +  + ]:         72 :     if (dnssec_rrset) {
     875                 :            :         // This log message covers both normal and wildcard cases, so we pass
     876                 :            :         // NULL for 'wildname'.
     877                 :            :         return (logAndCreateResult(name, NULL, type, NXRRSET, dnssec_rrset,
     878                 :            :                                    DATASRC_DATABASE_FOUND_NXRRSET_NSEC,
     879         [ +  - ]:          8 :                                    flags | RESULT_NSEC_SIGNED));
     880                 :            :     }
     881                 :            :     return (logAndCreateResult(name, wildname, type, NXRRSET, dnssec_rrset,
     882                 :            :                                wild ? DATASRC_DATABASE_WILDCARD_NXRRSET :
     883                 :            :                                DATASRC_DATABASE_FOUND_NXRRSET,
     884 [ +  - ][ +  + ]:        128 :                                flags | dnssec_ctx.getResultFlags()));
                 [ +  - ]
     885                 :            : }
     886                 :            : 
     887                 :            : ZoneFinder::ResultContext
     888                 :        221 : DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
     889                 :            :                                          FindOptions options,
     890                 :            :                                          const DelegationSearchResult& dresult,
     891                 :            :                                          std::vector<isc::dns::ConstRRsetPtr>*
     892                 :            :                                          target, FindDNSSECContext& dnssec_ctx)
     893                 :            : {
     894                 :            :     // On entry to this method, we know that the database doesn't have any
     895                 :            :     // entry for this name.  Before returning NXDOMAIN, we need to check
     896                 :            :     // for special cases.
     897                 :            : 
     898 [ +  - ][ +  + ]:        221 :     if (hasSubdomains(name.toText())) {
     899                 :            :         // Does the domain have a subdomain (i.e. it is an empty non-terminal)?
     900                 :            :         // If so, return NXRRSET instead of NXDOMAIN (as although the name does
     901                 :            :         // not exist in the database, it does exist in the DNS tree).
     902         [ +  - ]:         54 :         LOG_DEBUG(logger, DBG_TRACE_DETAILED,
     903                 :            :                   DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
     904 [ +  - ][ +  - ]:         27 :             arg(accessor_->getDBName()).arg(name);
     905                 :            :         return (ResultContext(NXRRSET, dnssec_ctx.getDNSSECRRset(name, true),
     906                 :         27 :                               dnssec_ctx.getResultFlags()));
     907         [ +  + ]:        194 :     } else if ((options & NO_WILDCARD) == 0) {
     908                 :            :         // It's not an empty non-terminal and wildcard matching is not
     909                 :            :         // disabled, so check for wildcards. If there is a wildcard match
     910                 :            :         // (i.e. all results except NXDOMAIN) return it; otherwise fall
     911                 :            :         // through to the NXDOMAIN case below.
     912                 :            :         const ResultContext wcontext =
     913                 :            :             findWildcardMatch(name, type, options, dresult, target,
     914                 :        186 :                               dnssec_ctx);
     915         [ +  + ]:        186 :         if (wcontext.code != NXDOMAIN) {
     916                 :            :             return (wcontext);
     917                 :            :         }
     918                 :            :     }
     919                 :            : 
     920                 :            :     // All avenues to find a match are now exhausted, return NXDOMAIN (plus
     921                 :            :     // NSEC records if requested).
     922         [ +  - ]:        220 :     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_NO_MATCH).
     923 [ +  - ][ +  - ]:        110 :               arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
         [ +  - ][ +  - ]
     924                 :            :     return (ResultContext(NXDOMAIN, dnssec_ctx.getDNSSECRRset(name, true),
     925                 :        110 :                           dnssec_ctx.getResultFlags()));
     926                 :            : }
     927                 :            : 
     928                 :            : ZoneFinder::ResultContext
     929                 :        751 : DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
     930                 :            :                                      std::vector<ConstRRsetPtr>* target,
     931                 :            :                                      const FindOptions options)
     932                 :            : {
     933         [ +  - ]:       1502 :     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
     934 [ +  - ][ +  - ]:        751 :               .arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
         [ +  - ][ +  - ]
     935                 :            : 
     936                 :            :     // find() variants generally expect 'name' to be included in the zone.
     937                 :            :     // Otherwise the search algorithm below won't work correctly, so we
     938                 :            :     // reject the unexpected case first.
     939                 :            :     const NameComparisonResult::NameRelation reln =
     940         [ +  - ]:        751 :         name.compare(getOrigin()).getRelation();
     941         [ +  + ]:        751 :     if (reln != NameComparisonResult::SUBDOMAIN &&
     942                 :            :         reln != NameComparisonResult::EQUAL) {
     943 [ +  - ][ +  - ]:         46 :         isc_throw(OutOfZone, name.toText() << " not in " << getOrigin());
         [ +  - ][ +  - ]
                 [ +  - ]
     944                 :            :     }
     945                 :            : 
     946                 :            :     // First, go through all superdomains from the origin down, searching for
     947                 :            :     // nodes that indicate a delegation (i.e. NS or DNAME, ignoring NS records
     948                 :            :     // at the apex).  If one is found, the search stops there.
     949                 :            :     //
     950                 :            :     // (In fact there could be RRs in the database corresponding to subdomains
     951                 :            :     // of the delegation.  The reason we do the search for the delegations
     952                 :            :     // first is because the delegation means that another zone is authoritative
     953                 :            :     // for the data and so should be consulted to retrieve it.  RRs below
     954                 :            :     // this delegation point can be found in a search for glue but not
     955                 :            :     // otherwise; in the latter case they are said to be occluded by the
     956                 :            :     // presence of the delegation.)
     957                 :       1454 :     const DelegationSearchResult dresult = findDelegationPoint(name, options);
     958         [ +  + ]:        726 :     if (dresult.rrset) {
     959                 :            :         // In this case no special flags are needed.
     960                 :         39 :         return (ResultContext(dresult.code, dresult.rrset));
     961                 :            :     }
     962                 :            : 
     963                 :            :     // If there is no delegation, look for the exact match to the request
     964                 :            :     // name/type/class.  However, there are special cases:
     965                 :            :     // - Requested name has a singleton CNAME record associated with it
     966                 :            :     // - Requested name is a delegation point (NS only but not at the zone
     967                 :            :     //   apex - DNAME is ignored here as it redirects DNS names subordinate to
     968                 :            :     //   the owner name - the owner name itself is not redirected.)
     969         [ +  - ]:       1374 :     const bool is_origin = (name == getOrigin());
     970 [ +  - ][ +  - ]:       1374 :     WantedTypes final_types(FINAL_TYPES());
     971         [ +  - ]:        687 :     final_types.insert(type);
     972                 :        687 :     const FoundRRsets found = getRRsets(name.toText(), final_types,
     973                 :        687 :                                         !is_origin, NULL,
     974 [ +  - ][ +  + ]:       1347 :                                         type == RRType::ANY());
                 [ +  - ]
     975         [ +  - ]:        660 :     FindDNSSECContext dnssec_ctx(*this, options);
     976         [ +  + ]:        660 :     if (found.first) {
     977                 :            :         // Something found at the domain name.  Look into it further to get
     978                 :            :         // the final result.
     979                 :            :         return (findOnNameResult(name, type, options, is_origin, found, NULL,
     980         [ +  + ]:        439 :                                  target, dnssec_ctx));
     981                 :            :     } else {
     982                 :            :         // Did not find anything at all at the domain name, so check for
     983                 :            :         // subdomains or wildcards.
     984                 :            :         return (findNoNameResult(name, type, options, dresult, target,
     985         [ +  - ]:        221 :                                  dnssec_ctx));
     986                 :            :     }
     987                 :            : }
     988                 :            : 
     989                 :            : // The behaviour is inspired by the one in the in-memory implementation.
     990                 :            : ZoneFinder::FindNSEC3Result
     991                 :         10 : DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
     992 [ +  - ][ +  - ]:         30 :     LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_DATABASE_FINDNSEC3).arg(name).
     993 [ +  + ][ +  - ]:         20 :         arg(recursive ? "recursive" : "non-recursive");
     994                 :            : 
     995                 :            :     // First, validate the input
     996         [ +  - ]:         10 :     const NameComparisonResult cmp_result(name.compare(getOrigin()));
     997 [ +  + ][ +  + ]:         10 :     if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
                 [ +  + ]
     998                 :          7 :         cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
     999 [ +  - ][ +  - ]:          4 :         isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: " <<
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    1000                 :            :                   name << ", zone: " << getOrigin() << "/" << getClass());
    1001                 :            :     }
    1002                 :            : 
    1003                 :            :     // Now, we need to get the NSEC3 params from the apex and create the hash
    1004                 :            :     // creator for it.
    1005                 :          8 :     const FoundRRsets nsec3param(getRRsets(getOrigin().toText(),
    1006 [ +  - ][ +  - ]:         16 :                                  NSEC3PARAM_TYPES(), false));
         [ +  - ][ +  - ]
    1007                 :          8 :     const FoundIterator param(nsec3param.second.find(RRType::NSEC3PARAM()));
    1008 [ +  - ][ +  + ]:          8 :     if (!nsec3param.first || param == nsec3param.second.end()) {
                 [ +  + ]
    1009                 :            :         // No NSEC3 params? :-(
    1010 [ +  - ][ +  - ]:          2 :         isc_throw(DataSourceError, "findNSEC3 attempt for non NSEC3 signed " <<
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    1011                 :            :                   "zone: " << getOrigin() << "/" << getClass());
    1012                 :            :     }
    1013                 :            :     // This takes the RRset received from the find method, takes the first RR
    1014                 :            :     // in it, casts it to NSEC3PARAM (as it should be that one) and then creates
    1015                 :            :     // the hash calculator class from it.
    1016                 :            :     const scoped_ptr<NSEC3Hash> calculator(NSEC3Hash::create(
    1017                 :            :         dynamic_cast<const generic::NSEC3PARAM&>(
    1018 [ +  - ][ +  - ]:          7 :             param->second->getRdataIterator()->getCurrent())));
         [ +  - ][ +  - ]
    1019                 :            : 
    1020                 :            :     // Few shortcut variables
    1021         [ +  - ]:          7 :     const unsigned olabels(getOrigin().getLabelCount());
    1022                 :          7 :     const unsigned qlabels(name.getLabelCount());
    1023 [ +  - ][ +  - ]:         14 :     const string otext(getOrigin().toText());
    1024                 :            : 
    1025                 :            :     // This will be set to the one covering the query name
    1026                 :            :     ConstRRsetPtr covering_proof;
    1027                 :            : 
    1028                 :            :     // We keep stripping the leftmost label until we find something.
    1029                 :            :     // In case it is recursive, we'll exit the loop at the first iteration.
    1030         [ +  - ]:         10 :     for (unsigned labels(qlabels); labels >= olabels; -- labels) {
    1031                 :            :         const string hash(calculator->calculate(labels == qlabels ? name :
    1032                 :            :                                                 name.split(qlabels - labels,
    1033 [ +  + ][ +  - ]:         20 :                                                            labels)));
         [ +  - ][ +  - ]
    1034                 :            :         // Get the exact match for the name.
    1035 [ +  - ][ +  - ]:         20 :         LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_DATABASE_FINDNSEC3_TRYHASH).
                 [ +  - ]
    1036 [ +  - ][ +  - ]:         10 :             arg(name).arg(labels).arg(hash);
         [ +  - ][ +  - ]
    1037                 :            : 
    1038                 :            :         DatabaseAccessor::IteratorContextPtr
    1039         [ +  - ]:         10 :             context(accessor_->getNSEC3Records(hash, zone_id_));
    1040                 :            : 
    1041         [ -  + ]:         10 :         if (!context) {
    1042 [ #  # ][ #  # ]:          0 :             isc_throw(Unexpected, "Iterator context null for hash " + hash);
                 [ #  # ]
    1043                 :            :         }
    1044                 :            : 
    1045         [ +  - ]:         20 :         const FoundRRsets nsec3(getRRsets(hash + "." + otext, NSEC3_TYPES(),
    1046 [ +  - ][ +  - ]:         20 :                                           false, NULL, false, context));
           [ +  -  +  - ]
                 [ +  - ]
    1047                 :            : 
    1048         [ +  + ]:         10 :         if (nsec3.first) {
    1049                 :            :             // We found an exact match against the current label.
    1050                 :          4 :             const FoundIterator it(nsec3.second.find(RRType::NSEC3()));
    1051         [ -  + ]:          4 :             if (it == nsec3.second.end()) {
    1052 [ #  # ][ #  # ]:          0 :                 isc_throw(DataSourceError, "Hash " + hash +
         [ #  # ][ #  # ]
                 [ #  # ]
    1053                 :            :                           "exists, but no NSEC3 there");
    1054                 :            :             }
    1055                 :            : 
    1056 [ +  - ][ +  - ]:          8 :             LOG_DEBUG(logger, DBG_TRACE_BASIC,
                 [ +  - ]
    1057 [ +  - ][ +  - ]:          4 :                       DATASRC_DATABASE_FINDNSEC3_MATCH).arg(name).arg(labels).
                 [ +  - ]
    1058         [ +  - ]:          4 :                 arg(*it->second);
    1059                 :            :             // Yes, we win
    1060                 :          8 :             return (FindNSEC3Result(true, labels, it->second, covering_proof));
    1061                 :            :         } else {
    1062                 :            :             // There's no exact match. We try a previous one. We must find it
    1063                 :            :             // (if the zone is properly signed).
    1064                 :          6 :             const string prevHash(accessor_->findPreviousNSEC3Hash(zone_id_,
    1065         [ +  - ]:         12 :                                                                    hash));
    1066 [ +  - ][ +  - ]:         12 :             LOG_DEBUG(logger, DBG_TRACE_BASIC,
                 [ +  - ]
    1067 [ +  - ][ +  - ]:          6 :                       DATASRC_DATABASE_FINDNSEC3_TRYHASH_PREV).arg(name).
    1068 [ +  - ][ +  - ]:          6 :                 arg(labels).arg(prevHash);
    1069 [ +  - ][ +  - ]:          6 :             context = accessor_->getNSEC3Records(prevHash, zone_id_);
    1070                 :          6 :             const FoundRRsets prev_nsec3(getRRsets(prevHash + "." + otext,
    1071         [ +  - ]:          6 :                                                    NSEC3_TYPES(), false, NULL,
    1072 [ +  - ][ +  - ]:         12 :                                                    false, context));
         [ +  - ][ +  - ]
                 [ +  - ]
    1073                 :            : 
    1074         [ -  + ]:          6 :             if (!prev_nsec3.first) {
    1075 [ #  # ][ #  # ]:          0 :                 isc_throw(DataSourceError, "Hash " + prevHash + " returned "
         [ #  # ][ #  # ]
                 [ #  # ]
    1076                 :            :                           "from findPreviousNSEC3Hash, but it is empty");
    1077                 :            :             }
    1078                 :            :             const FoundIterator
    1079                 :          6 :                 prev_it(prev_nsec3.second.find(RRType::NSEC3()));
    1080         [ -  + ]:          6 :             if (prev_it == prev_nsec3.second.end()) {
    1081 [ #  # ][ #  # ]:          0 :                 isc_throw(DataSourceError, "The previous hash " + prevHash +
         [ #  # ][ #  # ]
                 [ #  # ]
    1082                 :            :                           "exists, but does not contain the NSEC3");
    1083                 :            :             }
    1084                 :            : 
    1085         [ +  - ]:          6 :             covering_proof = prev_it->second;
    1086                 :            :             // In case it is recursive, we try to get an exact match a level
    1087                 :            :             // up. If it is not recursive, the caller is ok with a covering
    1088                 :            :             // one, so we just return it.
    1089         [ +  + ]:          6 :             if (!recursive) {
    1090 [ +  - ][ +  - ]:          6 :                 LOG_DEBUG(logger, DBG_TRACE_BASIC,
                 [ +  - ]
    1091 [ +  - ][ +  - ]:          3 :                           DATASRC_DATABASE_FINDNSEC3_COVER).arg(name).
    1092 [ +  - ][ +  - ]:          6 :                     arg(labels).arg(*covering_proof);
    1093                 :            :                 return (FindNSEC3Result(false, labels, covering_proof,
    1094                 :          3 :                                         ConstRRsetPtr()));
    1095                 :            :             }
    1096                 :            :         }
    1097                 :            :     }
    1098                 :            : 
    1099                 :            :     // The zone must contain at least the apex and that one should match
    1100                 :            :     // exactly. If that doesn't happen, we have a problem.
    1101 [ #  # ][ #  # ]:          0 :     isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely a "
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    1102                 :            :               "broken NSEC3 zone: " << otext << "/" << getClass());
    1103                 :            : }
    1104                 :            : 
    1105                 :            : Name
    1106                 :         43 : DatabaseClient::Finder::findPreviousName(const Name& name) const {
    1107                 :         43 :     const string str(accessor_->findPreviousName(zone_id_,
    1108 [ +  - ][ +  + ]:         81 :                                                  name.reverse().toText()));
    1109                 :            :     try {
    1110         [ +  + ]:         75 :         return (Name(str));
    1111                 :            :     }
    1112         [ -  + ]:          2 :     catch (const isc::dns::NameParserException&) {
    1113 [ -  + ][ -  + ]:          2 :         isc_throw(DataSourceError, "Bad name " + str + " from findPreviousName");
         [ -  + ][ -  + ]
                 [ -  + ]
    1114                 :            :     }
    1115                 :            : }
    1116                 :            : 
    1117                 :            : Name
    1118                 :       2421 : DatabaseClient::Finder::getOrigin() const {
    1119                 :       2421 :     return (origin_);
    1120                 :            : }
    1121                 :            : 
    1122                 :            : isc::dns::RRClass
    1123                 :       6059 : DatabaseClient::Finder::getClass() const {
    1124                 :            :     // TODO Implement
    1125                 :       6059 :     return isc::dns::RRClass::IN();
    1126                 :            : }
    1127                 :            : 
    1128                 :            : namespace {
    1129                 :            : 
    1130                 :            : /// This needs, beside of converting all data from textual representation, group
    1131                 :            : /// together rdata of the same RRsets. To do this, we hold one row of data ahead
    1132                 :            : /// of iteration. When we get a request to provide data, we create it from this
    1133                 :            : /// data and load a new one. If it is to be put to the same rrset, we add it.
    1134                 :            : /// Otherwise we just return what we have and keep the row as the one ahead
    1135                 :            : /// for next time.
    1136                 :            : class DatabaseIterator : public ZoneIterator {
    1137                 :            : public:
    1138                 :         39 :     DatabaseIterator(boost::shared_ptr<DatabaseAccessor> accessor,
    1139                 :            :                      const Name& zone_name,
    1140                 :            :                      const RRClass& rrclass,
    1141                 :            :                      bool separate_rrs) :
    1142                 :            :         accessor_(accessor),
    1143                 :            :         class_(rrclass),
    1144                 :            :         ready_(true),
    1145                 :          6 :         separate_rrs_(separate_rrs)
    1146                 :            :     {
    1147                 :            :         // Get the zone
    1148 [ +  - ][ +  - ]:         39 :         const pair<bool, int> zone(accessor_->getZone(zone_name.toText()));
    1149         [ +  + ]:         39 :         if (!zone.first) {
    1150                 :            :             // No such zone, can't continue
    1151 [ +  - ][ +  - ]:          8 :             isc_throw(DataSourceError, "Zone " + zone_name.toText() +
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    1152                 :            :                       " can not be iterated, because it doesn't exist "
    1153                 :            :                       "in this data source");
    1154                 :            :         }
    1155                 :            : 
    1156                 :            :         // Start a separate transaction.
    1157         [ +  - ]:         35 :         accessor_->startTransaction();
    1158                 :            : 
    1159                 :            :         // Find the SOA of the zone (may or may not succeed).  Note that
    1160                 :            :         // this must be done before starting the iteration context.
    1161         [ +  - ]:         35 :         soa_ = DatabaseClient::Finder(accessor_, zone.second, zone_name).
    1162 [ +  - ][ +  + ]:        104 :             find(zone_name, RRType::SOA())->rrset;
                 [ +  - ]
    1163                 :            : 
    1164                 :            :         // Request the context
    1165 [ +  - ][ +  - ]:         34 :         context_ = accessor_->getAllRecords(zone.second);
    1166                 :            :         // It must not return NULL, that's a bug of the implementation
    1167         [ +  + ]:         34 :         if (!context_) {
    1168 [ +  - ][ +  - ]:          2 :             isc_throw(isc::Unexpected, "Iterator context null at " +
         [ +  - ][ +  - ]
    1169                 :            :                       zone_name.toText());
    1170                 :            :         }
    1171                 :            : 
    1172                 :            :         // Prepare data for the next time
    1173         [ +  - ]:         33 :         getData();
    1174                 :         33 :     }
    1175                 :            : 
    1176                 :         33 :     virtual ~DatabaseIterator() {
    1177         [ +  + ]:         33 :         if (ready_) {
    1178         [ +  - ]:         15 :             accessor_->commit();
    1179                 :            :         }
    1180                 :         66 :     }
    1181                 :            : 
    1182                 :         12 :     virtual ConstRRsetPtr getSOA() const {
    1183                 :         12 :         return (soa_);
    1184                 :            :     }
    1185                 :            : 
    1186                 :        380 :     virtual isc::dns::ConstRRsetPtr getNextRRset() {
    1187         [ +  + ]:        380 :         if (!ready_) {
    1188         [ +  - ]:          6 :             isc_throw(isc::Unexpected, "Iterating past the zone end");
    1189                 :            :         }
    1190         [ +  + ]:        377 :         if (!data_ready_) {
    1191                 :            :             // At the end of zone
    1192                 :         18 :             accessor_->commit();
    1193                 :         18 :             ready_ = false;
    1194         [ +  - ]:         18 :             LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_END);
    1195                 :            :             return (ConstRRsetPtr());
    1196                 :            :         }
    1197                 :        359 :         const RRType rtype(rtype_txt_);
    1198                 :        359 :         RRsetPtr rrset(new RRset(Name(name_txt_), class_, rtype,
    1199 [ +  - ][ +  - ]:        359 :                                  RRTTL(ttl_txt_)));
         [ +  - ][ +  - ]
    1200                 :            :         // Remember the first RDATA of the RRset for comparison:
    1201                 :        359 :         const ConstRdataPtr rdata_base = rdata_;
    1202                 :            :         while (true) {
    1203                 :            :             // Extend the RRset with the new RDATA.
    1204         [ +  - ]:        808 :             rrset->addRdata(rdata_);
    1205                 :            : 
    1206                 :            :             // Retrieve the next record from the database.  If we reach the
    1207                 :            :             // end of the zone, done; if we were requested to separate all RRs,
    1208                 :            :             // just remember this record and return the single RR.
    1209         [ +  - ]:        404 :             getData();
    1210 [ +  + ][ +  + ]:        404 :             if (separate_rrs_ || !data_ready_) {
    1211                 :            :                 break;
    1212                 :            :             }
    1213                 :            : 
    1214                 :            :             // Check if the next record belongs to the same RRset.  If not,
    1215                 :            :             // we are done.  The next RDATA has been stored in rdata_, which
    1216                 :            :             // is used within this loop (if it belongs to the same RRset) or
    1217                 :            :             // in the next call.
    1218 [ +  - ][ +  - ]:        963 :             if (Name(name_txt_) != rrset->getName() ||
         [ +  + ][ +  + ]
         [ +  + ][ #  # ]
    1219 [ +  - ][ +  + ]:        631 :                 !isSameType(rtype, rdata_base, RRType(rtype_txt_), rdata_)) {
         [ +  + ][ #  # ]
                 [ #  # ]
    1220                 :            :                 break;
    1221                 :            :             }
    1222                 :            : 
    1223                 :            :             // Adjust TTL if necessary
    1224         [ +  - ]:         45 :             const RRTTL next_ttl(ttl_txt_);
    1225 [ +  - ][ +  + ]:         45 :             if (next_ttl != rrset->getTTL()) {
    1226 [ +  - ][ +  + ]:          9 :                 if (next_ttl < rrset->getTTL()) {
    1227         [ +  - ]:          2 :                     rrset->setTTL(next_ttl);
    1228                 :            :                 }
    1229 [ +  - ][ -  + ]:         18 :                 LOG_WARN(logger, DATASRC_DATABASE_ITERATE_TTL_MISMATCH).
                 [ +  - ]
    1230 [ +  - ][ +  - ]:          9 :                     arg(name_txt_).arg(class_).arg(rtype).arg(rrset->getTTL());
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    1231                 :            :             }
    1232                 :            :         }
    1233 [ +  - ][ +  - ]:        718 :         LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_NEXT).
                 [ +  - ]
    1234 [ +  - ][ +  - ]:        359 :             arg(rrset->getName()).arg(rrset->getType());
         [ +  - ][ +  - ]
                 [ +  - ]
    1235                 :            :         return (rrset);
    1236                 :            :     }
    1237                 :            : 
    1238                 :            : private:
    1239                 :            :     // Check two RDATA types are equivalent.  Basically it's a trivial
    1240                 :            :     // comparison, but if both are of RRSIG, we should also compare the types
    1241                 :            :     // covered.
    1242                 :            :     static bool isSameType(RRType type1, ConstRdataPtr rdata1,
    1243                 :            :                            RRType type2, ConstRdataPtr rdata2)
    1244                 :            :     {
    1245         [ +  + ]:        186 :         if (type1 != type2) {
    1246                 :            :             return (false);
    1247                 :            :         }
    1248         [ +  + ]:         86 :         if (type1 == RRType::RRSIG()) {
    1249 [ +  - ][ +  - ]:         43 :             return (dynamic_cast<const generic::RRSIG&>(*rdata1).typeCovered()
    1250                 :         43 :                     == dynamic_cast<const generic::RRSIG&>(*rdata2).
    1251 [ +  - ][ +  - ]:         43 :                     typeCovered());
    1252                 :            :         }
    1253                 :            :         return (true);
    1254                 :            :     }
    1255                 :            : 
    1256                 :            :     // Load next row of data
    1257                 :        437 :     void getData() {
    1258         [ +  + ]:       4807 :         string data[DatabaseAccessor::COLUMN_COUNT];
    1259         [ +  - ]:        437 :         data_ready_ = context_->getNext(data);
    1260         [ +  + ]:        437 :         if (data_ready_) {
    1261                 :        416 :             name_txt_ = data[DatabaseAccessor::NAME_COLUMN];
    1262                 :        416 :             rtype_txt_ = data[DatabaseAccessor::TYPE_COLUMN];
    1263                 :        416 :             ttl_txt_ = data[DatabaseAccessor::TTL_COLUMN];
    1264                 :            :             rdata_ = rdata::createRdata(RRType(rtype_txt_), class_,
    1265 [ +  - ][ +  - ]:        416 :                                         data[DatabaseAccessor::RDATA_COLUMN]);
    1266 [ +  + ][ #  # ]:       2622 :         }
    1267                 :        437 :     }
    1268                 :            : 
    1269                 :            :     // The dedicated accessor
    1270                 :            :     boost::shared_ptr<DatabaseAccessor> accessor_;
    1271                 :            :     // The context
    1272                 :            :     DatabaseAccessor::IteratorContextPtr context_;
    1273                 :            :     // Class of the zone
    1274                 :            :     const RRClass class_;
    1275                 :            :     // SOA of the zone, if any (it should normally exist)
    1276                 :            :     ConstRRsetPtr soa_;
    1277                 :            :     // Status
    1278                 :            :     bool ready_, data_ready_;
    1279                 :            :     // Data of the next row
    1280                 :            :     string name_txt_, rtype_txt_, ttl_txt_;
    1281                 :            :     // RDATA of the next row
    1282                 :            :     ConstRdataPtr rdata_;
    1283                 :            :     // Whether to modify differing TTL values, or treat a different TTL as
    1284                 :            :     // a different RRset
    1285                 :            :     const bool separate_rrs_;
    1286                 :            : };
    1287                 :            : 
    1288                 :            : }
    1289                 :            : 
    1290                 :            : ZoneIteratorPtr
    1291                 :         39 : DatabaseClient::getIterator(const isc::dns::Name& name,
    1292                 :            :                             bool separate_rrs) const
    1293                 :            : {
    1294                 :            :     ZoneIteratorPtr iterator = ZoneIteratorPtr(new DatabaseIterator(
    1295                 :         39 :                                                    accessor_->clone(), name,
    1296 [ +  - ][ +  + ]:         39 :                                                    rrclass_, separate_rrs));
    1297 [ +  - ][ +  - ]:         66 :     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE).
                 [ +  - ]
    1298 [ +  - ][ +  - ]:         33 :         arg(name);
    1299                 :            : 
    1300                 :         33 :     return (iterator);
    1301                 :            : }
    1302                 :            : 
    1303                 :            : //
    1304                 :            : // Zone updater using some database system as the underlying data source.
    1305                 :            : //
    1306                 :            : class DatabaseUpdater : public ZoneUpdater {
    1307                 :            : public:
    1308                 :        149 :     DatabaseUpdater(boost::shared_ptr<DatabaseAccessor> accessor, int zone_id,
    1309                 :            :             const Name& zone_name, const RRClass& zone_class,
    1310                 :            :             bool journaling) :
    1311                 :            :         committed_(false), accessor_(accessor), zone_id_(zone_id),
    1312         [ +  - ]:        149 :         db_name_(accessor->getDBName()), zone_name_(zone_name.toText()),
    1313                 :            :         zone_class_(zone_class), journaling_(journaling),
    1314                 :            :         diff_phase_(NOT_STARTED), serial_(0),
    1315 [ +  - ][ +  - ]:        298 :         finder_(new DatabaseClient::Finder(accessor_, zone_id_, zone_name))
         [ +  - ][ +  - ]
    1316                 :            :     {
    1317         [ +  - ]:        149 :         logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED)
    1318 [ +  - ][ +  - ]:        149 :             .arg(zone_name_).arg(zone_class_).arg(db_name_);
         [ +  - ][ +  - ]
    1319                 :        149 :     }
    1320                 :            : 
    1321                 :        149 :     virtual ~DatabaseUpdater() {
    1322         [ +  + ]:        149 :         if (!committed_) {
    1323                 :            :             try {
    1324         [ +  + ]:         69 :                 accessor_->rollback();
    1325         [ +  - ]:         68 :                 logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK)
    1326 [ +  - ][ +  - ]:         68 :                     .arg(zone_name_).arg(zone_class_).arg(db_name_);
         [ +  - ][ +  - ]
    1327 [ -  + ][ +  - ]:          2 :             } catch (const DataSourceError& e) {
    1328                 :            :                 // We generally expect that rollback always succeeds, and
    1329                 :            :                 // it should in fact succeed in a way we execute it.  But
    1330                 :            :                 // as the public API allows rollback() to fail and
    1331                 :            :                 // throw, we should expect it.  Obviously we cannot re-throw
    1332                 :            :                 // it.  The best we can do is to log it as a critical error.
    1333         [ -  + ]:          1 :                 logger.error(DATASRC_DATABASE_UPDATER_ROLLBACKFAIL)
    1334 [ -  + ][ -  + ]:          1 :                     .arg(zone_name_).arg(zone_class_).arg(db_name_)
         [ -  + ][ -  + ]
    1335         [ -  + ]:          2 :                     .arg(e.what());
    1336                 :            :             }
    1337                 :            :         }
    1338                 :            : 
    1339         [ +  - ]:        149 :         logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_DESTROYED)
    1340 [ +  - ][ +  - ]:        149 :             .arg(zone_name_).arg(zone_class_).arg(db_name_);
         [ +  - ][ +  - ]
    1341                 :        298 :     }
    1342                 :            : 
    1343                 :         79 :     virtual ZoneFinder& getFinder() { return (*finder_); }
    1344                 :            : 
    1345                 :            :     virtual void addRRset(const AbstractRRset& rrset);
    1346                 :            :     virtual void deleteRRset(const AbstractRRset& rrset);
    1347                 :            :     virtual void commit();
    1348                 :            : 
    1349                 :            : private:
    1350                 :            :     // A short cut typedef only for making the code shorter.
    1351                 :            :     typedef DatabaseAccessor Accessor;
    1352                 :            : 
    1353                 :            :     bool committed_;
    1354                 :            :     boost::shared_ptr<DatabaseAccessor> accessor_;
    1355                 :            :     const int zone_id_;
    1356                 :            :     const string db_name_;
    1357                 :            :     const string zone_name_;
    1358                 :            :     const RRClass zone_class_;
    1359                 :            :     const bool journaling_;
    1360                 :            :     // For the journals
    1361                 :            :     enum DiffPhase {
    1362                 :            :         NOT_STARTED,
    1363                 :            :         DELETE,
    1364                 :            :         ADD
    1365                 :            :     };
    1366                 :            :     DiffPhase diff_phase_;
    1367                 :            :     Serial serial_;
    1368                 :            :     boost::scoped_ptr<DatabaseClient::Finder> finder_;
    1369                 :            : 
    1370                 :            :     // This is a set of validation checks commonly used for addRRset() and
    1371                 :            :     // deleteRRset to minimize duplicate code logic and to make the main
    1372                 :            :     // code concise.
    1373                 :            :     void validateAddOrDelete(const char* const op_str,
    1374                 :            :                              const AbstractRRset& rrset,
    1375                 :            :                              DiffPhase prev_phase,
    1376                 :            :                              DiffPhase current_phase) const;
    1377                 :            : };
    1378                 :            : 
    1379                 :            : void
    1380                 :       1808 : DatabaseUpdater::validateAddOrDelete(const char* const op_str,
    1381                 :            :                                      const AbstractRRset& rrset,
    1382                 :            :                                      DiffPhase prev_phase,
    1383                 :            :                                      DiffPhase current_phase) const
    1384                 :            : {
    1385         [ +  + ]:       1808 :     if (committed_) {
    1386 [ +  - ][ +  - ]:          8 :         isc_throw(DataSourceError, op_str << " attempt after commit to zone: "
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    1387                 :            :                   << zone_name_ << "/" << zone_class_);
    1388                 :            :     }
    1389         [ +  + ]:       1804 :     if (rrset.getRdataCount() == 0) {
    1390 [ +  - ][ +  - ]:          8 :         isc_throw(DataSourceError, op_str << " attempt with an empty RRset: "
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
    1391                 :            :                   << rrset.getName() << "/" << zone_class_ << "/"
    1392                 :            :                   << rrset.getType());
    1393                 :            :     }
    1394         [ +  + ]:       1800 :     if (rrset.getClass() != zone_class_) {
    1395 [ +  - ][ +  - ]:          8 :         isc_throw(DataSourceError, op_str << " attempt for a different class "
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    1396                 :            :                   << zone_name_ << "/" << zone_class_ << ": "
    1397                 :            :                   << rrset.toText());
    1398                 :            :     }
    1399         [ +  + ]:       3592 :     if (rrset.getRRsig()) {
    1400 [ +  - ][ +  - ]:         12 :         isc_throw(DataSourceError, op_str << " attempt for RRset with RRSIG "
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    1401                 :            :                   << zone_name_ << "/" << zone_class_ << ": "
    1402                 :            :                   << rrset.toText());
    1403                 :            :     }
    1404         [ +  + ]:       1790 :     if (journaling_) {
    1405                 :       1253 :         const RRType rrtype(rrset.getType());
    1406 [ +  + ][ +  + ]:       1253 :         if (rrtype == RRType::SOA() && diff_phase_ != prev_phase) {
                 [ +  + ]
    1407 [ +  - ][ +  - ]:         12 :             isc_throw(isc::BadValue, op_str << " attempt in an invalid "
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    1408                 :            :                       << "diff phase: " << diff_phase_ << ", rrset: " <<
    1409                 :            :                       rrset.toText());
    1410                 :            :         }
    1411 [ +  + ][ +  + ]:       1247 :         if (rrtype != RRType::SOA() && diff_phase_ != current_phase) {
                 [ +  + ]
    1412 [ +  - ][ +  - ]:          8 :             isc_throw(isc::BadValue, "diff state change by non SOA: "
                 [ +  - ]
    1413                 :            :                       << rrset.toText());
    1414                 :            :         }
    1415                 :            :     }
    1416                 :       1780 : }
    1417                 :            : 
    1418                 :            : // This is a helper class used in adding/deleting RRsets to/from a database.
    1419                 :            : // The purpose of this class is to provide conversion interface from various
    1420                 :            : // parameters of the RRset to corresponding textual representations that the
    1421                 :            : // underlying database interface expects.  The necessary parameters and how
    1422                 :            : // to convert them depend on several things, such as whether it's NSEC3 related
    1423                 :            : // or not, or whether journaling is requested.  In order to avoid unnecessary
    1424                 :            : // conversion, this class also performs the conversion in a lazy manner.
    1425                 :            : // Also, in order to avoid redundant conversion when the conversion is
    1426                 :            : // requested for the same parameter multiple times, it remembers the
    1427                 :            : // conversion result first time, and reuses it for subsequent requests
    1428                 :            : // (this implicitly assumes copying std::string objects is not very expensive;
    1429                 :            : // this is often the case in some common implementations that have
    1430                 :            : // copy-on-write semantics for the string class).
    1431                 :       1780 : class RRParameterConverter {
    1432                 :            : public:
    1433                 :       1780 :     RRParameterConverter(const AbstractRRset& rrset) : rrset_(rrset)
    1434                 :            :     {}
    1435                 :       3154 :     const string& getName() {
    1436         [ +  + ]:       3154 :         if (name_.empty()) {
    1437                 :       3532 :             name_ = rrset_.getName().toText();
    1438                 :            :         }
    1439                 :       3154 :         return (name_);
    1440                 :            :     }
    1441                 :         18 :     const string& getNSEC3Name() {
    1442         [ +  - ]:         18 :         if (nsec3_name_.empty()) {
    1443         [ +  - ]:         36 :             nsec3_name_ = rrset_.getName().split(0, 1).toText(true);
    1444                 :            :         }
    1445                 :         18 :         return (nsec3_name_);
    1446                 :            :     }
    1447                 :            :     const string& getRevName() {
    1448         [ +  + ]:       1260 :         if (revname_.empty()) {
    1449 [ +  - ][ +  - ]:       2224 :             revname_ = rrset_.getName().reverse().toText();
                 [ +  - ]
    1450                 :            :         }
    1451                 :            :         return (revname_);
    1452                 :            :     }
    1453                 :       2515 :     const string& getTTL() {
    1454         [ +  + ]:       2515 :         if (ttl_.empty()) {
    1455                 :       3500 :             ttl_ = rrset_.getTTL().toText();
    1456                 :            :         }
    1457                 :       2515 :         return (ttl_);
    1458                 :            :     }
    1459                 :       3172 :     const string& getType() {
    1460         [ +  + ]:       3172 :         if (type_.empty()) {
    1461                 :       3560 :             type_ = rrset_.getType().toText();
    1462                 :            :         }
    1463                 :       3172 :         return (type_);
    1464                 :            :     }
    1465                 :            : 
    1466                 :            : private:
    1467                 :            :     string name_;
    1468                 :            :     string nsec3_name_;
    1469                 :            :     string revname_;
    1470                 :            :     string ttl_;
    1471                 :            :     string type_;
    1472                 :            :     const AbstractRRset& rrset_;
    1473                 :            : };
    1474                 :            : 
    1475                 :            : namespace {
    1476                 :            : // A shared shortcut to detect if the given type of RDATA is NSEC3 or
    1477                 :            : // RRSIG covering NSEC3.  RRSIG for NSEC3 should go to the (conceptual)
    1478                 :            : // separate namespace, so we need to check the covered type.
    1479                 :            : // Note: in principle the type covered should be the same for
    1480                 :            : // all RDATA, but the RRset interface doesn't ensure that condition.
    1481                 :            : // So we explicitly check that for every RDATA below.
    1482                 :            : bool
    1483                 :       1932 : isNSEC3KindType(RRType rrtype, const Rdata& rdata) {
    1484         [ +  + ]:       1932 :     if (rrtype == RRType::NSEC3()) {
    1485                 :            :         return (true);
    1486                 :            :     }
    1487   [ +  +  +  + ]:       3870 :     if (rrtype == RRType::RRSIG() &&
                 [ +  + ]
    1488         [ +  - ]:       1950 :         dynamic_cast<const generic::RRSIG&>(rdata).typeCovered() ==
    1489                 :         36 :         RRType::NSEC3())
    1490                 :            :     {
    1491                 :            :         return (true);
    1492                 :            :     }
    1493                 :       1932 :     return (false);
    1494                 :            : }
    1495                 :            : }
    1496                 :            : 
    1497                 :            : void
    1498                 :       1139 : DatabaseUpdater::addRRset(const AbstractRRset& rrset) {
    1499                 :       1139 :     validateAddOrDelete("add", rrset, DELETE, ADD);
    1500                 :            : 
    1501                 :            :     // It's guaranteed rrset has at least one RDATA at this point.
    1502                 :       1125 :     RdataIteratorPtr it = rrset.getRdataIterator();
    1503         [ +  + ]:       1125 :     if (journaling_) {
    1504                 :        618 :         diff_phase_ = ADD;
    1505 [ +  - ][ +  + ]:        618 :         if (rrset.getType() == RRType::SOA()) {
    1506         [ +  - ]:        613 :             serial_ = dynamic_cast<const generic::SOA&>(it->getCurrent()).
    1507 [ +  - ][ +  - ]:        613 :                 getSerial();
    1508                 :            :         }
    1509                 :            :     }
    1510                 :            : 
    1511                 :       1125 :     RRParameterConverter cvtr(rrset);
    1512 [ +  - ][ +  - ]:       2397 :     for (; !it->isLast(); it->next()) {
                 [ +  + ]
    1513         [ +  - ]:       1273 :         const Rdata& rdata = it->getCurrent();
    1514 [ +  - ][ +  - ]:       1273 :         const bool nsec3_type = isNSEC3KindType(rrset.getType(), rdata);
    1515                 :            : 
    1516                 :       1273 :         string sigtype;
    1517 [ +  - ][ +  + ]:       1273 :         if (rrset.getType() == RRType::RRSIG()) {
    1518                 :            :             // XXX: the current interface (based on the current sqlite3
    1519                 :            :             // data source schema) requires a separate "sigtype" column,
    1520                 :            :             // even though it won't be used in a newer implementation.
    1521                 :            :             // We should eventually clean up the schema design and simplify
    1522                 :            :             // the interface, but until then we have to conform to the schema.
    1523                 :         34 :             sigtype = dynamic_cast<const generic::RRSIG&>(rdata).
    1524 [ +  - ][ +  - ]:         68 :                 typeCovered().toText();
                 [ +  - ]
    1525                 :            :         }
    1526         [ +  - ]:       2546 :         const string& rdata_txt = rdata.toText();
    1527         [ +  + ]:       1273 :         if (journaling_) {
    1528                 :            :             const string journal[Accessor::DIFF_PARAM_COUNT] =
    1529 [ +  - ][ +  - ]:       3091 :                 { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata_txt };
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ #  # ]
    1530                 :        618 :             accessor_->addRecordDiff(zone_id_, serial_.getValue(),
    1531 [ +  + ][ +  + ]:       3090 :                                      Accessor::DIFF_ADD, journal);
                 [ +  + ]
    1532                 :            :         }
    1533         [ +  + ]:       1272 :         if (nsec3_type) {
    1534                 :            :             const string nsec3_columns[Accessor::ADD_NSEC3_COLUMN_COUNT] =
    1535 [ +  - ][ +  - ]:         36 :                 { cvtr.getNSEC3Name(), cvtr.getTTL(), cvtr.getType(),
                 [ +  - ]
    1536 [ +  - ][ +  - ]:         84 :                   rdata_txt };
         [ +  - ][ +  - ]
                 [ #  # ]
    1537 [ -  + ][ +  + ]:         60 :             accessor_->addNSEC3RecordToZone(nsec3_columns);
                 [ #  # ]
    1538                 :            :         } else {
    1539                 :            :             const string columns[Accessor::ADD_COLUMN_COUNT] =
    1540 [ +  - ][ +  - ]:       2520 :                 { cvtr.getName(), cvtr.getRevName(), cvtr.getTTL(),
    1541 [ +  - ][ +  - ]:      11340 :                   cvtr.getType(), sigtype, rdata_txt };
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ #  # ]
    1542 [ -  + ][ +  + ]:       8820 :             accessor_->addRecordToZone(columns);
                 [ #  # ]
    1543                 :            :         }
    1544                 :            :     }
    1545                 :       1124 : }
    1546                 :            : 
    1547                 :            : void
    1548                 :        669 : DatabaseUpdater::deleteRRset(const AbstractRRset& rrset) {
    1549                 :            :     // If this is the first operation, pretend we are starting a new delete
    1550                 :            :     // sequence after adds.  This will simplify the validation below.
    1551         [ +  + ]:        669 :     if (diff_phase_ == NOT_STARTED) {
    1552                 :         61 :         diff_phase_ = ADD;
    1553                 :            :     }
    1554                 :            : 
    1555                 :        669 :     validateAddOrDelete("delete", rrset, ADD, DELETE);
    1556                 :            : 
    1557                 :        655 :     RdataIteratorPtr it = rrset.getRdataIterator();
    1558         [ +  + ]:        655 :     if (journaling_) {
    1559                 :        625 :         diff_phase_ = DELETE;
    1560 [ +  - ][ +  + ]:        625 :         if (rrset.getType() == RRType::SOA()) {
    1561                 :            :             serial_ =
    1562         [ +  - ]:        620 :                 dynamic_cast<const generic::SOA&>(it->getCurrent()).
    1563 [ +  - ][ +  - ]:        620 :                 getSerial();
    1564                 :            :         }
    1565                 :            :     }
    1566                 :            : 
    1567                 :        655 :     RRParameterConverter cvtr(rrset);
    1568 [ +  - ][ +  - ]:       1312 :     for (; !it->isLast(); it->next()) {
                 [ +  + ]
    1569         [ +  - ]:        659 :         const Rdata& rdata = it->getCurrent();
    1570 [ +  - ][ +  - ]:        659 :         const bool nsec3_type = isNSEC3KindType(rrset.getType(), rdata);
    1571 [ +  - ][ +  - ]:       1318 :         const string& rdata_txt = it->getCurrent().toText();
    1572                 :            : 
    1573         [ +  + ]:        659 :         if (journaling_) {
    1574                 :            :             const string journal[Accessor::DIFF_PARAM_COUNT] =
    1575 [ +  - ][ +  - ]:       3127 :                 { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata_txt };
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ #  # ]
    1576                 :        625 :             accessor_->addRecordDiff(zone_id_, serial_.getValue(),
    1577 [ +  + ][ +  + ]:       3125 :                                      Accessor::DIFF_DELETE, journal);
                 [ +  + ]
    1578                 :            :         }
    1579                 :            :         const string params[Accessor::DEL_PARAM_COUNT] =
    1580                 :            :             { nsec3_type ? cvtr.getNSEC3Name() : cvtr.getName(),
    1581 [ +  + ][ +  - ]:       2628 :               cvtr.getType(), rdata_txt };
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ #  # ]
    1582         [ +  + ]:        657 :         if (nsec3_type) {
    1583         [ -  + ]:          6 :             accessor_->deleteNSEC3RecordInZone(params);
    1584                 :            :         } else {
    1585         [ -  + ]:        657 :             accessor_->deleteRecordInZone(params);
    1586                 :            :         }
    1587 [ +  + ][ #  # ]:       2628 :     }
    1588                 :        653 : }
    1589                 :            : 
    1590                 :            : void
    1591                 :         87 : DatabaseUpdater::commit() {
    1592         [ +  + ]:         87 :     if (committed_) {
    1593 [ +  - ][ +  - ]:          8 :         isc_throw(DataSourceError, "Duplicate commit attempt for "
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
    1594                 :            :                   << zone_name_ << "/" << zone_class_ << " on "
    1595                 :            :                   << db_name_);
    1596                 :            :     }
    1597 [ +  + ][ +  + ]:         83 :     if (journaling_ && diff_phase_ == DELETE) {
    1598         [ +  - ]:          4 :         isc_throw(isc::BadValue, "Update sequence not complete");
    1599                 :            :     }
    1600                 :         81 :     accessor_->commit();
    1601                 :         80 :     committed_ = true; // make sure the destructor won't trigger rollback
    1602                 :            : 
    1603                 :            :     // We release the accessor immediately after commit is completed so that
    1604                 :            :     // we don't hold the possible internal resource any longer.
    1605                 :         80 :     accessor_.reset();
    1606                 :            : 
    1607                 :         80 :     logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_COMMIT)
    1608 [ +  - ][ +  - ]:         80 :         .arg(zone_name_).arg(zone_class_).arg(db_name_);
                 [ +  - ]
    1609                 :         80 : }
    1610                 :            : 
    1611                 :            : // The updater factory
    1612                 :            : ZoneUpdaterPtr
    1613                 :        153 : DatabaseClient::getUpdater(const isc::dns::Name& name, bool replace,
    1614                 :            :                            bool journaling) const
    1615                 :            : {
    1616 [ +  + ][ +  + ]:        153 :     if (replace && journaling) {
    1617         [ +  - ]:          6 :         isc_throw(isc::BadValue, "Can't store journal and replace the whole "
    1618                 :            :                   "zone at the same time");
    1619                 :            :     }
    1620                 :        150 :     boost::shared_ptr<DatabaseAccessor> update_accessor(accessor_->clone());
    1621                 :        150 :     const std::pair<bool, int> zone(update_accessor->startUpdateZone(
    1622 [ +  - ][ +  - ]:        150 :                                         name.toText(), replace));
    1623         [ +  + ]:        150 :     if (!zone.first) {
    1624                 :            :         return (ZoneUpdaterPtr());
    1625                 :            :     }
    1626                 :            : 
    1627                 :            :     return (ZoneUpdaterPtr(new DatabaseUpdater(update_accessor, zone.second,
    1628 [ +  - ][ +  - ]:        149 :                                                name, rrclass_, journaling)));
    1629                 :            : }
    1630                 :            : 
    1631                 :            : //
    1632                 :            : // Zone journal reader using some database system as the underlying data
    1633                 :            : //  source.
    1634                 :            : //
    1635                 :            : class DatabaseJournalReader : public ZoneJournalReader {
    1636                 :            : private:
    1637                 :            :     // A shortcut typedef to keep the code concise.
    1638                 :            :     typedef DatabaseAccessor Accessor;
    1639                 :            : public:
    1640                 :            :     DatabaseJournalReader(boost::shared_ptr<Accessor> accessor, const Name& zone,
    1641                 :            :                           int zone_id, const RRClass& rrclass, uint32_t begin,
    1642                 :            :                           uint32_t end) :
    1643                 :            :         accessor_(accessor), zone_(zone), rrclass_(rrclass),
    1644         [ +  - ]:         23 :         begin_(begin), end_(end), finished_(false)
    1645                 :            :     {
    1646 [ +  + ][ +  - ]:         19 :         context_ = accessor_->getDiffs(zone_id, begin, end);
    1647                 :            :     }
    1648                 :         45 :     virtual ~DatabaseJournalReader() {}
    1649                 :        838 :     virtual ConstRRsetPtr getNextDiff() {
    1650         [ +  + ]:        838 :         if (finished_) {
    1651 [ +  - ][ +  - ]:          6 :             isc_throw(InvalidOperation,
                 [ +  - ]
    1652                 :            :                       "Diff read attempt past the end of sequence on "
    1653                 :            :                       << accessor_->getDBName());
    1654                 :            :         }
    1655                 :            : 
    1656         [ +  + ]:      10020 :         string data[Accessor::COLUMN_COUNT];
    1657 [ +  - ][ +  + ]:        835 :         if (!context_->getNext(data)) {
    1658                 :         10 :             finished_ = true;
    1659 [ +  - ][ +  - ]:         20 :             LOG_DEBUG(logger, DBG_TRACE_BASIC,
                 [ +  - ]
    1660                 :            :                       DATASRC_DATABASE_JOURNALREADER_END).
    1661 [ +  - ][ +  - ]:         10 :                 arg(zone_).arg(rrclass_).arg(accessor_->getDBName()).
         [ +  - ][ +  - ]
                 [ +  - ]
    1662 [ +  - ][ +  - ]:         10 :                 arg(begin_).arg(end_);
    1663                 :            :             return (ConstRRsetPtr());
    1664                 :            :         }
    1665                 :            : 
    1666                 :            :         try {
    1667                 :        824 :             RRsetPtr rrset(new RRset(Name(data[Accessor::NAME_COLUMN]),
    1668                 :            :                                      rrclass_,
    1669                 :            :                                      RRType(data[Accessor::TYPE_COLUMN]),
    1670 [ +  + ][ +  + ]:        825 :                                      RRTTL(data[Accessor::TTL_COLUMN])));
         [ +  + ][ +  - ]
         [ +  - ][ +  - ]
    1671         [ +  - ]:        822 :             rrset->addRdata(rdata::createRdata(rrset->getType(), rrclass_,
    1672 [ +  + ][ +  - ]:       1643 :                                                data[Accessor::RDATA_COLUMN]));
    1673 [ +  - ][ +  - ]:       1642 :             LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                 [ +  - ]
    1674                 :            :                       DATASRC_DATABASE_JOURNALREADER_NEXT).
    1675 [ +  - ][ +  - ]:        821 :                 arg(rrset->getName()).arg(rrset->getType()).
         [ +  - ][ +  - ]
                 [ +  - ]
    1676 [ +  - ][ +  - ]:       1642 :                 arg(zone_).arg(rrclass_).arg(accessor_->getDBName());
         [ +  - ][ +  - ]
    1677                 :            :             return (rrset);
    1678         [ -  + ]:          8 :         } catch (const Exception& ex) {
    1679 [ -  + ][ +  - ]:          8 :             LOG_ERROR(logger, DATASRC_DATABASE_JOURNALREADR_BADDATA).
                 [ -  + ]
    1680 [ -  + ][ -  + ]:          4 :                 arg(zone_).arg(rrclass_).arg(accessor_->getDBName()).
         [ -  + ][ -  + ]
                 [ -  + ]
    1681 [ -  + ][ -  + ]:          8 :                 arg(begin_).arg(end_).arg(ex.what());
                 [ -  + ]
    1682 [ -  + ][ -  + ]:          8 :             isc_throw(DataSourceError, "Failed to create RRset from diff on "
         [ -  + ][ -  + ]
                 [ -  + ]
    1683                 :            :                       << accessor_->getDBName());
    1684 [ +  + ][ +  + ]:       5010 :         }
    1685                 :            :     }
    1686                 :            : 
    1687                 :            : private:
    1688                 :            :     boost::shared_ptr<Accessor> accessor_;
    1689                 :            :     const Name zone_;
    1690                 :            :     const RRClass rrclass_;
    1691                 :            :     Accessor::IteratorContextPtr context_;
    1692                 :            :     const uint32_t begin_;
    1693                 :            :     const uint32_t end_;
    1694                 :            :     bool finished_;
    1695                 :            : };
    1696                 :            : 
    1697                 :            : // The JournalReader factory
    1698                 :            : pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
    1699                 :         22 : DatabaseClient::getJournalReader(const isc::dns::Name& zone,
    1700                 :            :                                  uint32_t begin_serial,
    1701                 :            :                                  uint32_t end_serial) const
    1702                 :            : {
    1703                 :         22 :     boost::shared_ptr<DatabaseAccessor> jnl_accessor(accessor_->clone());
    1704 [ +  - ][ +  - ]:         22 :     const pair<bool, int> zoneinfo(jnl_accessor->getZone(zone.toText()));
    1705         [ +  + ]:         22 :     if (!zoneinfo.first) {
    1706                 :            :         return (pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>(
    1707                 :            :                     ZoneJournalReader::NO_SUCH_ZONE,
    1708                 :            :                     ZoneJournalReaderPtr()));
    1709                 :            :     }
    1710                 :            : 
    1711                 :            :     try {
    1712                 :            :         const pair<ZoneJournalReader::Result, ZoneJournalReaderPtr> ret(
    1713                 :            :             ZoneJournalReader::SUCCESS,
    1714                 :            :             ZoneJournalReaderPtr(new DatabaseJournalReader(jnl_accessor,
    1715                 :            :                                                            zone,
    1716                 :            :                                                            zoneinfo.second,
    1717                 :            :                                                            rrclass_,
    1718                 :            :                                                            begin_serial,
    1719         [ +  - ]:         23 :                                                            end_serial)));
    1720 [ +  - ][ +  - ]:         30 :         LOG_DEBUG(logger, DBG_TRACE_BASIC,
                 [ +  - ]
    1721 [ +  - ][ +  - ]:         15 :                   DATASRC_DATABASE_JOURNALREADER_START).arg(zone).arg(rrclass_).
                 [ +  - ]
    1722 [ +  - ][ +  - ]:         30 :             arg(jnl_accessor->getDBName()).arg(begin_serial).arg(end_serial);
         [ +  - ][ +  - ]
    1723                 :            :         return (ret);
    1724 [ -  + ][ +  - ]:          8 :     } catch (const NoSuchSerial&) {
    1725                 :            :         return (pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>(
    1726                 :            :                     ZoneJournalReader::NO_SUCH_VERSION,
    1727                 :            :                     ZoneJournalReaderPtr()));
    1728                 :            :     }
    1729                 :            : }
    1730                 :            : }
    1731                 :       1871 : }

Generated by: LCOV version 1.9