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