Skip to content

Commit 852c73a

Browse files
authored
Ignore data frome stale sockets, timers, etc. Clean up state when connection is failed or completed (#55)
1 parent bed3d9c commit 852c73a

File tree

3 files changed

+348
-40
lines changed

3 files changed

+348
-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: 174 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}
@@ -564,8 +590,16 @@ defmodule ExICE.Priv.ICEAgent do
564590
{:ok, _pair} ->
565591
ice_agent
566592

593+
:error when ice_agent.state in [:failed, :completed] ->
594+
Logger.warning("""
595+
Received keepalive request for non-existant candidate pair but we are in state: #{ice_agent.state}. \
596+
Ignoring.\
597+
""")
598+
599+
ice_agent
600+
567601
:error ->
568-
Logger.warning("Received keepalive request for non-existant candidate pair")
602+
Logger.warning("Received keepalive request for non-existant candidate pair. Ignoring.")
569603
ice_agent
570604
end
571605
end
@@ -588,19 +622,58 @@ defmodule ExICE.Priv.ICEAgent do
588622
handle_turn_gathering_transaction_response(ice_agent, turn_tr_id, turn_tr, packet)
589623

590624
from_turn?(ice_agent, src_ip, src_port) ->
591-
handle_turn_message(ice_agent, socket, src_ip, src_port, packet)
625+
local_cands = Map.values(ice_agent.local_cands)
626+
627+
case find_relay_cand_by_socket(local_cands, socket) do
628+
nil ->
629+
Logger.debug("""
630+
Couldn't find relay candidate for:
631+
socket: #{inspect(socket)}
632+
src address: #{inspect({src_ip, src_port})}.
633+
Ignoring incoming TURN message.
634+
""")
635+
636+
ice_agent
637+
638+
relay_cand ->
639+
handle_turn_message(ice_agent, relay_cand, src_ip, src_port, packet)
640+
end
592641

593642
ExSTUN.stun?(packet) ->
594-
handle_stun_message(ice_agent, socket, src_ip, src_port, packet)
643+
local_cands = Map.values(ice_agent.local_cands)
644+
645+
case find_host_cand(local_cands, socket) do
646+
nil ->
647+
Logger.debug("""
648+
Couldn't find host candidate for #{inspect(src_ip)}:#{src_port}. \
649+
Ignoring incoming STUN message.\
650+
""")
651+
652+
ice_agent
653+
654+
host_cand ->
655+
handle_stun_message(ice_agent, host_cand, src_ip, src_port, packet)
656+
end
595657

596658
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)
659+
with remote_cands <- Map.values(ice_agent.remote_cands),
660+
%_{} = local_cand <- find_host_cand(Map.values(ice_agent.local_cands), socket),
661+
%_{} = remote_cand <- find_remote_cand(remote_cands, src_ip, src_port) do
662+
%CandidatePair{} =
663+
pair = Checklist.find_pair(ice_agent.checklist, local_cand.base.id, remote_cand.id)
599664

600-
%CandidatePair{} =
601-
pair = Checklist.find_pair(ice_agent.checklist, local_cand.base.id, remote_cand.id)
665+
handle_data_message(ice_agent, pair, packet)
666+
else
667+
_ ->
668+
Logger.debug("""
669+
Couldn't find host or remote candidate for:
670+
socket: #{inspect(socket)}
671+
src address: #{inspect({src_ip, src_port})}.
672+
Ignoring incoming data message.
673+
""")
602674

603-
handle_data_message(ice_agent, pair, packet)
675+
ice_agent
676+
end
604677
end
605678
end
606679

@@ -900,9 +973,7 @@ defmodule ExICE.Priv.ICEAgent do
900973
end
901974
end
902975

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-
976+
defp handle_turn_message(ice_agent, %cand_mod{} = cand, src_ip, src_port, packet) do
906977
case cand_mod.receive_data(cand, src_ip, src_port, packet) do
907978
{:ok, cand} ->
908979
put_in(ice_agent.local_cands[cand.base.id], cand)
@@ -935,23 +1006,10 @@ defmodule ExICE.Priv.ICEAgent do
9351006
end
9361007
end
9371008

938-
defp handle_stun_message(ice_agent, socket, src_ip, src_port, packet) do
1009+
defp handle_stun_message(ice_agent, host_cand, src_ip, src_port, packet) do
9391010
case ExSTUN.Message.decode(packet) do
9401011
{: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
1012+
do_handle_stun_message(ice_agent, host_cand, src_ip, src_port, msg)
9551013

9561014
{:error, reason} ->
9571015
Logger.warning("Couldn't decode stun message: #{inspect(reason)}")
@@ -1787,14 +1845,10 @@ defmodule ExICE.Priv.ICEAgent do
17871845
end
17881846

17891847
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)
1848+
Enum.each(ice_agent.sockets, fn socket ->
1849+
{:ok, {ip, port}} = ice_agent.transport_module.sockname(socket)
1850+
Logger.debug("Closing socket: #{inspect(ip)}:#{port}.")
1851+
:ok = ice_agent.transport_module.close(socket)
17981852
end)
17991853

18001854
{ufrag, pwd} = generate_credentials()
@@ -1816,7 +1870,8 @@ defmodule ExICE.Priv.ICEAgent do
18161870

18171871
%__MODULE__{
18181872
ice_agent
1819-
| gathering_transactions: %{},
1873+
| sockets: [],
1874+
gathering_transactions: %{},
18201875
selected_pair_id: nil,
18211876
conn_checks: %{},
18221877
checklist: %{},
@@ -1957,7 +2012,88 @@ defmodule ExICE.Priv.ICEAgent do
19572012

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

2274+
defp disable_timer(%{ta_timer: nil} = ice_agent), do: ice_agent
2275+
21382276
defp disable_timer(ice_agent) do
21392277
Process.cancel_timer(ice_agent.ta_timer)
21402278

0 commit comments

Comments
 (0)