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:
- Host -- read local NIC addresses (synchronous; always available).
- 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.
- 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
@spec build_relay( {:inet.ip_address(), :inet.port_number()}, non_neg_integer() ) :: XMediaLib.SDP.Candidate.t()
Builds a relay %Candidate{} from a TURN relayed transport address.
@spec build_srflx( {:inet.ip_address(), :inet.port_number()}, XMediaLib.SDP.Candidate.t(), non_neg_integer() ) :: XMediaLib.SDP.Candidate.t()
Builds an srflx candidate linked to a host base (same foundation, raddr/rport).
@spec gather_host({:inet.ip_address(), :inet.port_number()}, keyword()) :: [ XMediaLib.SDP.Candidate.t() ]
Gathers host candidates for a bound UDP socket (alias for host_candidates/2).
@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).
@spec gather_srflx( {:inet.ip_address(), :inet.port_number()}, [XMediaLib.SDP.Candidate.t()], keyword() ) :: [XMediaLib.SDP.Candidate.t()]
Gathers srflx candidates for all (host, stun_server) pairs concurrently.
@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: [...]}.
@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 (defaulttrue):getifaddrs- injectablefn -> {:ok, ifaddrs} | {:error, _}for tests
@spec host_candidates({:inet.ip_address(), :inet.port_number()}, keyword()) :: [ XMediaLib.SDP.Candidate.t() ]
Builds host %Candidate{} structs for one UDP bind port.
Parses host[:port] from a URI body (shared by STUN/TURN parsers).
@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}.
@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}.
@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.
@spec query_srflx( {:inet.ip_address(), :inet.port_number()}, XMediaLib.SDP.Candidate.t(), String.t() | {:inet.ip_address(), :inet.port_number()}, keyword() ) :: {:ok, XMediaLib.SDP.Candidate.t()} | {:error, term()}
Queries one STUN server for srflx using injectable :send_recv.
:send_recv is fn request_wire, {stun_ip, stun_port} -> {:ok, response_wire} | {:error, _}.
@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.
@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.
@spec stun_binding_request() :: binary()
Plain RFC 8489 STUN Binding request wire (no ICE credentials).
@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).