]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
auth AXFR server: abort on chunk with TC set 11954/head
authorPeter van Dijk <peter.van.dijk@powerdns.com>
Wed, 14 Sep 2022 12:52:40 +0000 (14:52 +0200)
committerPeter van Dijk <peter.van.dijk@powerdns.com>
Tue, 27 Sep 2022 11:04:23 +0000 (13:04 +0200)
pdns/dnspacket.cc
pdns/dnspacket.hh
pdns/tcpreceiver.cc

index 1a7de51ed55231c9e062e8ff4cbbf8187933123c..9e40f6f0ad04c45c1d82446bf8c856fd7b9ffd4d 100644 (file)
@@ -63,10 +63,10 @@ DNSPacket::DNSPacket(bool isQuery): d_isQuery(isQuery)
   memset(&d, 0, sizeof(d));
 }
 
-const string& DNSPacket::getString()
+const string& DNSPacket::getString(bool throwsOnTruncation)
 {
   if(!d_wrapped)
-    wrapup();
+    wrapup(throwsOnTruncation);
 
   return d_rawpacket;
 }
@@ -263,7 +263,7 @@ bool DNSPacket::isEmpty()
 /** Must be called before attempting to access getData(). This function stuffs all resource
  *  records found in rrs into the data buffer. It also frees resource records queued for us.
  */
-void DNSPacket::wrapup()
+void DNSPacket::wrapup(bool throwsOnTruncation)
 {
   if(d_wrapped) {
     return;
@@ -356,6 +356,9 @@ void DNSPacket::wrapup()
         pw.startRecord(pos->dr.d_name, pos->dr.d_type, pos->dr.d_ttl, pos->dr.d_class, pos->dr.d_place);
         pos->dr.d_content->toPacket(pw);
         if(pw.size() + optsize > (d_tcp ? 65535 : getMaxReplyLen())) {
+          if (throwsOnTruncation) {
+            throw PDNSException("attempt to write an oversized chunk");
+          }
           pw.rollback();
           pw.truncate();
           pw.getHeader()->tc=1;
index 283fd90f84521bf72a6d309491f44cecd0c01ce0..582f5902cb0bb04c669610f13701a6a497f79318 100644 (file)
@@ -58,7 +58,7 @@ public:
 
   int noparse(const char *mesg, size_t len); //!< just suck the data inward
   int parse(const char *mesg, size_t len); //!< parse a raw UDP or TCP packet and suck the data inward
-  const string& getString(); //!< for serialization - just passes the whole packet
+  const string& getString(bool throwsOnTruncation=false); //!< for serialization - just passes the whole packet. If throwsOnTruncation is set, an exception will be raised if the records are too large to fit inside a single DNS payload, instead of setting the TC bit
 
   // address & socket manipulation
   void setRemote(const ComboAddress*, std::optional<ComboAddress> = std::nullopt);
@@ -105,7 +105,7 @@ public:
   void setQuestion(int op, const DNSName &qdomain, int qtype);  // wipes 'd', sets a random id, creates start of packet (domain, type, class etc)
 
   DTime d_dt; //!< the time this packet was created. replyPacket() copies this in for you, so d_dt becomes the time spent processing the question+answer
-  void wrapup();  // writes out queued rrs, and generates the binary packet. also shuffles. also rectifies dnsheader 'd', and copies it to the stringbuffer
+  void wrapup(bool throwsOnTruncation=false);  // writes out queued rrs, and generates the binary packet. also shuffles. also rectifies dnsheader 'd', and copies it to the stringbuffer. If throwsOnTruncation is set, an exception will be raised if the records are too large to fit inside a single DNS payload, instead of setting the TC bit
   void spoofQuestion(const DNSPacket& qd); //!< paste in the exact right case of the question. Useful for PacketCache
   unsigned int getMinTTL(); //!< returns lowest TTL of any record in the packet
   bool isEmpty(); //!< returns true if there are no rrs in the packet
index 47a1c29ba159efd67f3886bcc92041079ea3cbf7..7514211146563b07987e1714c8894552691845fe 100644 (file)
@@ -61,6 +61,7 @@
 #include "proxy-protocol.hh"
 #include "noinitvector.hh"
 #include "gss_context.hh"
+#include "pdnsexception.hh"
 extern AuthPacketCache PC;
 extern StatBag S;
 
@@ -172,9 +173,11 @@ static void writenWithTimeout(int fd, const void *buffer, unsigned int n, unsign
 
 void TCPNameserver::sendPacket(std::unique_ptr<DNSPacket>& p, int outsock, bool last)
 {
+  uint16_t len=htons(p->getString(true).length());
+
+  // this also calls p->getString; call it after our explicit call so throwsOnTruncation=true is honoured
   g_rs.submitResponse(*p, false, last);
 
-  uint16_t len=htons(p->getString().length());
   string buffer((const char*)&len, 2);
   buffer.append(p->getString());
   writenWithTimeout(outsock, buffer.c_str(), buffer.length(), d_idleTimeout);
@@ -1121,7 +1124,12 @@ send:
     if(!outpacket->getRRS().empty()) {
       if(haveTSIGDetails && !tsigkeyname.empty())
         outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true); // first answer is 'normal'
-      sendPacket(outpacket, outsock, false);
+      try {
+        sendPacket(outpacket, outsock, false);
+      }
+      catch (PDNSException& pe) {
+        throw PDNSException("during axfr-out of "+target.toString()+", this happened: "+pe.reason);
+      }
       trc.d_mac=outpacket->d_trc.d_mac;
       outpacket=getFreshAXFRPacket(q);
     }