File 2387-ssh-handle-HostAddr-arg-and-ip-option-for-daemons.patch of Package erlang

From e20ce5b9174e5ac0e1279a1af5be80f9c1b35caa Mon Sep 17 00:00:00 2001
From: Hans Nilsson <hans@erlang.org>
Date: Tue, 21 Mar 2017 15:21:46 +0100
Subject: [PATCH 07/13] ssh: handle HostAddr arg and ip-option for daemons

---
 lib/ssh/src/ssh.erl | 221 +++++++++++++++++++++++++++++-----------------------
 1 file changed, 124 insertions(+), 97 deletions(-)

diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index c13955679..ff424b738 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.
-- 
2.12.2

openSUSE Build Service is sponsored by