aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/src')
-rw-r--r--lib/ssh/src/ssh.app.src4
-rw-r--r--lib/ssh/src/ssh.appup.src28
-rw-r--r--lib/ssh/src/ssh.erl64
-rw-r--r--lib/ssh/src/ssh.hrl1
-rw-r--r--lib/ssh/src/ssh_acceptor.erl44
-rw-r--r--lib/ssh/src/ssh_auth.erl11
-rw-r--r--lib/ssh/src/ssh_bits.erl4
-rw-r--r--lib/ssh/src/ssh_cli.erl12
-rw-r--r--lib/ssh/src/ssh_connection.erl33
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl33
-rw-r--r--lib/ssh/src/ssh_io.erl2
-rw-r--r--lib/ssh/src/ssh_message.erl31
-rw-r--r--lib/ssh/src/ssh_sftp.erl7
-rw-r--r--lib/ssh/src/ssh_sftpd.erl20
-rw-r--r--lib/ssh/src/ssh_transport.erl80
-rw-r--r--lib/ssh/src/ssh_xfer.erl57
16 files changed, 299 insertions, 132 deletions
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 74d7293be0..e0a51b3574 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -38,6 +38,8 @@
{registered, []},
{applications, [kernel, stdlib, crypto, public_key]},
{env, []},
- {mod, {ssh_app, []}}]}.
+ {mod, {ssh_app, []}},
+ {runtime_dependencies, ["stdlib-2.0","public_key-0.22","kernel-3.0",
+ "erts-6.0","crypto-3.3"]}]}.
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 32f7cc470b..8269f89e40 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -1,7 +1,7 @@
-%%
+%% -*- erlang -*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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
@@ -19,13 +19,25 @@
{"%VSN%",
[
- {<<"2.1\\.*">>, [{restart_application, ssh}]},
- {<<"2.0\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ {"3.0.2", [{load_module, ssh_message, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_io, soft_purge, soft_purge, []}]},
+ {"3.0.1", [{load_module, ssh, soft_purge, soft_purge, []},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, []},
+ {load_module, ssh_message, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_io, soft_purge, soft_purge, []}]},
+ {<<".*">>, [{restart_application, ssh}]}
],
[
- {<<"2.1\\.*">>,[{restart_application, ssh}]},
- {<<"2.0\\.*">>, [{restart_application, ssh}]},
- {<<"1\\.*">>, [{restart_application, ssh}]}
+ {"3.0.2", [{load_module, ssh_message, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_io, soft_purge, soft_purge, []}]},
+ {"3.0.1", [{load_module, ssh, soft_purge, soft_purge, []},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, []},
+ {load_module, ssh_message, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh_io, soft_purge, soft_purge, []}]},
+ {<<".*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 2685b1553b..8a8d4bb89e 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -1,7 +1,7 @@
-%%
+%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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,8 +32,8 @@
shell/1, shell/2, shell/3]).
%%--------------------------------------------------------------------
--spec start() -> ok.
--spec start(permanent | transient | temporary) -> ok.
+-spec start() -> ok | {error, term()}.
+-spec start(permanent | transient | temporary) -> ok | {error, term()}.
%%
%% Description: Starts the ssh application. Default type
%% is temporary. see application(3)
@@ -51,7 +51,7 @@ start(Type) ->
application:start(ssh, Type).
%%--------------------------------------------------------------------
--spec stop() -> ok.
+-spec stop() -> ok | {error, term()}.
%%
%% Description: Stops the ssh application.
%%--------------------------------------------------------------------
@@ -59,8 +59,8 @@ stop() ->
application:stop(ssh).
%%--------------------------------------------------------------------
--spec connect(string(), integer(), proplists:proplists()) -> {ok, pid()} | {error, term()}.
--spec connect(string(), integer(), proplists:proplists(), timeout()) -> {ok, pid()} | {error, term()}.
+-spec connect(string(), integer(), proplists:proplist()) -> {ok, pid()} | {error, term()}.
+-spec connect(string(), integer(), proplists:proplist(), timeout()) -> {ok, pid()} | {error, term()}.
%%
%% Description: Starts an ssh connection.
%%--------------------------------------------------------------------
@@ -73,8 +73,8 @@ connect(Host, Port, Options, Timeout) ->
{SocketOptions, SshOptions} ->
{_, Transport, _} = TransportOpts =
proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
- Inet = proplists:get_value(inet, SshOptions, inet),
- try Transport:connect(Host, Port, [ {active, false}, Inet | SocketOptions], Timeout) of
+ ConnectionTimeout = proplists:get_value(connect_timeout, Options, infinity),
+ try Transport:connect(Host, Port, [ {active, false} | SocketOptions], ConnectionTimeout) of
{ok, Socket} ->
Opts = [{user_pid, self()}, {host, Host} | fix_idle_time(SshOptions)],
ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
@@ -255,8 +255,8 @@ do_start_daemon(Host, Port, Options, SocketOptions) ->
handle_options(Opts) ->
try handle_option(proplists:unfold(Opts), [], []) of
- {_,_} = Options ->
- Options
+ {Inet, Ssh} ->
+ {handle_ip(Inet), Ssh}
catch
throw:Error ->
Error
@@ -332,6 +332,14 @@ handle_option([{idle_time, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{rekey_limit, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{max_sessions, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{negotiation_timeout, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{parallel_login, _} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([parallel_login|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option({parallel_login,true}) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
@@ -360,6 +368,12 @@ handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), leng
end;
handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity ->
Opt;
+handle_ssh_option({max_sessions, Value} = Opt) when is_integer(Value), Value>0 ->
+ Opt;
+handle_ssh_option({negotiation_timeout, Value} = Opt) when is_integer(Value); Value == infinity ->
+ Opt;
+handle_ssh_option({parallel_login, Value} = Opt) when Value==true ; Value==false ->
+ Opt;
handle_ssh_option({user, Value} = Opt) when is_list(Value) ->
Opt;
handle_ssh_option({dsa_pass_phrase, Value} = Opt) when is_list(Value) ->
@@ -378,7 +392,8 @@ handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
Opt;
handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
is_atom(Function) ->
-
+ Opt;
+handle_ssh_option({exec, Function} = Opt) when is_function(Function) ->
Opt;
handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) ->
Opt;
@@ -418,13 +433,14 @@ handle_ssh_option(Opt) ->
throw({error, {eoptions, Opt}}).
handle_inet_option({active, _} = Opt) ->
- throw({error, {{eoptions, Opt}, "Ssh has built in flow control, "
- "and activ is handled internaly user is not allowd"
+ throw({error, {{eoptions, Opt}, "SSH has built in flow control, "
+ "and active is handled internally, user is not allowed"
"to specify this option"}});
-handle_inet_option({inet, Value} = Opt) when (Value == inet) or (Value == inet6) ->
- Opt;
+
+handle_inet_option({inet, Value}) when (Value == inet) or (Value == inet6) ->
+ Value;
handle_inet_option({reuseaddr, _} = Opt) ->
- throw({error, {{eoptions, Opt},"Is set internaly user is not allowd"
+ throw({error, {{eoptions, Opt},"Is set internally, user is not allowed"
"to specify this option"}});
%% Option verified by inet
handle_inet_option(Opt) ->
@@ -445,3 +461,17 @@ handle_pref_algs([H|T], Acc) ->
_ ->
false
end.
+
+handle_ip(Inet) -> %% Default to ipv4
+ case lists:member(inet, Inet) of
+ true ->
+ Inet;
+ false ->
+ case lists:member(inet6, Inet) of
+ true ->
+ Inet;
+ false ->
+ [inet | Inet]
+ end
+ end.
+
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 94ced9da6f..0c4d34f89c 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -54,6 +54,7 @@
-define(uint32(X), << ?UINT32(X) >> ).
-define(uint64(X), << ?UINT64(X) >> ).
-define(string(X), << ?STRING(list_to_binary(X)) >> ).
+-define(string_utf8(X), << ?STRING(unicode:characters_to_binary(X)) >> ).
-define(binary(X), << ?STRING(X) >>).
-define(SSH_CIPHER_NONE, 0).
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index 91905b2eaf..7302196674 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -80,15 +80,36 @@ acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout) ->
ListenSocket, AcceptTimeout)
end.
-handle_connection(_Callback, Address, Port, Options, Socket) ->
+handle_connection(Callback, Address, Port, Options, Socket) ->
SystemSup = ssh_system_sup:system_supervisor(Address, Port),
- {ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, Options),
- ConnectionSup = ssh_subsystem_sup:connection_supervisor(SubSysSup),
- ssh_connection_handler:start_connection(server, Socket,
- [{supervisors, [{system_sup, SystemSup},
- {subsystem_sup, SubSysSup},
- {connection_sup, ConnectionSup}]}
- | Options], infinity).
+ SSHopts = proplists:get_value(ssh_opts, Options, []),
+ MaxSessions = proplists:get_value(max_sessions,SSHopts,infinity),
+ case number_of_connections(SystemSup) < MaxSessions of
+ true ->
+ {ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, Options),
+ ConnectionSup = ssh_subsystem_sup:connection_supervisor(SubSysSup),
+ Timeout = proplists:get_value(negotiation_timeout, SSHopts, 2*60*1000),
+ ssh_connection_handler:start_connection(server, Socket,
+ [{supervisors, [{system_sup, SystemSup},
+ {subsystem_sup, SubSysSup},
+ {connection_sup, ConnectionSup}]}
+ | Options], Timeout);
+ false ->
+ Callback:close(Socket),
+ IPstr = if is_tuple(Address) -> inet:ntoa(Address);
+ true -> Address
+ end,
+ Str = try io_lib:format('~s:~p',[IPstr,Port])
+ catch _:_ -> "port "++integer_to_list(Port)
+ end,
+ error_logger:info_report("Ssh login attempt to "++Str++" denied due to option "
+ "max_sessions limits to "++ io_lib:write(MaxSessions) ++
+ " sessions."
+ ),
+ {error,max_sessions}
+ end.
+
+
handle_error(timeout) ->
ok;
@@ -114,3 +135,10 @@ handle_error(Reason) ->
String = lists:flatten(io_lib:format("Accept error: ~p", [Reason])),
error_logger:error_report(String),
exit({accept_failed, String}).
+
+
+number_of_connections(SystemSup) ->
+ length([X ||
+ {R,X,supervisor,[ssh_subsystem_sup]} <- supervisor:which_children(SystemSup),
+ is_reference(R)
+ ]).
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 1fa3df847f..45fd907383 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -83,7 +83,7 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb,
method = "password",
data =
<<?BOOLEAN(?FALSE),
- ?STRING(list_to_binary(Password))>>},
+ ?STRING(unicode:characters_to_binary(Password))>>},
Ssh)
end.
@@ -190,14 +190,13 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
data = Data}, _,
#ssh{opts = Opts} = Ssh) ->
<<_:8, ?UINT32(Sz), BinPwd:Sz/binary>> = Data,
- Password = binary_to_list(BinPwd),
-
+ Password = unicode:characters_to_list(BinPwd),
case check_password(User, Password, Opts) of
true ->
{authorized, User,
ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)};
false ->
- {not_authorized, {User, {passwd, Password}},
+ {not_authorized, {User, {error,"Bad user or password"}},
ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
authentications = "",
partial_success = false}, Ssh)}
@@ -229,7 +228,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
ssh_transport:ssh_packet(
#ssh_msg_userauth_success{}, Ssh)};
false ->
- {not_authorized, {User, {error, "Invalid signature"}},
+ {not_authorized, {User, undefined},
ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
authentications="publickey,password",
partial_success = false}, Ssh)}
@@ -352,7 +351,7 @@ verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) ->
build_sig_data(SessionId, User, Service, KeyBlob, Alg) ->
Sig = [?binary(SessionId),
?SSH_MSG_USERAUTH_REQUEST,
- ?string(User),
+ ?string_utf8(User),
?string(Service),
?binary(<<"publickey">>),
?TRUE,
diff --git a/lib/ssh/src/ssh_bits.erl b/lib/ssh/src/ssh_bits.erl
index 2b0241cb83..8aaff93b9f 100644
--- a/lib/ssh/src/ssh_bits.erl
+++ b/lib/ssh/src/ssh_bits.erl
@@ -116,6 +116,10 @@ enc(Xs, [string|Ts], Offset) ->
X0 = hd(Xs),
Y = ?string(X0),
[Y | enc(tl(Xs),Ts,Offset+size(Y))];
+enc(Xs, [string_utf8|Ts], Offset) ->
+ X0 = hd(Xs),
+ Y = ?string_utf8(X0),
+ [Y | enc(tl(Xs),Ts,Offset+size(Y))];
enc(Xs, [binary|Ts], Offset) ->
X0 = hd(Xs),
Y = ?binary(X0),
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 77453e8fd7..18841e3d2d 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -457,17 +457,17 @@ bin_to_list(I) when is_integer(I) ->
start_shell(ConnectionHandler, State) ->
Shell = State#state.shell,
- ConnectionInfo = ssh_connection_handler:info(ConnectionHandler,
+ ConnectionInfo = ssh_connection_handler:connection_info(ConnectionHandler,
[peer, user]),
ShellFun = case is_function(Shell) of
true ->
- {ok, User} =
+ User =
proplists:get_value(user, ConnectionInfo),
case erlang:fun_info(Shell, arity) of
{arity, 1} ->
fun() -> Shell(User) end;
{arity, 2} ->
- [{_, PeerAddr}] =
+ {_, PeerAddr} =
proplists:get_value(peer, ConnectionInfo),
fun() -> Shell(User, PeerAddr) end;
_ ->
@@ -485,9 +485,9 @@ start_shell(_ConnectionHandler, Cmd, #state{exec={M, F, A}} = State) ->
State#state{group = Group, buf = empty_buf()};
start_shell(ConnectionHandler, Cmd, #state{exec=Shell} = State) when is_function(Shell) ->
- ConnectionInfo = ssh_connection_handler:info(ConnectionHandler,
+ ConnectionInfo = ssh_connection_handler:connection_info(ConnectionHandler,
[peer, user]),
- {ok, User} =
+ User =
proplists:get_value(user, ConnectionInfo),
ShellFun =
case erlang:fun_info(Shell, arity) of
@@ -496,7 +496,7 @@ start_shell(ConnectionHandler, Cmd, #state{exec=Shell} = State) when is_function
{arity, 2} ->
fun() -> Shell(Cmd, User) end;
{arity, 3} ->
- [{_, PeerAddr}] =
+ {_, PeerAddr} =
proplists:get_value(peer, ConnectionInfo),
fun() -> Shell(Cmd, User, PeerAddr) end;
_ ->
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 03dddae3c8..33849f4527 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -271,10 +271,36 @@ cancel_tcpip_forward(ConnectionHandler, BindIP, Port) ->
%%--------------------------------------------------------------------
%%% Internal API
%%--------------------------------------------------------------------
+l2b(L) when is_integer(hd(L)) ->
+ try list_to_binary(L)
+ of
+ B -> B
+ catch
+ _:_ ->
+ unicode:characters_to_binary(L)
+ end;
+l2b([H|T]) ->
+ << (l2b(H))/binary, (l2b(T))/binary >>;
+l2b(B) when is_binary(B) ->
+ B;
+l2b([]) ->
+ <<>>.
+
+
+
channel_data(ChannelId, DataType, Data, Connection, From)
when is_list(Data)->
channel_data(ChannelId, DataType,
- list_to_binary(Data), Connection, From);
+%% list_to_binary(Data), Connection, From);
+ l2b(Data), Connection, From);
+ %% try list_to_binary(Data)
+ %% of
+ %% B -> B
+ %% catch
+ %% _:_ -> io:format('BAD BINARY: ~p~n',[Data]),
+ %% unicode:characters_to_binary(Data)
+ %% end,
+ %% Connection, From);
channel_data(ChannelId, DataType, Data,
#connection{channel_cache = Cache} = Connection,
@@ -756,9 +782,8 @@ handle_cli_msg(#connection{channel_cache = Cache} = Connection,
erlang:monitor(process, Pid),
Channel = Channel0#channel{user = Pid},
ssh_channel:cache_update(Cache, Channel),
- Reply = {connection_reply,
- channel_success_msg(RemoteId)},
- {{replies, [{channel_data, Pid, Reply0}, Reply]}, Connection};
+ {Reply, Connection1} = reply_msg(Channel, Connection, Reply0),
+ {{replies, [Reply]}, Connection1};
_Other ->
Reply = {connection_reply,
channel_failure_msg(RemoteId)},
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 3462b98172..86804c4436 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -104,14 +104,12 @@ start_connection(client = Role, Socket, Options, Timeout) ->
start_connection(server = Role, Socket, Options, Timeout) ->
try
- Sups = proplists:get_value(supervisors, Options),
- ConnectionSup = proplists:get_value(connection_sup, Sups),
- Opts = [{supervisors, Sups}, {user_pid, self()} | proplists:get_value(ssh_opts, Options, [])],
- {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Opts]),
- {_, Callback, _} = proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
- socket_control(Socket, Pid, Callback),
- Ref = erlang:monitor(process, Pid),
- handshake(Pid, Ref, Timeout)
+ case proplists:get_value(parallel_login, Options, false) of
+ true ->
+ spawn(fun() -> start_server_connection(Role, Socket, Options, Timeout) end);
+ false ->
+ start_server_connection(Role, Socket, Options, Timeout)
+ end
catch
exit:{noproc, _} ->
{error, ssh_not_started};
@@ -119,6 +117,18 @@ start_connection(server = Role, Socket, Options, Timeout) ->
{error, Error}
end.
+
+start_server_connection(server = Role, Socket, Options, Timeout) ->
+ Sups = proplists:get_value(supervisors, Options),
+ ConnectionSup = proplists:get_value(connection_sup, Sups),
+ Opts = [{supervisors, Sups}, {user_pid, self()} | proplists:get_value(ssh_opts, Options, [])],
+ {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Opts]),
+ {_, Callback, _} = proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
+ socket_control(Socket, Pid, Callback),
+ Ref = erlang:monitor(process, Pid),
+ handshake(Pid, Ref, Timeout).
+
+
start_link(Role, Socket, Options) ->
{ok, proc_lib:spawn_link(?MODULE, init, [[Role, Socket, Options]])}.
@@ -157,7 +167,7 @@ init([Role, Socket, SshOpts]) ->
%%--------------------------------------------------------------------
-spec open_channel(pid(), string(), iodata(), integer(), integer(),
- timeout()) -> {open, channel_id()} | {open_error, term(), string(), string()}.
+ timeout()) -> {open, channel_id()} | {error, term()}.
%%--------------------------------------------------------------------
open_channel(ConnectionHandler, ChannelType, ChannelSpecificData,
InitialWindowSize,
@@ -206,7 +216,7 @@ global_request(ConnectionHandler, Type, false = Reply, Data) ->
send_all_state_event(ConnectionHandler, {global_request, self(), Type, Reply, Data}).
%%--------------------------------------------------------------------
--spec send(pid(), channel_id(), integer(), iolist(), timeout()) ->
+-spec send(pid(), channel_id(), integer(), iodata(), timeout()) ->
ok | {error, timeout} | {error, closed}.
%%--------------------------------------------------------------------
send(ConnectionHandler, ChannelId, Type, Data, Timeout) ->
@@ -1474,8 +1484,7 @@ ssh_channel_info([ _ | Rest], Channel, Acc) ->
log_error(Reason) ->
Report = io_lib:format("Erlang ssh connection handler failed with reason: "
- "~p ~n, Stacktace: ~p ~n"
- "please report this to [email protected] \n",
+ "~p ~n, Stacktrace: ~p ~n",
[Reason, erlang:get_stacktrace()]),
error_logger:error_report(Report),
"Internal error".
diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl
index 832b144db9..35336bce8b 100644
--- a/lib/ssh/src/ssh_io.erl
+++ b/lib/ssh/src/ssh_io.erl
@@ -81,6 +81,8 @@ format(Fmt, Args) ->
trim(Line) when is_list(Line) ->
lists:reverse(trim1(lists:reverse(trim1(Line))));
+trim(Line) when is_binary(Line) ->
+ trim(unicode:characters_to_list(Line));
trim(Other) -> Other.
trim1([$\s|Cs]) -> trim(Cs);
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index a63881db64..76b57cb995 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -120,7 +120,7 @@ encode(#ssh_msg_userauth_request{
data = Data
}) ->
ssh_bits:encode([?SSH_MSG_USERAUTH_REQUEST, User, Service, Method, Data],
- [byte, string, string, string, '...']);
+ [byte, string_utf8, string, string, '...']);
encode(#ssh_msg_userauth_failure{
authentications = Auths,
partial_success = Bool
@@ -135,7 +135,7 @@ encode(#ssh_msg_userauth_banner{
language = Lang
}) ->
ssh_bits:encode([?SSH_MSG_USERAUTH_BANNER, Banner, Lang],
- [byte, string, string]);
+ [byte, string_utf8, string]);
encode(#ssh_msg_userauth_pk_ok{
algorithm_name = Alg,
@@ -255,7 +255,7 @@ encode(#ssh_msg_ignore{data = Data}) ->
ssh_bits:encode([?SSH_MSG_IGNORE, Data], [byte, string]);
encode(#ssh_msg_unimplemented{sequence = Seq}) ->
- ssh_bits:encode([?SSH_MSG_IGNORE, Seq], [byte, uint32]);
+ ssh_bits:encode([?SSH_MSG_UNIMPLEMENTED, Seq], [byte, uint32]);
encode(#ssh_msg_debug{always_display = Bool,
message = Msg,
@@ -391,13 +391,6 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST), ?UINT32(Len0), Name:Len0/binary,
data = Data};
%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
-decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?UINT32(Len), Alg:Len/binary, KeyBlob/binary>>) ->
- #ssh_msg_userauth_pk_ok{
- algorithm_name = Alg,
- key_blob = KeyBlob
- };
-
-%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?UINT32(Len0), Prompt:Len0/binary,
?UINT32(Len1), Lang:Len1/binary>>) ->
#ssh_msg_userauth_passwd_changereq{
@@ -405,6 +398,13 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?UINT32(Len0), Prompt:Len0/b
languge = Lang
};
+%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
+decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?UINT32(Len), Alg:Len/binary, KeyBlob/binary>>) ->
+ #ssh_msg_userauth_pk_ok{
+ algorithm_name = Alg,
+ key_blob = KeyBlob
+ };
+
decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?UINT32(Num), Data/binary>>) ->
#ssh_msg_userauth_info_response{
num_responses = Num,
@@ -461,10 +461,19 @@ decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code),
language = Lang
};
+%% Accept bad disconnects from ancient openssh clients that doesn't send language tag. Use english as a work-around.
+decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code),
+ ?UINT32(Len0), Desc:Len0/binary>>) ->
+ #ssh_msg_disconnect{
+ code = Code,
+ description = unicode:characters_to_list(Desc),
+ language = <<"en">>
+ };
+
decode(<<?SSH_MSG_NEWKEYS>>) ->
#ssh_msg_newkeys{};
-decode(<<?BYTE(?SSH_MSG_IGNORE), Data/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_IGNORE), ?UINT32(Len), Data:Len/binary>>) ->
#ssh_msg_ignore{data = Data};
decode(<<?BYTE(?SSH_MSG_UNIMPLEMENTED), ?UINT32(Seq)>>) ->
diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl
index 10167a9223..0ea2366ac7 100644
--- a/lib/ssh/src/ssh_sftp.erl
+++ b/lib/ssh/src/ssh_sftp.erl
@@ -352,7 +352,7 @@ write_file(Pid, Name, List) ->
write_file(Pid, Name, List, ?FILEOP_TIMEOUT).
write_file(Pid, Name, List, FileOpTimeout) when is_list(List) ->
- write_file(Pid, Name, list_to_binary(List), FileOpTimeout);
+ write_file(Pid, Name, unicode:characters_to_binary(List), FileOpTimeout);
write_file(Pid, Name, Bin, FileOpTimeout) ->
case open(Pid, Name, [write, binary], FileOpTimeout) of
{ok, Handle} ->
@@ -514,7 +514,7 @@ do_handle_call({pread,Async,Handle,At,Length}, From, State) ->
case get_mode(Handle, State2) of
binary -> {{ok,Data}, State2};
text ->
- {{ok,binary_to_list(Data)}, State2}
+ {{ok,unicode:characters_to_list(Data)}, State2}
end;
(Rep, State2) ->
{Rep, State2}
@@ -535,8 +535,7 @@ do_handle_call({read,Async,Handle,Length}, From, State) ->
fun({ok,Data}, State2) ->
case get_mode(Handle, State2) of
binary -> {{ok,Data}, State2};
- text ->
- {{ok,binary_to_list(Data)}, State2}
+ text -> {{ok,binary_to_list(Data)}, State2}
end;
(Rep, State2) -> {Rep, State2}
end);
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index 174ca0126b..52665635f0 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -214,8 +214,7 @@ handle_op(?SSH_FXP_INIT, Version, B, State) when is_binary(B) ->
handle_op(?SSH_FXP_REALPATH, ReqId,
<<?UINT32(Rlen), RPath:Rlen/binary>>,
State0) ->
- RelPath0 = binary_to_list(RPath),
- RelPath = relate_file_name(RelPath0, State0, _Canonicalize=false),
+ RelPath = relate_file_name(RPath, State0, _Canonicalize=false),
{Res, State} = resolve_symlinks(RelPath, State0),
case Res of
{ok, AbsPath} ->
@@ -231,7 +230,7 @@ handle_op(?SSH_FXP_OPENDIR, ReqId,
<<?UINT32(RLen), RPath:RLen/binary>>,
State0 = #state{xf = #ssh_xfer{vsn = Vsn},
file_handler = FileMod, file_state = FS0}) ->
- RelPath = binary_to_list(RPath),
+ RelPath = unicode:characters_to_list(RPath),
AbsPath = relate_file_name(RelPath, State0),
XF = State0#state.xf,
@@ -312,9 +311,8 @@ handle_op(?SSH_FXP_WRITE, ReqId,
?SSH_FX_INVALID_HANDLE),
State
end;
-handle_op(?SSH_FXP_READLINK, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>,
+handle_op(?SSH_FXP_READLINK, ReqId, <<?UINT32(PLen), RelPath:PLen/binary>>,
State = #state{file_handler = FileMod, file_state = FS0}) ->
- RelPath = binary_to_list(BPath),
AbsPath = relate_file_name(RelPath, State),
{Res, FS1} = FileMod:read_link(AbsPath, FS0),
case Res of
@@ -524,10 +522,10 @@ close_our_file({_,Fd}, FileMod, FS0) ->
%%% stat: do the stat
stat(Vsn, ReqId, Data, State, F) when Vsn =< 3->
<<?UINT32(BLen), BPath:BLen/binary>> = Data,
- stat(ReqId, binary_to_list(BPath), State, F);
+ 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(ReqId, binary_to_list(BPath), State, F).
+ stat(ReqId, unicode:characters_to_list(BPath), State, F).
fstat(Vsn, ReqId, Data, State) when Vsn =< 3->
<<?UINT32(HLen), Handle:HLen/binary>> = Data,
@@ -609,13 +607,13 @@ decode_4_acess([]) ->
open(Vsn, ReqId, Data, State) when Vsn =< 3 ->
<<?UINT32(BLen), BPath:BLen/binary, ?UINT32(PFlags),
_Attrs/binary>> = Data,
- Path = binary_to_list(BPath),
+ Path = unicode:characters_to_list(BPath),
Flags = ssh_xfer:decode_open_flags(Vsn, PFlags),
do_open(ReqId, State, Path, Flags);
open(Vsn, ReqId, Data, State) when Vsn >= 4 ->
<<?UINT32(BLen), BPath:BLen/binary, ?UINT32(Access),
?UINT32(PFlags), _Attrs/binary>> = Data,
- Path = binary_to_list(BPath),
+ Path = unicode:characters_to_list(BPath),
FlagBits = ssh_xfer:decode_open_flags(Vsn, PFlags),
AcessBits = ssh_xfer:decode_ace_mask(Access),
%% TODO: This is to make sure the Access flags are not ignored
@@ -675,7 +673,7 @@ resolve_symlinks_2(["." | RestPath], State0, LinkCnt, AccPath) ->
resolve_symlinks_2([".." | RestPath], State0, LinkCnt, AccPath) ->
%% Remove the last path component
AccPathComps0 = filename:split(AccPath),
- Path = case lists:reverse(tl(lists:reverse(AccPathComps0))) of
+ Path = case lists:droplast(AccPathComps0) of
[] ->
"";
AccPathComps ->
@@ -712,7 +710,7 @@ relate_file_name(File, State) ->
relate_file_name(File, State, _Canonicalize=true).
relate_file_name(File, State, Canonicalize) when is_binary(File) ->
- relate_file_name(binary_to_list(File), State, Canonicalize);
+ relate_file_name(unicode:characters_to_list(File), State, Canonicalize);
relate_file_name(File, #state{cwd = CWD, root = ""}, Canonicalize) ->
relate_filename_to_path(File, CWD, Canonicalize);
relate_file_name(File, #state{root = Root}, Canonicalize) ->
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 27723dc870..ea05c849b7 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -113,15 +113,28 @@ key_init(client, Ssh, Value) ->
key_init(server, Ssh, Value) ->
Ssh#ssh{s_keyinit = Value}.
+available_ssh_algos() ->
+ Supports = crypto:supports(),
+ CipherAlgos = [{aes_ctr, "aes128-ctr"}, {aes_cbc128, "aes128-cbc"}, {des3_cbc, "3des-cbc"}],
+ Ciphers = [SshAlgo ||
+ {CryptoAlgo, SshAlgo} <- CipherAlgos,
+ lists:member(CryptoAlgo, proplists:get_value(ciphers, Supports, []))],
+ HashAlgos = [{sha256, "hmac-sha2-256"}, {sha, "hmac-sha1"}],
+ Hashs = [SshAlgo ||
+ {CryptoAlgo, SshAlgo} <- HashAlgos,
+ lists:member(CryptoAlgo, proplists:get_value(hashs, Supports, []))],
+ {Ciphers, Hashs}.
+
kexinit_messsage(client, Random, Compression, HostKeyAlgs) ->
+ {CipherAlgs, HashAlgs} = available_ssh_algos(),
#ssh_msg_kexinit{
cookie = Random,
kex_algorithms = ["diffie-hellman-group1-sha1"],
server_host_key_algorithms = HostKeyAlgs,
- encryption_algorithms_client_to_server = ["aes128-cbc","3des-cbc"],
- encryption_algorithms_server_to_client = ["aes128-cbc","3des-cbc"],
- mac_algorithms_client_to_server = ["hmac-sha1"],
- mac_algorithms_server_to_client = ["hmac-sha1"],
+ encryption_algorithms_client_to_server = CipherAlgs,
+ encryption_algorithms_server_to_client = CipherAlgs,
+ mac_algorithms_client_to_server = HashAlgs,
+ mac_algorithms_server_to_client = HashAlgs,
compression_algorithms_client_to_server = Compression,
compression_algorithms_server_to_client = Compression,
languages_client_to_server = [],
@@ -129,14 +142,15 @@ kexinit_messsage(client, Random, Compression, HostKeyAlgs) ->
};
kexinit_messsage(server, Random, Compression, HostKeyAlgs) ->
+ {CipherAlgs, HashAlgs} = available_ssh_algos(),
#ssh_msg_kexinit{
cookie = Random,
kex_algorithms = ["diffie-hellman-group1-sha1"],
server_host_key_algorithms = HostKeyAlgs,
- encryption_algorithms_client_to_server = ["aes128-cbc","3des-cbc"],
- encryption_algorithms_server_to_client = ["aes128-cbc","3des-cbc"],
- mac_algorithms_client_to_server = ["hmac-sha1"],
- mac_algorithms_server_to_client = ["hmac-sha1"],
+ encryption_algorithms_client_to_server = CipherAlgs,
+ encryption_algorithms_server_to_client = CipherAlgs,
+ mac_algorithms_client_to_server = HashAlgs,
+ mac_algorithms_server_to_client = HashAlgs,
compression_algorithms_client_to_server = Compression,
compression_algorithms_server_to_client = Compression,
languages_client_to_server = [],
@@ -636,7 +650,21 @@ encrypt_init(#ssh{encrypt = 'aes128-cbc', role = server} = Ssh) ->
<<K:16/binary>> = hash(Ssh, "D", 128),
{ok, Ssh#ssh{encrypt_keys = K,
encrypt_block_size = 16,
- encrypt_ctx = IV}}.
+ encrypt_ctx = IV}};
+encrypt_init(#ssh{encrypt = 'aes128-ctr', role = client} = Ssh) ->
+ IV = hash(Ssh, "A", 128),
+ <<K:16/binary>> = hash(Ssh, "C", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{encrypt_keys = K,
+ encrypt_block_size = 16,
+ encrypt_ctx = State}};
+encrypt_init(#ssh{encrypt = 'aes128-ctr', role = server} = Ssh) ->
+ IV = hash(Ssh, "B", 128),
+ <<K:16/binary>> = hash(Ssh, "D", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{encrypt_keys = K,
+ encrypt_block_size = 16,
+ encrypt_ctx = State}}.
encrypt_final(Ssh) ->
{ok, Ssh#ssh{encrypt = none,
@@ -658,7 +686,11 @@ encrypt(#ssh{encrypt = 'aes128-cbc',
encrypt_ctx = IV0} = Ssh, Data) ->
Enc = crypto:block_encrypt(aes_cbc128, K,IV0,Data),
IV = crypto:next_iv(aes_cbc, Enc),
- {Ssh#ssh{encrypt_ctx = IV}, Enc}.
+ {Ssh#ssh{encrypt_ctx = IV}, Enc};
+encrypt(#ssh{encrypt = 'aes128-ctr',
+ encrypt_ctx = State0} = Ssh, Data) ->
+ {State, Enc} = crypto:stream_encrypt(State0,Data),
+ {Ssh#ssh{encrypt_ctx = State}, Enc}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -690,7 +722,21 @@ decrypt_init(#ssh{decrypt = 'aes128-cbc', role = server} = Ssh) ->
hash(Ssh, "C", 128)},
<<K:16/binary>> = KD,
{ok, Ssh#ssh{decrypt_keys = K, decrypt_ctx = IV,
- decrypt_block_size = 16}}.
+ decrypt_block_size = 16}};
+decrypt_init(#ssh{decrypt = 'aes128-ctr', role = client} = Ssh) ->
+ IV = hash(Ssh, "B", 128),
+ <<K:16/binary>> = hash(Ssh, "D", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{decrypt_keys = K,
+ decrypt_block_size = 16,
+ decrypt_ctx = State}};
+decrypt_init(#ssh{decrypt = 'aes128-ctr', role = server} = Ssh) ->
+ IV = hash(Ssh, "A", 128),
+ <<K:16/binary>> = hash(Ssh, "C", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{decrypt_keys = K,
+ decrypt_block_size = 16,
+ decrypt_ctx = State}}.
decrypt_final(Ssh) ->
@@ -711,7 +757,11 @@ decrypt(#ssh{decrypt = 'aes128-cbc', decrypt_keys = Key,
decrypt_ctx = IV0} = Ssh, Data) ->
Dec = crypto:block_decrypt(aes_cbc128, Key,IV0,Data),
IV = crypto:next_iv(aes_cbc, Data),
- {Ssh#ssh{decrypt_ctx = IV}, Dec}.
+ {Ssh#ssh{decrypt_ctx = IV}, Dec};
+decrypt(#ssh{decrypt = 'aes128-ctr',
+ decrypt_ctx = State0} = Ssh, Data) ->
+ {State, Enc} = crypto:stream_decrypt(State0,Data),
+ {Ssh#ssh{decrypt_ctx = State}, Enc}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Compression
@@ -846,7 +896,9 @@ mac('hmac-sha1-96', Key, SeqNum, Data) ->
mac('hmac-md5', Key, SeqNum, Data) ->
crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data]);
mac('hmac-md5-96', Key, SeqNum, Data) ->
- crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data], mac_digest_size('hmac-md5-96')).
+ crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data], mac_digest_size('hmac-md5-96'));
+mac('hmac-sha2-256', Key, SeqNum, Data) ->
+ crypto:hmac(sha256, Key, [<<?UINT32(SeqNum)>>, Data]).
%% return N hash bytes (HASH)
hash(SSH, Char, Bits) ->
@@ -911,12 +963,14 @@ mac_key_size('hmac-sha1') -> 20*8;
mac_key_size('hmac-sha1-96') -> 20*8;
mac_key_size('hmac-md5') -> 16*8;
mac_key_size('hmac-md5-96') -> 16*8;
+mac_key_size('hmac-sha2-256')-> 32*8;
mac_key_size(none) -> 0.
mac_digest_size('hmac-sha1') -> 20;
mac_digest_size('hmac-sha1-96') -> 12;
mac_digest_size('hmac-md5') -> 20;
mac_digest_size('hmac-md5-96') -> 12;
+mac_digest_size('hmac-sha2-256') -> 32;
mac_digest_size(none) -> 0.
peer_name({Host, _}) ->
diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl
index e18e18a9a9..63d01fd9de 100644
--- a/lib/ssh/src/ssh_xfer.erl
+++ b/lib/ssh/src/ssh_xfer.erl
@@ -72,7 +72,6 @@ protocol_version_request(XF) ->
open(XF, ReqID, FileName, Access, Flags, Attrs) ->
Vsn = XF#ssh_xfer.vsn,
- FileName1 = unicode:characters_to_binary(FileName),
MBits = if Vsn >= 5 ->
M = encode_ace_mask(Access),
?uint32(M);
@@ -82,7 +81,7 @@ open(XF, ReqID, FileName, Access, Flags, Attrs) ->
F = encode_open_flags(Flags),
xf_request(XF,?SSH_FXP_OPEN,
[?uint32(ReqID),
- ?binary(FileName1),
+ ?string_utf8(FileName),
MBits,
?uint32(F),
encode_ATTR(Vsn,Attrs)]).
@@ -90,7 +89,7 @@ open(XF, ReqID, FileName, Access, Flags, Attrs) ->
opendir(XF, ReqID, DirName) ->
xf_request(XF, ?SSH_FXP_OPENDIR,
[?uint32(ReqID),
- ?string(DirName)]).
+ ?string_utf8(DirName)]).
close(XF, ReqID, Handle) ->
@@ -127,13 +126,11 @@ write(XF,ReqID, Handle, Offset, Data) ->
remove(XF, ReqID, File) ->
xf_request(XF, ?SSH_FXP_REMOVE,
[?uint32(ReqID),
- ?string(File)]).
+ ?string_utf8(File)]).
%% Rename a file/directory
-rename(XF, ReqID, Old, New, Flags) ->
+rename(XF, ReqID, OldPath, NewPath, Flags) ->
Vsn = XF#ssh_xfer.vsn,
- OldPath = unicode:characters_to_binary(Old),
- NewPath = unicode:characters_to_binary(New),
FlagBits
= if Vsn >= 5 ->
F0 = encode_rename_flags(Flags),
@@ -143,30 +140,27 @@ rename(XF, ReqID, Old, New, Flags) ->
end,
xf_request(XF, ?SSH_FXP_RENAME,
[?uint32(ReqID),
- ?binary(OldPath),
- ?binary(NewPath),
+ ?string_utf8(OldPath),
+ ?string_utf8(NewPath),
FlagBits]).
%% Create directory
mkdir(XF, ReqID, Path, Attrs) ->
- Path1 = unicode:characters_to_binary(Path),
xf_request(XF, ?SSH_FXP_MKDIR,
[?uint32(ReqID),
- ?binary(Path1),
+ ?string_utf8(Path),
encode_ATTR(XF#ssh_xfer.vsn, Attrs)]).
%% Remove a directory
rmdir(XF, ReqID, Dir) ->
- Dir1 = unicode:characters_to_binary(Dir),
xf_request(XF, ?SSH_FXP_RMDIR,
[?uint32(ReqID),
- ?binary(Dir1)]).
+ ?string_utf8(Dir)]).
%% Stat file
stat(XF, ReqID, Path, Flags) ->
- Path1 = unicode:characters_to_binary(Path),
Vsn = XF#ssh_xfer.vsn,
AttrFlags = if Vsn >= 5 ->
F = encode_attr_flags(Vsn, Flags),
@@ -176,13 +170,12 @@ stat(XF, ReqID, Path, Flags) ->
end,
xf_request(XF, ?SSH_FXP_STAT,
[?uint32(ReqID),
- ?binary(Path1),
+ ?string_utf8(Path),
AttrFlags]).
%% Stat file - follow symbolic links
lstat(XF, ReqID, Path, Flags) ->
- Path1 = unicode:characters_to_binary(Path),
Vsn = XF#ssh_xfer.vsn,
AttrFlags = if Vsn >= 5 ->
F = encode_attr_flags(Vsn, Flags),
@@ -192,7 +185,7 @@ lstat(XF, ReqID, Path, Flags) ->
end,
xf_request(XF, ?SSH_FXP_LSTAT,
[?uint32(ReqID),
- ?binary(Path1),
+ ?string_utf8(Path),
AttrFlags]).
%% Stat open file
@@ -211,10 +204,9 @@ fstat(XF, ReqID, Handle, Flags) ->
%% Modify file attributes
setstat(XF, ReqID, Path, Attrs) ->
- Path1 = unicode:characters_to_binary(Path),
xf_request(XF, ?SSH_FXP_SETSTAT,
[?uint32(ReqID),
- ?binary(Path1),
+ ?string_utf8(Path),
encode_ATTR(XF#ssh_xfer.vsn, Attrs)]).
@@ -227,10 +219,9 @@ fsetstat(XF, ReqID, Handle, Attrs) ->
%% Read a symbolic link
readlink(XF, ReqID, Path) ->
- Path1 = unicode:characters_to_binary(Path),
xf_request(XF, ?SSH_FXP_READLINK,
[?uint32(ReqID),
- ?binary(Path1)]).
+ ?string_utf8(Path)]).
%% Create a symbolic link
@@ -244,10 +235,9 @@ symlink(XF, ReqID, LinkPath, TargetPath) ->
%% Convert a path into a 'canonical' form
realpath(XF, ReqID, Path) ->
- Path1 = unicode:characters_to_binary(Path),
xf_request(XF, ?SSH_FXP_REALPATH,
[?uint32(ReqID),
- ?binary(Path1)]).
+ ?string_utf8(Path)]).
extended(XF, ReqID, Request, Data) ->
xf_request(XF, ?SSH_FXP_EXTENDED,
@@ -296,7 +286,10 @@ xf_send_names(#ssh_xfer{cm = CM, channel = Channel, vsn = Vsn},
Count = length(NamesAndAttrs),
{Data, Len} = encode_names(Vsn, NamesAndAttrs),
Size = 1 + 4 + 4 + Len,
- ToSend = [<<?UINT32(Size), ?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(Count)>>,
+ ToSend = [<<?UINT32(Size),
+ ?SSH_FXP_NAME,
+ ?UINT32(ReqId),
+ ?UINT32(Count)>>,
Data],
ssh_connection:send(CM, Channel, ToSend).
@@ -818,25 +811,27 @@ decode_names(_Vsn, 0, _Data) ->
decode_names(Vsn, I, <<?UINT32(Len), FileName:Len/binary,
?UINT32(LLen), _LongName:LLen/binary,
Tail/binary>>) when Vsn =< 3 ->
- Name = binary_to_list(FileName),
+ Name = unicode:characters_to_list(FileName),
{A, Tail2} = decode_ATTR(Vsn, Tail),
[{Name, A} | decode_names(Vsn, I-1, Tail2)];
decode_names(Vsn, I, <<?UINT32(Len), FileName:Len/binary,
Tail/binary>>) when Vsn >= 4 ->
- Name = binary_to_list(FileName),
+ Name = unicode:characters_to_list(FileName),
{A, Tail2} = decode_ATTR(Vsn, Tail),
[{Name, A} | decode_names(Vsn, I-1, Tail2)].
encode_names(Vsn, NamesAndAttrs) ->
lists:mapfoldl(fun(N, L) -> encode_name(Vsn, N, L) end, 0, NamesAndAttrs).
-encode_name(Vsn, {Name,Attr}, Len) when Vsn =< 3 ->
+encode_name(Vsn, {NameUC,Attr}, Len) when Vsn =< 3 ->
+ Name = binary_to_list(unicode:characters_to_binary(NameUC)),
NLen = length(Name),
EncAttr = encode_ATTR(Vsn, Attr),
ALen = size(EncAttr),
NewLen = Len + NLen*2 + 4 + 4 + ALen,
{[<<?UINT32(NLen)>>, Name, <<?UINT32(NLen)>>, Name, EncAttr], NewLen};
-encode_name(Vsn, {Name,Attr}, Len) when Vsn >= 4 ->
+encode_name(Vsn, {NameUC,Attr}, Len) when Vsn >= 4 ->
+ Name = binary_to_list(unicode:characters_to_binary(NameUC)),
NLen = length(Name),
EncAttr = encode_ATTR(Vsn, Attr),
ALen = size(EncAttr),
@@ -851,9 +846,9 @@ encode_acl_items([ACE|As]) ->
Type = encode_ace_type(ACE#ssh_xfer_ace.type),
Flag = encode_ace_flag(ACE#ssh_xfer_ace.flag),
Mask = encode_ace_mask(ACE#ssh_xfer_ace.mask),
- Who = list_to_binary(ACE#ssh_xfer_ace.who),
+ Who = ACE#ssh_xfer_ace.who,
[?uint32(Type), ?uint32(Flag), ?uint32(Mask),
- ?binary(Who) | encode_acl_items(As)];
+ ?string_utf8(Who) | encode_acl_items(As)];
encode_acl_items([]) ->
[].
@@ -872,7 +867,7 @@ decode_acl_items(I, <<?UINT32(Type),
[#ssh_xfer_ace { type = decode_ace_type(Type),
flag = decode_ace_flag(Flag),
mask = decode_ace_mask(Mask),
- who = binary_to_list(BWho)} | Acc]).
+ who = unicode:characters_to_list(BWho)} | Acc]).
encode_extensions(Exts) ->
Count = length(Exts),