25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-22 16:20:52 +01:00
xmpp.chapril.org-ejabberd/src/mod_pres_counter.erl
2012-02-23 17:52:34 +02:00

135 lines
3.6 KiB
Erlang

%%%----------------------------------------------------------------------
%%% File : mod_pres_counter.erl
%%% Author : Ahmed Omar
%%% Purpose : Presence subscription flood prevention
%%% Created : 23 Sep 2010 by Ahmed Omar
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------
-module(mod_pres_counter).
-behavior(gen_mod).
-export([start/2,
stop/1,
check_packet/6]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-record(pres_counter, {dir, start, count, logged = false}).
start(Host, _Opts) ->
ejabberd_hooks:add(privacy_check_packet, Host,
?MODULE, check_packet, 25),
ok.
stop(Host) ->
ejabberd_hooks:delete(privacy_check_packet, Host,
?MODULE, check_packet, 25),
ok.
check_packet(_, _User, Server,
_PrivacyList,
{From, To, {xmlelement, Name, Attrs, _}},
Dir) ->
case Name of
"presence" ->
IsSubscription =
case xml:get_attr_s("type", Attrs) of
"subscribe" -> true;
"subscribed" -> true;
"unsubscribe" -> true;
"unsubscribed" -> true;
_ -> false
end,
if
IsSubscription ->
JID = case Dir of
in -> To;
out -> From
end,
update(Server, JID, Dir);
true ->
allow
end;
_ ->
allow
end.
update(Server, JID, Dir) ->
%% get options
StormCount = gen_mod:get_module_opt(Server, ?MODULE, count, 5),
TimeInterval = gen_mod:get_module_opt(Server, ?MODULE, interval, 60),
{MegaSecs, Secs, _MicroSecs} = now(),
TimeStamp = MegaSecs * 1000000 + Secs,
case read(Dir) of
undefined ->
write(Dir, #pres_counter{dir = Dir,
start = TimeStamp,
count = 1}),
allow;
#pres_counter{start = TimeStart, count = Count, logged = Logged} = R ->
%% record for this key exists, check if we're
%% within TimeInterval seconds, and whether the StormCount is
%% high enough. or else just increment the count.
if
TimeStamp - TimeStart > TimeInterval ->
write(Dir, R#pres_counter{
start = TimeStamp,
count = 1}),
allow;
(Count =:= StormCount) and Logged ->
{stop, deny};
Count =:= StormCount ->
write(Dir, R#pres_counter{logged = true}),
case Dir of
in ->
?WARNING_MSG(
"User ~s is being flooded, "
"ignoring received presence subscriptions",
[jlib:jid_to_string(JID)]);
out ->
IP = ejabberd_sm:get_user_ip(
JID#jid.luser,
JID#jid.lserver,
JID#jid.lresource),
?WARNING_MSG(
"Flooder detected: ~s, on IP: ~s "
"ignoring sent presence subscriptions~n",
[jlib:jid_to_string(JID),
jlib:ip_to_list(IP)])
end,
{stop, deny};
true ->
write(Dir, R#pres_counter{
start = TimeStamp,
count = Count + 1}),
allow
end
end.
read(K)->
get({pres_counter, K}).
write(K, V)->
put({pres_counter, K}, V).