ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
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)
 
const char * ice_protocol_name (ice_protocol_t protocol)
 
uint32_t ice_calculate_priority (ice_candidate_type_t type, uint16_t local_preference, uint8_t component_id)
 
uint32_t ice_calculate_priority_for_candidate (const ice_candidate_t *candidate)
 
asciichat_error_t ice_parse_candidate (const char *line, ice_candidate_t *candidate)
 
asciichat_error_t ice_format_candidate (const ice_candidate_t *candidate, char *line, size_t line_size)
 
asciichat_error_t ice_gather_candidates (const ice_config_t *config)
 
asciichat_error_t ice_add_remote_candidate (webrtc_peer_connection_t *pc, const ice_candidate_t *candidate, const char *mid)
 
bool ice_is_connected (webrtc_peer_connection_t *pc)
 
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)
 

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 20 of file ice.c.

Function Documentation

◆ ice_add_remote_candidate()

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

Definition at line 423 of file ice.c.

424 {
425 if (!pc || !candidate || !mid) {
426 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid remote candidate parameters");
427 }
428
429 // Validate candidate has required fields
430 if (candidate->ip_address[0] == '\0' || candidate->port == 0) {
431 return SET_ERRNO(ERROR_INVALID_PARAM, "Candidate missing IP address or port");
432 }
433
434 log_debug("ICE: Adding remote candidate %s:%u (type=%s, foundation=%s) on mid=%s", candidate->ip_address,
435 candidate->port, ice_candidate_type_name(candidate->type), candidate->foundation, mid);
436
437 // Format candidate to SDP attribute line for libdatachannel
438 char candidate_line[512];
439 asciichat_error_t err = ice_format_candidate(candidate, candidate_line, sizeof(candidate_line));
440 if (err != ASCIICHAT_OK) {
441 return err;
442 }
443
444 // Delegate to WebRTC layer
445 return webrtc_add_remote_candidate(pc, candidate_line, mid);
446}
asciichat_error_t ice_format_candidate(const ice_candidate_t *candidate, char *line, size_t line_size)
Definition ice.c:323
const char * ice_candidate_type_name(ice_candidate_type_t type)
Definition ice.c:27
asciichat_error_t webrtc_add_remote_candidate(webrtc_peer_connection_t *pc, const char *candidate, const char *mid)

References ice_candidate_type_name(), ice_format_candidate(), 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 
)

Definition at line 102 of file ice.c.

102 {
103 // RFC 5245: priority = (2^24 * typePreference) + (2^8 * localPreference) + (256 - componentID)
104
105 // Type preference (higher is better):
106 // - Host: 126 (most preferred, lowest latency)
107 // - SRFLX: 100 (STUN-discovered address)
108 // - PRFLX: 110 (discovered during connectivity checks)
109 // - Relay: 0 (least preferred, uses bandwidth)
110
111 uint8_t type_preference = 0;
112 switch (type) {
113 case ICE_CANDIDATE_HOST:
114 type_preference = 126;
115 break;
116 case ICE_CANDIDATE_SRFLX:
117 type_preference = 100;
118 break;
119 case ICE_CANDIDATE_PRFLX:
120 type_preference = 110;
121 break;
122 case ICE_CANDIDATE_RELAY:
123 type_preference = 0;
124 break;
125 default:
126 type_preference = 0;
127 break;
128 }
129
130 uint32_t priority =
131 (((uint32_t)type_preference) << 24) | (((uint32_t)local_preference) << 8) | (256 - (uint32_t)component_id);
132
133 return priority;
134}

Referenced by ice_calculate_priority_for_candidate().

◆ ice_calculate_priority_for_candidate()

uint32_t ice_calculate_priority_for_candidate ( const ice_candidate_t *  candidate)

Definition at line 136 of file ice.c.

136 {
137 if (!candidate) {
138 return 0;
139 }
140
141 // Calculate local preference based on IP classification
142 uint16_t local_pref = ice_calculate_local_preference_by_ip(candidate->ip_address);
143
144 // Use standard RFC 5245 priority formula with IP-aware local preference
145 return ice_calculate_priority(candidate->type, local_pref, (uint8_t)candidate->component_id);
146}
uint32_t ice_calculate_priority(ice_candidate_type_t type, uint16_t local_preference, uint8_t component_id)
Definition ice.c:102

References ice_calculate_priority().

◆ ice_candidate_type_name()

const char * ice_candidate_type_name ( ice_candidate_type_t  type)

Definition at line 27 of file ice.c.

27 {
28 switch (type) {
29 case ICE_CANDIDATE_HOST:
30 return "host";
31 case ICE_CANDIDATE_SRFLX:
32 return "srflx";
33 case ICE_CANDIDATE_PRFLX:
34 return "prflx";
35 case ICE_CANDIDATE_RELAY:
36 return "relay";
37 default:
38 return "unknown";
39 }
40}

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 
)

Definition at line 323 of file ice.c.

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

References ice_candidate_type_name(), ice_protocol_name(), and safe_snprintf().

Referenced by ice_add_remote_candidate().

◆ ice_gather_candidates()

asciichat_error_t ice_gather_candidates ( const ice_config_t *  config)

Definition at line 389 of file ice.c.

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

◆ ice_get_selected_pair()

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

Definition at line 462 of file ice.c.

463 {
464 // Delegate to C++ implementation which uses libdatachannel's C++ API
465 return ice_get_selected_pair_impl(pc, local_candidate, remote_candidate);
466}
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) {
61 return ERROR_INVALID_PARAM;
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) {
68 return ERROR_INVALID_PARAM;
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
79 return ERROR_INVALID_STATE;
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}
int webrtc_get_rtc_id(webrtc_peer_connection_t *pc)
Get the internal libdatachannel peer connection ID.

References webrtc_get_rtc_id().

Referenced by ice_get_selected_pair().

◆ ice_is_connected()

bool ice_is_connected ( webrtc_peer_connection_t *  pc)

Definition at line 448 of file ice.c.

448 {
449 if (!pc) {
450 return false;
451 }
452
453 // Check peer connection state
454 webrtc_state_t state = webrtc_get_state(pc);
455 return (state == WEBRTC_STATE_CONNECTED);
456}
webrtc_state_t webrtc_get_state(webrtc_peer_connection_t *pc)

References webrtc_get_state().

◆ ice_parse_candidate()

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

Definition at line 319 of file ice.c.

319 {
320 return ice_parse_candidate_pcre2(line, candidate);
321}

◆ ice_protocol_name()

const char * ice_protocol_name ( ice_protocol_t  protocol)

Definition at line 42 of file ice.c.

42 {
43 switch (protocol) {
44 case ICE_PROTOCOL_UDP:
45 return "udp";
46 case ICE_PROTOCOL_TCP:
47 return "tcp";
48 default:
49 return "unknown";
50 }
51}

Referenced by ice_format_candidate().