25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-12-24 17:29:28 +01:00

Add module to dump c2s connection

This commit is contained in:
Christophe Romain 2010-07-13 18:28:07 +02:00
parent 4862251f34
commit cc0503fd5e
7 changed files with 289 additions and 71 deletions

View File

@ -63,42 +63,7 @@
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("mod_privacy.hrl").
-define(SETS, gb_sets).
-define(DICT, dict).
%% pres_a contains all the presence available send (either through roster mechanism or directed).
%% Directed presence unavailable remove user from pres_a.
-record(state, {socket,
sockmod,
socket_monitor,
xml_socket,
streamid,
sasl_state,
access,
shaper,
zlib = false,
tls = false,
tls_required = false,
tls_enabled = false,
tls_options = [],
authenticated = false,
jid,
user = "", server = ?MYNAME, resource = "",
sid,
pres_t = ?SETS:new(),
pres_f = ?SETS:new(),
pres_a = ?SETS:new(),
pres_i = ?SETS:new(),
pres_last, pres_pri,
pres_timestamp,
pres_invis = false,
privacy_list = #userlist{},
conn = unknown,
auth_module = unknown,
ip,
fsm_limit_opts,
lang}).
-include("ejabberd_c2s.hrl").
%-define(DBGFSM, true).
@ -224,21 +189,21 @@ init([{SockMod, Socket}, Opts, FSMLimitOpts]) ->
Socket
end,
SocketMonitor = SockMod:monitor(Socket1),
{ok, wait_for_stream, #state{socket = Socket1,
sockmod = SockMod,
socket_monitor = SocketMonitor,
xml_socket = XMLSocket,
zlib = Zlib,
tls = TLS,
tls_required = StartTLSRequired,
tls_enabled = TLSEnabled,
tls_options = TLSOpts,
streamid = new_id(),
access = Access,
shaper = Shaper,
ip = IP,
fsm_limit_opts = FSMLimitOpts},
?C2S_OPEN_TIMEOUT}
StateData = #state{socket = Socket1,
sockmod = SockMod,
socket_monitor = SocketMonitor,
xml_socket = XMLSocket,
zlib = Zlib,
tls = TLS,
tls_required = StartTLSRequired,
tls_enabled = TLSEnabled,
tls_options = TLSOpts,
streamid = new_id(),
access = Access,
shaper = Shaper,
ip = IP,
fsm_limit_opts = FSMLimitOpts},
{ok, wait_for_stream, StateData, ?C2S_OPEN_TIMEOUT}
end;
init([StateName, StateData, _FSMLimitOpts]) ->
MRef = (StateData#state.sockmod):monitor(StateData#state.socket),
@ -534,8 +499,7 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
privacy_get_user_list, StateData#state.server,
#userlist{},
[U, StateData#state.server]),
NewStateData =
StateData#state{
NewStateData = StateData#state{
user = U,
resource = R,
jid = JID,
@ -545,7 +509,11 @@ wait_for_auth({xmlstreamelement, El}, StateData) ->
pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList},
maybe_migrate(session_established, NewStateData);
DebugFlag = ejabberd_hooks:run_fold(c2s_debug_start_hook,
NewStateData#state.server,
false,
[self(), NewStateData]),
maybe_migrate(session_established, NewStateData#state{debug=DebugFlag});
_ ->
?INFO_MSG(
"(~w) Failed legacy authentication for ~s",
@ -910,7 +878,11 @@ wait_for_session({xmlstreamelement, El}, StateData) ->
pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList},
maybe_migrate(session_established, NewStateData);
DebugFlag = ejabberd_hooks:run_fold(c2s_debug_start_hook,
NewStateData#state.server,
false,
[self(), NewStateData]),
maybe_migrate(session_established, NewStateData#state{debug=DebugFlag});
_ ->
ejabberd_hooks:run(forbidden_session_hook,
StateData#state.server, [JID]),
@ -1026,7 +998,7 @@ session_established2(El, StateData) ->
ejabberd_hooks:run(
user_send_packet,
Server,
[FromJID, ToJID, PresenceEl]),
[StateData#state.debug, FromJID, ToJID, PresenceEl]),
case ToJID of
#jid{user = User,
server = Server,
@ -1042,13 +1014,17 @@ session_established2(El, StateData) ->
"iq" ->
case jlib:iq_query_info(NewEl) of
#iq{xmlns = ?NS_PRIVACY} = IQ ->
ejabberd_hooks:run(
user_send_packet,
Server,
[StateData#state.debug, FromJID, ToJID, NewEl]),
process_privacy_iq(
FromJID, ToJID, IQ, StateData);
_ ->
ejabberd_hooks:run(
user_send_packet,
Server,
[FromJID, ToJID, NewEl]),
[StateData#state.debug, FromJID, ToJID, NewEl]),
ejabberd_router:route(
FromJID, ToJID, NewEl),
StateData
@ -1056,7 +1032,7 @@ session_established2(El, StateData) ->
"message" ->
ejabberd_hooks:run(user_send_packet,
Server,
[FromJID, ToJID, NewEl]),
[StateData#state.debug, FromJID, ToJID, NewEl]),
check_privacy_route(FromJID, StateData, FromJID,
ToJID, NewEl),
StateData;
@ -1361,7 +1337,7 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
send_element(StateData, FixedPacket),
ejabberd_hooks:run(user_receive_packet,
StateData#state.server,
[StateData#state.jid, From, To, FixedPacket]),
[StateData#state.debug, StateData#state.jid, From, To, FixedPacket]),
ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]),
fsm_next_state(StateName, NewState);
true ->
@ -1419,13 +1395,16 @@ print_state(State = #state{pres_t = T, pres_f = F, pres_a = A, pres_i = I}) ->
pres_a = {pres_a, ?SETS:size(A)},
pres_i = {pres_i, ?SETS:size(I)}
}.
%%----------------------------------------------------------------------
%% Func: terminate/3
%% Purpose: Shutdown the fsm
%% Returns: any
%%----------------------------------------------------------------------
terminate({migrated, ClonePid}, StateName, StateData) ->
ejabberd_hooks:run(c2s_debug_stop_hook,
StateData#state.server,
[self(), StateData]),
if StateName == session_established ->
?INFO_MSG("(~w) Migrating ~s to ~p on node ~p",
[StateData#state.socket,
@ -2149,7 +2128,7 @@ resend_offline_messages(#state{user = User,
send_element(StateData, FixedPacket),
ejabberd_hooks:run(user_receive_packet,
StateData#state.server,
[StateData#state.jid,
[StateData#state.debug, StateData#state.jid,
From, To, FixedPacket]);
true ->
ok

62
src/ejabberd_c2s.hrl Normal file
View File

@ -0,0 +1,62 @@
%%%----------------------------------------------------------------------
%%%
%%% 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
%%%
%%%----------------------------------------------------------------------
-ifndef(mod_privacy_hrl).
-include("mod_privacy.hrl").
-endif.
-define(SETS, gb_sets).
-define(DICT, dict).
%% pres_a contains all the presence available send (either through roster mechanism or directed).
%% Directed presence unavailable remove user from pres_a.
-record(state, {socket,
sockmod,
socket_monitor,
xml_socket,
streamid,
sasl_state,
access,
shaper,
zlib = false,
tls = false,
tls_required = false,
tls_enabled = false,
tls_options = [],
authenticated = false,
jid,
user = "", server = ?MYNAME, resource = "",
sid,
pres_t = ?SETS:new(),
pres_f = ?SETS:new(),
pres_a = ?SETS:new(),
pres_i = ?SETS:new(),
pres_last, pres_pri,
pres_timestamp,
pres_invis = false,
privacy_list = #userlist{},
conn = unknown,
auth_module = unknown,
ip,
fsm_limit_opts,
lang,
debug=false}).

175
src/mod_c2s_debug.erl Normal file
View File

@ -0,0 +1,175 @@
%% Usage:
%% In config file:
%% {mod_c2s_debug, [{logdir, "/tmp/xmpplogs"}]},
%% From Erlang shell:
%% mod_c2s_debug:start("localhost", []).
%% mod_c2s_debug:stop("localhost").
%%
%% Warning: Only one module for the debug handler can be defined.
-module(mod_c2s_debug).
-author('mremond@process-one.net').
-behaviour(gen_mod).
-behavior(gen_server).
-export([start/2, start_link/2, stop/1,
debug_start/3, debug_stop/2, log_packet/4, log_packet/5]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("ejabberd_c2s.hrl").
-record(modstate, {host, logdir, pid, iodevice}).
-record(clientinfo, {pid, jid, auth_module, ip}).
-define(SUPERVISOR, ejabberd_sup).
-define(PROCNAME, c2s_debug).
%%====================================================================
%% gen_mod callbacks
%%====================================================================
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
Spec = {Proc, {?MODULE, start_link, [Host, Opts]},
transient, 2000, worker, [?MODULE]},
supervisor:start_child(?SUPERVISOR, Spec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:delete_child(?SUPERVISOR, Proc).
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
%%====================================================================
%% Hooks
%%====================================================================
%% Debug handled by another module... Do nothing:
debug_start(_Status, Pid, C2SState) ->
Host = C2SState#state.server,
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
JID = jlib:jid_to_string(C2SState#state.jid),
AuthModule = C2SState#state.auth_module,
IP = C2SState#state.ip,
ClientInfo = #clientinfo{pid = Pid, jid = JID, auth_module = AuthModule, ip = IP},
gen_server:call(Proc, {debug_start, ClientInfo}).
debug_stop(Pid, C2SState) ->
Host = C2SState#state.server,
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:cast(Proc, {debug_stop, Pid}).
log_packet(false, _FromJID, _ToJID, _Packet) ->
ok;
log_packet(true, FromJID, ToJID, Packet) ->
Host = FromJID#jid.lserver,
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:cast(Proc, {addlog, {"Send", FromJID, ToJID, Packet}}).
log_packet(false, _JID, _FromJID, _ToJID, _Packet) ->
ok;
log_packet(true, JID, FromJID, ToJID, Packet) ->
Host = JID#jid.lserver,
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:cast(Proc, {addlog, {"Receive", FromJID, ToJID, Packet}}).
%%====================================================================
%% gen_server callbacks
%%====================================================================
init([Host, Opts]) ->
?INFO_MSG("Starting c2s debug module for: ~p", [Host]),
MyHost = gen_mod:get_opt_host(Host, Opts, "c2s_debug.@HOST@"),
ejabberd_hooks:add(c2s_debug_start_hook, Host,
?MODULE, debug_start, 50),
ejabberd_hooks:add(c2s_debug_stop_hook, Host,
?MODULE, debug_stop, 50),
ejabberd_hooks:add(user_send_packet, Host, ?MODULE, log_packet, 50),
ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, log_packet, 50),
Logdir = gen_mod:get_opt(logdir, Opts, "/tmp/xmpplogs/"),
make_dir_rec(Logdir),
{ok, #modstate{host = MyHost, logdir = Logdir}}.
terminate(_Reason, #modstate{host = Host}) ->
?INFO_MSG("Stopping c2s debug module for: ~s", [Host]),
ejabberd_hooks:delete(c2s_debug_start_hook, Host,
?MODULE, debug_start, 50),
ejabberd_hooks:delete(c2s_debug_stop_hook, Host,
?MODULE, debug_stop, 50),
ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, log_packet, 50).
handle_call({debug_start, ClientInfo}, _From, #modstate{pid=undefined} = State) ->
Pid = ClientInfo#clientinfo.pid,
?INFO_MSG("Debug started for PID:~p", [Pid]),
JID = ClientInfo#clientinfo.jid,
AuthModule = ClientInfo#clientinfo.auth_module,
IP = ClientInfo#clientinfo.ip,
{ok, IOD} = file:open(filename(State#modstate.logdir), [append]),
Line = io_lib:format("~s - Session open~nJID: ~s~nAuthModule: ~p~nIP: ~p~n",
[timestamp(), JID, AuthModule, IP]),
file:write(IOD, Line),
{reply, true, State#modstate{pid = Pid, iodevice = IOD}};
handle_call({debug_start, _ClientInfo}, _From, State) ->
{reply, false, State};
handle_call(stop, _From, State) ->
{stop, normal, ok, State};
handle_call(_Req, _From, State) ->
{reply, {error, badarg}, State}.
handle_cast({addlog, _}, #modstate{iodevice=undefined} = State) ->
{noreply, State};
handle_cast({addlog, {Direction, FromJID, ToJID, Packet}}, #modstate{iodevice=IOD} = State) ->
LogEntry = io_lib:format("=====~n~s - ~s~nFrom: ~s~nTo: ~s~n~s~n", [timestamp(), Direction,
jlib:jid_to_string(FromJID),
jlib:jid_to_string(ToJID),
xml:element_to_string(Packet)]),
file:write(IOD, LogEntry),
{noreply, State};
handle_cast({debug_stop, Pid}, #modstate{pid=Pid, iodevice=IOD} = State) ->
Line = io_lib:format("=====~n~s - Session closed~n",
[timestamp()]),
file:write(IOD, Line),
file:close(IOD),
{noreply, State#modstate{pid = undefined, iodevice=undefined}};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% Generate filename
filename(LogDir) ->
Filename = lists:flatten(timestamp()) ++ "-c2s.log",
filename:join([LogDir, Filename]).
%% Generate timestamp
timestamp() ->
{Y,Mo,D} = erlang:date(),
{H,Mi,S} = erlang:time(),
io_lib:format("~4.4.0w~2.2.0w~2.2.0w-~2.2.0w~2.2.0w~2.2.0w", [Y,Mo,D,H,Mi,S]).
%% Create dir recusively
make_dir_rec(Dir) ->
case file:read_file_info(Dir) of
{ok, _} ->
ok;
{error, enoent} ->
DirS = filename:split(Dir),
DirR = lists:sublist(DirS, length(DirS)-1),
make_dir_rec(filename:join(DirR)),
file:make_dir(Dir)
end.

View File

@ -53,7 +53,7 @@
]).
%% hook handlers
-export([user_send_packet/3]).
-export([user_send_packet/4]).
-include("ejabberd.hrl").
-include("jlib.hrl").
@ -141,7 +141,8 @@ read_caps([], Result) ->
%%====================================================================
%% Hooks
%%====================================================================
user_send_packet(#jid{luser = User, lserver = Server} = From,
user_send_packet(_DebugFlag,
#jid{luser = User, lserver = Server} = From,
#jid{luser = User, lserver = Server, lresource = ""},
{xmlelement, "presence", Attrs, Els}) ->
Type = xml:get_attr_s("type", Attrs),
@ -155,7 +156,7 @@ user_send_packet(#jid{luser = User, lserver = Server} = From,
true ->
ok
end;
user_send_packet(_From, _To, _Packet) ->
user_send_packet(_DebugFlag, _From, _To, _Packet) ->
ok.
caps_stream_features(Acc, MyHost) ->

View File

@ -51,7 +51,7 @@
handle_info/2, code_change/3]).
%% Hook callbacks
-export([iq_ping/3, user_online/3, user_offline/3, user_send/3]).
-export([iq_ping/3, user_online/3, user_offline/3, user_send/4]).
-record(state, {host = "",
send_pings = ?DEFAULT_SEND_PINGS,
@ -193,7 +193,7 @@ user_online(_SID, JID, _Info) ->
user_offline(_SID, JID, _Info) ->
stop_ping(JID#jid.lserver, JID).
user_send(JID, _From, _Packet) ->
user_send(_DebugFlag, JID, _From, _Packet) ->
start_ping(JID#jid.lserver, JID).
%%====================================================================

View File

@ -19,6 +19,8 @@
%%%
%%%----------------------------------------------------------------------
-define(mod_privacy_hrl, true).
-record(privacy, {us,
default = none,
lists = []}).

View File

@ -31,8 +31,8 @@
-export([start/2,
stop/1,
log_user_send/3,
log_user_receive/4]).
log_user_send/4,
log_user_receive/5]).
-include("ejabberd.hrl").
-include("jlib.hrl").
@ -51,10 +51,10 @@ stop(Host) ->
?MODULE, log_user_receive, 50),
ok.
log_user_send(From, To, Packet) ->
log_user_send(_DebugFlag, From, To, Packet) ->
log_packet(From, To, Packet, From#jid.lserver).
log_user_receive(_JID, From, To, Packet) ->
log_user_receive(_DebugFlag, _JID, From, To, Packet) ->
log_packet(From, To, Packet, To#jid.lserver).
@ -74,4 +74,3 @@ log_packet(From, To, {xmlelement, Name, Attrs, Els}, Host) ->
luser = "", lserver = Logger, lresource = ""},
{xmlelement, "route", [], [FixedPacket]})
end, Loggers).