ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
ice.c File Reference

ICE candidate gathering and exchange. More...

Go to the source code of this file.

Macros

#define PCRE2_CODE_UNIT_WIDTH   8
 

Functions

const char * ice_candidate_type_name (ice_candidate_type_t type)
 Get human-readable candidate type name.
 
const char * ice_protocol_name (ice_protocol_t protocol)
 Get human-readable protocol name.
 
uint32_t ice_calculate_priority (ice_candidate_type_t type, uint16_t local_preference, uint8_t component_id)
 Calculate candidate priority.
 
uint32_t ice_calculate_priority_for_candidate (const ice_candidate_t *candidate)
 Calculate candidate priority with IP-aware local preference.
 
asciichat_error_t ice_parse_candidate (const char *line, ice_candidate_t *candidate)
 Parse ICE candidate from attribute string.
 
asciichat_error_t ice_format_candidate (const ice_candidate_t *candidate, char *line, size_t line_size)
 Format ICE candidate to attribute string.
 
asciichat_error_t ice_gather_candidates (const ice_config_t *config)
 Start ICE candidate gathering.
 
asciichat_error_t ice_add_remote_candidate (webrtc_peer_connection_t *pc, const ice_candidate_t *candidate, const char *mid)
 Add remote candidate to peer connection.
 
bool ice_is_connected (webrtc_peer_connection_t *pc)
 Check ICE connection state.
 
asciichat_error_t ice_get_selected_pair_impl (webrtc_peer_connection_t *pc, ice_candidate_t *local_candidate, ice_candidate_t *remote_candidate)
 Get selected ICE candidate pair (C++ implementation)
 
asciichat_error_t ice_get_selected_pair (webrtc_peer_connection_t *pc, ice_candidate_t *local_candidate, ice_candidate_t *remote_candidate)
 Get selected candidate pair.
 

Detailed Description

ICE candidate gathering and exchange.

Implements ICE candidate collection from STUN/TURN servers and relay via ACDS signaling.

Date
January 2026

Definition in file ice.c.

Macro Definition Documentation

◆ PCRE2_CODE_UNIT_WIDTH

#define PCRE2_CODE_UNIT_WIDTH   8

Definition at line 19 of file ice.c.

Function Documentation

◆ ice_add_remote_candidate()

asciichat_error_t ice_add_remote_candidate ( struct webrtc_peer_connection *  pc,
const ice_candidate_t *  candidate,
const char *  mid 
)

Add remote candidate to peer connection.

Called when receiving candidate from peer. The WebRTC agent will test connectivity with this candidate.

Parameters
pcPeer connection to add candidate to
candidateRemote candidate from peer
midMedia stream ID
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 420 of file ice.c.

421 {
422 if (!pc || !candidate || !mid) {
423 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid remote candidate parameters");
424 }
425
426 // Validate candidate has required fields
427 if (candidate->ip_address[0] == '\0' || candidate->port == 0) {
428 return SET_ERRNO(ERROR_INVALID_PARAM, "Candidate missing IP address or port");
429 }
430
431 log_debug("ICE: Adding remote candidate %s:%u (type=%s, foundation=%s) on mid=%s", candidate->ip_address,
432 candidate->port, ice_candidate_type_name(candidate->type), candidate->foundation, mid);
433
434 // Format candidate to SDP attribute line for libdatachannel
435 char candidate_line[512];
436 asciichat_error_t err = ice_format_candidate(candidate, candidate_line, sizeof(candidate_line));
437 if (err != ASCIICHAT_OK) {
438 return err;
439 }
440
441 // Delegate to WebRTC layer
442 return webrtc_add_remote_candidate(pc, candidate_line, mid);
443}
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
asciichat_error_t webrtc_add_remote_candidate(webrtc_peer_connection_t *pc, const char *candidate, const char *mid)
Add remote ICE candidate.
asciichat_error_t ice_format_candidate(const ice_candidate_t *candidate, char *line, size_t line_size)
Format ICE candidate to attribute string.
Definition ice.c:320
const char * ice_candidate_type_name(ice_candidate_type_t type)
Get human-readable candidate type name.
Definition ice.c:26
char ip_address[64]
IP address (IPv4 or IPv6)
Definition ice.h:119
char foundation[32]
Unique identifier for candidate (for pairing)
Definition ice.h:113
uint16_t port
Port number.
Definition ice.h:120
ice_candidate_type_t type
host, srflx, prflx, or relay
Definition ice.h:123

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ice_candidate_t::foundation, ice_candidate_type_name(), ice_format_candidate(), ice_candidate_t::ip_address, log_debug, ice_candidate_t::port, SET_ERRNO, ice_candidate_t::type, and webrtc_add_remote_candidate().

