aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/ssh/doc/src/ssh.xml28
-rw-r--r--lib/ssh/src/ssh.erl24
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl29
-rw-r--r--lib/ssh/test/Makefile5
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl51
-rw-r--r--lib/ssh/test/ssh_peername_sockname_server.erl56
6 files changed, 177 insertions, 16 deletions
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index fb58a4b014..896b98edc2 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -334,6 +334,20 @@
</func>
<func>
+ <name>peername(ConnectionRef) -> {ok, {Address,Port}} | {error,Error} </name>
+ <fsummary> </fsummary>
+ <type>
+ <v> ConnectionRef = ssh_connection_ref()</v>
+ <v> Address = ip_address()</v>
+ <v> Port = integer()</v>
+ </type>
+ <desc>
+ <p>Returns the address and port for the other end of a connection.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name>shell(Host) -> </name>
<name>shell(Host, Option) -> </name>
<name>shell(Host, Port, Option) -> _</name>
@@ -353,6 +367,20 @@
</func>
<func>
+ <name>sockname(ConnectionRef) -> {ok, {Address,Port}} | {error,Error} </name>
+ <fsummary> </fsummary>
+ <type>
+ <v> ConnectionRef = ssh_connection_ref()</v>
+ <v> Address = ip_address()</v>
+ <v> Port = integer()</v>
+ </type>
+ <desc>
+ <p>Returns the local address and port number for a connection.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name>start() -> </name>
<name>start(Type) -> ok | {error, Reason}</name>
<fsummary>Starts the SSH application. </fsummary>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 7d5478c3f6..80d20abbbd 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -28,6 +28,8 @@
-export([start/0, start/1, stop/0, connect/3, connect/4, close/1, connection_info/2,
channel_info/3,
daemon/1, daemon/2, daemon/3,
+ peername/1,
+ sockname/1,
stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2,
shell/1, shell/2, shell/3]).
@@ -245,6 +247,28 @@ shell(Host, Port, Options) ->
end.
%%--------------------------------------------------------------------
+%% Function: peername(ConnectionRef) -> {ok, {Host,Port}}
+%% | {error,Error}
+%%
+%% Description: Returns the peer address of the connection
+%%--------------------------------------------------------------------
+peername(ConnectionRef) ->
+ [{peer, {_Name,{IP,Port}}}] =
+ ssh_connection_manager:connection_info(ConnectionRef, [peer]),
+ {ok, {IP,Port}}.
+
+%%--------------------------------------------------------------------
+%% Function: sockname(ConnectionRef) -> {ok, {Host,Port}}
+%% | {error,Error}
+%%
+%% Description: Returns the local address of the connection
+%%--------------------------------------------------------------------
+sockname(ConnectionRef) ->
+ [{sockname, Result}] =
+ ssh_connection_manager:connection_info(ConnectionRef, [sockname]),
+ Result.
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
fix_idle_time(SshOptions) ->
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 9de4dd5967..c3e8a3c742 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -46,7 +46,7 @@
handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
%% spawn export
--export([ssh_info_handler/3]).
+-export([ssh_info_handler/4]).
-record(state, {
transport_protocol, % ex: tcp
@@ -533,7 +533,7 @@ handle_event(renegotiate, StateName, State) ->
{next_state, StateName, State};
handle_event({info, From, Options}, StateName, #state{ssh_params = Ssh} = State) ->
- spawn(?MODULE, ssh_info_handler, [Options, Ssh, From]),
+ spawn(?MODULE, ssh_info_handler, [Options, Ssh, State, From]),
{next_state, StateName, State};
handle_event(data_size, connected, #state{ssh_params = Ssh0} = State) ->
{ok, [{send_oct,Sent}]} = inet:getstat(State#state.socket, [send_oct]),
@@ -1022,26 +1022,29 @@ retry_fun(User, Reason, Opts) ->
catch Fun(User, Reason)
end.
-ssh_info_handler(Options, Ssh, From) ->
- Info = ssh_info(Options, Ssh, []),
+ssh_info_handler(Options, Ssh, State, From) ->
+ Info = ssh_info(Options, Ssh, State, []),
ssh_connection_manager:send_msg({channel_requst_reply, From, Info}).
-ssh_info([], _, Acc) ->
+ssh_info([], _, _, Acc) ->
Acc;
ssh_info([client_version | Rest], #ssh{c_vsn = IntVsn,
- c_version = StringVsn} = SshParams, Acc) ->
- ssh_info(Rest, SshParams, [{client_version, {IntVsn, StringVsn}} | Acc]);
+ c_version = StringVsn} = SshParams, State, Acc) ->
+ ssh_info(Rest, SshParams, State, [{client_version, {IntVsn, StringVsn}} | Acc]);
ssh_info([server_version | Rest], #ssh{s_vsn = IntVsn,
- s_version = StringVsn} = SshParams, Acc) ->
- ssh_info(Rest, SshParams, [{server_version, {IntVsn, StringVsn}} | Acc]);
+ s_version = StringVsn} = SshParams, State, Acc) ->
+ ssh_info(Rest, SshParams, State, [{server_version, {IntVsn, StringVsn}} | Acc]);
-ssh_info([peer | Rest], #ssh{peer = Peer} = SshParams, Acc) ->
- ssh_info(Rest, SshParams, [{peer, Peer} | Acc]);
+ssh_info([peer | Rest], #ssh{peer = Peer} = SshParams, State, Acc) ->
+ ssh_info(Rest, SshParams, State, [{peer, Peer} | Acc]);
-ssh_info([ _ | Rest], SshParams, Acc) ->
- ssh_info(Rest, SshParams, Acc).
+ssh_info([sockname | Rest], SshParams, #state{socket=Socket}=State, Acc) ->
+ ssh_info(Rest, SshParams, State, [{sockname,inet:sockname(Socket)}|Acc]);
+
+ssh_info([ _ | Rest], SshParams, State, Acc) ->
+ ssh_info(Rest, SshParams, State, Acc).
log_error(Reason) ->
Report = io_lib:format("Erlang ssh connection handler failed with reason: "
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index f5db31baee..13caafc055 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2012. All Rights Reserved.
+# Copyright Ericsson AB 2004-2013. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -38,7 +38,8 @@ MODULES= \
ssh_sftpd_SUITE \
ssh_sftpd_erlclient_SUITE \
ssh_connection_SUITE \
- ssh_echo_server
+ ssh_echo_server \
+ ssh_peername_sockname_server
HRL_FILES_NEEDED_IN_TEST= \
$(ERL_TOP)/lib/ssh/src/ssh.hrl \
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 0aa60624bf..e8f1d5213c 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -22,6 +22,7 @@
-module(ssh_basic_SUITE).
-include_lib("common_test/include/ct.hrl").
+-include_lib("kernel/include/inet.hrl").
%% Note: This directive should only be used in test suites.
-compile(export_all).
@@ -48,7 +49,9 @@ all() ->
close].
groups() ->
- [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey, openssh_zlib_basic_test]},
+ [{dsa_key, [], [send,
+ peername_sockname,
+ exec, exec_compressed, shell, known_hosts, idle_time, rekey, openssh_zlib_basic_test]},
{rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey, openssh_zlib_basic_test]},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
@@ -473,6 +476,52 @@ send(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+peername_sockname() ->
+ [{doc, "Test ssh:peername/1 and ssh:sockname/1"}].
+peername_sockname(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {subsystems, [{"peername_sockname",
+ {ssh_peername_sockname_server, []}}
+ ]}
+ ]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false}]),
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ success = ssh_connection:subsystem(ConnectionRef, ChannelId, "peername_sockname", infinity),
+ {ok,{HostPeerClient,PortPeerClient}} = ssh:peername(ConnectionRef),
+ {ok,{HostSockClient,PortSockClient}} = ssh:sockname(ConnectionRef),
+ receive
+ {ssh_cm, ConnectionRef, {data, ChannelId, _, Response}} ->
+ {PeerNameSrv,SockNameSrv} = binary_to_term(Response),
+ {ok,{HostPeerSrv,PortPeerSrv}} = PeerNameSrv,
+ {ok,{HostSockSrv,PortSockSrv}} = SockNameSrv,
+ host_equal(HostPeerSrv, HostSockClient),
+ PortPeerSrv = PortSockClient,
+ host_equal(HostSockSrv, HostPeerClient),
+ PortSockSrv = PortPeerClient,
+ host_equal(HostSockSrv, Host),
+ PortSockSrv = Port
+ after 10000 ->
+ throw(timeout)
+ end.
+
+host_equal(H1, H2) ->
+ not ordsets:is_disjoint(ips(H1), ips(H2)).
+
+ips(IP) when is_tuple(IP) -> ordsets:from_list([IP]);
+ips(Name) when is_list(Name) ->
+ {ok,#hostent{h_addr_list=IPs4}} = inet:gethostbyname(Name,inet),
+ {ok,#hostent{h_addr_list=IPs6}} = inet:gethostbyname(Name,inet6),
+ ordsets:from_list(IPs4++IPs6).
+
+%%--------------------------------------------------------------------
close() ->
[{doc, "Simulate that we try to close an already closed connection"}].
close(Config) when is_list(Config) ->
diff --git a/lib/ssh/test/ssh_peername_sockname_server.erl b/lib/ssh/test/ssh_peername_sockname_server.erl
new file mode 100644
index 0000000000..7664f3ee25
--- /dev/null
+++ b/lib/ssh/test/ssh_peername_sockname_server.erl
@@ -0,0 +1,56 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssh_peername_sockname_server).
+
+%% The purpose of this module is to perform tests on the server side of an
+%% ssh connection.
+
+
+-behaviour(ssh_daemon_channel).
+-record(state, {}).
+
+-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]).
+
+init([]) ->
+ {ok, #state{}}.
+
+handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) ->
+ ssh_connection:send(ConnectionManager, ChannelId,
+ term_to_binary(
+ {catch ssh:peername(ConnectionManager),
+ catch ssh:sockname(ConnectionManager)
+ })
+ ),
+ {ok, State}.
+
+handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _Error, _}},
+ State) ->
+ {stop, ChannelId, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, _Status}}, State) ->
+ {stop, ChannelId, State};
+
+handle_ssh_msg({ssh_cm, _CM, _}, State) ->
+ {ok, State}.
+
+terminate(_Reason, _State) ->
+ ok.