Skip to content

Commit dece98e

Browse files
committed
Add support for relay candidates
1 parent d51ceae commit dece98e

File tree

19 files changed

+805
-478
lines changed

19 files changed

+805
-478
lines changed

.credo.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,14 @@
122122
#
123123
{Credo.Check.Refactor.Apply, []},
124124
{Credo.Check.Refactor.CondStatements, []},
125-
{Credo.Check.Refactor.CyclomaticComplexity, []},
125+
{Credo.Check.Refactor.CyclomaticComplexity, [max_complexity: 10]},
126126
{Credo.Check.Refactor.FunctionArity, []},
127127
{Credo.Check.Refactor.LongQuoteBlocks, []},
128128
{Credo.Check.Refactor.MatchInCondition, []},
129129
{Credo.Check.Refactor.MapJoin, []},
130130
{Credo.Check.Refactor.NegatedConditionsInUnless, []},
131131
{Credo.Check.Refactor.NegatedConditionsWithElse, []},
132-
{Credo.Check.Refactor.Nesting, []},
132+
{Credo.Check.Refactor.Nesting, [max_nesting: 3]},
133133
{Credo.Check.Refactor.UnlessWithElse, []},
134134
{Credo.Check.Refactor.WithClauses, []},
135135
{Credo.Check.Refactor.FilterFilter, []},
@@ -199,7 +199,7 @@
199199
{Credo.Check.Warning.MapGetUnsafePass, []},
200200
{Credo.Check.Warning.MixEnv, []},
201201
{Credo.Check.Warning.UnsafeToAtom, []},
202-
202+
203203

204204
# {Credo.Check.Refactor.MapInto, []},
205205

example/peer.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ defmodule Peer do
100100
{_, _, _, _} -> true
101101
{_, _, _, _, _, _, _, _} -> false
102102
end,
103-
stun_servers: ["stun:stun.l.google.com:19302"]
103+
ice_servers: [%ICEServer{url: "stun:stun.l.google.com:19302"}]
104104
)
105105

106106
{:ok, ufrag, passwd} = ICEAgent.get_local_credentials(pid)

