ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
Connection Management

πŸ”— Server connection establishment, packet transmission, and state tracking More...

Files

file  server.c
 πŸŒ Client connection manager: TCP connection, reconnection with exponential backoff, and thread-safe transmission
 
file  server.h
 ascii-chat Client Server Connection Management Interface
 

Functions

int server_connection_init ()
 Initialize the server connection management subsystem.
 
int server_connection_establish (const char *address, int port, int reconnect_attempt, bool first_connection, bool has_ever_connected)
 Establish connection to ascii-chat server.
 
bool server_connection_is_active ()
 Check if server connection is currently active.
 
socket_t server_connection_get_socket ()
 Get current socket file descriptor.
 
acip_transport_tserver_connection_get_transport (void)
 Get ACIP transport instance.
 
void server_connection_set_transport (acip_transport_t *transport)
 Set ACIP transport instance from connection fallback.
 
uint32_t server_connection_get_client_id ()
 Get client ID assigned by server.
 
const char * server_connection_get_ip ()
 Get resolved server IP address.
 
void server_connection_set_ip (const char *ip)
 Set the server IP address.
 
void server_connection_close ()
 Close the server connection gracefully.
 
void server_connection_shutdown ()
 Emergency connection shutdown for signal handlers.
 
void server_connection_lost ()
 Signal that connection has been lost.
 
bool server_connection_is_lost ()
 Check if connection loss has been detected.
 
void server_connection_cleanup ()
 Cleanup connection management subsystem.
 
asciichat_error_t threaded_send_packet (packet_type_t type, const void *data, size_t len)
 Thread-safe packet transmission.
 
int threaded_send_audio_batch_packet (const float *samples, int num_samples, int batch_count)
 Thread-safe batched audio packet transmission.
 
asciichat_error_t threaded_send_audio_opus (const uint8_t *opus_data, size_t opus_size, int sample_rate, int frame_duration)
 Thread-safe Opus audio frame transmission.
 
asciichat_error_t threaded_send_audio_opus_batch (const uint8_t *opus_data, size_t opus_size, const uint16_t *frame_sizes, int frame_count)
 Thread-safe Opus audio batch packet transmission.
 
int threaded_send_ping_packet (void)
 Thread-safe ping packet transmission.
 
int threaded_send_pong_packet (void)
 Thread-safe pong packet transmission.
 
int threaded_send_stream_start_packet (uint32_t stream_type)
 Thread-safe stream start packet transmission.
 
int threaded_send_terminal_size_with_auto_detect (unsigned short width, unsigned short height)
 Thread-safe terminal size packet transmission with auto-detection.
 
int threaded_send_client_join_packet (const char *display_name, uint32_t capabilities)
 Thread-safe client join packet transmission.
 
int server_send_packet (packet_type_t type, const void *data, size_t len)
 Send general packet to server.
 
int server_send_audio (const float *samples, int num_samples)
 Send audio data to server.
 
int server_send_audio_batch (const float *samples, int num_samples, int batch_count)
 Send batched audio data to server.
 
int server_send_terminal_capabilities (unsigned short width, unsigned short height)
 Send terminal capabilities to server.
 
int server_send_ping ()
 Send ping keepalive packet.
 
int server_send_pong ()
 Send pong response packet.
 
int server_send_stream_start (uint32_t stream_type)
 Send stream start notification.
 
int server_send_stream_stop (uint32_t stream_type)
 Send stream stop notification.
 

Variables

crypto_handshake_context_t g_crypto_ctx = {0}
 Per-connection crypto handshake context.
 

Detailed Description

πŸ”— Server connection establishment, packet transmission, and state tracking

Connection Management

Overview

The connection management subsystem handles TCP socket creation, connection establishment, cryptographic handshake coordination, and thread-safe packet transmission to the server.

Implementation: src/client/server.c, src/client/server.h

Connection State

Global State Variables:

  • g_sockfd: Socket file descriptor
  • g_client_id: Server-assigned client ID
  • g_server_ip: Resolved server IP address
  • g_connection_active: Atomic boolean for connection status
  • g_connection_lost: Atomic boolean for connection loss detection
  • g_send_mutex: Mutex protecting all packet sends

Connection Establishment

Connection Flow

int server_connection_establish(const char *address, int port,
int reconnect_attempt, bool first_connection,
bool has_ever_connected)
{
// 1. Create socket
socket_t sockfd = socket(AF_INET, SOCK_STREAM, 0);
// 2. Resolve address (IPv4/IPv6)
struct addrinfo *result = resolve_address(address, port);
// 3. Connect with timeout
if (connect(sockfd, result->ai_addr, result->ai_addrlen) < 0) {
}
// 4. Perform cryptographic handshake
crypto_handshake_result_t handshake_result =
perform_crypto_handshake_client(sockfd, &crypto_ctx, opt_server_key,
server_ip, opt_client_key);
if (handshake_result != HANDSHAKE_SUCCESS &&
handshake_result != HANDSHAKE_WARNING_NO_CLIENT_AUTH) {
socket_close(sockfd);
return map_handshake_error(handshake_result);
}
// 5. Send client capabilities
send_client_capabilities();
// 6. Update global state
g_sockfd = sockfd;
atomic_store(&g_connection_active, true);
atomic_store(&g_connection_lost, false);
}
int server_connection_establish(const char *address, int port, int reconnect_attempt, bool first_connection, bool has_ever_connected)
Establish connection to ascii-chat server.
int socket_t
Socket handle type (POSIX: int)
Definition socket.h:50
int socket_close(socket_t sock)
Close a socket.
ASCIICHAT_API char opt_server_key[OPTIONS_BUFF_SIZE]
@ CONNECTION_ERROR_GENERIC
Generic error (retry allowed)
@ CONNECTION_SUCCESS
Connection established successfully.

Connection Error Codes

Error Types:

  • CONNECTION_SUCCESS (0): Connection established successfully
  • CONNECTION_WARNING_NO_CLIENT_AUTH (1): Connected but server not verifying client
  • CONNECTION_ERROR_GENERIC (-1): Network error (allow retry)
  • CONNECTION_ERROR_AUTH_FAILED (-2): Client authentication failed (no retry)
  • CONNECTION_ERROR_HOST_KEY_FAILED (-3): Server host key verification failed (no retry)

Packet Transmission

Thread-Safe Packet Send

int send_packet_to_server(packet_type_t type, const void *data, size_t data_len,
uint32_t client_id)
{
// Thread-safe send with global mutex
mutex_lock(&g_send_mutex);
// Check connection status
mutex_unlock(&g_send_mutex);
return -1;
}
// Send encrypted packet
int result = send_packet(g_sockfd, &crypto_ctx, type, data, data_len, client_id);
mutex_unlock(&g_send_mutex);
// Detect connection loss on send error
if (result < 0) {
}
return result;
}
bool server_connection_is_active()
Check if server connection is currently active.
void server_connection_lost()
Signal that connection has been lost.
unsigned int uint32_t
Definition common.h:58
int send_packet(socket_t sockfd, packet_type_t type, const void *data, size_t len)
Send a basic packet without encryption.
Definition packet.c:754
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:281
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
Definition mutex.h:140
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
Definition mutex.h:175

Thread Safety:

  • ALL packet sends MUST go through send_packet_to_server()
  • Global g_send_mutex prevents interleaved packets
  • Socket FD checked under mutex protection
  • Connection loss detected atomically

Connection Loss Detection

Loss Detection Triggers

Connection loss detected by:

  1. Socket write error during send_packet_to_server()
  2. Socket read error during receive_packet() in protocol thread
  3. Keepalive timeout (no pong for 30 seconds)
  4. Decryption failure (corrupted stream)

Loss Handling

{
// Atomic compare-exchange ensures only first detection triggers action
bool expected = true;
if (atomic_compare_exchange_strong(&g_connection_active, &expected, false)) {
// First thread to detect loss
atomic_store(&g_connection_lost, true);
log_warn("Connection lost detected");
}
}
#define log_warn(...)
Log a WARN message.

Connection Cleanup

Graceful Close

{
if (g_sockfd != INVALID_SOCKET_VALUE) {
socket_shutdown(g_sockfd, SHUT_RDWR); // Signal shutdown
socket_close(g_sockfd); // Close socket
}
atomic_store(&g_connection_active, false);
g_client_id = 0;
}
void server_connection_close()
Close the server connection gracefully.
int socket_shutdown(socket_t sock, int how)
Shutdown socket I/O.
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:52

