-- Prosody IM -- Copyright (C) 2008-2010 Matthew Wild -- Copyright (C) 2008-2010 Waqas Hussain -- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. -- local create_throttle = require "util.throttle".create; local new_cache = require "util.cache".new; local ip_util = require "util.ip"; local new_ip = ip_util.new_ip; local match_ip = ip_util.match; local parse_cidr = ip_util.parse_cidr; local errors = require "util.error"; -- COMPAT drop old option names local min_seconds_between_registrations = module:get_option_number("min_seconds_between_registrations"); local allowlist_only = module:get_option_boolean("allowlist_registration_only", module:get_option_boolean("whitelist_registration_only")); local allowlisted_ips = module:get_option_set("registration_allowlist", module:get_option("registration_whitelist", { "127.0.0.1", "::1" }))._items; local blocklisted_ips = module:get_option_set("registration_blocklist", module:get_option_set("registration_blacklist", {}))._items; local throttle_max = module:get_option_number("registration_throttle_max", min_seconds_between_registrations and 1); local throttle_period = module:get_option_number("registration_throttle_period", min_seconds_between_registrations); local throttle_cache_size = module:get_option_number("registration_throttle_cache_size", 100); local blocklist_overflow = module:get_option_boolean("blocklist_on_registration_throttle_overload", module:get_option_boolean("blacklist_on_registration_throttle_overload", false)); local throttle_cache = new_cache(throttle_cache_size, blocklist_overflow and function (ip, throttle) if not throttle:peek() then module:log("info", "Adding ip %s to registration blocklist", ip); blocklisted_ips[ip] = true; end end or nil); local function check_throttle(ip) if not throttle_max then return true end local throttle = throttle_cache:get(ip); if not throttle then throttle = create_throttle(throttle_max, throttle_period); end throttle_cache:set(ip, throttle); return throttle:poll(1); end local function ip_in_set(set, ip) if set[ip] then return true; end ip = new_ip(ip); for in_set in pairs(set) do if match_ip(ip, parse_cidr(in_set)) then return true; end end return false; end local err_registry = { blocklisted = { text = "Your IP address is blocklisted"; type = "auth"; condition = "forbidden"; }; not_allowlisted = { text = "Your IP address is not allowlisted"; type = "auth"; condition = "forbidden"; }; throttled = { text = "Too many registrations from this IP address recently"; type = "wait"; condition = "policy-violation"; }; } module:hook("user-registering", function (event) local session = event.session; local ip = event.ip or session and session.ip; local log = session and session.log or module._log; if not ip then log("warn", "IP not known; can't apply blocklist/allowlist"); elseif ip_in_set(blocklisted_ips, ip) then log("debug", "Registration disallowed by blocklist"); event.allowed = false; event.error = errors.new("blocklisted", event, err_registry); elseif (allowlist_only and not ip_in_set(allowlisted_ips, ip)) then log("debug", "Registration disallowed by allowlist"); event.allowed = false; event.error = errors.new("not_allowlisted", event, err_registry); elseif throttle_max and not ip_in_set(allowlisted_ips, ip) then if not check_throttle(ip) then log("debug", "Registrations over limit for ip %s", ip or "?"); event.allowed = false; event.error = errors.new("throttled", event, err_registry); end end if event.error then -- COMPAT pre-util.error event.reason = event.error.text; event.error_type = event.error.type; event.error_condition = event.error.condition; end end);