lib/ex_ice/candidate_pair.ex

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,19 @@ defmodule ExICE.CandidatePair do
33
ICE candidate pair representation.
44
"""
55

6-
alias ExICE.Candidate
7-
86
@type state() :: :waiting | :in_progress | :succeeded | :failed | :frozen
97

108
@type t() :: %__MODULE__{
119
id: integer(),
12-
local_cand: Candidate.t(),
10+
local_cand_id: integer(),
1311
nominated?: boolean(),
1412
priority: non_neg_integer(),
15-
remote_cand: Candidate.t(),
13+
remote_cand_id: integer(),
1614
state: state(),
1715
valid?: boolean()
1816
}
1917

20-
@enforce_keys [:id, :local_cand, :remote_cand, :priority]
18+
@enforce_keys [:id, :local_cand_id, :remote_cand_id, :priority]
2119
defstruct @enforce_keys ++
2220
[
2321
nominated?: false,

lib/ex_ice/ice_agent.ex

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ defmodule ExICE.ICEAgent do
4848
This behavior can be overwritten using the following options.
4949
5050
* `ip_filter` - filter applied when gathering local candidates
51-
* `stun_servers` - list of STUN servers
51+
* `ice_servers` - list of STUN/TURN servers
52+
* `ice_transport_policy` - candidate types to be used.
53+
* `all` - all ICE candidates will be considered (default).
54+
* `relay` - only relay candidates will be considered.
5255
* `on_gathering_state_change` - where to send gathering state change notifications. Defaults to a process that spawns `ExICE`.
5356
* `on_connection_state_change` - where to send connection state change notifications. Defaults to a process that spawns `ExICE`.
5457
* `on_data` - where to send data. Defaults to a process that spawns `ExICE`.
@@ -59,7 +62,14 @@ defmodule ExICE.ICEAgent do
5962
"""
6063
@type opts() :: [
6164
ip_filter: (:inet.ip_address() -> boolean),
62-
stun_servers: [String.t()],
65+
ice_servers: [
66+
%{
67+
:url => String.t(),
68+
optional(:username) => String.t(),
69+
optional(:credential) => String.t()
70+
}
71+
],
72+
ice_transport_policy: :all | :relay | nil,
6373
on_gathering_state_change: pid() | nil,
6474
on_connection_state_change: pid() | nil,
6575
on_data: pid() | nil,
@@ -347,6 +357,12 @@ defmodule ExICE.ICEAgent do
347357
{:noreply, %{state | ice_agent: ice_agent}}
348358
end
349359

360+
@impl true
361+
def handle_info({:ex_turn, ref, msg}, state) do
362+
ice_agent = ExICE.Priv.ICEAgent.handle_ex_turn_msg(state.ice_agent, ref, msg)
363+
{:noreply, %{state | ice_agent: ice_agent}}
364+
end
365+
350366
@impl true
351367
def handle_info(msg, state) do
352368
Logger.warning("Got unexpected msg: #{inspect(msg)}")

lib/ex_ice/priv/candidate.ex

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ defmodule ExICE.Priv.Candidate do
2727
@callback send_data(t(), :inet.ip_address(), :inet.port_number(), binary()) ::
2828
{:ok, t()} | {:error, term(), t()}
2929

30-
@callback receive_data(t(), :inet.ip_address(), :inet.port_number(), binary()) ::
31-
{:ok, t()} | {:ok, binary(), t()} | {:error, term(), t()}
32-
3330
@spec priority(type()) :: integer()
3431
def priority(type) do
3532
type_preference =

lib/ex_ice/priv/candidate/host.ex

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,4 @@ defmodule ExICE.Priv.Candidate.Host do
3030
{:error, reason} -> {:error, reason, cand}
3131
end
3232
end
33-
34-
@impl true
35-
def receive_data(cand, _src_ip, _src_port, data) do
36-
{:ok, data, cand}
37-
end
3833
end

lib/ex_ice/priv/candidate/prflx.ex

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,4 @@ defmodule ExICE.Priv.Candidate.Prflx do
3030
{:error, reason} -> {:error, reason, cand}
3131
end
3232
end
33-
34-
@impl true
35-
def receive_data(cand, _src_ip, _src_port, data) do
36-
{:ok, data, cand}
37-
end
3833
end

lib/ex_ice/priv/candidate/relay.ex

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ defmodule ExICE.Priv.Candidate.Relay do
66

77
@type t() :: %__MODULE__{base: CandidateBase.t()}
88

9-
@enforce_keys [:base]
10-
defstruct @enforce_keys
9+
@enforce_keys [:base, :client]
10+
defstruct @enforce_keys ++ [buffered_packets: []]
1111

1212
@impl true
1313
def new(config) do
14-
%__MODULE__{base: CandidateBase.new(:relay, config)}
14+
%__MODULE__{base: CandidateBase.new(:relay, config), client: Keyword.fetch!(config, :client)}
1515
end
1616

1717
@impl true
@@ -25,14 +25,74 @@ defmodule ExICE.Priv.Candidate.Relay do
2525

2626
@impl true
2727
def send_data(cand, dst_ip, dst_port, data) do
28-
case cand.base.transport_module.send(cand.base.socket, {dst_ip, dst_port}, data) do
29-
:ok -> {:ok, cand}
30-
{:error, reason} -> {:error, reason, cand}
28+
if MapSet.member?(cand.client.permissions, dst_ip) do
29+
{:send, turn_addr, data, client} = ExTURN.Client.send(cand.client, {dst_ip, dst_port}, data)
30+
cand = %{cand | client: client}
31+
do_send(cand, turn_addr, data)
32+
else
33+
{:send, turn_addr, turn_data, client} = ExTURN.Client.create_permission(cand.client, dst_ip)
34+
35+
cand = %{
36+
cand
37+
| client: client,
38+
buffered_packets: [{dst_ip, dst_port, data} | cand.buffered_packets]
39+
}
40+
41+
do_send(cand, turn_addr, turn_data)
3142
end
3243
end
3344

34-
@impl true
35-
def receive_data(cand, _src_ip, _src_port, data) do
36-
{:ok, data, cand}
45+
@spec receive_data(t(), :inet.ip_address(), :inet.port_number(), binary()) ::
46+
{:ok, t()}
47+
| {:ok, :inet.ip_address(), :inet.port_number(), t()}
48+
| {:error, term(), t()}
49+
def receive_data(cand, src_ip, src_port, data) do
50+
case ExTURN.Client.handle_message(cand.client, {:socket_data, src_ip, src_port, data}) do
51+
{:permission_created, permission_ip, client} ->
52+
cand = %{cand | client: client}
53+
send_buffered_packets(cand, permission_ip)
54+
55+
{:channel_created, _addr, client} ->
56+
cand = %{cand | client: client}
57+
{:ok, cand}
58+
59+
{:data, {src_ip, src_port}, data, client} ->
60+
cand = %{cand | client: client}
61+
{:ok, src_ip, src_port, data, cand}
62+
63+
{:error, reason, client} ->
64+
cand = %{cand | client: client}
65+
{:error, reason, cand}
66+
end
67+
end
68+
69+
defp send_buffered_packets(cand, permission_ip) do
70+
{packets_to_send, rest} =
71+
Enum.split_with(cand.buffered_packets, fn {dst_ip, _dst_port, _data} ->
72+
dst_ip == permission_ip
73+
end)
74+
75+
cand = %{cand | buffered_packets: rest}
76+
do_send_buffered_packets(cand, Enum.reverse(packets_to_send))
77+
end
78+
79+
defp do_send_buffered_packets(cand, []), do: {:ok, cand}
80+
81+
defp do_send_buffered_packets(cand, [{dst_ip, dst_port, packet} | packets]) do
82+
{:send, turn_addr, data, client} = ExTURN.Client.send(cand.client, {dst_ip, dst_port}, packet)
83+
84+
cand = %{cand | client: client}
85+
86+
case do_send(cand, turn_addr, data) do
87+
{:ok, cand} -> do_send_buffered_packets(cand, packets)
88+
{:error, _reason, _cand} = error -> error
89+
end
90+
end
91+
92+
defp do_send(cand, dst_addr, data) do
93+
case cand.base.transport_module.send(cand.base.socket, dst_addr, data) do
94+
:ok -> {:ok, cand}
95+
{:error, reason} -> {:error, reason, cand}
96+
end
3797
end
3898
end

lib/ex_ice/priv/candidate/srflx.ex

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,4 @@ defmodule ExICE.Priv.Candidate.Srflx do
3030
{:error, reason} -> {:error, reason, cand}
3131
end
3232
end
33-
34-
@impl true
35-
def receive_data(cand, _src_ip, _src_port, data) do
36-
{:ok, data, cand}
37-
end
3833
end

lib/ex_ice/priv/candidate_pair.ex

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@ defmodule ExICE.Priv.CandidatePair do
1111

1212
@type t() :: %__MODULE__{
1313
id: integer(),
14-
local_cand: Candidate.t(),
14+
local_cand_id: Candidate.id(),
1515
nominate?: boolean(),
1616
nominated?: boolean(),
1717
priority: non_neg_integer(),
18-
remote_cand: Candidate.t(),
18+
remote_cand_id: Candidate.id(),
1919
state: state(),
2020
valid?: boolean,
2121
succeeded_pair_id: integer() | nil,
2222
discovered_pair_id: integer() | nil,
2323
keepalive_timer: reference() | nil
2424
}
2525

26-
@enforce_keys [:id, :local_cand, :remote_cand, :priority]
26+
@enforce_keys [:id, :local_cand_id, :remote_cand_id, :priority]
2727
defstruct @enforce_keys ++
2828
[
2929
nominate?: false,
@@ -39,12 +39,12 @@ defmodule ExICE.Priv.CandidatePair do
3939
@spec new(Candidate.t(), Candidate.t(), ExICE.ICEAgent.role(), state(), valid?: boolean()) ::
4040
t()
4141
def new(local_cand, remote_cand, agent_role, state, opts \\ []) do
42-
priority = priority(agent_role, local_cand, remote_cand)
42+
priority = priority(agent_role, local_cand.base.priority, remote_cand.priority)
4343

4444
%__MODULE__{
4545
id: Utils.id(),
46-
local_cand: local_cand,
47-
remote_cand: remote_cand,
46+
local_cand_id: local_cand.base.id,
47+
remote_cand_id: remote_cand.id,
4848
priority: priority,
4949
state: state,
5050
valid?: opts[:valid?] || false
@@ -66,19 +66,19 @@ defmodule ExICE.Priv.CandidatePair do
6666
end
6767

6868
@doc false
69-
@spec recompute_priority(t(), ExICE.ICEAgent.role()) :: t()
70-
def recompute_priority(pair, role) do
71-
%__MODULE__{pair | priority: priority(role, pair.local_cand, pair.remote_cand)}
69+
@spec recompute_priority(t(), integer(), integer(), ExICE.ICEAgent.role()) :: t()
70+
def recompute_priority(pair, local_cand_prio, remote_cand_prio, role) do
71+
%__MODULE__{pair | priority: priority(role, local_cand_prio, remote_cand_prio)}
7272
end
7373

7474
@doc false
75-
@spec priority(ExICE.ICEAgent.role(), Candidate.t(), ExICE.Candidate.t()) :: non_neg_integer()
76-
def priority(:controlling, local_cand, remote_cand) do
77-
do_priority(local_cand.base.priority, remote_cand.priority)
75+
@spec priority(ExICE.ICEAgent.role(), integer(), integer()) :: non_neg_integer()
76+
def priority(:controlling, local_cand_prio, remote_cand_prio) do
77+
do_priority(local_cand_prio, remote_cand_prio)
7878
end
7979

80-
def priority(:controlled, local_cand, remote_cand) do
81-
do_priority(remote_cand.priority, local_cand.base.priority)
80+
def priority(:controlled, local_cand_prio, remote_cand_prio) do
81+
do_priority(remote_cand_prio, local_cand_prio)
8282
end
8383

8484
defp do_priority(g, d) do
@@ -89,15 +89,12 @@ defmodule ExICE.Priv.CandidatePair do
8989
@doc false
9090
@spec to_candidate_pair(t()) :: ExICE.CandidatePair.t()
9191
def to_candidate_pair(pair) do
92-
%cand_mod{} = cand = pair.local_cand
93-
local_cand = cand_mod.to_candidate(cand)
94-
9592
%ExICE.CandidatePair{
9693
id: pair.id,
97-
local_cand: local_cand,
94+
local_cand_id: pair.local_cand_id,
9895
nominated?: pair.nominated?,
9996
priority: pair.priority,
100-
remote_cand: pair.remote_cand,
97+
remote_cand_id: pair.remote_cand_id,
10198
state: pair.state,
10299
valid?: pair.valid?
103100
}

0 commit comments

Comments
 (0)