Emergency Shutdown

{
// Signal-safe emergency shutdown
if (g_sockfd != INVALID_SOCKET_VALUE) {
socket_close(g_sockfd); // Immediate close (no shutdown)
}
}
void server_connection_shutdown()
Emergency connection shutdown for signal handlers.

Best Practices

DO:

  • Always use send_packet_to_server() for packet transmission
  • Check server_connection_is_active() before operations
  • Use atomic operations for connection state flags
  • Wait for thread exit before reconnection

DON'T:

  • Don't send packets without g_send_mutex
  • Don't close socket while threads may be using it
  • Don't ignore return values from send operations
  • Don't modify g_sockfd directly (use provided functions)
See also
src/client/server.c
src/client/server.h
Client Overview

Function Documentation

◆ server_connection_cleanup()

void server_connection_cleanup ( )

#include <server.c>

Cleanup connection management subsystem.

Closes any active connection and destroys synchronization objects. Called during client shutdown.

Definition at line 858 of file src/client/server.c.

858 {
861 mutex_destroy(&g_send_mutex);
862}
void log_set_terminal_output(bool enabled)
Control stderr output to terminal.
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.

References log_set_terminal_output(), mutex_destroy(), and server_connection_close().

◆ server_connection_close()

void server_connection_close ( )

#include <server.c>

Close the server connection gracefully.

Close server connection gracefully.

Marks connection as inactive and closes the socket. This function is safe to call multiple times and from multiple threads.

Definition at line 765 of file src/client/server.c.

765 {
766 atomic_store(&g_connection_active, false);
767
768 // Destroy ACIP transport before closing socket
769 if (g_client_transport) {
770 acip_transport_destroy(g_client_transport);
771 g_client_transport = NULL;
772 }
773
774 if (g_sockfd != INVALID_SOCKET_VALUE) {
775 close_socket(g_sockfd);
776 g_sockfd = INVALID_SOCKET_VALUE;
777 }
778
779 g_my_client_id = 0;
780
781 // Cleanup crypto context if encryption was enabled
782 if (g_encryption_enabled) {
784 g_encryption_enabled = false;
785 }
786
787 // Turn ON terminal logging when connection is closed
789}
void crypto_handshake_cleanup(crypto_handshake_context_t *ctx)
Cleanup crypto handshake context with secure memory wiping.
crypto_handshake_context_t g_crypto_ctx
Per-connection crypto handshake context.
#define close_socket
void acip_transport_destroy(acip_transport_t *transport)
Destroy transport and free all resources.

References acip_transport_destroy(), close_socket, crypto_handshake_cleanup(), g_crypto_ctx, INVALID_SOCKET_VALUE, and log_set_terminal_output().

Referenced by client_main(), and server_connection_cleanup().

◆ server_connection_establish()

int server_connection_establish ( const char *  address,
int  port,
int  reconnect_attempt,
bool  first_connection,
bool  has_ever_connected 
)

#include <server.c>

Establish connection to ascii-chat server.

Attempts to connect to the specified server with full capability negotiation. Implements reconnection logic with exponential backoff for failed attempts. On successful connection, performs initial handshake including terminal capabilities and client join protocol.

Parameters
addressServer IP address or hostname
portServer port number
reconnect_attemptCurrent reconnection attempt number (0 for first)
first_connectionTrue if this is the initial connection attempt
has_ever_connectedTrue if a connection was ever successfully established
Returns
0 on success, negative on error
Parameters
addressServer IP address or hostname
portServer port number
reconnect_attemptCurrent reconnection attempt (0 for first)
first_connectionTrue if this is the initial connection
has_ever_connectedTrue if a connection was ever successfully established
Returns
0 on success, negative on error

Definition at line 322 of file src/client/server.c.

