Skip to content

Commit 20002ce

Browse files
committed
Don't move to the closed state after receiving close_notify alert
1 parent 83fbefd commit 20002ce

File tree

13 files changed

+213
-101
lines changed

13 files changed

+213
-101
lines changed

examples/chat/lib/chat/peer_handler.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ defmodule Chat.PeerHandler do
106106
defp handle_webrtc_msg({:connection_state_change, conn_state}, state) do
107107
Logger.info("Connection state changed: #{conn_state}")
108108

109-
if conn_state in [:failed, :closed] do
110-
{:stop, {:shutdown, :pc_failed_or_closed}, state}
109+
if conn_state == :failed do
110+
{:stop, {:shutdown, :pc_failed}, state}
111111
else
112112
{:ok, state}
113113
end

examples/dtmf/lib/dtmf/peer_handler.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ defmodule Dtmf.PeerHandler do
125125
defp handle_webrtc_msg({:connection_state_change, conn_state}, state) do
126126
Logger.info("Connection state changed: #{conn_state}")
127127

128-
if conn_state in [:failed, :closed] do
129-
{:stop, {:shutdown, :pc_failed_or_closed}, state}
128+
if conn_state == :failed do
129+
{:stop, {:shutdown, :pc_failed}, state}
130130
else
131131
{:ok, state}
132132
end

examples/echo/lib/echo/peer_handler.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ defmodule Echo.PeerHandler do
115115
defp handle_webrtc_msg({:connection_state_change, conn_state}, state) do
116116
Logger.info("Connection state changed: #{conn_state}")
117117

118-
if conn_state in [:failed, :closed] do
119-
{:stop, {:shutdown, :pc_failed_or_closed}, state}
118+
if conn_state == :failed do
119+
{:stop, {:shutdown, :pc_failed}, state}
120120
else
121121
{:ok, state}
122122
end

examples/save_to_file/lib/save_to_file/peer_handler.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ defmodule SaveToFile.PeerHandler do
136136
defp handle_webrtc_msg({:connection_state_change, conn_state}, state) do
137137
Logger.info("Connection state changed: #{conn_state}")
138138

139-
if conn_state in [:failed, :closed] do
140-
{:stop, {:shutdown, :pc_failed_or_closed}, state}
139+
if conn_state == :failed do
140+
{:stop, {:shutdown, :pc_failed}, state}
141141
else
142142
{:ok, state}
143143
end

examples/send_from_file/lib/send_from_file/peer_handler.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@ defmodule SendFromFile.PeerHandler do
249249
defp handle_webrtc_msg({:connection_state_change, conn_state}, state) do
250250
Logger.info("Connection state changed: #{conn_state}")
251251

252-
if conn_state in [:failed, :closed] do
253-
{:stop, {:shutdown, :pc_failed_or_closed}, state}
252+
if conn_state == :failed do
253+
{:stop, {:shutdown, :pc_failed}, state}
254254
else
255255
{:ok, state}
256256
end

examples/whip_whep/lib/whip_whep/forwarder.ex

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,12 @@ defmodule WhipWhep.Forwarder do
6969
end
7070

7171
@impl true
72-
def handle_info(
73-
{:ex_webrtc, pc, {:connection_state_change, conn_state}},
74-
%{input_pc: pc} = state
75-
)
76-
when conn_state in [:failed, :closed] do
77-
Logger.info("Input peer connection (#{inspect(pc)}) state change: #{conn_state}. Removing.")
72+
def handle_info({:ex_webrtc, pc, {:connection_state_change, :failed}}, %{input_pc: pc} = state) do
73+
Logger.info("Input peer connection (#{inspect(pc)}) state change: failed. Removing.")
7874
:ok = PeerConnection.stop(state.input_pc)
7975

