--- 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.

