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