--- a/mod_firewall/conditions.lib.lua Sun Feb 19 21:08:30 2017 +0000
+++ b/mod_firewall/conditions.lib.lua Sun Feb 19 21:10:26 2017 +0000
@@ -252,4 +252,15 @@
return ("not not session.firewall_marked_"..idsafe(name));
end
+-- CHECK LIST: spammers contains $<@from>
+function condition_handlers.CHECK_LIST(list_condition)
+ local list_name, expr = list_condition:match("(%S+) contains (.+)$");
+ if not list_name and expr then
+ error("Error parsing list check, syntax: LISTNAME contains EXPRESSION");
+ end
+ local meta_deps = {};
+ expr = meta(("%q"):format(expr), meta_deps);
+ return ("list_%s:contains(%s) == true"):format(list_name, expr), { "list:"..list_name, unpack(meta_deps) };
+end
+
return condition_handlers;
--- a/mod_firewall/definitions.lib.lua Sun Feb 19 21:08:30 2017 +0000
+++ b/mod_firewall/definitions.lib.lua Sun Feb 19 21:10:26 2017 +0000
@@ -4,6 +4,8 @@
local definition_handlers = {};
+local http = require "net.http";
+local timer = require "util.timer";
local set = require"util.set";
local new_throttle = require "util.throttle".create;
@@ -57,5 +59,109 @@
};
end
+local list_backends = {
+ memory = {
+ init = function (self, type, opts)
+ if opts.limit then
+ local have_cache_lib, cache_lib = pcall(require, "util.cache");
+ if not have_cache_lib then
+ error("In-memory lists with a size limit require Prosody 0.10");
+ end
+ self.cache = cache_lib.new((assert(tonumber(opts.limit), "Invalid list limit")));
+ if not self.cache.table then
+ error("In-memory lists with a size limit require a newer version of Prosody 0.10");
+ end
+ self.items = self.cache:table();
+ else
+ self.items = {};
+ end
+ end;
+ add = function (self, item)
+ self.items[item] = true;
+ end;
+ remove = function (self, item)
+ self.items[item] = nil;
+ end;
+ contains = function (self, item)
+ return self.items[item] == true;
+ end;
+ };
+ http = {
+ init = function (self, url, opts)
+ local poll_interval = assert(tonumber(opts.ttl or "3600"), "invalid ttl for <"..url.."> (expected number of seconds)");
+ local pattern = opts.pattern or "([^\r\n]+)\r?\n";
+ assert(pcall(string.match, "", pattern), "invalid pattern for <"..url..">");
+ if opts.hash then
+ assert(opts.hash:match("^%w+$") and type(hashes[opts.hash]) == "function", "invalid hash function: "..opts.hash);
+ self.hash_function = hashes[opts.hash];
+ end
+ local etag;
+ local function update_list()
+ http.request(url, {
+ headers = {
+ ["If-None-Match"] = etag;
+ };
+ }, function (body, code, response)
+ if code == 200 and body then
+ etag = response.headers.etag;
+ local items = {};
+ for entry in body:gmatch(pattern) do
+ items[entry] = true;
+ end
+ self.items = items;
+ module:log("debug", "Fetched updated list from <%s>", url);
+ elseif code == 304 then
+ module:log("debug", "List at <%s> is unchanged", url);
+ else
+ module:log("warn", "Failed to fetch list from <%s>: %d %s", url, code, tostring(body));
+ end
+ if poll_interval > 0 then
+ timer.add_task(poll_interval, update_list);
+ end
+ end);
+ end
+ update_list();
+ timer.add_task(0, update_list);
+ end;
+ contains = function (self, item)
+ if self.hash_function then
+ item = self.hash_function(item);
+ end
+ return self.items and self.items[item] == true;
+ end;
+ };
+};
+
+local function create_list(list_backend, list_def, opts)
+ if not list_backends[list_backend] then
+ error("Unknown list type '"..list_backend.."'", 0);
+ end
+ local list = setmetatable({}, { __index = list_backends[list_backend] });
+ if list.init then
+ list:init(list_def, opts);
+ end
+ return list;
+end
+
+--[[
+%LIST spammers: memory (source: /etc/spammers.txt)
+
+%LIST spammers: memory (source: /etc/spammers.txt)
+
+
+%LIST spammers: http://example.com/blacklist.txt
+]]
+
+function definition_handlers.LIST(list_name, list_definition)
+ local list_backend = list_definition:match("^%w+");
+ local opts = {};
+ local opt_string = list_definition:match("^%S+%s+%((.+)%)");
+ if opt_string then
+ for opt_k, opt_v in opt_string:gmatch("(%w+): ?([^,]+)") do
+ opts[opt_k] = opt_v;
+ end
+ end
+ return create_list(list_backend, list_definition:match("^%S+"), opts);
+end
+
return definition_handlers;
-
--- a/mod_firewall/mod_firewall.lua Sun Feb 19 21:08:30 2017 +0000
+++ b/mod_firewall/mod_firewall.lua Sun Feb 19 21:10:26 2017 +0000
@@ -191,6 +191,12 @@
local_code = [[local roster_entry = (to_node and rostermanager.load_roster(to_node, to_host) or {})[bare_from];]];
depends = { "rostermanager", "split_to", "bare_from" };
};
+ list = { global_code = function (list)
+ assert(idsafe(list), "Invalid list name: "..list);
+ assert(active_definitions.LIST[list], "Unknown list: "..list);
+ return ("local list_%s = lists[%q];"):format(list, list);
+ end
+ };
};
local function include_dep(dependency, code)