"Fossies" - the Fresh Open Source Software Archive

Member "pdns-auth-4.2.0/modules/lmdbbackend/lmdbbackend.cc" (27 Aug 2019, 47817 Bytes) of package /linux/misc/dns/pdns-auth-4.2.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "lmdbbackend.cc" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * This file is part of PowerDNS or dnsdist.
    3  * Copyright -- PowerDNS.COM B.V. and its contributors
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of version 2 of the GNU General Public License as
    7  * published by the Free Software Foundation.
    8  *
    9  * In addition, for the avoidance of any doubt, permission is granted to
   10  * link this program with OpenSSL and to (re)distribute the binaries
   11  * produced as the result of such linking.
   12  *
   13  * This program is distributed in the hope that it will be useful,
   14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  * GNU General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU General Public License
   19  * along with this program; if not, write to the Free Software
   20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   21  */
   22 #ifdef HAVE_CONFIG_H
   23 #include "config.h"
   24 #endif
   25 #include "pdns/utility.hh"
   26 #include "pdns/dnsbackend.hh"
   27 #include "pdns/dns.hh"
   28 #include "pdns/dnspacket.hh"
   29 #include "pdns/base32.hh"
   30 #include "pdns/dnssecinfra.hh"
   31 #include "pdns/pdnsexception.hh"
   32 #include "pdns/logger.hh"
   33 #include "pdns/version.hh"
   34 #include "pdns/arguments.hh"
   35 #include <boost/archive/binary_oarchive.hpp>
   36 #include <boost/archive/binary_iarchive.hpp>
   37 #include <boost/serialization/vector.hpp>
   38 #include <boost/serialization/string.hpp>
   39 #include <boost/serialization/utility.hpp>
   40 // #include <boost/iostreams/stream.hpp>
   41 // #include <boost/iostreams/stream_buffer.hpp>
   42 
   43 #include <boost/iostreams/device/back_inserter.hpp>
   44 // #include <sstream>
   45 
   46 
   47 #include "lmdbbackend.hh"
   48 
   49 #define SCHEMAVERSION 1
   50 
   51 LMDBBackend::LMDBBackend(const std::string& suffix)
   52 {
   53   setArgPrefix("lmdb"+suffix);
   54   
   55   string syncMode = toLower(getArg("sync-mode"));
   56 
   57   if(syncMode == "nosync")
   58     d_asyncFlag = MDB_NOSYNC;
   59   else if(syncMode == "nometasync")
   60     d_asyncFlag = MDB_NOMETASYNC;
   61   else if(syncMode == "mapasync")
   62     d_asyncFlag = MDB_MAPASYNC;
   63   else if(syncMode.empty() || syncMode == "sync")
   64     d_asyncFlag = 0;
   65   else
   66     throw std::runtime_error("Unknown sync mode "+syncMode+" requested for LMDB backend");
   67 
   68   d_tdomains = std::make_shared<tdomains_t>(getMDBEnv(getArg("filename").c_str(), MDB_NOSUBDIR | d_asyncFlag, 0600), "domains");
   69   d_tmeta = std::make_shared<tmeta_t>(d_tdomains->getEnv(), "metadata");
   70   d_tkdb = std::make_shared<tkdb_t>(d_tdomains->getEnv(), "keydata");
   71   d_ttsig = std::make_shared<ttsig_t>(d_tdomains->getEnv(), "tsig");
   72   
   73   auto pdnsdbi = d_tdomains->getEnv()->openDB("pdns", MDB_CREATE);
   74   auto txn = d_tdomains->getEnv()->getRWTransaction();
   75   MDBOutVal _schemaversion;
   76   if(!txn.get(pdnsdbi, "schemaversion", _schemaversion)) {
   77     auto schemaversion = _schemaversion.get<uint32_t>();
   78     if (schemaversion != SCHEMAVERSION) {
   79       throw std::runtime_error("Expected LMDB schema version "+std::to_string(SCHEMAVERSION)+" but got "+std::to_string(schemaversion));
   80     }
   81   }
   82   else {
   83     txn.put(pdnsdbi, "schemaversion", SCHEMAVERSION);
   84   }
   85   MDBOutVal shards;
   86   if(!txn.get(pdnsdbi, "shards", shards)) {
   87     
   88     d_shards = shards.get<uint32_t>();
   89     if(d_shards != atoi(getArg("shards").c_str())) {
   90       g_log << Logger::Warning<<"Note: configured number of lmdb shards ("<<atoi(getArg("shards").c_str())<<") is different from on-disk ("<<d_shards<<"). Using on-disk shard number"<<endl;
   91     }
   92   }
   93   else {
   94     d_shards = atoi(getArg("shards").c_str());
   95     txn.put(pdnsdbi, "shards", d_shards);
   96   }
   97   txn.commit();
   98   d_trecords.resize(d_shards);
   99   d_dolog = ::arg().mustDo("query-logging");
  100 }
  101 
  102 
  103 
  104 namespace boost {
  105 namespace serialization {
  106 
  107 template<class Archive>
  108 void save(Archive & ar, const DNSName& g, const unsigned int version)
  109 {
  110   if(!g.empty()) {
  111     std::string tmp = g.toDNSStringLC(); // g++ 4.8 woes
  112     ar & tmp;
  113   }
  114   else
  115     ar & "";
  116 }
  117 
  118 template<class Archive>
  119 void load(Archive & ar, DNSName& g, const unsigned int version)
  120 {
  121   string tmp;
  122   ar & tmp;
  123   if(tmp.empty())
  124     g = DNSName();
  125   else
  126     g = DNSName(tmp.c_str(), tmp.size(), 0, false);
  127 }
  128 
  129 template<class Archive>
  130 void save(Archive & ar, const QType& g, const unsigned int version)
  131 {
  132   uint16_t tmp = g.getCode(); // g++ 4.8 woes
  133   ar & tmp;
  134 }
  135 
  136 template<class Archive>
  137 void load(Archive & ar, QType& g, const unsigned int version)
  138 {
  139   uint16_t tmp;
  140   ar & tmp; 
  141   g = QType(tmp);
  142 }
  143   
  144 template<class Archive>
  145 void serialize(Archive & ar, DomainInfo& g, const unsigned int version)
  146 {
  147   ar & g.zone;
  148   ar & g.last_check;
  149   ar & g.account;
  150   ar & g.masters;
  151   ar & g.id;
  152   ar & g.notified_serial;
  153   ar & g.kind;
  154 }
  155 
  156 template<class Archive>
  157 void serialize(Archive & ar, LMDBBackend::DomainMeta& g, const unsigned int version)
  158 {
  159   ar & g.domain & g.key & g.value;
  160 }
  161 
  162 template<class Archive>
  163 void serialize(Archive & ar, LMDBBackend::KeyDataDB& g, const unsigned int version)
  164 {
  165   ar & g.domain & g.content & g.flags & g.active;
  166 }
  167 
  168 template<class Archive>
  169 void serialize(Archive & ar, TSIGKey& g, const unsigned int version)
  170 {
  171   ar & g.name;
  172   ar & g.algorithm; // this is the ordername
  173   ar & g.key;
  174 }
  175 
  176 
  177   
  178 } // namespace serialization
  179 } // namespace boost
  180 
  181 BOOST_SERIALIZATION_SPLIT_FREE(DNSName);
  182 BOOST_SERIALIZATION_SPLIT_FREE(QType);
  183 BOOST_IS_BITWISE_SERIALIZABLE(ComboAddress);
  184 
  185 template<>
  186 std::string serToString(const DNSResourceRecord& rr)
  187 {
  188   // only does content, ttl, auth
  189   std::string ret;
  190   uint16_t len = rr.content.length();
  191   ret.reserve(2+len+8);
  192   
  193   ret.assign((const char*)&len, 2);
  194   ret += rr.content;
  195   ret.append((const char*)&rr.ttl, 4);
  196   ret.append(1, (char)rr.auth);
  197   ret.append(1, (char)false);
  198   ret.append(1, (char)rr.disabled);
  199   return ret;
  200 }
  201 
  202 template<>
  203 void serFromString(const string_view& str, DNSResourceRecord& rr)
  204 {
  205   uint16_t len;
  206   memcpy(&len, &str[0], 2);
  207   rr.content.assign(&str[2], len);    // len bytes
  208   memcpy(&rr.ttl, &str[2] + len, 4);
  209   rr.auth = str[str.size()-3];
  210   rr.disabled = str[str.size()-1];
  211   rr.wildcardname.clear();
  212 }
  213 
  214 
  215 std::string serializeContent(uint16_t qtype, const DNSName& domain, const std::string& content)
  216 {
  217   auto drc = DNSRecordContent::mastermake(qtype, 1, content);
  218   return drc->serialize(domain, false);
  219 }
  220 
  221 std::shared_ptr<DNSRecordContent> unserializeContentZR(uint16_t qtype, const DNSName& qname, const std::string& content)
  222 {
  223   if(qtype == QType::A && content.size() == 4) {
  224     return std::make_shared<ARecordContent>(*((uint32_t*)content.c_str()));
  225   }
  226   return DNSRecordContent::unserialize(qname, qtype, content);
  227 }
  228 
  229 
  230 /* design. If you ask a question without a zone id, we lookup the best
  231    zone id for you, and answer from that. This is different than other backends, but I can't see why it would not work.
  232 
  233    The index we use is "zoneid,canonical relative name". This index is also used
  234    for AXFR.
  235 
  236    Note - domain_id, name and type are ONLY present on the index!
  237 */
  238 
  239 #if BOOST_VERSION >= 106100
  240 #define StringView string_view
  241 #else
  242 #define StringView string
  243 #endif
  244 
  245 void LMDBBackend::deleteDomainRecords(RecordsRWTransaction& txn, uint32_t domain_id, uint16_t qtype)
  246 {
  247   compoundOrdername co;
  248   string match = co(domain_id);
  249 
  250   auto cursor = txn.txn.getCursor(txn.db->dbi);
  251   MDBOutVal key, val;
  252   //  cout<<"Match: "<<makeHexDump(match);
  253   if(!cursor.lower_bound(match, key, val) ) {
  254     while(key.get<StringView>().rfind(match, 0) == 0) {
  255       if(qtype == QType::ANY || co.getQType(key.get<StringView>()) == qtype)
  256         cursor.del(MDB_NODUPDATA);
  257       if(cursor.next(key, val)) break;
  258     } 
  259   }
  260 }
  261 
  262 /* Here's the complicated story. Other backends have just one transaction, which is either
  263    on or not. 
  264    
  265    You can't call feedRecord without a transaction started with startTransaction.
  266 
  267    However, other functions can be called after startTransaction() or without startTransaction()
  268      (like updateDNSSECOrderNameAndAuth)
  269 
  270 
  271 
  272 */
  273 
  274 bool LMDBBackend::startTransaction(const DNSName &domain, int domain_id)
  275 {
  276   // cout <<"startTransaction("<<domain<<", "<<domain_id<<")"<<endl;
  277   int real_id = domain_id;
  278   if(real_id < 0) {
  279     auto rotxn = d_tdomains->getROTransaction();
  280     DomainInfo di;
  281     real_id = rotxn.get<0>(domain, di);
  282     // cout<<"real_id = "<<real_id << endl;
  283     if(!real_id)
  284       return false;
  285   }
  286   if(d_rwtxn) {
  287     throw DBException("Attempt to start a transaction while one was open already");
  288   }
  289   d_rwtxn = getRecordsRWTransaction(real_id);
  290     
  291   d_transactiondomain = domain;
  292   d_transactiondomainid = real_id;
  293   if(domain_id >= 0) {
  294     deleteDomainRecords(*d_rwtxn, domain_id);
  295   }
  296 
  297   return true;
  298 }
  299 
  300 bool LMDBBackend::commitTransaction()
  301 {
  302   // cout<<"Commit transaction" <<endl;
  303   d_rwtxn->txn.commit();
  304   d_rwtxn.reset();
  305   return true;
  306 }
  307 
  308 bool LMDBBackend::abortTransaction()
  309 {
  310   // cout<<"Abort transaction"<<endl;
  311   d_rwtxn->txn.abort();
  312   d_rwtxn.reset();
  313 
  314   return true;
  315 }
  316 
  317 // d_rwtxn must be set here
  318 bool LMDBBackend::feedRecord(const DNSResourceRecord &r, const DNSName &ordername, bool ordernameIsNSEC3)
  319 {
  320   DNSResourceRecord rr(r);
  321   rr.qname.makeUsRelative(d_transactiondomain);
  322   rr.content = serializeContent(rr.qtype.getCode(), r.qname, rr.content);
  323   rr.disabled = false;
  324 
  325   compoundOrdername co;
  326   d_rwtxn->txn.put(d_rwtxn->db->dbi, co(r.domain_id, rr.qname, rr.qtype.getCode()), serToString(rr));
  327 
  328   if(ordernameIsNSEC3 && !ordername.empty()) {
  329     MDBOutVal val;
  330     if(d_rwtxn->txn.get(d_rwtxn->db->dbi, co(r.domain_id, rr.qname, QType::NSEC3), val)) {
  331       rr.ttl = 0;
  332       rr.content=rr.qname.toDNSStringLC();
  333       rr.auth = 0;
  334       string ser = serToString(rr);
  335       d_rwtxn->txn.put(d_rwtxn->db->dbi, co(r.domain_id, ordername, QType::NSEC3), ser);
  336 
  337       rr.ttl = 1;
  338       rr.content = ordername.toDNSString();
  339       ser = serToString(rr);
  340       d_rwtxn->txn.put(d_rwtxn->db->dbi, co(r.domain_id, rr.qname, QType::NSEC3), ser);
  341     }
  342   }
  343   return true;
  344 }
  345 
  346 bool LMDBBackend::feedEnts(int domain_id, map<DNSName,bool>& nonterm)
  347 {
  348   DNSResourceRecord rr;
  349   rr.ttl = 0;
  350   compoundOrdername co;
  351   for(const auto& nt: nonterm) {
  352     rr.qname = nt.first.makeRelative(d_transactiondomain);
  353     rr.auth = nt.second;
  354     rr.disabled = true;
  355 
  356     std::string ser = serToString(rr);
  357     d_rwtxn->txn.put(d_rwtxn->db->dbi, co(domain_id, rr.qname, 0), ser);
  358   }
  359   return true;
  360 }
  361 
  362 bool LMDBBackend::feedEnts3(int domain_id, const DNSName &domain, map<DNSName,bool> &nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow)
  363 {
  364   string ser;
  365   DNSName ordername;
  366   DNSResourceRecord rr;
  367   compoundOrdername co;
  368   for(const auto& nt: nonterm) {
  369     rr.qname = nt.first.makeRelative(domain);
  370     rr.ttl = 0;
  371     rr.auth = nt.second;
  372     rr.disabled = nt.second;
  373     ser = serToString(rr);
  374     d_rwtxn->txn.put(d_rwtxn->db->dbi, co(domain_id, rr.qname, 0), ser);
  375 
  376     if(!narrow && rr.auth) {
  377       rr.content = rr.qname.toDNSString();
  378       rr.auth = false;
  379       rr.disabled = false;
  380       ser = serToString(rr);
  381 
  382       ordername=DNSName(toBase32Hex(hashQNameWithSalt(ns3prc, nt.first)));
  383       d_rwtxn->txn.put(d_rwtxn->db->dbi, co(domain_id, ordername, QType::NSEC3), ser);
  384 
  385       rr.ttl = 1;
  386       rr.content = ordername.toDNSString();
  387       ser = serToString(rr);
  388       d_rwtxn->txn.put(d_rwtxn->db->dbi, co(domain_id, rr.qname, QType::NSEC3), ser);
  389     }
  390   }
  391   return true;
  392 }
  393 
  394 
  395 // might be called within a transaction, might also be called alone
  396 bool LMDBBackend::replaceRRSet(uint32_t domain_id, const DNSName& qname, const QType& qt, const vector<DNSResourceRecord>& rrset)
  397 {
  398   // zonk qname/qtype within domain_id (go through qname, check domain_id && qtype)
  399   shared_ptr<RecordsRWTransaction> txn;
  400   bool needCommit = false;
  401   if(d_rwtxn && d_transactiondomainid==domain_id) {
  402     txn = d_rwtxn;
  403     //    cout<<"Reusing open transaction"<<endl;
  404   }
  405   else {
  406     //    cout<<"Making a new RW txn for replace rrset"<<endl;
  407     txn = getRecordsRWTransaction(domain_id);
  408     needCommit = true;
  409   }
  410 
  411   DomainInfo di;
  412   if (!d_tdomains->getROTransaction().get(domain_id, di)) {
  413     return false;
  414   }
  415 
  416   compoundOrdername co;
  417   auto cursor = txn->txn.getCursor(txn->db->dbi);
  418   MDBOutVal key, val;
  419   string match =co(domain_id, qname.makeRelative(di.zone), qt.getCode());
  420   if(!cursor.find(match, key, val)) {
  421     do {
  422       cursor.del(MDB_NODUPDATA);
  423     } while(!cursor.next(key, val) && key.get<StringView>().rfind(match, 0) == 0);
  424   }
  425 
  426   for(auto rr : rrset) {
  427     rr.content = serializeContent(rr.qtype.getCode(), rr.qname, rr.content);
  428     rr.qname.makeUsRelative(di.zone);
  429     txn->txn.put(txn->db->dbi, match, serToString(rr));
  430   }
  431 
  432   if(needCommit)
  433     txn->txn.commit();
  434 
  435   return true;
  436 }
  437 
  438 // tempting to templatize these two functions but the pain is not worth it
  439 std::shared_ptr<LMDBBackend::RecordsRWTransaction> LMDBBackend::getRecordsRWTransaction(uint32_t id)
  440 {
  441   auto& shard =d_trecords[id % d_shards];
  442   if(!shard.env) {
  443     shard.env = getMDBEnv( (getArg("filename")+"-"+std::to_string(id % d_shards)).c_str(),
  444                            MDB_NOSUBDIR | d_asyncFlag, 0600);
  445     shard.dbi = shard.env->openDB("records", MDB_CREATE | MDB_DUPSORT);
  446   }
  447   auto ret = std::make_shared<RecordsRWTransaction>(shard.env->getRWTransaction());
  448   ret->db = std::make_shared<RecordsDB>(shard);
  449 
  450   return ret;
  451 }
  452 
  453 std::shared_ptr<LMDBBackend::RecordsROTransaction> LMDBBackend::getRecordsROTransaction(uint32_t id)
  454 {
  455   auto& shard =d_trecords[id % d_shards];
  456   if(!shard.env) {
  457     shard.env = getMDBEnv( (getArg("filename")+"-"+std::to_string(id % d_shards)).c_str(),
  458                            MDB_NOSUBDIR | d_asyncFlag, 0600);
  459     shard.dbi = shard.env->openDB("records", MDB_CREATE | MDB_DUPSORT);
  460   }
  461   
  462   auto ret = std::make_shared<RecordsROTransaction>(shard.env->getROTransaction());
  463   ret->db = std::make_shared<RecordsDB>(shard);
  464   return ret;
  465 }
  466 
  467 
  468 bool LMDBBackend::deleteDomain(const DNSName &domain)
  469 {
  470   auto doms = d_tdomains->getRWTransaction();
  471 
  472   DomainInfo di;
  473   auto id = doms.get<0>(domain, di); 
  474   if(!id)
  475     return false;
  476   
  477   shared_ptr<RecordsRWTransaction> txn;
  478   bool needCommit = false;
  479   if(d_rwtxn && d_transactiondomainid == id) {
  480     txn = d_rwtxn;
  481     //    cout<<"Reusing open transaction"<<endl;
  482   }
  483   else {
  484     //    cout<<"Making a new RW txn for delete domain"<<endl;
  485     txn = getRecordsRWTransaction(id);
  486     needCommit = true;
  487   }
  488 
  489   
  490   doms.del(id);
  491   compoundOrdername co;
  492   string match=co(id);
  493 
  494   auto cursor = txn->txn.getCursor(txn->db->dbi);
  495   MDBOutVal key, val;
  496   if(!cursor.find(match, key, val)) {
  497     do {
  498       cursor.del(MDB_NODUPDATA);
  499     } while(!cursor.next(key, val) && key.get<StringView>().rfind(match, 0) == 0);
  500   }
  501 
  502   if(needCommit)
  503     txn->txn.commit();
  504   
  505   doms.commit();
  506 
  507   return true;
  508 }
  509 
  510 bool LMDBBackend::list(const DNSName &target, int id, bool include_disabled)
  511 {
  512   d_inlist=true;
  513   DomainInfo di;
  514   {
  515     auto dtxn = d_tdomains->getROTransaction();
  516     
  517     if((di.id = dtxn.get<0>(target, di))) 
  518       ; //      cout<<"Found domain "<<target<<" on domain_id "<<di.id <<", list requested "<<id<<endl;
  519     else {
  520       // cout<<"Did not find "<<target<<endl;
  521       return false;
  522     }
  523   }
  524   
  525   d_rotxn = getRecordsROTransaction(di.id);
  526   compoundOrdername co;
  527   d_matchkey = co(di.id);
  528   d_getcursor = std::make_shared<MDBROCursor>(d_rotxn->txn.getCursor(d_rotxn->db->dbi));
  529   MDBOutVal key, val;
  530   d_inlist = true;
  531   
  532   if(d_getcursor->lower_bound(d_matchkey, key, val) || key.get<StringView>().rfind(d_matchkey, 0) != 0) {
  533     // cout<<"Found nothing for list"<<endl;
  534     d_getcursor.reset();
  535     return true;
  536   }
  537   
  538   d_lookupqname = target;
  539   
  540   return true;
  541 }
  542 
  543 void LMDBBackend::lookup(const QType &type, const DNSName &qdomain, DNSPacket *p, int zoneId)
  544 {
  545   if(d_dolog) {
  546     g_log << Logger::Warning << "Got lookup for "<<qdomain<<"|"<<type.getName()<<" in zone "<< zoneId<<endl;
  547     d_dtime.set();
  548   }
  549   DNSName hunt(qdomain);
  550   DomainInfo di;
  551   if(zoneId < 0) {
  552     auto rotxn = d_tdomains->getROTransaction();
  553     
  554     do {
  555       zoneId = rotxn.get<0>(hunt, di);
  556     } while (!zoneId && type != QType::SOA && hunt.chopOff());
  557     if(zoneId <= 0) {
  558       //      cout << "Did not find zone for "<< qdomain<<endl;
  559       d_getcursor.reset();
  560       return;
  561     }
  562   }
  563   else {
  564     if(!d_tdomains->getROTransaction().get(zoneId, di)) {
  565       // cout<<"Could not find a zone with id "<<zoneId<<endl;
  566       d_getcursor.reset();
  567       return;
  568     }
  569     hunt = di.zone;
  570   }
  571     
  572   DNSName relqname = qdomain.makeRelative(hunt);
  573   //  cout<<"get will look for "<<relqname<< " in zone "<<hunt<<" with id "<<zoneId<<endl;
  574   d_rotxn = getRecordsROTransaction(zoneId);
  575 
  576   compoundOrdername co;
  577   d_getcursor = std::make_shared<MDBROCursor>(d_rotxn->txn.getCursor(d_rotxn->db->dbi));
  578   MDBOutVal key, val;
  579   if(type.getCode() == QType::ANY) {
  580     d_matchkey = co(zoneId,relqname);
  581   }
  582   else {
  583     d_matchkey= co(zoneId,relqname, type.getCode());
  584   }
  585   d_inlist=false;
  586   
  587   if(d_getcursor->lower_bound(d_matchkey, key, val) || key.get<StringView>().rfind(d_matchkey, 0) != 0) {
  588     d_getcursor.reset();
  589     if(d_dolog) {
  590       g_log<<Logger::Warning<< "Query "<<((long)(void*)this)<<": "<<d_dtime.udiffNoReset()<<" usec to execute (found nothing)"<<endl;
  591     }
  592     return;
  593   }
  594   
  595   if(d_dolog) {
  596     g_log<<Logger::Warning<< "Query "<<((long)(void*)this)<<": "<<d_dtime.udiffNoReset()<<" usec to execute"<<endl;
  597   }
  598     
  599   d_lookuptype=type;
  600   d_lookupqname = qdomain;
  601   d_lookupdomain = hunt;
  602   d_lookupdomainid = zoneId;
  603 }
  604 
  605 bool LMDBBackend::get(DNSZoneRecord& rr)
  606 {
  607   if(d_inlist)
  608     return get_list(rr);
  609   else
  610     return get_lookup(rr);
  611 }
  612 
  613 bool LMDBBackend::get(DNSResourceRecord& rr)
  614 {
  615   //  cout <<"Old-school get called"<<endl;
  616   DNSZoneRecord dzr;
  617   if(d_inlist) {
  618     if(!get_list(dzr))
  619       return false;
  620   }
  621   else {
  622     if(!get_lookup(dzr))
  623       return false;
  624   }
  625   rr.qname = dzr.dr.d_name;
  626   rr.ttl = dzr.dr.d_ttl;
  627   rr.qtype =dzr.dr.d_type;
  628   rr.content = dzr.dr.d_content->getZoneRepresentation(true);
  629   rr.domain_id = dzr.domain_id;
  630   rr.auth = dzr.auth;
  631   //  cout<<"old school called for "<<rr.qname<<", "<<rr.qtype.getName()<<endl;
  632   return true;
  633 }
  634 
  635 bool LMDBBackend::getSOA(const DNSName &domain, SOAData &sd)
  636 {
  637   //  cout <<"Native getSOA called"<<endl;
  638   lookup(QType(QType::SOA), domain, 0, -1);
  639   DNSZoneRecord dzr;
  640   bool found=false;
  641   while(get(dzr)) {
  642     auto src = getRR<SOARecordContent>(dzr.dr);
  643     sd.domain_id = dzr.domain_id;
  644     sd.ttl = dzr.dr.d_ttl;
  645     sd.qname = dzr.dr.d_name;
  646     
  647     sd.nameserver = src->d_mname;
  648     sd.hostmaster = src->d_rname;
  649     sd.serial = src->d_st.serial;
  650     sd.refresh = src->d_st.refresh;
  651     sd.retry = src->d_st.retry;
  652     sd.expire = src->d_st.expire;
  653     sd.default_ttl = src->d_st.minimum;
  654     
  655     sd.db = this;
  656     found=true;
  657   }
  658   return found;
  659 }
  660 bool LMDBBackend::get_list(DNSZoneRecord& rr)
  661 {
  662   for(;;) {
  663     if(!d_getcursor)  {
  664       d_rotxn.reset();
  665       return false;
  666     }
  667     
  668     MDBOutVal keyv, val;
  669 
  670     d_getcursor->current(keyv, val);
  671     DNSResourceRecord drr;
  672     serFromString(val.get<string>(), drr);
  673   
  674     auto key = keyv.get<string_view>();
  675     rr.dr.d_name = compoundOrdername::getQName(key) + d_lookupqname;
  676     rr.domain_id = compoundOrdername::getDomainID(key);
  677     rr.dr.d_type = compoundOrdername::getQType(key).getCode();
  678     rr.dr.d_ttl = drr.ttl;
  679     rr.auth = drr.auth;
  680   
  681     if(rr.dr.d_type == QType::NSEC3) {
  682       //      cout << "Had a magic NSEC3, skipping it" << endl;
  683       if(d_getcursor->next(keyv, val) || keyv.get<StringView>().rfind(d_matchkey, 0) != 0) {
  684         d_getcursor.reset();
  685       }
  686       continue;
  687     }
  688     rr.dr.d_content = unserializeContentZR(rr.dr.d_type, rr.dr.d_name, drr.content);
  689     
  690     if(d_getcursor->next(keyv, val) || keyv.get<StringView>().rfind(d_matchkey, 0) != 0) {
  691       d_getcursor.reset();
  692     }
  693     break;
  694   }
  695   return true;
  696 }
  697 
  698 
  699 bool LMDBBackend::get_lookup(DNSZoneRecord& rr)
  700 {
  701   for(;;) {
  702     if(!d_getcursor) {
  703       d_rotxn.reset();
  704       return false;
  705     }
  706     MDBOutVal keyv, val;
  707     d_getcursor->current(keyv, val);
  708     DNSResourceRecord drr;
  709     serFromString(val.get<string>(), drr);
  710     
  711     auto key = keyv.get<string_view>();
  712     
  713     rr.dr.d_name = compoundOrdername::getQName(key) + d_lookupdomain;
  714     
  715     rr.domain_id = compoundOrdername::getDomainID(key);
  716     //  cout << "We found "<<rr.qname<< " in zone id "<<rr.domain_id <<endl;
  717     rr.dr.d_type = compoundOrdername::getQType(key).getCode();
  718     rr.dr.d_ttl = drr.ttl;
  719     if(rr.dr.d_type == QType::NSEC3) {
  720       //      cout << "Hit a magic NSEC3 skipping" << endl;
  721       if(d_getcursor->next(keyv, val) || keyv.get<StringView>().rfind(d_matchkey, 0) != 0) {
  722         d_getcursor.reset();
  723         d_rotxn.reset();
  724       }
  725       continue;
  726     }
  727     
  728     rr.dr.d_content = unserializeContentZR(rr.dr.d_type, rr.dr.d_name, drr.content);
  729     rr.auth = drr.auth;
  730     if(d_getcursor->next(keyv, val) || keyv.get<StringView>().rfind(d_matchkey, 0) != 0) {
  731       d_getcursor.reset();
  732       d_rotxn.reset();
  733     }
  734     break;
  735   }
  736 
  737   
  738   return true;
  739 }
  740 
  741 
  742 bool LMDBBackend::getDomainInfo(const DNSName &domain, DomainInfo &di, bool getSerial)
  743 {
  744   auto txn = d_tdomains->getROTransaction();
  745 
  746   if(!(di.id=txn.get<0>(domain, di)))
  747     return false;
  748   di.backend = this;
  749   return true;
  750 }
  751 
  752 
  753 int LMDBBackend::genChangeDomain(const DNSName& domain, std::function<void(DomainInfo&)> func)
  754 {
  755   auto txn = d_tdomains->getRWTransaction();
  756 
  757   DomainInfo di;
  758 
  759   auto id = txn.get<0>(domain, di);
  760   func(di);
  761   txn.put(di, id);
  762   
  763   txn.commit();
  764   return true;
  765 }
  766 
  767 int LMDBBackend::genChangeDomain(uint32_t id, std::function<void(DomainInfo&)> func)
  768 {
  769   DomainInfo di;
  770 
  771   auto txn = d_tdomains->getRWTransaction();
  772 
  773   if(!txn.get(id , di))
  774     return false;
  775   
  776   func(di);
  777   
  778   txn.put(di, id);
  779 
  780   txn.commit();
  781   return true;
  782 }
  783 
  784 
  785 bool LMDBBackend::setKind(const DNSName &domain, const DomainInfo::DomainKind kind)
  786 {
  787   return genChangeDomain(domain, [kind](DomainInfo& di) {
  788       di.kind = kind;
  789     });
  790 }
  791 
  792 bool LMDBBackend::setAccount(const DNSName &domain, const std::string& account)
  793 {
  794   return genChangeDomain(domain, [account](DomainInfo& di) {
  795       di.account = account;
  796     });
  797 }
  798 
  799 
  800 void LMDBBackend::setFresh(uint32_t domain_id)
  801 {
  802   genChangeDomain(domain_id, [](DomainInfo& di) {
  803       di.last_check = time(0);
  804     });
  805 }
  806 
  807 void LMDBBackend::setNotified(uint32_t domain_id, uint32_t serial)
  808 {
  809   genChangeDomain(domain_id, [serial](DomainInfo& di) {
  810       di.serial = serial;
  811     });
  812 }
  813 
  814 
  815 bool LMDBBackend::setMaster(const DNSName &domain, const std::string& ips)
  816 {
  817   vector<ComboAddress> masters;
  818   vector<string> parts;
  819   stringtok(parts, ips, " \t;,");
  820   for(const auto& ip : parts) 
  821     masters.push_back(ComboAddress(ip, 53));
  822   
  823   return genChangeDomain(domain, [&masters](DomainInfo& di) {
  824       di.masters = masters;
  825     });
  826 }
  827 
  828 bool LMDBBackend::createDomain(const DNSName &domain)
  829 {
  830   return createDomain(domain, "NATIVE", "", "");
  831 }
  832           
  833 bool LMDBBackend::createDomain(const DNSName &domain, const string &type, const string &masters, const string &account)
  834 {
  835   DomainInfo di;
  836 
  837   auto txn = d_tdomains->getRWTransaction();
  838   if(txn.get<0>(domain, di)) {
  839     throw DBException("Domain '"+domain.toLogString()+"' exists already");
  840   }
  841   
  842   di.zone = domain;
  843   if(pdns_iequals(type, "master"))
  844     di.kind = DomainInfo::Master;
  845   else if(pdns_iequals(type, "slave"))
  846     di.kind = DomainInfo::Slave;
  847   else if(pdns_iequals(type, "native"))
  848     di.kind = DomainInfo::Native;
  849   else
  850     throw DBException("Unable to create domain of unknown type '"+type+"'");
  851   di.account = account;
  852 
  853   txn.put(di);
  854   txn.commit();
  855 
  856   return true;
  857 }
  858 
  859 void LMDBBackend::getAllDomains(vector<DomainInfo> *domains, bool include_disabled)
  860 {
  861   compoundOrdername co;
  862   MDBOutVal val;
  863   domains->clear();
  864   auto txn = d_tdomains->getROTransaction();
  865   for(auto iter = txn.begin(); iter != txn.end(); ++iter) {
  866     DomainInfo di=*iter;
  867     di.id = iter.getID();
  868 
  869     auto txn = getRecordsROTransaction(iter.getID());
  870     if(!txn->txn.get(txn->db->dbi, co(di.id, g_rootdnsname, QType::SOA), val)) {
  871       DNSResourceRecord rr;
  872       serFromString(val.get<string_view>(), rr);
  873 
  874       if(rr.content.size() >= 5 * sizeof(uint32_t)) {
  875         uint32_t serial = *reinterpret_cast<uint32_t*>(&rr.content[rr.content.size() - (5 * sizeof(uint32_t))]);
  876         di.serial = ntohl(serial);
  877       }
  878     } else if(!include_disabled) {
  879       continue;
  880     }
  881     domains->push_back(di);
  882   }
  883 }
  884 
  885 void LMDBBackend::getUnfreshSlaveInfos(vector<DomainInfo>* domains)
  886 {
  887   //  cout<<"Start of getUnfreshSlaveInfos"<<endl;
  888   domains->clear();
  889   auto txn = d_tdomains->getROTransaction();
  890 
  891   time_t now = time(0);
  892   for(auto iter = txn.begin(); iter != txn.end(); ++iter) {
  893     if(iter->kind != DomainInfo::Slave)
  894       continue;
  895 
  896     auto txn2 = getRecordsROTransaction(iter.getID());
  897     compoundOrdername co;
  898     MDBOutVal val;
  899     uint32_t serial = 0;
  900     if(!txn2->txn.get(txn2->db->dbi, co(iter.getID(), g_rootdnsname, QType::SOA), val)) {
  901       DNSResourceRecord rr;
  902       serFromString(val.get<string_view>(), rr);
  903       struct soatimes 
  904       {
  905         uint32_t serial;
  906         uint32_t refresh;
  907         uint32_t retry;
  908         uint32_t expire;
  909         uint32_t minimum;
  910       } st;
  911 
  912       memcpy(&st, &rr.content[rr.content.size()-sizeof(soatimes)], sizeof(soatimes));
  913 
  914       if((time_t)(iter->last_check + ntohl(st.refresh)) >= now) { // still fresh
  915         continue; // try next domain
  916       }
  917       //      cout << di.last_check <<" + " <<sdata.refresh<<" > = " << now << "\n";
  918       serial = ntohl(st.serial);
  919     }
  920     else {
  921       //      cout << "Could not find SOA for "<<iter->zone<<" with id "<<iter.getID()<<endl;
  922       serial=0;  
  923     }
  924     DomainInfo di=*iter;    
  925     di.id = iter.getID();
  926     di.serial = serial;
  927 
  928     domains->push_back(di);
  929   }
  930   //  cout<<"END of getUnfreshSlaveInfos"<<endl;
  931 }
  932 
  933 bool LMDBBackend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta)
  934 {
  935   meta.clear();
  936   auto txn = d_tmeta->getROTransaction();
  937   auto range = txn.equal_range<0>(name);
  938   
  939   for(auto& iter = range.first; iter != range.second; ++iter) {
  940     meta[iter->key].push_back(iter->value);
  941   }
  942   return true;
  943 }
  944 
  945 bool LMDBBackend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
  946 {
  947   auto txn = d_tmeta->getRWTransaction();
  948 
  949   auto range = txn.equal_range<0>(name);
  950 
  951   for(auto& iter = range.first; iter != range.second; ++iter) {
  952     if(iter-> key == kind)
  953       iter.del();
  954   }
  955 
  956   for(const auto& m : meta) {
  957     DomainMeta dm{name, kind, m};
  958     txn.put(dm);
  959   }
  960   txn.commit();
  961   return true;
  962 
  963 }
  964 
  965 bool LMDBBackend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys)
  966 {
  967   auto txn = d_tkdb->getROTransaction();
  968   auto range = txn.equal_range<0>(name);
  969   for(auto& iter = range.first; iter != range.second; ++iter) {
  970     KeyData kd{iter->content, iter.getID(), iter->flags, iter->active};
  971     keys.push_back(kd);
  972   }
  973 
  974   return true;
  975 }
  976 
  977 bool LMDBBackend::removeDomainKey(const DNSName& name, unsigned int id)
  978 {
  979   auto txn = d_tkdb->getRWTransaction();
  980   KeyDataDB kdb;
  981   if(txn.get(id, kdb)) {
  982     if(kdb.domain == name) {
  983       txn.del(id);
  984       txn.commit();
  985       return true;
  986     }
  987   }
  988   // cout << "??? wanted to remove domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
  989   return true;
  990 }
  991 
  992 bool LMDBBackend::addDomainKey(const DNSName& name, const KeyData& key, int64_t& id)
  993 {
  994   auto txn = d_tkdb->getRWTransaction();
  995   KeyDataDB kdb{name, key.content, key.flags, key.active};
  996   id = txn.put(kdb);
  997   txn.commit();
  998     
  999   return true;
 1000 }
 1001 
 1002 bool LMDBBackend::activateDomainKey(const DNSName& name, unsigned int id)
 1003 {
 1004   auto txn = d_tkdb->getRWTransaction();
 1005   KeyDataDB kdb;
 1006   if(txn.get(id, kdb)) {
 1007     if(kdb.domain == name) {
 1008       txn.modify(id, [](KeyDataDB& kdb)
 1009                  {
 1010                    kdb.active = true;
 1011                  });
 1012       txn.commit();
 1013       return true;
 1014     }
 1015   }
 1016 
 1017   // cout << "??? wanted to activate domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
 1018   return true;
 1019 }
 1020 
 1021 bool LMDBBackend::deactivateDomainKey(const DNSName& name, unsigned int id)
 1022 {
 1023   auto txn = d_tkdb->getRWTransaction();
 1024   KeyDataDB kdb;
 1025   if(txn.get(id, kdb)) {
 1026     if(kdb.domain == name) {
 1027       txn.modify(id, [](KeyDataDB& kdb)
 1028                  {
 1029                    kdb.active = false;
 1030                  });
 1031       txn.commit();
 1032       return true;
 1033     }
 1034   }
 1035   // cout << "??? wanted to activate domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
 1036   return true;
 1037 }
 1038 
 1039 bool LMDBBackend::getBeforeAndAfterNamesAbsolute(uint32_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after) 
 1040 {
 1041   //  cout << __PRETTY_FUNCTION__<< ": "<<id <<", "<<qname << " " << unhashed<<endl;
 1042 
 1043   DomainInfo di;
 1044   if(!d_tdomains->getROTransaction().get(id, di)) {
 1045     // domain does not exist, tough luck
 1046     return false;
 1047   }
 1048   // cout <<"Zone: "<<di.zone<<endl;
 1049   
 1050   compoundOrdername co;
 1051   auto txn = getRecordsROTransaction(id);
 1052 
 1053   auto cursor = txn->txn.getCursor(txn->db->dbi);
 1054   MDBOutVal key, val;
 1055 
 1056   DNSResourceRecord rr;
 1057   
 1058   string matchkey = co(id, qname, QType::NSEC3);
 1059   if(cursor.lower_bound(matchkey, key, val)) {
 1060     // this is beyond the end of the database
 1061     // cout << "Beyond end of database!" << endl;
 1062     cursor.last(key, val);
 1063 
 1064     for(;;) {
 1065       if(co.getDomainID(key.get<StringView>()) != id) {
 1066         //cout<<"Last record also not part of this zone!"<<endl;
 1067         // this implies something is wrong in the database, nothing we can do
 1068         return false;
 1069       }
 1070       
 1071       if(co.getQType(key.get<StringView>()) == QType::NSEC3) {
 1072         serFromString(val.get<StringView>(), rr);
 1073         if(!rr.ttl) // the kind of NSEC3 we need
 1074           break;
 1075       }
 1076       if(cursor.prev(key, val)) {
 1077         // hit beginning of database, again means something is wrong with it
 1078         return false;
 1079       }
 1080     }
 1081     before = co.getQName(key.get<StringView>());
 1082     unhashed = DNSName(rr.content.c_str(), rr.content.size(), 0, false) + di.zone;
 1083 
 1084     // now to find after .. at the beginning of the zone
 1085     if(cursor.lower_bound(co(id), key, val)) {
 1086       // cout<<"hit end of zone find when we shouldn't"<<endl;
 1087       return false;
 1088     }
 1089     for(;;) {
 1090       if(co.getQType(key.get<StringView>()) == QType::NSEC3) {
 1091         serFromString(val.get<StringView>(), rr);
 1092         if(!rr.ttl)
 1093           break;
 1094       }
 1095 
 1096       if(cursor.next(key, val) || co.getDomainID(key.get<StringView>()) != id) {
 1097         // cout<<"hit end of zone or database when we shouldn't"<<endl;
 1098         return false;
 1099       }
 1100     }
 1101     after = co.getQName(key.get<StringView>());
 1102     // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
 1103     return true;
 1104   }
 1105 
 1106   // cout<<"Ended up at "<<co.getQName(key.get<StringView>()) <<endl;
 1107 
 1108   before = co.getQName(key.get<StringView>());
 1109   if(before == qname) { 
 1110     // cout << "Ended up on exact right node" << endl;
 1111     before = co.getQName(key.get<StringView>());
 1112     // unhashed should be correct now, maybe check?
 1113     if(cursor.next(key, val)) {
 1114       // xxx should find first hash now
 1115 
 1116       if(cursor.lower_bound(co(id), key, val)) {
 1117         // cout<<"hit end of zone find when we shouldn't for id "<<id<< __LINE__<<endl;
 1118         return false;
 1119       }
 1120       for(;;) {
 1121         if(co.getQType(key.get<StringView>()) == QType::NSEC3) {
 1122           serFromString(val.get<StringView>(), rr);
 1123           if(!rr.ttl)
 1124             break;
 1125         }
 1126         
 1127         if(cursor.next(key, val) || co.getDomainID(key.get<StringView>()) != id) {
 1128           // cout<<"hit end of zone or database when we shouldn't" << __LINE__<<endl;
 1129           return false;
 1130         }
 1131       }
 1132       after = co.getQName(key.get<StringView>());
 1133       // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
 1134       return true;
 1135     }
 1136   }
 1137   else {
 1138     // cout <<"Going backwards to find 'before'"<<endl;
 1139     int count=0;
 1140     for(;;) {
 1141       if(co.getQName(key.get<StringView>()).canonCompare(qname) && co.getQType(key.get<StringView>()) == QType::NSEC3) {
 1142         // cout<<"Potentially stopping traverse at "<< co.getQName(key.get<StringView>()) <<", " << (co.getQName(key.get<StringView>()).canonCompare(qname))<<endl;
 1143         // cout<<"qname = "<<qname<<endl;
 1144         // cout<<"here  = "<<co.getQName(key.get<StringView>())<<endl;
 1145         serFromString(val.get<StringView>(), rr);
 1146         if(!rr.ttl) 
 1147           break;
 1148       }
 1149       
 1150       if(cursor.prev(key, val) || co.getDomainID(key.get<StringView>()) != id ) {
 1151         // cout <<"XXX Hit *beginning* of zone or database"<<endl;
 1152         // this can happen, must deal with it
 1153         // should now find the last hash of the zone
 1154 
 1155         if(cursor.lower_bound(co(id+1), key, val)) {
 1156           // cout << "Could not find the next higher zone, going to the end of the database then"<<endl;
 1157           cursor.last(key, val);
 1158         }
 1159         else
 1160           cursor.prev(key, val);
 1161 
 1162         for(;;) {
 1163           if(co.getDomainID(key.get<StringView>()) != id) {
 1164             //cout<<"Last record also not part of this zone!"<<endl;
 1165             // this implies something is wrong in the database, nothing we can do
 1166             return false;
 1167           }
 1168           
 1169           if(co.getQType(key.get<StringView>()) == QType::NSEC3) {
 1170             serFromString(val.get<StringView>(), rr);
 1171             if(!rr.ttl) // the kind of NSEC3 we need
 1172               break;
 1173           }
 1174           if(cursor.prev(key, val)) {
 1175             // hit beginning of database, again means something is wrong with it
 1176             return false;
 1177           }
 1178         }
 1179         before = co.getQName(key.get<StringView>());
 1180         unhashed = DNSName(rr.content.c_str(), rr.content.size(), 0, false) + di.zone;
 1181         // cout <<"Should still find 'after'!"<<endl;
 1182         // for 'after', we need to find the first hash of this zone
 1183 
 1184         if(cursor.lower_bound(co(id), key, val)) {
 1185           // cout<<"hit end of zone find when we shouldn't"<<endl;
 1186           // means database is wrong, nothing we can do
 1187           return false;
 1188         }
 1189         for(;;) {
 1190           if(co.getQType(key.get<StringView>()) == QType::NSEC3) {
 1191             serFromString(val.get<StringView>(), rr);
 1192             if(!rr.ttl)
 1193               break;
 1194           }
 1195           
 1196           if(cursor.next(key, val)) {
 1197             // means database is wrong, nothing we can do
 1198             // cout<<"hit end of zone when we shouldn't 2"<<endl;
 1199             return false;
 1200           }
 1201         }
 1202         after = co.getQName(key.get<StringView>());
 1203 
 1204         
 1205         // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
 1206         return true;
 1207       }
 1208       ++count;
 1209     }
 1210     before = co.getQName(key.get<StringView>());
 1211     unhashed = DNSName(rr.content.c_str(), rr.content.size(), 0, false) + di.zone;
 1212     // cout<<"Went backwards, found "<<before<<endl;
 1213     // return us to starting point
 1214     while(count--)
 1215       cursor.next(key, val);
 1216   }
 1217   //  cout<<"Now going forward"<<endl;
 1218   for(int count = 0 ;;++count) {
 1219     if((count && cursor.next(key, val)) || co.getDomainID(key.get<StringView>()) != id ) {
 1220       // cout <<"Hit end of database or zone, finding first hash then in zone "<<id<<endl;
 1221       if(cursor.lower_bound(co(id), key, val)) {
 1222         // cout<<"hit end of zone find when we shouldn't"<<endl;
 1223         // means database is wrong, nothing we can do
 1224         return false;
 1225       }
 1226       for(;;) {
 1227         if(co.getQType(key.get<StringView>()) == QType::NSEC3) {
 1228           serFromString(val.get<StringView>(), rr);
 1229           if(!rr.ttl)
 1230             break;
 1231         }
 1232         
 1233         if(cursor.next(key, val)) {
 1234           // means database is wrong, nothing we can do
 1235           // cout<<"hit end of zone when we shouldn't 2"<<endl;
 1236           return false;
 1237         }
 1238         // cout << "Next.. "<<endl;
 1239       }
 1240       after = co.getQName(key.get<StringView>());
 1241       
 1242       // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
 1243       return true;
 1244     }
 1245     
 1246     // cout<<"After "<<co.getQName(key.get<StringView>()) <<endl;
 1247     if(co.getQType(key.get<StringView>()) == QType::NSEC3) {
 1248       serFromString(val.get<StringView>(), rr);
 1249       if(!rr.ttl) {
 1250         break;
 1251       }
 1252     }
 1253   }
 1254   after = co.getQName(key.get<StringView>());
 1255   // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
 1256   return true;
 1257 }
 1258 
 1259 bool LMDBBackend::getBeforeAndAfterNames(uint32_t id, const DNSName& zonenameU, const DNSName& qname, DNSName& before, DNSName& after)
 1260 {
 1261   DNSName zonename = zonenameU.makeLowerCase();
 1262   //  cout << __PRETTY_FUNCTION__<< ": "<<id <<", "<<zonename << ", '"<<qname<<"'"<<endl;
 1263 
 1264   auto txn = getRecordsROTransaction(id);
 1265   compoundOrdername co;
 1266   DNSName qname2 = qname.makeRelative(zonename);
 1267   string matchkey=co(id,qname2);
 1268   auto cursor = txn->txn.getCursor(txn->db->dbi);
 1269   MDBOutVal key, val;
 1270   // cout<<"Lower_bound for "<<qname2<<endl;
 1271   if(cursor.lower_bound(matchkey, key, val)) {
 1272     // cout << "Hit end of database, bummer"<<endl;
 1273     cursor.last(key, val);
 1274     if(co.getDomainID(key.get<string_view>()) == id) {
 1275       before = co.getQName(key.get<string_view>()) + zonename;
 1276       after = zonename;
 1277     }
 1278     // else
 1279       // cout << "We were at end of database, but this zone is not there?!"<<endl;
 1280     return true;
 1281   }
 1282   // cout<<"Cursor is at "<<co.getQName(key.get<string_view>()) <<", in zone id "<<co.getDomainID(key.get<string_view>())<< endl;
 1283 
 1284   if(co.getQType(key.get<string_view>()).getCode() && co.getDomainID(key.get<string_view>()) ==id && co.getQName(key.get<string_view>()) == qname2) { // don't match ENTs
 1285     // cout << "Had an exact match!"<<endl;
 1286     before = qname2 + zonename;
 1287     int rc;
 1288     for(;;) {
 1289       rc=cursor.next(key, val);
 1290       if(rc) break;
 1291       
 1292       if(co.getDomainID(key.get<string_view>()) == id && key.get<StringView>().rfind(matchkey, 0)==0)
 1293         continue;
 1294       DNSResourceRecord rr;
 1295       serFromString(val.get<StringView>(), rr);
 1296       if(co.getQType(key.get<string_view>()).getCode() && (rr.auth || co.getQType(key.get<string_view>()).getCode() == QType::NS))
 1297         break;
 1298     }
 1299     if(rc || co.getDomainID(key.get<string_view>()) != id) {
 1300       // cout << "We hit the end of the zone or database. 'after' is apex" << endl;
 1301       after=zonename;
 1302       return false;
 1303     }
 1304     after = co.getQName(key.get<string_view>()) + zonename;
 1305     return true;
 1306   }
 1307 
 1308   
 1309   if(co.getDomainID(key.get<string_view>()) != id) {
 1310     // cout << "Ended up in next zone, 'after' is zonename" <<endl;
 1311     after = zonename;
 1312     // cout << "Now hunting for previous" << endl;
 1313     int rc;
 1314     for(;;) {
 1315       rc=cursor.prev(key, val);
 1316       if(rc) {
 1317         // cout<<"Reversed into zone, but got not found from lmdb" <<endl;
 1318         return false;
 1319       }
 1320       
 1321       if(co.getDomainID(key.get<string_view>()) != id) {
 1322         // cout<<"Reversed into zone, but found wrong zone id " << co.getDomainID(key.get<string_view>()) << " != "<<id<<endl;
 1323         // "this can't happen"
 1324         return false;
 1325       }
 1326       DNSResourceRecord rr;
 1327       serFromString(val.get<StringView>(), rr);
 1328       if(co.getQType(key.get<string_view>()).getCode() && (rr.auth || co.getQType(key.get<string_view>()).getCode() == QType::NS))
 1329         break;
 1330     }
 1331 
 1332     before = co.getQName(key.get<string_view>()) + zonename;
 1333     // cout<<"Found: "<< before<<endl;
 1334     return true;
 1335   }
 1336 
 1337   // cout <<"We ended up after "<<qname<<", on "<<co.getQName(key.get<string_view>())<<endl;
 1338 
 1339   int skips = 0;
 1340   for(; ;) {
 1341     DNSResourceRecord rr;
 1342     serFromString(val.get<StringView>(), rr);
 1343     if(co.getQType(key.get<string_view>()).getCode() && (rr.auth || co.getQType(key.get<string_view>()).getCode() == QType::NS)) {
 1344       after = co.getQName(key.get<string_view>()) + zonename;
 1345       // cout <<"Found auth ("<<rr.auth<<") or an NS record "<<after<<", type: "<<co.getQType(key.get<string_view>()).getName()<<", ttl = "<<rr.ttl<<endl;
 1346       // cout << makeHexDump(val.get<string>()) << endl;
 1347       break;
 1348     }
 1349     // cout <<"  oops, " << co.getQName(key.get<string_view>()) << " was not auth "<<rr.auth<< " type=" << rr.qtype.getName()<<" or NS, so need to skip ahead a bit more" << endl;
 1350     int rc = cursor.next(key, val);
 1351     if(!rc)
 1352       ++skips;
 1353     if(rc || co.getDomainID(key.get<string_view>()) != id ) {
 1354       // cout << "  oops, hit end of database or zone. This means after is apex" <<endl;
 1355       after = zonename;
 1356       break;
 1357     }
 1358   }
 1359   // go back to where we were
 1360   while(skips--)
 1361     cursor.prev(key,val);
 1362   
 1363   for(;;) {
 1364     int rc = cursor.prev(key, val);
 1365     if(rc || co.getDomainID(key.get<string_view>()) != id) {
 1366       // XX I don't think this case can happen
 1367       // cout << "We hit the beginning of the zone or database.. now what" << endl;
 1368       return false;
 1369     }
 1370     before = co.getQName(key.get<string_view>()) + zonename;
 1371     DNSResourceRecord rr;
 1372     serFromString(val.get<string_view>(), rr);
 1373     // cout<<"And before to "<<before<<", auth = "<<rr.auth<<endl;
 1374     if(co.getQType(key.get<string_view>()).getCode() && (rr.auth || co.getQType(key.get<string_view>()) == QType::NS))
 1375       break;
 1376     // cout << "Oops, that was wrong, go back one more"<<endl;
 1377   }
 1378 
 1379   return true;
 1380 
 1381 }
 1382 
 1383 bool LMDBBackend::updateDNSSECOrderNameAndAuth(uint32_t domain_id, const DNSName& qname, const DNSName& ordername, bool auth, const uint16_t qtype)
 1384 {
 1385   //  cout << __PRETTY_FUNCTION__<< ": "<< domain_id <<", '"<<qname <<"', '"<<ordername<<"', "<<auth<< ", " << qtype << endl;
 1386   shared_ptr<RecordsRWTransaction> txn;
 1387   bool needCommit = false;
 1388   if(d_rwtxn && d_transactiondomainid==domain_id) {
 1389     txn = d_rwtxn;
 1390     //    cout<<"Reusing open transaction"<<endl;
 1391   }
 1392   else {
 1393     //    cout<<"Making a new RW txn for " << __PRETTY_FUNCTION__ <<endl;
 1394     txn = getRecordsRWTransaction(domain_id);
 1395     needCommit = true;
 1396   }
 1397 
 1398   DomainInfo di;
 1399   if(!d_tdomains->getROTransaction().get(domain_id, di)) {
 1400     //    cout<<"Could not find domain_id "<<domain_id <<endl;
 1401     return false;
 1402   }
 1403 
 1404   DNSName rel = qname.makeRelative(di.zone);
 1405 
 1406   compoundOrdername co;
 1407   string matchkey = co(domain_id, rel);
 1408 
 1409   auto cursor = txn->txn.getCursor(txn->db->dbi);
 1410   MDBOutVal key, val;
 1411   if(cursor.lower_bound(matchkey, key, val)) {
 1412     // cout << "Could not find anything"<<endl;
 1413     return false;
 1414   }
 1415 
 1416   bool hasOrderName = !ordername.empty();
 1417   bool needNSEC3 = hasOrderName;
 1418 
 1419   for(; key.get<StringView>().rfind(matchkey,0) == 0; ) {
 1420     DNSResourceRecord rr;
 1421     rr.qtype = co.getQType(key.get<StringView>());
 1422 
 1423     if(rr.qtype != QType::NSEC3) {
 1424       serFromString(val.get<StringView>(), rr);
 1425       if(!needNSEC3 && qtype != QType::ANY) {
 1426         needNSEC3 = (rr.disabled && QType(qtype) != rr.qtype);
 1427       }
 1428 
 1429       if((qtype == QType::ANY || QType(qtype) == rr.qtype) && (rr.disabled != hasOrderName || rr.auth != auth)) {
 1430         rr.auth = auth;
 1431         rr.disabled = hasOrderName;
 1432         string repl = serToString(rr);
 1433         cursor.put(key, repl);
 1434       }
 1435     }
 1436 
 1437     if(cursor.next(key, val))
 1438       break;
 1439   }
 1440 
 1441   bool del = false;
 1442   DNSResourceRecord rr;
 1443   matchkey = co(domain_id,rel,QType::NSEC3);
 1444   if(!txn->txn.get(txn->db->dbi, matchkey, val)) {
 1445     serFromString(val.get<string_view>(), rr);
 1446 
 1447     if(needNSEC3) {
 1448       if(hasOrderName && rr.content != ordername.toDNSStringLC()) {
 1449           del = true;
 1450       }
 1451     } else {
 1452       del = true;
 1453     }
 1454     if(del) {
 1455       txn->txn.del(txn->db->dbi, co(domain_id, DNSName(rr.content.c_str(), rr.content.size(), 0, false), QType::NSEC3));
 1456       txn->txn.del(txn->db->dbi, matchkey);
 1457     }
 1458   } else {
 1459     del = true;
 1460   }
 1461 
 1462   if(hasOrderName && del) {
 1463     matchkey = co(domain_id,rel,QType::NSEC3);
 1464 
 1465     rr.ttl=0;
 1466     rr.auth=0;
 1467     rr.content=rel.toDNSStringLC();
 1468 
 1469     string str = serToString(rr);
 1470     txn->txn.put(txn->db->dbi, co(domain_id,ordername,QType::NSEC3), str);
 1471     rr.ttl = 1;
 1472     rr.content = ordername.toDNSStringLC();
 1473     str = serToString(rr);
 1474     txn->txn.put(txn->db->dbi, matchkey, str);  // 2
 1475   }
 1476 
 1477   if(needCommit)
 1478     txn->txn.commit();
 1479   return false;
 1480 }
 1481 
 1482 bool LMDBBackend::updateEmptyNonTerminals(uint32_t domain_id, set<DNSName>& insert, set<DNSName>& erase, bool remove) 
 1483 {
 1484   // cout << __PRETTY_FUNCTION__<< ": "<< domain_id << ", insert.size() "<<insert.size()<<", "<<erase.size()<<", " <<remove<<endl;
 1485 
 1486   bool needCommit = false;
 1487   shared_ptr<RecordsRWTransaction> txn;
 1488   if(d_rwtxn && d_transactiondomainid == domain_id) {
 1489     txn = d_rwtxn;
 1490     //    cout<<"Reusing open transaction"<<endl;
 1491   }
 1492   else {
 1493     //    cout<<"Making a new RW txn for delete domain"<<endl;
 1494     txn = getRecordsRWTransaction(domain_id);
 1495     needCommit = true;
 1496   }
 1497 
 1498   
 1499   // if remove is set, all ENTs should be removed & nothing else should be done
 1500   if(remove) {
 1501     deleteDomainRecords(*txn, domain_id, 0);
 1502   }
 1503   else {
 1504     DomainInfo di;
 1505     auto rotxn = d_tdomains->getROTransaction();
 1506     if(!rotxn.get(domain_id, di)) {
 1507       // cout <<"No such domain with id "<<domain_id<<endl;
 1508       return false;
 1509     }
 1510     compoundOrdername co;
 1511     for(const auto& n : insert) {
 1512       DNSResourceRecord rr;
 1513       rr.qname = n.makeRelative(di.zone);
 1514       rr.ttl = 0;
 1515       rr.auth = true;
 1516 
 1517       std::string ser = serToString(rr);
 1518 
 1519       txn->txn.put(txn->db->dbi, co(domain_id, rr.qname, 0), ser);
 1520 
 1521       DNSResourceRecord rr2;
 1522       serFromString(ser, rr2);
 1523       
 1524       // cout <<" +"<<n<<endl;
 1525     }
 1526     for(auto n : erase) {
 1527       // cout <<" -"<<n<<endl;
 1528       n.makeUsRelative(di.zone);
 1529       txn->txn.del(txn->db->dbi, co(domain_id, n, 0));
 1530     }
 1531   }
 1532   if(needCommit)
 1533     txn->txn.commit();
 1534   return false;
 1535 }
 1536 
 1537 /* TSIG */
 1538 bool LMDBBackend::getTSIGKey(const DNSName& name, DNSName* algorithm, string* content)
 1539 {
 1540   auto txn = d_ttsig->getROTransaction();
 1541 
 1542   TSIGKey tk;
 1543   if(!txn.get<0>(name, tk))
 1544     return false;
 1545   if(algorithm)
 1546     *algorithm = tk.algorithm;
 1547   if(content)
 1548     *content = tk.key;
 1549   return true;
 1550 
 1551 }
 1552 // this deletes an old key if it has the same algorithm
 1553 bool LMDBBackend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content)
 1554 {
 1555   auto txn = d_ttsig->getRWTransaction();
 1556 
 1557   for(auto range = txn.equal_range<0>(name); range.first != range.second; ++range.first) {
 1558     if(range.first->algorithm == algorithm)
 1559       range.first.del();
 1560   }
 1561 
 1562   TSIGKey tk;
 1563   tk.name = name;
 1564   tk.algorithm = algorithm;
 1565   tk.key=content;
 1566   
 1567   txn.put(tk);
 1568   txn.commit();
 1569   
 1570   return true;
 1571 }
 1572 bool LMDBBackend::deleteTSIGKey(const DNSName& name)
 1573 {
 1574   auto txn = d_ttsig->getRWTransaction();
 1575   TSIGKey tk;
 1576 
 1577   for(auto range = txn.equal_range<0>(name); range.first != range.second; ++range.first) {
 1578     range.first.del();
 1579   }
 1580   txn.commit();
 1581   return true;
 1582 }
 1583 bool LMDBBackend::getTSIGKeys(std::vector< struct TSIGKey > &keys)
 1584 {
 1585   auto txn = d_ttsig->getROTransaction();
 1586 
 1587   keys.clear();
 1588   for(auto iter = txn.begin(); iter != txn.end(); ++iter) {
 1589     keys.push_back(*iter);
 1590   }
 1591   return false;
 1592 }
 1593 
 1594 
 1595 
 1596 
 1597 class LMDBFactory : public BackendFactory
 1598 {
 1599 public:
 1600   LMDBFactory() : BackendFactory("lmdb") {}
 1601   void declareArguments(const string &suffix="")
 1602   {
 1603     declare(suffix,"filename","Filename for lmdb","./pdns.lmdb");
 1604     declare(suffix,"sync-mode","Synchronisation mode: nosync, nometasync, mapasync, sync","mapasync");
 1605     // there just is no room for more on 32 bit
 1606     declare(suffix,"shards","Records database will be split into this number of shards", (sizeof(long) == 4) ? "2" : "64"); 
 1607   }
 1608   DNSBackend *make(const string &suffix="")
 1609   {
 1610     return new LMDBBackend(suffix);
 1611   }
 1612 };
 1613 
 1614 
 1615 
 1616 
 1617 /* THIRD PART */
 1618 
 1619 class LMDBLoader
 1620 {
 1621 public:
 1622   LMDBLoader()
 1623   {
 1624     BackendMakers().report(new LMDBFactory);
 1625     g_log << Logger::Info << "[lmdbbackend] This is the lmdb backend version " VERSION
 1626 #ifndef REPRODUCIBLE
 1627       << " (" __DATE__ " " __TIME__ ")"
 1628 #endif
 1629       << " reporting" << endl;
 1630   }  
 1631 };
 1632 
 1633 static LMDBLoader randomLoader;