diff mod_firewall/mod_firewall.lua @ 5920:254a21a104aa

mod_server_contact_info: Backport from prosody trunk
author Matthew Wild <mwild1@gmail.com>
date Fri, 07 Jun 2024 16:14:58 +0100
parents 1d1eadff331d
children
line wrap: on
line diff
--- a/mod_firewall/mod_firewall.lua	Fri Jun 07 15:53:30 2024 +0100
+++ b/mod_firewall/mod_firewall.lua	Fri Jun 07 16:14:58 2024 +0100
@@ -315,6 +315,10 @@
 	new_long_id = {
 		global_code = [[local new_long_id = require "util.id".long;]];
 	};
+
+	trace = {
+		global_code = [[local trace_init = module:require("trace").init;]];
+	};
 };
 
 local function include_dep(dependency, code)
@@ -371,9 +375,9 @@
 	module:require"marks";
 end
 
-local function new_rule(ruleset, chain)
+local function new_rule(ruleset, chain, line_no)
 	assert(chain, "no chain specified");
-	local rule = { conditions = {}, actions = {}, deps = {} };
+	local rule = { conditions = {}, actions = {}, deps = {}, line_no = line_no };
 	table.insert(ruleset[chain], rule);
 	return rule;
 end
@@ -385,6 +389,7 @@
 		return "Error compiling "..filename.." on line "..line_no..": "..err;
 	end
 
+	local metadata = { debug = {} };
 	local ruleset = {
 		deliver = {};
 	};
@@ -429,6 +434,12 @@
 				return nil, errmsg("Only event chains supported at the moment");
 			end
 			ruleset[chain] = ruleset[chain] or {};
+		elseif not(state) and line:sub(1, 2) == "@@" then
+			local k, v = line:match("^@@%s*([^%s=]+)%s*=%s*(.+)$");
+			if not k then
+				return nil, errmsg("Unable to parse metadata assignment (expected '@@ key = value')");
+			end
+			metadata[k] = v;
 		elseif not(state) and line:sub(1,1) == "%" then -- Definition (zone, limit, etc.)
 			local what, name = line:match("^%%%s*([%w_]+) +([^ :]+)");
 			if not definition_handlers[what] then
@@ -483,12 +494,13 @@
 			end
 		elseif state == "actions" then -- state is actions but action pattern did not match
 			state = nil; -- Awaiting next rule, etc.
-			table.insert(ruleset[chain], rule);
+			table.insert(ruleset[chain], rule); -- FIXME: Is this a bug? Rule should have already been inserted by new_rule()?
 			rule = nil;
 		else
-			if not state then
+			-- Condition
+			if not state then -- Starting a new rule block?
 				state = "rules";
-				rule = new_rule(ruleset, chain);
+				rule = new_rule(ruleset, chain, line_no);
 			end
 			-- Check standard modifiers for the condition (e.g. NOT)
 			local negated;
@@ -514,10 +526,10 @@
 			end
 		end
 	end
-	return ruleset;
+	return ruleset, metadata;
 end
 
-local function process_firewall_rules(ruleset)
+local function process_firewall_rules(ruleset, metadata)
 	-- Compile ruleset and return complete code
 
 	local chain_handlers = {};
@@ -537,8 +549,13 @@
 			end
 		end
 
+		if metadata.trace then
+			include_dep("trace", code);
+			table.insert(code, ("local trace = trace_init(%q, %q);"):format(metadata.filename, chain_name))
+		end
+
 		local condition_cache, n_conditions = {}, 0;
-		for _, rule in ipairs(rules) do
+		for rule_n, rule in ipairs(rules) do
 			for _, dep in ipairs(rule.deps) do
 				include_dep(dep, code);
 			end
@@ -562,6 +579,17 @@
 					else
 						rule.conditions[i] = (negated and "not(" or "(")..condition..")";
 					end
+
+					if metadata.trace then
+						-- Wrap each condition in a tracer
+						rule.conditions[i] = ("trace(%d, %d, %s)"):format(rule_n, i, rule.conditions[i]);
+					end
+				end
+
+				if metadata.trace then
+					-- Trace overall action
+					table.insert(rule.actions, 1, ("trace(%d, nil, true)"):format(rule_n));
+					table.insert(rule.actions, ("else trace(%d, nil, false)"):format(rule_n));
 				end
 
 				rule_code = "if "..table.concat(rule.conditions, " and ").." then\n\t\t\t"
@@ -592,9 +620,9 @@
 end
 
 local function compile_firewall_rules(filename)
-	local ruleset, err = parse_firewall_rules(filename);
-	if not ruleset then return nil, err; end
-	local chain_handlers = process_firewall_rules(ruleset);
+	local ruleset, metadata = parse_firewall_rules(filename);
+	if not ruleset then return nil, metadata; end
+	local chain_handlers = process_firewall_rules(ruleset, metadata);
 	return chain_handlers;
 end