1: -module(mim_c2s_SUITE). 2: 3: -compile([export_all, nowarn_export_all]). 4: 5: -include_lib("common_test/include/ct.hrl"). 6: -include_lib("eunit/include/eunit.hrl"). 7: -include_lib("exml/include/exml_stream.hrl"). 8: -include_lib("escalus/include/escalus.hrl"). 9: -include_lib("escalus/include/escalus_xmlns.hrl"). 10: -define(BAD_RESOURCE, <<"\x{EFBB}"/utf8>>). 11: -define(MAX_STANZA_SIZE, 1024). 12: 13: -import(distributed_helper, [mim/0]). 14: 15: %%-------------------------------------------------------------------- 16: %% Suite configuration 17: %%-------------------------------------------------------------------- 18: 19: all() -> 20: [ 21: {group, basic}, 22: {group, backwards_compatible_session} 23: ]. 24: 25: groups() -> 26: [ 27: {basic, [parallel], 28: [ 29: client_sets_stream_from_server_answers_with_to, 30: stream_from_does_not_match_sasl_jid_results_in_stream_error, 31: two_users_can_log_and_chat, 32: too_big_stanza_is_rejected, 33: too_big_opening_tag_is_rejected, 34: message_sent_to_malformed_jid_results_in_error, 35: verify_session_establishment_is_not_announced, 36: invalid_resource_fails_to_log 37: ]}, 38: {backwards_compatible_session, [parallel], 39: [ 40: verify_session_establishment_is_announced 41: ]} 42: ]. 43: 44: %%-------------------------------------------------------------------- 45: %% Init & teardown 46: %%-------------------------------------------------------------------- 47: init_per_suite(Config) -> 48: HostType = domain_helper:host_type(), 49: Config1 = dynamic_modules:save_modules(HostType, Config), 50: dynamic_modules:ensure_stopped(HostType, [mod_presence]), 51: EscalusOverrides = [{initial_activity, fun(_) -> ok end}, 52: {start_ready_clients, fun ?MODULE:escalus_start/2}], 53: escalus:init_per_suite([{escalus_overrides, EscalusOverrides} | Config1 ]). 54: 55: end_per_suite(Config) -> 56: dynamic_modules:restore_modules(Config), 57: mongoose_helper:restore_config(Config), 58: escalus:end_per_suite(Config). 59: 60: init_per_group(basic, Config) -> 61: Steps = [start_stream, stream_features, maybe_use_ssl, authenticate, bind], 62: Config1 = save_c2s_listener(Config), 63: Config2 = escalus_users:update_userspec(Config1, alice, connection_steps, Steps), 64: Config3 = escalus_users:update_userspec(Config2, bob, connection_steps, Steps), 65: configure_c2s_listener(Config3, #{backwards_compatible_session => false, 66: max_stanza_size => ?MAX_STANZA_SIZE}), 67: Config3; 68: init_per_group(backwards_compatible_session, Config) -> 69: Config. 70: 71: end_per_group(basic, Config) -> 72: escalus_fresh:clean(), 73: restore_c2s_listener(Config), 74: Config; 75: end_per_group(backwards_compatible_session, Config) -> 76: escalus_fresh:clean(), 77: Config. 78: 79: init_per_testcase(Name, Config) -> 80: escalus:init_per_testcase(Name, Config). 81: 82: end_per_testcase(Name, Config) -> 83: escalus:end_per_testcase(Name, Config). 84: 85: %%-------------------------------------------------------------------- 86: %% tests 87: %%-------------------------------------------------------------------- 88: client_sets_stream_from_server_answers_with_to(Config) -> 89: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 90: Alice = escalus_connection:connect(AliceSpec), 91: escalus_client:send(Alice, stream_start(Alice)), 92: [StreamStartAnswer, _StreamFeatures] = escalus_client:wait_for_stanzas(Alice, 2, 500), 93: #xmlstreamstart{name = <<"stream:stream">>, attrs = Attrs} = StreamStartAnswer, 94: FromClient = jid:from_binary(escalus_utils:get_jid(Alice)), 95: {_, FromServerBin} = lists:keyfind(<<"to">>, 1, Attrs), 96: FromServer = jid:from_binary(FromServerBin), 97: ?assert(jid:are_equal(FromClient, FromServer)), 98: escalus_connection:stop(Alice). 99: 100: stream_from_does_not_match_sasl_jid_results_in_stream_error(Config) -> 101: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 102: Alice = escalus_connection:connect(AliceSpec), 103: Server = escalus_utils:get_server(Alice), 104: escalus_client:send(Alice, stream_start(Server, <<"not_alice@", Server/binary>>)), 105: [_StreamStartAnswer, _StreamFeatures] = escalus_client:wait_for_stanzas(Alice, 2, 500), 106: try escalus_auth:auth_plain(Alice, AliceSpec) of 107: _ -> error(authentication_with_inconsistent_jid_succeeded) 108: catch 109: throw:{auth_failed, _User, AuthReply} -> 110: escalus:assert(is_stream_error, [<<"invalid-from">>, <<>>], AuthReply), 111: escalus:assert(is_stream_end, escalus_client:wait_for_stanza(Alice)), 112: true = escalus_connection:wait_for_close(Alice, timer:seconds(1)) 113: end. 114: 115: two_users_can_log_and_chat(Config) -> 116: AliceHost = escalus_users:get_server(Config, alice), 117: HostType = domain_helper:domain_to_host_type(mim(), AliceHost), 118: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 119: MongooseMetrics = [{[global, data, xmpp, received, xml_stanza_size], changed}, 120: {[global, data, xmpp, sent, xml_stanza_size], changed}, 121: {[global, data, xmpp, received, c2s, tcp], changed}, 122: {[global, data, xmpp, sent, c2s, tcp], changed}, 123: {[HostTypePrefix, data, xmpp, c2s, message, processing_time], changed}, 124: {[global, data, xmpp, received, c2s, tls], 0}, 125: {[global, data, xmpp, sent, c2s, tls], 0}], 126: escalus:fresh_story([{mongoose_metrics, MongooseMetrics} | Config], 127: [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 128: escalus_client:send(Alice, escalus_stanza:chat_to(Bob, <<"Hi!">>)), 129: escalus:assert(is_chat_message, [<<"Hi!">>], escalus_client:wait_for_stanza(Bob)), 130: escalus_client:send(Bob, escalus_stanza:chat_to(Alice, <<"Hi!">>)), 131: escalus:assert(is_chat_message, [<<"Hi!">>], escalus_client:wait_for_stanza(Alice)) 132: end). 133: 134: too_big_stanza_is_rejected(Config) -> 135: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 136: {ok, Alice, _Features} = escalus_connection:start(AliceSpec), 137: BigBody = base16:encode(crypto:strong_rand_bytes(?MAX_STANZA_SIZE)), 138: escalus_client:send(Alice, escalus_stanza:chat_to(Alice, BigBody)), 139: escalus:assert(is_stream_error, [<<"policy-violation">>, <<>>], escalus_client:wait_for_stanza(Alice)), 140: escalus:assert(is_stream_end, escalus_client:wait_for_stanza(Alice)), 141: true = escalus_connection:wait_for_close(Alice, timer:seconds(1)). 142: 143: too_big_opening_tag_is_rejected(Config) -> 144: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 145: {ok, Alice, _Features} = escalus_connection:start(AliceSpec, []), 146: BigAttrs = [{<<"bigattr">>, base16:encode(crypto:strong_rand_bytes(?MAX_STANZA_SIZE))}], 147: escalus_client:send(Alice, #xmlel{name = <<"stream:stream">>, attrs = BigAttrs}), 148: escalus:assert(is_stream_start, escalus_client:wait_for_stanza(Alice)), 149: escalus:assert(is_stream_error, [<<"xml-not-well-formed">>, <<>>], 150: escalus_client:wait_for_stanza(Alice)), 151: escalus:assert(is_stream_end, escalus_client:wait_for_stanza(Alice)), 152: true = escalus_connection:wait_for_close(Alice, timer:seconds(1)). 153: 154: message_sent_to_malformed_jid_results_in_error(Config) -> 155: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 156: % Alice sends message with malformed "to" 157: Stanza = escalus_client:send_and_wait(Alice, 158: escalus_stanza:chat_to(<<"@invalid">>, <<"Hi!">>)), 159: % Alice receives error 160: escalus_assert:is_error(Stanza, <<"modify">>, <<"jid-malformed">>), 161: % Alice resends message with proper "to" 162: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"Hi!">>)), 163: % Bob gets the message 164: escalus_assert:is_chat_message(<<"Hi!">>, escalus_client:wait_for_stanza(Bob)) 165: end). 166: 167: invalid_resource_fails_to_log(Config) -> 168: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 169: Steps = [start_stream, stream_features, authenticate], 170: {ok, Alice, _Features} = escalus_connection:start(AliceSpec, Steps), 171: BindStanza = escalus_stanza:bind(?BAD_RESOURCE), 172: escalus_connection:send(Alice, BindStanza), 173: Response = escalus_client:wait_for_stanza(Alice), 174: escalus_assert:is_error(Response, <<"modify">>, <<"bad-request">>), 175: escalus_connection:stop(Alice). 176: 177: verify_session_establishment_is_not_announced(Config) -> 178: MaybeSessionFeature = start_connection_maybe_get_session_feature(Config), 179: ?assertEqual(undefined, MaybeSessionFeature). 180: 181: verify_session_establishment_is_announced(Config) -> 182: MaybeSessionFeature = start_connection_maybe_get_session_feature(Config), 183: ?assertNotEqual(undefined, MaybeSessionFeature). 184: 185: start_connection_maybe_get_session_feature(Config) -> 186: Steps = [start_stream, stream_features], 187: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 188: {ok, Client = #client{props = Props}, _} = escalus_connection:start(AliceSpec, Steps), 189: ok = escalus_auth:auth_plain(Client, Props), 190: escalus_connection:reset_parser(Client), 191: Client1 = escalus_session:start_stream(Client), 192: Features = escalus_connection:get_stanza(Client1, wait_for_features), 193: escalus_connection:stop(Client1), 194: exml_query:path(Features, [{element_with_ns, <<"session">>, ?NS_SESSION}]). 195: 196: %%-------------------------------------------------------------------- 197: %% helpers 198: %%-------------------------------------------------------------------- 199: 200: stream_start(Client) -> 201: Server = escalus_utils:get_server(Client), 202: From = escalus_utils:get_jid(Client), 203: stream_start(Server, From). 204: 205: stream_start(Server, From) -> 206: #xmlstreamstart{name = <<"stream:stream">>, 207: attrs = [{<<"to">>, Server}, 208: {<<"from">>, From}, 209: {<<"version">>, <<"1.0">>}, 210: {<<"xml:lang">>, <<"en">>}, 211: {<<"xmlns">>, <<"jabber:client">>}, 212: {<<"xmlns:stream">>, <<"http://etherx.jabber.org/streams">>}]}. 213: 214: save_c2s_listener(Config) -> 215: C2SPort = ct:get_config({hosts, mim, c2s_port}), 216: [C2SListener] = mongoose_helper:get_listeners(mim(), #{port => C2SPort, module => mongoose_c2s_listener}), 217: [{c2s_listener, C2SListener} | Config]. 218: 219: restore_c2s_listener(Config) -> 220: C2SListener = ?config(c2s_listener, Config), 221: mongoose_helper:restart_listener(mim(), C2SListener). 222: 223: configure_c2s_listener(Config, ExtraC2SOpts) -> 224: C2SListener = ?config(c2s_listener, Config), 225: NewC2SListener = maps:merge(C2SListener, ExtraC2SOpts), 226: mongoose_helper:restart_listener(mim(), NewC2SListener). 227: 228: escalus_start(Cfg, FlatCDs) -> 229: {_, RClients} = lists:foldl( 230: fun({UserSpec, BaseResource}, {N, Acc}) -> 231: Resource = escalus_overridables:do(Cfg, modify_resource, [BaseResource], 232: {escalus_utils, identity}), 233: {ok, Client} = escalus_client:start(Cfg, UserSpec, Resource), 234: {N+1, [Client|Acc]} 235: end, {1, []}, FlatCDs), 236: Clients = lists:reverse(RClients), 237: [ escalus_assert:has_no_stanzas(Client) || Client <- Clients ], 238: Clients.