Software /
code /
prosody-modules
Comparison
mod_firewall/mod_firewall.lua @ 956:33d6642f4db7
mod_firewall: Tighten up error handling, and split rules->Lua and Lua->bytecode compilation into separate functions
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Fri, 05 Apr 2013 18:05:21 +0100 |
parent | 955:97454c088b6c |
child | 960:d773a51af9b1 |
comparison
equal
deleted
inserted
replaced
955:97454c088b6c | 956:33d6642f4db7 |
---|---|
1 | 1 |
2 local resolve_relative_path = require "core.configmanager".resolve_relative_path; | 2 local resolve_relative_path = require "core.configmanager".resolve_relative_path; |
3 local logger = require "util.logger".init; | 3 local logger = require "util.logger".init; |
4 local set = require "util.set"; | 4 local set = require "util.set"; |
5 local add_filter = require "util.filters".add_filter; | 5 local add_filter = require "util.filters".add_filter; |
6 | |
7 | 6 |
8 zones = {}; | 7 zones = {}; |
9 local zones = zones; | 8 local zones = zones; |
10 setmetatable(zones, { | 9 setmetatable(zones, { |
11 __index = function (zones, zone) | 10 __index = function (zones, zone) |
111 end | 110 end |
112 | 111 |
113 local function compile_firewall_rules(filename) | 112 local function compile_firewall_rules(filename) |
114 local line_no = 0; | 113 local line_no = 0; |
115 | 114 |
115 local function errmsg(err) | |
116 return "Error compiling "..filename.." on line "..line_no..": "..err; | |
117 end | |
118 | |
116 local ruleset = { | 119 local ruleset = { |
117 deliver = {}; | 120 deliver = {}; |
118 }; | 121 }; |
119 | 122 |
120 local chain = "deliver"; -- Default chain | 123 local chain = "deliver"; -- Default chain |
144 :format(line_no); | 147 :format(line_no); |
145 end | 148 end |
146 state = nil; | 149 state = nil; |
147 elseif not(state) and line:match("^::") then | 150 elseif not(state) and line:match("^::") then |
148 chain = line:gsub("^::%s*", ""); | 151 chain = line:gsub("^::%s*", ""); |
152 local chain_info = chains[chain_name]; | |
153 if not chain_info then | |
154 return nil, errmsg("Unknown chain: "..chain_name); | |
155 elseif chain_info.type ~= "event" then | |
156 return nil, errmsg("Only event chains supported at the moment"); | |
157 end | |
149 ruleset[chain] = ruleset[chain] or {}; | 158 ruleset[chain] = ruleset[chain] or {}; |
150 elseif not(state) and line:match("^ZONE ") then | 159 elseif not(state) and line:match("^ZONE ") then |
151 local zone_name = line:match("^ZONE ([^:]+)"); | 160 local zone_name = line:match("^ZONE ([^:]+)"); |
161 if not zone_name:match("^%a[%w_]*$") then | |
162 return nil, errmsg("Invalid character(s) in zone name: "..zone_name); | |
163 end | |
152 local zone_members = line:match("^ZONE .-: ?(.*)"); | 164 local zone_members = line:match("^ZONE .-: ?(.*)"); |
153 local zone_member_list = {}; | 165 local zone_member_list = {}; |
154 for member in zone_members:gmatch("[^, ]+") do | 166 for member in zone_members:gmatch("[^, ]+") do |
155 zone_member_list[#zone_member_list+1] = member; | 167 zone_member_list[#zone_member_list+1] = member; |
156 end | 168 end |
166 local action = line:match("^%P+"); | 178 local action = line:match("^%P+"); |
167 if not action_handlers[action] then | 179 if not action_handlers[action] then |
168 return nil, ("Unknown action on line %d: %s"):format(line_no, action or "<unknown>"); | 180 return nil, ("Unknown action on line %d: %s"):format(line_no, action or "<unknown>"); |
169 end | 181 end |
170 table.insert(rule.actions, "-- "..line) | 182 table.insert(rule.actions, "-- "..line) |
171 local action_string, action_deps = action_handlers[action](line:match("=(.+)$")); | 183 local ok, action_string, action_deps = pcall(action_handlers[action], line:match("=(.+)$")); |
184 if not ok then | |
185 return nil, errmsg(action_string); | |
186 end | |
172 table.insert(rule.actions, action_string); | 187 table.insert(rule.actions, action_string); |
173 for _, dep in ipairs(action_deps or {}) do | 188 for _, dep in ipairs(action_deps or {}) do |
174 table.insert(rule.deps, dep); | 189 table.insert(rule.deps, dep); |
175 end | 190 end |
176 elseif state == "actions" then -- state is actions but action pattern did not match | 191 elseif state == "actions" then -- state is actions but action pattern did not match |
193 condition = condition:gsub(" ", ""); | 208 condition = condition:gsub(" ", ""); |
194 if not condition_handlers[condition] then | 209 if not condition_handlers[condition] then |
195 return nil, ("Unknown condition on line %d: %s"):format(line_no, condition); | 210 return nil, ("Unknown condition on line %d: %s"):format(line_no, condition); |
196 end | 211 end |
197 -- Get the code for this condition | 212 -- Get the code for this condition |
198 local condition_code, condition_deps = condition_handlers[condition](line:match(":%s?(.+)$")); | 213 local ok, condition_code, condition_deps = pcall(condition_handlers[condition], line:match(":%s?(.+)$")); |
214 if not ok then | |
215 return nil, errmsg(condition_code); | |
216 end | |
199 if negated then condition_code = "not("..condition_code..")"; end | 217 if negated then condition_code = "not("..condition_code..")"; end |
200 table.insert(rule.conditions, condition_code); | 218 table.insert(rule.conditions, condition_code); |
201 for _, dep in ipairs(condition_deps or {}) do | 219 for _, dep in ipairs(condition_deps or {}) do |
202 table.insert(rule.deps, dep); | 220 table.insert(rule.deps, dep); |
203 end | 221 end |
233 | 251 |
234 ]]..table.concat(code, " ")..[[ | 252 ]]..table.concat(code, " ")..[[ |
235 end; | 253 end; |
236 end]]; | 254 end]]; |
237 | 255 |
238 print(code_string) | 256 chain_handlers[chain_name] = code_string; |
239 | |
240 -- Prepare event handler function | |
241 local chunk, err = loadstring(code_string, "="..filename); | |
242 if not chunk then | |
243 return nil, "Error compiling (probably a compiler bug, please report): "..err; | |
244 end | |
245 chunk = chunk()(zones, logger(filename)); -- Returns event handler with 'zones' upvalue. | |
246 chain_handlers[chain_name] = chunk; | |
247 end | 257 end |
248 | 258 |
249 return chain_handlers; | 259 return chain_handlers; |
260 end | |
261 | |
262 local function compile_handler(code_string, filename) | |
263 print(code_string) | |
264 -- Prepare event handler function | |
265 local chunk, err = loadstring(code_string, "="..filename); | |
266 if not chunk then | |
267 return nil, "Error compiling (probably a compiler bug, please report): "..err; | |
268 end | |
269 local function fire_event(name, data) | |
270 return module:fire_event(name, data); | |
271 end | |
272 chunk = chunk()(zones, fire_event, logger(filename)); -- Returns event handler with 'zones' upvalue. | |
273 return chunk; | |
250 end | 274 end |
251 | 275 |
252 function module.load() | 276 function module.load() |
253 local firewall_scripts = module:get_option_set("firewall_scripts", {}); | 277 local firewall_scripts = module:get_option_set("firewall_scripts", {}); |
254 for script in firewall_scripts do | 278 for script in firewall_scripts do |
256 local chain_functions, err = compile_firewall_rules(script) | 280 local chain_functions, err = compile_firewall_rules(script) |
257 | 281 |
258 if not chain_functions then | 282 if not chain_functions then |
259 module:log("error", "Error compiling %s: %s", script, err or "unknown error"); | 283 module:log("error", "Error compiling %s: %s", script, err or "unknown error"); |
260 else | 284 else |
261 for chain, handler in pairs(chain_functions) do | 285 for chain, handler_code in pairs(chain_functions) do |
262 local chain_definition = chains[chain]; | 286 local handler, err = compile_handler(handler_code, "mod_firewall::"..chain); |
263 if chain_definition.type == "event" then | 287 if not handler then |
264 for _, event_name in ipairs(chain_definition) do | 288 module:log("error", "Compilation error for %s: %s", script, err); |
265 module:hook(event_name, handler, chain_definition.priority); | 289 else |
290 local chain_definition = chains[chain]; | |
291 if chain_definition and chain_definition.type == "event" then | |
292 for _, event_name in ipairs(chain_definition) do | |
293 module:hook(event_name, handler, chain_definition.priority); | |
294 end | |
295 elseif not chain_name:match("^user/") then | |
296 module:log("warn", "Unknown chain %q", chain); | |
266 end | 297 end |
298 module:hook("firewall/chains/"..chain, handler); | |
267 end | 299 end |
268 end | 300 end |
269 end | 301 end |
270 end | 302 end |
271 end | 303 end |