]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Add feature to add "Cookies Unsupported" entries to cookie-table using rec_control
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Thu, 4 Sep 2025 11:24:24 +0000 (13:24 +0200)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Thu, 4 Sep 2025 13:14:28 +0000 (15:14 +0200)
Signed-off-by: Otto Moerbeek <otto.moerbeek@open-xchange.com>
pdns/recursordist/docs/manpages/rec_control.1.rst
pdns/recursordist/lwres.cc
pdns/recursordist/lwres.hh
pdns/recursordist/rec-cookiestore.cc
pdns/recursordist/rec_channel_rec.cc
regression-tests.recursor-dnssec/test_Cookies.py

index 1a57f0e85e4816dd800e0dad2c2a29636b43e1b8..47011b6a183920885cd3b6dbf0e87d5660d97b95 100644 (file)
@@ -53,6 +53,9 @@ Options
 
 Commands
 --------
+add-cookies-unsupported *IP* [*IP*...]
+    Add non-expiring IPs of servers that do not support cookies to the cookie table.
+
 add-dont-throttle-names *NAME* [*NAME*...]
     Add names for nameserver domains that may not be throttled.
 
index 08020a56e6ee49d755ebb74f938f24158a4f139d..c4f2af9602ad9063c875742d803c67ec56bbc707 100644 (file)
@@ -70,6 +70,29 @@ bool g_ECSHardening;
 
 static LockGuarded<CookieStore> s_cookiestore;
 
+uint64_t addCookiesUnsupported(vector<string>::iterator begin, vector<string>::iterator end)
+{
+  auto lock = s_cookiestore.lock();
+  uint64_t count = 0;
+  while (begin != end) {
+    try {
+      CookieEntry entry;
+      entry.d_address = ComboAddress(*begin, 53);
+      entry.setSupport(CookieEntry::Support::Unsupported, std::numeric_limits<time_t>::max());
+      auto [iter, inserted] = lock->insert(entry);
+      if (!inserted) {
+        lock->replace(iter, entry);
+      }
+      ++count;
+    }
+    catch (const PDNSException &) {
+      ;
+    }
+    ++begin;
+  }
+  return count;
+}
+
 uint64_t clearCookies(vector<string>::iterator begin, vector<string>::iterator end)
 {
   auto lock = s_cookiestore.lock();
index 2faada4dfec6f97af82cd75ff92afa8804d1f7a9..544f34f40bdb6b56a7d16602a6570d5f88715eed 100644 (file)
@@ -101,5 +101,6 @@ LWResult::Result arecvfrom(PacketBuffer& packet, int flags, const ComboAddress&
 LWResult::Result asyncresolve(const OptLog& log, const ComboAddress& address, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, const ResolveContext& context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers, const std::set<uint16_t>& exportTypes, LWResult* lwr, bool* chained);
 uint64_t dumpCookies(int fileDesc);
 uint64_t clearCookies(vector<string>::iterator begin, vector<string>::iterator end);
+uint64_t addCookiesUnsupported(vector<string>::iterator begin, vector<string>::iterator end);
 void pruneCookies(time_t cutoff);
 void enableOutgoingCookies(bool flag);
index c19832d94f00dd6194e36b53bcca2bb5266982c4..885315cb0f22a1ffe1bfb05ca8821081295e74bd 100644 (file)
@@ -54,7 +54,7 @@ uint64_t CookieStore::dump(int fileDesc) const
             entry.d_address.toString().c_str(), entry.d_localaddress.toString().c_str(),
             entry.d_cookie.toDisplayString().c_str(),
             CookieEntry::toString(entry.d_support).c_str(),
-            timestamp(entry.d_lastupdate, tmp));
+            entry.d_lastupdate == std::numeric_limits<time_t>::max() ? "Forever" : timestamp(entry.d_lastupdate, tmp));
   }
   return count;
 }
