16#include <ascii-chat/discovery/database.h>
17#include <ascii-chat/discovery/session.h>
18#include <ascii-chat/discovery/identity.h>
19#include <ascii-chat/crypto/handshake/server.h>
21#include <ascii-chat/network/acip/acds.h>
22#include <ascii-chat/network/acip/acds_handlers.h>
23#include <ascii-chat/network/acip/acds_client.h>
24#include <ascii-chat/network/acip/send.h>
25#include <ascii-chat/network/acip/transport.h>
26#include <ascii-chat/network/rate_limit/rate_limit.h>
27#include <ascii-chat/network/rate_limit/sqlite.h>
28#include <ascii-chat/network/errors.h>
29#include <ascii-chat/log/logging.h>
30#include <ascii-chat/platform/socket.h>
31#include <ascii-chat/network/network.h>
32#include <ascii-chat/buffer_pool.h>
33#include <ascii-chat/network/tcp/server.h>
34#include <ascii-chat/util/ip.h>
35#include <ascii-chat/util/time.h>
55static uint64_t get_current_time_ns(
void) {
78 log_warn(
"Too many active migrations (max 32)");
105static void monitor_host_migrations(
acds_server_t *server, uint64_t migration_timeout_ms) {
110 uint64_t now = get_current_time_ns();
111 uint64_t migration_timeout_ns = migration_timeout_ms * NS_PER_MS_INT;
114 while (i < server->num_active_migrations) {
119 if (elapsed_ns < migration_timeout_ns) {
124 uint64_t elapsed_ms = elapsed_ns / NS_PER_MS_INT;
125 log_warn(
"Host migration timeout for session %02x%02x... (elapsed %llu ms)", ctx->
session_id[0], ctx->
session_id[1],
126 (
unsigned long long)elapsed_ms);
130 if (result != ASCIICHAT_OK) {
131 log_warn(
"Failed to clear host for timed-out migration: %s", asciichat_error_string(result));
135 if (i < server->num_active_migrations - 1) {
150static void *cleanup_thread_func(
void *arg) {
156 log_info(
"Cleanup thread started (rate limits + expired sessions)");
158 while (!atomic_load(&server->
shutdown)) {
161 for (
int i = 0; i < 3000 && !atomic_load(&server->
shutdown); i++) {
165 if (atomic_load(&server->
shutdown)) {
170 log_debug(
"Running rate limit cleanup...");
172 if (result != ASCIICHAT_OK) {
173 log_warn(
"Rate limit cleanup failed");
177 log_debug(
"Running expired session cleanup...");
181 log_debug(
"Checking for completed host migrations...");
182 monitor_host_migrations(server, 5000);
185 log_info(
"Cleanup thread exiting");
190 if (!server || !config) {
191 return SET_ERRNO(ERROR_INVALID_PARAM,
"server or config is NULL");
194 memset(server, 0,
sizeof(*server));
199 if (result != ASCIICHAT_OK) {
207 return SET_ERRNO(ERROR_MEMORY,
"Failed to create rate limiter");
214 tcp_server_config_t tcp_config = {
215 .port = config->
port,
216 .ipv4_address = (config->
address[0] !=
'\0') ? config->
address : NULL,
220 .accept_timeout_sec = 1,
227 if (result != ASCIICHAT_OK) {
234 atomic_store(&server->
shutdown,
false);
237 log_warn(
"Failed to create worker thread pool");
241 return SET_ERRNO(ERROR_MEMORY,
"Failed to create worker thread pool");
246 log_warn(
"Failed to spawn cleanup thread (continuing without cleanup)");
249 log_info(
"Discovery server initialized successfully");
255 return SET_ERRNO(ERROR_INVALID_PARAM,
"server is NULL");
258 log_info(
"Discovery server accepting connections on port %d", server->
config.
port);
270 atomic_store(&server->
shutdown,
true);
276 size_t remaining_clients;
277 int shutdown_attempts = 0;
278 const int max_shutdown_attempts = 100;
281 shutdown_attempts < max_shutdown_attempts) {
282 log_debug(
"Waiting for %zu client handler threads to exit (attempt %d/%d)", remaining_clients,
283 shutdown_attempts + 1, max_shutdown_attempts);
288 if (remaining_clients > 0) {
289 log_warn(
"Server shutdown: %zu client handler threads still running after 10 seconds", remaining_clients);
290 }
else if (shutdown_attempts > 0) {
291 log_debug(
"All client handler threads exited gracefully");
298 log_debug(
"Worker thread pool stopped");
313 log_info(
"Server shutdown complete");
322#define ACDS_CREATE_TRANSPORT(socket, transport_var) \
323 acip_transport_t *transport_var = acip_tcp_transport_create(socket, NULL); \
324 if (!transport_var) { \
325 log_error("Failed to create ACDS transport"); \
329#define ACDS_DESTROY_TRANSPORT(transport_var) acip_transport_destroy(transport_var)
337static void acds_on_session_create(
const acip_session_create_t *req,
int client_socket,
const char *client_ip,
341 log_debug(
"SESSION_CREATE packet from %s", client_ip);
347 void *client_data_ptr = NULL;
349 acip_send_error(transport, ERROR_INVALID_PARAM,
"Client data not found");
356 bool is_zero_key =
true;
357 for (
size_t i = 0; i < 32; i++) {
358 if (req->identity_pubkey[i] != 0) {
370 "SESSION_CREATE with zero key from %s: identity verification not required, treating as anonymous session",
373 memcpy(&client_data->
pending_session, req,
sizeof(acip_session_create_t));
379 acip_send_error(transport, ERROR_INVALID_PARAM,
"Zero key received but not in multi-key session creation mode");
385 log_info(
"SESSION_CREATE finalize from %s: %zu identity key(s)", client_ip, client_data->
num_pending_keys);
389 log_info(
"ACDS: Auto-detecting server public IP from connection source: %s", client_ip);
392 log_info(
"ACDS: Auto-detected server_address='%s'", client_data->
pending_session.server_address);
398 acip_session_created_t resp;
399 memset(&resp, 0,
sizeof(resp));
401 asciichat_error_t create_result =
403 if (create_result == ASCIICHAT_OK) {
405 size_t stun_size = (size_t)resp.stun_count *
sizeof(stun_server_t);
406 size_t turn_size = (size_t)resp.turn_count *
sizeof(turn_server_t);
407 size_t total_size =
sizeof(resp) + stun_size + turn_size;
409 uint8_t *payload = SAFE_MALLOC(total_size, uint8_t *);
411 acip_send_error(transport, ERROR_MEMORY,
"Out of memory building response");
419 memcpy(payload, &resp,
sizeof(resp));
422 if (resp.stun_count > 0) {
427 if (resp.turn_count > 0) {
435 log_info(
"Session created: %.*s (UUID: %02x%02x..., %zu keys, %d STUN, %d TURN servers)", resp.session_string_len,
436 resp.session_string, resp.session_id[0], resp.session_id[1], client_data->
num_pending_keys,
437 resp.stun_count, resp.turn_count);
441 log_warn(
"Session creation failed for %s: %s", client_ip, asciichat_error_string(create_result));
458 acip_send_error(transport, ERROR_INVALID_PARAM,
"Maximum identity keys exceeded");
466 acip_send_error(transport, ERROR_INVALID_PARAM,
"Duplicate identity key");
476 log_debug(
"SESSION_CREATE key #%zu from %s (pubkey: %02x%02x...)", client_data->
num_pending_keys, client_ip,
477 req->identity_pubkey[0], req->identity_pubkey[1]);
498 log_warn(
"SESSION_CREATE rejected from %s: invalid timestamp (replay attack protection)", client_ip);
499 acip_send_error(transport, ERROR_CRYPTO_VERIFICATION,
"Timestamp validation failed - too old or in the future");
506 req->identity_pubkey, req->timestamp, req->capabilities, req->max_participants, req->signature);
508 if (verify_result != ASCIICHAT_OK) {
509 log_warn(
"SESSION_CREATE rejected from %s: invalid signature (identity verification failed)", client_ip);
510 acip_send_error(transport, ERROR_CRYPTO_VERIFICATION,
"Identity signature verification failed");
515 log_debug(
"SESSION_CREATE signature verified from %s (pubkey: %02x%02x...)", client_ip, req->identity_pubkey[0],
516 req->identity_pubkey[1]);
520 if (req->session_type == SESSION_TYPE_DIRECT_TCP) {
522 if (req->server_address[0] ==
'\0') {
523 SAFE_STRNCPY(req->server_address, client_ip,
sizeof(req->server_address));
524 log_info(
"SESSION_CREATE from %s: auto-detected server address (bind was 0.0.0.0)", client_ip);
528 if (strcmp(req->server_address, client_ip) != 0) {
529 log_warn(
"SESSION_CREATE rejected from %s: server_address '%s' does not match actual connection IP", client_ip,
530 req->server_address);
532 "Direct TCP sessions require server_address to match your actual IP");
536 log_debug(
"SESSION_CREATE reachability verified: %s matches connection source", req->server_address);
540 memcpy(&client_data->
pending_session, req,
sizeof(acip_session_create_t));
545 log_info(
"SESSION_CREATE started from %s: multi-key mode (key #1 stored, waiting for more or zero-key finalize)",
552static void acds_on_session_lookup(
const acip_session_lookup_t *req,
int client_socket,
const char *client_ip,
556 log_debug(
"SESSION_LOOKUP packet from %s", client_ip);
567 acip_session_info_t resp;
568 memset(&resp, 0,
sizeof(resp));
571 char session_string[49] = {0};
573 (req->session_string_len <
sizeof(session_string) - 1) ? req->session_string_len :
sizeof(session_string) - 1;
574 memcpy(session_string, req->session_string, copy_len);
577 if (lookup_result == ASCIICHAT_OK) {
579 log_info(
"Session lookup for '%s' from %s: %s", session_string, client_ip, resp.found ?
"found" :
"not found");
582 log_warn(
"Session lookup failed for %s: %s", client_ip, asciichat_error_string(lookup_result));
586static void acds_on_session_join(
const acip_session_join_t *req,
int client_socket,
const char *client_ip,
590 log_debug(
"SESSION_JOIN packet from %s", client_ip);
605 log_warn(
"SESSION_JOIN rejected from %s: invalid timestamp (replay attack protection)", client_ip);
606 acip_session_joined_t error_resp;
607 memset(&error_resp, 0,
sizeof(error_resp));
608 error_resp.success = 0;
609 error_resp.error_code = ERROR_CRYPTO_VERIFICATION;
610 SAFE_STRNCPY(error_resp.error_message,
"Timestamp validation failed",
sizeof(error_resp.error_message));
616 asciichat_error_t verify_result =
619 if (verify_result != ASCIICHAT_OK) {
620 log_warn(
"SESSION_JOIN rejected from %s: invalid signature (identity verification failed)", client_ip);
621 acip_session_joined_t error_resp;
622 memset(&error_resp, 0,
sizeof(error_resp));
623 error_resp.success = 0;
624 error_resp.error_code = ERROR_CRYPTO_VERIFICATION;
625 SAFE_STRNCPY(error_resp.error_message,
"Identity signature verification failed",
626 sizeof(error_resp.error_message));
631 log_debug(
"SESSION_JOIN signature verified from %s (pubkey: %02x%02x...)", client_ip, req->identity_pubkey[0],
632 req->identity_pubkey[1]);
635 acip_session_joined_t resp;
636 memset(&resp, 0,
sizeof(resp));
639 if (join_result == ASCIICHAT_OK && resp.success) {
643 void *retrieved_data = NULL;
646 memcpy(client_data->
session_id, resp.session_id, 16);
651 log_info(
"Client %s joined session (participant %02x%02x...)", client_ip, resp.participant_id[0],
652 resp.participant_id[1]);
655 log_warn(
"Session join failed for %s: %s", client_ip, resp.error_message);
659static void acds_on_session_leave(
const acip_session_leave_t *req,
int client_socket,
const char *client_ip,
663 log_debug(
"SESSION_LEAVE packet from %s", client_ip);
669 if (leave_result == ASCIICHAT_OK) {
670 log_info(
"Client %s left session", client_ip);
673 void *retrieved_data = NULL;
679 acip_send_error(transport, leave_result, asciichat_error_string(leave_result));
680 log_warn(
"Session leave failed for %s: %s", client_ip, asciichat_error_string(leave_result));
684static void acds_on_webrtc_sdp(
const acip_webrtc_sdp_t *sdp,
size_t payload_len,
int client_socket,
685 const char *client_ip,
void *app_ctx) {
688 log_debug(
"WEBRTC_SDP packet from %s", client_ip);
695 if (relay_result != ASCIICHAT_OK) {
697 log_warn(
"SDP relay failed from %s: %s", client_ip, asciichat_error_string(relay_result));
701static void acds_on_webrtc_ice(
const acip_webrtc_ice_t *ice,
size_t payload_len,
int client_socket,
702 const char *client_ip,
void *app_ctx) {
705 log_debug(
"WEBRTC_ICE packet from %s", client_ip);
712 if (relay_result != ASCIICHAT_OK) {
714 log_warn(
"ICE relay failed from %s: %s", client_ip, asciichat_error_string(relay_result));
718static void acds_on_discovery_ping(
const void *payload,
size_t payload_len,
int client_socket,
const char *client_ip,
728 log_debug(
"PING from %s, sending PONG", client_ip);
734static void acds_on_host_announcement(
const acip_host_announcement_t *announcement,
int client_socket,
735 const char *client_ip,
void *app_ctx) {
738 log_info(
"HOST_ANNOUNCEMENT from %s: host_id=%02x%02x..., address=%s:%u, conn_type=%d", client_ip,
739 announcement->host_id[0], announcement->host_id[1], announcement->host_address, announcement->host_port,
740 announcement->connection_type);
746 asciichat_error_t result =
748 announcement->host_address, announcement->host_port, announcement->connection_type);
750 if (result != ASCIICHAT_OK) {
752 log_warn(
"HOST_ANNOUNCEMENT failed from %s: %s", client_ip, asciichat_error_string(result));
761 log_info(
"Session host updated via HOST_ANNOUNCEMENT from %s", client_ip);
765static void acds_on_host_lost(
const acip_host_lost_t *host_lost,
int client_socket,
const char *client_ip,
769 log_info(
"HOST_LOST from %s: session=%02x%02x..., participant=%02x%02x..., last_host=%02x%02x..., reason=%u",
770 client_ip, host_lost->session_id[0], host_lost->session_id[1], host_lost->participant_id[0],
771 host_lost->participant_id[1], host_lost->last_host_id[0], host_lost->last_host_id[1],
772 host_lost->disconnect_reason);
779 if (result != ASCIICHAT_OK) {
781 log_warn(
"HOST_LOST failed from %s: %s", client_ip, asciichat_error_string(result));
788 migration_context_t *migration = find_or_create_migration(server, host_lost->session_id,
true);
791 log_warn(
"HOST_LOST: Failed to create migration context from %s", client_ip);
796 log_info(
"Migration tracking started for session %02x%02x... (participant %02x%02x...)", host_lost->session_id[0],
797 host_lost->session_id[1], host_lost->participant_id[0], host_lost->participant_id[1]);
808static const acip_acds_callbacks_t g_acds_callbacks = {
809 .on_session_create = acds_on_session_create,
810 .on_session_lookup = acds_on_session_lookup,
811 .on_session_join = acds_on_session_join,
812 .on_session_leave = acds_on_session_leave,
813 .on_webrtc_sdp = acds_on_webrtc_sdp,
814 .on_webrtc_ice = acds_on_webrtc_ice,
815 .on_discovery_ping = acds_on_discovery_ping,
816 .on_host_announcement = acds_on_host_announcement,
817 .on_host_lost = acds_on_host_lost,
822 tcp_client_context_t *ctx = (tcp_client_context_t *)arg;
824 log_error(
"Client handler: NULL context");
829 socket_t client_socket = ctx->client_socket;
832 char client_ip[INET6_ADDRSTRLEN] = {0};
835 log_info(
"Client handler started for %s", client_ip);
844 memset(client_data, 0,
sizeof(*client_data));
850 if (handshake_result != ASCIICHAT_OK) {
851 log_error(
"Failed to initialize crypto handshake for client %s", client_ip);
852 SAFE_FREE(client_data);
859 client_data->
handshake_ctx.server_public_key.type = KEY_TYPE_ED25519;
860 client_data->
handshake_ctx.server_private_key.type = KEY_TYPE_ED25519;
865 SAFE_FREE(client_data);
871 log_debug(
"Client %s registered (socket=%d, total=%zu)", client_ip, client_socket,
875 log_debug(
"Performing crypto handshake with client %s", client_ip);
879 if (handshake_result != ASCIICHAT_OK) {
880 log_warn(
"Crypto handshake start failed for client %s", client_ip);
888 if (handshake_result != ASCIICHAT_OK) {
889 log_warn(
"Crypto handshake auth challenge failed for client %s", client_ip);
897 if (handshake_result != ASCIICHAT_OK) {
898 log_warn(
"Crypto handshake complete failed for client %s", client_ip);
905 log_info(
"Crypto handshake complete for client %s", client_ip);
908 while (atomic_load(&server->
tcp_server.running)) {
909 packet_type_t packet_type;
910 void *payload = NULL;
911 size_t payload_size = 0;
914 int result =
receive_packet(client_socket, &packet_type, &payload, &payload_size);
917 asciichat_error_context_t err_ctx;
918 bool has_context = HAS_ERRNO(&err_ctx);
921 asciichat_error_t error = GET_ERRNO();
922 if (error == ERROR_NETWORK_TIMEOUT ||
923 (error == ERROR_NETWORK && has_context && strstr(err_ctx.context_message,
"timed out") != NULL)) {
925 log_debug(
"Client %s: receive timeout, continuing to wait for packets", client_ip);
933 log_info(
"Client %s disconnected", client_ip);
940 log_debug(
"Received packet type 0x%02X from %s, length=%zu", packet_type, client_ip, payload_size);
945 (packet_type == PACKET_TYPE_ACIP_SESSION_CREATE || packet_type == PACKET_TYPE_ACIP_DISCOVERY_PING ||
946 packet_type == PACKET_TYPE_PING || packet_type == PACKET_TYPE_PONG);
949 log_warn(
"Client %s sent packet type 0x%02X during multi-key session creation - only SESSION_CREATE/PING/PONG "
951 client_ip, packet_type);
955 if (error_transport) {
957 "Only SESSION_CREATE/PING/PONG allowed during multi-key session creation");
971 acip_acds_callbacks_t callbacks = g_acds_callbacks;
972 callbacks.app_ctx = server;
974 asciichat_error_t dispatch_result =
977 if (dispatch_result != ASCIICHAT_OK) {
978 log_warn(
"ACIP handler failed for packet type 0x%02X from %s: %s", packet_type, client_ip,
979 asciichat_error_string(dispatch_result));
992 socket_close(client_socket);
995 log_info(
"Client handler finished for %s", client_ip);
asciichat_error_t acds_verify_session_create(const uint8_t identity_pubkey[32], uint64_t timestamp, uint8_t capabilities, uint8_t max_participants, const uint8_t signature[64])
asciichat_error_t acds_verify_session_join(const uint8_t identity_pubkey[32], uint64_t timestamp, const char *session_string, const uint8_t signature[64])
bool acds_validate_timestamp(uint64_t timestamp_ms, uint32_t window_seconds)
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)
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
asciichat_error_t crypto_handshake_init(crypto_handshake_context_t *ctx, bool is_server)
asciichat_error_t database_session_update_host(sqlite3 *db, const uint8_t session_id[16], const uint8_t host_participant_id[16], const char *host_address, uint16_t host_port, uint8_t connection_type)
asciichat_error_t database_session_clear_host(sqlite3 *db, const uint8_t session_id[16])
asciichat_error_t database_session_lookup(sqlite3 *db, const char *session_string, const acds_config_t *config, acip_session_info_t *resp)
asciichat_error_t database_session_join(sqlite3 *db, const acip_session_join_t *req, const acds_config_t *config, acip_session_joined_t *resp)
asciichat_error_t database_session_start_migration(sqlite3 *db, const uint8_t session_id[16])
asciichat_error_t database_session_leave(sqlite3 *db, const uint8_t session_id[16], const uint8_t participant_id[16])
void database_close(sqlite3 *db)
asciichat_error_t database_session_create(sqlite3 *db, const acip_session_create_t *req, const acds_config_t *config, acip_session_created_t *resp)
asciichat_error_t database_init(const char *db_path, sqlite3 **db)
void database_session_cleanup_expired(sqlite3 *db)
bool check_and_record_rate_limit(rate_limiter_t *rate_limiter, const char *client_ip, rate_event_type_t event_type, socket_t client_socket, const char *operation_name)
asciichat_error_t crypto_handshake_server_auth_challenge_socket(crypto_handshake_context_t *ctx, socket_t client_socket)
Legacy wrapper: Auth challenge using socket (TCP clients only)
asciichat_error_t crypto_handshake_server_start_socket(crypto_handshake_context_t *ctx, socket_t client_socket)
Legacy wrapper: Start handshake using socket (TCP clients only)
asciichat_error_t crypto_handshake_server_complete_socket(crypto_handshake_context_t *ctx, socket_t client_socket)
Legacy wrapper: Complete handshake using socket (TCP clients only)
void tcp_server_destroy(tcp_server_t *server)
void tcp_server_reject_client(socket_t socket, const char *reason)
asciichat_error_t tcp_server_remove_client(tcp_server_t *server, socket_t socket)
asciichat_error_t tcp_server_init(tcp_server_t *server, const tcp_server_config_t *config)
asciichat_error_t tcp_server_get_client(tcp_server_t *server, socket_t socket, void **out_data)
asciichat_error_t tcp_server_run(tcp_server_t *server)
const char * tcp_client_context_get_ip(const tcp_client_context_t *ctx, char *buf, size_t len)
size_t tcp_server_get_client_count(tcp_server_t *server)
asciichat_error_t tcp_server_add_client(tcp_server_t *server, socket_t socket, void *client_data)
int receive_packet(socket_t sockfd, packet_type_t *type, void **data, size_t *len)
Receive a basic packet without encryption.
void rate_limiter_destroy(rate_limiter_t *limiter)
void rate_limiter_set_sqlite_db(rate_limiter_t *limiter, void *db)
rate_limiter_t * rate_limiter_create_sqlite(const char *db_path)
asciichat_error_t rate_limiter_prune(rate_limiter_t *limiter, uint32_t max_age_secs)
asciichat_error_t acip_send_session_joined(acip_transport_t *transport, const acip_session_joined_t *response)
asciichat_error_t acip_send_error(acip_transport_t *transport, uint32_t error_code, const char *message)
asciichat_error_t acip_send_pong(acip_transport_t *transport)
asciichat_error_t acip_send_session_info(acip_transport_t *transport, const acip_session_info_t *info)
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)
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.
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.
void * acds_client_handler(void *arg)
Per-client connection handler (thread entry point)
#define ACDS_DESTROY_TRANSPORT(transport_var)
void acds_server_shutdown(acds_server_t *server)
Shutdown discovery server.
asciichat_error_t acds_server_init(acds_server_t *server, const acds_config_t *config)
Initialize discovery server.
#define ACDS_CREATE_TRANSPORT(socket, transport_var)
asciichat_error_t acds_server_run(acds_server_t *server)
Run discovery server main loop.
Per-client connection data.
acip_session_create_t pending_session
Pending session data (from first SESSION_CREATE)
bool handshake_complete
Whether crypto handshake has completed.
uint8_t session_id[16]
Session UUID (valid if joined_session)
uint8_t participant_id[16]
Participant UUID (valid if joined_session)
crypto_handshake_context_t handshake_ctx
Handshake context for encrypted communication.
bool joined_session
Whether client has successfully joined a session.
size_t num_pending_keys
Number of keys received so far.
uint8_t pending_session_keys[MAX_IDENTITY_KEYS][32]
Array of identity public keys.
bool in_multikey_session_create
True during multi-key SESSION_CREATE sequence.
Discovery server configuration.
char address[256]
IPv4 bind address (empty = all interfaces)
turn_server_t turn_servers[4]
TURN server configurations.
char database_path[512]
SQLite database path.
bool require_server_identity
Require servers to provide signed identity when creating sessions.
int port
TCP listen port (default 27225)
char address6[256]
IPv6 bind address (empty = all interfaces)
bool require_client_identity
Require clients to provide signed identity when joining sessions.
stun_server_t stun_servers[4]
STUN server configurations.
acds_config_t config
Runtime configuration.
uint8_t identity_public[32]
Ed25519 public key.
size_t num_active_migrations
Number of active migrations.
atomic_bool shutdown
Shutdown flag for worker threads.
tcp_server_t tcp_server
TCP server abstraction.
uint8_t identity_secret[64]
Ed25519 secret key.
sqlite3 * db
SQLite database handle.
struct rate_limiter_s * rate_limiter
SQLite-backed rate limiter.
migration_context_t active_migrations[32]
Slots for up to 32 concurrent migrations.
thread_pool_t * worker_pool
Thread pool for background workers.
In-memory host migration context.
uint64_t migration_start_ns
When migration started (nanoseconds since sokol_time setup)
uint8_t session_id[16]
Session UUID.
acip_transport_t * acip_tcp_transport_create(socket_t sockfd, crypto_context_t *crypto_ctx)
void thread_pool_destroy(thread_pool_t *pool)
thread_pool_t * thread_pool_create(const char *pool_name)
asciichat_error_t thread_pool_spawn(thread_pool_t *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)
uint64_t time_get_ns(void)