33static void generate_uuid(
uint8_t uuid_out[16]) {
34 randombytes_buf(uuid_out, 16);
37 uuid_out[6] = (uuid_out[6] & 0x0F) | 0x40;
40 uuid_out[8] = (uuid_out[8] & 0x3F) | 0x80;
47static uint64_t get_current_time_ms(
void) {
49 clock_gettime(CLOCK_REALTIME, &ts);
59static bool verify_password(
const char *password,
const char *hash) {
60 return crypto_pwhash_str_verify(hash, password, strlen(password)) == 0;
76static unsigned long session_string_hash(
const char *session_string) {
78 return (
unsigned long)fnv1a_hash_string(session_string);
87static int session_string_match(
struct cds_lfht_node *node,
const void *key) {
89 const char *session_string = (
const char *)key;
98static void session_free_rcu(
struct rcu_head *head) {
125 struct cds_lfht_iter iter;
128 cds_lfht_for_each_entry(registry->
sessions, &iter, entry, hash_node) {
174 memset(registry, 0,
sizeof(*registry));
181 registry->
sessions = cds_lfht_new(256, 256, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
186 log_info(
"Session registry initialized (RCU lock-free hash table)");
191 if (!registry || !registry->
sessions) {
203 struct cds_lfht_iter iter;
204 int deleted_count = 0;
208 cds_lfht_for_each_entry(registry->
sessions, &iter, entry, hash_node) {
214 call_rcu(&entry->
rcu_head, session_free_rcu);
221 log_debug(
"Deleted %d sessions during registry shutdown", deleted_count);
229 int ret = cds_lfht_destroy(registry->
sessions, NULL);
231 log_warn(
"Failed to destroy RCU hash table (returned %d)", ret);
234 log_info(
"Session registry destroyed");
243 if (!registry || !req || !config || !resp) {
247 memset(resp, 0,
sizeof(*resp));
251 if (req->reserved_string_len > 0) {
253 const char *reserved_str = (
const char *)(req + 1);
256 memcpy(session_string, reserved_str, len);
257 session_string[len] =
'\0';
277 memset(session, 0,
sizeof(*session));
292 memcpy(session->
host_pubkey, req->identity_pubkey, 32);
315 uint64_t now = get_current_time_ms();
322 unsigned long hash = session_string_hash(session_string);
323 struct cds_lfht_node *ret_node =
324 cds_lfht_add_unique(registry->
sessions, hash, session_string_match, session_string, &session->
hash_node);
334 resp->session_string_len = (
uint8_t)strlen(session_string);
335 SAFE_STRNCPY(resp->session_string, session_string,
sizeof(resp->session_string));
336 memcpy(resp->session_id, session->
session_id, 16);
351 if (!registry || !session_string || !config || !resp) {
355 memset(resp, 0,
sizeof(*resp));
363 unsigned long hash = session_string_hash(session_string);
364 struct cds_lfht_iter iter;
365 cds_lfht_lookup(registry->
sessions, hash, session_string_match, session_string, &iter);
366 struct cds_lfht_node *node = cds_lfht_iter_get_node(&iter);
371 log_debug(
"Session lookup failed: %s (not found)", session_string);
379 memcpy(resp->session_id, session->
session_id, 16);
380 memcpy(resp->host_pubkey, session->
host_pubkey, 32);
401 log_debug(
"Session lookup: %s (found, participants=%d/%d)", session_string, resp->current_participants,
402 resp->max_participants);
409 if (!registry || !req || !config || !resp) {
413 memset(resp, 0,
sizeof(*resp));
420 memcpy(session_string, req->session_string, len);
421 session_string[len] =
'\0';
429 unsigned long hash = session_string_hash(session_string);
430 struct cds_lfht_iter iter;
431 cds_lfht_lookup(registry->
sessions, hash, session_string_match, session_string, &iter);
432 struct cds_lfht_node *node = cds_lfht_iter_get_node(&iter);
437 SAFE_STRNCPY(resp->error_message,
"Session not found",
sizeof(resp->error_message));
438 log_warn(
"Session join failed: %s (not found)", session_string);
452 SAFE_STRNCPY(resp->error_message,
"Session is full",
sizeof(resp->error_message));
453 log_warn(
"Session join failed: %s (full)", session_string);
459 if (!verify_password(req->password, session->
password_hash)) {
463 SAFE_STRNCPY(resp->error_message,
"Invalid password",
sizeof(resp->error_message));
464 log_warn(
"Session join failed: %s (invalid password)", session_string);
467 }
else if (session->
has_password && !req->has_password) {
471 SAFE_STRNCPY(resp->error_message,
"Password required",
sizeof(resp->error_message));
472 log_warn(
"Session join failed: %s (password required)", session_string);
477 int slot = find_empty_slot_locked(session);
482 SAFE_STRNCPY(resp->error_message,
"No participant slots available",
sizeof(resp->error_message));
483 log_error(
"Session join failed: %s (no slots, but count was not full)", session_string);
495 memset(participant, 0,
sizeof(*participant));
500 participant->
joined_at = get_current_time_ms();
510 memcpy(resp->session_id, session->
session_id, 16);
513 bool reveal_ip =
false;
522 log_warn(
"Session join: %s has no password and expose_ip_publicly=false - IP NOT REVEALED", session_string);
539 log_debug(
"Generated TURN credentials for session %s", session_string);
541 log_warn(
"Failed to generate TURN credentials for session %s", session_string);
545 log_info(
"Participant joined session %s (participants=%d/%d, server=%s:%d, type=%s)", session_string,
549 log_info(
"Participant joined session %s (participants=%d/%d, IP WITHHELD - auth required)", session_string,
607 call_rcu(&session->
rcu_head, session_free_rcu);
616 if (!registry || !registry->
sessions) {
620 uint64_t now = get_current_time_ms();
621 size_t removed_count = 0;
626 struct cds_lfht_iter iter;
629 cds_lfht_for_each_entry(registry->
sessions, &iter, entry, hash_node) {
632 (
unsigned long long)entry->
created_at, (
unsigned long long)entry->
expires_at, (
unsigned long long)now);
638 call_rcu(&entry->
rcu_head, session_free_rcu);
645 if (removed_count > 0) {
646 log_info(
"Cleaned up %zu expired sessions", removed_count);
649 if (removed_count > 100) {
650 log_debug(
"Synchronizing RCU after bulk session cleanup");
🔍 ASCII-Chat Discovery Service (acds) main entry point
#️⃣ FNV-1a Hash Function Implementation
#define ACIP_MAX_SESSION_STRING_LEN
Maximum session string length (e.g., "swift-river-mountain" = 20 chars)
#define ACIP_SESSION_EXPIRATION_MS
Session expiration time (24 hours in milliseconds)
@ ACIP_ERROR_INVALID_PASSWORD
Password verification failed.
@ ACIP_ERROR_SESSION_NOT_FOUND
Session does not exist.
@ ACIP_ERROR_SESSION_FULL
Session has reached max participants.
@ ACIP_ERROR_NONE
No error (success)
@ SESSION_TYPE_WEBRTC
WebRTC P2P mesh with STUN/TURN relay.
#define SAFE_STRNCPY(dst, src, size)
#define SAFE_MALLOC(size, cast)
unsigned long long uint64_t
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
📝 Logging API with multiple log levels and terminal output control
void session_registry_destroy(session_registry_t *registry)
Destroy session registry.
asciichat_error_t session_leave(session_registry_t *registry, const uint8_t session_id[16], const uint8_t participant_id[16])
Leave session.
asciichat_error_t session_registry_init(session_registry_t *registry)
Initialize session registry.
asciichat_error_t session_create(session_registry_t *registry, const acip_session_create_t *req, const acds_config_t *config, acip_session_created_t *resp)
Create new session.
asciichat_error_t session_join(session_registry_t *registry, const acip_session_join_t *req, const acds_config_t *config, acip_session_joined_t *resp)
Join existing session.
asciichat_error_t session_lookup(session_registry_t *registry, const char *session_string, const acds_config_t *config, acip_session_info_t *resp)
Lookup session by string.
void session_cleanup_expired(session_registry_t *registry)
Clean up expired sessions.
🎯 Session registry for discovery service (lock-free RCU implementation)
#define MAX_PARTICIPANTS
Maximum participants per session.
uint8_t participant_id[16]
bool acds_string_validate(const char *str)
Validate session string format.
asciichat_error_t acds_string_generate(char *output, size_t output_size)
Generate random session string.
Session string generation for discovery service.
Discovery server configuration.
bool require_server_verify
ACDS policy: require servers to verify client identity during handshake.
uint8_t turn_count
Number of configured TURN servers (0-4)
char turn_secret[256]
Shared secret for TURN credential generation (HMAC-SHA1)
uint8_t stun_count
Number of configured STUN servers (0-4)
bool require_client_verify
ACDS policy: require clients to verify server identity during handshake.
Participant in a session.
uint8_t participant_id[16]
UUID.
uint8_t identity_pubkey[32]
Ed25519 public key.
uint64_t joined_at
Unix timestamp (ms)
Session entry (RCU hash table node)
bool expose_ip_publicly
Allow IP disclosure without verification (explicit opt-in via –acds-expose-ip)
mutex_t participant_mutex
Fine-grained lock for participant list.
bool has_password
Password protection flag.
char server_address[64]
IPv4/IPv6 address or hostname.
uint8_t max_participants
1-8
uint8_t host_pubkey[32]
Host's Ed25519 key.
struct cds_lfht_node hash_node
RCU lock-free hash table node (keyed by session_string)
uint8_t session_id[16]
UUID.
char password_hash[128]
Argon2id hash (if has_password)
uint64_t created_at
Unix timestamp (ms)
char session_string[48]
e.g., "swift-river-mountain" (lookup key)
struct rcu_head rcu_head
For deferred freeing via call_rcu()
uint64_t expires_at
Unix timestamp (ms) - created_at + 24h.
uint8_t capabilities
bit 0: video, bit 1: audio
uint8_t session_type
acds_session_type_t: 0=DIRECT_TCP, 1=WEBRTC
participant_t * participants[8]
Participant array.
uint16_t server_port
Port number for client connection.
uint8_t current_participants
Active participant count.
Session registry (lock-free RCU)
struct cds_lfht * sessions
RCU lock-free hash table.
TURN server credentials (username + password)
⏱️ High-precision timing utilities using sokol_time.h and uthash
asciichat_error_t turn_generate_credentials(const char *session_id, const char *secret, uint32_t validity_seconds, turn_credentials_t *out_credentials)
Generate time-limited TURN credentials.
TURN server credential generation for WebRTC.