index 1640f4733f2c349cdaa488d6fa740e00314c371a..b4ef29de1e7588654b2bdc4bc450d996f3377483 100644 (file)
@@ -1882,6 +1882,7 @@ static void* pleaseSupplantProxyMapping(const ProxyMapping& proxyMapping)
 static RecursorControlChannel::Answer help()
 {
   return {0,
+          "add-cookies-unsupported [IP...]  add non-expiring 'Unsupported' entry for IP to cookie table\n"
           "add-dont-throttle-names [N...]   add names that are not allowed to be throttled\n"
           "add-dont-throttle-netmasks [N...]\n"
           "                                 add netmasks that are not allowed to be throttled\n"
@@ -2111,6 +2112,10 @@ RecursorControlChannel::Answer RecursorControlParser::getAnswer(int socket, cons
     auto count = clearCookies(begin, end);
     return {0, "Cleared " + std::to_string(count) + " entr" + addS(count, "y", "ies") + " from cookies table\n"};
   }
+  if (cmd == "add-cookies-unsupported") {
+    auto count = addCookiesUnsupported(begin, end);
+    return {0, "Added " + std::to_string(count) + " entr" + addS(count, "y", "ies") + " to cookies table\n"};
+  }
   if (cmd == "dump-cookies") {
     return doDumpToFile(socket, pleaseDumpCookiesMap, cmd, false);
   }
index 23982c5d48f621e23eba20fca9e3efb1979781d8..63427ec17a6b00258e248ed12746698f2ddd5378 100644 (file)
@@ -94,7 +94,7 @@ packetcache:
     def testAuthRepliesWithCookie(self):
         confdir = os.path.join('configs', self._confdir)
         # Case: rec gets a proper client and server cookie back
-        self.recControl(confdir, 'clear-cookies')
+        self.recControl(confdir, 'clear-cookies', '*')
         query = dns.message.make_query('supported.cookies.example.', 'A')
         expected = dns.rrset.from_text('supported.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
         res = self.sendUDPQuery(query)
@@ -114,7 +114,7 @@ packetcache:
     def testAuthSendsIncorrectClientCookie(self):
         confdir = os.path.join('configs', self._confdir)
         # Case: rec gets a an incorrect client cookie back, we ignore that over TCP
-        self.recControl(confdir, 'clear-cookies')
+        self.recControl(confdir, 'clear-cookies', '*')
         query = dns.message.make_query('wrongcc.cookies.example.', 'A')
         expected = dns.rrset.from_text('wrongcc.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
         res = self.sendUDPQuery(query)
@@ -125,7 +125,7 @@ packetcache:
     def testAuthSendsBADCOOKIEOverUDP(self):
         confdir = os.path.join('configs', self._confdir)
         # Case: rec gets a BADCOOKIE, even on retry and should fall back to TCP
-        self.recControl(confdir, 'clear-cookies')
+        self.recControl(confdir, 'clear-cookies', '*')
         query = dns.message.make_query('badcookie.cookies.example.', 'A')
         expected = dns.rrset.from_text('badcookie.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
         res = self.sendUDPQuery(query)
@@ -136,7 +136,7 @@ packetcache:
     def testAuthSendsMalformedCookie(self):
         confdir = os.path.join('configs', self._confdir)
         # Case: rec gets a malformed cookie, should ignore packet
-        self.recControl(confdir, 'clear-cookies')
+        self.recControl(confdir, 'clear-cookies', '*')
         query = dns.message.make_query('malformed.cookies.example.', 'A')
         expected = dns.rrset.from_text('malformed.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
         res = self.sendUDPQuery(query)
@@ -148,7 +148,7 @@ packetcache:
     def testForgottenCookie(self):
         confdir = os.path.join('configs', self._confdir)
         # Case: rec gets a proper client and server cookie back
-        self.recControl(confdir, 'clear-cookies')
+        self.recControl(confdir, 'clear-cookies', '*')
         query = dns.message.make_query('supported3.cookies.example.', 'A')
         expected = dns.rrset.from_text('supported3.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
         res = self.sendUDPQuery(query)
@@ -158,7 +158,7 @@ packetcache:
 
         # Case: we get a an correct client and server cookie back
         # We HAVE cleared the cookie tables, so the old server cookie is fogotten
-        self.recControl(confdir, 'clear-cookies')
+        self.recControl(confdir, 'clear-cookies', '*')
         query = dns.message.make_query('supported4.cookies.example.', 'A')
         expected = dns.rrset.from_text('supported4.cookies.example.', 15, dns.rdataclass.IN, 'A', '127.0.0.1')
         res = self.sendUDPQuery(query)