diff options
author | Raimo Niskanen <raimo@erlang.org> | 2023-01-30 12:16:26 +0100 |
---|---|---|
committer | Raimo Niskanen <raimo@erlang.org> | 2023-02-09 15:33:37 +0100 |
commit | 30a57b34f77a6a80afd4482e3fccd28a73c10c9c (patch) | |
tree | 3c17da29e603cc084fd5d68ff0596516bf8c57f7 /lib/ssl/test/inet_epmd_dist_cryptcookie_socket.erl | |
parent | 01d4b6947c23c0edec86b299b3a5825d94a51f80 (diff) | |
download | erlang-30a57b34f77a6a80afd4482e3fccd28a73c10c9c.tar.gz |
Clean up module naming
Diffstat (limited to 'lib/ssl/test/inet_epmd_dist_cryptcookie_socket.erl')
-rw-r--r-- | lib/ssl/test/inet_epmd_dist_cryptcookie_socket.erl | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/lib/ssl/test/inet_epmd_dist_cryptcookie_socket.erl b/lib/ssl/test/inet_epmd_dist_cryptcookie_socket.erl new file mode 100644 index 0000000000..1e7470b054 --- /dev/null +++ b/lib/ssl/test/inet_epmd_dist_cryptcookie_socket.erl @@ -0,0 +1,240 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2023. 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 for dist_cryptcookie over socket +%% +-module(inet_epmd_dist_cryptcookie_socket). +-feature(maybe_expr, enable). + +%% DistMod API +-export([net_address/0, listen_open/2, listen_port/3, listen_close/1, + accept_open/2, accept_controller/3, accepted/3, + connect/3]). + +-export([supported/0]). + +%% Socket I/O Stream internal exports (export entry fun()s) +-export([stream_recv/2, stream_send/2, + stream_controlling_process/2]). + +-include_lib("kernel/include/net_address.hrl"). +-include_lib("kernel/include/dist.hrl"). +-include_lib("kernel/include/dist_util.hrl"). + +-define(FAMILY, inet). + +%% ------------------------------------------------------------ +net_address() -> + #net_address{ + protocol = dist_cryptcookie:protocol(), + family = ?FAMILY }. + +%% ------------------------------------------------------------ +listen_open(#net_address{ family = Family}, ListenOptions) -> + maybe + Key = backlog, + Default = 128, + Backlog = proplists:get_value(Key, ListenOptions, Default), + {ok, ListenSocket} ?= + socket:open(Family, stream), + ok ?= + setopts( + ListenSocket, + inet_epmd_dist:merge_options( + ListenOptions, [inet_epmd_dist:nodelay()], [])), + {ok, {ListenSocket, Backlog}} + else + {error, _} = Error -> + Error + end. + +setopts(Socket, Options) -> + gen_tcp_socket:socket_setopts(Socket, Options). + +%% ------------------------------------------------------------ +listen_port( + #net_address{ family = Family }, Port, {ListenSocket, Backlog}) -> + maybe + Sockaddr = + #{family => Family, + addr => any, + port => Port}, + ok ?= + socket:bind(ListenSocket, Sockaddr), + ok ?= + socket:listen(ListenSocket, Backlog), + {ok, #{ addr := Ip, port := ListenPort}} ?= + socket:sockname(ListenSocket), + {ok, {ListenSocket, {Ip, ListenPort}}} + else + {error, _} = Error -> + Error + end. + +%% ------------------------------------------------------------ +listen_close(ListenSocket) -> + socket:close(ListenSocket). + +%% ------------------------------------------------------------ +accept_open(_NetAddress, ListenSocket) -> + maybe + {ok, Socket} ?= + socket:accept(ListenSocket), + {ok, #{ addr := Ip }} ?= + socket:sockname(Socket), + {ok, #{ addr := PeerIp, port := PeerPort }} ?= + socket:peername(Socket), + inet_epmd_dist:check_ip(Ip, PeerIp), + inet_epmd_dist:wait_for_code_server([crypto]), + Stream = stream(Socket), + DistCtrlHandle = dist_cryptcookie:start_dist_ctrl(Stream), + {DistCtrlHandle, {PeerIp, PeerPort}} + else + {error, Reason} -> + exit({?FUNCTION_NAME, Reason}) + end. + +%% ------------------------------------------------------------ +accept_controller(_NetAddress, Controller, DistCtrlHandle) -> + dist_cryptcookie:controlling_process(DistCtrlHandle, Controller). + +%% ------------------------------------------------------------ +accepted(NetAddress, _Timer, DistCtrlHandle) -> + dist_cryptcookie:hs_data(NetAddress, DistCtrlHandle). + +%% ------------------------------------------------------------ +connect( + #net_address{ address = {Ip, Port}, family = Family } = NetAddress, + _Timer, ConnectOptions) -> + maybe + {ok, Socket} ?= + socket:open(Family, stream), + ok ?= + setopts( + Socket, + inet_epmd_dist:merge_options( + ConnectOptions, [inet_epmd_dist:nodelay()], [])), + ConnectAddress = + #{ family => Family, + addr => Ip, + port => Port }, + ok ?= + socket:connect(Socket, ConnectAddress), + Stream = stream(Socket), + DistCtrlHandle = dist_cryptcookie:start_dist_ctrl(Stream), + dist_cryptcookie:hs_data(NetAddress, DistCtrlHandle) + else + {error, _} = Error -> + Error + end. + +%% ------------------------------------------------------------ +%% A socket as an I/O Stream +%% +%% Stream :: {InStream, OutStream, ControllingProcessFun}. +%% +%% InStream :: [InFun | InState]. +%% InFun :: fun (InStream, Size) -> +%% [Data | NewInStream] | +%% [closed | DebugTerm] +%% NewInStream :: InStream +%% %% If Size == 0 and there is no pending input data; +%% %% return immediately with empty Data, +%% %% otherwise wait for Size bytes of data +%% %% or any amount of data > 0 +%% +%% OutStream :: [OutFun | OutState] +%% OutFun :: fun (OutStream, Data) -> +%% NewOutStream | +%% [closed | DebugTerm] +%% NewOutStream :: OutStream +%% +%% Data :: binary() or list(binary()) +%% +%% ControllingProcessFun :: fun (Stream, pid()) -> NewStream +%% +%% NewSTream :: Stream + +stream(Socket) -> + {stream_in(Socket), stream_out(Socket), + fun ?MODULE:stream_controlling_process/2}. + +stream_in(Socket) -> + [fun ?MODULE:stream_recv/2 | Socket]. + +stream_recv(InStream = [_ | Socket], Size) -> + case + if + Size =:= 0 -> + socket:recv(Socket, 0, 0); + true -> + socket:recv(Socket, Size, infinity) + end + of + {ok, Data} -> + [Data | InStream]; + {error, {Reason, _Data}} -> + stream_recv_error(InStream, Reason); + {error, timeout} -> + [<<>> | InStream]; + {error, Reason} -> + stream_recv_error(InStream, Reason) + end. + +stream_recv_error(InStream, Reason) -> + if + Reason =:= closed; + Reason =:= econnreset -> + [closed | InStream]; + true -> + erlang:error({?MODULE, ?FUNCTION_NAME, Reason}) + end. + +stream_out(Socket) -> + [fun ?MODULE:stream_send/2 | Socket]. + +stream_send(OutStream, Bin) when is_binary(Bin) -> + stream_send(OutStream, [Bin]); +stream_send(OutStream = [_ | Socket], Data) -> + case socket:sendmsg(Socket, #{ iov => Data }) of + ok -> + OutStream; + {error, closed} -> + [closed | OutStream]; + {error, Reason} -> + erlang:error({?MODULE, ?FUNCTION_NAME, Reason, [OutStream, Data]}) + end. + +stream_controlling_process(Stream = {_, [_ | Socket], _}, Pid) -> + %% + case socket:setopt(Socket, {otp,controlling_process}, Pid) of + ok -> + Stream; + {error, Reason} -> + erlang:error({?MODULE, ?FUNCTION_NAME, Reason}) + end. + +%% ------------------------------------------------------------ +supported() -> + maybe + ok ?= inet_epmd_socket:supported(), + dist_cryptcookie:supported() + end. |