Mercurial > 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 |
| parents | 97454c088b6c |
| children | 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 |
