comparison mod_firewall/definitions.lib.lua @ 2520:c6fd8975704b

mod_firewall: Initial support for lists, in-memory and HTTP
author Matthew Wild <mwild1@gmail.com>
date Sun, 19 Feb 2017 21:10:26 +0000
parents 5fe483b73fd2
children 72cbec103709
comparison
equal deleted inserted replaced
2519:d4bc434a60a4 2520:c6fd8975704b
2 -- Name arguments are unused here 2 -- Name arguments are unused here
3 -- luacheck: ignore 212 3 -- luacheck: ignore 212
4 4
5 local definition_handlers = {}; 5 local definition_handlers = {};
6 6
7 local http = require "net.http";
8 local timer = require "util.timer";
7 local set = require"util.set"; 9 local set = require"util.set";
8 local new_throttle = require "util.throttle".create; 10 local new_throttle = require "util.throttle".create;
9 11
10 local multirate_cache_size = module:get_option_number("firewall_multirate_cache_limit", 1000); 12 local multirate_cache_size = module:get_option_number("firewall_multirate_cache_limit", 1000);
11 13
55 } 57 }
56 end; 58 end;
57 }; 59 };
58 end 60 end
59 61
62 local list_backends = {
63 memory = {
64 init = function (self, type, opts)
65 if opts.limit then
66 local have_cache_lib, cache_lib = pcall(require, "util.cache");
67 if not have_cache_lib then
68 error("In-memory lists with a size limit require Prosody 0.10");
69 end
70 self.cache = cache_lib.new((assert(tonumber(opts.limit), "Invalid list limit")));
71 if not self.cache.table then
72 error("In-memory lists with a size limit require a newer version of Prosody 0.10");
73 end
74 self.items = self.cache:table();
75 else
76 self.items = {};
77 end
78 end;
79 add = function (self, item)
80 self.items[item] = true;
81 end;
82 remove = function (self, item)
83 self.items[item] = nil;
84 end;
85 contains = function (self, item)
86 return self.items[item] == true;
87 end;
88 };
89 http = {
90 init = function (self, url, opts)
91 local poll_interval = assert(tonumber(opts.ttl or "3600"), "invalid ttl for <"..url.."> (expected number of seconds)");
92 local pattern = opts.pattern or "([^\r\n]+)\r?\n";
93 assert(pcall(string.match, "", pattern), "invalid pattern for <"..url..">");
94 if opts.hash then
95 assert(opts.hash:match("^%w+$") and type(hashes[opts.hash]) == "function", "invalid hash function: "..opts.hash);
96 self.hash_function = hashes[opts.hash];
97 end
98 local etag;
99 local function update_list()
100 http.request(url, {
101 headers = {
102 ["If-None-Match"] = etag;
103 };
104 }, function (body, code, response)
105 if code == 200 and body then
106 etag = response.headers.etag;
107 local items = {};
108 for entry in body:gmatch(pattern) do
109 items[entry] = true;
110 end
111 self.items = items;
112 module:log("debug", "Fetched updated list from <%s>", url);
113 elseif code == 304 then
114 module:log("debug", "List at <%s> is unchanged", url);
115 else
116 module:log("warn", "Failed to fetch list from <%s>: %d %s", url, code, tostring(body));
117 end
118 if poll_interval > 0 then
119 timer.add_task(poll_interval, update_list);
120 end
121 end);
122 end
123 update_list();
124 timer.add_task(0, update_list);
125 end;
126 contains = function (self, item)
127 if self.hash_function then
128 item = self.hash_function(item);
129 end
130 return self.items and self.items[item] == true;
131 end;
132 };
133 };
134
135 local function create_list(list_backend, list_def, opts)
136 if not list_backends[list_backend] then
137 error("Unknown list type '"..list_backend.."'", 0);
138 end
139 local list = setmetatable({}, { __index = list_backends[list_backend] });
140 if list.init then
141 list:init(list_def, opts);
142 end
143 return list;
144 end
145
146 --[[
147 %LIST spammers: memory (source: /etc/spammers.txt)
148
149 %LIST spammers: memory (source: /etc/spammers.txt)
150
151
152 %LIST spammers: http://example.com/blacklist.txt
153 ]]
154
155 function definition_handlers.LIST(list_name, list_definition)
156 local list_backend = list_definition:match("^%w+");
157 local opts = {};
158 local opt_string = list_definition:match("^%S+%s+%((.+)%)");
159 if opt_string then
160 for opt_k, opt_v in opt_string:gmatch("(%w+): ?([^,]+)") do
161 opts[opt_k] = opt_v;
162 end
163 end
164 return create_list(list_backend, list_definition:match("^%S+"), opts);
165 end
166
60 return definition_handlers; 167 return definition_handlers;
61