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:
parent
151668ac10
commit
a4ec064455
@ -323,24 +323,25 @@ get_subscribed(FsmRef) ->
|
||||
|
||||
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
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 ->
|
||||
send_header(StateData, ?MYNAME, <<"">>, Lang),
|
||||
send_header(StateData, ?MYNAME, Version, Lang),
|
||||
send_element(StateData, xmpp:serr_invalid_namespace()),
|
||||
{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:
|
||||
%% Protocols or specifications that specify limited buffer sizes for
|
||||
%% language tags MUST allow for language tags of at least 35 characters.
|
||||
%% 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">>,
|
||||
send_element(StateData,
|
||||
xmpp:serr_policy_violation(Txt, ?MYLANG)),
|
||||
{stop, normal, StateData};
|
||||
#stream_start{to = undefined, lang = Lang} ->
|
||||
#stream_start{to = undefined, lang = Lang, version = Version} ->
|
||||
Txt = <<"Missing 'to' attribute">>,
|
||||
send_header(StateData, ?MYNAME, <<"">>, Lang),
|
||||
send_header(StateData, ?MYNAME, Version, Lang),
|
||||
send_element(StateData,
|
||||
xmpp:serr_improper_addressing(Txt, Lang)),
|
||||
{stop, normal, StateData};
|
||||
@ -463,7 +464,7 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
send_header(StateData, Server, <<"">>, ?MYLANG),
|
||||
send_header(StateData, Server, StreamVersion, ?MYLANG),
|
||||
if not StateData#state.tls_enabled and
|
||||
StateData#state.tls_required ->
|
||||
send_element(
|
||||
@ -492,7 +493,7 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||
end
|
||||
catch _:{xmpp_codec, 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)),
|
||||
{stop, normal, StateData}
|
||||
end;
|
||||
@ -517,13 +518,8 @@ wait_for_auth({xmlstreamelement, #xmlel{} = El}, StateData) ->
|
||||
decode_element(El, wait_for_auth, StateData);
|
||||
wait_for_auth(Pkt, StateData) when ?IS_STREAM_MGMT_PACKET(Pkt) ->
|
||||
fsm_next_state(wait_for_auth, dispatch_stream_mgmt(Pkt, StateData));
|
||||
wait_for_auth(#iq{type = get,
|
||||
sub_els = [#legacy_auth{username = U}]} = IQ, StateData) ->
|
||||
Username = case U of
|
||||
undefined -> <<"">>;
|
||||
_ -> U
|
||||
end,
|
||||
Auth = #legacy_auth{username = Username, password = <<>>, resource = <<>>},
|
||||
wait_for_auth(#iq{type = get, sub_els = [#legacy_auth{}]} = IQ, StateData) ->
|
||||
Auth = #legacy_auth{username = <<>>, password = <<>>, resource = <<>>},
|
||||
Res = case ejabberd_auth:plain_password_required(StateData#state.server) of
|
||||
false ->
|
||||
xmpp:make_iq_result(IQ, Auth#legacy_auth{digest = <<>>});
|
||||
|
@ -19,8 +19,9 @@
|
||||
wait_for_master/1, wait_for_slave/1,
|
||||
make_iq_result/1, start_event_relay/0,
|
||||
stop_event_relay/1, put_event/2, get_event/1,
|
||||
bind/1, auth/1, open_session/1, zlib/1, starttls/1,
|
||||
close_socket/1]).
|
||||
bind/1, auth/1, auth/2, open_session/1, open_session/2,
|
||||
zlib/1, starttls/1, close_socket/1, init_stream/1,
|
||||
auth_legacy/2, auth_legacy/3]).
|
||||
|
||||
-include("suite.hrl").
|
||||
|
||||
@ -154,15 +155,26 @@ init_per_testcase(stop_ejabberd, Config) ->
|
||||
open_session(bind(auth(connect(Config))));
|
||||
init_per_testcase(TestCase, OrigConfig) ->
|
||||
subscribe_to_events(OrigConfig),
|
||||
TestGroup = proplists:get_value(
|
||||
name, ?config(tc_group_properties, 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),
|
||||
SlaveResource = ?config(slave_resource, OrigConfig),
|
||||
Test = atom_to_list(TestCase),
|
||||
IsMaster = lists:suffix("_master", Test),
|
||||
IsSlave = lists:suffix("_slave", 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!#$%^*()`~+-;_=[]{}|\\">>;
|
||||
true -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>
|
||||
end,
|
||||
@ -172,11 +184,15 @@ init_per_testcase(TestCase, OrigConfig) ->
|
||||
end,
|
||||
Slave = if IsCarbons ->
|
||||
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, SlaveResource);
|
||||
IsReplaced ->
|
||||
jid:make(User, Server, Resource);
|
||||
true ->
|
||||
jid:make(<<"test_slave!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource)
|
||||
end,
|
||||
Master = if IsCarbons ->
|
||||
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, MasterResource);
|
||||
IsReplaced ->
|
||||
jid:make(User, Server, Resource);
|
||||
true ->
|
||||
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource)
|
||||
end,
|
||||
@ -184,29 +200,35 @@ init_per_testcase(TestCase, OrigConfig) ->
|
||||
set_opt(slave, Slave,
|
||||
set_opt(master, Master,
|
||||
set_opt(resource, MyResource, OrigConfig)))),
|
||||
case TestCase of
|
||||
test_connect ->
|
||||
case Test of
|
||||
"test_connect" ++ _ ->
|
||||
Config;
|
||||
test_auth ->
|
||||
"test_legacy_auth" ++ _ ->
|
||||
init_stream(set_opt(stream_version, <<"">>, Config));
|
||||
"test_auth" ++ _ ->
|
||||
connect(Config);
|
||||
test_starttls ->
|
||||
"test_starttls" ++ _ ->
|
||||
connect(Config);
|
||||
test_zlib ->
|
||||
"test_zlib" ->
|
||||
connect(Config);
|
||||
test_register ->
|
||||
"test_register" ->
|
||||
connect(Config);
|
||||
auth_md5 ->
|
||||
"auth_md5" ->
|
||||
connect(Config);
|
||||
auth_plain ->
|
||||
"auth_plain" ->
|
||||
connect(Config);
|
||||
test_bind ->
|
||||
"unauthenticated_" ++ _ ->
|
||||
connect(Config);
|
||||
"test_bind" ->
|
||||
auth(connect(Config));
|
||||
sm_resume ->
|
||||
"sm_resume" ->
|
||||
auth(connect(Config));
|
||||
sm_resume_failed ->
|
||||
"sm_resume_failed" ->
|
||||
auth(connect(Config));
|
||||
test_open_session ->
|
||||
"test_open_session" ->
|
||||
bind(auth(connect(Config)));
|
||||
"replaced" ++ _ ->
|
||||
auth(connect(Config));
|
||||
_ when IsMaster or IsSlave ->
|
||||
Password = ?config(password, Config),
|
||||
ejabberd_auth:try_register(User, Server, Password),
|
||||
@ -218,30 +240,56 @@ init_per_testcase(TestCase, OrigConfig) ->
|
||||
end_per_testcase(_TestCase, _Config) ->
|
||||
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() ->
|
||||
[{generic, [sequence],
|
||||
[test_connect,
|
||||
[{generic, [parallel],
|
||||
[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_zlib,
|
||||
test_auth,
|
||||
test_auth_fail,
|
||||
test_bind,
|
||||
test_open_session,
|
||||
presence,
|
||||
codec_failure,
|
||||
unsupported_query,
|
||||
bad_nonza,
|
||||
invalid_from,
|
||||
ping,
|
||||
version,
|
||||
time,
|
||||
stats,
|
||||
sm,
|
||||
sm_resume,
|
||||
sm_resume_failed,
|
||||
disco]},
|
||||
{presence, [sequence], [presence]},
|
||||
{sm, [sequence],
|
||||
[sm,
|
||||
sm_resume,
|
||||
sm_resume_failed]},
|
||||
{test_proxy65, [parallel],
|
||||
[proxy65_master, proxy65_slave]}].
|
||||
[proxy65_master, proxy65_slave]},
|
||||
{replaced, [parallel],
|
||||
[replaced_master, replaced_slave]}].
|
||||
|
||||
db_tests(riak) ->
|
||||
%% No support for mod_pubsub
|
||||
[{single_user, [sequence],
|
||||
[test_register,
|
||||
legacy_auth_tests(),
|
||||
auth_plain,
|
||||
auth_md5,
|
||||
presence_broadcast,
|
||||
@ -273,6 +321,7 @@ db_tests(riak) ->
|
||||
db_tests(DB) when DB == mnesia; DB == redis ->
|
||||
[{single_user, [sequence],
|
||||
[test_register,
|
||||
legacy_auth_tests(),
|
||||
auth_plain,
|
||||
auth_md5,
|
||||
presence_broadcast,
|
||||
@ -319,6 +368,7 @@ db_tests(_) ->
|
||||
%% No support for carboncopy
|
||||
[{single_user, [sequence],
|
||||
[test_register,
|
||||
legacy_auth_tests(),
|
||||
auth_plain,
|
||||
auth_md5,
|
||||
presence_broadcast,
|
||||
@ -361,12 +411,14 @@ db_tests(_) ->
|
||||
ldap_tests() ->
|
||||
[{ldap_tests, [sequence],
|
||||
[test_auth,
|
||||
test_auth_fail,
|
||||
vcard_get,
|
||||
ldap_shared_roster_get]}].
|
||||
|
||||
extauth_tests() ->
|
||||
[{extauth_tests, [sequence],
|
||||
[test_auth,
|
||||
test_auth_fail,
|
||||
test_unregister]}].
|
||||
|
||||
groups() ->
|
||||
@ -381,15 +433,15 @@ groups() ->
|
||||
{riak, [sequence], db_tests(riak)}].
|
||||
|
||||
all() ->
|
||||
[{group, ldap},
|
||||
[%%{group, ldap},
|
||||
{group, no_db},
|
||||
{group, mnesia},
|
||||
{group, redis},
|
||||
{group, mysql},
|
||||
{group, pgsql},
|
||||
{group, sqlite},
|
||||
{group, extauth},
|
||||
{group, riak},
|
||||
%%{group, redis},
|
||||
%%{group, mysql},
|
||||
%%{group, pgsql},
|
||||
%%{group, sqlite},
|
||||
%%{group, extauth},
|
||||
%%{group, riak},
|
||||
stop_ejabberd].
|
||||
|
||||
stop_ejabberd(Config) ->
|
||||
@ -398,6 +450,48 @@ stop_ejabberd(Config) ->
|
||||
?recv1({xmlstreamend, <<"stream:stream">>}),
|
||||
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) ->
|
||||
disconnect(connect(Config)).
|
||||
|
||||
@ -462,6 +556,28 @@ try_unregister(Config) ->
|
||||
?recv1(#stream_error{reason = conflict}),
|
||||
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) ->
|
||||
Mechs = ?config(mechs, Config),
|
||||
case lists:member(<<"DIGEST-MD5">>, Mechs) of
|
||||
@ -482,14 +598,37 @@ auth_plain(Config) ->
|
||||
{skipped, 'PLAIN_not_available'}
|
||||
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) ->
|
||||
disconnect(auth(Config)).
|
||||
|
||||
test_auth_fail(Config0) ->
|
||||
Config = set_opt(user, <<"wrong">>, Config0),
|
||||
disconnect(auth(Config, _ShouldFail = true)).
|
||||
|
||||
test_bind(Config) ->
|
||||
disconnect(bind(Config)).
|
||||
|
||||
test_open_session(Config) ->
|
||||
disconnect(open_session(Config)).
|
||||
disconnect(open_session(Config, true)).
|
||||
|
||||
roster_get(Config) ->
|
||||
#iq{type = result, sub_els = [#roster_query{items = []}]} =
|
||||
@ -519,6 +658,21 @@ roster_ver(Config) ->
|
||||
sub_els = [#roster_query{ver = Ver2}]}),
|
||||
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) ->
|
||||
send(Config, #presence{}),
|
||||
JID = my_jid(Config),
|
||||
@ -604,6 +758,18 @@ disco(Config) ->
|
||||
end, Items),
|
||||
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) ->
|
||||
Server = ?config(server, Config),
|
||||
ServerJID = jid:make(<<"">>, Server, <<"">>),
|
||||
|
150
test/suite.erl
150
test/suite.erl
@ -32,7 +32,7 @@ init_config(Config) ->
|
||||
{ok, CfgContentTpl} = file:read_file(ConfigPathTpl),
|
||||
CfgContent = process_config_tpl(CfgContentTpl, [
|
||||
{c2s_port, 5222},
|
||||
{loglevel, 4},
|
||||
{loglevel, 5},
|
||||
{s2s_port, 5269},
|
||||
{web_port, 5280},
|
||||
{mysql_server, <<"localhost">>},
|
||||
@ -64,6 +64,12 @@ init_config(Config) ->
|
||||
{slave_nick, <<"slave_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||
{room_subject, <<"hello, world!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||
{certfile, CertFile},
|
||||
{ns_client, ?NS_CLIENT},
|
||||
{ns_stream, ?NS_STREAM},
|
||||
{stream_version, <<"1.0">>},
|
||||
{stream_id, <<"">>},
|
||||
{mechs, []},
|
||||
{lang, <<"en">>},
|
||||
{base_dir, BaseDir},
|
||||
{resource, <<"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),
|
||||
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) ->
|
||||
process_stream_features(init_stream(Config)).
|
||||
|
||||
init_stream(Config) ->
|
||||
Version = ?config(stream_version, Config),
|
||||
{ok, Sock} = ejabberd_socket:connect(
|
||||
?config(server_host, Config),
|
||||
?config(server_port, Config),
|
||||
[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) ->
|
||||
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),
|
||||
process_stream_features(Config) ->
|
||||
#stream_features{sub_els = Fs} = recv(),
|
||||
Mechs = lists:flatmap(
|
||||
fun(#sasl_mechanisms{list = Ms}) ->
|
||||
@ -169,24 +191,27 @@ starttls(Config) ->
|
||||
?config(socket, Config),
|
||||
[{certfile, ?config(certfile, Config)},
|
||||
connect]),
|
||||
init_stream(set_opt(socket, TLSSocket, Config)).
|
||||
process_stream_features(init_stream(set_opt(socket, TLSSocket, Config))).
|
||||
|
||||
zlib(Config) ->
|
||||
send(Config, #compress{methods = [<<"zlib">>]}),
|
||||
#compressed{} = recv(),
|
||||
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, false).
|
||||
|
||||
auth(Config, ShouldFail) ->
|
||||
Mechs = ?config(mechs, Config),
|
||||
HaveMD5 = lists:member(<<"DIGEST-MD5">>, Mechs),
|
||||
HavePLAIN = lists:member(<<"PLAIN">>, Mechs),
|
||||
if HavePLAIN ->
|
||||
auth_SASL(<<"PLAIN">>, Config);
|
||||
auth_SASL(<<"PLAIN">>, Config, ShouldFail);
|
||||
HaveMD5 ->
|
||||
auth_SASL(<<"DIGEST-MD5">>, Config);
|
||||
auth_SASL(<<"DIGEST-MD5">>, Config, ShouldFail);
|
||||
true ->
|
||||
ct:fail(no_sasl_mechanisms_available)
|
||||
auth_legacy(Config, false, ShouldFail)
|
||||
end.
|
||||
|
||||
bind(Config) ->
|
||||
@ -198,29 +223,83 @@ bind(Config) ->
|
||||
Config.
|
||||
|
||||
open_session(Config) ->
|
||||
#iq{type = result, sub_els = []} =
|
||||
send_recv(Config, #iq{type = set, sub_els = [#xmpp_session{}]}),
|
||||
open_session(Config, false).
|
||||
|
||||
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.
|
||||
|
||||
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, false).
|
||||
|
||||
auth_SASL(Mech, Config, ShouldFail) ->
|
||||
{Response, SASL} = sasl_new(Mech,
|
||||
?config(user, Config),
|
||||
?config(server, Config),
|
||||
?config(password, Config)),
|
||||
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
|
||||
#sasl_success{} when ShouldFail ->
|
||||
ct:fail(sasl_auth_should_have_failed);
|
||||
#sasl_success{} ->
|
||||
ejabberd_socket:reset_stream(?config(socket, Config)),
|
||||
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),
|
||||
send_text(Config, stream_header(Config)),
|
||||
#stream_start{xmlns = ?NS_CLIENT, version = <<"1.0">>} = recv(),
|
||||
#stream_features{sub_els = Fs} = recv(),
|
||||
#xmpp_session{optional = true} = lists:keyfind(xmpp_session, 1, Fs),
|
||||
lists:foldl(
|
||||
fun(#feature_sm{}, ConfigAcc) ->
|
||||
set_opt(sm, true, ConfigAcc);
|
||||
@ -232,7 +311,9 @@ wait_auth_SASL_result(Config) ->
|
||||
#sasl_challenge{text = ClientIn} ->
|
||||
{Response, SASL} = (?config(sasl, Config))(ClientIn),
|
||||
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{} ->
|
||||
ct:fail(sasl_auth_failed)
|
||||
end.
|
||||
@ -252,18 +333,23 @@ match_failure(Received, Matches) ->
|
||||
recv() ->
|
||||
receive
|
||||
{'$gen_event', {xmlstreamelement, 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;
|
||||
decode(El);
|
||||
{'$gen_event', {xmlstreamstart, Name, Attrs}} ->
|
||||
decode(#xmlel{name = Name, attrs = Attrs});
|
||||
{'$gen_event', Event} ->
|
||||
Event
|
||||
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)
|
||||
when Tag == <<"stream:features">>; Tag == <<"stream:error">> ->
|
||||
NewAttrs = [{<<"xmlns">>, <<"http://etherx.jabber.org/streams">>}
|
||||
|
@ -5,12 +5,6 @@
|
||||
-include("mod_proxy65.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(PUBSUB(Node), <<(?NS_PUBSUB)/binary, "#", Node>>).
|
||||
|
Loading…
Reference in New Issue
Block a user