ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
discovery.c File Reference

Parallel mDNS and ACDS session discovery implementation. More...

Go to the source code of this file.

Data Structures

struct  discovery_thread_state_t
 Thread-safe result sharing between discovery threads. More...
 
struct  mdns_query_state_t
 Internal state for collecting discovered services. More...
 
struct  mdns_thread_context_t
 Context for mDNS discovery thread. More...
 
struct  acds_thread_context_t
 Context for ACDS discovery thread. More...
 

Functions

void pubkey_to_hex (const uint8_t pubkey[32], char hex_out[65])
 
asciichat_error_t hex_to_pubkey (const char *hex_str, uint8_t pubkey_out[32])
 
discovery_tui_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.
 
void discovery_mdns_destroy (discovery_tui_server_t *servers)
 Free memory from mDNS discovery results.
 
void discovery_config_init_defaults (discovery_config_t *config)
 
asciichat_error_t discover_session_parallel (const char *session_string, const discovery_config_t *config, discovery_result_t *result)
 

Detailed Description

Parallel mDNS and ACDS session discovery implementation.

This module provides automated discovery of ascii-chat servers via parallel mDNS (local LAN) and ACDS (internet) lookups.

Architecture:

  • mDNS discovery: discovery_mdns_query() - core implementation (this module)
  • ACDS discovery: ACDS client implementation for centralized lookup
  • Parallel coordination: Concurrent threads with "race to success" semantics
  • TUI wrapper: discovery_tui.c calls discovery_mdns_query() for interactive selection

Definition in file discovery.c.

Function Documentation

◆ discover_session_parallel()

asciichat_error_t discover_session_parallel ( const char *  session_string,
const discovery_config_t config,
discovery_result_t *  result 
)

Definition at line 518 of file discovery.c.

