/** * @file ice-agent.ts * @description A small but RFC 8445-compliant ICE agent for a single data * component, with browser-compatible connectivity checks and TURN relay. * @module ice/ice-agent * * Responsibilities: * - Gather UDP host candidates, server-reflexive (srflx) candidates via STUN, * and relay candidates via TURN (RFC 5766 ALLOCATE). * - Send/answer STUN Binding connectivity checks carrying USERNAME, * MESSAGE-INTEGRITY (keyed by the remote/local ice-pwd), PRIORITY, the * ICE-CONTROLLING/CONTROLLED role attribute, and USE-CANDIDATE. * - Nominate a candidate pair and expose it as the selected path. * - Demultiplex inbound datagrams per RFC 7983: STUN (first byte 0-3) is * handled internally; everything else (DTLS records, first byte 20-63) is * emitted as 'data' for the upper stack. * * Each local candidate carries a `transport` with a uniform interface so the * connectivity-check and data paths are identical whether the candidate is a * host socket or a TURN relay: * transport.send(buf, remoteAddress, remotePort) * transport.onMessage = (buf, {address, port}) => ... */ import { EventEmitter } from 'events'; /** Remote info accompanying an inbound datagram. */ interface RemoteInfo { address: string; port: number; } /** * Uniform transport abstraction shared by host sockets and TURN relays. */ interface Transport { kind: string; send(buf: Buffer, address: string, port: number): void; onMessage: ((msg: Buffer, rinfo: RemoteInfo) => void) | null; close(): void; } /** A local ICE candidate. */ interface LocalCandidate { foundation: string; component: number; protocol: string; priority: number; address: string; port: number; type: string; transport: Transport; sdp: string; } /** A remote ICE candidate (parsed from an a=candidate line or object). */ interface RemoteCandidate { address: string; port: number; priority?: number; type?: string; } /** A candidate pair under connectivity checking. */ interface CandidatePair { key?: string; local: { transport: Transport; } & Partial; remote: { address: string; port: number; } & Partial; state?: string; nominated?: boolean; /** Hex transaction id of this pair's outstanding check, if any. */ pendingTxid?: string; } /** Description of a single ICE server entry. */ interface IceServer { urls: string | string[]; username?: string; credential?: string; /** * Validate the server's TLS certificate for TURN-over-TLS (turns: + tcp). * Defaults to true; set false to accept self-signed certs (insecure). */ rejectUnauthorized?: boolean; } /** Options accepted by {@link IceAgent#gather}. */ interface GatherOptions { iceServers?: IceServer[]; iceTransportPolicy?: 'all' | 'relay'; } /** Parsed query parameters from a STUN/TURN URL. */ type IceServerParams = Record; /** Result of {@link parseIceServerUrl}. */ interface ParsedIceServerUrl { scheme: string; protocol: string; host: string; port: number; transport: string; params: IceServerParams; } /** Options accepted by the {@link IceAgent} constructor. */ interface IceAgentOptions { role: 'controlling' | 'controlled'; localUfrag: string; localPwd: string; } /** * Compute an ICE candidate priority (RFC 8445 ยง5.1.2.1). */ declare function candidatePriority(type: string, localPref?: number, componentId?: number): number; /** * Parse a STUN/TURN server URL: (stun|turn|turns):host[:port][?key=val&...]. * Query parameters are returned in `params`; a flag without a value (e.g. * "?secure") is recorded as `true`, an empty value ("?transport=") as "". * @param {string} url * @returns {{scheme:string, protocol:string, host:string, port:number, * transport:string, params:Object}|null} null if the URL is invalid. */ declare function parseIceServerUrl(url: string): ParsedIceServerUrl | null; declare class IceAgent extends EventEmitter { #private; role: 'controlling' | 'controlled'; localUfrag: string; localPwd: string; remoteUfrag: string | null; remotePwd: string | null; /** * @param {Object} opts * @param {'controlling'|'controlled'} opts.role * @param {string} opts.localUfrag * @param {string} opts.localPwd */ constructor(opts: IceAgentOptions); /** * Gather candidates. Host candidates always; srflx/relay when iceServers are * given. With iceTransportPolicy 'relay', only relay candidates are kept. * @param {Object} [opts] * @param {Array<{urls:string|string[],username?:string,credential?:string}>} [opts.iceServers] * @param {'all'|'relay'} [opts.iceTransportPolicy='all'] */ gather(opts?: GatherOptions): Promise; getLocalCandidates(): LocalCandidate[]; /** Set remote ICE credentials (from the peer's SDP). */ setRemoteCredentials(ufrag: string, pwd: string): void; /** * Add a remote candidate (parsed from an a=candidate line or object). * @param {{address:string, port:number, priority?:number, type?:string}} cand */ addRemoteCandidate(cand: RemoteCandidate): void; /** Begin connectivity checks (call once remote creds + candidates exist). */ start(): void; /** * Send application (DTLS) data over the selected path. * @param {Buffer} data */ send(data: Buffer): void; getSelectedPair(): CandidatePair | null; /** Type of the selected local candidate ('host'|'srflx'|'relay'|'prflx'). */ getSelectedCandidateType(): string | null | undefined; close(): void; } export { IceAgent, candidatePriority, parseIceServerUrl };