Skip to content

Commit bad6656

Browse files
committed
Requested changes
1 parent 8248126 commit bad6656

File tree

5 files changed

+121
-101
lines changed

5 files changed

+121
-101
lines changed

lib/ex_ice/ice_agent.ex

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,19 @@ defmodule ExICE.ICEAgent do
4848
@type ip_filter() :: (:inet.ip_address() -> boolean)
4949

5050
@typedoc """
51-
Mapping function used instead of STUN server. Maps local ip addresses into public ones.
52-
These public addresses are then used to create server reflexive candidates.
51+
Function called for each host candidate to derive a new "fabricated" srflx candidate from it.
52+
This function takes host's ip as an argument and should return srflx's ip as a result or nil if for given host candidate
53+
there should be no srflx one.
5354
5455
Note that each returned IP address must be unique.
5556
If the mapping function repeatedly returns the same address,
5657
it will be ignored, and only one server reflexive candidate will be created.
5758
58-
This function is meant to be used for server implementations where the public addresses are well known
59-
and NAT use 1 to 1 port mapping.
59+
This function is meant to be used for server implementations where the public addresses are well known.
60+
NAT uses 1 to 1 port mapping and using STUN server for discovering public IP is undesirable
61+
(e.g. because of unknown response time).
6062
"""
61-
@type map_to_nat_ip() :: (:inet.ip_address() -> :inet.ip_address() | nil)
63+
@type host_to_srflx_ip_mapper() :: (:inet.ip_address() -> :inet.ip_address() | nil)
6264

6365
@typedoc """
6466
ICE Agent configuration options.
@@ -84,16 +86,9 @@ defmodule ExICE.ICEAgent do
8486
* `on_connection_state_change` - where to send connection state change notifications. Defaults to a process that spawns `ExICE`.
8587
* `on_data` - where to send data. Defaults to a process that spawns `ExICE`.
8688
* `on_new_candidate` - where to send new candidates. Defaults to a process that spawns `ExICE`.
87-
* `map_to_nat_ip` - Mapping function used instead of STUN server. Maps
88-
local ip addresses into public ones.
89-
These public addresses are then used to create server reflexive candidates.
90-
91-
Note that each returned IP address must be unique.
92-
If the mapping function repeatedly returns the same address,
93-
it will be ignored, and only one server reflexive candidate will be created.
94-
95-
This function is meant to be used for server implementations where the public addresses are well known
96-
and NAT use 1 to 1 port mapping.
89+
* `host_to_srflx_ip_mapper` - function called for each host candidate to derive a new "fabricated" srflx candidate from it.
90+
This function takes host's ip as an argument and should return srflx's ip as a result or nil if for given host candidate
91+
there should be no srflx one.
9792
"""
9893
@type opts() :: [
9994
role: role() | nil,
@@ -112,7 +107,7 @@ defmodule ExICE.ICEAgent do
112107
on_connection_state_change: pid() | nil,
113108
on_data: pid() | nil,
114109
on_new_candidate: pid() | nil,
115-
map_to_nat_ip: map_to_nat_ip() | nil
110+
host_to_srflx_ip_mapper: host_to_srflx_ip_mapper() | nil
116111
]
117112

