-- Rules to detect forwarding
+local fun = require "fun"
+
rspamd_config.FWD_GOOGLE = {
- callback = function (task)
- if not (task:has_from(1) and task:has_recipients(1)) then
- return false
- end
- local envfrom = task:get_from(1)
- local envrcpts = task:get_recipients(1)
- -- Forwarding will only be to a single recipient
- if #envrcpts > 1 then return false end
- -- Get recipient and compute VERP address
- local rcpt = envrcpts[1].addr:lower()
- local verp = rcpt:gsub('@','=')
- -- Get the user portion of the envfrom
- local ef_user = envfrom[1].user:lower()
- -- Check for a match
- if ef_user:find('+caf_=' .. verp, 1, true) then
- local _,_,user = ef_user:find('^(.+)+caf_=')
- if user then
- user = user .. '@' .. envfrom[1].domain
- return true, user
- end
- end
- return false
- end,
- score = 0.0,
- description = "Message was forwarded by Google",
- group = "forwarding"
+ callback = function (task)
+ if not (task:has_from(1) and task:has_recipients(1)) then
+ return false
+ end
+ local envfrom = task:get_from(1)
+ local envrcpts = task:get_recipients(1)
+ -- Forwarding will only be to a single recipient
+ if #envrcpts > 1 then return false end
+ -- Get recipient and compute VERP address
+ local rcpt = envrcpts[1].addr:lower()
+ local verp = rcpt:gsub('@','=')
+ -- Get the user portion of the envfrom
+ local ef_user = envfrom[1].user:lower()
+ -- Check for a match
+ if ef_user:find('+caf_=' .. verp, 1, true) then
+ local _,_,user = ef_user:find('^(.+)+caf_=')
+ if user then
+ user = user .. '@' .. envfrom[1].domain
+ return true, user
+ end
+ end
+ return false
+ end,
+ score = 0.0,
+ description = "Message was forwarded by Google",
+ group = "forwarding"
}
rspamd_config.FWD_YANDEX = {
- callback = function (task)
- if not (task:has_from(1) and task:has_recipients(1)) then
- return false
- end
- local hostname = task:get_hostname()
- if hostname and hostname:lower():find('%.yandex%.[a-z]+$') then
- if task:get_header_raw('X-Yandex-Forward') then
- return true
- end
- end
- return false
- end,
- score = 0.0,
- description = "Message was forwarded by Yandex",
- group = "forwarding"
+ callback = function (task)
+ if not (task:has_from(1) and task:has_recipients(1)) then
+ return false
+ end
+ local hostname = task:get_hostname()
+ if hostname and hostname:lower():find('%.yandex%.[a-z]+$') then
+ if task:get_header_raw('X-Yandex-Forward') then
+ return true
+ end
+ end
+ return false
+ end,
+ score = 0.0,
+ description = "Message was forwarded by Yandex",
+ group = "forwarding"
}
rspamd_config.FWD_MAILRU = {
- callback = function (task)
- if not (task:has_from(1) and task:has_recipients(1)) then
- return false
- end
- local hostname = task:get_hostname()
- if hostname and hostname:lower():find('%.mail%.ru$') then
- if task:get_header_raw('X-MailRu-Forward') then
- return true
- end
- end
- return false
- end,
- score = 0.0,
- description = "Message was forwarded by Mail.ru",
- group = "forwarding"
+ callback = function (task)
+ if not (task:has_from(1) and task:has_recipients(1)) then
+ return false
+ end
+ local hostname = task:get_hostname()
+ if hostname and hostname:lower():find('%.mail%.ru$') then
+ if task:get_header_raw('X-MailRu-Forward') then
+ return true
+ end
+ end
+ return false
+ end,
+ score = 0.0,
+ description = "Message was forwarded by Mail.ru",
+ group = "forwarding"
}
rspamd_config.FWD_SRS = {
- callback = function (task)
- if not (task:has_from(1) and task:has_recipients(1)) then
- return false
- end
- local envfrom = task:get_from(1)
- local envrcpts = task:get_recipients(1)
- -- Forwarding is only to a single recipient
- if #envrcpts > 1 then return false end
- -- Get recipient and compute rewritten SRS address
- local srs = '=' .. envrcpts[1].domain:lower() ..
- '=' .. envrcpts[1].user:lower()
- if envfrom[1].user:lower():find('^srs[01]=') and
- envfrom[1].user:lower():find(srs, 1, false)
- then
- return true
- end
- return false
- end,
- score = 0.0,
- description = "Message was forwarded using SRS",
- group = "forwarding"
+ callback = function (task)
+ if not (task:has_from(1) and task:has_recipients(1)) then
+ return false
+ end
+ local envfrom = task:get_from(1)
+ local envrcpts = task:get_recipients(1)
+ -- Forwarding is only to a single recipient
+ if #envrcpts > 1 then return false end
+ -- Get recipient and compute rewritten SRS address
+ local srs = '=' .. envrcpts[1].domain:lower() ..
+ '=' .. envrcpts[1].user:lower()
+ if envfrom[1].user:lower():find('^srs[01]=') and
+ envfrom[1].user:lower():find(srs, 1, false)
+ then
+ return true
+ end
+ return false
+ end,
+ score = 0.0,
+ description = "Message was forwarded using SRS",
+ group = "forwarding"
}
rspamd_config.FORWARDED = {
- callback = function (task)
- if not task:has_recipients(1) then return false end
- local envrcpts = task:get_recipients(1)
- -- Forwarding will only be for single recipient messages
- if #envrcpts > 1 then return false end
- -- Get any other headers we might need
- local lu = task:get_header('List-Unsubscribe')
- local to = task:get_recipients(2)
- local matches = 0
- -- Retrieve and loop through all Received headers
- local rcvds = task:get_received_headers()
+ callback = function (task)
+ local function normalize_addr(addr)
+ addr = string.match(addr, '^<?([^<]*)>?$') or addr
+ local cap, _,domain = string.match(addr, '^([^%+][^%+]*)(%+[^@]*)@(.*)$')
+ if cap then
+ addr = string.format('%s@%s', cap, domain)
+ end
+
+ return addr
+ end
+
+ if not task:has_recipients(1) then return false end
+ local envrcpts = task:get_recipients(1)
+ -- Forwarding will only be for single recipient messages
+ if #envrcpts > 1 then return false end
+ -- Get any other headers we might need
+ local lu = task:get_header('List-Unsubscribe')
+ local to = task:get_recipients(2)
+ local matches = 0
+ -- Retrieve and loop through all Received headers
+ local rcvds = task:get_received_headers()
- if rcvds then
- for _, rcvd in ipairs(rcvds) do
- local addr = rcvd['for']
- if addr then
- matches = matches + 1
- -- Check that it doesn't match the envrcpt
- -- TODO: remove any plus addressing?
- if addr ~= envrcpts[1].addr:lower() then
- -- Check for mailing-lists as they will have the same signature
- if matches < 2 and lu and to and to[1].addr:lower() == addr then
- return false
- else
- return true, addr
- end
- end
- -- Prevent any other iterations as we only want
- -- process the first matching Received header
+ if rcvds then
+ for _, rcvd in ipairs(rcvds) do
+ local addr = rcvd['for']
+ if addr then
+ addr = normalize_addr(addr)
+ matches = matches + 1
+ -- Check that it doesn't match the envrcpt
+ if addr ~= envrcpts[1].addr:lower() then
+ -- Check for mailing-lists as they will have the same signature
+ if matches < 2 and lu and to and to[1].addr:lower() == addr then
return false
+ else
+ return true, 1.0, addr
end
end
+ -- Prevent any other iterations as we only want
+ -- process the first matching Received header
+ return false
end
- return false
- end,
- score = 0.0,
- description = "Message was forwarded",
- group = "forwarding"
+ end
+ end
+ return false
+ end,
+ score = 0.0,
+ description = "Message was forwarded",
+ group = "forwarding"
}