aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/test')
-rw-r--r--lib/ssh/test/Makefile6
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl142
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl33
-rw-r--r--lib/ssh/test/ssh_peername_sockname_server.erl54
-rw-r--r--lib/ssh/test/ssh_test_cli.erl81
5 files changed, 290 insertions, 26 deletions
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index f5db31baee..740dbd0235 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,9 @@ MODULES= \
ssh_sftpd_SUITE \
ssh_sftpd_erlclient_SUITE \
ssh_connection_SUITE \
- ssh_echo_server
+ ssh_echo_server \
+ ssh_peername_sockname_server \
+ ssh_test_cli
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..b4e3871efd 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).
@@ -45,15 +46,21 @@ all() ->
daemon_already_started,
server_password_option,
server_userpassword_option,
- close].
+ double_close].
groups() ->
- [{dsa_key, [], [send, 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_key, [], basic_tests()},
+ {rsa_key, [], basic_tests()},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
{internal_error, [], [internal_error]}
].
+
+basic_tests() ->
+ [send, close, peername_sockname,
+ exec, exec_compressed, shell, cli, known_hosts,
+ idle_time, rekey, openssh_zlib_basic_test].
+
%%--------------------------------------------------------------------
init_per_suite(Config) ->
case catch crypto:start() of
@@ -252,7 +259,7 @@ idle_time(Config) ->
ssh_connection:close(ConnectionRef, Id),
receive
after 10000 ->
- {error,channel_closed} = ssh_connection:session_channel(ConnectionRef, 1000)
+ {error, closed} = ssh_connection:session_channel(ConnectionRef, 1000)
end,
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
@@ -300,6 +307,41 @@ shell(Config) when is_list(Config) ->
end.
%%--------------------------------------------------------------------
+cli() ->
+ [{doc, ""}].
+cli(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},
+ {password, "morot"},
+ {ssh_cli, {ssh_test_cli, [cli]}},
+ {subsystems, []},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ct:sleep(500),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ssh_connection:shell(ConnectionRef, ChannelId),
+ ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>),
+ receive
+ {ssh_cm, ConnectionRef,
+ {data,0,0, <<"\r\nYou are accessing a dummy, type \"q\" to exit\r\n\n">>}} ->
+ ok = ssh_connection:send(ConnectionRef, ChannelId, <<"q">>)
+ end,
+
+ receive
+ {ssh_cm, ConnectionRef,{closed, ChannelId}} ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
daemon_already_started() ->
[{doc, "Test that get correct error message if you try to start a daemon",
"on an adress that already runs a daemon see also seq10667"}].
@@ -445,10 +487,11 @@ internal_error(Config) when is_list(Config) ->
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{user_dir, UserDir},
{failfun, fun ssh_test_lib:failfun/2}]),
- {error,"Internal error"} =
+ {error, Error} =
ssh:connect(Host, Port, [{silently_accept_hosts, true},
{user_dir, UserDir},
{user_interaction, false}]),
+ check_error(Error),
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
@@ -473,9 +516,85 @@ send(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+peername_sockname() ->
+ [{doc, "Test ssh:connection_info([peername, sockname])"}].
+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),
+ [{peer, {_Name, {HostPeerClient,PortPeerClient} = ClientPeer}}] =
+ ssh:connection_info(ConnectionRef, [peer]),
+ [{sockname, {HostSockClient,PortSockClient} = ClientSock}] =
+ ssh:connection_info(ConnectionRef, [sockname]),
+ ct:pal("Client: ~p ~p", [ClientPeer, ClientSock]),
+ receive
+ {ssh_cm, ConnectionRef, {data, ChannelId, _, Response}} ->
+ {PeerNameSrv,SockNameSrv} = binary_to_term(Response),
+ {HostPeerSrv,PortPeerSrv} = PeerNameSrv,
+ {HostSockSrv,PortSockSrv} = SockNameSrv,
+ ct:pal("Server: ~p ~p", [PeerNameSrv, 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"}].
+ [{doc, "Client receives close when server closes"}].
close(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ {Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ Client =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false}]),
+ {ok, ChannelId} = ssh_connection:session_channel(Client, infinity),
+
+ ssh:stop_daemon(Server),
+ receive
+ {ssh_cm, Client,{closed, ChannelId}} ->
+ ok
+ after 5000 ->
+ ct:fail(timeout)
+ end.
+
+%%--------------------------------------------------------------------
+double_close() ->
+ [{doc, "Simulate that we try to close an already closed connection"}].
+double_close(Config) when is_list(Config) ->
SystemDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
@@ -494,6 +613,8 @@ close(Config) when is_list(Config) ->
exit(CM, {shutdown, normal}),
ok = ssh:close(CM).
+%%--------------------------------------------------------------------
+
openssh_zlib_basic_test() ->
[{doc, "Test basic connection with openssh_zlib"}].
openssh_zlib_basic_test(Config) ->
@@ -515,6 +636,15 @@ openssh_zlib_basic_test(Config) ->
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
+%% Due to timing the error message may or may not be delivered to
+%% the "tcp-application" before the socket closed message is recived
+check_error("Internal error") ->
+ ok;
+check_error("Connection closed") ->
+ ok;
+check_error(Error) ->
+ ct:fail(Error).
+
basic_test(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index 6c781e0e91..f4f0682b40 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -73,6 +73,9 @@ end_per_group(_, Config) ->
%%--------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
+ %% To make sure we start clean as it is not certain that
+ %% end_per_testcase will be run!
+ ssh:stop(),
ssh:start(),
Config.
@@ -91,7 +94,6 @@ simple_exec(Config) when is_list(Config) ->
{ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:exec(ConnectionRef, ChannelId0,
"echo testing", infinity),
-
%% receive response to input
receive
{ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}} ->
@@ -146,7 +148,6 @@ small_cat(Config) when is_list(Config) ->
{ssh_cm, ConnectionRef,{closed, ChannelId0}} ->
ok
end.
-
%%--------------------------------------------------------------------
big_cat() ->
[{doc,"Use 'cat' to echo large data block back to us."}].
@@ -204,37 +205,33 @@ send_after_exit(Config) when is_list(Config) ->
ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
{user_interaction, false}]),
{ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ Data = <<"I like spaghetti squash">>,
%% Shell command "false" will exit immediately
success = ssh_connection:exec(ConnectionRef, ChannelId0,
"false", infinity),
-
- timer:sleep(2000), %% Allow incoming eof/close/exit_status ssh messages to be processed
-
- Data = <<"I like spaghetti squash">>,
- case ssh_connection:send(ConnectionRef, ChannelId0, Data, 2000) of
- {error, closed} -> ok;
- ok ->
- ct:fail({expected,{error,closed}});
- {error, timeout} ->
- ct:fail({expected,{error,closed}});
- Else ->
- ct:fail(Else)
- end,
-
- %% receive close messages
receive
{ssh_cm, ConnectionRef, {eof, ChannelId0}} ->
ok
end,
receive
- {ssh_cm, ConnectionRef, {exit_status, ChannelId0, _}} ->
+ {ssh_cm, ConnectionRef, {exit_status, ChannelId0, _ExitStatus}} ->
ok
end,
receive
{ssh_cm, ConnectionRef,{closed, ChannelId0}} ->
ok
+ end,
+ case ssh_connection:send(ConnectionRef, ChannelId0, Data, 2000) of
+ {error, closed} -> ok;
+ ok ->
+ ct:fail({expected,{error,closed}, {got, ok}});
+ {error, timeout} ->
+ ct:fail({expected,{error,closed}, {got, {error, timeout}}});
+ Else ->
+ ct:fail(Else)
end.
+
%%--------------------------------------------------------------------
interrupted_send() ->
[{doc, "Use a subsystem that echos n char and then sends eof to cause a channel exit partway through a large send."}].
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..bc505695d3
--- /dev/null
+++ b/lib/ssh/test/ssh_peername_sockname_server.erl
@@ -0,0 +1,54 @@
+%%
+%% %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) ->
+ [{peer, {_Name, Peer}}] = ssh:connection_info(ConnectionManager, [peer]),
+ [{sockname, Sock}] = ssh:connection_info(ConnectionManager, [sockname]),
+ ssh_connection:send(ConnectionManager, ChannelId,
+ term_to_binary({Peer, Sock})),
+ {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.
diff --git a/lib/ssh/test/ssh_test_cli.erl b/lib/ssh/test/ssh_test_cli.erl
new file mode 100644
index 0000000000..cd9ad5f2ff
--- /dev/null
+++ b/lib/ssh/test/ssh_test_cli.erl
@@ -0,0 +1,81 @@
+-module(ssh_test_cli).
+
+-export([init/1, terminate/2, handle_ssh_msg/2, handle_msg/2]).
+
+-record(state, {
+ type,
+ id,
+ ref,
+ port
+ }).
+
+init([Type]) ->
+ {ok, #state{type = Type}}.
+
+handle_msg({ssh_channel_up, Id, Ref}, S) ->
+ User = get_ssh_user(Ref),
+ ok = ssh_connection:send(Ref,
+ Id,
+ << "\r\nYou are accessing a dummy, type \"q\" to exit\r\n\n" >>),
+ Port = run_portprog(User, S#state.type),
+ {ok, S#state{port = Port, id = Id, ref = Ref}};
+
+handle_msg({Port, {data, Data}}, S = #state{port = Port}) ->
+ ok = ssh_connection:send(S#state.ref, S#state.id, Data),
+ {ok, S};
+handle_msg({Port, {exit_status, Exit}}, S = #state{port = Port}) ->
+ if
+ S#state.type =:= cli ->
+ ok = ssh_connection:send(S#state.ref, S#state.id, << "\r\n" >>);
+ true ->
+ ok
+ end,
+ ok = ssh_connection:exit_status(S#state.ref, S#state.id, Exit),
+ {stop, S#state.id, S#state{port = undefined}};
+handle_msg({'EXIT', Port, _}, S = #state{port = Port}) ->
+ ok = ssh_connection:exit_status(S#state.ref, S#state.id, 0),
+ {stop, S#state.id, S#state{port = undefined}};
+handle_msg(_Msg, S) ->
+ {ok, S}.
+
+handle_ssh_msg({ssh_cm, Ref, {data, Id, _Type, <<"q">>}}, S) ->
+ ssh_connection:send_eof(Ref, Id),
+ {stop, Id, S};
+handle_ssh_msg({ssh_cm, _Ref, {data, _Id, _Type, Data}}, S) ->
+ true = port_command(S#state.port, Data),
+ {ok, S};
+handle_ssh_msg({ssh_cm, _, {eof, _}}, S) ->
+ {ok, S};
+handle_ssh_msg({ssh_cm, Ref, {env, Id, WantReply, _Var, _Value}}, S) ->
+ ok = ssh_connection:reply_request(Ref, WantReply, success, Id),
+ {ok, S};
+handle_ssh_msg({ssh_cm, Ref, {pty, Id, WantReply, _Terminal_jox}}, S) ->
+ ok = ssh_connection:reply_request(Ref, WantReply, success, Id),
+ {ok, S};
+handle_ssh_msg({ssh_cm, Ref, {shell, Id, WantReply}}, S) ->
+ ok = ssh_connection:reply_request(Ref, WantReply, success, Id),
+ {ok, S};
+handle_ssh_msg({ssh_cm, _, {signal, _, _}}, S) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ {ok, S};
+handle_ssh_msg({ssh_cm, _,
+ {window_change, _Id, _Width, _Height, _Pixw, _PixH}}, S) ->
+ {ok, S};
+handle_ssh_msg({ssh_cm, _, {exit_signal, Id, _, _, _}},
+ S) ->
+ {stop, Id, S}.
+
+terminate(_Why, _S) ->
+ nop.
+
+run_portprog(User, cli) ->
+ Pty_bin = os:find_executable("cat"),
+ open_port({spawn_executable, Pty_bin},
+ [stream, {cd, "/tmp"}, {env, [{"USER", User}]},
+ {args, []}, binary,
+ exit_status, use_stdio, stderr_to_stdout]).
+
+get_ssh_user(Ref) ->
+ [{user, User}] = ssh:connection_info(Ref, [user]),
+ User.
+