view mod_throttle_unsolicited/mod_throttle_unsolicited.lua @ 5608:1893ae742f66

mod_http_oauth2: Show errors on device flow user code entry page If the user enters the code incorrectly, having to click back to try again is no fun. Instead, show the error and the code entry form again.
author Kim Alvefur <zash@zash.se>
date Wed, 19 Jul 2023 13:05:47 +0200
parents 4d8a68557941
children
line wrap: on
line source

local st = require"util.stanza";
local jid_split = require "util.jid".split;
local jid_bare = require "util.jid".bare;
local is_contact_subscribed = require "core.rostermanager".is_contact_subscribed;
local throttle = require "util.throttle";
local gettime = require "socket".gettime;

local max = module:get_option_number("unsolicited_messages_per_minute", 10);
local s2s_max = module:get_option_number("unsolicited_s2s_messages_per_minute");
local multiplier = module:get_option_number("throttle_unsolicited_burst", 1);

function check_subscribed(event)
	local stanza, origin = event.stanza, event.origin;
	local log = origin.log or module._log;
	if stanza.attr.type == "error" then return end

	local to_orig = stanza.attr.to;
	if to_orig == nil or to_orig == origin.full_jid then return end -- to self

	local to_bare = jid_bare(to_orig);
	local from_jid = jid_bare(stanza.attr.from);
	if to_bare == from_jid then return end -- to own resource

	-- Check if it's a message to a joined room
	local rooms = origin.rooms_joined;
	if rooms and rooms[to_bare] then
		return
	end

	-- Retrieve or create throttle object
	local lim = origin.throttle_unsolicited;
	if not lim then
		lim = throttle.create(max * multiplier, 60 * multiplier);
		origin.throttle_unsolicited = lim;
	end

	local to_user, to_host = jid_split(to_orig);
	if to_user and not is_contact_subscribed(to_user, to_host, from_jid) then
		log("debug", "[unsolicited] %s is not subscribed to %s@%s", from_jid, to_user, to_host);
		if not lim:poll(1) then
			log("warn", "[unsolicited] Sent too many messages to non-contacts, bouncing message");
			origin.firewall_mark_unsolicited = gettime();
			origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
			return true;
		end
	end
end

module:hook("pre-message/bare", check_subscribed, 200);
module:hook("pre-message/full", check_subscribed, 200);

local full_sessions = prosody.full_sessions;

-- Rooms and throttle creation will differ for s2s
function check_subscribed_s2s(event)
	local stanza, origin = event.stanza, event.origin;
	local log = origin.log or module._log;

	if origin.type ~= "s2sin" then return end

	local to_orig = stanza.attr.to;
	local from_orig = stanza.attr.from;
	local from_bare = jid_bare(from_orig);

	local target = full_sessions[to_orig];
	if target then
		local rooms = target.rooms_joined;
		if rooms and rooms[from_bare] then
			log("debug", "Message to joined room, no limit");
			return
		end
	end

	-- Retrieve or create throttle object
	local lim = origin.throttle_unsolicited;
	if not lim then
		log("debug", "New s2s throttle");
		lim = throttle.create(s2s_max * multiplier, 60 * multiplier);
		origin.throttle_unsolicited = lim;
	end

	return check_subscribed(event);
end

if s2s_max then
	module:hook("message/bare", check_subscribed_s2s, 200);
	module:hook("message/full", check_subscribed_s2s, 200);
end

module:depends("track_muc_joins");