@@ -62,6 +62,11 @@ defmodule ExWebRTC.DTLSTransport do
6262 GenServer . call ( dtls_transport , :set_ice_connected )
6363 end
6464
65+ @ spec set_ice_disconnected ( dtls_transport ( ) ) :: :ok
66+ def set_ice_disconnected ( dtls_transport ) do
67+ GenServer . call ( dtls_transport , :set_ice_disconnected )
68+ end
69+
6570 @ spec get_certs_info ( dtls_transport ( ) ) :: % {
6671 local_cert_info: cert_info ( ) ,
6772 remote_cert_info: cert_info ( ) | nil
@@ -131,8 +136,8 @@ defmodule ExWebRTC.DTLSTransport do
131136 remote_cert: nil ,
132137 remote_base64_cert: nil ,
133138 remote_fingerprint: nil ,
134- in_srtp: ExLibSRTP . new ( ) ,
135- out_srtp: ExLibSRTP . new ( ) ,
139+ in_srtp: nil ,
140+ out_srtp: nil ,
136141 # sha256 hex dump
137142 peer_fingerprint: nil ,
138143 dtls_state: :new ,
@@ -187,6 +192,12 @@ defmodule ExWebRTC.DTLSTransport do
187192 { :reply , :ok , state }
188193 end
189194
195+ @ impl true
196+ def handle_call ( :set_ice_disconnected , _from , state ) do
197+ state = % { state | ice_connected: false }
198+ { :reply , :ok , state }
199+ end
200+
190201 @ impl true
191202 def handle_call ( :get_certs_info , _from , state ) do
192203 local_cert_info = % {
@@ -217,9 +228,18 @@ defmodule ExWebRTC.DTLSTransport do
217228 end
218229
219230 @ impl true
220- def handle_call ( { :start_dtls , mode , peer_fingerprint } , _from , % { dtls: nil } = state )
221- when mode in [ :active , :passive ] do
222- Logger . debug ( "Started DTLSTransport with role #{ mode } " )
231+ def handle_call (
232+ { :start_dtls , mode , peer_fingerprint } ,
233+ _from ,
234+ % { dtls: dtls , dtls_state: dtls_state } = state
235+ )
236+ when mode in [ :active , :passive ] and ( dtls == nil or dtls_state == :closed ) do
237+ if dtls_state == :closed do
238+ Logger . debug ( "Re-initializing DTLSTransport with role #{ mode } " )
239+ else
240+ Logger . debug ( "Starting DTLSTransport with role #{ mode } " )
241+ end
242+
223243 ex_dtls_mode = if mode == :active , do: :client , else: :server
224244
225245 dtls =
@@ -238,13 +258,20 @@ defmodule ExWebRTC.DTLSTransport do
238258 data -> send ( self ( ) , { :ex_ice , state . ice_pid , { :data , data } } )
239259 end
240260
241- state = % {
242- state
243- | dtls: dtls ,
244- mode: mode ,
245- peer_fingerprint: peer_fingerprint ,
246- buffered_remote_packets: nil
247- }
261+ state =
262+ % {
263+ state
264+ | dtls: dtls ,
265+ mode: mode ,
266+ in_srtp: ExLibSRTP . new ( ) ,
267+ out_srtp: ExLibSRTP . new ( ) ,
268+ peer_fingerprint: peer_fingerprint ,
269+ # clear remote info in case we are re-initializing dtls transport
270+ remote_cert: nil ,
271+ remote_base64_cert: nil ,
272+ remote_fingerprint: nil
273+ }
274+ |> update_dtls_state ( :new )
248275
249276 { :reply , :ok , state }
250277 end
@@ -262,9 +289,7 @@ defmodule ExWebRTC.DTLSTransport do
262289
263290 @ impl true
264291 def handle_call ( :close , _from , state ) do
265- { :ok , packets } = ExDTLS . close ( state . dtls )
266- :ok = do_send ( state , packets )
267- state = update_dtls_state ( state , :closed , notify: false )
292+ state = do_close ( state , notify: false )
268293 { :reply , :ok , state }
269294 end
270295
@@ -322,6 +347,13 @@ defmodule ExWebRTC.DTLSTransport do
322347 { :noreply , state }
323348 end
324349
350+ @ impl true
351+ def handle_info ( :dtls_timeout , % { dtls: nil } = state ) do
352+ # ignore stale timers that were scheduled before calling
353+ # close/1 or receiving close_notify DTLS alert
354+ { :noreply , state }
355+ end
356+
325357 @ impl true
326358 def handle_info ( :dtls_timeout , % { buffered_local_packets: buffered_local_packets } = state ) do
327359 case ExDTLS . handle_timeout ( state . dtls ) do
@@ -360,8 +392,7 @@ defmodule ExWebRTC.DTLSTransport do
360392 # See W3C WebRTC sec. 5.5.1
361393 # peer_closed_for_writing is returned when the remote side
362394 # sends close_notify alert
363- ExDTLS . close ( state . dtls )
364- state = update_dtls_state ( state , :closed )
395+ state = do_close ( state )
365396 { :noreply , state }
366397
367398 { :error , _reason } ->
@@ -382,9 +413,19 @@ defmodule ExWebRTC.DTLSTransport do
382413 Logger . debug ( "Stopping DTLSTransport with reason: #{ inspect ( reason ) } " )
383414 end
384415
385- defp handle_ice_data ( { :data , _data } , % { dtls_state: dtls_state } = state )
386- when dtls_state in [ :failed , :closed ] do
387- Logger . debug ( "Received DTLS packets in state #{ dtls_state } . Ignoring." )
416+ defp handle_ice_data ( { :data , _data } , % { dtls_state: :closed } = state ) do
417+ # Ignore incoming packets when we are in the closed state.
418+ # IMPORTANT!!! This isn't optimal.
419+ # When DTLSTransport has been closed because of receiving close_notify alert,
420+ # it can still be re-used after doing an ice restart with a different peer.
421+ # In such case, we might start getting remote ICE packets before getting remote SDP answer
422+ # (see the case clause below).
423+ # These packets could be buffered and processed once we receive said SDP answer to speedup
424+ # connection establishment time (instead of waiting for retransmissions).
425+ # However, it is hard to differentiate this case from a case where DTLSTransport received
426+ # close_notify alert, but the remote side behaves incorrectly and is still sending ICE packets.
427+ # Buffering incoming packets in closed state, we don't know whether they belong to current or previous
428+ # session, which can lead to hard to find bugs.
388429 { :ok , state }
389430 end
390431
@@ -542,6 +583,23 @@ defmodule ExWebRTC.DTLSTransport do
542583 % { state | buffered_remote_rtp_packets: [ ] }
543584 end
544585
586+ defp do_close ( state , opts \\ [ ] ) do
587+ { :ok , packets } = ExDTLS . close ( state . dtls )
588+ :ok = do_send ( state , packets )
589+
590+ % {
591+ state
592+ | buffered_local_packets: nil ,
593+ buffered_remote_packets: nil ,
594+ buffered_remote_rtp_packets: [ ] ,
595+ in_srtp: nil ,
596+ out_srtp: nil ,
597+ dtls: nil ,
598+ mode: nil
599+ }
600+ |> update_dtls_state ( :closed , opts )
601+ end
602+
545603 defp do_send ( state , data ) when is_list ( data ) do
546604 Enum . each ( data , & ( :ok = do_send ( state , & 1 ) ) )
547605 end
0 commit comments