diff options
-rw-r--r-- | lib/ssl/src/Makefile | 3 | ||||
-rw-r--r-- | lib/ssl/src/inet_tls_dist.erl | 2 | ||||
-rw-r--r-- | lib/ssl/src/ssl.app.src | 1 | ||||
-rw-r--r-- | lib/ssl/src/ssl_tls_dist_ctrl.erl | 405 |
4 files changed, 2 insertions, 409 deletions
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index f6fe23b584..8eba5cf347 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -87,8 +87,7 @@ MODULES= \ ssl_v2 \ ssl_v3 \ tls_v1 \ - dtls_v1 \ - ssl_tls_dist_ctrl + dtls_v1 INTERNAL_HRL_FILES = \ ssl_alert.hrl ssl_cipher.hrl \ diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index 72cf73e79c..131c3b56a5 100644 --- a/lib/ssl/src/inet_tls_dist.erl +++ b/lib/ssl/src/inet_tls_dist.erl @@ -294,7 +294,7 @@ wait_for_code_server() -> %% unloaded, and the function call that triggered loading it fails %% with 'undef', which is rather confusing. %% - %% Thus, the ssl_tls_dist_ctrl process will terminate, and be + %% Thus, the accept process will terminate, and be %% restarted by ssl_dist_sup. However, it won't have any memory %% of being asked by net_kernel to listen for incoming %% connections. Hence, the node will believe that it's open for diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index a5286b6747..a75193a008 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -37,7 +37,6 @@ %% Erlang Distribution over SSL/TLS inet_tls_dist, inet6_tls_dist, - ssl_tls_dist_ctrl, ssl_dist_sup, ssl_dist_connection_sup, ssl_dist_admin_sup, diff --git a/lib/ssl/src/ssl_tls_dist_ctrl.erl b/lib/ssl/src/ssl_tls_dist_ctrl.erl deleted file mode 100644 index 1504d1c1e4..0000000000 --- a/lib/ssl/src/ssl_tls_dist_ctrl.erl +++ /dev/null @@ -1,405 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2017. All Rights Reserved. -%% -%% 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 -%% -%% 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% -%% --module(ssl_tls_dist_ctrl). - --include_lib("kernel/include/dist.hrl"). --include_lib("kernel/include/dist_util.hrl"). --include_lib("kernel/include/net_address.hrl"). - --export([start/1, get_socket/1, set_supervisor/2, hs_data_common/1]). - -%%% ------------------------------------------------------------ - -%% In order to avoid issues with lingering signal binaries -%% we enable off-heap message queue data as well as fullsweep -%% after 0. The fullsweeps will be cheap since we have more -%% or less no live data. -common_spawn_opts() -> - [{message_queue_data, off_heap}, - {fullsweep_after, 0}]. - -%%% ------------------------------------------------------------ - -start(SslSocket) -> - spawn_opt( - fun () -> - setup(SslSocket) - end, - [{priority, max}] ++ common_spawn_opts()). - -get_socket(DistCtrl) -> - call(DistCtrl, get_socket). - -set_supervisor(DistCtrl, Pid) -> - call(DistCtrl, {set_supervisor, Pid}). - -hs_data_common(DistCtrl) -> - TickHandler = call(DistCtrl, tick_handler), - SslSocket = get_socket(DistCtrl), - #hs_data{ - f_send = - fun (Ctrl, Packet) when Ctrl == DistCtrl -> - call(Ctrl, {send, Packet}) - end, - f_recv = - fun (Ctrl, Length, Timeout) when Ctrl == DistCtrl -> - case call(Ctrl, {recv, Length, Timeout}) of - {ok, Bin} when is_binary(Bin) -> - {ok, binary_to_list(Bin)}; - Other -> - Other - end - end, - f_setopts_pre_nodeup = - fun (Ctrl) when Ctrl == DistCtrl -> - call(Ctrl, pre_nodeup) - end, - f_setopts_post_nodeup = - fun (Ctrl) when Ctrl == DistCtrl -> - call(Ctrl, post_nodeup) - end, - f_getll = - fun (Ctrl) when Ctrl == DistCtrl -> - call(Ctrl, getll) - end, - f_handshake_complete = - fun (Ctrl, Node, DHandle) when Ctrl == DistCtrl -> - call(Ctrl, {handshake_complete, Node, DHandle}) - end, - f_address = - fun (Ctrl, Node) when Ctrl == DistCtrl -> - case call(Ctrl, {check_address, Node}) of - {error, no_node} -> - %% No '@' or more than one '@' in node name. - ?shutdown(no_node); - Res -> - Res - end - end, - mf_setopts = - fun (Ctrl, Opts) when Ctrl == DistCtrl -> - case setopts_filter(Opts) of - [] -> - ssl:setopts(SslSocket, Opts); - Opts1 -> - {error, {badopts,Opts1}} - end - end, - mf_getopts = - fun (Ctrl, Opts) when Ctrl == DistCtrl -> - ssl:getopts(SslSocket, Opts) - end, - mf_getstat = - fun (Ctrl) when Ctrl == DistCtrl -> - case ssl:getstat( - SslSocket, [recv_cnt, send_cnt, send_pend]) of - {ok, Stat} -> - split_stat(Stat,0,0,0); - Error -> - Error - end - end, - mf_tick = - fun (Ctrl) when Ctrl == DistCtrl -> - TickHandler ! tick - end}. - -%%% ------------------------------------------------------------ - -call(DistCtrl, Msg) -> - Ref = erlang:monitor(process, DistCtrl), - DistCtrl ! {Ref, self(), Msg}, - receive - {Ref, Res} -> - erlang:demonitor(Ref, [flush]), - Res; - {'DOWN', Ref, process, DistCtrl, Reason} -> - exit({dist_controller_exit, Reason}) - end. - -setopts_filter(Opts) -> - [Opt || {K,_} = Opt <- Opts, - K =:= active orelse K =:= deliver orelse K =:= packet]. - -split_stat([{recv_cnt, R}|Stat], _, W, P) -> - split_stat(Stat, R, W, P); -split_stat([{send_cnt, W}|Stat], R, _, P) -> - split_stat(Stat, R, W, P); -split_stat([{send_pend, P}|Stat], R, W, _) -> - split_stat(Stat, R, W, P); -split_stat([], R, W, P) -> - {ok, R, W, P}. - -%%% ------------------------------------------------------------ -%%% Distribution controller processes -%%% ------------------------------------------------------------ - -%% -%% There will be five parties working together when the -%% connection is up: -%% - The SSL socket. Providing a TLS connection -%% to the other node. -%% - The output handler. It will dispatch all outgoing -%% traffic from the VM to the gen_tcp socket. This -%% process is registered as distribution controller -%% for this channel with the VM. -%% - The input handler. It will dispatch all incoming -%% traffic from the gen_tcp socket to the VM. This -%% process is also the socket owner and receives -%% incoming traffic using active-N. -%% - The tick handler. Dispatches asynchronous tick -%% requests to the socket. It executes on max priority -%% since it is important to get ticks through to the -%% other end. -%% - The channel supervisor (provided by dist_util). It -%% monitors traffic. Issue tick requests to the tick -%% handler when no outgoing traffic is seen and bring -%% the connection down if no incoming traffic is seen. -%% This process also executes on max priority. -%% -%% These parties are linked togheter so should one -%% of them fail, all of them are terminated and the -%% connection is taken down. -%% - - -%% -%% The tick handler process writes a tick to the -%% socket when it receives a 'tick' message from -%% the connection supervisor. -%% -%% We are not allowed to block the connection -%% superviser when writing a tick and we also want -%% the tick to go through even during a heavily -%% loaded system. gen_tcp does not have a -%% non-blocking send operation exposed in its API -%% and we don't want to run the distribution -%% controller under high priority. Therefore this -%% separate process with max prio that dispatches -%% ticks. -%% -tick_handler(SslSocket) -> - receive - tick -> - %% May block due to busy port... - sock_send(SslSocket, ""), - flush_ticks(SslSocket); - _ -> - tick_handler(SslSocket) - end. - -flush_ticks(SslSocket) -> - receive - tick -> - flush_ticks(SslSocket) - after 0 -> - tick_handler(SslSocket) - end. - -setup(SslSocket) -> - TickHandler = - spawn_opt( - fun () -> - tick_handler(SslSocket) - end, - [link, {priority, max}] ++ common_spawn_opts()), - setup_loop(SslSocket, TickHandler, undefined). - -%% -%% During the handshake phase we loop in setup(). -%% When the connection is up we spawn an input handler and -%% continue as output handler. -%% -setup_loop(SslSocket, TickHandler, Sup) -> - receive - {ssl_closed, SslSocket} -> - exit(connection_closed); - {ssl_error, SslSocket} -> - exit(connection_closed); - - {Ref, From, {set_supervisor, Pid}} -> - Res = link(Pid), - From ! {Ref, Res}, - setup_loop(SslSocket, TickHandler, Pid); - - {Ref, From, tick_handler} -> - From ! {Ref, TickHandler}, - setup_loop(SslSocket, TickHandler, Sup); - - {Ref, From, get_socket} -> - From ! {Ref, SslSocket}, - setup_loop(SslSocket, TickHandler, Sup); - - {Ref, From, {send, Packet}} -> - Res = ssl:send(SslSocket, Packet), - From ! {Ref, Res}, - setup_loop(SslSocket, TickHandler, Sup); - - {Ref, From, {recv, Length, Timeout}} -> - Res = ssl:recv(SslSocket, Length, Timeout), - From ! {Ref, Res}, - setup_loop(SslSocket, TickHandler, Sup); - - {Ref, From, {check_address, Node}} -> - Res = - case ssl:peername(SslSocket) of - {ok, Address} -> - case inet_tls_dist:split_node(Node) of - false -> - {error, no_node}; - Host -> - #net_address{ - address=Address, host=Host, - protocol=tls, family=inet} - end - end, - From ! {Ref, Res}, - setup_loop(SslSocket, TickHandler, Sup); - - {Ref, From, pre_nodeup} -> - Res = - ssl:setopts( - SslSocket, - [{packet, 4}, inet_tls_dist:nodelay()]), - From ! {Ref, Res}, - setup_loop(SslSocket, TickHandler, Sup); - - {Ref, From, post_nodeup} -> - Res = - ssl:setopts( - SslSocket, - [{packet, 4}, inet_tls_dist:nodelay()]), - From ! {Ref, Res}, - setup_loop(SslSocket, TickHandler, Sup); - - {Ref, From, getll} -> - From ! {Ref, {ok, self()}}, - setup_loop(SslSocket, TickHandler, Sup); - - {Ref, From, {handshake_complete, _Node, DHandle}} -> - From ! {Ref, ok}, - %% Handshake complete! Begin dispatching traffic... - %% - %% Use a dedicated input process to push the - %% input-output-flow-control-deadlock problem - %% to the SSL implementation. - InputHandler = - spawn_opt( - fun () -> - link(Sup), - ssl:setopts(SslSocket, [{active, once}]), - receive - DHandle -> - input_loop(DHandle, SslSocket) - end - end, - [link] ++ common_spawn_opts()), - ok = ssl:controlling_process(SslSocket, InputHandler), - ok = erlang:dist_ctrl_input_handler(DHandle, InputHandler), - InputHandler ! DHandle, - %% - %% From now on we execute on normal priority - process_flag(priority, normal), - erlang:dist_ctrl_get_data_notification(DHandle), - output_loop(DHandle, SslSocket) - end. - -input_loop(DHandle, SslSocket) -> - receive - {ssl_closed, SslSocket} -> - %% Connection to remote node terminated... - exit(connection_closed); - {ssl_error, SslSocket, _Reason} -> - %% Connection to remote node terminated... - exit(connection_closed); - {ssl, SslSocket, Data} -> - %% Incoming data from remote node... - ok = ssl:setopts(SslSocket, [{active, once}]), - try erlang:dist_ctrl_put_data(DHandle, Data) - catch _:_ -> death_row() - end, - input_loop(DHandle, SslSocket); - _ -> - %% Drop garbage message... - input_loop(DHandle, SslSocket) - end. - -output_loop(DHandle, SslSocket) -> - receive - dist_data -> - %% Outgoing data from this node... - try send_data(DHandle, SslSocket) - catch _ : _ -> death_row() - end, - output_loop(DHandle, SslSocket); - {send, From, Ref, Data} -> - %% This is for testing only! - %% - %% Needed by some OTP distribution - %% test suites... - sock_send(SslSocket, Data), - From ! {Ref, ok}, - output_loop(DHandle, SslSocket); - _ -> - %% Drop garbage message... - output_loop(DHandle, SslSocket) - end. - -send_data(DHandle, SslSocket) -> - case erlang:dist_ctrl_get_data(DHandle) of - none -> - erlang:dist_ctrl_get_data_notification(DHandle); - Data -> - sock_send(SslSocket, Data), - send_data(DHandle, SslSocket) - end. - -sock_send(SslSocket, Data) -> - try ssl:send(SslSocket, Data) of - ok -> ok; - {error, Reason} -> - death_row({send_error, Reason}) - catch - Type:Reason -> - death_row({send_error, {Type, Reason}}) - end. - --spec death_row() -> no_return(). -death_row() -> - death_row(connection_closed). - --spec death_row(term()) -> no_return(). -%% death_row(normal) -> -%% %% We do not want to exit with normal -%% %% exit reason since it wont bring down -%% %% linked processes... -%% death_row(); -death_row(Reason) -> - %% When the connection is on its way down operations - %% begin to fail. We catch the failures and call - %% this function waiting for termination. We should - %% be terminated by one of our links to the other - %% involved parties that began bringing the - %% connection down. By waiting for termination we - %% avoid altering the exit reason for the connection - %% teardown. We however limit the wait to 5 seconds - %% and bring down the connection ourselves if not - %% terminated... - receive after 5000 -> exit(Reason) end. |