diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/mod_limits.lua | 96 | ||||
-rw-r--r-- | plugins/mod_s2s/mod_s2s.lua | 2 | ||||
-rw-r--r-- | plugins/mod_s2s/s2sout.lib.lua | 10 | ||||
-rw-r--r-- | plugins/mod_server_contact_info.lua | 49 |
4 files changed, 152 insertions, 5 deletions
diff --git a/plugins/mod_limits.lua b/plugins/mod_limits.lua new file mode 100644 index 00000000..2a6ee8a2 --- /dev/null +++ b/plugins/mod_limits.lua @@ -0,0 +1,96 @@ +-- Because we deal we pre-authed sessions and streams we can't be host-specific +module:set_global(); + +local filters = require "util.filters"; +local throttle = require "util.throttle"; +local timer = require "util.timer"; + +local limits_cfg = module:get_option("limits", {}); +local limits_resolution = module:get_option_number("limits_resolution", 1); + +local default_bytes_per_second = 3000; +local default_burst = 2; + +local rate_units = { b = 1, k = 3, m = 6, g = 9, t = 12 } -- Plan for the future. +local function parse_rate(rate, sess_type) + local quantity, unit, exp; + if rate then + quantity, unit = rate:match("^(%d+) ?([^/]+)/s$"); + exp = quantity and rate_units[unit:sub(1,1):lower()]; + end + if not exp then + module:log("error", "Error parsing rate for %s: %q, using default rate (%d bytes/s)", sess_type, rate, default_bytes_per_second); + return default_bytes_per_second; + end + return quantity*(10^exp); +end + +local function parse_burst(burst, sess_type) + if type(burst) == "string" then + burst = burst:match("^(%d+) ?s$"); + end + local n_burst = tonumber(burst); + if not n_burst then + module:log("error", "Unable to parse burst for %s: %q, using default burst interval (%ds)", sess_type, tostring(burst), default_burst); + end + return n_burst or default_burst; +end + +-- Process config option into limits table: +-- limits = { c2s = { bytes_per_second = X, burst_seconds = Y } } +local limits = {}; + +for sess_type, sess_limits in pairs(limits_cfg) do + limits[sess_type] = { + bytes_per_second = parse_rate(sess_limits.rate, sess_type); + burst_seconds = parse_burst(sess_limits.burst, sess_type); + }; +end + +local default_filter_set = {}; + +function default_filter_set.bytes_in(bytes, session) + local throttle = session.throttle; + if throttle then + local ok, balance, outstanding = throttle:poll(#bytes, true); + if not ok then + session.log("debug", "Session over rate limit (%d) with %d (by %d), pausing", throttle.max, #bytes, outstanding); + session.conn:pause(); -- Read no more data from the connection until there is no outstanding data + local outstanding_data = bytes:sub(-outstanding); + bytes = bytes:sub(1, #bytes-outstanding); + timer.add_task(limits_resolution, function () + if not session.conn then return; end + if throttle:peek(#outstanding_data) then + session.log("debug", "Resuming paused session"); + session.conn:resume(); + end + -- Handle what we can of the outstanding data + session.data(outstanding_data); + end); + end + end + return bytes; +end + +local type_filters = { + c2s = default_filter_set; + s2sin = default_filter_set; + s2sout = default_filter_set; +}; + +local function filter_hook(session) + local session_type = session.type:match("^[^_]+"); + local filter_set, opts = type_filters[session_type], limits[session_type]; + if opts then + session.throttle = throttle.create(opts.bytes_per_second * opts.burst_seconds, opts.burst_seconds); + filters.add_filter(session, "bytes/in", filter_set.bytes_in, 1000); + end +end + +function module.load() + filters.add_filter_hook(filter_hook); +end + +function module.unload() + filters.remove_filter_hook(filter_hook); +end diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index b46b7e2a..bfd8f9af 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -180,6 +180,7 @@ end -- Stream is authorised, and ready for normal stanzas function mark_connected(session) + local sendq = session.sendq; local from, to = session.from_host, session.to_host; @@ -211,6 +212,7 @@ function mark_connected(session) session.sendq = nil; end + session.resolver = nil; session.ip_hosts = nil; session.srv_hosts = nil; end diff --git a/plugins/mod_s2s/s2sout.lib.lua b/plugins/mod_s2s/s2sout.lib.lua index 61d6086e..cd8553e1 100644 --- a/plugins/mod_s2s/s2sout.lib.lua +++ b/plugins/mod_s2s/s2sout.lib.lua @@ -49,6 +49,8 @@ function s2sout.initiate_connection(host_session) initialize_filters(host_session); host_session.version = 1; + host_session.resolver = adns.resolver(); + -- Kick the connection attempting machine into life if not s2sout.attempt_connection(host_session) then -- Intentionally not returning here, the @@ -84,9 +86,7 @@ function s2sout.attempt_connection(host_session, err) if not err then -- This is our first attempt log("debug", "First attempt to connect to %s, starting with SRV lookup...", to_host); host_session.connecting = true; - local handle; - handle = adns.lookup(function (answer) - handle = nil; + host_session.resolver:lookup(function (answer) local srv_hosts = { answer = answer }; host_session.srv_hosts = srv_hosts; host_session.srv_choice = 0; @@ -168,7 +168,7 @@ function s2sout.try_connect(host_session, connect_host, connect_port, err) local have_other_result = not(has_ipv4) or not(has_ipv6) or false; if has_ipv4 then - handle4 = adns.lookup(function (reply, err) + handle4 = host_session.resolver:lookup(function (reply, err) handle4 = nil; if reply and reply[#reply] and reply[#reply].a then @@ -206,7 +206,7 @@ function s2sout.try_connect(host_session, connect_host, connect_port, err) end if has_ipv6 then - handle6 = adns.lookup(function (reply, err) + handle6 = host_session.resolver:lookup(function (reply, err) handle6 = nil; if reply and reply[#reply] and reply[#reply].aaaa then diff --git a/plugins/mod_server_contact_info.lua b/plugins/mod_server_contact_info.lua new file mode 100644 index 00000000..7ee8a08f --- /dev/null +++ b/plugins/mod_server_contact_info.lua @@ -0,0 +1,49 @@ +-- XEP-0157: Contact Addresses for XMPP Services for Prosody +-- +-- Copyright (C) 2011-2016 Kim Alvefur +-- +-- This file is MIT/X11 licensed. +-- + +local t_insert = table.insert; +local array = require "util.array"; +local df_new = require "util.dataforms".new; + +-- Source: http://xmpp.org/registrar/formtypes.html#http:--jabber.org-network-serverinfo +local valid_types = { + abuse = true; + admin = true; + feedback = true; + sales = true; + security = true; + support = true; +} + +local contact_config = module:get_option("contact_info"); +if not contact_config or not next(contact_config) then -- we'll use admins from the config as default + local admins = module:get_option_inherited_set("admins", {}); + if admins:empty() then + module:log("error", "No contact_info or admins set in config"); + return -- Nothing to attach, so we'll just skip it. + end + module:log("info", "No contact_info in config, using admins as fallback"); + contact_config = { + admin = array.collect( admins / function(admin) return "xmpp:" .. admin; end); + }; +end + +local form_layout = { + { value = "http://jabber.org/network/serverinfo"; type = "hidden"; name = "FORM_TYPE"; }; +}; + +local form_values = {}; + +for t in pairs(valid_types) do + local addresses = contact_config[t]; + if addresses then + t_insert(form_layout, { name = t .. "-addresses", type = "list-multi" }); + form_values[t .. "-addresses"] = addresses; + end +end + +module:add_extension(df_new(form_layout):form(form_values, "result")); |