From: Remi Gacogne Date: Fri, 6 Jan 2023 15:46:53 +0000 (+0100) Subject: dnsdist: Add SetReducedTTLResponseAction X-Git-Tag: dnsdist-1.8.0-rc1~127^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d55fe43ab3319705228c9028663b5f248b63b79d;p=thirdparty%2Fpdns.git dnsdist: Add SetReducedTTLResponseAction --- diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index 472195d010..9065317118 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -692,6 +692,7 @@ const std::vector g_consoleKeywords{ { "setProxyProtocolMaximumPayloadSize", true, "max", "Set the maximum size of a Proxy Protocol payload, in bytes" }, { "setQueryCount", true, "bool", "set whether queries should be counted" }, { "setQueryCountFilter", true, "func", "filter queries that would be counted, where `func` is a function with parameter `dq` which decides whether a query should and how it should be counted" }, + { "SetReducedTTLResponseAction", true, "percentage", "Reduce the TTL of records in a response to a given percentage" }, { "setRingBuffersLockRetries", true, "n", "set the number of attempts to get a non-blocking lock to a ringbuffer shard before blocking" }, { "setRingBuffersOptions", true, "{ lockRetries=int, recordQueries=true, recordResponses=true }", "set ringbuffer options" }, { "setRingBuffersSize", true, "n [, numberOfShards]", "set the capacity of the ringbuffers used for live traffic inspection to `n`, and optionally the number of shards to use to `numberOfShards`" }, diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index b36f4bad97..e82c0d23d8 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -2001,6 +2001,32 @@ private: uint8_t d_type; }; +class SetReducedTTLResponseAction : public DNSResponseAction, public boost::noncopyable +{ +public: + // this action does not stop the processing + SetReducedTTLResponseAction(uint8_t percentage) : d_ratio(percentage / 100.0) + { + } + + DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override + { + auto visitor = [&](uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl) { + return ttl * d_ratio; + }; + editDNSPacketTTL(reinterpret_cast(dr->getMutableData().data()), dr->getData().size(), visitor); + return DNSResponseAction::Action::None; + } + + std::string toString() const override + { + return "reduce ttl to " + std::to_string(d_ratio * 100) + " of its value"; + } + +private: + double d_ratio{1.0}; +}; + template static void addAction(GlobalStateHolder > *someRuleActions, const luadnsrule_t& var, const std::shared_ptr& action, boost::optional& params) { setLuaSideEffect(); @@ -2272,6 +2298,13 @@ void setupLuaActions(LuaContext& luaCtx) return std::shared_ptr(new LimitTTLResponseAction(0, max)); }); + luaCtx.writeFunction("SetReducedTTLResponseAction", [](uint8_t percentage) { + if (percentage > 100) { + throw std::runtime_error(std::string("SetReducedTTLResponseAction takes a percentage between 0 and 100.")); + } + return std::shared_ptr(new SetReducedTTLResponseAction(percentage)); + }); + luaCtx.writeFunction("ClearRecordTypesResponseAction", [](LuaTypeOrArrayOf types) { std::set qtypes{}; if (types.type() == typeid(int)) { diff --git a/pdns/dnsdistdist/docs/rules-actions.rst b/pdns/dnsdistdist/docs/rules-actions.rst index 7d2237a60e..1077859a0d 100644 --- a/pdns/dnsdistdist/docs/rules-actions.rst +++ b/pdns/dnsdistdist/docs/rules-actions.rst @@ -1494,6 +1494,16 @@ The following actions exist. :param table values: A table of types and values to send, for example: ``{ [0] = foo", [42] = "bar" }`` +.. function:: SetReducedTTLResponseAction(percentage) + + .. versionadded:: 1.8.0 + + Reduce the TTL of records in a response to a percentage of the original TTL. For example, + passing 50 means that the original TTL will be cut in half. + Subsequent rules are processed after this action. + + :param int percentage: The percentage to use + .. function:: SetSkipCacheAction() .. versionadded:: 1.6.0 diff --git a/regression-tests.dnsdist/test_Responses.py b/regression-tests.dnsdist/test_Responses.py index 243ae6f50d..af16a644bf 100644 --- a/regression-tests.dnsdist/test_Responses.py +++ b/regression-tests.dnsdist/test_Responses.py @@ -346,6 +346,39 @@ class TestResponseRuleLimitTTL(DNSDistTest): self.assertNotEqual(response.answer[0].ttl, receivedResponse.answer[0].ttl) self.assertEqual(receivedResponse.answer[0].ttl, self._lowttl) +class TestSetReducedTTL(DNSDistTest): + + _percentage = 42 + _initialTTL = 100 + _config_params = ['_percentage', '_testServerPort'] + _config_template = """ + addResponseAction(AllRule(), SetReducedTTLResponseAction(%d)) + newServer{address="127.0.0.1:%s"} + """ + + def testLimitTTL(self): + """ + Responses: Reduce TTL to 42% + """ + name = 'reduced-ttl.responses.tests.powerdns.com.' + query = dns.message.make_query(name, 'A', 'IN') + response = dns.message.make_response(query) + rrset = dns.rrset.from_text(name, + self._initialTTL, + dns.rdataclass.IN, + dns.rdatatype.A, + '192.0.2.1') + response.answer.append(rrset) + + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, response) + receivedQuery.id = query.id + self.assertEqual(query, receivedQuery) + self.assertEqual(response, receivedResponse) + self.assertNotEqual(response.answer[0].ttl, receivedResponse.answer[0].ttl) + self.assertEqual(receivedResponse.answer[0].ttl, self._percentage) + class TestResponseLuaActionReturnSyntax(DNSDistTest): _config_template = """