mod_muc_limits/mod_muc_limits.lua
author Matthew Wild <mwild1@gmail.com>
Fri, 03 Apr 2020 12:26:56 +0100
changeset 3969 2b10e51d85a6
parent 3968 15355caf4553
child 5568 731ba9400c10
permissions -rw-r--r--
mod_muc_limits: Add config option to limit to join stanzas only This is a bit more limited in pre-0.11 MUC modules, because it just detects stanzas sent to full JIDs (which would include all presence and nick changes). This option is useful for setups where users are typically unaffiliated, but trusted (e.g. if access to the room is gated through some other means such as password/token auth).
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
1057
0b41122b19f9 mod_muc_limits: Abort loading on non-MUC hosts (thanks Ge0rG)
Matthew Wild <mwild1@gmail.com>
parents: 1042
diff changeset
     1
1772
163967467308 mod_muc_limits: Update to work with both the new and the old MUC API
Kim Alvefur <zash@zash.se>
parents: 1428
diff changeset
     2
local mod_muc = module:depends"muc";
163967467308 mod_muc_limits: Update to work with both the new and the old MUC API
Kim Alvefur <zash@zash.se>
parents: 1428
diff changeset
     3
local rooms = rawget(mod_muc, "rooms"); -- Old MUC API
554
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     4
1042
5fd0860c86cd mod_muc_limits: Allow stanzas from affiliated users even if they are not in the room
Matthew Wild <mwild1@gmail.com>
parents: 1040
diff changeset
     5
local jid_split, jid_bare = require "util.jid".split, require "util.jid".bare;
554
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     6
local st = require "util.stanza";
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     7
local new_throttle = require "util.throttle".create;
1038
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
     8
local t_insert, t_concat = table.insert, table.concat;
554
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
     9
557
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    10
local xmlns_muc = "http://jabber.org/protocol/muc";
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    11
554
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    12
local period = math.max(module:get_option_number("muc_event_rate", 0.5), 0);
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    13
local burst = math.max(module:get_option_number("muc_burst_factor", 6), 1);
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    14
1036
a44e755f7579 mod_muc_limits: Add muc_max_nick_length option, defaulting to 23 (bytes)
Matthew Wild <mwild1@gmail.com>
parents: 1035
diff changeset
    15
local max_nick_length = module:get_option_number("muc_max_nick_length", 23); -- Default chosen through scientific methods
3969
2b10e51d85a6 mod_muc_limits: Add config option to limit to join stanzas only
Matthew Wild <mwild1@gmail.com>
parents: 3968
diff changeset
    16
