aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/dtls_connection.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/dtls_connection.erl')
-rw-r--r--lib/ssl/src/dtls_connection.erl295
1 files changed, 170 insertions, 125 deletions
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 03725089dd..37719ad439 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2018. 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.
@@ -36,26 +36,26 @@
%% Internal application API
%% Setup
--export([start_fsm/8, start_link/7, init/1]).
+-export([start_fsm/8, start_link/7, init/1, pids/1]).
%% State transition handling
--export([next_record/1, next_event/3, next_event/4, handle_common_event/4]).
+-export([next_event/3, next_event/4, handle_common_event/4]).
%% Handshake handling
-export([renegotiate/2, send_handshake/2,
queue_handshake/2, queue_change_cipher/2,
- reinit_handshake_data/1, select_sni_extension/1, empty_connection_state/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, close/5, protocol_name/0]).
+-export([encode_alert/3, send_alert/2, send_alert_in_connection/2, close/5, protocol_name/0]).
%% Data handling
--export([encode_data/3, passive_receive/2, next_record_if_active/1,
+-export([encode_data/3, next_record/1,
send/3, socket/5, setopts/3, getopts/3]).
%% gen_statem state functions
-export([init/3, error/3, downgrade/3, %% Initiation and take down states
- hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states
+ hello/3, user_hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states
connection/3]).
%% gen_statem callbacks
-export([callback_mode/0, terminate/3, code_change/4, format_status/2]).
@@ -72,9 +72,8 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker}
try
{ok, Pid} = dtls_connection_sup:start_child([Role, Host, Port, Socket,
Opts, User, CbInfo]),
- {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, Pid, CbModule, Tracker),
- ok = ssl_connection:handshake(SslSocket, Timeout),
- {ok, SslSocket}
+ {ok, SslSocket} = ssl_connection:socket_control(?MODULE, Socket, [Pid], CbModule, Tracker),
+ ssl_connection:handshake(SslSocket, Timeout)
catch
error:{badmatch, {error, _} = Error} ->
Error
@@ -92,14 +91,19 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
init([Role, Host, Port, Socket, Options, User, CbInfo]) ->
process_flag(trap_exit, true),
- State0 = 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_connection:ssl_config(State0#state.ssl_options, Role, State0),
gen_statem:enter_loop(?MODULE, [], init, State)
catch
throw:Error ->
- gen_statem:enter_loop(?MODULE, [], error, {Error,State0})
+ EState = State0#state{protocol_specific = Map#{error => Error}},
+ gen_statem:enter_loop(?MODULE, [], error, EState)
end.
+
+pids(_) ->
+ [self()].
+
%%====================================================================
%% State transition handling
%%====================================================================
@@ -138,9 +142,8 @@ next_record(#state{protocol_buffers =
Buffers#protocol_buffers{dtls_cipher_texts = Rest},
connection_states = ConnectionStates});
next_record(#state{role = server,
- socket = {Listener, {Client, _}},
- transport_cb = gen_udp} = State) ->
- dtls_udp_listener:active_once(Listener, Client, self()),
+ socket = {Listener, {Client, _}}} = State) ->
+ dtls_packet_demux:active_once(Listener, Client, self()),
{no_record, State};
next_record(#state{role = client,
socket = {_Server, Socket} = DTLSSocket,
@@ -159,9 +162,9 @@ next_record(State) ->
next_event(StateName, Record, State) ->
next_event(StateName, Record, State, []).
-next_event(connection = StateName, no_record,
+next_event(StateName, no_record,
#state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) ->
- case next_record_if_active(State0) of
+ case next_record(State0) of
{no_record, State} ->
ssl_connection:hibernate_after(StateName, State, Actions);
{#ssl_tls{epoch = CurrentEpoch,
@@ -175,21 +178,18 @@ next_event(connection = StateName, no_record,
{#ssl_tls{epoch = Epoch,
type = ?HANDSHAKE,
version = _Version}, State1} = _Record when Epoch == CurrentEpoch-1 ->
- {State2, MoreActions} = send_handshake_flight(State1, CurrentEpoch),
- {NextRecord, State} = next_record(State2),
- next_event(StateName, NextRecord, State, Actions ++ MoreActions);
+ {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 ->
- {State2, MoreActions} = send_handshake_flight(State1, CurrentEpoch),
- {NextRecord, State} = next_record(State2),
- next_event(StateName, NextRecord, State, Actions ++ MoreActions);
+ {State, MoreActions} = send_handshake_flight(State1, CurrentEpoch),
+ next_event(StateName, no_record, State, Actions ++ MoreActions);
{#ssl_tls{epoch = _Epoch,
- version = _Version}, State1} ->
+ version = _Version}, State} ->
%% TODO maybe buffer later epoch
- {Record, State} = next_record(State1),
- next_event(StateName, Record, State, Actions);
+ next_event(StateName, no_record, State, Actions);
{#alert{} = Alert, State} ->
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end;
@@ -207,24 +207,20 @@ next_event(connection = StateName, Record,
#ssl_tls{epoch = Epoch,
type = ?HANDSHAKE,
version = _Version} when Epoch == CurrentEpoch-1 ->
- {State1, MoreActions} = send_handshake_flight(State0, CurrentEpoch),
- {NextRecord, State} = next_record(State1),
- next_event(StateName, NextRecord, State, Actions ++ MoreActions);
+ {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 ->
- {State1, MoreActions} = send_handshake_flight(State0, CurrentEpoch),
- {NextRecord, State} = next_record(State1),
- next_event(StateName, NextRecord, State, Actions ++ MoreActions);
+ {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
- no_record ->
- {next_state, StateName, State0, Actions};
#ssl_tls{epoch = CurrentEpoch,
version = Version} = Record ->
State = dtls_version(StateName, Version, State0),
@@ -233,8 +229,7 @@ next_event(StateName, Record,
#ssl_tls{epoch = _Epoch,
version = _Version} = _Record ->
%% TODO maybe buffer later epoch
- {Record, State} = next_record(State0),
- next_event(StateName, Record, State, Actions);
+ next_event(StateName, no_record, State0, Actions);
#alert{} = Alert ->
{next_state, StateName, State0, [{next_event, internal, Alert} | Actions]}
end.
@@ -251,8 +246,7 @@ handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE,
try
case dtls_handshake:get_dtls_handshake(Version, Data, Buffers0) of
{[], Buffers} ->
- {Record, State} = next_record(State0#state{protocol_buffers = Buffers}),
- next_event(StateName, Record, State);
+ next_event(StateName, no_record, State0#state{protocol_buffers = Buffers});
{Packets, Buffers} ->
State = State0#state{protocol_buffers = Buffers},
Events = dtls_handshake_events(Packets),
@@ -288,15 +282,12 @@ handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) ->
renegotiate(#state{role = client} = State, Actions) ->
%% Handle same way as if server requested
%% the renegotiation
- {next_state, connection, State,
- [{next_event, internal, #hello_request{}} | Actions]};
-
+ next_event(connection, no_record, State, [{next_event, internal, #hello_request{}} | Actions]);
renegotiate(#state{role = server} = State0, Actions) ->
HelloRequest = ssl_handshake:hello_request(),
State1 = prepare_flight(State0),
- {State2, MoreActions} = send_handshake(HelloRequest, State1),
- {Record, State} = next_record(State2),
- next_event(hello, Record, State, Actions ++ MoreActions).
+ {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),
@@ -329,10 +320,14 @@ queue_change_cipher(ChangeCipher, #state{flight_buffer = Flight,
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{protocol_buffers = Buffers} = State) ->
State#state{premaster_secret = undefined,
public_key_info = undefined,
- tls_handshake_history = ssl_handshake:init_handshake_history(),
+ tls_handshake_history = ssl_handshake:init_handshake_history(),
flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
flight_buffer = new_flight(),
protocol_buffers =
@@ -366,6 +361,10 @@ send_alert(Alert, #state{negotiated_version = Version,
send(Transport, Socket, BinMsg),
State0#state{connection_states = ConnectionStates}.
+send_alert_in_connection(Alert, State) ->
+ _ = send_alert(Alert, State),
+ ok.
+
close(downgrade, _,_,_,_) ->
ok;
%% Other
@@ -382,23 +381,6 @@ protocol_name() ->
encode_data(Data, Version, ConnectionStates0)->
dtls_record:encode_data(Data, Version, ConnectionStates0).
-passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
- case Buffer of
- <<>> ->
- {Record, State} = next_record(State0),
- next_event(StateName, Record, State);
- _ ->
- {Record, State} = ssl_connection:read_application_data(<<>>, State0),
- next_event(StateName, Record, State)
- end.
-next_record_if_active(State =
- #state{socket_options =
- #socket_options{active = false}}) ->
- {no_record ,State};
-
-next_record_if_active(State) ->
- next_record(State).
-
send(Transport, {_, {{_,_}, _} = Socket}, Data) ->
send(Transport, Socket, Data);
send(Transport, Socket, Data) ->
@@ -440,16 +422,15 @@ init({call, From}, {start, Timeout},
HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions),
State1 = prepare_flight(State0#state{negotiated_version = Version}),
{State2, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}),
- State3 = State2#state{negotiated_version = Version, %% Requested version
- session =
- Session0#session{session_id = Hello#client_hello.session_id},
- start_or_recv_from = From,
- timer = Timer,
- flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}
- },
- {Record, State} = next_record(State3),
- next_event(hello, Record, State, Actions);
-init({call, _} = Type, Event, #state{role = server, transport_cb = gen_udp} = State) ->
+ State = State2#state{negotiated_version = Version, %% Requested version
+ session =
+ Session0#session{session_id = Hello#client_hello.session_id},
+ start_or_recv_from = From,
+ timer = Timer,
+ flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT}
+ },
+ next_event(hello, no_record, State, Actions);
+init({call, _} = Type, Event, #state{role = server, data_tag = udp} = State) ->
Result = gen_handshake(?FUNCTION_NAME, Type, Event,
State#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
protocol_specific = #{current_cookie_secret => dtls_v1:cookie_secret(),
@@ -458,7 +439,6 @@ init({call, _} = Type, Event, #state{role = server, transport_cb = gen_udp} = St
max_ignored_alerts => 10}}),
erlang:send_after(dtls_v1:cookie_timeout(), self(), new_cookie_secret),
Result;
-
init({call, _} = Type, Event, #state{role = server} = State) ->
%% I.E. DTLS over sctp
gen_handshake(?FUNCTION_NAME, Type, Event, State#state{flight_state = reliable});
@@ -472,8 +452,10 @@ init(Type, Event, State) ->
%%--------------------------------------------------------------------
error(enter, _, State) ->
{keep_state, State};
-error({call, From}, {start, _Timeout}, {Error, State}) ->
- {stop_and_reply, normal, {reply, From, {error, Error}}, State};
+error({call, From}, {start, _Timeout},
+ #state{protocol_specific = #{error := Error}} = State) ->
+ ssl_connection:stop_and_reply(
+ normal, {reply, From, {error, Error}}, State);
error({call, _} = Call, Msg, State) ->
gen_handshake(?FUNCTION_NAME, Call, Msg, State);
error(_, _, _) ->
@@ -491,10 +473,11 @@ hello(enter, _, #state{role = client} = State0) ->
{State, Actions} = handle_flight_timer(State0),
{keep_state, State, Actions};
hello(internal, #client_hello{cookie = <<>>,
- client_version = Version} = Hello, #state{role = server,
- transport_cb = Transport,
- socket = Socket,
- protocol_specific = #{current_cookie_secret := Secret}} = State0) ->
+ client_version = Version} = Hello,
+ #state{role = server,
+ transport_cb = Transport,
+ socket = Socket,
+ protocol_specific = #{current_cookie_secret := Secret}} = State0) ->
{ok, {IP, Port}} = dtls_socket:peername(Transport, Socket),
Cookie = dtls_handshake:cookie(Secret, IP, Port, Hello),
%% FROM RFC 6347 regarding HelloVerifyRequest message:
@@ -505,27 +488,9 @@ hello(internal, #client_hello{cookie = <<>>,
%% negotiated.
VerifyRequest = dtls_handshake:hello_verify_request(Cookie, ?HELLO_VERIFY_REQUEST_VERSION),
State1 = prepare_flight(State0#state{negotiated_version = Version}),
- {State2, Actions} = send_handshake(VerifyRequest, State1),
- {Record, State} = next_record(State2),
- next_event(?FUNCTION_NAME, Record, State#state{tls_handshake_history = ssl_handshake:init_handshake_history()}, Actions);
-hello(internal, #client_hello{cookie = Cookie} = Hello, #state{role = server,
- transport_cb = Transport,
- socket = Socket,
- protocol_specific = #{current_cookie_secret := Secret,
- previous_cookie_secret := PSecret}} = State0) ->
- {ok, {IP, Port}} = dtls_socket:peername(Transport, Socket),
- case dtls_handshake:cookie(Secret, IP, Port, Hello) of
- Cookie ->
- handle_client_hello(Hello, State0);
- _ ->
- case dtls_handshake:cookie(PSecret, IP, Port, Hello) of
- Cookie ->
- handle_client_hello(Hello, State0);
- _ ->
- %% Handle bad cookie as new cookie request RFC 6347 4.1.2
- hello(internal, Hello#client_hello{cookie = <<>>}, State0)
- end
- end;
+ {State, Actions} = send_handshake(VerifyRequest, State1),
+ next_event(?FUNCTION_NAME, no_record,
+ State#state{tls_handshake_history = ssl_handshake:init_handshake_history()}, Actions);
hello(internal, #hello_verify_request{cookie = Cookie}, #state{role = client,
host = Host, port = Port,
ssl_options = SslOpts,
@@ -544,12 +509,42 @@ hello(internal, #hello_verify_request{cookie = Cookie}, #state{role = client,
State1 = prepare_flight(State0#state{tls_handshake_history = ssl_handshake:init_handshake_history()}),
{State2, Actions} = send_handshake(Hello, State1),
- State3 = State2#state{negotiated_version = Version, %% Requested version
- session =
- Session0#session{session_id =
- Hello#client_hello.session_id}},
- {Record, State} = next_record(State3),
- next_event(?FUNCTION_NAME, Record, State, Actions);
+ State = State2#state{negotiated_version = Version, %% Requested version
+ session =
+ Session0#session{session_id =
+ Hello#client_hello.session_id}},
+ next_event(?FUNCTION_NAME, no_record, State, Actions);
+hello(internal, #client_hello{extensions = Extensions} = Hello,
+ #state{ssl_options = #ssl_options{handshake = hello},
+ start_or_recv_from = From} = State) ->
+ {next_state, user_hello, State#state{start_or_recv_from = undefined,
+ hello = Hello},
+ [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]};
+hello(internal, #server_hello{extensions = Extensions} = Hello,
+ #state{ssl_options = #ssl_options{handshake = hello},
+ start_or_recv_from = From} = State) ->
+ {next_state, user_hello, State#state{start_or_recv_from = undefined,
+ hello = Hello},
+ [{reply, From, {ok, ssl_connection:map_extensions(Extensions)}}]};
+hello(internal, #client_hello{cookie = Cookie} = Hello,
+ #state{role = server,
+ transport_cb = Transport,
+ socket = Socket,
+ protocol_specific = #{current_cookie_secret := Secret,
+ previous_cookie_secret := PSecret}} = State0) ->
+ {ok, {IP, Port}} = dtls_socket:peername(Transport, Socket),
+ case dtls_handshake:cookie(Secret, IP, Port, Hello) of
+ Cookie ->
+ handle_client_hello(Hello, State0);
+ _ ->
+ case dtls_handshake:cookie(PSecret, IP, Port, Hello) of
+ Cookie ->
+ handle_client_hello(Hello, State0);
+ _ ->
+ %% Handle bad cookie as new cookie request RFC 6347 4.1.2
+ hello(internal, Hello#client_hello{cookie = <<>>}, State0)
+ end
+ end;
hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
negotiated_version = ReqVersion,
@@ -569,6 +564,11 @@ hello(internal, {handshake, {#client_hello{cookie = <<>>} = Handshake, _}}, Stat
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),
+ %% This will reset the retransmission timer by repeating the enter state event
+ {repeat_state, State, Actions};
hello(info, Event, State) ->
gen_info(Event, ?FUNCTION_NAME, State);
hello(state_timeout, Event, State) ->
@@ -576,6 +576,11 @@ hello(state_timeout, Event, State) ->
hello(Type, Event, State) ->
gen_handshake(?FUNCTION_NAME, Type, Event, State).
+user_hello(enter, _, State) ->
+ {keep_state, State};
+user_hello(Type, Event, State) ->
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
+
%%--------------------------------------------------------------------
-spec abbreviated(gen_statem:event_type(), term(), #state{}) ->
gen_statem:state_function_result().
@@ -612,8 +617,7 @@ certify(internal = Type, #server_hello_done{} = Event, State) ->
ssl_connection:certify(Type, Event, prepare_flight(State), ?MODULE);
certify(internal, #change_cipher_spec{type = <<1>>}, State0) ->
{State1, Actions0} = send_handshake_flight(State0, retransmit_epoch(?FUNCTION_NAME, State0)),
- {Record, State2} = next_record(State1),
- {next_state, ?FUNCTION_NAME, State, Actions} = next_event(?FUNCTION_NAME, Record, State2, Actions0),
+ {next_state, ?FUNCTION_NAME, State, Actions} = 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) ->
@@ -666,13 +670,11 @@ connection(internal, #hello_request{}, #state{host = Host, port = Port,
Version = Hello#client_hello.client_version,
HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions),
State1 = prepare_flight(State0),
- {State2, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}),
- {Record, State} =
- next_record(
- State2#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
- session = Session0#session{session_id
- = Hello#client_hello.session_id}}),
- next_event(hello, Record, State, Actions);
+ {State, Actions} = send_handshake(Hello, State1#state{negotiated_version = HelloVersion}),
+ next_event(hello, no_record, State#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
+ session = Session0#session{session_id
+ = Hello#client_hello.session_id}},
+ Actions);
connection(internal, #client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) ->
%% Mitigate Computational DoS attack
%% http://www.educatedguesswork.org/2011/10/ssltls_and_computational_dos.html
@@ -687,6 +689,12 @@ connection(internal, #client_hello{}, #state{role = server, allow_renegotiate =
State1 = send_alert(Alert, State0),
{Record, State} = ssl_connection:prepare_connection(State1, ?MODULE),
next_event(?FUNCTION_NAME, Record, State);
+connection({call, From}, {application_data, Data}, State) ->
+ try
+ send_application_data(Data, From, ?FUNCTION_NAME, State)
+ catch throw:Error ->
+ ssl_connection:hibernate_after(?FUNCTION_NAME, State, [{reply, From, Error}])
+ end;
connection(Type, Event, State) ->
ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE).
@@ -840,7 +848,7 @@ handle_info({Protocol, _, _, _, Data}, StateName,
next_event(StateName, Record, State);
#alert{} = Alert ->
ssl_connection:handle_normal_shutdown(Alert, StateName, State0),
- {stop, {shutdown, own_alert}}
+ ssl_connection:stop({shutdown, own_alert}, State0)
end;
handle_info({CloseTag, Socket}, StateName,
#state{socket = Socket,
@@ -865,7 +873,7 @@ handle_info({CloseTag, Socket}, StateName,
ok
end,
ssl_connection:handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State),
- {stop, {shutdown, transport_closed}};
+ ssl_connection: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
@@ -886,21 +894,20 @@ handle_state_timeout(flight_retransmission_timeout, StateName,
#state{flight_state = {retransmit, NextTimeout}} = State0) ->
{State1, Actions0} = send_handshake_flight(State0#state{flight_state = {retransmit, NextTimeout}},
retransmit_epoch(StateName, State0)),
- {Record, State2} = next_record(State1),
- {next_state, StateName, State, Actions} = next_event(StateName, Record, State2, Actions0),
+ {next_state, StateName, State, Actions} = 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) ->
+handle_alerts(_, {stop, _, _} = Stop) ->
Stop;
handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State));
handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)).
-handle_own_alert(Alert, Version, StateName, #state{transport_cb = gen_udp,
+handle_own_alert(Alert, Version, StateName, #state{data_tag = udp,
role = Role,
ssl_options = Options} = State0) ->
case ignore_alert(Alert, State0) of
@@ -969,8 +976,7 @@ unprocessed_events(Events) ->
update_handshake_history(#hello_verify_request{}, _, Hist) ->
Hist;
update_handshake_history(_, Handshake, Hist) ->
- %% DTLS never needs option "v2_hello_compatible" to be true
- ssl_handshake:update_handshake_history(Hist, iolist_to_binary(Handshake), false).
+ ssl_handshake:update_handshake_history(Hist, iolist_to_binary(Handshake)).
prepare_flight(#state{flight_buffer = Flight,
connection_states = ConnectionStates0,
protocol_buffers =
@@ -992,10 +998,10 @@ next_flight(Flight) ->
change_cipher_spec => undefined,
handshakes_after_change_cipher_spec => []}.
-handle_flight_timer(#state{transport_cb = gen_udp,
+handle_flight_timer(#state{data_tag = udp,
flight_state = {retransmit, Timeout}} = State) ->
start_retransmision_timer(Timeout, State);
-handle_flight_timer(#state{transport_cb = gen_udp,
+handle_flight_timer(#state{data_tag = udp,
flight_state = connection} = State) ->
{State, []};
handle_flight_timer(State) ->
@@ -1109,3 +1115,42 @@ log_ignore_alert(true, StateName, Alert, Role) ->
[Role, StateName, Txt]);
log_ignore_alert(false, _, _,_) ->
ok.
+
+send_application_data(Data, From, _StateName,
+ #state{socket = Socket,
+ negotiated_version = Version,
+ protocol_cb = Connection,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}} = State0) ->
+
+ case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of
+ true ->
+ renegotiate(State0#state{renegotiation = {true, internal}},
+ [{next_event, {call, From}, {application_data, Data}}]);
+ false ->
+ {Msgs, ConnectionStates} =
+ Connection:encode_data(Data, Version, ConnectionStates0),
+ State = State0#state{connection_states = ConnectionStates},
+ case Connection:send(Transport, Socket, Msgs) of
+ ok ->
+ ssl_connection:hibernate_after(connection, State, [{reply, From, ok}]);
+ Result ->
+ ssl_connection:hibernate_after(connection, State, [{reply, From, Result}])
+ end
+ end.
+
+time_to_renegotiate(_Data,
+ #{current_write := #{sequence_number := Num}},
+ RenegotiateAt) ->
+
+ %% We could do test:
+ %% is_time_to_renegotiate((erlang:byte_size(_Data) div
+ %% ?MAX_PLAIN_TEXT_LENGTH) + 1, RenegotiateAt), but we chose to
+ %% have a some what lower renegotiateAt and a much cheaper test
+ is_time_to_renegotiate(Num, RenegotiateAt).
+
+is_time_to_renegotiate(N, M) when N < M->
+ false;
+is_time_to_renegotiate(_,_) ->
+ true.