summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Nilsson <hans@erlang.org>2019-09-02 15:13:41 +0200
committerHans Nilsson <hans@erlang.org>2019-09-20 10:31:47 +0200
commit6169211c12a48c31eadba4473940a28aa675c944 (patch)
tree1192300ea8b2edb6b428400e0d3959f15af78b99
parent604499b2b752fe0c09c06dc2e7d10e767f4d5456 (diff)
downloaderlang-6169211c12a48c31eadba4473940a28aa675c944.tar.gz
ssh: direct-tcpip, client part
-rw-r--r--lib/ssh/doc/src/ssh.xml21
-rw-r--r--lib/ssh/src/ssh.erl56
-rw-r--r--lib/ssh/src/ssh_connection.erl2
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl20
-rw-r--r--lib/ssh/src/ssh_tcpip_forward_acceptor.erl3
5 files changed, 100 insertions, 2 deletions
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index b38ab2ce5f..bb8893f4a7 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -1323,6 +1323,27 @@
</p>
</desc>
</func>
+
+ <func>
+ <name name="tcpip_tunnel_to_server" arity="5" since=""/>
+ <name name="tcpip_tunnel_to_server" arity="6" since=""/>
+ <fsummary>TCP/IP tunneling from a client to a server ("direct-tcpip")</fsummary>
+ <desc>
+ <p>Tells the local client to listen to <c>ListenHost:ListenPort</c>. When someone
+ connects to that address, the connection is forwarded in an encrypted channel to the peer server
+ of <c>ConnectionRef</c>. That server then connects to <c>ConnectToHost:ConnectToPort</c>.
+ </p>
+ <p>The returned <c>TrueListenPort</c> is the port that is listened to. It is the same as
+ <c>ListenPort</c>, except when <c>ListenPort = 0</c>. In that case a free port is selected
+ by the underlying OS.
+ </p>
+ <p>Note that in case of an Erlang/OTP SSH server (daemon) as peer, that server must have been
+ started with the option
+ <seealso marker="#type-tcpip_tunnel_in_daemon_option">tcpip_tunnel_in</seealso>
+ to allow the connection.
+ </p>
+ </desc>
+ </func>
</funcs>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 1471654a89..dc50c9cd6c 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -40,7 +40,8 @@
stop_listener/1, stop_listener/2, stop_listener/3,
stop_daemon/1, stop_daemon/2, stop_daemon/3,
shell/1, shell/2, shell/3,
- tcpip_tunnel_from_server/5, tcpip_tunnel_from_server/6
+ tcpip_tunnel_from_server/5, tcpip_tunnel_from_server/6,
+ tcpip_tunnel_to_server/5, tcpip_tunnel_to_server/6
]).
%%% "Deprecated" types export:
@@ -566,6 +567,59 @@ chk_algos_opts(Opts) ->
end.
%%--------------------------------------------------------------------
+%% Ask local client to listen to ListenHost:ListenPort. When someone
+%% connects that address, connect to ConnectToHost:ConnectToPort from
+%% the server.
+%%--------------------------------------------------------------------
+-spec tcpip_tunnel_to_server(ConnectionRef,
+ ListenHost, ListenPort,
+ ConnectToHost, ConnectToPort
+ ) ->
+ {ok,TrueListenPort} | {error, term()} when
+ ConnectionRef :: connection_ref(),
+ ListenHost :: host(),
+ ListenPort :: inet:port_number(),
+ ConnectToHost :: host(),
+ ConnectToPort :: inet:port_number(),
+ TrueListenPort :: inet:port_number().
+
+tcpip_tunnel_to_server(ConnectionHandler, ListenHost, ListenPort, ConnectToHost, ConnectToPort) ->
+ tcpip_tunnel_to_server(ConnectionHandler, ListenHost, ListenPort, ConnectToHost, ConnectToPort, infinity).
+
+
+-spec tcpip_tunnel_to_server(ConnectionRef,
+ ListenHost, ListenPort,
+ ConnectToHost, ConnectToPort,
+ Timeout) ->
+ {ok,TrueListenPort} | {error, term()} when
+ ConnectionRef :: connection_ref(),
+ ListenHost :: host(),
+ ListenPort :: inet:port_number(),
+ ConnectToHost :: host(),
+ ConnectToPort :: inet:port_number(),
+ Timeout :: timeout(),
+ TrueListenPort :: inet:port_number().
+
+tcpip_tunnel_to_server(ConnectionHandler, ListenHost, ListenPort, ConnectToHost0, ConnectToPort, Timeout) ->
+ SockOpts = [],
+ try
+ list_to_binary(
+ case mangle_connect_address(ConnectToHost0,SockOpts) of
+ IP when is_tuple(IP) -> inet_parse:ntoa(IP);
+ _ when is_list(ConnectToHost0) -> ConnectToHost0
+ end)
+ of
+ ConnectToHost ->
+ ssh_connection_handler:handle_direct_tcpip(ConnectionHandler,
+ mangle_tunnel_address(ListenHost), ListenPort,
+ ConnectToHost, ConnectToPort,
+ Timeout)
+ catch
+ _:_ ->
+ {error, bad_connect_to_address}
+ end.
+
+%%--------------------------------------------------------------------
%% Ask remote server to listen to ListenHost:ListenPort. When someone
%% connects that address, connect to ConnectToHost:ConnectToPort from
%% the client.
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 1c22b095e8..3fb4222d53 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -685,7 +685,7 @@ handle_msg(#ssh_msg_channel_open{channel_type = "direct-tcpip",
send_buf = queue:new()
}),
gen_tcp:controlling_process(Sock, Pid),
- ssh_tcpip_forward_srv:use_socket(Pid, Sock),
+ inet:setopts(Sock, [{active,once}]),
{channel_open_confirmation_msg(RemoteId, ChId,
?DEFAULT_WINDOW_SIZE,
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index bae27da330..3ed42aff8d 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -49,6 +49,7 @@
available_hkey_algorithms/2,
open_channel/6,
start_channel/5,
+ handle_direct_tcpip/6,
request/6, request/7,
reply_request/3,
global_request/5,
@@ -207,6 +208,10 @@ start_channel(ConnectionHandler, CallbackModule, ChannelId, Args, Exec) ->
Args, Exec, Opts).
%%--------------------------------------------------------------------
+handle_direct_tcpip(ConnectionHandler, ListenHost, ListenPort, ConnectToHost, ConnectToPort, Timeout) ->
+ call(ConnectionHandler, {handle_direct_tcpip, ListenHost, ListenPort, ConnectToHost, ConnectToPort, Timeout}).
+
+%%--------------------------------------------------------------------
-spec request(connection_ref(),
pid(),
channel_id(),
@@ -1583,6 +1588,21 @@ handle_event(info, {fwd_connect_received, Sock, ChId, ChanCB}, StateName, #data{
inet:setopts(Sock, [{active,once}]),
keep_state_and_data;
+handle_event({call,From},
+ {handle_direct_tcpip, ListenHost, ListenPort, ConnectToHost, ConnectToPort, _Timeout},
+ _StateName,
+ #data{connection_state = #connection{sub_system_supervisor=SubSysSup}}) ->
+ case ssh_tcpip_forward_acceptor:supervised_start(ssh_subsystem_sup:tcpip_fwd_supervisor(SubSysSup),
+ {ListenHost, ListenPort},
+ {ConnectToHost, ConnectToPort},
+ "direct-tcpip", ssh_tcpip_forward_client,
+ self()) of
+ {ok,LPort} ->
+ {keep_state_and_data, [{reply,From,{ok,LPort}}]};
+ {error,Error} ->
+ {keep_state_and_data, [{reply,From,{error,Error}}]}
+ end;
+
handle_event(info, UnexpectedMessage, StateName, D = #data{ssh_params = Ssh}) ->
case unexpected_fun(UnexpectedMessage, D) of
report ->
diff --git a/lib/ssh/src/ssh_tcpip_forward_acceptor.erl b/lib/ssh/src/ssh_tcpip_forward_acceptor.erl
index 60a0a7a0e1..6f2fda2bf9 100644
--- a/lib/ssh/src/ssh_tcpip_forward_acceptor.erl
+++ b/lib/ssh/src/ssh_tcpip_forward_acceptor.erl
@@ -79,6 +79,9 @@ acceptor_loop(LSock, ListenAddrStr, ListenPort, ConnectToAddr, ChanType, ChanCB,
case ConnectToAddr of
undefined ->
<<?STRING(ListenAddrStr), ?UINT32(ListenPort),
+ ?STRING(RemHostBin), ?UINT32(RemPort)>>;
+ {ConnectToHost, ConnectToPort} ->
+ <<?STRING(ConnectToHost), ?UINT32(ConnectToPort),
?STRING(RemHostBin), ?UINT32(RemPort)>>
end,
case ssh_connection:open_channel(ConnPid, ChanType, Data, infinity) of