◆ ice_calculate_priority()

uint32_t ice_calculate_priority ( ice_candidate_type_t  type,
uint16_t  local_preference,
uint8_t  component_id 
)

Calculate candidate priority.

Implements RFC 5245 priority formula: priority = (2^24 * typePreference) + (2^8 * localPreference) + (256 - componentID)

Parameters
typeCandidate type (determines typePreference)
local_preferenceLocal preference (0-65535, higher = preferred)
component_idComponent ID (1 or 2)
Returns
Calculated priority

Definition at line 101 of file ice.c.

101 {
102 // RFC 5245: priority = (2^24 * typePreference) + (2^8 * localPreference) + (256 - componentID)
103
104 // Type preference (higher is better):
105 // - Host: 126 (most preferred, lowest latency)
106 // - SRFLX: 100 (STUN-discovered address)
107 // - PRFLX: 110 (discovered during connectivity checks)
108 // - Relay: 0 (least preferred, uses bandwidth)
109
110 uint8_t type_preference = 0;
111 switch (type) {
113 type_preference = 126;
114 break;
116 type_preference = 100;
117 break;
119 type_preference = 110;
120 break;
122 default:
123 type_preference = 0;
124 break;
125 }
126
127 uint32_t priority =
128 (((uint32_t)type_preference) << 24) | (((uint32_t)local_preference) << 8) | (256 - (uint32_t)component_id);
129
130 return priority;
131}
unsigned int uint32_t
Definition common.h:58
unsigned char uint8_t
Definition common.h:56
@ ICE_CANDIDATE_SRFLX
Server-reflexive (NAT-discovered via STUN)
Definition ice.h:76
@ ICE_CANDIDATE_HOST
Host candidate (local IP address)
Definition ice.h:75
@ ICE_CANDIDATE_RELAY
Relay candidate (TURN server)
Definition ice.h:78
@ ICE_CANDIDATE_PRFLX
Peer-reflexive (discovered during checks)
Definition ice.h:77

References ICE_CANDIDATE_HOST, ICE_CANDIDATE_PRFLX, ICE_CANDIDATE_RELAY, and ICE_CANDIDATE_SRFLX.

Referenced by ice_calculate_priority_for_candidate().

◆ ice_calculate_priority_for_candidate()

uint32_t ice_calculate_priority_for_candidate ( const ice_candidate_t *  candidate)

Calculate candidate priority with IP-aware local preference.

Automatically determines local_preference based on IP address classification:

  • LAN addresses (192.168.x.x, 10.x.x.x, etc.): Highest priority (65535)
  • Localhost (127.x.x.x, ::1): High priority (65000)
  • Internet (public IPs): Medium priority (32768)
  • Unknown/invalid: Lowest priority (0)

This prioritization improves connection establishment by preferring:

  1. Local network peers (lowest latency, no NAT)
  2. Public IPs (direct connectivity)
  3. Unknown addresses (fallback)
Parameters
candidateICE candidate with IP address to classify
Returns
Calculated priority using RFC 5245 formula with IP-based local preference
Example
ice_candidate_t candidate = {
.ip_address = "192.168.1.100",
.component_id = 1
};
// Returns high priority due to LAN address
uint32_t ice_calculate_priority_for_candidate(const ice_candidate_t *candidate)
Calculate candidate priority with IP-aware local preference.
Definition ice.c:133
Single ICE candidate for connectivity.
Definition ice.h:111

Definition at line 133 of file ice.c.

133 {
134 if (!candidate) {
135 return 0;
136 }
137
138 // Calculate local preference based on IP classification
139 uint16_t local_pref = ice_calculate_local_preference_by_ip(candidate->ip_address);
140
141 // Use standard RFC 5245 priority formula with IP-aware local preference
142 return ice_calculate_priority(candidate->type, local_pref, (uint8_t)candidate->component_id);
143}
unsigned short uint16_t
Definition common.h:57
uint32_t ice_calculate_priority(ice_candidate_type_t type, uint16_t local_preference, uint8_t component_id)
Calculate candidate priority.
Definition ice.c:101
uint32_t component_id
Component (1=RTP, 2=RTCP; usually 1)
Definition ice.h:114

