aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh')
-rw-r--r--lib/ssh/doc/src/notes.xml33
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl46
-rw-r--r--lib/ssh/test/Makefile3
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl92
-rw-r--r--lib/ssh/test/ssh_eqc_event_handler.erl43
-rw-r--r--lib/ssh/test/ssh_property_test_SUITE.erl7
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl31
-rw-r--r--lib/ssh/test/ssh_test_lib.erl25
-rw-r--r--lib/ssh/vsn.mk2
9 files changed, 246 insertions, 36 deletions
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index a4897668e4..773a472818 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,39 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If a client illegaly sends an info-line and then
+ immediatly closes the TCP-connection, a badmatch
+ exception was raised.</p>
+ <p>
+ Own Id: OTP-13966</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 4.3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Intermittent ssh ERROR REPORT mentioning
+ nonblocking_sender</p>
+ <p>
+ Own Id: OTP-13953 Aux Id: seq13199 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.3.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index facf6b561a..ced049f0d0 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -339,7 +339,6 @@ renegotiate_data(ConnectionHandler) ->
ssh_params :: #ssh{}
| undefined,
socket :: inet:socket(),
- sender :: pid() | undefined,
decrypted_data_buffer = <<>> :: binary(),
encrypted_data_buffer = <<>> :: binary(),
undecrypted_packet_length :: undefined | non_neg_integer(),
@@ -368,10 +367,9 @@ init_connection_handler(Role, Socket, Opts) ->
{Protocol, Callback, CloseTag} =
proplists:get_value(transport, Opts, ?DefaultTransport),
S0#data{ssh_params = init_ssh_record(Role, Socket, Opts),
- sender = spawn_link(fun() -> nonblocking_sender(Socket, Callback) end),
- transport_protocol = Protocol,
- transport_cb = Callback,
- transport_close_tag = CloseTag
+ transport_protocol = Protocol,
+ transport_cb = Callback,
+ transport_close_tag = CloseTag
}
of
S ->
@@ -547,6 +545,7 @@ handle_event(_, {info_line,_Line}, {hello,Role}, D) ->
case Role of
client ->
%% The server may send info lines to the client before the version_exchange
+ %% RFC4253/4.2
inet:setopts(D#data.socket, [{active, once}]),
keep_state_and_data;
server ->
@@ -1447,15 +1446,18 @@ start_the_connection_child(UserPid, Role, Socket, Options) ->
%% Stopping
-type finalize_termination_result() :: ok .
-finalize_termination(_StateName, D) ->
- case D#data.connection_state of
+finalize_termination(_StateName, #data{transport_cb = Transport,
+ connection_state = Connection,
+ socket = Socket}) ->
+ case Connection of
#connection{system_supervisor = SysSup,
sub_system_supervisor = SubSysSup} when is_pid(SubSysSup) ->
ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
_ ->
do_nothing
end,
- close_transport(D).
+ (catch Transport:close(Socket)),
+ ok.
%%--------------------------------------------------------------------
%% "Invert" the Role
@@ -1510,34 +1512,10 @@ send_msg(Msg, State=#data{ssh_params=Ssh0}) when is_tuple(Msg) ->
send_bytes(Bytes, State),
State#data{ssh_params=Ssh}.
-send_bytes(Bytes, #data{sender = Sender}) ->
- Sender ! {send,Bytes},
- ok.
-
-close_transport(D) ->
- D#data.sender ! close,
+send_bytes(Bytes, #data{socket = Socket, transport_cb = Transport}) ->
+ _ = Transport:send(Socket, Bytes),
ok.
-
-nonblocking_sender(Socket, Callback) ->
- receive
- {send, Bytes} ->
- case Callback:send(Socket, Bytes) of
- ok ->
- nonblocking_sender(Socket, Callback);
- E = {error,_} ->
- exit({shutdown,E})
- end;
-
- close ->
- case Callback:close(Socket) of
- ok ->
- ok;
- E = {error,_} ->
- exit({shutdown,E})
- end
- end.
-
handle_version({2, 0} = NumVsn, StrVsn, Ssh0) ->
Ssh = counterpart_versions(NumVsn, StrVsn, Ssh0),
{ok, Ssh};
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 6ce6d6f537..3fca78237c 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -52,7 +52,8 @@ MODULES= \
ssh_echo_server \
ssh_peername_sockname_server \
ssh_test_cli \
- ssh_relay
+ ssh_relay \
+ ssh_eqc_event_handler
HRL_FILES_NEEDED_IN_TEST= \
$(ERL_TOP)/lib/ssh/test/ssh_test_lib.hrl \
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl
new file mode 100644
index 0000000000..c07140dc43
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_client_info_timing.erl
@@ -0,0 +1,92 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ssh_eqc_client_info_timing).
+
+-compile(export_all).
+
+-proptest(eqc).
+-proptest([triq,proper]).
+
+-ifndef(EQC).
+-ifndef(PROPER).
+-ifndef(TRIQ).
+-define(EQC,true).
+%%-define(PROPER,true).
+%%-define(TRIQ,true).
+-endif.
+-endif.
+-endif.
+
+-ifdef(EQC).
+-include_lib("eqc/include/eqc.hrl").
+-define(MOD_eqc,eqc).
+
+-else.
+-ifdef(PROPER).
+-include_lib("proper/include/proper.hrl").
+-define(MOD_eqc,proper).
+
+-else.
+-ifdef(TRIQ).
+-define(MOD_eqc,triq).
+-include_lib("triq/include/triq.hrl").
+
+-endif.
+-endif.
+-endif.
+
+
+%%% Properties:
+
+prop_seq(_Config) ->
+ {ok,Pid} = ssh_eqc_event_handler:add_report_handler(),
+ {_, _, Port} = init_daemon(),
+ numtests(1000,
+ ?FORALL(Delay, choose(0,100),%% Micro seconds
+ try
+ send_bad_sequence(Port, Delay, Pid),
+ not any_relevant_error_report(Pid)
+ catch
+ C:E -> io:format('~p:~p~n',[C,E]),
+ false
+ end
+ )).
+
+send_bad_sequence(Port, Delay, Pid) ->
+ {ok,S} = gen_tcp:connect("localhost",Port,[]),
+ gen_tcp:send(S,"Illegal info-string\r\n"),
+ ssh_test_lib:sleep_microsec(Delay),
+ gen_tcp:close(S).
+
+any_relevant_error_report(Pid) ->
+ {ok, Reports} = ssh_eqc_event_handler:get_reports(Pid),
+ lists:any(fun({error_report,_,{_,supervisor_report,L}}) when is_list(L) ->
+ lists:member({reason,{badmatch,{error,closed}}}, L);
+ (_) ->
+ false
+ end, Reports).
+
+%%%================================================================
+init_daemon() ->
+ ok = begin ssh:stop(), ssh:start() end,
+ ssh_test_lib:daemon([]).
+
diff --git a/lib/ssh/test/ssh_eqc_event_handler.erl b/lib/ssh/test/ssh_eqc_event_handler.erl
new file mode 100644
index 0000000000..233965012a
--- /dev/null
+++ b/lib/ssh/test/ssh_eqc_event_handler.erl
@@ -0,0 +1,43 @@
+-module(ssh_eqc_event_handler).
+
+-compile(export_all).
+
+-behaviour(gen_event).
+
+add_report_handler() ->
+ error_logger:add_report_handler(?MODULE, [self(),Ref=make_ref()]),
+ receive
+ {event_handler_started,HandlerPid,Ref} ->
+ {ok,HandlerPid}
+ end.
+
+get_reports(Pid) ->
+ Pid ! {get_reports,self(),Ref=make_ref()},
+ receive
+ {reports,Reports,Ref} ->
+ {ok,Reports}
+ end.
+
+%%%================================================================
+
+-record(state, {
+ reports = []
+ }).
+
+%% error_logger:add_report_handler(ssh_eqc_event_handler, [self()]).
+
+init([CallerPid,Ref]) ->
+ CallerPid ! {event_handler_started,self(),Ref},
+ {ok, #state{}}.
+
+handle_event(Event, State) ->
+ {ok, State#state{reports = [Event|State#state.reports]}}.
+
+handle_info({get_reports,From,Ref}, State) ->
+ From ! {reports, lists:reverse(State#state.reports), Ref},
+ {ok, State#state{reports=[]}}.
+
+handle_call(_Request, State) -> {ok,reply,State}.
+terminate(_Arg, _State) -> stop.
+
+code_change(_OldVsn, State, _Extra) -> {ok, State}.
diff --git a/lib/ssh/test/ssh_property_test_SUITE.erl b/lib/ssh/test/ssh_property_test_SUITE.erl
index c8aabcedb7..7ba2732a88 100644
--- a/lib/ssh/test/ssh_property_test_SUITE.erl
+++ b/lib/ssh/test/ssh_property_test_SUITE.erl
@@ -38,6 +38,7 @@
-include_lib("common_test/include/ct.hrl").
all() -> [{group, messages},
+ client_sends_info_timing,
{group, client_server}
].
@@ -106,3 +107,9 @@ client_server_parallel_multi(Config) ->
ssh_eqc_client_server:prop_parallel_multi(Config),
Config
).
+
+client_sends_info_timing(Config) ->
+ ct_property_test:quickcheck(
+ ssh_eqc_client_info_timing:prop_seq(Config),
+ Config
+ ).
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index 4fac1f718a..93d0bc2eb0 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -48,6 +48,7 @@ suite() ->
all() ->
[{group,tool_tests},
+ client_info_line,
{group,kex},
{group,service_requests},
{group,authentication},
@@ -575,6 +576,36 @@ client_handles_keyboard_interactive_0_pwds(Config) ->
).
+
+%%%--------------------------------------------------------------------
+client_info_line(_Config) ->
+ %% A client must not send an info-line. If it does, the server should handle
+ %% handle this gracefully
+ {ok,Pid} = ssh_eqc_event_handler:add_report_handler(),
+ {_, _, Port} = ssh_test_lib:daemon([]),
+
+ %% Fake client:
+ {ok,S} = gen_tcp:connect("localhost",Port,[]),
+ gen_tcp:send(S,"An illegal info-string\r\n"),
+ gen_tcp:close(S),
+
+ %% wait for server to react:
+ timer:sleep(1000),
+
+ %% check if a badmatch was received:
+ {ok, Reports} = ssh_eqc_event_handler:get_reports(Pid),
+ case lists:any(fun({error_report,_,{_,supervisor_report,L}}) when is_list(L) ->
+ lists:member({reason,{badmatch,{error,closed}}}, L);
+ (_) ->
+ false
+ end, Reports) of
+ true ->
+ ct:fail("Bad error report on info_line from client");
+ false ->
+ ok
+ end.
+
+
%%%================================================================
%%%==== Internal functions ========================================
%%%================================================================
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 6233680dce..c43c6519f9 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -767,3 +767,28 @@ open_port(Arg1, ExtraOpts) ->
use_stdio,
overlapped_io, hide %only affects windows
| ExtraOpts]).
+
+%%%----------------------------------------------------------------
+%%% Sleeping
+
+%%% Milli sec
+sleep_millisec(Nms) -> receive after Nms -> ok end.
+
+%%% Micro sec
+sleep_microsec(Nus) ->
+ busy_wait(Nus, erlang:system_time(microsecond)).
+
+busy_wait(Nus, T0) ->
+ T = erlang:system_time(microsecond) - T0,
+ Tleft = Nus - T,
+ if
+ Tleft > 2000 ->
+ sleep_millisec((Tleft-1500) div 1000), % μs -> ms
+ busy_wait(Nus,T0);
+ Tleft > 1 ->
+ busy_wait(Nus, T0);
+ true ->
+ T
+ end.
+
+%%%----------------------------------------------------------------
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 09e707ad07..7eeeaf796e 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.3.3
+SSH_VSN = 4.3.5
APP_VSN = "ssh-$(SSH_VSN)"