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