References ice_candidate_t::component_id, ice_calculate_priority(), ice_candidate_t::ip_address, and ice_candidate_t::type.

◆ ice_candidate_type_name()

const char * ice_candidate_type_name ( ice_candidate_type_t  type)

Get human-readable candidate type name.

Parameters
typeCandidate type
Returns
Static string (e.g., "host", "srflx", "relay")

Definition at line 26 of file ice.c.

26 {
27 switch (type) {
29 return "host";
31 return "srflx";
33 return "prflx";
35 return "relay";
36 default:
37 return "unknown";
38 }
39}

References ICE_CANDIDATE_HOST, ICE_CANDIDATE_PRFLX, ICE_CANDIDATE_RELAY, and ICE_CANDIDATE_SRFLX.

Referenced by ice_add_remote_candidate(), and ice_format_candidate().

◆ ice_format_candidate()

asciichat_error_t ice_format_candidate ( const ice_candidate_t *  candidate,
char *  line,
size_t  line_size 
)

Format ICE candidate to attribute string.

Converts ice_candidate_t structure to SDP attribute line.

Parameters
candidateCandidate to format
lineOutput buffer for SDP line (without "a=candidate:" prefix)
line_sizeSize of output buffer
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 320 of file ice.c.

320 {
321 if (!candidate || !line || line_size == 0) {
322 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid candidate format parameters");
323 }
324
325 // Format: foundation component protocol priority ip port typ type [raddr rport] [tcptype type]
326 // Example: 1 1 udp 2130706431 192.168.1.1 54321 typ host
327 // Example with raddr: 1 1 udp 2130706431 203.0.113.45 54321 typ srflx raddr 192.168.1.1 rport 12345
328
329 int written = safe_snprintf(line, line_size, "%s %u %s %u %s %u typ %s", candidate->foundation,
330 candidate->component_id, ice_protocol_name(candidate->protocol), candidate->priority,
331 candidate->ip_address, candidate->port, ice_candidate_type_name(candidate->type));
332
333 if (written < 0 || (size_t)written >= line_size) {
334 return SET_ERRNO(ERROR_BUFFER_FULL, "Candidate line too large for buffer");
335 }
336
337 size_t remaining = line_size - (size_t)written;
338 char *pos = line + written;
339
340 // 2. If srflx/prflx/relay: append raddr and rport
341 if (candidate->type == ICE_CANDIDATE_SRFLX || candidate->type == ICE_CANDIDATE_PRFLX ||
342 candidate->type == ICE_CANDIDATE_RELAY) {
343 if (candidate->raddr[0] != '\0') {
344 int appended = safe_snprintf(pos, remaining, " raddr %s rport %u", candidate->raddr, candidate->rport);
345 if (appended < 0 || (size_t)appended >= remaining) {
346 return SET_ERRNO(ERROR_BUFFER_FULL, "Cannot append raddr/rport to candidate line");
347 }
348 pos += appended;
349 remaining -= (size_t)appended;
350 }
351 }
352
353 // 3. If TCP: append tcptype
354 if (candidate->protocol == ICE_PROTOCOL_TCP) {
355 const char *tcptype_str = "passive"; // default
356 if (candidate->tcp_type == ICE_TCP_TYPE_ACTIVE) {
357 tcptype_str = "active";
358 } else if (candidate->tcp_type == ICE_TCP_TYPE_SO) {
359 tcptype_str = "so";
360 }
361
362 int appended = safe_snprintf(pos, remaining, " tcptype %s", tcptype_str);
363 if (appended < 0 || (size_t)appended >= remaining) {
364 return SET_ERRNO(ERROR_BUFFER_FULL, "Cannot append tcptype to candidate line");
365 }
366 pos += appended;
367 remaining -= (size_t)appended;
368 }
369
370 // 4. Append any extensions
371 if (candidate->extensions[0] != '\0') {
372 int appended = safe_snprintf(pos, remaining, " %s", candidate->extensions);
373 if (appended < 0 || (size_t)appended >= remaining) {
374 return SET_ERRNO(ERROR_BUFFER_FULL, "Cannot append extensions to candidate line");
375 }
376 }
377
378 // 5. Ensure line is null-terminated (snprintf already does this)
379 return ASCIICHAT_OK;
380}
@ ERROR_BUFFER_FULL
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
const char * ice_protocol_name(ice_protocol_t protocol)
Get human-readable protocol name.
Definition ice.c:41
@ ICE_PROTOCOL_TCP
Definition ice.h:86
@ ICE_TCP_TYPE_SO
Simultaneous open.
Definition ice.h:95
@ ICE_TCP_TYPE_ACTIVE
Actively opens connection.
Definition ice.h:93
uint32_t priority
Candidate priority (used for preference)
Definition ice.h:116
uint16_t rport
Related port.
Definition ice.h:127
ice_protocol_t protocol
UDP or TCP.
Definition ice.h:115
char extensions[256]
Additional extensions (e.g., "tcptype passive")
Definition ice.h:133
ice_tcp_type_t tcp_type
active, passive, so
Definition ice.h:130
char raddr[64]
Related address (for srflx/prflx)
Definition ice.h:126

