%% API
-export([start_link/0, route/3, have_connection/1,
- has_key/2, get_connections_pids/1, try_register/1,
- remove_connection/3, find_connection/2,
+ make_key/2, get_connections_pids/1, try_register/1,
+ remove_connection/2, find_connection/2,
dirty_get_connections/0, allow_host/2,
incoming_s2s_number/0, outgoing_s2s_number/0,
clean_temporarily_blocked_table/0,
%% once a server is temporarly blocked, it stay blocked for 60 seconds
-record(s2s, {fromto = {<<"">>, <<"">>} :: {binary(), binary()} | '_',
- pid = self() :: pid() | '_' | '$1',
- key = <<"">> :: binary() | '_'}).
+ pid = self() :: pid() | '_' | '$1'}).
-record(state, {}).
end.
-spec remove_connection({binary(), binary()},
- pid(), binary()) -> {atomic, ok} |
- ok |
- {aborted, any()}.
+ pid()) -> {atomic, ok} | ok | {aborted, any()}.
-remove_connection(FromTo, Pid, Key) ->
+remove_connection(FromTo, Pid) ->
case catch mnesia:dirty_match_object(s2s,
- #s2s{fromto = FromTo, pid = Pid,
- _ = '_'})
+ #s2s{fromto = FromTo, pid = Pid})
of
- [#s2s{pid = Pid, key = Key}] ->
+ [#s2s{pid = Pid}] ->
F = fun () ->
- mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid,
- key = Key})
+ mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid})
end,
mnesia:transaction(F);
_ -> ok
false
end.
--spec has_key({binary(), binary()}, binary()) -> boolean().
-
-has_key(FromTo, Key) ->
- case mnesia:dirty_select(s2s,
- [{#s2s{fromto = FromTo, key = Key, _ = '_'},
- [],
- ['$_']}]) of
- [] ->
- false;
- _ ->
- true
- end.
-
-spec get_connections_pids({binary(), binary()}) -> [pid()].
get_connections_pids(FromTo) ->
[]
end.
--spec try_register({binary(), binary()}) -> {key, binary()} | false.
+-spec try_register({binary(), binary()}) -> boolean().
try_register(FromTo) ->
- Key = randoms:get_string(),
MaxS2SConnectionsNumber = max_s2s_connections_number(FromTo),
MaxS2SConnectionsNumberPerNode =
max_s2s_connections_number_per_node(FromTo),
MaxS2SConnectionsNumber,
MaxS2SConnectionsNumberPerNode),
if NeededConnections > 0 ->
- mnesia:write(#s2s{fromto = FromTo, pid = self(),
- key = Key}),
- {key, Key};
+ mnesia:write(#s2s{fromto = FromTo, pid = self()}),
+ true;
true -> false
end
end,
{error, <<"Cannot get peer certificate">>}
end.
+make_key({From, To}, StreamID) ->
+ Secret = ejabberd_config:get_option(shared_key, fun(V) -> V end),
+ p1_sha:to_hexlist(
+ crypto:hmac(sha256, p1_sha:to_hexlist(crypto:hash(sha256, Secret)),
+ [To, " ", From, " ", StreamID])).
+
%%====================================================================
%% gen_server callbacks
%%====================================================================
new_connection(MyServer, Server, From, FromTo,
MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode) ->
- Key = randoms:get_string(),
{ok, Pid} = ejabberd_s2s_out:start(
- MyServer, Server, {new, Key}),
+ MyServer, Server, new),
F = fun() ->
L = mnesia:read({s2s, FromTo}),
NeededConnections = needed_connections_number(L,
MaxS2SConnectionsNumber,
MaxS2SConnectionsNumberPerNode),
if NeededConnections > 0 ->
- mnesia:write(#s2s{fromto = FromTo, pid = Pid,
- key = Key}),
+ mnesia:write(#s2s{fromto = FromTo, pid = Pid}),
?INFO_MSG("New s2s connection started ~p", [Pid]),
Pid;
true -> choose_connection(From, L)
end,
case catch mnesia:table_info(s2s, attributes) of
[fromto, node, key] ->
- mnesia:transform_table(s2s, ignore, [fromto, pid, key]),
+ mnesia:transform_table(s2s, ignore, [fromto, pid]),
+ mnesia:clear_table(s2s);
+ [fromto, pid, key] ->
+ mnesia:transform_table(s2s, ignore, [fromto, pid]),
mnesia:clear_table(s2s);
- [fromto, pid, key] -> ok;
+ [fromto, pid] -> ok;
{'EXIT', _} -> ok
end,
case lists:member(local_s2s, mnesia:system_info(tables)) of
-record(state,
{socket :: ejabberd_socket:socket_state(),
streamid = <<"">> :: binary(),
+ remote_streamid = <<"">> :: binary(),
use_v10 = true :: boolean(),
tls = false :: boolean(),
tls_required = false :: boolean(),
server = <<"">> :: binary(),
queue = queue:new() :: ?TQUEUE,
delay_to_retry = undefined_delay :: undefined_delay | non_neg_integer(),
- new = false :: false | binary(),
+ new = false :: boolean(),
verify = false :: false | {pid(), binary(), binary()},
bridge :: {atom(), atom()},
timer = make_ref() :: reference()}).
true -> TLSOpts4
end,
{New, Verify} = case Type of
- {new, Key} -> {Key, false};
+ new -> {true, false};
{verify, Pid, Key, SID} ->
start_connection(self()), {false, {Pid, Key, SID}}
end,
wait_for_stream({xmlstreamstart, _Name, Attrs},
StateData) ->
- {CertCheckRes, CertCheckMsg, NewStateData} =
+ {CertCheckRes, CertCheckMsg, StateData0} =
if StateData#state.tls_certverify, StateData#state.tls_enabled ->
{Res, Msg} =
ejabberd_s2s:check_peer_certificate(ejabberd_socket,
true ->
{no_verify, <<"Not verified">>, StateData}
end,
+ RemoteStreamID = xml:get_attr_s(<<"id">>, Attrs),
+ NewStateData = StateData0#state{remote_streamid = RemoteStreamID},
case {xml:get_attr_s(<<"xmlns">>, Attrs),
xml:get_attr_s(<<"xmlns:db">>, Attrs),
xml:get_attr_s(<<"version">>, Attrs) == <<"1.0">>}
?DEBUG("terminated: ~p", [{Reason, StateName}]),
case StateData#state.new of
false -> ok;
- Key ->
+ true ->
ejabberd_s2s:remove_connection({StateData#state.myname,
StateData#state.server},
- self(), Key)
+ self())
end,
bounce_queue(StateData#state.queue,
?ERR_REMOTE_SERVER_NOT_FOUND),
send_db_request(StateData) ->
Server = StateData#state.server,
New = case StateData#state.new of
- false ->
- case ejabberd_s2s:try_register({StateData#state.myname,
- Server})
- of
- {key, Key} -> Key;
- false -> false
- end;
- Key -> Key
+ false ->
+ ejabberd_s2s:try_register({StateData#state.myname, Server});
+ true ->
+ true
end,
NewStateData = StateData#state{new = New},
try case New of
- false -> ok;
- Key1 ->
+ false -> ok;
+ true ->
+ Key1 = ejabberd_s2s:make_key(
+ {StateData#state.myname, Server},
+ StateData#state.remote_streamid),
send_element(StateData,
#xmlel{name = <<"db:result">>,
attrs =