80-
Enum.each(state.pending_outputs, &PeerConnection.close(&1))
81-
Enum.each(state.outputs, fn {pc, _output} -> PeerConnection.close(pc) end)
76+
Enum.each(state.pending_outputs, &PeerConnection.stop(&1))
77+
Enum.each(state.outputs, fn {pc, _output} -> PeerConnection.stop(pc) end)
8278

8379
state = %{
8480
state
@@ -124,9 +120,8 @@ defmodule WhipWhep.Forwarder do
124120
end
125121

126122
@impl true
127-
def handle_info({:ex_webrtc, pc, {:connection_state_change, conn_state}}, state)
128-
when conn_state in [:failed, :closed] do
129-
Logger.info("Output peer connection (#{inspect(pc)}) state change: #{conn_state}. Removing.")
123+
def handle_info({:ex_webrtc, pc, {:connection_state_change, :failed}}, state) do
124+
Logger.info("Output peer connection (#{inspect(pc)}) state change: failed. Removing.")
130125
:ok = PeerConnection.stop(pc)
131126
pending_outputs = List.delete(state.pending_outputs, pc)
132127
outputs = Map.delete(state.outputs, pc)

lib/ex_webrtc/dtls_transport.ex

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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 =
@@ -241,9 +261,15 @@ defmodule ExWebRTC.DTLSTransport do
241261
state = %{
242262
state
243263
| dtls: dtls,
264+
dtls_state: :new,
244265
mode: mode,
266+
in_srtp: ExLibSRTP.new(),
267+
out_srtp: ExLibSRTP.new(),
245268
peer_fingerprint: peer_fingerprint,
246-
buffered_remote_packets: nil
269+
# cleare remote info in case we are re-initializing dtls transport
270+
remote_cert: nil,
271+
remote_base64_cert: nil,
272+
remote_fingerprint: nil
247273
}
248274

249275
{:reply, :ok, state}
@@ -262,9 +288,7 @@ defmodule ExWebRTC.DTLSTransport do
262288

263289
@impl true
264290
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)
291+
state = do_close(state, notify: false)
268292
{:reply, :ok, state}
269293
end
270294

@@ -360,8 +384,7 @@ defmodule ExWebRTC.DTLSTransport do
360384
# See W3C WebRTC sec. 5.5.1
361385
# peer_closed_for_writing is returned when the remote side
362386
# sends close_notify alert
363-
ExDTLS.close(state.dtls)
364-
state = update_dtls_state(state, :closed)
387+
state = do_close(state)
365388
{:noreply, state}
366389

367390
{:error, _reason} ->
@@ -382,9 +405,20 @@ defmodule ExWebRTC.DTLSTransport do
382405
Logger.debug("Stopping DTLSTransport with reason: #{inspect(reason)}")
383406
end
384407

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.")
408+
defp handle_ice_data({:data, _data}, %{dtls_state: :closed} = state) do
409+
# Ignore incoming packets when we are in the closed state.
410+
# IMPORTNAT!!!
411+
# This isn't optimal.
412+
# When DTLSTransport has been closed because of receiving close_notify alert,
413+
# it can still be re-used after doing an ice restart with a different peer.
414+
# In such case, we might start getting remote ICE packets before getting remote SDP answer
415+
# (see the case clause below).
416+
# These packets could be buffered and processed once we receive said SDP answer to speedup
417+
# connection establishment time (instead of waiting for retransmissions).
418+
# However, it is hard to differentiate this case from a case where DTLSTransport received
419+
# close_notify alert, but the remote side behaves incorrectly and still sends ICE packets.
420+
# Buffering incoming packets in closed state, we don't know whether they belong to current or previous
421+
# session, which can lead to hard to find bugs.
388422
{:ok, state}
389423
end
390424

@@ -542,6 +576,23 @@ defmodule ExWebRTC.DTLSTransport do
542576
%{state | buffered_remote_rtp_packets: []}
543577
end
544578

