diff options
Diffstat (limited to 'lib/ssh')
-rw-r--r-- | lib/ssh/doc/src/notes.xml | 15 | ||||
-rw-r--r-- | lib/ssh/doc/src/ssh_connection.xml | 59 | ||||
-rw-r--r-- | lib/ssh/src/Makefile | 1 | ||||
-rw-r--r-- | lib/ssh/src/ssh.app.src | 1 | ||||
-rw-r--r-- | lib/ssh/src/ssh.appup.src | 30 | ||||
-rw-r--r-- | lib/ssh/src/ssh.erl | 1 | ||||
-rw-r--r-- | lib/ssh/src/ssh_acceptor.erl | 4 | ||||
-rw-r--r-- | lib/ssh/src/ssh_auth.erl | 24 | ||||
-rw-r--r-- | lib/ssh/src/ssh_channel.erl | 14 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connect.hrl | 4 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connection.erl | 72 | ||||
-rw-r--r-- | lib/ssh/src/ssh_connection_handler.erl | 101 | ||||
-rw-r--r-- | lib/ssh/src/ssh_info.erl | 193 | ||||
-rw-r--r-- | lib/ssh/src/ssh_message.erl | 5 | ||||
-rw-r--r-- | lib/ssh/test/property_test/ssh_eqc_client_server.erl | 63 | ||||
-rw-r--r-- | lib/ssh/test/property_test/ssh_eqc_encode_decode.erl | 2 | ||||
-rw-r--r-- | lib/ssh/test/ssh_connection_SUITE.erl | 66 | ||||
-rw-r--r-- | lib/ssh/test/ssh_to_openssh_SUITE.erl | 45 | ||||
-rw-r--r-- | lib/ssh/vsn.mk | 2 |
19 files changed, 599 insertions, 103 deletions
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 3da3ca3ec8..f3db05192e 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -29,6 +29,21 @@ <file>notes.xml</file> </header> +<section><title>Ssh 3.0.8</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixes of login blocking after port scanning.</p> + <p> + Own Id: OTP-12247 Aux Id: seq12726 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 3.0.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml index 72e7252536..ff72cf7ee0 100644 --- a/lib/ssh/doc/src/ssh_connection.xml +++ b/lib/ssh/doc/src/ssh_connection.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2008</year> - <year>2013</year> + <year>2014</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -137,7 +137,7 @@ <tag><c><![CDATA[{pty, ssh_channel_id(), boolean() = WantReply, {string() = Terminal, integer() = CharWidth, - integer() = RowHeight, integer() = PixelWidth, integer() = PixelHight, + integer() = RowHeight, integer() = PixelWidth, integer() = PixelHeight, [{atom() | integer() = Opcode, integer() = Value}] = TerminalModes}}]]></c></tag> <item>A pseudo-terminal has been requested for the @@ -148,11 +148,11 @@ drawable area of the window. The <c>Opcode</c> in the <c>TerminalModes</c> list is the mnemonic name, represented as an lowercase erlang atom, defined in - <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 8, - or the opcode if the mnemonic name is not listed in the + <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 8. + It may also be an opcode if the mnemonic name is not listed in the RFC. Example <c>OP code: 53, mnemonic name ECHO erlang atom: - echo</c>. There is currently no API function to generate this - event.</item> + echo</c>.This event is sent as result of calling <seealso + marker="ssh_connection#ptty_alloc/4">ssh_connection:ptty_alloc/4</seealso></item> <tag><c><![CDATA[{shell, boolean() = WantReply}]]></c></tag> <item> This message will request that the user's default shell @@ -273,7 +273,52 @@ </desc> </func> - <func> + <func> + <name>ptty_alloc(ConnectionRef, ChannelId, Options, Timeout) -> success | failure</name> + <fsummary>Send status replies to requests that want such replies. </fsummary> + <type> + <v> ConnectionRef = ssh_connection_ref() </v> + <v> ChannelId = ssh_channel_id()</v> + <v> Options = proplists:proplist()</v> + </type> + <desc> + <p> Sends a SSH Connection Protocol pty_req, to allocate a pseudo tty. + Should be called by a SSH client process. + Options are: + </p> + + <taglist> + <tag>{term, string()}</tag> + <item> + Defaults to os:getenv("TERM") or "vt100" if it is undefined. + </item> + <tag>{width, integer()}</tag> + <item> + Defaults to 80 if pixel_width is not defined. + </item> + <tag>{height, integer()}</tag> + <item> + Defaults to 24 if pixel_height is not defined. + </item> + <tag>{pixel_width, integer()}</tag> + <item> + Is disregarded if width is defined. + </item> + <tag>{pixel_height, integer()}</tag> + <item> + Is disregarded if height is defined. + </item> + <tag>{pty_opts, [{posix_atom(), integer()}]}</tag> + <item> + Option may be an empty list, otherwise + see possible POSIX names in section 8 in <url href="http://www.ietf.org/rfc/rfc4254.txt"> RFC 4254</url>. + </item> + </taglist> + + </desc> + </func> + + <func> <name>reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok</name> <fsummary>Send status replies to requests that want such replies. </fsummary> <type> diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 53c755d3cb..90d71107ad 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -65,6 +65,7 @@ MODULES= \ ssh_cli \ ssh_file \ ssh_io \ + ssh_info \ ssh_math \ ssh_message \ ssh_no_io \ diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index e0a51b3574..4ad55b34ca 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -23,6 +23,7 @@ sshd_sup, ssh_file, ssh_io, + ssh_info, ssh_math, ssh_no_io, ssh_server_key_api, diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 42603cc23c..600c01454c 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -19,7 +19,22 @@ {"%VSN%", [ + {"3.0.7", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_connection_handler, soft_purge, soft_purge, []}, + {load_module, ssh_info, soft_purge, soft_purge, []}, + {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]}, {"3.0.6", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_connection_handler, soft_purge, soft_purge, []}, + {load_module, ssh_info, soft_purge, soft_purge, []}, {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]}, {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]}, {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]}, @@ -27,7 +42,22 @@ {<<".*">>, [{restart_application, ssh}]} ], [ + {"3.0.7", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_connection_handler, soft_purge, soft_purge, []}, + {load_module, ssh_info, soft_purge, soft_purge, []}, + {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]}, {"3.0.6", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]}, + {load_module, ssh_connection_handler, soft_purge, soft_purge, []}, + {load_module, ssh_info, soft_purge, soft_purge, []}, {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]}, {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]}, {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]}, diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index de047d3c83..eae33e3683 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -194,6 +194,7 @@ shell(Host, Port, Options) -> {ok, ConnectionRef} -> case ssh_connection:session_channel(ConnectionRef, infinity) of {ok,ChannelId} -> + success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []), Args = [{channel_cb, ssh_shell}, {init_args,[ConnectionRef, ChannelId]}, {cm, ConnectionRef}, {channel_id, ChannelId}], diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl index 7302196674..6c443eeb9c 100644 --- a/lib/ssh/src/ssh_acceptor.erl +++ b/lib/ssh/src/ssh_acceptor.erl @@ -22,7 +22,8 @@ -module(ssh_acceptor). %% Internal application API --export([start_link/5]). +-export([start_link/5, + number_of_connections/1]). %% spawn export -export([acceptor_init/6, acceptor_loop/6]). @@ -140,5 +141,6 @@ handle_error(Reason) -> number_of_connections(SystemSup) -> length([X || {R,X,supervisor,[ssh_subsystem_sup]} <- supervisor:which_children(SystemSup), + is_pid(X), is_reference(R) ]). diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index b4d406ba8d..45c4d52d7e 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -184,9 +184,8 @@ handle_userauth_request(#ssh_msg_service_request{name = handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", method = "password", - data = Data}, _, + data = <<?FALSE, ?UINT32(Sz), BinPwd:Sz/binary>>}, _, #ssh{opts = Opts} = Ssh) -> - <<_:8, ?UINT32(Sz), BinPwd:Sz/binary>> = Data, Password = unicode:characters_to_list(BinPwd), case check_password(User, Password, Opts) of true -> @@ -201,6 +200,27 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", + method = "password", + data = <<?TRUE, + _/binary + %% ?UINT32(Sz1), OldBinPwd:Sz1/binary, + %% ?UINT32(Sz2), NewBinPwd:Sz2/binary + >> + }, _, + Ssh) -> + %% Password change without us having sent SSH_MSG_USERAUTH_PASSWD_CHANGEREQ (because we never do) + %% RFC 4252 says: + %% SSH_MSG_USERAUTH_FAILURE without partial success - The password + %% has not been changed. Either password changing was not supported, + %% or the old password was bad. + + {not_authorized, {User, {error,"Password change not supported"}}, + ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ + authentications = "", + partial_success = false}, Ssh)}; + +handle_userauth_request(#ssh_msg_userauth_request{user = User, + service = "ssh-connection", method = "none"}, _, #ssh{userauth_supported_methods = Methods} = Ssh) -> {not_authorized, {User, undefined}, diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl index 508ae637cf..5c24f362b1 100644 --- a/lib/ssh/src/ssh_channel.erl +++ b/lib/ssh/src/ssh_channel.erl @@ -67,7 +67,8 @@ %% Internal application API -export([cache_create/0, cache_lookup/2, cache_update/2, cache_delete/1, cache_delete/2, cache_foldl/3, - cache_find/2]). + cache_find/2, + get_print_info/1]). -record(state, { cm, @@ -190,6 +191,14 @@ init([Options]) -> %% {stop, Reason, State} %% Description: Handling call messages %%-------------------------------------------------------------------- +handle_call(get_print_info, _From, State) -> + Reply = + {{State#state.cm, + State#state.channel_id}, + io_lib:format('CB=~p',[State#state.channel_cb]) + }, + {reply, Reply, State}; + handle_call(Request, From, #state{channel_cb = Module, channel_state = ChannelState} = State) -> try Module:handle_call(Request, From, ChannelState) of @@ -333,6 +342,9 @@ cache_find(ChannelPid, Cache) -> Channel end. +get_print_info(Pid) -> + call(Pid, get_print_info, 1000). + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl index 9307dbbad0..d14f7ce27d 100644 --- a/lib/ssh/src/ssh_connect.hrl +++ b/lib/ssh/src/ssh_connect.hrl @@ -165,6 +165,10 @@ recipient_channel }). +-define(TERMINAL_WIDTH, 80). +-define(TERMINAL_HEIGHT, 24). +-define(DEFAULT_TERMINAL, "vt100"). + -define(TTY_OP_END,0). %% Indicates end of options. -define(VINTR,1). %% Interrupt character; 255 if none. Similarly for the %% other characters. Not all of these characters are diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index 87f37b93ef..593443e11c 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% Copyright Ericsson AB 2008-2014. 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 @@ -32,11 +32,11 @@ %% API -export([session_channel/2, session_channel/4, exec/4, shell/2, subsystem/4, send/3, send/4, send/5, - send_eof/2, adjust_window/3, setenv/5, close/2, reply_request/4]). + send_eof/2, adjust_window/3, setenv/5, close/2, reply_request/4, + ptty_alloc/3, ptty_alloc/4]). %% Potential API currently unsupported and not tested --export([open_pty/3, open_pty/7, - open_pty/9, window_change/4, window_change/6, +-export([window_change/4, window_change/6, direct_tcpip/6, direct_tcpip/8, tcpip_forward/3, cancel_tcpip_forward/3, signal/3, exit_status/3]). @@ -189,6 +189,25 @@ reply_request(_,false, _, _) -> ok. %%-------------------------------------------------------------------- +-spec ptty_alloc(pid(), channel_id(), proplists:proplist()) -> success | failiure. +%% +%% +%% Description: Sends a ssh connection protocol pty_req. +%%-------------------------------------------------------------------- +ptty_alloc(ConnectionHandler, Channel, Options) -> + ptty_alloc(ConnectionHandler, Channel, Options, infinity). +ptty_alloc(ConnectionHandler, Channel, Options, TimeOut) -> + {Width, PixWidth} = pty_default_dimensions(width, Options), + {Hight, PixHight} = pty_default_dimensions(hight, Options), + pty_req(ConnectionHandler, Channel, + proplists:get_value(term, Options, default_term()), + proplists:get_value(width, Options, Width), + proplists:get_value(hight, Options, Hight), + proplists:get_value(pixel_widh, Options, PixWidth), + proplists:get_value(pixel_hight, Options, PixHight), + proplists:get_value(pty_opts, Options, []), TimeOut + ). +%%-------------------------------------------------------------------- %% Not yet officialy supported! The following functions are part of the %% initial contributed ssh application. They are untested. Do we want them? %% Should they be documented and tested? @@ -211,23 +230,6 @@ exit_status(ConnectionHandler, Channel, Status) -> ssh_connection_handler:request(ConnectionHandler, Channel, "exit-status", false, [?uint32(Status)], 0). -open_pty(ConnectionHandler, Channel, TimeOut) -> - open_pty(ConnectionHandler, Channel, - os:getenv("TERM"), 80, 24, [], TimeOut). - -open_pty(ConnectionHandler, Channel, Term, Width, Height, PtyOpts, TimeOut) -> - open_pty(ConnectionHandler, Channel, Term, Width, - Height, 0, 0, PtyOpts, TimeOut). - -open_pty(ConnectionHandler, Channel, Term, Width, Height, - PixWidth, PixHeight, PtyOpts, TimeOut) -> - ssh_connection_handler:request(ConnectionHandler, - Channel, "pty-req", true, - [?string(Term), - ?uint32(Width), ?uint32(Height), - ?uint32(PixWidth),?uint32(PixHeight), - encode_pty_opts(PtyOpts)], TimeOut). - direct_tcpip(ConnectionHandler, RemoteHost, RemotePort, OrigIP, OrigPort, Timeout) -> direct_tcpip(ConnectionHandler, RemoteHost, RemotePort, OrigIP, OrigPort, @@ -1080,6 +1082,27 @@ flow_control([_|_], #channel{flow_control = From, flow_control(_,_,_) -> []. +pty_req(ConnectionHandler, Channel, Term, Width, Height, + PixWidth, PixHeight, PtyOpts, TimeOut) -> + ssh_connection_handler:request(ConnectionHandler, + Channel, "pty-req", true, + [?string(Term), + ?uint32(Width), ?uint32(Height), + ?uint32(PixWidth),?uint32(PixHeight), + encode_pty_opts(PtyOpts)], TimeOut). + +pty_default_dimensions(Dimension, Options) -> + case proplists:get_value(Dimension, Options, 0) of + N when is_integer(N), N > 0 -> + {N, 0}; + _ -> + case proplists:get_value(list_to_atom("pixel_" ++ atom_to_list(Dimension)), Options, 0) of + N when is_integer(N), N > 0 -> + {0, N}; + _ -> + {?TERMINAL_WIDTH, 0} + end + end. encode_pty_opts(Opts) -> Bin = list_to_binary(encode_pty_opts2(Opts)), @@ -1277,3 +1300,10 @@ decode_ip(Addr) when is_binary(Addr) -> {ok,A} -> A end. +default_term() -> + case os:getenv("TERM") of + false -> + ?DEFAULT_TERMINAL; + Str when is_list(Str)-> + Str + end. diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 4fbc5d0ae2..8b7c4a5f80 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -41,11 +41,13 @@ global_request/4, send/5, send_eof/2, info/1, info/2, connection_info/2, channel_info/3, adjust_window/3, close/2, stop/1, renegotiate/1, renegotiate_data/1, - start_connection/4]). + start_connection/4, + get_print_info/1]). %% gen_fsm callbacks -export([hello/2, kexinit/2, key_exchange/2, new_keys/2, - userauth/2, connected/2]). + userauth/2, connected/2, + error/2]). -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, format_status/2, code_change/4]). @@ -171,9 +173,23 @@ init([Role, Socket, SshOpts]) -> State#state{ssh_params = Ssh}) catch _:Error -> - gen_fsm:enter_loop(?MODULE, [], error, {Error, State0}) + gen_fsm:enter_loop(?MODULE, [], error, {Error, State}) end. +%% Temporary fix for the Nessus error. SYN-> <-SYNACK ACK-> RST-> ? +error(_Event, {Error,State=#state{}}) -> + case Error of + {badmatch,{error,enotconn}} -> + %% {error,enotconn} probably from inet:peername in + %% init_ssh(server,..)/5 called from init/1 + {stop, {shutdown,"TCP connenction to server was prematurely closed by the client"}, State}; + _ -> + {stop, {shutdown,{init,Error}}, State} + end; +error(Event, State) -> + %% State deliberately not checked beeing #state. This is a panic-clause... + {stop, {shutdown,{init,{spurious_error,Event}}}, State}. + %%-------------------------------------------------------------------- -spec open_channel(pid(), string(), iodata(), integer(), integer(), timeout()) -> {open, channel_id()} | {error, term()}. @@ -240,6 +256,9 @@ send_eof(ConnectionHandler, ChannelId) -> %%-------------------------------------------------------------------- -spec connection_info(pid(), [atom()]) -> proplists:proplist(). %%-------------------------------------------------------------------- +get_print_info(ConnectionHandler) -> + sync_send_all_state_event(ConnectionHandler, get_print_info, 1000). + connection_info(ConnectionHandler, Options) -> sync_send_all_state_event(ConnectionHandler, {connection_info, Options}). @@ -550,7 +569,7 @@ connected({#ssh_msg_kexinit{}, _Payload} = Event, State) -> %%-------------------------------------------------------------------- handle_event(#ssh_msg_disconnect{description = Desc} = DisconnectMsg, _StateName, #state{} = State) -> - handle_disconnect(DisconnectMsg, State), + handle_disconnect(peer, DisconnectMsg, State), {stop, {shutdown, Desc}, State}; handle_event(#ssh_msg_ignore{}, StateName, State) -> @@ -605,7 +624,7 @@ handle_event(renegotiate, connected, #state{ssh_params = Ssh0} renegotiate = true})}; handle_event(renegotiate, StateName, State) -> - timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiatie]), + timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiate]), %% Allready in keyexcahange so ignore {next_state, StateName, State}; @@ -758,6 +777,20 @@ handle_sync_event({recv_window, ChannelId}, _From, StateName, end, {reply, Reply, StateName, next_packet(State)}; +handle_sync_event(get_print_info, _From, StateName, State) -> + Reply = + try + {inet:sockname(State#state.socket), + inet:peername(State#state.socket) + } + of + {{ok,Local}, {ok,Remote}} -> {{Local,Remote},io_lib:format("statename=~p",[StateName])}; + _ -> {{"-",0},"-"} + catch + _:_ -> {{"?",0},"?"} + end, + {reply, Reply, StateName, State}; + handle_sync_event({connection_info, Options}, _From, StateName, State) -> Info = ssh_info(Options, State, []), {reply, Info, StateName, State}; @@ -936,6 +969,10 @@ terminate(normal, _, #state{transport_cb = Transport, (catch Transport:close(Socket)), ok; +terminate({shutdown,{init,Reason}}, StateName, State) -> + error_logger:info_report(io_lib:format("Erlang ssh in connection handler init: ~p~n",[Reason])), + terminate(normal, StateName, State); + %% Terminated by supervisor terminate(shutdown, StateName, #state{ssh_params = Ssh0} = State) -> DisconnectMsg = @@ -951,8 +988,10 @@ terminate({shutdown, #ssh_msg_disconnect{} = Msg}, StateName, {SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0), send_msg(SshPacket, State), terminate(normal, StateName, State#state{ssh_params = Ssh}); + terminate({shutdown, _}, StateName, State) -> terminate(normal, StateName, State); + terminate(Reason, StateName, #state{ssh_params = Ssh0, starter = _Pid, connection_state = Connection} = State) -> terminate_subsytem(Connection), @@ -965,6 +1004,7 @@ terminate(Reason, StateName, #state{ssh_params = Ssh0, starter = _Pid, send_msg(SshPacket, State), terminate(normal, StateName, State#state{ssh_params = Ssh}). + terminate_subsytem(#connection{system_supervisor = SysSup, sub_system_supervisor = SubSysSup}) when is_pid(SubSysSup) -> ssh_system_sup:stop_subsystem(SysSup, SubSysSup); @@ -1161,7 +1201,10 @@ send_all_state_event(FsmPid, Event) -> gen_fsm:send_all_state_event(FsmPid, Event). sync_send_all_state_event(FsmPid, Event) -> - try gen_fsm:sync_send_all_state_event(FsmPid, Event, infinity) + sync_send_all_state_event(FsmPid, Event, infinity). + +sync_send_all_state_event(FsmPid, Event, Timeout) -> + try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout) catch exit:{noproc, _} -> {error, closed}; @@ -1258,13 +1301,23 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName, generate_event(Msg, StateName, State0, EncData) -> Event = ssh_message:decode(Msg), State = generate_event_new_state(State0, EncData), - case Event of - #ssh_msg_kexinit{} -> - %% We need payload for verification later. - event({Event, Msg}, StateName, State); - _ -> - event(Event, StateName, State) - end. + try + case Event of + #ssh_msg_kexinit{} -> + %% We need payload for verification later. + event({Event, Msg}, StateName, State); + _ -> + event(Event, StateName, State) + end + catch + _:_ -> + DisconnectMsg = + #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR, + description = "Encountered unexpected input", + language = "en"}, + handle_disconnect(DisconnectMsg, State) + end. + handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From, @@ -1442,17 +1495,27 @@ handle_ssh_packet(Length, StateName, #state{decoded_data_buffer = DecData0, handle_disconnect(DisconnectMsg, State0) end. -handle_disconnect(#ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0, - role = Role} = State0) -> +handle_disconnect(DisconnectMsg, State) -> + handle_disconnect(own, DisconnectMsg, State). + +handle_disconnect(#ssh_msg_disconnect{} = DisconnectMsg, State, Error) -> + handle_disconnect(own, DisconnectMsg, State, Error); +handle_disconnect(Type, #ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0, role = Role} = State0) -> {disconnect, _, {{replies, Replies}, Connection}} = ssh_connection:handle_msg(Msg, Connection0, Role), - State = send_replies(Replies, State0), + State = send_replies(disconnect_replies(Type, Msg, Replies), State0), {stop, {shutdown, Desc}, State#state{connection_state = Connection}}. -handle_disconnect(#ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0, - role = Role} = State0, ErrorMsg) -> + +handle_disconnect(Type, #ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0, + role = Role} = State0, ErrorMsg) -> {disconnect, _, {{replies, Replies}, Connection}} = ssh_connection:handle_msg(Msg, Connection0, Role), - State = send_replies(Replies, State0), + State = send_replies(disconnect_replies(Type, Msg, Replies), State0), {stop, {shutdown, {Desc, ErrorMsg}}, State#state{connection_state = Connection}}. +disconnect_replies(own, Msg, Replies) -> + [{connection_reply, Msg} | Replies]; +disconnect_replies(peer, _, Replies) -> + Replies. + counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) -> Ssh#ssh{c_vsn = NumVsn , c_version = StrVsn}; counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) -> diff --git a/lib/ssh/src/ssh_info.erl b/lib/ssh/src/ssh_info.erl new file mode 100644 index 0000000000..9ed598b3ab --- /dev/null +++ b/lib/ssh/src/ssh_info.erl @@ -0,0 +1,193 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2014. 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% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Print some info of a running ssh aplication. +%%---------------------------------------------------------------------- + +-module(ssh_info). + +-compile(export_all). + +print() -> + try supervisor:which_children(ssh_sup) + of + _ -> + io:nl(), + print_general(), + io:nl(), + underline("Client part", $=), + print_clients(), + io:nl(), + underline("Server part", $=), + print_servers(), + io:nl(), + %% case os:type() of + %% {unix,_} -> + %% io:nl(), + %% underline("Linux part", $=), + %% underline("Listening"), + %% catch io:format(os:cmd("netstat -tpln")), + %% io:nl(), + %% underline("Other"), + %% catch io:format(os:cmd("netstat -tpn")); + %% _ -> ok + %% end, + underline("Supervisors", $=), + walk_sups(ssh_sup), + io:nl() + catch + _:_ -> + io:format("Ssh not found~n",[]) + end. + +%%%================================================================ +print_general() -> + {_Name, Slogan, Ver} = lists:keyfind(ssh,1,application:which_applications()), + underline(io_lib:format("~s ~s", [Slogan, Ver]), $=), + io:format('This printout is generated ~s. ~n',[datetime()]). + +%%%================================================================ +print_clients() -> + try + lists:foreach(fun print_client/1, supervisor:which_children(sshc_sup)) + catch + C:E -> + io:format('***FAILED: ~p:~p~n',[C,E]) + end. + +print_client({undefined,Pid,supervisor,[ssh_connection_handler]}) -> + {{Local,Remote},_Str} = ssh_connection_handler:get_print_info(Pid), + io:format(" Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]); +print_client(Other) -> + io:format(" [[Other 1: ~p]]~n",[Other]). + + +%%%================================================================ +print_servers() -> + try + lists:foreach(fun print_server/1, supervisor:which_children(sshd_sup)) + catch + C:E -> + io:format('***FAILED: ~p:~p~n',[C,E]) + end. + +print_server({{server,ssh_system_sup,LocalHost,LocalPort},Pid,supervisor,[ssh_system_sup]}) when is_pid(Pid) -> + io:format('Local=~s (~p children)~n',[fmt_host_port({LocalHost,LocalPort}), + ssh_acceptor:number_of_connections(Pid)]), + lists:foreach(fun print_system_sup/1, supervisor:which_children(Pid)); +print_server(Other) -> + io:format(" [[Other 2: ~p]]~n",[Other]). + +print_system_sup({Ref,Pid,supervisor,[ssh_subsystem_sup]}) when is_reference(Ref), + is_pid(Pid) -> + lists:foreach(fun print_channels/1, supervisor:which_children(Pid)); +print_system_sup({{ssh_acceptor_sup,LocalHost,LocalPort}, Pid,supervisor, [ssh_acceptor_sup]}) when is_pid(Pid) -> + io:format(" [Acceptor for ~s]~n",[fmt_host_port({LocalHost,LocalPort})]); +print_system_sup(Other) -> + io:format(" [[Other 3: ~p]]~n",[Other]). + +print_channels({{server,ssh_channel_sup,_,_},Pid,supervisor,[ssh_channel_sup]}) when is_pid(Pid) -> + lists:foreach(fun print_channel/1, supervisor:which_children(Pid)); +print_channels(Other) -> + io:format(" [[Other 4: ~p]]~n",[Other]). + + +print_channel({Ref,Pid,worker,[ssh_channel]}) when is_reference(Ref), + is_pid(Pid) -> + {{ConnManager,ChannelID}, Str} = ssh_channel:get_print_info(Pid), + {{Local,Remote},StrM} = ssh_connection_handler:get_print_info(ConnManager), + io:format(' ch ~p: ~s ~s',[ChannelID, StrM, Str]), + io:format(" Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]); +print_channel(Other) -> + io:format(" [[Other 5: ~p]]~n",[Other]). + +%%%================================================================ +-define(inc(N), (N+4)). + +walk_sups(StartPid) -> + io:format("Start at ~p, ~s.~n",[StartPid,dead_or_alive(StartPid)]), + walk_sups(children(StartPid), _Indent=?inc(0)). + +walk_sups([H={_,Pid,SupOrWorker,_}|T], Indent) -> + indent(Indent), io:format('~200p ~p is ~s~n',[H,Pid,dead_or_alive(Pid)]), + case SupOrWorker of + supervisor -> walk_sups(children(Pid), ?inc(Indent)); + _ -> ok + end, + walk_sups(T, Indent); +walk_sups([], _) -> + ok. + +dead_or_alive(Name) when is_atom(Name) -> + case whereis(Name) of + undefined -> + "**UNDEFINED**"; + Pid -> + dead_or_alive(Pid) + end; +dead_or_alive(Pid) when is_pid(Pid) -> + case process_info(Pid) of + undefined -> "**DEAD**"; + _ -> "alive" + end. + +indent(I) -> io:format('~*c',[I,$ ]). + +children(Pid) -> + Parent = self(), + Helper = spawn(fun() -> + Parent ! {self(),supervisor:which_children(Pid)} + end), + receive + {Helper,L} when is_list(L) -> + L + after + 2000 -> + catch exit(Helper, kill), + [] + end. + +%%%================================================================ +underline(Str) -> + underline(Str, $-). + +underline(Str, LineChar) -> + Len = lists:flatlength(Str), + io:format('~s~n',[Str]), + line(Len,LineChar). + +line(Len, Char) -> + io:format('~*c~n', [Len,Char]). + + +datetime() -> + {{YYYY,MM,DD}, {H,M,S}} = calendar:now_to_universal_time(now()), + lists:flatten(io_lib:format('~4w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w UTC',[YYYY,MM,DD, H,M,S])). + + +fmt_host_port({{A,B,C,D},Port}) -> io_lib:format('~p.~p.~p.~p:~p',[A,B,C,D,Port]); +fmt_host_port({Host,Port}) -> io_lib:format('~s:~p',[Host,Port]). + + + +nyi() -> + io:format('Not yet implemented~n',[]), + nyi. diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl index 891ccec24c..66e7717095 100644 --- a/lib/ssh/src/ssh_message.erl +++ b/lib/ssh/src/ssh_message.erl @@ -505,6 +505,11 @@ erl_boolean(1) -> decode_kex_init(<<?BYTE(Bool), ?UINT32(X)>>, Acc, 0) -> list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc])); +decode_kex_init(<<?BYTE(Bool)>>, Acc, 0) -> + %% The mandatory trailing UINT32 is missing. Assume the value it anyhow must have + %% See rfc 4253 7.1 + X = 0, + list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc])); decode_kex_init(<<?UINT32(Len), Data:Len/binary, Rest/binary>>, Acc, N) -> Names = string:tokens(unicode:characters_to_list(Data), ","), decode_kex_init(Rest, [Names | Acc], N -1). diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server.erl b/lib/ssh/test/property_test/ssh_eqc_client_server.erl index cf895ae85e..123b48412b 100644 --- a/lib/ssh/test/property_test/ssh_eqc_client_server.erl +++ b/lib/ssh/test/property_test/ssh_eqc_client_server.erl @@ -32,6 +32,10 @@ -else. +%% Limit the testing time on CI server... this needs to be improved in % from total budget. +-define(TESTINGTIME(Prop), eqc:testing_time(30,Prop)). + + -include_lib("eqc/include/eqc.hrl"). -include_lib("eqc/include/eqc_statem.hrl"). -eqc_group_commands(true). @@ -75,7 +79,9 @@ -define(SUBSYSTEMS, ["echo1", "echo2", "echo3", "echo4"]). --define(SERVER_ADDRESS, { {127,1,1,1}, inet_port({127,1,1,1}) }). +-define(SERVER_ADDRESS, { {127,1,0,choose(1,254)}, % IP + choose(1024,65535) % Port + }). -define(SERVER_EXTRA_OPTIONS, [{parallel_login,bool()}] ). @@ -97,7 +103,7 @@ %% To be called as eqc:quickcheck( ssh_eqc_client_server:prop_seq() ). prop_seq() -> - do_prop_seq(?SSH_DIR). + ?TESTINGTIME(do_prop_seq(?SSH_DIR)). %% To be called from a common_test test suite prop_seq(CT_Config) -> @@ -105,9 +111,10 @@ prop_seq(CT_Config) -> do_prop_seq(DataDir) -> - ?FORALL(Cmds,commands(?MODULE, #state{data_dir=DataDir}), + setup_rsa(DataDir), + ?FORALL(Cmds,commands(?MODULE), begin - {H,Sf,Result} = run_commands(?MODULE,Cmds), + {H,Sf,Result} = run_commands(?MODULE,Cmds,[{data_dir,DataDir}]), present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok) end). @@ -116,33 +123,35 @@ full_path(SSHdir, CT_Config) -> SSHdir). %%%---- prop_parallel() -> - do_prop_parallel(?SSH_DIR). + ?TESTINGTIME(do_prop_parallel(?SSH_DIR)). %% To be called from a common_test test suite prop_parallel(CT_Config) -> do_prop_parallel(full_path(?SSH_DIR, CT_Config)). do_prop_parallel(DataDir) -> - ?FORALL(Cmds,parallel_commands(?MODULE, #state{data_dir=DataDir}), + setup_rsa(DataDir), + ?FORALL(Cmds,parallel_commands(?MODULE), begin - {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds), + {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds,[{data_dir,DataDir}]), present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok) end). %%%---- prop_parallel_multi() -> - do_prop_parallel_multi(?SSH_DIR). + ?TESTINGTIME(do_prop_parallel_multi(?SSH_DIR)). %% To be called from a common_test test suite prop_parallel_multi(CT_Config) -> do_prop_parallel_multi(full_path(?SSH_DIR, CT_Config)). do_prop_parallel_multi(DataDir) -> + setup_rsa(DataDir), ?FORALL(Repetitions,?SHRINK(1,[10]), - ?FORALL(Cmds,parallel_commands(?MODULE, #state{data_dir=DataDir}), + ?FORALL(Cmds,parallel_commands(?MODULE), ?ALWAYS(Repetitions, begin - {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds), + {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds,[{data_dir,DataDir}]), present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok) end))). @@ -151,14 +160,12 @@ do_prop_parallel_multi(DataDir) -> %%% called when using commands/1 initial_state() -> - S = initial_state(#state{}), - S#state{initialized=true}. + #state{}. %%% called when using commands/2 -initial_state(S) -> +initial_state(DataDir) -> application:stop(ssh), - ssh:start(), - setup_rsa(S#state.data_dir). + ssh:start(). %%%---------------- weight(S, ssh_send) -> 5*length([C || C<-S#state.channels, has_subsyst(C)]); @@ -172,7 +179,7 @@ weight(_S, _) -> 1. initial_state_pre(S) -> not S#state.initialized. -initial_state_args(S) -> [S]. +initial_state_args(_) -> [{var,data_dir}]. initial_state_next(S, _, _) -> S#state{initialized=true}. @@ -180,10 +187,17 @@ initial_state_next(S, _, _) -> S#state{initialized=true}. %%% Start a new daemon %%% Precondition: not more than ?MAX_NUM_SERVERS started +%%% This is a bit funny because we need to pick an IP address and Port to +%%% run the server on, but there is no way to atomically select a free Port! +%%% +%%% Therefore we just grab one IP-Port pair randomly and try to start the ssh server +%%% on that pair. If it fails, we just forget about it and goes on. Yes, it +%%% is a waste of cpu cycles, but at least it works! + ssh_server_pre(S) -> S#state.initialized andalso length(S#state.servers) < ?MAX_NUM_SERVERS. -ssh_server_args(S) -> [?SERVER_ADDRESS, S#state.data_dir, ?SERVER_EXTRA_OPTIONS]. +ssh_server_args(_) -> [?SERVER_ADDRESS, {var,data_dir}, ?SERVER_EXTRA_OPTIONS]. ssh_server({IP,Port}, DataDir, ExtraOptions) -> ok(ssh:daemon(IP, Port, @@ -194,8 +208,10 @@ ssh_server({IP,Port}, DataDir, ExtraOptions) -> | ExtraOptions ])). +ssh_server_post(_S, _Args, {error,eaddrinuse}) -> true; ssh_server_post(_S, _Args, Result) -> is_ok(Result). +ssh_server_next(S, {error,eaddrinuse}, _) -> S; ssh_server_next(S, Result, [{IP,Port},_,_]) -> S#state{servers=[#srvr{ref = Result, address = IP, @@ -241,15 +257,16 @@ do(Pid, Fun, Timeout) when is_function(Fun,0) -> ssh_open_connection_pre(S) -> S#state.servers /= []. -ssh_open_connection_args(S) -> [oneof(S#state.servers), S#state.data_dir]. +ssh_open_connection_args(S) -> [oneof(S#state.servers), {var,data_dir}]. ssh_open_connection(#srvr{address=Ip, port=Port}, DataDir) -> ok(ssh:connect(ensure_string(Ip), Port, [ {silently_accept_hosts, true}, {user_dir, user_dir(DataDir)}, - {user_interaction, false} - ])). + {user_interaction, false}, + {connect_timeout, 2000} + ], 2000)). ssh_open_connection_post(_S, _Args, Result) -> is_ok(Result). @@ -569,12 +586,6 @@ median(_) -> %%%================================================================ %%% The rest is taken and modified from ssh_test_lib.erl -inet_port(IpAddress)-> - {ok, Socket} = gen_tcp:listen(0, [{ip,IpAddress},{reuseaddr,true}]), - {ok, Port} = inet:port(Socket), - gen_tcp:close(Socket), - Port. - setup_rsa(Dir) -> erase_dir(system_dir(Dir)), erase_dir(user_dir(Dir)), diff --git a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl index 34630bdc91..57ea2012c1 100644 --- a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl +++ b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl @@ -25,8 +25,6 @@ -proptest(eqc). -proptest([triq,proper]). --include_lib("ct_property_test.hrl"). - -ifndef(EQC). -ifndef(PROPER). -ifndef(TRIQ). diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index 3c537d719c..553d0f5720 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -36,7 +36,7 @@ all() -> [ - {group, openssh_payload}, + {group, openssh}, start_subsystem_on_closed_channel, interrupted_send, start_shell, @@ -49,11 +49,19 @@ all() -> stop_listener ]. groups() -> - [{openssh_payload, [], [simple_exec, - small_cat, - big_cat, - send_after_exit - ]}]. + [{openssh, [], payload() ++ ptty()}]. + +payload() -> + [simple_exec, + small_cat, + big_cat, + send_after_exit]. + +ptty() -> + [ptty_alloc_default, + ptty_alloc, + ptty_alloc_pixel]. + %%-------------------------------------------------------------------- init_per_suite(Config) -> case catch crypto:start() of @@ -67,7 +75,7 @@ end_per_suite(_Config) -> crypto:stop(). %%-------------------------------------------------------------------- -init_per_group(openssh_payload, _Config) -> +init_per_group(openssh, _Config) -> case gen_tcp:connect("localhost", 22, []) of {error,econnrefused} -> {skip,"No openssh deamon"}; @@ -242,6 +250,42 @@ send_after_exit(Config) when is_list(Config) -> end. %%-------------------------------------------------------------------- +ptty_alloc_default() -> + [{doc, "Test sending PTTY alloc message with only defaults."}]. + +ptty_alloc_default(Config) when is_list(Config) -> + ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []), + ssh:close(ConnectionRef). + +%%-------------------------------------------------------------------- +ptty_alloc() -> + [{doc, "Test sending PTTY alloc message with width,height options."}]. + +ptty_alloc(Config) when is_list(Config) -> + ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, + [{term, default_term()}, {width, 70}, {high, 20}]), + ssh:close(ConnectionRef). + + +%%-------------------------------------------------------------------- +ptty_alloc_pixel() -> + [{doc, "Test sending PTTY alloc message pixel options."}]. + +ptty_alloc_pixel(Config) when is_list(Config) -> + ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, + [{term, default_term()}, {pixel_widh, 630}, {pixel_hight, 470}]), + ssh:close(ConnectionRef). + +%%-------------------------------------------------------------------- start_subsystem_on_closed_channel(Config) -> PrivDir = ?config(priv_dir, Config), UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth @@ -603,3 +647,11 @@ ssh_exec(Cmd) -> spawn(fun() -> io:format(Cmd ++ "\n") end). + +default_term() -> + case os:getenv("TERM") of + false -> + "vt100"; + Str when is_list(Str)-> + Str + end. diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index 41fbd324c4..af70eeb46c 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -120,13 +120,8 @@ erlang_shell_client_openssh_server(Config) when is_list(Config) -> receive_hej(), IO ! {input, self(), "exit\n"}, receive_logout(), - receive - {'EXIT', Shell, normal} -> - ok; - Other1 -> - ct:fail({unexpected_msg, Other1}) - end. - + receive_normal_exit(Shell). + %-------------------------------------------------------------------- erlang_client_openssh_server_exec() -> [{doc, "Test api function ssh_connection:exec"}]. @@ -529,11 +524,22 @@ erlang_client_openssh_server_nonexistent_subsystem(Config) when is_list(Config) %%-------------------------------------------------------------------- receive_hej() -> receive - <<"Hej\n">> = Hej-> + <<"Hej", _binary>> = Hej -> + ct:pal("Expected result: ~p~n", [Hej]); + <<"Hej\n", _binary>> = Hej -> + ct:pal("Expected result: ~p~n", [Hej]); + <<"Hej\r\n", _/binary>> = Hej -> ct:pal("Expected result: ~p~n", [Hej]); Info -> - ct:pal("Extra info: ~p~n", [Info]), - receive_hej() + Lines = binary:split(Info, [<<"\r\n">>], [global]), + case lists:member(<<"Hej">>, Lines) of + true -> + ct:pal("Expected result found in lines: ~p~n", [Lines]), + ok; + false -> + ct:pal("Extra info: ~p~n", [Info]), + receive_hej() + end end. receive_logout() -> @@ -543,13 +549,20 @@ receive_logout() -> <<"Connection closed">> -> ok end; - <<"TERM environment variable not set.\n">> -> %% Windows work around - receive_logout(); - Other0 -> - ct:fail({unexpected_msg, Other0}) - end. - + Info -> + ct:pal("Extra info when logging out: ~p~n", [Info]), + receive_logout() + end. +receive_normal_exit(Shell) -> + receive + {'EXIT', Shell, normal} -> + ok; + <<"\r\n">> -> + receive_normal_exit(Shell); + Other -> + ct:fail({unexpected_msg, Other}) + end. %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 866b192101..68544c1d0e 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 3.0.7 +SSH_VSN = 3.0.8 APP_VSN = "ssh-$(SSH_VSN)" |