]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add SetReducedTTLResponseAction
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 6 Jan 2023 15:46:53 +0000 (16:46 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 6 Jan 2023 15:46:53 +0000 (16:46 +0100)
pdns/dnsdist-console.cc
pdns/dnsdist-lua-actions.cc
pdns/dnsdistdist/docs/rules-actions.rst
regression-tests.dnsdist/test_Responses.py

index 472195d010a6210bf045f6fa595b9c0f798ea603..9065317118af2ab356ad68784b7ba9cc4ba80b54 100644 (file)
@@ -692,6 +692,7 @@ const std::vector<ConsoleKeyword> 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`" },
index b36f4bad973217c134adc52cb1042b243f50d30c..e82c0d23d86ee33f5ecbf5b9f6b7bd3293757121 100644 (file)
@@ -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<char *>(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<typename T, typename ActionT>
 static void addAction(GlobalStateHolder<vector<T> > *someRuleActions, const luadnsrule_t& var, const std::shared_ptr<ActionT>& action, boost::optional<luaruleparams_t>& params) {
   setLuaSideEffect();
@@ -2272,6 +2298,13 @@ void setupLuaActions(LuaContext& luaCtx)
       return std::shared_ptr<DNSResponseAction>(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<DNSResponseAction>(new SetReducedTTLResponseAction(percentage));
+    });
+
   luaCtx.writeFunction("ClearRecordTypesResponseAction", [](LuaTypeOrArrayOf<int> types) {
       std::set<QType> qtypes{};
       if (types.type() == typeid(int)) {
index 7d2237a60e181dc3b058131629044acb003a53d8..1077859a0df046220de636bd2c264950b5fe547e 100644 (file)
@@ -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
index 243ae6f50d442b63f211636990a22d8c34b801b3..af16a644bf3bc4527632eb696dd0be4a18d63be0 100644 (file)
@@ -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 = """