ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
ice.h File Reference

ICE (Interactive Connectivity Establishment) for WebRTC. More...

Go to the source code of this file.

Data Structures

struct  ice_candidate_t
 Single ICE candidate for connectivity. More...
 
struct  ice_config_t
 ICE gathering configuration. More...
 

Typedefs

typedef asciichat_error_t(* ice_send_candidate_callback_t) (const ice_candidate_t *candidate, const char *mid, void *user_data)
 Callback for sending ICE candidate to peer.
 

Enumerations

enum  ice_candidate_type_t { ICE_CANDIDATE_HOST = 0 , ICE_CANDIDATE_SRFLX = 1 , ICE_CANDIDATE_PRFLX = 2 , ICE_CANDIDATE_RELAY = 3 }
 ICE candidate type enumeration (RFC 5245) More...
 
enum  ice_protocol_t { ICE_PROTOCOL_UDP = 0 , ICE_PROTOCOL_TCP = 1 }
 ICE candidate transport protocol. More...
 
enum  ice_tcp_type_t { ICE_TCP_TYPE_ACTIVE = 0 , ICE_TCP_TYPE_PASSIVE = 1 , ICE_TCP_TYPE_SO = 2 }
 ICE candidate TCP type (if applicable) More...
 

Functions

asciichat_error_t ice_gather_candidates (const ice_config_t *config)
 Start ICE candidate gathering.
 
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.
 
uint32_t ice_calculate_priority (ice_candidate_type_t type, uint16_t local_preference, uint8_t component_id)
 Calculate candidate priority.
 
asciichat_error_t ice_add_remote_candidate (const ice_candidate_t *candidate, const char *mid)
 Add remote candidate to peer connection.
 
bool ice_is_connected (void)
 Check ICE connection state.
 
asciichat_error_t ice_get_selected_pair (ice_candidate_t *local_candidate, ice_candidate_t *remote_candidate)
 Get selected candidate pair.
 
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.
 

Detailed Description

ICE (Interactive Connectivity Establishment) for WebRTC.

Handles ICE candidate gathering, exchange, and connectivity checking. ICE is the mechanism that allows WebRTC peers to discover and connect across NATs and firewalls using STUN and TURN servers.

ICE Candidate Types

Host candidates: Local IP addresses

  • Direct connection if both peers on same network

Server-reflexive (srflx) candidates: NAT-discovered addresses

  • Obtained from STUN server
  • Allows hole punching across NATs

Peer-reflexive (prflx) candidates: Discovered during connectivity checks

  • May emerge when direct STUN-based connection works

Relay candidates: TURN relay server addresses

  • Guaranteed to work through any firewall
  • Last resort for restrictive networks

ICE Candidate Exchange

  1. Gathering phase: Collect candidates from all sources
    • Host candidates (network interfaces)
    • STUN server response
    • TURN relay addresses
  2. Signaling phase: Send candidates to peer via ACDS relay
    • Each candidate sent as ACIP_WEBRTC_ICE packet
    • Order: host first, then srflx, then relay
  3. Connectivity checking: Verify which candidates work
    • WebRTC agent tests each candidate pair
    • Selects best pair (lowest latency, highest bandwidth)
  4. Connection: Data flows through selected candidate pair
    • May change during session (ICE restart)

Integration with ACDS

ICE candidates are sent via ACDS signaling relay:

  • PACKET_TYPE_ACIP_WEBRTC_ICE for each candidate
  • Peer decodes and adds to peer connection
  • Peer agent tests connectivity with received candidates
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

Definition in file ice.h.

Typedef Documentation

◆ ice_send_candidate_callback_t

typedef asciichat_error_t(* ice_send_candidate_callback_t) (const ice_candidate_t *candidate, const char *mid, void *user_data)

Callback for sending ICE candidate to peer.

Called by ICE agent when a new candidate is discovered. Implementation should send via ACDS relay.

Parameters
candidateDiscovered candidate
midMedia stream ID (e.g., "0" for audio, "1" for video)
user_dataUser context pointer
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 151 of file ice.h.

Enumeration Type Documentation

◆ ice_candidate_type_t

ICE candidate type enumeration (RFC 5245)

Enumerator
ICE_CANDIDATE_HOST 

Host candidate (local IP address)

