]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add a new chain of rules triggered after cache insertion
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 6 Dec 2022 15:23:04 +0000 (16:23 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 6 Dec 2022 15:24:19 +0000 (16:24 +0100)
The general idea is to be able to store the unedited version into
the cache while delivering a different version to the actual client.
This is useful when one is sending different answers to different
clients, like when dealing with abuse traffic, but still want to be
able to cache the initial response from the backend.
We already have a chain of rules that are triggered after a cache-hit,
but until now we lacked the ability to trigger after getting the
response corresponding to a cache-miss.

pdns/dnsdist-lua-actions.cc
pdns/dnsdist-lua-rules.cc
pdns/dnsdist-tcp.cc
pdns/dnsdist-web.cc
pdns/dnsdist.cc
pdns/dnsdist.hh
pdns/dnsdistdist/dnsdist-tcp-upstream.hh
pdns/dnsdistdist/docs/rules-actions.rst
pdns/dnsdistdist/doh.cc
pdns/dnsdistdist/test-dnsdisttcp_cc.cc
regression-tests.dnsdist/test_CacheInsertedResponses.py [new file with mode: 0644]

index 7aab4c5de0923dfc21f4964a5cbcdea5448a93ac..d0df07c7914c47d7920c9e8fc458ac1fbaa64db5 100644 (file)
@@ -2094,6 +2094,14 @@ void setupLuaActions(LuaContext& luaCtx)
       addAction(&g_cachehitrespruleactions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
     });
 
