aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh')
-rw-r--r--lib/ssh/doc/src/notes.xml30
-rw-r--r--lib/ssh/src/ssh.erl60
-rw-r--r--lib/ssh/src/ssh_sftpd.erl7
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl22
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl18
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl4
-rw-r--r--lib/ssh/vsn.mk2
7 files changed, 115 insertions, 28 deletions
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 370cf4c3a0..0bc4baf5eb 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,21 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.7.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed port leakage if a ssh:daemon call failed.</p>
+ <p>
+ Own Id: OTP-15397 Aux Id: ERL-801 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.7.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -293,6 +308,21 @@
</section>
</section>
+<section><title>Ssh 4.6.9.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed port leakage if a ssh:daemon call failed.</p>
+ <p>
+ Own Id: OTP-15397 Aux Id: ERL-801 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.6.9.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 086fa6e5f8..9281bf84a7 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -270,25 +270,38 @@ daemon(Host0, Port0, UserOptions0) when 0 =< Port0, Port0 =< 65535,
try
{Host1, UserOptions} = handle_daemon_args(Host0, UserOptions0),
#{} = Options0 = ssh_options:handle_options(server, UserOptions),
-
- {{Host,Port}, ListenSocket} =
- open_listen_socket(Host1, Port0, Options0),
-
- %% Now Host,Port is what to use for the supervisor to register its name,
- %% and ListenSocket is for listening on connections. But it is still owned
- %% by self()...
-
- finalize_start(Host, Port, ?GET_OPT(profile, Options0),
- ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options0),
- fun(Opts, Result) ->
- {_, Callback, _} = ?GET_OPT(transport, Opts),
- receive
- {request_control, ListenSocket, ReqPid} ->
- ok = Callback:controlling_process(ListenSocket, ReqPid),
- ReqPid ! {its_yours,ListenSocket},
- Result
- end
- end)
+ {open_listen_socket(Host1, Port0, Options0), Options0}
+ of
+ {{{Host,Port}, ListenSocket}, Options1} ->
+ try
+ %% Now Host,Port is what to use for the supervisor to register its name,
+ %% and ListenSocket is for listening on connections. But it is still owned
+ %% by self()...
+ finalize_start(Host, Port, ?GET_OPT(profile, Options1),
+ ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options1),
+ fun(Opts, Result) ->
+ {_, Callback, _} = ?GET_OPT(transport, Opts),
+ receive
+ {request_control, ListenSocket, ReqPid} ->
+ ok = Callback:controlling_process(ListenSocket, ReqPid),
+ ReqPid ! {its_yours,ListenSocket},
+ Result
+ end
+ end)
+ of
+ {error,Err} ->
+ close_listen_socket(ListenSocket, Options1),
+ {error,Err};
+ OK ->
+ OK
+ catch
+ error:Error ->
+ close_listen_socket(ListenSocket, Options1),
+ error(Error);
+ exit:Exit ->
+ close_listen_socket(ListenSocket, Options1),
+ exit(Exit)
+ end
catch
throw:bad_fd ->
{error,bad_fd};
@@ -524,6 +537,15 @@ open_listen_socket(_Host0, Port0, Options0) ->
{{LHost,LPort}, LSock}.
%%%----------------------------------------------------------------
+close_listen_socket(ListenSocket, Options) ->
+ try
+ {_, Callback, _} = ?GET_OPT(transport, Options),
+ Callback:close(ListenSocket)
+ catch
+ _C:_E -> ok
+ end.
+
+%%%----------------------------------------------------------------
finalize_start(Host, Port, Profile, Options0, F) ->
try
%% throws error:Error if no usable hostkey is found
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index 278f6a9780..aa9ba0f9bb 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -508,11 +508,8 @@ close_our_file({_,Fd}, FileMod, FS0) ->
FS1.
%%% stat: do the stat
-stat(Vsn, ReqId, Data, State, F) when Vsn =< 3->
- <<?UINT32(BLen), BPath:BLen/binary>> = Data,
- stat(ReqId, unicode:characters_to_list(BPath), State, F);
-stat(Vsn, ReqId, Data, State, F) when Vsn >= 4->
- <<?UINT32(BLen), BPath:BLen/binary, ?UINT32(_Flags)>> = Data,
+stat(Vsn, ReqId, Data, State, F) ->
+ <<?UINT32(BLen), BPath:BLen/binary, _/binary>> = Data,
stat(ReqId, unicode:characters_to_list(BPath), State, F).
fstat(Vsn, ReqId, Data, State) when Vsn =< 3->
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index da94b5722f..5de6d52092 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -43,7 +43,9 @@ suite() ->
{timetrap,{seconds,40}}].
all() ->
- [{group, all_tests}].
+ [{group, all_tests},
+ daemon_already_started
+ ].
groups() ->
[{all_tests, [parallel], [{group, ssh_renegotiate_SUITE},
@@ -801,6 +803,24 @@ daemon_already_started(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+%%% Test that a failed daemon start does not leave the port open
+daemon_error_closes_port(Config) ->
+ GoodSystemDir = proplists:get_value(data_dir, Config),
+ Port = ssh_test_lib:inet_port(),
+ {error,_} = ssh_test_lib:daemon(Port, []), % No system dir
+ case ssh_test_lib:daemon(Port, [{system_dir, GoodSystemDir}]) of
+ {error,eaddrinuse} ->
+ {fail, "Port leakage"};
+ {error,Error} ->
+ ct:log("Strange error: ~p",[Error]),
+ {fail, "Strange error"};
+ {Pid, _Host, Port} ->
+ %% Ok
+ ssh:stop_daemon(Pid)
+ end.
+
+
+%%--------------------------------------------------------------------
%%% check that known_hosts is updated correctly
known_hosts(Config) when is_list(Config) ->
SystemDir = proplists:get_value(data_dir, Config),
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
index f4eef2dc77..8e82527c6e 100644
--- a/lib/ssh/test/ssh_compat_SUITE.erl
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -1126,7 +1126,24 @@ prepare_local_directory(ServerRootDir) ->
"chmod 222 unreadable_file",
"exit"].
+
check_local_directory(ServerRootDir) ->
+ TimesToTry = 3, % sleep 0.5, 1, 2 and then 4 secs (7.5s in total)
+ check_local_directory(ServerRootDir, 500, TimesToTry-1).
+
+check_local_directory(ServerRootDir, SleepTime, N) ->
+ case do_check_local_directory(ServerRootDir) of
+ {error,Error} when N>0 ->
+ %% Could be that the erlang side is faster and the docker's operations
+ %% are not yet finalized.
+ %% Sleep for a while and retry a few times:
+ timer:sleep(SleepTime),
+ check_local_directory(ServerRootDir, 2*SleepTime, N-1);
+ Other ->
+ Other
+ end.
+
+do_check_local_directory(ServerRootDir) ->
case lists:sort(ok(file:list_dir(ServerRootDir)) -- [".",".."]) of
["ex_tst1","mydir","tst2"] ->
{ok,Expect} = file:read_file(filename:join(ServerRootDir,"ex_tst1")),
@@ -1161,6 +1178,7 @@ check_local_directory(ServerRootDir) ->
{error,{bad_dir_contents,"/"}}
end.
+
call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir) ->
{DockerIP,DockerPort} = ip_port(Config),
{ok,C} = ssh:connect(DockerIP, DockerPort,
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index 778e4a5fc8..6aa587dc7f 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -1124,12 +1124,12 @@ start_our_shell(_User, _Peer) ->
ssh_exec_echo(Cmd) ->
spawn(fun() ->
- io:format("echo "++Cmd ++ "\n")
+ io:format("echo ~s\n", [Cmd])
end).
ssh_exec_echo(Cmd, User) ->
spawn(fun() ->
- io:format(io_lib:format("echo ~s ~s\n",[User,Cmd]))
+ io:format("echo ~s ~s\n",[User,Cmd])
end).
ssh_exec_echo(Cmd, User, _PeerAddr) ->
ssh_exec_echo(Cmd,User).
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 3ba1e177be..2890d7fe5b 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,4 +1,4 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.7.2
+SSH_VSN = 4.7.3
APP_VSN = "ssh-$(SSH_VSN)"