]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: Switch to TCP in case of spoofing (near-miss) attempts
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 17 Nov 2020 10:51:14 +0000 (11:51 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 17 Nov 2020 10:51:14 +0000 (11:51 +0100)
Instead of treating this like an unrecoverable network error and
trying the next server, let's switch to TCP instead. This might
prevent a DoS by making us try every single servers and failing,
and will make the spoofing attempt a bit much harder.

pdns/lwres.hh
pdns/pdns_recursor.cc
pdns/syncres.cc
pdns/syncres.hh

index 9a2023ce38f74c02ea2e3fe96b7fedda93ae1659..c27c57c2d91759b9e8c635ad27423d3b5ac94d06 100644 (file)
@@ -55,7 +55,7 @@ class LWResult
 public:
   LWResult() : d_usec(0) {}
 
-  enum class Result : uint8_t { Timeout=0, Success=1, PermanentError=2 /* not transport related */, OSLimitError=3 };
+  enum class Result : uint8_t { Timeout=0, Success=1, PermanentError=2 /* not transport related */, OSLimitError=3, Spoofed=4 /* Spoofing attempt (too many near-misses) */ };
 
   vector<DNSRecord> d_records;
   int d_rcode{0};
index 615681d3136b512e7003d22181d65c3f3f63ec81..6b5a7bcaedc91337bb65c7195720962941ff55b3 100644 (file)
@@ -693,9 +693,7 @@ LWResult::Result asendto(const char *data, size_t len, int flags,
 LWResult::Result arecvfrom(std::string& packet, int flags, const ComboAddress& fromaddr, size_t *d_len,
                            uint16_t id, const DNSName& domain, uint16_t qtype, int fd, const struct timeval* now)
 {
-  static boost::optional<unsigned int> nearMissLimit;
-  if(!nearMissLimit)
-    nearMissLimit=::arg().asNum("spoof-nearmiss-max");
+  static const boost::optional<unsigned int> nearMissLimit = ::arg().asNum("spoof-nearmiss-max");
 
   PacketID pident;
   pident.fd=fd;
@@ -718,7 +716,7 @@ LWResult::Result arecvfrom(std::string& packet, int flags, const ComboAddress& f
     if (*nearMissLimit && pident.nearMisses > *nearMissLimit) {
       g_log<<Logger::Error<<"Too many ("<<pident.nearMisses<<" > "<<*nearMissLimit<<") bogus answers for '"<<domain<<"' from "<<fromaddr.toString()<<", assuming spoof attempt."<<endl;
       g_stats.spoofCount++;
-      return LWResult::Result::PermanentError;
+      return LWResult::Result::Spoofed;
     }
 
     return LWResult::Result::Success;
index 80cf52cfbe444d2acef8fd02364a2467c20031ff..92f674e4de412ba7f3ec07c3f526409e17f9e9f5 100644 (file)
@@ -605,11 +605,11 @@ LWResult::Result SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsM
     // ednsstatus might be cleared, so do a new lookup
     ednsstatus = t_sstorage.ednsstatus.insert(ip).first;
     mode = &ednsstatus->mode;
-    if (ret == LWResult::Result::PermanentError || ret == LWResult::Result::OSLimitError) {
+    if (ret == LWResult::Result::PermanentError || ret == LWResult::Result::OSLimitError || ret == LWResult::Result::Spoofed) {
       return ret; // transport error, nothing to learn here
     }
 
-    if(ret == LWResult::Result::Timeout) { // timeout, not doing anything with it now
+    if (ret == LWResult::Result::Timeout) { // timeout, not doing anything with it now
       return ret;
     }
     else if (*mode == EDNSStatus::UNKNOWN || *mode == EDNSStatus::EDNSOK || *mode == EDNSStatus::EDNSIGNORANT ) {
@@ -3523,7 +3523,7 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
   return done;
 }
 
-bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated)
+bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool& truncated, bool& spoofed)
 {
   bool chained = false;
   LWResult::Result resolveret = LWResult::Result::Success;
@@ -3602,11 +3602,14 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname,
       if(t_timeouts)
         t_timeouts->push_back(remoteIP);
     }
-    else if(resolveret == LWResult::Result::OSLimitError) {
+    else if (resolveret == LWResult::Result::OSLimitError) {
       /* OS resource limit reached */
       LOG(prefix<<qname<<": hit a local resource limit resolving"<< (doTCP ? " over TCP" : "")<<", probable error: "<<stringerror()<<endl);
       g_stats.resourceLimits++;
     }
+    else if (resolveret == LWResult::Result::Spoofed) {
+      spoofed = true;
+    }
     else {
       /* -1 means server unreachable */
       s_unreachables++;
@@ -3680,8 +3683,8 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname,
     t_sstorage.fails.clear(remoteIP);
   }
 
-  if(lwr.d_tcbit) {
-    *truncated = true;
+  if (lwr.d_tcbit) {
+    truncated = true;
 
     if (doTCP) {
       LOG(prefix<<qname<<": truncated bit set, over TCP?"<<endl);
@@ -4005,12 +4008,13 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
           }
 
           bool truncated = false;
+          bool spoofed = false;
           bool gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
-                                             tns->first, *remoteIP, false, &truncated);
-          if (gotAnswer && truncated ) {
+                                             tns->first, *remoteIP, false, truncated, spoofed);
+          if (spoofed || (gotAnswer && truncated) ) {
             /* retry, over TCP this time */
             gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded,
-                                          tns->first, *remoteIP, true, &truncated);
+                                          tns->first, *remoteIP, true, truncated, spoofed);
           }
 
           if (!gotAnswer) {
index 36b5cf7a505b6b4d6e36337b6e77ce911fd6e536..bd32049994f629cfb35b369e16fce08ace7c3a85 100644 (file)
@@ -808,7 +808,7 @@ private:
 
   int doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret,
                   unsigned int depth, set<GetBestNSAnswer>&beenthere, vState& state, StopAtDelegation* stopAtDelegation);
-  bool doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool* truncated);
+  bool doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType& qtype, LWResult& lwr, boost::optional<Netmask>& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool& truncated, bool& spoofed);
   bool processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, DNSName& auth, bool wasForwarded, const boost::optional<Netmask> ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector<DNSRecord>& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state);
 
   int doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, vState& state);