+  luaCtx.writeFunction("addCacheInsertedResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
+    if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
+      throw std::runtime_error("addCacheInsertedResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
+    }
+
+    addAction(&g_cacheInsertedRespRuleActions, var, boost::get<std::shared_ptr<DNSResponseAction> >(era), params);
+  });
+
   luaCtx.writeFunction("addSelfAnsweredResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction>> era, boost::optional<luaruleparams_t> params) {
       if (era.type() != typeid(std::shared_ptr<DNSResponseAction>)) {
         throw std::runtime_error("addSelfAnsweredResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
index 5fd9c9cd74ed9819b97a389028e53579153ed37a..60ec310dd5a0281fa116c8300557d28b6a458925 100644 (file)
@@ -272,6 +272,22 @@ void setupLuaRules(LuaContext& luaCtx)
       mvRule(&g_cachehitrespruleactions, from, to);
     });
 
+  luaCtx.writeFunction("showCacheInsertedResponseRules", [](boost::optional<ruleparams_t> vars) {
+    showRules(&g_cacheInsertedRespRuleActions, vars);
+  });
+
+  luaCtx.writeFunction("rmCacheInsertedResponseRule", [](boost::variant<unsigned int, std::string> id) {
+    rmRule(&g_cacheInsertedRespRuleActions, id);
+  });
+
+  luaCtx.writeFunction("mvCacheInsertedResponseRuleToTop", []() {
+    moveRuleToTop(&g_cacheInsertedRespRuleActions);
+  });
+
+  luaCtx.writeFunction("mvCacheInsertedResponseRule", [](unsigned int from, unsigned int to) {
+    mvRule(&g_cacheInsertedRespRuleActions, from, to);
+  });
+
   luaCtx.writeFunction("showSelfAnsweredResponseRules", [](boost::optional<ruleparams_t> vars) {
       showRules(&g_selfansweredrespruleactions, vars);
     });
index c9f4120b7720c7f0ab04896e7b11e0d6f6d1c49d..c800c616a9f0cf59ef196f3f49c8bd750cb77e29 100644 (file)
@@ -538,7 +538,7 @@ void IncomingTCPConnectionState::handleResponse(const struct timeval& now, TCPRe
 
     memcpy(&response.d_cleartextDH, dr.getHeader(), sizeof(response.d_cleartextDH));
 
-    if (!processResponse(response.d_buffer, state->d_threadData.localRespRuleActions, dr, false, false)) {
+    if (!processResponse(response.d_buffer, *state->d_threadData.localRespRuleActions, *state->d_threadData.localCacheInsertedRespRuleActions, dr, false, false)) {
       state->terminateClientConnection();
       return;
     }
index 603a8e5bee0432fa1f9939dc2a0ef502d69df995..39363981f2452c7e0db2f7d1184c67698bcf9378 100644 (file)
@@ -833,6 +833,7 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp)
   addRulesToPrometheusOutput(output, g_ruleactions);
   addRulesToPrometheusOutput(output, g_respruleactions);
   addRulesToPrometheusOutput(output, g_cachehitrespruleactions);
+  addRulesToPrometheusOutput(output, g_cacheInsertedRespRuleActions);
   addRulesToPrometheusOutput(output, g_selfansweredrespruleactions);
 
 #ifndef DISABLE_DYNBLOCKS
@@ -1195,6 +1196,7 @@ static void handleStats(const YaHTTP::Request& req, YaHTTP::Response& resp)
   }
   auto responseRules = someResponseRulesToJson(&g_respruleactions);
   auto cacheHitResponseRules = someResponseRulesToJson(&g_cachehitrespruleactions);
+  auto cacheInsertedResponseRules = someResponseRulesToJson(&g_cacheInsertedRespRuleActions);
   auto selfAnsweredResponseRules = someResponseRulesToJson(&g_selfansweredrespruleactions);
 
   string acl;
@@ -1236,6 +1238,7 @@ static void handleStats(const YaHTTP::Request& req, YaHTTP::Response& resp)
     { "rules", std::move(rules) },
     { "response-rules", std::move(responseRules) },
     { "cache-hit-response-rules", std::move(cacheHitResponseRules) },
+    { "cache-inserted-response-rules", std::move(cacheInsertedResponseRules) },
     { "self-answered-response-rules", std::move(selfAnsweredResponseRules) },
     { "acl", std::move(acl) },
     { "local", std::move(localaddressesStr) },
index d0b3fbec937d059a47ce3bf8352722d828690350..b34a54b43d6ccb24827e224ab3e7a5d6f38377b5 100644 (file)
@@ -135,6 +135,7 @@ std::vector<uint32_t> g_TCPFastOpenKey;
 GlobalStateHolder<vector<DNSDistRuleAction> > g_ruleactions;
 GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_respruleactions;
 GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitrespruleactions;
+GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cacheInsertedRespRuleActions;
 GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredrespruleactions;
 
 Rings g_rings;
@@ -477,15 +478,15 @@ static bool encryptResponse(PacketBuffer& response, size_t maximumSize, bool tcp
 }
 #endif /* HAVE_DNSCRYPT */
 
-static bool applyRulesToResponse(LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr)
+static bool applyRulesToResponse(const std::vector<DNSDistResponseRuleAction>& respRuleActions, DNSResponse& dr)
 {
-  DNSResponseAction::Action action=DNSResponseAction::Action::None;
+  DNSResponseAction::Action action = DNSResponseAction::Action::None;
   std::string ruleresult;
-  for(const auto& lr : *localRespRuleActions) {
-    if(lr.d_rule->matches(&dr)) {
+  for (const auto& lr : respRuleActions) {
+    if (lr.d_rule->matches(&dr)) {
       lr.d_rule->d_matches++;
-      action=(*lr.d_action)(&dr, &ruleresult);
-      switch(action) {
+      action = (*lr.d_action)(&dr, &ruleresult);
+      switch (action) {
       case DNSResponseAction::Action::Allow:
         return true;
         break;
@@ -514,9 +515,9 @@ static bool applyRulesToResponse(LocalStateHolder<vector<DNSDistResponseRuleActi
 
 // whether the query was received over TCP or not (for rules, dnstap, protobuf, ...) will be taken from the DNSResponse, but receivedOverUDP is used to insert into the cache,
 // so that answers received over UDP for DoH are still cached with UDP answers.
-bool processResponse(PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted, bool receivedOverUDP)
+bool processResponse(PacketBuffer& response, const vector<DNSDistResponseRuleAction>& respRuleActions, const vector<DNSDistResponseRuleAction>& insertedRespRuleActions, DNSResponse& dr, bool muted, bool receivedOverUDP)
 {
-  if (!applyRulesToResponse(localRespRuleActions, dr)) {
+  if (!applyRulesToResponse(respRuleActions, dr)) {
     return false;
   }
 
@@ -547,6 +548,10 @@ bool processResponse(PacketBuffer& response, LocalStateHolder<vector<DNSDistResp
     }
 
     dr.packetCache->insert(cacheKey, zeroScope ? boost::none : dr.subnet, dr.cacheFlags, dr.dnssecOK, *dr.qname, dr.qtype, dr.qclass, response, receivedOverUDP, dr.getHeader()->rcode, dr.tempFailureTTL);
+
+    if (!applyRulesToResponse(insertedRespRuleActions, dr)) {
+      return false;
+    }
   }
 
 #ifdef HAVE_DNSCRYPT
@@ -637,6 +642,7 @@ void responderThread(std::shared_ptr<DownstreamState> dss)
   try {
   setThreadName("dnsdist/respond");
   auto localRespRuleActions = g_respruleactions.getLocal();
+  auto localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal();
   const size_t initialBufferSize = getInitialUDPPacketBufferSize();
   PacketBuffer response(initialBufferSize);
 
@@ -744,7 +750,7 @@ void responderThread(std::shared_ptr<DownstreamState> dss)
         }
         memcpy(&cleartextDH, dr.getHeader(), sizeof(cleartextDH));
 
-        if (!processResponse(response, localRespRuleActions, dr, ids->cs && ids->cs->muted, true)) {
+        if (!processResponse(response, *localRespRuleActions, *localCacheInsertedRespRuleActions, dr, ids->cs && ids->cs->muted, true)) {
           dss->releaseState(queryId);
           continue;
         }
@@ -1233,7 +1239,7 @@ static bool prepareOutgoingResponse(LocalHolders& holders, ClientState& cs, DNSQ
   dr.qTag = std::move(dq.qTag);
   dr.delayMsec = dq.delayMsec;
 
-  if (!applyRulesToResponse(cacheHit ? holders.cacheHitRespRuleactions : holders.selfAnsweredRespRuleactions, dr)) {
+  if (!applyRulesToResponse(cacheHit ? *holders.cacheHitRespRuleactions : *holders.selfAnsweredRespRuleactions, dr)) {
     return false;
   }
 
@@ -1413,6 +1419,7 @@ public:
     auto& ids = response.d_idstate;
 
     static thread_local LocalStateHolder<vector<DNSDistResponseRuleAction>> localRespRuleActions = g_respruleactions.getLocal();
+    static thread_local LocalStateHolder<vector<DNSDistResponseRuleAction>> localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal();
     DNSResponse dr = makeDNSResponseFromIDState(ids, response.d_buffer);
     if (response.d_buffer.size() > d_payloadSize) {
       vinfolog("Got a response of size %d over TCP, while the initial UDP payload size was %d, truncating", response.d_buffer.size(), d_payloadSize);
@@ -1423,7 +1430,7 @@ public:
     dnsheader cleartextDH;
     memcpy(&cleartextDH, dr.getHeader(), sizeof(cleartextDH));
 
-    if (!processResponse(response.d_buffer, localRespRuleActions, dr, false, true)) {
+    if (!processResponse(response.d_buffer, *localRespRuleActions, *localCacheInsertedRespRuleActions, dr, false, true)) {
       return;
     }
 
index 698aee212e5c398921f91a042230d59085bce929..651268bb74b03ff6280d6f90009453ae953f93f5 100644 (file)
@@ -1146,6 +1146,7 @@ extern GlobalStateHolder<vector<DNSDistRuleAction> > g_ruleactions;
 extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_respruleactions;
 extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitrespruleactions;
 extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredrespruleactions;
+extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cacheInsertedRespRuleActions;
 extern GlobalStateHolder<NetmaskGroup> g_ACL;
 
 extern ComboAddress g_serverControl; // not changed during runtime
@@ -1181,7 +1182,7 @@ extern std::vector<std::shared_ptr<DynBPFFilter> > g_dynBPFFilters;
 
 struct LocalHolders
 {
-  LocalHolders(): acl(g_ACL.getLocal()), policy(g_policy.getLocal()), ruleactions(g_ruleactions.getLocal()), cacheHitRespRuleactions(g_cachehitrespruleactions.getLocal()), selfAnsweredRespRuleactions(g_selfansweredrespruleactions.getLocal()), servers(g_dstates.getLocal()), dynNMGBlock(g_dynblockNMG.getLocal()), dynSMTBlock(g_dynblockSMT.getLocal()), pools(g_pools.getLocal())
+  LocalHolders(): acl(g_ACL.getLocal()), policy(g_policy.getLocal()), ruleactions(g_ruleactions.getLocal()), cacheHitRespRuleactions(g_cachehitrespruleactions.getLocal()), cacheInsertedRespRuleActions(g_cacheInsertedRespRuleActions.getLocal()), selfAnsweredRespRuleactions(g_selfansweredrespruleactions.getLocal()), servers(g_dstates.getLocal()), dynNMGBlock(g_dynblockNMG.getLocal()), dynSMTBlock(g_dynblockSMT.getLocal()), pools(g_pools.getLocal())
   {
   }
 
@@ -1189,6 +1190,7 @@ struct LocalHolders
   LocalStateHolder<ServerPolicy> policy;
   LocalStateHolder<vector<DNSDistRuleAction> > ruleactions;
   LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheHitRespRuleactions;
+  LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheInsertedRespRuleActions;
   LocalStateHolder<vector<DNSDistResponseRuleAction> > selfAnsweredRespRuleactions;
   LocalStateHolder<servers_t> servers;
   LocalStateHolder<NetmaskTree<DynBlock, AddressAndPortRange> > dynNMGBlock;
@@ -1210,7 +1212,7 @@ bool getLuaNoSideEffect(); // set if there were only explicit declarations of _n
 void resetLuaSideEffect(); // reset to indeterminate state
 
 bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote, unsigned int& qnameWireLength);
-bool processResponse(PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted, bool receivedOverUDP);
+bool processResponse(PacketBuffer& response, const std::vector<DNSDistResponseRuleAction>& respRuleActions, const std::vector<DNSDistResponseRuleAction>& insertedRespRuleActions, DNSResponse& dr, bool muted, bool receivedOverUDP);
 bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dq, std::string& ruleresult, bool& drop);
 
 bool checkQueryHeaders(const struct dnsheader* dh, ClientState& cs);
index 6fa76ab6a1a2415f49a9c6260ffaa5c280146932..fea6bdb28aad33d141476a4357e7ba4ffbd74118 100644 (file)
@@ -6,12 +6,14 @@
 class TCPClientThreadData
 {
 public:
-  TCPClientThreadData(): localRespRuleActions(g_respruleactions.getLocal()), mplexer(std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent()))
+  TCPClientThreadData():
+    localRespRuleActions(g_respruleactions.getLocal()), localCacheInsertedRespRuleActions(g_cacheInsertedRespRuleActions.getLocal()), mplexer(std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent()))
   {
   }
 
   LocalHolders holders;
-  LocalStateHolder<vector<DNSDistResponseRuleAction> > localRespRuleActions;
+  LocalStateHolder<vector<DNSDistResponseRuleAction>> localRespRuleActions;
+  LocalStateHolder<vector<DNSDistResponseRuleAction>> localCacheInsertedRespRuleActions;
   std::unique_ptr<FDMultiplexer> mplexer{nullptr};
   int crossProtocolResponsesPipe{-1};
 };
index 5173ee6bc0ac91ec58e93e136fe61ad4adbfe315..4c31d5788c6b4f9174674c845aaae5e0b1cf8e34 100644 (file)
@@ -353,6 +353,58 @@ Functions for manipulating Cache Hit Response Rules:
 
   Before 1.6.0 this function used to move the last cache hit response rule to the first position, which is now handled by :func:`mvCacheHitResponseRuleToTop`.
 
+Functions for manipulating Cache Inserted Response Rules:
+
+.. function:: addCacheInsertedResponseAction(DNSRule, action [, options])
+
+  .. versionadded:: 1.8.0
+
+  Add a Rule and ResponseAction that is executed after a cache entry has been inserted to the existing rules.
+
+  :param DNSRule: A DNSRule, e.g. an :func:`AllRule` or a compounded bunch of rules using e.g. :func:`AndRule`
+  :param action: The action to take
+  :param table options: A table with key: value pairs with options.
+
+  Options:
+
+  * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule.
+  * ``name``: string - Name to assign to the new rule.
+
+.. function:: mvCacheInsertedResponseRule(from, to)
+
+  .. versionadded:: 1.8.0
+
+  Move cache inserted response rule ``from`` to a position where it is in front of ``to``.
+  ``to`` can be one larger than the largest rule, in which case the rule will be moved to the last position.
+
+  :param int from: Rule number to move
+  :param int to: Location to more the Rule to
+
+.. function:: mvCacheInsertedResponseRuleToTop()
+
+  .. versionadded:: 1.8.0
+
+  This function moves the last cache inserted response rule to the first position.
+
+.. function:: rmCacheInsertedResponseRule(id)
+
+  .. versionadded:: 1.8.0
+
+  :param int id: The position of the rule to remove if ``id`` is numerical, its UUID or name otherwise
+
+.. function:: showCacheInsertedResponseRules([options])
+
+  .. versionadded:: 1.8.0
+
+  Show all defined cache inserted response rules, optionally displaying their UUIDs.
+
+  :param table options: A table with key: value pairs with display options.
+
+  Options:
+
+  * ``showUUIDs=false``: bool - Whether to display the UUIDs, defaults to false.
+  * ``truncateRuleWidth=-1``: int - Truncate rules output to ``truncateRuleWidth`` size. Defaults to ``-1`` to display the full rule.
+
 Functions for manipulating Self-Answered Response Rules:
 
 .. function:: addSelfAnsweredResponseAction(DNSRule, action [, options])
index cf9c3166908fcd426f8c339907bfec2f28b32d01..088f618b740ec93d4cae03ca27d122bc24be9794 100644 (file)
@@ -458,12 +458,13 @@ public:
     du->response = std::move(response.d_buffer);
     du->ids = std::move(response.d_idstate);
 
-    thread_local LocalStateHolder<vector<DNSDistResponseRuleAction>> localRespRuleActions = g_respruleactions.getLocal();
+    static thread_local LocalStateHolder<vector<DNSDistResponseRuleAction>> localRespRuleActions = g_respruleactions.getLocal();
+    static thread_local LocalStateHolder<vector<DNSDistResponseRuleAction>> localCacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal();
     DNSResponse dr = makeDNSResponseFromIDState(du->ids, du->response);
     dnsheader cleartextDH;
     memcpy(&cleartextDH, dr.getHeader(), sizeof(cleartextDH));
 
-    if (!processResponse(du->response, localRespRuleActions, dr, false, false)) {
+    if (!processResponse(du->response, *localRespRuleActions, *localCacheInsertedRespRuleActions, dr, false, false)) {
       du.reset();
       return;
     }
@@ -1666,12 +1667,13 @@ void handleUDPResponseForDoH(DOHUnitUniquePtr&& du, PacketBuffer&& udpResponse,
 
   const dnsheader* dh = reinterpret_cast<const struct dnsheader*>(du->response.data());
   if (!dh->tc) {
-    thread_local LocalStateHolder<vector<DNSDistResponseRuleAction>> localRespRuleActions = g_respruleactions.getLocal();
+    static thread_local LocalStateHolder<vector<DNSDistResponseRuleAction>> localRespRuleActions = g_respruleactions.getLocal();
+    static thread_local LocalStateHolder<vector<DNSDistResponseRuleAction>> localcacheInsertedRespRuleActions = g_cacheInsertedRespRuleActions.getLocal();
     DNSResponse dr = makeDNSResponseFromIDState(du->ids, du->response);
     dnsheader cleartextDH;
     memcpy(&cleartextDH, dr.getHeader(), sizeof(cleartextDH));
 
-    if (!processResponse(du->response, localRespRuleActions, dr, false, true)) {
+    if (!processResponse(du->response, *localRespRuleActions, *localcacheInsertedRespRuleActions, dr, false, true)) {
       return;
     }
 
index f3c8678a39ffceb27ced88088d0858160afb326c..056c08ed12ccb6877de2ad7a7d85274ef4a51dec 100644 (file)
@@ -36,6 +36,7 @@ GlobalStateHolder<NetmaskGroup> g_ACL;
 GlobalStateHolder<vector<DNSDistRuleAction> > g_ruleactions;
 GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_respruleactions;
 GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitrespruleactions;
+GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cacheInsertedRespRuleActions;
 GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredrespruleactions;
 GlobalStateHolder<servers_t> g_dstates;
 
@@ -78,12 +79,12 @@ bool responseContentMatches(const PacketBuffer& response, const DNSName& qname,
   return true;
 }
 
-static std::function<bool(PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted)> s_processResponse;
+static std::function<bool(PacketBuffer& response, DNSResponse& dr, bool muted)> s_processResponse;
 
-bool processResponse(PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted, bool receivedOverUDP)
+bool processResponse(PacketBuffer& response, const std::vector<DNSDistResponseRuleAction>& localRespRuleActions, const std::vector<DNSDistResponseRuleAction>& localCacheInsertedRespRuleActions, DNSResponse& dr, bool muted, bool receivedOverUDP)
 {
   if (s_processResponse) {
-    return s_processResponse(response, localRespRuleActions, dr, muted);
+    return s_processResponse(response, dr, muted);
   }
 
   return false;
@@ -898,7 +899,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -938,7 +939,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       throw std::runtime_error("Unexpected error while processing the response");
     };
 
@@ -977,7 +978,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return false;
     };
 
@@ -1020,7 +1021,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1047,7 +1048,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
     s_processQuery = [](DNSQuestion& dq, ClientState& cs, LocalHolders& holders, std::shared_ptr<DownstreamState>& selectedBackend) -> ProcessQueryResult {
       return ProcessQueryResult::SendAnswer;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1085,7 +1086,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1153,7 +1154,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1216,7 +1217,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1252,7 +1253,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1298,7 +1299,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1355,7 +1356,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1411,7 +1412,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1470,7 +1471,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1522,7 +1523,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1582,7 +1583,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1623,7 +1624,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1685,7 +1686,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -1874,7 +1875,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -2006,7 +2007,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -2186,7 +2187,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -2262,7 +2263,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       }
       return ProcessQueryResult::Drop;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -2345,7 +2346,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       }
       return ProcessQueryResult::Drop;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -2462,7 +2463,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -2614,7 +2615,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -2821,7 +2822,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -2995,7 +2996,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = proxyEnabledBackend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -3259,7 +3260,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -3385,7 +3386,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = proxyEnabledBackend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -3470,7 +3471,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = proxyEnabledBackend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -3535,7 +3536,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -3726,7 +3727,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = backend1;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -3811,7 +3812,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
@@ -4043,7 +4044,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendNotOOOR)
       selectedBackend = backend;
       return ProcessQueryResult::PassToBackend;
     };
-    s_processResponse = [](PacketBuffer& response, LocalStateHolder<vector<DNSDistResponseRuleAction> >& localRespRuleActions, DNSResponse& dr, bool muted) -> bool {
+    s_processResponse = [](PacketBuffer& response, DNSResponse& dr, bool muted) -> bool {
       return true;
     };
 
diff --git a/regression-tests.dnsdist/test_CacheInsertedResponses.py b/regression-tests.dnsdist/test_CacheInsertedResponses.py
new file mode 100644 (file)
index 0000000..42b7007
--- /dev/null
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+import base64
+import time
+import dns
+from dnsdisttests import DNSDistTest
+
+class TestCacheInsertedResponses(DNSDistTest):
+
+    capTTLMax = 3600
+    capTTLMin = 60
+    _config_template = """
+    pc = newPacketCache(100, {maxTTL=86400, minTTL=1})
+    getPool(""):setCache(pc)
+    addCacheInsertedResponseAction(makeRule("cacheinsertedresponses.tests.powerdns.com."), LimitTTLResponseAction(%d, %d))
+    newServer{address="127.0.0.1:%s"}
+    """
+    _config_params = ['capTTLMax', 'capTTLMin', '_testServerPort']
+
+    def testTTLSetAfterInsertion(self):
+        """
+        CacheInsertedResponse: Check that the TTL is capped after inserting into the cache
+        """
+        initialTTL = 86400
+        name = 'reduce-ttl-after-insertion.cacheinsertedresponses.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'AAAA', 'IN')
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    initialTTL,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '::1')
+        response.answer.append(rrset)
+
+        responseOnMiss = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    self.capTTLMax,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '::1')
+        responseOnMiss.answer.append(rrset)
+
+        # first query to fill the cache
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEqual(query, receivedQuery)
+        self.assertEqual(receivedResponse, responseOnMiss)
+        self.assertLessEqual(receivedResponse.answer[0].ttl, self.capTTLMax)
+
+        # now the result should be cached
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEqual(receivedResponse, response)
+        self.assertGreater(receivedResponse.answer[0].ttl, self.capTTLMax)
+        self.assertLessEqual(receivedResponse.answer[0].ttl, initialTTL)
+
+    def testTTLRaisedAfterInsertion(self):
+        """
+        CacheInsertedResponse: Check that the TTL can be raised after inserting into the cache
+        """
+        initialTTL = 0
+        name = 'raise-ttl-after-insertion.cacheinsertedresponses.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'AAAA', 'IN')
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    initialTTL,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '::1')
+        response.answer.append(rrset)
+
+        responseOnMiss = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    self.capTTLMax,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '::1')
+        responseOnMiss.answer.append(rrset)
+
+        # first query to fill the cache
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEqual(query, receivedQuery)
+        self.assertEqual(receivedResponse, responseOnMiss)
+        self.assertGreater(receivedResponse.answer[0].ttl, initialTTL)
+        self.assertLessEqual(receivedResponse.answer[0].ttl, self.capTTLMin)
+
+        # the result should NOT have been cached
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEqual(query, receivedQuery)
+        self.assertEqual(receivedResponse, responseOnMiss)
+        self.assertGreater(receivedResponse.answer[0].ttl, initialTTL)
+        self.assertLessEqual(receivedResponse.answer[0].ttl, self.capTTLMin)
+