From 6169211c12a48c31eadba4473940a28aa675c944 Mon Sep 17 00:00:00 2001
From: Hans Nilsson
Date: Mon, 2 Sep 2019 15:13:41 +0200
Subject: ssh: direct-tcpip, client part
---
lib/ssh/doc/src/ssh.xml | 21 +++++++++++
lib/ssh/src/ssh.erl | 56 +++++++++++++++++++++++++++++-
lib/ssh/src/ssh_connection.erl | 2 +-
lib/ssh/src/ssh_connection_handler.erl | 20 +++++++++++
lib/ssh/src/ssh_tcpip_forward_acceptor.erl | 3 ++
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 @@
+
+
+
+
+ TCP/IP tunneling from a client to a server ("direct-tcpip")
+
+ Tells the local client to listen to ListenHost:ListenPort. When someone
+ connects to that address, the connection is forwarded in an encrypted channel to the peer server
+ of ConnectionRef. That server then connects to ConnectToHost:ConnectToPort.
+
+ The returned TrueListenPort is the port that is listened to. It is the same as
+ ListenPort, except when ListenPort = 0. In that case a free port is selected
+ by the underlying OS.
+
+ Note that in case of an Erlang/OTP SSH server (daemon) as peer, that server must have been
+ started with the option
+ tcpip_tunnel_in
+ to allow the connection.
+
+
+
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:
@@ -565,6 +566,59 @@ chk_algos_opts(Opts) ->
{error, {non_algo_opts_found,OtherOps}}
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
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,
@@ -206,6 +207,10 @@ start_channel(ConnectionHandler, CallbackModule, ChannelId, Args, Exec) ->
ConnectionHandler, CallbackModule, ChannelId,
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(),
@@ -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 ->
<>;
+ {ConnectToHost, ConnectToPort} ->
+ <>
end,
case ssh_connection:open_channel(ConnPid, ChanType, Data, infinity) of
--
cgit v1.2.1