]> git.ipfire.org Git - thirdparty/pdns.git/blobdiff - pdns/ixfr.cc
Merge pull request #8223 from PowerDNS/omoerbeek-patch-1
[thirdparty/pdns.git] / pdns / ixfr.cc
index 74982b518f779e2e13d24d345196c037cc4a2a07..0a7dca9f5638829b783a9dd3af2c686c01c0ad2e 100644 (file)
@@ -27,7 +27,7 @@
 #include "tsigverifier.hh"
 
 vector<pair<vector<DNSRecord>, vector<DNSRecord> > > processIXFRRecords(const ComboAddress& master, const DNSName& zone,
-                                                                        const vector<DNSRecord>& records, const std::shared_ptr<SOARecordContent> masterSOA)
+                                                                        const vector<DNSRecord>& records, const std::shared_ptr<SOARecordContent>& masterSOA)
 {
   vector<pair<vector<DNSRecord>, vector<DNSRecord> > >  ret;
 
@@ -51,7 +51,7 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > processIXFRRecords(const Co
 
     auto sr = getRR<SOARecordContent>(records[pos]);
     if (!sr) {
-      throw std::runtime_error("Error getting the content of the first SOA record of this IXFR sequence for zone '"+zone.toString()+"' from master '"+master.toStringWithPort()+"'");
+      throw std::runtime_error("Error getting the content of the first SOA record of this IXFR sequence for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"'");
     }
 
     // cerr<<"Serial is "<<sr->d_st.serial<<", final serial is "<<masterSOA->d_st.serial<<endl;
@@ -59,6 +59,12 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > processIXFRRecords(const Co
     // the serial of this SOA record is the serial of the
     // zone before the removals and updates of this sequence
     if (sr->d_st.serial == masterSOA->d_st.serial) {
+      if (records.size() == 2) {
+        // if the entire update is two SOAs records with the same
+        // serial, this is actually an empty AXFR!
+        return {{remove, records}};
+      }
+
       // if it's the final SOA, there is nothing for us to see
       break;
     }
@@ -71,12 +77,12 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > processIXFRRecords(const Co
     }
 
     if (pos >= records.size()) {
-      throw std::runtime_error("No SOA record to finish the removals part of the IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+      throw std::runtime_error("No SOA record to finish the removals part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
     }
 
     sr = getRR<SOARecordContent>(records[pos]);
     if (!sr) {
-      throw std::runtime_error("Invalid SOA record to finish the removals part of the IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+      throw std::runtime_error("Invalid SOA record to finish the removals part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
     }
 
     // this is the serial of the zone after the removals
@@ -91,22 +97,22 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > processIXFRRecords(const Co
     }
 
     if (pos >= records.size()) {
-      throw std::runtime_error("No SOA record to finish the additions part of the IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+      throw std::runtime_error("No SOA record to finish the additions part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
     }
 
     sr = getRR<SOARecordContent>(records[pos]);
     if (!sr) {
-      throw std::runtime_error("Invalid SOA record to finish the additions part of the IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+      throw std::runtime_error("Invalid SOA record to finish the additions part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
     }
 
     if (sr->d_st.serial != newSerial) {
-      throw std::runtime_error("Invalid serial (" + std::to_string(sr->d_st.serial) + ", expecting " + std::to_string(newSerial) + ") in the SOA record finishing the additions part of the IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+      throw std::runtime_error("Invalid serial (" + std::to_string(sr->d_st.serial) + ", expecting " + std::to_string(newSerial) + ") in the SOA record finishing the additions part of the IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
     }
 
     if (newSerial == masterSOA->d_st.serial) {
       // this was the last sequence
       if (pos != (records.size() - 1)) {
-        throw std::runtime_error("Trailing records after the last IXFR sequence of zone '" + zone.toString() + "' from " + master.toStringWithPort());
+        throw std::runtime_error("Trailing records after the last IXFR sequence of zone '" + zone.toLogString() + "' from " + master.toStringWithPort());
       }
     }
 
@@ -138,7 +144,7 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
     try {
       trc.d_algoName = getTSIGAlgoName(the);
     } catch(PDNSException& pe) {
-      throw std::runtime_error("TSIG algorithm '"+tt.algo.toString()+"' is unknown.");
+      throw std::runtime_error("TSIG algorithm '"+tt.algo.toLogString()+"' is unknown.");
     }
     trc.d_time = time((time_t*)NULL);
     trc.d_fudge = 300;
@@ -168,8 +174,14 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
   std::shared_ptr<SOARecordContent> masterSOA = nullptr;
   vector<DNSRecord> records;
   size_t receivedBytes = 0;
+  int8_t ixfrInProgress = -2;
+  std::string reply;
 
   for(;;) {
+    // IXFR end
+    if (ixfrInProgress >= 0)
+      break;
+
     if(s.read((char*)&len, sizeof(len)) != sizeof(len))
       break;
 
@@ -179,20 +191,20 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
       break;
 
     if (maxReceivedBytes > 0 && (maxReceivedBytes - receivedBytes) < (size_t) len)
-      throw std::runtime_error("Reached the maximum number of received bytes in an IXFR delta for zone '"+zone.toString()+"' from master "+master.toStringWithPort());
+      throw std::runtime_error("Reached the maximum number of received bytes in an IXFR delta for zone '"+zone.toLogString()+"' from master "+master.toStringWithPort());
 
-    char reply[len]; 
-    readn2(s.getHandle(), reply, len);
+    reply.resize(len);
+    readn2(s.getHandle(), &reply.at(0), len);
     receivedBytes += len;
 
-    MOADNSParser mdp(false, string(reply, len));
+    MOADNSParser mdp(false, reply);
     if(mdp.d_header.rcode) 
-      throw std::runtime_error("Got an error trying to IXFR zone '"+zone.toString()+"' from master '"+master.toStringWithPort()+"': "+RCode::to_s(mdp.d_header.rcode));
+      throw std::runtime_error("Got an error trying to IXFR zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"': "+RCode::to_s(mdp.d_header.rcode));
 
     //    cout<<"Got a response, rcode: "<<mdp.d_header.rcode<<", got "<<mdp.d_answers.size()<<" answers"<<endl;
 
     if(!tt.algo.empty()) { // TSIG verify message
-      tsigVerifier.check(std::string(reply, len), mdp);
+      tsigVerifier.check(reply, mdp);
     }
 
     for(auto& r: mdp.d_answers) {
@@ -200,12 +212,12 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
       if(!masterSOA) {
         // we have not seen the first SOA record yet
         if (r.first.d_type != QType::SOA) {
-          throw std::runtime_error("The first record of the IXFR answer for zone '"+zone.toString()+"' from master '"+master.toStringWithPort()+"' is not a SOA ("+QType(r.first.d_type).getName()+")");
+          throw std::runtime_error("The first record of the IXFR answer for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"' is not a SOA ("+QType(r.first.d_type).getName()+")");
         }
 
-       auto sr = getRR<SOARecordContent>(r.first);
-       if (!sr) {
-          throw std::runtime_error("Error getting the content of the first SOA record of the IXFR answer for zone '"+zone.toString()+"' from master '"+master.toStringWithPort()+"'");
+        auto sr = getRR<SOARecordContent>(r.first);
+        if (!sr) {
+          throw std::runtime_error("Error getting the content of the first SOA record of the IXFR answer for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"'");
         }
 
         if(sr->d_st.serial == std::dynamic_pointer_cast<SOARecordContent>(oursr.d_content)->d_st.serial) {
@@ -213,6 +225,17 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
           return ret;
         }
         masterSOA = sr;
+      } else if (r.first.d_type == QType::SOA) {
+        auto sr = getRR<SOARecordContent>(r.first);
+        if (!sr) {
+          throw std::runtime_error("Error getting the content of SOA record of IXFR answer for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort()+"'");
+        }
+
+        // we hit the last SOA record
+        // IXFR is considered to be done if we hit the last SOA record twice
+        if (masterSOA->d_st.serial == sr->d_st.serial) {
+          ixfrInProgress++;
+        }
       }
 
       if(r.first.d_place != DNSResourceRecord::ANSWER) {
@@ -222,7 +245,7 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
         if(r.first.d_type == QType::OPT)
           continue;
 
-        throw std::runtime_error("Unexpected record (" +QType(r.first.d_type).getName()+") in non-answer section ("+std::to_string(r.first.d_place)+")in IXFR response for zone '"+zone.toString()+"' from master '"+master.toStringWithPort());
+        throw std::runtime_error("Unexpected record (" +QType(r.first.d_type).getName()+") in non-answer section ("+std::to_string(r.first.d_place)+")in IXFR response for zone '"+zone.toLogString()+"' from master '"+master.toStringWithPort());
       }
 
       r.first.d_name.makeUsRelative(zone);