comparison mod_firewall/mod_firewall.lua @ 6536:24c34968dd45

mod_firewall: Preserve original source of actions/conditions for debug purposes
author Matthew Wild <mwild1@gmail.com>
date Mon, 11 May 2026 13:18:25 +0100
parents 82b6a03b9dfa
children 9bc83fba89ac
comparison
equal deleted inserted replaced
6535:82b6a03b9dfa 6536:24c34968dd45
386 module:require"marks"; 386 module:require"marks";
387 end 387 end
388 388
389 local function new_rule(ruleset, chain, line_no) 389 local function new_rule(ruleset, chain, line_no)
390 assert(chain, "no chain specified"); 390 assert(chain, "no chain specified");
391 local rule = { conditions = {}, actions = {}, deps = {}, line_no = line_no }; 391 local rule = { conditions = {}, actions = {}, deps = {}, line_no = line_no, source = { conditions = {}, actions = {} } };
392 table.insert(ruleset[chain], rule); 392 table.insert(ruleset[chain], rule);
393 return rule; 393 return rule;
394 end 394 end
395 395
396 local function parse_firewall_rules(filename) 396 local function parse_firewall_rules(filename)
498 local ok, action_string, action_deps = pcall(action_handlers[action], line:match("=(.+)$")); 498 local ok, action_string, action_deps = pcall(action_handlers[action], line:match("=(.+)$"));
499 if not ok then 499 if not ok then
500 return nil, errmsg(action_string); 500 return nil, errmsg(action_string);
501 end 501 end
502 table.insert(rule.actions, action_string); 502 table.insert(rule.actions, action_string);
503 table.insert(rule.source.actions, line);
503 for _, dep in ipairs(action_deps or {}) do 504 for _, dep in ipairs(action_deps or {}) do
504 table.insert(rule.deps, dep); 505 table.insert(rule.deps, dep);
505 end 506 end
506 elseif state == "actions" then -- state is actions but action pattern did not match 507 elseif state == "actions" then -- state is actions but action pattern did not match
507 state = nil; -- Awaiting next rule, etc. 508 state = nil; -- Awaiting next rule, etc.
513 state = "rules"; 514 state = "rules";
514 rule = new_rule(ruleset, chain, line_no); 515 rule = new_rule(ruleset, chain, line_no);
515 end 516 end
516 -- Check standard modifiers for the condition (e.g. NOT) 517 -- Check standard modifiers for the condition (e.g. NOT)
517 local negated; 518 local negated;
518 local condition = line:match("^[^:=%.?]*"); 519 local raw_condition = line:match("^[^:=%.?]*");
520 local condition = raw_condition;
521
519 if condition:find("%f[%w]NOT%f[^%w]") then 522 if condition:find("%f[%w]NOT%f[^%w]") then
520 local s, e = condition:match("%f[%w]()NOT()%f[^%w]"); 523 local s, e = condition:match("%f[%w]()NOT()%f[^%w]");
521 condition = (condition:sub(1,s-1)..condition:sub(e+1, -1)):match("^%s*(.-)%s*$"); 524 condition = (condition:sub(1,s-1)..condition:sub(e+1, -1)):match("^%s*(.-)%s*$");
522 negated = true; 525 negated = true;
523 end 526 end
530 if not ok then 533 if not ok then
531 return nil, errmsg(condition_code); 534 return nil, errmsg(condition_code);
532 end 535 end
533 if negated then condition_code = "not("..condition_code..")"; end 536 if negated then condition_code = "not("..condition_code..")"; end
534 table.insert(rule.conditions, condition_code); 537 table.insert(rule.conditions, condition_code);
538 table.insert(rule.source.conditions, line);
535 for _, dep in ipairs(condition_deps or {}) do 539 for _, dep in ipairs(condition_deps or {}) do
536 table.insert(rule.deps, dep); 540 table.insert(rule.deps, dep);
537 end 541 end
538 end 542 end
539 end 543 end
614 618
615 for name in pairs(definition_handlers) do 619 for name in pairs(definition_handlers) do
616 table.insert(code.global_header, 1, "local "..name:lower().."s = definitions."..name..";"); 620 table.insert(code.global_header, 1, "local "..name:lower().."s = definitions."..name..";");
617 end 621 end
618 622
619 local code_string = "return function (definitions, fire_event, log, module, pass_return)\n\t" 623 -- The function signature here must match what gets passed in compile_handler()
624 local code_string = "return function (definitions, fire_event, log, module, pass_return, source_rules)\n\t"
620 ..table.concat(code.global_header, "\n\t") 625 ..table.concat(code.global_header, "\n\t")
621 .."\n\tlocal db = require 'util.debug';\n\n\t" 626 .."\n\tlocal db = require 'util.debug';\n\n\t"
622 .."return function (event)\n\t\t" 627 .."return function (event)\n\t\t"
623 .."local stanza, session = event.stanza, event.origin;\n" 628 .."local stanza, session = event.stanza, event.origin;\n"
624 ..table.concat(code, "") 629 ..table.concat(code, "")
625 .."\n\tend;\nend"; 630 .."\n\tend;\nend";
626 631
627 chain_handlers[chain_name] = code_string; 632 chain_handlers[chain_name] = { code = code_string, rules = rules };
628 end 633 end
629 634
630 return chain_handlers; 635 return chain_handlers;
631 end 636 end
632 637
637 return chain_handlers; 642 return chain_handlers;
638 end 643 end
639 644
640 -- Compile handler code into a factory that produces a valid event handler. Factory accepts 645 -- Compile handler code into a factory that produces a valid event handler. Factory accepts
641 -- a value to be returned on PASS 646 -- a value to be returned on PASS
642 local function compile_handler(code_string, filename) 647 local function compile_handler(code_string, filename, source_rules)
643 -- Prepare event handler function 648 -- Prepare event handler function
644 local chunk, err = envload(code_string, "="..filename, _G); 649 local chunk, err = envload(code_string, "="..filename, _G);
645 if not chunk then 650 if not chunk then
646 return nil, "Error compiling (probably a compiler bug, please report): "..err; 651 return nil, "Error compiling (probably a compiler bug, please report): "..err;
647 end 652 end
651 local init_ok, initialized_chunk = pcall(chunk); 656 local init_ok, initialized_chunk = pcall(chunk);
652 if not init_ok then 657 if not init_ok then
653 return nil, "Error initializing compiled rules: "..initialized_chunk; 658 return nil, "Error initializing compiled rules: "..initialized_chunk;
654 end 659 end
655 return function (pass_return) 660 return function (pass_return)
656 return initialized_chunk(active_definitions, fire_event, logger(filename), module, pass_return); -- Returns event handler with upvalues 661 return initialized_chunk(active_definitions, fire_event, logger(filename), module, pass_return, source_rules); -- Returns event handler with upvalues
657 end 662 end
658 end 663 end
659 664
660 local function resolve_script_path(script_path) 665 local function resolve_script_path(script_path)
661 local relative_to = prosody.paths.config; 666 local relative_to = prosody.paths.config;
691 local errs; 696 local errs;
692 697
693 -- Loop through the chains in the script, and for each chain attach the compiled code to the 698 -- Loop through the chains in the script, and for each chain attach the compiled code to the
694 -- relevant events, keeping track in events_hooked so we can cleanly unload later 699 -- relevant events, keeping track in events_hooked so we can cleanly unload later
695 local events_hooked = {}; 700 local events_hooked = {};
696 for chain, handler_code in pairs(chain_functions) do 701 for chain, chain_info in pairs(chain_functions) do
697 local new_handler, err = compile_handler(handler_code, "mod_firewall::"..chain); 702 local handler_code = chain_info.code;
703 local new_handler, err = compile_handler(handler_code, "mod_firewall:"..base_name.."::"..chain, chain_info.rules);
698 if not new_handler then 704 if not new_handler then
699 module:log("error", "Compilation error for %s: %s", script, err); 705 module:log("error", "Compilation error for %s: %s", script, err);
700 errs = true; 706 errs = true;
701 break; 707 break;
702 else 708 else
821 print(); 827 print();
822 print("local active_definitions = "..serialize(active_definitions)..";"); 828 print("local active_definitions = "..serialize(active_definitions)..";");
823 print(); 829 print();
824 end 830 end
825 local c = 0; 831 local c = 0;
826 for chain, handler_code in pairs(chain_functions) do 832 for chain_name, chain_info in pairs(chain_functions) do
833 local handler_code = chain_info.code;
827 c = c + 1; 834 c = c + 1;
828 print("---- Chain "..chain:gsub("_", " ")); 835 print("---- Chain "..chain_name:gsub("_", " "));
829 local chain_func_name = "chain_"..tostring(c).."_"..chain:gsub("%p", "_"); 836 local chain_func_name = "chain_"..tostring(c).."_"..chain_name:gsub("%p", "_");
830 if not verbose then 837 if not verbose then
831 print(("%s = %s;"):format(chain_func_name, handler_code:sub(8))); 838 print(("%s = %s;"):format(chain_func_name, handler_code:sub(8)));
832 else 839 else
833 840
834 print(("local %s = (%s)(active_definitions, fire_event, logger(%q));"):format(chain_func_name, handler_code:sub(8), filename)); 841 print(("local %s = (%s)(active_definitions, fire_event, logger(%q));"):format(chain_func_name, handler_code:sub(8), filename));
835 print(); 842 print();
836 843
837 local chain_definition = chains[chain]; 844 local chain_definition = chains[chain_name];
838 if chain_definition and chain_definition.type == "event" then 845 if chain_definition and chain_definition.type == "event" then
839 for _, event_name in ipairs(chain_definition) do 846 for _, event_name in ipairs(chain_definition) do
840 print(("module:hook(%q, %s, %d);"):format(event_name, chain_func_name, chain_definition.priority or 0)); 847 print(("module:hook(%q, %s, %d);"):format(event_name, chain_func_name, chain_definition.priority or 0));
841 end 848 end
842 end 849 end
843 print(("module:hook(%q, %s, %d);"):format("firewall/chains/"..chain, chain_func_name, chain_definition.priority or 0)); 850 print(("module:hook(%q, %s, %d);"):format("firewall/chains/"..chain_name, chain_func_name, chain_definition.priority or 0));
844 end 851 end
845 852
846 print("---- End of chain "..chain); 853 print("---- End of chain "..chain_name);
847 print(); 854 print();
848 end 855 end
849 print("end -- End of file "..filename); 856 print("end -- End of file "..filename);
850 end 857 end
851 end 858 end