References ASCIICHAT_OK, ice_candidate_t::component_id, ERROR_BUFFER_FULL, ERROR_INVALID_PARAM, ice_candidate_t::extensions, ice_candidate_t::foundation, ICE_CANDIDATE_PRFLX, ICE_CANDIDATE_RELAY, ICE_CANDIDATE_SRFLX, ice_candidate_type_name(), ice_protocol_name(), ICE_PROTOCOL_TCP, ICE_TCP_TYPE_ACTIVE, ICE_TCP_TYPE_SO, ice_candidate_t::ip_address, ice_candidate_t::port, ice_candidate_t::priority, ice_candidate_t::protocol, ice_candidate_t::raddr, ice_candidate_t::rport, safe_snprintf(), SET_ERRNO, ice_candidate_t::tcp_type, and ice_candidate_t::type.

Referenced by ice_add_remote_candidate().

◆ ice_gather_candidates()

asciichat_error_t ice_gather_candidates ( const ice_config_t *  config)

Start ICE candidate gathering.

Initiates gathering of candidates from all sources:

  • Host candidates (network interfaces)
  • STUN server (server-reflexive)
  • TURN relay

Candidates are reported via callback as they're discovered.

Parameters
configICE gathering configuration
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 386 of file ice.c.

386 {
387 if (!config) {
388 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid ICE config");
389 }
390
391 if (!config->send_callback) {
392 return SET_ERRNO(ERROR_INVALID_PARAM, "ICE config missing send_callback");
393 }
394
395 log_debug("ICE: Starting candidate gathering (ufrag=%s, pwd=%s)", config->ufrag, config->pwd);
396
397 // NOTE: libdatachannel (juice) handles ICE candidate gathering internally.
398 // This function is a placeholder for the callback-based API.
399 // The actual gathering happens in the peer_manager when you:
400 // 1. Create peer connection with rtcCreatePeerConnection()
401 // 2. Set ice candidate callback with rtcSetIceCandidateCallback()
402 // 3. libdatachannel automatically gathers candidates from:
403 // - Local interfaces (host candidates)
404 // - STUN servers (server-reflexive candidates)
405 // - TURN servers (relay candidates)
406 // 4. When a candidate is found, rtcSetIceCandidateCallback() is called
407 // 5. Application (webrtc.c) formats and sends via ACDS signaling
408 //
409 // This ice_gather_candidates() function may not be called directly -
410 // the gathering happens automatically when peer connection is created.
411 // Keeping this for API compatibility.
412
413 return ASCIICHAT_OK;
414}
const char * ufrag
Username fragment for ICE (from offer)
Definition ice.h:158
ice_send_candidate_callback_t send_callback
Called for each gathered candidate.
Definition ice.h:160
const char * pwd
Password for ICE (from offer)
Definition ice.h:159

References ASCIICHAT_OK, ERROR_INVALID_PARAM, log_debug, ice_config_t::pwd, ice_config_t::send_callback, SET_ERRNO, and ice_config_t::ufrag.

◆ ice_get_selected_pair()

asciichat_error_t ice_get_selected_pair ( struct webrtc_peer_connection *  pc,
ice_candidate_t *  local_candidate,
ice_candidate_t *  remote_candidate 
)

Get selected candidate pair.

Returns the local and remote candidates that were selected for data flow. Uses libdatachannel's rtcGetSelectedCandidatePair() API via C++ bindings.

Parameters
pcPeer connection to query
local_candidateSelected local candidate (output, may be NULL)
remote_candidateSelected remote candidate (output, may be NULL)
Returns
ASCIICHAT_OK if pair selected, ERROR_INVALID_STATE if no pair selected yet

Definition at line 459 of file ice.c.

