]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add a Lua hook called when a new dynamic block is inserted
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 13 Nov 2023 10:59:24 +0000 (11:59 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 14 Nov 2023 14:43:39 +0000 (15:43 +0100)
pdns/dnsdist-dynblocks.hh
pdns/dnsdist-lua-inspection.cc
pdns/dnsdistdist/dnsdist-dynblocks.cc
pdns/dnsdistdist/dnsdist-lua-inspection-ffi.h
pdns/dnsdistdist/docs/reference/config.rst

index 25c1036c231d3e524c2c20b7b7d8c4349a501827..a8bd5ae613fcffdf01be8a9cecd083864374a202 100644 (file)
@@ -44,7 +44,7 @@ struct LuaContext::Pusher<dnsdist_ffi_stat_node_t*> {
     }
 };
 
-typedef std::function<bool(dnsdist_ffi_stat_node_t*)> dnsdist_ffi_stat_node_visitor_t;
+using dnsdist_ffi_stat_node_visitor_t = std::function<bool(dnsdist_ffi_stat_node_t*)>;
 
 struct dnsdist_ffi_stat_node_t
 {
@@ -58,6 +58,8 @@ struct dnsdist_ffi_stat_node_t
   std::optional<std::string>& reason;
 };
 
+using dnsdist_ffi_dynamic_block_inserted_hook = std::function<void(uint8_t type, const char* key, const char* reason, uint8_t action, uint64_t duration, bool warning)>;
+
 class DynBlockRulesGroup
 {
 private:
@@ -273,6 +275,11 @@ public:
     d_smtVisitorFFI = std::move(visitor);
   }
 
+  void setNewBlockHook(dnsdist_ffi_dynamic_block_inserted_hook& callback)
+  {
+    d_newBlockHook = std::move(callback);
+  }
+
   void setMasks(uint8_t v4, uint8_t v6, uint8_t port)
   {
     d_v4Mask = v4;
@@ -404,6 +411,7 @@ private:
   SuffixMatchNode d_excludedDomains;
   smtVisitor_t d_smtVisitor;
   dnsdist_ffi_stat_node_visitor_t d_smtVisitorFFI;
+  dnsdist_ffi_dynamic_block_inserted_hook d_newBlockHook;
   uint8_t d_v6Mask{128};
   uint8_t d_v4Mask{32};
   uint8_t d_portMask{0};
index f1bb31fb160b79a371d20e331cc718caa76a7969..105d422ea8832cc02fe9ad64e6cdefe5d2e57e25 100644 (file)
@@ -860,6 +860,11 @@ void setupLuaInspection(LuaContext& luaCtx)
         group->setSuffixMatchRuleFFI(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, std::move(visitor));
       }
     });
+  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(dnsdist_ffi_dynamic_block_inserted_hook)>("setNewBlockInsertedHook", [](std::shared_ptr<DynBlockRulesGroup>& group, dnsdist_ffi_dynamic_block_inserted_hook hook) {
+      if (group) {
+        group->setNewBlockHook(hook);
+      }
+    });
   luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setRCodeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
       if (group) {
         group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
index fbb2fefa2f1d3df7c780a8f260cd036cb87589d7..87ad7b4ae5368953b301e171a974e9ca0e0437ba 100644 (file)
@@ -319,6 +319,14 @@ void DynBlockRulesGroup::addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock,
   }
 
   updated = dnsdist::DynamicBlocks::addOrRefreshBlock(*blocks, now, requestor, rule.d_blockReason, rule.d_blockDuration, rule.d_action, warning, d_beQuiet);
+  if (updated && d_newBlockHook) {
+    try {
+      d_newBlockHook(dnsdist_ffi_dynamic_block_type_nmt, requestor.toString().c_str(), rule.d_blockReason.c_str(), static_cast<uint8_t>(rule.d_action), rule.d_blockDuration, warning);
+    }
+    catch (const std::exception& exp) {
+      warnlog("Error calling the Lua hook after a dynamic block insertion: %s", exp.what());
+    }
+  }
 }
 
 void DynBlockRulesGroup::addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks, const struct timespec& now, const DNSName& name, const DynBlockRule& rule, bool& updated)
@@ -329,6 +337,14 @@ void DynBlockRulesGroup::addOrRefreshBlockSMT(SuffixMatchTree<DynBlock>& blocks,
   }
 
   updated = dnsdist::DynamicBlocks::addOrRefreshBlockSMT(blocks, now, name, rule.d_blockReason, rule.d_blockDuration, rule.d_action, d_beQuiet);
+  if (updated && d_newBlockHook) {
+    try {
+      d_newBlockHook(dnsdist_ffi_dynamic_block_type_smt, name.toString().c_str(), rule.d_blockReason.c_str(), static_cast<uint8_t>(rule.d_action), rule.d_blockDuration, false);
+    }
+    catch (const std::exception& exp) {
+      warnlog("Error calling the Lua hook after a dynamic block insertion: %s", exp.what());
+    }
+  }
 }
 
 void DynBlockRulesGroup::processQueryRules(counts_t& counts, const struct timespec& now)
index ca70eedaffcb60d50fda5e314b7412f06bd33efe..0ee42a3a32b79b56300f4bc55c108584d997e743 100644 (file)
@@ -44,3 +44,7 @@ uint64_t dnsdist_ffi_stat_node_get_children_hits(const dnsdist_ffi_stat_node_t*
 
 void dnsdist_ffi_state_node_set_reason(dnsdist_ffi_stat_node_t* node, const char* reason, size_t reasonSize) __attribute__ ((visibility ("default")));
 
+typedef enum {
+ dnsdist_ffi_dynamic_block_type_nmt = 0,
+ dnsdist_ffi_dynamic_block_type_smt = 1,
+} dnsdist_ffi_dynamic_block_type;
index 95a65724a41d88f9e9887c7d50c341d1c63e9cac..f5bd25e4ac2da9621300344c9578dfd7c513d2bd 100644 (file)
@@ -1547,6 +1547,19 @@ faster than the existing rules.
     :param int action: The action to take when the dynamic block matches, see :ref:`DNSAction <DNSAction>`. (default to the one set with :func:`setDynBlocksAction`)
     :param int warningRate: If set to a non-zero value, the rate above which a warning message will be issued and a no-op block inserted
 
+  .. method:: DynBlockRulesGroup:setNewBlockInsertedHook(hook)
+
+    .. versionadded:: 1.9.0
+
+    Set a Lua function that will be called everytime a new dynamic block is inserted. The function receives:
+
+    * an integer whose value is 0 if the block is Netmask-based one (Client IP or range) and 1 instead (Domain name suffix)
+    * the key (Client IP/range or domain suffix) as a string
+    * the reason of the block as a string
+    * the action of the block as an integer
+    * the duration of the block in seconds
+    * whether this is a warning block (true) or not (false)
+
   .. method:: DynBlockRulesGroup:setRCodeRate(rcode, rate, seconds, reason, blockingTime [, action [, warningRate]])
 
     Adds a rate-limiting rule for responses of code ``rcode``, equivalent to: