ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
signaling.c
Go to the documentation of this file.
1
14#include <ascii-chat/discovery/database.h>
15#include <ascii-chat/discovery/session.h>
16#include <ascii-chat/log/logging.h>
17#include <ascii-chat/network/network.h>
18#include <string.h>
19
23static bool is_broadcast_uuid(const uint8_t uuid[16]) {
24 for (int i = 0; i < 16; i++) {
25 if (uuid[i] != 0) {
26 return false;
27 }
28 }
29 return true;
30}
31
35static bool uuid_equals(const uint8_t a[16], const uint8_t b[16]) {
36 return memcmp(a, b, 16) == 0;
37}
38
47
51static void find_participant_callback(socket_t socket, void *client_data, void *user_arg) {
53
54 if (!client_data) {
55 return; // Client not yet joined a session
56 }
57
58 acds_client_data_t *acds_data = (acds_client_data_t *)client_data;
59 if (!acds_data->joined_session) {
60 return; // Client connected but hasn't joined
61 }
62
63 // Check if participant_id matches
64 if (uuid_equals(acds_data->participant_id, ctx->target_participant_id)) {
65 ctx->found_socket = socket;
66 ctx->found = true;
67 }
68}
69
73typedef struct {
74 const uint8_t *target_session_id;
75 const uint8_t *exclude_participant_id;
76 packet_type_t packet_type;
77 const void *packet;
78 size_t packet_len;
79 size_t sent_count;
81
85static void broadcast_callback(socket_t socket, void *client_data, void *user_arg) {
87
88 if (!client_data) {
89 log_debug("Broadcast: socket=%d has no client_data", socket);
90 return; // Client not yet joined a session
91 }
92
93 acds_client_data_t *acds_data = (acds_client_data_t *)client_data;
94 if (!acds_data->joined_session) {
95 log_debug("Broadcast: socket=%d not joined (joined_session=false)", socket);
96 return; // Client connected but hasn't joined
97 }
98
99 log_debug("Broadcast: checking socket=%d (session=%02x%02x..., participant=%02x%02x...)", socket,
100 acds_data->session_id[0], acds_data->session_id[1], acds_data->participant_id[0],
101 acds_data->participant_id[1]);
102
103 // Check if client is in target session
104 if (!uuid_equals(acds_data->session_id, ctx->target_session_id)) {
105 log_debug("Broadcast: socket=%d in different session", socket);
106 return; // Different session
107 }
108
109 // Skip excluded participant (e.g., sender)
110 if (ctx->exclude_participant_id && uuid_equals(acds_data->participant_id, ctx->exclude_participant_id)) {
111 log_debug("Broadcast: socket=%d is excluded sender (participant=%02x%02x...)", socket, acds_data->participant_id[0],
112 acds_data->participant_id[1]);
113 return; // Skip sender
114 }
115
116 // Send packet to this participant
117 log_debug("Broadcast: sending to socket=%d (participant=%02x%02x...)", socket, acds_data->participant_id[0],
118 acds_data->participant_id[1]);
119 int result = send_packet(socket, ctx->packet_type, ctx->packet, ctx->packet_len);
120 if (result == 0) {
121 ctx->sent_count++;
122 } else {
123 log_warn("Failed to send packet to participant (socket=%d)", socket);
124 }
125}
126
127asciichat_error_t signaling_relay_sdp(sqlite3 *db, tcp_server_t *tcp_server, const acip_webrtc_sdp_t *sdp,
128 size_t total_packet_len) {
129 if (!db || !tcp_server || !sdp) {
130 return SET_ERRNO(ERROR_INVALID_PARAM, "db, tcp_server, or sdp is NULL");
131 }
132
133 /* Find session by UUID using database lookup */
134 log_debug("SDP relay: Looking up session_id=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
135 sdp->session_id[0], sdp->session_id[1], sdp->session_id[2], sdp->session_id[3], sdp->session_id[4],
136 sdp->session_id[5], sdp->session_id[6], sdp->session_id[7], sdp->session_id[8], sdp->session_id[9],
137 sdp->session_id[10], sdp->session_id[11], sdp->session_id[12], sdp->session_id[13], sdp->session_id[14],
138 sdp->session_id[15]);
139 session_entry_t *session = database_session_find_by_id(db, sdp->session_id);
140 if (!session) {
141 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Session not found for SDP relay");
142 }
143 session_entry_destroy(session); // We only need to validate existence
144
145 // Check if broadcast (recipient_id all zeros) or unicast
146 if (is_broadcast_uuid(sdp->recipient_id)) {
147 // Broadcast to all participants in session (except sender)
148 log_debug("Broadcasting SDP to all participants in session (excluding sender)");
149 return signaling_broadcast(db, tcp_server, sdp->session_id, PACKET_TYPE_ACIP_WEBRTC_SDP, sdp, total_packet_len,
150 sdp->sender_id);
151 } else {
152 // Unicast to specific recipient
154 .target_participant_id = sdp->recipient_id, .found_socket = INVALID_SOCKET_VALUE, .found = false};
155
156 tcp_server_foreach_client(tcp_server, find_participant_callback, &ctx);
157
158 if (!ctx.found) {
159 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Recipient participant not found (may be offline)");
160 }
161
162 // Send SDP packet to recipient
163 int result = send_packet(ctx.found_socket, PACKET_TYPE_ACIP_WEBRTC_SDP, sdp, total_packet_len);
164 if (result != 0) {
165 return SET_ERRNO(ERROR_NETWORK, "Failed to send SDP packet to recipient");
166 }
167
168 log_debug("Relayed SDP from sender to recipient (socket=%d)", ctx.found_socket);
169 }
170
171 return ASCIICHAT_OK;
172}
173
174asciichat_error_t signaling_relay_ice(sqlite3 *db, tcp_server_t *tcp_server, const acip_webrtc_ice_t *ice,
175 size_t total_packet_len) {
176 if (!db || !tcp_server || !ice) {
177 return SET_ERRNO(ERROR_INVALID_PARAM, "db, tcp_server, or ice is NULL");
178 }
179
180 /* Find session by UUID using database lookup */
181 session_entry_t *session = database_session_find_by_id(db, ice->session_id);
182 if (!session) {
183 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Session not found for ICE relay");
184 }
185 session_entry_destroy(session); // We only need to validate existence
186
187 // Check if broadcast (recipient_id all zeros) or unicast
188 if (is_broadcast_uuid(ice->recipient_id)) {
189 // Broadcast to all participants in session (except sender)
190 log_debug("Broadcasting ICE candidate to all participants in session (excluding sender)");
191 return signaling_broadcast(db, tcp_server, ice->session_id, PACKET_TYPE_ACIP_WEBRTC_ICE, ice, total_packet_len,
192 ice->sender_id);
193 } else {
194 // Unicast to specific recipient
196 .target_participant_id = ice->recipient_id, .found_socket = INVALID_SOCKET_VALUE, .found = false};
197
198 tcp_server_foreach_client(tcp_server, find_participant_callback, &ctx);
199
200 if (!ctx.found) {
201 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Recipient participant not found (may be offline)");
202 }
203
204 // Send ICE packet to recipient
205 int result = send_packet(ctx.found_socket, PACKET_TYPE_ACIP_WEBRTC_ICE, ice, total_packet_len);
206 if (result != 0) {
207 return SET_ERRNO(ERROR_NETWORK, "Failed to send ICE packet to recipient");
208 }
209
210 log_debug("Relayed ICE candidate from sender to recipient (socket=%d)", ctx.found_socket);
211 }
212
213 return ASCIICHAT_OK;
214}
215
216asciichat_error_t signaling_broadcast(sqlite3 *db, tcp_server_t *tcp_server, const uint8_t session_id[16],
217 packet_type_t packet_type, const void *packet, size_t packet_len,
218 const uint8_t *exclude_participant_id) {
219 if (!db || !tcp_server || !session_id || !packet) {
220 return SET_ERRNO(ERROR_INVALID_PARAM, "db, tcp_server, session_id, or packet is NULL");
221 }
222
223 /* Find session by UUID using database lookup */
224 session_entry_t *session = database_session_find_by_id(db, session_id);
225 if (!session) {
226 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Session not found for broadcast");
227 }
228 session_entry_destroy(session); // We only need to validate existence
229
230 // Broadcast to all participants in session (excluding sender if specified)
232 .exclude_participant_id = exclude_participant_id,
233 .packet_type = packet_type,
234 .packet = packet,
235 .packet_len = packet_len,
236 .sent_count = 0};
237
238 tcp_server_foreach_client(tcp_server, broadcast_callback, &ctx);
239
240 if (ctx.sent_count == 0) {
241 log_warn("Broadcast sent to 0 participants (all offline or not joined yet)");
242 } else {
243 log_debug("Broadcast sent to %zu participants", ctx.sent_count);
244 }
245
246 return ASCIICHAT_OK;
247}
session_entry_t * database_session_find_by_id(sqlite3 *db, const uint8_t session_id[16])
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)
int send_packet(socket_t sockfd, packet_type_t type, const void *data, size_t len)
Send a basic packet without encryption.
Definition packet.c:753
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:216
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:174
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:127
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.
Context for broadcasting to session participants.
Definition signaling.c:73
packet_type_t packet_type
Packet type to send.
Definition signaling.c:76
size_t packet_len
Packet length.
Definition signaling.c:78
const uint8_t * target_session_id
Session to broadcast to.
Definition signaling.c:74
const void * packet
Packet data.
Definition signaling.c:77
const uint8_t * exclude_participant_id
Participant to exclude (NULL = no exclusion)
Definition signaling.c:75
size_t sent_count
Number of successful sends.
Definition signaling.c:79
Context for finding client by participant_id.
Definition signaling.c:42
socket_t found_socket
Socket if found.
Definition signaling.c:44
const uint8_t * target_participant_id
Participant to find.
Definition signaling.c:43
bool found
Whether client was found.
Definition signaling.c:45