local join_only = module:get_option_boolean("muc_limit_joins_only", false);
1038
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    17
local dropped_count = 0;
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    18
local dropped_jids;
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    19
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    20
local function log_dropped()
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    21
	module:log("warn", "Dropped %d stanzas from %d JIDs: %s", dropped_count, #dropped_jids, t_concat(dropped_jids, ", "));
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    22
	dropped_count = 0;
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    23
	dropped_jids = nil;
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    24
end
1036
a44e755f7579 mod_muc_limits: Add muc_max_nick_length option, defaulting to 23 (bytes)
Matthew Wild <mwild1@gmail.com>
parents: 1035
diff changeset
    25
554
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    26
local function handle_stanza(event)
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    27
	local origin, stanza = event.origin, event.stanza;
555
2356ad05fdb6 mod_muc_limits: Don't limit room leaving
Matthew Wild <mwild1@gmail.com>
parents: 554
diff changeset
    28
	if stanza.name == "presence" and stanza.attr.type == "unavailable" then -- Don't limit room leaving
2356ad05fdb6 mod_muc_limits: Don't limit room leaving
Matthew Wild <mwild1@gmail.com>
parents: 554
diff changeset
    29
		return;
2356ad05fdb6 mod_muc_limits: Don't limit room leaving
Matthew Wild <mwild1@gmail.com>
parents: 554
diff changeset
    30
	end
1042
5fd0860c86cd mod_muc_limits: Allow stanzas from affiliated users even if they are not in the room
Matthew Wild <mwild1@gmail.com>
parents: 1040
diff changeset
    31
	local dest_room, dest_host, dest_nick = jid_split(stanza.attr.to);
3421
1534d0715d35 mod_muc_limits: Add support for new MUC API in Prosody 0.11
Kim Alvefur <zash@zash.se>
parents: 3406
diff changeset
    32
	local room = event.room or rooms[dest_room.."@"..dest_host];
554
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    33
	if not room then return; end
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    34
	local from_jid = stanza.attr.from;
3422
9be9dd434813 mod_muc_limits: Simplify bypass for affiliated users
Kim Alvefur <zash@zash.se>
parents: 3421
diff changeset
    35
	if room:get_affiliation(jid_bare(from_jid)) then
554
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    36
		module:log("debug", "Skipping stanza from affiliated user...");
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    37
		return;
1058
1255de347dd4 mod_muc_limits: Fix traceback on presence sent to the room's bare JID
Matthew Wild <mwild1@gmail.com>
parents: 1057
diff changeset
    38
	elseif dest_nick and max_nick_length and stanza.name == "presence" and not room._occupants[stanza.attr.to] and #dest_nick > max_nick_length then
1036
a44e755f7579 mod_muc_limits: Add muc_max_nick_length option, defaulting to 23 (bytes)
Matthew Wild <mwild1@gmail.com>
parents: 1035
diff changeset
    39
		module:log("debug", "Forbidding long (%d bytes) nick in %s", #dest_nick, dest_room)
a44e755f7579 mod_muc_limits: Add muc_max_nick_length option, defaulting to 23 (bytes)
Matthew Wild <mwild1@gmail.com>
parents: 1035
diff changeset
    40
		origin.send(st.error_reply(stanza, "modify", "policy-violation", "Your nick name is too long, please use a shorter one")
a44e755f7579 mod_muc_limits: Add muc_max_nick_length option, defaulting to 23 (bytes)
Matthew Wild <mwild1@gmail.com>
parents: 1035
diff changeset
    41
			:up():tag("x", { xmlns = xmlns_muc }));
a44e755f7579 mod_muc_limits: Add muc_max_nick_length option, defaulting to 23 (bytes)
Matthew Wild <mwild1@gmail.com>
parents: 1035
diff changeset
    42
		return true;
554
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    43
	end
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    44
	local throttle = room.throttle;
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    45
	if not room.throttle then
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    46
		throttle = new_throttle(period*burst, burst);
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    47
		room.throttle = throttle;
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    48
	end
3192
5c3f3f5a4377 Backed out parts of changeset a81456a13797
Kim Alvefur <zash@zash.se>
parents: 3102
diff changeset
    49
	if not throttle:poll(1) then
1038
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    50
		module:log("debug", "Dropping stanza for %s@%s from %s, over rate limit", dest_room, dest_host, from_jid);
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    51
		if not dropped_jids then
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    52
			dropped_jids = { [from_jid] = true, from_jid };
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    53
			module:add_timer(5, log_dropped);
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    54
		elseif not dropped_jids[from_jid] then
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    55
			dropped_jids[from_jid] = true;
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    56
			t_insert(dropped_jids, from_jid);
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    57
		end
edb06824a5a4 mod_muc_limits: Condense multiple dropped stanzas into a single log message every 5 seconds
Matthew Wild <mwild1@gmail.com>
parents: 1036
diff changeset
    58
		dropped_count = dropped_count + 1;
1205
7d2d440e2fa5 mod_muc_limits: Just drop error stanzas instead of bouncing them with more error stanzas
Kim Alvefur <zash@zash.se>
parents: 1058
diff changeset
    59
		if stanza.attr.type == "error" then -- We don't want to bounce errors
7d2d440e2fa5 mod_muc_limits: Just drop error stanzas instead of bouncing them with more error stanzas
Kim Alvefur <zash@zash.se>
parents: 1058
diff changeset
    60
			return true;
7d2d440e2fa5 mod_muc_limits: Just drop error stanzas instead of bouncing them with more error stanzas
Kim Alvefur <zash@zash.se>
parents: 1058
diff changeset
    61
		end
557
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    62
		local reply = st.error_reply(stanza, "wait", "policy-violation", "The room is currently overactive, please try again later");
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    63
		local body = stanza:get_child_text("body");
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    64
		if body then
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    65
			reply:up():tag("body"):text(body):up();
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    66
		end
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    67
		local x = stanza:get_child("x", xmlns_muc);
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    68
		if x then
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    69
			reply:add_child(st.clone(x));
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    70
		end
14f39769c9e0 mod_muc_limits: Echo any MUC <x> or <body> in the error reply (required to make Gajim display the error)
Matthew Wild <mwild1@gmail.com>
parents: 556
diff changeset
    71
		origin.send(reply);
554
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    72
		return true;
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    73
	end
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    74
end
a2b0174b5c48 mod_muc_limits: New module to impose overall rate-limits on a MUC (not on individual users)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
    75
3453
5e91a34b1e71 mod_muc_limits: Fix being unload on trunk
Kim Alvefur <zash@zash.se>
parents: 3422
diff changeset
    76
if rooms then
5e91a34b1e71 mod_muc_limits: Fix being unload on trunk
Kim Alvefur <zash@zash.se>
parents: 3422
diff changeset
    77
	function module.unload()
3968
15355caf4553 mod_muc_limits: Add [luacheck] annotation to silence unused loop variable warning
Matthew Wild <mwild1@gmail.com>
parents: 3453
diff changeset
    78
		for room_jid, room in pairs(rooms) do --luacheck: ignore 213/room_jid
3453
5e91a34b1e71 mod_muc_limits: Fix being unload on trunk
Kim Alvefur <zash@zash.se>
parents: 3422
diff changeset
    79
			room.throttle = nil;
5e91a34b1e71 mod_muc_limits: Fix being unload on trunk
Kim Alvefur <zash@zash.se>
parents: 3422
diff changeset
    80
		end
556
e50bdbaa7802 mod_muc_limits: Remove throttle object from all rooms on unload (to make sure new settings are applied on reload)
Matthew Wild <mwild1@gmail.com>
parents: 555
diff changeset
    81
	end
e50bdbaa7802 mod_muc_limits: Remove throttle object from all rooms on unload (to make sure new settings are applied on reload)
Matthew Wild <mwild1@gmail.com>
parents: 555
diff changeset
    82
3421
1534d0715d35 mod_muc_limits: Add support for new MUC API in Prosody 0.11
Kim Alvefur <zash@zash.se>
parents: 3406
diff changeset
    83
	module:hook("presence/full", handle_stanza, 501);
3969
2b10e51d85a6 mod_muc_limits: Add config option to limit to join stanzas only
Matthew Wild <mwild1@gmail.com>
parents: 3968
diff changeset
    84
	if not join_only then
2b10e51d85a6 mod_muc_limits: Add config option to limit to join stanzas only
Matthew Wild <mwild1@gmail.com>
parents: 3968
diff changeset
    85
		module:hook("message/bare", handle_stanza, 501);
2b10e51d85a6 mod_muc_limits: Add config option to limit to join stanzas only
Matthew Wild <mwild1@gmail.com>
parents: 3968
diff changeset
    86
		module:hook("message/full", handle_stanza, 501);
2b10e51d85a6 mod_muc_limits: Add config option to limit to join stanzas only
Matthew Wild <mwild1@gmail.com>
parents: 3968
diff changeset
    87
		module:hook("presence/bare", handle_stanza, 501);
2b10e51d85a6 mod_muc_limits: Add config option to limit to join stanzas only
Matthew Wild <mwild1@gmail.com>
parents: 3968
diff changeset
    88
	end
3421
1534d0715d35 mod_muc_limits: Add support for new MUC API in Prosody 0.11
Kim Alvefur <zash@zash.se>
parents: 3406
diff changeset
    89
else
1534d0715d35 mod_muc_limits: Add support for new MUC API in Prosody 0.11
Kim Alvefur <zash@zash.se>
parents: 3406
diff changeset
    90
	module:hook("muc-occupant-pre-join", handle_stanza);
3969
2b10e51d85a6 mod_muc_limits: Add config option to limit to join stanzas only
Matthew Wild <mwild1@gmail.com>
parents: 3968
diff changeset
    91
	if not join_only then
2b10e51d85a6 mod_muc_limits: Add config option to limit to join stanzas only
Matthew Wild <mwild1@gmail.com>
parents: 3968
diff changeset
    92
		module:hook("muc-occupant-pre-change", handle_stanza);
2b10e51d85a6 mod_muc_limits: Add config option to limit to join stanzas only
Matthew Wild <mwild1@gmail.com>
parents: 3968
diff changeset
    93
		module:hook("muc-occupant-groupchat", handle_stanza);
2b10e51d85a6 mod_muc_limits: Add config option to limit to join stanzas only
Matthew Wild <mwild1@gmail.com>
parents: 3968
diff changeset
    94
	end
3421
1534d0715d35 mod_muc_limits: Add support for new MUC API in Prosody 0.11
Kim Alvefur <zash@zash.se>
parents: 3406
diff changeset
    95
end