519 {
520 if (!session_string || !config || !result) {
521 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters to discover_session_parallel");
522 return ERROR_INVALID_PARAM;
523 }
524
525 // Validate session string format
526 if (!is_session_string(session_string)) {
527 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid session string format");
528 return ERROR_INVALID_PARAM;
529 }
530
531 memset(result, 0, sizeof(*result));
532 log_info("Discovery: Looking up session '%s'", session_string);
533
534 // Initialize thread state
536 memset(&state, 0, sizeof(state));
537 state.result = result;
538 mutex_init(&state.lock);
539 cond_init(&state.signal);
540
541 // Determine which discovery methods to use
542 bool use_mdns = true;
543 bool use_acds = config->expected_pubkey != NULL || config->insecure_mode;
544
545 if (!use_acds) {
546 log_debug("Discovery: mDNS-only mode (no --server-key and no --acds-insecure)");
547 }
548
549 // Spawn mDNS thread
550 asciichat_thread_t mdns_thread;
551 asciichat_thread_init(&mdns_thread);
552
553 if (use_mdns) {
554 mdns_thread_context_t *mdns_ctx = SAFE_MALLOC(sizeof(*mdns_ctx), mdns_thread_context_t *);
555 if (!mdns_ctx) {
556 SET_ERRNO(ERROR_MEMORY, "Failed to allocate mDNS context");
557 mutex_destroy(&state.lock);
558 cond_destroy(&state.signal);
559 return ERROR_MEMORY;
560 }
561
562 mdns_ctx->session_string = session_string;
563 mdns_ctx->state = &state;
564 mdns_ctx->expected_pubkey = config->expected_pubkey;
565
566 int thread_err = asciichat_thread_create(&mdns_thread, mdns_thread_fn, mdns_ctx);
567 if (thread_err != 0) {
568 log_warn("Discovery: Failed to spawn mDNS thread");
569 SAFE_FREE(mdns_ctx);
570 use_mdns = false;
571 }
572 }
573
574 // Spawn ACDS thread
575 asciichat_thread_t acds_thread;
576 asciichat_thread_init(&acds_thread);
577
578 if (use_acds) {
579 acds_thread_context_t *acds_ctx = SAFE_MALLOC(sizeof(*acds_ctx), acds_thread_context_t *);
580 if (!acds_ctx) {
581 SET_ERRNO(ERROR_MEMORY, "Failed to allocate ACDS context");
582 if (use_mdns && asciichat_thread_is_initialized(&mdns_thread)) {
583 asciichat_thread_join(&mdns_thread, NULL);
584 }
585 mutex_destroy(&state.lock);
586 cond_destroy(&state.signal);
587 return ERROR_MEMORY;
588 }
589
590 acds_ctx->session_string = session_string;
591 acds_ctx->state = &state;
592 acds_ctx->config = config;
593
594 int thread_err = asciichat_thread_create(&acds_thread, acds_thread_fn, acds_ctx);
595 if (thread_err != 0) {
596 log_warn("Discovery: Failed to spawn ACDS thread");
597 SAFE_FREE(acds_ctx);
598 use_acds = false;
599 }
600 }
601
602 // Wait for result (mDNS timeout 2s + ACDS timeout 5s max)
603 uint32_t wait_timeout_ms = config->acds_timeout_ms + 1000;
604 mutex_lock(&state.lock);
605 {
606 uint32_t elapsed_ms = 0;
607 while (!state.found && elapsed_ms < wait_timeout_ms) {
608 // Check if both threads are done
609 if (state.mdns_done && state.acds_done) {
610 break;
611 }
612
613 // Wait with timeout
614 cond_timedwait(&state.signal, &state.lock, 500 * NS_PER_MS_INT);
615 elapsed_ms += 500;
616 }
617 }
618 mutex_unlock(&state.lock);
619
620 // Join threads
621 if (use_mdns && asciichat_thread_is_initialized(&mdns_thread)) {
622 asciichat_thread_join(&mdns_thread, NULL);
623 }
624 if (use_acds && asciichat_thread_is_initialized(&acds_thread)) {
625 asciichat_thread_join(&acds_thread, NULL);
626 }
627
628 // Cleanup
629 mutex_destroy(&state.lock);
630 cond_destroy(&state.signal);
631
632 // Check result
633 if (!result->success) {
634 SET_ERRNO(ERROR_NOT_FOUND, "Session '%s' not found (mDNS/ACDS timeout)", session_string);
635 return ERROR_NOT_FOUND;
636 }
637
638 log_info("Discovery: Session '%s' discovered via %s", session_string,
639 result->source == DISCOVERY_SOURCE_MDNS ? "mDNS" : "ACDS");
640
641 return ASCIICHAT_OK;
642}
bool is_session_string(const char *str)
Context for ACDS discovery thread.
Definition discovery.c:358
discovery_thread_state_t * state
Shared discovery state.
Definition discovery.c:360
const discovery_config_t * config
Discovery configuration.
Definition discovery.c:361
const char * session_string
Session string to look up.
Definition discovery.c:359
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
Context for mDNS discovery thread.
Definition discovery.c:278
discovery_thread_state_t * state
Shared discovery state.
Definition discovery.c:280
const uint8_t * expected_pubkey
Expected server public key (optional)
Definition discovery.c:281
const char * session_string
Session string to discover.
Definition discovery.c:279
int mutex_init(mutex_t *mutex)
Definition threading.c:16
int asciichat_thread_create(asciichat_thread_t *thread, void *(*start_routine)(void *), void *arg)
Definition threading.c:42
int asciichat_thread_join(asciichat_thread_t *thread, void **retval)
Definition threading.c:46
int mutex_destroy(mutex_t *mutex)
Definition threading.c:21

References discovery_thread_state_t::acds_done, asciichat_thread_create(), asciichat_thread_join(), acds_thread_context_t::config, mdns_thread_context_t::expected_pubkey, discovery_thread_state_t::found, is_session_string(), discovery_thread_state_t::lock, discovery_thread_state_t::mdns_done, mutex_destroy(), mutex_init(), discovery_thread_state_t::result, mdns_thread_context_t::session_string, acds_thread_context_t::session_string, discovery_thread_state_t::signal, mdns_thread_context_t::state, and acds_thread_context_t::state.

◆ discovery_config_init_defaults()

void discovery_config_init_defaults ( discovery_config_t config)

Definition at line 496 of file discovery.c.

496 {
497 if (!config)
498 return;
499
500 memset(config, 0, sizeof(*config));
501
502 // Check if debug or release build
503#ifdef NDEBUG
504 // Release: use internet ACDS
505 SAFE_STRNCPY(config->acds_server, "discovery.ascii-chat.com", sizeof(config->acds_server) - 1);
506#else
507 // Debug: use local ACDS
508 SAFE_STRNCPY(config->acds_server, "127.0.0.1", sizeof(config->acds_server) - 1);
509#endif
510
511 config->acds_port = OPT_ACDS_PORT_INT_DEFAULT;
512 config->mdns_timeout_ms = 2 * MS_PER_SEC_INT;
513 config->acds_timeout_ms = 5 * MS_PER_SEC_INT;
514 config->insecure_mode = false;
515 config->expected_pubkey = NULL;
516}
uint16_t acds_port
ACDS port (default: 27225)
Definition session.h:214

References discovery_config_t::acds_port.

◆ discovery_mdns_destroy()

