./ct_report/coverage/ejabberd_s2s_in.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : ejabberd_s2s_in.erl
3 %%% Author : Alexey Shchepin <alexey@process-one.net>
4 %%% Purpose : Serve incoming s2s connection
5 %%% Created : 6 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
6 %%%
7 %%%
8 %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
9 %%%
10 %%% This program is free software; you can redistribute it and/or
11 %%% modify it under the terms of the GNU General Public License as
12 %%% published by the Free Software Foundation; either version 2 of the
13 %%% License, or (at your option) any later version.
14 %%%
15 %%% This program is distributed in the hope that it will be useful,
16 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 %%% General Public License for more details.
19 %%%
20 %%% You should have received a copy of the GNU General Public License
21 %%% along with this program; if not, write to the Free Software
22 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 %%%
24 %%%----------------------------------------------------------------------
25
26 -module(ejabberd_s2s_in).
27 -author('alexey@process-one.net').
28 -behaviour(gen_fsm_compat).
29 -behaviour(mongoose_listener).
30
31 %% mongoose_listener API
32 -export([socket_type/0,
33 start_listener/1]).
34
35 %% External exports
36 -export([start/2,
37 start_link/2,
38 match_domain/2]).
39
40 %% gen_fsm callbacks
41 -export([init/1,
42 wait_for_stream/2,
43 wait_for_feature_request/2,
44 stream_established/2,
45 handle_event/3,
46 handle_sync_event/4,
47 code_change/4,
48 handle_info/3,
49 terminate/3]).
50
51 -ignore_xref([match_domain/2, socket_type/0, start/2, start_link/2,
52 stream_established/2, wait_for_feature_request/2, wait_for_stream/2]).
53
54 -include("mongoose.hrl").
55 -include("jlib.hrl").
56
57 -record(state, {socket,
58 sockmod :: ejabberd:sockmod(),
59 streamid :: binary(),
60 shaper,
61 tls = false :: boolean(),
62 tls_enabled = false :: boolean(),
63 tls_required = false :: boolean(),
64 tls_cert_verify = false :: boolean(),
65 tls_options :: mongoose_tls:options(),
66 server :: jid:server() | undefined,
67 host_type :: mongooseim:host_type() | undefined,
68 authenticated = false :: boolean(),
69 auth_domain :: binary() | undefined,
70 connections = dict:new(),
71 timer :: reference()
72 }).
73 -type state() :: #state{}.
74
75 -type statename() :: 'stream_established' | 'wait_for_feature_request'.
76 %% FSM handler return value
77 -type fsm_return() :: {'stop', Reason :: 'normal', state()}
78 | {'next_state', statename(), state()}
79 | {'next_state', statename(), state(), Timeout :: integer()}.
80 %-define(DBGFSM, true).
81
82 -ifdef(DBGFSM).
83 -define(FSMOPTS, [{debug, [trace]}]).
84 -else.
85 -define(FSMOPTS, []).
86 -endif.
87
88 %% Module start with or without supervisor:
89 -ifdef(NO_TRANSIENT_SUPERVISORS).
90 -define(SUPERVISOR_START, gen_fsm_compat:start(ejabberd_s2s_in, [SockData, Opts],
91 ?FSMOPTS)).
92 -else.
93 -define(SUPERVISOR_START, supervisor:start_child(ejabberd_s2s_in_sup,
94 [SockData, Opts])).
95 -endif.
96
97 -define(STREAM_HEADER(Version),
98 (<<"<?xml version='1.0'?>"
99 "<stream:stream "
100 "xmlns:stream='http://etherx.jabber.org/streams' "
101 "xmlns='jabber:server' "
102 "xmlns:db='jabber:server:dialback' "
103 "id='", (StateData#state.streamid)/binary, "'", Version/binary, ">">>)
104 ).
105
106 -type socket_data() :: {ejabberd:sockmod(), term()}.
107 -type options() :: #{shaper := atom(), tls := mongoose_tls:options(), atom() => any()}.
108
109 %%%----------------------------------------------------------------------
110 %%% API
111 %%%----------------------------------------------------------------------
112 -spec start(socket_data(), options()) ->
113 {error, _} | {ok, undefined | pid()} | {ok, undefined | pid(), _}.
114 start(SockData, Opts) ->
115 54 ?SUPERVISOR_START.
116
117 -spec start_link(socket_data(), options()) -> ignore | {error, _} | {ok, pid()}.
118 start_link(SockData, Opts) ->
119 54 gen_fsm_compat:start_link(ejabberd_s2s_in, [SockData, Opts], ?FSMOPTS).
120
121 -spec start_listener(options()) -> ok.
122 start_listener(Opts) ->
123 117 mongoose_tcp_listener:start_listener(Opts).
124
125 -spec socket_type() -> mongoose_listener:socket_type().
126 socket_type() ->
127 54 xml_stream.
128
129 %%%----------------------------------------------------------------------
130 %%% Callback functions from gen_fsm
131 %%%----------------------------------------------------------------------
132
133 %%----------------------------------------------------------------------
134 %% Func: init/1
135 %% Returns: {ok, StateName, StateData} |
136 %% {ok, StateName, StateData, Timeout} |
137 %% ignore |
138 %% {stop, StopReason}
139 %%----------------------------------------------------------------------
140 -spec init([socket_data() | options(), ...]) -> {ok, wait_for_stream, state()}.
141 init([{SockMod, Socket}, #{shaper := Shaper, tls := TLSOpts}]) ->
142 54 ?LOG_DEBUG(#{what => s2n_in_started,
143 text => <<"New incoming S2S connection">>,
144 54 sockmod => SockMod, socket => Socket}),
145 54 Timer = erlang:start_timer(ejabberd_s2s:timeout(), self(), []),
146 54 {ok, wait_for_stream,
147 #state{socket = Socket,
148 sockmod = SockMod,
149 streamid = new_id(),
150 shaper = Shaper,
151 tls_enabled = false,
152 tls_options = TLSOpts,
153 timer = Timer}}.
154
155 %%----------------------------------------------------------------------
156 %% Func: StateName/2
157 %% Returns: {next_state, NextStateName, NextStateData} |
158 %% {next_state, NextStateName, NextStateData, Timeout} |
159 %% {stop, Reason, NewStateData}
160 %%----------------------------------------------------------------------
161
162 -spec wait_for_stream(ejabberd:xml_stream_item(), state()) -> fsm_return().
163 wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
164 79 case maps:from_list(Attrs) of
165 AttrMap = #{<<"xmlns">> := <<"jabber:server">>, <<"to">> := Server} ->
166 77 case StateData#state.server of
167 undefined ->
168 52 case mongoose_domain_api:get_host_type(Server) of
169 {error, not_found} ->
170 1 stream_start_error(StateData, mongoose_xmpp_errors:host_unknown());
171 {ok, HostType} ->
172 51 UseTLS = mongoose_config:get_opt([{s2s, HostType}, use_starttls]),
173 51 {StartTLS, TLSRequired, TLSCertVerify} = get_tls_params(UseTLS),
174 51 start_stream(AttrMap, StateData#state{server = Server,
175 host_type = HostType,
176 tls = StartTLS,
177 tls_required = TLSRequired,
178 tls_cert_verify = TLSCertVerify})
179 end;
180 Server ->
181 24 start_stream(AttrMap, StateData);
182 _Other ->
183 1 Msg = <<"The 'to' attribute differs from the originally provided one">>,
184 1 stream_start_error(StateData, mongoose_xmpp_errors:host_unknown(?MYLANG, Msg))
185 end;
186 #{<<"xmlns">> := <<"jabber:server">>} ->
187 1 Msg = <<"The 'to' attribute is missing">>,
188 1 stream_start_error(StateData, mongoose_xmpp_errors:improper_addressing(?MYLANG, Msg));
189 _ ->
190 1 stream_start_error(StateData, mongoose_xmpp_errors:invalid_namespace())
191 end;
192 wait_for_stream({xmlstreamerror, _}, StateData) ->
193
:-(
stream_start_error(StateData, mongoose_xmpp_errors:xml_not_well_formed());
194 wait_for_stream(timeout, StateData) ->
195
:-(
{stop, normal, StateData};
196 wait_for_stream(closed, StateData) ->
197
:-(
{stop, normal, StateData}.
198
199 start_stream(#{<<"version">> := <<"1.0">>, <<"from">> := RemoteServer},
200 StateData = #state{tls = true, authenticated = false, server = Server,
201 host_type = HostType}) ->
202 43 SASL = case StateData#state.tls_enabled of
203 true ->
204 21 verify_cert_and_get_sasl(StateData#state.sockmod,
205 StateData#state.socket,
206 StateData#state.tls_cert_verify);
207 _Else ->
208 22 []
209 end,
210 43 StartTLS = get_tls_xmlel(StateData),
211 43 case SASL of
212 {error_cert_verif, CertError} ->
213 1 ?LOG_INFO(#{what => s2s_connection_closing,
214 text => <<"Closing s2s connection">>,
215 server => StateData#state.server,
216 remote_server => RemoteServer,
217 reason => cert_error,
218 1 cert_error => CertError}),
219 1 Res = stream_start_error(StateData,
220 mongoose_xmpp_errors:policy_violation(?MYLANG, CertError)),
221 1 {atomic, Pid} = ejabberd_s2s:find_connection(jid:make(<<>>, Server, <<>>),
222 jid:make(<<>>, RemoteServer, <<>>)),
223 1 ejabberd_s2s_out:stop_connection(Pid),
224 1 Res;
225 _ ->
226 42 send_text(StateData, ?STREAM_HEADER(<<" version='1.0'">>)),
227 42 send_element(StateData,
228 #xmlel{name = <<"stream:features">>,
229 children = SASL ++ StartTLS ++ stream_features(HostType, Server)}),
230 42 {next_state, wait_for_feature_request, StateData}
231 end;
232 start_stream(#{<<"version">> := <<"1.0">>},
233 StateData = #state{authenticated = true, host_type = HostType, server = Server}) ->
234 3 send_text(StateData, ?STREAM_HEADER(<<" version='1.0'">>)),
235 3 send_element(StateData, #xmlel{name = <<"stream:features">>,
236 children = stream_features(HostType, Server)}),
237 3 {next_state, stream_established, StateData};
238 start_stream(#{<<"xmlns:db">> := <<"jabber:server:dialback">>}, StateData) ->
239 27 send_text(StateData, ?STREAM_HEADER(<<>>)),
240 27 {next_state, stream_established, StateData};
241 start_stream(_, StateData) ->
242 2 stream_start_error(StateData, mongoose_xmpp_errors:invalid_xml()).
243
244 stream_start_error(StateData, Error) ->
245 7 send_text(StateData, ?STREAM_HEADER(<<>>)),
246 7 send_element(StateData, Error),
247 7 send_text(StateData, ?STREAM_TRAILER),
248 7 {stop, normal, StateData}.
249
250 -spec wait_for_feature_request(ejabberd:xml_stream_item(), state()
251 ) -> fsm_return().
252 wait_for_feature_request({xmlstreamelement, El}, StateData) ->
253 42 #xmlel{name = Name, attrs = Attrs, children = Els} = El,
254 42 TLS = StateData#state.tls,
255 42 TLSEnabled = StateData#state.tls_enabled,
256 42 SockMod = (StateData#state.sockmod):get_sockmod(StateData#state.socket),
257 42 case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
258 {?NS_TLS, <<"starttls">>} when TLS == true,
259 TLSEnabled == false,
260 SockMod == gen_tcp ->
261 22 ?LOG_DEBUG(#{what => s2s_starttls}),
262 22 TLSOpts = tls_options_with_certfile(StateData),
263 22 TLSSocket = mongoose_transport:starttls(StateData#state.sockmod,
264 StateData#state.socket, TLSOpts,
265 exml:to_binary(
266 #xmlel{name = <<"proceed">>,
267 attrs = [{<<"xmlns">>, ?NS_TLS}]})),
268 22 {next_state, wait_for_stream,
269 StateData#state{socket = TLSSocket,
270 streamid = new_id(),
271 tls_enabled = true,
272 tls_options = TLSOpts
273 }};
274 {?NS_SASL, <<"auth">>} when TLSEnabled ->
275 5 Mech = xml:get_attr_s(<<"mechanism">>, Attrs),
276 5 case Mech of
277 <<"EXTERNAL">> ->
278 4 Auth = jlib:decode_base64(xml:get_cdata(Els)),
279 4 AuthDomain = jid:nameprep(Auth),
280 4 CertData = (StateData#state.sockmod):get_peer_certificate(
281 StateData#state.socket),
282 4 AuthRes = check_auth_domain(AuthDomain, CertData),
283 4 handle_auth_res(AuthRes, AuthDomain, StateData);
284 _ ->
285 1 send_element(StateData,
286 #xmlel{name = <<"failure">>,
287 attrs = [{<<"xmlns">>, ?NS_SASL}],
288 children = [#xmlel{name = <<"invalid-mechanism">>}]}),
289 1 {stop, normal, StateData}
290 end;
291 _ ->
292 15 stream_established({xmlstreamelement, El}, StateData)
293 end;
294 wait_for_feature_request({xmlstreamend, _Name}, StateData) ->
295
:-(
send_text(StateData, ?STREAM_TRAILER),
296
:-(
{stop, normal, StateData};
297 wait_for_feature_request({xmlstreamerror, _}, StateData) ->
298
:-(
send_text(StateData, <<(mongoose_xmpp_errors:xml_not_well_formed_bin())/binary, (?STREAM_TRAILER)/binary>>),
299
:-(
{stop, normal, StateData};
300 wait_for_feature_request(closed, StateData) ->
301
:-(
{stop, normal, StateData}.
302
303 tls_options_with_certfile(#state{host_type = HostType, tls_options = TLSOptions}) ->
304 22 case ejabberd_s2s:lookup_certfile(HostType) of
305 22 {ok, CertFile} -> TLSOptions#{certfile => CertFile};
306
:-(
{error, not_found} -> TLSOptions
307 end.
308
309 -spec stream_established(ejabberd:xml_stream_item(), state()) -> fsm_return().
310 stream_established({xmlstreamelement, El}, StateData) ->
311 126 cancel_timer(StateData#state.timer),
312 126 Timer = erlang:start_timer(ejabberd_s2s:timeout(), self(), []),
313 126 case is_key_packet(El) of
314 {key, To, From, Id, Key} ->
315 28 ?LOG_DEBUG(#{what => s2s_in_get_key,
316 28 to => To, from => From, message_id => Id, key => Key}),
317 28 LTo = jid:nameprep(To),
318 28 LFrom = jid:nameprep(From),
319 %% Checks if the from domain is allowed and if the to
320 %% domain is handled by this server:
321 28 case {ejabberd_s2s:allow_host(LTo, LFrom),
322 28 mongoose_router:is_registered_route(LTo)
323
:-(
orelse ejabberd_router:is_component_dirty(LTo)} of
324 {true, true} ->
325 28 ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom),
326 28 ejabberd_s2s_out:start(LTo, LFrom,
327 {verify, self(),
328 Key, StateData#state.streamid}),
329 28 Conns = dict:store({LFrom, LTo}, wait_for_verification,
330 StateData#state.connections),
331 28 change_shaper(StateData, LTo, jid:make(<<>>, LFrom, <<>>)),
332 28 {next_state,
333 stream_established,
334 StateData#state{connections = Conns,
335 timer = Timer}};
336 {_, false} ->
337
:-(
send_text(StateData, exml:to_binary(mongoose_xmpp_errors:host_unknown())),
338
:-(
{stop, normal, StateData};
339 {false, _} ->
340
:-(
send_text(StateData, exml:to_binary(mongoose_xmpp_errors:invalid_from())),
341
:-(
{stop, normal, StateData}
342 end;
343 {verify, To, From, Id, Key} ->
344 27 ?LOG_DEBUG(#{what => s2s_in_verify_key,
345 27 to => To, from => From, message_id => Id, key => Key}),
346 27 LTo = jid:nameprep(To),
347 27 LFrom = jid:nameprep(From),
348 27 Type = case ejabberd_s2s:key(StateData#state.host_type, {LTo, LFrom}, Id) of
349 27 Key -> <<"valid">>;
350
:-(
_ -> <<"invalid">>
351 end,
352 27 send_element(StateData,
353 #xmlel{name = <<"db:verify">>,
354 attrs = [{<<"from">>, To},
355 {<<"to">>, From},
356 {<<"id">>, Id},
357 {<<"type">>, Type}]}),
358 27 {next_state, stream_established, StateData#state{timer = Timer}};
359 _ ->
360 71 NewEl = jlib:remove_attr(<<"xmlns">>, El),
361 71 #xmlel{attrs = Attrs} = NewEl,
362 71 FromS = xml:get_attr_s(<<"from">>, Attrs),
363 71 From = jid:from_binary(FromS),
364 71 ToS = xml:get_attr_s(<<"to">>, Attrs),
365 71 To = jid:from_binary(ToS),
366 71 case {From, To} of
367
:-(
{error, _} -> ok;
368
:-(
{_, error} -> ok;
369 71 _ -> route_incoming_stanza(From, To, NewEl, StateData)
370 end,
371 71 {next_state, stream_established, StateData#state{timer = Timer}}
372 end;
373 stream_established({valid, From, To}, StateData) ->
374 27 send_element(StateData,
375 #xmlel{name = <<"db:result">>,
376 attrs = [{<<"from">>, To},
377 {<<"to">>, From},
378 {<<"type">>, <<"valid">>}]}),
379 27 LFrom = jid:nameprep(From),
380 27 LTo = jid:nameprep(To),
381 27 NSD = StateData#state{
382 connections = dict:store({LFrom, LTo}, established,
383 StateData#state.connections)},
384 27 {next_state, stream_established, NSD};
385 stream_established({invalid, From, To}, StateData) ->
386
:-(
send_element(StateData,
387 #xmlel{name = <<"db:result">>,
388 attrs = [{<<"from">>, To},
389 {<<"to">>, From},
390 {<<"type">>, <<"invalid">>}]}),
391
:-(
LFrom = jid:nameprep(From),
392
:-(
LTo = jid:nameprep(To),
393
:-(
NSD = StateData#state{
394 connections = dict:erase({LFrom, LTo},
395 StateData#state.connections)},
396
:-(
{next_state, stream_established, NSD};
397 stream_established({xmlstreamend, _Name}, StateData) ->
398 1 send_text(StateData, ?STREAM_TRAILER),
399 1 {stop, normal, StateData};
400 stream_established({xmlstreamerror, _}, StateData) ->
401 1 send_text(StateData,
402 <<(mongoose_xmpp_errors:xml_not_well_formed_bin())/binary, (?STREAM_TRAILER)/binary>>),
403 1 {stop, normal, StateData};
404 stream_established(timeout, StateData) ->
405
:-(
{stop, normal, StateData};
406 stream_established(closed, StateData) ->
407 27 {stop, normal, StateData}.
408
409 -spec route_incoming_stanza(From :: jid:jid(),
410 To :: jid:jid(),
411 El :: exml:element(),
412 StateData :: state()) ->
413 mongoose_acc:t() | error.
414 route_incoming_stanza(From, To, El, StateData) ->
415 71 LFromS = From#jid.lserver,
416 71 LToS = To#jid.lserver,
417 71 #xmlel{name = Name} = El,
418 71 Acc = mongoose_acc:new(#{ location => ?LOCATION,
419 lserver => LToS,
420 element => El,
421 from_jid => From,
422 to_jid => To }),
423 71 case is_s2s_authenticated(LFromS, LToS, StateData) of
424 true ->
425 1 route_stanza(Name, Acc);
426 false ->
427 70 case is_s2s_connected(LFromS, LToS, StateData) of
428 true ->
429 70 route_stanza(Name, Acc);
430 false ->
431
:-(
error
432 end
433 end.
434
435 is_s2s_authenticated(_, _, #state{authenticated = false}) ->
436 70 false;
437 is_s2s_authenticated(LFrom, LTo, #state{auth_domain = LFrom}) ->
438 1 mongoose_router:is_registered_route(LTo)
439
:-(
orelse ejabberd_router:is_component_dirty(LTo);
440 is_s2s_authenticated(_, _, _) ->
441
:-(
false.
442
443 is_s2s_connected(LFrom, LTo, StateData) ->
444 70 case dict:find({LFrom, LTo}, StateData#state.connections) of
445 {ok, established} ->
446 70 true;
447 _ ->
448
:-(
false
449 end.
450
451 -spec route_stanza(binary(), mongoose_acc:t()) -> mongoose_acc:t().
452 route_stanza(<<"iq">>, Acc) ->
453 50 route_stanza(Acc);
454 route_stanza(<<"message">>, Acc) ->
455 21 route_stanza(Acc);
456 route_stanza(<<"presence">>, Acc) ->
457
:-(
route_stanza(Acc);
458 route_stanza(_, _Acc) ->
459
:-(
error.
460
461 -spec route_stanza(mongoose_acc:t()) -> mongoose_acc:t().
462 route_stanza(Acc) ->
463 71 From = mongoose_acc:from_jid(Acc),
464 71 To = mongoose_acc:to_jid(Acc),
465 71 Acc1 = mongoose_hooks:s2s_receive_packet(Acc),
466 71 ejabberd_router:route(From, To, Acc1).
467
468 %%----------------------------------------------------------------------
469 %% Func: StateName/3
470 %% Returns: {next_state, NextStateName, NextStateData} |
471 %% {next_state, NextStateName, NextStateData, Timeout} |
472 %% {reply, Reply, NextStateName, NextStateData} |
473 %% {reply, Reply, NextStateName, NextStateData, Timeout} |
474 %% {stop, Reason, NewStateData} |
475 %% {stop, Reason, Reply, NewStateData}
476 %%----------------------------------------------------------------------
477 %state_name(Event, From, StateData) ->
478 % Reply = ok,
479 % {reply, Reply, state_name, StateData}.
480
481 %%----------------------------------------------------------------------
482 %% Func: handle_event/3
483 %% Returns: {next_state, NextStateName, NextStateData} |
484 %% {next_state, NextStateName, NextStateData, Timeout} |
485 %% {stop, Reason, NewStateData}
486 %%----------------------------------------------------------------------
487 handle_event(_Event, StateName, StateData) ->
488 71 {next_state, StateName, StateData}.
489
490 %%----------------------------------------------------------------------
491 %% Func: handle_sync_event/4
492 %% Returns: The associated StateData for this connection
493 %% {reply, Reply, NextStateName, NextStateData}
494 %% Reply = {state_infos, [{InfoName::atom(), InfoValue::any()]
495 %%----------------------------------------------------------------------
496 -spec handle_sync_event(any(), any(), statename(), state()
497 ) -> {'reply', 'ok' | {'state_infos', [any(), ...]}, atom(), state()}.
498 handle_sync_event(get_state_infos, _From, StateName, StateData) ->
499 1 SockMod = StateData#state.sockmod,
500 1 {Addr, Port} = try SockMod:peername(StateData#state.socket) of
501 1 {ok, {A, P}} -> {A, P};
502
:-(
{error, _} -> {unknown, unknown}
503 catch
504
:-(
_:_ -> {unknown, unknown}
505 end,
506 1 Domains = case StateData#state.authenticated of
507 true ->
508
:-(
[StateData#state.auth_domain];
509 false ->
510 1 Connections = StateData#state.connections,
511 1 [D || {{D, _}, established} <-
512 1 dict:to_list(Connections)]
513 end,
514 1 Infos = [
515 {direction, in},
516 {statename, StateName},
517 {addr, Addr},
518 {port, Port},
519 {streamid, StateData#state.streamid},
520 {tls, StateData#state.tls},
521 {tls_enabled, StateData#state.tls_enabled},
522 {tls_options, StateData#state.tls_options},
523 {authenticated, StateData#state.authenticated},
524 {shaper, StateData#state.shaper},
525 {sockmod, SockMod},
526 {domains, Domains}
527 ],
528 1 Reply = {state_infos, Infos},
529 1 {reply, Reply, StateName, StateData};
530
531 %%----------------------------------------------------------------------
532 %% Func: handle_sync_event/4
533 %% Returns: {next_state, NextStateName, NextStateData} |
534 %% {next_state, NextStateName, NextStateData, Timeout} |
535 %% {reply, Reply, NextStateName, NextStateData} |
536 %% {reply, Reply, NextStateName, NextStateData, Timeout} |
537 %% {stop, Reason, NewStateData} |
538 %% {stop, Reason, Reply, NewStateData}
539 %%----------------------------------------------------------------------
540 handle_sync_event(_Event, _From, StateName, StateData) ->
541
:-(
Reply = ok,
542
:-(
{reply, Reply, StateName, StateData}.
543
544
545 code_change(_OldVsn, StateName, StateData, _Extra) ->
546
:-(
{ok, StateName, StateData}.
547
548 %%----------------------------------------------------------------------
549 %% Func: handle_info/3
550 %% Returns: {next_state, NextStateName, NextStateData} |
551 %% {next_state, NextStateName, NextStateData, Timeout} |
552 %% {stop, Reason, NewStateData}
553 %%----------------------------------------------------------------------
554 -spec handle_info(_, _, _) -> {next_state, atom(), state()} | {stop, normal, state()}.
555 handle_info({send_text, Text}, StateName, StateData) ->
556
:-(
?LOG_ERROR(#{what => s2s_in_send_text,
557 text => <<"Deprecated send_text info in ejabberd_s2s_in">>,
558
:-(
send_text => Text}),
559
:-(
send_text(StateData, Text),
560
:-(
{next_state, StateName, StateData};
561 handle_info({timeout, Timer, _}, _StateName,
562 #state{timer = Timer} = StateData) ->
563
:-(
{stop, normal, StateData};
564 handle_info(_, StateName, StateData) ->
565
:-(
{next_state, StateName, StateData}.
566
567
568 %%----------------------------------------------------------------------
569 %% Func: terminate/3
570 %% Purpose: Shutdown the fsm
571 %% Returns: any
572 %%----------------------------------------------------------------------
573 -spec terminate(any(), statename(), state()) -> 'ok'.
574 terminate(Reason, _StateName, StateData) ->
575 38 ?LOG_DEBUG(#{what => s2s_in_stopped, reason => Reason}),
576 38 (StateData#state.sockmod):close(StateData#state.socket),
577 38 ok.
578
579 %%%----------------------------------------------------------------------
580 %%% Internal functions
581 %%%----------------------------------------------------------------------
582
583 -spec send_text(state(), binary()) -> binary().
584 send_text(StateData, Text) ->
585 200 (StateData#state.sockmod):send(StateData#state.socket, Text).
586
587
588 -spec send_element(state(), exml:element()) -> binary().
589 send_element(StateData, El) ->
590 111 send_text(StateData, exml:to_binary(El)).
591
592 -spec stream_features(mongooseim:host_type(), binary()) -> [exml:element()].
593 stream_features(HostType, Domain) ->
594 45 mongoose_hooks:s2s_stream_features(HostType, Domain).
595
596 -spec change_shaper(state(), jid:lserver(), jid:jid()) -> any().
597 change_shaper(StateData, Host, JID) ->
598 28 {ok, HostType} = mongoose_domain_api:get_host_type(Host),
599 28 Shaper = acl:match_rule(HostType, StateData#state.shaper, JID),
600 28 (StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper).
601
602
603 -spec new_id() -> binary().
604 new_id() ->
605 79 mongoose_bin:gen_from_crypto().
606
607
608 -spec cancel_timer(reference()) -> 'ok'.
609 cancel_timer(Timer) ->
610 126 erlang:cancel_timer(Timer),
611 126 receive
612 {timeout, Timer, _} ->
613
:-(
ok
614 after 0 ->
615 126 ok
616 end.
617
618
619 -spec is_key_packet(exml:element()) -> 'false' | {'key', _, _, _, binary()}
620 | {'verify', _, _, _, binary()}.
621 is_key_packet(#xmlel{name = Name, attrs = Attrs,
622 children = Els}) when Name == <<"db:result">> ->
623 28 {key,
624 xml:get_attr_s(<<"to">>, Attrs),
625 xml:get_attr_s(<<"from">>, Attrs),
626 xml:get_attr_s(<<"id">>, Attrs),
627 xml:get_cdata(Els)};
628 is_key_packet(#xmlel{name = Name, attrs = Attrs,
629 children = Els}) when Name == <<"db:verify">> ->
630 27 {verify,
631 xml:get_attr_s(<<"to">>, Attrs),
632 xml:get_attr_s(<<"from">>, Attrs),
633 xml:get_attr_s(<<"id">>, Attrs),
634 xml:get_cdata(Els)};
635 is_key_packet(_) ->
636 71 false.
637
638
639 -spec match_domain(binary(), binary()) -> boolean().
640 match_domain(Domain, Domain) ->
641 3 true;
642 match_domain(Domain, Pattern) ->
643 3 DLabels = binary:split(Domain, <<".">>, [global]),
644 3 PLabels = binary:split(Pattern, <<".">>, [global]),
645 3 match_labels(DLabels, PLabels).
646
647
648 -spec match_labels([binary()], [binary()]) -> boolean().
649 match_labels([], []) ->
650
:-(
true;
651 match_labels([], [_ | _]) ->
652
:-(
false;
653 match_labels([_ | _], []) ->
654
:-(
false;
655 match_labels([DL | DLabels], [PL | PLabels]) ->
656 3 PLlist = binary_to_list(PL),
657 3 case lists:all(fun(C) -> (($a =< C) andalso (C =< $z))
658
:-(
orelse (($0 =< C) andalso (C =< $9))
659
:-(
orelse (C == $-) orelse (C == $*)
660 end, PLlist) of
661 true ->
662 3 Regexp = xmerl_regexp:sh_to_awk(PLlist),
663 3 case re:run(binary_to_list(DL), Regexp, [{capture, none}]) of
664 match ->
665
:-(
match_labels(DLabels, PLabels);
666 nomatch ->
667 3 false
668 end;
669 false ->
670
:-(
false
671 end.
672
673 verify_cert_and_get_sasl(SockMod, Socket, TLSCertVerify) ->
674 21 case SockMod:get_peer_certificate(Socket) of
675 {ok, _} ->
676 6 [#xmlel{name = <<"mechanisms">>,
677 attrs = [{<<"xmlns">>, ?NS_SASL}],
678 children = [#xmlel{name = <<"mechanism">>,
679 children = [#xmlcdata{content = <<"EXTERNAL">>}]}]}];
680 {bad_cert, CertVerifyRes} ->
681 15 check_sasl_tls_certveify(TLSCertVerify, CertVerifyRes);
682
:-(
no_peer_cert -> []
683 end.
684
685 check_sasl_tls_certveify(true, CertVerifyRes) ->
686 1 {error_cert_verif, CertVerifyRes};
687 check_sasl_tls_certveify(false, _) ->
688 14 [].
689
690 check_auth_domain(error, _) ->
691
:-(
false;
692 check_auth_domain(AuthDomain, {ok, Cert}) ->
693 4 case ejabberd_s2s:domain_utf8_to_ascii(AuthDomain) of
694 false ->
695 1 false;
696 PCAuthDomain ->
697 3 lists:any(
698 6 fun(D) -> match_domain( PCAuthDomain, D) end,
699 cert_utils:get_cert_domains(Cert))
700 end;
701 check_auth_domain(_, _) ->
702
:-(
false.
703
704 handle_auth_res(true, AuthDomain, StateData) ->
705 3 send_element(StateData,
706 #xmlel{name = <<"success">>,
707 attrs = [{<<"xmlns">>, ?NS_SASL}]}),
708 3 ?LOG_DEBUG(#{what => s2s_auth_success,
709 text => <<"Accepted s2s authentication">>,
710 3 socket => StateData#state.socket, auth_domain => AuthDomain}),
711 3 {next_state, wait_for_stream,
712 StateData#state{streamid = new_id(),
713 authenticated = true,
714 auth_domain = AuthDomain
715 }};
716 handle_auth_res(_, _, StateData) ->
717 1 send_element(StateData,
718 #xmlel{name = <<"failure">>,
719 attrs = [{<<"xmlns">>, ?NS_SASL}]}),
720 1 send_text(StateData, ?STREAM_TRAILER),
721 1 {stop, normal, StateData}.
722
723
724 get_tls_params(false) ->
725 19 {false, false, false};
726 get_tls_params(true) ->
727
:-(
{true, false, false};
728 get_tls_params(optional) ->
729 11 {true, false, false};
730 get_tls_params(required) ->
731 11 {true, true, false};
732 get_tls_params(required_trusted) ->
733 10 {true, true, true}.
734
735 get_tls_xmlel(#state{tls_enabled = true}) ->
736 21 [];
737 get_tls_xmlel(#state{tls_enabled = false, tls_required = false}) ->
738 8 [#xmlel{name = <<"starttls">>,
739 attrs = [{<<"xmlns">>, ?NS_TLS}]}];
740 get_tls_xmlel(#state{tls_enabled = false, tls_required = true}) ->
741 14 [#xmlel{name = <<"starttls">>,
742 attrs = [{<<"xmlns">>, ?NS_TLS}],
743 children = [#xmlel{name = <<"required">>}]}].
Line Hits Source