Skip to content

Commit cc50d4e

Browse files
committed
moar
1 parent b05dfa4 commit cc50d4e

File tree

15 files changed

+215
-115
lines changed

15 files changed

+215
-115
lines changed

lib/ex_ice/candidate.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ defmodule ExICE.Candidate do
107107
def family(%__MODULE__{address: {_, _, _, _}}), do: :ipv4
108108
def family(%__MODULE__{address: {_, _, _, _, _, _, _, _}}), do: :ipv6
109109

110+
@spec tcp_type(t()) :: tcp_type() | nil
110111
def tcp_type(%__MODULE__{tcp_type: tt}), do: tt
111112

112113
@doc false

lib/ex_ice/ice_agent.ex

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ defmodule ExICE.ICEAgent do
7171
until role is set, adding remote candidates or gathering local candidates won't possible, and calls to these
7272
functions will be ignored. Defaults to `nil`.
7373
* `ip_filter` - filter applied when gathering host candidates
74-
* `ports` - ports that will be used when gathering host candidates, otherwise the ports are chosen by the OS
74+
* `transport` - transport protocol to be used.
75+
* `udp` - use UDP only (default).
76+
* `tcp`- use TCP only. Note that relay candidates for TCP transport are not yet supported.
77+
* `ports` - ports that will be used when gathering host candidates, otherwise the ports are chosen by the OS.
78+
Warning: when using `transport: :tcp` and setting this option, make sure that this port range is used only by ICE Agents
79+
from this Erlang VM instance. Refer to the source of `ExICE.Priv.Transport.TCP` for more info.
7580
* `ice_servers` - list of STUN/TURN servers
7681
* `ice_transport_policy` - candidate types to be used.
7782
* `all` - all ICE candidates will be considered (default).
@@ -90,13 +95,11 @@ defmodule ExICE.ICEAgent do
9095
* `logger_metadata` - a keyword list of metadata to be attached to the Logger for all logs emitted by the ICEAgent process.
9196
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
9297
there should be no srflx one.
93-
* `transport` - transport protocol to be used.
94-
* `udp` - use UDP only (default).
95-
* `tcp`- use TCP only. Note that relay candidates for TCP transport are not yet supported.
9698
"""
9799
@type opts() :: [
98100
role: role() | nil,
99101
ip_filter: ip_filter(),
102+
transport: :udp | :tcp,
100103
ports: Enumerable.t(non_neg_integer()),
101104
ice_servers: [
102105
%{
@@ -112,8 +115,7 @@ defmodule ExICE.ICEAgent do
112115
on_data: pid() | nil,
113116
on_new_candidate: pid() | nil,
114117
host_to_srflx_ip_mapper: host_to_srflx_ip_mapper() | nil,
115-
logger_metadata: Enumerable.t({atom(), term()}),
116-
transport: :udp | :tcp
118+
logger_metadata: Enumerable.t({atom(), term()})
117119
]
118120

119121
@doc """

lib/ex_ice/priv/app.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ defmodule ExICE.Priv.App do
1111
children =
1212
[{Registry, keys: :unique, name: ExICE.Priv.Registry}] ++
1313
if kernel_ver >= {9, 1} do
14-
[{ExICE.Priv.MDNS.Resolver, :gen_udp}]
14+
[{ExICE.Priv.MDNS.Resolver, ExICE.Priv.Transport.UDP}]
1515
else
1616
Logger.warning("""
17-
Not starting MDNS resolver as it requires kernel version >= 9.1.
17+
Not starting mDNS resolver as it requires kernel version >= 9.1.
1818
Detected kernel version: #{inspect(kernel_ver)}
1919
""")
2020

lib/ex_ice/priv/ice_agent.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ defmodule ExICE.Priv.ICEAgent do
376376

377377
%{
378378
ice_agent
379-
| sockets: sockets,
379+
| sockets: Enum.map(sockets, fn %{socket: socket} -> socket end),
380380
gathering_transactions: relay_gathering_transactions
381381
}
382382
|> update_gathering_state()
@@ -2685,7 +2685,7 @@ defmodule ExICE.Priv.ICEAgent do
26852685
end
26862686

26872687
defp change_gathering_state(ice_agent, new_gathering_state, opts \\ []) do
2688-
Logger.debug("Gatering state change: #{ice_agent.gathering_state} -> #{new_gathering_state}")
2688+
Logger.debug("Gathering state change: #{ice_agent.gathering_state} -> #{new_gathering_state}")
26892689

