./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 = [] :: [{_, _}],
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(), 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
:-(
?SUPERVISOR_START.
116
117 -spec start_link(socket_data(), options()) -> ignore | {error, _} | {ok, pid()}.
118 start_link(SockData, Opts) ->
119
:-(
gen_fsm_compat:start_link(ejabberd_s2s_in, [SockData, Opts], ?FSMOPTS).
120
121 -spec start_listener(options()) -> ok.
122 start_listener(Opts) ->
123 76 mongoose_tcp_listener:start_listener(Opts).
124
125 -spec socket_type() -> mongoose_listener:socket_type().
126 socket_type() ->
127
:-(
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}, Opts = #{shaper := Shaper}]) ->
142
:-(
?LOG_DEBUG(#{what => s2n_in_started,
143 text => <<"New incoming S2S connection">>,
144
:-(
sockmod => SockMod, socket => Socket}),
145
:-(
TLSOpts = maps:get(tls, Opts, []),
146
:-(
Timer = erlang:start_timer(ejabberd_s2s:timeout(), self(), []),
147
:-(
{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
:-(
case maps:from_list(Attrs) of
166 AttrMap = #{<<"xmlns">> := <<"jabber:server">>, <<"to">> := Server} ->
167
:-(
case StateData#state.server of
168 undefined ->
169
:-(
case mongoose_domain_api:get_host_type(Server) of
170 {error, not_found} ->
171
:-(
stream_start_error(StateData, mongoose_xmpp_errors:host_unknown());
172 {ok, HostType} ->
173
:-(
UseTLS = mongoose_config:get_opt([{s2s, HostType}, use_starttls]),
174
:-(
{StartTLS, TLSRequired, TLSCertVerify} = get_tls_params(UseTLS),
175
:-(
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
:-(
start_stream(AttrMap, StateData);
183 _Other ->
184
:-(
Msg = <<"The 'to' attribute differs from the originally provided one">>,
185
:-(
stream_start_error(StateData, mongoose_xmpp_errors:host_unknown(?MYLANG, Msg))
186 end;
187 #{<<"xmlns">> := <<"jabber:server">>} ->
188
:-(
Msg = <<"The 'to' attribute is missing">>,
189
:-(
stream_start_error(StateData, mongoose_xmpp_errors:improper_addressing(?MYLANG, Msg));
190 _ ->
191
:-(
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
:-(
SASL = case StateData#state.tls_enabled of
204 true ->
205
:-(
verify_cert_and_get_sasl(StateData#state.sockmod,
206 StateData#state.socket,
207 StateData#state.tls_cert_verify);
208 _Else ->
209
:-(
[]
210 end,
211
:-(
StartTLS = get_tls_xmlel(StateData),
212
:-(
case SASL of
213 {error_cert_verif, CertError} ->
214
:-(
?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
:-(
cert_error => CertError}),
220
:-(
Res = stream_start_error(StateData,
221 mongoose_xmpp_errors:policy_violation(?MYLANG, CertError)),
222
:-(
{atomic, Pid} = ejabberd_s2s:find_connection(jid:make(<<>>, Server, <<>>),
223 jid:make(<<>>, RemoteServer, <<>>)),
224
:-(
ejabberd_s2s_out:stop_connection(Pid),
225
:-(
Res;
226 _ ->
227
:-(
send_text(StateData, ?STREAM_HEADER(<<" version='1.0'">>)),
228
:-(
send_element(StateData,
229 #xmlel{name = <<"stream:features">>,
230 children = SASL ++ StartTLS ++ stream_features(HostType, Server)}),
231
:-(
{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
:-(
send_text(StateData, ?STREAM_HEADER(<<" version='1.0'">>)),
236
:-(
send_element(StateData, #xmlel{name = <<"stream:features">>,
237 children = stream_features(HostType, Server)}),
238
:-(
{next_state, stream_established, StateData};
239 start_stream(#{<<"xmlns:db">> := <<"jabber:server:dialback">>}, StateData) ->
240
:-(
send_text(StateData, ?STREAM_HEADER(<<>>)),
241
:-(
{next_state, stream_established, StateData};
242 start_stream(_, StateData) ->
243
:-(
stream_start_error(StateData, mongoose_xmpp_errors:invalid_xml()).
244
245 stream_start_error(StateData, Error) ->
246
:-(
send_text(StateData, ?STREAM_HEADER(<<>>)),
247
:-(
send_element(StateData, Error),
248
:-(
send_text(StateData, ?STREAM_TRAILER),
249
:-(
{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
:-(
#xmlel{name = Name, attrs = Attrs, children = Els} = El,
255
:-(
TLS = StateData#state.tls,
256
:-(
TLSEnabled = StateData#state.tls_enabled,
257
:-(
SockMod = (StateData#state.sockmod):get_sockmod(StateData#state.socket),
258
:-(
case {xml:get_attr_s(<<"xmlns">>, Attrs), Name} of
259 {?NS_TLS, <<"starttls">>} when TLS == true,
260 TLSEnabled == false,
261 SockMod == gen_tcp ->
262
:-(
?LOG_DEBUG(#{what => s2s_starttls}),
263
:-(
TLSOpts = tls_options(StateData),
264
:-(
TLSSocket = (StateData#state.sockmod):starttls(
265 StateData#state.socket, TLSOpts,
266 exml:to_binary(
267 #xmlel{name = <<"proceed">>,
268 attrs = [{<<"xmlns">>, ?NS_TLS}]})),
269
:-(
{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
:-(
Mech = xml:get_attr_s(<<"mechanism">>, Attrs),
277
:-(
case Mech of
278 <<"EXTERNAL">> ->
279
:-(
Auth = jlib:decode_base64(xml:get_cdata(Els)),
280
:-(
AuthDomain = jid:nameprep(Auth),
281
:-(
CertData = (StateData#state.sockmod):get_peer_certificate(
282 StateData#state.socket),
283
:-(
AuthRes = check_auth_domain(AuthDomain, CertData),
284
:-(
handle_auth_res(AuthRes, AuthDomain, StateData);
285 _ ->
286
:-(
send_element(StateData,
287 #xmlel{name = <<"failure">>,
288 attrs = [{<<"xmlns">>, ?NS_SASL}],
289 children = [#xmlel{name = <<"invalid-mechanism">>}]}),
290
:-(
{stop, normal, StateData}
291 end;
292 _ ->
293
:-(
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
:-(
case ejabberd_s2s:lookup_certfile(HostType) of
306 {ok, CertFile} ->
307
:-(
[{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
:-(
cancel_timer(StateData#state.timer),
315
:-(
Timer = erlang:start_timer(ejabberd_s2s:timeout(), self(), []),
316
:-(
case is_key_packet(El) of
317 {key, To, From, Id, Key} ->
318
:-(
?LOG_DEBUG(#{what => s2s_in_get_key,
319
:-(
to => To, from => From, message_id => Id, key => Key}),
320
:-(
LTo = jid:nameprep(To),
321
:-(
LFrom = jid:nameprep(From),
322 %% Checks if the from domain is allowed and if the to
323 %% domain is handled by this server:
324
:-(
case {ejabberd_s2s:allow_host(LTo, LFrom),
325
:-(
mongoose_router:is_registered_route(LTo)
326
:-(
orelse ejabberd_router:is_component_dirty(LTo)} of
327 {true, true} ->
328
:-(
ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom),
329
:-(
ejabberd_s2s_out:start(LTo, LFrom,
330 {verify, self(),
331 Key, StateData#state.streamid}),
332
:-(
Conns = dict:store({LFrom, LTo}, wait_for_verification,
333 StateData#state.connections),
334
:-(
change_shaper(StateData, LTo, jid:make(<<>>, LFrom, <<>>)),
335
:-(
{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
:-(
?LOG_DEBUG(#{what => s2s_in_verify_key,
348
:-(
to => To, from => From, message_id => Id, key => Key}),
349
:-(
LTo = jid:nameprep(To),
350
:-(
LFrom = jid:nameprep(From),
351
:-(
Type = case ejabberd_s2s:key(StateData#state.host_type, {LTo, LFrom}, Id) of
352
:-(
Key -> <<"valid">>;
353
:-(
_ -> <<"invalid">>
354 end,
355
:-(
send_element(StateData,
356 #xmlel{name = <<"db:verify">>,
357 attrs = [{<<"from">>, To},
358 {<<"to">>, From},
359 {<<"id">>, Id},
360 {<<"type">>, Type}]}),
361
:-(
{next_state, stream_established, StateData#state{timer = Timer}};
362 _ ->
363
:-(
NewEl = jlib:remove_attr(<<"xmlns">>, El),
364
:-(
#xmlel{attrs = Attrs} = NewEl,
365
:-(
FromS = xml:get_attr_s(<<"from">>, Attrs),
366
:-(
From = jid:from_binary(FromS),
367
:-(
ToS = xml:get_attr_s(<<"to">>, Attrs),
368
:-(
To = jid:from_binary(ToS),
369
:-(
case {From, To} of
370
:-(
{error, _} -> ok;
371
:-(
{_, error} -> ok;
372
:-(
_ -> route_incoming_stanza(From, To, NewEl, StateData)
373 end,
374
:-(
{next_state, stream_established, StateData#state{timer = Timer}}
375 end;
376 stream_established({valid, From, To}, StateData) ->
377
:-(
send_element(StateData,
378 #xmlel{name = <<"db:result">>,
379 attrs = [{<<"from">>, To},
380 {<<"to">>, From},
381 {<<"type">>, <<"valid">>}]}),
382
:-(
LFrom = jid:nameprep(From),
383
:-(
LTo = jid:nameprep(To),
384
:-(
NSD = StateData#state{
385 connections = dict:store({LFrom, LTo}, established,
386 StateData#state.connections)},
387
:-(
{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
:-(
send_text(StateData, ?STREAM_TRAILER),
402
:-(
{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
:-(
{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
:-(
LFromS = From#jid.lserver,
419
:-(
LToS = To#jid.lserver,
420
:-(
#xmlel{name = Name} = El,
421
:-(
Acc = mongoose_acc:new(#{ location => ?LOCATION,
422 lserver => LToS,
423 element => El,
424 from_jid => From,
425 to_jid => To }),
426
:-(
case is_s2s_authenticated(LFromS, LToS, StateData) of
427 true ->
428
:-(
route_stanza(Name, Acc);
429 false ->
430
:-(
case is_s2s_connected(LFromS, LToS, StateData) of
431 true ->
432
:-(
route_stanza(Name, Acc);
433 false ->
434
:-(
error
435 end
436 end.
437
438 is_s2s_authenticated(_, _, #state{authenticated = false}) ->
439
:-(
false;
440 is_s2s_authenticated(LFrom, LTo, #state{auth_domain = LFrom}) ->
441
:-(
mongoose_router:is_registered_route(LTo)
442
:-(
orelse ejabberd_router:is_component_dirty(LTo);
443 is_s2s_authenticated(_, _, _) ->
444
:-(
false.
445
446 is_s2s_connected(LFrom, LTo, StateData) ->
447
:-(
case dict:find({LFrom, LTo}, StateData#state.connections) of
448 {ok, established} ->
449
:-(
true;
450 _ ->
451
:-(
false
452 end.
453
454 -spec route_stanza(binary(), mongoose_acc:t()) -> mongoose_acc:t().
455 route_stanza(<<"iq">>, Acc) ->
456
:-(
route_stanza(Acc);
457 route_stanza(<<"message">>, Acc) ->
458
:-(
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
:-(
From = mongoose_acc:from_jid(Acc),
467
:-(
To = mongoose_acc:to_jid(Acc),
468
:-(
Acc1 = mongoose_hooks:s2s_receive_packet(Acc),
469
:-(
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
:-(
{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
:-(
SockMod = StateData#state.sockmod,
503
:-(
{Addr, Port} = try SockMod:peername(StateData#state.socket) of
504
:-(
{ok, {A, P}} -> {A, P};
505
:-(
{error, _} -> {unknown, unknown}
506 catch
507
:-(
_:_ -> {unknown, unknown}
508 end,
509
:-(
Domains = case StateData#state.authenticated of
510 true ->
511
:-(
[StateData#state.auth_domain];
512 false ->
513
:-(
Connections = StateData#state.connections,
514
:-(
[D || {{D, _}, established} <-
515
:-(
dict:to_list(Connections)]
516 end,
517
:-(
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
:-(
Reply = {state_infos, Infos},
532
:-(
{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
:-(
?LOG_DEBUG(#{what => s2s_in_stopped, reason => Reason}),
579
:-(
(StateData#state.sockmod):close(StateData#state.socket),
580
:-(
ok.
581
582 %%%----------------------------------------------------------------------
583 %%% Internal functions
584 %%%----------------------------------------------------------------------
585
586 -spec send_text(state(), binary()) -> binary().
587 send_text(StateData, Text) ->
588
:-(
(StateData#state.sockmod):send(StateData#state.socket, Text).
589
590
591 -spec send_element(state(), exml:element()) -> binary().
592 send_element(StateData, El) ->
593
:-(
send_text(StateData, exml:to_binary(El)).
594
595 -spec stream_features(mongooseim:host_type(), binary()) -> [exml:element()].
596 stream_features(HostType, Domain) ->
597
:-(
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
:-(
{ok, HostType} = mongoose_domain_api:get_host_type(Host),
602
:-(
Shaper = acl:match_rule(HostType, StateData#state.shaper, JID),
603
:-(
(StateData#state.sockmod):change_shaper(StateData#state.socket, Shaper).
604
605
606 -spec new_id() -> binary().
607 new_id() ->
608
:-(
mongoose_bin:gen_from_crypto().
609
610
611 -spec cancel_timer(reference()) -> 'ok'.
612 cancel_timer(Timer) ->
613
:-(
erlang:cancel_timer(Timer),
614
:-(
receive
615 {timeout, Timer, _} ->
616
:-(
ok
617 after 0 ->
618
:-(
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
:-(
{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
:-(
{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
:-(
false.
640
641
642 -spec match_domain(binary(), binary()) -> boolean().
643 match_domain(Domain, Domain) ->
644
:-(
true;
645 match_domain(Domain, Pattern) ->
646
:-(
DLabels = binary:split(Domain, <<".">>, [global]),
647
:-(
PLabels = binary:split(Pattern, <<".">>, [global]),
648
:-(
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
:-(
PLlist = binary_to_list(PL),
660
:-(
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
:-(
Regexp = xmerl_regexp:sh_to_awk(PLlist),
666
:-(
case re:run(binary_to_list(DL), Regexp, [{capture, none}]) of
667 match ->
668
:-(
match_labels(DLabels, PLabels);
669 nomatch ->
670
:-(
false
671 end;
672 false ->
673
:-(
false
674 end.
675
676 verify_cert_and_get_sasl(SockMod, Socket, TLSCertVerify) ->
677
:-(
case SockMod:get_peer_certificate(Socket) of
678 {ok, _} ->
679
:-(
[#xmlel{name = <<"mechanisms">>,
680 attrs = [{<<"xmlns">>, ?NS_SASL}],
681 children = [#xmlel{name = <<"mechanism">>,
682 children = [#xmlcdata{content = <<"EXTERNAL">>}]}]}];
683 {bad_cert, CertVerifyRes} ->
684
:-(
check_sasl_tls_certveify(TLSCertVerify, CertVerifyRes);
685
:-(
no_peer_cert -> []
686 end.
687
688 check_sasl_tls_certveify(true, CertVerifyRes) ->
689
:-(
{error_cert_verif, CertVerifyRes};
690 check_sasl_tls_certveify(false, _) ->
691
:-(
[].
692
693 check_auth_domain(error, _) ->
694
:-(
false;
695 check_auth_domain(AuthDomain, {ok, Cert}) ->
696
:-(
case ejabberd_s2s:domain_utf8_to_ascii(AuthDomain) of
697 false ->
698
:-(
false;
699 PCAuthDomain ->
700
:-(
lists:any(
701
:-(
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
:-(
send_element(StateData,
709 #xmlel{name = <<"success">>,
710 attrs = [{<<"xmlns">>, ?NS_SASL}]}),
711
:-(
?LOG_DEBUG(#{what => s2s_auth_success,
712 text => <<"Accepted s2s authentication">>,
713
:-(
socket => StateData#state.socket, auth_domain => AuthDomain}),
714
:-(
{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
:-(
send_element(StateData,
721 #xmlel{name = <<"failure">>,
722 attrs = [{<<"xmlns">>, ?NS_SASL}]}),
723
:-(
send_text(StateData, ?STREAM_TRAILER),
724
:-(
{stop, normal, StateData}.
725
726
727 get_tls_params(false) ->
728
:-(
{false, false, false};
729 get_tls_params(true) ->
730
:-(
{true, false, false};
731 get_tls_params(optional) ->
732
:-(
{true, false, false};
733 get_tls_params(required) ->
734
:-(
{true, true, false};
735 get_tls_params(required_trusted) ->
736
:-(
{true, true, true}.
737
738 get_tls_xmlel(#state{tls_enabled = true}) ->
739
:-(
[];
740 get_tls_xmlel(#state{tls_enabled = false, tls_required = false}) ->
741
:-(
[#xmlel{name = <<"starttls">>,
742 attrs = [{<<"xmlns">>, ?NS_TLS}]}];
743 get_tls_xmlel(#state{tls_enabled = false, tls_required = true}) ->
744
:-(
[#xmlel{name = <<"starttls">>,
745 attrs = [{<<"xmlns">>, ?NS_TLS}],
746 children = [#xmlel{name = <<"required">>}]}].
Line Hits Source