ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
acds_handlers.c
Go to the documentation of this file.
1
17#include <ascii-chat/log/log.h>
19#include <ascii-chat/common.h>
21#include <string.h>
22
23// =============================================================================
24// ACDS Handler Function Types and Hash Table
25// =============================================================================
26
27// ACDS handler function signature
28typedef asciichat_error_t (*acip_acds_handler_func_t)(const void *payload, size_t payload_len,
29 acip_transport_t *transport, const char *client_ip,
30 const acip_acds_callbacks_t *callbacks);
31
32// Hash table for O(1) packet dispatch
33#define ACDS_HASH_SIZE 32
34#define ACDS_HANDLER_COUNT 11
35
43
44#define ACDS_HASH(type) ((type) % ACDS_HASH_SIZE)
45
46static inline int acds_handler_hash_lookup(const acds_hash_entry_t *table, packet_type_t type) {
47 uint32_t h = ACDS_HASH(type);
48 for (int i = 0; i < ACDS_HASH_SIZE; i++) {
49 uint32_t slot = (h + i) % ACDS_HASH_SIZE;
50 if (table[slot].key == 0)
51 return -1;
52 if (table[slot].key == type)
53 return table[slot].handler_idx;
54 }
55 return -1;
56}
57
58// =============================================================================
59// Forward Declarations
60// =============================================================================
61
62static asciichat_error_t handle_acds_session_create(const void *payload, size_t payload_len,
63 acip_transport_t *transport, const char *client_ip,
64 const acip_acds_callbacks_t *callbacks);
65static asciichat_error_t handle_acds_session_lookup(const void *payload, size_t payload_len,
66 acip_transport_t *transport, const char *client_ip,
67 const acip_acds_callbacks_t *callbacks);
68static asciichat_error_t handle_acds_session_join(const void *payload, size_t payload_len, acip_transport_t *transport,
69 const char *client_ip, const acip_acds_callbacks_t *callbacks);
70static asciichat_error_t handle_acds_session_leave(const void *payload, size_t payload_len, acip_transport_t *transport,
71 const char *client_ip, const acip_acds_callbacks_t *callbacks);
72static asciichat_error_t handle_acds_webrtc_sdp(const void *payload, size_t payload_len, acip_transport_t *transport,
73 const char *client_ip, const acip_acds_callbacks_t *callbacks);
74static asciichat_error_t handle_acds_webrtc_ice(const void *payload, size_t payload_len, acip_transport_t *transport,
75 const char *client_ip, const acip_acds_callbacks_t *callbacks);
76static asciichat_error_t handle_acds_ping(const void *payload, size_t payload_len, acip_transport_t *transport,
77 const char *client_ip, const acip_acds_callbacks_t *callbacks);
78static asciichat_error_t handle_acds_pong(const void *payload, size_t payload_len, acip_transport_t *transport,
79 const char *client_ip, const acip_acds_callbacks_t *callbacks);
80static asciichat_error_t handle_acds_host_announcement(const void *payload, size_t payload_len,
81 acip_transport_t *transport, const char *client_ip,
82 const acip_acds_callbacks_t *callbacks);
83static asciichat_error_t handle_acds_host_lost(const void *payload, size_t payload_len, acip_transport_t *transport,
84 const char *client_ip, const acip_acds_callbacks_t *callbacks);
85static asciichat_error_t handle_acds_network_quality(const void *payload, size_t payload_len,
86 acip_transport_t *transport, const char *client_ip,
87 const acip_acds_callbacks_t *callbacks);
88
89// ACDS handler dispatch table
90static const acip_acds_handler_func_t g_acds_handlers[ACDS_HANDLER_COUNT] = {
91 handle_acds_ping, // 0
92 handle_acds_pong, // 1
93 handle_acds_session_create, // 2
94 handle_acds_session_lookup, // 3
95 handle_acds_session_join, // 4
96 handle_acds_session_leave, // 5
97 handle_acds_webrtc_sdp, // 6
98 handle_acds_webrtc_ice, // 7
99 handle_acds_host_announcement, // 8
100 handle_acds_host_lost, // 9
101 handle_acds_network_quality, // 10
102};
103
104// ACDS packet type -> handler index hash table
105// clang-format off
106static const acds_hash_entry_t g_acds_handler_hash[ACDS_HASH_SIZE] = {
107 [9] = {PACKET_TYPE_PING, 0}, // hash(5001)=9
108 [10] = {PACKET_TYPE_PONG, 1}, // hash(5002)=10
109 [13] = {PACKET_TYPE_ACIP_HOST_ANNOUNCEMENT, 8}, // hash(6061)=13
110 [16] = {PACKET_TYPE_ACIP_SESSION_CREATE, 2}, // hash(6000)=16
111 [17] = {PACKET_TYPE_ACIP_HOST_LOST, 9}, // hash(6065)=17
112 [18] = {PACKET_TYPE_ACIP_SESSION_LOOKUP, 3}, // hash(6002)=18
113 [20] = {PACKET_TYPE_ACIP_SESSION_JOIN, 4}, // hash(6004)=20
114 [22] = {PACKET_TYPE_ACIP_SESSION_LEAVE, 5}, // hash(6006)=22
115 [25] = {PACKET_TYPE_ACIP_WEBRTC_SDP, 6}, // hash(6009)=25
116 [26] = {PACKET_TYPE_ACIP_WEBRTC_ICE, 7}, // hash(6010)=26
117 [28] = {PACKET_TYPE_ACIP_NETWORK_QUALITY, 10}, // hash(6060)=28
118};
119// clang-format on
120
121// =============================================================================
122// Public API
123// =============================================================================
124
126 size_t payload_len, const char *client_ip,
127 const acip_acds_callbacks_t *callbacks) {
128 if (!callbacks) {
129 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid callbacks");
130 }
131
133 log_info("★ ACIP_HANDLE_ACDS_PACKET: Dispatching type=%u (NETWORK_QUALITY) from %s, payload_len=%zu", type,
134 client_ip, payload_len);
135 }
136
137 // O(1) dispatch via hash table lookup
138 int idx = acds_handler_hash_lookup(g_acds_handler_hash, type);
139 if (idx < 0) {
140 return SET_ERRNO(ERROR_INVALID_PARAM, "Unhandled ACDS packet type: %d from %s", type, client_ip);
141 }
142
144 log_info("★ ACIP_HANDLE_ACDS_PACKET: Found handler at index=%d", idx);
145 }
146
147 return g_acds_handlers[idx](payload, payload_len, transport, client_ip, callbacks);
148}
149
150// =============================================================================
151// ACDS Handler Implementations
152// =============================================================================
153
154static asciichat_error_t handle_acds_session_create(const void *payload, size_t payload_len,
155 acip_transport_t *transport, const char *client_ip,
156 const acip_acds_callbacks_t *callbacks) {
157 if (!callbacks->on_session_create) {
158 return ASCIICHAT_OK;
159 }
160
161 if (payload_len < sizeof(acip_session_create_t)) {
162 return SET_ERRNO(ERROR_INVALID_PARAM, "SESSION_CREATE payload too small from %s", client_ip);
163 }
164
165 const acip_session_create_t *req = (const acip_session_create_t *)payload;
166
167 // Multi-key protocol: validate parameters only for first key (key_index == 0)
168 // total_keys > 0 indicates multi-key mode; skip validation for subsequent keys
169 if (req->key_index == 0 || req->total_keys == 0) {
170 // Validate session parameters for first key or single-key mode
171 if (req->max_participants == 0 || req->max_participants > 32) {
172 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid max_participants: %u from %s (expected: 1-32)",
173 req->max_participants, client_ip);
174 }
175
176 if (req->session_type > 1) {
177 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid session_type: %u from %s (expected: 0=DIRECT_TCP or 1=WEBRTC)",
178 req->session_type, client_ip);
179 }
180
181 // Validate server port (0 = system assigned, 1-65535 = valid)
182 // Allow 0 for auto-assignment during WEBRTC mode
183 if (req->session_type == 0 && req->server_port == 0) {
184 return SET_ERRNO(ERROR_INVALID_PARAM, "DIRECT_TCP session requires valid server_port from %s", client_ip);
185 }
186 }
187
188 callbacks->on_session_create(req, transport, client_ip, callbacks->app_ctx);
189 return ASCIICHAT_OK;
190}
191
192static asciichat_error_t handle_acds_session_lookup(const void *payload, size_t payload_len,
193 acip_transport_t *transport, const char *client_ip,
194 const acip_acds_callbacks_t *callbacks) {
195 if (!callbacks->on_session_lookup) {
196 return ASCIICHAT_OK;
197 }
198
199 if (payload_len < sizeof(acip_session_lookup_t)) {
200 return SET_ERRNO(ERROR_INVALID_PARAM, "SESSION_LOOKUP payload too small from %s", client_ip);
201 }
202
203 const acip_session_lookup_t *req = (const acip_session_lookup_t *)payload;
204 callbacks->on_session_lookup(req, transport, client_ip, callbacks->app_ctx);
205 return ASCIICHAT_OK;
206}
207
208static asciichat_error_t handle_acds_session_join(const void *payload, size_t payload_len, acip_transport_t *transport,
209 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
210 if (!callbacks->on_session_join) {
211 return ASCIICHAT_OK;
212 }
213
214 if (payload_len < sizeof(acip_session_join_t)) {
215 return SET_ERRNO(ERROR_INVALID_PARAM, "SESSION_JOIN payload too small from %s", client_ip);
216 }
217
218 const acip_session_join_t *req = (const acip_session_join_t *)payload;
219 callbacks->on_session_join(req, transport, client_ip, callbacks->app_ctx);
220 return ASCIICHAT_OK;
221}
222
223static asciichat_error_t handle_acds_session_leave(const void *payload, size_t payload_len, acip_transport_t *transport,
224 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
225 if (!callbacks->on_session_leave) {
226 return ASCIICHAT_OK;
227 }
228
229 if (payload_len < sizeof(acip_session_leave_t)) {
230 return SET_ERRNO(ERROR_INVALID_PARAM, "SESSION_LEAVE payload too small from %s", client_ip);
231 }
232
233 const acip_session_leave_t *req = (const acip_session_leave_t *)payload;
234 callbacks->on_session_leave(req, transport, client_ip, callbacks->app_ctx);
235 return ASCIICHAT_OK;
236}
237
238static asciichat_error_t handle_acds_webrtc_sdp(const void *payload, size_t payload_len, acip_transport_t *transport,
239 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
240 if (!callbacks->on_webrtc_sdp) {
241 return ASCIICHAT_OK;
242 }
243
244 if (payload_len < sizeof(acip_webrtc_sdp_t)) {
245 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_SDP payload too small from %s", client_ip);
246 }
247
248 const acip_webrtc_sdp_t *sdp = (const acip_webrtc_sdp_t *)payload;
249
250 // Validate sdp_len against actual payload size (convert from network byte order)
251 uint16_t sdp_len_host = NET_TO_HOST_U16(sdp->sdp_len);
252 size_t expected_size = sizeof(acip_webrtc_sdp_t) + sdp_len_host;
253 if (expected_size > payload_len) {
254 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_SDP size mismatch from %s: claims %u bytes but payload is %zu",
255 client_ip, sdp_len_host, payload_len);
256 }
257
258 callbacks->on_webrtc_sdp(sdp, payload_len, transport, client_ip, callbacks->app_ctx);
259 return ASCIICHAT_OK;
260}
261
262static asciichat_error_t handle_acds_webrtc_ice(const void *payload, size_t payload_len, acip_transport_t *transport,
263 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
264 if (!callbacks->on_webrtc_ice) {
265 return ASCIICHAT_OK;
266 }
267
268 if (payload_len < sizeof(acip_webrtc_ice_t)) {
269 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_ICE payload too small from %s", client_ip);
270 }
271
272 const acip_webrtc_ice_t *ice = (const acip_webrtc_ice_t *)payload;
273
274 // Validate candidate_len against actual payload size (convert from network byte order)
275 uint16_t candidate_len_host = NET_TO_HOST_U16(ice->candidate_len);
276 size_t expected_size = sizeof(acip_webrtc_ice_t) + candidate_len_host;
277 if (expected_size > payload_len) {
278 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_ICE size mismatch from %s: claims %u bytes but payload is %zu",
279 client_ip, candidate_len_host, payload_len);
280 }
281
282 callbacks->on_webrtc_ice(ice, payload_len, transport, client_ip, callbacks->app_ctx);
283 return ASCIICHAT_OK;
284}
285
286// Keepalive handlers for PING/PONG packets
287static asciichat_error_t handle_acds_ping(const void *payload, size_t payload_len, acip_transport_t *transport,
288 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
289 (void)payload;
290 (void)payload_len;
291 (void)transport;
292 (void)callbacks;
293
294 log_debug("★ PING received from %s", client_ip);
295 // Simply acknowledge (log only) - client expects us to respond naturally
296 return ASCIICHAT_OK;
297}
298
299static asciichat_error_t handle_acds_pong(const void *payload, size_t payload_len, acip_transport_t *transport,
300 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
301 (void)payload;
302 (void)payload_len;
303 (void)transport;
304 (void)callbacks;
305
306 log_debug("★ PONG received from %s (heartbeat OK)", client_ip);
307 // Simply acknowledge (log only) - used for timeout/heartbeat detection
308 return ASCIICHAT_OK;
309}
310
311static asciichat_error_t handle_acds_host_announcement(const void *payload, size_t payload_len,
312 acip_transport_t *transport, const char *client_ip,
313 const acip_acds_callbacks_t *callbacks) {
314 if (!callbacks->on_host_announcement) {
315 return ASCIICHAT_OK;
316 }
317
318 if (payload_len < sizeof(acip_host_announcement_t)) {
319 return SET_ERRNO(ERROR_INVALID_PARAM, "HOST_ANNOUNCEMENT payload too small from %s", client_ip);
320 }
321
322 const acip_host_announcement_t *announcement = (const acip_host_announcement_t *)payload;
323 callbacks->on_host_announcement(announcement, transport, client_ip, callbacks->app_ctx);
324 return ASCIICHAT_OK;
325}
326
327static asciichat_error_t handle_acds_host_lost(const void *payload, size_t payload_len, acip_transport_t *transport,
328 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
329 if (!callbacks->on_host_lost) {
330 return ASCIICHAT_OK;
331 }
332
333 if (payload_len < sizeof(acip_host_lost_t)) {
334 return SET_ERRNO(ERROR_INVALID_PARAM, "HOST_LOST payload too small from %s", client_ip);
335 }
336
337 const acip_host_lost_t *host_lost = (const acip_host_lost_t *)payload;
338 callbacks->on_host_lost(host_lost, transport, client_ip, callbacks->app_ctx);
339 return ASCIICHAT_OK;
340}
341
342static asciichat_error_t handle_acds_network_quality(const void *payload, size_t payload_len,
343 acip_transport_t *transport, const char *client_ip,
344 const acip_acds_callbacks_t *callbacks) {
345 if (!callbacks->on_network_quality) {
346 return ASCIICHAT_OK;
347 }
348
349 if (payload_len < sizeof(acip_nat_quality_t)) {
350 return SET_ERRNO(ERROR_INVALID_PARAM, "NETWORK_QUALITY payload too small from %s", client_ip);
351 }
352
353 callbacks->on_network_quality(payload, payload_len, transport, client_ip, callbacks->app_ctx);
354 return ASCIICHAT_OK;
355}
asciichat_error_t acip_handle_acds_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, const char *client_ip, const acip_acds_callbacks_t *callbacks)
Handle incoming ACDS packet with O(1) dispatch.
#define ACDS_HANDLER_COUNT
#define ACDS_HASH_SIZE
#define ACDS_HASH(type)
asciichat_error_t(* acip_acds_handler_func_t)(const void *payload, size_t payload_len, acip_transport_t *transport, const char *client_ip, const acip_acds_callbacks_t *callbacks)
ACIP Discovery Server (ACDS) packet handlers.
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
⚙️ Common definitions, error codes, macros, and types shared throughout the application
🔄 Network byte order conversion helpers
#define NET_TO_HOST_U16(val)
Definition endian.h:111
acip_session_join_t
Definition acds.h:403
acip_session_lookup_t
Definition acds.h:304
acip_webrtc_ice_t
Definition acds.h:650
acip_host_lost_t
Definition acds.h:952
acip_host_announcement_t
Definition acds.h:910
acip_webrtc_sdp_t
Definition acds.h:604
acip_session_create_t
Definition acds.h:221
acip_session_leave_t
Definition acds.h:524
acip_nat_quality_t
Definition acds.h:891
unsigned short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58
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
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#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_PONG
Keepalive pong response.
Definition packet.h:385
@ PACKET_TYPE_ACIP_NETWORK_QUALITY
NETWORK_QUALITY exchange (unified packet for all quality metrics)
Definition packet.h:455
@ PACKET_TYPE_ACIP_HOST_LOST
Host lost notification - lightweight (Participant -> ACDS)
Definition packet.h:465
@ PACKET_TYPE_ACIP_HOST_ANNOUNCEMENT
Host announcement (Participant -> ACDS, "I won negotiation")
Definition packet.h:457
@ PACKET_TYPE_ACIP_SESSION_CREATE
Create new session (Client -> Discovery Server)
Definition packet.h:407
@ PACKET_TYPE_ACIP_SESSION_LOOKUP
Lookup session by string (Client -> Discovery Server)
Definition packet.h:411
@ PACKET_TYPE_ACIP_SESSION_LEAVE
Leave session (Client -> Discovery Server)
Definition packet.h:419
@ PACKET_TYPE_ACIP_WEBRTC_SDP
WebRTC SDP offer/answer (bidirectional)
Definition packet.h:426
@ PACKET_TYPE_ACIP_SESSION_JOIN
Join existing session (Client -> Discovery Server)
Definition packet.h:415
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:383
📝 Logging API with multiple log levels and terminal output control
ACIP protocol message structures and helpers.
ACIP shared/bidirectional packet sending functions.
Hash table entry for ACDS packet type to handler mapping.
packet_type_t key
Packet type (0 = empty slot)
uint8_t handler_idx
Handler index (0-based)
ACDS packet handler callbacks.
void(* on_webrtc_sdp)(const acip_webrtc_sdp_t *sdp, size_t payload_len, acip_transport_t *transport, const char *client_ip, void *app_ctx)
Called when client sends WebRTC SDP offer/answer.
void(* on_webrtc_ice)(const acip_webrtc_ice_t *ice, size_t payload_len, acip_transport_t *transport, const char *client_ip, void *app_ctx)
Called when client sends WebRTC ICE candidate.
void(* on_session_join)(const acip_session_join_t *req, acip_transport_t *transport, const char *client_ip, void *app_ctx)
Called when client joins a session.
void(* on_network_quality)(const void *payload, size_t payload_len, acip_transport_t *transport, const char *client_ip, void *app_ctx)
Called when client sends NAT network quality for negotiation (discovery mode)
void(* on_host_announcement)(const acip_host_announcement_t *announcement, acip_transport_t *transport, const char *client_ip, void *app_ctx)
Called when client announces they are hosting (discovery mode)
void(* on_session_lookup)(const acip_session_lookup_t *req, acip_transport_t *transport, const char *client_ip, void *app_ctx)
Called when client looks up session info.
void * app_ctx
Application context (passed to all callbacks)
void(* on_session_leave)(const acip_session_leave_t *req, acip_transport_t *transport, const char *client_ip, void *app_ctx)
Called when client leaves a session.
void(* on_session_create)(const acip_session_create_t *req, acip_transport_t *transport, const char *client_ip, void *app_ctx)
Called when client requests session creation.
void(* on_host_lost)(const acip_host_lost_t *host_lost, acip_transport_t *transport, const char *client_ip, void *app_ctx)
Called when participant reports host has disconnected (discovery mode)
Transport instance structure.
Definition transport.h:214
Transport abstraction layer for ACIP protocol.