comparison 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
parents
children b729414b4bf1
comparison
equal deleted inserted replaced
946:2c5430ff1c11 947:c91cac3b823f
1 local action_handlers = {};
2
3 -- Takes an XML string and returns a code string that builds that stanza
4 -- using st.stanza()
5 local function compile_xml(data)
6 local code = {};
7 local first, short_close = true, nil;
8 for tagline, text in data:gmatch("<([^>]+)>([^<]*)") do
9 if tagline:sub(-1,-1) == "/" then
10 tagline = tagline:sub(1, -2);
11 short_close = true;
12 end
13 if tagline:sub(1,1) == "/" then
14 code[#code+1] = (":up()");
15 else
16 local name, attr = tagline:match("^(%S*)%s*(.*)$");
17 local attr_str = {};
18 for k, _, v in attr:gmatch("(%S+)=([\"'])([^%2]-)%2") do
19 if #attr_str == 0 then
20 table.insert(attr_str, ", { ");
21 else
22 table.insert(attr_str, ", ");
23 end
24 if k:match("^%a%w*$") then
25 table.insert(attr_str, string.format("%s = %q", k, v));
26 else
27 table.insert(attr_str, string.format("[%q] = %q", k, v));
28 end
29 end
30 if #attr_str > 0 then
31 table.insert(attr_str, " }");
32 end
33 if first then
34 code[#code+1] = (string.format("st.stanza(%q %s)", name, #attr_str>0 and table.concat(attr_str) or ", nil"));
35 first = nil;
36 else
37 code[#code+1] = (string.format(":tag(%q%s)", name, table.concat(attr_str)));
38 end
39 end
40 if text and text:match("%S") then
41 code[#code+1] = (string.format(":text(%q)", text));
42 elseif short_close then
43 short_close = nil;
44 code[#code+1] = (":up()");
45 end
46 end
47 return table.concat(code, "");
48 end
49
50
51 function action_handlers.DROP()
52 return "log('debug', 'Firewall dropping stanza: %s', tostring(stanza)); return true;";
53 end
54
55 function action_handlers.STRIP(tag_desc)
56 local code = {};
57 local name, xmlns = tag_desc:match("^(%S+) (.+)$");
58 if not name then
59 name, xmlns = tag_desc, nil;
60 end
61 if name == "*" then
62 name = nil;
63 end
64 code[#code+1] = ("local stanza_xmlns = stanza.attr.xmlns; ");
65 code[#code+1] = "stanza:maptags(function (tag) if ";
66 if name then
67 code[#code+1] = ("tag.name == %q and "):format(name);
68 end
69 if xmlns then
70 code[#code+1] = ("(tag.attr.xmlns or stanza_xmlns) == %q "):format(xmlns);
71 else
72 code[#code+1] = ("tag.attr.xmlns == stanza_xmlns ");
73 end
74 code[#code+1] = "then return nil; end return tag; end );";
75 return table.concat(code);
76 end
77
78 function action_handlers.INJECT(tag)
79 return "stanza:add_child("..compile_xml(tag)..")", { "st" };
80 end
81
82 local error_types = {
83 ["bad-request"] = "modify";
84 ["conflict"] = "cancel";
85 ["feature-not-implemented"] = "cancel";
86 ["forbidden"] = "auth";
87 ["gone"] = "cancel";
88 ["internal-server-error"] = "cancel";
89 ["item-not-found"] = "cancel";
90 ["jid-malformed"] = "modify";
91 ["not-acceptable"] = "modify";
92 ["not-allowed"] = "cancel";
93 ["not-authorized"] = "auth";
94 ["payment-required"] = "auth";
95 ["policy-violation"] = "modify";
96 ["recipient-unavailable"] = "wait";
97 ["redirect"] = "modify";
98 ["registration-required"] = "auth";
99 ["remote-server-not-found"] = "cancel";
100 ["remote-server-timeout"] = "wait";
101 ["resource-constraint"] = "wait";
102 ["service-unavailable"] = "cancel";
103 ["subscription-required"] = "auth";
104 ["undefined-condition"] = "cancel";
105 ["unexpected-request"] = "wait";
106 };
107
108
109 local function route_modify(make_new, to, drop)
110 local reroute, deps = "session.send(newstanza)", { "st" };
111 if to then
112 reroute = ("newstanza.attr.to = %q; core_post_stanza(session, newstanza)"):format(to);
113 deps[#deps+1] = "core_post_stanza";
114 end
115 return ([[local newstanza = st.%s; %s; %s; ]])
116 :format(make_new, reroute, drop and "return true" or ""), deps;
117 end
118
119 function action_handlers.BOUNCE(with)
120 local error = with and with:match("^%S+") or "service-unavailable";
121 local error_type = error:match(":(%S+)");
122 if not error_type then
123 error_type = error_types[error] or "cancel";
124 else
125 error = error:match("^[^:]+");
126 end
127 error, error_type = string.format("%q", error), string.format("%q", error_type);
128 local text = with and with:match(" %((.+)%)$");
129 if text then
130 text = string.format("%q", text);
131 else
132 text = "nil";
133 end
134 return route_modify(("error_reply(stanza, %s, %s, %s)"):format(error_type, error, text), nil, true);
135 end
136
137 function action_handlers.REDIRECT(where)
138 return route_modify("clone(stanza)", where, true, true);
139 end
140
141 function action_handlers.COPY(where)
142 return route_modify("clone(stanza)", where, true, false);
143 end
144
145 function action_handlers.LOG(string)
146 local level = string:match("^%[(%a+)%]") or "info";
147 string = string:gsub("^%[%a+%] ?", "");
148 return (("log(%q, %q)"):format(level, string)
149 :gsub("$top", [["..stanza:top_tag().."]])
150 :gsub("$stanza", [["..stanza.."]])
151 :gsub("$(%b())", [["..%1.."]]));
152 end
153
154 function action_handlers.RULEDEP(dep)
155 return "", { dep };
156 end
157
158 return action_handlers;