]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Fix a bug in the Dynamic Blocks when port ranges are used
authorRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 20 Oct 2021 15:45:59 +0000 (17:45 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 21 Oct 2021 08:23:22 +0000 (10:23 +0200)
Thanks Otto!

pdns/dnsdist-lua.cc
pdns/dnsdist.cc
pdns/dnsdistdist/dnsdist-dynblocks.cc
pdns/dnsdistdist/test-dnsdistdynblocks_hh.cc

index 3ce9c065e9402959551607f0b8e0310c4ddbceb9..07ee8f97c1100254f4b29528584fb307fdceff1a 100644 (file)
@@ -1392,6 +1392,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
                          until.tv_sec += actualSeconds;
                          for (const auto& capair : m) {
                            unsigned int count = 0;
+                           /* this legacy interface does not support ranges or ports, use DynBlockRulesGroup instead */
                            AddressAndPortRange requestor(capair.first, capair.first.isIPv4() ? 32 : 128, 0);
                            auto got = slow.lookup(requestor);
                            bool expired = false;
index 10064e9bc6097ee18d1e130021cba11bdca09538..41e8bd37456d7d07e8314dea3c3a2e2c7dced3d1 100644 (file)
@@ -861,6 +861,7 @@ static bool applyRulesToQuery(LocalHolders& holders, DNSQuestion& dq, const stru
     }
   }
 
+  /* the Dynamic Block mechanism supports address and port ranges, so we need to pass the full address and port */
   if (auto got = holders.dynNMGBlock->lookup(AddressAndPortRange(*dq.remote, dq.remote->isIPv4() ? 32 : 128, 16))) {
     auto updateBlockStats = [&got]() {
       ++g_stats.dynBlocked;
index a1230f1017b2b3dfcc2ef81199597febb6b4a649..dccf2aa8c12ef41353c1a8179f512fa6bfcf0dd2 100644 (file)
@@ -176,6 +176,7 @@ bool DynBlockRulesGroup::checkIfResponseCodeMatches(const Rings::Response& respo
 
 void DynBlockRulesGroup::addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock, AddressAndPortRange> >& blocks, const struct timespec& now, const AddressAndPortRange& requestor, const DynBlockRule& rule, bool& updated, bool warning)
 {
+  /* network exclusions are address-based only (no port) */
   if (d_excludedSubnets.match(requestor.getNetwork())) {
     /* do not add a block for excluded subnets */
     return;
@@ -187,7 +188,7 @@ void DynBlockRulesGroup::addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock,
   struct timespec until = now;
   until.tv_sec += rule.d_blockDuration;
   unsigned int count = 0;
-  const auto& got = blocks->lookup(requestor.getNetwork());
+  const auto& got = blocks->lookup(requestor);
   bool expired = false;
   bool wasWarning = false;
   bool bpf = false;
@@ -226,6 +227,7 @@ void DynBlockRulesGroup::addOrRefreshBlock(boost::optional<NetmaskTree<DynBlock,
     if (db.action == DNSAction::Action::Drop && g_defaultBPFFilter &&
         ((requestor.isIPv4() && requestor.getBits() == 32) || (requestor.isIPv6() && requestor.getBits() == 128))) {
       try {
+        /* the current BPF filter implementation only supports full addresses (/32 or /128) and no port */
         g_defaultBPFFilter->block(requestor.getNetwork());
         bpf = true;
       }
index 3193e6692b8558023058b8b995aeb10e34e5e95d..f556a6048c669b4b4ca8c4d16021ac0acef0c7b2 100644 (file)
@@ -346,6 +346,39 @@ BOOST_AUTO_TEST_CASE(test_DynBlockRulesGroup_QueryRate_V4Ports) {
       /* outside of the range should not */
       BOOST_CHECK(g_dynblockNMG.getLocal()->lookup(AddressAndPortRange(ComboAddress("192.0.2.1:16384"), 32, 16)) == nullptr);
     }
+
+    /* we (again) insert just above 50 qps from several clients the same IPv4 port range, this should update the block which will
+       check by looking at the blocked counter */
+    {
+      auto block = g_dynblockNMG.getLocal()->lookup(AddressAndPortRange(ComboAddress("192.0.2.1:0"), 32, 16));
+      BOOST_REQUIRE(block != nullptr);
+      BOOST_CHECK_EQUAL(block->second.blocks, 0U);
+      block->second.blocks = 42U;
+    }
+
+    g_rings.clear();
+    BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), 0U);
+
+    for (size_t idx = 0; idx < numberOfQueries; idx++) {
+      ComboAddress requestor("192.0.2.1:" + std::to_string(idx));
+      g_rings.insertQuery(now, requestor, qname, qtype, size, dh, protocol);
+      g_rings.insertResponse(now, requestor, qname, qtype, responseTime, size, dh, backend, outgoingProtocol);
+    }
+    BOOST_CHECK_EQUAL(g_rings.getNumberOfQueryEntries(), numberOfQueries);
+
+    dbrg.apply(now);
+
+    BOOST_CHECK_EQUAL(g_dynblockNMG.getLocal()->size(), 1U);
+    {
+      /* previous address/port should still be blocked */
+      auto block = g_dynblockNMG.getLocal()->lookup(AddressAndPortRange(ComboAddress("192.0.2.1:0"), 32, 16));
+      BOOST_REQUIRE(block != nullptr);
+      BOOST_CHECK_EQUAL(block->second.blocks, 42U);
+    }
+
+    /* but not a different one */
+    BOOST_CHECK(g_dynblockNMG.getLocal()->lookup(AddressAndPortRange(ComboAddress("192.0.2.1:16384"), 32, 16)) == nullptr);
+
   }
 }