25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-24 16:23:40 +01:00

Add more tests for C2S

This commit is contained in:
Evgeniy Khramtsov 2016-09-20 14:04:07 +03:00
parent 151668ac10
commit a4ec064455
4 changed files with 326 additions and 84 deletions

View File

@ -323,24 +323,25 @@ get_subscribed(FsmRef) ->
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
try xmpp:decode(#xmlel{name = Name, attrs = Attrs}) of try xmpp:decode(#xmlel{name = Name, attrs = Attrs}) of
#stream_start{xmlns = NS_CLIENT, stream_xmlns = NS_STREAM, lang = Lang} #stream_start{xmlns = NS_CLIENT, stream_xmlns = NS_STREAM,
version = Version, lang = Lang}
when NS_CLIENT /= ?NS_CLIENT; NS_STREAM /= ?NS_STREAM -> when NS_CLIENT /= ?NS_CLIENT; NS_STREAM /= ?NS_STREAM ->
send_header(StateData, ?MYNAME, <<"">>, Lang), send_header(StateData, ?MYNAME, Version, Lang),
send_element(StateData, xmpp:serr_invalid_namespace()), send_element(StateData, xmpp:serr_invalid_namespace()),
{stop, normal, StateData}; {stop, normal, StateData};
#stream_start{lang = Lang} when byte_size(Lang) > 35 -> #stream_start{lang = Lang, version = Version} when byte_size(Lang) > 35 ->
%% As stated in BCP47, 4.4.1: %% As stated in BCP47, 4.4.1:
%% Protocols or specifications that specify limited buffer sizes for %% Protocols or specifications that specify limited buffer sizes for
%% language tags MUST allow for language tags of at least 35 characters. %% language tags MUST allow for language tags of at least 35 characters.
%% Do not store long language tag to avoid possible DoS/flood attacks %% Do not store long language tag to avoid possible DoS/flood attacks
send_header(StateData, ?MYNAME, <<"">>, ?MYLANG), send_header(StateData, ?MYNAME, Version, ?MYLANG),
Txt = <<"Too long value of 'xml:lang' attribute">>, Txt = <<"Too long value of 'xml:lang' attribute">>,
send_element(StateData, send_element(StateData,
xmpp:serr_policy_violation(Txt, ?MYLANG)), xmpp:serr_policy_violation(Txt, ?MYLANG)),
{stop, normal, StateData}; {stop, normal, StateData};
#stream_start{to = undefined, lang = Lang} -> #stream_start{to = undefined, lang = Lang, version = Version} ->
Txt = <<"Missing 'to' attribute">>, Txt = <<"Missing 'to' attribute">>,
send_header(StateData, ?MYNAME, <<"">>, Lang), send_header(StateData, ?MYNAME, Version, Lang),
send_element(StateData, send_element(StateData,
xmpp:serr_improper_addressing(Txt, Lang)), xmpp:serr_improper_addressing(Txt, Lang)),
{stop, normal, StateData}; {stop, normal, StateData};
@ -463,7 +464,7 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
end end
end; end;
_ -> _ ->
send_header(StateData, Server, <<"">>, ?MYLANG), send_header(StateData, Server, StreamVersion, ?MYLANG),
if not StateData#state.tls_enabled and if not StateData#state.tls_enabled and
StateData#state.tls_required -> StateData#state.tls_required ->
send_element( send_element(
@ -492,7 +493,7 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
end end
catch _:{xmpp_codec, Why} -> catch _:{xmpp_codec, Why} ->
Txt = xmpp:format_error(Why), Txt = xmpp:format_error(Why),
send_header(StateData, ?MYNAME, <<"">>, ?MYLANG), send_header(StateData, ?MYNAME, <<"1.0">>, ?MYLANG),
send_element(StateData, xmpp:serr_not_well_formed(Txt, ?MYLANG)), send_element(StateData, xmpp:serr_not_well_formed(Txt, ?MYLANG)),
{stop, normal, StateData} {stop, normal, StateData}
end; end;
@ -517,13 +518,8 @@ wait_for_auth({xmlstreamelement, #xmlel{} = El}, StateData) ->
decode_element(El, wait_for_auth, StateData); decode_element(El, wait_for_auth, StateData);
wait_for_auth(Pkt, StateData) when ?IS_STREAM_MGMT_PACKET(Pkt) -> wait_for_auth(Pkt, StateData) when ?IS_STREAM_MGMT_PACKET(Pkt) ->
fsm_next_state(wait_for_auth, dispatch_stream_mgmt(Pkt, StateData)); fsm_next_state(wait_for_auth, dispatch_stream_mgmt(Pkt, StateData));
wait_for_auth(#iq{type = get, wait_for_auth(#iq{type = get, sub_els = [#legacy_auth{}]} = IQ, StateData) ->
sub_els = [#legacy_auth{username = U}]} = IQ, StateData) -> Auth = #legacy_auth{username = <<>>, password = <<>>, resource = <<>>},
Username = case U of
undefined -> <<"">>;
_ -> U
end,
Auth = #legacy_auth{username = Username, password = <<>>, resource = <<>>},
Res = case ejabberd_auth:plain_password_required(StateData#state.server) of Res = case ejabberd_auth:plain_password_required(StateData#state.server) of
false -> false ->
xmpp:make_iq_result(IQ, Auth#legacy_auth{digest = <<>>}); xmpp:make_iq_result(IQ, Auth#legacy_auth{digest = <<>>});

View File

@ -19,8 +19,9 @@
wait_for_master/1, wait_for_slave/1, wait_for_master/1, wait_for_slave/1,
make_iq_result/1, start_event_relay/0, make_iq_result/1, start_event_relay/0,
stop_event_relay/1, put_event/2, get_event/1, stop_event_relay/1, put_event/2, get_event/1,
bind/1, auth/1, open_session/1, zlib/1, starttls/1, bind/1, auth/1, auth/2, open_session/1, open_session/2,
close_socket/1]). zlib/1, starttls/1, close_socket/1, init_stream/1,
auth_legacy/2, auth_legacy/3]).
-include("suite.hrl"). -include("suite.hrl").
@ -154,15 +155,26 @@ init_per_testcase(stop_ejabberd, Config) ->
open_session(bind(auth(connect(Config)))); open_session(bind(auth(connect(Config))));
init_per_testcase(TestCase, OrigConfig) -> init_per_testcase(TestCase, OrigConfig) ->
subscribe_to_events(OrigConfig), subscribe_to_events(OrigConfig),
TestGroup = proplists:get_value(
name, ?config(tc_group_properties, OrigConfig)),
Server = ?config(server, OrigConfig), Server = ?config(server, OrigConfig),
Resource = ?config(resource, OrigConfig), Resource = case TestGroup of
generic ->
randoms:get_string();
legacy_auth ->
randoms:get_string();
_ ->
?config(resource, OrigConfig)
end,
MasterResource = ?config(master_resource, OrigConfig), MasterResource = ?config(master_resource, OrigConfig),
SlaveResource = ?config(slave_resource, OrigConfig), SlaveResource = ?config(slave_resource, OrigConfig),
Test = atom_to_list(TestCase), Test = atom_to_list(TestCase),
IsMaster = lists:suffix("_master", Test), IsMaster = lists:suffix("_master", Test),
IsSlave = lists:suffix("_slave", Test), IsSlave = lists:suffix("_slave", Test),
IsCarbons = lists:prefix("carbons_", Test), IsCarbons = lists:prefix("carbons_", Test),
User = if IsMaster or IsCarbons -> <<"test_master!#$%^*()`~+-;_=[]{}|\\">>; IsReplaced = lists:prefix("replaced_", Test),
User = if IsReplaced -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>;
IsMaster or IsCarbons -> <<"test_master!#$%^*()`~+-;_=[]{}|\\">>;
IsSlave -> <<"test_slave!#$%^*()`~+-;_=[]{}|\\">>; IsSlave -> <<"test_slave!#$%^*()`~+-;_=[]{}|\\">>;
true -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">> true -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>
end, end,
@ -172,11 +184,15 @@ init_per_testcase(TestCase, OrigConfig) ->
end, end,
Slave = if IsCarbons -> Slave = if IsCarbons ->
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, SlaveResource); jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, SlaveResource);
IsReplaced ->
jid:make(User, Server, Resource);
true -> true ->
jid:make(<<"test_slave!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource) jid:make(<<"test_slave!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource)
end, end,
Master = if IsCarbons -> Master = if IsCarbons ->
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, MasterResource); jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, MasterResource);
IsReplaced ->
jid:make(User, Server, Resource);
true -> true ->
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource) jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource)
end, end,
@ -184,29 +200,35 @@ init_per_testcase(TestCase, OrigConfig) ->
set_opt(slave, Slave, set_opt(slave, Slave,
set_opt(master, Master, set_opt(master, Master,
set_opt(resource, MyResource, OrigConfig)))), set_opt(resource, MyResource, OrigConfig)))),
case TestCase of case Test of
test_connect -> "test_connect" ++ _ ->
Config; Config;
test_auth -> "test_legacy_auth" ++ _ ->
init_stream(set_opt(stream_version, <<"">>, Config));
"test_auth" ++ _ ->
connect(Config); connect(Config);
test_starttls -> "test_starttls" ++ _ ->
connect(Config); connect(Config);
test_zlib -> "test_zlib" ->
connect(Config); connect(Config);
test_register -> "test_register" ->
connect(Config); connect(Config);
auth_md5 -> "auth_md5" ->
connect(Config); connect(Config);
auth_plain -> "auth_plain" ->
connect(Config); connect(Config);
test_bind -> "unauthenticated_" ++ _ ->
connect(Config);
"test_bind" ->
auth(connect(Config)); auth(connect(Config));
sm_resume -> "sm_resume" ->
auth(connect(Config)); auth(connect(Config));
sm_resume_failed -> "sm_resume_failed" ->
auth(connect(Config)); auth(connect(Config));
test_open_session -> "test_open_session" ->
bind(auth(connect(Config))); bind(auth(connect(Config)));
"replaced" ++ _ ->
auth(connect(Config));
_ when IsMaster or IsSlave -> _ when IsMaster or IsSlave ->
Password = ?config(password, Config), Password = ?config(password, Config),
ejabberd_auth:try_register(User, Server, Password), ejabberd_auth:try_register(User, Server, Password),
@ -218,30 +240,56 @@ init_per_testcase(TestCase, OrigConfig) ->
end_per_testcase(_TestCase, _Config) -> end_per_testcase(_TestCase, _Config) ->
ok. ok.
legacy_auth_tests() ->
{legacy_auth, [parallel],
[test_legacy_auth,
test_legacy_auth_digest,
test_legacy_auth_no_resource,
test_legacy_auth_bad_jid,
test_legacy_auth_fail]}.
no_db_tests() -> no_db_tests() ->
[{generic, [sequence], [{generic, [parallel],
[test_connect, [test_connect_bad_xml,
test_connect_unknown_ns,
test_connect_bad_ns_client,
test_connect_bad_ns_stream,
test_connect_bad_lang,
test_connect_bad_to,
test_connect_missing_to,
test_connect,
unauthenticated_iq,
unauthenticated_stanza,
test_starttls, test_starttls,
test_zlib, test_zlib,
test_auth, test_auth,
test_auth_fail,
test_bind, test_bind,
test_open_session, test_open_session,
presence, codec_failure,
unsupported_query,
bad_nonza,
invalid_from,
ping, ping,
version, version,
time, time,
stats, stats,
sm,
sm_resume,
sm_resume_failed,
disco]}, disco]},
{presence, [sequence], [presence]},
{sm, [sequence],
[sm,
sm_resume,
sm_resume_failed]},
{test_proxy65, [parallel], {test_proxy65, [parallel],
[proxy65_master, proxy65_slave]}]. [proxy65_master, proxy65_slave]},
{replaced, [parallel],
[replaced_master, replaced_slave]}].
db_tests(riak) -> db_tests(riak) ->
%% No support for mod_pubsub %% No support for mod_pubsub
[{single_user, [sequence], [{single_user, [sequence],
[test_register, [test_register,
legacy_auth_tests(),
auth_plain, auth_plain,
auth_md5, auth_md5,
presence_broadcast, presence_broadcast,
@ -273,6 +321,7 @@ db_tests(riak) ->
db_tests(DB) when DB == mnesia; DB == redis -> db_tests(DB) when DB == mnesia; DB == redis ->
[{single_user, [sequence], [{single_user, [sequence],
[test_register, [test_register,
legacy_auth_tests(),
auth_plain, auth_plain,
auth_md5, auth_md5,
presence_broadcast, presence_broadcast,
@ -319,6 +368,7 @@ db_tests(_) ->
%% No support for carboncopy %% No support for carboncopy
[{single_user, [sequence], [{single_user, [sequence],
[test_register, [test_register,
legacy_auth_tests(),
auth_plain, auth_plain,
auth_md5, auth_md5,
presence_broadcast, presence_broadcast,
@ -361,12 +411,14 @@ db_tests(_) ->
ldap_tests() -> ldap_tests() ->
[{ldap_tests, [sequence], [{ldap_tests, [sequence],
[test_auth, [test_auth,
test_auth_fail,
vcard_get, vcard_get,
ldap_shared_roster_get]}]. ldap_shared_roster_get]}].
extauth_tests() -> extauth_tests() ->
[{extauth_tests, [sequence], [{extauth_tests, [sequence],
[test_auth, [test_auth,
test_auth_fail,
test_unregister]}]. test_unregister]}].
groups() -> groups() ->
@ -381,15 +433,15 @@ groups() ->
{riak, [sequence], db_tests(riak)}]. {riak, [sequence], db_tests(riak)}].
all() -> all() ->
[{group, ldap}, [%%{group, ldap},
{group, no_db}, {group, no_db},
{group, mnesia}, {group, mnesia},
{group, redis}, %%{group, redis},
{group, mysql}, %%{group, mysql},
{group, pgsql}, %%{group, pgsql},
{group, sqlite}, %%{group, sqlite},
{group, extauth}, %%{group, extauth},
{group, riak}, %%{group, riak},
stop_ejabberd]. stop_ejabberd].
stop_ejabberd(Config) -> stop_ejabberd(Config) ->
@ -398,6 +450,48 @@ stop_ejabberd(Config) ->
?recv1({xmlstreamend, <<"stream:stream">>}), ?recv1({xmlstreamend, <<"stream:stream">>}),
Config. Config.
test_connect_bad_xml(Config) ->
Config0 = init_stream(set_opt(ns_client, <<"'">>, Config)),
?recv1(#stream_error{reason = 'not-well-formed'}),
?recv1({xmlstreamend, <<"stream:stream">>}),
close_socket(Config0).
test_connect_unknown_ns(Config) ->
Config0 = init_stream(set_opt(ns_client, <<"wrong">>, Config)),
?recv1(#stream_error{reason = 'not-well-formed'}),
?recv1({xmlstreamend, <<"stream:stream">>}),
close_socket(Config0).
test_connect_bad_ns_client(Config) ->
Config0 = init_stream(set_opt(ns_client, ?NS_SERVER, Config)),
?recv1(#stream_error{reason = 'invalid-namespace'}),
?recv1({xmlstreamend, <<"stream:stream">>}),
close_socket(Config0).
test_connect_bad_ns_stream(Config) ->
Config0 = init_stream(set_opt(ns_stream, <<"wrong">>, Config)),
?recv1(#stream_error{reason = 'invalid-namespace'}),
?recv1({xmlstreamend, <<"stream:stream">>}),
close_socket(Config0).
test_connect_bad_lang(Config) ->
Config0 = init_stream(set_opt(lang, lists:duplicate(36, $x), Config)),
?recv1(#stream_error{reason = 'policy-violation'}),
?recv1({xmlstreamend, <<"stream:stream">>}),
close_socket(Config0).
test_connect_bad_to(Config) ->
Config0 = init_stream(set_opt(server, <<"wrong.com">>, Config)),
?recv1(#stream_error{reason = 'host-unknown'}),
?recv1({xmlstreamend, <<"stream:stream">>}),
close_socket(Config0).
test_connect_missing_to(Config) ->
Config0 = init_stream(set_opt(server, <<"">>, Config)),
?recv1(#stream_error{reason = 'improper-addressing'}),
?recv1({xmlstreamend, <<"stream:stream">>}),
close_socket(Config0).
test_connect(Config) -> test_connect(Config) ->
disconnect(connect(Config)). disconnect(connect(Config)).
@ -462,6 +556,28 @@ try_unregister(Config) ->
?recv1(#stream_error{reason = conflict}), ?recv1(#stream_error{reason = conflict}),
Config. Config.
unauthenticated_stanza(Config) ->
%% Unauthenticated stanza should be silently dropped.
send(Config, #message{to = server_jid(Config)}),
disconnect(Config).
unauthenticated_iq(Config) ->
#iq{type = error} =
send_recv(Config, #iq{type = get, sub_els = [#disco_info{}]}),
disconnect(Config).
bad_nonza(Config) ->
%% Unsupported and invalid nonza should be silently dropped.
send(Config, #caps{}),
send(Config, #stanza_error{type = wrong}),
disconnect(Config).
invalid_from(Config) ->
send(Config, #message{from = jid:make(randoms:get_string())}),
?recv1(#stream_error{reason = 'invalid-from'}),
?recv1({xmlstreamend, <<"stream:stream">>}),
close_socket(Config).
auth_md5(Config) -> auth_md5(Config) ->
Mechs = ?config(mechs, Config), Mechs = ?config(mechs, Config),
case lists:member(<<"DIGEST-MD5">>, Mechs) of case lists:member(<<"DIGEST-MD5">>, Mechs) of
@ -482,14 +598,37 @@ auth_plain(Config) ->
{skipped, 'PLAIN_not_available'} {skipped, 'PLAIN_not_available'}
end. end.
test_legacy_auth(Config) ->
disconnect(auth_legacy(Config, _Digest = false)).
test_legacy_auth_digest(Config) ->
ServerJID = server_jid(Config),
disconnect(auth_legacy(Config, _Digest = true)).
test_legacy_auth_no_resource(Config0) ->
Config = set_opt(resource, <<"">>, Config0),
disconnect(auth_legacy(Config, _Digest = false, _ShouldFail = true)).
test_legacy_auth_bad_jid(Config0) ->
Config = set_opt(user, <<"@">>, Config0),
disconnect(auth_legacy(Config, _Digest = false, _ShouldFail = true)).
test_legacy_auth_fail(Config0) ->
Config = set_opt(user, <<"wrong">>, Config0),
disconnect(auth_legacy(Config, _Digest = false, _ShouldFail = true)).
test_auth(Config) -> test_auth(Config) ->
disconnect(auth(Config)). disconnect(auth(Config)).
test_auth_fail(Config0) ->
Config = set_opt(user, <<"wrong">>, Config0),
disconnect(auth(Config, _ShouldFail = true)).
test_bind(Config) -> test_bind(Config) ->
disconnect(bind(Config)). disconnect(bind(Config)).
test_open_session(Config) -> test_open_session(Config) ->
disconnect(open_session(Config)). disconnect(open_session(Config, true)).
roster_get(Config) -> roster_get(Config) ->
#iq{type = result, sub_els = [#roster_query{items = []}]} = #iq{type = result, sub_els = [#roster_query{items = []}]} =
@ -519,6 +658,21 @@ roster_ver(Config) ->
sub_els = [#roster_query{ver = Ver2}]}), sub_els = [#roster_query{ver = Ver2}]}),
disconnect(Config). disconnect(Config).
codec_failure(Config) ->
#iq{type = error} = send_recv(Config, #iq{type = wrong}),
disconnect(Config).
unsupported_query(Config) ->
ServerJID = server_jid(Config),
#iq{type = error} = send_recv(Config, #iq{type = get, to = ServerJID}),
#iq{type = error} = send_recv(Config, #iq{type = get, to = ServerJID,
sub_els = [#caps{}]}),
#iq{type = error} = send_recv(Config, #iq{type = get, to = ServerJID,
sub_els = [#roster_query{},
#disco_info{},
#privacy_query{}]}),
disconnect(Config).
presence(Config) -> presence(Config) ->
send(Config, #presence{}), send(Config, #presence{}),
JID = my_jid(Config), JID = my_jid(Config),
@ -604,6 +758,18 @@ disco(Config) ->
end, Items), end, Items),
disconnect(Config). disconnect(Config).
replaced_master(Config0) ->
Config = bind(Config0),
wait_for_slave(Config),
?recv1(#stream_error{reason = conflict}),
?recv1({xmlstreamend, <<"stream:stream">>}),
close_socket(Config).
replaced_slave(Config0) ->
wait_for_master(Config0),
Config = bind(Config0),
disconnect(Config).
sm(Config) -> sm(Config) ->
Server = ?config(server, Config), Server = ?config(server, Config),
ServerJID = jid:make(<<"">>, Server, <<"">>), ServerJID = jid:make(<<"">>, Server, <<"">>),

View File

@ -32,7 +32,7 @@ init_config(Config) ->
{ok, CfgContentTpl} = file:read_file(ConfigPathTpl), {ok, CfgContentTpl} = file:read_file(ConfigPathTpl),
CfgContent = process_config_tpl(CfgContentTpl, [ CfgContent = process_config_tpl(CfgContentTpl, [
{c2s_port, 5222}, {c2s_port, 5222},
{loglevel, 4}, {loglevel, 5},
{s2s_port, 5269}, {s2s_port, 5269},
{web_port, 5280}, {web_port, 5280},
{mysql_server, <<"localhost">>}, {mysql_server, <<"localhost">>},
@ -64,6 +64,12 @@ init_config(Config) ->
{slave_nick, <<"slave_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {slave_nick, <<"slave_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
{room_subject, <<"hello, world!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {room_subject, <<"hello, world!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
{certfile, CertFile}, {certfile, CertFile},
{ns_client, ?NS_CLIENT},
{ns_stream, ?NS_STREAM},
{stream_version, <<"1.0">>},
{stream_id, <<"">>},
{mechs, []},
{lang, <<"en">>},
{base_dir, BaseDir}, {base_dir, BaseDir},
{resource, <<"resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {resource, <<"resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
{master_resource, <<"master_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {master_resource, <<"master_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
@ -118,20 +124,36 @@ process_config_tpl(Content, [{Name, DefaultValue} | Rest]) ->
NewContent = binary:replace(Content, <<"@@",(atom_to_binary(Name, latin1))/binary, "@@">>, Val), NewContent = binary:replace(Content, <<"@@",(atom_to_binary(Name, latin1))/binary, "@@">>, Val),
process_config_tpl(NewContent, Rest). process_config_tpl(NewContent, Rest).
stream_header(Config) ->
NSStream = ?config(ns_stream, Config),
NSClient = ?config(ns_client, Config),
Lang = ?config(lang, Config),
To = case ?config(server, Config) of
<<"">> -> <<"">>;
Server -> <<"to='", Server/binary, "'">>
end,
Version = ?config(stream_version, Config),
io_lib:format("<?xml version='1.0'?><stream:stream "
"xmlns:stream='~s' xmlns='~s' ~s "
"version='~s' xml:lang='~s'>",
[NSStream, NSClient, To, Version, Lang]).
connect(Config) -> connect(Config) ->
process_stream_features(init_stream(Config)).
init_stream(Config) ->
Version = ?config(stream_version, Config),
{ok, Sock} = ejabberd_socket:connect( {ok, Sock} = ejabberd_socket:connect(
?config(server_host, Config), ?config(server_host, Config),
?config(server_port, Config), ?config(server_port, Config),
[binary, {packet, 0}, {active, false}]), [binary, {packet, 0}, {active, false}]),
init_stream(set_opt(socket, Sock, Config)). NewConfig = set_opt(socket, Sock, Config),
ok = send_text(NewConfig, stream_header(NewConfig)),
#stream_start{id = ID, xmlns = ?NS_CLIENT,
version = Version} = recv(),
set_opt(stream_id, ID, NewConfig).
init_stream(Config) -> process_stream_features(Config) ->
ok = send_text(Config, io_lib:format(?STREAM_HEADER,
[?config(server, Config)])),
{xmlstreamstart, <<"stream:stream">>, Attrs} = recv(),
<<"jabber:client">> = fxml:get_attr_s(<<"xmlns">>, Attrs),
<<"1.0">> = fxml:get_attr_s(<<"version">>, Attrs),
#stream_features{sub_els = Fs} = recv(), #stream_features{sub_els = Fs} = recv(),
Mechs = lists:flatmap( Mechs = lists:flatmap(
fun(#sasl_mechanisms{list = Ms}) -> fun(#sasl_mechanisms{list = Ms}) ->
@ -169,24 +191,27 @@ starttls(Config) ->
?config(socket, Config), ?config(socket, Config),
[{certfile, ?config(certfile, Config)}, [{certfile, ?config(certfile, Config)},
connect]), connect]),
init_stream(set_opt(socket, TLSSocket, Config)). process_stream_features(init_stream(set_opt(socket, TLSSocket, Config))).
zlib(Config) -> zlib(Config) ->
send(Config, #compress{methods = [<<"zlib">>]}), send(Config, #compress{methods = [<<"zlib">>]}),
#compressed{} = recv(), #compressed{} = recv(),
ZlibSocket = ejabberd_socket:compress(?config(socket, Config)), ZlibSocket = ejabberd_socket:compress(?config(socket, Config)),
init_stream(set_opt(socket, ZlibSocket, Config)). process_stream_features(init_stream(set_opt(socket, ZlibSocket, Config))).
auth(Config) -> auth(Config) ->
auth(Config, false).
auth(Config, ShouldFail) ->
Mechs = ?config(mechs, Config), Mechs = ?config(mechs, Config),
HaveMD5 = lists:member(<<"DIGEST-MD5">>, Mechs), HaveMD5 = lists:member(<<"DIGEST-MD5">>, Mechs),
HavePLAIN = lists:member(<<"PLAIN">>, Mechs), HavePLAIN = lists:member(<<"PLAIN">>, Mechs),
if HavePLAIN -> if HavePLAIN ->
auth_SASL(<<"PLAIN">>, Config); auth_SASL(<<"PLAIN">>, Config, ShouldFail);
HaveMD5 -> HaveMD5 ->
auth_SASL(<<"DIGEST-MD5">>, Config); auth_SASL(<<"DIGEST-MD5">>, Config, ShouldFail);
true -> true ->
ct:fail(no_sasl_mechanisms_available) auth_legacy(Config, false, ShouldFail)
end. end.
bind(Config) -> bind(Config) ->
@ -198,29 +223,83 @@ bind(Config) ->
Config. Config.
open_session(Config) -> open_session(Config) ->
#iq{type = result, sub_els = []} = open_session(Config, false).
send_recv(Config, #iq{type = set, sub_els = [#xmpp_session{}]}),
open_session(Config, Force) ->
if Force ->
#iq{type = result, sub_els = []} =
send_recv(Config, #iq{type = set, sub_els = [#xmpp_session{}]});
true ->
ok
end,
Config. Config.
auth_legacy(Config, IsDigest) ->
auth_legacy(Config, IsDigest, false).
auth_legacy(Config, IsDigest, ShouldFail) ->
ServerJID = server_jid(Config),
U = ?config(user, Config),
R = ?config(resource, Config),
P = ?config(password, Config),
#iq{type = result,
from = ServerJID,
sub_els = [#legacy_auth{username = <<"">>,
password = <<"">>,
resource = <<"">>} = Auth]} =
send_recv(Config,
#iq{to = ServerJID, type = get,
sub_els = [#legacy_auth{}]}),
Res = case Auth#legacy_auth.digest of
<<"">> when IsDigest ->
StreamID = ?config(stream_id, Config),
D = p1_sha:sha(<<StreamID/binary, P/binary>>),
send_recv(Config, #iq{to = ServerJID, type = set,
sub_els = [#legacy_auth{username = U,
resource = R,
digest = D}]});
_ when not IsDigest ->
send_recv(Config, #iq{to = ServerJID, type = set,
sub_els = [#legacy_auth{username = U,
resource = R,
password = P}]})
end,
case Res of
#iq{from = ServerJID, type = result, sub_els = []} ->
if ShouldFail ->
ct:fail(legacy_auth_should_have_failed);
true ->
Config
end;
#iq{from = ServerJID, type = error} ->
if ShouldFail ->
Config;
true ->
ct:fail(legacy_auth_failed)
end
end.
auth_SASL(Mech, Config) -> auth_SASL(Mech, Config) ->
auth_SASL(Mech, Config, false).
auth_SASL(Mech, Config, ShouldFail) ->
{Response, SASL} = sasl_new(Mech, {Response, SASL} = sasl_new(Mech,
?config(user, Config), ?config(user, Config),
?config(server, Config), ?config(server, Config),
?config(password, Config)), ?config(password, Config)),
send(Config, #sasl_auth{mechanism = Mech, text = Response}), send(Config, #sasl_auth{mechanism = Mech, text = Response}),
wait_auth_SASL_result(set_opt(sasl, SASL, Config)). wait_auth_SASL_result(set_opt(sasl, SASL, Config), ShouldFail).
wait_auth_SASL_result(Config) -> wait_auth_SASL_result(Config, ShouldFail) ->
case recv() of case recv() of
#sasl_success{} when ShouldFail ->
ct:fail(sasl_auth_should_have_failed);
#sasl_success{} -> #sasl_success{} ->
ejabberd_socket:reset_stream(?config(socket, Config)), ejabberd_socket:reset_stream(?config(socket, Config)),
send_text(Config, send_text(Config, stream_header(Config)),
io_lib:format(?STREAM_HEADER, #stream_start{xmlns = ?NS_CLIENT, version = <<"1.0">>} = recv(),
[?config(server, Config)])),
{xmlstreamstart, <<"stream:stream">>, Attrs} = recv(),
<<"jabber:client">> = fxml:get_attr_s(<<"xmlns">>, Attrs),
<<"1.0">> = fxml:get_attr_s(<<"version">>, Attrs),
#stream_features{sub_els = Fs} = recv(), #stream_features{sub_els = Fs} = recv(),
#xmpp_session{optional = true} = lists:keyfind(xmpp_session, 1, Fs),
lists:foldl( lists:foldl(
fun(#feature_sm{}, ConfigAcc) -> fun(#feature_sm{}, ConfigAcc) ->
set_opt(sm, true, ConfigAcc); set_opt(sm, true, ConfigAcc);
@ -232,7 +311,9 @@ wait_auth_SASL_result(Config) ->
#sasl_challenge{text = ClientIn} -> #sasl_challenge{text = ClientIn} ->
{Response, SASL} = (?config(sasl, Config))(ClientIn), {Response, SASL} = (?config(sasl, Config))(ClientIn),
send(Config, #sasl_response{text = Response}), send(Config, #sasl_response{text = Response}),
wait_auth_SASL_result(set_opt(sasl, SASL, Config)); wait_auth_SASL_result(set_opt(sasl, SASL, Config), ShouldFail);
#sasl_failure{} when ShouldFail ->
Config;
#sasl_failure{} -> #sasl_failure{} ->
ct:fail(sasl_auth_failed) ct:fail(sasl_auth_failed)
end. end.
@ -252,18 +333,23 @@ match_failure(Received, Matches) ->
recv() -> recv() ->
receive receive
{'$gen_event', {xmlstreamelement, El}} -> {'$gen_event', {xmlstreamelement, El}} ->
try decode(El);
Pkt = xmpp:decode(El), {'$gen_event', {xmlstreamstart, Name, Attrs}} ->
ct:pal("recv: ~p ->~n~s", [El, xmpp_codec:pp(Pkt)]), decode(#xmlel{name = Name, attrs = Attrs});
Pkt
catch _:{xmpp_codec, Why} ->
ct:fail("recv failed: ~p->~n~s",
[El, xmpp:format_error(Why)])
end;
{'$gen_event', Event} -> {'$gen_event', Event} ->
Event Event
end. end.
decode(El) ->
try
Pkt = xmpp:decode(El),
ct:pal("recv: ~p ->~n~s", [El, xmpp_codec:pp(Pkt)]),
Pkt
catch _:{xmpp_codec, Why} ->
ct:fail("recv failed: ~p->~n~s",
[El, xmpp:format_error(Why)])
end.
fix_ns(#xmlel{name = Tag, attrs = Attrs} = El) fix_ns(#xmlel{name = Tag, attrs = Attrs} = El)
when Tag == <<"stream:features">>; Tag == <<"stream:error">> -> when Tag == <<"stream:features">>; Tag == <<"stream:error">> ->
NewAttrs = [{<<"xmlns">>, <<"http://etherx.jabber.org/streams">>} NewAttrs = [{<<"xmlns">>, <<"http://etherx.jabber.org/streams">>}

View File

@ -5,12 +5,6 @@
-include("mod_proxy65.hrl"). -include("mod_proxy65.hrl").
-include("xmpp_codec.hrl"). -include("xmpp_codec.hrl").
-define(STREAM_HEADER,
<<"<?xml version='1.0'?><stream:stream "
"xmlns:stream='http://etherx.jabber.org/stream"
"s' xmlns='jabber:client' to='~s' version='1.0"
"'>">>).
-define(STREAM_TRAILER, <<"</stream:stream>">>). -define(STREAM_TRAILER, <<"</stream:stream>">>).
-define(PUBSUB(Node), <<(?NS_PUBSUB)/binary, "#", Node>>). -define(PUBSUB(Node), <<(?NS_PUBSUB)/binary, "#", Node>>).