323 {
324 (void)first_connection; // Currently unused
325 if (!address || port <= 0) {
326 log_error("Invalid address or port parameters");
327 return -1;
328 }
329
330 // Close any existing connection
331 if (g_sockfd != INVALID_SOCKET_VALUE) {
332 close_socket(g_sockfd);
333 g_sockfd = INVALID_SOCKET_VALUE;
334 }
335
336 // Apply reconnection delay if this is a retry
337 if (reconnect_attempt > 0) {
338 unsigned int delay_us = get_reconnect_delay(reconnect_attempt);
339 // Reconnection attempt logged only to file
340 platform_sleep_usec(delay_us);
341
342 // Check if user requested exit during reconnection delay
343 if (should_exit()) {
344 log_debug("Exit requested during reconnection delay");
345 return -1;
346 }
347 } else {
348 // Initial connection logged only to file
349 }
350
351 // Resolve server address using getaddrinfo() for IPv4/IPv6 support
352 // Special handling for localhost: ensure we try both IPv6 (::1) and IPv4 (127.0.0.1)
353 // Many systems only map "localhost" to 127.0.0.1 in /etc/hosts
354 bool is_localhost =
355 (strcmp(address, "localhost") == 0 || strcmp(address, "127.0.0.1") == 0 || strcmp(address, "::1") == 0);
356
357 struct addrinfo hints, *res = NULL, *addr_iter;
358 memset(&hints, 0, sizeof(hints));
359 hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
360 hints.ai_socktype = SOCK_STREAM;
361 if (is_localhost) {
362 hints.ai_flags = AI_NUMERICSERV; // Optimize for localhost
363 }
364
365 char port_str[16];
366 SAFE_SNPRINTF(port_str, sizeof(port_str), "%d", port);
367
368 // For localhost, try IPv6 loopback (::1) first, then fall back to DNS resolution
369 if (is_localhost) {
370 log_debug("Localhost detected - trying IPv6 loopback [::1]:%s first...", port_str);
371 hints.ai_family = AF_INET6;
372 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
373
374 int ipv6_result = getaddrinfo("::1", port_str, &hints, &res);
375 if (ipv6_result == 0 && res != NULL) {
376 // Try IPv6 loopback connection
377 g_sockfd = socket_create(res->ai_family, res->ai_socktype, res->ai_protocol);
378 if (g_sockfd != INVALID_SOCKET_VALUE) {
379 log_info("Trying IPv6 loopback connection to [::1]:%s...", port_str);
380 if (connect_with_timeout(g_sockfd, res->ai_addr, res->ai_addrlen, CONNECT_TIMEOUT)) {
381 log_debug("Connection successful using IPv6 loopback");
382 SAFE_STRNCPY(g_server_ip, "::1", sizeof(g_server_ip));
383 freeaddrinfo(res);
384 res = NULL; // Prevent double-free at connection_success label
385 goto connection_success;
386 }
387 if (socket_get_error(g_sockfd) != 0) {
388 log_debug("NETWORK_ERROR: %d", (int)socket_get_error(g_sockfd));
389 } else {
390 // log_debug("IPv6 loopback connection failed: %s", network_error_string());
391 }
392 close_socket(g_sockfd);
393 g_sockfd = INVALID_SOCKET_VALUE;
394 }
395 freeaddrinfo(res);
396 res = NULL;
397 }
398
399 // Check if user requested exit (Ctrl-C) before trying IPv4
400 if (should_exit()) {
401 log_debug("Exit requested during connection attempt");
402 return -1;
403 }
404
405 // IPv6 failed, try IPv4 loopback (127.0.0.1)
406 log_debug("IPv6 failed, trying IPv4 loopback 127.0.0.1:%s...", port_str);
407 hints.ai_family = AF_INET;
408
409 int ipv4_result = getaddrinfo("127.0.0.1", port_str, &hints, &res);
410 if (ipv4_result == 0 && res != NULL) {
411 g_sockfd = socket_create(res->ai_family, res->ai_socktype, res->ai_protocol);
412 if (g_sockfd != INVALID_SOCKET_VALUE) {
413 log_info("Trying IPv4 loopback connection to 127.0.0.1:%s...", port_str);
414 if (connect_with_timeout(g_sockfd, res->ai_addr, res->ai_addrlen, CONNECT_TIMEOUT)) {
415 log_debug("Connection successful using IPv4 loopback");
416 SAFE_STRNCPY(g_server_ip, "127.0.0.1", sizeof(g_server_ip));
417 freeaddrinfo(res);
418 res = NULL; // Prevent double-free at connection_success label
419 goto connection_success;
420 }
421 if (socket_get_error(g_sockfd) != 0) {
422 log_debug("NETWORK_ERROR: %d", (int)socket_get_error(g_sockfd));
423 } else {
424 // log_debug("IPv4 loopback connection failed: %s", network_error_string());
425 }
426 close_socket(g_sockfd);
427 g_sockfd = INVALID_SOCKET_VALUE;
428 }
429 freeaddrinfo(res);
430 res = NULL;
431 }
432
433 // Both IPv6 and IPv4 loopback failed for localhost
434 log_warn("Could not connect to localhost using either IPv6 or IPv4 loopback");
435 return -1;
436 }
437
438 // For non-localhost addresses, use standard resolution
439 log_debug("Resolving server address '%s' port %s...", address, port_str);
440 hints.ai_family = AF_UNSPEC;
441 hints.ai_flags = 0;
442 int getaddr_result = getaddrinfo(address, port_str, &hints, &res);
443 if (getaddr_result != 0) {
444 log_error("Failed to resolve server address '%s': %s", address, gai_strerror(getaddr_result));
445 return -1;
446 }
447
448 // Try each address returned by getaddrinfo() until one succeeds
449 // Prefer IPv6 over IPv4: try IPv6 addresses first, then fall back to IPv4
450 for (int address_family = AF_INET6; address_family >= AF_INET; address_family -= (AF_INET6 - AF_INET)) {
451 for (addr_iter = res; addr_iter != NULL; addr_iter = addr_iter->ai_next) {
452 // Skip addresses that don't match current pass (IPv6 first, then IPv4)
453 if (addr_iter->ai_family != address_family) {
454 continue;
455 }
456
457 // Create socket with appropriate address family
458 g_sockfd = socket_create(addr_iter->ai_family, addr_iter->ai_socktype, addr_iter->ai_protocol);
459 if (g_sockfd == INVALID_SOCKET_VALUE) {
460 log_debug("Could not create socket for address family %d: %s", addr_iter->ai_family, network_error_string());
461 continue; // Try next address
462 }
463
464 // Log which address family we're trying
465 if (addr_iter->ai_family == AF_INET) {
466 log_debug("Trying IPv4 connection...");
467 } else if (addr_iter->ai_family == AF_INET6) {
468 log_debug("Trying IPv6 connection...");
469 }
470
471 // Attempt connection with timeout
472 if (connect_with_timeout(g_sockfd, addr_iter->ai_addr, addr_iter->ai_addrlen, CONNECT_TIMEOUT)) {
473 // Connection successful!
474 log_debug("Connection successful using %s", addr_iter->ai_family == AF_INET ? "IPv4"
475 : addr_iter->ai_family == AF_INET6 ? "IPv6"
476 : "unknown protocol");
477
478 // Extract server IP address for known_hosts
479 if (format_ip_address(addr_iter->ai_family, addr_iter->ai_addr, g_server_ip, sizeof(g_server_ip)) ==
480 ASCIICHAT_OK) {
481 log_debug("Resolved server IP: %s", g_server_ip);
482 } else {
483 log_warn("Failed to format server IP address");
484 }
485
486 goto connection_success; // Break out of both loops
487 }
488
489 // Connection failed - close socket and try next address
490 if (socket_get_error(g_sockfd) != 0) {
491 log_debug("NETWORK_ERROR: %d", (int)socket_get_error(g_sockfd));
492 } else {
493 // log_debug("Connection failed: %s", network_error_string());
494 }
495 close_socket(g_sockfd);
496 g_sockfd = INVALID_SOCKET_VALUE;
497 }
498 }
499
500connection_success:
501
502 if (res) {
503 freeaddrinfo(res);
504 }
505
506 // If we exhausted all addresses without success, fail
507 if (g_sockfd == INVALID_SOCKET_VALUE) {
508 log_warn("Could not connect to server %s:%d (tried all addresses)", address, port);
509 return -1;
510 }
511
512 // Connection successful - extract local port for client ID
513 struct sockaddr_storage local_addr = {0};
514 socklen_t addr_len = sizeof(local_addr);
515 if (getsockname(g_sockfd, (struct sockaddr *)&local_addr, &addr_len) == -1) {
516 log_error("Failed to get local socket address: %s", network_error_string());
517 close_socket(g_sockfd);
518 g_sockfd = INVALID_SOCKET_VALUE;
519 return -1;
520 }
521
522 // Extract port from either IPv4 or IPv6 address
523 int local_port = 0;
524 if (((struct sockaddr *)&local_addr)->sa_family == AF_INET) {
525 local_port = NET_TO_HOST_U16(((struct sockaddr_in *)&local_addr)->sin_port);
526 } else if (((struct sockaddr *)&local_addr)->sa_family == AF_INET6) {
527 local_port = NET_TO_HOST_U16(((struct sockaddr_in6 *)&local_addr)->sin6_port);
528 }
529 g_my_client_id = (uint32_t)local_port;
530
531 // Mark connection as active immediately after successful socket connection
532 atomic_store(&g_connection_active, true);
533 atomic_store(&g_connection_lost, false);
534 atomic_store(&g_should_reconnect, false);
535
536 // Initialize crypto BEFORE starting protocol handshake
537 // Note: server IP is already set above in the connection loop
538 log_debug("CLIENT_CONNECT: Calling client_crypto_init()");
539 if (client_crypto_init() != 0) {
540 log_error("Failed to initialize crypto (password required or incorrect)");
541 log_debug("CLIENT_CONNECT: client_crypto_init() failed");
542 close_socket(g_sockfd);
543 g_sockfd = INVALID_SOCKET_VALUE;
544 return CONNECTION_ERROR_AUTH_FAILED; // SSH key password was wrong - no retry
545 }
546
547 // Perform crypto handshake if encryption is enabled
548 log_debug("CLIENT_CONNECT: Calling client_crypto_handshake()");
549 int handshake_result = client_crypto_handshake(g_sockfd);
550 if (handshake_result != 0) {
551 log_error("Crypto handshake failed");
552 log_debug("CLIENT_CONNECT: client_crypto_handshake() failed with code %d", handshake_result);
553 close_socket(g_sockfd);
554 g_sockfd = INVALID_SOCKET_VALUE;
556 "Crypto handshake failed with server - this usually indicates a protocol mismatch or network issue");
557 }
558 log_debug("CLIENT_CONNECT: client_crypto_handshake() succeeded");
559
560 // Create ACIP transport for protocol-agnostic packet sending
561 // The transport wraps the socket with encryption context from the handshake
563 g_client_transport = acip_tcp_transport_create(g_sockfd, (crypto_context_t *)crypto_ctx);
564 if (!g_client_transport) {
565 log_error("Failed to create ACIP transport");
566 close_socket(g_sockfd);
567 g_sockfd = INVALID_SOCKET_VALUE;
568 return -1;
569 }
570 log_debug("CLIENT_CONNECT: Created ACIP transport with crypto context");
571
572 // Turn OFF terminal logging when successfully connected to server
573 // First connection - we'll disable logging after main.c shows the "Connected successfully" message
574 if (!GET_OPTION(snapshot_mode)) {
575 log_debug("Connected to server - terminal logging will be disabled after initial setup");
576 } else {
577 log_debug("Connected to server - terminal logging kept enabled for snapshot mode");
578 }
579
580 // Configure socket options for optimal performance
581 if (socket_set_keepalive(g_sockfd, true) < 0) {
582 log_warn("Failed to set socket keepalive: %s", network_error_string());
583 }
584
585 // Configure socket buffers and TCP_NODELAY for optimal performance
586 asciichat_error_t sock_config_result = socket_configure_buffers(g_sockfd);
587 if (sock_config_result != ASCIICHAT_OK) {
588 log_warn("Failed to configure socket: %s", network_error_string());
589 }
590
591 // Send initial terminal capabilities to server (this may generate debug logs)
593 if (result < 0) {
594 log_error("Failed to send initial capabilities to server: %s", network_error_string());
595 close_socket(g_sockfd);
596 g_sockfd = INVALID_SOCKET_VALUE;
597 return -1;
598 }
599
600 // Now disable terminal logging after capabilities are sent (for reconnections)
601 if (!GET_OPTION(snapshot_mode) && has_ever_connected) {
603 log_debug("Reconnected to server - terminal logging disabled to prevent interference with ASCII display");
604 }
605
606 // Send client join packet for multi-user support
607 uint32_t my_capabilities = CLIENT_CAP_VIDEO; // Basic video capability
608 log_info("GET_OPTION(audio_enabled) = %d (sending CLIENT_JOIN)", GET_OPTION(audio_enabled));
609 if (GET_OPTION(audio_enabled)) {
610 log_info("Adding CLIENT_CAP_AUDIO to capabilities");
611 my_capabilities |= CLIENT_CAP_AUDIO;
612 }
613 if (GET_OPTION(color_mode) != COLOR_MODE_NONE) {
614 my_capabilities |= CLIENT_CAP_COLOR;
615 }
616 if (GET_OPTION(stretch)) {
617 my_capabilities |= CLIENT_CAP_STRETCH;
618 }
619
620 // Generate display name from username + PID
621 const char *display_name = platform_get_username();
622
623 char my_display_name[MAX_DISPLAY_NAME_LEN];
624 int pid = getpid();
625 SAFE_SNPRINTF(my_display_name, sizeof(my_display_name), "%s-%d", display_name, pid);
626
627 if (threaded_send_client_join_packet(my_display_name, my_capabilities) < 0) {
628 log_error("Failed to send client join packet: %s", network_error_string());
629 close_socket(g_sockfd);
630 g_sockfd = INVALID_SOCKET_VALUE;
631 return -1;
632 }
633
634 // Connection already marked as active after socket creation
635
636 return 0;
637}
bool should_exit()
Check if client should exit.
#define NET_TO_HOST_U16(val)
Definition endian.h:116
int threaded_send_terminal_size_with_auto_detect(unsigned short width, unsigned short height)
Thread-safe terminal size packet transmission with auto-detection.
int threaded_send_client_join_packet(const char *display_name, uint32_t capabilities)
Thread-safe client join packet transmission.
const crypto_context_t * crypto_client_get_context(void)
Get crypto context for encryption/decryption.
int client_crypto_init(void)
Initialize client crypto handshake.
int client_crypto_handshake(socket_t socket)
Perform crypto handshake with server.
bool crypto_client_is_ready(void)
Check if crypto handshake is ready.
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:412
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
Definition common.h:151
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ERROR_CRYPTO_HANDSHAKE
Definition error_codes.h:91
@ ASCIICHAT_OK
Definition error_codes.h:48
#define MAX_DISPLAY_NAME_LEN
Maximum display name length in characters.
Definition limits.h:20
#define log_error(...)
Log an ERROR message.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
#define CONNECT_TIMEOUT
Connection timeout in seconds (3 seconds)
Definition network.h:98
#define CLIENT_CAP_STRETCH
Client can stretch frames to fill terminal.
Definition packet.h:818
bool connect_with_timeout(socket_t sockfd, const struct sockaddr *addr, socklen_t addrlen, int timeout_seconds)
Connect with timeout.
Definition network.c:547
#define CLIENT_CAP_COLOR
Client supports color rendering.
Definition packet.h:817
const char * network_error_string()
Get human-readable error string for network errors.
Definition network.c:535
asciichat_error_t socket_configure_buffers(socket_t sockfd)
Configure socket buffers and TCP_NODELAY for optimal performance.
Definition network.c:493
#define CLIENT_CAP_AUDIO
Client can send/receive audio.
Definition packet.h:816
#define CLIENT_CAP_VIDEO
Client can send/receive video.
Definition packet.h:815
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
Definition options.h:644
#define COLOR_MODE_NONE
Monochrome mode.
Definition options.h:157
int socket_set_keepalive(socket_t sock, bool keepalive)
Set SO_KEEPALIVE socket option.
int socket_get_error(socket_t sock)
Get socket-specific error code.
void platform_sleep_usec(unsigned int usec)
High-precision sleep function with microsecond precision.
socket_t socket_create(int domain, int type, int protocol)
Create a new socket.
const char * platform_get_username(void)
Get the current username.
asciichat_error_t format_ip_address(int family, const struct sockaddr *addr, char *output, size_t output_size)
Format IP address from socket address structure.
Definition ip.c:205
@ CONNECTION_ERROR_AUTH_FAILED
Authentication failure (no retry)
Cryptographic context structure.
acip_transport_t * acip_tcp_transport_create(socket_t sockfd, crypto_context_t *crypto_ctx)
Create TCP transport from existing socket.

