aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/src/ssh.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/src/ssh.erl')
-rw-r--r--lib/ssh/src/ssh.erl471
1 files changed, 401 insertions, 70 deletions
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 7ed17618e7..09b07b7a2a 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -1,18 +1,19 @@
%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2016. 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/.
+%% 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
%%
-%% 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.
+%% 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%
%%
@@ -24,12 +25,22 @@
-include("ssh.hrl").
-include("ssh_connect.hrl").
-include_lib("public_key/include/public_key.hrl").
+-include_lib("kernel/include/file.hrl").
-export([start/0, start/1, stop/0, connect/3, connect/4, close/1, connection_info/2,
channel_info/3,
daemon/1, daemon/2, daemon/3,
- stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2,
- shell/1, shell/2, shell/3]).
+ daemon_info/1,
+ default_algorithms/0,
+ stop_listener/1, stop_listener/2, stop_listener/3,
+ stop_daemon/1, stop_daemon/2, stop_daemon/3,
+ shell/1, shell/2, shell/3
+ ]).
+
+%%% Type exports
+-export_type([connection_ref/0,
+ channel_id/0
+ ]).
%%--------------------------------------------------------------------
-spec start() -> ok | {error, term()}.
@@ -76,7 +87,7 @@ connect(Host, Port, Options, Timeout) ->
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)],
+ Opts = [{user_pid,self()}, {host,Host} | SshOptions],
ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
{error, Reason} ->
{error, Reason}
@@ -113,9 +124,9 @@ channel_info(ConnectionRef, ChannelId, Options) ->
ssh_connection_handler:channel_info(ConnectionRef, ChannelId, Options).
%%--------------------------------------------------------------------
--spec daemon(integer()) -> {ok, pid()}.
--spec daemon(integer(), proplists:proplist()) -> {ok, pid()}.
--spec daemon(any | inet:ip_address(), integer(), proplists:proplist()) -> {ok, pid()}.
+-spec daemon(integer()) -> {ok, pid()} | {error, term()}.
+-spec daemon(integer(), proplists:proplist()) -> {ok, pid()} | {error, term()}.
+-spec daemon(any | inet:ip_address(), integer(), proplists:proplist()) -> {ok, pid()} | {error, term()}.
%% Description: Starts a server listening for SSH connections
%% on the given port.
@@ -148,6 +159,19 @@ daemon(HostAddr, Port, Options0) ->
start_daemon(Host, Port, Options, Inet).
%%--------------------------------------------------------------------
+daemon_info(Pid) ->
+ case catch ssh_system_sup:acceptor_supervisor(Pid) of
+ AsupPid when is_pid(AsupPid) ->
+ [Port] =
+ [Prt || {{ssh_acceptor_sup,any,Prt,default},
+ _WorkerPid,worker,[ssh_acceptor]} <- supervisor:which_children(AsupPid)],
+ {ok, [{port,Port}]};
+
+ _ ->
+ {error,bad_daemon_ref}
+ end.
+
+%%--------------------------------------------------------------------
-spec stop_listener(pid()) -> ok.
-spec stop_listener(inet:ip_address(), integer()) -> ok.
%%
@@ -157,7 +181,9 @@ daemon(HostAddr, Port, Options0) ->
stop_listener(SysSup) ->
ssh_system_sup:stop_listener(SysSup).
stop_listener(Address, Port) ->
- ssh_system_sup:stop_listener(Address, Port).
+ stop_listener(Address, Port, ?DEFAULT_PROFILE).
+stop_listener(Address, Port, Profile) ->
+ ssh_system_sup:stop_listener(Address, Port, Profile).
%%--------------------------------------------------------------------
-spec stop_daemon(pid()) -> ok.
@@ -169,8 +195,9 @@ stop_listener(Address, Port) ->
stop_daemon(SysSup) ->
ssh_system_sup:stop_system(SysSup).
stop_daemon(Address, Port) ->
- ssh_system_sup:stop_system(Address, Port).
-
+ ssh_system_sup:stop_system(Address, Port, ?DEFAULT_PROFILE).
+stop_daemon(Address, Port, Profile) ->
+ ssh_system_sup:stop_system(Address, Port, Profile).
%%--------------------------------------------------------------------
-spec shell(string()) -> _.
-spec shell(string(), proplists:proplist()) -> _.
@@ -208,36 +235,72 @@ shell(Host, Port, Options) ->
end.
%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+default_algorithms() ->
+ ssh_transport:default_algorithms().
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-fix_idle_time(SshOptions) ->
- case proplists:get_value(idle_time, SshOptions) of
- undefined ->
- [{idle_time, infinity}|SshOptions];
- _ ->
- SshOptions
- end.
start_daemon(Host, Port, Options, Inet) ->
case handle_options(Options) of
{error, _Reason} = Error ->
Error;
{SocketOptions, SshOptions}->
- do_start_daemon(Host, Port,[{role, server} |SshOptions] , [Inet | SocketOptions])
+ try
+ do_start_daemon(Host, Port,[{role, server} |SshOptions] , [Inet | SocketOptions])
+ catch
+ throw:bad_fd -> {error,bad_fd};
+ _C:_E -> {error,{cannot_start_daemon,_C,_E}}
+ end
end.
-do_start_daemon(Host, Port, Options, SocketOptions) ->
- case ssh_system_sup:system_supervisor(Host, Port) of
+do_start_daemon(Host0, Port0, SshOptions, SocketOptions) ->
+ {Host,Port1} =
+ try
+ case proplists:get_value(fd, SocketOptions) of
+ undefined ->
+ {Host0,Port0};
+ Fd when Port0==0 ->
+ find_hostport(Fd);
+ _ ->
+ {Host0,Port0}
+ end
+ catch
+ _:_ -> throw(bad_fd)
+ end,
+ Profile = proplists:get_value(profile, SshOptions, ?DEFAULT_PROFILE),
+ {Port, WaitRequestControl, Opts} =
+ case Port1 of
+ 0 -> %% Allocate the socket here to get the port number...
+ {_, Callback, _} =
+ proplists:get_value(transport, SshOptions, {tcp, gen_tcp, tcp_closed}),
+ {ok,LSock} = ssh_acceptor:callback_listen(Callback, 0, SocketOptions),
+ {ok,{_,LPort}} = inet:sockname(LSock),
+ {LPort,
+ {LSock,Callback},
+ [{lsocket,LSock},{lsock_owner,self()}]
+ };
+ _ ->
+ {Port1, false, []}
+ end,
+ case ssh_system_sup:system_supervisor(Host, Port, Profile) of
undefined ->
%% It would proably make more sense to call the
%% address option host but that is a too big change at the
%% monent. The name is a legacy name!
try sshd_sup:start_child([{address, Host},
- {port, Port}, {role, server},
+ {port, Port},
+ {role, server},
{socket_opts, SocketOptions},
- {ssh_opts, Options}]) of
+ {ssh_opts, SshOptions}
+ | Opts]) of
{error, {already_started, _}} ->
{error, eaddrinuse};
- Result = {Code, _} when (Code == ok) or (Code == error) ->
+ Result = {ok,_} ->
+ sync_request_control(WaitRequestControl),
+ Result;
+ Result = {error, _} ->
Result
catch
exit:{noproc, _} ->
@@ -246,20 +309,42 @@ do_start_daemon(Host, Port, Options, SocketOptions) ->
Sup ->
AccPid = ssh_system_sup:acceptor_supervisor(Sup),
case ssh_acceptor_sup:start_child(AccPid, [{address, Host},
- {port, Port}, {role, server},
+ {port, Port},
+ {role, server},
{socket_opts, SocketOptions},
- {ssh_opts, Options}]) of
+ {ssh_opts, SshOptions}
+ | Opts]) of
{error, {already_started, _}} ->
{error, eaddrinuse};
{ok, _} ->
+ sync_request_control(WaitRequestControl),
{ok, Sup};
Other ->
Other
end
end.
+sync_request_control(false) ->
+ ok;
+sync_request_control({LSock,Callback}) ->
+ receive
+ {request_control,LSock,ReqPid} ->
+ ok = Callback:controlling_process(LSock, ReqPid),
+ ReqPid ! {its_yours,LSock},
+ ok
+ end.
+
+find_hostport(Fd) ->
+ %% Using internal functions inet:open/8 and inet:close/0.
+ %% Don't try this at home unless you know what you are doing!
+ {ok,S} = inet:open(Fd, {0,0,0,0}, 0, [], tcp, inet, stream, inet_tcp),
+ {ok, HostPort} = inet:sockname(S),
+ ok = inet:close(S),
+ HostPort.
+
+
handle_options(Opts) ->
- try handle_option(proplists:unfold(Opts), [], []) of
+ try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of
{Inet, Ssh} ->
{handle_ip(Inet), Ssh}
catch
@@ -267,6 +352,30 @@ handle_options(Opts) ->
Error
end.
+
+algs_compatibility(Os0) ->
+ %% Take care of old options 'public_key_alg' and 'pref_public_key_algs'
+ case proplists:get_value(public_key_alg, Os0) of
+ undefined ->
+ Os0;
+ A when is_atom(A) ->
+ %% Skip public_key_alg if pref_public_key_algs is defined:
+ Os = lists:keydelete(public_key_alg, 1, Os0),
+ case proplists:get_value(pref_public_key_algs,Os) of
+ undefined when A == 'ssh-rsa' ; A==ssh_rsa ->
+ [{pref_public_key_algs,['ssh-rsa','ssh-dss']} | Os];
+ undefined when A == 'ssh-dss' ; A==ssh_dsa ->
+ [{pref_public_key_algs,['ssh-dss','ssh-rsa']} | Os];
+ undefined ->
+ throw({error, {eoptions, {public_key_alg,A} }});
+ _ ->
+ Os
+ end;
+ V ->
+ throw({error, {eoptions, {public_key_alg,V} }})
+ end.
+
+
handle_option([], SocketOptions, SshOptions) ->
{SocketOptions, SshOptions};
handle_option([{system_dir, _} = Opt | Rest], SocketOptions, SshOptions) ->
@@ -279,8 +388,6 @@ handle_option([{silently_accept_hosts, _} = Opt | Rest], SocketOptions, SshOptio
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{user_interaction, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{public_key_alg, _} = Opt | Rest], SocketOptions, SshOptions) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{connect_timeout, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{user, _} = Opt | Rest], SocketOptions, SshOptions) ->
@@ -295,11 +402,13 @@ handle_option([{user_passwords, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([{compression, _} = Opt | Rest], SocketOptions, SshOptions) ->
+handle_option([{key_cb, {Module, Options}} | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option({key_cb, Module}),
+ handle_ssh_priv_option({key_cb_private, Options}) |
+ SshOptions]);
+handle_option([{key_cb, Module} | Rest], SocketOptions, SshOptions) ->
+ handle_option([{key_cb, {Module, []}} | Rest], SocketOptions, SshOptions);
+handle_option([{keyboard_interact_fun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
%%Backwards compatibility
handle_option([{allow_user_interaction, Value} | Rest], SocketOptions, SshOptions) ->
@@ -310,6 +419,8 @@ handle_option([{connectfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{disconnectfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{unexpectedfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{failfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{ssh_msg_debug_fun, _} = Opt | Rest], SocketOptions, SshOptions) ->
@@ -335,6 +446,12 @@ handle_option([{auth_method_kb_interactive_data, _} = Opt | Rest], SocketOptions
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{pref_public_key_algs, _} = Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{preferred_algorithms,_} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{dh_gex_groups,_} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{dh_gex_limits,_} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{quiet_mode, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{idle_time, _} = Opt | Rest], SocketOptions, SshOptions) ->
@@ -343,16 +460,23 @@ 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([{max_channels, _} = 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]);
+%% (Is handled by proplists:unfold above:)
+%% handle_option([parallel_login|Rest], SocketOptions, SshOptions) ->
+%% handle_option(Rest, SocketOptions, [handle_ssh_option({parallel_login,true}) | SshOptions]);
handle_option([{minimal_remote_max_packet_size, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{id_string, _ID} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{profile, _ID} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{max_random_length_padding, _Bool} = Opt|Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
@@ -360,25 +484,83 @@ handle_option([Opt | Rest], SocketOptions, SshOptions) ->
handle_ssh_option({minimal_remote_max_packet_size, Value} = Opt) when is_integer(Value), Value >=0 ->
Opt;
handle_ssh_option({system_dir, Value} = Opt) when is_list(Value) ->
- Opt;
+ check_dir(Opt);
handle_ssh_option({user_dir, Value} = Opt) when is_list(Value) ->
- Opt;
+ check_dir(Opt);
handle_ssh_option({user_dir_fun, Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_boolean(Value) ->
Opt;
handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) ->
Opt;
-handle_ssh_option({public_key_alg, ssh_dsa}) ->
- {public_key_alg, 'ssh-dss'};
-handle_ssh_option({public_key_alg, ssh_rsa}) ->
- {public_key_alg, 'ssh-rsa'};
-handle_ssh_option({public_key_alg, Value} = Opt) when Value == 'ssh-rsa'; Value == 'ssh-dss' ->
+handle_ssh_option({preferred_algorithms,[_|_]} = Opt) ->
+ handle_pref_algs(Opt);
+
+handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) ->
+ {dh_gex_groups,
+ collect_per_size(
+ lists:foldl(
+ fun({N,G,P}, Acc) when is_integer(N),N>0,
+ is_integer(G),G>0,
+ is_integer(P),P>0 ->
+ [{N,{G,P}} | Acc];
+ ({N,{G,P}}, Acc) when is_integer(N),N>0,
+ is_integer(G),G>0,
+ is_integer(P),P>0 ->
+ [{N,{G,P}} | Acc];
+ ({N,GPs}, Acc) when is_list(GPs) ->
+ lists:foldr(fun({Gi,Pi}, Acci) when is_integer(Gi),Gi>0,
+ is_integer(Pi),Pi>0 ->
+ [{N,{Gi,Pi}} | Acci]
+ end, Acc, GPs)
+ end, [], L0))};
+
+handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0,
+ Tag == file ;
+ Tag == ssh_moduli_file ->
+ {ok,GroupDefs} =
+ case Tag of
+ file ->
+ file:consult(File);
+ ssh_moduli_file ->
+ case file:open(File,[read]) of
+ {ok,D} ->
+ try
+ {ok,Moduli} = read_moduli_file(D, 1, []),
+ file:close(D),
+ {ok, Moduli}
+ catch
+ _:_ ->
+ throw({error, {{eoptions, Opt}, "Bad format in file "++File}})
+ end;
+ {error,enoent} ->
+ throw({error, {{eoptions, Opt}, "File not found:"++File}});
+ {error,Error} ->
+ throw({error, {{eoptions, Opt}, io_lib:format("Error reading file ~s: ~p",[File,Error])}})
+ end
+ end,
+
+ try
+ handle_ssh_option({dh_gex_groups,GroupDefs})
+ catch
+ _:_ ->
+ throw({error, {{eoptions, Opt}, "Bad format in file: "++File}})
+ end;
+
+
+handle_ssh_option({dh_gex_limits,{Min,Max}} = Opt) when is_integer(Min), Min>0,
+ is_integer(Max), Max>=Min ->
+ %% Server
+ Opt;
+handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0,
+ is_integer(I), I>=Min,
+ is_integer(Max), Max>=I ->
+ %% Client
Opt;
handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), length(Value) >= 1 ->
- case handle_pref_algs(Value, []) of
+ case handle_user_pref_pubkey_algs(Value, []) of
{true, NewOpts} ->
- NewOpts;
+ {pref_public_key_algs, NewOpts};
_ ->
throw({error, {eoptions, Opt}})
end;
@@ -386,6 +568,8 @@ handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value
Opt;
handle_ssh_option({max_sessions, Value} = Opt) when is_integer(Value), Value>0 ->
Opt;
+handle_ssh_option({max_channels, 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 ->
@@ -400,10 +584,17 @@ handle_ssh_option({password, Value} = Opt) when is_list(Value) ->
Opt;
handle_ssh_option({user_passwords, Value} = Opt) when is_list(Value)->
Opt;
-handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value) ->
+handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value,2) ->
+ Opt;
+handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value,4) ->
Opt;
handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) ->
Opt;
+handle_ssh_option({key_cb, {CallbackMod, CallbackOptions}} = Opt) when is_atom(CallbackMod),
+ is_list(CallbackOptions) ->
+ Opt;
+handle_ssh_option({keyboard_interact_fun, Value} = Opt) when is_function(Value,3) ->
+ Opt;
handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
Opt;
handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
@@ -424,7 +615,9 @@ handle_ssh_option({infofun, Value} = Opt) when is_function(Value) ->
Opt;
handle_ssh_option({connectfun, Value} = Opt) when is_function(Value) ->
Opt;
-handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) ->
+handle_ssh_option({disconnectfun, Value} = Opt) when is_function(Value) ->
+ Opt;
+handle_ssh_option({unexpectedfun, Value} = Opt) when is_function(Value,2) ->
Opt;
handle_ssh_option({failfun, Value} = Opt) when is_function(Value) ->
Opt;
@@ -458,9 +651,17 @@ handle_ssh_option({id_string, random}) ->
{id_string, {random,2,5}}; %% 2 - 5 random characters
handle_ssh_option({id_string, ID} = Opt) when is_list(ID) ->
Opt;
+handle_ssh_option({max_random_length_padding, Value} = Opt) when is_integer(Value),
+ Value =< 255 ->
+ Opt;
+handle_ssh_option({profile, Value} = Opt) when is_atom(Value) ->
+ Opt;
handle_ssh_option(Opt) ->
throw({error, {eoptions, Opt}}).
+handle_ssh_priv_option({key_cb_private, Value} = Opt) when is_list(Value) ->
+ Opt.
+
handle_inet_option({active, _} = Opt) ->
throw({error, {{eoptions, Opt}, "SSH has built in flow control, "
"and active is handled internally, user is not allowed"
@@ -474,23 +675,83 @@ handle_inet_option({reuseaddr, _} = Opt) ->
%% Option verified by inet
handle_inet_option(Opt) ->
Opt.
+
+
%% Check preferred algs
-handle_pref_algs([], Acc) ->
- {true, lists:reverse(Acc)};
-handle_pref_algs([H|T], Acc) ->
- case H of
- ssh_dsa ->
- handle_pref_algs(T, ['ssh-dss'| Acc]);
- ssh_rsa ->
- handle_pref_algs(T, ['ssh-rsa'| Acc]);
- 'ssh-dss' ->
- handle_pref_algs(T, ['ssh-dss'| Acc]);
- 'ssh-rsa' ->
- handle_pref_algs(T, ['ssh-rsa'| Acc]);
- _ ->
- false
+
+handle_pref_algs({preferred_algorithms,Algs}) ->
+ try alg_duplicates(Algs, [], []) of
+ [] ->
+ {preferred_algorithms,
+ [try ssh_transport:supported_algorithms(Key)
+ of
+ DefAlgs -> handle_pref_alg(Key,Vals,DefAlgs)
+ catch
+ _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}},
+ "Bad preferred_algorithms key"}})
+ end || {Key,Vals} <- Algs]
+ };
+
+ Dups ->
+ throw({error, {{eoptions, {preferred_algorithms,Dups}}, "Duplicates found"}})
+ catch
+ _:_ ->
+ throw({error, {{eoptions, preferred_algorithms}, "Malformed"}})
end.
+alg_duplicates([{K,V}|KVs], Ks, Dups0) ->
+ Dups =
+ case lists:member(K,Ks) of
+ true ->
+ [K|Dups0];
+ false ->
+ Dups0
+ end,
+ case V--lists:usort(V) of
+ [] ->
+ alg_duplicates(KVs, [K|Ks], Dups);
+ Ds ->
+ alg_duplicates(KVs, [K|Ks], Dups++Ds)
+ end;
+alg_duplicates([], _Ks, Dups) ->
+ Dups.
+
+handle_pref_alg(Key,
+ Vs=[{client2server,C2Ss=[_|_]},{server2client,S2Cs=[_|_]}],
+ [{client2server,Sup_C2Ss},{server2client,Sup_S2Cs}]
+ ) ->
+ chk_alg_vs(Key, C2Ss, Sup_C2Ss),
+ chk_alg_vs(Key, S2Cs, Sup_S2Cs),
+ {Key, Vs};
+
+handle_pref_alg(Key,
+ Vs=[{server2client,[_|_]},{client2server,[_|_]}],
+ Sup=[{client2server,_},{server2client,_}]
+ ) ->
+ handle_pref_alg(Key, lists:reverse(Vs), Sup);
+
+handle_pref_alg(Key,
+ Vs=[V|_],
+ Sup=[{client2server,_},{server2client,_}]
+ ) when is_atom(V) ->
+ handle_pref_alg(Key, [{client2server,Vs},{server2client,Vs}], Sup);
+
+handle_pref_alg(Key,
+ Vs=[V|_],
+ Sup=[S|_]
+ ) when is_atom(V), is_atom(S) ->
+ chk_alg_vs(Key, Vs, Sup),
+ {Key, Vs};
+
+handle_pref_alg(Key, Vs, _) ->
+ throw({error, {{eoptions, {preferred_algorithms,[{Key,Vs}]}}, "Badly formed list"}}).
+
+chk_alg_vs(OptKey, Values, SupportedValues) ->
+ case (Values -- SupportedValues) of
+ [] -> Values;
+ Bad -> throw({error, {{eoptions, {OptKey,Bad}}, "Unsupported value(s) found"}})
+ end.
+
handle_ip(Inet) -> %% Default to ipv4
case lists:member(inet, Inet) of
true ->
@@ -503,4 +764,74 @@ handle_ip(Inet) -> %% Default to ipv4
[inet | Inet]
end
end.
-
+
+check_dir({_,Dir} = Opt) ->
+ case directory_exist_readable(Dir) of
+ ok ->
+ Opt;
+ {error,Error} ->
+ throw({error, {eoptions,{Opt,Error}}})
+ end.
+
+directory_exist_readable(Dir) ->
+ case file:read_file_info(Dir) of
+ {ok, #file_info{type = directory,
+ access = Access}} ->
+ case Access of
+ read -> ok;
+ read_write -> ok;
+ _ -> {error, eacces}
+ end;
+
+ {ok, #file_info{}}->
+ {error, enotdir};
+
+ {error, Error} ->
+ {error, Error}
+ end.
+
+
+
+collect_per_size(L) ->
+ lists:foldr(
+ fun({Sz,GP}, [{Sz,GPs}|Acc]) -> [{Sz,[GP|GPs]}|Acc];
+ ({Sz,GP}, Acc) -> [{Sz,[GP]}|Acc]
+ end, [], lists:sort(L)).
+
+read_moduli_file(D, I, Acc) ->
+ case io:get_line(D,"") of
+ {error,Error} ->
+ {error,Error};
+ eof ->
+ {ok, Acc};
+ "#" ++ _ -> read_moduli_file(D, I+1, Acc);
+ <<"#",_/binary>> -> read_moduli_file(D, I+1, Acc);
+ Data ->
+ Line = if is_binary(Data) -> binary_to_list(Data);
+ is_list(Data) -> Data
+ end,
+ try
+ [_Time,_Type,_Tests,_Tries,Size,G,P] = string:tokens(Line," \r\n"),
+ M = {list_to_integer(Size),
+ {list_to_integer(G), list_to_integer(P,16)}
+ },
+ read_moduli_file(D, I+1, [M|Acc])
+ catch
+ _:_ ->
+ read_moduli_file(D, I+1, Acc)
+ end
+ end.
+
+handle_user_pref_pubkey_algs([], Acc) ->
+ {true, lists:reverse(Acc)};
+handle_user_pref_pubkey_algs([H|T], Acc) ->
+ case lists:member(H, ?SUPPORTED_USER_KEYS) of
+ true ->
+ handle_user_pref_pubkey_algs(T, [H| Acc]);
+
+ false when H==ssh_dsa -> handle_user_pref_pubkey_algs(T, ['ssh-dss'| Acc]);
+ false when H==ssh_rsa -> handle_user_pref_pubkey_algs(T, ['ssh-rsa'| Acc]);
+
+ false ->
+ false
+ end.