1 |
|
-module(mongoose_s2s_cets). |
2 |
|
-behaviour(mongoose_s2s_backend). |
3 |
|
|
4 |
|
-export([init/1, |
5 |
|
get_s2s_out_pids/1, |
6 |
|
try_register/2, |
7 |
|
remove_connection/2, |
8 |
|
node_cleanup/1]). |
9 |
|
|
10 |
|
-export([register_secret/2, |
11 |
|
get_shared_secret/1]). |
12 |
|
|
13 |
|
%% Internal usage (export so the callback would survive multiple code reloads) |
14 |
|
-export([handle_secret_conflict/2]). |
15 |
|
|
16 |
|
-include("mongoose_logger.hrl"). |
17 |
|
|
18 |
|
-define(TABLE, cets_s2s_session). |
19 |
|
-define(SECRET_TABLE, cets_s2s_secret). |
20 |
|
|
21 |
|
-type secret_tuple() :: {HostType :: mongooseim:host_type(), TS :: integer(), Secret :: ejabberd_s2s:base16_secret()}. |
22 |
|
|
23 |
|
-spec init(map()) -> ok. |
24 |
|
init(_) -> |
25 |
:-( |
cets:start(?TABLE, #{}), |
26 |
|
%% Non-random, non-node-specific keys |
27 |
|
%% This means that default merging would not work |
28 |
:-( |
cets:start(?SECRET_TABLE, #{handle_conflict => fun ?MODULE:handle_secret_conflict/2}), |
29 |
:-( |
cets_discovery:add_table(mongoose_cets_discovery, ?TABLE), |
30 |
:-( |
cets_discovery:add_table(mongoose_cets_discovery, ?SECRET_TABLE), |
31 |
:-( |
ok. |
32 |
|
|
33 |
|
%% Chooses the most recent value of two. |
34 |
|
%% Even if we choose the wrong record - nothing bad would happen |
35 |
|
%% (we still need to choose one). |
36 |
|
%% Choosing the record with the highest timestamp is just a logical behaviour |
37 |
|
%% (it also matches the logic of mongoose_s2s_lib:check_shared_secret/2, where updated secret |
38 |
|
%% in the config is updated across all nodes in the cluster). |
39 |
|
%% Example call: |
40 |
|
%% handle_secret_conflict({<<"localhost">>, 1689858975612268, <<"4e48dc4898b23f512059">>}, |
41 |
|
%% {<<"localhost">>, 1689859177195451, <<"56fdcd3ec63ff8299eb0">>}). |
42 |
|
-spec handle_secret_conflict(secret_tuple(), secret_tuple()) -> secret_tuple(). |
43 |
|
handle_secret_conflict(Rec1, Rec2) when Rec1 > Rec2 -> |
44 |
:-( |
Rec1; |
45 |
|
handle_secret_conflict(_Rec1, Rec2) -> |
46 |
:-( |
Rec2. |
47 |
|
|
48 |
|
%% Pid lists |
49 |
|
-spec get_s2s_out_pids(ejabberd_s2s:fromto()) -> ejabberd_s2s:s2s_pids(). |
50 |
|
get_s2s_out_pids(FromTo) -> |
51 |
:-( |
R = {{FromTo, '$1'}}, |
52 |
:-( |
ets:select(?TABLE, [{R, [], ['$1']}]). |
53 |
|
|
54 |
|
-spec try_register(Pid :: pid(), |
55 |
|
FromTo :: ejabberd_s2s:fromto()) -> boolean(). |
56 |
|
try_register(Pid, FromTo) -> |
57 |
:-( |
Pids = get_s2s_out_pids(FromTo), |
58 |
:-( |
case mongoose_s2s_lib:need_more_connections(FromTo, Pids) of |
59 |
|
true -> |
60 |
:-( |
cets:insert(?TABLE, {{FromTo, Pid}}), |
61 |
:-( |
true; |
62 |
|
false -> |
63 |
:-( |
false |
64 |
|
end. |
65 |
|
|
66 |
|
-spec remove_connection(FromTo :: ejabberd_s2s:fromto(), Pid :: pid()) -> ok. |
67 |
|
remove_connection(FromTo, Pid) -> |
68 |
:-( |
cets:delete(?TABLE, {FromTo, Pid}), |
69 |
:-( |
ok. |
70 |
|
|
71 |
|
%% node_cleanup is called on each node in the cluster, when Node is down |
72 |
|
-spec node_cleanup(Node :: node()) -> ok. |
73 |
|
node_cleanup(Node) -> |
74 |
:-( |
KeyPattern = {'_', '$1'}, |
75 |
:-( |
R = {KeyPattern}, |
76 |
:-( |
Guard = {'==', {node, '$1'}, Node}, |
77 |
:-( |
ets:select_delete(?TABLE, [{R, [Guard], [true]}]), |
78 |
:-( |
ok. |
79 |
|
|
80 |
|
%% Secrets |
81 |
|
-spec register_secret(HostType :: mongooseim:host_type(), |
82 |
|
Secret :: ejabberd_s2s:base16_secret()) -> ok. |
83 |
|
register_secret(HostType, Secret) -> |
84 |
|
%% We store timestamp so we could use it when merging two tables when clustering. |
85 |
|
%% Secrets is a very small table and get_shared_secret is called rarely, |
86 |
|
%% so having an extra field is not a problem. |
87 |
:-( |
TS = erlang:system_time(microsecond), |
88 |
:-( |
cets:insert(?SECRET_TABLE, {HostType, TS, Secret}), |
89 |
:-( |
ok. |
90 |
|
|
91 |
|
-spec get_shared_secret(mongooseim:host_type()) -> |
92 |
|
{ok, ejabberd_s2s:base16_secret()} | {error, not_found}. |
93 |
|
get_shared_secret(HostType) -> |
94 |
:-( |
case ets:lookup(?SECRET_TABLE, HostType) of |
95 |
|
[{_HostType, _TS, Secret}] -> |
96 |
:-( |
{ok, Secret}; |
97 |
|
[] -> |
98 |
:-( |
{error, not_found} |
99 |
|
end. |