References acip_tcp_transport_create(), ASCIICHAT_OK, CLIENT_CAP_AUDIO, CLIENT_CAP_COLOR, CLIENT_CAP_STRETCH, CLIENT_CAP_VIDEO, client_crypto_handshake(), client_crypto_init(), close_socket, COLOR_MODE_NONE, CONNECT_TIMEOUT, connect_with_timeout(), CONNECTION_ERROR_AUTH_FAILED, crypto_client_get_context(), crypto_client_is_ready(), ERROR_CRYPTO_HANDSHAKE, FATAL, format_ip_address(), GET_OPTION, INVALID_SOCKET_VALUE, log_debug, log_error, log_info, log_set_terminal_output(), log_warn, MAX_DISPLAY_NAME_LEN, NET_TO_HOST_U16, network_error_string(), platform_get_username(), platform_sleep_usec(), SAFE_SNPRINTF, SAFE_STRNCPY, should_exit(), socket_configure_buffers(), socket_create(), socket_get_error(), socket_set_keepalive(), threaded_send_client_join_packet(), and threaded_send_terminal_size_with_auto_detect().

◆ server_connection_get_client_id()

uint32_t server_connection_get_client_id ( )

#include <server.c>

Get client ID assigned by server.

Returns
Client ID (based on local port) or 0 if not connected
Client ID or 0 if not connected

Definition at line 719 of file src/client/server.c.

719 {
720 return g_my_client_id;
721}

◆ server_connection_get_ip()

const char * server_connection_get_ip ( )

#include <server.c>

Get resolved server IP address.

Returns the server's IP address that was resolved during connection establishment. Used for known_hosts verification and logging.

Returns
Server IP address string (IPv4 or IPv6), or empty string if not connected
Server IP address string (IPv4 or IPv6)

Definition at line 733 of file src/client/server.c.

733 {
734 return g_server_ip;
735}

Referenced by client_crypto_init().

◆ server_connection_get_socket()

socket_t server_connection_get_socket ( )

#include <server.c>

Get current socket file descriptor.

Returns
Socket file descriptor or INVALID_SOCKET_VALUE if disconnected
Socket FD or INVALID_SOCKET_VALUE if disconnected

Definition at line 657 of file src/client/server.c.

657 {
658 return g_sockfd;
659}

Referenced by crypto_client_initiate_rekey(), crypto_client_send_rekey_complete(), crypto_client_send_rekey_response(), threaded_send_audio_batch_packet(), threaded_send_client_join_packet(), threaded_send_stream_start_packet(), and threaded_send_terminal_size_with_auto_detect().

◆ server_connection_get_transport()

struct acip_transport * server_connection_get_transport ( void  )

#include <server.c>

Get ACIP transport instance.

Returns
Transport instance or NULL if not connected

Definition at line 668 of file src/client/server.c.

668 {
669 return g_client_transport;
670}

◆ server_connection_init()

int server_connection_init ( )

#include <server.c>

Initialize the server connection management subsystem.

Initialize server connection management subsystem.

Sets up the send mutex and initializes connection state variables. Must be called once during client startup before any connection attempts.

Returns
0 on success, non-zero on failure
0 on success, negative on error

Definition at line 287 of file src/client/server.c.

287 {
288 // Initialize mutex for thread-safe packet sending
289 if (mutex_init(&g_send_mutex) != 0) {
290 log_error("Failed to initialize send mutex");
291 return -1;
292 }
293
294 // Initialize connection state
295 g_sockfd = INVALID_SOCKET_VALUE;
296 g_client_transport = NULL;
297 atomic_store(&g_connection_active, false);
298 atomic_store(&g_connection_lost, false);
299 atomic_store(&g_should_reconnect, false);
300 g_my_client_id = 0;
301
302 return 0;
303}
int mutex_init(mutex_t *mutex)
Initialize a mutex.

References INVALID_SOCKET_VALUE, log_error, and mutex_init().

