Index: src/ejabberd.cfg.example
===================================================================
--- src/ejabberd.cfg.example	(revision 1279)
+++ src/ejabberd.cfg.example	(working copy)
@@ -96,6 +96,18 @@
 %%{route_subdomains, s2s}.
 
 
+%%%   ==============
+%%%   SOCKS CLIENT 
+%%%
+%%
+%%
+{socks5_server, {127,0,0,1}}.
+{socks5_port, 9050}.
+%%{socks_use, none}. 
+{socks5_use, all}. 
+%%{socks5_use, dot_onion_only}. 
+
+
 %%%   ===============
 %%%   LISTENING PORTS
 
Index: src/ejabberd_config.erl
===================================================================
--- src/ejabberd_config.erl	(revision 1279)
+++ src/ejabberd_config.erl	(working copy)
@@ -138,6 +138,10 @@
 	    add_option(listen, Val, State);
 	{language, Val} ->
 	    add_option(language, Val, State);
+
+	{socks5_server, Addr} -> inet_db:set_socks_server(Addr), State;
+	{socks5_port, Port} -> inet_db:set_socks_port(Port), State;
+	{socks5_use, Use} -> add_option(socks5_use, Use, State);
 	{outgoing_s2s_port, Port} ->
 	    add_option(outgoing_s2s_port, Port, State);
 	{s2s_use_starttls, Port} ->
