Skip to content

Commit ca57d69

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

File tree

13 files changed

+205
-103
lines changed

13 files changed

+205
-103
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: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ defmodule ExWebRTC.DTLSTransport do
131131
remote_cert: nil,
132132
remote_base64_cert: nil,
133133
remote_fingerprint: nil,
134-
in_srtp: ExLibSRTP.new(),
135-
out_srtp: ExLibSRTP.new(),
134+
in_srtp: nil,
135+
out_srtp: nil,
136136
# sha256 hex dump
137137
peer_fingerprint: nil,
138138
dtls_state: :new,
@@ -217,9 +217,18 @@ defmodule ExWebRTC.DTLSTransport do
217217
end
218218

219219
@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}")
220+
def handle_call(
221+
{:start_dtls, mode, peer_fingerprint},
222+
_from,
223+
%{dtls: dtls, dtls_state: dtls_state} = state
224+
)
225+
when mode in [:active, :passive] and (dtls == nil or dtls_state == :closed) do
226+
if dtls_state == :closed do
227+
Logger.debug("Re-initializing DTLSTransport with role #{mode}")
228+
else
229+
Logger.debug("Starting DTLSTransport with role #{mode}")
230+
end
231+
223232
ex_dtls_mode = if mode == :active, do: :client, else: :server
224233

225234
dtls =
@@ -234,16 +243,26 @@ defmodule ExWebRTC.DTLSTransport do
234243
# plant the buffered remote packets in the mailbox
235244
# as if it came from the ICE transport
236245
case state.buffered_remote_packets do
237-
nil -> :ok
238-
data -> send(self(), {:ex_ice, state.ice_pid, {:data, data}})
246+
nil ->
247+
:ok
248+
249+
data ->
250+
dbg(:sending)
251+
send(self(), {:ex_ice, state.ice_pid, {:data, data}})
239252
end
240253

241254
state = %{
242255
state
243256
| dtls: dtls,
257+
dtls_state: :new,
244258
mode: mode,
259+
in_srtp: ExLibSRTP.new(),
260+
out_srtp: ExLibSRTP.new(),
245261
peer_fingerprint: peer_fingerprint,
246-
buffered_remote_packets: nil
262+
# cleare remote info in case we are re-initializing dtls transport
263+
remote_cert: nil,
264+
remote_base64_cert: nil,
265+
remote_fingerprint: nil
247266
}
248267

249268
{:reply, :ok, state}
@@ -262,9 +281,7 @@ defmodule ExWebRTC.DTLSTransport do
262281

263282
@impl true
264283
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)
284+
state = do_close(state, notify: false)
268285
{:reply, :ok, state}
269286
end
270287

@@ -360,8 +377,7 @@ defmodule ExWebRTC.DTLSTransport do
360377
# See W3C WebRTC sec. 5.5.1
361378
# peer_closed_for_writing is returned when the remote side
362379
# sends close_notify alert
363-
ExDTLS.close(state.dtls)
364-
state = update_dtls_state(state, :closed)
380+
state = do_close(state)
365381
{:noreply, state}
366382

367383
{:error, _reason} ->
@@ -382,9 +398,20 @@ defmodule ExWebRTC.DTLSTransport do
382398
Logger.debug("Stopping DTLSTransport with reason: #{inspect(reason)}")
383399
end
384400

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

@@ -542,6 +569,23 @@ defmodule ExWebRTC.DTLSTransport do
542569
%{state | buffered_remote_rtp_packets: []}
543570
end
544571

572+
defp do_close(state, opts \\ []) do
573+
{:ok, packets} = ExDTLS.close(state.dtls)
574+
:ok = do_send(state, packets)
575+
576+
%{
577+
state
578+
| buffered_local_packets: nil,
579+
buffered_remote_packets: nil,
580+
buffered_remote_rtp_packets: [],
581+
in_srtp: nil,
582+
out_srtp: nil,
583+
dtls: nil,
584+
mode: nil
585+
}
586+
|> update_dtls_state(:closed, opts)
587+
end
588+
545589
defp do_send(state, data) when is_list(data) do
546590
Enum.each(data, &(:ok = do_send(state, &1)))
547591
end

lib/ex_webrtc/peer_connection.ex

Lines changed: 27 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

@@ -1548,17 +1562,12 @@ defmodule ExWebRTC.PeerConnection do
15481562

15491563
next_conn_state = next_conn_state(state.ice_state, new_dtls_state)
15501564

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()
1565+
state =
1566+
%{state | dtls_state: new_dtls_state}
1567+
|> update_conn_state(next_conn_state)
1568+
|> maybe_connect_sctp()
15591569

1560-
{:noreply, state}
1561-
end
1570+
{:noreply, state}
15621571
end
15631572

15641573
@impl true
@@ -2325,13 +2334,8 @@ defmodule ExWebRTC.PeerConnection do
23252334
# the order of these clauses is important
23262335
defp next_conn_state(ice_state, dtls_state)
23272336

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

2333-
defp next_conn_state(_ice_state, :closed), do: :closed
2334-
23352339
defp next_conn_state(:failed, _dtls_state), do: :failed
23362340

23372341
defp next_conn_state(_ice_state, :failed), do: :failed
@@ -2342,8 +2346,9 @@ defmodule ExWebRTC.PeerConnection do
23422346
when ice_state in [:new, :checking] or dtls_state in [:new, :connecting],
23432347
do: :connecting
23442348

2345-
defp next_conn_state(ice_state, :connected) when ice_state in [:connected, :completed],
2346-
do: :connected
2349+
defp next_conn_state(ice_state, dtls_state)
2350+
when ice_state in [:connected, :completed] and dtls_state in [:connected, :closed],
2351+
do: :connected
23472352

23482353
defp update_conn_state(state, conn_state, opts \\ [])
23492354
defp update_conn_state(%{conn_state: conn_state} = state, conn_state, _opts), do: state
@@ -2551,27 +2556,6 @@ defmodule ExWebRTC.PeerConnection do
25512556
%SessionDescription{type: type, sdp: to_string(sdp)}
25522557
end
25532558

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-
25752559
defp generate_ssrcs(state) do
25762560
generate_ssrcs(state.transceivers, Map.keys(state.demuxer.ssrc_to_mid))
25772561
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)