Skip to content

Commit 21436bb

Browse files
committed
Ignore data frome stale sockets, timers, etc. Clean up state when connection is failed or completed
1 parent bed3d9c commit 21436bb

File tree

3 files changed

+354
-40
lines changed

3 files changed

+354
-40
lines changed

lib/ex_ice/priv/conn_check_handler/controlling.ex

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,18 @@ defmodule ExICE.Priv.ConnCheckHandler.Controlling do
5252
@impl true
5353
def update_nominated_flag(%ICEAgent{eoc: true} = ice_agent, pair_id, true) do
5454
Logger.debug("Nomination succeeded. Selecting pair: #{inspect(pair_id)}")
55-
ice_agent = ICEAgent.change_connection_state(ice_agent, :completed)
5655

5756
pair = Map.fetch!(ice_agent.checklist, pair_id)
5857
pair = %CandidatePair{pair | nominate?: false, nominated?: true}
59-
checklist = Map.put(ice_agent.checklist, pair.id, pair)
60-
ice_agent = %ICEAgent{ice_agent | checklist: checklist}
58+
ice_agent = put_in(ice_agent.checklist[pair.id], pair)
6159

6260
# the controlling agent could nominate only when eoc was set
6361
# and checklist finished
6462
unless Checklist.finished?(ice_agent.checklist) do
6563
Logger.warning("Nomination succeeded but checklist hasn't finished.")
6664
end
6765

68-
%ICEAgent{ice_agent | nominating?: {false, nil}, selected_pair_id: pair.id}
66+
ice_agent = %ICEAgent{ice_agent | nominating?: {false, nil}, selected_pair_id: pair.id}
67+
ICEAgent.change_connection_state(ice_agent, :completed)
6968
end
7069
end

lib/ex_ice/priv/ice_agent.ex

Lines changed: 180 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ defmodule ExICE.Priv.ICEAgent do
220220
end
221221

