}
// Returns pairs of "remove & add" vectors. If you get an empty remove, it means you got an AXFR!
-vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAddress& primary, const DNSName& zone, const DNSRecord& oursr,
- const TSIGTriplet& tt, const ComboAddress* laddr, size_t maxReceivedBytes)
+vector<pair<vector<DNSRecord>, vector<DNSRecord>>> 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<pair<vector<DNSRecord>, vector<DNSRecord> > > ret;
vector<uint8_t> packet;
DNSPacketWriter pw(packet, zone, QType::IXFR);
msg.append((const char*)&packet[0], packet.size());
Socket s(primary.sin4.sin_family, SOCK_STREAM);
- // cout<<"going to connect"<<endl;
- if(laddr)
+ if (laddr != nullptr) {
s.bind(*laddr);
- s.connect(primary);
- // cout<<"Connected"<<endl;
- s.writen(msg);
+ }
+ s.setNonBlocking();
+
+ const time_t xfrStart = time(nullptr);
+
+ // Helper function: if we have a total timeout, check it and set elapsed to the total time taken sofar,
+ // otherwise set elapsed to 0, making the total time limit ineffective
+ const auto timeoutChecker = [=] () -> 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:
// RECORDS TO REMOVE
// SOA WHERE THIS DELTA GOES
// RECORDS TO ADD
- // CURRENT PRIMARY SOA
+ // CURRENT PRIMARY SOA
std::shared_ptr<SOARecordContent> primarySOA = nullptr;
vector<DNSRecord> records;
size_t receivedBytes = 0;
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;
break;
}
- if(s.read((char*)&len, sizeof(len)) != sizeof(len))
+ elapsed = timeoutChecker();
+ if (s.readWithTimeout(reinterpret_cast<char*>(&len), sizeof(len), static_cast<int>(xfrTimeout - elapsed)) != sizeof(len)) {
break;
+ }
- len=ntohs(len);
- // cout<<"Got chunk of "<<len<<" bytes"<<endl;
- if(!len)
+ len = ntohs(len);
+ if (len == 0) {
break;
+ }
- if (maxReceivedBytes > 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: "<<mdp.d_header.rcode<<", got "<<mdp.d_answers.size()<<" answers"<<endl;
-
- if(!tt.algo.empty()) { // TSIG verify message
+ if (!tt.algo.empty()) { // TSIG verify message
tsigVerifier.check(reply, mdp);
}
- for(auto& r: mdp.d_answers) {
- // cout<<r.first.d_name<< " " <<r.first.d_content->getZoneRepresentation()<<endl;
+ for (auto& r: mdp.d_answers) {
if(!primarySOA) {
// we have not seen the first SOA record yet
if (r.first.d_type != QType::SOA) {
#include "dnsparser.hh"
#include "dnsrecords.hh"
-vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAddress& primary, const DNSName& zone,
- const DNSRecord& sr, const TSIGTriplet& tt=TSIGTriplet(),
- const ComboAddress* laddr=0, size_t maxReceivedBytes=0);
+vector<pair<vector<DNSRecord>, vector<DNSRecord>>> 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<pair<vector<DNSRecord>, vector<DNSRecord> > > processIXFRRecords(const ComboAddress& primary, const DNSName& zone,
- const vector<DNSRecord>& records, const std::shared_ptr<SOARecordContent>& primarySOA);
+vector<pair<vector<DNSRecord>, vector<DNSRecord>>> processIXFRRecords(const ComboAddress& primary, const DNSName& zone,
+ const vector<DNSRecord>& records, const std::shared_ptr<SOARecordContent>& primarySOA);
}
cout<<"got new serial: "<<serial<<", initiating IXFR!"<<endl;
- auto deltas = getIXFRDeltas(master, zone, ourSoa, tt);
+ auto deltas = getIXFRDeltas(master, zone, ourSoa, 20, false, tt);
cout<<"Got "<<deltas.size()<<" deltas, applying.."<<endl;
for(const auto& delta : deltas) {
return true;
}
-void RPZIXFRTracker(const std::vector<ComboAddress>& primaries, const boost::optional<DNSFilterEngine::Policy>& 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<SOARecordContent> sr, const std::string& dumpZoneFileName, uint64_t configGeneration)
+void RPZIXFRTracker(const std::vector<ComboAddress>& primaries, const boost::optional<DNSFilterEngine::Policy>& 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<SOARecordContent> sr, const std::string& dumpZoneFileName, uint64_t configGeneration)
{
setThreadName("pdns-r/RPZIXFR");
bool isPreloaded = sr != nullptr;
std::shared_ptr<DNSFilterEngine::Zone> newZone = std::make_shared<DNSFilterEngine::Zone>(*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);
}
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;
extern bool g_logRPZChanges;
std::shared_ptr<SOARecordContent> loadRPZFromFile(const std::string& fname, std::shared_ptr<DNSFilterEngine::Zone> zone, const boost::optional<DNSFilterEngine::Policy>& defpol, bool defpolOverrideLocal, uint32_t maxTTL);
-void RPZIXFRTracker(const std::vector<ComboAddress>& primaries, const boost::optional<DNSFilterEngine::Policy>& 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<SOARecordContent> sr, const std::string& dumpZoneFileName, uint64_t configGeneration);
+void RPZIXFRTracker(const std::vector<ComboAddress>& primaries, const boost::optional<DNSFilterEngine::Policy>& 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<SOARecordContent> sr, const std::string& dumpZoneFileName, uint64_t configGeneration);
struct rpzStats
{
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<SOARecordContent>(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 "<<deltas.size()<<" deltas from serial "<<di.serial<<", applying.."<<endl;