◆ server_connection_is_active()

bool server_connection_is_active ( )

#include <server.c>

Check if server connection is currently active.

Returns
true if connection is active, false otherwise

Definition at line 646 of file src/client/server.c.

646 {
647 return atomic_load(&g_connection_active) && (g_sockfd != INVALID_SOCKET_VALUE);
648}

References INVALID_SOCKET_VALUE.

Referenced by __attribute__(), and client_main().

◆ server_connection_is_lost()

bool server_connection_is_lost ( )

#include <server.c>

Check if connection loss has been detected.

Check if connection loss was detected.

Returns
true if connection loss was flagged, false otherwise
true if connection lost, false otherwise

Definition at line 846 of file src/client/server.c.

846 {
847 return atomic_load(&g_connection_lost);
848}

Referenced by __attribute__(), and protocol_connection_lost().

◆ server_connection_lost()

void server_connection_lost ( )

#include <server.c>

Signal that connection has been lost.

Called by other modules (typically protocol handlers) when they detect connection failure. Triggers reconnection logic in main loop.

Definition at line 830 of file src/client/server.c.

830 {
831 atomic_store(&g_connection_lost, true);
832 atomic_store(&g_connection_active, false);
833
834 // Turn ON terminal logging when connection is lost
837}
void display_full_reset()
Perform full display reset.
Definition display.c:301

References display_full_reset(), and log_set_terminal_output().

Referenced by __attribute__(), threaded_send_audio_batch_packet(), threaded_send_audio_opus(), threaded_send_audio_opus_batch(), and threaded_send_packet().

◆ server_connection_set_ip()

void server_connection_set_ip ( const char *  ip)

#include <server.c>

Set the server IP address.

Updates the global server IP address. Used by new connection code paths that don't use the legacy server_connect() function.

Parameters
ipServer IP address string
ipServer IP address string

Definition at line 747 of file src/client/server.c.

747 {
748 if (ip) {
749 SAFE_STRNCPY(g_server_ip, ip, sizeof(g_server_ip));
750 log_debug("Server IP set to: %s", g_server_ip);
751 } else {
752 g_server_ip[0] = '\0';
753 log_debug("Server IP cleared");
754 }
755}

References log_debug, and SAFE_STRNCPY.

◆ server_connection_set_transport()

void server_connection_set_transport ( acip_transport_t transport)

#include <server.c>

Set ACIP transport instance from connection fallback.

Used to integrate the transport from the 3-stage connection fallback orchestrator (TCP β†’ STUN β†’ TURN) into the server connection management layer.

Parameters
transportTransport instance created by connection_attempt_with_fallback()
transportTransport instance created by connection_attempt_with_fallback()

Used to integrate the transport from the 3-stage connection fallback orchestrator (TCP β†’ STUN β†’ TURN) into the server connection management layer.

Definition at line 682 of file src/client/server.c.

682 {
683 log_debug("server_connection_set_transport() called with transport=%p", (void *)transport);
684
685 // Clean up any existing transport
686 if (g_client_transport) {
687 log_warn("Replacing existing transport with new fallback transport");
688 acip_transport_destroy(g_client_transport);
689 }
690
691 log_debug("Setting g_client_transport to %p", (void *)transport);
692 g_client_transport = transport;
693
694 // Mark connection as active when transport is set
695 if (transport) {
696 log_debug("Transport is non-NULL, extracting socket...");
697 // Extract socket from transport for backward compatibility with socket-based checks
698 g_sockfd = acip_transport_get_socket(transport);
699 log_debug("Socket extracted: %d", (int)g_sockfd);
700
701 atomic_store(&g_connection_active, true);
702 log_debug("Server connection transport set and marked active (sockfd=%d)", (int)g_sockfd);
703 } else {
704 g_sockfd = INVALID_SOCKET_VALUE;
705 atomic_store(&g_connection_active, false);
706 log_debug("Server connection transport cleared and marked inactive");
707 }
708
709 log_debug("server_connection_set_transport() completed");
710}

References acip_transport_destroy(), INVALID_SOCKET_VALUE, log_debug, and log_warn.

Referenced by client_main().

◆ server_connection_shutdown()

void server_connection_shutdown ( )

#include <server.c>

Emergency connection shutdown for signal handlers.

Emergency shutdown for signal handlers.

Performs immediate connection shutdown without waiting for graceful close procedures. Uses socket shutdown to interrupt any blocking recv() operations in other threads.

Definition at line 800 of file src/client/server.c.

800 {
801 // NOTE: This function may be called from:
802 // - Signal handlers on Unix (async-signal-safe context)
803 // - SetConsoleCtrlHandler callback thread on Windows (separate thread context)
804 // Only use atomic operations and simple system calls - NO mutex locks, NO malloc, NO logging.
805
806 atomic_store(&g_connection_active, false);
807 atomic_store(&g_connection_lost, true);
808
809 if (g_sockfd != INVALID_SOCKET_VALUE) {
810 // Only shutdown() the socket to interrupt blocking recv()/send() operations.
811 // Do NOT close() here - on Windows, closing the socket while another thread
812 // is using it is undefined behavior and can cause STATUS_STACK_BUFFER_OVERRUN.
813 // The actual socket close happens in server_connection_close() which is called
814 // from the main thread after worker threads have been joined.
815 socket_shutdown(g_sockfd, SHUT_RDWR);
816 }
817
818 // DO NOT call log_set_terminal_output() here - it uses mutex which is NOT async-signal-safe.
819 // The normal cleanup path in shutdown_client() will handle logging state.
820}

References INVALID_SOCKET_VALUE, and socket_shutdown().

Referenced by protocol_stop_connection().

◆ server_send_audio()

int server_send_audio ( const float *  samples,
int  num_samples 
)

#include <server.h>

Send audio data to server.

Parameters
samplesAudio sample buffer
num_samplesNumber of samples
Returns
0 on success, negative on error

◆ server_send_audio_batch()

int server_send_audio_batch ( const float *  samples,
int  num_samples,
int  batch_count 
)

#include <server.h>

Send batched audio data to server.

Parameters
samplesBatched audio samples
num_samplesTotal sample count
batch_countNumber of packets in batch
Returns
0 on success, negative on error

◆ server_send_packet()

int server_send_packet ( packet_type_t  type,
const void *  data,
size_t  len 
)

#include <server.h>

Send general packet to server.

Parameters
typePacket type identifier
dataPacket payload
lenPayload length
Returns
0 on success, negative on error

◆ server_send_ping()

int server_send_ping ( )

#include <server.h>

Send ping keepalive packet.

Returns
0 on success, negative on error

◆ server_send_pong()

int server_send_pong ( )

#include <server.h>

Send pong response packet.

Returns
0 on success, negative on error

◆ server_send_stream_start()

int server_send_stream_start ( uint32_t  stream_type)

#include <server.h>

Send stream start notification.

Parameters
stream_typeType of stream (audio/video)
Returns
0 on success, negative on error

◆ server_send_stream_stop()

int server_send_stream_stop ( uint32_t  stream_type)

#include <server.h>

Send stream stop notification.

Parameters
stream_typeType of stream (audio/video)
Returns
0 on success, negative on error

◆ server_send_terminal_capabilities()

int server_send_terminal_capabilities ( unsigned short  width,
unsigned short  height 
)

#include <server.h>

Send terminal capabilities to server.

Parameters
widthTerminal width in characters
heightTerminal height in characters
Returns
0 on success, negative on error

◆ threaded_send_audio_batch_packet()

int threaded_send_audio_batch_packet ( const float *  samples,
int  num_samples,
int  batch_count 
)

#include <server.c>

Thread-safe batched audio packet transmission.

Sends a batched audio packet to the server with proper mutex protection and connection state checking. Automatically handles encryption if crypto is ready.

Parameters
samplesAudio sample buffer containing batched samples
num_samplesTotal number of samples in the batch
batch_countNumber of audio chunks in this batch
Returns
0 on success, negative on error
Parameters
samplesAudio sample buffer containing batched samples
num_samplesTotal number of samples in the batch
batch_countNumber of audio chunks in this batch
Returns
0 on success, negative on error

Definition at line 922 of file src/client/server.c.