26902690
if opts[:notify] != false do
26912691
notify(ice_agent.on_gathering_state_change, {:gathering_state_change, new_gathering_state})

lib/ex_ice/priv/mdns/resolver.ex

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ defmodule ExICE.Priv.MDNS.Resolver do
1212
@rtx_timeout_ms 500
1313

1414
@spec start_link(module()) :: GenServer.on_start()
15-
def start_link(transport_module \\ :gen_udp) do
15+
def start_link(transport_module \\ ExICE.Priv.Transport.UDP) do
1616
GenServer.start_link(__MODULE__, transport_module, name: __MODULE__)
1717
end
1818

@@ -28,39 +28,44 @@ defmodule ExICE.Priv.MDNS.Resolver do
2828

2929
@impl true
3030
def init(transport_module) do
31-
Logger.debug("Starting MDNS Resolver")
31+
Logger.debug("Starting mDNS Resolver")
3232
{:ok, %{transport_module: transport_module, cache: %{}}, {:continue, nil}}
3333
end
3434

3535
@impl true
3636
def handle_continue(_, state) do
3737
ret =
38-
state.transport_module.open(
39-
# Listen on the port specific to mDNS traffic.
40-
# `add_membership` option only defines an address.
41-
@mdns_port,
42-
mode: :binary,
43-
reuseaddr: true,
44-
active: true,
45-
# Allow other apps to bind to @mdns_port.
46-
# If there are multiple sockets, bound to the same port,
47-
# and subscribed to the same group (in fact, if one socket
48-
# subscribes to some group, all other sockets bound to
49-
# the same port also join this group), all those sockets
50-
# will receive every message. In other words, `reuseport` for
51-
# multicast works differently than for casual sockets.
52-
reuseport: true,
53-
# Support running two ICE agents on a single machine.
54-
# In other case, our request won't be delivered to the mDNS address owner
55-
# running on the same machine (e.g., a web browser).
56-
multicast_loop: true,
57-
# Receive responses - they are sent to the multicast address.
58-
# The second argument specifies interfaces where we should listen
59-
# for multicast traffic.
60-
# This option works on interfaces i.e. it affects all sockets
61-
# bound to the same port.
62-
add_membership: {{224, 0, 0, 251}, {0, 0, 0, 0}}
63-
)
38+
if state.transport_module.transport() == :udp do
39+
state.transport_module.setup_socket(
40+
nil,
41+
# Listen on the port specific to mDNS traffic.
42+
# `add_membership` option only defines an address.
43+
@mdns_port,
44+
mode: :binary,
45+
reuseaddr: true,
46+
active: true,
47+
# Allow other apps to bind to @mdns_port.
48+
# If there are multiple sockets, bound to the same port,
49+
# and subscribed to the same group (in fact, if one socket
50+
# subscribes to some group, all other sockets bound to
51+
# the same port also join this group), all those sockets
52+
# will receive every message. In other words, `reuseport` for
53+
# multicast works differently than for casual sockets.
54+
reuseport: true,
55+
# Support running two ICE agents on a single machine.
56+
# In other case, our request won't be delivered to the mDNS address owner
57+
# running on the same machine (e.g., a web browser).
58+
multicast_loop: true,
59+
# Receive responses - they are sent to the multicast address.
60+
# The second argument specifies interfaces where we should listen
61+
# for multicast traffic.
62+
# This option works on interfaces i.e. it affects all sockets
63+
# bound to the same port.
64+
add_membership: {{224, 0, 0, 251}, {0, 0, 0, 0}}
65+
)
66+
else
67+
{:error, :using_tcp_transport}
68+
end
6469

6570
case ret do
6671
{:ok, socket} ->
@@ -69,7 +74,7 @@ defmodule ExICE.Priv.MDNS.Resolver do
6974

7075
{:error, reason} ->
7176
Logger.warning("""
72-
Couldn't start MDNS resolver, reason: #{reason}. MDNS candidates won't be resolved.
77+
Couldn't start mDNS resolver, reason: #{reason}. mDNS candidates won't be resolved.
7378
""")
7479

7580
{:stop, {:shutdown, reason}, state}

lib/ex_ice/priv/transport/tcp.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ defmodule ExICE.Priv.Transport.TCP do
2222
def setup_socket(ip, port, socket_opts, tp_opts) do
2323
case Registry.lookup(ExICE.Priv.Registry, {ip, port}) do
2424
# This protects us from reusing ports ONLY within the same VM instance
25-
# See `TCP.Client` for more info
25+
# See `ExICE.Priv.Transport.TCP.Client.setup_socket/5` for more info
2626
[{_pid, _}] ->
2727
# TODO: Consider using another (custom) reason to distinguish from POSIX EADDRINUSE
2828
{:error, :eaddrinuse}