222222
@spec set_remote_credentials(t(), binary(), binary()) :: t()
223+
def set_remote_credentials(%__MODULE__{state: :failed} = ice_agent, _, _) do
224+
Logger.debug("Tried to set remote credentials in failed state. ICE restart needed. Ignoring.")
225+
ice_agent
226+
end
227+
223228
def set_remote_credentials(
224229
%__MODULE__{remote_ufrag: nil, remote_pwd: nil} = ice_agent,
225230
ufrag,
@@ -249,6 +254,11 @@ defmodule ExICE.Priv.ICEAgent do
249254
end
250255

251256
@spec gather_candidates(t()) :: t()
257+
def gather_candidates(%__MODULE__{state: :failed} = ice_agent) do
258+
Logger.warning("Can't gather candidates in state failed. ICE restart needed. Ignoring.")
259+
ice_agent
260+
end
261+
252262
def gather_candidates(%__MODULE__{gathering_state: :gathering} = ice_agent) do
253263
Logger.warning("Can't gather candidates. Gathering already in progress. Ignoring.")
254264
ice_agent
@@ -311,6 +321,12 @@ defmodule ExICE.Priv.ICEAgent do
311321
end
312322

313323
@spec add_remote_candidate(t(), Candidate.t()) :: t()
324+
def add_remote_candidate(%__MODULE__{state: :failed} = ice_agent, _) do
325+
# Completed state will be caught by the next clause
326+
Logger.debug("Can't add remote candidate in state failed. ICE restart needed. Ignoring.")
327+
ice_agent
328+
end
329+
314330
def add_remote_candidate(%__MODULE__{eoc: true} = ice_agent, remote_cand) do
315331
Logger.warning("""
316332
Received remote candidate after end-of-candidates. Ignoring.
@@ -361,6 +377,11 @@ defmodule ExICE.Priv.ICEAgent do
361377
end
362378

363379
@spec end_of_candidates(t()) :: t()
380+
def end_of_candidates(%__MODULE__{state: :failed} = ice_agent) do
381+
Logger.debug("Can't set end-of-candidates flag in state failed. Ignoring.")
382+
ice_agent
383+
end
384+
364385
def end_of_candidates(%__MODULE__{role: :controlled} = ice_agent) do
365386
Logger.debug("Setting end-of-candidates flag.")
366387
ice_agent = %{ice_agent | eoc: true}
@@ -418,7 +439,7 @@ defmodule ExICE.Priv.ICEAgent do
418439

419440
@spec handle_ta_timeout(t()) :: t()
420441
def handle_ta_timeout(%__MODULE__{state: state} = ice_agent)
421-
when state.state in [:completed, :failed] do
442+
when state in [:completed, :failed] do
422443
Logger.warning("""
423444
Ta timer fired in unexpected state: #{state}.
424445
Trying to update gathering and connection states.
@@ -477,6 +498,11 @@ defmodule ExICE.Priv.ICEAgent do
477498
end
478499

479500
@spec handle_eoc_timeout(t()) :: t()
501+
def handle_eoc_timeout(%__MODULE__{state: :failed} = ice_agent) do
502+
Logger.debug("EOC timer fired but we are in the failed state. Ignoring.")
503+
%{ice_agent | eoc_timer: nil}
504+
end
505+
480506
def handle_eoc_timeout(%{eoc: true} = ice_agent) do
481507
Logger.debug("EOC timer fired but EOC flag is already set. Ignoring.")
482508
%{ice_agent | eoc_timer: nil}
@@ -489,6 +515,12 @@ defmodule ExICE.Priv.ICEAgent do
489515
end
490516

491517
@spec handle_pair_timeout(t()) :: t()
518+
def handle_pair_timeout(%__MODULE__{state: state} = ice_agent)
519+
when state in [:failed, :completed] do
520+
Logger.debug("Pair timeout timer fired in state: #{state}. Ignoring.")
521+
ice_agent
522+
end
523+
492524
def handle_pair_timeout(ice_agent) do
493525
start_pair_timer()
494526

@@ -564,8 +596,16 @@ defmodule ExICE.Priv.ICEAgent do
564596
{:ok, _pair} ->
565597
ice_agent
566598

599+
:error when ice_agent.state in [:failed, :completed] ->
600+
Logger.warning("""
601+
Received keepalive request for non-existant candidate pair but we are in state: #{ice_agent.state}. \
602+
Ignoring.\
603+
""")
604+
605+
ice_agent
606+
567607
:error ->
568-
Logger.warning("Received keepalive request for non-existant candidate pair")
608+
Logger.warning("Received keepalive request for non-existant candidate pair. Ignoring.")
569609
ice_agent
570610
end
571611
end
@@ -588,19 +628,58 @@ defmodule ExICE.Priv.ICEAgent do
588628
handle_turn_gathering_transaction_response(ice_agent, turn_tr_id, turn_tr, packet)
589629

590630
from_turn?(ice_agent, src_ip, src_port) ->
591-
handle_turn_message(ice_agent, socket, src_ip, src_port, packet)
631+
local_cands = Map.values(ice_agent.local_cands)
632+
633+
case find_relay_cand_by_socket(local_cands, socket) do
634+
nil ->
635+
Logger.debug("""
636+
Couldn't find relay candidate for:
637+
socket: #{inspect(socket)}
638+
src address: #{inspect({src_ip, src_port})}.
639+
Ignoring incoming TURN message.
640+
""")
641+
642+
ice_agent
643+
644+
relay_cand ->
645+
handle_turn_message(ice_agent, relay_cand, src_ip, src_port, packet)
646+
end
592647

593648
ExSTUN.stun?(packet) ->
594-
handle_stun_message(ice_agent, socket, src_ip, src_port, packet)
649+
local_cands = Map.values(ice_agent.local_cands)
650+
651+
case find_host_cand(local_cands, socket) do
652+
nil ->
653+
Logger.debug("""
654+
Couldn't find host candidate for #{inspect(src_ip)}:#{src_port}. \
655+
Ignoring incoming STUN message.\
656+
""")
657+
658+
ice_agent
659+
660+
host_cand ->
661+
handle_stun_message(ice_agent, host_cand, src_ip, src_port, packet)
662+
end
595663

596664
true ->
597-
local_cand = find_host_cand(Map.values(ice_agent.local_cands), socket)
598-
remote_cand = find_remote_cand(Map.values(ice_agent.remote_cands), src_ip, src_port)
665+
with remote_cands <- Map.values(ice_agent.remote_cands),
666+
%_{} = local_cand <- find_host_cand(Map.values(ice_agent.local_cands), socket),
667+
%_{} = remote_cand <- find_remote_cand(remote_cands, src_ip, src_port) do
668+
%CandidatePair{} =
669+
pair = Checklist.find_pair(ice_agent.checklist, local_cand.base.id, remote_cand.id)
599670

600-
%CandidatePair{} =
601-
pair = Checklist.find_pair(ice_agent.checklist, local_cand.base.id, remote_cand.id)
671+
handle_data_message(ice_agent, pair, packet)
672+
else
673+
_ ->
674+
Logger.debug("""
675+
Couldn't find host or remote candidate for:
676+
socket: #{inspect(socket)}
677+
src address: #{inspect({src_ip, src_port})}.
678+
Ignoring incoming data message.
679+
""")
602680

603-
handle_data_message(ice_agent, pair, packet)
681+
ice_agent
682+
end
604683
end
605684
end
606685

@@ -900,9 +979,7 @@ defmodule ExICE.Priv.ICEAgent do
900979
end
901980
end
902981

903-
defp handle_turn_message(ice_agent, socket, src_ip, src_port, packet) do
904-
%cand_mod{} = cand = find_relay_cand_by_socket(Map.values(ice_agent.local_cands), socket)
905-
982+
defp handle_turn_message(ice_agent, %cand_mod{} = cand, src_ip, src_port, packet) do
906983
case cand_mod.receive_data(cand, src_ip, src_port, packet) do
907984
{:ok, cand} ->
908985
put_in(ice_agent.local_cands[cand.base.id], cand)
@@ -935,23 +1012,10 @@ defmodule ExICE.Priv.ICEAgent do
9351012
end
9361013
end
9371014

938-
defp handle_stun_message(ice_agent, socket, src_ip, src_port, packet) do
1015+
defp handle_stun_message(ice_agent, host_cand, src_ip, src_port, packet) do
9391016
case ExSTUN.Message.decode(packet) do
9401017
{:ok, msg} ->
941-
local_cands = Map.values(ice_agent.local_cands)
942-
943-
case find_host_cand(local_cands, socket) do
944-
nil ->
945-
Logger.debug("""
946-
Couldn't find host candidate for #{inspect(src_ip)}:#{src_port}. \
947-
Ignoring incoming STUN message.\
948-
""")
949-
950-
ice_agent
951-
952-
local_cand ->
953-
do_handle_stun_message(ice_agent, local_cand, src_ip, src_port, msg)
954-
end
1018+
do_handle_stun_message(ice_agent, host_cand, src_ip, src_port, msg)
9551019

9561020
{:error, reason} ->
9571021
Logger.warning("Couldn't decode stun message: #{inspect(reason)}")
@@ -1787,14 +1851,10 @@ defmodule ExICE.Priv.ICEAgent do
17871851
end
17881852

17891853
defp do_restart(ice_agent) do
1790-
ice_agent.local_cands
1791-
|> Enum.uniq_by(fn {_id, cand} -> cand.base.socket end)
1792-
|> Enum.each(fn {_id, cand} ->
1793-
Logger.debug("""
1794-
Closing local candidate's socket: #{inspect(cand.base.base_address)}:#{cand.base.base_port}.
1795-
""")
1796-
1797-
:ok = ice_agent.transport_module.close(cand.base.socket)
1854+
Enum.each(ice_agent.sockets, fn socket ->
1855+
{:ok, {ip, port}} = ice_agent.transport_module.sockname(socket)
1856+
Logger.debug("Closing socket: #{inspect(ip)}:#{port}.")
1857+
:ok = ice_agent.transport_module.close(socket)
17981858
end)
17991859

18001860
{ufrag, pwd} = generate_credentials()
@@ -1816,7 +1876,8 @@ defmodule ExICE.Priv.ICEAgent do
18161876

18171877
%__MODULE__{
18181878
ice_agent
1819-
| gathering_transactions: %{},
1879+
| sockets: [],
1880+
gathering_transactions: %{},
18201881
selected_pair_id: nil,
18211882
conn_checks: %{},
18221883
checklist: %{},
@@ -1957,7 +2018,88 @@ defmodule ExICE.Priv.ICEAgent do
19572018

19582019
@doc false
19592020
@spec change_connection_state(t(), atom()) :: t()
2021+
def change_connection_state(ice_agent, :failed) do
2022+
Enum.each(ice_agent.sockets, fn socket ->
2023+
:ok = ice_agent.transport_module.close(socket)
2024+
end)
2025+
2026+
%{
2027+
ice_agent
2028+
| sockets: [],
2029+
gathering_transactions: %{},
2030+
selected_pair_id: nil,
2031+
conn_checks: %{},
2032+
checklist: %{},
2033+
local_cands: %{},
2034+
remote_cands: %{},
2035+
local_ufrag: nil,
2036+
local_pwd: nil,
2037+
remote_ufrag: nil,
2038+
remote_pwd: nil,
2039+
eoc: false,
2040+
nominating?: {false, nil}
2041+
}
2042+
|> disable_timer()
2043+
|> do_change_connection_state(:failed)
2044+
end
2045+
2046+
def change_connection_state(ice_agent, :completed) do
2047+
selected_pair = Map.fetch!(ice_agent.checklist, ice_agent.selected_pair_id)
2048+
succeeded_pair = Map.fetch!(ice_agent.checklist, selected_pair.succeeded_pair_id)
2049+
2050+
if selected_pair.id != selected_pair.discovered_pair_id do
2051+
raise """
2052+
Selected pair isn't also discovered pair. This should never happen.
2053+
Selected pair: #{inspect(selected_pair)}\
2054+
"""
2055+
end
2056+
2057+
local_cand = Map.fetch!(ice_agent.local_cands, succeeded_pair.local_cand_id)
2058+
2059+
Enum.each(ice_agent.sockets, fn socket ->
2060+
unless socket == local_cand.base.socket do
2061+
:ok = ice_agent.transport_module.close(socket)
2062+
end
2063+
end)
2064+
2065+
# We need to use Map.filter as selected_pair might not
2066+
# be the same as succeeded pair
2067+
local_cands =
2068+
Map.filter(
2069+
ice_agent.local_cands,
2070+
fn {cand_id, _cand} ->
2071+
cand_id in [selected_pair.local_cand_id, succeeded_pair.local_cand_id]
2072+
end
2073+
)
2074+
2075+
remote_cands =
2076+
Map.filter(
2077+
ice_agent.remote_cands,
2078+
fn {cand_id, _cand} ->
2079+
cand_id in [selected_pair.remote_cand_id, succeeded_pair.remote_cand_id]
2080+
end
2081+
)
2082+
2083+
checklist =
2084+
Map.filter(ice_agent.checklist, fn {pair_id, _pair} ->
2085+
pair_id in [selected_pair.id, succeeded_pair.id]
2086+
end)
2087+
2088+
%{
2089+
ice_agent
2090+
| sockets: [local_cand.base.socket],
2091+
local_cands: local_cands,
2092+
remote_cands: remote_cands,
2093+
checklist: checklist
2094+
}
2095+
|> do_change_connection_state(:completed)
2096+
end
2097+
19602098
def change_connection_state(ice_agent, new_conn_state) do
2099+
do_change_connection_state(ice_agent, new_conn_state)
2100+
end
2101+
2102+
defp do_change_connection_state(ice_agent, new_conn_state) do
19612103
Logger.debug("Connection state change: #{ice_agent.state} -> #{new_conn_state}")
19622104
notify(ice_agent.on_connection_state_change, {:connection_state_change, new_conn_state})
19632105
%__MODULE__{ice_agent | state: new_conn_state}
@@ -2135,6 +2277,8 @@ defmodule ExICE.Priv.ICEAgent do
21352277
%{ice_agent | ta_timer: timer}
21362278
end
21372279

2280+
defp disable_timer(%{ta_timer: nil} = ice_agent), do: ice_agent
2281+
21382282
defp disable_timer(ice_agent) do
21392283
Process.cancel_timer(ice_agent.ta_timer)
21402284

0 commit comments

Comments
 (0)