579+
defp do_close(state, opts \\ []) do
580+
{:ok, packets} = ExDTLS.close(state.dtls)
581+
:ok = do_send(state, packets)
582+
583+
%{
584+
state
585+
| buffered_local_packets: nil,
586+
buffered_remote_packets: nil,
587+
buffered_remote_rtp_packets: [],
588+
in_srtp: nil,
589+
out_srtp: nil,
590+
dtls: nil,
591+
mode: nil
592+
}
593+
|> update_dtls_state(:closed, opts)
594+
end
595+
545596
defp do_send(state, data) when is_list(data) do
546597
Enum.each(data, &(:ok = do_send(state, &1)))
547598
end

lib/ex_webrtc/peer_connection.ex

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ defmodule ExWebRTC.PeerConnection do
685685

686686
@impl true
687687
def handle_call(
688-
{:set_sender_code, _sender_id, _codec},
688+
{:set_sender_codec, _sender_id, _codec},
689689
_from,
690690
%{signaling_state: :closed} = state
691691
) do
@@ -1151,7 +1151,7 @@ defmodule ExWebRTC.PeerConnection do
11511151
def handle_call(
11521152
{:create_data_channel, _label, _opts},
11531153
_from,
1154-
%{connection_state: :closed} = state
1154+
%{signaling_state: :closed} = state
11551155
) do
11561156
{:reply, {:error, :invalid_state}, state}
11571157
end
@@ -1382,14 +1382,28 @@ defmodule ExWebRTC.PeerConnection do
13821382

13831383
@impl true
13841384
def handle_call(:close, _from, %{signaling_state: :closed} = state) do
1385-
{:reply, {:error, :invalid_state}, state}
1385+
{:reply, :ok, state}
13861386
end
13871387

13881388
@impl true
13891389
def handle_call(:close, _from, state) do
13901390
Logger.debug("Closing peer connection")
1391-
# don't emit state change events
1392-
state = do_close(state, notify: false)
1391+
transceivers = Enum.map(state.transceivers, &RTPTransceiver.stop(&1))
1392+
sctp_transport = SCTPTransport.close_abruptly(state.sctp_transport)
1393+
:ok = DTLSTransport.close(state.dtls_transport)
1394+
:ok = state.ice_transport.close(state.ice_pid)
1395+
1396+
state =
1397+
%{
1398+
state
1399+
| ice_state: :closed,
1400+
dtls_state: :closed,
1401+
transceivers: transceivers,
1402+
sctp_transport: sctp_transport
1403+
}
1404+
|> update_signaling_state(:closed, notify: false)
1405+
|> update_conn_state(:closed, notify: false)
1406+
13931407
{:reply, :ok, state}
13941408
end
13951409

@@ -1518,6 +1532,10 @@ defmodule ExWebRTC.PeerConnection do
15181532
:ok = DTLSTransport.set_ice_connected(state.dtls_transport)
15191533
end
15201534

1535+
if new_ice_state == :failed do
1536+
:ok = DTLSTransport.set_ice_disconnected(state.dtls_transport)
1537+
end
1538+
15211539
{:noreply, state}
15221540
end
15231541

@@ -1548,17 +1566,12 @@ defmodule ExWebRTC.PeerConnection do
15481566

15491567
next_conn_state = next_conn_state(state.ice_state, new_dtls_state)
15501568

1551-
if next_conn_state == :closed and state.conn_state != :closed do
1552-
state = do_close(state)
1553-
{:noreply, state}
1554-
else
1555-
state =
1556-
%{state | dtls_state: new_dtls_state}
1557-
|> update_conn_state(next_conn_state)
1558-
|> maybe_connect_sctp()
1569+
state =
1570+
%{state | dtls_state: new_dtls_state}
1571+
|> update_conn_state(next_conn_state)
1572+
|> maybe_connect_sctp()
15591573

1560-
{:noreply, state}
1561-
end
1574+
{:noreply, state}
15621575
end
15631576

15641577
@impl true
@@ -2325,13 +2338,8 @@ defmodule ExWebRTC.PeerConnection do
23252338
# the order of these clauses is important
23262339
defp next_conn_state(ice_state, dtls_state)
23272340

