./ct_report/coverage/mod_stream_management_sasl2.COVER.html

1 -module(mod_stream_management_sasl2).
2
3 -define(SM, mod_stream_management).
4
5 -include("jlib.hrl").
6 -include("mongoose_ns.hrl").
7
8 -export([hooks/1]).
9
10 -export([sasl2_stream_features/3,
11 sasl2_start/3,
12 sasl2_success/3,
13 bind2_stream_features/3,
14 bind2_enable_features/3]).
15
16 %% Helpers
17 -spec hooks(mongooseim:host_type()) -> gen_hook:hook_list().
18 hooks(HostType) ->
19 756 [
20 {sasl2_stream_features, HostType, fun ?MODULE:sasl2_stream_features/3, #{}, 50},
21 {sasl2_start, HostType, fun ?MODULE:sasl2_start/3, #{}, 50},
22 {sasl2_success, HostType, fun ?MODULE:sasl2_success/3, #{}, 20},
23 {bind2_stream_features, HostType, fun ?MODULE:bind2_stream_features/3, #{}, 50},
24 {bind2_enable_features, HostType, fun ?MODULE:bind2_enable_features/3, #{}, 50}
25 ].
26
27 %% Hook handlers
28 -spec sasl2_stream_features(Acc, #{c2s_data := mongoose_c2s:data()}, gen_hook:extra()) ->
29 {ok, Acc} when Acc :: [exml:element()].
30 sasl2_stream_features(Acc, _, _) ->
31 29 Resume = #xmlel{name = <<"sm">>, attrs = [{<<"xmlns">>, ?NS_STREAM_MGNT_3}]},
32 29 {ok, [Resume | Acc]}.
33
34 -spec sasl2_start(SaslAcc, #{stanza := exml:element()}, gen_hook:extra()) ->
35 {ok, SaslAcc} when SaslAcc :: mongoose_acc:t().
36 sasl2_start(SaslAcc, #{stanza := El}, _) ->
37 24 case exml_query:path(El, [{element_with_ns, <<"resume">>, ?NS_STREAM_MGNT_3}]) of
38 undefined ->
39 17 {ok, SaslAcc};
40 SmRequest ->
41 7 {ok, mod_sasl2:put_inline_request(SaslAcc, ?MODULE, SmRequest)}
42 end.
43
44 %% If SASL2 inline stream resumption is requested, that MUST be processed FIRST by the server.
45 -spec sasl2_success(mongoose_acc:t(), mod_sasl2:c2s_state_data(), gen_hook:extra()) ->
46 mongoose_c2s_hooks:result().
47 sasl2_success(SaslAcc, _, _) ->
48 20 case mod_sasl2:get_inline_request(SaslAcc, ?MODULE, undefined) of
49 undefined ->
50 13 {ok, SaslAcc};
51 SmRequest ->
52 7 handle_sasl2_resume(SaslAcc, SmRequest)
53 end.
54
55 -spec bind2_stream_features(Acc, #{c2s_data := mongoose_c2s:data()}, gen_hook:extra()) ->
56 {ok, Acc} when Acc :: [exml:element()].
57 bind2_stream_features(Acc, _, _) ->
58 29 SmFeature = #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_STREAM_MGNT_3}]},
59 29 {ok, [SmFeature | Acc]}.
60
61 -spec bind2_enable_features(SaslAcc, mod_sasl2:c2s_state_data(), gen_hook:extra()) ->
62 {ok, SaslAcc} when SaslAcc :: mongoose_acc:t().
63 bind2_enable_features(SaslAcc, Params, _) ->
64 10 Inline = #{request := BindRequest} = mod_bind2:get_bind_request(SaslAcc),
65 10 case exml_query:subelement_with_name_and_ns(BindRequest, <<"enable">>, ?NS_STREAM_MGNT_3) of
66 undefined ->
67 8 {ok, SaslAcc};
68 El ->
69 2 handle_bind_enable(SaslAcc, Params, Inline, El)
70 end.
71
72 %% If resumption is successful:
73 %% - MUST skip resource binding (a resumed session already has a resource bound)
74 %% - MUST entirely ignore the potentially inlined <bind/> request
75 %% - MUST NOT send stream features in this case (overriding behaviour defined in SASL2)
76 -spec handle_sasl2_resume(mongoose_acc:t(), mod_sasl2:inline_request()) ->
77 mongoose_c2s_hooks:result().
78 handle_sasl2_resume(SaslAcc, #{request := El}) ->
79 7 #{c2s_state := C2SState, c2s_data := C2SData} = mod_sasl2:get_state_data(SaslAcc),
80 7 case mod_stream_management:handle_resume(C2SData, C2SState, El) of
81 {stream_mgmt_error, ErrorStanza} ->
82 2 {ok, mod_sasl2:update_inline_request(SaslAcc, ?MODULE, ErrorStanza, failure)};
83 {error, _ErrorStanza, _Reason} -> %% This signifies a stream-error, but we discard those here
84 1 SimpleErrorStanza = mod_stream_management_stanzas:stream_mgmt_failed(<<"bad-request">>),
85 1 {ok, mod_sasl2:update_inline_request(SaslAcc, ?MODULE, SimpleErrorStanza, failure)};
86 {error, ErrorStanza} ->
87 2 {ok, mod_sasl2:update_inline_request(SaslAcc, ?MODULE, ErrorStanza, failure)};
88 {ok, #{resumed := Resumed, forward := ToForward} = Ret} ->
89 2 SaslAcc1 = mod_sasl2:update_inline_request(SaslAcc, ?MODULE, Resumed, success),
90 2 SaslAcc2 = mod_sasl2:set_state_data(SaslAcc1, Ret),
91 2 SaslAcc3 = mod_sasl2:request_block_future_stream_features(SaslAcc2),
92 %% We stop here to completely ignore subsequent inlines (for example BIND2)
93 2 {stop, mongoose_c2s_acc:to_acc(SaslAcc3, socket_send, ToForward)}
94 end.
95
96 -spec handle_bind_enable(SaslAcc, mod_sasl2:c2s_state_data(), mod_sasl2:inline_request(), exml:element()) ->
97 {ok, SaslAcc} when SaslAcc :: mongoose_acc:t().
98 handle_bind_enable(SaslAcc, #{c2s_data := C2SData}, Inline, El) ->
99 2 case mod_stream_management:if_not_already_enabled_create_sm_state(C2SData) of
100 error ->
101
:-(
mod_stream_management:stream_error(SaslAcc, C2SData);
102 SmState ->
103 2 do_handle_bind_enable(SaslAcc, C2SData, SmState, Inline, El)
104 end.
105
106 -spec do_handle_bind_enable(SaslAcc, mongoose_c2s:data(), mod_stream_management:sm_state(), mod_sasl2:inline_request(), exml:element()) ->
107 {ok, SaslAcc} when SaslAcc :: mongoose_acc:t().
108 do_handle_bind_enable(SaslAcc, C2SData, SmState, Inline, El) ->
109 2 case exml_query:attr(El, <<"resume">>, false) of
110 false ->
111 1 Stanza = mod_stream_management_stanzas:stream_mgmt_enabled(),
112 1 SaslAcc1 = mongoose_c2s_acc:to_acc(SaslAcc, state_mod, {?SM, SmState}),
113 1 SaslAcc2 = mod_bind2:append_inline_bound_answer(SaslAcc1, Inline, Stanza),
114 1 {ok, SaslAcc2};
115 Attr when Attr =:= <<"true">>; Attr =:= <<"1">> ->
116 1 Stanza = mod_stream_management:register_smid_return_enabled_stanza(C2SData),
117 1 SaslAcc1 = mongoose_c2s_acc:to_acc(SaslAcc, state_mod, {?SM, SmState}),
118 1 SaslAcc2 = mod_bind2:append_inline_bound_answer(SaslAcc1, Inline, Stanza),
119 1 {ok, SaslAcc2};
120 _ ->
121
:-(
mod_stream_management:stream_error(SaslAcc, C2SData)
122 end.
Line Hits Source