commit 2228965: [Feature] Ratelimit: Consider number of SMTP recipients

Vsevolod Stakhov vsevolod at highsecure.ru
Mon Sep 9 10:35:05 UTC 2019


Author: Vsevolod Stakhov
Date: 2019-09-09 11:09:19 +0100
URL: https://github.com/rspamd/rspamd/commit/2228965ad89c6da4aecde6cc0d0904109c763a1f

[Feature] Ratelimit: Consider number of SMTP recipients

---
 src/plugins/lua/ratelimit.lua | 44 ++++++++++++++++++++++++++++++-------------
 1 file changed, 31 insertions(+), 13 deletions(-)

diff --git a/src/plugins/lua/ratelimit.lua b/src/plugins/lua/ratelimit.lua
index ca4de6216..bd77b1ad6 100644
--- a/src/plugins/lua/ratelimit.lua
+++ b/src/plugins/lua/ratelimit.lua
@@ -58,6 +58,7 @@ local settings = {
 -- KEYS[3] - bucket leak rate (messages per millisecond)
 -- KEYS[4] - bucket burst
 -- KEYS[5] - expire for a bucket
+-- KEYS[6] - number of recipients
 -- return 1 if message should be ratelimited and 0 if not
 -- Redis keys used:
 --   l - last hit
@@ -97,7 +98,7 @@ local bucket_check_script = [[
    dynb = tonumber(redis.call('HGET', KEYS[1], 'db')) / 10000.0
    if dynb == 0 then dynb = 0.0001 end
 
-   if burst > 0 and (burst + 1) > tonumber(KEYS[4]) * dynb then
+   if burst > 0 and (burst + tonumber(KEYS[6])) > tonumber(KEYS[4]) * dynb then
      return {1, tostring(burst), tostring(dynr), tostring(dynb), tostring(leaked)}
    end
   else
@@ -118,6 +119,7 @@ local bucket_check_id
 -- KEYS[5] - max dyn rate (min: 1/x)
 -- KEYS[6] - max burst rate (min: 1/x)
 -- KEYS[7] - expire for a bucket
+-- KEYS[8] - number of recipients (or increase rate)
 -- Redis keys used:
 --   l - last hit
 --   b - current burst
@@ -185,7 +187,7 @@ local bucket_update_script = [[
   local burst = tonumber(redis.call('HGET', KEYS[1], 'b'))
   if burst < 0 then burst = 0 end
 
-  redis.call('HINCRBYFLOAT', KEYS[1], 'b', 1)
+  redis.call('HINCRBYFLOAT', KEYS[1], 'b', tonumber(KEYS[8]))
   redis.call('HSET', KEYS[1], 'l', KEYS[2])
   redis.call('EXPIRE', KEYS[1], KEYS[7])
 
@@ -282,7 +284,8 @@ end
 
 local bucket_schema = ts.shape{
   burst = ts.number + ts.string / lua_util.dehumanize_number,
-  rate = ts.number + ts.string / str_to_rate
+  rate = ts.number + ts.string / str_to_rate,
+  skip_recipients = ts.boolean:is_optional(),
 }
 
 local function parse_limit(name, data)
@@ -622,6 +625,11 @@ local function ratelimit_cb(task)
   -- Don't do anything if pre-result has been already set
   if task:has_pre_result() then return end
 
+  local _,nrcpt = task:has_recipients('smtp')
+  if not nrcpt or nrcpt <= 0 then
+    nrcpt = 1
+  end
+
   if nprefixes > 0 then
     -- Save prefixes to the cache to allow update
     task:cache_set('ratelimit_prefixes', prefixes)
@@ -632,13 +640,16 @@ local function ratelimit_cb(task)
     for pr,value in pairs(prefixes) do
       local bucket = value.bucket
       local rate = (bucket.rate) / 1000.0 -- Leak rate in messages/ms
+      local bincr = nrcpt
+      if bucket.skip_recipients then bincr = 1 end
+
       lua_util.debugm(N, task, "check limit %s:%s -> %s (%s/%s)",
           value.name, pr, value.hash, bucket.burst, bucket.rate)
       lua_redis.exec_redis_script(bucket_check_id,
-              {key = value.hash, task = task, is_write = true},
-              gen_check_cb(pr, bucket, value.name, value.hash),
-              {value.hash, tostring(now), tostring(rate), tostring(bucket.burst),
-                  tostring(settings.expire)})
+          {key = value.hash, task = task, is_write = true},
+          gen_check_cb(pr, bucket, value.name, value.hash),
+          {value.hash, tostring(now), tostring(rate), tostring(bucket.burst),
+           tostring(settings.expire), tostring(bincr)})
     end
   end
 end
@@ -656,6 +667,10 @@ local function ratelimit_update_cb(task)
     end
 
     local verdict = lua_util.get_task_verdict(task)
+    local _,nrcpt = task:has_recipients('smtp')
+    if not nrcpt or nrcpt <= 0 then
+      nrcpt = 1
+    end
 
     -- Update each bucket
     for k, v in pairs(prefixes) do
@@ -663,7 +678,7 @@ local function ratelimit_update_cb(task)
       local function update_bucket_cb(err, data)
         if err then
           rspamd_logger.errx(task, 'cannot update rate bucket %s: %s',
-                  k, err)
+              k, err)
         else
           lua_util.debugm(N, task,
               "updated limit %s:%s -> %s (%s/%s), burst: %s, dyn_rate: %s, dyn_burst: %s",
@@ -685,12 +700,15 @@ local function ratelimit_update_cb(task)
         mult_rate = bucket.ham_factor_rate or 1.0
       end
 
+      local bincr = nrcpt
+      if bucket.skip_recipients then bincr = 1 end
+
       lua_redis.exec_redis_script(bucket_update_id,
-              {key = v.hash, task = task, is_write = true},
-              update_bucket_cb,
-              {v.hash, tostring(now), tostring(mult_rate), tostring(mult_burst),
-               tostring(settings.max_rate_mult), tostring(settings.max_bucket_mult),
-               tostring(settings.expire)})
+          {key = v.hash, task = task, is_write = true},
+          update_bucket_cb,
+          {v.hash, tostring(now), tostring(mult_rate), tostring(mult_burst),
+           tostring(settings.max_rate_mult), tostring(settings.max_bucket_mult),
+           tostring(settings.expire), tostring(bincr)})
     end
   end
 end


More information about the Commits mailing list