2328-
# Give closed precedence over failed.
2329-
# Failed connection can be restarted.
2330-
# Closed connection can't be reused.
23312341
defp next_conn_state(:closed, _dtls_state), do: :closed
23322342

2333-
defp next_conn_state(_ice_state, :closed), do: :closed
2334-
23352343
defp next_conn_state(:failed, _dtls_state), do: :failed
23362344

23372345
defp next_conn_state(_ice_state, :failed), do: :failed
@@ -2342,8 +2350,9 @@ defmodule ExWebRTC.PeerConnection do
23422350
when ice_state in [:new, :checking] or dtls_state in [:new, :connecting],
23432351
do: :connecting
23442352

2345-
defp next_conn_state(ice_state, :connected) when ice_state in [:connected, :completed],
2346-
do: :connected
2353+
defp next_conn_state(ice_state, dtls_state)
2354+
when ice_state in [:connected, :completed] and dtls_state in [:connected, :closed],
2355+
do: :connected
23472356

23482357
defp update_conn_state(state, conn_state, opts \\ [])
23492358
defp update_conn_state(%{conn_state: conn_state} = state, conn_state, _opts), do: state
@@ -2551,27 +2560,6 @@ defmodule ExWebRTC.PeerConnection do
25512560
%SessionDescription{type: type, sdp: to_string(sdp)}
25522561
end
25532562

2554-
defp do_close(state, opts \\ []) do
2555-
transceivers = Enum.map(state.transceivers, &RTPTransceiver.stop(&1))
2556-
sctp_transport = SCTPTransport.close_abruptly(state.sctp_transport)
2557-
:ok = DTLSTransport.close(state.dtls_transport)
2558-
:ok = state.ice_transport.close(state.ice_pid)
2559-
2560-
if opts[:notify] != false do
2561-
notify(state.owner, {:ice_connection_state_change, :closed})
2562-
end
2563-
2564-
%{
2565-
state
2566-
| ice_state: :closed,
2567-
dtls_state: :closed,
2568-
transceivers: transceivers,
2569-
sctp_transport: sctp_transport
2570-
}
2571-
|> update_signaling_state(:closed, opts)
2572-
|> update_conn_state(:closed, opts)
2573-
end
2574-
25752563
defp generate_ssrcs(state) do
25762564
generate_ssrcs(state.transceivers, Map.keys(state.demuxer.ssrc_to_mid))
25772565
end

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ defmodule ExWebRTC.MixProject do
6262
{:ex_ice, github: "elixir-webrtc/ex_ice", branch: "close"},
6363
# {:ex_dtls, "~> 0.17.0"},
6464
# {:ex_dtls, path: "/home/michal/Repos/elixir-webrtc/ex_dtls"},
65-
{:ex_dtls, github: "elixir-webrtc/ex_dtls", branch: "disconnect"},
65+
{:ex_dtls, github: "elixir-webrtc/ex_dtls"},
6666
{:ex_libsrtp, "~> 0.7.1"},
6767
{:ex_rtp, "~> 0.4.0"},
6868
{:ex_rtcp, "~> 0.4.0"},

mix.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"},
1212
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
1313
"ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"},
14-
"ex_dtls": {:git, "https://github.com/elixir-webrtc/ex_dtls.git", "fb6d85d7e285154a2ad68d69ccd30d423efe6f55", [branch: "disconnect"]},
14+
"ex_dtls": {:git, "https://github.com/elixir-webrtc/ex_dtls.git", "2773c01f0593cf31d5bf5534c40b2def17abc921", []},
1515
"ex_ice": {:git, "https://github.com/elixir-webrtc/ex_ice.git", "c13e5ad9d379efc5cad3977b3676cf5c34113a73", [branch: "close"]},
1616
"ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"},
1717
"ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"},

0 commit comments

Comments
 (0)