]> granicus.if.org Git - pdns/commitdiff
import suck() from master
authorKees Monshouwer <mind04@monshouwer.org>
Thu, 30 Apr 2015 18:19:08 +0000 (20:19 +0200)
committermind04 <mind04@monshouwer.org>
Thu, 30 Apr 2015 22:12:18 +0000 (00:12 +0200)
pdns/slavecommunicator.cc

index 9d9433411df5b12a839d656d281475b63710a7b3..492ac412b9cc4e937e99bfcbee4576043d3c704c 100644 (file)
@@ -66,43 +66,21 @@ void CommunicatorClass::addSuckRequest(const string &domain, const string &maste
 void CommunicatorClass::suck(const string &domain,const string &remote)
 {
   L<<Logger::Error<<"Initiating transfer of '"<<domain<<"' from remote '"<<remote<<"'"<<endl;
-  uint32_t domain_id;
   PacketHandler P; // fresh UeberBackend
 
   DomainInfo di;
   di.backend=0;
-  bool first=true;    
+  bool transaction=false;
   try {
     UeberBackend *B=dynamic_cast<UeberBackend *>(P.getBackend());  // copy of the same UeberBackend
-    NSEC3PARAMRecordContent ns3pr, hadNs3pr;
-    bool narrow, hadNarrow=false;
     DNSSECKeeper dk (B); // reuse our backend for DNSSECKeeper
-    bool dnssecZone = false;
-    bool haveNSEC3=false;
-    if(dk.isSecuredZone(domain)) {
-      dnssecZone=true;
-      haveNSEC3=dk.getNSEC3PARAM(domain, &ns3pr, &narrow);
-      if (haveNSEC3) {
-        hadNs3pr = ns3pr;
-        hadNarrow = narrow;
-      }
-    }
-
-    const bool hadNSEC3 = haveNSEC3;
-    const bool hadPresigned = dk.isPresigned(domain);
-    const bool hadDnssecZone = dnssecZone;
 
     if(!B->getDomainInfo(domain, di) || !di.backend) { // di.backend and B are mostly identical
       L<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl;
       return;
     }
-    domain_id=di.id;
+    uint32_t domain_id=di.id;
 
-    Resolver::res_t recs;
-    set<string> nsset, qnames;
-    
-    ComboAddress raddr(remote, 53);
-    
     string tsigkeyname, tsigalgorithm, tsigsecret;
   
     if(dk.getTSIGForAccess(domain, remote, &tsigkeyname)) {
@@ -133,7 +111,7 @@ void CommunicatorClass::suck(const string &domain,const string &remote)
     
     vector<string> localaddr;
     ComboAddress laddr;
-    
+
     if(B->getDomainMetadata(domain, "AXFR-SOURCE", localaddr) && !localaddr.empty()) {
       try {
         laddr = ComboAddress(localaddr[0]);
@@ -144,18 +122,39 @@ void CommunicatorClass::suck(const string &domain,const string &remote)
         return;
       }
     } else {
-                 laddr.sin4.sin_family = 0;
+      laddr.sin4.sin_family = 0;
+    }
+
+    bool hadDnssecZone = false;
+    bool hadPresigned = false;
+    bool hadNSEC3 = false;
+    NSEC3PARAMRecordContent ns3pr, hadNs3pr;
+    bool isNarrow, hadNarrow=false;
+
+    if(dk.isSecuredZone(domain)) {
+      hadDnssecZone=true;
+      hadPresigned=dk.isPresigned(domain);
+      if (dk.getNSEC3PARAM(domain, &ns3pr, &isNarrow)) {
+        hadNSEC3 = true;
+        hadNs3pr = ns3pr;
+        hadNarrow = isNarrow;
+      }
     }
 
-    AXFRRetriever retriever(raddr, domain.c_str(), tsigkeyname, tsigalgorithm, tsigsecret,
-               (laddr.sin4.sin_family == 0) ? NULL : &laddr);
+    bool isDnssecZone = false;
+    bool isPresigned = false;
+    bool isNSEC3 = false;
+    bool optOutFlag = false;
 
-    bool gotPresigned = false;
-    bool gotNSEC3 = false;
-    bool gotOptOutFlag = false;
+    bool first=true;
+    bool firstNSEC3=true;
     unsigned int soa_serial = 0;
+    set<string> nsset, qnames, secured;
     vector<DNSResourceRecord> rrs;
-    set<string> secured;
+
+    ComboAddress raddr(remote, 53);
+    AXFRRetriever retriever(raddr, domain.c_str(), tsigkeyname, tsigalgorithm, tsigsecret, (laddr.sin4.sin_family == 0) ? NULL : &laddr);
+    Resolver::res_t recs;
     while(retriever.getChunk(recs)) {
       if(first) {
         L<<Logger::Error<<"AXFR started for '"<<domain<<"'"<<endl;
@@ -166,108 +165,180 @@ void CommunicatorClass::suck(const string &domain,const string &remote)
         if(i->qtype.getCode() == QType::OPT || i->qtype.getCode() == QType::TSIG) // ignore EDNS0 & TSIG
           continue;
 
-        if(!endsOn(i->qname, domain)) { 
+        if(!endsOn(i->qname, domain)) {
           L<<Logger::Error<<"Remote "<<remote<<" tried to sneak in out-of-zone data '"<<i->qname<<"'|"<<i->qtype.getName()<<" during AXFR of zone '"<<domain<<"', ignoring"<<endl;
           continue;
         }
 
-        if (i->qtype.getCode() == QType::NSEC3PARAM) {
-          ns3pr = NSEC3PARAMRecordContent(i->content);
-          narrow = false;
-          dnssecZone = haveNSEC3 = gotPresigned = gotNSEC3 = true;
-          continue;
-        } else if (i->qtype.getCode() == QType::NSEC3) {
-          dnssecZone = gotPresigned = true;
-          NSEC3RecordContent ns3rc(i->content);
-          gotOptOutFlag = ns3rc.d_flags & 1;
-          if (ns3rc.d_set.count(QType::NS) && !pdns_iequals(i->qname, domain))
-            secured.insert(toLower(makeRelative(i->qname, domain)));
-          continue;
-        } else if (i->qtype.getCode() == QType::NSEC) {
-          dnssecZone = gotPresigned = true;
-          continue;
-        }
-
-        if(i->qtype.getCode() == QType::SOA) {
-          if(soa_serial != 0)
-            continue; //skip the last SOA
-          SOAData sd;
-          fillSOAData(i->content,sd);
-          soa_serial = sd.serial;
+        vector<DNSResourceRecord> out;
+        if(!pdl || !pdl->axfrfilter(raddr, domain, *i, out)) {
+          out.push_back(*i);
         }
 
-        i->domain_id=domain_id;
+        BOOST_FOREACH(DNSResourceRecord& rr, out) {
+          switch(rr.qtype.getCode()) {
+            case QType::NSEC3PARAM: {
+              ns3pr = NSEC3PARAMRecordContent(rr.content);
+              isDnssecZone = isNSEC3 = true;
+              isNarrow = false;
+              continue;
+            }
+            case QType::NSEC3: {
+              NSEC3RecordContent ns3rc(rr.content);
+              if (firstNSEC3) {
+                isDnssecZone = isPresigned = true;
+                firstNSEC3 = false;
+              } else if (optOutFlag != (ns3rc.d_flags & 1))
+                throw AhuException("Zones with a mixture of Opt-Out NSEC3 RRs and non-Opt-Out NSEC3 RRs are not supported.");
+              optOutFlag = ns3rc.d_flags & 1;
+              if (ns3rc.d_set.count(QType::NS) && !pdns_iequals(rr.qname, domain))
+                secured.insert(toLower(makeRelative(rr.qname, domain)));
+              continue;
+            }
+            case QType::NSEC: {
+              isDnssecZone = isPresigned = true;
+              continue;
+            }
+            case QType::SOA: {
+              if(soa_serial != 0)
+                continue; //skip the last SOA
+              SOAData sd;
+              fillSOAData(rr.content,sd);
+              soa_serial = sd.serial;
+              break;
+            }
+            case QType::NS: {
+              if(!pdns_iequals(rr.qname, domain))
+                nsset.insert(rr.qname);
+              break;
+            }
+            default:
+              break;
+          }
 
-#if 0
-        if(i->qtype.getCode()>=60000)
-          throw DBException("Database can't store unknown record type "+lexical_cast<string>(i->qtype.getCode()-1024));
-#endif
+          qnames.insert(rr.qname);
 
-        vector<DNSResourceRecord> out;
-        if(pdl && pdl->axfrfilter(raddr, domain, *i, out)) {
-          BOOST_FOREACH(const DNSResourceRecord& rr, out) {
-            rrs.push_back(rr);
-          }
-        } else {
-          rrs.push_back(*i);
+          rr.domain_id=domain_id;
+          rrs.push_back(rr);
         }
       }
     }
 
+    if(isNSEC3) {
+      ns3pr.d_flags = optOutFlag ? 1 : 0;
+    }
+
 
-    BOOST_FOREACH(const DNSResourceRecord& rr, rrs) {
-      if(rr.qtype.getCode() == QType::NS && !pdns_iequals(rr.qname, domain))
-        nsset.insert(rr.qname);
-      qnames.insert(rr.qname);
+    if(!isPresigned) {
+      DNSSECKeeper::keyset_t keys = dk.getKeys(domain);
+      if(!keys.empty()) {
+        isDnssecZone = true;
+        isNSEC3 = hadNSEC3;
+        ns3pr = hadNs3pr;
+        optOutFlag = (hadNs3pr.d_flags & 1);
+        isNarrow = hadNarrow;
+      }
     }
 
-    if(dnssecZone) {
-      if(!haveNSEC3) 
+
+    if(isDnssecZone) {
+      if(!isNSEC3)
         L<<Logger::Info<<"Adding NSEC ordering information"<<endl;
-      else if(!narrow)
+      else if(!isNarrow)
         L<<Logger::Info<<"Adding NSEC3 hashed ordering information for '"<<domain<<"'"<<endl;
       else
         L<<Logger::Info<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
     }
 
-    if (hadPresigned && !gotNSEC3) { // not sure why this is here
-      // we only had NSEC3 because we were a presigned zone...
-      haveNSEC3 = false;
-    }
-
 
+    transaction=di.backend->startTransaction(domain, domain_id);
     L<<Logger::Error<<"Transaction started for '"<<domain<<"'"<<endl;
-    di.backend->startTransaction(domain, domain_id);
+
+    // update the presigned flag and NSEC3PARAM
+    if (isDnssecZone) {
+      // update presigned if there was a change
+      if (isPresigned && !hadPresigned) {
+        // zone is now presigned
+        dk.setPresigned(domain);
+      } else if (hadPresigned && !isPresigned) {
+        // zone is no longer presigned
+        dk.unsetPresigned(domain);
+      }
+      // update NSEC3PARAM
+      if (isNSEC3) {
+        // zone is NSEC3, only update if there was a change
+        if (!hadNSEC3 || (hadNarrow  != isNarrow) ||
+            (ns3pr.d_algorithm != hadNs3pr.d_algorithm) ||
+            (ns3pr.d_flags != hadNs3pr.d_flags) ||
+            (ns3pr.d_iterations != hadNs3pr.d_iterations) ||
+            (ns3pr.d_salt != hadNs3pr.d_salt)) {
+          dk.setNSEC3PARAM(domain, ns3pr, isNarrow);
+        }
+      } else if (hadNSEC3 ) {
+         // zone is no longer NSEC3
+         dk.unsetNSEC3PARAM(domain);
+      }
+    } else if (hadDnssecZone) {
+      // zone is no longer signed
+      if (hadPresigned) {
+        // remove presigned
+        dk.unsetPresigned(domain);
+      }
+      if (hadNSEC3) {
+        // unset NSEC3PARAM
+        dk.unsetNSEC3PARAM(domain);
+      }
+    }
 
     bool doent=true;
     uint32_t maxent = ::arg().asNum("max-ent-entries");
     string ordername, shorter;
-    set<string> nonterm, rrterm;
+    set<string> rrterm, nonterm;
+
 
     BOOST_FOREACH(DNSResourceRecord& rr, rrs) {
 
+      if(!isPresigned) {
+        if (rr.qtype.getCode() == QType::RRSIG)
+          continue;
+        if(isDnssecZone && rr.qtype.getCode() == QType::DNSKEY && !::arg().mustDo("direct-dnskey"))
+          continue;
+      }
+
       // Figure out auth and ents
       rr.auth=true;
       shorter=rr.qname;
       rrterm.clear();
       do {
         if(doent) {
-          if (!qnames.count(shorter) && !nonterm.count(shorter) && !rrterm.count(shorter))
+          if (!qnames.count(shorter))
             rrterm.insert(shorter);
         }
-        if(nsset.count(shorter) && rr.qtype.getCode() != QType::DS) {
+        if(nsset.count(shorter) && rr.qtype.getCode() != QType::DS)
           rr.auth=false;
-          break;
-        }
+
         if (pdns_iequals(shorter, domain)) // stop at apex
           break;
       }while(chopOff(shorter));
 
-      // Insert ents for auth rrs
-      if(doent && rr.auth) {
-        nonterm.insert(rrterm.begin(), rrterm.end());
+      // Insert ents
+      if(doent && !rrterm.empty()) {
+        //bool auth;
+        if (!rr.auth && rr.qtype.getCode() == QType::NS) {
+          if (isNSEC3)
+            ordername=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname)));
+          //auth=(!isNSEC3 || !optOutFlag || secured.count(ordername));
+        } //else
+          //auth=rr.auth;
+
+        BOOST_FOREACH(const string nt, rrterm){
+          if (!nonterm.count(nt))
+              nonterm.insert(nt);
+        }
+
         if(nonterm.size() > maxent) {
-          L<<Logger::Error<<"AXFR zone "<<domain<<" has too many empty non terminals."<<endl;
+          if (!::arg().asNum("max-ent-entries"))
+            L<<Logger::Error<<"AXFR zone "<<domain<<" has too many empty non terminals."<<endl;
           nonterm.clear();
           doent=false;
         }
@@ -278,11 +349,11 @@ void CommunicatorClass::suck(const string &domain,const string &remote)
         rr.auth=true;
 
       // Add ordername and insert record
-      if (dnssecZone && rr.qtype.getCode() != QType::RRSIG) {
-        if (haveNSEC3) {
+      if (isDnssecZone && rr.qtype.getCode() != QType::RRSIG) {
+        if (isNSEC3) {
           // NSEC3
           ordername=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname)));
-          if(!narrow && (rr.auth || (rr.qtype.getCode() == QType::NS && (!gotOptOutFlag || secured.count(ordername))))) {
+          if(!isNarrow && (rr.auth || (rr.qtype.getCode() == QType::NS && (!optOutFlag || secured.count(ordername))))) {
             di.backend->feedRecord(rr, &ordername);
           } else
             di.backend->feedRecord(rr);
@@ -300,44 +371,14 @@ void CommunicatorClass::suck(const string &domain,const string &remote)
 
     // Insert empty non-terminals
     if(doent && !nonterm.empty()) {
-      if (haveNSEC3) {
-        di.backend->feedEnts3(domain_id, domain, nonterm, ns3pr.d_iterations, ns3pr.d_salt, narrow);
+      if (isNSEC3) {
+        di.backend->feedEnts3(domain_id, domain, nonterm, ns3pr.d_iterations, ns3pr.d_salt, isNarrow);
       } else
         di.backend->feedEnts(domain_id, nonterm);
     }
 
-    // now we also need to update the presigned flag and NSEC3PARAM
-    // for the zone
-    if (gotPresigned) {
-      if (!hadDnssecZone && !hadPresigned) {
-        // zone is now presigned
-        dk.setPresigned(domain);
-      }
-
-      if (hadPresigned || !hadDnssecZone)
-      {
-        // this is a presigned zone, update NSEC3PARAM
-        if (gotNSEC3) {
-          ns3pr.d_flags = gotOptOutFlag ? 1 : 0;
-         // only update if there was a change
-          if (!hadNSEC3 || (narrow != hadNarrow) ||
-              (ns3pr.d_algorithm != hadNs3pr.d_algorithm) ||
-              (ns3pr.d_flags != hadNs3pr.d_flags) ||
-              (ns3pr.d_iterations != hadNs3pr.d_iterations) ||
-              (ns3pr.d_salt != hadNs3pr.d_salt)) {
-            dk.setNSEC3PARAM(domain, ns3pr, narrow);
-          }
-        } else if (hadNSEC3) {
-          dk.unsetNSEC3PARAM(domain);
-        }
-      }
-    } else if (hadPresigned) {
-      // zone is no longer presigned
-      dk.unsetPresigned(domain);
-      dk.unsetNSEC3PARAM(domain);
-    }
-
     di.backend->commitTransaction();
+    transaction = false;
     di.backend->setFresh(domain_id);
     PC.purge(domain+"$");
 
@@ -348,35 +389,35 @@ void CommunicatorClass::suck(const string &domain,const string &remote)
   }
   catch(DBException &re) {
     L<<Logger::Error<<"Unable to feed record during incoming AXFR of '"+domain+"': "<<re.reason<<endl;
-    if(di.backend && !first) {
+    if(di.backend && transaction) {
       L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
       di.backend->abortTransaction();
     }
   }
   catch(MOADNSException &re) {
     L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"+domain+"' (MOADNSException): "<<re.what()<<endl;
-    if(di.backend && !first) {
+    if(di.backend && transaction) {
       L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
       di.backend->abortTransaction();
     }
   }
   catch(std::exception &re) {
     L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"+domain+"' (std::exception): "<<re.what()<<endl;
-    if(di.backend && !first) {
+    if(di.backend && transaction) {
       L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
       di.backend->abortTransaction();
     }
   }
   catch(ResolverException &re) {
     L<<Logger::Error<<"Unable to AXFR zone '"+domain+"' from remote '"<<remote<<"' (resolver): "<<re.reason<<endl;
-    if(di.backend && !first) {
+    if(di.backend && transaction) {
       L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
       di.backend->abortTransaction();
     }
   }
   catch(AhuException &ae) {
     L<<Logger::Error<<"Unable to AXFR zone '"+domain+"' from remote '"<<remote<<"' (AhuException): "<<ae.reason<<endl;
-    if(di.backend && !first) {
+    if(di.backend && transaction) {
       L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
       di.backend->abortTransaction();
     }