118113
@doc """

lib/ex_ice/priv/gatherer.ex

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
defmodule ExICE.Priv.Gatherer do
22
@moduledoc false
33

4+
alias ExICE.ICEAgent
45
alias ExICE.Priv.{Candidate, Transport, Utils}
56
alias ExSTUN.Message
67
alias ExSTUN.Message.Type
@@ -139,6 +140,104 @@ defmodule ExICE.Priv.Gatherer do
139140
end
140141
end
141142

143+
@spec fabricate_srflx_candidates([Candidate.Host.t()], ICEAgent.host_to_srflx_ip_mapper(), %{
144+
:inet.ip_address() => non_neg_integer()
145+
}) :: [Candidate.Srflx.t()]
146+
def fabricate_srflx_candidates(_host_cands, nil, _local_preferences) do
147+
[]
148+
end
149+
150+
def fabricate_srflx_candidates(host_cands, host_to_srflx_ip_mapper, local_preferences) do
151+
do_fabricate_srflx_candidates(
152+
host_cands,
153+
host_to_srflx_ip_mapper,
154+
local_preferences,
155+
[],
156+
[]
157+
)
158+
end
159+
160+
defp do_fabricate_srflx_candidates(
161+
[],
162+
_host_to_srflx_ip_mapper,
163+
_local_preferences,
164+
srflx_cands,
165+
_external_ips
166+
) do
167+
srflx_cands
168+
end
169+
170+
defp do_fabricate_srflx_candidates(
171+
[host_cand | rest],
172+
host_to_srflx_ip_mapper,
173+
local_preferences,
174+
srflx_cands,
175+
external_ips
176+
) do
177+
external_ip = host_to_srflx_ip_mapper.(host_cand.base.address)
178+
179+
if valid_external_ip?(external_ip, host_cand.base.address, external_ips) do
180+
priority =
181+
Candidate.priority!(local_preferences, host_cand.base.address, :srflx)
182+
183+
cand =
184+
Candidate.Srflx.new(
185+
address: external_ip,
186+
port: host_cand.base.port,
187+
base_address: host_cand.base.address,
188+
base_port: host_cand.base.port,
189+
priority: priority,
190+
transport_module: host_cand.base.transport_module,
191+
socket: host_cand.base.socket
192+
)
193+
194+
Logger.debug("New srflx candidate from NAT mapping: #{inspect(cand)}")
195+
196+
do_fabricate_srflx_candidates(
197+
rest,
198+
host_to_srflx_ip_mapper,
199+
local_preferences,
200+
[cand | srflx_cands],
201+
[external_ip | external_ips]
202+
)
203+
else
204+
do_fabricate_srflx_candidates(
205+
rest,
206+
host_to_srflx_ip_mapper,
207+
local_preferences,
208+
srflx_cands,
209+
external_ips
210+
)
211+
end
212+
end
213+
214+
defp valid_external_ip?(external_ip, host_ip, external_ips) do
215+
same_type? = :inet.is_ipv4_address(external_ip) == :inet.is_ipv4_address(host_ip)
216+
217+
cond do
218+
host_ip == external_ip ->
219+
log_warning(host_ip, external_ip, "external IP is the same as local IP")
220+
false
221+
222+
not :inet.is_ip_address(external_ip) or not same_type? ->
223+
log_warning(host_ip, external_ip, "not valid IP address")
224+
false
225+
226+
external_ip in external_ips ->
227+
log_warning(host_ip, external_ip, "address already in use")
228+
false
229+
230+
true ->
231+
true
232+
end
233+
end
234+
235+
defp log_warning(host_ip, external_ip, reason),
236+
do:
237+
Logger.warning(
238+
"Ignoring NAT mapping: #{inspect(host_ip)} to #{inspect(external_ip)}, #{inspect(reason)}"
239+
)
240+
142241
defp loopback_if?({_int_name, int}) do
143242
:loopback in int[:flags]
144243
end

lib/ex_ice/priv/ice_agent.ex

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ defmodule ExICE.Priv.ICEAgent do
1010
ConnCheckHandler,
1111
Gatherer,
1212
IfDiscovery,
13-
NATMapper,
1413
Transport,
1514
Utils
1615
}
@@ -91,6 +90,7 @@ defmodule ExICE.Priv.ICEAgent do
9190
stun_servers: [],
9291
turn_servers: [],
9392
resolved_turn_servers: [],
93+
host_to_srflx_ip_mapper: nil,
9494
# stats
9595
bytes_sent: 0,
9696
bytes_received: 0,
@@ -99,8 +99,7 @@ defmodule ExICE.Priv.ICEAgent do
9999
selected_candidate_pair_changes: 0,
100100
# binding requests that failed to pass checks required to assign them to specific candidate pair
101101
# e.g. missing required attributes, role conflict, authentication, etc.
102-
unmatched_requests: 0,
103-
map_to_nat_ip: nil
102+
unmatched_requests: 0
104103
]
105104

106105
@spec unmarshal_remote_candidate(String.t()) :: {:ok, Candidate.t()} | {:error, term()}
@@ -168,7 +167,7 @@ defmodule ExICE.Priv.ICEAgent do
168167
local_pwd: local_pwd,
169168
stun_servers: stun_servers,
170169
turn_servers: turn_servers,
171-
map_to_nat_ip: opts[:map_to_nat_ip]
170+
host_to_srflx_ip_mapper: opts[:host_to_srflx_ip_mapper]
172171
}
173172
end
174173

@@ -328,9 +327,9 @@ defmodule ExICE.Priv.ICEAgent do
328327
ice_agent = %__MODULE__{ice_agent | local_preferences: local_preferences}
329328

330329
srflx_cands =
331-
NATMapper.create_srflx_candidates(
330+
Gatherer.fabricate_srflx_candidates(
332331
host_cands,
333-
ice_agent.map_to_nat_ip,
332+
ice_agent.host_to_srflx_ip_mapper,
334333
ice_agent.local_preferences
335334
)
336335

lib/ex_ice/priv/nat_mapper.ex

Lines changed: 0 additions & 73 deletions
This file was deleted.

test/priv/ice_agent_test.exs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2631,11 +2631,11 @@ defmodule ExICE.Priv.ICEAgentTest do
26312631
end
26322632
end
26332633

2634-
describe "NAT mapping" do
2634+
describe "host to prefabricated srflx mapper" do
26352635
alias ExICE.Priv.Candidate
26362636

26372637
@ipv4 {10, 10, 10, 10}
2638-
@ipv6 {0, 0, 0, 0, 0, 0, 0, 1}
2638+
@ipv6 {64_512, 0, 0, 0, 0, 0, 0, 1}
26392639
@invalid_ip :invalid_ip
26402640

26412641
test "adds srflx candidate" do
@@ -2650,7 +2650,7 @@ defmodule ExICE.Priv.ICEAgentTest do
26502650
assert srflx_cand =~ "typ srflx"
26512651
end
26522652

2653-
test "creates only one candidate if external ip repeats itself" do
2653+
test "creates only one candidate if external ip is repeated" do
26542654
ice_agent = spawn_ice_agent(IfDiscovery.MockMulti, fn _ip -> @ipv4 end)
26552655

26562656
assert [%Candidate.Srflx{base: %{address: @ipv4}}] = srflx_candidates(ice_agent)
@@ -2701,7 +2701,7 @@ defmodule ExICE.Priv.ICEAgentTest do
27012701
transport_module: Transport.Mock,
27022702
if_discovery_module: IfDiscovery.MockSingle,
27032703
ice_servers: [%{urls: "stun:192.168.0.3:19302"}],
2704-
map_to_nat_ip: fn _ip -> @ipv4 end
2704+
host_to_srflx_ip_mapper: fn _ip -> @ipv4 end
27052705
)
27062706
|> ICEAgent.set_remote_credentials("remoteufrag", "remotepwd")
27072707
|> ICEAgent.gather_candidates()
@@ -2732,14 +2732,14 @@ defmodule ExICE.Priv.ICEAgentTest do
27322732
assert [%Candidate.Srflx{}] = srflx_candidates(ice_agent)
27332733
end
27342734

2735-
defp spawn_ice_agent(discovery_module, map_to_nat_ip) do
2735+
defp spawn_ice_agent(discovery_module, host_to_srflx_ip_mapper) do
27362736
%ICEAgent{gathering_state: :complete} =
27372737
ICEAgent.new(
27382738
controlling_process: self(),
27392739
role: :controlled,
27402740
transport_module: Transport.Mock,
27412741
if_discovery_module: discovery_module,
2742-
map_to_nat_ip: map_to_nat_ip
2742+
host_to_srflx_ip_mapper: host_to_srflx_ip_mapper
27432743
)
27442744
|> ICEAgent.set_remote_credentials("remoteufrag", "remotepwd")
27452745
|> ICEAgent.gather_candidates()

0 commit comments

Comments
 (0)