From: Jonas Meurer <mejo@debian.org>
Date: Sun, 18 Sep 2016 12:39:23 +0200
Subject: [PATCH] Add limits to the size of received AXFR, in megabytes

Origin: a014f4c224a7b21f1c648257d1fd1128413129aa
Origin-From: Remi Gacogne <remi.gacogne@powerdns.com>
Origin-Date: Thu, 7 Jul 2016 16:17:22 +0200

This prevents resource exhaustion in case the master is sending a
very large amount of data in an update.

diff -rNu a/pdns/common_startup.cc b/pdns/common_startup.cc
--- a/pdns/common_startup.cc	2016-09-18 11:43:18.000000000 +0200
+++ b/pdns/common_startup.cc	2016-09-18 12:25:41.084684252 +0200
@@ -137,6 +137,8 @@
   ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
   
   ::arg().setSwitch("traceback-handler","Enable the traceback handler (Linux only)")="yes";
+
+  ::arg().set("xfr-max-received-mbytes", "Maximum number of megabytes received from an incoming AXFR")="100";
 }
 
 void declareStats(void)
diff -rNu a/pdns/pdns.conf-dist b/pdns/pdns.conf-dist
--- a/pdns/pdns.conf-dist	2012-05-04 12:13:23.000000000 +0200
+++ b/pdns/pdns.conf-dist	2016-09-18 12:29:34.535897396 +0200
@@ -404,4 +404,8 @@
 #
 # wildcard-url=no
 
+#################################
+# xfr-max-received-mbytes      Maximum number of megabytes received from an incoming AXFR
+#
+# xfr-max-received-mbytes=100
 
diff -rNu a/pdns/resolver.cc b/pdns/resolver.cc
--- a/pdns/resolver.cc	2012-05-04 12:13:23.000000000 +0200
+++ b/pdns/resolver.cc	2016-09-18 12:34:35.887084262 +0200
@@ -307,8 +307,8 @@
 }
 
 AXFRRetriever::AXFRRetriever(const ComboAddress& remote, const string& domain, const string& tsigkeyname, const string& tsigalgorithm, 
-  const string& tsigsecret)
-: d_tsigkeyname(tsigkeyname), d_tsigsecret(tsigsecret), d_tsigPos(0), d_nonSignedMessages(0)
+  const string& tsigsecret, size_t maxReceivedBytes)
+  : d_tsigkeyname(tsigkeyname), d_tsigsecret(tsigsecret), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes), d_tsigPos(0), d_nonSignedMessages(0)
 {
   ComboAddress local;
   if(remote.sin4.sin_family == AF_INET)
@@ -383,7 +383,13 @@
   if(len<0)
     throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
   
+  if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
+    throw ResolverException("Reached the maximum number of received bytes during AXFR");
+
   timeoutReadn(len); 
+
+  d_receivedBytes += (uint16_t) len;
+
   MOADNSParser mdp(d_buf.get(), len);
 
   int err = parseResult(mdp, "", 0, 0, &res);
diff -rNu a/pdns/resolver.hh b/pdns/resolver.hh
--- a/pdns/resolver.hh	2012-05-04 12:13:23.000000000 +0200
+++ b/pdns/resolver.hh	2016-09-18 12:36:24.946826674 +0200
@@ -84,7 +84,7 @@
 class AXFRRetriever : public boost::noncopyable
 {
   public:
-    AXFRRetriever(const ComboAddress& remote, const string& zone, const string& tsigkeyname=string(), const string& tsigalgorithm=string(), const string& tsigsecret=string());
+    AXFRRetriever(const ComboAddress& remote, const string& zone, const string& tsigkeyname=string(), const string& tsigalgorithm=string(), const string& tsigsecret=string(), size_t maxReceivedBytes=0);
 	~AXFRRetriever();
     int getChunk(Resolver::res_t &res);  
   
@@ -103,6 +103,8 @@
     string d_tsigsecret;
     string d_prevMac; // RFC2845 4.4
     string d_signData;
+    size_t d_receivedBytes;
+    size_t d_maxReceivedBytes;
     uint32_t d_tsigPos;
     uint d_nonSignedMessages; // RFC2845 4.4
     TSIGRecordContent d_trc;
diff -rNu a/pdns/slavecommunicator.cc b/pdns/slavecommunicator.cc
--- a/pdns/slavecommunicator.cc	2012-05-04 12:13:23.000000000 +0200
+++ b/pdns/slavecommunicator.cc	2016-09-18 12:38:41.770522631 +0200
@@ -144,7 +144,7 @@
         return;
       }
     }
-    AXFRRetriever retriever(raddr, domain.c_str(), tsigkeyname, tsigalgorithm, tsigsecret);
+    AXFRRetriever retriever(raddr, domain.c_str(), tsigkeyname, tsigalgorithm, tsigsecret, ((size_t) ::arg().asNum("xfr-max-received-mbytes")) * 1024 * 1024);
 
     bool gotPresigned = false;
     bool gotNSEC3 = false;
