./ct_report/coverage/ejabberd_s2s.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : ejabberd_s2s.erl
3 %%% Author : Alexey Shchepin <alexey@process-one.net>
4 %%% Purpose : S2S connections manager
5 %%% Created : 7 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).
27 -author('alexey@process-one.net').
28
29 -xep([{xep, 185}, {version, "1.0"}]).
30
31 -behaviour(gen_server).
32 -behaviour(xmpp_router).
33
34 %% API
35 -export([start_link/0,
36 filter/4,
37 route/4,
38 have_connection/1,
39 key/3,
40 get_connections_pids/1,
41 try_register/1,
42 remove_connection/2,
43 find_connection/2,
44 dirty_get_connections/0,
45 allow_host/2,
46 incoming_s2s_number/0,
47 outgoing_s2s_number/0,
48 domain_utf8_to_ascii/1,
49 timeout/0,
50 lookup_certfile/1
51 ]).
52
53 %% Hooks callbacks
54 -export([node_cleanup/2]).
55
56 %% gen_server callbacks
57 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
58 terminate/2, code_change/3]).
59
60 %% ejabberd API
61 -export([get_info_s2s_connections/1]).
62
63 -ignore_xref([dirty_get_connections/0, get_info_s2s_connections/1, have_connection/1,
64 incoming_s2s_number/0, node_cleanup/2, outgoing_s2s_number/0, start_link/0]).
65
66 -include("mongoose.hrl").
67 -include("jlib.hrl").
68 -include("ejabberd_commands.hrl").
69
70 -define(DEFAULT_MAX_S2S_CONNECTIONS_NUMBER, 1).
71 -define(DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE, 1).
72
73 -type fromto() :: {'global' | jid:server(), jid:server()}.
74 -record(s2s, {
75 fromto,
76 pid
77 }).
78 -type s2s() :: #s2s{
79 fromto :: fromto(),
80 pid :: pid()
81 }.
82 -record(s2s_shared, {
83 host_type :: mongooseim:host_type(),
84 secret :: binary()
85 }).
86 -record(state, {}).
87
88 %%====================================================================
89 %% API
90 %%====================================================================
91 %%--------------------------------------------------------------------
92 %% Description: Starts the server
93 %%--------------------------------------------------------------------
94 -spec start_link() -> 'ignore' | {'error', _} | {'ok', pid()}.
95 start_link() ->
96 82 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
97
98 filter(From, To, Acc, Packet) ->
99 96 {From, To, Acc, Packet}.
100
101 route(From, To, Acc, Packet) ->
102 96 do_route(From, To, Acc, Packet).
103
104 -spec remove_connection(_, pid()) -> 'ok' | {'aborted', _} | {'atomic', _}.
105 remove_connection(FromTo, Pid) ->
106 39 case catch mnesia:dirty_match_object(s2s, #s2s{fromto = FromTo,
107 pid = Pid}) of
108 [#s2s{pid = Pid}] ->
109 32 F = fun() ->
110 32 mnesia:delete_object(#s2s{fromto = FromTo,
111 pid = Pid})
112 end,
113 32 mnesia:transaction(F);
114 _ ->
115 7 ok
116 end.
117
118 have_connection(FromTo) ->
119
:-(
case catch mnesia:dirty_read(s2s, FromTo) of
120 [_] ->
121
:-(
true;
122 _ ->
123
:-(
false
124 end.
125
126 -spec get_connections_pids(_) -> ['undefined' | pid()].
127 get_connections_pids(FromTo) ->
128 27 case catch mnesia:dirty_read(s2s, FromTo) of
129 L when is_list(L) ->
130 27 [Connection#s2s.pid || Connection <- L];
131 _ ->
132
:-(
[]
133 end.
134
135 -spec try_register(fromto()) -> boolean().
136 try_register(FromTo) ->
137 27 MaxS2SConnectionsNumber = max_s2s_connections_number(FromTo),
138 27 MaxS2SConnectionsNumberPerNode =
139 max_s2s_connections_number_per_node(FromTo),
140 27 F = fun() ->
141 27 L = mnesia:read({s2s, FromTo}),
142 27 NeededConnections = needed_connections_number(
143 L, MaxS2SConnectionsNumber,
144 MaxS2SConnectionsNumberPerNode),
145 27 case NeededConnections > 0 of
146 true ->
147 13 mnesia:write(#s2s{fromto = FromTo,
148 pid = self()}),
149 13 true;
150 false ->
151 14 false
152 end
153 end,
154 27 case mnesia:transaction(F) of
155 {atomic, Res} ->
156 27 Res;
157 _ ->
158
:-(
false
159 end.
160
161 dirty_get_connections() ->
162
:-(
mnesia:dirty_all_keys(s2s).
163
164 %%====================================================================
165 %% Hooks callbacks
166 %%====================================================================
167
168 node_cleanup(Acc, Node) ->
169
:-(
F = fun() ->
170
:-(
Es = mnesia:select(
171 s2s,
172 [{#s2s{pid = '$1', _ = '_'},
173 [{'==', {node, '$1'}, Node}],
174 ['$_']}]),
175
:-(
lists:foreach(fun(E) ->
176
:-(
mnesia:delete_object(E)
177 end, Es)
178 end,
179
:-(
Res = mnesia:async_dirty(F),
180
:-(
maps:put(?MODULE, Res, Acc).
181
182 -spec key(mongooseim:host_type(), {jid:lserver(), jid:lserver()}, binary()) ->
183 binary().
184 key(HostType, {From, To}, StreamID) ->
185 54 Secret = get_shared_secret(HostType),
186 54 SecretHashed = base16:encode(crypto:hash(sha256, Secret)),
187 54 HMac = crypto:mac(hmac, sha256, SecretHashed, [From, " ", To, " ", StreamID]),
188 54 base16:encode(HMac).
189
190 %%====================================================================
191 %% gen_server callbacks
192 %%====================================================================
193
194 %%--------------------------------------------------------------------
195 %% Function: init(Args) -> {ok, State} |
196 %% {ok, State, Timeout} |
197 %% ignore |
198 %% {stop, Reason}
199 %% Description: Initiates the server
200 %%--------------------------------------------------------------------
201 init([]) ->
202 82 mnesia:create_table(s2s, [{ram_copies, [node()]}, {type, bag},
203 {attributes, record_info(fields, s2s)}]),
204 82 mnesia:add_table_copy(s2s, node(), ram_copies),
205 82 mnesia:create_table(s2s_shared, [{ram_copies, [node()]},
206 {attributes, record_info(fields, s2s_shared)}]),
207 82 mnesia:add_table_copy(s2s_shared, node(), ram_copies),
208 82 {atomic, ok} = set_shared_secret(),
209 82 ejabberd_commands:register_commands(commands()),
210 82 ejabberd_hooks:add(node_cleanup, global, ?MODULE, node_cleanup, 50),
211 82 {ok, #state{}}.
212
213 %%--------------------------------------------------------------------
214 %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
215 %% {reply, Reply, State, Timeout} |
216 %% {noreply, State} |
217 %% {noreply, State, Timeout} |
218 %% {stop, Reason, Reply, State} |
219 %% {stop, Reason, State}
220 %% Description: Handling call messages
221 %%--------------------------------------------------------------------
222 handle_call(Request, From, State) ->
223
:-(
?UNEXPECTED_CALL(Request, From),
224
:-(
{reply, {error, unexpected_call}, State}.
225
226 %%--------------------------------------------------------------------
227 %% Function: handle_cast(Msg, State) -> {noreply, State} |
228 %% {noreply, State, Timeout} |
229 %% {stop, Reason, State}
230 %% Description: Handling cast messages
231 %%--------------------------------------------------------------------
232 handle_cast(Msg, State) ->
233
:-(
?UNEXPECTED_CAST(Msg),
234
:-(
{noreply, State}.
235
236 %%--------------------------------------------------------------------
237 %% Function: handle_info(Info, State) -> {noreply, State} |
238 %% {noreply, State, Timeout} |
239 %% {stop, Reason, State}
240 %% Description: Handling all non call/cast messages
241 %%--------------------------------------------------------------------
242
243 handle_info(Msg, State) ->
244
:-(
?UNEXPECTED_INFO(Msg),
245
:-(
{noreply, State}.
246
247 %%--------------------------------------------------------------------
248 %% Function: terminate(Reason, State) -> void()
249 %% Description: This function is called by a gen_server when it is about to
250 %% terminate. It should be the opposite of Module:init/1 and do any necessary
251 %% cleaning up. When it returns, the gen_server terminates with Reason.
252 %% The return value is ignored.
253 %%--------------------------------------------------------------------
254 terminate(_Reason, _State) ->
255
:-(
ejabberd_hooks:delete(node_cleanup, global, ?MODULE, node_cleanup, 50),
256
:-(
ejabberd_commands:unregister_commands(commands()),
257
:-(
ok.
258
259 %%--------------------------------------------------------------------
260 %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
261 %% Description: Convert process state when code is changed
262 %%--------------------------------------------------------------------
263 code_change(_OldVsn, State, _Extra) ->
264
:-(
{ok, State}.
265
266 %%--------------------------------------------------------------------
267 %%% Internal functions
268 %%--------------------------------------------------------------------
269
270 -spec do_route(From :: jid:jid(),
271 To :: jid:jid(),
272 Acc :: mongoose_acc:t(),
273 Packet :: exml:element()) ->
274 {done, mongoose_acc:t()}. % this is the 'last resort' router, it always returns 'done'.
275 do_route(From, To, Acc, Packet) ->
276 96 ?LOG_DEBUG(#{what => s2s_route, acc => Acc}),
277 96 case find_connection(From, To) of
278 {atomic, Pid} when is_pid(Pid) ->
279 91 ?LOG_DEBUG(#{what => s2s_found_connection,
280 text => <<"Send packet to s2s connection">>,
281 91 s2s_pid => Pid, acc => Acc}),
282 91 #xmlel{attrs = Attrs} = Packet,
283 91 NewAttrs = jlib:replace_from_to_attrs(jid:to_binary(From),
284 jid:to_binary(To),
285 Attrs),
286 91 NewPacket = Packet#xmlel{attrs = NewAttrs},
287 91 Acc1 = mongoose_hooks:s2s_send_packet(Acc, From, To, Packet),
288 91 send_element(Pid, Acc1, NewPacket),
289 91 {done, Acc1};
290 {aborted, _Reason} ->
291 3 case mongoose_acc:stanza_type(Acc) of
292 <<"error">> ->
293
:-(
{done, Acc};
294 <<"result">> ->
295
:-(
{done, Acc};
296 _ ->
297 3 ?LOG_DEBUG(#{what => s2s_connection_not_found, acc => Acc}),
298 3 {Acc1, Err} = jlib:make_error_reply(
299 Acc, Packet, mongoose_xmpp_errors:service_unavailable()),
300 3 Acc2 = ejabberd_router:route(To, From, Acc1, Err),
301 3 {done, Acc2}
302 end
303 end.
304
305 -spec find_connection(From :: jid:jid(),
306 To :: jid:jid()) -> {'aborted', _} | {'atomic', _}.
307 find_connection(From, To) ->
308 97 #jid{lserver = MyServer} = From,
309 97 #jid{lserver = Server} = To,
310 97 FromTo = {MyServer, Server},
311 97 MaxS2SConnectionsNumber = max_s2s_connections_number(FromTo),
312 95 MaxS2SConnectionsNumberPerNode =
313 max_s2s_connections_number_per_node(FromTo),
314 95 ?LOG_DEBUG(#{what => s2s_find_connection, from_server => MyServer, to_server => Server}),
315 95 case catch mnesia:dirty_read(s2s, FromTo) of
316 {'EXIT', Reason} ->
317
:-(
{aborted, Reason};
318 [] ->
319 %% We try to establish all the connections if the host is not a
320 %% service and if the s2s host is not blacklisted or
321 %% is in whitelist:
322 32 maybe_open_several_connections(From, To, MyServer, Server, FromTo,
323 MaxS2SConnectionsNumber,
324 MaxS2SConnectionsNumberPerNode);
325 L when is_list(L) ->
326 63 maybe_open_missing_connections(From, MyServer, Server, FromTo,
327 MaxS2SConnectionsNumber,
328 MaxS2SConnectionsNumberPerNode, L)
329 end.
330
331 maybe_open_missing_connections(From, MyServer, Server, FromTo,
332 MaxS2SConnectionsNumber,
333 MaxS2SConnectionsNumberPerNode, L) ->
334 63 NeededConnections = needed_connections_number(
335 L, MaxS2SConnectionsNumber,
336 MaxS2SConnectionsNumberPerNode),
337 63 case NeededConnections > 0 of
338 true ->
339 %% We establish the missing connections for this pair.
340
:-(
open_several_connections(
341 NeededConnections, MyServer,
342 Server, From, FromTo,
343 MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode);
344 false ->
345 %% We choose a connexion from the pool of opened ones.
346 63 {atomic, choose_connection(From, L)}
347 end.
348
349 maybe_open_several_connections(From, To, MyServer, Server, FromTo,
350 MaxS2SConnectionsNumber,
351 MaxS2SConnectionsNumberPerNode) ->
352 %% We try to establish all the connections if the host is not a
353 %% service and if the s2s host is not blacklisted or
354 %% is in whitelist:
355 32 case not is_service(From, To) andalso allow_host(MyServer, Server) of
356 true ->
357 29 NeededConnections = needed_connections_number(
358 [], MaxS2SConnectionsNumber,
359 MaxS2SConnectionsNumberPerNode),
360 29 open_several_connections(
361 NeededConnections, MyServer,
362 Server, From, FromTo,
363 MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode);
364 false ->
365 3 {aborted, error}
366 end.
367
368 -spec choose_connection(From :: jid:jid(),
369 Connections :: [s2s()]) -> any().
370 choose_connection(From, Connections) ->
371 70 choose_pid(From, [C#s2s.pid || C <- Connections]).
372
373 -spec choose_pid(From :: jid:jid(), Pids :: [pid()]) -> pid().
374 choose_pid(From, Pids) ->
375 99 Pids1 = case [P || P <- Pids, node(P) == node()] of
376
:-(
[] -> Pids;
377 99 Ps -> Ps
378 end,
379 % Use sticky connections based on the JID of the sender
380 % (without the resource to ensure that a muc room always uses the same connection)
381 99 Pid = lists:nth(erlang:phash2(jid:to_bare(From), length(Pids1)) + 1, Pids1),
382 99 ?LOG_DEBUG(#{what => s2s_choose_pid, from => From, s2s_pid => Pid}),
383 99 Pid.
384
385 -spec open_several_connections(N :: pos_integer(), MyServer :: jid:server(),
386 Server :: jid:server(), From :: jid:jid(), FromTo :: fromto(),
387 MaxS2S :: pos_integer(), MaxS2SPerNode :: pos_integer())
388 -> {'aborted', _} | {'atomic', _}.
389 open_several_connections(N, MyServer, Server, From, FromTo,
390 MaxS2SConnectionsNumber,
391 MaxS2SConnectionsNumberPerNode) ->
392 29 ConnectionsResult =
393 29 [new_connection(MyServer, Server, From, FromTo,
394 MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode)
395 29 || _N <- lists:seq(1, N)],
396 29 case [PID || {atomic, PID} <- ConnectionsResult] of
397 [] ->
398
:-(
hd(ConnectionsResult);
399 PIDs ->
400 29 {atomic, choose_pid(From, PIDs)}
401 end.
402
403 -spec new_connection(MyServer :: jid:server(), Server :: jid:server(),
404 From :: jid:jid(), FromTo :: fromto(), MaxS2S :: pos_integer(),
405 MaxS2SPerNode :: pos_integer()) -> {'aborted', _} | {'atomic', _}.
406 new_connection(MyServer, Server, From, FromTo = {FromServer, ToServer},
407 MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode) ->
408 29 {ok, Pid} = ejabberd_s2s_out:start(
409 MyServer, Server, new),
410 29 F = fun() ->
411 33 L = mnesia:read({s2s, FromTo}),
412 30 NeededConnections = needed_connections_number(
413 L, MaxS2SConnectionsNumber,
414 MaxS2SConnectionsNumberPerNode),
415 30 case NeededConnections > 0 of
416 true ->
417 23 mnesia:write(#s2s{fromto = FromTo,
418 pid = Pid}),
419 22 ?LOG_INFO(#{what => s2s_new_connection,
420 text => <<"New s2s connection started">>,
421 from_server => FromServer,
422 to_server => ToServer,
423 22 s2s_pid => Pid}),
424 22 Pid;
425 false ->
426 7 choose_connection(From, L)
427 end
428 end,
429 29 TRes = mnesia:transaction(F),
430 29 case TRes of
431 {atomic, Pid} ->
432 22 ejabberd_s2s_out:start_connection(Pid);
433 _ ->
434 7 ejabberd_s2s_out:stop_connection(Pid)
435 end,
436 29 TRes.
437
438 -spec max_s2s_connections_number(fromto()) -> pos_integer().
439 max_s2s_connections_number({From, To}) ->
440 124 {ok, HostType} = mongoose_domain_api:get_host_type(From),
441 122 case acl:match_rule(HostType, max_s2s_connections, jid:make(<<"">>, To, <<"">>)) of
442
:-(
Max when is_integer(Max) -> Max;
443 122 _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER
444 end.
445
446 -spec max_s2s_connections_number_per_node(fromto()) -> pos_integer().
447 max_s2s_connections_number_per_node({From, To}) ->
448 122 {ok, HostType} = mongoose_domain_api:get_host_type(From),
449 122 case acl:match_rule(HostType, max_s2s_connections_per_node, jid:make(<<"">>, To, <<"">>)) of
450
:-(
Max when is_integer(Max) -> Max;
451 122 _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE
452 end.
453
454 -spec needed_connections_number([any()], pos_integer(), pos_integer()) -> integer().
455 needed_connections_number(Ls, MaxS2SConnectionsNumber,
456 MaxS2SConnectionsNumberPerNode) ->
457 149 LocalLs = [L || L <- Ls, node(L#s2s.pid) == node()],
458 149 lists:min([MaxS2SConnectionsNumber - length(Ls),
459 MaxS2SConnectionsNumberPerNode - length(LocalLs)]).
460
461 %%--------------------------------------------------------------------
462 %% Function: is_service(From, To) -> true | false
463 %% Description: Return true if the destination must be considered as a
464 %% service.
465 %% --------------------------------------------------------------------
466 -spec is_service(jid:jid(), jid:jid()) -> boolean().
467 is_service(From, To) ->
468 32 LFromDomain = From#jid.lserver,
469 32 case mongoose_config:lookup_opt({route_subdomains, LFromDomain}) of
470 {ok, s2s} -> % bypass RFC 3920 10.3
471
:-(
false;
472 {error, not_found} ->
473 32 Hosts = ?MYHOSTS,
474 32 P = fun(ParentDomain) -> lists:member(ParentDomain, Hosts) end,
475 32 lists:any(P, parent_domains(To#jid.lserver))
476 end.
477
478 -spec parent_domains(binary()) -> [binary(), ...].
479 parent_domains(Domain) ->
480 32 parent_domains(Domain, [Domain]).
481
482 -spec parent_domains(binary(), [binary(), ...]) -> [binary(), ...].
483 parent_domains(<<>>, Acc) ->
484 32 lists:reverse(Acc);
485 parent_domains(<<$., Rest/binary>>, Acc) ->
486 19 parent_domains(Rest, [Rest | Acc]);
487 parent_domains(<<_, Rest/binary>>, Acc) ->
488 334 parent_domains(Rest, Acc).
489
490 -spec send_element(pid(), mongoose_acc:t(), exml:element()) ->
491 {'send_element', mongoose_acc:t(), exml:element()}.
492 send_element(Pid, Acc, El) ->
493 91 Pid ! {send_element, Acc, El}.
494
495 timeout() ->
496 284 600000.
497 %%--------------------------------------------------------------------
498 %% Function: domain_utf8_to_ascii(Domain) -> binary() | false
499 %% Description: Converts a UTF-8 domain to ASCII (IDNA)
500 %% --------------------------------------------------------------------
501 -spec domain_utf8_to_ascii(binary() | string()) -> binary() | false.
502 domain_utf8_to_ascii(Domain) ->
503 10 case catch idna:utf8_to_ascii(Domain) of
504 {'EXIT', _} ->
505 2 false;
506 AsciiDomain ->
507 8 list_to_binary(AsciiDomain)
508 end.
509
510 %%%----------------------------------------------------------------------
511 %%% ejabberd commands
512
513 -spec commands() -> [ejabberd_commands:cmd(), ...].
514 commands() ->
515 82 [
516 #ejabberd_commands{name = incoming_s2s_number,
517 tags = [stats, s2s],
518 desc = "Number of incoming s2s connections on the node",
519 module = ?MODULE, function = incoming_s2s_number,
520 args = [],
521 result = {s2s_incoming, integer}},
522 #ejabberd_commands{name = outgoing_s2s_number,
523 tags = [stats, s2s],
524 desc = "Number of outgoing s2s connections on the node",
525 module = ?MODULE, function = outgoing_s2s_number,
526 args = [],
527 result = {s2s_outgoing, integer}}
528 ].
529
530 -spec incoming_s2s_number() -> non_neg_integer().
531 incoming_s2s_number() ->
532
:-(
length(supervisor:which_children(ejabberd_s2s_in_sup)).
533
534 -spec outgoing_s2s_number() -> non_neg_integer().
535 outgoing_s2s_number() ->
536
:-(
length(supervisor:which_children(ejabberd_s2s_out_sup)).
537
538
539 %% Check if host is in blacklist or white list
540 allow_host(MyServer, S2SHost) ->
541 56 case mongoose_domain_api:get_host_type(MyServer) of
542 {error, not_found} ->
543
:-(
false;
544 {ok, HostType} ->
545 56 case mongoose_config:lookup_opt([{s2s, HostType}, host_policy, S2SHost]) of
546 {ok, allow} ->
547
:-(
true;
548 {ok, deny} ->
549
:-(
false;
550 {error, not_found} ->
551 56 mongoose_config:get_opt([{s2s, HostType}, default_policy]) =:= allow
552 56 andalso mongoose_hooks:s2s_allow_host(MyServer, S2SHost) =:= allow
553 end
554 end.
555
556 %% @doc Get information about S2S connections of the specified type.
557 -spec get_info_s2s_connections('in' | 'out') -> [[{atom(), any()}, ...]].
558 get_info_s2s_connections(Type) ->
559 2 ChildType = case Type of
560 1 in -> ejabberd_s2s_in_sup;
561 1 out -> ejabberd_s2s_out_sup
562 end,
563 2 Connections = supervisor:which_children(ChildType),
564 2 get_s2s_info(Connections, Type).
565
566 -type connstate() :: 'restarting' | 'undefined' | pid().
567 -type conn() :: { any(), connstate(), 'supervisor' | 'worker', 'dynamic' | [_] }.
568 -spec get_s2s_info(Connections :: [conn()],
569 Type :: 'in' | 'out'
570 ) -> [[{any(), any()}, ...]]. % list of lists
571 get_s2s_info(Connections, Type)->
572 2 complete_s2s_info(Connections, Type, []).
573
574 -spec complete_s2s_info(Connections :: [conn()],
575 Type :: 'in' | 'out',
576 Result :: [[{any(), any()}, ...]] % list of lists
577 ) -> [[{any(), any()}, ...]]. % list of lists
578 complete_s2s_info([], _, Result)->
579 2 Result;
580 complete_s2s_info([Connection|T], Type, Result)->
581 3 {_, PID, _, _}=Connection,
582 3 State = get_s2s_state(PID),
583 3 complete_s2s_info(T, Type, [State|Result]).
584
585 -spec get_s2s_state(connstate()) -> [{atom(), any()}, ...].
586 get_s2s_state(S2sPid)->
587 3 Infos = case gen_fsm_compat:sync_send_all_state_event(S2sPid, get_state_infos) of
588 3 {state_infos, Is} -> [{status, open} | Is];
589
:-(
{noproc, _} -> [{status, closed}]; %% Connection closed
590
:-(
{badrpc, _} -> [{status, error}]
591 end,
592 3 [{s2s_pid, S2sPid} | Infos].
593
594 -spec get_shared_secret(mongooseim:host_type()) -> binary().
595 get_shared_secret(HostType) ->
596 54 [#s2s_shared{secret = Secret}] = ets:lookup(s2s_shared, HostType),
597 54 Secret.
598
599 -spec set_shared_secret() -> mnesia:t_result(ok).
600 set_shared_secret() ->
601 82 mnesia:transaction(fun() ->
602 82 [set_shared_secret_t(HostType) || HostType <- ?ALL_HOST_TYPES],
603 82 ok
604 end).
605
606 -spec set_shared_secret_t(mongooseim:host_type()) -> ok.
607 set_shared_secret_t(HostType) ->
608 421 Secret = case mongoose_config:lookup_opt([{s2s, HostType}, shared]) of
609 {ok, SecretFromConfig} ->
610
:-(
SecretFromConfig;
611 {error, not_found} ->
612 421 base16:encode(crypto:strong_rand_bytes(10))
613 end,
614 421 mnesia:write(#s2s_shared{host_type = HostType, secret = Secret}).
615
616 -spec lookup_certfile(mongooseim:host_type()) -> {ok, string()} | {error, not_found}.
617 lookup_certfile(HostType) ->
618 78 case mongoose_config:lookup_opt({domain_certfile, HostType}) of
619 {ok, CertFile} ->
620
:-(
CertFile;
621 {error, not_found} ->
622 78 mongoose_config:lookup_opt([{s2s, HostType}, certfile])
623 end.
Line Hits Source