From: Otto Moerbeek Date: Thu, 4 Sep 2025 11:24:24 +0000 (+0200) Subject: Add feature to add "Cookies Unsupported" entries to cookie-table using rec_control X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3c9174beabcda7e258b2544b1c5b4ba8082e5b7d;p=thirdparty%2Fpdns.git Add feature to add "Cookies Unsupported" entries to cookie-table using rec_control Signed-off-by: Otto Moerbeek --- diff --git a/pdns/recursordist/docs/manpages/rec_control.1.rst b/pdns/recursordist/docs/manpages/rec_control.1.rst index 1a57f0e85..47011b6a1 100644 --- a/pdns/recursordist/docs/manpages/rec_control.1.rst +++ b/pdns/recursordist/docs/manpages/rec_control.1.rst @@ -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. diff --git a/pdns/recursordist/lwres.cc b/pdns/recursordist/lwres.cc index 08020a56e..c4f2af960 100644 --- a/pdns/recursordist/lwres.cc +++ b/pdns/recursordist/lwres.cc @@ -70,6 +70,29 @@ bool g_ECSHardening; static LockGuarded s_cookiestore; +uint64_t addCookiesUnsupported(vector::iterator begin, vector::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::max()); + auto [iter, inserted] = lock->insert(entry); + if (!inserted) { + lock->replace(iter, entry); + } + ++count; + } + catch (const PDNSException &) { + ; + } + ++begin; + } + return count; +} + uint64_t clearCookies(vector::iterator begin, vector::iterator end) { auto lock = s_cookiestore.lock(); diff --git a/pdns/recursordist/lwres.hh b/pdns/recursordist/lwres.hh index 2faada4df..544f34f40 100644 --- a/pdns/recursordist/lwres.hh +++ b/pdns/recursordist/lwres.hh @@ -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& srcmask, const ResolveContext& context, const std::shared_ptr>>& outgoingLoggers, const std::shared_ptr>>& fstrmLoggers, const std::set& exportTypes, LWResult* lwr, bool* chained); uint64_t dumpCookies(int fileDesc); uint64_t clearCookies(vector::iterator begin, vector::iterator end); +uint64_t addCookiesUnsupported(vector::iterator begin, vector::iterator end); void pruneCookies(time_t cutoff); void enableOutgoingCookies(bool flag); diff --git a/pdns/recursordist/rec-cookiestore.cc b/pdns/recursordist/rec-cookiestore.cc index c19832d94..885315cb0 100644 --- a/pdns/recursordist/rec-cookiestore.cc +++ b/pdns/recursordist/rec-cookiestore.cc @@ -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::max() ? "Forever" : timestamp(entry.d_lastupdate, tmp)); } return count; } diff --git a/pdns/recursordist/rec_channel_rec.cc b/pdns/recursordist/rec_channel_rec.cc index 1640f4733..b4ef29de1 100644 --- a/pdns/recursordist/rec_channel_rec.cc +++ b/pdns/recursordist/rec_channel_rec.cc @@ -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); } diff --git a/regression-tests.recursor-dnssec/test_Cookies.py b/regression-tests.recursor-dnssec/test_Cookies.py index 23982c5d4..63427ec17 100644 --- a/regression-tests.recursor-dnssec/test_Cookies.py +++ b/regression-tests.recursor-dnssec/test_Cookies.py @@ -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)