922 {
923 mutex_lock(&g_send_mutex);
924
925 // Recheck connection status INSIDE the mutex to prevent TOCTOU race
927 if (!atomic_load(&g_connection_active) || sockfd == INVALID_SOCKET_VALUE) {
928 mutex_unlock(&g_send_mutex);
929 return -1;
930 }
931
932 // Get crypto context if encryption is enabled
934 int result = send_audio_batch_packet(sockfd, samples, num_samples, batch_count, (crypto_context_t *)crypto_ctx);
935
936 mutex_unlock(&g_send_mutex);
937
938 // If send failed due to network error, signal connection loss
939 if (result < 0) {
941 }
942
943 return result;
944}
socket_t server_connection_get_socket()
Get current socket file descriptor.
asciichat_error_t send_audio_batch_packet(socket_t sockfd, const float *samples, int num_samples, int batch_count, crypto_context_t *crypto_ctx)
Send a batched audio packet with encryption support.
Definition packet.c:1072

References crypto_client_get_context(), crypto_client_is_ready(), INVALID_SOCKET_VALUE, mutex_lock, mutex_unlock, send_audio_batch_packet(), server_connection_get_socket(), and server_connection_lost().

◆ threaded_send_audio_opus()

asciichat_error_t threaded_send_audio_opus ( const uint8_t opus_data,
size_t  opus_size,
int  sample_rate,
int  frame_duration 
)

#include <server.c>

Thread-safe Opus audio frame transmission.

Sends a single Opus-encoded audio frame to the server with proper synchronization and encryption support.

Parameters
opus_dataOpus-encoded audio data
opus_sizeSize of encoded frame
sample_rateSample rate in Hz
frame_durationFrame duration in milliseconds
Returns
0 on success, negative on error

Sends a single Opus-encoded audio frame to the server with proper synchronization and encryption support.

Parameters
opus_dataOpus-encoded audio data
opus_sizeSize of encoded frame
sample_rateSample rate in Hz
frame_durationFrame duration in milliseconds
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 960 of file src/client/server.c.

961 {
962 mutex_lock(&g_send_mutex);
963
964 // Recheck connection status and transport INSIDE the mutex to prevent TOCTOU race
965 if (!atomic_load(&g_connection_active) || !g_client_transport) {
966 mutex_unlock(&g_send_mutex);
967 return SET_ERRNO(ERROR_NETWORK, "Connection not active or transport unavailable");
968 }
969
970 // Build Opus packet with header
971 size_t header_size = 16; // sample_rate (4), frame_duration (4), reserved (8)
972 size_t total_size = header_size + opus_size;
973 void *packet_data = buffer_pool_alloc(NULL, total_size);
974 if (!packet_data) {
975 mutex_unlock(&g_send_mutex);
976 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for Opus packet: %zu bytes", total_size);
977 }
978
979 // Write header in network byte order
980 uint8_t *buf = (uint8_t *)packet_data;
981 uint32_t sr = HOST_TO_NET_U32((uint32_t)sample_rate);
982 uint32_t fd = HOST_TO_NET_U32((uint32_t)frame_duration);
983 memcpy(buf, &sr, 4);
984 memcpy(buf + 4, &fd, 4);
985 memset(buf + 8, 0, 8); // Reserved
986
987 // Copy Opus data
988 memcpy(buf + header_size, opus_data, opus_size);
989
990 // Send packet via ACIP transport (handles encryption automatically)
991 asciichat_error_t result =
992 packet_send_via_transport(g_client_transport, PACKET_TYPE_AUDIO_OPUS, packet_data, total_size);
993
994 // Clean up
995 buffer_pool_free(NULL, packet_data, total_size);
996 mutex_unlock(&g_send_mutex);
997
998 // If send failed due to network error, signal connection loss
999 if (result != ASCIICHAT_OK) {
1001 return result;
1002 }
1003
1004 return ASCIICHAT_OK;
1005}
#define HOST_TO_NET_U32(val)
Definition endian.h:71
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
Free a buffer back to the pool (lock-free)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
@ ERROR_NETWORK
Definition error_codes.h:69
@ ERROR_MEMORY
Definition error_codes.h:53
@ PACKET_TYPE_AUDIO_OPUS
Opus-encoded single audio frame.
Definition packet.h:357
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:40

References ASCIICHAT_OK, buffer_pool_alloc(), buffer_pool_free(), ERROR_MEMORY, ERROR_NETWORK, HOST_TO_NET_U32, mutex_lock, mutex_unlock, packet_send_via_transport(), PACKET_TYPE_AUDIO_OPUS, server_connection_lost(), and SET_ERRNO.

◆ threaded_send_audio_opus_batch()

asciichat_error_t threaded_send_audio_opus_batch ( const uint8_t opus_data,
size_t  opus_size,
const uint16_t frame_sizes,
int  frame_count 
)

#include <server.c>

Thread-safe Opus audio batch packet transmission.

Sends a batch of Opus-encoded audio frames to the server with proper synchronization and encryption support.

Parameters
opus_dataOpus-encoded audio data (multiple frames concatenated)
opus_sizeTotal size of Opus data in bytes
frame_sizesArray of individual frame sizes (variable-length frames)
frame_countNumber of Opus frames in the batch
Returns
0 on success, negative on error

Sends a batch of Opus-encoded audio frames to the server with proper synchronization and encryption support.

Parameters
opus_dataOpus-encoded audio data (multiple frames concatenated)
opus_sizeTotal size of Opus data in bytes
frame_sizesArray of individual frame sizes (variable-length frames)
frame_countNumber of Opus frames in the batch
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 1021 of file src/client/server.c.

1022 {
1023 mutex_lock(&g_send_mutex);
1024
1025 // Recheck connection status and transport INSIDE the mutex to prevent TOCTOU race
1026 if (!atomic_load(&g_connection_active) || !g_client_transport) {
1027 mutex_unlock(&g_send_mutex);
1028 return SET_ERRNO(ERROR_NETWORK, "Connection not active or transport unavailable");
1029 }
1030
1031 // Opus uses 20ms frames at 48kHz (960 samples = 20ms)
1032 asciichat_error_t result =
1033 acip_send_audio_opus_batch(g_client_transport, opus_data, opus_size, frame_sizes, frame_count, 48000, 20);
1034
1035 mutex_unlock(&g_send_mutex);
1036
1037 // If send failed due to network error, signal connection loss
1038 if (result != ASCIICHAT_OK) {
1040 return result;
1041 }
1042
1043 return ASCIICHAT_OK;
1044}
asciichat_error_t acip_send_audio_opus_batch(acip_transport_t *transport, const void *opus_data, size_t opus_len, const uint16_t *frame_sizes, uint32_t frame_count, uint32_t sample_rate, uint32_t frame_duration)
Send batched Opus-encoded audio frames.
Definition send.c:130

References acip_send_audio_opus_batch(), ASCIICHAT_OK, ERROR_NETWORK, mutex_lock, mutex_unlock, server_connection_lost(), and SET_ERRNO.

◆ threaded_send_client_join_packet()

int threaded_send_client_join_packet ( const char *  display_name,
uint32_t  capabilities 
)

#include <server.c>

Thread-safe client join packet transmission.

Sends client join packet with display name and capabilities to the server.

Parameters
display_nameClient display name
capabilitiesClient capability flags
Returns
0 on success, negative on error
Parameters
display_nameClient display name
capabilitiesClient capability flags
Returns
0 on success, negative on error

Definition at line 1187 of file src/client/server.c.

