mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-20 16:15:59 +01:00
mod_pres_counter prevents subscription flood (thanks to Ahmed Omar and Alexey Shchepin)(EJAB-1388)
This commit is contained in:
parent
0359e345b0
commit
792512459d
@ -80,6 +80,7 @@
|
||||
\newcommand{\modoffline}{\module{mod\_offline}}
|
||||
\newcommand{\modofflineodbc}{\module{mod\_offline\_odbc}}
|
||||
\newcommand{\modping}{\module{mod\_ping}}
|
||||
\newcommand{\modprescounter}{\module{mod\_pres\_counter}}
|
||||
\newcommand{\modprivacy}{\module{mod\_privacy}}
|
||||
\newcommand{\modprivacyodbc}{\module{mod\_privacy\_odbc}}
|
||||
\newcommand{\modprivate}{\module{mod\_private}}
|
||||
@ -2531,6 +2532,7 @@ The following table lists all modules included in \ejabberd{}.
|
||||
\hline \ahrefloc{modoffline}{\modoffline{}} & Offline message storage (\xepref{0160}) & \\
|
||||
\hline \ahrefloc{modoffline}{\modofflineodbc{}} & Offline message storage (\xepref{0160}) & supported DB (*) \\
|
||||
\hline \ahrefloc{modping}{\modping{}} & XMPP Ping and periodic keepalives (\xepref{0199}) & \\
|
||||
\hline \ahrefloc{modprescounter}{\modprivacy{}} & Detect presence subscription flood & \\
|
||||
\hline \ahrefloc{modprivacy}{\modprivacy{}} & Blocking Communication (XMPP IM) & \\
|
||||
\hline \ahrefloc{modprivacy}{\modprivacyodbc{}} & Blocking Communication (XMPP IM) & supported DB (*) \\
|
||||
\hline \ahrefloc{modprivate}{\modprivate{}} & Private XML Storage (\xepref{0049}) & \\
|
||||
@ -3555,6 +3557,39 @@ and if a client does not answer to the ping in less than 32 seconds, its connect
|
||||
]}.
|
||||
\end{verbatim}
|
||||
|
||||
\makesubsection{modprescounter}{\modprescounter{}}
|
||||
\ind{modules!\modprescounter{}}
|
||||
|
||||
This module detects flood/spam in presence subscription stanza traffic.
|
||||
If a user sends or receives more of those stanzas in a time interval,
|
||||
the exceeding stanzas are silently dropped, and warning is logged.
|
||||
|
||||
Configuration options:
|
||||
\begin{description}
|
||||
\titem{\{count, StanzaNumber\}}\ind{options!count}
|
||||
The number of subscription presence stanzas
|
||||
(subscribe, unsubscribe, subscribed, unsubscribed)
|
||||
allowed for any direction (input or output)
|
||||
per time interval.
|
||||
Please note that two users subscribing to each other usually generate
|
||||
4 stanzas, so the recommended value is 4 or more.
|
||||
The default value is: 5.
|
||||
\titem{\{interval, Seconds\}}\ind{options!interval}
|
||||
The time interval defined in seconds.
|
||||
The default value is 60.
|
||||
\end{description}
|
||||
|
||||
This example enables the module, and allows up to 5 presence subscription stanzas
|
||||
to be sent or received by the users in 60 seconds:
|
||||
\begin{verbatim}
|
||||
{modules,
|
||||
[
|
||||
...
|
||||
{mod_pres_counter, [{count, 5}, {interval, 60}]},
|
||||
...
|
||||
]}.
|
||||
\end{verbatim}
|
||||
|
||||
\makesubsection{modprivacy}{\modprivacy{}}
|
||||
\ind{modules!\modprivacy{}}\ind{Blocking Communication}\ind{Privacy Rules}\ind{protocols!RFC 3921: XMPP IM}
|
||||
|
||||
|
@ -511,6 +511,7 @@
|
||||
%%{mod_muc_log,[]},
|
||||
{mod_offline, [{access_max_user_messages, max_user_offline_messages}]},
|
||||
{mod_ping, []},
|
||||
%%{mod_pres_counter,[{count, 5}, {interval, 60}]},
|
||||
{mod_privacy, []},
|
||||
{mod_private, []},
|
||||
%%{mod_proxy65,[]},
|
||||
|
134
src/mod_pres_counter.erl
Normal file
134
src/mod_pres_counter.erl
Normal file
@ -0,0 +1,134 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_pres_counter.erl
|
||||
%%% Author : Ahmed Omar
|
||||
%%% Purpose : Presence subscription flood prevention
|
||||
%%% Created : 23 Sep 2010 by Ahmed Omar
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2010 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).
|
Loading…
Reference in New Issue
Block a user