diff options
author | Ingela Anderton Andin <ingela@erlang.org> | 2020-12-02 17:41:53 +0100 |
---|---|---|
committer | Ingela Anderton Andin <ingela@erlang.org> | 2020-12-07 13:16:10 +0100 |
commit | f61c4302a8a5e6185e05d0f97d1c7bbc0d2621fc (patch) | |
tree | 96e1edf142f0c5457193484d7317df58bca4d734 /lib/ssl/src/dtls_connection.erl | |
parent | 35b7bd5e4175b1d49b1c7058090029abdf6505cb (diff) | |
download | erlang-f61c4302a8a5e6185e05d0f97d1c7bbc0d2621fc.tar.gz |
ssl: Continue refactor
Split tls_connection to tls_connection (TLS-1.0 - TLS-1.2) and
tls_gen_connection (TLS protocol version generic) used as protocol_cb.
Split dtls_connection to dtls_connection (DTLS-1 - DTLS-1.2) and
dtls_gen_connection (DTLS protocol version generic) used as protocol_cb.
Rename ssl_connection -> tls_dtls_connection common code for pre TLS/DTLS-1.3
Diffstat (limited to 'lib/ssl/src/dtls_connection.erl')
-rw-r--r-- | lib/ssl/src/dtls_connection.erl | 797 |
1 files changed, 90 insertions, 707 deletions
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index b030300a89..fb389dcb4d 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -128,35 +128,9 @@ %% Internal application API %% Setup --export([start_fsm/8, - start_link/7, - init/1, - pids/1]). +-export([init/1]). -%% State transition handling --export([next_event/3, - next_event/4, - handle_protocol_record/3]). - -%% Handshake handling --export([renegotiate/2, - send_handshake/2, - queue_handshake/2, - queue_change_cipher/2, - reinit/1, - reinit_handshake_data/1, - select_sni_extension/1, - empty_connection_state/2]). - -%% Alert and close handling --export([encode_alert/3, - send_alert/2, - send_alert_in_connection/2, - close/5, - protocol_name/0]). - -%% Data handling --export([socket/4, setopts/3, getopts/3]). +-export([renegotiate/2]). %% gen_statem state functions -export([initial_hello/3, @@ -182,361 +156,35 @@ %%==================================================================== %% Setup %%==================================================================== -start_fsm(Role, Host, Port, Socket, {#{erl_dist := false},_, Tracker} = Opts, - User, {CbModule, _, _, _, _} = CbInfo, - Timeout) -> - try - {ok, Pid} = dtls_connection_sup:start_child([Role, Host, Port, Socket, - Opts, User, CbInfo]), - {ok, SslSocket} = ssl_gen_statem:socket_control(?MODULE, Socket, [Pid], CbModule, Tracker), - ssl_gen_statem:handshake(SslSocket, Timeout) - catch - error:{badmatch, {error, _} = Error} -> - Error - end. - -%%-------------------------------------------------------------------- --spec start_link(atom(), ssl:host(), inet:port_number(), port(), list(), pid(), tuple()) -> - {ok, pid()} | ignore | {error, reason()}. -%% -%% Description: Creates a gen_statem process which calls Module:init/1 to -%% initialize. -%%-------------------------------------------------------------------- -start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> - {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}. - init([Role, Host, Port, Socket, Options, User, CbInfo]) -> process_flag(trap_exit, true), - State0 = #state{protocol_specific = Map} = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), + State0 = #state{protocol_specific = Map} = + initial_state(Role, Host, Port, Socket, Options, User, CbInfo), try - State = ssl_gen_statem:ssl_config(State0#state.ssl_options, Role, State0), + State = ssl_gen_statem:ssl_config(State0#state.ssl_options, + Role, State0), gen_statem:enter_loop(?MODULE, [], initial_hello, State) catch throw:Error -> - EState = State0#state{protocol_specific = Map#{error => Error}}, + EState = State0#state{protocol_specific = + Map#{error => Error}}, gen_statem:enter_loop(?MODULE, [], config_error, EState) end. - -pids(_) -> - [self()]. - %%==================================================================== -%% State transition handling +%% Handshake %%==================================================================== -next_record(#state{handshake_env = - #handshake_env{unprocessed_handshake_events = N} = HsEnv} - = State) when N > 0 -> - {no_record, State#state{handshake_env = - HsEnv#handshake_env{unprocessed_handshake_events = N-1}}}; -next_record(#state{protocol_buffers = - #protocol_buffers{dtls_cipher_texts = [#ssl_tls{epoch = Epoch} = CT | Rest]} - = Buffers, - connection_states = #{current_read := #{epoch := Epoch}} = ConnectionStates} = State) -> - CurrentRead = dtls_record:get_connection_state_by_epoch(Epoch, ConnectionStates, read), - case dtls_record:replay_detect(CT, CurrentRead) of - false -> - decode_cipher_text(State#state{connection_states = ConnectionStates}) ; - true -> - %% Ignore replayed record - next_record(State#state{protocol_buffers = - Buffers#protocol_buffers{dtls_cipher_texts = Rest}, - connection_states = ConnectionStates}) - end; -next_record(#state{protocol_buffers = - #protocol_buffers{dtls_cipher_texts = [#ssl_tls{epoch = Epoch} | Rest]} - = Buffers, - connection_states = #{current_read := #{epoch := CurrentEpoch}} = ConnectionStates} = State) - when Epoch > CurrentEpoch -> - %% TODO Buffer later Epoch message, drop it for now - next_record(State#state{protocol_buffers = - Buffers#protocol_buffers{dtls_cipher_texts = Rest}, - connection_states = ConnectionStates}); -next_record(#state{protocol_buffers = - #protocol_buffers{dtls_cipher_texts = [ _ | Rest]} - = Buffers, - connection_states = ConnectionStates} = State) -> - %% Drop old epoch message - next_record(State#state{protocol_buffers = - Buffers#protocol_buffers{dtls_cipher_texts = Rest}, - connection_states = ConnectionStates}); -next_record(#state{static_env = #static_env{role = server, - socket = {Listener, {Client, _}}}} = State) -> - dtls_packet_demux:active_once(Listener, Client, self()), - {no_record, State}; -next_record(#state{protocol_specific = #{active_n_toggle := true, - active_n := N} = ProtocolSpec, - static_env = #static_env{role = client, - socket = {_Server, Socket} = DTLSSocket, - close_tag = CloseTag, - transport_cb = Transport}} = State) -> - case dtls_socket:setopts(Transport, Socket, [{active,N}]) of - ok -> - {no_record, State#state{protocol_specific = - ProtocolSpec#{active_n_toggle => false}}}; - _ -> - self() ! {CloseTag, DTLSSocket}, - {no_record, State} - end; -next_record(State) -> - {no_record, State}. - -next_event(StateName, Record, State) -> - next_event(StateName, Record, State, []). - -next_event(StateName, no_record, - #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) -> - case next_record(State0) of - {no_record, State} -> - ssl_gen_statem:hibernate_after(StateName, State, Actions); - {#ssl_tls{epoch = CurrentEpoch, - type = ?HANDSHAKE, - version = Version} = Record, State1} -> - State = dtls_version(StateName, Version, State1), - {next_state, StateName, State, - [{next_event, internal, {protocol_record, Record}} | Actions]}; - {#ssl_tls{epoch = CurrentEpoch} = Record, State} -> - {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; - {#ssl_tls{epoch = Epoch, - type = ?HANDSHAKE, - version = _Version}, State1} = _Record when Epoch == CurrentEpoch-1 -> - {State, MoreActions} = send_handshake_flight(State1, CurrentEpoch), - next_event(StateName, no_record, State, Actions ++ MoreActions); - %% From FLIGHT perspective CHANGE_CIPHER_SPEC is treated as a handshake - {#ssl_tls{epoch = Epoch, - type = ?CHANGE_CIPHER_SPEC, - version = _Version}, State1} = _Record when Epoch == CurrentEpoch-1 -> - {State, MoreActions} = send_handshake_flight(State1, CurrentEpoch), - next_event(StateName, no_record, State, Actions ++ MoreActions); - {#ssl_tls{epoch = _Epoch, - version = _Version}, State} -> - %% TODO maybe buffer later epoch - next_event(StateName, no_record, State, Actions); - {#alert{} = Alert, State} -> - Version = State#state.connection_env#connection_env.negotiated_version, - handle_own_alert(Alert, Version, StateName, State) - end; -next_event(connection = StateName, Record, - #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) -> - case Record of - #ssl_tls{epoch = CurrentEpoch, - type = ?HANDSHAKE, - version = Version} = Record -> - State = dtls_version(StateName, Version, State0), - {next_state, StateName, State, - [{next_event, internal, {protocol_record, Record}} | Actions]}; - #ssl_tls{epoch = CurrentEpoch} -> - {next_state, StateName, State0, [{next_event, internal, {protocol_record, Record}} | Actions]}; - #ssl_tls{epoch = Epoch, - type = ?HANDSHAKE, - version = _Version} when Epoch == CurrentEpoch-1 -> - {State, MoreActions} = send_handshake_flight(State0, CurrentEpoch), - next_event(StateName, no_record, State, Actions ++ MoreActions); - %% From FLIGHT perspective CHANGE_CIPHER_SPEC is treated as a handshake - #ssl_tls{epoch = Epoch, - type = ?CHANGE_CIPHER_SPEC, - version = _Version} when Epoch == CurrentEpoch-1 -> - {State, MoreActions} = send_handshake_flight(State0, CurrentEpoch), - next_event(StateName, no_record, State, Actions ++ MoreActions); - _ -> - next_event(StateName, no_record, State0, Actions) - end; -next_event(StateName, Record, - #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) -> - case Record of - #ssl_tls{epoch = CurrentEpoch, - version = Version} = Record -> - State = dtls_version(StateName, Version, State0), - {next_state, StateName, State, - [{next_event, internal, {protocol_record, Record}} | Actions]}; - #ssl_tls{epoch = _Epoch, - version = _Version} = _Record -> - %% TODO maybe buffer later epoch - next_event(StateName, no_record, State0, Actions); - #alert{} = Alert -> - Version = State0#state.connection_env#connection_env.negotiated_version, - handle_own_alert(Alert, Version, StateName, State0) - end. - -%%% DTLS record protocol level application data messages - -handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName0, State0) -> - case ssl_gen_statem:read_application_data(Data, State0) of - {stop, _, _} = Stop-> - Stop; - {Record, State1} -> - {next_state, StateName, State, Actions} = next_event(StateName0, Record, State1), - ssl_gen_statem:hibernate_after(StateName, State, Actions) - end; -%%% DTLS record protocol level handshake messages -handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, - fragment = Data}, - StateName, - #state{protocol_buffers = Buffers0, - connection_env = #connection_env{negotiated_version = Version}, - ssl_options = Options} = State) -> - try - case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0, Options) of - {[], Buffers} -> - next_event(StateName, no_record, State#state{protocol_buffers = Buffers}); - {Packets, Buffers} -> - HsEnv = State#state.handshake_env, - Events = dtls_handshake_events(Packets), - {next_state, StateName, - State#state{protocol_buffers = Buffers, - handshake_env = - HsEnv#handshake_env{unprocessed_handshake_events - = unprocessed_events(Events)}}, Events} - end - catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State) - end; -%%% DTLS record protocol level change cipher messages -handle_protocol_record(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) -> - {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]}; -%%% DTLS record protocol level Alert messages -handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, - #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - case decode_alerts(EncAlerts) of - Alerts = [_|_] -> - handle_alerts(Alerts, {next_state, StateName, State}); - #alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State) - end; -%% Ignore unknown TLS record level protocol messages -handle_protocol_record(#ssl_tls{type = _Unknown}, StateName, State) -> - {next_state, StateName, State, []}. - -%%==================================================================== -%% Handshake handling -%%==================================================================== - renegotiate(#state{static_env = #static_env{role = client}} = State0, Actions) -> %% Handle same way as if server requested %% the renegotiation - State = reinit_handshake_data(State0), + State = dtls_gen_connection:reinit_handshake_data(State0), {next_state, connection, State, [{next_event, internal, #hello_request{}} | Actions]}; renegotiate(#state{static_env = #static_env{role = server}} = State0, Actions) -> HelloRequest = ssl_handshake:hello_request(), State1 = prepare_flight(State0), - {State, MoreActions} = send_handshake(HelloRequest, State1), - next_event(hello, no_record, State, Actions ++ MoreActions). - -send_handshake(Handshake, #state{connection_states = ConnectionStates} = State) -> - #{epoch := Epoch} = ssl_record:current_connection_state(ConnectionStates, write), - send_handshake_flight(queue_handshake(Handshake, State), Epoch). - -queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv, - connection_env = #connection_env{negotiated_version = Version}, - flight_buffer = #{handshakes := HsBuffer0, - change_cipher_spec := undefined, - next_sequence := Seq} = Flight0, - ssl_options = #{log_level := LogLevel}} = State) -> - Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq), - Hist = update_handshake_history(Handshake0, Handshake, Hist0), - ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake0), - - State#state{flight_buffer = Flight0#{handshakes => [Handshake | HsBuffer0], - next_sequence => Seq +1}, - handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}; - -queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv, - connection_env = #connection_env{negotiated_version = Version}, - flight_buffer = #{handshakes_after_change_cipher_spec := Buffer0, - next_sequence := Seq} = Flight0, - ssl_options = #{log_level := LogLevel}} = State) -> - Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq), - Hist = update_handshake_history(Handshake0, Handshake, Hist0), - ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake0), - - State#state{flight_buffer = Flight0#{handshakes_after_change_cipher_spec => [Handshake | Buffer0], - next_sequence => Seq +1}, - handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}}. - -queue_change_cipher(ChangeCipher, #state{flight_buffer = Flight, - connection_states = ConnectionStates0} = State) -> - ConnectionStates = - dtls_record:next_epoch(ConnectionStates0, write), - State#state{flight_buffer = Flight#{change_cipher_spec => ChangeCipher}, - connection_states = ConnectionStates}. - -reinit(State) -> - %% To be API compatible with TLS NOOP here - reinit_handshake_data(State). -reinit_handshake_data(#state{static_env = #static_env{data_tag = DataTag}, - protocol_buffers = Buffers, - protocol_specific = PS, - handshake_env = HsEnv} = State) -> - State#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = ssl_handshake:init_handshake_history(), - public_key_info = undefined, - premaster_secret = undefined}, - protocol_specific = PS#{flight_state => initial_flight_state(DataTag)}, - flight_buffer = new_flight(), - protocol_buffers = - Buffers#protocol_buffers{ - dtls_handshake_next_seq = 0, - dtls_handshake_next_fragments = [], - dtls_handshake_later_fragments = [] - }}. - -select_sni_extension(#client_hello{extensions = #{sni := SNI}}) -> - SNI; -select_sni_extension(_) -> - undefined. - -empty_connection_state(ConnectionEnd, BeastMitigation) -> - Empty = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation), - dtls_record:empty_connection_state(Empty). - -%%==================================================================== -%% Alert and close handling -%%==================================================================== -encode_alert(#alert{} = Alert, Version, ConnectionStates) -> - dtls_record:encode_alert_record(Alert, Version, ConnectionStates). - -send_alert(Alert, #state{static_env = #static_env{socket = Socket, - transport_cb = Transport}, - - connection_env = #connection_env{negotiated_version = Version}, - connection_states = ConnectionStates0, - ssl_options = #{log_level := LogLevel}} = State0) -> - {BinMsg, ConnectionStates} = - encode_alert(Alert, Version, ConnectionStates0), - send(Transport, Socket, BinMsg), - ssl_logger:debug(LogLevel, outbound, 'record', BinMsg), - State0#state{connection_states = ConnectionStates}. - -send_alert_in_connection(Alert, State) -> - _ = send_alert(Alert, State), - ok. - -close(downgrade, _,_,_,_) -> - ok; -%% Other -close(_, Socket, Transport, _,_) -> - dtls_socket:close(Transport,Socket). - -protocol_name() -> - "DTLS". - -%%==================================================================== -%% Data handling -%%==================================================================== - -send(Transport, {Listener, Socket}, Data) when is_pid(Listener) -> % Server socket - dtls_socket:send(Transport, Socket, Data); -send(Transport, Socket, Data) -> % Client socket - dtls_socket:send(Transport, Socket, Data). - -socket(Pid, Transport, Socket, _Tracker) -> - dtls_socket:socket(Pid, Transport, Socket, ?MODULE). - -setopts(Transport, Socket, Other) -> - dtls_socket:setopts(Transport, Socket, Other). - -getopts(Transport, Socket, Tag) -> - dtls_socket:getopts(Transport, Socket, Tag). + {State, MoreActions} = dtls_gen_connection:send_handshake(HelloRequest, State1), + dtls_gen_connection:next_event(hello, no_record, State, Actions ++ MoreActions). %%-------------------------------------------------------------------- %% State functions @@ -570,19 +218,22 @@ initial_hello({call, From}, {start, Timeout}, HelloVersion = dtls_record:hello_version(Version, Versions), State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}, connection_states = ConnectionStates1}), - {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}), + {State2, Actions} = + dtls_gen_connection:send_handshake(Hello, + State1#state{connection_env = + CEnv#connection_env{negotiated_version = HelloVersion}}), State = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% RequestedVersion session = Session, start_or_recv_from = From}, - next_event(hello, no_record, State, [{{timeout, handshake}, Timeout, close} | Actions]); + dtls_gen_connection:next_event(hello, no_record, State, [{{timeout, handshake}, Timeout, close} | Actions]); initial_hello({call, _} = Type, Event, #state{static_env = #static_env{role = server}, - protocol_specific = PS} = State) -> + protocol_specific = PS} = State) -> Result = ssl_gen_statem:?FUNCTION_NAME(Type, Event, - State#state{protocol_specific = - PS#{current_cookie_secret => dtls_v1:cookie_secret(), - previous_cookie_secret => <<>>, - ignored_alerts => 0, - max_ignored_alerts => 10}}), + State#state{protocol_specific = + PS#{current_cookie_secret => dtls_v1:cookie_secret(), + previous_cookie_secret => <<>>, + ignored_alerts => 0, + max_ignored_alerts => 10}}), erlang:send_after(dtls_v1:cookie_timeout(), self(), new_cookie_secret), Result; initial_hello(Type, Event, State) -> @@ -617,7 +268,7 @@ hello(internal, #client_hello{cookie = <<>>, handshake_env = HsEnv, connection_env = CEnv, protocol_specific = #{current_cookie_secret := Secret}} = State0) -> - case ssl_connection:handle_sni_extension(State0, Hello) of + case tls_dtls_connection:handle_sni_extension(State0, Hello) of #state{} = State1 -> {ok, {IP, Port}} = dtls_socket:peername(Transport, Socket), Cookie = dtls_handshake:cookie(Secret, IP, Port, Hello), @@ -629,27 +280,28 @@ hello(internal, #client_hello{cookie = <<>>, %% negotiated. VerifyRequest = dtls_handshake:hello_verify_request(Cookie, ?HELLO_VERIFY_REQUEST_VERSION), State2 = prepare_flight(State1#state{connection_env = CEnv#connection_env{negotiated_version = Version}}), - {State, Actions} = send_handshake(VerifyRequest, State2), - next_event(?FUNCTION_NAME, no_record, - State#state{handshake_env = HsEnv#handshake_env{ - tls_handshake_history = - ssl_handshake:init_handshake_history()}}, + {State, Actions} = dtls_gen_connection:send_handshake(VerifyRequest, State2), + dtls_gen_connection:next_event(?FUNCTION_NAME, no_record, + State#state{handshake_env = HsEnv#handshake_env{ + tls_handshake_history = + ssl_handshake:init_handshake_history()}}, Actions); #alert{} = Alert -> - handle_own_alert(Alert, Version,?FUNCTION_NAME, State0) + ssl_gen_statem:handle_own_alert(Alert, Version,?FUNCTION_NAME, State0) end; -hello(internal, #hello_verify_request{cookie = Cookie}, #state{static_env = #static_env{role = client, - host = Host, - port = Port}, - handshake_env = #handshake_env{renegotiation = {Renegotiation, _}, - ocsp_stapling_state = OcspState0} = HsEnv, - connection_env = CEnv, - ssl_options = #{ocsp_stapling := OcspStaplingOpt, - ocsp_nonce := OcspNonceOpt} = SslOpts, - session = #session{own_certificates = OwnCerts, - session_id = Id}, - connection_states = ConnectionStates0 - } = State0) -> +hello(internal, #hello_verify_request{cookie = Cookie}, + #state{static_env = #static_env{role = client, + host = Host, + port = Port}, + handshake_env = #handshake_env{renegotiation = {Renegotiation, _}, + ocsp_stapling_state = OcspState0} = HsEnv, + connection_env = CEnv, + ssl_options = #{ocsp_stapling := OcspStaplingOpt, + ocsp_nonce := OcspNonceOpt} = SslOpts, + session = #session{own_certificates = OwnCerts, + session_id = Id}, + connection_states = ConnectionStates0 + } = State0) -> OcspNonce = tls_handshake:ocsp_nonce(OcspNonceOpt, OcspStaplingOpt), Hello = dtls_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts, Id, Renegotiation, OwnCerts, OcspNonce), @@ -657,24 +309,25 @@ hello(internal, #hello_verify_request{cookie = Cookie}, #state{static_env = #sta State1 = prepare_flight(State0#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = ssl_handshake:init_handshake_history(), - ocsp_stapling_state = OcspState0#{ocsp_nonce => OcspNonce}}}), + ocsp_stapling_state = + OcspState0#{ocsp_nonce => OcspNonce}}}), - {State2, Actions} = send_handshake(Hello, State1), + {State2, Actions} = dtls_gen_connection:send_handshake(Hello, State1), State = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version} % RequestedVersion }, - next_event(?FUNCTION_NAME, no_record, State, Actions); + dtls_gen_connection:next_event(?FUNCTION_NAME, no_record, State, Actions); hello(internal, #client_hello{extensions = Extensions, client_version = ClientVersion} = Hello, #state{ssl_options = #{handshake := hello}, handshake_env = HsEnv, start_or_recv_from = From} = State0) -> - case ssl_connection:handle_sni_extension(State0, Hello) of + case tls_dtls_connection:handle_sni_extension(State0, Hello) of #state{} = State -> {next_state, user_hello, State#state{start_or_recv_from = undefined, handshake_env = HsEnv#handshake_env{hello = Hello}}, [{reply, From, {ok, Extensions}}]}; #alert{} = Alert -> - handle_own_alert(Alert, ClientVersion, ?FUNCTION_NAME, State0) + ssl_gen_statem:handle_own_alert(Alert, ClientVersion, ?FUNCTION_NAME, State0) end; hello(internal, #server_hello{extensions = Extensions} = Hello, #state{ssl_options = #{ @@ -716,12 +369,13 @@ hello(internal, #server_hello{} = Hello, ssl_options = SslOptions} = State) -> case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation, OldId) of #alert{} = Alert -> - handle_own_alert(Alert, ReqVersion, ?FUNCTION_NAME, State); + ssl_gen_statem:handle_own_alert(Alert, ReqVersion, ?FUNCTION_NAME, State); {Version, NewId, ConnectionStates, ProtoExt, Protocol, OcspState} -> - ssl_connection:handle_session(Hello, + tls_dtls_connection:handle_session(Hello, Version, NewId, ConnectionStates, ProtoExt, Protocol, - State#state{handshake_env = HsEnv#handshake_env{ - ocsp_stapling_state = maps:merge(OcspState0,OcspState)}}) + State#state{handshake_env = + HsEnv#handshake_env{ + ocsp_stapling_state = maps:merge(OcspState0,OcspState)}}) end; hello(internal, {handshake, {#client_hello{cookie = <<>>} = Handshake, _}}, State) -> %% Initial hello should not be in handshake history @@ -730,8 +384,9 @@ hello(internal, {handshake, {#hello_verify_request{} = Handshake, _}}, State) -> %% hello_verify should not be in handshake history {next_state, ?FUNCTION_NAME, State, [{next_event, internal, Handshake}]}; hello(internal, #change_cipher_spec{type = <<1>>}, State0) -> - {State1, Actions0} = send_handshake_flight(State0, retransmit_epoch(?FUNCTION_NAME, State0)), - {next_state, ?FUNCTION_NAME, State, Actions} = next_event(?FUNCTION_NAME, no_record, State1, Actions0), + {State1, Actions0} = dtls_gen_connection:send_handshake_flight(State0, retransmit_epoch(?FUNCTION_NAME, State0)), + {next_state, ?FUNCTION_NAME, State, Actions} = + dtls_gen_connection:next_event(?FUNCTION_NAME, no_record, State1, Actions0), %% This will reset the retransmission timer by repeating the enter state event {repeat_state, State, Actions}; hello(info, Event, State) -> @@ -797,8 +452,9 @@ certify(info, Event, State) -> certify(internal = Type, #server_hello_done{} = Event, State) -> gen_handshake(?FUNCTION_NAME, Type, Event, prepare_flight(State)); certify(internal, #change_cipher_spec{type = <<1>>}, State0) -> - {State1, Actions0} = send_handshake_flight(State0, retransmit_epoch(?FUNCTION_NAME, State0)), - {next_state, ?FUNCTION_NAME, State, Actions} = next_event(?FUNCTION_NAME, no_record, State1, Actions0), + {State1, Actions0} = dtls_gen_connection:send_handshake_flight(State0, retransmit_epoch(?FUNCTION_NAME, State0)), + {next_state, ?FUNCTION_NAME, State, Actions} = + dtls_gen_connection:next_event(?FUNCTION_NAME, no_record, State1, Actions0), %% This will reset the retransmission timer by repeating the enter state event {repeat_state, State, Actions}; certify(state_timeout, Event, State) -> @@ -859,12 +515,16 @@ connection(internal, #hello_request{}, #state{static_env = #static_env{host = Ho Version = Hello#client_hello.client_version, HelloVersion = dtls_record:hello_version(Version, Versions), State1 = prepare_flight(State0), - {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}), - State = State2#state{protocol_specific = PS#{flight_state => initial_flight_state(DataTag)}, + {State2, Actions} = + dtls_gen_connection:send_handshake(Hello, + State1#state{connection_env = + CEnv#connection_env{negotiated_version = HelloVersion}}), + State = State2#state{protocol_specific = PS#{flight_state => dtls_gen_connection:initial_flight_state(DataTag)}, session = Session}, - next_event(hello, no_record, State, Actions); -connection(internal, #client_hello{} = Hello, #state{static_env = #static_env{role = server}, - handshake_env = #handshake_env{allow_renegotiate = true} = HsEnv} = State) -> + dtls_gen_connection:next_event(hello, no_record, State, Actions); +connection(internal, #client_hello{} = Hello, + #state{static_env = #static_env{role = server}, + handshake_env = #handshake_env{allow_renegotiate = true} = HsEnv} = State) -> %% Mitigate Computational DoS attack %% http://www.educatedguesswork.org/2011/10/ssltls_and_computational_dos.html %% http://www.thc.org/thc-ssl-dos/ Rather than disabling client @@ -874,12 +534,13 @@ connection(internal, #client_hello{} = Hello, #state{static_env = #static_env{ro {next_state, hello, State#state{handshake_env = HsEnv#handshake_env{renegotiation = {true, peer}, allow_renegotiate = false}}, [{next_event, internal, Hello}]}; -connection(internal, #client_hello{}, #state{static_env = #static_env{role = server}, +connection(internal, #client_hello{}, #state{static_env = #static_env{role = server, + protocol_cb = Connection}, handshake_env = #handshake_env{allow_renegotiate = false}} = State0) -> Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION), - State1 = send_alert(Alert, State0), - {Record, State} = ssl_gen_statem:prepare_connection(State1, ?MODULE), - next_event(?FUNCTION_NAME, Record, State); + State1 = dtls_gen_connection:send_alert(Alert, State0), + {Record, State} = ssl_gen_statem:prepare_connection(State1, Connection), + dtls_gen_connection:next_event(?FUNCTION_NAME, Record, State); connection({call, From}, {application_data, Data}, State) -> try send_application_data(Data, From, ?FUNCTION_NAME, State) @@ -887,7 +548,7 @@ connection({call, From}, {application_data, Data}, State) -> ssl_gen_statem:hibernate_after(?FUNCTION_NAME, State, [{reply, From, Error}]) end; connection(Type, Event, State) -> - ssl_connection:?FUNCTION_NAME(Type, Event, State). + tls_dtls_connection:?FUNCTION_NAME(Type, Event, State). %%TODO does this make sense for DTLS ? %%-------------------------------------------------------------------- @@ -897,7 +558,7 @@ connection(Type, Event, State) -> downgrade(enter, _, State) -> {keep_state, State}; downgrade(Type, Event, State) -> - ssl_connection:?FUNCTION_NAME(Type, Event, State). + tls_dtls_connection:?FUNCTION_NAME(Type, Event, State). %%-------------------------------------------------------------------- %% gen_statem callbacks @@ -939,7 +600,7 @@ initial_state(Role, Host, Port, Socket, InitStatEnv = #static_env{ role = Role, transport_cb = CbModule, - protocol_cb = ?MODULE, + protocol_cb = dtls_gen_connection, data_tag = DataTag, close_tag = CloseTag, error_tag = ErrorTag, @@ -967,61 +628,15 @@ initial_state(Role, Host, Port, Socket, protocol_buffers = #protocol_buffers{}, user_data_buffer = {[],0,[]}, start_or_recv_from = undefined, - flight_buffer = new_flight(), + flight_buffer = dtls_gen_connection:new_flight(), protocol_specific = #{active_n => InternalActiveN, active_n_toggle => true, - flight_state => initial_flight_state(DataTag)} + flight_state => dtls_gen_connection:initial_flight_state(DataTag)} }. -initial_flight_state(udp)-> - {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}; -initial_flight_state(_) -> - reliable. - -next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ - dtls_record_buffer = Buf0, - dtls_cipher_texts = CT0} = Buffers, - connection_env = #connection_env{negotiated_version = Version}, - static_env = #static_env{data_tag = DataTag}, - ssl_options = SslOpts} = State0) -> - case dtls_record:get_dtls_records(Data, - {DataTag, StateName, Version, - [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_DATAGRAM_VERSIONS]}, - Buf0, SslOpts) of - {Records, Buf1} -> - CT1 = CT0 ++ Records, - next_record(State0#state{protocol_buffers = - Buffers#protocol_buffers{dtls_record_buffer = Buf1, - dtls_cipher_texts = CT1}}); - #alert{} = Alert -> - Alert - end. - - -dtls_handshake_events(Packets) -> - lists:map(fun(Packet) -> - {next_event, internal, {handshake, Packet}} - end, Packets). - -decode_cipher_text(#state{protocol_buffers = #protocol_buffers{dtls_cipher_texts = [ CT | Rest]} = Buffers, - connection_states = ConnStates0} = State) -> - case dtls_record:decode_cipher_text(CT, ConnStates0) of - {Plain, ConnStates} -> - {Plain, State#state{protocol_buffers = - Buffers#protocol_buffers{dtls_cipher_texts = Rest}, - connection_states = ConnStates}}; - #alert{} = Alert -> - {Alert, State} - end. - -dtls_version(hello, Version, #state{static_env = #static_env{role = server}, - connection_env = CEnv} = State) -> - State#state{connection_env = CEnv#connection_env{negotiated_version = Version}}; %%Inital version -dtls_version(_,_, State) -> - State. handle_client_hello(#client_hello{client_version = ClientVersion} = Hello, State0) -> - case ssl_connection:handle_sni_extension(State0, Hello) of + case tls_dtls_connection:handle_sni_extension(State0, Hello) of #state{connection_states = ConnectionStates0, static_env = #static_env{trackers = Trackers}, handshake_env = #handshake_env{kex_algorithm = KeyExAlg, @@ -1034,7 +649,7 @@ handle_client_hello(#client_hello{client_version = ClientVersion} = Hello, State case dtls_handshake:hello(Hello, SslOpts, {SessionTracker, Session0, ConnectionStates0, OwnCerts, KeyExAlg}, Renegotiation) of #alert{} = Alert -> - handle_own_alert(Alert, ClientVersion, hello, State1); + ssl_gen_statem:handle_own_alert(Alert, ClientVersion, hello, State1); {Version, {Type, Session}, ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> Protocol = case Protocol0 of @@ -1052,119 +667,25 @@ handle_client_hello(#client_hello{client_version = ClientVersion} = Hello, State {next_state, hello, State, [{next_event, internal, {common_client_hello, Type, ServerHelloExt}}]} end; #alert{} = Alert -> - handle_own_alert(Alert, ClientVersion, hello, State0) + ssl_gen_statem:handle_own_alert(Alert, ClientVersion, hello, State0) end. -%% raw data from socket, unpack records -handle_info({Protocol, _, _, _, Data}, StateName, - #state{static_env = #static_env{role = Role, - data_tag = Protocol}} = State0) -> - case next_dtls_record(Data, StateName, State0) of - {Record, State} -> - next_event(StateName, Record, State); - #alert{} = Alert -> - ssl_gen_statem:handle_normal_shutdown(Alert#alert{role = Role}, StateName, State0), - {stop, {shutdown, own_alert}, State0} - end; -handle_info({PassiveTag, Socket}, StateName, - #state{static_env = #static_env{socket = {_, Socket}, - passive_tag = PassiveTag}, - protocol_specific = PS} = State) -> - next_event(StateName, no_record, - State#state{protocol_specific = PS#{active_n_toggle => true}}); - -handle_info({CloseTag, Socket}, StateName, - #state{static_env = #static_env{ - role = Role, - socket = Socket, - close_tag = CloseTag}, - connection_env = #connection_env{negotiated_version = Version}, - socket_options = #socket_options{active = Active}, - protocol_buffers = #protocol_buffers{dtls_cipher_texts = CTs}, - protocol_specific = PS} = State) -> - %% Note that as of DTLS 1.2 (TLS 1.1), - %% failure to properly close a connection no longer requires that a - %% session not be resumed. This is a change from DTLS 1.0 to conform - %% with widespread implementation practice. - case (Active == false) andalso (CTs =/= []) of - false -> - case Version of - {254, N} when N =< 253 -> - ok; - _ -> - %% As invalidate_sessions here causes performance issues, - %% we will conform to the widespread implementation - %% practice and go aginst the spec - %%invalidate_session(Role, Host, Port, Session) - ok - end, - Alert = ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY, transport_closed), - ssl_gen_statem:handle_normal_shutdown(Alert#alert{role = Role}, StateName, State), - {stop, {shutdown, transport_closed}, State}; - true -> - %% Fixes non-delivery of final DTLS record in {active, once}. - %% Basically allows the application the opportunity to set {active, once} again - %% and then receive the final message. - next_event(StateName, no_record, State#state{ - protocol_specific = PS#{active_n_toggle => true}}) - end; - -handle_info(new_cookie_secret, StateName, - #state{protocol_specific = #{current_cookie_secret := Secret} = CookieInfo} = State) -> - erlang:send_after(dtls_v1:cookie_timeout(), self(), new_cookie_secret), - {next_state, StateName, State#state{protocol_specific = - CookieInfo#{current_cookie_secret => dtls_v1:cookie_secret(), - previous_cookie_secret => Secret}}}; -handle_info(Msg, StateName, State) -> - ssl_gen_statem:handle_info(Msg, StateName, State). handle_state_timeout(flight_retransmission_timeout, StateName, #state{protocol_specific = #{flight_state := {retransmit, _NextTimeout}}} = State0) -> - {State1, Actions0} = send_handshake_flight(State0, - retransmit_epoch(StateName, State0)), - {next_state, StateName, State, Actions} = next_event(StateName, no_record, State1, Actions0), + {State1, Actions0} = dtls_gen_connection:send_handshake_flight(State0, + retransmit_epoch(StateName, State0)), + {next_state, StateName, State, Actions} = + dtls_gen_connection:next_event(StateName, no_record, State1, Actions0), %% This will reset the retransmission timer by repeating the enter state event {repeat_state, State, Actions}. -handle_alerts([], Result) -> - Result; -handle_alerts(_, {stop, _, _} = Stop) -> - Stop; -handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> - handle_alerts(Alerts, ssl_gen_statem:handle_alert(Alert, StateName, State)); -handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) -> - handle_alerts(Alerts, ssl_gen_statem:handle_alert(Alert, StateName, State)). - -handle_own_alert(Alert, Version, StateName, - #state{static_env = #static_env{data_tag = udp, - role = Role}, - ssl_options = #{log_level := LogLevel}} = State0) -> - case ignore_alert(Alert, State0) of - {true, State} -> - log_ignore_alert(LogLevel, StateName, Alert, Role), - {next_state, StateName, State}; - {false, State} -> - ssl_gen_statem:handle_own_alert(Alert, Version, StateName, State) - end; -handle_own_alert(Alert, Version, StateName, State) -> - ssl_gen_statem:handle_own_alert(Alert, Version, StateName, State). -encode_handshake_flight(Flight, Version, MaxFragmentSize, Epoch, ConnectionStates) -> - Fragments = lists:map(fun(Handshake) -> - dtls_handshake:fragment_handshake(Handshake, MaxFragmentSize) - end, Flight), - dtls_record:encode_handshake(Fragments, Version, Epoch, ConnectionStates). - -encode_change_cipher(#change_cipher_spec{}, Version, Epoch, ConnectionStates) -> - dtls_record:encode_change_cipher_spec(Version, Epoch, ConnectionStates). - -decode_alerts(Bin) -> - ssl_alert:decode(Bin). gen_handshake(StateName, Type, Event, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - try ssl_connection:StateName(Type, Event, State) of + try tls_dtls_connection:StateName(Type, Event, State) of Result -> Result catch @@ -1175,7 +696,7 @@ gen_handshake(StateName, Type, Event, end. gen_info(Event, connection = StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - try handle_info(Event, StateName, State) of + try dtls_gen_connection:handle_info(Event, StateName, State) of Result -> Result catch @@ -1186,7 +707,7 @@ gen_info(Event, connection = StateName, #state{connection_env = #connection_env end; gen_info(Event, StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - try handle_info(Event, StateName, State) of + try dtls_gen_connection:handle_info(Event, StateName, State) of Result -> Result catch @@ -1195,18 +716,7 @@ gen_info(Event, StateName, #state{connection_env = #connection_env{negotiated_ve malformed_handshake_data), Version, StateName, State) end. -unprocessed_events(Events) -> - %% The first handshake event will be processed immediately - %% as it is entered first in the event queue and - %% when it is processed there will be length(Events)-1 - %% handshake events left to process before we should - %% process more TLS-records received on the socket. - erlang:length(Events)-1. -update_handshake_history(#hello_verify_request{}, _, Hist) -> - Hist; -update_handshake_history(_, Handshake, Hist) -> - ssl_handshake:update_handshake_history(Hist, iolist_to_binary(Handshake)). prepare_flight(#state{flight_buffer = Flight, connection_states = ConnectionStates0, protocol_buffers = @@ -1217,11 +727,6 @@ prepare_flight(#state{flight_buffer = Flight, protocol_buffers = Buffers#protocol_buffers{ dtls_handshake_next_fragments = [], dtls_handshake_later_fragments = []}}. -new_flight() -> - #{next_sequence => 0, - handshakes => [], - change_cipher_spec => undefined, - handshakes_after_change_cipher_spec => []}. next_flight(Flight) -> Flight#{handshakes => [], @@ -1247,133 +752,11 @@ new_timeout(N) when N =< 30000 -> new_timeout(_) -> 60000. -send_handshake_flight(#state{static_env = #static_env{socket = Socket, - transport_cb = Transport}, - connection_env = #connection_env{negotiated_version = Version}, - flight_buffer = #{handshakes := Flight, - change_cipher_spec := undefined}, - connection_states = ConnectionStates0, - ssl_options = #{log_level := LogLevel}} = State0, - Epoch) -> - PMTUEstimate = 1400, %% TODO make configurable - #{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0, - MaxSize = min(MaxFragmentLength, PMTUEstimate), - {Encoded, ConnectionStates} = - encode_handshake_flight(lists:reverse(Flight), Version, MaxSize, Epoch, ConnectionStates0), - send(Transport, Socket, Encoded), - ssl_logger:debug(LogLevel, outbound, 'record', Encoded), - {State0#state{connection_states = ConnectionStates}, []}; - -send_handshake_flight(#state{static_env = #static_env{socket = Socket, - transport_cb = Transport}, - connection_env = #connection_env{negotiated_version = Version}, - flight_buffer = #{handshakes := [_|_] = Flight0, - change_cipher_spec := ChangeCipher, - handshakes_after_change_cipher_spec := []}, - connection_states = ConnectionStates0, - ssl_options = #{log_level := LogLevel}} = State0, - Epoch) -> - PMTUEstimate = 1400, %% TODO make configurable - #{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0, - MaxSize = min(MaxFragmentLength, PMTUEstimate), - {HsBefore, ConnectionStates1} = - encode_handshake_flight(lists:reverse(Flight0), Version, MaxSize, Epoch, ConnectionStates0), - {EncChangeCipher, ConnectionStates} = encode_change_cipher(ChangeCipher, Version, Epoch, ConnectionStates1), - - send(Transport, Socket, [HsBefore, EncChangeCipher]), - ssl_logger:debug(LogLevel, outbound, 'record', [HsBefore]), - ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), - {State0#state{connection_states = ConnectionStates}, []}; - -send_handshake_flight(#state{static_env = #static_env{socket = Socket, - transport_cb = Transport}, - connection_env = #connection_env{negotiated_version = Version}, - flight_buffer = #{handshakes := [_|_] = Flight0, - change_cipher_spec := ChangeCipher, - handshakes_after_change_cipher_spec := Flight1}, - connection_states = ConnectionStates0, - ssl_options = #{log_level := LogLevel}} = State0, - Epoch) -> - PMTUEstimate = 1400, %% TODO make configurable - #{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0, - MaxSize = min(MaxFragmentLength, PMTUEstimate), - {HsBefore, ConnectionStates1} = - encode_handshake_flight(lists:reverse(Flight0), Version, MaxSize, Epoch-1, ConnectionStates0), - {EncChangeCipher, ConnectionStates2} = - encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates1), - {HsAfter, ConnectionStates} = - encode_handshake_flight(lists:reverse(Flight1), Version, MaxSize, Epoch, ConnectionStates2), - send(Transport, Socket, [HsBefore, EncChangeCipher, HsAfter]), - ssl_logger:debug(LogLevel, outbound, 'record', [HsBefore]), - ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), - ssl_logger:debug(LogLevel, outbound, 'record', [HsAfter]), - {State0#state{connection_states = ConnectionStates}, []}; - -send_handshake_flight(#state{static_env = #static_env{socket = Socket, - transport_cb = Transport}, - connection_env = #connection_env{negotiated_version = Version}, - flight_buffer = #{handshakes := [], - change_cipher_spec := ChangeCipher, - handshakes_after_change_cipher_spec := Flight1}, - connection_states = ConnectionStates0, - ssl_options = #{log_level := LogLevel}} = State0, - Epoch) -> - PMTUEstimate = 1400, %% TODO make configurable - #{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0, - MaxSize = min(MaxFragmentLength, PMTUEstimate), - {EncChangeCipher, ConnectionStates1} = - encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0), - {HsAfter, ConnectionStates} = - encode_handshake_flight(lists:reverse(Flight1), Version, MaxSize, Epoch, ConnectionStates1), - send(Transport, Socket, [EncChangeCipher, HsAfter]), - ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), - ssl_logger:debug(LogLevel, outbound, 'record', [HsAfter]), - {State0#state{connection_states = ConnectionStates}, []}. - retransmit_epoch(_StateName, #state{connection_states = ConnectionStates}) -> #{epoch := Epoch} = ssl_record:current_connection_state(ConnectionStates, write), Epoch. -ignore_alert(#alert{level = ?FATAL}, #state{protocol_specific = #{ignored_alerts := N, - max_ignored_alerts := N}} = State) -> - {false, State}; -ignore_alert(#alert{level = ?FATAL} = Alert, - #state{protocol_specific = #{ignored_alerts := N} = PS} = State) -> - case is_ignore_alert(Alert) of - true -> - {true, State#state{protocol_specific = PS#{ignored_alerts => N+1}}}; - false -> - {false, State} - end; -ignore_alert(_, State) -> - {false, State}. - -%% RFC 6347 4.1.2.7. Handling Invalid Records -%% recommends to silently ignore invalid DTLS records when -%% upd is the transport. Note we do not support compression so no need -%% include ?DECOMPRESSION_FAILURE -is_ignore_alert(#alert{description = ?BAD_RECORD_MAC}) -> - true; -is_ignore_alert(#alert{description = ?RECORD_OVERFLOW}) -> - true; -is_ignore_alert(#alert{description = ?DECODE_ERROR}) -> - true; -is_ignore_alert(#alert{description = ?DECRYPT_ERROR}) -> - true; -is_ignore_alert(#alert{description = ?ILLEGAL_PARAMETER}) -> - true; -is_ignore_alert(_) -> - false. - -log_ignore_alert(Level, StateName, #alert{where = Location} = Alert, Role) -> - ssl_logger:log(info, - Level, #{alert => Alert, - alerter => ignored, - statename => StateName, - role => Role, - protocol => protocol_name()}, Location). - send_application_data(Data, From, _StateName, #state{static_env = #static_env{socket = Socket, transport_cb = Transport}, @@ -1391,7 +774,7 @@ send_application_data(Data, From, _StateName, {Msgs, ConnectionStates} = dtls_record:encode_data(Data, Version, ConnectionStates0), State = State0#state{connection_states = ConnectionStates}, - case send(Transport, Socket, Msgs) of + case dtls_gen_connection:send(Transport, Socket, Msgs) of ok -> ssl_logger:debug(LogLevel, outbound, 'record', Msgs), ssl_gen_statem:hibernate_after(connection, State, [{reply, From, ok}]); |