summaryrefslogtreecommitdiff
path: root/lib/ssh/src/ssh.erl
diff options
context:
space:
mode:
authorHans Nilsson <hans@erlang.org>2017-03-21 15:21:46 +0100
committerHans Nilsson <hans@erlang.org>2017-04-07 10:19:56 +0200
commite20ce5b9174e5ac0e1279a1af5be80f9c1b35caa (patch)
tree1a0e40d439ad893cc43c00112c98406d3b9d7c8a /lib/ssh/src/ssh.erl
parentda7902412f1e77b8241c0bacbeac2d6013e8f345 (diff)
downloaderlang-e20ce5b9174e5ac0e1279a1af5be80f9c1b35caa.tar.gz
ssh: handle HostAddr arg and ip-option for daemons
Diffstat (limited to 'lib/ssh/src/ssh.erl')
-rw-r--r--lib/ssh/src/ssh.erl221
1 files changed, 124 insertions, 97 deletions
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index c139556791..ff424b738c 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -182,16 +182,86 @@ daemon(Port) ->
daemon(Port, []).
-daemon(Port, UserOptions) when is_integer(Port), Port >= 0 ->
- daemon(any, Port, UserOptions);
-
daemon(Socket, UserOptions) when is_port(Socket) ->
- daemon(socket, Socket, UserOptions).
+ try
+ #{} = Options = ssh_options:handle_options(server, UserOptions),
+ case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
+ ok ->
+ {ok, {IP,Port}} = inet:sockname(Socket),
+ finalize_start(fmt_host(IP), Port, ?GET_OPT(profile, Options),
+ ?PUT_INTERNAL_OPT({connected_socket, Socket}, Options),
+ fun(Opts, DefaultResult) ->
+ try ssh_acceptor:handle_established_connection(
+ ?GET_INTERNAL_OPT(address, Opts),
+ ?GET_INTERNAL_OPT(port, Opts),
+ Opts,
+ Socket)
+ of
+ {error,Error} ->
+ {error,Error};
+ _ ->
+ DefaultResult
+ catch
+ C:R ->
+ {error,{could_not_start_connection,{C,R}}}
+ end
+ end);
+ {error,SockError} ->
+ {error,SockError}
+ end
+ catch
+ throw:bad_fd ->
+ {error,bad_fd};
+ throw:bad_socket ->
+ {error,bad_socket};
+ error:{badmatch,{error,Error}} ->
+ {error,Error};
+ error:Error ->
+ {error,Error};
+ _C:_E ->
+ {error,{cannot_start_daemon,_C,_E}}
+ end;
+
+daemon(Port, UserOptions) when 0 =< Port, Port =< 65535 ->
+ daemon(any, Port, UserOptions).
+
+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(fmt_host(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)
+ catch
+ throw:bad_fd ->
+ {error,bad_fd};
+ throw:bad_socket ->
+ {error,bad_socket};
+ error:{badmatch,{error,Error}} ->
+ {error,Error};
+ error:Error ->
+ {error,Error};
+ _C:_E ->
+ {error,{cannot_start_daemon,_C,_E}}
+ end.
-daemon(Host0, Port, UserOptions0) ->
- {Host, UserOptions} = handle_daemon_args(Host0, UserOptions0),
- start_daemon(Host, Port, ssh_options:handle_options(server, UserOptions)).
%%--------------------------------------------------------------------
-spec daemon_info(daemon_ref()) -> ok_error( [{atom(), term()}] ).
@@ -291,21 +361,49 @@ default_algorithms() ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-handle_daemon_args(Host, UserOptions0) ->
- case Host of
- socket ->
- {Host, UserOptions0};
- any ->
- {ok, Host0} = inet:gethostname(),
- Inet = proplists:get_value(inet, UserOptions0, inet),
- {Host0, [Inet | UserOptions0]};
- {_,_,_,_} ->
- {Host, [inet, {ip,Host} | UserOptions0]};
- {_,_,_,_,_,_,_,_} ->
- {Host, [inet6, {ip,Host} | UserOptions0]};
- _ ->
- error(badarg)
- end.
+handle_daemon_args(HostAddr, Opts) ->
+ IP = proplists:get_value(ip, Opts),
+ IPh = case inet:parse_strict_address(HostAddr) of
+ {ok, IPtuple} -> IPtuple;
+ {error, einval} when is_tuple(HostAddr),
+ size(HostAddr)==4 ; size(HostAddr)==6 -> HostAddr;
+ _ -> undefined
+ end,
+ handle_daemon_args(HostAddr, IPh, IP, Opts).
+
+
+%% HostAddr is 'any'
+handle_daemon_args(any, undefined, undefined, Opts) -> {any, Opts};
+handle_daemon_args(any, undefined, IP, Opts) -> {IP, Opts};
+
+%% HostAddr is 'loopback' or "localhost"
+handle_daemon_args(loopback, undefined, {127,_,_,_}=IP, Opts) -> {IP, Opts};
+handle_daemon_args(loopback, undefined, {0,0,0,0,0,0,0,1}=IP, Opts) -> {IP, Opts};
+handle_daemon_args(loopback, undefined, undefined, Opts) ->
+ IP = case proplists:get_value(inet,Opts) of
+ true -> {127,0,0,1};
+ inet -> {127,0,0,1};
+ inet6 -> {0,0,0,0,0,0,0,1};
+ _ -> case proplists:get_value(inet6,Opts) of
+ true -> {0,0,0,0,0,0,0,1};
+ _ -> {127,0,0,1} % default if no 'inet' nor 'inet6'
+ end
+ end,
+ {IP, [{ip,IP}|Opts]};
+handle_daemon_args("localhost", IPh, IP, Opts) ->
+ handle_daemon_args(loopback, IPh, IP, Opts);
+
+%% HostAddr is ip and no ip-option
+handle_daemon_args(_, IP, undefined, Opts) when is_tuple(IP) -> {IP, [{ip,IP}|Opts]};
+
+%% HostAddr and ip-option are equal
+handle_daemon_args(_, IP, IP, Opts) when is_tuple(IP) -> {IP, Opts};
+
+%% HostAddr is ip, but ip-option is different!
+handle_daemon_args(_, IPh, IPo, _) when is_tuple(IPh), is_tuple(IPo) -> error({eoption,{ip,IPo}});
+
+%% Something else. Whatever it is, it is wrong.
+handle_daemon_args(_, _, _, _) -> error(badarg).
%%%----------------------------------------------------------------
valid_socket_to_use(Socket, {tcp,_,_}) ->
@@ -332,97 +430,25 @@ is_tcp_socket(Socket) ->
end.
%%%----------------------------------------------------------------
-start_daemon(_, _, {error,Error}) ->
- {error,Error};
-
-start_daemon(socket, Socket, Options) ->
- case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
- ok ->
- start_daemon(inet:sockname(Socket), Socket, Options);
- {error,SockError} ->
- {error,SockError}
- end;
-
-start_daemon(Host, Port, Options) ->
- try
- do_start_daemon(Host, Port, Options)
- catch
- throw:bad_fd ->
- {error,bad_fd};
- throw:bad_socket ->
- {error,bad_socket};
- error:{badmatch,{error,Error}} ->
- {error,Error};
- _C:_E ->
- {error,{cannot_start_daemon,_C,_E}}
- end.
-
-%%%----------------------------------------------------------------
-do_start_daemon({error,Error}, _, _) ->
- {error,Error};
-
-do_start_daemon({ok, {IP,Port}}, Socket, Options0) ->
- finalize_start(fmt_host(IP),
- Port,
- ?PUT_INTERNAL_OPT({connected_socket, Socket}, Options0),
- fun(Opts, DefaultResult) ->
- try ssh_acceptor:handle_established_connection(
- ?GET_INTERNAL_OPT(address, Opts),
- ?GET_INTERNAL_OPT(port, Opts),
- Opts,
- Socket)
- of
- {error,Error} ->
- {error,Error};
- _ ->
- DefaultResult
- catch
- C:R ->
- {error,{could_not_start_connection,{C,R}}}
- end
- end);
-
-do_start_daemon(Host0, Port0, Options0) ->
- {{Host,Port}, ListenSocket} =
- open_listen_socket(Host0, 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,
- ?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(Host0, Port0, Options0) ->
case ?GET_SOCKET_OPT(fd, Options0) of
undefined ->
{ok,LSock} = ssh_acceptor:listen(Port0, Options0),
{ok,{_,LPort}} = inet:sockname(LSock),
- {{fmt_host(Host0),LPort}, LSock};
+ {{Host0,LPort}, LSock};
Fd when is_integer(Fd) ->
%% Do gen_tcp:listen with the option {fd,Fd}:
{ok,LSock} = ssh_acceptor:listen(0, Options0),
{ok,{LHost,LPort}} = inet:sockname(LSock),
- {{fmt_host(LHost),LPort}, LSock}
+ {{LHost,LPort}, LSock}
end.
%%%----------------------------------------------------------------
-finalize_start(Host, Port, Options0, F) ->
+finalize_start(Host, Port, Profile, Options0, F) ->
Options = ?PUT_INTERNAL_OPT([{address, Host},
{port, Port},
{role, server}], Options0),
- Profile = ?GET_OPT(profile, Options),
case ssh_system_sup:system_supervisor(Host, Port, Profile) of
undefined ->
try sshd_sup:start_child(Options) of
@@ -449,5 +475,6 @@ finalize_start(Host, Port, Options0, F) ->
end.
%%%----------------------------------------------------------------
+fmt_host(any) -> any;
fmt_host(IP) when is_tuple(IP) -> inet:ntoa(IP);
fmt_host(Str) when is_list(Str) -> Str.