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