PowerDNS / pdns

PowerDNS Authoritative, PowerDNS Recursor, dnsdist
https://www.powerdns.com/
GNU General Public License v2.0
3.69k stars 906 forks source link

Duplicated SOA records when IXFR turned into an AXFR #12984

Open v1shnya opened 1 year ago

v1shnya commented 1 year ago

Short description

Configured PowerDNS as a Secondary, turned IXFR on. Primary has no IXFR enabled.

So, PowerDNS tries IXFR and as it's not available switching to AXFR XFR-in zone: 10.in-addr.arpa, primary: 10.10.10.10, initiating transfer IXFR-in zone: 10.in-addr.arpa, primary: 10.10.10.10, starting IXFR IXFR-in zone: 10.in-addr.arpa, primary: 10.10.10.10, IXFR turned into an AXFR

The issue is that in this case we see duplicated SOA records $ pdnsutil list-zone 10.in-addr.arpa | grep SOA 10.in-addr.arpa 3600 IN SOA dns.example.com hostmaster.example.com 8889 900 600 86400 3600 10.in-addr.arpa 3600 IN SOA dns.example.com hostmaster.example.com 8889 900 600 86400 3600

Environment

Other information

All the functions below are located in slavecommunicator.cc https://github.com/PowerDNS/pdns/blob/master/pdns/slavecommunicator.cc

In case of AXFR request duplicate SOA record (end of AXFR message) is correctly removed, see soa_received check.

static vector<DNSResourceRecord> doAxfr(...)
...
      for(auto& rr :  out) {
        ....
        if(rr.qtype.getCode() == QType::SOA) {
          if(soa_received)
            continue; //skip the last SOA
          SOAData sd;
          fillSOAData(rr.content,sd);
          zs.soa_serial = sd.serial;
          soa_received = true;
        }
        rrs.push_back(rr);
      }

In case of IXFR turning into AXFR both SOA records (start and end of AXFR message) are put into the DB as the is no check for duplicate SOA record

void CommunicatorClass::suck(...)
    ....
        ixfrSuck(domain, tt, laddr, remote, zs, &axfr);
        if(!axfr.empty()) {
          g_log<<Logger::Notice<<logPrefix<<"IXFR turned into an AXFR"<<endl;
          logPrefix[0]='A'; // IXFR -> AXFR
          bool firstNSEC3=true;
          rrs.reserve(axfr.size());
          for(const auto& dr : axfr) {
            auto rr = DNSResourceRecord::fromWire(dr);
            (rr.qname += domain).makeUsLowerCase();
            rr.domain_id = zs.domain_id;
            if(!processRecordForZS(domain, firstNSEC3, rr, zs))
              continue;
            if(dr.d_type == QType::SOA) {
              auto sd = getRR<SOARecordContent>(dr);
              zs.soa_serial = sd->d_st.serial;
            }
            rrs.push_back(rr);
          }
Habbie commented 1 year ago

Thank you for this excellent report! We'll try to have a look soon.

v1shnya commented 1 year ago

Getting a debug from the remote side I got that on our IXFR request we getting IXFR response with the full zone transfer message! PowerDNS considers this sort of a replay as "IXFR turned into an AXFR"