-- https://rspamd.com/doc/configuration/settings.html
local rspamd_logger = require "rspamd_logger"
-local rspamd_maps = require "lua_maps"
+local lua_maps = require "lua_maps"
local lua_util = require "lua_util"
local rspamd_ip = require "rspamd_ip"
local rspamd_regexp = require "rspamd_regexp"
local function check_addr_setting(expected, addr)
local function check_specific_addr(elt)
if expected.name then
- if rspamd_maps.rspamd_maybe_check_map(expected.name, elt.addr) then
+ if lua_maps.rspamd_maybe_check_map(expected.name, elt.addr) then
return true
end
end
if expected.user then
- if rspamd_maps.rspamd_maybe_check_map(expected.user, elt.user) then
+ if lua_maps.rspamd_maybe_check_map(expected.user, elt.user) then
return true
end
end
if expected.domain and elt.domain then
- if rspamd_maps.rspamd_maybe_check_map(expected.domain, elt.domain) then
+ if lua_maps.rspamd_maybe_check_map(expected.domain, elt.domain) then
return true
end
end
return true
end
elseif expected.check then
- if rspamd_maps.rspamd_maybe_check_map(expected.check, str) then
+ if lua_maps.rspamd_maybe_check_map(expected.check, str) then
return true
end
end
local function check_ip_setting(expected, ip)
if not expected[2] then
- if rspamd_maps.rspamd_maybe_check_map(expected[1], ip:to_string()) then
+ if lua_maps.rspamd_maybe_check_map(expected[1], ip:to_string()) then
return true
end
else
return false
end
+local function check_map_setting(map, input)
+ return map:get_key(input)
+end
+
local function priority_to_string(pri)
if pri then
if pri >= 3 then
end
--- Process settings based on their priority
-local function process_settings_table(tbl, allow_ids, is_static)
- local get_priority = function(elt)
- local pri_tonum = function(p)
- if p then
- if type(p) == "number" then
- return tonumber(p)
- elseif type(p) == "string" then
- if p == "high" then
- return 3
- elseif p == "medium" then
- return 2
- end
-
- end
-
- end
-
- return 1
- end
-
- return pri_tonum(elt['priority'])
+local function convert_to_table(chk_elt, out)
+ if type(chk_elt) == 'string' then
+ return {out}
end
- -- Check the setting element internal data
- local process_setting_elt = function(name, elt)
+ return out
+end
- lua_util.debugm(N, rspamd_config, 'process settings "%s"', name)
- -- Process IP address: converted to a table {ip, mask}
- local function process_ip_condition(ip)
- local out = {}
+-- Process IP address: converted to a table {ip, mask}
+local function process_ip_condition(ip)
+ local out = {}
- if type(ip) == "table" then
- for _,v in ipairs(ip) do
- table.insert(out, process_ip_condition(v))
- end
- elseif type(ip) == "string" then
- local slash = string.find(ip, '/')
+ if type(ip) == "table" then
+ for _,v in ipairs(ip) do
+ table.insert(out, process_ip_condition(v))
+ end
+ elseif type(ip) == "string" then
+ local slash = string.find(ip, '/')
- if not slash then
- -- Just a plain IP address
- local res = rspamd_ip.from_string(ip)
+ if not slash then
+ -- Just a plain IP address
+ local res = rspamd_ip.from_string(ip)
- if res:is_valid() then
- out[1] = res
- out[2] = 0
- else
- -- It can still be a map
- out[1] = res
- end
- else
- local res = rspamd_ip.from_string(string.sub(ip, 1, slash - 1))
- local mask = tonumber(string.sub(ip, slash + 1))
+ if res:is_valid() then
+ out[1] = res
+ out[2] = 0
+ else
+ -- It can still be a map
+ out[1] = res
+ end
+ else
+ local res = rspamd_ip.from_string(string.sub(ip, 1, slash - 1))
+ local mask = tonumber(string.sub(ip, slash + 1))
- if res:is_valid() then
- out[1] = res
- out[2] = mask
- else
- rspamd_logger.errx(rspamd_config, "bad IP address: " .. ip)
- return nil
- end
- end
+ if res:is_valid() then
+ out[1] = res
+ out[2] = mask
else
+ rspamd_logger.errx(rspamd_config, "bad IP address: " .. ip)
return nil
end
-
- return out
end
+ else
+ return nil
+ end
- -- Process email like condition, converted to a table with fields:
- -- name - full email (surprise!)
- -- user - user part
- -- domain - domain part
- -- regexp - full email regexp (yes, it sucks)
- local function process_email_condition(addr)
- local out = {}
- if type(addr) == "table" then
- for _,v in ipairs(addr) do
- table.insert(out, process_email_condition(v))
+ return out
+end
+
+-- Process email like condition, converted to a table with fields:
+-- name - full email (surprise!)
+-- user - user part
+-- domain - domain part
+-- regexp - full email regexp (yes, it sucks)
+local function process_email_condition(addr)
+ local out = {}
+ if type(addr) == "table" then
+ for _,v in ipairs(addr) do
+ table.insert(out, process_email_condition(v))
+ end
+ elseif type(addr) == "string" then
+ if string.sub(addr, 1, 4) == "map:" then
+ -- It is map, don't apply any extra logic
+ out['name'] = addr
+ else
+ local start = string.sub(addr, 1, 1)
+ if start == '/' then
+ -- It is a regexp
+ local re = rspamd_regexp.create(addr)
+ if re then
+ out['regexp'] = re
+ else
+ rspamd_logger.errx(rspamd_config, "bad regexp: " .. addr)
+ return nil
end
- elseif type(addr) == "string" then
- if string.sub(addr, 1, 4) == "map:" then
- -- It is map, don't apply any extra logic
+
+ elseif start == '@' then
+ -- It is a domain if form @domain
+ out['domain'] = string.sub(addr, 2)
+ else
+ -- Check user@domain parts
+ local at = string.find(addr, '@')
+ if at then
+ -- It is full address
out['name'] = addr
else
- local start = string.sub(addr, 1, 1)
- if start == '/' then
- -- It is a regexp
- local re = rspamd_regexp.create(addr)
- if re then
- out['regexp'] = re
- else
- rspamd_logger.errx(rspamd_config, "bad regexp: " .. addr)
- return nil
- end
-
- elseif start == '@' then
- -- It is a domain if form @domain
- out['domain'] = string.sub(addr, 2)
- else
- -- Check user@domain parts
- local at = string.find(addr, '@')
- if at then
- -- It is full address
- out['name'] = addr
- else
- -- It is a user
- out['user'] = addr
- end
- end
+ -- It is a user
+ out['user'] = addr
end
- else
- return nil
end
-
- return out
end
+ else
+ return nil
+ end
- -- Convert a plain string condition to a table:
- -- check - string to match
- -- regexp - regexp to match
- local function process_string_condition(addr)
- local out = {}
- if type(addr) == "table" then
- for _,v in ipairs(addr) do
- table.insert(out, process_string_condition(v))
- end
- elseif type(addr) == "string" then
- if string.sub(addr, 1, 4) == "map:" then
- -- It is map, don't apply any extra logic
- out['check'] = addr
- else
- local start = string.sub(addr, 1, 1)
- if start == '/' then
- -- It is a regexp
- local re = rspamd_regexp.create(addr)
- if re then
- out['regexp'] = re
- else
- rspamd_logger.errx(rspamd_config, "bad regexp: " .. addr)
- return nil
- end
+ return out
+end
- else
- out['check'] = addr
- end
+-- Convert a plain string condition to a table:
+-- check - string to match
+-- regexp - regexp to match
+local function process_string_condition(addr)
+ local out = {}
+ if type(addr) == "table" then
+ for _,v in ipairs(addr) do
+ table.insert(out, process_string_condition(v))
+ end
+ elseif type(addr) == "string" then
+ if string.sub(addr, 1, 4) == "map:" then
+ -- It is map, don't apply any extra logic
+ out['check'] = addr
+ else
+ local start = string.sub(addr, 1, 1)
+ if start == '/' then
+ -- It is a regexp
+ local re = rspamd_regexp.create(addr)
+ if re then
+ out['regexp'] = re
+ else
+ rspamd_logger.errx(rspamd_config, "bad regexp: " .. addr)
+ return nil
end
+
else
- return nil
+ out['check'] = addr
end
-
- return out
end
+ else
+ return nil
+ end
+
+ return out
+end
+
+local function get_priority (elt)
+ local pri_tonum = function(p)
+ if p then
+ if type(p) == "number" then
+ return tonumber(p)
+ elseif type(p) == "string" then
+ if p == "high" then
+ return 3
+ elseif p == "medium" then
+ return 2
+ end
- local convert_to_table = function(chk_elt, out)
- if type(chk_elt) == 'string' then
- return {out}
end
- return out
end
- -- Used to create a checking closure: if value matches expected somehow, return true
- local function gen_check_closure(expected, check_func)
- return function(value)
- if not value then return false end
+ return 1
+ end
- if type(value) == 'function' then
- value = value()
- end
+ return pri_tonum(elt['priority'])
+end
- if value then
+-- Used to create a checking closure: if value matches expected somehow, return true
+local function gen_check_closure(expected, check_func)
+ return function(value)
+ if not value then return false end
- if not check_func then
- check_func = function(a, b) return a == b end
- end
+ if type(value) == 'function' then
+ value = value()
+ end
- local ret = fun.any(function(d)
- return check_func(d, value)
- end, expected)
- if ret then
- return true
- end
- end
+ if value then
+
+ if not check_func then
+ check_func = function(a, b) return a == b end
+ end
- return false
+ local ret
+ if type(expected) == 'table' then
+ ret = fun.any(function(d)
+ return check_func(d, value)
+ end, expected)
+ else
+ ret = check_func(expected, value)
+ end
+ if ret then
+ return true
end
end
+ return false
+ end
+end
+
+-- Process settings based on their priority
+local function process_settings_table(tbl, allow_ids, is_static)
+
+ -- Check the setting element internal data
+ local process_setting_elt = function(name, elt)
+
+ lua_util.debugm(N, rspamd_config, 'process settings "%s"', name)
+
local out = {}
local checks = {}
- if elt['ip'] then
+ if elt.ip then
local ips_table = process_ip_condition(elt['ip'])
if ips_table then
}
end
end
- if elt['client_ip'] then
- local client_ips_table = process_ip_condition(elt['client_ip'])
+ if elt.ip_map then
+ local ips_map = lua_maps.map_add_from_ucl(elt.ip_map, 'radix',
+ 'settings ip map for ' .. name)
+
+ if ips_map then
+ lua_util.debugm(N, rspamd_config, 'added ip_map condition to "%s"',
+ name)
+ checks.ip_map = {
+ check = gen_check_closure(ips_map, check_map_setting),
+ extract = function(task)
+ local ip = task:get_from_ip()
+ if ip and ip:is_valid() then return ip end
+ return nil
+ end,
+ }
+ end
+ end
+
+ if elt.client_ip then
+ local client_ips_table = process_ip_condition(elt.client_ip)
if client_ips_table then
lua_util.debugm(N, rspamd_config, 'added client_ip condition to "%s": %s',
}
end
end
- if elt['from'] then
- local from_condition = process_email_condition(elt['from'])
+ if elt.client_ip_map then
+ local ips_map = lua_maps.map_add_from_ucl(elt.ip_map, 'radix',
+ 'settings client ip map for ' .. name)
+
+ if ips_map then
+ lua_util.debugm(N, rspamd_config, 'added client ip_map condition to "%s"',
+ name)
+ checks.client_ip_map = {
+ check = gen_check_closure(ips_map, check_map_setting),
+ extract = function(task)
+ local ip = task:get_client_ip()
+ if ip and ip:is_valid() then return ip end
+ return nil
+ end,
+ }
+ end
+ end
+
+ if elt.from then
+ local from_condition = process_email_condition(elt.from)
if from_condition then
lua_util.debugm(N, rspamd_config, 'added from condition to "%s": %s',
}
end
end
- if elt['rcpt'] then
- local rcpt_condition = process_email_condition(elt['rcpt'])
+
+ if elt.rcpt then
+ local rcpt_condition = process_email_condition(elt.rcpt)
if rcpt_condition then
lua_util.debugm(N, rspamd_config, 'added rcpt condition to "%s": %s',
name, rcpt_condition)
}
end
end
- if elt['from_mime'] then
- local from_mime_condition = process_email_condition(elt['from_mime'])
+
+ if elt.from_mime then
+ local from_mime_condition = process_email_condition(elt.from_mime)
if from_mime_condition then
lua_util.debugm(N, rspamd_config, 'added from_mime condition to "%s": %s',
}
end
end
- if elt['rcpt_mime'] then
- local rcpt_mime_condition = process_email_condition(elt['rcpt'])
+
+ if elt.rcpt_mime then
+ local rcpt_mime_condition = process_email_condition(elt.rcpt_mime)
if rcpt_mime_condition then
lua_util.debugm(N, rspamd_config, 'added rcpt mime condition to "%s": %s',
name, rcpt_mime_condition)
}
end
end
- if elt['user'] then
- local user_condition = process_email_condition(elt['user'])
+
+ if elt.user then
+ local user_condition = process_email_condition(elt.user)
if user_condition then
lua_util.debugm(N, rspamd_config, 'added user condition to "%s": %s',
name, user_condition)
}
end
end
- if elt['hostname'] then
- local hostname_condition = process_string_condition(elt['hostname'])
+
+ if elt.hostname then
+ local hostname_condition = process_string_condition(elt.hostname)
if hostname_condition then
lua_util.debugm(N, rspamd_config, 'added hostname condition to "%s": %s',
name, hostname_condition)
}
end
end
- if elt['authenticated'] then
+
+ if elt.authenticated then
lua_util.debugm(N, rspamd_config, 'added authenticated condition to "%s"',
name)
checks.authenticated = {
end
}
end
+
if elt['local'] then
lua_util.debugm(N, rspamd_config, 'added local condition to "%s"',
name)
end
return safe_key
- end
+ end
-- Headers are tricky:
-- We create an closure with extraction function depending on header name
-- We also inserts it into `checks` table as an atom in form header:<hname>
end
end)
- if elt['selector'] then
+ if elt.selector then
local sel = lua_selectors.create_selector_closure(rspamd_config, elt.selector,
elt.delimiter or "")
-- Special, special case!
local inverse = false
- if elt['inverse'] then
+ if elt.inverse then
lua_util.debugm(N, rspamd_config, 'added inverse condition to "%s"',
name)
inverse = true
end
- -- Killmeplease
+ -- Count checks and create Rspamd expression from a set of rules
local nchecks = 0
for _,_ in pairs(checks) do nchecks = nchecks + 1 end