XGW.ICE.Gather (xgw v0.1.0)

Copy Markdown View Source

Discovers ICE candidates: host, server-reflexive (srflx), and TURN relay.

Candidate gathering in plain terms

Before checks can run, each side must advertise addresses it might be reachable on:

  1. Host -- read local NIC addresses (synchronous; always available).
  2. srflx -- ask a STUN server "what public address do you see?" so peers behind NAT learn a candidate that might work on the open internet.
  3. relay -- ask a TURN server to allocate a relay address that always works when direct paths fail (at the cost of an extra hop).

Host gather is synchronous. srflx and relay honor gather_timeout_ms (default 3000). Prefer waiting for gather before publishing answer SDP; host-only answers remain valid when STUN/TURN is slow or unconfigured.

RFCs

  • RFC 8445 -- host / srflx / relay candidate gathering
  • RFC 8489 -- STUN Binding for server-reflexive discovery
  • RFC 8656 -- TURN Allocate for relay candidates

Summary

Functions

Builds a relay %Candidate{} from a TURN relayed transport address.

Builds an srflx candidate linked to a host base (same foundation, raddr/rport).

Gathers host candidates for a bound UDP socket (alias for host_candidates/2).

Gathers relay candidates concurrently (one attempt per turn_servers entry).

Gathers srflx candidates for all (host, stun_server) pairs concurrently.

Gathers host candidates and waits up to gather_timeout_ms for srflx and relay.

Resolves host IP addresses for candidate advertisement.

Builds host %Candidate{} structs for one UDP bind port.

Parses host[:port] from a URI body (shared by STUN/TURN parsers).

Parses a STUN server URI (stun:host:port) to {ip, port}.

Parses a TURN server URI (turn:host:port) to {ip, port}.

Queries one TURN server for a relay candidate using injectable :send_recv.

Queries one STUN server for srflx using injectable :send_recv.

Derives relay %Candidate{} from a successful TURN Allocate response.

Derives srflx %Candidate{} from a Binding success and host base.

Plain RFC 8489 STUN Binding request wire (no ICE credentials).

Builds passive ICE-TCP host candidates (RFC 6544 tcptype passive).

Functions

build_relay(arg, local_preference \\ 65535)

Builds a relay %Candidate{} from a TURN relayed transport address.

build_srflx(arg, host, local_preference)

Builds an srflx candidate linked to a host base (same foundation, raddr/rport).

gather_host(sockname, opts \\ [])

Gathers host candidates for a bound UDP socket (alias for host_candidates/2).

gather_relay(sockname, opts)

@spec gather_relay({:inet.ip_address(), :inet.port_number()}, keyword()) :: [
  %{
    candidate: XMediaLib.SDP.Candidate.t(),
    allocation: XGW.TURN.Client.allocation()
  }
]

Gathers relay candidates concurrently (one attempt per turn_servers entry).

gather_srflx(sockname, host_bases, opts)

Gathers srflx candidates for all (host, stun_server) pairs concurrently.

gather_with_timeout(sockname, opts \\ [])

@spec gather_with_timeout({:inet.ip_address(), :inet.port_number()}, keyword()) :: %{
  host: [XMediaLib.SDP.Candidate.t()],
  srflx: [XMediaLib.SDP.Candidate.t()],
  relay: [XMediaLib.SDP.Candidate.t()]
}

Gathers host candidates and waits up to gather_timeout_ms for srflx and relay.

srflx and relay run concurrently under one timeout budget (not sequential).

Returns %{host: [...], srflx: [...], relay: [...]}.

host_addresses(opts \\ [])

@spec host_addresses(keyword()) :: [:inet.ip_address()]

Resolves host IP addresses for candidate advertisement.

Options:

  • :advertised_ips - explicit list (wins over all other sources)
  • :advertised_ip - single override
  • :include_loopback - include {127,0,0,1} when auto-detecting (default true)
  • :getifaddrs - injectable fn -> {:ok, ifaddrs} | {:error, _} for tests

host_candidates(arg, opts \\ [])

@spec host_candidates({:inet.ip_address(), :inet.port_number()}, keyword()) :: [
  XMediaLib.SDP.Candidate.t()
]

Builds host %Candidate{} structs for one UDP bind port.

parse_server_host(rest, error \\ :invalid_server)

Parses host[:port] from a URI body (shared by STUN/TURN parsers).

parse_stun_server(arg1)

@spec parse_stun_server(String.t()) ::
  {:ok, {:inet.ip_address(), :inet.port_number()}} | {:error, term()}

Parses a STUN server URI (stun:host:port) to {ip, port}.

parse_turn_server(uri)

@spec parse_turn_server(String.t()) ::
  {:ok, {:inet.ip_address(), :inet.port_number()}} | {:error, term()}

Parses a TURN server URI (turn:host:port) to {ip, port}.

query_relay(sockname, turn_server, opts)

@spec query_relay(
  {:inet.ip_address(), :inet.port_number()},
  String.t() | {:inet.ip_address(), :inet.port_number()},
  keyword()
) ::
  {:ok,
   %{
     candidate: XMediaLib.SDP.Candidate.t(),
     allocation: XGW.TURN.Client.allocation()
   }}
  | {:error, term()}

Queries one TURN server for a relay candidate using injectable :send_recv.

Returns {:ok, %{candidate: cand, allocation: alloc}} on success.

query_srflx(sockname, host_base, stun_server, opts)

Queries one STUN server for srflx using injectable :send_recv.

:send_recv is fn request_wire, {stun_ip, stun_port} -> {:ok, response_wire} | {:error, _}.

relay_from_allocate(stun, local_preference \\ 65535)

@spec relay_from_allocate(XMediaLib.Stun.t(), non_neg_integer()) ::
  {:ok, XMediaLib.SDP.Candidate.t()} | {:error, term()}

Derives relay %Candidate{} from a successful TURN Allocate response.

srflx_from_response(arg1, host)

@spec srflx_from_response(XMediaLib.Stun.t(), XMediaLib.SDP.Candidate.t()) ::
  {:ok, XMediaLib.SDP.Candidate.t()} | {:error, term()}

Derives srflx %Candidate{} from a Binding success and host base.

stun_binding_request()

@spec stun_binding_request() :: binary()

Plain RFC 8489 STUN Binding request wire (no ICE credentials).

tcp_passive_candidates(arg, opts \\ [])

@spec tcp_passive_candidates({:inet.ip_address(), :inet.port_number()}, keyword()) ::
  [
    XMediaLib.SDP.Candidate.t()
  ]

Builds passive ICE-TCP host candidates (RFC 6544 tcptype passive).