--- ejabberd-1.1.3/src/ejabberd_config.erl 2005-11-05 22:15:53.000000000 +0100 +++ ejabberd/src/ejabberd_config.erl 2007-03-19 20:20:03.000000000 +0100 @@ -112,6 +112,8 @@ add_option(s2s_use_starttls, Port, State); {s2s_certfile, CertFile} -> add_option(s2s_certfile, CertFile, State); + {s2s_hub, Server} -> + add_option(s2s_hub, Server, State); {domain_certfile, Domain, CertFile} -> add_option({domain_certfile, Domain}, CertFile, State); {Opt, Val} -> --- ejabberd-1.1.3/src/ejabberd_s2s.erl 2006-09-26 13:12:03.000000000 +0200 +++ ejabberd/src/ejabberd_s2s.erl 2007-03-19 20:21:02.000000000 +0100 @@ -20,6 +20,7 @@ try_register/1, remove_connection/1, dirty_get_connections/0, + find_connection/2, ctl_process/2 ]). @@ -215,10 +216,38 @@ false end. +%%%-------------------------------------------------------------- +%%% find_proxy(DestinationHost) -> direct | {via, ProxyServer} +%%% DestinationHost = jid(), ProxyServer = string() +%%%-------------------------------------------------------------- +find_proxy(To) -> + case ejabberd_config:get_local_option(s2s_hub) of + Opts when is_list(Opts) -> + LTo = To#jid.lserver, + case gen_mod:get_opt(host, Opts, LTo) of + ProxyServer when LTo /= ProxyServer -> + ACL = gen_mod:get_opt(access, Opts, all), + case acl:match_rule(global, ACL, To) of + allow -> + {via, ProxyServer}; + deny -> + direct + end; + _ -> + direct + end; + _ -> + direct + end. + find_connection(From, To) -> #jid{lserver = MyServer} = From, - #jid{lserver = Server} = To, - FromTo = {MyServer, Server}, + #jid{lserver = ToServer} = To, + Server = case find_proxy(To) of + direct -> ToServer; + {via, ProxyServer} -> ProxyServer + end, + FromTo = {MyServer, Server}, case catch mnesia:dirty_read(s2s, FromTo) of {'EXIT', Reason} -> {aborted, Reason}; --- ejabberd-1.1.3/src/ejabberd_s2s_in.erl 2006-06-19 04:32:57.000000000 +0200 +++ ejabberd/src/ejabberd_s2s_in.erl 2007-03-19 20:22:08.000000000 +0100 @@ -45,6 +45,7 @@ tls_enabled = false, tls_options = [], authenticated = false, + proxy, auth_domain, connections = ?DICT:new(), timer}). @@ -126,6 +127,12 @@ CertFile -> [{certfile, CertFile}] end, + Proxy = case ejabberd_config:get_local_option(s2s_hub) of + OptList when is_list(OptList) -> + gen_mod:get_opt(host, OptList, none); + _ -> + none + end, Timer = erlang:start_timer(?S2STIMEOUT, self(), []), {ok, wait_for_stream, #state{socket = Socket, @@ -133,6 +140,7 @@ receiver = ReceiverPid, streamid = new_id(), shaper = Shaper, + proxy = Proxy, tls = StartTLS, tls_enabled = false, tls_options = TLSOpts, @@ -361,44 +369,33 @@ (To /= error) and (From /= error) -> LFrom = From#jid.lserver, LTo = To#jid.lserver, + ProxyServer = StateData#state.proxy, + MyDomains = ejabberd_router:dirty_get_all_domains(), + AuthDomain = StateData#state.auth_domain, if StateData#state.authenticated -> - case (LFrom == StateData#state.auth_domain) - andalso - lists:member( - LTo, - ejabberd_router:dirty_get_all_domains()) of + case lists:member(LTo, MyDomains) of true -> - if ((Name == "iq") or - (Name == "message") or - (Name == "presence")) -> - ejabberd_router:route( - From, To, NewEl); - true -> - error - end; + case lists:member(AuthDomain, [LFrom, ProxyServer]) of + true -> + route_stanza(Name, From, To, NewEl); + false -> + error + end; false -> - error - end; + case lists:member(LFrom, MyDomains) of + true -> + route_stanza(Name, From, To, NewEl); + false -> + error + end + end; true -> - case ?DICT:find({LFrom, LTo}, - StateData#state.connections) of - {ok, established} -> - if ((Name == "iq") or - (Name == "message") or - (Name == "presence")) -> - ejabberd_router:route( - From, To, NewEl); - true -> - error - end; - _ -> - error - end - end; + error + end; true -> - error - end, + error + end, {next_state, stream_established, StateData#state{timer = Timer}} end; @@ -414,7 +411,9 @@ LTo = jlib:nameprep(To), NSD = StateData#state{ connections = ?DICT:store({LFrom, LTo}, established, - StateData#state.connections)}, + StateData#state.connections), + auth_domain = LFrom, + authenticated = true}, {next_state, stream_established, NSD}; stream_established({invalid, From, To}, StateData) -> @@ -530,6 +529,14 @@ send_element(StateData, El) -> send_text(StateData, xml:element_to_string(El)). +route_stanza(Name, From, To, El) -> + if ((Name == "iq") or + (Name == "message") or + (Name == "presence")) -> + ejabberd_router:route(From, To, El); + true -> + error + end. change_shaper(StateData, Host, JID) -> Shaper = acl:match_rule(Host, StateData#state.shaper, JID), --- ejabberd-1.1.3/src/mod_s2s_proxy.erl 1970-01-01 09:00:00.000000000 +0900 +++ ejabberd/src/mod_s2s_proxy.erl 2006-09-25 13:46:09.000000000 +1000 @@ -0,0 +1,89 @@ +-module(mod_s2s_proxy). +-author('xram@jabber.ru'). + +-behaviour(gen_mod). +-behaviour(gen_server). + +%% gen_server callbacks. +-export([init/1, + handle_info/2, + handle_call/3, + handle_cast/2, + terminate/2, + code_change/3 + ]). + +%% gen_mod callbacks. +-export([start/2, stop/1]). + +%% API. +-export([start_link/2]). + +-define(PROCNAME, ?MODULE). +-include("ejabberd.hrl"). +-include("jlib.hrl"). + +-record(state, {host, routes}). + +%% Unused callbacks. +handle_cast(_Request, State) -> + {noreply, State}. +code_change(_OldVsn, State, _Extra) -> + {ok, State}. +%% ----- + +start(Host, Opts) -> + Proc = gen_mod:get_module_proc(Host, ?PROCNAME), + ChildSpec = { + Proc, {?MODULE, start_link, [Host, Opts]}, + transient, 5000, worker, [?MODULE] + }, + supervisor:start_child(ejabberd_sup, ChildSpec). + +stop(Host) -> + Proc = gen_mod:get_module_proc(Host, ?PROCNAME), + gen_server:call(Proc, stop), + supervisor:terminate_child(ejabberd_sup, Proc), + supervisor:delete_child(ejabberd_sup, Proc). + +start_link(Host, Opts) -> + Proc = gen_mod:get_module_proc(Host, ?PROCNAME), + gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). + +init([Host, Opts]) -> + case gen_mod:get_opt(hosts, Opts, []) of + RelayRoutes when is_list(RelayRoutes) -> + ejabberd_router:register_routes(RelayRoutes), + {ok, #state{routes=RelayRoutes, host=Host}}; + Junk -> + ?ERROR_MSG("Incorrect configuration of relay domains: ~p", [Junk]), + error + end. + +handle_info({route, From, To, Packet}, #state{host=Host} = State) -> + MyHost = From#jid{lserver=Host, server=Host}, + %% FIXME: Exporting find_connection/2 from ejabberd_s2s.erl + %% looks like copy-pasting hack, but what can we do? + case ejabberd_s2s:find_connection(MyHost, To) of + {atomic, Pid} when is_pid(Pid) -> + {xmlelement, Name, Attrs, Els} = Packet, + NewAttrs = jlib:replace_from_to_attrs(jlib:jid_to_string(From), + jlib:jid_to_string(To), + Attrs), + Pid ! {send_element, {xmlelement, Name, NewAttrs, Els}}; + {aborted, _Reason} -> + ?DEBUG("delivery failed: ~p~n", [_Reason]) + end, + {noreply, State}; + +handle_info(_Info, State) -> + {noreply, State}. + +handle_call(stop, _From, State) -> + {stop, normal, ok, State}; +handle_call(_Request, _From, State) -> + {reply, ok, State}. + +terminate(_Reason, #state{routes = RelayRoutes}) -> + ejabberd_router:unregister_routes(RelayRoutes), + ok.