Index: src/socks5.erl
===================================================================
--- src/socks5.erl	(revision 0)
+++ src/socks5.erl	(revision 0)
@@ -0,0 +1,249 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.0, (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% http://www.erlang.org/EPL1_0.txt
+%% 
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%% 
+%% The Original Code is Erlang-4.7.3, December, 1998.
+%% 
+%% The Initial Developer of the Original Code is Ericsson Telecom
+%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
+%% Telecom AB. All Rights Reserved.
+%% 
+%% Contributor(s): ______________________________________.''
+%%
+%%% File    : socks5.erl
+%%% Author  : Tony Rogvall <tony@erix.ericsson.se>
+%%% Purpose :  Socks5 general interface
+%%% Created :  4 Mar 1997 by Tony Rogvall <tony@erix.ericsson.se>
+
+
+%%%
+%%%  code derived (by deletion of unneeded code) 
+%%%
+
+-module(socks5).
+
+
+%%%-include("inet_int.hrl").
+%%%
+%%% macros from inet_int.hrl, included in order to remove dependency to
+%%% kernel .hrl file
+%%%
+
+-define(int16(X), [((X) bsr 8) band 16#ff, (X) band 16#ff]).
+-define(u16(X1,X0), (((X1) bsl 8) bor (X0))).
+
+%%%
+
+-include("socks5.hrl").
+
+-export([open/0, open/1, open/2, connect/3, connect/4]).
+-export([is_direct/1]).
+
+open() ->
+    do_open([], infinity).
+
+open(Opts) ->
+    do_open(Opts, infinity).
+
+open(Opts, infinity) -> 
+    do_open(Opts, infinity);
+open(Opts, TimeOut) when integer(TimeOut), TimeOut >= 0 ->
+    do_open(Opts, TimeOut).
+
+do_open(Opts, TimeOut) ->
+    case inet_db:socks_option(server) of
+	[] -> {error, "no socks5 server"};
+	Server ->
+	    Port = inet_db:socks_option(port),
+	    case inet_tcp:connect(
+		   Server, Port,
+		   Opts ++ [{active,false}, {header, 0}], TimeOut) of
+		{ok, S} -> auth(S);
+		Error -> Error
+	    end
+    end.
+
+auth(S) ->
+    Methods = inet_db:socks_option(methods),
+    Ms = encode_methods(Methods),
+    inet_tcp:send(S, [?SOCKS5_VER, length(Methods) | Ms]),
+    case inet_tcp:recv(S, 2) of
+	{ok, [?SOCKS5_VER, M]} ->
+	    socks5_auth:negotiate(S, decode_method(M));
+	_ ->
+	    {error, unknown}
+    end.
+
+%%
+%% Connect to remote address
+%%
+connect(S, IP, Port) when integer(Port) -> 
+    do_connect(S, IP, Port, infinity).
+
+connect(S, IP, Port,infinity) when integer(Port) -> 
+    do_connect(S, IP, Port, infinity);
+connect(S, IP, Port, Time) when integer(Port),integer(Time),Time >=0 -> 
+    do_connect(S, IP, Port, Time).
+
+do_connect(S, IP, Port, Time) -> 
+    case enc_address(IP) of
+	{ok, Address} ->
+	    cmd(S, ?SOCKS5_REQ_CONNECT, Address, Port, Time);
+	Error -> Error
+    end.
+
+%%
+%% Encode socks5 address format
+%% return {ok, AddressString} or {error,Reason}
+%%
+enc_address(IPV4) when tuple(IPV4), size(IPV4) == 4 ->
+    Addr = tuple_to_list(IPV4),
+    case is_byte_list(Addr) of
+	false -> {error, einval};
+	true  -> {ok, [?SOCKS5_ATYP_V4 | Addr]}
+    end;
+enc_address(IPV6) when tuple(IPV6), size(IPV6) == 8 ->
+    Addr = lists:flatmap(fun(X) when X>=0, X=<16#ffff ->
+				 [(X bsr 8) band 16#ff, X band 16#ff];
+			    (_) -> error
+			 end, tuple_to_list(IPV6)),
+    case is_byte_list(Addr) of
+	false -> {error, einval};
+	true  -> {ok, [?SOCKS5_ATYP_V6 | Addr]}
+    end;    
+enc_address(Dom) when atom(Dom) ->
+    Addr = atom_to_list(Dom),
+    {ok, [?SOCKS5_ATYP_DOM, length(Addr), Addr]};
+enc_address(Dom) when list(Dom) ->
+    case is_byte_list(Dom) of
+	false -> {error, einval};
+	true -> {ok, [?SOCKS5_ATYP_DOM, length(Dom), Dom]}
+    end;
+enc_address(_) -> 
+    {error, einval}.
+
+
+%%
+%% Decode address format and read rest of reply
+%%
+dec_address(S, ?SOCKS5_ATYP_V4, A) ->
+    case inet_tcp:recv(S, 5) of
+	{ok, [B,C,D,P1,P0]} ->
+	    {ok, {{A,B,C,D}, ?u16(P1,P0)}};
+	_ -> dec_error(S)
+    end;
+dec_address(S, ?SOCKS5_ATYP_V6, X1) ->
+    case inet_tcp:recv(S, 17) of
+	{ok, As} ->
+	    [X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13,X14,X15,X16,P1,P0] = As,
+	    {ok, { { ?u16(X1,X2),?u16(X3,X4),?u16(X5,X6),?u16(X7,X8),
+		    ?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)},
+		  ?u16(P1,P0)}};
+	_ -> dec_error(S)
+    end;
+dec_address(S, ?SOCKS5_ATYP_DOM, Len) ->
+    Len1 = Len+2,
+    case inet_tcp:recv(S, Len1) of
+	{ok, Addr} ->
+	    Dom = lists:sublist(Addr, 1, Len),
+	    [P1,P0] = lists:sublist(Addr, Len+1, 2),
+	    {ok, {Dom, ?u16(P1, P0)}};
+	_ -> dec_error(S)
+    end;
+dec_address(S, _, _) ->
+    dec_error(S).
+
+
+dec_error(S) ->
+    inet_tcp:close(S),
+    {error, "Unknown error"}.
+
+	    
+
+%% Send command to socks server
+cmd(S, Cmd, Address, Port, TimeOut) ->
+    inet_tcp:send(S, [?SOCKS5_VER, Cmd, 0, Address, ?int16(Port)]),
+    rep(S, TimeOut).
+    
+%% Read from socks server
+rep(S, TimeOut) ->
+    case inet_tcp:recv(S, 5, TimeOut) of
+	{ok, [?SOCKS5_VER, ?SOCKS5_REP_OK, _, AType, A1]} ->
+	    dec_address(S, AType, A1);
+	{ok, [?SOCKS5_VER, Rep | _]} ->
+	    inet_tcp:close(S),
+	    case Rep of
+		?SOCKS5_REP_FAIL ->
+		    {error, "General SOCKS server failure"};
+		?SOCKS5_REP_NOT_ALLOWED -> 
+		    {error, "Connection not allowed by rule set"};
+		?SOCKS5_REP_NET_UNREACHABLE ->
+		    {error, "Network unreachable"};
+		?SOCKS5_REP_HOST_UNREACHABLE ->
+		    {error, "Host unreachable"};
+		?SOCKS5_REP_REFUSED ->
+		    {error, "Connection refused"};
+		?SOCKS5_REP_TTL_EXPIRED ->
+		    {error, "TTL expired"};
+		?SOCKS5_REP_CMD_NOT_SUPPORTED ->
+		    {error, "Command not supported"};
+		?SOCKS5_REP_ATYP_NOT_SUPPORTED ->
+		    {error, "Address type not supported"};
+		_ ->
+		    {error, "Unknown error"}
+	    end;
+	{error,timeout} ->
+	    {error, timeout};
+	_ ->
+	    dec_error(S)
+    end.
+
+
+encode_methods([none | Ms]) ->
+    [?SOCKS5_AUTH_NONE | encode_methods(Ms)];
+encode_methods([gssapi | Ms]) ->
+    [?SOCKS5_AUTH_GSSAPI | encode_methods(Ms)];
+encode_methods([user | Ms]) ->
+    [?SOCKS5_AUTH_USER | encode_methods(Ms)];
+encode_methods([Other | Ms]) when integer(Other), Other < 16#ff ->
+    [Other | encode_methods(Ms)];
+encode_methods([]) -> [].
+
+
+decode_method(?SOCKS5_AUTH_NONE) -> none;
+decode_method(?SOCKS5_AUTH_GSSAPI) -> gssapi;
+decode_method(?SOCKS5_AUTH_USER) -> user;
+decode_method(?SOCKS5_AUTH_ERR) -> error;
+decode_method(Other) -> Other.
+
+is_byte_list([H | T]) when H >= 0, H =< 255 ->
+    is_byte_list(T);
+is_byte_list([]) -> true;
+is_byte_list(_) -> false.
+
+%%
+%% Check if an ip address is direct i.e not via the socks server
+%%
+is_direct(IP) ->
+    is_direct(inet_db:socks_option(noproxy), IP).
+
+is_direct([{Net,Mask} | Ms], IP) ->
+    case is_net(Net,Mask,IP) of
+	true -> true;
+	false -> is_direct(Ms, IP)
+    end;
+is_direct([], _) -> false.
+
+is_net({NA,NB,NC,ND},{MA,MB,MC,MD},{A,B,C,D}) ->
+    if A band MA == NA,
+       B band MB == NB,
+       C band MC == NC,
+       D band MD == ND -> true;
+       true -> false
+    end.
Index: src/ejabberd_socket.erl
===================================================================
--- src/ejabberd_socket.erl	(revision 1279)
+++ src/ejabberd_socket.erl	(working copy)
@@ -30,6 +30,7 @@
 %% API
 -export([start/4,
 	 connect/3,
+	 connect/4,
 	 starttls/2,
 	 starttls/3,
 	 compress/1,
@@ -48,7 +49,7 @@
 
 %%====================================================================
 %% API
-%%====================================================================
+  %,%====================================================================
 %%--------------------------------------------------------------------
 %% Function: 
 %% Description:
@@ -84,25 +85,32 @@
     end.
 
 connect(Addr, Port, Opts) ->
-    case gen_tcp:connect(Addr, Port, Opts) of
-	{ok, Socket} ->
-	    Receiver = ejabberd_receiver:start(Socket, gen_tcp, none),
-	    SocketData = #socket_state{sockmod = gen_tcp,
-				       socket = Socket,
-				       receiver = Receiver},
-	    Pid = self(),
-	    case gen_tcp:controlling_process(Socket, Receiver) of
-		ok ->
-		    ejabberd_receiver:become_controller(Receiver, Pid),
-		    {ok, SocketData};
-		{error, _Reason} = Error ->
-		    gen_tcp:close(Socket),
-		    Error
-	    end;
-	{error, _Reason} = Error ->
-	    Error
-    end.
+    connect (Addr, Port, Opts, gen_tcp).
 
+connect(Addr, Port, Opts, Type) ->
+    ConnectFun = case Type of
+       gen_tcp -> fun (A,P,O) -> gen_tcp:connect(A,P,O) end;
+       socks5 -> fun (A,P,O) -> socks5_tcp:connect(A,P,O) end 
+       end,
+    case ConnectFun(Addr, Port, Opts)  of
+    {ok, Socket} ->
+       Receiver = ejabberd_receiver:start(Socket, gen_tcp, none),
+       SocketData = #socket_state{sockmod = gen_tcp,
+                      socket = Socket,
+                      receiver = Receiver},
+       Pid = self(),
+       case gen_tcp:controlling_process(Socket, Receiver) of
+       ok ->
+           ejabberd_receiver:become_controller(Receiver, Pid),
+           {ok, SocketData};
+       {error, _Reason} = Error ->
+           gen_tcp:close(Socket),
+           Error
+       end;
+    {error, _Reason} = Error ->
+       Error
+    end. 
+
 starttls(SocketData, TLSOpts) ->
     {ok, TLSSocket} = tls:tcp_to_tls(SocketData#socket_state.socket, TLSOpts),
     ejabberd_receiver:starttls(SocketData#socket_state.receiver, TLSSocket),
@@ -173,3 +181,5 @@
 %%====================================================================
 %% Internal functions
 %%====================================================================
+
+
Index: src/socks5_tcp.erl
===================================================================
--- src/socks5_tcp.erl	(revision 0)
+++ src/socks5_tcp.erl	(revision 0)
@@ -0,0 +1,103 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.0, (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% http://www.erlang.org/EPL1_0.txt
+%% 
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%% 
+%% The Original Code is Erlang-4.7.3, December, 1998.
+%% 
+%% The Initial Developer of the Original Code is Ericsson Telecom
+%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
+%% Telecom AB. All Rights Reserved.
+%% 
+%% Contributor(s): ______________________________________.''
+%%
+%%% File    : socks5_tcp.erl
+%%% Author  : Tony Rogvall <tony@erix.ericsson.se>
+%%% Purpose : Socks5 wrapper for tcp
+%%% Created : 16 Apr 1997 by Tony Rogvall <tony@erix.ericsson.se>
+
+%%%
+%%%  code derived (by deletion of unneeded code and renaming) from socks5.erl 
+%%%
+
+
+-module(socks5_tcp).
+
+
+-vsn('0.01').
+
+
+-export([connect/3, connect/4, close/1]).
+-export([send/2, recv/2, recv/3]).
+-export([controlling_process/2]).
+
+%% NOTE. the sock_opts are reversed to fit ll_setopt and must be 
+%% reversed again before call to setopts !!
+%%
+
+%% "Standard" inet  functions
+
+send(Socket, Packet) -> 
+    gen_tcp:send(Socket, Packet).
+
+recv(Socket, Length) -> 
+    gen_tcp:recv(Socket, Length).
+
+recv(Socket, Length, Time) -> 
+    gen_tcp:recv(Socket, Length, Time).
+
+close(Socket) ->
+    gen_tcp:close(Socket).
+
+controlling_process(Socket, Owner) -> 
+    gen_tcp:controlling_process(Socket, Owner).
+
+
+
+%% reset connect options
+c_opts() ->
+    [{active, false}, {header,0}].
+
+%%
+%% Connect
+%%
+connect(Address, Port, Opts) ->
+    do_connect(Address, Port, Opts, infinity).
+
+connect(Address, Port, Opts, infinity) ->
+    do_connect(Address, Port, Opts, infinity);
+connect(Address, Port, Opts, Timeout) when integer(Timeout), Timeout >= 0 ->
+    do_connect(Address, Port, Opts, Timeout).
+
+do_connect({A,B,C,D}, Port, Opts, Time) when integer(A+B+C+D), integer(Port) ->
+    case socks5:is_direct({A,B,C,D}) of
+    true -> inet_tcp:connect({A,B,C,D}, Port, Opts, Time);
+    false -> do_connect1({A,B,C,D}, Port, Opts,Time)
+    end;
+do_connect(Name, Port, Opts, Time) when list(Name), integer(Port) ->
+    do_connect1(Name, Port, Opts, Time).
+  
+do_connect1(Address, Port, Opts, Time) ->
+    case socks5:open([], Time) of
+      {ok, S} ->
+          case socks5:connect(S, Address, Port, Time) of
+            {ok, {BndAddress, BndPort}} ->
+
+                inet:setopts(S, c_opts() ++ 
+                   lists:reverse(Opts)),
+                inet:setsockname(S, {BndAddress, BndPort}),
+                inet:setpeername(S, {{0,0,0,0},Port}),
+                {ok, S};
+            Error ->
+                close(S),
+                Error
+          end;
+      Error -> Error    
+    end.
+
+
Index: src/socks5_auth.erl
===================================================================
--- src/socks5_auth.erl	(revision 0)
+++ src/socks5_auth.erl	(revision 0)
@@ -0,0 +1,91 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.0, (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% http://www.erlang.org/EPL1_0.txt
+%% 
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%% 
+%% The Original Code is Erlang-4.7.3, December, 1998.
+%% 
+%% The Initial Developer of the Original Code is Ericsson Telecom
+%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
+%% Telecom AB. All Rights Reserved.
+%% 
+%% Contributor(s): ______________________________________.''
+%%
+%%% File    : socks5_auth.erl
+%%% Author  : Tony Rogvall <tony@erix.ericsson.se>
+%%% Purpose : Subnegotiations for socks5
+%%% Created : 17 Apr 1997 by Tony Rogvall <tony@erix.ericsson.se>
+
+%%%
+%%%  code derived (one minor fix) from socks5.hrl
+%%%
+
+
+
+-module(socks5_auth).
+-author('tony@erix.ericsson.se').
+
+-export([negotiate/2]).
+
+-define(UNAME_PASSWD_VER, 16#01).
+
+%% Simple version
+negotiate(S, none) ->
+    {ok, S};
+negotiate(S, user) ->
+    %% Check in inet_db for file or user/password ???
+    io:format("Enter Socks5 User/Password (. to cancel)\n"),
+    case read_user_password() of
+	{ok, {User,Password}} ->
+	    inet_tcp:send(S, [?UNAME_PASSWD_VER, length(User), User,
+			      length(Password), Password]),
+	    case inet_tcp:recv(S, 2) of
+		{ok, [?UNAME_PASSWD_VER, 0]} ->
+		    {ok, S};
+		_ ->
+		    inet_tcp:close(S),
+		    {error, "socks5 negotiation failure"}
+	    end;
+	Error -> 
+	    inet_tcp:close(S), 
+	    Error
+    end;
+negotiate(S, gssapi) ->
+    inet_tcp:close(S),
+    {error, "gssapi negotiation not implemented yet"};
+negotiate(S, error) ->
+    inet_tcp:close(S),
+    {error, einval};
+negotiate(S, _Method) ->
+    inet_tcp:close(S),
+    {error, "unknown subnegotiation"}.
+
+
+read_user_password() ->
+    case prompt_string('User: ') of
+	{ok, User} ->
+	    case prompt_string('Password: ') of
+		{ok, Password} ->
+		    {ok, {User, Password}};
+		Error -> Error
+	    end;
+	Error -> Error
+    end.
+    
+prompt_string(Prompt) ->
+    Line = io:get_line(Prompt),
+    Len = length(Line),
+    if Len == 1 ->
+	    prompt_string(Prompt);
+       Line == ".\n" ->
+	    {error, "cancel"};
+       Len >= 256 ->
+	    {ok, lists:sublist(Line, 1, 255)};
+       true ->
+	    {ok, lists:sublist(Line, 1, Len-1)}
+    end.
Index: src/ejabberd.hrl
===================================================================
--- src/ejabberd.hrl	(revision 1279)
+++ src/ejabberd.hrl	(working copy)
@@ -31,7 +31,7 @@
 
 -define(EJABBERD_URI, "http://www.process-one.net/en/ejabberd/").
 
--define(S2STIMEOUT, 600000).
+-define(S2STIMEOUT, 6000000).
 
 %%-define(DBGFSM, true).
 
Index: src/socks5.hrl
===================================================================
--- src/socks5.hrl	(revision 0)
+++ src/socks5.hrl	(revision 0)
@@ -0,0 +1,49 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.0, (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% http://www.erlang.org/EPL1_0.txt
+%% 
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%% 
+%% The Original Code is Erlang-4.7.3, December, 1998.
+%% 
+%% The Initial Developer of the Original Code is Ericsson Telecom
+%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
+%% Telecom AB. All Rights Reserved.
+%% 
+%% Contributor(s): ______________________________________.''
+%%
+%%% File    : socks5.hrl
+%%% Author  : Tony Rogvall <tony@erix.ericsson.se>
+%%% Purpose : Definition of macros and constants for socksv5
+%%% Created :  3 Mar 1997 by Tony Rogvall <tony@erix.ericsson.se>
+
+
+-define(SOCKS5_VER, 16#05).
+
+-define(SOCKS5_AUTH_NONE,   16#00).
+-define(SOCKS5_AUTH_GSSAPI, 16#01).
+-define(SOCKS5_AUTH_USER,   16#02).
+-define(SOCKS5_AUTH_ERR,    16#ff).
+
+-define(SOCKS5_REQ_CONNECT,  16#01).
+-define(SOCKS5_REQ_BIND,     16#02).
+-define(SOCKS5_REQ_UDP_ASSOC,16#03).
+
+-define(SOCKS5_ATYP_V4,  16#01).
+-define(SOCKS5_ATYP_DOM, 16#03).
+-define(SOCKS5_ATYP_V6,  16#04).
+
+-define(SOCKS5_REP_OK,   16#00).
+-define(SOCKS5_REP_FAIL, 16#01).
+-define(SOCKS5_REP_NOT_ALLOWED, 16#02).
+-define(SOCKS5_REP_NET_UNREACHABLE, 16#03).
+-define(SOCKS5_REP_HOST_UNREACHABLE, 16#04).
+-define(SOCKS5_REP_REFUSED, 16#05).
+-define(SOCKS5_REP_TTL_EXPIRED, 16#06).
+-define(SOCKS5_REP_CMD_NOT_SUPPORTED, 16#07).
+-define(SOCKS5_REP_ATYP_NOT_SUPPORTED, 16#08).
+
Index: src/ejabberd_s2s_out.erl
===================================================================
--- src/ejabberd_s2s_out.erl	(revision 1279)
+++ src/ejabberd_s2s_out.erl	(working copy)
@@ -63,6 +63,8 @@
 		tls_required = false,
 		tls_enabled = false,
 		tls_options = [],
+		socks5_use = none,
+		is_socks5 = false,
 		authenticated = false,
 		db_enabled = true,
 		try_auth = true,
@@ -91,6 +93,7 @@
 -define(FSMLIMITS,[]).
 %% -define(FSMLIMITS, [{max_queue, 2000}]).
 -define(FSMTIMEOUT, 5000).
+-define(FSMTIMEOUT_SOCKS5, 60000).
 
 -define(STREAM_HEADER,
 	"<?xml version='1.0'?>"
@@ -155,6 +158,14 @@
 		  CertFile ->
 		      [{certfile, CertFile}, connect]
 	      end,
+    Socks5 = case ejabberd_config:get_local_option(socks5_use) of
+		   dot_onion_only ->
+		      dot_onion_only;
+		   all ->
+		      all;
+		   _ ->
+		      none
+	      end,
     {New, Verify} = case Type of
 			{new, Key} ->
 			    {Key, false};
@@ -169,6 +180,7 @@
 			     queue = queue:new(),
 			     myname = From,
 			     server = Server,
+			     socks5_use = Socks5,
 			     new = New,
 			     verify = Verify,
 			     timer = Timer}}.
@@ -187,33 +199,32 @@
 				StateData#state.server,
 				StateData#state.new,
 				StateData#state.verify}]),
-    AddrList = case idna:domain_utf8_to_ascii(StateData#state.server) of
-		   false -> [];
-		   ASCIIAddr ->
-		       get_addr_port(ASCIIAddr)
-	       end,
-    case lists:foldl(fun({Addr, Port}, Acc) ->
-			     case Acc of
-				 {ok, Socket} ->
-				     {ok, Socket};
-				 _ ->
-				     open_socket1(Addr, Port)
-			     end
-		     end, {error, badarg}, AddrList) of
+    ASCIIAddr = idna:domain_utf8_to_ascii(StateData#state.server), 
+    Is_socks5 = case StateData#state.socks5_use of
+	all -> true;
+	dot_onion_only -> is_dot_onion_domain(ASCIIAddr);
+	_ -> false
+    end,
+    ConnectFun = if 
+	Is_socks5 ->fun (X) -> open_socks5_socket(X) end;
+	true ->  fun (X) -> open_tcp_socket(X) end
+    end,
+    case ConnectFun(ASCIIAddr) of   
 	{ok, Socket} ->
 	    Version = if
-			  StateData#state.use_v10 ->
-			      " version='1.0'";
+		StateData#state.use_v10 ->
+		      " version='1.0'";
 			  true ->
 			      ""
 		      end,
 	    NewStateData = StateData#state{socket = Socket,
+					   is_socks5 = Is_socks5,
 					   tls_enabled = false,
 					   streamid = new_id()},
 	    send_text(NewStateData, io_lib:format(?STREAM_HEADER,
 						  [StateData#state.server,
 						   Version])),
-	    {next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT};
+	    {next_state, wait_for_stream, NewStateData, fsmtimeout(NewStateData)};
 	{error, _Reason} ->
 	    ?INFO_MSG("s2s connection: ~s -> ~s (remote server not found)",
 		      [StateData#state.myname, StateData#state.server]),
@@ -232,7 +243,37 @@
     {next_state, open_socket, StateData}.
 
 %%----------------------------------------------------------------------
-open_socket1(Addr, Port) ->
+open_socks5_socket(ASCIIAddr) ->
+    ?DEBUG("OPEN SOCKS5 SOCKET ~s~n", [ASCIIAddr]),
+    case ejabberd_socket:connect(ASCIIAddr, outgoing_s2s_port(), 
+				[binary, {packet, 0},
+				{active, false}], socks5) of
+	{ok, Socket} ->
+	    {ok, Socket};
+	{error, Reason} ->
+	    ?DEBUG("s2s_out: inet6 connect return ~p~n", [Reason]),
+	    {error, Reason};
+	{'EXIT', Reason} ->
+	    ?DEBUG("s2s_out: inet6 connect crashed ~p~n", [Reason]),
+	    {error, Reason}
+    end.
+
+open_tcp_socket(ASCIIAddr) ->
+    AddrList = case get_addr_port(ASCIIAddr) of
+	  false -> [];
+	  X -> X 
+	end,
+    lists:foldl(fun({Addr, Port}, Acc) ->
+		     case Acc of
+			 {ok, Socket} ->
+			     {ok, Socket};
+			 _ -> 
+			     open_tcp_socket1(Addr, Port)
+		     end
+	     end, {error, badard}, AddrList). 
+
+
+open_tcp_socket1(Addr, Port) ->
     ?DEBUG("s2s_out: connecting to ~s:~p~n", [Addr, Port]),
     Res = case catch ejabberd_socket:connect(
 		       Addr, Port,
@@ -274,9 +315,9 @@
 	    send_db_request(StateData);
 	{"jabber:server", "jabber:server:dialback", true} when
 	StateData#state.use_v10 ->
-	    {next_state, wait_for_features, StateData, ?FSMTIMEOUT};
+	    {next_state, wait_for_features, StateData, fsmtimeout(StateData)};
 	{"jabber:server", "", true} when StateData#state.use_v10 ->
-	    {next_state, wait_for_features, StateData#state{db_enabled = false}, ?FSMTIMEOUT};
+	    {next_state, wait_for_features, StateData#state{db_enabled = false}, fsmtimeout(StateData)};
 	_ ->
 	    send_text(StateData, ?INVALID_NAMESPACE_ERR),
 	    ?INFO_MSG("Closing s2s connection: ~s -> ~s (invalid namespace)",
@@ -330,7 +371,7 @@
 		    NextState = wait_for_validation,
 		    %% TODO: Should'nt we close the connection here ?
 		    {next_state, NextState, StateData,
-		     get_timeout_interval(NextState)};
+		     get_timeout_interval(StateData, NextState)};
 		{Pid, _Key, _SID} ->
 		    case Type of
 			"valid" ->
@@ -350,11 +391,11 @@
 			true ->
 			    NextState = wait_for_validation,
 			    {next_state, NextState, StateData,
-			     get_timeout_interval(NextState)}
+			     get_timeout_interval(StateData, NextState)}
 		    end
 	    end;
 	_ ->
-	    {next_state, wait_for_validation, StateData, ?FSMTIMEOUT*3}
+	    {next_state, wait_for_validation, StateData, fsmtimeout(StateData)*3}
     end;
 
 wait_for_validation({xmlstreamend, _Name}, StateData) ->
@@ -438,21 +479,21 @@
 				    jlib:encode_base64(
 				      StateData#state.myname)}]}),
 		    {next_state, wait_for_auth_result,
-		     StateData#state{try_auth = false}, ?FSMTIMEOUT};
+		     StateData#state{try_auth = false}, fsmtimeout(StateData)};
 		StartTLS and StateData#state.tls and
 		(not StateData#state.tls_enabled) ->
 		    send_element(StateData,
 				 {xmlelement, "starttls",
 				  [{"xmlns", ?NS_TLS}], []}),
 		    {next_state, wait_for_starttls_proceed, StateData,
-		     ?FSMTIMEOUT};
+		     fsmtimeout(StateData)};
 		StartTLSRequired and (not StateData#state.tls) ->
 		    ?DEBUG("restarted: ~p", [{StateData#state.myname,
 					      StateData#state.server}]),
 		    ejabberd_socket:close(StateData#state.socket),
 		    {next_state, reopen_socket,
 		     StateData#state{socket = undefined,
-				     use_v10 = false}, ?FSMTIMEOUT};
+				     use_v10 = false}, fsmtimeout(StateData)};
 		StateData#state.db_enabled ->
 		    send_db_request(StateData);
 		true ->
@@ -461,7 +502,7 @@
 						% TODO: clear message queue
 		    ejabberd_socket:close(StateData#state.socket),
 		    {next_state, reopen_socket, StateData#state{socket = undefined,
-								use_v10 = false}, ?FSMTIMEOUT}
+								use_v10 = false}, fsmtimeout(StateData)}
 	    end;
 	_ ->
 	    send_text(StateData,
@@ -506,7 +547,7 @@
 		    {next_state, wait_for_stream,
 		     StateData#state{streamid = new_id(),
 				     authenticated = true
-				    }, ?FSMTIMEOUT};
+				    }, fsmtimeout(StateData)};
 		_ ->
 		    send_text(StateData,
 			      xml:element_to_string(?SERR_BAD_FORMAT) ++
@@ -522,7 +563,7 @@
 					      StateData#state.server}]),
 		    ejabberd_socket:close(StateData#state.socket),
 		    {next_state, reopen_socket,
-		     StateData#state{socket = undefined}, ?FSMTIMEOUT};
+		     StateData#state{socket = undefined}, fsmtimeout(StateData)};
 		_ ->
 		    send_text(StateData,
 			      xml:element_to_string(?SERR_BAD_FORMAT) ++
@@ -587,7 +628,7 @@
 			      io_lib:format(?STREAM_HEADER,
 					    [StateData#state.server,
 					     " version='1.0'"])),
-		    {next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT};
+		    {next_state, wait_for_stream, NewStateData, fsmtimeout(StateData)};
 		_ ->
 		    send_text(StateData,
 			      xml:element_to_string(?SERR_BAD_FORMAT) ++
@@ -622,21 +663,21 @@
 
 
 reopen_socket({xmlstreamelement, _El}, StateData) ->
-    {next_state, reopen_socket, StateData, ?FSMTIMEOUT};
+    {next_state, reopen_socket, StateData, fsmtimeout(StateData)};
 reopen_socket({xmlstreamend, _Name}, StateData) ->
-    {next_state, reopen_socket, StateData, ?FSMTIMEOUT};
+    {next_state, reopen_socket, StateData, fsmtimeout(StateData)};
 reopen_socket({xmlstreamerror, _}, StateData) ->
-    {next_state, reopen_socket, StateData, ?FSMTIMEOUT};
+    {next_state, reopen_socket, StateData, fsmtimeout(StateData)};
 reopen_socket(timeout, StateData) ->
     ?INFO_MSG("reopen socket: timeout", []),
     {stop, normal, StateData};
 reopen_socket(closed, StateData) ->
     p1_fsm:send_event(self(), init),
-    {next_state, open_socket, StateData, ?FSMTIMEOUT}.
+    {next_state, open_socket, StateData, fsmtimeout(StateData)}.
 
 %% This state is use to avoid reconnecting to often to bad sockets
 wait_before_retry(_Event, StateData) ->
-    {next_state, wait_before_retry, StateData, ?FSMTIMEOUT}.
+    {next_state, wait_before_retry, StateData, fsmtimeout(StateData)}.
 
 stream_established({xmlstreamelement, El}, StateData) ->
     ?DEBUG("s2S stream established", []),
@@ -709,7 +750,7 @@
 %%          {stop, Reason, NewStateData}
 %%----------------------------------------------------------------------
 handle_event(_Event, StateName, StateData) ->
-    {next_state, StateName, StateData, get_timeout_interval(StateName)}.
+    {next_state, StateName, StateData, get_timeout_interval(StateData, StateName)}.
 
 %%----------------------------------------------------------------------
 %% Func: handle_sync_event/4
@@ -722,7 +763,7 @@
 %%----------------------------------------------------------------------
 handle_sync_event(_Event, _From, StateName, StateData) ->
     Reply = ok,
-    {reply, Reply, StateName, StateData, get_timeout_interval(StateName)}.
+    {reply, Reply, StateName, StateData, get_timeout_interval(StateData, StateName)}.
 
 code_change(_OldVsn, StateName, StateData, _Extra) ->
     {ok, StateName, StateData}.
@@ -738,7 +779,7 @@
     cancel_timer(StateData#state.timer),
     Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
     {next_state, StateName, StateData#state{timer = Timer},
-     get_timeout_interval(StateName)};
+     get_timeout_interval(StateData, StateName)};
 
 handle_info({send_element, El}, StateName, StateData) ->
     case StateName of
@@ -755,7 +796,7 @@
 	_ ->
 	    Q = queue:in(El, StateData#state.queue),
 	    {next_state, StateName, StateData#state{queue = Q},
-	     get_timeout_interval(StateName)}
+	     get_timeout_interval(StateData, StateName)}
     end;
 
 handle_info({timeout, Timer, _}, wait_before_retry,
@@ -769,7 +810,7 @@
     {stop, normal, StateData};
 
 handle_info(_, StateName, StateData) ->
-    {next_state, StateName, StateData, get_timeout_interval(StateName)}.
+    {next_state, StateName, StateData, get_timeout_interval(StateData, StateName)}.
 
 %%----------------------------------------------------------------------
 %% Func: terminate/3
@@ -896,7 +937,7 @@
 			   {"id", SID}],
 			  [{xmlcdata, Key2}]})
     end,
-    {next_state, wait_for_validation, StateData#state{new = New}, ?FSMTIMEOUT*6}.
+    {next_state, wait_for_validation, StateData#state{new = New}, fsmtimeout(StateData)*6}.
 
 
 is_verify_res({xmlelement, Name, Attrs, _Els}) when Name == "db:result" ->
@@ -991,16 +1032,16 @@
 
 %% Calcultate timeout depending on which state we are in:
 %% Can return integer > 0 | infinity
-get_timeout_interval(StateName) ->
+get_timeout_interval(StateData, StateName) ->
     case StateName of
 	%% Validation implies dialback: Networking can take longer:
 	wait_for_validation ->
-	    ?FSMTIMEOUT*6;
+	    fsmtimeout(StateData)*6;
 	%% When stream is established, we only rely on S2S Timeout timer:
 	stream_established ->
 	    infinity;
 	_ ->
-	    ?FSMTIMEOUT
+	    fsmtimeout(StateData)
     end.
 
 %% This function is intended to be called at the end of a state
@@ -1013,3 +1054,21 @@
     Timer = erlang:start_timer(Delay, self(), []),
     {next_state, wait_before_retry, StateData#state{timer=Timer,
 						    queue = queue:new()}}.
+
+%% 
+fsmtimeout(StateData) ->
+    if 
+	StateData#state.is_socks5 ->?FSMTIMEOUT_SOCKS5;
+	true -> ?FSMTIMEOUT
+    end.
+
+%% figure out wheter Host is a .onion domain or not
+is_dot_onion_domain(Host) ->
+    case split_hostname(Host, $., []) of
+	[_ | ["onion"] ] -> true;
+	_ -> false
+    end.
+
+split_hostname([Chr|T], Chr, Ack) -> [lists:reverse(Ack)|split_hostname(T, Chr, [])];
+split_hostname([H|T], Chr, Ack)   -> split_hostname(T, Chr, [H|Ack]);
+split_hostname([], _, Ack)        -> [lists:reverse(Ack)].