1187 {
1189 if (!atomic_load(&g_connection_active) || sockfd == INVALID_SOCKET_VALUE) {
1190 return -1;
1191 }
1192
1193 // Build CLIENT_JOIN packet locally
1194 client_info_packet_t join_packet;
1195 SAFE_MEMSET(&join_packet, sizeof(join_packet), 0, sizeof(join_packet));
1196 join_packet.client_id = HOST_TO_NET_U32(0); // Will be assigned by server
1197 SAFE_SNPRINTF(join_packet.display_name, MAX_DISPLAY_NAME_LEN, "%s", display_name ? display_name : "Unknown");
1198 join_packet.capabilities = HOST_TO_NET_U32(capabilities);
1199
1200 // Use threaded_send_packet() which handles encryption
1201 int send_result = threaded_send_packet(PACKET_TYPE_CLIENT_JOIN, &join_packet, sizeof(join_packet));
1202 if (send_result == 0) {
1203 mutex_lock(&g_send_mutex);
1204 bool active = atomic_load(&g_connection_active);
1205 socket_t socket_snapshot = g_sockfd;
1206 const crypto_context_t *crypto_ctx = crypto_client_is_ready() ? crypto_client_get_context() : NULL;
1207 if (active && socket_snapshot != INVALID_SOCKET_VALUE) {
1208 (void)log_network_message(
1209 socket_snapshot, (const struct crypto_context_t *)crypto_ctx, LOG_INFO, REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER,
1210 "CLIENT_JOIN sent (display=\"%s\", capabilities=0x%x)", join_packet.display_name, capabilities);
1211 }
1212 mutex_unlock(&g_send_mutex);
1213 }
1214 return send_result;
1215}
asciichat_error_t threaded_send_packet(packet_type_t type, const void *data, size_t len)
Thread-safe packet transmission.
#define SAFE_MEMSET(dest, dest_size, ch, count)
Definition common.h:389
asciichat_error_t log_network_message(socket_t sockfd, const struct crypto_context_t *crypto_ctx, log_level_t level, remote_log_direction_t direction, const char *fmt,...)
Send a formatted log message over the network.
@ REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER
@ LOG_INFO
Definition log/logging.h:62
char display_name[MAX_DISPLAY_NAME_LEN]
User display name (null-terminated, max MAX_DISPLAY_NAME_LEN bytes)
Definition packet.h:549
uint32_t client_id
Unique client identifier (1-9, 0 = server)
Definition packet.h:547
uint32_t capabilities
Client capabilities bitmask (CLIENT_CAP_VIDEO | CLIENT_CAP_AUDIO | CLIENT_CAP_COLOR | CLIENT_CAP_STRE...
Definition packet.h:552
@ PACKET_TYPE_CLIENT_JOIN
Client announces capability to send media.
Definition packet.h:300
Client information packet structure.
Definition packet.h:545

References client_info_packet_t::capabilities, client_info_packet_t::client_id, crypto_client_get_context(), crypto_client_is_ready(), client_info_packet_t::display_name, HOST_TO_NET_U32, INVALID_SOCKET_VALUE, LOG_INFO, log_network_message(), MAX_DISPLAY_NAME_LEN, mutex_lock, mutex_unlock, PACKET_TYPE_CLIENT_JOIN, REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER, SAFE_MEMSET, SAFE_SNPRINTF, server_connection_get_socket(), and threaded_send_packet().

Referenced by server_connection_establish().

◆ threaded_send_packet()

asciichat_error_t threaded_send_packet ( packet_type_t  type,
const void *  data,
size_t  len 
)

#include <server.c>

Thread-safe packet transmission.

Sends a packet to the server with proper mutex protection and connection state checking. Automatically handles encryption if crypto is ready.

Parameters
typePacket type identifier
dataPacket payload
lenPayload length
Returns
0 on success, negative on error

Sends a packet to the server with proper mutex protection and connection state checking. Automatically handles encryption if crypto is ready.

Parameters
typePacket type identifier
dataPacket payload
lenPayload length
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 885 of file src/client/server.c.

885 {
886 mutex_lock(&g_send_mutex);
887
888 // Recheck connection status and transport INSIDE the mutex to prevent TOCTOU race
889 if (!atomic_load(&g_connection_active) || !g_client_transport) {
890 mutex_unlock(&g_send_mutex);
891 return SET_ERRNO(ERROR_NETWORK, "Connection not active or transport unavailable");
892 }
893
894 // Use ACIP transport which handles encryption and compression automatically
895 asciichat_error_t result = packet_send_via_transport(g_client_transport, type, data, len);
896
897 mutex_unlock(&g_send_mutex);
898
899 // If send failed due to network error, signal connection loss
900 if (result != ASCIICHAT_OK) {
902 return result;
903 }
904
905 return ASCIICHAT_OK;
906}

References ASCIICHAT_OK, ERROR_NETWORK, mutex_lock, mutex_unlock, packet_send_via_transport(), server_connection_lost(), and SET_ERRNO.

Referenced by threaded_send_client_join_packet(), threaded_send_ping_packet(), threaded_send_pong_packet(), threaded_send_stream_start_packet(), and threaded_send_terminal_size_with_auto_detect().

◆ threaded_send_ping_packet()

int threaded_send_ping_packet ( void  )

#include <server.c>

Thread-safe ping packet transmission.

Returns
0 on success, negative on error

Definition at line 1053 of file src/client/server.c.

1053 {
1054 // Use threaded_send_packet which handles encryption, mutex locking, and connection state
1055 return threaded_send_packet(PACKET_TYPE_PING, NULL, 0);
1056}
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:295

References PACKET_TYPE_PING, and threaded_send_packet().

Referenced by __attribute__().

◆ threaded_send_pong_packet()

int threaded_send_pong_packet ( void  )

#include <server.c>

Thread-safe pong packet transmission.

Returns
0 on success, negative on error

Definition at line 1065 of file src/client/server.c.

1065 {
1066 // Use threaded_send_packet which handles encryption, mutex locking, and connection state
1067 return threaded_send_packet(PACKET_TYPE_PONG, NULL, 0);
1068}
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:297

References PACKET_TYPE_PONG, and threaded_send_packet().

◆ threaded_send_stream_start_packet()

int threaded_send_stream_start_packet ( uint32_t  stream_type)

#include <server.c>

Thread-safe stream start packet transmission.

Parameters
stream_typeType of stream (audio/video)
Returns
0 on success, negative on error

Definition at line 1078 of file src/client/server.c.

1078 {
1080 if (!atomic_load(&g_connection_active) || sockfd == INVALID_SOCKET_VALUE) {
1081 return -1;
1082 }
1083
1084 // Build STREAM_START packet locally
1085 uint32_t type_data = HOST_TO_NET_U32(stream_type);
1086
1087 // Use threaded_send_packet() which handles encryption
1088 return threaded_send_packet(PACKET_TYPE_STREAM_START, &type_data, sizeof(type_data));
1089}
@ PACKET_TYPE_STREAM_START
Client requests to start sending video/audio.
Definition packet.h:304

References HOST_TO_NET_U32, INVALID_SOCKET_VALUE, PACKET_TYPE_STREAM_START, server_connection_get_socket(), and threaded_send_packet().

Referenced by audio_start_thread(), capture_start_thread(), and protocol_start_connection().

◆ threaded_send_terminal_size_with_auto_detect()

int threaded_send_terminal_size_with_auto_detect ( unsigned short  width,
unsigned short  height 
)

#include <server.c>

Thread-safe terminal size packet transmission with auto-detection.

Sends terminal capabilities packet to the server including terminal size, color capabilities, and rendering preferences. Auto-detects terminal capabilities if not explicitly specified.

Parameters
widthTerminal width in characters
heightTerminal height in characters
Returns
0 on success, negative on error
Parameters
widthTerminal width in characters
heightTerminal height in characters
Returns
0 on success, negative on error

Definition at line 1104 of file src/client/server.c.

1104 {
1105
1107 if (!atomic_load(&g_connection_active) || sockfd == INVALID_SOCKET_VALUE) {
1108 return -1;
1109 }
1110
1111 // Build terminal capabilities packet locally
1112 // Detect terminal capabilities automatically
1114
1115 // Apply user's color mode override
1116 caps = apply_color_mode_override(caps);
1117
1118 // Check if detection was reliable, use fallback only for auto-detection
1119 if (!caps.detection_reliable && GET_OPTION(color_mode) == COLOR_MODE_AUTO) {
1120 log_warn("Terminal capability detection not reliable, using fallback");
1121 SAFE_MEMSET(&caps, sizeof(caps), 0, sizeof(caps));
1123 caps.color_count = 2;
1124 caps.capabilities = 0;
1125 SAFE_STRNCPY(caps.term_type, "unknown", sizeof(caps.term_type));
1126 SAFE_STRNCPY(caps.colorterm, "", sizeof(caps.colorterm));
1127 caps.detection_reliable = 0;
1128 }
1129
1130 // Convert to network packet format with proper byte order
1132 net_packet.capabilities = HOST_TO_NET_U32(caps.capabilities);
1133 net_packet.color_level = HOST_TO_NET_U32(caps.color_level);
1134 net_packet.color_count = HOST_TO_NET_U32(caps.color_count);
1135 net_packet.render_mode = HOST_TO_NET_U32(caps.render_mode);
1136 net_packet.width = HOST_TO_NET_U16(width);
1137 net_packet.height = HOST_TO_NET_U16(height);
1138 net_packet.palette_type = HOST_TO_NET_U32(GET_OPTION(palette_type));
1139 net_packet.utf8_support = HOST_TO_NET_U32(caps.utf8_support ? 1 : 0);
1140
1141 const options_t *opts = options_get();
1142 if (GET_OPTION(palette_type) == PALETTE_CUSTOM && GET_OPTION(palette_custom_set)) {
1143 const char *palette_custom = opts && opts->palette_custom_set ? opts->palette_custom : "";
1144 SAFE_STRNCPY(net_packet.palette_custom, palette_custom, sizeof(net_packet.palette_custom));
1145 net_packet.palette_custom[sizeof(net_packet.palette_custom) - 1] = '\0';
1146 } else {
1147 SAFE_MEMSET(net_packet.palette_custom, sizeof(net_packet.palette_custom), 0, sizeof(net_packet.palette_custom));
1148 }
1149
1150 // Set desired FPS
1151 if (g_max_fps > 0) {
1152 net_packet.desired_fps = (uint8_t)(g_max_fps > 144 ? 144 : g_max_fps);
1153 } else {
1154 net_packet.desired_fps = caps.desired_fps;
1155 }
1156
1157 if (net_packet.desired_fps == 0) {
1158 net_packet.desired_fps = DEFAULT_MAX_FPS;
1159 }
1160
1161 SAFE_STRNCPY(net_packet.term_type, caps.term_type, sizeof(net_packet.term_type));
1162 net_packet.term_type[sizeof(net_packet.term_type) - 1] = '\0';
1163
1164 SAFE_STRNCPY(net_packet.colorterm, caps.colorterm, sizeof(net_packet.colorterm));
1165 net_packet.colorterm[sizeof(net_packet.colorterm) - 1] = '\0';
1166
1167 net_packet.detection_reliable = caps.detection_reliable;
1168 net_packet.utf8_support = GET_OPTION(force_utf8) ? 1 : 0;
1169
1170 SAFE_MEMSET(net_packet.reserved, sizeof(net_packet.reserved), 0, sizeof(net_packet.reserved));
1171
1172 // Use threaded_send_packet() which handles encryption
1173 return threaded_send_packet(PACKET_TYPE_CLIENT_CAPABILITIES, &net_packet, sizeof(net_packet));
1174}
#define HOST_TO_NET_U16(val)
Definition endian.h:101
#define DEFAULT_MAX_FPS
Default maximum frame rate (frames per second)
Definition limits.h:26
int g_max_fps
Runtime configurable maximum frame rate (can be overridden via environment or command line)
Definition common.c:30
uint32_t capabilities
Terminal capabilities bitmask (TERM_CAP_* flags)
Definition packet.h:911
uint32_t color_level
Color level enum value (terminal_color_mode_t)
Definition packet.h:913
uint32_t color_count
Actual color count (16, 256, or 16777216)
Definition packet.h:915
uint16_t height
Terminal height in characters.
Definition packet.h:921
uint8_t reserved[2]
Reserved bytes for alignment (must be zero)
Definition packet.h:937
uint32_t render_mode
Render mode enum value (foreground/background/half-block)
Definition packet.h:917
uint16_t width
Terminal width in characters.
Definition packet.h:919
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
Definition packet.h:935
uint8_t detection_reliable
Detection reliability flag (1=reliable detection, 0=best guess)
Definition packet.h:927
char palette_custom[64]
Custom palette characters (if palette_type == PALETTE_CUSTOM)
Definition packet.h:933
char colorterm[32]
$COLORTERM environment variable value (for debugging)
Definition packet.h:925
uint32_t palette_type
Palette type enum value (palette_type_t)
Definition packet.h:931
uint32_t utf8_support
UTF-8 support flag (0=no UTF-8, 1=UTF-8 supported)
Definition packet.h:929
char term_type[32]
$TERM environment variable value (for debugging)
Definition packet.h:923
#define COLOR_MODE_AUTO
Backward compatibility aliases for color mode enum values.
Definition options.h:156
const options_t * options_get(void)
Get current options (lock-free read)
Definition rcu.c:222
@ PACKET_TYPE_CLIENT_CAPABILITIES
Client reports terminal capabilities.
Definition packet.h:293
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:96
terminal_capabilities_t apply_color_mode_override(terminal_capabilities_t caps)
Apply command-line overrides to detected capabilities.
terminal_capabilities_t detect_terminal_capabilities(void)
Detect terminal capabilities.
@ TERM_COLOR_NONE
No color support (monochrome terminal)
Definition terminal.h:428
Consolidated options structure.
Definition options.h:439
char palette_custom[256]
Custom palette characters.
Definition options.h:582
bool palette_custom_set
True if custom palette was set.
Definition options.h:583
Terminal capabilities packet structure (Packet Type 5)
Definition packet.h:909
Complete terminal capabilities structure.
Definition terminal.h:485
terminal_color_mode_t color_level
Detected color support level (terminal_color_mode_t)
Definition terminal.h:487
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
Definition terminal.h:509
render_mode_t render_mode
Preferred rendering mode (render_mode_t)
Definition terminal.h:497
bool utf8_support
True if terminal supports UTF-8 encoding.
Definition terminal.h:493
uint32_t capabilities
Capability flags bitmask (terminal_capability_flags_t)
Definition terminal.h:489
uint32_t color_count
Maximum number of colors (16, 256, or 16777216)
Definition terminal.h:491
char term_type[64]
$TERM environment variable value (for debugging)
Definition terminal.h:499
char colorterm[64]
$COLORTERM environment variable value (for debugging)
Definition terminal.h:501
bool detection_reliable
True if detection is confident (reliable detection)
Definition terminal.h:495

References apply_color_mode_override(), terminal_capabilities_packet_t::capabilities, terminal_capabilities_t::capabilities, terminal_capabilities_packet_t::color_count, terminal_capabilities_t::color_count, terminal_capabilities_packet_t::color_level, terminal_capabilities_t::color_level, COLOR_MODE_AUTO, terminal_capabilities_packet_t::colorterm, terminal_capabilities_t::colorterm, DEFAULT_MAX_FPS, terminal_capabilities_packet_t::desired_fps, terminal_capabilities_t::desired_fps, detect_terminal_capabilities(), terminal_capabilities_packet_t::detection_reliable, terminal_capabilities_t::detection_reliable, g_max_fps, GET_OPTION, terminal_capabilities_packet_t::height, HOST_TO_NET_U16, HOST_TO_NET_U32, INVALID_SOCKET_VALUE, log_warn, options_get(), PACKET_TYPE_CLIENT_CAPABILITIES, terminal_capabilities_packet_t::palette_custom, PALETTE_CUSTOM, terminal_capabilities_packet_t::palette_type, terminal_capabilities_packet_t::render_mode, terminal_capabilities_t::render_mode, terminal_capabilities_packet_t::reserved, SAFE_MEMSET, SAFE_STRNCPY, server_connection_get_socket(), TERM_COLOR_NONE, terminal_capabilities_packet_t::term_type, terminal_capabilities_t::term_type, threaded_send_packet(), terminal_capabilities_packet_t::utf8_support, terminal_capabilities_t::utf8_support, and terminal_capabilities_packet_t::width.

Referenced by protocol_start_connection(), and server_connection_establish().

Variable Documentation

◆ g_crypto_ctx

crypto_handshake_context_t g_crypto_ctx = {0}

#include <server.c>

Per-connection crypto handshake context.

Maintains the cryptographic state for the current connection, including key exchange state, encryption keys, and handshake progress.

Note
This is not static because it may be accessed from crypto.c

Definition at line 198 of file src/client/server.c.

198{0};

Referenced by client_crypto_handshake(), client_crypto_init(), crypto_client_cleanup(), crypto_client_decrypt_packet(), crypto_client_encrypt_packet(), crypto_client_get_context(), crypto_client_initiate_rekey(), crypto_client_is_ready(), crypto_client_process_rekey_request(), crypto_client_process_rekey_response(), crypto_client_send_rekey_complete(), crypto_client_send_rekey_response(), crypto_client_should_rekey(), and server_connection_close().