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>
27 const char *client_ip,
const acip_acds_callbacks_t *callbacks);
30#define ACDS_HASH_SIZE 32
31#define ACDS_HANDLER_COUNT 11
41#define ACDS_HASH(type) ((type) % ACDS_HASH_SIZE)
43static inline int acds_handler_hash_lookup(
const acds_hash_entry_t *table, packet_type_t type) {
47 if (table[slot].key == 0)
49 if (table[slot].key == type)
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);
86 handle_acds_session_create,
87 handle_acds_session_lookup,
88 handle_acds_session_join,
89 handle_acds_session_leave,
90 handle_acds_webrtc_sdp,
91 handle_acds_webrtc_ice,
92 handle_acds_discovery_ping,
93 handle_acds_host_announcement,
94 handle_acds_host_lost,
100 [9] = {PACKET_TYPE_PING, 0},
101 [10] = {PACKET_TYPE_PONG, 1},
102 [13] = {PACKET_TYPE_ACIP_HOST_ANNOUNCEMENT, 9},
103 [16] = {PACKET_TYPE_ACIP_SESSION_CREATE, 2},
104 [17] = {PACKET_TYPE_ACIP_HOST_LOST, 10},
105 [18] = {PACKET_TYPE_ACIP_SESSION_LOOKUP, 3},
106 [20] = {PACKET_TYPE_ACIP_SESSION_JOIN, 4},
107 [21] = {PACKET_TYPE_ACIP_DISCOVERY_PING, 8},
108 [22] = {PACKET_TYPE_ACIP_SESSION_LEAVE, 5},
109 [25] = {PACKET_TYPE_ACIP_WEBRTC_SDP, 6},
110 [26] = {PACKET_TYPE_ACIP_WEBRTC_ICE, 7},
119 size_t payload_len,
int client_socket,
const char *client_ip,
120 const acip_acds_callbacks_t *callbacks) {
122 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid callbacks");
127 int idx = acds_handler_hash_lookup(g_acds_handler_hash, type);
129 return SET_ERRNO(ERROR_INVALID_PARAM,
"Unhandled ACDS packet type: %d from %s", type, client_ip);
132 return g_acds_handlers[idx](payload, payload_len, client_socket, client_ip, callbacks);
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) {
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);
149 const acip_session_create_t *req = (
const acip_session_create_t *)payload;
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);
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);
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);
168 callbacks->on_session_create(req, client_socket, client_ip, callbacks->app_ctx);
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) {
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);
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);
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) {
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);
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);
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) {
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);
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);
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) {
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);
227 const acip_webrtc_sdp_t *sdp = (
const acip_webrtc_sdp_t *)payload;
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);
237 callbacks->on_webrtc_sdp(sdp, payload_len, client_socket, client_ip, callbacks->app_ctx);
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) {
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);
251 const acip_webrtc_ice_t *ice = (
const acip_webrtc_ice_t *)payload;
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);
261 callbacks->on_webrtc_ice(ice, payload_len, client_socket, client_ip, callbacks->app_ctx);
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) {
270 if (callbacks->on_discovery_ping) {
271 callbacks->on_discovery_ping(payload, payload_len, client_socket, client_ip, callbacks->app_ctx);
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) {
282 log_debug(
"ACDS keepalive: Received PING from %s, responding with PONG", client_ip);
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));
291 log_debug(
"ACDS keepalive: Sent PONG to %s", client_ip);
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) {
302 log_debug(
"ACDS keepalive: Received PONG from %s", client_ip);
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) {
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);
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);
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) {
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);
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);
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
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.
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)