lib/ex_ice/priv/transport/tcp_client.ex

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,44 @@ defmodule ExICE.Priv.Transport.TCP.Client do
22
@moduledoc false
33

44
use GenServer
5+
56
require Logger
67

8+
alias ExICE.Priv.Transport
9+
710
@connect_timeout_ms 500
811

912
@spec start_link(Keyword.t()) :: GenServer.on_start()
1013
def start_link(opts \\ []) do
1114
GenServer.start_link(__MODULE__, opts ++ [ice_agent: self()])
1215
end
1316

17+
@spec setup_socket(
18+
GenServer.server(),
19+
:inet.ip_address(),
20+
:inet.port_number(),
21+
[Transport.open_option()],
22+
Transport.transport_options()
23+
) :: {:ok, Transport.socket()} | {:error, term()}
1424
def setup_socket(pid, ip, port, socket_opts, tp_opts) do
1525
GenServer.call(pid, {:setup_socket, ip, port, socket_opts, tp_opts})
1626
end
1727

1828
# HACK: using listen sockets here is ugly, but was easier to fit into the existing ICE Agent implementation.
1929
# This should be changed, especially because we're going to want to close the listen sockets
2030
# after the connection is successfully established.
31+
@spec send(
32+
GenServer.server(),
33+
Transport.socket(),
34+
{:inet.ip_address(), :inet.port_number()},
35+
binary(),
36+
Transport.transport_options()
37+
) :: :ok | {:error, term()}
2138
def send(pid, listen_socket, dest, packet, tp_opts \\ []) do
2239
GenServer.call(pid, {:send, listen_socket, dest, packet, tp_opts})
2340
end
2441

42+
@spec close(GenServer.server(), Transport.socket()) :: :ok
2543
def close(pid, listen_socket) do
2644
GenServer.call(pid, {:close, listen_socket})
2745
end
@@ -55,8 +73,6 @@ defmodule ExICE.Priv.Transport.TCP.Client do
5573
# * This, however, does not protect us from binding to sockets opened with SO_REUSEPORT,
5674
# that are in use by another OS process with the same effective UID as this one,
5775
# which can happen i.e. when we run ICE Agents in two separate VM instances.
58-
#
59-
# TODO: warn about this in the documentation of ICE Agent's `:ports` option
6076
socket_opts = socket_opts ++ [ip: ip, reuseport: true]
6177
tcp_type = Keyword.fetch!(tp_opts, :tcp_type)
6278

@@ -71,11 +87,12 @@ defmodule ExICE.Priv.Transport.TCP.Client do
7187
end
7288

7389
state =
74-
%{state |
75-
listen_socket: listen_socket,
76-
socket_opts: socket_opts,
77-
tcp_type: tcp_type,
78-
connections: %{}
90+
%{
91+
state
92+
| listen_socket: listen_socket,
93+
socket_opts: socket_opts,
94+
tcp_type: tcp_type,
95+
connections: %{}
7996
}
8097

8198
{:ok, _} = Registry.register(ExICE.Priv.Registry, local, self())
@@ -195,7 +212,12 @@ defmodule ExICE.Priv.Transport.TCP.Client do
195212
{remote_ip, remote_port} = remote
196213

197214
# TODO: determine how big of a timeout we should use here
198-
case :gen_tcp.connect(remote_ip, remote_port, state.socket_opts ++ [port: local_port], @connect_timeout_ms) do
215+
case :gen_tcp.connect(
216+
remote_ip,
217+
remote_port,
218+
state.socket_opts ++ [port: local_port],
219+
@connect_timeout_ms
220+
) do
199221
{:ok, socket} ->
200222
Logger.debug("""
201223
Successfully initiated new connection.

lib/ex_ice/priv/transport/udp.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ defmodule ExICE.Priv.Transport.UDP do
1111

1212
@impl true
1313
def setup_socket(ip, port, socket_opts, _tp_opts \\ []) do
14-
:gen_udp.open(port, socket_opts ++ [ip: ip])
14+
ip_opt = if ip, do: [ip: ip], else: []
15+
:gen_udp.open(port, socket_opts ++ ip_opt)
1516
end
1617

1718
@impl true

0 commit comments

Comments
 (0)