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