Software /
code /
prosody-modules
Diff
mod_firewall/actions.lib.lua @ 947:c91cac3b823f
mod_firewall: General stanza filtering plugin with a declarative rule-based syntax
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Wed, 03 Apr 2013 16:11:20 +0100 |
child | 949:b729414b4bf1 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_firewall/actions.lib.lua Wed Apr 03 16:11:20 2013 +0100 @@ -0,0 +1,158 @@ +local action_handlers = {}; + +-- Takes an XML string and returns a code string that builds that stanza +-- using st.stanza() +local function compile_xml(data) + local code = {}; + local first, short_close = true, nil; + for tagline, text in data:gmatch("<([^>]+)>([^<]*)") do + if tagline:sub(-1,-1) == "/" then + tagline = tagline:sub(1, -2); + short_close = true; + end + if tagline:sub(1,1) == "/" then + code[#code+1] = (":up()"); + else + local name, attr = tagline:match("^(%S*)%s*(.*)$"); + local attr_str = {}; + for k, _, v in attr:gmatch("(%S+)=([\"'])([^%2]-)%2") do + if #attr_str == 0 then + table.insert(attr_str, ", { "); + else + table.insert(attr_str, ", "); + end + if k:match("^%a%w*$") then + table.insert(attr_str, string.format("%s = %q", k, v)); + else + table.insert(attr_str, string.format("[%q] = %q", k, v)); + end + end + if #attr_str > 0 then + table.insert(attr_str, " }"); + end + if first then + code[#code+1] = (string.format("st.stanza(%q %s)", name, #attr_str>0 and table.concat(attr_str) or ", nil")); + first = nil; + else + code[#code+1] = (string.format(":tag(%q%s)", name, table.concat(attr_str))); + end + end + if text and text:match("%S") then + code[#code+1] = (string.format(":text(%q)", text)); + elseif short_close then + short_close = nil; + code[#code+1] = (":up()"); + end + end + return table.concat(code, ""); +end + + +function action_handlers.DROP() + return "log('debug', 'Firewall dropping stanza: %s', tostring(stanza)); return true;"; +end + +function action_handlers.STRIP(tag_desc) + local code = {}; + local name, xmlns = tag_desc:match("^(%S+) (.+)$"); + if not name then + name, xmlns = tag_desc, nil; + end + if name == "*" then + name = nil; + end + code[#code+1] = ("local stanza_xmlns = stanza.attr.xmlns; "); + code[#code+1] = "stanza:maptags(function (tag) if "; + if name then + code[#code+1] = ("tag.name == %q and "):format(name); + end + if xmlns then + code[#code+1] = ("(tag.attr.xmlns or stanza_xmlns) == %q "):format(xmlns); + else + code[#code+1] = ("tag.attr.xmlns == stanza_xmlns "); + end + code[#code+1] = "then return nil; end return tag; end );"; + return table.concat(code); +end + +function action_handlers.INJECT(tag) + return "stanza:add_child("..compile_xml(tag)..")", { "st" }; +end + +local error_types = { + ["bad-request"] = "modify"; + ["conflict"] = "cancel"; + ["feature-not-implemented"] = "cancel"; + ["forbidden"] = "auth"; + ["gone"] = "cancel"; + ["internal-server-error"] = "cancel"; + ["item-not-found"] = "cancel"; + ["jid-malformed"] = "modify"; + ["not-acceptable"] = "modify"; + ["not-allowed"] = "cancel"; + ["not-authorized"] = "auth"; + ["payment-required"] = "auth"; + ["policy-violation"] = "modify"; + ["recipient-unavailable"] = "wait"; + ["redirect"] = "modify"; + ["registration-required"] = "auth"; + ["remote-server-not-found"] = "cancel"; + ["remote-server-timeout"] = "wait"; + ["resource-constraint"] = "wait"; + ["service-unavailable"] = "cancel"; + ["subscription-required"] = "auth"; + ["undefined-condition"] = "cancel"; + ["unexpected-request"] = "wait"; +}; + + +local function route_modify(make_new, to, drop) + local reroute, deps = "session.send(newstanza)", { "st" }; + if to then + reroute = ("newstanza.attr.to = %q; core_post_stanza(session, newstanza)"):format(to); + deps[#deps+1] = "core_post_stanza"; + end + return ([[local newstanza = st.%s; %s; %s; ]]) + :format(make_new, reroute, drop and "return true" or ""), deps; +end + +function action_handlers.BOUNCE(with) + local error = with and with:match("^%S+") or "service-unavailable"; + local error_type = error:match(":(%S+)"); + if not error_type then + error_type = error_types[error] or "cancel"; + else + error = error:match("^[^:]+"); + end + error, error_type = string.format("%q", error), string.format("%q", error_type); + local text = with and with:match(" %((.+)%)$"); + if text then + text = string.format("%q", text); + else + text = "nil"; + end + return route_modify(("error_reply(stanza, %s, %s, %s)"):format(error_type, error, text), nil, true); +end + +function action_handlers.REDIRECT(where) + return route_modify("clone(stanza)", where, true, true); +end + +function action_handlers.COPY(where) + return route_modify("clone(stanza)", where, true, false); +end + +function action_handlers.LOG(string) + local level = string:match("^%[(%a+)%]") or "info"; + string = string:gsub("^%[%a+%] ?", ""); + return (("log(%q, %q)"):format(level, string) + :gsub("$top", [["..stanza:top_tag().."]]) + :gsub("$stanza", [["..stanza.."]]) + :gsub("$(%b())", [["..%1.."]])); +end + +function action_handlers.RULEDEP(dep) + return "", { dep }; +end + +return action_handlers;