ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
signaling.c
Go to the documentation of this file.
1
16#include <ascii-chat/log/log.h>
19#include <string.h>
20
24static bool is_broadcast_uuid(const uint8_t uuid[16]) {
25 for (int i = 0; i < 16; i++) {
26 if (uuid[i] != 0) {
27 return false;
28 }
29 }
30 return true;
31}
32
36static bool uuid_equals(const uint8_t a[16], const uint8_t b[16]) {
37 return memcmp(a, b, 16) == 0;
38}
39
49
53static void find_participant_callback(socket_t socket, void *client_data, void *user_arg) {
55
56 if (!client_data) {
57 return; // Client not yet joined a session
58 }
59
60 acds_client_data_t *acds_data = (acds_client_data_t *)client_data;
61 if (!acds_data->joined_session) {
62 return; // Client connected but hasn't joined
63 }
64
65 // Check if participant_id matches
66 if (uuid_equals(acds_data->participant_id, ctx->target_participant_id)) {
67 ctx->found_socket = socket;
68 ctx->found_transport = acds_data->transport;
69 ctx->found = true;
70 }
71}
72
84
88static void broadcast_callback(socket_t socket, void *client_data, void *user_arg) {
89 (void)socket; // Suppress unused parameter warning - use transport instead
91
92 if (!client_data) {
93 log_debug("Broadcast: socket has no client_data");
94 return; // Client not yet joined a session
95 }
96
97 acds_client_data_t *acds_data = (acds_client_data_t *)client_data;
98 if (!acds_data->joined_session) {
99 log_debug("Broadcast: client not joined (joined_session=false)");
100 return; // Client connected but hasn't joined
101 }
102
103 if (!acds_data->transport) {
104 log_warn("Broadcast: client has no transport");
105 return;
106 }
107
108 log_debug("Broadcast: checking participant=%02x%02x... (session=%02x%02x...)", acds_data->participant_id[0],
109 acds_data->participant_id[1], acds_data->session_id[0], acds_data->session_id[1]);
110
111 // Check if client is in target session
112 if (!uuid_equals(acds_data->session_id, ctx->target_session_id)) {
113 log_debug("Broadcast: client in different session");
114 return; // Different session
115 }
116
117 // Skip excluded participant (e.g., sender)
118 if (ctx->exclude_participant_id && uuid_equals(acds_data->participant_id, ctx->exclude_participant_id)) {
119 log_debug("Broadcast: excluding sender (participant=%02x%02x...)", acds_data->participant_id[0],
120 acds_data->participant_id[1]);
121 return; // Skip sender
122 }
123
124 // Send packet through transport (not raw socket)
125 log_info("★ BROADCAST_CALLBACK: sending packet_type=%u to participant=%02x%02x...", ctx->packet_type,
126 acds_data->participant_id[0], acds_data->participant_id[1]);
127 asciichat_error_t result =
128 packet_send_via_transport(acds_data->transport, ctx->packet_type, ctx->packet, ctx->packet_len, 0);
129 if (result == ASCIICHAT_OK) {
130 ctx->sent_count++;
131 log_info("★ BROADCAST_CALLBACK: successfully sent to participant=%02x%02x...", acds_data->participant_id[0],
132 acds_data->participant_id[1]);
133 } else {
134 log_warn("★ BROADCAST_CALLBACK_FAILED: Failed to send to participant=%02x%02x...: %s", acds_data->participant_id[0],
135 acds_data->participant_id[1], asciichat_error_string(result));
136 }
137}
138
140 size_t total_packet_len) {
141 if (!db || !tcp_server || !sdp) {
142 return SET_ERRNO(ERROR_INVALID_PARAM, "db, tcp_server, or sdp is NULL");
143 }
144
145 /* Find session by UUID using database lookup */
146 log_debug("SDP relay: Looking up session_id=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
147 sdp->session_id[0], sdp->session_id[1], sdp->session_id[2], sdp->session_id[3], sdp->session_id[4],
148 sdp->session_id[5], sdp->session_id[6], sdp->session_id[7], sdp->session_id[8], sdp->session_id[9],
149 sdp->session_id[10], sdp->session_id[11], sdp->session_id[12], sdp->session_id[13], sdp->session_id[14],
150 sdp->session_id[15]);
151 session_entry_t *session = database_session_find_by_id(db, sdp->session_id);
152 if (!session) {
153 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Session not found for SDP relay");
154 }
155 session_entry_destroy(session); // We only need to validate existence
156
157 // Check if broadcast (recipient_id all zeros) or unicast
158 if (is_broadcast_uuid(sdp->recipient_id)) {
159 // Broadcast to all participants in session (except sender)
160 log_debug("Broadcasting SDP to all participants in session (excluding sender)");
161 return signaling_broadcast(db, tcp_server, sdp->session_id, PACKET_TYPE_ACIP_WEBRTC_SDP, sdp, total_packet_len,
162 sdp->sender_id);
163 } else {
164 // Unicast to specific recipient
166 .target_participant_id = sdp->recipient_id, .found_socket = INVALID_SOCKET_VALUE, .found = false};
167
168 tcp_server_foreach_client(tcp_server, find_participant_callback, &ctx);
169
170 if (!ctx.found) {
171 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Recipient participant not found (may be offline)");
172 }
173
174 // Send SDP packet to recipient
175 asciichat_error_t result =
177 if (result != ASCIICHAT_OK) {
178 return SET_ERRNO(ERROR_NETWORK, "Failed to send SDP packet to recipient");
179 }
180
181 log_debug("Relayed SDP from sender to recipient (socket=%d)", ctx.found_socket);
182 }
183
184 return ASCIICHAT_OK;
185}
186
188 size_t total_packet_len) {
189 if (!db || !tcp_server || !ice) {
190 return SET_ERRNO(ERROR_INVALID_PARAM, "db, tcp_server, or ice is NULL");
191 }
192
193 /* Find session by UUID using database lookup */
194 session_entry_t *session = database_session_find_by_id(db, ice->session_id);
195 if (!session) {
196 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Session not found for ICE relay");
197 }
198 session_entry_destroy(session); // We only need to validate existence
199
200 // Check if broadcast (recipient_id all zeros) or unicast
201 if (is_broadcast_uuid(ice->recipient_id)) {
202 // Broadcast to all participants in session (except sender)
203 log_debug("Broadcasting ICE candidate to all participants in session (excluding sender)");
204 return signaling_broadcast(db, tcp_server, ice->session_id, PACKET_TYPE_ACIP_WEBRTC_ICE, ice, total_packet_len,
205 ice->sender_id);
206 } else {
207 // Unicast to specific recipient
209 .target_participant_id = ice->recipient_id, .found_socket = INVALID_SOCKET_VALUE, .found = false};
210
211 tcp_server_foreach_client(tcp_server, find_participant_callback, &ctx);
212
213 if (!ctx.found) {
214 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Recipient participant not found (may be offline)");
215 }
216
217 // Send ICE packet to recipient
218 asciichat_error_t result =
220 if (result != ASCIICHAT_OK) {
221 return SET_ERRNO(ERROR_NETWORK, "Failed to send ICE packet to recipient");
222 }
223
224 log_debug("Relayed ICE candidate from sender to recipient (socket=%d)", ctx.found_socket);
225 }
226
227 return ASCIICHAT_OK;
228}
229
231 packet_type_t packet_type, const void *packet, size_t packet_len,
232 const uint8_t *exclude_participant_id) {
233 if (!db || !tcp_server || !session_id || !packet) {
234 return SET_ERRNO(ERROR_INVALID_PARAM, "db, tcp_server, session_id, or packet is NULL");
235 }
236
237 log_info("★ SIGNALING_BROADCAST_START: packet_type=%u, packet_len=%zu, session=%02x%02x...", packet_type, packet_len,
238 session_id[0], session_id[1]);
239 if (exclude_participant_id) {
240 log_debug("★ SIGNALING_BROADCAST: excluding participant=%02x%02x...", exclude_participant_id[0],
241 exclude_participant_id[1]);
242 }
243
244 /* Find session by UUID using database lookup */
246 if (!session) {
247 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Session not found for broadcast");
248 }
249 session_entry_destroy(session); // We only need to validate existence
250
251 // Broadcast to all participants in session (excluding sender if specified)
253 .exclude_participant_id = exclude_participant_id,
254 .packet_type = packet_type,
255 .packet = packet,
256 .packet_len = packet_len,
257 .sent_count = 0};
258
259 tcp_server_foreach_client(tcp_server, broadcast_callback, &ctx);
260
261 if (ctx.sent_count == 0) {
262 log_warn("★ SIGNALING_BROADCAST_COMPLETE: sent to 0 participants (all offline or not joined yet)");
263 } else {
264 log_info("★ SIGNALING_BROADCAST_COMPLETE: successfully sent to %zu participants", ctx.sent_count);
265 }
266
267 return ASCIICHAT_OK;
268}
269
271 const acip_nat_quality_t *quality, size_t total_packet_len) {
272 if (!db || !tcp_server || !quality) {
273 return SET_ERRNO(ERROR_INVALID_PARAM, "db, tcp_server, or quality is NULL");
274 }
275
276 // Validate packet size
277 if (total_packet_len < sizeof(acip_nat_quality_t)) {
278 return SET_ERRNO(ERROR_NETWORK_SIZE, "NETWORK_QUALITY packet too small");
279 }
280
281 // Extract session_id and sender_id from the quality packet
282 const uint8_t *session_id = quality->session_id;
283 const uint8_t *sender_id = quality->participant_id;
284
285 // Find session by UUID using database lookup
286 log_debug("NETWORK_QUALITY relay: session_id=%02x%02x..., sender_id=%02x%02x...", session_id[0], session_id[1],
287 sender_id[0], sender_id[1]);
289 if (!session) {
290 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Session not found for NETWORK_QUALITY relay");
291 }
292 session_entry_destroy(session); // We only need to validate existence
293
294 // Broadcast to all participants in session (except sender)
295 log_debug("Broadcasting NETWORK_QUALITY to all participants in session (excluding sender)");
296 return signaling_broadcast(db, tcp_server, session_id, PACKET_TYPE_ACIP_NETWORK_QUALITY, quality, total_packet_len,
297 sender_id);
298}
session_entry_t * database_session_find_by_id(sqlite3 *db, const uint8_t session_id[16])
Find session by session_id.
acip_webrtc_ice_t
Definition acds.h:650
acip_webrtc_sdp_t
Definition acds.h:604
acip_nat_quality_t
Definition acds.h:891
unsigned char uint8_t
Definition common.h:56
#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
@ ERROR_NETWORK
Definition error_codes.h:77
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:81
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_NETWORK_SIZE
Definition error_codes.h:82
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
@ PACKET_TYPE_ACIP_WEBRTC_ICE
WebRTC ICE candidate (bidirectional)
Definition packet.h:428
@ PACKET_TYPE_ACIP_NETWORK_QUALITY
NETWORK_QUALITY exchange (unified packet for all quality metrics)
Definition packet.h:455
@ PACKET_TYPE_ACIP_WEBRTC_SDP
WebRTC SDP offer/answer (bidirectional)
Definition packet.h:426
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:278
int socket_t
void session_entry_destroy(session_entry_t *entry)
Free a session entry and all its resources.
void tcp_server_foreach_client(tcp_server_t *server, tcp_client_foreach_fn callback, void *user_arg)
Iterate over all clients.
📝 Logging API with multiple log levels and terminal output control
🌐 Core network I/O operations with timeout support
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:41
ACIP shared/bidirectional packet sending functions.
asciichat_error_t signaling_relay_network_quality(sqlite3 *db, tcp_server_t *tcp_server, const acip_nat_quality_t *quality, size_t total_packet_len)
Relay NETWORK_QUALITY to session participants.
Definition signaling.c:270
asciichat_error_t signaling_broadcast(sqlite3 *db, tcp_server_t *tcp_server, const uint8_t session_id[16], packet_type_t packet_type, const void *packet, size_t packet_len, const uint8_t *exclude_participant_id)
Broadcast packet to all session participants.
Definition signaling.c:230
asciichat_error_t signaling_relay_ice(sqlite3 *db, tcp_server_t *tcp_server, const acip_webrtc_ice_t *ice, size_t total_packet_len)
Relay ICE candidate to recipient.
Definition signaling.c:187
asciichat_error_t signaling_relay_sdp(sqlite3 *db, tcp_server_t *tcp_server, const acip_webrtc_sdp_t *sdp, size_t total_packet_len)
Relay SDP offer/answer to recipient.
Definition signaling.c:139
uint8_t session_id[16]
Per-client connection data.
uint8_t session_id[16]
Session UUID (valid if joined_session)
uint8_t participant_id[16]
Participant UUID (valid if joined_session)
bool joined_session
Whether client has successfully joined a session.
acip_transport_t * transport
Persistent transport for this client connection.
Transport instance structure.
Definition transport.h:214
Context for broadcasting to session participants.
Definition signaling.c:76
packet_type_t packet_type
Packet type to send.
Definition signaling.c:79
size_t packet_len
Packet length.
Definition signaling.c:81
const uint8_t * target_session_id
Session to broadcast to.
Definition signaling.c:77
const void * packet
Packet data.
Definition signaling.c:80
const uint8_t * exclude_participant_id
Participant to exclude (NULL = no exclusion)
Definition signaling.c:78
size_t sent_count
Number of successful sends.
Definition signaling.c:82
Context for finding client by participant_id.
Definition signaling.c:43
socket_t found_socket
Socket if found.
Definition signaling.c:45
const uint8_t * target_participant_id
Participant to find.
Definition signaling.c:44
bool found
Whether client was found.
Definition signaling.c:47
acip_transport_t * found_transport
Transport if found.
Definition signaling.c:46
Session entry data structure.