460 {
461 // Delegate to C++ implementation which uses libdatachannel's C++ API
462 return ice_get_selected_pair_impl(pc, local_candidate, remote_candidate);
463}
asciichat_error_t ice_get_selected_pair_impl(webrtc_peer_connection_t *pc, ice_candidate_t *local_candidate, ice_candidate_t *remote_candidate)
Get selected ICE candidate pair (C++ implementation)

References ice_get_selected_pair_impl().

◆ ice_get_selected_pair_impl()

asciichat_error_t ice_get_selected_pair_impl ( webrtc_peer_connection_t *  pc,
ice_candidate_t *  local_candidate,
ice_candidate_t *  remote_candidate 
)

Get selected ICE candidate pair (C++ implementation)

This function uses libdatachannel's C++ API to retrieve the selected candidate pair, which is not available in the C API.

Definition at line 58 of file ice_selected_pair.cpp.

59 {
60 if (!pc) {
62 }
63
64 try {
65 // Get the libdatachannel peer connection ID using helper function
66 int rtc_id = webrtc_get_rtc_id(pc);
67 if (rtc_id < 0) {
69 }
70
71 // Query selected candidate pair from libdatachannel
72 // Note: This requires libdatachannel to have the selected candidate pair available
73 char local_buf[512];
74 char remote_buf[512];
75
76 // Try to get the selected local candidate
77 if (rtcGetSelectedCandidatePair(rtc_id, local_buf, sizeof(local_buf), remote_buf, sizeof(remote_buf)) < 0) {
78 // No pair selected yet, or error accessing it
80 }
81
82 // Parse local candidate if requested
83 if (local_candidate) {
84 std::string local_str(local_buf);
85 asciichat_error_t err = parse_datachannel_candidate(local_str, local_candidate);
86 if (err != ASCIICHAT_OK) {
87 return err;
88 }
89 }
90
91 // Parse remote candidate if requested
92 if (remote_candidate) {
93 std::string remote_str(remote_buf);
94 asciichat_error_t err = parse_datachannel_candidate(remote_str, remote_candidate);
95 if (err != ASCIICHAT_OK) {
96 return err;
97 }
98 }
99
100 return ASCIICHAT_OK;
101
102 } catch (const std::exception &e) {
103 return ERROR_NETWORK;
104 } catch (...) {
105 return ERROR_NETWORK;
106 }
107}
@ ERROR_INVALID_STATE
@ ERROR_NETWORK
Definition error_codes.h:77
int webrtc_get_rtc_id(webrtc_peer_connection_t *pc)
Get the internal libdatachannel peer connection ID.

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_NETWORK, and webrtc_get_rtc_id().

Referenced by ice_get_selected_pair().

◆ ice_is_connected()

bool ice_is_connected ( struct webrtc_peer_connection *  pc)

Check ICE connection state.

Parameters
pcPeer connection to check
Returns
true if connected, false otherwise

Definition at line 445 of file ice.c.

445 {
446 if (!pc) {
447 return false;
448 }
449
450 // Check peer connection state
452 return (state == WEBRTC_STATE_CONNECTED);
453}
webrtc_state_t webrtc_get_state(webrtc_peer_connection_t *pc)
Get current connection state.
webrtc_state_t
WebRTC connection state.
@ WEBRTC_STATE_CONNECTED
DataChannel established and ready.

References webrtc_get_state(), and WEBRTC_STATE_CONNECTED.

◆ ice_parse_candidate()

asciichat_error_t ice_parse_candidate ( const char *  line,
ice_candidate_t *  candidate 
)

Parse ICE candidate from attribute string.

Converts SDP candidate attribute line to ice_candidate_t structure.

Format: a=candidate:foundation component protocol priority ip port typ type [raddr rport] [extensions]

Parameters
lineSDP candidate line (without "a=candidate:" prefix)
candidateParsed candidate (output)
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 316 of file ice.c.

316 {
317 return ice_parse_candidate_pcre2(line, candidate);
318}

◆ ice_protocol_name()

const char * ice_protocol_name ( ice_protocol_t  protocol)

Get human-readable protocol name.

Parameters
protocolProtocol type
Returns
Static string (e.g., "udp", "tcp")

Definition at line 41 of file ice.c.

41 {
42 switch (protocol) {
44 return "udp";
46 return "tcp";
47 default:
48 return "unknown";
49 }
50}
@ ICE_PROTOCOL_UDP
Definition ice.h:85

References ICE_PROTOCOL_TCP, and ICE_PROTOCOL_UDP.

Referenced by ice_format_candidate().