]> granicus.if.org Git - pdns/commitdiff
improve IXFR to AXFR fallback
authorKees Monshouwer <mind04@monshouwer.org>
Sun, 3 Mar 2013 20:56:49 +0000 (21:56 +0100)
committermind04 <mind04@monshouwer.org>
Tue, 7 Jan 2014 12:57:28 +0000 (13:57 +0100)
pdns/tcpreceiver.cc
pdns/tcpreceiver.hh

index 7d3b705c9894837fe30acf72403399e23b855ad8..0aa570bdfee1fc58a03cba69130f0ea71f2afd4b 100644 (file)
@@ -286,9 +286,15 @@ void *TCPNameserver::doConnection(void *data)
       if(packet->parse(mesg, pktlen)<0)
         break;
       
-      if(packet->qtype.getCode()==QType::AXFR || packet->qtype.getCode()==QType::IXFR ) {
-        if(doAXFR(packet->qdomain, packet, fd)) 
-          S.inc("tcp-answers");  
+      if(packet->qtype.getCode()==QType::AXFR) {
+        if(doAXFR(packet->qdomain, packet, fd))
+          S.inc("tcp-answers");
+        continue;
+      }
+
+      if(packet->qtype.getCode()==QType::IXFR) {
+        if(doIXFR(packet, fd))
+          S.inc("tcp-answers");
         continue;
       }
 
@@ -834,6 +840,130 @@ int TCPNameserver::doAXFR(const string &target, shared_ptr<DNSPacket> q, int out
   return 1;
 }
 
+int TCPNameserver::doIXFR(shared_ptr<DNSPacket> q, int outsock)
+{
+  shared_ptr<DNSPacket> outpacket=getFreshAXFRPacket(q);
+  if(q->d_dnssecOk)
+    outpacket->d_dnssecOk=true; // RFC 5936, 2.2.5 'SHOULD'
+
+  DNSSECKeeper dk;
+  NSEC3PARAMRecordContent ns3pr;
+  bool narrow;
+  bool NSEC3Zone=false;
+
+  dk.clearCaches(q->qdomain);
+  bool securedZone = dk.isSecuredZone(q->qdomain);
+  if(dk.getNSEC3PARAM(q->qdomain, &ns3pr, &narrow)) {
+    NSEC3Zone=true;
+    if(narrow) {
+      L<<Logger::Error<<"Not doing IXFR of an NSEC3 narrow zone.."<<endl;
+      L<<Logger::Error<<"IXFR of domain '"<<q->qdomain<<"' denied to "<<q->getRemote()<<endl;
+      outpacket->setRcode(RCode::Refused);
+      sendPacket(outpacket,outsock);
+      return 0;
+    }
+  }
+
+  uint32_t serial = 0;
+  MOADNSParser mdp(q->getString());
+  for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
+    const DNSRecord *rr = &i->first;
+    if (rr->d_type == QType::SOA && rr->d_place == DNSRecord::Nameserver) {
+      vector<string>parts;
+      stringtok(parts, rr->d_content->getZoneRepresentation());
+      if (parts.size() >= 3) {
+        serial=atoi(parts[2].c_str());
+      } else {
+        L<<Logger::Error<<"No serial in IXFR query"<<endl;
+        outpacket->setRcode(RCode::FormErr);
+        sendPacket(outpacket,outsock);
+        return 0;
+      }
+    } else {
+      L<<Logger::Error<<"Additional records in IXFR query"<<endl;
+      outpacket->setRcode(RCode::FormErr);
+      sendPacket(outpacket,outsock);
+      return 0;
+    }
+  }
+
+  L<<Logger::Error<<"IXFR of domain '"<<q->qdomain<<"' initiated by "<<q->getRemote()<<" with serial "<<serial<<endl;
+
+  SOAData sd;
+  sd.db=(DNSBackend *)-1; // force uncached answer
+  {
+    Lock l(&s_plock);
+    DLOG(L<<"Looking for SOA"<<endl); // find domain_id via SOA and list complete domain. No SOA, no IXFR
+    if(!s_P) {
+      L<<Logger::Error<<"TCP server is without backend connections in doIXFR, launching"<<endl;
+      s_P=new PacketHandler;
+    }
+
+    if(!s_P->getBackend()->getSOA(q->qdomain, sd)) {
+      L<<Logger::Error<<"IXFR of domain '"<<q->qdomain<<"' failed: not authoritative"<<endl;
+      outpacket->setRcode(9); // 'NOTAUTH'
+      sendPacket(outpacket,outsock);
+      return 0;
+    }
+  }
+
+  string target = q->qdomain;
+
+  UeberBackend db;
+  sd.db=(DNSBackend *)-1; // force uncached answer
+  if(!db.getSOA(target, sd)) {
+    L<<Logger::Error<<"IXFR of domain '"<<target<<"' failed: not authoritative in second instance"<<endl;
+    outpacket->setRcode(9); // 'NOTAUTH'
+    sendPacket(outpacket,outsock);
+    return 0;
+  }
+
+  if(!sd.db || sd.db==(DNSBackend *)-1) {
+    L<<Logger::Error<<"Error determining backend for domain '"<<target<<"' trying to serve an IXFR"<<endl;
+    outpacket->setRcode(RCode::ServFail);
+    sendPacket(outpacket,outsock);
+    return 0;
+  }
+  if (!rfc1982LessThan(serial, sd.serial)) {
+    TSIGRecordContent trc;
+    string tsigkeyname, tsigsecret;
+
+    q->getTSIGDetails(&trc, &tsigkeyname, 0);
+
+    if(!tsigkeyname.empty()) {
+      string tsig64, algorithm;
+      Lock l(&s_plock);
+      s_P->getBackend()->getTSIGKey(tsigkeyname, &algorithm, &tsig64);
+      B64Decode(tsig64, tsigsecret);
+    }
+
+    UeberBackend signatureDB;
+
+    // SOA *must* go out first, our signing pipe might reorder
+    DLOG(L<<"Sending out SOA"<<endl);
+    DNSResourceRecord soa = makeDNSRRFromSOAData(sd);
+    outpacket->addRecord(soa);
+    editSOA(dk, sd.qname, outpacket.get());
+    if(securedZone) {
+      set<string, CIStringCompare> authSet;
+      authSet.insert(target);
+      addRRSigs(dk, signatureDB, authSet, outpacket->getRRS());
+    }
+
+    if(!tsigkeyname.empty())
+      outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac); // first answer is 'normal'
+
+    sendPacket(outpacket, outsock);
+
+    L<<Logger::Error<<"IXFR of domain '"<<target<<"' to "<<q->getRemote()<<" finished"<<endl;
+
+    return 1;
+  }
+
+  L<<Logger::Error<<"IXFR fallback to AXFR for domain '"<<target<<"' our serial "<<sd.serial<<endl;
+  return doAXFR(q->qdomain, q, outsock);
+}
+
 TCPNameserver::~TCPNameserver()
 {
   delete d_connectionroom_sem;
index b58a474d4b137b163c077744fcc2ced604347cbf..d26b1b61918355d46ef1d613650724fd8d7e44cc 100644 (file)
@@ -53,6 +53,7 @@ private:
   static int readLength(int fd, ComboAddress *remote);
   static void getQuestion(int fd, char *mesg, int pktlen, const ComboAddress& remote);
   static int doAXFR(const string &target, boost::shared_ptr<DNSPacket> q, int outsock);
+  static int doIXFR(boost::shared_ptr<DNSPacket> q, int outsock);
   static bool canDoAXFR(boost::shared_ptr<DNSPacket> q);
   static void *doConnection(void *data);
   static void *launcher(void *data);