ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
discovery.c
Go to the documentation of this file.
1
17#include <ascii-chat/ui/mdns.h> // For ui_mdns_server_t struct only
21#include <ascii-chat/log/log.h>
24#include <string.h>
25#include <ctype.h>
26#include <stdlib.h>
27#include <time.h>
28
29// ============================================================================
30// Thread Coordination
31// ============================================================================
32
44
45// ============================================================================
46// Utility Functions
47// ============================================================================
48
49void pubkey_to_hex(const uint8_t pubkey[32], char hex_out[65]) {
50 if (!pubkey || !hex_out)
51 return;
52 for (int i = 0; i < 32; i++) {
53 safe_snprintf(hex_out + (i * 2), 3, "%02x", pubkey[i]);
54 }
55 hex_out[64] = '\0';
56}
57
58asciichat_error_t hex_to_pubkey(const char *hex_str, uint8_t pubkey_out[32]) {
59 if (!hex_str || !pubkey_out) {
60 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid hex_str or pubkey_out pointer");
62 }
63
64 if (strlen(hex_str) != 64) {
65 SET_ERRNO(ERROR_INVALID_PARAM, "Hex string must be exactly 64 characters");
67 }
68
69 for (int i = 0; i < 32; i++) {
70 char hex_byte[3];
71 hex_byte[0] = hex_str[i * 2];
72 hex_byte[1] = hex_str[i * 2 + 1];
73 hex_byte[2] = '\0';
74
75 if (!isxdigit(hex_byte[0]) || !isxdigit(hex_byte[1])) {
76 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid hex character in string");
78 }
79
80 pubkey_out[i] = (uint8_t)strtol(hex_byte, NULL, 16);
81 }
82
83 return ASCIICHAT_OK;
84}
85
86// NOTE: is_session_string() has been moved to lib/discovery/strings.c with enhanced
87// validation against cached wordlists. See that module for the implementation.
88
89// ============================================================================
90// TXT Record Parsing
91// ============================================================================
92// mDNS Query Implementation (Core Module)
93// ============================================================================
94
106
111static void discovery_mdns_callback(const asciichat_mdns_discovery_t *discovery, void *user_data) {
112 mdns_query_state_t *state = (mdns_query_state_t *)user_data;
113 if (!state || !discovery) {
114 return;
115 }
116
117 // Check if we've exceeded capacity
118 if (state->count >= state->capacity) {
119 log_warn("mDNS: Reached maximum server capacity (%d)", state->capacity);
120 return;
121 }
122
123 // Check if timeout exceeded
124 int64_t elapsed = (int64_t)time_ns_to_ms(time_get_ns()) - state->start_time_ms;
125 if (elapsed > state->timeout_ms) {
126 state->query_complete = true;
127 return;
128 }
129
130 // Only accept services of the right type
131 if (strstr(discovery->type, "_ascii-chat._tcp") == NULL) {
132 return;
133 }
134
135 // Check if we already have this server (avoid duplicates)
136 for (int i = 0; i < state->count; i++) {
137 if (strcmp(state->servers[i].name, discovery->name) == 0 && state->servers[i].port == discovery->port) {
138 // Update TTL if newer
139 if (discovery->ttl > state->servers[i].ttl) {
140 state->servers[i].ttl = discovery->ttl;
141 }
142 return; // Already have this server
143 }
144 }
145
146 // Add new server to our array
147 ui_mdns_server_t *server = &state->servers[state->count];
148 memset(server, 0, sizeof(ui_mdns_server_t));
149
150 // Copy service information
151 SAFE_STRNCPY(server->name, discovery->name, sizeof(server->name));
152 SAFE_STRNCPY(server->ipv4, discovery->ipv4, sizeof(server->ipv4));
153 SAFE_STRNCPY(server->ipv6, discovery->ipv6, sizeof(server->ipv6));
154 server->port = discovery->port;
155 server->ttl = discovery->ttl;
156
157 // Prefer IPv4 address as the primary address, fall back to hostname
158 if (discovery->ipv4[0] != '\0') {
159 SAFE_STRNCPY(server->address, discovery->ipv4, sizeof(server->address));
160 } else if (discovery->host[0] != '\0') {
161 SAFE_STRNCPY(server->address, discovery->host, sizeof(server->address));
162 } else if (discovery->ipv6[0] != '\0') {
163 SAFE_STRNCPY(server->address, discovery->ipv6, sizeof(server->address));
164 }
165
166 state->count++;
167 log_debug("mDNS: Found server '%s' at %s:%u", discovery->name, server->address, discovery->port);
168}
169
179ui_mdns_server_t *discovery_mdns_query(int timeout_ms, int max_servers, bool quiet, int *out_count) {
180 if (!out_count) {
181 SET_ERRNO(ERROR_INVALID_PARAM, "out_count pointer is NULL");
182 return NULL;
183 }
184
185 *out_count = 0;
186
187 // Apply defaults
188 if (timeout_ms <= 0) {
189 timeout_ms = 2 * MS_PER_SEC_INT;
190 }
191 if (max_servers <= 0) {
192 max_servers = 20;
193 }
194
195 // Allocate state for collecting servers
196 mdns_query_state_t state;
197 memset(&state, 0, sizeof(state));
198 state.capacity = max_servers;
199 state.timeout_ms = timeout_ms;
200 state.start_time_ms = (int64_t)time_ns_to_ms(time_get_ns());
201
202 // Allocate server array
203 state.servers = SAFE_MALLOC((size_t)state.capacity * sizeof(ui_mdns_server_t), ui_mdns_server_t *);
204 if (!state.servers) {
205 SET_ERRNO(ERROR_MEMORY, "Failed to allocate mDNS discovery server array");
206 return NULL;
207 }
208 memset(state.servers, 0, state.capacity * sizeof(ui_mdns_server_t));
209
210 if (!quiet) {
211 log_info("mDNS: Searching for ascii-chat servers on local network (timeout: %dms)", state.timeout_ms);
212 printf("๐Ÿ” Searching for ascii-chat servers on LAN...\n");
213 }
214
215 // Initialize mDNS
217 if (!mdns) {
218 log_warn("mDNS: Failed to initialize mDNS - discovery unavailable");
219 return state.servers;
220 }
221
222 // Start mDNS query for _ascii-chat._tcp services
223 asciichat_error_t query_result =
224 asciichat_mdns_query(mdns, "_ascii-chat._tcp.local", discovery_mdns_callback, &state);
225
226 if (query_result != ASCIICHAT_OK) {
227 log_info("mDNS: Query failed - no servers found via service discovery");
229 return state.servers;
230 }
231
232 // Poll for responses until timeout
233 int64_t deadline = state.start_time_ms + state.timeout_ms;
234 while (!state.query_complete && (int64_t)time_ns_to_ms(time_get_ns()) < deadline) {
235 int poll_timeout = (int)(deadline - (int64_t)time_ns_to_ms(time_get_ns()));
236 if (poll_timeout < 0) {
237 poll_timeout = 0;
238 }
239 if (poll_timeout > 100) {
240 poll_timeout = 100; // Check every 100ms
241 }
242 asciichat_mdns_update(mdns, poll_timeout);
243 }
244
245 // Cleanup mDNS
247
248 if (!quiet) {
249 if (state.count > 0) {
250 printf("โœ… Found %d ascii-chat server%s on LAN\n", state.count, state.count == 1 ? "" : "s");
251 log_info("mDNS: Found %d server(s)", state.count);
252 } else {
253 printf("โŒ No ascii-chat servers found on LAN\n");
254 log_info("mDNS: No servers found");
255 }
256 }
257
258 *out_count = state.count;
259 return state.servers;
260}
261
266 SAFE_FREE(servers);
267}
268
269// ============================================================================
270// mDNS Discovery Thread
271// ============================================================================
272
282
284static void *mdns_thread_fn(void *arg) {
286 if (!ctx)
287 return NULL;
288
289 // Call the public mDNS query function in this module
290 int discovered_count = 0;
291 ui_mdns_server_t *discovered_servers =
292 discovery_mdns_query((int)(ctx->timeout_ms > 0 ? ctx->timeout_ms : 2000), 20, true, &discovered_count);
293
294 if (!discovered_servers || discovered_count == 0) {
295 log_debug("mDNS: No servers found (this is normal if no servers are on LAN)");
296 if (discovered_servers) {
297 discovery_mdns_destroy(discovered_servers);
298 }
299 mutex_lock(&ctx->state->lock);
300 ctx->state->mdns_done = true;
301 cond_signal(&ctx->state->signal);
302 mutex_unlock(&ctx->state->lock);
303 SAFE_FREE(ctx);
304 return NULL;
305 }
306
307 // Search for server matching our session string
308 for (int i = 0; i < discovered_count; i++) {
309 ui_mdns_server_t *server = &discovered_servers[i];
310
311 if (strcmp(server->name, ctx->session_string) == 0) {
312 // Found a match!
313 mutex_lock(&ctx->state->lock);
314 {
315 if (!ctx->state->found) {
316 ctx->state->found = true;
317 ctx->state->result->success = true;
318 ctx->state->result->source = DISCOVERY_SOURCE_MDNS;
319 ctx->state->result->server_port = server->port;
320
321 const char *best_addr = (server->ipv4[0] != '\0') ? server->ipv4 : server->ipv6;
322 SAFE_STRNCPY(ctx->state->result->server_address, best_addr, sizeof(ctx->state->result->server_address) - 1);
323 ctx->state->result->server_address[sizeof(ctx->state->result->server_address) - 1] = '\0';
324
326 sizeof(ctx->state->result->mdns_service_name) - 1);
327 ctx->state->result->mdns_service_name[sizeof(ctx->state->result->mdns_service_name) - 1] = '\0';
328
329 log_info("mDNS: Found session '%s' at %s:%d", ctx->session_string, ctx->state->result->server_address,
330 ctx->state->result->server_port);
331 cond_signal(&ctx->state->signal);
332 }
333 }
334 mutex_unlock(&ctx->state->lock);
335 break;
336 }
337 }
338
339 discovery_mdns_destroy(discovered_servers);
340
341 mutex_lock(&ctx->state->lock);
342 ctx->state->mdns_done = true;
343 cond_signal(&ctx->state->signal);
344 mutex_unlock(&ctx->state->lock);
345 SAFE_FREE(ctx);
346 return NULL;
347}
348
349// ============================================================================
350// ACDS Discovery Thread
351// ============================================================================
352
361
363static void *acds_thread_fn(void *arg) {
365 if (!ctx)
366 return NULL;
367
368 acds_client_t client;
369 memset(&client, 0, sizeof(client));
370
371 acds_client_config_t client_config;
372 memset(&client_config, 0, sizeof(client_config));
373
374 SAFE_STRNCPY(client_config.server_address, ctx->config->acds_server, sizeof(client_config.server_address) - 1);
375 client_config.server_port = ctx->config->acds_port;
376 client_config.timeout_ms = ctx->config->acds_timeout_ms;
377
378 // Connect to ACDS server
379 asciichat_error_t conn_err = acds_client_connect(&client, &client_config);
380 if (conn_err != ASCIICHAT_OK) {
381 log_debug("ACDS: Failed to connect to %s:%d", ctx->config->acds_server, ctx->config->acds_port);
382 mutex_lock(&ctx->state->lock);
383 ctx->state->acds_done = true;
384 cond_signal(&ctx->state->signal);
385 mutex_unlock(&ctx->state->lock);
386 SAFE_FREE(ctx);
387 return NULL;
388 }
389
390 // Look up session
391 acds_session_lookup_result_t lookup_result;
392 memset(&lookup_result, 0, sizeof(lookup_result));
393
394 asciichat_error_t lookup_err = acds_session_lookup(&client, ctx->session_string, &lookup_result);
395 if (lookup_err != ASCIICHAT_OK) {
396 log_debug("ACDS: Session lookup failed for '%s'", ctx->session_string);
397 acds_client_disconnect(&client);
398 mutex_lock(&ctx->state->lock);
399 ctx->state->acds_done = true;
400 cond_signal(&ctx->state->signal);
401 mutex_unlock(&ctx->state->lock);
402 SAFE_FREE(ctx);
403 return NULL;
404 }
405
406 if (!lookup_result.found) {
407 log_debug("ACDS: Session '%s' not found", ctx->session_string);
408 acds_client_disconnect(&client);
409 mutex_lock(&ctx->state->lock);
410 ctx->state->acds_done = true;
411 cond_signal(&ctx->state->signal);
412 mutex_unlock(&ctx->state->lock);
413 SAFE_FREE(ctx);
414 return NULL;
415 }
416
417 // Verify pubkey if provided
418 if (ctx->config->expected_pubkey) {
419 if (memcmp(lookup_result.host_pubkey, ctx->config->expected_pubkey, 32) != 0) {
420 log_warn("ACDS: Session found but pubkey mismatch (MITM?)");
421 acds_client_disconnect(&client);
422 mutex_lock(&ctx->state->lock);
423 ctx->state->acds_done = true;
424 cond_signal(&ctx->state->signal);
425 mutex_unlock(&ctx->state->lock);
426 SAFE_FREE(ctx);
427 return NULL;
428 }
429 }
430
431 // Join session to get server connection details
432 acds_session_join_params_t join_params;
433 memset(&join_params, 0, sizeof(join_params));
434 join_params.session_string = ctx->session_string;
435
436 if (ctx->config->client_pubkey) {
437 memcpy(join_params.identity_pubkey, ctx->config->client_pubkey, 32);
438 }
439 if (ctx->config->client_seckey) {
440 memcpy(join_params.identity_seckey, ctx->config->client_seckey, 64);
441 }
442 if (ctx->config->password) {
443 join_params.has_password = true;
444 SAFE_STRNCPY(join_params.password, ctx->config->password, sizeof(join_params.password) - 1);
445 }
446
447 acds_session_join_result_t join_result;
448 memset(&join_result, 0, sizeof(join_result));
449
450 asciichat_error_t join_err = acds_session_join(&client, &join_params, &join_result);
451 acds_client_disconnect(&client);
452
453 if (join_err != ASCIICHAT_OK || !join_result.success) {
454 log_debug("ACDS: Session join failed: %s", join_result.error_message);
455 mutex_lock(&ctx->state->lock);
456 ctx->state->acds_done = true;
457 cond_signal(&ctx->state->signal);
458 mutex_unlock(&ctx->state->lock);
459 SAFE_FREE(ctx);
460 return NULL;
461 }
462
463 // Found it! Populate result
464 mutex_lock(&ctx->state->lock);
465 {
466 if (!ctx->state->found) {
467 ctx->state->found = true;
468 ctx->state->result->success = true;
469 ctx->state->result->source = DISCOVERY_SOURCE_ACDS;
470
471 memcpy(ctx->state->result->host_pubkey, lookup_result.host_pubkey, 32);
472 memcpy(ctx->state->result->session_id, join_result.session_id, 16);
473 memcpy(ctx->state->result->participant_id, join_result.participant_id, 16);
474
476 sizeof(ctx->state->result->server_address) - 1);
477 ctx->state->result->server_port = join_result.server_port;
478
479 log_info("ACDS: Found session '%s' at %s:%d", ctx->session_string, ctx->state->result->server_address,
480 ctx->state->result->server_port);
481 cond_signal(&ctx->state->signal);
482 }
483 }
484 mutex_unlock(&ctx->state->lock);
485
486 SAFE_FREE(ctx);
487 return NULL;
488}
489
490// ============================================================================
491// Main Discovery Function
492// ============================================================================
493
495 if (!config)
496 return;
497
498 memset(config, 0, sizeof(*config));
499
500 // Check if debug or release build
501#ifdef NDEBUG
502 // Release: use internet ACDS
503 SAFE_STRNCPY(config->acds_server, "discovery.ascii-chat.com", sizeof(config->acds_server) - 1);
504#else
505 // Debug: use local ACDS
506 SAFE_STRNCPY(config->acds_server, "127.0.0.1", sizeof(config->acds_server) - 1);
507#endif
508
510 config->mdns_timeout_ms = 2 * MS_PER_SEC_INT;
511 config->acds_timeout_ms = 5 * MS_PER_SEC_INT;
512 config->insecure_mode = false;
513 config->expected_pubkey = NULL;
514}
515
516asciichat_error_t discover_session_parallel(const char *session_string, const discovery_config_t *config,
517 discovery_result_t *result) {
518 if (!session_string || !config || !result) {
519 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters to discover_session_parallel");
520 return ERROR_INVALID_PARAM;
521 }
522
523 // Validate session string format
524 if (!is_session_string(session_string)) {
525 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid session string format");
526 return ERROR_INVALID_PARAM;
527 }
528
529 memset(result, 0, sizeof(*result));
530 log_info("Discovery: Looking up session '%s'", session_string);
531
532 // Initialize thread state
534 memset(&state, 0, sizeof(state));
535 state.result = result;
536 mutex_init(&state.lock, "mdns_resolve");
537 cond_init(&state.signal, "mdns_signal");
538
539 // Determine which discovery methods to use
540 bool use_mdns = true;
541 bool use_acds = config->expected_pubkey != NULL || config->insecure_mode;
542
543 if (!use_acds) {
544 log_debug("Discovery: mDNS-only mode (no --server-key and no --acds-insecure)");
545 }
546
547 // Spawn mDNS thread
548 asciichat_thread_t mdns_thread;
549 asciichat_thread_init(&mdns_thread);
550
551 if (use_mdns) {
552 mdns_thread_context_t *mdns_ctx = SAFE_MALLOC(sizeof(*mdns_ctx), mdns_thread_context_t *);
553 if (!mdns_ctx) {
554 SET_ERRNO(ERROR_MEMORY, "Failed to allocate mDNS context");
555 mutex_destroy(&state.lock);
556 cond_destroy(&state.signal);
557 return ERROR_MEMORY;
558 }
559
560 mdns_ctx->session_string = session_string;
561 mdns_ctx->state = &state;
562 mdns_ctx->expected_pubkey = config->expected_pubkey;
563
564 int thread_err = asciichat_thread_create(&mdns_thread, "mdns", mdns_thread_fn, mdns_ctx);
565 if (thread_err != 0) {
566 log_warn("Discovery: Failed to spawn mDNS thread");
567 SAFE_FREE(mdns_ctx);
568 use_mdns = false;
569 }
570 }
571
572 // Spawn ACDS thread
573 asciichat_thread_t acds_thread;
574 asciichat_thread_init(&acds_thread);
575
576 if (use_acds) {
577 acds_thread_context_t *acds_ctx = SAFE_MALLOC(sizeof(*acds_ctx), acds_thread_context_t *);
578 if (!acds_ctx) {
579 SET_ERRNO(ERROR_MEMORY, "Failed to allocate ACDS context");
580 if (use_mdns && asciichat_thread_is_initialized(&mdns_thread)) {
581 asciichat_thread_join(&mdns_thread, NULL);
582 }
583 mutex_destroy(&state.lock);
584 cond_destroy(&state.signal);
585 return ERROR_MEMORY;
586 }
587
588 acds_ctx->session_string = session_string;
589 acds_ctx->state = &state;
590 acds_ctx->config = config;
591
592 int thread_err = asciichat_thread_create(&acds_thread, "acds_client", acds_thread_fn, acds_ctx);
593 if (thread_err != 0) {
594 log_warn("Discovery: Failed to spawn ACDS thread");
595 SAFE_FREE(acds_ctx);
596 use_acds = false;
597 }
598 }
599
600 // Wait for result (mDNS timeout 2s + ACDS timeout 5s max)
601 uint32_t wait_timeout_ms = config->acds_timeout_ms + 1000;
602 mutex_lock(&state.lock);
603 {
604 uint32_t elapsed_ms = 0;
605 while (!state.found && elapsed_ms < wait_timeout_ms) {
606 // Check if both threads are done
607 if (state.mdns_done && state.acds_done) {
608 break;
609 }
610
611 // Wait with timeout
612 cond_timedwait(&state.signal, &state.lock, 500 * NS_PER_MS_INT);
613 elapsed_ms += 500;
614 }
615 }
616 mutex_unlock(&state.lock);
617
618 // Join threads
619 if (use_mdns && asciichat_thread_is_initialized(&mdns_thread)) {
620 asciichat_thread_join(&mdns_thread, NULL);
621 }
622 if (use_acds && asciichat_thread_is_initialized(&acds_thread)) {
623 asciichat_thread_join(&acds_thread, NULL);
624 }
625
626 // Cleanup
627 mutex_destroy(&state.lock);
628 cond_destroy(&state.signal);
629
630 // Check result
631 if (!result->success) {
632 SET_ERRNO(ERROR_NOT_FOUND, "Session '%s' not found (mDNS/ACDS timeout)", session_string);
633 return ERROR_NOT_FOUND;
634 }
635
636 log_info("Discovery: Session '%s' discovered via %s", session_string,
637 result->source == DISCOVERY_SOURCE_MDNS ? "mDNS" : "ACDS");
638
639 return ASCIICHAT_OK;
640}
asciichat_error_t acds_session_lookup(acds_client_t *client, const char *session_string, acds_session_lookup_result_t *result)
Look up session by string.
asciichat_error_t acds_session_join(acds_client_t *client, const acds_session_join_params_t *params, acds_session_join_result_t *result)
Join an existing session.
asciichat_error_t acds_client_connect(acds_client_t *client, const acds_client_config_t *config)
Connect to ACDS server.
void acds_client_disconnect(acds_client_t *client)
Disconnect from ACDS server.
ACIP client-side protocol library and ACDS client connection API.
bool is_session_string(const char *str)
Check if a string is a valid session string.
void pubkey_to_hex(const uint8_t pubkey[32], char hex_out[65])
Convert Ed25519 public key to hex string.
Definition discovery.c:49
asciichat_error_t hex_to_pubkey(const char *hex_str, uint8_t pubkey_out[32])
Convert hex string to Ed25519 public key.
Definition discovery.c:58
void discovery_mdns_destroy(ui_mdns_server_t *servers)
Free memory from mDNS discovery results.
Definition discovery.c:265
ui_mdns_server_t * discovery_mdns_query(int timeout_ms, int max_servers, bool quiet, int *out_count)
Public mDNS query function used by both parallel discovery and TUI wrapper.
Definition discovery.c:179
void discovery_config_init_defaults(discovery_config_t *config)
Initialize discovery config with defaults.
Definition discovery.c:494
asciichat_error_t discover_session_parallel(const char *session_string, const discovery_config_t *config, discovery_result_t *result)
Look up session in parallel on mDNS and ACDS.
Definition discovery.c:516
unsigned int uint32_t
Definition common.h:58
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
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
@ ERROR_NOT_FOUND
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
uint64_t time_get_ns(void)
Get current monotonic time in nanoseconds.
Definition util/time.c:108
#define NS_PER_MS_INT
Definition time.h:156
#define MS_PER_SEC_INT
Definition time.h:164
#define OPT_ACDS_PORT_INT_DEFAULT
Default ACDS discovery service port (integer)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
void asciichat_thread_init(asciichat_thread_t *thread)
Initialize a thread handle to an uninitialized state.
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
int mutex_init(mutex_t *mutex, const char *name)
Initialize a mutex with a name.
Definition threading.c:16
int cond_signal(cond_t *cond)
Signal a condition variable (wake one waiting thread)
bool asciichat_thread_is_initialized(asciichat_thread_t *thread)
Check if a thread handle has been initialized.
int cond_init(cond_t *cond, const char *name)
Initialize a condition variable with a name.
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
#define cond_timedwait(cond, mutex, timeout_ns)
Wait on a condition variable with timeout (with debug tracking in debug builds)
Definition cond.h:300
int cond_destroy(cond_t *cond)
Destroy a condition variable.
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
Definition threading.c:22
๐Ÿงต Cross-platform thread interface for ascii-chat
#define asciichat_thread_create(thread_ptr, attr, start_routine, arg)
void * asciichat_thread_t
#define asciichat_thread_join(thread, timeout_ms)
๐Ÿ“ Logging API with multiple log levels and terminal output control
asciichat_error_t asciichat_mdns_update(asciichat_mdns_t *mdns, int timeout_ms)
Process pending mDNS events (must be called regularly)
void asciichat_mdns_destroy(asciichat_mdns_t *mdns)
Shutdown mDNS context and cleanup.
asciichat_mdns_t * asciichat_mdns_init(void)
Initialize mDNS context.
asciichat_error_t asciichat_mdns_query(asciichat_mdns_t *mdns, const char *service_type, asciichat_mdns_discovery_callback_fn callback, void *user_data)
Query for services on the local network.
Cross-platform mutex interface for ascii-chat.
ACDS client connection configuration.
char server_address[256]
ACDS server address (e.g., "discovery.ascii.chat" or "127.0.0.1")
uint32_t timeout_ms
Connection timeout in milliseconds.
uint16_t server_port
ACDS server port (default: 27225)
ACDS client connection handle.
Session join parameters.
uint8_t identity_pubkey[32]
Participant's Ed25519 public key.
bool has_password
Password provided.
char password[128]
Password (if has_password)
const char * session_string
Session to join.
uint8_t identity_seckey[64]
Ed25519 secret key (for signing)
Session join result.
char error_message[129]
Error message (if !success, null-terminated)
uint8_t participant_id[16]
Participant UUID (if success)
bool success
Join succeeded.
char server_address[65]
Server IP/hostname (if success, null-terminated)
uint16_t server_port
Server port (if success)
uint8_t session_id[16]
Session UUID (if success)
Session lookup result.
bool found
Session exists.
uint8_t host_pubkey[32]
Host's Ed25519 public key.
Context for ACDS discovery thread.
Definition discovery.c:356
discovery_thread_state_t * state
Shared discovery state.
Definition discovery.c:358
const discovery_config_t * config
Discovery configuration.
Definition discovery.c:359
const char * session_string
Session string to look up.
Definition discovery.c:357
Discovered service information.
Internal mDNS context structure.
Condition variable type (POSIX: pthread_cond_t with debug tracking)
Definition cond.h:63
Configuration for discovery session.
const uint8_t * client_seckey
Client's Ed25519 secret key (can be NULL)
Definition discovery.h:127
uint32_t acds_timeout_ms
ACDS lookup timeout (default: 5000ms)
Definition discovery.h:123
uint32_t mdns_timeout_ms
mDNS search timeout (default: 2000ms)
Definition discovery.h:122
uint16_t acds_port
ACDS port (default: 27225)
const char * password
Session password (optional)
char acds_server[256]
ACDS server address (e.g., "localhost" or "discovery.ascii-chat.com")
Definition discovery.h:118
bool insecure_mode
Allow no verification (โ€“acds-insecure flag)
Definition discovery.h:115
const uint8_t * expected_pubkey
Expected server pubkey (NULL = no verification)
Definition discovery.h:114
const uint8_t * client_pubkey
Client's Ed25519 public key (can be NULL)
Definition discovery.h:126
Result from session discovery.
Definition discovery.h:86
char mdns_service_name[256]
mDNS service instance name (e.g., "swift-river-mountain")
Definition discovery.h:97
uint16_t server_port
Server port (typically 27224)
Definition discovery.h:90
uint8_t host_pubkey[32]
Ed25519 public key of discovered server.
Definition discovery.h:88
uint8_t participant_id[16]
Assigned participant ID (from SESSION_JOIN)
Definition discovery.h:102
uint8_t session_id[16]
ACDS session UUID.
Definition discovery.h:101
char server_address[256]
Server IP or hostname.
Definition discovery.h:89
bool success
Discovery succeeded.
Definition discovery.h:87
enum discovery_result_t::@15 source
Which discovery method found the server.
Thread-safe result sharing between discovery threads.
Definition discovery.c:36
bool found
Whether a session was found.
Definition discovery.c:42
discovery_result_t * result
Shared discovery result.
Definition discovery.c:39
cond_t signal
Condition variable for signaling.
Definition discovery.c:38
bool mdns_done
Whether mDNS discovery completed.
Definition discovery.c:40
mutex_t lock
Mutex for thread-safe access.
Definition discovery.c:37
bool acds_done
Whether ACDS discovery completed.
Definition discovery.c:41
Internal state for collecting discovered services.
Definition discovery.c:98
ui_mdns_server_t * servers
Array of discovered servers.
Definition discovery.c:99
int count
Number of servers discovered so far.
Definition discovery.c:100
int64_t start_time_ms
When discovery started (for timeout)
Definition discovery.c:102
bool query_complete
Set when discovery completes.
Definition discovery.c:104
int capacity
Allocated capacity.
Definition discovery.c:101
int timeout_ms
Discovery timeout in milliseconds.
Definition discovery.c:103
Context for mDNS discovery thread.
Definition discovery.c:276
discovery_thread_state_t * state
Shared discovery state.
Definition discovery.c:278
uint32_t timeout_ms
Discovery timeout in milliseconds.
Definition discovery.c:280
const uint8_t * expected_pubkey
Expected server public key (optional)
Definition discovery.c:279
const char * session_string
Session string to discover.
Definition discovery.c:277
Mutex type (POSIX: pthread_mutex_t with debug tracking)
Discovered server information from mDNS.
Definition ui/mdns.h:57
char name[256]
Service instance name (e.g., "swift-river-canyon")
Definition ui/mdns.h:58
uint32_t ttl
TTL remaining (seconds)
Definition ui/mdns.h:63
char ipv4[16]
IPv4 address (if available)
Definition ui/mdns.h:61
char ipv6[46]
IPv6 address (if available)
Definition ui/mdns.h:62
uint16_t port
Server port number.
Definition ui/mdns.h:60
char address[256]
Server address (IPv4, IPv6, or hostname)
Definition ui/mdns.h:59
โฑ๏ธ High-precision timing utilities using sokol_time.h and uthash
mDNS service discovery UI with terminal_screen rendering