File pdns-4.4.2-xfr.patch of Package pdns.17450

--- pdns-4.4.2/pdns/ixfr.cc	2021-11-24 01:04:47.000000000 +0100
+++ pdns-4.4.2-xfr/pdns/ixfr.cc	2022-03-18 11:07:16.000000000 +0100
@@ -174,13 +174,21 @@
   std::shared_ptr<SOARecordContent> masterSOA = nullptr;
   vector<DNSRecord> records;
   size_t receivedBytes = 0;
-  int8_t ixfrInProgress = -2;
   std::string reply;
 
+  enum transferStyle { Unknown, AXFR, IXFR } style = Unknown;
+  const unsigned int expectedSOAForAXFR = 2;
+  const unsigned int expectedSOAForIXFR = 3;
+  unsigned int masterSOACount = 0;
+
   for(;;) {
-    // IXFR end
-    if (ixfrInProgress >= 0)
+    // IXFR or AXFR style end reached? We don't want to process trailing data after the closing SOA
+    if (style == AXFR && masterSOACount == expectedSOAForAXFR) {
+      break;
+    }
+    else if (style == IXFR && masterSOACount == expectedSOAForIXFR) {
       break;
+    }
 
     if(s.read((char*)&len, sizeof(len)) != sizeof(len))
       break;
@@ -225,16 +233,31 @@
           return ret;
         }
         masterSOA = sr;
+        ++masterSOACount;
       } 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
+        // we hit a marker SOA record
         if (masterSOA->d_st.serial == sr->d_st.serial) {
-          ixfrInProgress++;
+          ++masterSOACount;
+        }
+      }
+      // When we see the 2nd record, we can decide what the style is
+      if (records.size() == 1 && style == Unknown) {
+        if (r.first.d_type != QType::SOA) {
+          // Non-empty AXFR style has a non-SOA record following the first SOA
+          style = AXFR;
+        }
+        else if (masterSOACount == expectedSOAForAXFR) {
+          // Empty zone AXFR style: start SOA is immediately followed by end marker SOA
+          style = AXFR;
+        }
+        else {
+          // IXFR has a 2nd SOA (with different serial) following the first
+          style = IXFR;
         }
       }
 
@@ -253,7 +276,21 @@
     }
   }
 
-  //  cout<<"Got "<<records.size()<<" records"<<endl;
+  switch (style) {
+  case IXFR:
+    if (masterSOACount != expectedSOAForIXFR) {
+      throw std::runtime_error("Incomplete IXFR transfer for '" + zone.toLogString() + "' from primary '" + master.toStringWithPort());
+    }
+    break;
+  case AXFR:
+    if (masterSOACount != expectedSOAForAXFR){
+      throw std::runtime_error("Incomplete AXFR style transfer for '" + zone.toLogString() + "' from primary '" + master.toStringWithPort());
+    }
+    break;
+  case Unknown:
+    throw std::runtime_error("Incomplete XFR for '" + zone.toLogString() + "' from primary '" + master.toStringWithPort());
+    break;
+  }
 
   return processIXFRRecords(master, zone, records, masterSOA);
 }
openSUSE Build Service is sponsored by