ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
acds_handlers.c
Go to the documentation of this file.
1
12#include <ascii-chat/network/acip/acds_handlers.h>
13#include <ascii-chat/network/acip/messages.h>
14#include <ascii-chat/network/packet.h>
15#include <ascii-chat/log/logging.h>
16#include <ascii-chat/asciichat_errno.h>
17#include <ascii-chat/common.h>
18#include <ascii-chat/util/endian.h>
19#include <string.h>
20
21// =============================================================================
22// ACDS Handler Function Types and Hash Table
23// =============================================================================
24
25// ACDS handler function signature
26typedef asciichat_error_t (*acip_acds_handler_func_t)(const void *payload, size_t payload_len, int client_socket,
27 const char *client_ip, const acip_acds_callbacks_t *callbacks);
28
29// Hash table for O(1) packet dispatch
30#define ACDS_HASH_SIZE 32
31#define ACDS_HANDLER_COUNT 11
32
36typedef struct {
37 packet_type_t key;
38 uint8_t handler_idx;
40
41#define ACDS_HASH(type) ((type) % ACDS_HASH_SIZE)
42
43static inline int acds_handler_hash_lookup(const acds_hash_entry_t *table, packet_type_t type) {
44 uint32_t h = ACDS_HASH(type);
45 for (int i = 0; i < ACDS_HASH_SIZE; i++) {
46 uint32_t slot = (h + i) % ACDS_HASH_SIZE;
47 if (table[slot].key == 0)
48 return -1;
49 if (table[slot].key == type)
50 return table[slot].handler_idx;
51 }
52 return -1;
53}
54
55// =============================================================================
56// Forward Declarations
57// =============================================================================
58
59static asciichat_error_t handle_acds_session_create(const void *payload, size_t payload_len, int client_socket,
60 const char *client_ip, const acip_acds_callbacks_t *callbacks);
61static asciichat_error_t handle_acds_session_lookup(const void *payload, size_t payload_len, int client_socket,
62 const char *client_ip, const acip_acds_callbacks_t *callbacks);
63static asciichat_error_t handle_acds_session_join(const void *payload, size_t payload_len, int client_socket,
64 const char *client_ip, const acip_acds_callbacks_t *callbacks);
65static asciichat_error_t handle_acds_session_leave(const void *payload, size_t payload_len, int client_socket,
66 const char *client_ip, const acip_acds_callbacks_t *callbacks);
67static asciichat_error_t handle_acds_webrtc_sdp(const void *payload, size_t payload_len, int client_socket,
68 const char *client_ip, const acip_acds_callbacks_t *callbacks);
69static asciichat_error_t handle_acds_webrtc_ice(const void *payload, size_t payload_len, int client_socket,
70 const char *client_ip, const acip_acds_callbacks_t *callbacks);
71static asciichat_error_t handle_acds_discovery_ping(const void *payload, size_t payload_len, int client_socket,
72 const char *client_ip, const acip_acds_callbacks_t *callbacks);
73static asciichat_error_t handle_acds_ping(const void *payload, size_t payload_len, int client_socket,
74 const char *client_ip, const acip_acds_callbacks_t *callbacks);
75static asciichat_error_t handle_acds_pong(const void *payload, size_t payload_len, int client_socket,
76 const char *client_ip, const acip_acds_callbacks_t *callbacks);
77static asciichat_error_t handle_acds_host_announcement(const void *payload, size_t payload_len, int client_socket,
78 const char *client_ip, const acip_acds_callbacks_t *callbacks);
79static asciichat_error_t handle_acds_host_lost(const void *payload, size_t payload_len, int client_socket,
80 const char *client_ip, const acip_acds_callbacks_t *callbacks);
81
82// ACDS handler dispatch table
83static const acip_acds_handler_func_t g_acds_handlers[ACDS_HANDLER_COUNT] = {
84 handle_acds_ping, // 0
85 handle_acds_pong, // 1
86 handle_acds_session_create, // 2
87 handle_acds_session_lookup, // 3
88 handle_acds_session_join, // 4
89 handle_acds_session_leave, // 5
90 handle_acds_webrtc_sdp, // 6
91 handle_acds_webrtc_ice, // 7
92 handle_acds_discovery_ping, // 8
93 handle_acds_host_announcement, // 9
94 handle_acds_host_lost, // 10
95};
96
97// ACDS packet type -> handler index hash table
98// clang-format off
99static const acds_hash_entry_t g_acds_handler_hash[ACDS_HASH_SIZE] = {
100 [9] = {PACKET_TYPE_PING, 0}, // hash(5001)=9
101 [10] = {PACKET_TYPE_PONG, 1}, // hash(5002)=10
102 [13] = {PACKET_TYPE_ACIP_HOST_ANNOUNCEMENT, 9}, // hash(6061)=13
103 [16] = {PACKET_TYPE_ACIP_SESSION_CREATE, 2}, // hash(6000)=16
104 [17] = {PACKET_TYPE_ACIP_HOST_LOST, 10}, // hash(6065)=17
105 [18] = {PACKET_TYPE_ACIP_SESSION_LOOKUP, 3}, // hash(6002)=18
106 [20] = {PACKET_TYPE_ACIP_SESSION_JOIN, 4}, // hash(6004)=20
107 [21] = {PACKET_TYPE_ACIP_DISCOVERY_PING, 8}, // hash(6100)=20, probed->21
108 [22] = {PACKET_TYPE_ACIP_SESSION_LEAVE, 5}, // hash(6006)=22
109 [25] = {PACKET_TYPE_ACIP_WEBRTC_SDP, 6}, // hash(6009)=25
110 [26] = {PACKET_TYPE_ACIP_WEBRTC_ICE, 7}, // hash(6010)=26
111};
112// clang-format on
113
114// =============================================================================
115// Public API
116// =============================================================================
117
118asciichat_error_t acip_handle_acds_packet(acip_transport_t *transport, packet_type_t type, const void *payload,
119 size_t payload_len, int client_socket, const char *client_ip,
120 const acip_acds_callbacks_t *callbacks) {
121 if (!callbacks) {
122 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid callbacks");
123 }
124 (void)transport;
125
126 // O(1) dispatch via hash table lookup
127 int idx = acds_handler_hash_lookup(g_acds_handler_hash, type);
128 if (idx < 0) {
129 return SET_ERRNO(ERROR_INVALID_PARAM, "Unhandled ACDS packet type: %d from %s", type, client_ip);
130 }
131
132 return g_acds_handlers[idx](payload, payload_len, client_socket, client_ip, callbacks);
133}
134
135// =============================================================================
136// ACDS Handler Implementations
137// =============================================================================
138
139static asciichat_error_t handle_acds_session_create(const void *payload, size_t payload_len, int client_socket,
140 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
141 if (!callbacks->on_session_create) {
142 return ASCIICHAT_OK;
143 }
144
145 if (payload_len < sizeof(acip_session_create_t)) {
146 return SET_ERRNO(ERROR_INVALID_PARAM, "SESSION_CREATE payload too small from %s", client_ip);
147 }
148
149 const acip_session_create_t *req = (const acip_session_create_t *)payload;
150
151 // Validate session parameters
152 if (req->max_participants == 0 || req->max_participants > 32) {
153 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid max_participants: %u from %s (expected: 1-32)",
154 req->max_participants, client_ip);
155 }
156
157 if (req->session_type > 1) {
158 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid session_type: %u from %s (expected: 0=DIRECT_TCP or 1=WEBRTC)",
159 req->session_type, client_ip);
160 }
161
162 // Validate server port (0 = system assigned, 1-65535 = valid)
163 // Allow 0 for auto-assignment during WEBRTC mode
164 if (req->session_type == 0 && req->server_port == 0) {
165 return SET_ERRNO(ERROR_INVALID_PARAM, "DIRECT_TCP session requires valid server_port from %s", client_ip);
166 }
167
168 callbacks->on_session_create(req, client_socket, client_ip, callbacks->app_ctx);
169 return ASCIICHAT_OK;
170}
171
172static asciichat_error_t handle_acds_session_lookup(const void *payload, size_t payload_len, int client_socket,
173 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
174 if (!callbacks->on_session_lookup) {
175 return ASCIICHAT_OK;
176 }
177
178 if (payload_len < sizeof(acip_session_lookup_t)) {
179 return SET_ERRNO(ERROR_INVALID_PARAM, "SESSION_LOOKUP payload too small from %s", client_ip);
180 }
181
182 const acip_session_lookup_t *req = (const acip_session_lookup_t *)payload;
183 callbacks->on_session_lookup(req, client_socket, client_ip, callbacks->app_ctx);
184 return ASCIICHAT_OK;
185}
186
187static asciichat_error_t handle_acds_session_join(const void *payload, size_t payload_len, int client_socket,
188 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
189 if (!callbacks->on_session_join) {
190 return ASCIICHAT_OK;
191 }
192
193 if (payload_len < sizeof(acip_session_join_t)) {
194 return SET_ERRNO(ERROR_INVALID_PARAM, "SESSION_JOIN payload too small from %s", client_ip);
195 }
196
197 const acip_session_join_t *req = (const acip_session_join_t *)payload;
198 callbacks->on_session_join(req, client_socket, client_ip, callbacks->app_ctx);
199 return ASCIICHAT_OK;
200}
201
202static asciichat_error_t handle_acds_session_leave(const void *payload, size_t payload_len, int client_socket,
203 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
204 if (!callbacks->on_session_leave) {
205 return ASCIICHAT_OK;
206 }
207
208 if (payload_len < sizeof(acip_session_leave_t)) {
209 return SET_ERRNO(ERROR_INVALID_PARAM, "SESSION_LEAVE payload too small from %s", client_ip);
210 }
211
212 const acip_session_leave_t *req = (const acip_session_leave_t *)payload;
213 callbacks->on_session_leave(req, client_socket, client_ip, callbacks->app_ctx);
214 return ASCIICHAT_OK;
215}
216
217static asciichat_error_t handle_acds_webrtc_sdp(const void *payload, size_t payload_len, int client_socket,
218 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
219 if (!callbacks->on_webrtc_sdp) {
220 return ASCIICHAT_OK;
221 }
222
223 if (payload_len < sizeof(acip_webrtc_sdp_t)) {
224 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_SDP payload too small from %s", client_ip);
225 }
226
227 const acip_webrtc_sdp_t *sdp = (const acip_webrtc_sdp_t *)payload;
228
229 // Validate sdp_len against actual payload size (convert from network byte order)
230 uint16_t sdp_len_host = NET_TO_HOST_U16(sdp->sdp_len);
231 size_t expected_size = sizeof(acip_webrtc_sdp_t) + sdp_len_host;
232 if (expected_size > payload_len) {
233 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_SDP size mismatch from %s: claims %u bytes but payload is %zu",
234 client_ip, sdp_len_host, payload_len);
235 }
236
237 callbacks->on_webrtc_sdp(sdp, payload_len, client_socket, client_ip, callbacks->app_ctx);
238 return ASCIICHAT_OK;
239}
240
241static asciichat_error_t handle_acds_webrtc_ice(const void *payload, size_t payload_len, int client_socket,
242 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
243 if (!callbacks->on_webrtc_ice) {
244 return ASCIICHAT_OK;
245 }
246
247 if (payload_len < sizeof(acip_webrtc_ice_t)) {
248 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_ICE payload too small from %s", client_ip);
249 }
250
251 const acip_webrtc_ice_t *ice = (const acip_webrtc_ice_t *)payload;
252
253 // Validate candidate_len against actual payload size (convert from network byte order)
254 uint16_t candidate_len_host = NET_TO_HOST_U16(ice->candidate_len);
255 size_t expected_size = sizeof(acip_webrtc_ice_t) + candidate_len_host;
256 if (expected_size > payload_len) {
257 return SET_ERRNO(ERROR_INVALID_PARAM, "WEBRTC_ICE size mismatch from %s: claims %u bytes but payload is %zu",
258 client_ip, candidate_len_host, payload_len);
259 }
260
261 callbacks->on_webrtc_ice(ice, payload_len, client_socket, client_ip, callbacks->app_ctx);
262 return ASCIICHAT_OK;
263}
264
265static asciichat_error_t handle_acds_discovery_ping(const void *payload, size_t payload_len, int client_socket,
266 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
267 (void)payload;
268 (void)payload_len;
269
270 if (callbacks->on_discovery_ping) {
271 callbacks->on_discovery_ping(payload, payload_len, client_socket, client_ip, callbacks->app_ctx);
272 }
273 return ASCIICHAT_OK;
274}
275
276static asciichat_error_t handle_acds_ping(const void *payload, size_t payload_len, int client_socket,
277 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
278 (void)payload;
279 (void)payload_len;
280 (void)callbacks;
281
282 log_debug("ACDS keepalive: Received PING from %s, responding with PONG", client_ip);
283
284 // Respond with PONG to keep connection alive
285 asciichat_error_t result = packet_send(client_socket, PACKET_TYPE_PONG, NULL, 0);
286 if (result != ASCIICHAT_OK) {
287 log_warn("ACDS keepalive: Failed to send PONG to %s: %s", client_ip, asciichat_error_string(result));
288 return result;
289 }
290
291 log_debug("ACDS keepalive: Sent PONG to %s", client_ip);
292 return ASCIICHAT_OK;
293}
294
295static asciichat_error_t handle_acds_pong(const void *payload, size_t payload_len, int client_socket,
296 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
297 (void)payload;
298 (void)payload_len;
299 (void)client_socket;
300 (void)callbacks;
301
302 log_debug("ACDS keepalive: Received PONG from %s", client_ip);
303 return ASCIICHAT_OK;
304}
305
306static asciichat_error_t handle_acds_host_announcement(const void *payload, size_t payload_len, int client_socket,
307 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
308 if (!callbacks->on_host_announcement) {
309 return ASCIICHAT_OK;
310 }
311
312 if (payload_len < sizeof(acip_host_announcement_t)) {
313 return SET_ERRNO(ERROR_INVALID_PARAM, "HOST_ANNOUNCEMENT payload too small from %s", client_ip);
314 }
315
316 const acip_host_announcement_t *announcement = (const acip_host_announcement_t *)payload;
317 callbacks->on_host_announcement(announcement, client_socket, client_ip, callbacks->app_ctx);
318 return ASCIICHAT_OK;
319}
320
321static asciichat_error_t handle_acds_host_lost(const void *payload, size_t payload_len, int client_socket,
322 const char *client_ip, const acip_acds_callbacks_t *callbacks) {
323 if (!callbacks->on_host_lost) {
324 return ASCIICHAT_OK;
325 }
326
327 if (payload_len < sizeof(acip_host_lost_t)) {
328 return SET_ERRNO(ERROR_INVALID_PARAM, "HOST_LOST payload too small from %s", client_ip);
329 }
330
331 const acip_host_lost_t *host_lost = (const acip_host_lost_t *)payload;
332 callbacks->on_host_lost(host_lost, client_socket, client_ip, callbacks->app_ctx);
333 return ASCIICHAT_OK;
334}
asciichat_error_t(* acip_acds_handler_func_t)(const void *payload, size_t payload_len, int client_socket, const char *client_ip, const acip_acds_callbacks_t *callbacks)
asciichat_error_t acip_handle_acds_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, int client_socket, const char *client_ip, const acip_acds_callbacks_t *callbacks)
#define ACDS_HANDLER_COUNT
#define ACDS_HASH_SIZE
#define ACDS_HASH(type)
asciichat_error_t packet_send(socket_t sockfd, packet_type_t type, const void *data, size_t len)
Send a packet with proper header and CRC32.
Definition packet.c:288
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)