summaryrefslogtreecommitdiffstats
path: root/src/plugins/lua/multimap.lua
blob: 18d5c503e13a5fee99b55a27d97e7c283f7f2f87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
-- Multimap is rspamd module designed to define and operate with different maps

local rules = {}

function split(str, delim, maxNb)
	-- Eliminate bad cases...
	if string.find(str, delim) == nil then
		return { str }
	end
	if maxNb == nil or maxNb < 1 then
		maxNb = 0    -- No limit
	end
	local result = {}
	local pat = "(.-)" .. delim .. "()"
	local nb = 0
	local lastPos
	for part, pos in string.gfind(str, pat) do
		nb = nb + 1
		result[nb] = part
		lastPos = pos
		if nb == maxNb then break end
	end
	-- Handle the last field
	if nb ~= maxNb then
		result[nb + 1] = string.sub(str, lastPos)
	end
	return result
end

function check_multimap(task)
	for _,rule in ipairs(rules) do
		if rule['type'] == 'ip' then
			local ip = task:get_from_ip_num()
			if rule['ips']:get_key(ip) then
				task:insert_result(rule['symbol'], 1)
			end
		elseif rule['type'] == 'header' then
			local headers = task:get_message():get_header(rule['header'])
			if headers then
				for _,hv in ipairs(headers) do
					if rule['pattern'] then
						-- extract a part from header
						local _,_,ext = string.find(hv, rule['pattern'])
						if ext then
							if rule['hash']:get_key(ext) then
								task:insert_result(rule['symbol'], 1)
							end
						end
					else
						if rule['hash']:get_key(hv) then
							task:insert_result(rule['symbol'], 1)
						end
					end
				end
			end
		end
	end
end

function add_rule(params)
	local newrule = {
		type = 'ip',
		header = nil,
		pattern = nil,
		map = nil,
		symbol = nil
	}
	for _,param in ipairs(params) do
		local _,_,name,value = string.find(param, '(%w+)%s*=%s*(.+)')
		if not name or not value then
			rspamd_logger:err('invalid rule: '..param)
			return 0
		end
		if name == 'type' then
			if value == 'ip' then
				newrule['type'] = 'ip'
			elseif value == 'header' then
				newrule['type'] = 'header'
			else
				rspamd_logger:err('invalid rule type: '.. value)
				return 0
			end
		elseif name == 'header' then
			newrule['header'] = value
		elseif name == 'pattern' then
			newrule['pattern'] = value
		elseif name == 'map' then
			newrule['map'] = value
		elseif name == 'symbol' then
			newrule['symbol'] = value
		else	
			rspamd_logger:err('invalid rule option: '.. name)
			return 0
		end

	end
	if not newrule['symbol'] or not newrule['map'] or not newrule['symbol'] then
		rspamd_logger:err('incomplete rule')
		return 0
	end
	if newrule['type'] == 'ip' then
		newrule['ips'] = rspamd_config:add_radix_map (newrule['map'])
	else
		newrule['hash'] = rspamd_config:add_hash_map (newrule['map'])
	end
	table.insert(rules, newrule)
	return 1
end

local opts =  rspamd_config:get_all_opt('multimap')
if opts then
	local strrules = opts['rule']
	if strrules then
		for _,value in ipairs(strrules) do
			local params = split(value, ',')
			if not add_rule (params) then
				rspamd_logger:err('cannot add rule: "'..value..'"')
			end
		end
	end
end

if table.maxn(rules) > 0 then
	-- add fake symbol to check all maps inside a single callback
	rspamd_config:register_symbol('MULTIMAP', 1.0, 'check_multimap')
end