diff options
Diffstat (limited to 'lib/ssl/src/ssl_gen_statem.erl')
-rw-r--r-- | lib/ssl/src/ssl_gen_statem.erl | 309 |
1 files changed, 202 insertions, 107 deletions
diff --git a/lib/ssl/src/ssl_gen_statem.erl b/lib/ssl/src/ssl_gen_statem.erl index eed0025ad7..12a4721a09 100644 --- a/lib/ssl/src/ssl_gen_statem.erl +++ b/lib/ssl/src/ssl_gen_statem.erl @@ -38,7 +38,8 @@ init/1]). %% TLS connection setup --export([ssl_config/3, +-export([init_ssl_config/3, + ssl_config/3, connect/8, handshake/7, handshake/2, @@ -60,7 +61,8 @@ set_opts/2, peer_certificate/1, negotiated_protocol/1, - connection_information/2 + connection_information/2, + ktls_handover/1 ]). %% Erlang Distribution export @@ -96,6 +98,9 @@ %% Log handling -export([format_status/2]). +%% Tracing +-export([handle_trace/3]). + %%-------------------------------------------------------------------- %%% Initial Erlang process setup %%-------------------------------------------------------------------- @@ -106,7 +111,8 @@ %% Description: Creates a process which calls Module:init/1 to %% choose appropriat gen_statem and initialize. %%-------------------------------------------------------------------- -start_link(Role, Sender, Host, Port, Socket, {#{receiver_spawn_opts := ReceiverOpts}, _, _} = Options, User, CbInfo) -> +start_link(Role, Sender, Host, Port, Socket, {SslOpts, _, _} = Options, User, CbInfo) -> + ReceiverOpts = maps:get(receiver_spawn_opts, SslOpts, []), Opts = [link | proplists:delete(link, ReceiverOpts)], Pid = proc_lib:spawn_opt(?MODULE, init, [[Role, Sender, Host, Port, Socket, Options, User, CbInfo]], Opts), {ok, Pid}. @@ -118,7 +124,8 @@ start_link(Role, Sender, Host, Port, Socket, {#{receiver_spawn_opts := ReceiverO %% Description: Creates a gen_statem process which calls Module:init/1 to %% initialize. %%-------------------------------------------------------------------- -start_link(Role, Host, Port, Socket, {#{receiver_spawn_opts := ReceiverOpts}, _, _} = Options, User, CbInfo) -> +start_link(Role, Host, Port, Socket, {SslOpts, _, _} = Options, User, CbInfo) -> + ReceiverOpts = maps:get(receiver_spawn_opts, SslOpts, []), Opts = [link | proplists:delete(link, ReceiverOpts)], Pid = proc_lib:spawn_opt(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]], Opts), {ok, Pid}. @@ -128,30 +135,51 @@ start_link(Role, Host, Port, Socket, {#{receiver_spawn_opts := ReceiverOpts}, _, -spec init(list()) -> no_return(). %% Description: Initialization %%-------------------------------------------------------------------- -init([_Role, _Sender, _Host, _Port, _Socket, {#{erl_dist := ErlDist} = TLSOpts, _, _}, _User, _CbInfo] = InitArgs) -> +init([Role, _Sender, _Host, _Port, _Socket, {TLSOpts, _, _}, _User, _CbInfo] = InitArgs) -> process_flag(trap_exit, true), - case ErlDist of + case maps:get(erl_dist, TLSOpts, false) of true -> process_flag(priority, max); _ -> ok end, - ConnectionFsm = tls_connection_fsm(TLSOpts), - ConnectionFsm:init(InitArgs); -init([_Role, _Host, _Port, _Socket, {TLSOpts, _, _}, _User, _CbInfo] = InitArgs) -> + case {Role, TLSOpts} of + {?CLIENT_ROLE, #{versions := [?TLS_1_3]}} -> + tls_client_connection_1_3:init(InitArgs); + {?SERVER_ROLE, #{versions := [?TLS_1_3]}} -> + tls_server_connection_1_3:init(InitArgs); + {_,_} -> + tls_connection:init(InitArgs) + end; +init([_Role, _Host, _Port, _Socket, _TLSOpts, _User, _CbInfo] = InitArgs) -> process_flag(trap_exit, true), - ConnectionFsm = dtls_connection_fsm(TLSOpts), - ConnectionFsm:init(InitArgs). + dtls_connection:init(InitArgs). %%==================================================================== %% TLS connection setup %%==================================================================== %%-------------------------------------------------------------------- +-spec init_ssl_config(ssl_options(), client | server, #state{}) -> #state{}. +%%-------------------------------------------------------------------- +init_ssl_config(Opts, Role, #state{ssl_options = #{handshake := Handshake}, + handshake_env = HsEnv} = State0) -> + ContinueStatus = case Handshake of + hello -> + %% Will pause handshake after hello message to + %% enable user to react to hello extensions + pause; + full -> + Handshake + end, + ssl_config(Opts, Role, + State0#state{handshake_env = + HsEnv#handshake_env{continue_status = ContinueStatus}}). + +%%-------------------------------------------------------------------- -spec ssl_config(ssl_options(), client | server, #state{}) -> #state{}. %%-------------------------------------------------------------------- ssl_config(Opts, Role, #state{static_env = InitStatEnv0, - ssl_options = #{handshake := Handshake}, handshake_env = HsEnv, connection_env = CEnv} = State0) -> {ok, #{cert_db_ref := Ref, @@ -165,15 +193,6 @@ ssl_config(Opts, Role, #state{static_env = InitStatEnv0, TimeStamp = erlang:monotonic_time(), Session = State0#state.session, - ContinueStatus = case Handshake of - hello -> - %% Will pause handshake after hello message to - %% enable user to react to hello extensions - pause; - full -> - Handshake - end, - State0#state{session = Session#session{time_stamp = TimeStamp}, static_env = InitStatEnv0#static_env{ file_ref_db = FileRefHandle, @@ -182,8 +201,8 @@ ssl_config(Opts, Role, #state{static_env = InitStatEnv0, crl_db = CRLDbHandle, session_cache = CacheHandle }, - handshake_env = HsEnv#handshake_env{diffie_hellman_params = DHParams, - continue_status = ContinueStatus}, + handshake_env = + HsEnv#handshake_env{diffie_hellman_params = DHParams}, connection_env = CEnv#connection_env{cert_key_alts = CertKeyAlts}, ssl_options = Opts}. @@ -292,21 +311,23 @@ socket_control(Connection, Socket, Pid, Transport) -> -spec socket_control(tls_gen_connection | dtls_gen_connection, port(), [pid()], atom(), [pid()] | atom()) -> {ok, #sslsocket{}} | {error, reason()}. %%-------------------------------------------------------------------- -socket_control(dtls_gen_connection = Connection, Socket, Pids, Transport, udp_listener) -> +socket_control(dtls_gen_connection, Socket, Pids, Transport, udp_listener) -> %% dtls listener process must have the socket control - {ok, Connection:socket(Pids, Transport, Socket, undefined)}; + {ok, dtls_gen_connection:socket(Pids, Transport, Socket, undefined)}; -socket_control(tls_gen_connection = Connection, Socket, [Pid|_] = Pids, Transport, Trackers) -> +socket_control(tls_gen_connection, Socket, [Pid|_] = Pids, Transport, Trackers) -> case Transport:controlling_process(Socket, Pid) of ok -> - {ok, Connection:socket(Pids, Transport, Socket, Trackers)}; + {ok, tls_gen_connection:socket(Pids, Transport, Socket, Trackers)}; {error, Reason} -> {error, Reason} end; -socket_control(dtls_gen_connection = Connection, {PeerAddrPort, Socket}, [Pid|_] = Pids, Transport, Trackers) -> +socket_control(dtls_gen_connection, {PeerAddrPort, Socket}, + [Pid|_] = Pids, Transport, Trackers) -> case Transport:controlling_process(Socket, Pid) of ok -> - {ok, Connection:socket(Pids, Transport, {PeerAddrPort, Socket}, Trackers)}; + {ok, dtls_gen_connection:socket(Pids, Transport, {PeerAddrPort, Socket}, + Trackers)}; {error, Reason} -> {error, Reason} end. @@ -420,6 +441,14 @@ peer_certificate(ConnectionPid) -> negotiated_protocol(ConnectionPid) -> call(ConnectionPid, negotiated_protocol). +%%-------------------------------------------------------------------- +-spec ktls_handover(pid()) -> {ok, map()} | {error, reason()}. +%% +%% Description: Returns the negotiated protocol +%%-------------------------------------------------------------------- +ktls_handover(ConnectionPid) -> + call(ConnectionPid, ktls_handover). + dist_handshake_complete(ConnectionPid, DHandle) -> gen_statem:cast(ConnectionPid, {dist_handshake_complete, DHandle}). @@ -460,8 +489,6 @@ initial_hello({call, From}, {start, Timeout}, %% Versions is a descending list of supported versions. versions := [HelloVersion|_] = Versions, session_tickets := SessionTickets, - ocsp_stapling := OcspStaplingOpt, - ocsp_nonce := OcspNonceOpt, early_data := EarlyData} = SslOpts, session = Session, connection_states = ConnectionStates0 @@ -471,9 +498,9 @@ initial_hello({call, From}, {start, Timeout}, %% Update UseTicket in case of automatic session resumption. The automatic ticket handling %% also takes it into account if the ticket is suitable for sending early data not exceeding %% the max_early_data_size or if it can only be used for session resumption. - {UseTicket, State1} = tls_handshake_1_3:maybe_automatic_session_resumption(State0), + {UseTicket, State1} = tls_client_connection_1_3:maybe_automatic_session_resumption(State0), TicketData = tls_handshake_1_3:get_ticket_data(self(), SessionTickets, UseTicket), - OcspNonce = tls_handshake:ocsp_nonce(OcspNonceOpt, OcspStaplingOpt), + OcspNonce = tls_handshake:ocsp_nonce(SslOpts), Hello0 = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Session#session.session_id, Renegotiation, @@ -507,21 +534,24 @@ initial_hello({call, From}, {start, Timeout}, %% ServerHello is processed. RequestedVersion = tls_record:hello_version(Versions), - {Ref,Maybe} = tls_handshake_1_3:maybe(), + {Ref,Maybe} = tls_gen_connection_1_3:do_maybe(), try %% Send Early Data - State4 = Maybe(tls_handshake_1_3:maybe_send_early_data(State3)), + State4 = Maybe(tls_client_connection_1_3:maybe_send_early_data(State3)), {#state{handshake_env = HsEnv1} = State5, _} = Connection:send_handshake_flight(State4), + OcspStaplingKeyPresent = maps:is_key(ocsp_stapling, SslOpts), State = State5#state{ connection_env = CEnv#connection_env{ negotiated_version = RequestedVersion}, session = Session, - handshake_env = HsEnv1#handshake_env{ - ocsp_stapling_state = OcspState0#{ocsp_nonce => OcspNonce, - ocsp_stapling => OcspStaplingOpt}}, + handshake_env = + HsEnv1#handshake_env{ + ocsp_stapling_state = + OcspState0#{ocsp_nonce => OcspNonce, + ocsp_stapling => OcspStaplingKeyPresent}}, start_or_recv_from = From, key_share = KeyShare}, NextState = next_statem_state(Versions, Role), @@ -534,17 +564,17 @@ initial_hello({call, From}, {start, Timeout}, initial_hello({call, From}, {start, Timeout}, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, ssl_options = #{versions := Versions}} = State0) -> - - NextState = next_statem_state(Versions, Role), + + NextState = next_statem_state(Versions, Role), Connection:next_event(NextState, no_record, State0#state{start_or_recv_from = From}, [{{timeout, handshake}, Timeout, close}]); - + initial_hello({call, From}, {start, {Opts, EmOpts}, Timeout}, #state{static_env = #static_env{role = Role}, ssl_options = OrigSSLOptions, socket_options = SockOpts} = State0) -> try - SslOpts = ssl:handle_options(Opts, Role, OrigSSLOptions), + SslOpts = ssl:update_options(Opts, Role, OrigSSLOptions), State = ssl_config(SslOpts, Role, State0), initial_hello({call, From}, {start, Timeout}, State#state{ssl_options = SslOpts, @@ -646,6 +676,45 @@ connection({call, From}, {error, timeout} -> {stop_and_reply, {shutdown, downgrade_fail}, [{reply, From, {error, timeout}}]} end; +connection({call, From}, ktls_handover, #state{ + static_env = #static_env{ + transport_cb = Transport, + socket = Socket + }, + connection_env = #connection_env{ + user_application = {_Mon, Pid}, + negotiated_version = TlsVersion + }, + ssl_options = #{ktls := true}, + socket_options = SocketOpts, + connection_states = #{ + current_write := #{ + security_parameters := #security_parameters{cipher_suite = CipherSuite}, + cipher_state := WriteState, + sequence_number := WriteSeq + }, + current_read := #{ + cipher_state := ReadState, + sequence_number := ReadSeq + } + } +}) -> + Reply = case Transport:controlling_process(Socket, Pid) of + ok -> + {ok, #{ + socket => Socket, + tls_version => TlsVersion, + cipher_suite => CipherSuite, + socket_options => SocketOpts, + write_state => WriteState, + write_seq => WriteSeq, + read_state => ReadState, + read_seq => ReadSeq + }}; + {error, Reason} -> + {error, Reason} + end, + {stop_and_reply, {shutdown, ktls}, [{reply, From, Reply}]}; connection({call, From}, Msg, State) -> handle_call(Msg, From, ?FUNCTION_NAME, State); connection(cast, {dist_handshake_complete, DHandle}, @@ -953,8 +1022,9 @@ passive_receive(#state{user_data_buffer = {Front,BufferSize,Rear}, %%==================================================================== hibernate_after(connection = StateName, - #state{ssl_options= #{hibernate_after := HibernateAfter}} = State, + #state{ssl_options= SslOpts} = State, Actions) -> + HibernateAfter = maps:get(hibernate_after, SslOpts, infinity), {next_state, StateName, State, [{timeout, HibernateAfter, hibernate} | Actions]}; hibernate_after(StateName, State, Actions) -> {next_state, StateName, State, Actions}. @@ -1006,26 +1076,8 @@ handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = Pids = Connection:pids(State), alert_user(Pids, Transport, Trackers, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, StateName, Connection). -handle_alert(#alert{level = ?FATAL} = Alert0, StateName, - #state{static_env = #static_env{role = Role, - socket = Socket, - host = Host, - port = Port, - trackers = Trackers, - transport_cb = Transport, - protocol_cb = Connection}, - connection_env = #connection_env{user_application = {_Mon, Pid}}, - ssl_options = #{log_level := LogLevel}, - start_or_recv_from = From, - session = Session, - socket_options = Opts} = State) -> - invalidate_session(Role, Host, Port, Session), - Alert = Alert0#alert{role = opposite_role(Role)}, - log_alert(LogLevel, Role, Connection:protocol_name(), - StateName, Alert), - Pids = Connection:pids(State), - alert_user(Pids, Transport, Trackers, Socket, StateName, Opts, Pid, From, Alert, Role, StateName, Connection), - {stop, {shutdown, normal}, State}; +handle_alert(#alert{level = ?FATAL} = Alert, StateName, State) -> + handle_fatal_alert(Alert, StateName, State); handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, downgrade= StateName, State) -> {next_state, StateName, State, [{next_event, internal, Alert}]}; @@ -1092,27 +1144,61 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, %% Go back to connection! State = Connection:reinit(State0#state{handshake_env = HsEnv#handshake_env{renegotiation = undefined}}), Connection:next_event(connection, no_record, State); +handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName, + #state{static_env = #static_env{role = Role, + protocol_cb = Connection}, + ssl_options = #{log_level := LogLevel}} = State) when StateName =/= connection -> + log_alert(LogLevel, Role, + Connection:protocol_name(), StateName, + Alert#alert{role = opposite_role(Role)}), + %% Wait for close alert that should follow or handshake timeout + Connection:next_event(StateName, no_record, State); %% Gracefully log and ignore all other warning alerts pre TLS-1.3 handle_alert(#alert{level = ?WARNING} = Alert, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, connection_env = #connection_env{negotiated_version = Version}, - ssl_options = #{log_level := LogLevel}} = State) when Version < {3,4} -> + ssl_options = #{log_level := LogLevel}} = State) when ?TLS_LT(Version, ?TLS_1_3) -> log_alert(LogLevel, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), Connection:next_event(StateName, no_record, State); -handle_alert(Alert0, StateName, State) -> +handle_alert(Alert, StateName, State) -> %% In TLS-1.3 all error alerts are fatal not matter of legacy level - handle_alert(Alert0#alert{level = ?FATAL}, StateName, State). + %% but keep the level for the log so that users looking at what is + %% sent and what is logged are not confused! Or if some one sends + %% user cancel alert in connection which is inappropriate! + handle_fatal_alert(Alert, StateName, State). + +handle_fatal_alert(Alert0, StateName, + #state{static_env = #static_env{role = Role, + socket = Socket, + host = Host, + port = Port, + trackers = Trackers, + transport_cb = Transport, + protocol_cb = Connection}, + connection_env = #connection_env{user_application = {_Mon, Pid}}, + ssl_options = #{log_level := LogLevel}, + start_or_recv_from = From, + session = Session, + socket_options = Opts} = State) -> + invalidate_session(Role, Host, Port, Session), + Alert = Alert0#alert{role = opposite_role(Role)}, + log_alert(LogLevel, Role, Connection:protocol_name(), + StateName, Alert), + Pids = Connection:pids(State), + alert_user(Pids, Transport, Trackers, Socket, StateName, Opts, Pid, From, Alert, Role, StateName, Connection), + {stop, {shutdown, normal}, State}. -handle_trusted_certs_db(#state{ssl_options = - #{cacertfile := <<>>, cacerts := []}}) -> +handle_trusted_certs_db(#state{ssl_options =#{cacerts := []} = Opts}) + when not is_map_key(cacertfile, Opts) -> %% No trusted certs specified ok; handle_trusted_certs_db(#state{static_env = #static_env{cert_db_ref = Ref, cert_db = CertDb}, - ssl_options = #{cacertfile := <<>>}}) when CertDb =/= undefined -> + ssl_options = Opts}) + when CertDb =/= undefined, not is_map_key(cacertfile, Opts) -> %% Certs provided as DER directly can not be shared %% with other connections and it is safe to delete them when the connection ends. ssl_pkix_db:remove_trusted_certs(Ref, CertDb); @@ -1130,9 +1216,9 @@ handle_trusted_certs_db(#state{static_env = #static_env{cert_db_ref = Ref, ok end. -maybe_invalidate_session({3, 4},_, _, _, _, _) -> +maybe_invalidate_session(?TLS_1_3,_, _, _, _, _) -> ok; -maybe_invalidate_session({3, N}, Type, Role, Host, Port, Session) when N < 4 -> +maybe_invalidate_session(Version, Type, Role, Host, Port, Session) when ?TLS_LT(Version, ?TLS_1_3) -> maybe_invalidate_session(Type, Role, Host, Port, Session). maybe_invalidate_session({false, first}, server = Role, Host, Port, Session) -> @@ -1140,6 +1226,9 @@ maybe_invalidate_session({false, first}, server = Role, Host, Port, Session) -> maybe_invalidate_session(_, _, _, _, _) -> ok. +terminate({shutdown, ktls}, connection, State) -> + %% Socket shall not be closed as it should be returned to user + handle_trusted_certs_db(State); terminate({shutdown, downgrade}, downgrade, State) -> %% Socket shall not be closed as it should be returned to user handle_trusted_certs_db(State); @@ -1191,10 +1280,9 @@ format_status(normal, [_, StateName, State]) -> [{data, [{"State", {StateName, State}}]}]; format_status(terminate, [_, StateName, State]) -> SslOptions = (State#state.ssl_options), - NewOptions = SslOptions#{password => ?SECRET_PRINTOUT, - cert => ?SECRET_PRINTOUT, + NewOptions = SslOptions#{ + certs_keys => ?SECRET_PRINTOUT, cacerts => ?SECRET_PRINTOUT, - key => ?SECRET_PRINTOUT, dh => ?SECRET_PRINTOUT, psk_identity => ?SECRET_PRINTOUT, srp_identity => ?SECRET_PRINTOUT}, @@ -1210,24 +1298,16 @@ format_status(terminate, [_, StateName, State]) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -tls_connection_fsm(#{versions := [{3,4}]}) -> - tls_connection_1_3; -tls_connection_fsm(_) -> - tls_connection. - -dtls_connection_fsm(_) -> - dtls_connection. - next_statem_state([Version], client) -> case ssl:tls_version(Version) of - {3,4} -> + ?TLS_1_3 -> wait_sh; _ -> hello end; next_statem_state([Version], server) -> case ssl:tls_version(Version) of - {3,4} -> + ?TLS_1_3 -> start; _ -> hello @@ -1276,11 +1356,8 @@ is_sni_value(Hostname) -> true end. -is_hostname_recognized(#{sni_fun := undefined, - sni_hosts := SNIHosts}, Hostname) -> - proplists:is_defined(Hostname, SNIHosts); -is_hostname_recognized(_, _) -> - true. +is_hostname_recognized(#{sni_fun := Fun}, Hostname) -> + Fun(Hostname) =:= undefined. handle_sni_hostname(Hostname, #state{static_env = #static_env{role = Role} = InitStatEnv0, @@ -1290,7 +1367,7 @@ handle_sni_hostname(Hostname, NewOptions = update_ssl_options_from_sni(Opts, Hostname), case NewOptions of undefined -> - case maps:get(server_name_indication, Opts) of + case maps:get(server_name_indication, Opts, undefined) of disable when Role == client-> State0; _ -> @@ -1320,23 +1397,14 @@ handle_sni_hostname(Hostname, } end. -update_ssl_options_from_sni(#{sni_fun := SNIFun, - sni_hosts := SNIHosts} = OrigSSLOptions, SNIHostname) -> - SSLOptions = - case SNIFun of - undefined -> - proplists:get_value(SNIHostname, - SNIHosts); - SNIFun -> - SNIFun(SNIHostname) - end, - case SSLOptions of +update_ssl_options_from_sni(#{sni_fun := SNIFun} = OrigSSLOptions, SNIHostname) -> + case SNIFun(SNIHostname) of undefined -> undefined; - _ -> + SSLOptions -> VersionsOpt = proplists:get_value(versions, SSLOptions, []), FallBackOptions = filter_for_versions(VersionsOpt, OrigSSLOptions), - ssl:handle_options(SSLOptions, server, FallBackOptions) + ssl:update_options(SSLOptions, server, FallBackOptions) end. filter_for_versions([], OrigSSLOptions) -> @@ -1897,7 +1965,7 @@ connection_info(#state{handshake_env = #handshake_env{sni_hostname = SNIHostname security_info(#state{connection_states = ConnectionStates, static_env = #static_env{role = Role}, - ssl_options = #{keep_secrets := KeepSecrets}, + ssl_options = Opts, protocol_specific = ProtocolSpecific}) -> ReadState = ssl_record:current_connection_state(ConnectionStates, read), #{security_parameters := @@ -1908,6 +1976,8 @@ security_info(#state{connection_states = ConnectionStates, client_early_data_secret = ServerEarlyData }} = ReadState, BaseSecurityInfo = [{client_random, ClientRand}, {server_random, ServerRand}, {master_secret, MasterSecret}], + + KeepSecrets = maps:get(keep_secrets, Opts, false), if KeepSecrets =/= true -> BaseSecurityInfo; true -> @@ -2175,12 +2245,37 @@ keylog_secret(SecretBin, sha384) -> keylog_secret(SecretBin, sha512) -> io_lib:format("~128.16.0B", [binary:decode_unsigned(SecretBin)]). -maybe_generate_client_shares(#{versions := [Version|_], +maybe_generate_client_shares(#{versions := [?TLS_1_3|_], supported_groups := #supported_groups{ - supported_groups = [Group|_]}}) - when Version =:= {3,4} -> + supported_groups = [Group|_]}}) -> %% Generate only key_share entry for the most preferred group ssl_cipher:generate_client_shares([Group]); maybe_generate_client_shares(_) -> undefined. + +%%%################################################################ +%%%# +%%%# Tracing +%%%# +handle_trace(api, + {call, {?MODULE, connect, [Connection | _]}}, Stack0) -> + {io_lib:format("Connection = ~w", [Connection]), Stack0}; +handle_trace(rle, + {call, {?MODULE, init, Args = [[Role | _]]}}, Stack0) -> + {io_lib:format("(*~w) Args = ~W", [Role, Args, 3]), [{role, Role} | Stack0]}; +handle_trace(hbn, + {call, {?MODULE, hibernate_after, + [_StateName = connection, State, Actions]}}, + Stack) -> + #state{ssl_options= #{hibernate_after := HibernateAfter}} = State, + {io_lib:format("* * * maybe hibernating in ~w ms * * * Actions = ~W ", + [HibernateAfter, Actions, 10]), Stack}; +handle_trace(hbn, + {return_from, {?MODULE, hibernate_after, 3}, + {Cmd, Arg,_State, Actions}}, + Stack) -> + {io_lib:format("Cmd = ~w Arg = ~w Actions = ~W", [Cmd, Arg, Actions, 10]), Stack}; +handle_trace(hbn, + {call, {?MODULE, handle_common_event, [timeout, hibernate, connection | _]}}, Stack) -> + {io_lib:format("* * * hibernating * * *", []), Stack}. |