Skip to content

Commit e0b8f29

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

File tree

2 files changed

+198
-26
lines changed

2 files changed

+198
-26
lines changed

lib/ex_ice/priv/ice_agent.ex

Lines changed: 123 additions & 26 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 cought 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}
@@ -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
@@ -577,6 +617,10 @@ defmodule ExICE.Priv.ICEAgent do
577617
:inet.port_number(),
578618
binary()
579619
) :: t()
620+
def handle_udp(%{state: :failed} = ice_agent, _socket, _src_ip, _src_port, _packet) do
621+
ice_agent
622+
end
623+
580624
def handle_udp(ice_agent, socket, src_ip, src_port, packet) do
581625
turn_tr_id = {socket, {src_ip, src_port}}
582626
turn_tr = Map.get(ice_agent.gathering_transactions, turn_tr_id)
@@ -588,19 +632,58 @@ defmodule ExICE.Priv.ICEAgent do
588632
handle_turn_gathering_transaction_response(ice_agent, turn_tr_id, turn_tr, packet)
589633

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

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

596668
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)
669+
with remote_cands <- Map.values(ice_agent.remote_cands),
670+
%_{} = local_cand <- find_host_cand(Map.values(ice_agent.local_cands), socket),
671+
%_{} = remote_cand <- find_remote_cand(remote_cands, src_ip, src_port) do
672+
%CandidatePair{} =
673+
pair = Checklist.find_pair(ice_agent.checklist, local_cand.base.id, remote_cand.id)
599674

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

603-
handle_data_message(ice_agent, pair, packet)
685+
ice_agent
686+
end
604687
end
605688
end
606689

@@ -900,9 +983,7 @@ defmodule ExICE.Priv.ICEAgent do
900983
end
901984
end
902985

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-
986+
defp handle_turn_message(ice_agent, %cand_mod{} = cand, src_ip, src_port, packet) do
906987
case cand_mod.receive_data(cand, src_ip, src_port, packet) do
907988
{:ok, cand} ->
908989
put_in(ice_agent.local_cands[cand.base.id], cand)
@@ -935,23 +1016,10 @@ defmodule ExICE.Priv.ICEAgent do
9351016
end
9361017
end
9371018

