Mercurial > prosody-modules
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 |
