diff options
Diffstat (limited to 'src/ejabberd_s2s.erl')
-rw-r--r-- | src/ejabberd_s2s.erl | 215 |
1 files changed, 91 insertions, 124 deletions
diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index a7b3234fd..0eab46337 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -5,7 +5,7 @@ %%% Created : 7 Dec 2002 by Alexey Shchepin <alexey@process-one.net> %%% %%% -%%% ejabberd, Copyright (C) 2002-2015 ProcessOne +%%% ejabberd, Copyright (C) 2002-2016 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -25,27 +25,32 @@ -module(ejabberd_s2s). +-protocol({xep, 220, '1.1'}). + +-behaviour(ejabberd_config). + -author('alexey@process-one.net'). -behaviour(gen_server). %% 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, list_temporarily_blocked_hosts/0, external_host_overloaded/1, is_temporarly_blocked/1, - check_peer_certificate/3]). + check_peer_certificate/3, + get_commands_spec/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -%% ejabberd API --export([get_info_s2s_connections/1, transform_options/1]). +-export([get_info_s2s_connections/1, + transform_options/1, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -71,23 +76,15 @@ %% 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, {}). -record(temporarily_blocked, {host = <<"">> :: binary(), - timestamp = now() :: erlang:timestamp()}). + timestamp :: integer()}). -type temporarily_blocked() :: #temporarily_blocked{}. -%%==================================================================== -%% API -%%==================================================================== -%%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} -%% Description: Starts the server -%%-------------------------------------------------------------------- start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). @@ -117,9 +114,9 @@ external_host_overloaded(Host) -> "seconds", [Host, ?S2S_OVERLOAD_BLOCK_PERIOD]), mnesia:transaction(fun () -> + Time = p1_time_compat:monotonic_time(), mnesia:write(#temporarily_blocked{host = Host, - timestamp = - now()}) + timestamp = Time}) end). -spec is_temporarly_blocked(binary()) -> boolean(). @@ -128,7 +125,8 @@ is_temporarly_blocked(Host) -> case mnesia:dirty_read(temporarily_blocked, Host) of [] -> false; [#temporarily_blocked{timestamp = T} = Entry] -> - case timer:now_diff(now(), T) of + Diff = p1_time_compat:monotonic_time() - T, + case p1_time_compat:convert_time_unit(Diff, native, micro_seconds) of N when N > (?S2S_OVERLOAD_BLOCK_PERIOD) * 1000 * 1000 -> mnesia:dirty_delete_object(Entry), false; _ -> true @@ -136,19 +134,15 @@ is_temporarly_blocked(Host) -> 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 @@ -158,25 +152,12 @@ remove_connection(FromTo, Pid, Key) -> have_connection(FromTo) -> case catch mnesia:dirty_read(s2s, FromTo) of - [_] -> + [_] -> true; _ -> 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) -> @@ -187,10 +168,9 @@ 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), @@ -200,9 +180,8 @@ try_register(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, @@ -221,7 +200,7 @@ check_peer_certificate(SockMod, Sock, Peer) -> {ok, Cert} -> case SockMod:get_verify_result(Sock) of 0 -> - case idna:domain_utf8_to_ascii(Peer) of + case ejabberd_idna:domain_utf8_to_ascii(Peer) of false -> {error, <<"Cannot decode remote server name">>}; AsciiPeer -> @@ -235,61 +214,44 @@ check_peer_certificate(SockMod, Sock, Peer) -> end end; VerifyRes -> - {error, p1_tls:get_cert_verify_string(VerifyRes, Cert)} + {error, fast_tls:get_cert_verify_string(VerifyRes, Cert)} end; + {error, _Reason} -> + {error, <<"Cannot get peer certificate">>}; error -> - {error, <<"Cannot get peer certificate">>} + {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 %%==================================================================== -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- init([]) -> update_tables(), - mnesia:create_table(s2s, [{ram_copies, [node()]}, {type, bag}, - {attributes, record_info(fields, s2s)}]), + mnesia:create_table(s2s, + [{ram_copies, [node()]}, + {type, bag}, + {attributes, record_info(fields, s2s)}]), mnesia:add_table_copy(s2s, node(), ram_copies), mnesia:subscribe(system), - ejabberd_commands:register_commands(commands()), - mnesia:create_table(temporarily_blocked, [{ram_copies, [node()]}, {attributes, record_info(fields, temporarily_blocked)}]), + ejabberd_commands:register_commands(get_commands_spec()), + mnesia:create_table(temporarily_blocked, + [{ram_copies, [node()]}, + {attributes, record_info(fields, temporarily_blocked)}]), {ok, #state{}}. -%%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} -%% Description: Handling call messages -%%-------------------------------------------------------------------- handle_call(_Request, _From, State) -> - Reply = ok, - {reply, Reply, State}. + {reply, ok, State}. -%%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling cast messages -%%-------------------------------------------------------------------- handle_cast(_Msg, State) -> {noreply, State}. -%%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling all non call/cast messages -%%-------------------------------------------------------------------- handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> clean_table_from_bad_node(Node), {noreply, State}; @@ -303,27 +265,17 @@ handle_info({route, From, To, Packet}, State) -> {noreply, State}; handle_info(_Info, State) -> {noreply, State}. -%%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() -%% Description: This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any necessary -%% cleaning up. When it returns, the gen_server terminates with Reason. -%% The return value is ignored. -%%-------------------------------------------------------------------- terminate(_Reason, _State) -> - ejabberd_commands:unregister_commands(commands()), + ejabberd_commands:unregister_commands(get_commands_spec()), ok. -%%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: Convert process state when code is changed -%%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- + clean_table_from_bad_node(Node) -> F = fun() -> Es = mnesia:select( @@ -347,8 +299,8 @@ do_route(From, To, Packet) -> #xmlel{name = Name, attrs = Attrs, children = Els} = Packet, NewAttrs = - jlib:replace_from_to_attrs(jlib:jid_to_string(From), - jlib:jid_to_string(To), Attrs), + jlib:replace_from_to_attrs(jid:to_string(From), + jid:to_string(To), Attrs), #jid{lserver = MyServer} = From, ejabberd_hooks:run(s2s_send_packet, MyServer, [From, To, Packet]), @@ -356,7 +308,7 @@ do_route(From, To, Packet) -> #xmlel{name = Name, attrs = NewAttrs, children = Els}), ok; {aborted, _Reason} -> - case xml:get_tag_attr_s(<<"type">>, Packet) of + case fxml:get_tag_attr_s(<<"type">>, Packet) of <<"error">> -> ok; <<"result">> -> ok; _ -> @@ -422,7 +374,7 @@ choose_pid(From, Pids) -> Ps -> Ps end, Pid = - lists:nth(erlang:phash(jlib:jid_remove_resource(From), + lists:nth(erlang:phash(jid:remove_resource(From), length(Pids1)), Pids1), ?DEBUG("Using ejabberd_s2s_out ~p~n", [Pid]), @@ -442,17 +394,15 @@ open_several_connections(N, MyServer, Server, From, 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) @@ -467,7 +417,7 @@ new_connection(MyServer, Server, From, FromTo, max_s2s_connections_number({From, To}) -> case acl:match_rule(From, max_s2s_connections, - jlib:make_jid(<<"">>, To, <<"">>)) + jid:make(<<"">>, To, <<"">>)) of Max when is_integer(Max) -> Max; _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER @@ -475,7 +425,7 @@ max_s2s_connections_number({From, To}) -> max_s2s_connections_number_per_node({From, To}) -> case acl:match_rule(From, max_s2s_connections_per_node, - jlib:make_jid(<<"">>, To, <<"">>)) + jid:make(<<"">>, To, <<"">>)) of Max when is_integer(Max) -> Max; _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE @@ -514,17 +464,19 @@ parent_domains(Domain) -> end, [], lists:reverse(str:tokens(Domain, <<".">>))). -send_element(Pid, El) -> Pid ! {send_element, El}. +send_element(Pid, El) -> + Pid ! {send_element, El}. %%%---------------------------------------------------------------------- %%% ejabberd commands -commands() -> +get_commands_spec() -> [#ejabberd_commands{name = incoming_s2s_number, tags = [stats, s2s], desc = "Number of incoming s2s connections on " "the node", + policy = admin, module = ?MODULE, function = incoming_s2s_number, args = [], result = {s2s_incoming, integer}}, #ejabberd_commands{name = outgoing_s2s_number, @@ -532,6 +484,7 @@ commands() -> desc = "Number of outgoing s2s connections on " "the node", + policy = admin, module = ?MODULE, function = outgoing_s2s_number, args = [], result = {s2s_outgoing, integer}}]. @@ -552,16 +505,17 @@ update_tables() -> 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] -> ok; + [fromto, pid, key] -> + mnesia:transform_table(s2s, ignore, [fromto, pid]), + mnesia:clear_table(s2s); + [fromto, pid] -> ok; {'EXIT', _} -> ok end, case lists:member(local_s2s, mnesia:system_info(tables)) of - true -> - mnesia:delete_table(local_s2s); - false -> - ok + true -> mnesia:delete_table(local_s2s); + false -> ok end. %% Check if host is in blacklist or white list @@ -585,7 +539,7 @@ allow_host1(MyHost, S2SHost) -> s2s_access, fun(A) when is_atom(A) -> A end, all), - JID = jlib:make_jid(<<"">>, S2SHost, <<"">>), + JID = jid:make(<<"">>, S2SHost, <<"">>), case acl:match_rule(MyHost, Rule, JID) of deny -> false; allow -> @@ -655,10 +609,15 @@ get_s2s_state(S2sPid) -> [{s2s_pid, S2sPid} | Infos]. get_cert_domains(Cert) -> - {rdnSequence, Subject} = - (Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.subject, - Extensions = - (Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.extensions, + TBSCert = Cert#'Certificate'.tbsCertificate, + Subject = case TBSCert#'TBSCertificate'.subject of + {rdnSequence, Subj} -> lists:flatten(Subj); + _ -> [] + end, + Extensions = case TBSCert#'TBSCertificate'.extensions of + Exts when is_list(Exts) -> Exts; + _ -> [] + end, lists:flatmap(fun (#'AttributeTypeAndValue'{type = ?'id-at-commonName', value = Val}) -> @@ -669,7 +628,7 @@ get_cert_domains(Cert) -> true -> error end, if D /= error -> - case jlib:string_to_jid(D) of + case jid:from_string(D) of #jid{luser = <<"">>, lserver = LD, lresource = <<"">>} -> [LD]; @@ -681,7 +640,7 @@ get_cert_domains(Cert) -> end; (_) -> [] end, - lists:flatten(Subject)) + Subject) ++ lists:flatmap(fun (#'Extension'{extnID = ?'id-ce-subjectAltName', @@ -705,7 +664,7 @@ get_cert_domains(Cert) -> when is_binary(D) -> case - jlib:string_to_jid((D)) + jid:from_string((D)) of #jid{luser = <<"">>, @@ -714,7 +673,7 @@ get_cert_domains(Cert) -> lresource = <<"">>} -> case - idna:domain_utf8_to_ascii(LD) + ejabberd_idna:domain_utf8_to_ascii(LD) of false -> []; @@ -728,7 +687,7 @@ get_cert_domains(Cert) -> ({dNSName, D}) when is_list(D) -> case - jlib:string_to_jid(list_to_binary(D)) + jid:from_string(list_to_binary(D)) of #jid{luser = <<"">>, lserver = LD, @@ -771,3 +730,11 @@ match_labels([DL | DLabels], [PL | PLabels]) -> end; false -> false end. + +opt_type(route_subdomains) -> + fun (s2s) -> s2s; + (local) -> local + end; +opt_type(s2s_access) -> + fun (A) when is_atom(A) -> A end; +opt_type(_) -> [route_subdomains, s2s_access]. |