From: Otto Moerbeek Date: Mon, 24 Oct 2022 14:25:59 +0000 (+0200) Subject: Timout handling for ixfrs as a client. X-Git-Tag: rec-4.8.0-beta2~2^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ee3a91dba6922cd4ff0d05c29bfae68ab4a43a83;p=thirdparty%2Fpdns.git Timout handling for ixfrs as a client. One complicating factor is that this is shared code, but auth and rec do not agree on the definiton of the timeout value: auth states it is a maximum idle time, while rec state it is the total xfr time. While both apporaches make sense and in the end we would like to enforce both, we now go for a more simple solution that respects auth or rec behaviour based on a flag. (cherry picked from commit fee334ae0f5083d47f9adc207d5a1a6d36ebc2ac) --- diff --git a/pdns/ixfr.cc b/pdns/ixfr.cc index 1154eb0136..531fec6d62 100644 --- a/pdns/ixfr.cc +++ b/pdns/ixfr.cc @@ -123,9 +123,13 @@ vector, vector > > processIXFRRecords(const Co } // Returns pairs of "remove & add" vectors. If you get an empty remove, it means you got an AXFR! -vector, vector > > getIXFRDeltas(const ComboAddress& primary, const DNSName& zone, const DNSRecord& oursr, - const TSIGTriplet& tt, const ComboAddress* laddr, size_t maxReceivedBytes) +vector, vector>> getIXFRDeltas(const ComboAddress& primary, const DNSName& zone, const DNSRecord& oursr, + uint16_t xfrTimeout, bool totalTimeout, + const TSIGTriplet& tt, const ComboAddress* laddr, size_t maxReceivedBytes) { + // Auth documents xfrTimeout to be a max idle time + // Rec documents it do be a total XFR time + // vector, vector > > ret; vector packet; DNSPacketWriter pw(packet, zone, QType::IXFR); @@ -157,12 +161,30 @@ vector, vector > > getIXFRDeltas(const ComboAd msg.append((const char*)&packet[0], packet.size()); Socket s(primary.sin4.sin_family, SOCK_STREAM); - // cout<<"going to connect"< time_t { + time_t elapsed = 0; + if (totalTimeout) { + elapsed = time(nullptr) - xfrStart; + if (elapsed >= xfrTimeout) { + throw std::runtime_error("Reached the maximum elapsed time in an IXFR delta for zone '" + zone.toLogString() + "' from primary " + primary.toStringWithPort()); + } + } + return elapsed; + }; + + s.connect(primary, xfrTimeout); + + time_t elapsed = timeoutChecker(); + s.writenWithTimeout(msg.data(), msg.size(), xfrTimeout - elapsed); // CURRENT PRIMARY SOA // REPEAT: @@ -170,7 +192,7 @@ vector, vector > > getIXFRDeltas(const ComboAd // RECORDS TO REMOVE // SOA WHERE THIS DELTA GOES // RECORDS TO ADD - // CURRENT PRIMARY SOA + // CURRENT PRIMARY SOA std::shared_ptr primarySOA = nullptr; vector records; size_t receivedBytes = 0; @@ -181,7 +203,7 @@ vector, vector > > getIXFRDeltas(const ComboAd const unsigned int expectedSOAForIXFR = 3; unsigned int primarySOACount = 0; - for(;;) { + for (;;) { // IXFR or AXFR style end reached? We don't want to process trailing data after the closing SOA if (style == AXFR && primarySOACount == expectedSOAForAXFR) { break; @@ -190,33 +212,38 @@ vector, vector > > getIXFRDeltas(const ComboAd break; } - if(s.read((char*)&len, sizeof(len)) != sizeof(len)) + elapsed = timeoutChecker(); + if (s.readWithTimeout(reinterpret_cast(&len), sizeof(len), static_cast(xfrTimeout - elapsed)) != sizeof(len)) { break; + } - len=ntohs(len); - // cout<<"Got chunk of "< 0 && (maxReceivedBytes - receivedBytes) < (size_t) len) + 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.toLogString()+"' from primary "+primary.toStringWithPort()); + } reply.resize(len); - readn2(s.getHandle(), &reply.at(0), len); + + elapsed = timeoutChecker(); + const struct timeval remainingTime = { .tv_sec = xfrTimeout - elapsed, .tv_usec = 0 }; + const struct timeval idleTime = remainingTime; + readn2WithTimeout(s.getHandle(), &reply.at(0), len, idleTime, remainingTime, false); receivedBytes += len; MOADNSParser mdp(false, reply); - if(mdp.d_header.rcode) + if (mdp.d_header.rcode) { throw std::runtime_error("Got an error trying to IXFR zone '"+zone.toLogString()+"' from primary '"+primary.toStringWithPort()+"': "+RCode::to_s(mdp.d_header.rcode)); + } - // cout<<"Got a response, rcode: "<getZoneRepresentation()<, vector > > getIXFRDeltas(const ComboAddress& primary, const DNSName& zone, - const DNSRecord& sr, const TSIGTriplet& tt=TSIGTriplet(), - const ComboAddress* laddr=0, size_t maxReceivedBytes=0); +vector, vector>> getIXFRDeltas(const ComboAddress& primary, const DNSName& zone, + const DNSRecord& sr, + uint16_t xfrTimeout = 0, bool totalTime = false, + const TSIGTriplet& tt=TSIGTriplet(), + const ComboAddress* laddr=0, size_t maxReceivedBytes=0); -vector, vector > > processIXFRRecords(const ComboAddress& primary, const DNSName& zone, - const vector& records, const std::shared_ptr& primarySOA); +vector, vector>> processIXFRRecords(const ComboAddress& primary, const DNSName& zone, + const vector& records, const std::shared_ptr& primarySOA); diff --git a/pdns/ixplore.cc b/pdns/ixplore.cc index 2bb262e2a5..90e60e505c 100644 --- a/pdns/ixplore.cc +++ b/pdns/ixplore.cc @@ -187,7 +187,7 @@ int main(int argc, char** argv) { } cout<<"got new serial: "<& primaries, const boost::optional& defpol, bool defpolOverrideLocal, uint32_t maxTTL, size_t zoneIdx, const TSIGTriplet& tt, size_t maxReceivedBytes, const ComboAddress& localAddress, const uint16_t axfrTimeout, const uint32_t refreshFromConf, std::shared_ptr sr, const std::string& dumpZoneFileName, uint64_t configGeneration) +void RPZIXFRTracker(const std::vector& primaries, const boost::optional& defpol, bool defpolOverrideLocal, uint32_t maxTTL, size_t zoneIdx, const TSIGTriplet& tt, size_t maxReceivedBytes, const ComboAddress& localAddress, const uint16_t xfrTimeout, const uint32_t refreshFromConf, std::shared_ptr sr, const std::string& dumpZoneFileName, uint64_t configGeneration) { setThreadName("pdns-r/RPZIXFR"); bool isPreloaded = sr != nullptr; @@ -407,7 +407,7 @@ void RPZIXFRTracker(const std::vector& primaries, const boost::opt std::shared_ptr newZone = std::make_shared(*oldZone); for (const auto& primary : primaries) { try { - sr = loadRPZFromServer(logger, primary, zoneName, newZone, defpol, defpolOverrideLocal, maxTTL, tt, maxReceivedBytes, localAddress, axfrTimeout); + sr = loadRPZFromServer(logger, primary, zoneName, newZone, defpol, defpolOverrideLocal, maxTTL, tt, maxReceivedBytes, localAddress, xfrTimeout); newZone->setSerial(sr->d_st.serial); newZone->setRefresh(sr->d_st.refresh); refresh = std::max(refreshFromConf ? refreshFromConf : newZone->getRefresh(), 1U); @@ -476,7 +476,7 @@ void RPZIXFRTracker(const std::vector& primaries, const boost::opt } try { - deltas = getIXFRDeltas(primary, zoneName, dr, tt, &local, maxReceivedBytes); + deltas = getIXFRDeltas(primary, zoneName, dr, xfrTimeout, true, tt, &local, maxReceivedBytes); /* no need to try another primary */ break; diff --git a/pdns/rpzloader.hh b/pdns/rpzloader.hh index 304ab5ab08..fcb379cb4b 100644 --- a/pdns/rpzloader.hh +++ b/pdns/rpzloader.hh @@ -27,7 +27,7 @@ extern bool g_logRPZChanges; std::shared_ptr loadRPZFromFile(const std::string& fname, std::shared_ptr zone, const boost::optional& defpol, bool defpolOverrideLocal, uint32_t maxTTL); -void RPZIXFRTracker(const std::vector& primaries, const boost::optional& defpol, bool defpolOverrideLocal, uint32_t maxTTL, size_t zoneIdx, const TSIGTriplet& tt, size_t maxReceivedBytes, const ComboAddress& localAddress, const uint16_t axfrTimeout, const uint32_t reloadFromConf, shared_ptr sr, const std::string& dumpZoneFileName, uint64_t configGeneration); +void RPZIXFRTracker(const std::vector& primaries, const boost::optional& defpol, bool defpolOverrideLocal, uint32_t maxTTL, size_t zoneIdx, const TSIGTriplet& tt, size_t maxReceivedBytes, const ComboAddress& localAddress, const uint16_t xfrTimeout, const uint32_t reloadFromConf, shared_ptr sr, const std::string& dumpZoneFileName, uint64_t configGeneration); struct rpzStats { diff --git a/pdns/slavecommunicator.cc b/pdns/slavecommunicator.cc index fe5c40a5aa..c99e944f3e 100644 --- a/pdns/slavecommunicator.cc +++ b/pdns/slavecommunicator.cc @@ -438,13 +438,14 @@ void CommunicatorClass::ixfrSuck(const DNSName &domain, const TSIGTriplet& tt, c return; } + uint16_t xfrTimeout = ::arg().asNum("axfr-fetch-timeout"); soatimes st; memset(&st, 0, sizeof(st)); st.serial=di.serial; DNSRecord drsoa; drsoa.d_content = std::make_shared(g_rootdnsname, g_rootdnsname, st); - auto deltas = getIXFRDeltas(remote, domain, drsoa, tt, laddr.sin4.sin_family ? &laddr : nullptr, ((size_t) ::arg().asNum("xfr-max-received-mbytes")) * 1024 * 1024); + auto deltas = getIXFRDeltas(remote, domain, drsoa, xfrTimeout, false, tt, laddr.sin4.sin_family ? &laddr : nullptr, ((size_t) ::arg().asNum("xfr-max-received-mbytes")) * 1024 * 1024); zs.numDeltas=deltas.size(); // cout<<"Got "<