void discovery_mdns_destroy ( discovery_tui_server_t *  servers)

Free memory from mDNS discovery results.

Definition at line 267 of file discovery.c.

267 {
268 SAFE_FREE(servers);
269}

Referenced by discovery_tui_free_results().

◆ discovery_mdns_query()

discovery_tui_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.

Parameters
timeout_msQuery timeout in milliseconds
max_serversMaximum servers to discover
quietIf true, suppresses progress messages
out_countOutput: number of servers discovered
Returns
Array of discovered servers, or NULL on error. Use discovery_mdns_destroy() to free.

Definition at line 179 of file discovery.c.

179 {
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(discovery_tui_server_t), discovery_tui_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(discovery_tui_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 SAFE_FREE(state.servers);
220 return NULL;
221 }
222
223 // Start mDNS query for _ascii-chat._tcp services
224 asciichat_error_t query_result =
225 asciichat_mdns_query(mdns, "_ascii-chat._tcp.local", discovery_mdns_callback, &state);
226
227 if (query_result != ASCIICHAT_OK) {
228 log_info("mDNS: Query failed - no servers found via service discovery");
230 SAFE_FREE(state.servers);
231 return NULL;
232 }
233
234 // Poll for responses until timeout
235 int64_t deadline = state.start_time_ms + state.timeout_ms;
236 while (!state.query_complete && (int64_t)time_ns_to_ms(time_get_ns()) < deadline) {
237 int poll_timeout = (int)(deadline - (int64_t)time_ns_to_ms(time_get_ns()));
238 if (poll_timeout < 0) {
239 poll_timeout = 0;
240 }
241 if (poll_timeout > 100) {
242 poll_timeout = 100; // Check every 100ms
243 }
244 asciichat_mdns_update(mdns, poll_timeout);
245 }
246
247 // Cleanup mDNS
249
250 if (!quiet) {
251 if (state.count > 0) {
252 printf("✅ Found %d ascii-chat server%s on LAN\n", state.count, state.count == 1 ? "" : "s");
253 log_info("mDNS: Found %d server(s)", state.count);
254 } else {
255 printf("❌ No ascii-chat servers found on LAN\n");
256 log_info("mDNS: No servers found");
257 }
258 }
259
260 *out_count = state.count;
261 return state.servers;
262}
asciichat_error_t asciichat_mdns_update(asciichat_mdns_t *mdns, int timeout_ms)
Definition mdns.c:260
void asciichat_mdns_destroy(asciichat_mdns_t *mdns)
Definition mdns.c:66
asciichat_mdns_t * asciichat_mdns_init(void)
Definition mdns.c:35
asciichat_error_t asciichat_mdns_query(asciichat_mdns_t *mdns, const char *service_type, asciichat_mdns_discovery_callback_fn callback, void *user_data)
Definition mdns.c:224
Internal mDNS context structure.
Definition mdns.c:23
Internal state for collecting discovered services.
Definition discovery.c:98
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
discovery_tui_server_t * servers
Array of discovered servers.
Definition discovery.c:99
uint64_t time_get_ns(void)
Definition util/time.c:48

References asciichat_mdns_destroy(), asciichat_mdns_init(), asciichat_mdns_query(), asciichat_mdns_update(), mdns_query_state_t::capacity, mdns_query_state_t::count, mdns_query_state_t::query_complete, mdns_query_state_t::servers, mdns_query_state_t::start_time_ms, time_get_ns(), and mdns_query_state_t::timeout_ms.

Referenced by discovery_tui_query().

◆ hex_to_pubkey()

asciichat_error_t hex_to_pubkey ( const char *  hex_str,
uint8_t  pubkey_out[32] 
)

Definition at line 58 of file discovery.c.

58 {
59 if (!hex_str || !pubkey_out) {
60 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid hex_str or pubkey_out pointer");
61 return ERROR_INVALID_PARAM;
62 }
63
64 if (strlen(hex_str) != 64) {
65 SET_ERRNO(ERROR_INVALID_PARAM, "Hex string must be exactly 64 characters");
66 return ERROR_INVALID_PARAM;
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");
77 return ERROR_INVALID_PARAM;
78 }
79
80 pubkey_out[i] = (uint8_t)strtol(hex_byte, NULL, 16);
81 }
82
83 return ASCIICHAT_OK;
84}

◆ pubkey_to_hex()

void pubkey_to_hex ( const uint8_t  pubkey[32],
char  hex_out[65] 
)

Definition at line 49 of file discovery.c.

49 {
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}
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456

References safe_snprintf().

Referenced by client_crypto_handshake(), and server_crypto_handshake().