938-
defp handle_stun_message(ice_agent, socket, src_ip, src_port, packet) do
1019+
defp handle_stun_message(ice_agent, host_cand, src_ip, src_port, packet) do
9391020
case ExSTUN.Message.decode(packet) do
9401021
{: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
1022+
do_handle_stun_message(ice_agent, host_cand, src_ip, src_port, msg)
9551023

9561024
{:error, reason} ->
9571025
Logger.warning("Couldn't decode stun message: #{inspect(reason)}")
@@ -1957,7 +2025,36 @@ defmodule ExICE.Priv.ICEAgent do
19572025

19582026
@doc false
19592027
@spec change_connection_state(t(), atom()) :: t()
2028+
def change_connection_state(ice_agent, :failed) do
2029+
Enum.each(ice_agent.sockets, fn socket ->
2030+
:ok = ice_agent.transport_module.close(socket)
2031+
end)
2032+
2033+
ice_agent = %{
2034+
ice_agent
2035+
| sockets: [],
2036+
gathering_transactions: %{},
2037+
selected_pair_id: nil,
2038+
conn_checks: %{},
2039+
checklist: %{},
2040+
local_cands: %{},
2041+
remote_cands: %{},
2042+
local_ufrag: nil,
2043+
local_pwd: nil,
2044+
remote_ufrag: nil,
2045+
remote_pwd: nil,
2046+
eoc: false,
2047+
nominating?: {false, nil}
2048+
}
2049+
2050+
do_change_connection_state(ice_agent, :failed)
2051+
end
2052+
19602053
def change_connection_state(ice_agent, new_conn_state) do
2054+
do_change_connection_state(ice_agent, new_conn_state)
2055+
end
2056+
2057+
defp do_change_connection_state(ice_agent, new_conn_state) do
19612058
Logger.debug("Connection state change: #{ice_agent.state} -> #{new_conn_state}")
19622059
notify(ice_agent.on_connection_state_change, {:connection_state_change, new_conn_state})
19632060
%__MODULE__{ice_agent | state: new_conn_state}

test/priv/ice_agent_test.exs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,59 @@ defmodule ExICE.Priv.ICEAgentTest do
952952
assert ice_agent.state == :failed
953953
end
954954

955+
@tag :debug
956+
test "cleans up agent when moving to the failed state" do
957+
remote_cand = ExICE.Candidate.new(:host, address: {192, 168, 0, 3}, port: 8445)
958+
959+
ice_agent =
960+
ICEAgent.new(
961+
controlling_process: self(),
962+
role: :controlling,
963+
transport_module: Transport.Mock,
964+
if_discovery_module: IfDiscovery.Mock
965+
)
966+
|> ICEAgent.set_remote_credentials("remoteufrag", "remotepwd")
967+
|> ICEAgent.gather_candidates()
968+
|> ICEAgent.add_remote_candidate(remote_cand)
969+
970+
local_ufrag = ice_agent.local_ufrag
971+
972+
[socket] = ice_agent.sockets
973+
974+
# mark pair as failed
975+
[pair] = Map.values(ice_agent.checklist)
976+
ice_agent = put_in(ice_agent.checklist[pair.id], %{pair | state: :failed})
977+
978+
ice_agent = ICEAgent.end_of_candidates(ice_agent)
979+
980+
# agent should move to the failed state
981+
assert ice_agent.state == :failed
982+
assert ice_agent.sockets == []
983+
assert ice_agent.local_cands == %{}
984+
assert ice_agent.remote_cands == %{}
985+
986+
# assert that handle_udp ignores incoming data i.e. the state of ice_agent didn't change
987+
new_ice_agent =
988+
ICEAgent.handle_udp(ice_agent, socket, remote_cand.address, remote_cand.port, "some data")
989+
990+
assert ice_agent == new_ice_agent
991+
992+
# the same with incoming binding request
993+
req =
994+
binding_request(
995+
ice_agent.role,
996+
ice_agent.tiebreaker,
997+
local_ufrag,
998+
"remoteufrag",
999+
"remotepwd"
1000+
)
1001+
1002+
new_ice_agent =
1003+
ICEAgent.handle_udp(ice_agent, socket, remote_cand.address, remote_cand.port, req)
1004+
1005+
assert ice_agent == new_ice_agent
1006+
end
1007+
9551008
@stun_ip {192, 168, 0, 3}
9561009
@stun_ip_str :inet.ntoa(@stun_ip)
9571010
@stun_port 19_302
@@ -1439,6 +1492,28 @@ defmodule ExICE.Priv.ICEAgentTest do
14391492
Message.new(%Type{class: :indication, method: :binding}) |> Message.encode()
14401493
end
14411494

1495+
defp binding_request(role, tiebreaker, local_ufrag, remote_ufrag, remote_pwd) do
1496+
ice_attrs =
1497+
if role == :controlled do
1498+
[%ICEControlling{tiebreaker: tiebreaker + 1}, %UseCandidate{}]
1499+
else
1500+
[%ICEControlled{tiebreaker: tiebreaker - 1}]
1501+
end
1502+
1503+
attrs =
1504+
[
1505+
%Username{value: "#{remote_ufrag}:#{local_ufrag}"},
1506+
%Priority{priority: 1234}
1507+
] ++ ice_attrs
1508+
1509+
request =
1510+
Message.new(%Type{class: :request, method: :binding}, attrs)
1511+
|> Message.with_integrity(remote_pwd)
1512+
|> Message.with_fingerprint()
1513+
1514+
Message.encode(request)
1515+
end
1516+
14421517
defp binding_response(t_id, transport_module, socket, remote_pwd) do
14431518
{:ok, {sock_ip, sock_port}} = transport_module.sockname(socket)
14441519

0 commit comments

Comments
 (0)