ICE_CANDIDATE_SRFLX 

Server-reflexive (NAT-discovered via STUN)

ICE_CANDIDATE_PRFLX 

Peer-reflexive (discovered during checks)

ICE_CANDIDATE_RELAY 

Relay candidate (TURN server)

Definition at line 74 of file ice.h.

74 {
ice_candidate_type_t
ICE candidate type enumeration (RFC 5245)
Definition ice.h:74
@ 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

◆ ice_protocol_t

ICE candidate transport protocol.

Enumerator
ICE_PROTOCOL_UDP 
ICE_PROTOCOL_TCP 

Definition at line 84 of file ice.h.

84 {
ice_protocol_t
ICE candidate transport protocol.
Definition ice.h:84
@ ICE_PROTOCOL_UDP
Definition ice.h:85
@ ICE_PROTOCOL_TCP
Definition ice.h:86

◆ ice_tcp_type_t

ICE candidate TCP type (if applicable)

Enumerator
ICE_TCP_TYPE_ACTIVE 

Actively opens connection.

ICE_TCP_TYPE_PASSIVE 

Passively waits for connection.

ICE_TCP_TYPE_SO 

Simultaneous open.

Definition at line 92 of file ice.h.

92 {
95 ICE_TCP_TYPE_SO = 2,
ice_tcp_type_t
ICE candidate TCP type (if applicable)
Definition ice.h:92
@ ICE_TCP_TYPE_SO
Simultaneous open.
Definition ice.h:95
@ ICE_TCP_TYPE_ACTIVE
Actively opens connection.
Definition ice.h:93
@ ICE_TCP_TYPE_PASSIVE
Passively waits for connection.
Definition ice.h:94

Function Documentation

◆ ice_add_remote_candidate()

asciichat_error_t ice_add_remote_candidate ( 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
candidateRemote candidate from peer
midMedia stream ID
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 320 of file ice.c.

320 {
321 if (!candidate || !mid) {
322 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid remote candidate parameters");
323 }
324
325 // Validate candidate has required fields
326 if (candidate->ip_address[0] == '\0' || candidate->port == 0) {
327 return SET_ERRNO(ERROR_INVALID_PARAM, "Candidate missing IP address or port");
328 }
329
330 log_debug("ICE: Adding remote candidate %s:%u (type=%s, foundation=%s) on mid=%s", candidate->ip_address,
331 candidate->port, ice_candidate_type_name(candidate->type), candidate->foundation, mid);
332
333 // TODO: Implement remote candidate addition to peer connection
334 // Integration point with libdatachannel peer manager:
335 // Steps:
336 // 1. Get current peer connection from global state (webrtc_get_transport() or similar)
337 // 2. Call libdatachannel's addIceCandidate(candidate) function
338 // 3. libdatachannel will:
339 // - Add to remote candidate list
340 // - Begin connectivity checks with local candidates
341 // - Measure RTT, jitter, packet loss
342 // - Select best working pair
343 // 4. When connection succeeds, data channel becomes ready
344
345 // For now, log the candidate and return success
346 // Real implementation will integrate with webrtc_peer_manager_add_remote_candidate()
347 // or similar function that will be called by the signaling relay
348
349 return ASCIICHAT_OK;
350}
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_INVALID_PARAM
#define log_debug(...)
Log a DEBUG message.
const char * ice_candidate_type_name(ice_candidate_type_t type)
Get human-readable candidate type name.
Definition ice.c:21
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_candidate_t::ip_address, log_debug, ice_candidate_t::port, SET_ERRNO, and ice_candidate_t::type.

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

51 {
52 // RFC 5245: priority = (2^24 * typePreference) + (2^8 * localPreference) + (256 - componentID)
53
54 // Type preference (higher is better):
55 // - Host: 126 (most preferred, lowest latency)
56 // - SRFLX: 100 (STUN-discovered address)
57 // - PRFLX: 110 (discovered during connectivity checks)
58 // - Relay: 0 (least preferred, uses bandwidth)
59
60 uint8_t type_preference = 0;
61 switch (type) {
63 type_preference = 126;
64 break;
66 type_preference = 100;
67 break;
69 type_preference = 110;
70 break;
72 type_preference = 0;
73 break;
74 default:
75 type_preference = 0;
76 break;
77 }
78
79 uint32_t priority =
80 (((uint32_t)type_preference) << 24) | (((uint32_t)local_preference) << 8) | (256 - (uint32_t)component_id);
81
82 return priority;
83}
unsigned int uint32_t
Definition common.h:58
unsigned char uint8_t
Definition common.h:56

References ICE_CANDIDATE_HOST, ICE_CANDIDATE_PRFLX, ICE_CANDIDATE_RELAY, and ICE_CANDIDATE_SRFLX.

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

21 {
22 switch (type) {
24 return "host";
26 return "srflx";
28 return "prflx";
30 return "relay";
31 default:
32 return "unknown";
33 }
34}

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

220 {
221 if (!candidate || !line || line_size == 0) {
222 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid candidate format parameters");
223 }
224
225 // Format: foundation component protocol priority ip port typ type [raddr rport] [tcptype type]
226 // Example: 1 1 udp 2130706431 192.168.1.1 54321 typ host
227 // Example with raddr: 1 1 udp 2130706431 203.0.113.45 54321 typ srflx raddr 192.168.1.1 rport 12345
228
229 int written = snprintf(line, line_size, "%s %u %s %u %s %u typ %s", candidate->foundation, candidate->component_id,
230 ice_protocol_name(candidate->protocol), candidate->priority, candidate->ip_address,
231 candidate->port, ice_candidate_type_name(candidate->type));
232
233 if (written < 0 || (size_t)written >= line_size) {
234 return SET_ERRNO(ERROR_BUFFER_FULL, "Candidate line too large for buffer");
235 }
236
237 size_t remaining = line_size - (size_t)written;
238 char *pos = line + written;
239
240 // 2. If srflx/prflx/relay: append raddr and rport
241 if (candidate->type == ICE_CANDIDATE_SRFLX || candidate->type == ICE_CANDIDATE_PRFLX ||
242 candidate->type == ICE_CANDIDATE_RELAY) {
243 if (candidate->raddr[0] != '\0') {
244 int appended = snprintf(pos, remaining, " raddr %s rport %u", candidate->raddr, candidate->rport);
245 if (appended < 0 || (size_t)appended >= remaining) {
246 return SET_ERRNO(ERROR_BUFFER_FULL, "Cannot append raddr/rport to candidate line");
247 }
248 pos += appended;
249 remaining -= (size_t)appended;
250 }
251 }
252
253 // 3. If TCP: append tcptype
254 if (candidate->protocol == ICE_PROTOCOL_TCP) {
255 const char *tcptype_str = "passive"; // default
256 if (candidate->tcp_type == ICE_TCP_TYPE_ACTIVE) {
257 tcptype_str = "active";
258 } else if (candidate->tcp_type == ICE_TCP_TYPE_SO) {
259 tcptype_str = "so";
260 }
261
262 int appended = snprintf(pos, remaining, " tcptype %s", tcptype_str);
263 if (appended < 0 || (size_t)appended >= remaining) {
264 return SET_ERRNO(ERROR_BUFFER_FULL, "Cannot append tcptype to candidate line");
265 }
266 pos += appended;
267 remaining -= (size_t)appended;
268 }
269
270 // 4. Append any extensions
271 if (candidate->extensions[0] != '\0') {
272 int appended = snprintf(pos, remaining, " %s", candidate->extensions);
273 if (appended < 0 || (size_t)appended >= remaining) {
274 return SET_ERRNO(ERROR_BUFFER_FULL, "Cannot append extensions to candidate line");
275 }
276 }
277
278 // 5. Ensure line is null-terminated (snprintf already does this)
279 return ASCIICHAT_OK;
280}
@ ERROR_BUFFER_FULL
Definition error_codes.h:97
const char * ice_protocol_name(ice_protocol_t protocol)
Get human-readable protocol name.
Definition ice.c:36
uint32_t component_id
Component (1=RTP, 2=RTCP; usually 1)
Definition ice.h:114
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, SET_ERRNO, ice_candidate_t::tcp_type, and ice_candidate_t::type.

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

286 {
287 if (!config) {
288 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid ICE config");
289 }
290
291 if (!config->send_callback) {
292 return SET_ERRNO(ERROR_INVALID_PARAM, "ICE config missing send_callback");
293 }
294
295 log_debug("ICE: Starting candidate gathering (ufrag=%s, pwd=%s)", config->ufrag, config->pwd);
296
297 // NOTE: libdatachannel (juice) handles ICE candidate gathering internally.
298 // This function is a placeholder for the callback-based API.
299 // The actual gathering happens in the peer_manager when you:
300 // 1. Create peer connection with rtcCreatePeerConnection()
301 // 2. Set ice candidate callback with rtcSetIceCandidateCallback()
302 // 3. libdatachannel automatically gathers candidates from:
303 // - Local interfaces (host candidates)
304 // - STUN servers (server-reflexive candidates)
305 // - TURN servers (relay candidates)
306 // 4. When a candidate is found, rtcSetIceCandidateCallback() is called
307 // 5. Application (webrtc.c) formats and sends via ACDS signaling
308 //
309 // This ice_gather_candidates() function may not be called directly -
310 // the gathering happens automatically when peer connection is created.
311 // Keeping this for API compatibility.
312
313 return ASCIICHAT_OK;
314}
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 ( 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.

Parameters
local_candidateSelected local candidate (output, may be NULL)
remote_candidateSelected remote candidate (output, may be NULL)
Returns
ASCIICHAT_OK if pair selected, error if not yet determined

Definition at line 362 of file ice.c.

362 {
363 // TODO: Implement selected pair retrieval
364 // This requires integration with libdatachannel peer connection.
365 // For now, return error - will be implemented when peer_manager is integrated.
366 // Steps:
367 // 1. Get current peer connection from global state
368 // 2. Find the candidate pair in "Ready" state (data is flowing)
369 // 3. Copy local candidate if local_candidate != NULL
370 // 4. Copy remote candidate if remote_candidate != NULL
371 // 5. Return error if no pair selected yet
372
373 return SET_ERRNO(ERROR_INVALID_STATE, "No candidate pair selected yet");
374}
@ ERROR_INVALID_STATE

References ERROR_INVALID_STATE, and SET_ERRNO.

◆ ice_is_connected()

bool ice_is_connected ( void  )

Check ICE connection state.

Returns
true if at least one candidate pair is connected, false otherwise

Definition at line 352 of file ice.c.

352 {
353 // TODO: Implement connectivity check
354 // This requires integration with libdatachannel peer connection state machine.
355 // For now, return false - will be implemented when peer_manager state tracking is added.
356 // Step 1: Get current peer connection from global state
357 // Step 2: Check if state == CONNECTED or READY
358 // Step 3: Return true if at least one candidate pair has data flowing
359 return false;
360}

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

89 {
90 if (!line || !candidate) {
91 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid candidate parse parameters");
92 }
93
94 memset(candidate, 0, sizeof(ice_candidate_t));
95
96 // Parse: foundation component protocol priority ip port typ type [raddr rport] [extensions]
97 // Example: 1 1 udp 2130706431 192.168.1.1 54321 typ host
98
99 char line_copy[512];
100 SAFE_STRNCPY(line_copy, line, sizeof(line_copy));
101
102 char *saveptr = NULL;
103 char *token = NULL;
104
105 // 1. Parse foundation
106 token = strtok_r(line_copy, " ", &saveptr);
107 if (!token) {
108 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing foundation in candidate");
109 }
110 SAFE_STRNCPY(candidate->foundation, token, sizeof(candidate->foundation));
111
112 // 2. Parse component ID
113 token = strtok_r(NULL, " ", &saveptr);
114 if (!token || sscanf(token, "%u", &candidate->component_id) != 1) {
115 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid component ID");
116 }
117
118 // 3. Parse protocol (udp/tcp)
119 token = strtok_r(NULL, " ", &saveptr);
120 if (!token) {
121 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing protocol");
122 }
123 if (strcmp(token, "udp") == 0) {
124 candidate->protocol = ICE_PROTOCOL_UDP;
125 } else if (strcmp(token, "tcp") == 0) {
126 candidate->protocol = ICE_PROTOCOL_TCP;
127 } else {
128 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid protocol: %s", token);
129 }
130
131 // 4. Parse priority
132 token = strtok_r(NULL, " ", &saveptr);
133 if (!token || sscanf(token, "%u", &candidate->priority) != 1) {
134 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid priority");
135 }
136
137 // 5. Parse IP address
138 token = strtok_r(NULL, " ", &saveptr);
139 if (!token) {
140 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing IP address");
141 }
142 SAFE_STRNCPY(candidate->ip_address, token, sizeof(candidate->ip_address));
143
144 // 6. Parse port
145 token = strtok_r(NULL, " ", &saveptr);
146 if (!token || sscanf(token, "%hu", &candidate->port) != 1) {
147 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid port");
148 }
149
150 // 7. Expect "typ" keyword
151 token = strtok_r(NULL, " ", &saveptr);
152 if (!token || strcmp(token, "typ") != 0) {
153 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing 'typ' keyword");
154 }
155
156 // 8. Parse candidate type
157 token = strtok_r(NULL, " ", &saveptr);
158 if (!token) {
159 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing candidate type");
160 }
161
162 if (strcmp(token, "host") == 0) {
163 candidate->type = ICE_CANDIDATE_HOST;
164 } else if (strcmp(token, "srflx") == 0) {
165 candidate->type = ICE_CANDIDATE_SRFLX;
166 } else if (strcmp(token, "prflx") == 0) {
167 candidate->type = ICE_CANDIDATE_PRFLX;
168 } else if (strcmp(token, "relay") == 0) {
169 candidate->type = ICE_CANDIDATE_RELAY;
170 } else {
171 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid candidate type: %s", token);
172 }
173
174 // 9. If srflx/prflx/relay: parse raddr and rport
175 if (candidate->type == ICE_CANDIDATE_SRFLX || candidate->type == ICE_CANDIDATE_PRFLX ||
176 candidate->type == ICE_CANDIDATE_RELAY) {
177 token = strtok_r(NULL, " ", &saveptr);
178 if (token && strcmp(token, "raddr") == 0) {
179 token = strtok_r(NULL, " ", &saveptr);
180 if (!token) {
181 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing raddr value");
182 }
183 SAFE_STRNCPY(candidate->raddr, token, sizeof(candidate->raddr));
184
185 token = strtok_r(NULL, " ", &saveptr);
186 if (!token || strcmp(token, "rport") != 0) {
187 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing 'rport' keyword");
188 }
189
190 token = strtok_r(NULL, " ", &saveptr);
191 if (!token || sscanf(token, "%hu", &candidate->rport) != 1) {
192 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid rport value");
193 }
194 }
195 }
196
197 // 10. Parse any extensions (tcptype for TCP candidates)
198 if (candidate->protocol == ICE_PROTOCOL_TCP) {
199 token = strtok_r(NULL, " ", &saveptr);
200 while (token) {
201 if (strcmp(token, "tcptype") == 0) {
202 token = strtok_r(NULL, " ", &saveptr);
203 if (token) {
204 if (strcmp(token, "active") == 0) {
205 candidate->tcp_type = ICE_TCP_TYPE_ACTIVE;
206 } else if (strcmp(token, "passive") == 0) {
207 candidate->tcp_type = ICE_TCP_TYPE_PASSIVE;
208 } else if (strcmp(token, "so") == 0) {
209 candidate->tcp_type = ICE_TCP_TYPE_SO;
210 }
211 }
212 }
213 token = strtok_r(NULL, " ", &saveptr);
214 }
215 }
216
217 return ASCIICHAT_OK;
218}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
Single ICE candidate for connectivity.
Definition ice.h:111

References ASCIICHAT_OK, ice_candidate_t::component_id, ERROR_INVALID_PARAM, ice_candidate_t::foundation, ICE_CANDIDATE_HOST, ICE_CANDIDATE_PRFLX, ICE_CANDIDATE_RELAY, ICE_CANDIDATE_SRFLX, ICE_PROTOCOL_TCP, ICE_PROTOCOL_UDP, ICE_TCP_TYPE_ACTIVE, ICE_TCP_TYPE_PASSIVE, 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_STRNCPY, SET_ERRNO, ice_candidate_t::tcp_type, and ice_candidate_t::type.

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

36 {
37 switch (protocol) {
39 return "udp";
41 return "tcp";
42 default:
43 return "unknown";
44 }
45}

References ICE_PROTOCOL_TCP, and ICE_PROTOCOL_UDP.

Referenced by ice_format_candidate().