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