ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
Network Module

🌐 Core network I/O operations with timeout support More...

Files

file  frame_validator.c
 Frame validation implementation for IMAGE_FRAME packets.
file  client.c
 πŸŒ HTTPS client with BearSSL for fetching public keys from GitHub/GitLab with CA validation
file  network.c
 πŸŒ Cross-platform socket I/O with timeout management and connection handling
file  callback_timing.c
 WebSocket callback timing instrumentation implementation.
file  server.c
 WebSocket server implementation.
file  protocol.h
 Server packet processing and protocol implementation.
file  protocol.h
 ascii-chat Binary Network Protocol (complete system including ACIP discovery service)
file  dns.h
 πŸŒ DNS resolution utilities
file  frame_validator.h
 Frame validation utilities for IMAGE_FRAME packets.
file  client.h
 Simple HTTPS client for fetching public keys from GitHub/GitLab.
file  network.h
 πŸŒ Core network I/O operations with timeout support
file  parallel_connect.h
 Parallel IPv4/IPv6 connection with race-to-connect semantics.
file  tailscale.h
 Tailscale host identity helpers.
file  update_checker.h
 πŸ”„ Update checker for GitHub releases with DNS connectivity test and cache
file  callback_timing.h
 WebSocket callback timing instrumentation.
file  internal.h
 Internal WebSocket implementation types shared between transport.c and server.c.
file  server.h
 WebSocket server for accepting browser client connections.

Data Structures

struct  packet_header_t
 Fixed prefix shared by every ACIP packet on the wire. More...
struct  size_packet_t
 Terminal size update packet. More...
struct  client_info_packet_t
 Client information packet structure. More...
struct  stream_header_t
 Stream header packet structure. More...
struct  client_list_packet_t
 Snapshot of the clients currently known to the server. More...
struct  server_state_packet_t
 Server state packet structure. More...
struct  error_packet_t
 Error packet structure carrying error code and textual description. More...
struct  remote_log_packet_t
 Remote log packet structure carrying log level and message text. More...
struct  auth_failure_packet_t
 Authentication failure packet structure. More...
struct  protocol_version_packet_t
 Protocol version negotiation packet structure (Packet Type 1). More...
struct  ascii_frame_packet_t
 The wire metadata for one rendered ASCII frame. width and height describe the terminal grid, original_size and compressed_size bound the encoded text, and checksum detects corruption; flags carry frame-specific options. The variable text follows this fixed prefix and is owned by the packet storage until decoding completes. More...
struct  image_frame_packet_t
 Describes one compressed image sent through ACIP. width, height, and pixel_format tell the decoder how to interpret the payload; compressed_size bounds the following bytes, and checksum plus timestamp support validation and frame ordering. The packet header is fixed-size, but the image bytes are variable-length and owned by the packet buffer. More...
struct  audio_batch_packet_t
 The metadata prefix for a batch of encoded audio samples. batch_count, total_samples, sample_rate, and channels let the receiver allocate or route the batch correctly before it touches the encoded payload. The header is serialized with the packet; the audio bytes remain in the enclosing packet buffer. More...
struct  crypto_capabilities_packet_t
 Crypto capabilities packet structure (Packet Type 14). More...
struct  crypto_parameters_packet_t
 Crypto parameters packet structure (Packet Type 15). More...
struct  terminal_capabilities_packet_t
 Terminal capabilities packet structure (Packet Type 5). More...
struct  packet_envelope_t
 Packet envelope containing received packet data. More...
struct  update_check_result_t
 update check result keeps the values that move through the subsystem: update_available, last_check_time, dismissed_until, check_succeeded. Some members are pointers into caller- or subsystem-owned memory, so lifetime follows the API that populated them rather than the struct copy itself. More...

Macros

#define NETWORK_PACKET_TERM_TYPE_BUFFER_SIZE   32
#define NETWORK_PACKET_COLORTERM_BUFFER_SIZE   32
#define NETWORK_PACKET_PALETTE_CUSTOM_BUFFER_SIZE   64
#define ACIP_OPUS_BATCH_HEADER_SIZE   16
#define REMOTE_LOG_FLAG_TRUNCATED   0x0001U
 Remote log packet flag definitions.
#define NETWORK_UPDATE_CHECKER_LATEST_VERSION_BUFFER_SIZE   64
#define NETWORK_UPDATE_CHECKER_LATEST_SHA_BUFFER_SIZE   41
#define NETWORK_UPDATE_CHECKER_CURRENT_VERSION_BUFFER_SIZE   64
#define NETWORK_UPDATE_CHECKER_CURRENT_SHA_BUFFER_SIZE   41
#define NETWORK_UPDATE_CHECKER_RELEASE_URL_BUFFER_SIZE   512

Enumerations

enum  packet_type_t {
  PACKET_TYPE_PROTOCOL_VERSION = 1 , PACKET_TYPE_CRYPTO_CLIENT_HELLO = 1000 , PACKET_TYPE_CRYPTO_CAPABILITIES = 1100 , PACKET_TYPE_CRYPTO_PARAMETERS = 1101 ,
  PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT = 1102 , PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP = 1103 , PACKET_TYPE_CRYPTO_AUTH_CHALLENGE = 1104 , PACKET_TYPE_CRYPTO_AUTH_RESPONSE = 1105 ,
  PACKET_TYPE_CRYPTO_AUTH_FAILED = 1106 , PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP = 1107 , PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE = 1108 , PACKET_TYPE_CRYPTO_NO_ENCRYPTION = 1109 ,
  PACKET_TYPE_QUIC_READY = 1110 , PACKET_TYPE_QUIC_KEYFRAME_REQUEST = 1111 , PACKET_TYPE_CRYPTO_EPOCH_MARKER = 1112 , PACKET_TYPE_CRYPTO_REKEY_NEEDED = 1113 ,
  PACKET_TYPE_CRYPTO_EPOCH_ACK = 1114 , PACKET_TYPE_ENCRYPTED = 1200 , PACKET_TYPE_CRYPTO_REKEY_REQUEST = 1201 , PACKET_TYPE_CRYPTO_REKEY_RESPONSE = 1202 ,
  PACKET_TYPE_CRYPTO_REKEY_COMPLETE = 1203 , PACKET_TYPE_SIZE_MESSAGE = 2000 , PACKET_TYPE_AUDIO_MESSAGE = 2001 , PACKET_TYPE_TEXT_MESSAGE = 2002 ,
  PACKET_TYPE_ERROR_MESSAGE = 2003 , PACKET_TYPE_REMOTE_LOG = 2004 , PACKET_TYPE_ASCII_FRAME = 3000 , PACKET_TYPE_IMAGE_FRAME = 3001 ,
  PACKET_TYPE_IMAGE_FRAME_H265 = 3002 , PACKET_TYPE_IMAGE_FRAME_H264 = 3003 , PACKET_TYPE_AUDIO_BATCH = 4000 , PACKET_TYPE_AUDIO_OPUS_BATCH = 4001 ,
  PACKET_TYPE_CLIENT_CAPABILITIES = 5000 , PACKET_TYPE_PING = 5001 , PACKET_TYPE_PONG = 5002 , PACKET_TYPE_CLIENT_JOIN = 5003 ,
  PACKET_TYPE_CLIENT_LEAVE = 5004 , PACKET_TYPE_STREAM_START = 5005 , PACKET_TYPE_STREAM_STOP = 5006 , PACKET_TYPE_CLEAR_CONSOLE = 5007 ,
  PACKET_TYPE_SERVER_STATE = 5008 , PACKET_TYPE_ACIP_SESSION_CREATE = 6000 , PACKET_TYPE_ACIP_SESSION_CREATED = 6001 , PACKET_TYPE_ACIP_SESSION_LOOKUP = 6002 ,
  PACKET_TYPE_ACIP_SESSION_INFO = 6003 , PACKET_TYPE_ACIP_SESSION_JOIN = 6004 , PACKET_TYPE_ACIP_SESSION_JOINED = 6005 , PACKET_TYPE_ACIP_SESSION_LEAVE = 6006 ,
  PACKET_TYPE_ACIP_SESSION_END = 6007 , PACKET_TYPE_ACIP_SESSION_RECONNECT = 6008 , PACKET_TYPE_ACIP_WEBRTC_SDP = 6009 , PACKET_TYPE_ACIP_WEBRTC_ICE = 6010 ,
  PACKET_TYPE_ACIP_STRING_RESERVE = 6020 , PACKET_TYPE_ACIP_STRING_RESERVED = 6021 , PACKET_TYPE_ACIP_STRING_RENEW = 6022 , PACKET_TYPE_ACIP_STRING_RELEASE = 6023 ,
  PACKET_TYPE_ACIP_PARTICIPANT_LIST = 6050 , PACKET_TYPE_ACIP_RING_COLLECT = 6051 , PACKET_TYPE_ACIP_NETWORK_QUALITY = 6060 , PACKET_TYPE_ACIP_HOST_ANNOUNCEMENT = 6061 ,
  PACKET_TYPE_ACIP_HOST_DESIGNATED = 6062 , PACKET_TYPE_ACIP_SETTINGS_SYNC = 6063 , PACKET_TYPE_ACIP_SETTINGS_ACK = 6064 , PACKET_TYPE_ACIP_HOST_LOST = 6065 ,
  PACKET_TYPE_ACIP_FUTURE_HOST_ELECTED = 6066 , PACKET_TYPE_ACIP_PARTICIPANT_JOINED = 6067 , PACKET_TYPE_ACIP_PARTICIPANT_LEFT = 6068 , PACKET_TYPE_ACIP_BANDWIDTH_TEST = 6070 ,
  PACKET_TYPE_ACIP_BANDWIDTH_RESULT = 6071 , PACKET_TYPE_ACIP_BROADCAST_ACK = 6075 , PACKET_TYPE_RING_MEMBERS = 6100 , PACKET_TYPE_STATS_COLLECTION_START = 6101 ,
  PACKET_TYPE_STATS_UPDATE = 6102 , PACKET_TYPE_RING_ELECTION_RESULT = 6103 , PACKET_TYPE_STATS_ACK = 6104 , PACKET_TYPE_ACIP_DISCOVERY_PING = 6190 ,
  PACKET_TYPE_ACIP_ERROR = 6199
}
 Network protocol packet type enumeration. More...
enum  auth_failure_reason_t {
  AUTH_FAIL_PASSWORD_REQUIRED = 0x01 , AUTH_FAIL_PASSWORD_INCORRECT = 0x02 , AUTH_FAIL_CLIENT_KEY_REQUIRED = 0x04 , AUTH_FAIL_CLIENT_KEY_REJECTED = 0x08 ,
  AUTH_FAIL_SIGNATURE_INVALID = 0x10
}
 Authentication failure reason flags. More...
enum  packet_recv_result_t { PACKET_RECV_SUCCESS = 0 , PACKET_RECV_EOF = -1 , PACKET_RECV_ERROR = -2 , PACKET_RECV_SECURITY_VIOLATION = -3 }
 packet envelope is intentionally a small, concrete record. Fields such as type, data, len, was_encrypted give callers the identity and current value they need. It is normally borrowed from, or owned by, the surrounding subsystemβ€”not independently managed. More...
enum  install_method_t {
  INSTALL_METHOD_HOMEBREW , INSTALL_METHOD_SCOOP , INSTALL_METHOD_ARCH_AUR , INSTALL_METHOD_INSTALLER ,
  INSTALL_METHOD_GITHUB , INSTALL_METHOD_UNKNOWN
}
 Installation method for suggesting upgrade command. More...

Functions

bool dns_test_connectivity (const char *hostname)
 Test DNS connectivity by resolving a hostname.
asciichat_error_t frame_validate_legacy (size_t len, size_t expected_rgb_size)
 Validate legacy frame format.
asciichat_error_t frame_validate_new (void *data, size_t len, bool *out_compressed, uint32_t *out_data_size)
 Validate new frame format with optional compression.
asciichat_error_t frame_check_size_overflow (size_t header_size, size_t data_size)
 Check for overflow when adding header to data.
void frame_extract_dimensions (const void *data, uint32_t *width, uint32_t *height)
 Extract width and height from frame header.
void frame_extract_new_header (const void *data, uint32_t *compressed, uint32_t *data_size)
 Extract compressed flag and data size from new format header.
asciichat_error_t packet_header_build (packet_header_t *header, packet_type_t type, const socket_buffer_t *payload, size_t count, uint32_t client_id)
asciichat_error_t packet_encode (packet_type_t type, const void *payload, size_t length, uint32_t client_id, void *output, size_t capacity, size_t *written)
asciichat_error_t packet_validate (const void *packet, size_t length, size_t limit, packet_header_t *header)
void protocol_version_packet_init (protocol_version_packet_t *version, uint8_t supports_encryption)
 Initialize a protocol version packet with the current wire version.
asciichat_error_t update_check_perform (update_check_result_t *result)
 Check for updates from GitHub releases API.
asciichat_error_t update_check_load_cache (update_check_result_t *result)
 Load cached update check result.
asciichat_error_t update_check_save_cache (const update_check_result_t *result)
 Save update check result to cache.
void update_check_dismiss_for_a_week (update_check_result_t *result)
bool update_check_is_dismissed (const update_check_result_t *result)
bool update_check_is_cache_fresh (const update_check_result_t *result)
 Check if cache is fresh (< 7 days old).
bool update_check_try_fresh_cache (update_check_result_t *result)
install_method_t update_check_detect_install_method (void)
 Detect installation method.
void update_check_get_upgrade_suggestion (install_method_t method, const char *latest_version, char *buffer, size_t buffer_size)
 Get upgrade suggestion string.
const char * update_check_get_aur_helper (void)
void update_check_format_notification (const update_check_result_t *result, char *buffer, size_t buffer_size)
 Format update notification message.
asciichat_error_t update_check_startup (update_check_result_t *result)
 Perform startup update check with caching.

HTTPS Client

char * https_get (const char *hostname, const char *path)
 Perform HTTPS GET request.

Socket I/O Operations

ssize_t send_with_timeout (socket_t sockfd, const void *data, size_t len, uint64_t timeout_ns)
 Send data with timeout using chunked transmission.
ssize_t recv_with_timeout (socket_t sockfd, void *buf, size_t len, uint64_t timeout_ns)
 Receive data with timeout.
int accept_with_timeout (socket_t listenfd, struct sockaddr *addr, socklen_t *addrlen, uint64_t timeout_ns)
 Accept connection with timeout.
bool connect_with_timeout (socket_t sockfd, const struct sockaddr *addr, socklen_t addrlen, int timeout_seconds)
 Connect to server with timeout.

Socket Configuration Functions

asciichat_error_t set_socket_timeout (socket_t sockfd, uint64_t timeout_ns)
 Set socket timeout for send/receive operations.
asciichat_error_t set_socket_keepalive (socket_t sockfd)
 Enable TCP keepalive on socket.
asciichat_error_t set_socket_nonblocking (socket_t sockfd)
 Set socket to non-blocking mode.
asciichat_error_t socket_configure_buffers (socket_t sockfd)
 Configure socket buffers and TCP_NODELAY for optimal performance.

Error Reporting Functions

const char * network_error_string ()
 Get human-readable error string for network errors.

Basic Packet I/O Functions

asciichat_error_t packet_send (socket_t sockfd, packet_type_t type, const void *data, size_t len)
 Send a packet with header and CRC32 checksum.
asciichat_error_t packet_receive (socket_t sockfd, packet_type_t *type, void **data, size_t *len)
 Receive a packet with header validation and CRC32 checking.

Secure Packet I/O Functions

asciichat_error_t send_packet_secure (socket_t sockfd, packet_type_t type, const void *data, size_t len, crypto_context_t *crypto_ctx)
 Send a packet with encryption and compression support.
packet_recv_result_t receive_packet_secure (socket_t sockfd, void *crypto_ctx, bool enforce_encryption, packet_envelope_t *envelope)
 Receive a packet with decryption and decompression support.
asciichat_error_t packet_decrypt_envelope (packet_envelope_t *envelope, void *crypto_ctx)
 Decrypt a PACKET_TYPE_ENCRYPTED envelope and extract inner packet.
packet_recv_result_t receive_packet_secure_with_timeout (socket_t sockfd, void *crypto_ctx, bool enforce_encryption, packet_envelope_t *envelope, uint64_t timeout_ns)

Legacy Packet I/O Functions

These functions provide basic packet I/O without encryption support. Use send_packet_secure() and receive_packet_secure() for encryption support.

int send_packet (socket_t sockfd, packet_type_t type, const void *data, size_t len)
 Send a basic packet without encryption.
int receive_packet (socket_t sockfd, packet_type_t *type, void **data, size_t *len)
 Receive a basic packet without encryption.

Protocol Packet Functions

Convenience functions for sending specific protocol packets. These functions construct the appropriate packet structure and send it over the socket.

int send_ping_packet (socket_t sockfd)
 Send a ping packet (keepalive).
int send_pong_packet (socket_t sockfd)
 Send a pong packet (keepalive response).
int send_protocol_version_packet (socket_t sockfd, const protocol_version_packet_t *version)
 Send protocol version negotiation packet.
int send_crypto_capabilities_packet (socket_t sockfd, const crypto_capabilities_packet_t *caps)
 Send crypto capabilities packet.
int send_crypto_parameters_packet (socket_t sockfd, const crypto_parameters_packet_t *params)
 Send crypto parameters packet.
asciichat_error_t av_send_audio_opus_batch (socket_t sockfd, const uint8_t *opus_data, size_t opus_size, const uint16_t *frame_sizes, int sample_rate, int frame_duration, int frame_count, crypto_context_t *crypto_ctx)
 Send Opus-encoded audio batch packet with encryption support.
asciichat_error_t send_image_frame_packet (socket_t sockfd, const void *image_data, uint16_t width, uint16_t height, uint8_t format)
 Send image frame packet.
asciichat_error_t packet_send_error (socket_t sockfd, const crypto_context_t *crypto_ctx, asciichat_error_t error_code, const char *message)
 Send an error packet with optional encryption context.
size_t packet_format_error_stack (char *buffer, size_t size)
 Format the current thread's error stack for transport to a peer.
asciichat_error_t packet_parse_error_message (const void *data, size_t len, asciichat_error_t *out_error_code, char *message_buffer, size_t message_buffer_size, size_t *out_message_length)
 Parse an error packet payload into components.
asciichat_error_t packet_send_remote_log (socket_t sockfd, const crypto_context_t *crypto_ctx, log_level_t level, remote_log_direction_t direction, uint16_t flags, const char *message)
 Send a remote log packet with optional encryption context.
asciichat_error_t packet_parse_remote_log (const void *data, size_t len, log_level_t *out_level, remote_log_direction_t *out_direction, uint16_t *out_flags, char *message_buffer, size_t message_buffer_size, size_t *out_message_length)
 Parse a remote log packet payload into components.

Network Timeout Constants

Timeout values tuned for real-time video streaming. All timeouts are specified in seconds.

#define CONNECT_TIMEOUT   3
 Connection timeout in seconds (3 seconds).
#define SEND_TIMEOUT   1
 Send timeout in seconds (5 seconds).
#define RECV_TIMEOUT   1
 Receive timeout in seconds (1 second).
#define ACCEPT_TIMEOUT   1
 Accept timeout in seconds (1 second).

Test Environment Detection

#define network_is_test_environment()
 Check if we're in a test environment.

Socket Keepalive Settings

Keepalive settings to detect dead connections. TCP keepalive probes are sent when connection is idle to detect broken connections.

#define KEEPALIVE_IDLE   60
 Keepalive idle time in seconds (60 seconds).
#define KEEPALIVE_INTERVAL   10
 Keepalive interval in seconds (10 seconds).
#define KEEPALIVE_COUNT   8
 Keepalive probe count (8 probes).

Network Protocol Constants

Announces the key-exchange, authentication, and cipher algorithms an endpoint is willing to use. The supported-algorithm fields are capability bitmasks or lists, while the preferred values guide selection; the receiver uses this packet to build the later crypto-parameters message rather than keeping it as session state.

Fixed prefix shared by every ACIP packet on the wire

The first ACIP negotiation packet exchanged by two peers. Its protocol version and revision fields establish wire compatibility, while the encryption, compression, and feature flags tell the receiver which later packet paths are safe to use. It is assembled for transmission and parsed into host-sized values on receipt.

Terminal size update packet

When code needs to work with server state packet, it keeps connected_client_count, active_client_count here instead of scattering it across globals. Initialization establishes the handles and sizes; teardown must release any allocation, lock, thread, or callback context recorded in those fields.

For stream header, this is the live record that ties client_id, stream_type, timestamp together. The owning code creates it, updates it while work is active, and releases its handles and pointed-to storage before freeing the record.

The ACIP packet sent when a terminal announces what it can render. capabilities and color_level describe feature support, while width, height, and render_mode let the peer choose a compatible output format. The packed fields are serialized directly; do not treat this as a long-lived heap object.

#define LARGE_PACKET_THRESHOLD   (100 * 1024)
 Large packet size threshold (100KB).
#define MAX_PACKET_SIZE   ((size_t)5 * 1024 * 1024)
 Maximum packet size (5MB).
#define MAX_ERROR_MESSAGE_LENGTH   4096
 Maximum error message length (512 bytes).
#define MAX_REMOTE_LOG_MESSAGE_LENGTH   512
 Maximum remote log message length (512 bytes).

Timeout Configuration Constants

Timeout constants for handling packets of different sizes. Large packets require extended timeouts for successful transmission.

#define BASE_SEND_TIMEOUT   (5LL * NS_PER_SEC_INT)
 Base send timeout in nanoseconds (5 seconds).
#define LARGE_PACKET_EXTRA_TIMEOUT_PER_MB   (800LL * NS_PER_MS_INT)
 Extra timeout per MB for large packets (0.8 seconds per MB).
#define MIN_CLIENT_TIMEOUT   (10LL * NS_PER_SEC_INT)
 Minimum client timeout in nanoseconds (10 seconds).
#define MAX_CLIENT_TIMEOUT   (60LL * NS_PER_SEC_INT)
 Maximum client timeout in nanoseconds (60 seconds).

Audio Batching Constants

Audio batching configuration for efficient audio transmission. Batched audio reduces packet overhead and improves bandwidth usage.

#define AUDIO_BATCH_COUNT   32
 Number of audio chunks per batch (4 chunks).
#define AUDIO_BATCH_SAMPLES   (AUDIO_SAMPLES_PER_PACKET * AUDIO_BATCH_COUNT)
 Total samples in audio batch (8192 samples).
#define AUDIO_BATCH_MS   186
 Audio batch duration in milliseconds (~186ms).
#define AUDIO_SAMPLES_PER_PACKET   256
 Samples per audio packet (256 samples).

Protocol Constants

#define PACKET_MAGIC   MAGIC_PACKET_VALID
 Packet magic number (alias for MAGIC_PACKET_VALID).

Multi-User Protocol Constants

#define ASCIICHAT_DEFAULT_DISPLAY_NAME   "AsciiChatter"
 Default display name for clients without a custom name.

Protocol Negotiation Constants

#define FEATURE_RLE_ENCODING   0x01
 Feature flags for protocol_version_packet_t.
#define FEATURE_DELTA_FRAMES   0x02
 Delta frame encoding (future).
#define PROTOCOL_FEATURE_RECOVERABLE_ERRORS   0x04
#define PROTOCOL_FEATURE_IDENTITY_SET   0x08

Client Capability Flags

Bitmask flags for client capabilities in multi-user protocol.

#define CLIENT_CAP_VIDEO   0x01
 Client can send/receive video.
#define CLIENT_CAP_AUDIO   0x02
 Client can send/receive audio.
#define CLIENT_CAP_COLOR   0x04
 Client supports color rendering.
#define CLIENT_CAP_STRETCH   0x08
 Client can stretch frames to fill terminal.

Stream Type Flags

Bitmask flags for stream types in multi-user protocol.

#define STREAM_TYPE_VIDEO   0x01
 Video stream.
#define STREAM_TYPE_AUDIO   0x02
 Audio stream.

Crypto Algorithm Constants

Algorithm identifiers for key exchange, authentication, and encryption. Used in crypto handshake packet negotiation.

#define KEX_ALGO_X25519   0x01
 X25519 key exchange (Curve25519).
#define AUTH_ALGO_ED25519   0x01
 Ed25519 authentication (Edwards-curve signatures).
#define AUTH_ALGO_NONE   0x00
 No authentication (plaintext mode).
#define CIPHER_ALGO_XSALSA20_POLY1305   0x01
 XSalsa20-Poly1305 authenticated encryption.
#define CIPHER_ALGO_NONE   0x00
 No encryption (plaintext mode).

Crypto Mode Bitmasks

2-bit bitmask for encryption and authentication modes

These constants define all valid combinations of encryption and authentication in the crypto protocol. The supports_encryption field in protocol_version_packet_t uses these values to negotiate capabilities:

  • Bit 0 (0x01): ACIP_CRYPTO_ENCRYPT - payload encryption enabled
  • Bit 1 (0x02): ACIP_CRYPTO_AUTH - identity authentication enabled
#define ACIP_CRYPTO_NONE   0x00
 No encryption, no authentication (plaintext only).
#define ACIP_CRYPTO_ENCRYPT   0x01
 Encryption only (no identity verification).
#define ACIP_CRYPTO_AUTH   0x02
 Authentication only (no payload encryption).
#define ACIP_CRYPTO_FULL   0x03
 Full crypto: encryption + authentication.
#define ACIP_CRYPTO_HAS_ENCRYPT(mode)
 Check if crypto mode includes payload encryption.
#define ACIP_CRYPTO_HAS_AUTH(mode)
 Check if crypto mode includes authentication.

Detailed Description

🌐 Core network I/O operations with timeout support

Provides DNS resolution and connectivity testing utilities.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
February 2026

This module provides basic HTTPS GET functionality using BearSSL for TLS. It's designed specifically for fetching SSH/GPG public keys from GitHub and GitLab.

Note
TLS library: Uses BearSSL for TLS connections. BearSSL is a minimal TLS implementation with system CA certificate support.
Security: Uses system CA certificates for trust validation (20-year longevity). Validates server certificates against system trust store.
Key fetching: Fetches keys from GitHub/GitLab endpoints:
Key fetching functions: fetch_github_ssh_keys, fetch_gitlab_ssh_keys, fetch_github_gpg_keys, and fetch_gitlab_gpg_keys are in crypto/keys/https_keys.h. They properly belong in the keys module.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
October 2025

This module provides fundamental network I/O operations including socket management, timeout handling, and basic send/receive operations. All operations support configurable timeouts to prevent indefinite blocking, critical for real-time video streaming applications.

CORE RESPONSIBILITIES:

  1. Socket I/O operations with timeout support
  2. Connection management (connect, accept) with timeouts
  3. Socket configuration (keepalive, non-blocking, timeouts)
  4. Chunked transmission for large data transfers
  5. Error reporting and diagnostics

ARCHITECTURAL OVERVIEW:

TIMEOUT SYSTEM:

  • All I/O operations support configurable timeouts
  • Timeouts are tuned for real-time video streaming requirements
  • Chunked transmission prevents large data from blocking indefinitely
  • Platform-specific timeout implementation (select/poll/epoll)

SOCKET CONFIGURATION:

  • Keepalive settings to detect dead connections
  • Non-blocking mode support for asynchronous I/O
  • Socket-level timeout configuration
  • Platform abstraction for cross-platform compatibility

ERROR HANDLING:

  • Comprehensive error reporting with human-readable messages
  • Platform-specific error code translation
  • Network error string utilities

INTEGRATION WITH OTHER MODULES:

  • platform/socket.h: Uses platform socket abstraction
  • Used by packet.h for packet transmission
  • Used by av.h for media packet sending
  • Used by handshake.c for crypto handshake operations

PERFORMANCE CHARACTERISTICS:

  • Chunked transmission enables progressive sending of large frames
  • Timeout handling prevents indefinite blocking
  • Keepalive settings detect dead connections efficiently
  • Platform-specific optimizations where available
Note
Timeout values are tuned for real-time video streaming. Adjust CONNECT_TIMEOUT, SEND_TIMEOUT, and RECV_TIMEOUT if needed for different network conditions.
Chunked transmission is used automatically for large data to prevent timeouts and enable progress tracking.
Warning
Timeout values that are too short may cause false positives on slow networks. Values that are too long may delay error detection unnecessarily.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025
Version
2.0 (Post-Modularization)

This module provides comprehensive packet protocol implementation including packet verification, CRC validation, protocol compliance checking, encryption support, and compression integration. It serves as the core network protocol layer for ascii-chat's communication system.

CORE RESPONSIBILITIES:

  1. Packet header validation and CRC32 checksum verification
  2. Secure packet transmission with encryption support
  3. Secure packet reception with decryption and validation
  4. Protocol compliance checking and error handling
  5. Integration with cryptographic and compression subsystems

ARCHITECTURAL OVERVIEW:

PACKET STRUCTURE:

  • Header: Magic number, type, length, CRC32, client ID
  • Payload: Variable-length data (may be encrypted/compressed)
  • Validation: CRC32 checksum for integrity verification

ENCRYPTION INTEGRATION:

  • Automatic encryption/decryption when crypto context provided
  • Handshake packets always sent unencrypted (plaintext)
  • Session packets encrypted after handshake completion
  • Encryption policy enforcement (require/optional encryption)

COMPRESSION INTEGRATION:

  • Automatic compression for large packets (frames, audio)
  • Compression threshold-based decision making
  • Decompression on receive path

PROTOCOL VALIDATION:

  • Magic number validation to detect corruption
  • CRC32 checksum verification for integrity
  • Packet size validation to prevent attacks
  • Type validation for protocol compliance

INTEGRATION WITH OTHER MODULES:

PERFORMANCE CHARACTERISTICS:

  • Zero-copy operation when possible
  • Automatic memory management for packet buffers
  • Efficient CRC32 validation (hardware-accelerated when available)
  • Compression reduces bandwidth for large frames
Note
All packet functions handle encryption automatically when crypto context is provided. Plaintext packets are used for handshake.
Compression is automatically applied to large packets (frames, audio batches) based on size thresholds.
Warning
Packet buffers are allocated by receive functions and must be freed by caller using the allocated_buffer pointer.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025
Version
2.0 (Post-Modularization)

Provides automatic and manual update checking against GitHub releases API.

  • Checks once per week automatically at startup
  • Manual check via –check-update flag
  • DNS connectivity test before attempting HTTP request
  • Compares semantic versions to detect updates
  • Caches results in ~/.config/ascii-chat/last_update_check
  • Detects installation method (Homebrew, Arch AUR, GitHub)
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
February 2026

Packet Queues

Queues own their nodes and payload references. Use packet_queue_push_copy() for a borrowed view, packet_queue_push_take() to move a buffer without copying, or packet_queue_push_shared() for immutable fan-out. Successful take clears the caller pointer; rejection preserves it. All operations return contextual asciichat_error_t errors.

Every queue has both a packet-count limit and a byte limit. The default overflow policy rejects new packets. DROP_OLDEST must be explicitly selected for independently discardable media such as raw frames or audio blocks. Do not select it for dependency-bearing encoded video without recovery logic.

One queue mutex protects admission, list mutation, accounting and reclamation. Nodes use the shared buffer API; there is no independent node allocator. Dequeue transfers the existing node without another allocation. Release with packet_queue_free_packet(). Dequeued packets can outlive their queue.

Stop rejects new admissions and hides queued data from consumers. Clear and destroy still drain stopped queues. Join queue users before destruction. Queued storage is released automatically; already dequeued packets remain owned by their consumers.

Macro Definition Documentation

◆ ACCEPT_TIMEOUT

#define ACCEPT_TIMEOUT   1

#include <include/ascii-chat/network/network.h>

Accept timeout in seconds (1 second).

Maximum time to wait for incoming connections. Balanced between responsiveness and CPU usage. Status screen updates independently in its own thread at target FPS.

Definition at line 142 of file network/network.h.

◆ ACIP_CRYPTO_AUTH

#define ACIP_CRYPTO_AUTH   0x02

#include <include/ascii-chat/network/packet/packet.h>

Authentication only (no payload encryption).

Definition at line 1140 of file packet.h.

Referenced by crypto_policy_resolve(), and network_connection_establish().

◆ ACIP_CRYPTO_ENCRYPT

#define ACIP_CRYPTO_ENCRYPT   0x01

#include <include/ascii-chat/network/packet/packet.h>

Encryption only (no identity verification).

Definition at line 1139 of file packet.h.

Referenced by client_connection_create(), and crypto_policy_resolve().

◆ ACIP_CRYPTO_FULL

#define ACIP_CRYPTO_FULL   0x03

#include <include/ascii-chat/network/packet/packet.h>

Full crypto: encryption + authentication.

Definition at line 1141 of file packet.h.

Referenced by acip_security_receive(), acip_security_start(), crypto_policy_check_mode(), and network_client_negotiate().

◆ ACIP_CRYPTO_HAS_AUTH

#define ACIP_CRYPTO_HAS_AUTH ( mode)

#include <include/ascii-chat/network/packet/packet.h>

Value:
(((mode) & ACIP_CRYPTO_AUTH) != 0)
#define ACIP_CRYPTO_AUTH
Authentication only (no payload encryption).
Definition packet.h:1140

Check if crypto mode includes authentication.

Parameters
modeCrypto mode bitmask (ACIP_CRYPTO_*)
Returns
true if authentication is enabled, false otherwise

Definition at line 1155 of file packet.h.

Referenced by acip_security_receive(), client_handshake_receive(), crypto_policy_check_mode(), and peer_crypto_receive().

◆ ACIP_CRYPTO_HAS_ENCRYPT

#define ACIP_CRYPTO_HAS_ENCRYPT ( mode)

#include <include/ascii-chat/network/packet/packet.h>

Value:
(((mode) & ACIP_CRYPTO_ENCRYPT) != 0)
#define ACIP_CRYPTO_ENCRYPT
Encryption only (no identity verification).
Definition packet.h:1139

Check if crypto mode includes payload encryption.

Parameters
modeCrypto mode bitmask (ACIP_CRYPTO_*)
Returns
true if payload encryption is enabled, false otherwise

Definition at line 1148 of file packet.h.

Referenced by acip_security_init_options(), acip_security_receive(), acip_security_start(), client_acds_handshake_begin(), client_handshake_begin(), client_handshake_receive(), crypto_policy_check_mode(), handle_protocol_version_packet(), network_client_negotiate(), network_negotiation_receive(), peer_crypto_quic_begin(), peer_crypto_receive(), and peer_crypto_tick().

◆ ACIP_CRYPTO_NONE

#define ACIP_CRYPTO_NONE   0x00

#include <include/ascii-chat/network/packet/packet.h>

No encryption, no authentication (plaintext only).

Definition at line 1138 of file packet.h.

Referenced by acip_security_receive(), client_handshake_receive(), and network_negotiation_receive().

◆ ACIP_OPUS_BATCH_HEADER_SIZE

#define ACIP_OPUS_BATCH_HEADER_SIZE   16

#include <include/ascii-chat/network/packet/packet.h>

Definition at line 81 of file packet.h.

Referenced by acip_send_audio_opus_batch().

◆ ASCIICHAT_DEFAULT_DISPLAY_NAME

#define ASCIICHAT_DEFAULT_DISPLAY_NAME   "AsciiChatter"

#include <include/ascii-chat/network/packet/packet.h>

Default display name for clients without a custom name.

Used when a client connects without specifying a display name.

Definition at line 664 of file packet.h.

◆ AUDIO_BATCH_COUNT

#define AUDIO_BATCH_COUNT   32

#include <include/ascii-chat/network/packet/packet.h>

Number of audio chunks per batch (4 chunks).

Number of audio chunks aggregated into a single batch packet for efficiency.

Definition at line 221 of file packet.h.

◆ AUDIO_BATCH_MS

#define AUDIO_BATCH_MS   186

#include <include/ascii-chat/network/packet/packet.h>

Audio batch duration in milliseconds (~186ms).

Approximate duration of audio batch at 44.1kHz sample rate.

Definition at line 240 of file packet.h.

◆ AUDIO_BATCH_SAMPLES

#define AUDIO_BATCH_SAMPLES   (AUDIO_SAMPLES_PER_PACKET * AUDIO_BATCH_COUNT)

#include <include/ascii-chat/network/packet/packet.h>

Total samples in audio batch (8192 samples).

Total audio samples across all chunks in a batch. At 44.1kHz, this equals ~185.8ms of audio.

Definition at line 231 of file packet.h.

Referenced by handle_audio_batch_packet().

◆ AUDIO_SAMPLES_PER_PACKET

#define AUDIO_SAMPLES_PER_PACKET   256

#include <include/ascii-chat/network/packet/packet.h>

Samples per audio packet (256 samples).

Number of audio samples in a single audio packet. Smaller packets provide lower latency at the cost of increased packet overhead.

Definition at line 250 of file packet.h.

Referenced by handle_audio_packet().

◆ AUTH_ALGO_ED25519

#define AUTH_ALGO_ED25519   0x01

#include <include/ascii-chat/network/packet/packet.h>

Ed25519 authentication (Edwards-curve signatures).

Definition at line 1121 of file packet.h.

Referenced by client_handshake_receive(), crypto_handshake_server_send_parameters(), and peer_crypto_receive().

◆ AUTH_ALGO_NONE

#define AUTH_ALGO_NONE   0x00

#include <include/ascii-chat/network/packet/packet.h>

No authentication (plaintext mode).

Definition at line 1122 of file packet.h.

Referenced by client_handshake_receive(), crypto_handshake_server_send_parameters(), and peer_crypto_receive().

◆ BASE_SEND_TIMEOUT

#define BASE_SEND_TIMEOUT   (5LL * NS_PER_SEC_INT)

#include <include/ascii-chat/network/packet/packet.h>

Base send timeout in nanoseconds (5 seconds).

Default timeout for sending packets. Used as base value for calculating timeouts for large packets.

Definition at line 170 of file packet.h.

◆ CIPHER_ALGO_NONE

#define CIPHER_ALGO_NONE   0x00

◆ CIPHER_ALGO_XSALSA20_POLY1305

#define CIPHER_ALGO_XSALSA20_POLY1305   0x01

#include <include/ascii-chat/network/packet/packet.h>

XSalsa20-Poly1305 authenticated encryption.

Definition at line 1123 of file packet.h.

Referenced by client_handshake_receive(), crypto_handshake_server_send_parameters(), and peer_crypto_receive().

◆ CLIENT_CAP_AUDIO

#define CLIENT_CAP_AUDIO   0x02

#include <include/ascii-chat/network/packet/packet.h>

Client can send/receive audio.

Definition at line 980 of file packet.h.

Referenced by handle_client_join_packet().

◆ CLIENT_CAP_COLOR

#define CLIENT_CAP_COLOR   0x04

#include <include/ascii-chat/network/packet/packet.h>

Client supports color rendering.

Definition at line 981 of file packet.h.

Referenced by handle_client_join_packet().

◆ CLIENT_CAP_STRETCH

#define CLIENT_CAP_STRETCH   0x08

#include <include/ascii-chat/network/packet/packet.h>

Client can stretch frames to fill terminal.

Definition at line 982 of file packet.h.

Referenced by handle_client_join_packet().

◆ CLIENT_CAP_VIDEO

#define CLIENT_CAP_VIDEO   0x01

#include <include/ascii-chat/network/packet/packet.h>

Client can send/receive video.

Definition at line 979 of file packet.h.

Referenced by handle_client_join_packet().

◆ CONNECT_TIMEOUT

#define CONNECT_TIMEOUT   3

#include <include/ascii-chat/network/network.h>

Connection timeout in seconds (3 seconds).

Maximum time to wait for connection establishment. Reduced from default for faster connection attempts and quicker failure detection.

Definition at line 98 of file network/network.h.

◆ FEATURE_DELTA_FRAMES

#define FEATURE_DELTA_FRAMES   0x02

#include <include/ascii-chat/network/packet/packet.h>

Delta frame encoding (future).

Definition at line 843 of file packet.h.

◆ FEATURE_RLE_ENCODING

#define FEATURE_RLE_ENCODING   0x01

#include <include/ascii-chat/network/packet/packet.h>

Feature flags for protocol_version_packet_t.

Bitmask values for optional protocol features. Used in protocol negotiation to enable advanced capabilities.

Run-length encoding support

Definition at line 842 of file packet.h.

◆ KEEPALIVE_COUNT

#define KEEPALIVE_COUNT   8

#include <include/ascii-chat/network/network.h>

Keepalive probe count (8 probes).

Number of keepalive probes to send before considering connection dead.

Definition at line 201 of file network/network.h.

Referenced by set_socket_keepalive().

◆ KEEPALIVE_IDLE

#define KEEPALIVE_IDLE   60

#include <include/ascii-chat/network/network.h>

Keepalive idle time in seconds (60 seconds).

Time to wait before sending first keepalive probe after connection becomes idle.

Definition at line 183 of file network/network.h.

Referenced by set_socket_keepalive().

◆ KEEPALIVE_INTERVAL

#define KEEPALIVE_INTERVAL   10

#include <include/ascii-chat/network/network.h>

Keepalive interval in seconds (10 seconds).

Interval between subsequent keepalive probes.

Definition at line 192 of file network/network.h.

Referenced by set_socket_keepalive().

◆ KEX_ALGO_X25519

#define KEX_ALGO_X25519   0x01

#include <include/ascii-chat/network/packet/packet.h>

X25519 key exchange (Curve25519).

Definition at line 1120 of file packet.h.

Referenced by client_handshake_receive(), crypto_handshake_server_send_parameters(), and peer_crypto_receive().

◆ LARGE_PACKET_EXTRA_TIMEOUT_PER_MB

#define LARGE_PACKET_EXTRA_TIMEOUT_PER_MB   (800LL * NS_PER_MS_INT)

#include <include/ascii-chat/network/packet/packet.h>

Extra timeout per MB for large packets (0.8 seconds per MB).

Additional timeout added per MB for large packets to prevent premature timeout failures.

Definition at line 180 of file packet.h.

Referenced by packet_send_timeout_ns().

◆ LARGE_PACKET_THRESHOLD

#define LARGE_PACKET_THRESHOLD   (100 * 1024)

#include <include/ascii-chat/network/packet/packet.h>

Large packet size threshold (100KB).

Packets larger than this threshold are considered "large" and may receive extended timeouts or compression treatment.

Definition at line 120 of file packet.h.

Referenced by packet_send_timeout_ns().

◆ MAX_CLIENT_TIMEOUT

#define MAX_CLIENT_TIMEOUT   (60LL * NS_PER_SEC_INT)

#include <include/ascii-chat/network/packet/packet.h>

Maximum client timeout in nanoseconds (60 seconds).

Maximum timeout value for client operations. Prevents excessively long waits for dead connections.

Definition at line 200 of file packet.h.

Referenced by packet_send_timeout_ns().

◆ MAX_ERROR_MESSAGE_LENGTH

#define MAX_ERROR_MESSAGE_LENGTH   4096

#include <include/ascii-chat/network/packet/packet.h>

Maximum error message length (512 bytes).

Error packets include a message payload. This defines the maximum number of bytes allowed in the message portion to prevent excessive allocations and potential abuse.

Definition at line 139 of file packet.h.

Referenced by acip_send_error(), network_error_decode(), packet_parse_error_message(), and packet_send_error().

◆ MAX_PACKET_SIZE

#define MAX_PACKET_SIZE   ((size_t)5 * 1024 * 1024)

#include <include/ascii-chat/network/packet/packet.h>

Maximum packet size (5MB).

Absolute maximum size for any packet. Packets larger than this are rejected to prevent memory exhaustion attacks.

Definition at line 130 of file packet.h.

Referenced by acip_send_audio_batch(), acip_send_audio_opus_batch(), packet_decoder_feed(), packet_header_build(), packet_send(), packet_validate(), receive_packet_secure_with_timeout(), and send_packet_secure().

◆ MAX_REMOTE_LOG_MESSAGE_LENGTH

#define MAX_REMOTE_LOG_MESSAGE_LENGTH   512

#include <include/ascii-chat/network/packet/packet.h>

Maximum remote log message length (512 bytes).

Remote logging packets mirror the error packet structure but are intended for diagnostic log forwarding between client and server. Limiting the payload size keeps allocations predictable and prevents log flooding over the network.

Definition at line 149 of file packet.h.

Referenced by handle_remote_log_packet_from_client(), packet_parse_remote_log(), and packet_send_remote_log().

◆ MIN_CLIENT_TIMEOUT

#define MIN_CLIENT_TIMEOUT   (10LL * NS_PER_SEC_INT)

#include <include/ascii-chat/network/packet/packet.h>

Minimum client timeout in nanoseconds (10 seconds).

Minimum timeout value for client operations. Set to server timeout plus buffer for reliable operation.

Definition at line 190 of file packet.h.

Referenced by packet_send_timeout_ns().

◆ network_is_test_environment

#define network_is_test_environment ( )

#include <include/ascii-chat/network/network.h>

Value:
((int)is_test_environment())

Check if we're in a test environment.

Compatibility macro that calls is_test_environment() from tests/test_env.h. Used to adjust timeouts for faster test execution.

Returns
1 if test environment, 0 otherwise

Definition at line 162 of file network/network.h.

Referenced by send_with_timeout().

◆ NETWORK_PACKET_COLORTERM_BUFFER_SIZE

#define NETWORK_PACKET_COLORTERM_BUFFER_SIZE   32

#include <include/ascii-chat/network/packet/packet.h>

Definition at line 77 of file packet.h.

◆ NETWORK_PACKET_PALETTE_CUSTOM_BUFFER_SIZE

#define NETWORK_PACKET_PALETTE_CUSTOM_BUFFER_SIZE   64

#include <include/ascii-chat/network/packet/packet.h>

Definition at line 78 of file packet.h.

◆ NETWORK_PACKET_TERM_TYPE_BUFFER_SIZE

#define NETWORK_PACKET_TERM_TYPE_BUFFER_SIZE   32

#include <include/ascii-chat/network/packet/packet.h>

Definition at line 76 of file packet.h.

◆ NETWORK_UPDATE_CHECKER_CURRENT_SHA_BUFFER_SIZE

#define NETWORK_UPDATE_CHECKER_CURRENT_SHA_BUFFER_SIZE   41

#include <include/ascii-chat/network/update_checker.h>

Definition at line 26 of file update_checker.h.

◆ NETWORK_UPDATE_CHECKER_CURRENT_VERSION_BUFFER_SIZE

#define NETWORK_UPDATE_CHECKER_CURRENT_VERSION_BUFFER_SIZE   64

#include <include/ascii-chat/network/update_checker.h>

Definition at line 25 of file update_checker.h.

◆ NETWORK_UPDATE_CHECKER_LATEST_SHA_BUFFER_SIZE

#define NETWORK_UPDATE_CHECKER_LATEST_SHA_BUFFER_SIZE   41

#include <include/ascii-chat/network/update_checker.h>

Definition at line 24 of file update_checker.h.

◆ NETWORK_UPDATE_CHECKER_LATEST_VERSION_BUFFER_SIZE

#define NETWORK_UPDATE_CHECKER_LATEST_VERSION_BUFFER_SIZE   64

#include <include/ascii-chat/network/update_checker.h>

Definition at line 23 of file update_checker.h.

◆ NETWORK_UPDATE_CHECKER_RELEASE_URL_BUFFER_SIZE

#define NETWORK_UPDATE_CHECKER_RELEASE_URL_BUFFER_SIZE   512

#include <include/ascii-chat/network/update_checker.h>

Definition at line 27 of file update_checker.h.

Referenced by update_check_perform().

◆ PACKET_MAGIC

#define PACKET_MAGIC   MAGIC_PACKET_VALID

#include <include/ascii-chat/network/packet/packet.h>

Packet magic number (alias for MAGIC_PACKET_VALID).

Magic number used in packet headers for packet validation. Defined once in util/magic.h (0xA5C11C4A1 = "ASCIICHAT" in hex) and imported here for use in packet validation.

See also
MAGIC_PACKET_VALID

Definition at line 270 of file packet.h.

Referenced by acds_receive_packet(), packet_decoder_feed(), packet_header_build(), packet_queue_validate_packet(), packet_send(), packet_validate(), and receive_packet_secure_with_timeout().

◆ PROTOCOL_FEATURE_IDENTITY_SET

#define PROTOCOL_FEATURE_IDENTITY_SET   0x08

#include <include/ascii-chat/network/packet/packet.h>

CLIENT_HELLO accepts a bounded set of pinned Ed25519 keys

Definition at line 845 of file packet.h.

◆ PROTOCOL_FEATURE_RECOVERABLE_ERRORS

#define PROTOCOL_FEATURE_RECOVERABLE_ERRORS   0x04

#include <include/ascii-chat/network/packet/packet.h>

Peer accepts nonfatal audio error notifications

Definition at line 844 of file packet.h.

Referenced by handle_protocol_version_packet().

◆ RECV_TIMEOUT

#define RECV_TIMEOUT   1

#include <include/ascii-chat/network/network.h>

Receive timeout in seconds (1 second).

Reduced to 1 second to allow snapshot mode to check elapsed time frequently and exit cleanly when snapshot_delay expires. With the longer 30-second timeout, clients in snapshot mode would block waiting for data and miss their snapshot window. The 1-second timeout balances responsiveness with network latency tolerance.

Definition at line 127 of file network/network.h.

Referenced by packet_receive(), and receive_packet_secure().

◆ REMOTE_LOG_FLAG_TRUNCATED

#define REMOTE_LOG_FLAG_TRUNCATED   0x0001U

#include <include/ascii-chat/network/packet/packet.h>

Remote log packet flag definitions.

Message payload was truncated to fit the maximum length

Definition at line 776 of file packet.h.

Referenced by handle_remote_log_packet_from_client(), and packet_send_remote_log().

◆ SEND_TIMEOUT

#define SEND_TIMEOUT   1

#include <include/ascii-chat/network/network.h>

Send timeout in seconds (5 seconds).

Maximum time to wait for data transmission. Video frames need timely delivery to maintain real-time performance.

Definition at line 111 of file network/network.h.

Referenced by packet_send_timeout_ns().

◆ STREAM_TYPE_AUDIO

#define STREAM_TYPE_AUDIO   0x02

◆ STREAM_TYPE_VIDEO

#define STREAM_TYPE_VIDEO   0x01

Enumeration Type Documentation

◆ auth_failure_reason_t

#include <include/ascii-chat/network/packet/packet.h>

Authentication failure reason flags.

Bitmask enumeration for authentication failure reasons. Multiple flags can be combined to indicate multiple failure causes.

Enumerator
AUTH_FAIL_PASSWORD_REQUIRED 

Server requires password but client didn't provide one.

AUTH_FAIL_PASSWORD_INCORRECT 

Password verification failed (incorrect password).

AUTH_FAIL_CLIENT_KEY_REQUIRED 

Server requires client key but client didn't provide one.

AUTH_FAIL_CLIENT_KEY_REJECTED 

Client key not in whitelist (access denied).

AUTH_FAIL_SIGNATURE_INVALID 

Client signature verification failed (invalid signature).

Definition at line 801 of file packet.h.

801 {
auth_failure_reason_t
Authentication failure reason flags.
Definition packet.h:801
@ AUTH_FAIL_PASSWORD_INCORRECT
Password verification failed (incorrect password).
Definition packet.h:805
@ AUTH_FAIL_PASSWORD_REQUIRED
Server requires password but client didn't provide one.
Definition packet.h:803
@ AUTH_FAIL_CLIENT_KEY_REQUIRED
Server requires client key but client didn't provide one.
Definition packet.h:807
@ AUTH_FAIL_CLIENT_KEY_REJECTED
Client key not in whitelist (access denied).
Definition packet.h:809
@ AUTH_FAIL_SIGNATURE_INVALID
Client signature verification failed (invalid signature).
Definition packet.h:811

◆ install_method_t

#include <include/ascii-chat/network/update_checker.h>

Installation method for suggesting upgrade command.

Enumerator
INSTALL_METHOD_HOMEBREW 

Installed via Homebrew.

INSTALL_METHOD_SCOOP 

Installed via Scoop.

INSTALL_METHOD_ARCH_AUR 

Installed via Arch AUR (paru/yay).

INSTALL_METHOD_INSTALLER 

Installed by ascii-chat.com installer.

INSTALL_METHOD_GITHUB 

Manual install from GitHub releases.

INSTALL_METHOD_UNKNOWN 

Unknown installation method.

Definition at line 53 of file update_checker.h.

53 {
install_method_t
Installation method for suggesting upgrade command.
@ INSTALL_METHOD_ARCH_AUR
Installed via Arch AUR (paru/yay).
@ INSTALL_METHOD_UNKNOWN
Unknown installation method.
@ INSTALL_METHOD_GITHUB
Manual install from GitHub releases.
@ INSTALL_METHOD_INSTALLER
Installed by ascii-chat.com installer.
@ INSTALL_METHOD_SCOOP
Installed via Scoop.
@ INSTALL_METHOD_HOMEBREW
Installed via Homebrew.

◆ packet_recv_result_t

#include <include/ascii-chat/network/packet/packet.h>

packet envelope is intentionally a small, concrete record. Fields such as type, data, len, was_encrypted give callers the identity and current value they need. It is normally borrowed from, or owned by, the surrounding subsystemβ€”not independently managed.

Packet reception result codes

Result codes for packet reception operations. Negative values indicate errors, zero indicates success.

Enumerator
PACKET_RECV_SUCCESS 

Packet received successfully.

PACKET_RECV_EOF 

Connection closed (EOF).

PACKET_RECV_ERROR 

Network error occurred.

PACKET_RECV_SECURITY_VIOLATION 

Encryption policy violation (e.g., unencrypted packet when encryption required).

Definition at line 1210 of file packet.h.

1210 {
1214 PACKET_RECV_EOF = -1,
1216 PACKET_RECV_ERROR = -2,
packet_recv_result_t
packet envelope is intentionally a small, concrete record. Fields such as type, data,...
Definition packet.h:1210
@ PACKET_RECV_ERROR
Network error occurred.
Definition packet.h:1216
@ PACKET_RECV_EOF
Connection closed (EOF).
Definition packet.h:1214
@ PACKET_RECV_SECURITY_VIOLATION
Encryption policy violation (e.g., unencrypted packet when encryption required).
Definition packet.h:1218
@ PACKET_RECV_SUCCESS
Packet received successfully.
Definition packet.h:1212

◆ packet_type_t

#include <include/ascii-chat/network/packet/packet.h>

Network protocol packet type enumeration.

Defines all packet types used in the ascii-chat network protocol. Packet types are organized by priority: protocol negotiation, then all cryptographic operations, then media, control, audio, and messages.

PACKET CATEGORIES (with expansion room):

  • 1: Protocol negotiation
  • 2-1001: Cryptographic operations (1000 space reserved)
    • 2: Client hello (expansion room: 3-101)
    • 102-201: Handshake/exchange (expansion room: 113-201)
    • 202-301: Rekeying (expansion room: 205-301)
  • 1002-2001: Message packets (1000 space reserved)
  • 2002-3001: Media frame packets (1000 space reserved)
  • 3002-4001: Audio packets (1000 space reserved)
  • 4002-5001: Control/state packets (1000 space reserved)
  • 6001+: Discovery/ACIP protocol
Note
Crypto handshake packets (2-12) and rekey packets (14-16) are ALWAYS sent unencrypted. Use packet_is_handshake_type() to check.
PACKET_TYPE_ENCRYPTED (13) is used for encrypted session packets after handshake completion.
Enumerator
PACKET_TYPE_PROTOCOL_VERSION 

Protocol version and capabilities negotiation.

PACKET_TYPE_CRYPTO_CLIENT_HELLO 

Client -> Server: Expected server key fingerprint for multi-key selection (UNENCRYPTED).

PACKET_TYPE_CRYPTO_CAPABILITIES 

Client -> Server: Supported crypto algorithms (UNENCRYPTED).

PACKET_TYPE_CRYPTO_PARAMETERS 

Server -> Client: Chosen algorithms + data sizes (UNENCRYPTED).

PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT 

Server -> Client: {server_pubkey[32]} (UNENCRYPTED).

PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP 

Client -> Server: {client_pubkey[32]} (UNENCRYPTED).

PACKET_TYPE_CRYPTO_AUTH_CHALLENGE 

Server -> Client: {nonce[32]} (UNENCRYPTED).

PACKET_TYPE_CRYPTO_AUTH_RESPONSE 

Client -> Server: {HMAC[32]} (UNENCRYPTED).

PACKET_TYPE_CRYPTO_AUTH_FAILED 

Server -> Client: "authentication failed" (UNENCRYPTED).

PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP 

Server -> Client: {HMAC[32]} server proves knowledge (UNENCRYPTED).

PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE 

Server -> Client: "encryption ready" (UNENCRYPTED).

PACKET_TYPE_CRYPTO_NO_ENCRYPTION 

Client -> Server: "I want to proceed without encryption" (UNENCRYPTED).

PACKET_TYPE_QUIC_READY 

Internal authenticated readiness barrier for ACIP over QUIC.

PACKET_TYPE_QUIC_KEYFRAME_REQUEST 

Internal authenticated request for a QUIC encoded-video keyframe.

PACKET_TYPE_CRYPTO_EPOCH_MARKER 
PACKET_TYPE_CRYPTO_REKEY_NEEDED 
PACKET_TYPE_CRYPTO_EPOCH_ACK 
PACKET_TYPE_ENCRYPTED 

Encrypted packet (after handshake completion).

PACKET_TYPE_CRYPTO_REKEY_REQUEST 

Initiator -> Responder: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey).

PACKET_TYPE_CRYPTO_REKEY_RESPONSE 

Responder -> Initiator: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey).

PACKET_TYPE_CRYPTO_REKEY_COMPLETE 

Initiator -> Responder: Empty (encrypted with NEW key, but still handshake).

PACKET_TYPE_SIZE_MESSAGE 

Terminal size message.

PACKET_TYPE_AUDIO_MESSAGE 

Audio message.

PACKET_TYPE_TEXT_MESSAGE 

Text message.

PACKET_TYPE_ERROR_MESSAGE 

Error packet with asciichat_error_t code and human-readable message.

PACKET_TYPE_REMOTE_LOG 

Bidirectional remote logging packet.

PACKET_TYPE_ASCII_FRAME 

Complete ASCII frame with all metadata.

PACKET_TYPE_IMAGE_FRAME 

Complete RGB image with dimensions.

PACKET_TYPE_IMAGE_FRAME_H265 

H.265-encoded frame from client (HEVC/H.265 codec).

PACKET_TYPE_IMAGE_FRAME_H264 

H.264 Annex B access unit: flags:u8, width:u16be, height:u16be, data.

PACKET_TYPE_AUDIO_BATCH 

Batched audio packets for efficiency.

PACKET_TYPE_AUDIO_OPUS_BATCH 

Batched Opus-encoded audio frames.

PACKET_TYPE_CLIENT_CAPABILITIES 

Client reports terminal capabilities.

PACKET_TYPE_PING 

Keepalive ping packet.

PACKET_TYPE_PONG 

Keepalive pong response.

PACKET_TYPE_CLIENT_JOIN 

Client announces capability to send media.

PACKET_TYPE_CLIENT_LEAVE 

Clean disconnect notification.

PACKET_TYPE_STREAM_START 

Client requests to start sending video/audio.

PACKET_TYPE_STREAM_STOP 

Client stops sending media.

PACKET_TYPE_CLEAR_CONSOLE 

Server tells client to clear console.

PACKET_TYPE_SERVER_STATE 

Server sends current state to clients.

PACKET_TYPE_ACIP_SESSION_CREATE 

Create new session (Client -> Discovery Server).

PACKET_TYPE_ACIP_SESSION_CREATED 

Session created response (Discovery Server -> Client).

PACKET_TYPE_ACIP_SESSION_LOOKUP 

Lookup session by string (Client -> Discovery Server).

PACKET_TYPE_ACIP_SESSION_INFO 

Session info response (Discovery Server -> Client).

PACKET_TYPE_ACIP_SESSION_JOIN 

Join existing session (Client -> Discovery Server).

PACKET_TYPE_ACIP_SESSION_JOINED 

Session joined response (Discovery Server -> Client).

PACKET_TYPE_ACIP_SESSION_LEAVE 

Leave session (Client -> Discovery Server).

PACKET_TYPE_ACIP_SESSION_END 

End session (Host -> Discovery Server).

PACKET_TYPE_ACIP_SESSION_RECONNECT 

Reconnect to session (Client -> Discovery Server).

PACKET_TYPE_ACIP_WEBRTC_SDP 

WebRTC SDP offer/answer (bidirectional).

PACKET_TYPE_ACIP_WEBRTC_ICE 

WebRTC ICE candidate (bidirectional).

PACKET_TYPE_ACIP_STRING_RESERVE 

Reserve session string (Client -> Discovery Server).

PACKET_TYPE_ACIP_STRING_RESERVED 

String reserved response (Discovery Server -> Client).

PACKET_TYPE_ACIP_STRING_RENEW 

Renew string reservation (Client -> Discovery Server).

PACKET_TYPE_ACIP_STRING_RELEASE 

Release string reservation (Client -> Discovery Server).

PACKET_TYPE_ACIP_PARTICIPANT_LIST 

Participant list with ring order (ACDS -> Participants).

PACKET_TYPE_ACIP_RING_COLLECT 

Ring collect request (Participant -> Next Participant).

PACKET_TYPE_ACIP_NETWORK_QUALITY 

NETWORK_QUALITY exchange (unified packet for all quality metrics).

PACKET_TYPE_ACIP_HOST_ANNOUNCEMENT 

Host announcement (Participant -> ACDS, "I won negotiation").

PACKET_TYPE_ACIP_HOST_DESIGNATED 

Host designated (ACDS -> All Participants).

PACKET_TYPE_ACIP_SETTINGS_SYNC 

Settings sync (Initiator -> Host -> All Participants).

PACKET_TYPE_ACIP_SETTINGS_ACK 

Settings acknowledgment (Participant -> Initiator).

PACKET_TYPE_ACIP_HOST_LOST 

Host lost notification - lightweight (Participant -> ACDS).

PACKET_TYPE_ACIP_FUTURE_HOST_ELECTED 

Future host elected announcement (Quorum Leader -> ACDS -> All Participants).

PACKET_TYPE_ACIP_PARTICIPANT_JOINED 

Participant joined notification (ACDS -> Existing Participants).

PACKET_TYPE_ACIP_PARTICIPANT_LEFT 

Participant left notification (ACDS -> Remaining Participants).

PACKET_TYPE_ACIP_BANDWIDTH_TEST 

Bandwidth test request (Client -> ACDS).

PACKET_TYPE_ACIP_BANDWIDTH_RESULT 

Bandwidth test results (ACDS -> Client).

PACKET_TYPE_ACIP_BROADCAST_ACK 

Broadcast acknowledgment for reliable delivery.

PACKET_TYPE_RING_MEMBERS 

Ring member list announcement (ACDS -> All Participants).

PACKET_TYPE_STATS_COLLECTION_START 

Start statistics collection phase (Coordinator -> All Participants).

PACKET_TYPE_STATS_UPDATE 

Statistics update during collection phase (Participant -> Next Participant).

PACKET_TYPE_RING_ELECTION_RESULT 

Final election result (Quorum Leader -> All Participants).

PACKET_TYPE_STATS_ACK 

Acknowledgment of statistics update (Participant -> Sender).

PACKET_TYPE_ACIP_DISCOVERY_PING 

Discovery server ping (keepalive).

PACKET_TYPE_ACIP_ERROR 

Generic error response (Discovery Server -> Client).

Definition at line 301 of file packet.h.

301 {
304
305 // ============================================================================
306 // Cryptographic Operations (Types 1000-1999)
307 // ============================================================================
308
309 // ============================================================================
310 // Cryptographic Client Hello (Types 1000-1099)
311 // ============================================================================
312
315
316 // ============================================================================
317 // Cryptographic Handshake (Types 1100-1199)
318 // ============================================================================
319 // ALWAYS UNENCRYPTED (except ENCRYPTED which wraps encrypted data)
320
348
349 // ============================================================================
350 // Cryptographic Rekeying (Types 1200-1299)
351 // ============================================================================
352 // ALWAYS UNENCRYPTED during rekey phase
353
362
363 // ============================================================================
364 // Message Packets (Types 2000-2999)
365 // ============================================================================
366
377
378 // ============================================================================
379 // Media Frames (Types 3000-3999)
380 // ============================================================================
381
390
391 // ============================================================================
392 // Audio Packets (Types 4000-4999)
393 // ============================================================================
394
399
400 // ============================================================================
401 // Control/State Packets (Types 5000-5999)
402 // ============================================================================
403
407 PACKET_TYPE_PING = 5001,
409 PACKET_TYPE_PONG = 5002,
422
423 // ============================================================================
424 // Discovery Service Protocol (ACDS) (Types 6000+)
425 // ============================================================================
426 // ACIP packets use range 6000+ for discovery mode operations
427 // See network/acip/protocol.h for protocol overview and utility functions
428 // See network/acip/acds.h for ACDS message structures and detailed documentation
429
448
453
462
463 // ============================================================================
464 // Ring Consensus Protocol (6050-6051)
465 // ============================================================================
466 // Used for proactive future host election every 5 minutes
467
472
473 // ============================================================================
474 // Host Negotiation & Migration Protocol (6060-6069)
475 // ============================================================================
476 // Used by discovery mode for dynamic host selection and failover
477
496
497 // ============================================================================
498 // Bandwidth Testing (6070-6071)
499 // ============================================================================
500 // Used for measuring network bandwidth during NAT quality detection
501
506
509
510 // ============================================================================
511 // Ring Consensus Protocol (6100-6104)
512 // ============================================================================
513 // Used for collecting statistics and coordinating ring-based consensus
514
525
526 // ============================================================================
527 // Discovery Service Keepalive (6190-6199)
528 // ============================================================================
529
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:301
@ PACKET_TYPE_CRYPTO_CLIENT_HELLO
Client -> Server: Expected server key fingerprint for multi-key selection (UNENCRYPTED).
Definition packet.h:314
@ PACKET_TYPE_ACIP_WEBRTC_ICE
WebRTC ICE candidate (bidirectional).
Definition packet.h:452
@ PACKET_TYPE_QUIC_READY
Internal authenticated readiness barrier for ACIP over QUIC.
Definition packet.h:342
@ PACKET_TYPE_AUDIO_OPUS_BATCH
Batched Opus-encoded audio frames.
Definition packet.h:398
@ PACKET_TYPE_ACIP_PARTICIPANT_JOINED
Participant joined notification (ACDS -> Existing Participants).
Definition packet.h:493
@ PACKET_TYPE_CLIENT_LEAVE
Clean disconnect notification.
Definition packet.h:413
@ PACKET_TYPE_ACIP_SESSION_INFO
Session info response (Discovery Server -> Client).
Definition packet.h:437
@ PACKET_TYPE_IMAGE_FRAME
Complete RGB image with dimensions.
Definition packet.h:385
@ PACKET_TYPE_CRYPTO_AUTH_RESPONSE
Client -> Server: {HMAC[32]} (UNENCRYPTED).
Definition packet.h:332
@ PACKET_TYPE_TEXT_MESSAGE
Text message.
Definition packet.h:372
@ PACKET_TYPE_AUDIO_MESSAGE
Audio message.
Definition packet.h:370
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:409
@ PACKET_TYPE_ACIP_SESSION_RECONNECT
Reconnect to session (Client -> Discovery Server).
Definition packet.h:447
@ PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE
Server -> Client: "encryption ready" (UNENCRYPTED).
Definition packet.h:338
@ PACKET_TYPE_ACIP_PARTICIPANT_LIST
Participant list with ring order (ACDS -> Participants).
Definition packet.h:469
@ PACKET_TYPE_STREAM_START
Client requests to start sending video/audio.
Definition packet.h:415
@ PACKET_TYPE_ACIP_SETTINGS_SYNC
Settings sync (Initiator -> Host -> All Participants).
Definition packet.h:485
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT
Server -> Client: {server_pubkey[32]} (UNENCRYPTED).
Definition packet.h:326
@ PACKET_TYPE_ACIP_ERROR
Generic error response (Discovery Server -> Client).
Definition packet.h:533
@ PACKET_TYPE_CRYPTO_EPOCH_MARKER
Definition packet.h:345
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP
Client -> Server: {client_pubkey[32]} (UNENCRYPTED).
Definition packet.h:328
@ PACKET_TYPE_CRYPTO_REKEY_COMPLETE
Initiator -> Responder: Empty (encrypted with NEW key, but still handshake).
Definition packet.h:361
@ PACKET_TYPE_ACIP_NETWORK_QUALITY
NETWORK_QUALITY exchange (unified packet for all quality metrics).
Definition packet.h:479
@ PACKET_TYPE_SERVER_STATE
Server sends current state to clients.
Definition packet.h:421
@ PACKET_TYPE_ACIP_HOST_LOST
Host lost notification - lightweight (Participant -> ACDS).
Definition packet.h:489
@ PACKET_TYPE_ACIP_BROADCAST_ACK
Broadcast acknowledgment for reliable delivery.
Definition packet.h:508
@ PACKET_TYPE_ACIP_HOST_DESIGNATED
Host designated (ACDS -> All Participants).
Definition packet.h:483
@ PACKET_TYPE_ACIP_STRING_RESERVE
Reserve session string (Client -> Discovery Server).
Definition packet.h:455
@ PACKET_TYPE_CRYPTO_AUTH_FAILED
Server -> Client: "authentication failed" (UNENCRYPTED).
Definition packet.h:334
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion).
Definition packet.h:355
@ PACKET_TYPE_CRYPTO_REKEY_REQUEST
Initiator -> Responder: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey).
Definition packet.h:357
@ PACKET_TYPE_SIZE_MESSAGE
Terminal size message.
Definition packet.h:368
@ PACKET_TYPE_REMOTE_LOG
Bidirectional remote logging packet.
Definition packet.h:376
@ PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP
Server -> Client: {HMAC[32]} server proves knowledge (UNENCRYPTED).
Definition packet.h:336
@ PACKET_TYPE_ACIP_HOST_ANNOUNCEMENT
Host announcement (Participant -> ACDS, "I won negotiation").
Definition packet.h:481
@ PACKET_TYPE_PROTOCOL_VERSION
Protocol version and capabilities negotiation.
Definition packet.h:303
@ PACKET_TYPE_ACIP_SETTINGS_ACK
Settings acknowledgment (Participant -> Initiator).
Definition packet.h:487
@ PACKET_TYPE_ACIP_BANDWIDTH_TEST
Bandwidth test request (Client -> ACDS).
Definition packet.h:503
@ PACKET_TYPE_CLEAR_CONSOLE
Server tells client to clear console.
Definition packet.h:419
@ PACKET_TYPE_CRYPTO_EPOCH_ACK
Definition packet.h:347
@ PACKET_TYPE_CRYPTO_NO_ENCRYPTION
Client -> Server: "I want to proceed without encryption" (UNENCRYPTED).
Definition packet.h:340
@ PACKET_TYPE_ACIP_SESSION_CREATE
Create new session (Client -> Discovery Server).
Definition packet.h:431
@ PACKET_TYPE_STATS_UPDATE
Statistics update during collection phase (Participant -> Next Participant).
Definition packet.h:520
@ PACKET_TYPE_ACIP_STRING_RESERVED
String reserved response (Discovery Server -> Client).
Definition packet.h:457
@ PACKET_TYPE_IMAGE_FRAME_H265
H.265-encoded frame from client (HEVC/H.265 codec).
Definition packet.h:387
@ PACKET_TYPE_ACIP_DISCOVERY_PING
Discovery server ping (keepalive).
Definition packet.h:531
@ PACKET_TYPE_CRYPTO_AUTH_CHALLENGE
Server -> Client: {nonce[32]} (UNENCRYPTED).
Definition packet.h:330
@ PACKET_TYPE_ASCII_FRAME
Complete ASCII frame with all metadata.
Definition packet.h:383
@ PACKET_TYPE_CRYPTO_PARAMETERS
Server -> Client: Chosen algorithms + data sizes (UNENCRYPTED).
Definition packet.h:324
@ PACKET_TYPE_ACIP_SESSION_LOOKUP
Lookup session by string (Client -> Discovery Server).
Definition packet.h:435
@ PACKET_TYPE_CRYPTO_CAPABILITIES
Client -> Server: Supported crypto algorithms (UNENCRYPTED).
Definition packet.h:322
@ PACKET_TYPE_RING_ELECTION_RESULT
Final election result (Quorum Leader -> All Participants).
Definition packet.h:522
@ PACKET_TYPE_ACIP_SESSION_LEAVE
Leave session (Client -> Discovery Server).
Definition packet.h:443
@ PACKET_TYPE_ACIP_PARTICIPANT_LEFT
Participant left notification (ACDS -> Remaining Participants).
Definition packet.h:495
@ PACKET_TYPE_ACIP_WEBRTC_SDP
WebRTC SDP offer/answer (bidirectional).
Definition packet.h:450
@ PACKET_TYPE_ACIP_SESSION_JOIN
Join existing session (Client -> Discovery Server).
Definition packet.h:439
@ PACKET_TYPE_ACIP_SESSION_CREATED
Session created response (Discovery Server -> Client).
Definition packet.h:433
@ PACKET_TYPE_CRYPTO_REKEY_NEEDED
Definition packet.h:346
@ PACKET_TYPE_STATS_ACK
Acknowledgment of statistics update (Participant -> Sender).
Definition packet.h:524
@ PACKET_TYPE_ACIP_STRING_RELEASE
Release string reservation (Client -> Discovery Server).
Definition packet.h:461
@ PACKET_TYPE_CLIENT_JOIN
Client announces capability to send media.
Definition packet.h:411
@ PACKET_TYPE_CLIENT_CAPABILITIES
Client reports terminal capabilities.
Definition packet.h:405
@ PACKET_TYPE_ACIP_FUTURE_HOST_ELECTED
Future host elected announcement (Quorum Leader -> ACDS -> All Participants).
Definition packet.h:491
@ PACKET_TYPE_IMAGE_FRAME_H264
Definition packet.h:389
@ PACKET_TYPE_ACIP_BANDWIDTH_RESULT
Bandwidth test results (ACDS -> Client).
Definition packet.h:505
@ PACKET_TYPE_ACIP_STRING_RENEW
Renew string reservation (Client -> Discovery Server).
Definition packet.h:459
@ PACKET_TYPE_ERROR_MESSAGE
Error packet with asciichat_error_t code and human-readable message.
Definition packet.h:374
@ PACKET_TYPE_RING_MEMBERS
Ring member list announcement (ACDS -> All Participants).
Definition packet.h:516
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:407
@ PACKET_TYPE_QUIC_KEYFRAME_REQUEST
Internal authenticated request for a QUIC encoded-video keyframe.
Definition packet.h:344
@ PACKET_TYPE_AUDIO_BATCH
Batched audio packets for efficiency.
Definition packet.h:396
@ PACKET_TYPE_CRYPTO_REKEY_RESPONSE
Responder -> Initiator: {new_ephemeral_pk[32]} (UNENCRYPTED during rekey).
Definition packet.h:359
@ PACKET_TYPE_ACIP_SESSION_JOINED
Session joined response (Discovery Server -> Client).
Definition packet.h:441
@ PACKET_TYPE_STATS_COLLECTION_START
Start statistics collection phase (Coordinator -> All Participants).
Definition packet.h:518
@ PACKET_TYPE_STREAM_STOP
Client stops sending media.
Definition packet.h:417
@ PACKET_TYPE_ACIP_RING_COLLECT
Ring collect request (Participant -> Next Participant).
Definition packet.h:471
@ PACKET_TYPE_ACIP_SESSION_END
End session (Host -> Discovery Server).
Definition packet.h:445

Function Documentation

◆ accept_with_timeout()

int accept_with_timeout ( socket_t listenfd,
struct sockaddr * addr,
socklen_t * addrlen,
uint64_t timeout_ns )

#include <include/ascii-chat/network/network.h>

Accept connection with timeout.

Parameters
listenfdListening socket file descriptor
addrOutput: Client address structure (can be NULL)
addrlenInput/output: Length of address structure
timeout_nsTimeout in nanoseconds
Returns
Client socket file descriptor on success, -1 on error

Accepts incoming connection with timeout support. Waits up to timeout_ns for incoming connection.

Note
If addr is NULL, client address information is not returned.
addrlen must point to initialized value containing size of addr buffer. On return, contains actual size of address.
Parameters
listenfdListening socket
addrClient address structure
addrlenLength of address structure
timeout_secondsTimeout in seconds
Returns
Client socket, or -1 on error

Definition at line 283 of file network/network.c.

283 {
284 // Set up poll for accept timeout (in nanoseconds)
285 struct pollfd pfd;
286 pfd.fd = listenfd;
287 pfd.events = POLLIN;
288 pfd.revents = 0;
289
290 int result = socket_poll(&pfd, 1, (int64_t)timeout_ns);
291
292 if (result <= 0) {
293 if (result == 0) {
294 // Timeout is expected behavior for server waiting for connections
295 errno_set_with_system_error(ERROR_NETWORK_TIMEOUT, __FILE__, __LINE__, __func__, ETIMEDOUT);
296 return -1;
297 }
298
299 if (network_handle_select_error(result)) {
300 return -1; // Don't retry for accept
301 }
302 return -1;
303 }
304
305 // Check if socket is ready
306 if (!(pfd.revents & POLLIN)) {
307 errno_set_with_system_error(ERROR_NETWORK_TIMEOUT, __FILE__, __LINE__, __func__, ETIMEDOUT);
308 return -1;
309 }
310
311 // Check if socket is still valid before attempting accept
312 if (listenfd == INVALID_SOCKET_VALUE) {
313 SET_ERRNO(ERROR_NETWORK, "listening socket is closed");
314 return -1;
315 }
316
317 socket_t accept_result = socket_accept(listenfd, addr, addrlen, "connection");
318
319 if (accept_result == INVALID_SOCKET_VALUE) {
320 // Check if this is a socket closed error (common during shutdown)
321 int error_code = socket_get_last_error();
322
323 if (socket_is_invalid_socket_error(error_code)) {
324 // During shutdown, don't log this as an error since it's expected behavior
325 SET_ERRNO(ERROR_NETWORK, "Network operation failed");
326 } else {
327 SET_ERRNO_SYS(ERROR_NETWORK_BIND, "accept failed: %s", socket_get_error_string());
328 }
329 return -1;
330 }
331
332 return (int)accept_result;
333}
asciichat_error_t errno_set_with_system_error(asciichat_error_t code, const char *file, int line, const char *function, int sys_errno)
#define SET_ERRNO(code,...)
#define SET_ERRNO_SYS(code,...)
@ ERROR_NETWORK_BIND
Definition error_codes.h:73
@ ERROR_NETWORK
Definition error_codes.h:72
@ ERROR_NETWORK_TIMEOUT
Definition error_codes.h:75
#define ETIMEDOUT
int socket_t

References errno_set_with_system_error(), ERROR_NETWORK, ERROR_NETWORK_BIND, ERROR_NETWORK_TIMEOUT, ETIMEDOUT, SET_ERRNO, and SET_ERRNO_SYS.

◆ av_send_audio_opus_batch()

asciichat_error_t av_send_audio_opus_batch ( socket_t sockfd,
const uint8_t * opus_data,
size_t opus_size,
const uint16_t * frame_sizes,
int sample_rate,
int frame_duration,
int frame_count,
crypto_context_t * crypto_ctx )

#include <include/ascii-chat/network/packet/packet.h>

Send Opus-encoded audio batch packet with encryption support.

Parameters
sockfdSocket file descriptor
opus_dataOpus-encoded audio data buffer
opus_sizeSize of Opus-encoded data in bytes
frame_sizesArray of frame sizes (one per Opus frame)
sample_rateSample rate in Hz (e.g., 48000)
frame_durationFrame duration in milliseconds (e.g., 20)
frame_countNumber of Opus frames in batch
crypto_ctxCryptographic context for encryption (NULL for plaintext)
Returns
ASCIICHAT_OK on success, error code on failure

Sends PACKET_TYPE_AUDIO_OPUS_BATCH with multiple Opus-encoded frames. Opus provides better compression than raw audio (30-100 bytes per 20ms frame).

Note
Opus packets are NOT compressed again (already compressed by Opus codec).
Encryption is applied automatically when crypto_ctx is provided.

Definition at line 1000 of file packet.c.

1002 {
1003 if (!opus_data || opus_size == 0 || !frame_sizes || sample_rate <= 0 || frame_duration <= 0 || frame_count <= 0) {
1005 "Invalid Opus batch parameters: opus_data=%p, opus_size=%zu, frame_sizes=%p, sample_rate=%d, "
1006 "frame_duration=%d, frame_count=%d",
1007 (const void *)opus_data, opus_size, (const void *)frame_sizes, sample_rate, frame_duration,
1008 frame_count);
1009 }
1010
1011 // Validate frame_count to prevent integer overflow in size calculations
1012 // Max reasonable frames per batch: ~1 second of 10ms frames = 100 frames
1013 if (frame_count > 1000) {
1014 return SET_ERRNO(ERROR_INVALID_PARAM, "Too many Opus frames: %d (max 1000)", frame_count);
1015 }
1016
1017 // Allocate buffer for header + frame sizes + encoded data
1018 size_t header_size = 16; // sample_rate (4), frame_duration (4), frame_count (4), reserved (4)
1019 size_t frame_sizes_bytes = (size_t)frame_count * sizeof(uint16_t);
1020 size_t total_size = header_size + frame_sizes_bytes + opus_size;
1021 void *packet_data = buffer_alloc(NULL, total_size);
1022 if (!packet_data) {
1023 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for Opus batch packet: %zu bytes", total_size);
1024 }
1025
1026 // Write header (network byte order for cross-platform compatibility)
1027 uint8_t *buf = (uint8_t *)packet_data;
1028 uint32_t sr = HOST_TO_NET_U32((uint32_t)sample_rate);
1029 uint32_t fd = HOST_TO_NET_U32((uint32_t)frame_duration);
1030 uint32_t fc = HOST_TO_NET_U32((uint32_t)frame_count);
1031 memcpy(buf, &sr, 4);
1032 memcpy(buf + 4, &fd, 4);
1033 memcpy(buf + 8, &fc, 4);
1034 memset(buf + 12, 0, 4); // Reserved
1035
1036 // Write frame sizes array (convert each to network byte order)
1037 uint16_t *frame_sizes_out = (uint16_t *)(buf + header_size);
1038 for (int i = 0; i < frame_count; i++) {
1039 frame_sizes_out[i] = HOST_TO_NET_U16(frame_sizes[i]);
1040 }
1041
1042 // Copy Opus data
1043 memcpy(buf + header_size + frame_sizes_bytes, opus_data, opus_size);
1044
1045 // Send packet (with encryption support)
1046 asciichat_error_t result =
1047 send_packet_secure(sockfd, PACKET_TYPE_AUDIO_OPUS_BATCH, packet_data, total_size, crypto_ctx);
1048
1049 // Clean up
1050 buffer_release(packet_data);
1051
1052 return result;
1053}
void * buffer_alloc(buffer_domain_t *domain, size_t size)
Definition buffer.c:274
#define buffer_release(data)
Definition buffer.h:91
#define HOST_TO_NET_U16(val)
Definition endian.h:96
#define HOST_TO_NET_U32(val)
Definition endian.h:66
unsigned short uint16_t
Definition common.h:61
unsigned int uint32_t
Definition common.h:62
unsigned char uint8_t
Definition common.h:60
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_MEMORY
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
asciichat_error_t send_packet_secure(socket_t sockfd, packet_type_t type, const void *data, size_t len, crypto_context_t *crypto_ctx)
Send a packet with encryption and compression support.
Definition packet.c:492

References buffer_alloc(), buffer_release, ERROR_INVALID_PARAM, ERROR_MEMORY, HOST_TO_NET_U16, HOST_TO_NET_U32, PACKET_TYPE_AUDIO_OPUS_BATCH, send_packet_secure(), and SET_ERRNO.

◆ connect_with_timeout()

bool connect_with_timeout ( socket_t sockfd,
const struct sockaddr * addr,
socklen_t addrlen,
int timeout_seconds )

#include <include/ascii-chat/network/network.h>

Connect to server with timeout.

Parameters
sockfdSocket file descriptor
addrServer address structure
addrlenAddress structure length
timeout_secondsTimeout in seconds
Returns
true on success, false on failure

Establishes connection to server with timeout support. Waits up to timeout_seconds for connection to complete.

Note
Uses platform-specific connection timeout mechanism (select/poll).
Returns false on timeout or connection failure. Use network_error_string() to get human-readable error description.

Connect to server with timeout.

Parameters
sockfdSocket file descriptor
addrAddress to connect to
addrlenAddress length
timeout_secondsTimeout in seconds
Returns
true on success, false on failure

Definition at line 466 of file network/network.c.

466 {
467 if (sockfd == INVALID_SOCKET_VALUE) {
468 errno = EBADF;
469 return false;
470 }
471
472 // Set socket to non-blocking for timeout control
473 if (set_socket_nonblocking(sockfd) != ASCIICHAT_OK) {
474 return false;
475 }
476
477 // Attempt connection
478 int result = connect(sockfd, addr, addrlen);
479
480 if (result == 0) {
481 // Connected immediately
482 if (socket_set_blocking(sockfd) != 0) {
483 return false;
484 }
485 return true;
486 }
487
488 // Check if connection is in progress (expected for non-blocking sockets)
489 int error = socket_get_last_error();
490 if (!socket_is_in_progress_error(error) && !socket_is_would_block_error(error)) {
491 log_warn("TCP connection could not start (socket error %d)", error);
492 return false;
493 }
494
495 // Use select to wait for connection with timeout
496 fd_set write_fds;
497 struct timeval timeout;
498
499 socket_fd_zero(&write_fds);
500 socket_fd_set(sockfd, &write_fds);
501
502 timeout.tv_sec = timeout_seconds;
503 timeout.tv_usec = 0;
504
505 result = socket_select(sockfd, NULL, &write_fds, NULL, &timeout);
506
507 if (result <= 0) {
508 log_warn("TCP connection wait failed (result %d, socket error %d)", result,
509 result == 0 ? 0 : socket_get_last_error());
510 return false; // Timeout or error
511 }
512
513 if (!socket_fd_isset(sockfd, &write_fds)) {
514 return false; // Socket not ready
515 }
516
517 // Check if connection was successful
518 int error_code = 0;
519 socklen_t error_len = sizeof(error_code);
520
521 if (socket_getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error_code, &error_len) != 0) {
522 return false;
523 }
524
525 if (error_code != 0) {
526 log_warn("TCP connection failed (socket error %d)", error_code);
527 return false;
528 }
529
530 // Connection successful - restore blocking mode
531 if (socket_set_blocking(sockfd) != 0) {
532 log_warn("Failed to restore socket to blocking mode after connect");
533 // Continue anyway - non-blocking mode works but may cause issues with send/recv
534 }
535
536 return true;
537}
@ ASCIICHAT_OK
Definition error_codes.h:46
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
asciichat_error_t set_socket_nonblocking(socket_t sockfd)
Set socket non-blocking.
#define EBADF
int errno

References ASCIICHAT_OK, EBADF, errno, log_warn, and set_socket_nonblocking().

◆ dns_test_connectivity()

bool dns_test_connectivity ( const char * hostname)

#include <include/ascii-chat/network/dns.h>

Test DNS connectivity by resolving a hostname.

Attempts to resolve the given hostname to verify DNS connectivity. Uses a short timeout to avoid blocking.

Parameters
hostnameHostname to resolve (e.g., "api.github.com")
Returns
true if DNS resolution succeeds, false otherwise
Note
Logs warnings on failure
IPv4 resolution only (AF_INET)

Definition at line 13 of file dns.c.

13 {
14 ASCII_OTEL_SCOPE(DNS_TEST_CONNECTIVITY);
15 if (!hostname) {
16 log_warn("NULL hostname provided to DNS connectivity test");
17 return false;
18 }
19
20 char ascii_hostname[HOSTNAME_BUFFER_SIZE];
21 asciichat_error_t normalized = hostname_to_ascii(hostname, ascii_hostname, sizeof(ascii_hostname));
22 if (normalized != ASCIICHAT_OK)
23 return false;
24 hostname = ascii_hostname;
25
26 struct addrinfo hints, *result = NULL;
27 memset(&hints, 0, sizeof(hints));
28 /* Connectivity is not IPv4-specific. Restricting this probe to AF_INET
29 * reports a false DNS failure on IPv6-only or IPv6-preferred networks even
30 * though the HTTP client can connect successfully. */
31 hints.ai_family = AF_UNSPEC;
32 hints.ai_socktype = SOCK_STREAM;
33
34 log_debug("Testing DNS connectivity to %s...", hostname);
35 log_debug("β˜… ABOUT TO CALL getaddrinfo for %s", hostname);
36
37 /* Resolve the same service used by the update request. Passing a NULL
38 * service is rejected by some Windows resolver configurations even though
39 * hostname resolution itself is healthy. */
40 int ret = getaddrinfo(hostname, "443", &hints, &result);
41 log_debug("β˜… getaddrinfo returned %d", ret);
42 if (ret != 0) {
43 log_warn("DNS resolution failed for %s: %s", hostname, gai_strerror(ret));
44 return false;
45 }
46
47 if (result) {
48 freeaddrinfo(result);
49 }
50
51 log_debug("DNS connectivity test succeeded for %s", hostname);
52 return true;
53}
#define HOSTNAME_BUFFER_SIZE
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
asciichat_error_t hostname_to_ascii(const char *hostname, char *output, size_t capacity)
Definition url.c:103
#define ASCII_OTEL_SCOPE(id)
Definition otel.h:72

References ASCII_OTEL_SCOPE, ASCIICHAT_OK, HOSTNAME_BUFFER_SIZE, hostname_to_ascii(), log_debug, and log_warn.

◆ frame_check_size_overflow()

asciichat_error_t frame_check_size_overflow ( size_t header_size,
size_t data_size )

#include <include/ascii-chat/network/frame_validator.h>

Check for overflow when adding header to data.

Parameters
header_sizeHeader size in bytes
data_sizeData size in bytes
Returns
ASCIICHAT_OK if safe, ERROR_BUFFER_OVERFLOW if would overflow

Definition at line 18 of file network/frame_validator.c.

18 {
19 if (data_size > SIZE_MAX - header_size) {
21 format_bytes_pretty(data_size, size_str, sizeof(size_str));
22 SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Frame size overflow: %s", size_str);
24 }
25 return ASCIICHAT_OK;
26}
@ ERROR_BUFFER_OVERFLOW
void format_bytes_pretty(size_t bytes, char *out, size_t out_capacity)
Format byte count into human-readable string.
Definition util/format.c:11
#define NETWORK_FRAME_VALIDATOR_SIZE_BUFFER_SIZE

References ASCIICHAT_OK, ERROR_BUFFER_OVERFLOW, format_bytes_pretty(), NETWORK_FRAME_VALIDATOR_SIZE_BUFFER_SIZE, and SET_ERRNO.

Referenced by frame_validate_legacy(), frame_validate_new(), and handle_image_frame_packet().

◆ frame_extract_dimensions()

void frame_extract_dimensions ( const void * data,
uint32_t * width,
uint32_t * height )

#include <include/ascii-chat/network/frame_validator.h>

Extract width and height from frame header.

Parameters
dataPacket data (must be at least 8 bytes)
widthOutput: frame width
heightOutput: frame height

Definition at line 89 of file network/frame_validator.c.

89 {
90 uint32_t width_net, height_net;
91 memcpy(&width_net, data, FRAME_HEADER_FIELD_SIZE);
92 memcpy(&height_net, (char *)data + FRAME_HEADER_FIELD_SIZE, FRAME_HEADER_FIELD_SIZE);
93 *width = NET_TO_HOST_U32(width_net);
94 *height = NET_TO_HOST_U32(height_net);
95}
#define NET_TO_HOST_U32(val)
Definition endian.h:81
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
#define FRAME_HEADER_FIELD_SIZE
Size of each header field (uint32_t = 4 bytes).

References FRAME_HEADER_FIELD_SIZE, height, NET_TO_HOST_U32, and width.

◆ frame_extract_new_header()

void frame_extract_new_header ( const void * data,
uint32_t * compressed,
uint32_t * data_size )

#include <include/ascii-chat/network/frame_validator.h>

Extract compressed flag and data size from new format header.

Parameters
dataPacket data (must be at least 16 bytes)
compressedOutput: whether data is compressed
data_sizeOutput: size of data field

Definition at line 97 of file network/frame_validator.c.

97 {
98 uint32_t compressed_net, size_net;
99 memcpy(&compressed_net, (char *)data + FRAME_HEADER_FIELD_SIZE * 2, FRAME_HEADER_FIELD_SIZE);
100 memcpy(&size_net, (char *)data + FRAME_HEADER_FIELD_SIZE * 3, FRAME_HEADER_FIELD_SIZE);
101 *compressed = NET_TO_HOST_U32(compressed_net);
102 *data_size = NET_TO_HOST_U32(size_net);
103}

References FRAME_HEADER_FIELD_SIZE, and NET_TO_HOST_U32.

Referenced by frame_validate_new().

◆ frame_validate_legacy()

asciichat_error_t frame_validate_legacy ( size_t len,
size_t expected_rgb_size )

#include <include/ascii-chat/network/frame_validator.h>

Validate legacy frame format.

Parameters
lenTotal packet length
expected_rgb_sizeExpected RGB data size (width * height * 3)
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 28 of file network/frame_validator.c.

28 {
29 // Check minimum header size
30 if (len < FRAME_HEADER_SIZE_LEGACY) {
31 SET_ERRNO(ERROR_INVALID_FRAME, "Legacy frame header too small: %zu bytes", len);
33 }
34
35 // Check for overflow
37 if (err != ASCIICHAT_OK) {
38 return err;
39 }
40
41 size_t expected_total = FRAME_HEADER_SIZE_LEGACY + expected_rgb_size;
42 if (len != expected_total) {
43 SET_ERRNO(ERROR_INVALID_FRAME, "Legacy frame length mismatch: expected %zu got %zu", expected_total, len);
45 }
46
47 return ASCIICHAT_OK;
48}
@ ERROR_INVALID_FRAME
asciichat_error_t frame_check_size_overflow(size_t header_size, size_t data_size)
Check for overflow when adding header to data.
#define FRAME_HEADER_SIZE_LEGACY
Legacy frame header size (width:4 + height:4).

References ASCIICHAT_OK, ERROR_INVALID_FRAME, frame_check_size_overflow(), FRAME_HEADER_SIZE_LEGACY, and SET_ERRNO.

Referenced by handle_image_frame_packet().

◆ frame_validate_new()

asciichat_error_t frame_validate_new ( void * data,
size_t len,
bool * out_compressed,
uint32_t * out_data_size )

#include <include/ascii-chat/network/frame_validator.h>

Validate new frame format with optional compression.

Parameters
dataPacket data
lenTotal packet length
out_compressedOutput: whether data is compressed (optional)
out_data_sizeOutput: size of compressed/uncompressed data (optional)
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 50 of file network/frame_validator.c.

50 {
51 // Check minimum new format header size
52 if (len < FRAME_HEADER_SIZE_NEW) {
53 SET_ERRNO(ERROR_INVALID_FRAME, "New frame header too small: %zu bytes", len);
55 }
56
57 uint32_t compressed_flag, data_size;
58 frame_extract_new_header(data, &compressed_flag, &data_size);
59
60 size_t data_size_sz = (size_t)data_size;
61
62 // Check data size against maximum
63 if (data_size_sz > IMAGE_MAX_PIXELS_SIZE) {
65 format_bytes_pretty(data_size_sz, size_str, sizeof(size_str));
66 SET_ERRNO(ERROR_INVALID_FRAME, "Frame data too large: %s", size_str);
68 }
69
70 // Check for overflow
72 if (err != ASCIICHAT_OK) {
73 return err;
74 }
75
76 size_t expected_total = FRAME_HEADER_SIZE_NEW + data_size_sz;
77 if (len != expected_total) {
78 SET_ERRNO(ERROR_INVALID_FRAME, "New frame length mismatch: expected %zu got %zu", expected_total, len);
80 }
81
82 if (out_compressed)
83 *out_compressed = (compressed_flag != 0);
84 if (out_data_size)
85 *out_data_size = data_size;
86 return ASCIICHAT_OK;
87}
void frame_extract_new_header(const void *data, uint32_t *compressed, uint32_t *data_size)
Extract compressed flag and data size from new format header.
#define IMAGE_MAX_PIXELS_SIZE
Maximum pixel data size in bytes.
#define FRAME_HEADER_SIZE_NEW
New frame header size (width:4 + height:4 + compressed:4 + size:4).

References ASCIICHAT_OK, ERROR_INVALID_FRAME, format_bytes_pretty(), frame_check_size_overflow(), frame_extract_new_header(), FRAME_HEADER_SIZE_NEW, IMAGE_MAX_PIXELS_SIZE, NETWORK_FRAME_VALIDATOR_SIZE_BUFFER_SIZE, and SET_ERRNO.

◆ https_get()

char * https_get ( const char * hostname,
const char * path )

#include <include/ascii-chat/network/http/client/client.h>

Perform HTTPS GET request.

Parameters
hostnameServer hostname (e.g., "github.com", must not be NULL)
pathResource path (e.g., "/username.keys", must not be NULL)
Returns
Allocated string containing response body (caller must free), or NULL on error

Makes a secure HTTPS connection to the specified hostname and fetches the resource at the given path. Uses system CA certificates for validation.

Note
TLS connection: Uses BearSSL for TLS connections. Validates server certificate against system CA certificates.
Memory management: Returns allocated string that caller must free. Use SAFE_FREE() to free the returned string.
Error handling: Returns NULL on error (network error, TLS error, etc.). Error messages are logged via logging system.
Hostname format: Hostname should be domain name only (no "https://" prefix). Example: "github.com", not "https://github.com".
Path format: Path should start with "/" for absolute paths. Example: "/username.keys", not "username.keys".
Response body: Returns complete response body as null-terminated string. Response may contain multiple keys (one per line for SSH keys).
Warning
Network dependency: Requires network connectivity and valid hostname. May fail if network is unavailable or hostname is unreachable.
Memory leak: Caller must free returned string using SAFE_FREE(). Function allocates memory that must be freed.
Certificate validation: Uses system CA certificates for validation. May fail if system CA certificates are missing or outdated.

Definition at line 180 of file lib/network/http/client/client.c.

180 {
181 ASCII_OTEL_SCOPE(HTTP);
182 ascii_otel_http(&otel_scope, hostname, path);
183 char *body = NULL;
184 if (!hostname || !path) {
185 log_error("Invalid arguments");
186 return NULL;
187 }
188
189 char ascii_hostname[HOSTNAME_BUFFER_SIZE];
190 asciichat_error_t normalized = hostname_to_ascii(hostname, ascii_hostname, sizeof(ascii_hostname));
191 if (normalized != ASCIICHAT_OK)
192 return NULL;
193 hostname = ascii_hostname;
194
195 log_info("HTTPS GET https://%s%s", hostname, path);
196
197 // Load system CA certificates
198 char *pem_data = NULL;
199 size_t pem_size = 0;
200 if (platform_load_system_ca_certs(&pem_data, &pem_size) != 0) {
201 log_error("Failed to load system CA certificates");
202 return NULL;
203 }
204
205 // Parse PEM certificates into BearSSL trust anchors
207 defer(free_http_anchors(&anchors));
208 size_t num_anchors = read_trust_anchors_from_memory(&anchors, (unsigned char *)pem_data, pem_size);
209 SAFE_FREE(pem_data);
210
211 if (num_anchors == 0) {
212 log_error("No trust anchors loaded");
213 return NULL;
214 }
215 log_info("Loaded %zu trust anchors", num_anchors);
216
217 // Resolve hostname using getaddrinfo (modern, non-deprecated API)
218 struct addrinfo hints, *result;
219 memset(&hints, 0, sizeof(hints));
220 hints.ai_family = AF_INET; // IPv4
221 hints.ai_socktype = SOCK_STREAM;
222 hints.ai_protocol = IPPROTO_TCP;
223
224 int dns_result;
225 {
226 ASCII_OTEL_SCOPE(DNS_RESOLVE);
227 if (otel_scope.retained)
228 gotel_attr_string(&otel_scope.scope, "dns.question.name", hostname);
229 dns_result = getaddrinfo(hostname, "443", &hints, &result);
230 if (dns_result && otel_scope.retained)
231 gotel_error(&otel_scope.scope, "dns.error", "DNS lookup failed");
232 }
233 if (dns_result != 0) {
234 log_error("Failed to resolve hostname: %s", hostname);
235 return NULL;
236 }
237
238 defer(if (result) freeaddrinfo(result));
239
240 // Create TCP socket
241 socket_t sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
242 if (sock == INVALID_SOCKET_VALUE) {
243 log_error("Failed to create socket");
244 return NULL;
245 }
246
247 defer(if (sock != INVALID_SOCKET_VALUE) socket_close(sock));
248
249 // Set socket timeouts to avoid blocking indefinitely on stalled connections.
250 // Windows expects a DWORD timeout in milliseconds, while POSIX expects a
251 // struct timeval. Passing timeval to Winsock makes the timeout effectively
252 // a few milliseconds and commonly aborts the TLS handshake.
253#ifdef _WIN32
254 DWORD timeout_ms = 5000;
255 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout_ms, sizeof(timeout_ms));
256 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout_ms, sizeof(timeout_ms));
257#else
258 struct timeval timeout = {.tv_sec = 5, .tv_usec = 0};
259 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout));
260 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout));
261#endif
262
263 // Connect to server
264 if (connect(sock, result->ai_addr, (int)result->ai_addrlen) != 0) {
265 log_error("Failed to connect to %s:443", hostname);
266 return NULL;
267 }
268
269 freeaddrinfo(result);
270 result = NULL;
271
272 log_info("Connected to %s:443", hostname);
273
274 // Initialize BearSSL X.509 minimal validator
275 br_x509_minimal_context xc;
276 br_x509_minimal_init(&xc, &br_sha256_vtable, anchors.buf, anchors.ptr);
277
278 // Initialize BearSSL client context
279 br_ssl_client_context sc;
280 br_ssl_client_init_full(&sc, &xc, anchors.buf, anchors.ptr);
281
282 // Set I/O buffer
283 const size_t iobuf_size = BR_SSL_BUFSIZE_BIDI;
284 SAFE_MALLOC_DEFER_FREE(iobuf, iobuf_size, unsigned char *);
285 br_ssl_engine_set_buffer(&sc.eng, iobuf, BR_SSL_BUFSIZE_BIDI, 1);
286
287 // Initialize I/O context
288 br_sslio_context ioc;
289 br_sslio_init(&ioc, &sc.eng, sock_read, &sock, sock_write, &sock);
290
291 // Start TLS handshake
292 br_ssl_client_reset(&sc, hostname, 0);
293
294 log_info("Starting TLS handshake with %s", hostname);
295
296 // Build HTTP request
297 char request[BUFFER_SIZE_LARGE];
298 int request_len = safe_snprintf(request, sizeof(request),
299 "GET %s HTTP/1.1\r\n"
300 "Host: %s\r\n"
301 "Connection: close\r\n"
302 "User-Agent: ascii-chat/" ASCII_CHAT_VERSION_STRING "\r\n"
303 "\r\n",
304 path, hostname);
305
306 // Send HTTP request over TLS
307 if (br_sslio_write_all(&ioc, request, (size_t)request_len) != 0) {
308 log_error("Failed to send HTTP request");
309 return NULL;
310 }
311
312 br_sslio_flush(&ioc);
313 log_info("Sent HTTP request");
314
315 // Read HTTP response
316 size_t response_capacity = 8192;
317 size_t response_len = 0;
318 SAFE_MALLOC_DEFER_FREE(response_buf, response_capacity, char *);
319
320 while (1) {
321 // Ensure we have space to read
322 if (response_len + 1024 > response_capacity) {
323 response_capacity *= 2;
324 if (response_capacity > HTTPS_MAX_RESPONSE_SIZE) {
325 log_error("Response exceeds maximum size (%d bytes)", HTTPS_MAX_RESPONSE_SIZE);
326 return NULL;
327 }
328 response_buf = SAFE_REALLOC(response_buf, response_capacity, char *);
329 }
330
331 // Read data (reserve 1 byte for null terminator)
332 int n = br_sslio_read(&ioc, response_buf + response_len, response_capacity - response_len - 1);
333 if (n < 0) {
334 // Check for TLS errors
335 int err = br_ssl_engine_last_error(&sc.eng);
336 if (err != BR_ERR_OK) {
337 log_error("TLS error: %d", err);
338 return NULL;
339 }
340 // EOF or connection closed
341 break;
342 }
343 if (n == 0) {
344 break; // EOF
345 }
346
347 response_len += (size_t)n;
348 }
349
350 response_buf[response_len] = '\0';
351 log_info("Received %zu bytes", response_len);
352
353 // Close connection
354 br_sslio_close(&ioc);
355 socket_close(sock);
356 sock = INVALID_SOCKET_VALUE;
357
358 // Parse HTTP response
359 int code = parse_http_status(response_buf, response_len);
360 if (code >= 0 && otel_scope.retained)
361 gotel_http_attributes(&otel_scope.scope, "GET", code);
362 if (code != 200) {
363 if (code < 0)
364 SET_ERRNO(ERROR_NETWORK, "Invalid HTTP response: %s", response_buf);
365 else
366 SET_ERRNO(ERROR_NETWORK, "HTTP request failed: %.50s", response_buf);
367 return NULL;
368 }
369
370 body = extract_http_body(response_buf, response_len);
371 if (body)
372 ascii_otel_http_status(&otel_scope, 200);
373
374 return body;
375}
#define BUFFER_SIZE_LARGE
Large buffer size (1024 bytes).
#define SAFE_REALLOC(ptr, size, cast)
Definition common.h:285
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC_DEFER_FREE(name, size, cast)
Definition common.h:386
br_x509_trust_anchor * buf
Definition pem.h:80
#define ANCHOR_LIST_INIT
Initializer for anchor_list.
Definition pem.h:94
size_t read_trust_anchors_from_memory(anchor_list *dst, const unsigned char *pem_data, size_t pem_len)
Read trust anchors from PEM-encoded data in memory.
Definition pem.c:457
size_t ptr
Definition pem.h:81
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.
asciichat_error_t platform_load_system_ca_certs(char **pem_data_out, size_t *pem_size_out)
Load system CA certificates for TLS/HTTPS.
#define HTTPS_MAX_RESPONSE_SIZE
void ascii_otel_http(ascii_otel_scope_t *s, const char *hostname, const char *path)
Definition otel.c:812
void ascii_otel_http_status(ascii_otel_scope_t *s, int status)
Definition otel.c:828
Vector type for trust anchors.
Definition pem.h:79

References ANCHOR_LIST_INIT, ascii_otel_http(), ascii_otel_http_status(), ASCII_OTEL_SCOPE, ASCIICHAT_OK, anchor_list::buf, BUFFER_SIZE_LARGE, ERROR_NETWORK, HOSTNAME_BUFFER_SIZE, hostname_to_ascii(), HTTPS_MAX_RESPONSE_SIZE, log_error, log_info, platform_load_system_ca_certs(), anchor_list::ptr, read_trust_anchors_from_memory(), SAFE_FREE, SAFE_MALLOC_DEFER_FREE, SAFE_REALLOC, safe_snprintf(), and SET_ERRNO.

Referenced by parse_public_key(), and update_check_perform().

◆ network_error_string()

const char * network_error_string ( )

#include <include/ascii-chat/network/network.h>

Get human-readable error string for network errors.

Returns
Error string describing last network error

Returns a human-readable description of the last network error. Useful for error logging and user-facing error messages.

Note
Error string is thread-local - each thread has its own error state.
Parameters
error_codeError code
Returns
Error string

Definition at line 454 of file network/network.c.

454 {
455 return socket_get_error_string();
456}

Referenced by socket_configure_buffers().

◆ packet_decrypt_envelope()

asciichat_error_t packet_decrypt_envelope ( packet_envelope_t * envelope,
void * crypto_ctx )

#include <include/ascii-chat/network/packet/packet.h>

Decrypt a PACKET_TYPE_ENCRYPTED envelope and extract inner packet.

If the envelope contains PACKET_TYPE_ENCRYPTED, decrypts the payload and updates the envelope with the inner packet (type, data, length). Used by both TCP and non-socket (WebRTC) transports to handle encryption uniformly.

Parameters
envelopePacket envelope to decrypt (will be modified if encrypted)
crypto_ctxCrypto context for decryption (required if envelope is encrypted)
Returns
ASCIICHAT_OK on success, error code otherwise
Note
Updates envelope->type, envelope->data, envelope->len, and envelope->allocated_buffer to point to decrypted plaintext
Caller must free the updated envelope->allocated_buffer

Decrypt a PACKET_TYPE_ENCRYPTED envelope and extract inner packet.

Parameters
sockfdSocket file descriptor
Returns
0 on success, -1 on error

Decrypt a PACKET_TYPE_ENCRYPTED envelope and extract inner packet

Shared decryption logic for both TCP (receive_packet_secure) and non-socket (WebRTC) transports. Decrypts PACKET_TYPE_ENCRYPTED envelopes and updates the envelope with the inner packet type and data.

Parameters
envelopePacket envelope to decrypt (will be modified if encrypted)
crypto_ctxCrypto context for decryption (required if envelope is encrypted)
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 665 of file packet.c.

665 {
666 if (!envelope) {
667 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid envelope");
668 }
669
670 // Only decrypt if this is an encrypted packet
671 if (envelope->type != PACKET_TYPE_ENCRYPTED) {
672 return ASCIICHAT_OK; // Not encrypted, nothing to do
673 }
674
675 if (!crypto_ctx) {
676 return SET_ERRNO(ERROR_CRYPTO, "Received encrypted packet but no crypto context");
677 }
678
679 uint8_t *plaintext = NULL;
680 size_t plaintext_length = 0;
681 bool consumed = false;
682 asciichat_error_t result =
684 envelope->len, &plaintext, &plaintext_length, NULL, &consumed);
685 if (result != ASCIICHAT_OK)
686 return result;
687 packet_header_t header;
688 memcpy(&header, plaintext, sizeof(header));
689 if (envelope->allocated_buffer && buffer_is_unique(envelope->allocated_buffer) &&
690 envelope->allocated_size >= plaintext_length) {
691 memcpy(envelope->allocated_buffer, plaintext, plaintext_length);
692 buffer_release(plaintext);
693 plaintext = envelope->allocated_buffer;
694 } else {
695 buffer_release(envelope->allocated_buffer);
696 envelope->allocated_size = buffer_capacity(plaintext);
697 }
698 envelope->type = NET_TO_HOST_U16(header.type);
699 envelope->data = plaintext + sizeof(header);
700 envelope->len = plaintext_length - sizeof(header);
701 envelope->allocated_buffer = plaintext;
702 envelope->was_encrypted = true;
703 envelope->consumed = consumed;
704 return ASCIICHAT_OK;
705}
size_t buffer_capacity(const void *data)
Definition buffer.c:280
bool buffer_is_unique(const void *data)
Definition buffer.c:317
#define NET_TO_HOST_U16(val)
Definition endian.h:111
@ ERROR_CRYPTO
Definition error_codes.h:93
asciichat_error_t acip_security_decrypt(crypto_context_t *crypto, unsigned flow, unsigned delivery, const void *payload, size_t length, uint8_t **output, size_t *written, uint64_t *sequence, bool *consumed)
#define CRYPTO_RECORD_RELIABLE
Definition record.h:9
#define CRYPTO_RECORD_ANY_CHANNEL
Definition record.h:11
Fixed prefix shared by every ACIP packet on the wire.
Definition packet.h:628

References acip_security_decrypt(), ASCIICHAT_OK, buffer_capacity(), buffer_is_unique(), buffer_release, CRYPTO_RECORD_ANY_CHANNEL, CRYPTO_RECORD_RELIABLE, ERROR_CRYPTO, ERROR_INVALID_PARAM, NET_TO_HOST_U16, PACKET_TYPE_ENCRYPTED, and SET_ERRNO.

Referenced by acds_receive_packet(), packet_receive_via_transport(), and receive_packet_secure_with_timeout().

◆ packet_encode()

asciichat_error_t packet_encode ( packet_type_t type,
const void * payload,
size_t length,
uint32_t client_id,
void * output,
size_t capacity,
size_t * written )

#include <include/ascii-chat/network/packet/packet.h>

Definition at line 67 of file packet.c.

68 {
69 if (!output || !written)
70 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing packet output");
71 *written = 0;
72 if (capacity < sizeof(packet_header_t) || length > capacity - sizeof(packet_header_t))
73 return SET_ERRNO(ERROR_NETWORK_SIZE, "Packet output is too small");
74 socket_buffer_t slice = {payload, length};
75 packet_header_t header;
76 asciichat_error_t result = packet_header_build(&header, type, &slice, 1, client_id);
77 if (result != ASCIICHAT_OK)
78 return result;
79 if (length)
80 memmove((uint8_t *)output + sizeof(header), payload, length);
81 memcpy(output, &header, sizeof(header));
82 *written = sizeof(header) + length;
83 return ASCIICHAT_OK;
84}
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
asciichat_error_t packet_header_build(packet_header_t *header, packet_type_t type, const socket_buffer_t *payload, size_t count, uint32_t client_id)
Definition packet.c:47

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK_SIZE, packet_header_build(), and SET_ERRNO.

Referenced by acip_security_rekey_encode().

◆ packet_format_error_stack()

size_t packet_format_error_stack ( char * buffer,
size_t size )

#include <include/ascii-chat/network/packet/packet.h>

Format the current thread's error stack for transport to a peer.

Parameters
bufferDestination buffer
sizeDestination buffer size
Returns
Number of bytes written, excluding the null terminator

Definition at line 707 of file packet.c.

707 {
708 if (!buffer || size == 0)
709 return 0;
710 buffer[0] = '\0';
712 size_t frame_count = errno_snapshot(thread_current_id(), frames, ASCIICHAT_ERRNO_MAX_DEPTH).copied;
713 size_t offset = 0;
714 for (size_t i = 0; i < frame_count && offset < size - 1; ++i) {
715 const asciichat_error_context_t *frame = &frames[i];
716 const char *file = extract_project_relative_path(frame->file, ASCIICHAT_OWNED_SOURCE_DIRS);
717#ifdef NDEBUG
718 int written = snprintf(buffer + offset, size - offset, "%s%s (%s:%d in %s())", i ? "\n" : "",
719 frame->context_message, file, frame->line, frame->function ? frame->function : "<unknown>");
720#else
721 int written = snprintf(buffer + offset, size - offset, "%s%s (%s:%d in %s())\tfull-path=%s", i ? "\n" : "",
722 frame->context_message, file, frame->line, frame->function ? frame->function : "<unknown>",
723 frame->file ? frame->file : "");
724#endif
725 if (written < 0)
726 break;
727 size_t available = size - offset;
728 offset += (size_t)written < available ? (size_t)written : available - 1;
729 }
730 return offset;
731}
asciichat_errno_snapshot_t errno_snapshot(uint64_t id, asciichat_error_context_t *out, size_t capacity)
#define ASCIICHAT_ERRNO_MAX_DEPTH
const char * extract_project_relative_path(const char *file, const char *const *owned_dirs)
Extract a path relative to the source root embedded by CMake.

References ASCIICHAT_ERRNO_MAX_DEPTH, asciichat_error_context_t::context_message, asciichat_errno_snapshot_t::copied, errno_snapshot(), extract_project_relative_path(), asciichat_error_context_t::file, asciichat_error_context_t::function, and asciichat_error_context_t::line.

◆ packet_header_build()

asciichat_error_t packet_header_build ( packet_header_t * header,
packet_type_t type,
const socket_buffer_t * payload,
size_t count,
uint32_t client_id )

#include <include/ascii-chat/network/packet/packet.h>

Shared ACIP wire codec. Headers are returned in network byte order.

Definition at line 47 of file packet.c.

48 {
49 if (!header || (!payload && count) || count > SOCKET_IOV_MAX)
50 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid packet header arguments");
51 size_t length = 0;
52 uint32_t crc = 0;
53 for (size_t i = 0; i < count; ++i) {
54 if ((!payload[i].data && payload[i].len) || payload[i].len > MAX_PACKET_SIZE - length)
55 return SET_ERRNO(ERROR_NETWORK_SIZE, "Invalid packet payload size");
56 length += payload[i].len;
57 crc = asciichat_crc32_update(crc, payload[i].data, payload[i].len);
58 }
59 *header = (packet_header_t){.magic = HOST_TO_NET_U64(PACKET_MAGIC),
60 .type = HOST_TO_NET_U16(type),
61 .length = HOST_TO_NET_U32((uint32_t)length),
62 .crc32 = HOST_TO_NET_U32(crc),
63 .client_id = HOST_TO_NET_U32(client_id)};
64 return ASCIICHAT_OK;
65}
#define HOST_TO_NET_U64(val)
Definition endian.h:213
#define MAX_PACKET_SIZE
Maximum packet size (5MB).
Definition packet.h:130
#define PACKET_MAGIC
Packet magic number (alias for MAGIC_PACKET_VALID).
Definition packet.h:270
uint32_t asciichat_crc32_update(uint32_t previous, const void *data, size_t len)
Compute CRC32 checksum with hardware acceleration (if available).
Definition crc32.c:82

References asciichat_crc32_update(), ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK_SIZE, HOST_TO_NET_U16, HOST_TO_NET_U32, HOST_TO_NET_U64, MAX_PACKET_SIZE, PACKET_MAGIC, and SET_ERRNO.

Referenced by acip_packet_encrypt(), packet_encode(), packet_send_via_transportv(), and send_packet_secure().

◆ packet_parse_error_message()

asciichat_error_t packet_parse_error_message ( const void * data,
size_t len,
asciichat_error_t * out_error_code,
char * message_buffer,
size_t message_buffer_size,
size_t * out_message_length )

#include <include/ascii-chat/network/packet/packet.h>

Parse an error packet payload into components.

Parameters
dataPacket payload buffer
lenPayload length in bytes
out_error_codeOutput pointer for asciichat_error_t value (must not be NULL)
message_bufferDestination buffer for message string (must not be NULL)
message_buffer_sizeSize of destination buffer in bytes (must be > 0)
out_message_lengthOptional output for message length reported by sender
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 782 of file packet.c.

784 {
785 if (!data || len < sizeof(error_packet_t) || !out_error_code || !message_buffer || message_buffer_size == 0) {
787 "Invalid parameters: data=%p len=%zu out_error_code=%p message_buffer=%p buffer_size=%zu", data,
788 len, out_error_code, message_buffer, message_buffer_size);
789 }
790
791 const error_packet_t *packet = (const error_packet_t *)data;
792 uint32_t raw_error_code = NET_TO_HOST_U32(packet->error_code);
793 uint32_t raw_message_length = NET_TO_HOST_U32(packet->message_length);
794
795 if (raw_message_length > MAX_ERROR_MESSAGE_LENGTH) {
796 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Error message length too large: %u", raw_message_length);
797 }
798
799 size_t total_required = sizeof(error_packet_t) + (size_t)raw_message_length;
800 if (total_required > len) {
801 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Error packet truncated: expected %zu bytes, have %zu", total_required,
802 len);
803 }
804
805 const uint8_t *message_bytes = (const uint8_t *)data + sizeof(error_packet_t);
806 size_t copy_len = raw_message_length;
807 if (copy_len >= message_buffer_size) {
808 copy_len = message_buffer_size - 1;
809 }
810
811 if (copy_len > 0) {
812 memcpy(message_buffer, message_bytes, copy_len);
813 }
814 message_buffer[copy_len] = '\0';
815
816 if (out_message_length) {
817 *out_message_length = raw_message_length;
818 }
819
820 *out_error_code = (asciichat_error_t)raw_error_code;
821 return ASCIICHAT_OK;
822}
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
#define MAX_ERROR_MESSAGE_LENGTH
Maximum error message length (512 bytes).
Definition packet.h:139
Error packet structure carrying error code and textual description.
Definition packet.h:767
uint32_t message_length
Length of message payload in bytes (0-512).
Definition packet.h:771
uint32_t error_code
Error code from asciichat_error_t enumeration.
Definition packet.h:769

References ASCIICHAT_OK, error_packet_t::error_code, ERROR_INVALID_PARAM, ERROR_NETWORK_PROTOCOL, MAX_ERROR_MESSAGE_LENGTH, error_packet_t::message_length, NET_TO_HOST_U32, and SET_ERRNO.

Referenced by network_error_decode().

◆ packet_parse_remote_log()

asciichat_error_t packet_parse_remote_log ( const void * data,
size_t len,
log_level_t * out_level,
remote_log_direction_t * out_direction,
uint16_t * out_flags,
char * message_buffer,
size_t message_buffer_size,
size_t * out_message_length )

#include <include/ascii-chat/network/packet/packet.h>

Parse a remote log packet payload into components.

Parameters
dataPacket payload buffer
lenPayload length in bytes
out_levelOutput pointer for log level (must not be NULL)
out_directionOutput pointer for direction (must not be NULL)
out_flagsOutput pointer for flags (must not be NULL)
message_bufferDestination buffer for message string (must not be NULL)
message_buffer_sizeSize of destination buffer in bytes (must be > 0)
out_message_lengthOptional output for message length reported by sender
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 881 of file packet.c.

884 {
885 if (!data || len < sizeof(remote_log_packet_t) || !out_level || !out_direction || !out_flags || !message_buffer ||
886 message_buffer_size == 0) {
887 return SET_ERRNO(
889 "Invalid parameters: data=%p len=%zu out_level=%p out_direction=%p out_flags=%p buffer=%p size=%zu", data, len,
890 out_level, out_direction, out_flags, message_buffer, message_buffer_size);
891 }
892
893 const remote_log_packet_t *packet = (const remote_log_packet_t *)data;
894 uint8_t raw_level = packet->log_level;
895 if (raw_level > LOG_FATAL) {
896 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid remote log level: %u", raw_level);
897 }
898 *out_level = (log_level_t)raw_level;
899
900 uint8_t raw_direction = packet->direction;
901 if (raw_direction > REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER) {
902 raw_direction = REMOTE_LOG_DIRECTION_UNKNOWN;
903 }
904 *out_direction = (remote_log_direction_t)raw_direction;
905
906 uint16_t raw_flags = NET_TO_HOST_U16(packet->flags);
907 *out_flags = raw_flags;
908
909 uint32_t raw_message_length = NET_TO_HOST_U32(packet->message_length);
910 if (raw_message_length > MAX_REMOTE_LOG_MESSAGE_LENGTH) {
911 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Remote log message length too large: %u", raw_message_length);
912 }
913
914 size_t total_required = sizeof(remote_log_packet_t) + (size_t)raw_message_length;
915 if (total_required > len) {
916 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Remote log packet truncated: expected %zu bytes, have %zu",
917 total_required, len);
918 }
919
920 const uint8_t *message_bytes = (const uint8_t *)data + sizeof(remote_log_packet_t);
921 size_t copy_len = raw_message_length;
922 if (copy_len >= message_buffer_size) {
923 copy_len = message_buffer_size - 1;
924 }
925
926 if (copy_len > 0) {
927 memcpy(message_buffer, message_bytes, copy_len);
928 }
929 message_buffer[copy_len] = '\0';
930
931 if (out_message_length) {
932 *out_message_length = raw_message_length;
933 }
934
935 return ASCIICHAT_OK;
936}
enum remote_log_direction remote_log_direction_t
Remote log packet direction enumeration.
log_level_t
Logging levels enumeration.
Definition types.h:29
@ REMOTE_LOG_DIRECTION_UNKNOWN
Definition network/log.h:21
@ REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER
Definition network/log.h:23
#define MAX_REMOTE_LOG_MESSAGE_LENGTH
Maximum remote log message length (512 bytes).
Definition packet.h:149
Remote log packet structure carrying log level and message text.
Definition packet.h:781
uint16_t flags
Additional flags (REMOTE_LOG_FLAG_*).
Definition packet.h:787
uint32_t message_length
Message payload length in bytes (0-512).
Definition packet.h:789
uint8_t log_level
Log level associated with the message (log_level_t cast to uint8_t).
Definition packet.h:783
uint8_t direction
Direction hint so receivers can annotate origin.
Definition packet.h:785
#define LOG_FATAL
Definition types.h:43

References ASCIICHAT_OK, remote_log_packet_t::direction, ERROR_INVALID_PARAM, ERROR_NETWORK_PROTOCOL, remote_log_packet_t::flags, LOG_FATAL, remote_log_packet_t::log_level, MAX_REMOTE_LOG_MESSAGE_LENGTH, remote_log_packet_t::message_length, NET_TO_HOST_U16, NET_TO_HOST_U32, REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER, REMOTE_LOG_DIRECTION_UNKNOWN, and SET_ERRNO.

Referenced by handle_remote_log_packet_from_client().

◆ packet_receive()

asciichat_error_t packet_receive ( socket_t sockfd,
packet_type_t * type,
void ** data,
size_t * len )

#include <include/ascii-chat/network/packet/packet.h>

Receive a packet with header validation and CRC32 checking.

Parameters
sockfdSocket file descriptor
typeOutput: Packet type
dataOutput: Packet payload data (allocated by function, freed by caller)
lenOutput: Payload data length in bytes
Returns
ASCIICHAT_OK on success, error code on failure

Receives a complete packet, validates header, and verifies CRC32 checksum. Allocates buffer for payload data which must be freed by caller.

Note
Allocates buffer for payload data - caller must free when done.
Validates magic number and CRC32 checksum before returning data.
Warning
Allocated data buffer must be freed by caller to prevent memory leaks.

Receive a packet with header validation and CRC32 checking.

Parameters
sockfdSocket file descriptor
typeOutput: packet type
dataOutput: packet data (allocated by caller, freed by caller)
lenOutput: data length
Returns
0 on success, -1 on error

Definition at line 407 of file packet.c.

407 {
408 if (sockfd == INVALID_SOCKET_VALUE) {
409 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket descriptor");
410 }
411 if (!type || !data || !len) {
412 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: type=%p, data=%p, len=%p", type, data, len);
413 }
414
415 // Read packet header into memory from network socket
416 packet_header_t header;
417 uint64_t header_timeout_ns = RECV_TIMEOUT * NS_PER_SEC_INT;
418 ssize_t received = recv_with_timeout(sockfd, &header, sizeof(header), header_timeout_ns);
419 if (received < 0) {
420 // Error context is already set by recv_with_timeout
421 return ERROR_NETWORK;
422 }
423 if ((size_t)received != sizeof(header)) {
424 if (received == 0) {
425 log_warn("Connection closed while reading packet header");
426 return SET_ERRNO(ERROR_NETWORK, "Connection closed by peer while reading packet header");
427 }
428
429 return SET_ERRNO(ERROR_NETWORK, "Partial packet header received: %zd/%zu bytes", received, sizeof(header));
430 }
431
432 // Validate packet header
433 uint16_t pkt_type;
434 uint32_t pkt_len;
435 uint32_t expected_crc;
436 if (packet_validate_header(&header, &pkt_type, &pkt_len, &expected_crc) != ASCIICHAT_OK) {
437 // Error context is already set by packet_validate_header
439 }
440
441 // Allocate buffer for payload
442 void *payload = NULL;
443 defer_named(release_payload, buffer_release(payload));
444 if (pkt_len > 0) {
445 payload = buffer_alloc(
446 packet_is_handshake_type(pkt_type) || pkt_type == PACKET_TYPE_ERROR_MESSAGE ? buffer_control_domain() : NULL,
447 pkt_len);
448 if (!payload)
449 return SET_ERRNO(ERROR_MEMORY, "Packet payload exceeds buffer budget");
450
451 // Use adaptive timeout for large packets
452 uint64_t recv_timeout = packet_send_timeout_ns(pkt_len);
453 received = recv_with_timeout(sockfd, payload, pkt_len, recv_timeout);
454 if (received < 0) {
455 return SET_ERRNO_SYS(ERROR_NETWORK, "Failed to receive packet payload");
456 }
457 if (received != (ssize_t)pkt_len) {
458 return SET_ERRNO(ERROR_NETWORK, "Partial packet payload received: %zd/%u bytes", received, pkt_len);
459 }
460
461 // Validate CRC32
462 if (packet_validate_crc32(payload, pkt_len, expected_crc) != ASCIICHAT_OK) {
463 // Error context is already set by packet_validate_crc32
465 }
466 }
467
468 // Return results
469 *type = (packet_type_t)pkt_type;
470 *data = payload;
471 defer_cancel(release_payload);
472 *len = pkt_len;
473
474 return ASCIICHAT_OK;
475}
buffer_domain_t * buffer_control_domain(void)
Definition buffer.c:436
unsigned long long uint64_t
Definition common.h:63
#define NS_PER_SEC_INT
Definition time.h:169
ssize_t recv_with_timeout(socket_t sockfd, void *buf, size_t len, uint64_t timeout_ns)
Receive data with timeout.
#define RECV_TIMEOUT
Receive timeout in seconds (1 second).
uint64_t packet_send_timeout_ns(size_t packet_size)
Definition packet.c:117

References ASCIICHAT_OK, buffer_alloc(), buffer_control_domain(), buffer_release, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK, ERROR_NETWORK_PROTOCOL, log_warn, NS_PER_SEC_INT, packet_send_timeout_ns(), PACKET_TYPE_ERROR_MESSAGE, RECV_TIMEOUT, recv_with_timeout(), SET_ERRNO, and SET_ERRNO_SYS.

Referenced by receive_packet().

◆ packet_send()

asciichat_error_t packet_send ( socket_t sockfd,
packet_type_t type,
const void * data,
size_t len )

#include <include/ascii-chat/network/packet/packet.h>

Send a packet with header and CRC32 checksum.

Parameters
sockfdSocket file descriptor
typePacket type (from packet_types.h)
dataPacket payload data (can be NULL for header-only packets)
lenPayload data length in bytes
Returns
ASCIICHAT_OK on success, error code on failure

Sends a complete packet with header (magic, type, length, CRC32) and optional payload. CRC32 is computed automatically and included in header.

Note
Packet header is constructed with all required fields including magic number, type, length, CRC32, and client ID.
CRC32 is computed over payload data (or zero if data is NULL).

Send a packet with header and CRC32 checksum.

Parameters
sockfdSocket file descriptor
typePacket type
dataPacket data
lenData length
Returns
0 on success, -1 on error

Definition at line 366 of file packet.c.

366 {
367 if (sockfd == INVALID_SOCKET_VALUE) {
368 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket descriptor");
369 }
370
371 if (len > MAX_PACKET_SIZE) {
372 return SET_ERRNO(ERROR_NETWORK_SIZE, "Packet too large: %zu > %d", len, MAX_PACKET_SIZE);
373 }
374
375 if (len && !data)
376 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing packet payload");
377 packet_header_t header = {.magic = HOST_TO_NET_U64(PACKET_MAGIC),
378 .type = HOST_TO_NET_U16((uint16_t)type),
379 .length = HOST_TO_NET_U32((uint32_t)len),
380 .crc32 = HOST_TO_NET_U32(len > 0 ? asciichat_crc32(data, len) : 0),
381 .client_id = HOST_TO_NET_U32(0)}; // Always initialize client_id to 0 in network byte order
382
383 // Calculate timeout based on packet size (in nanoseconds)
384 uint64_t timeout = packet_send_timeout_ns(len);
385
386 socket_buffer_t slices[] = {{&header, sizeof(header)}, {data, len}};
387 asciichat_error_t result = socket_sendv_all(sockfd, slices, 2, timeout);
388 if (result != ASCIICHAT_OK)
389 return result;
390
391#ifdef DEBUG_NETWORK
392 log_debug_every(US_PER_SEC_INT, "Sent packet type=%d, len=%zu, errno=%d (%s)", type, len, errno,
394#endif
395
396 return 0;
397}
#define SAFE_STRERROR(errnum)
Definition common.h:480
#define US_PER_SEC_INT
Definition time.h:173
#define asciichat_crc32(data, len)
Main CRC32 dispatcher macro - use this in application code.
Definition crc32.h:147
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
Definition log/log.h:749

References asciichat_crc32, ASCIICHAT_OK, errno, ERROR_INVALID_PARAM, ERROR_NETWORK_SIZE, HOST_TO_NET_U16, HOST_TO_NET_U32, HOST_TO_NET_U64, log_debug_every, MAX_PACKET_SIZE, PACKET_MAGIC, packet_send_timeout_ns(), SAFE_STRERROR, SET_ERRNO, and US_PER_SEC_INT.

Referenced by packet_send_error(), packet_send_remote_log(), send_image_frame_packet(), send_packet(), and send_packet_secure().

◆ packet_send_error()

asciichat_error_t packet_send_error ( socket_t sockfd,
const crypto_context_t * crypto_ctx,
asciichat_error_t error_code,
const char * message )

#include <include/ascii-chat/network/packet/packet.h>

Send an error packet with optional encryption context.

Parameters
sockfdSocket file descriptor
crypto_ctxCrypto context for encryption (NULL or not ready sends plaintext)
error_codeError code from asciichat_error_t enumeration
messageLegacy argument; fixed public text is sent for the allowlisted error code
Returns
ASCIICHAT_OK on success, error code otherwise

When the crypto context is ready, the packet is encrypted automatically. During the handshake (or when encryption is disabled), the packet is sent in plaintext so protocol errors can be delivered before encryption is active.

Definition at line 733 of file packet.c.

734 {
735 if (sockfd == INVALID_SOCKET_VALUE) {
736 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket descriptor");
737 }
738
739 error_code = network_error_public_code(error_code);
740 // This packet is valid before peer authentication, so use only the public
741 // error description rather than contextual errno data.
742 (void)message;
743 message = asciichat_error_string(error_code);
744
745 size_t message_len = strnlen(message, MAX_ERROR_MESSAGE_LENGTH);
746 if (message_len == MAX_ERROR_MESSAGE_LENGTH) {
747 log_warn("Error message truncated to %zu bytes", (size_t)MAX_ERROR_MESSAGE_LENGTH);
748 }
749
750 size_t payload_len = sizeof(error_packet_t) + message_len;
751 uint8_t *payload = SAFE_MALLOC(payload_len, uint8_t *);
752 if (!payload) {
753 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate %zu bytes for error packet", payload_len);
754 }
755
756 error_packet_t *packet = (error_packet_t *)payload;
757 packet->error_code = HOST_TO_NET_U32((uint32_t)error_code);
758 packet->message_length = HOST_TO_NET_U32((uint32_t)message_len);
759
760 if (message_len > 0) {
761 memcpy(payload + sizeof(error_packet_t), message, message_len);
762 }
763
764 bool encryption_ready = crypto_ctx && crypto_ctx->encrypt_data && crypto_is_ready(crypto_ctx);
765 asciichat_error_t send_result;
766
767 if (encryption_ready) {
768 send_result =
769 send_packet_secure(sockfd, PACKET_TYPE_ERROR_MESSAGE, payload, payload_len, (crypto_context_t *)crypto_ctx);
770 } else {
771 send_result = packet_send(sockfd, PACKET_TYPE_ERROR_MESSAGE, payload, payload_len);
772 }
773 SAFE_FREE(payload);
774
775 if (send_result != ASCIICHAT_OK) {
776 return SET_ERRNO(ERROR_NETWORK, "Failed to send error packet: %s", asciichat_error_string(send_result));
777 }
778
779 return ASCIICHAT_OK;
780}
#define SAFE_MALLOC(size, cast)
Definition common.h:265
bool crypto_is_ready(const crypto_context_t *ctx)
Check if key exchange is complete and ready for encryption.
asciichat_error_t packet_send(socket_t sockfd, packet_type_t type, const void *data, size_t len)
Send a packet with proper header and CRC32.
Definition packet.c:366
asciichat_error_t network_error_public_code(asciichat_error_t code)
Endpoint cryptographic state for handshake, packet protection, authentication, and rekeying.

References ASCIICHAT_OK, crypto_is_ready(), crypto_context_t::encrypt_data, error_packet_t::error_code, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK, HOST_TO_NET_U32, log_warn, MAX_ERROR_MESSAGE_LENGTH, error_packet_t::message_length, network_error_public_code(), packet_send(), PACKET_TYPE_ERROR_MESSAGE, SAFE_FREE, SAFE_MALLOC, send_packet_secure(), and SET_ERRNO.

◆ packet_send_remote_log()

asciichat_error_t packet_send_remote_log ( socket_t sockfd,
const crypto_context_t * crypto_ctx,
log_level_t level,
remote_log_direction_t direction,
uint16_t flags,
const char * message )

#include <include/ascii-chat/network/packet/packet.h>

Send a remote log packet with optional encryption context.

Parameters
sockfdSocket file descriptor
crypto_ctxCrypto context for encryption (NULL or not ready sends plaintext)
levelLog level to transmit
directionDirection flag (REMOTE_LOG_DIRECTION_*)
flagsAdditional flags (REMOTE_LOG_FLAG_*)
messageLog message text
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 824 of file packet.c.

825 {
826 if (sockfd == INVALID_SOCKET_VALUE) {
827 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket descriptor");
828 }
829
830 if (level < LOG_DEV || level > LOG_FATAL) {
831 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid log level: %d", level);
832 }
833
834 const char *safe_message = message ? message : "";
835 bool truncated = false;
836 size_t message_len = strnlen(safe_message, MAX_REMOTE_LOG_MESSAGE_LENGTH);
837 if (message_len == MAX_REMOTE_LOG_MESSAGE_LENGTH && safe_message[message_len] != '\0') {
838 truncated = true;
839 }
840
841 size_t payload_len = sizeof(remote_log_packet_t) + message_len;
842 uint8_t *payload = SAFE_MALLOC(payload_len, uint8_t *);
843 if (!payload) {
844 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate %zu bytes for remote log packet", payload_len);
845 }
846
847 remote_log_packet_t *packet = (remote_log_packet_t *)payload;
848 packet->log_level = (uint8_t)level;
849 packet->direction = (uint8_t)direction;
850 uint16_t final_flags = flags;
851 if (truncated) {
852 final_flags |= REMOTE_LOG_FLAG_TRUNCATED;
853 }
854 packet->flags = HOST_TO_NET_U16(final_flags);
855 packet->message_length = HOST_TO_NET_U32((uint32_t)message_len);
856
857 if (message_len > 0) {
858 memcpy(payload + sizeof(remote_log_packet_t), safe_message, message_len);
859 }
860
861 bool encryption_ready = crypto_ctx && crypto_ctx->encrypt_data && crypto_is_ready(crypto_ctx);
862 asciichat_error_t send_result;
863
864 if (encryption_ready) {
865 int secure_result =
866 send_packet_secure(sockfd, PACKET_TYPE_REMOTE_LOG, payload, payload_len, (crypto_context_t *)crypto_ctx);
867 send_result = secure_result == 0 ? ASCIICHAT_OK : ERROR_NETWORK;
868 } else {
869 send_result = packet_send(sockfd, PACKET_TYPE_REMOTE_LOG, payload, payload_len);
870 }
871
872 SAFE_FREE(payload);
873
874 if (send_result != ASCIICHAT_OK) {
875 return SET_ERRNO(ERROR_NETWORK, "Failed to send remote log packet: %d", send_result);
876 }
877
878 return ASCIICHAT_OK;
879}
#define REMOTE_LOG_FLAG_TRUNCATED
Remote log packet flag definitions.
Definition packet.h:776

References ASCIICHAT_OK, crypto_is_ready(), remote_log_packet_t::direction, crypto_context_t::encrypt_data, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK, remote_log_packet_t::flags, HOST_TO_NET_U16, HOST_TO_NET_U32, LOG_FATAL, remote_log_packet_t::log_level, MAX_REMOTE_LOG_MESSAGE_LENGTH, remote_log_packet_t::message_length, packet_send(), PACKET_TYPE_REMOTE_LOG, REMOTE_LOG_FLAG_TRUNCATED, SAFE_FREE, SAFE_MALLOC, send_packet_secure(), and SET_ERRNO.

◆ packet_validate()

asciichat_error_t packet_validate ( const void * packet,
size_t length,
size_t limit,
packet_header_t * header )

#include <include/ascii-chat/network/packet/packet.h>

Validate exactly one complete packet, including the empty-payload checksum. A zero limit uses MAX_PACKET_SIZE. The optional header output is a copy.

Definition at line 86 of file packet.c.

86 {
87 if (!packet || length < sizeof(packet_header_t))
88 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Incomplete packet header");
89 packet_header_t header;
90 memcpy(&header, packet, sizeof(header));
91 size_t payload = length - sizeof(header);
92 if (NET_TO_HOST_U64(header.magic) != PACKET_MAGIC || NET_TO_HOST_U32(header.length) != payload ||
93 payload > (limit ? limit : MAX_PACKET_SIZE))
94 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid packet framing or size");
95 if (asciichat_crc32((const uint8_t *)packet + sizeof(header), payload) != NET_TO_HOST_U32(header.crc32))
96 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Packet CRC mismatch");
97 if (output)
98 *output = header;
99 return ASCIICHAT_OK;
100}
#define NET_TO_HOST_U64(val)
Definition endian.h:228

References asciichat_crc32, ASCIICHAT_OK, ERROR_NETWORK_PROTOCOL, MAX_PACKET_SIZE, NET_TO_HOST_U32, NET_TO_HOST_U64, PACKET_MAGIC, and SET_ERRNO.

Referenced by acip_packet_open_sequence(), acip_security_open(), acip_security_rekey_receive(), packet_decoder_feed(), packet_receive_via_transport(), peer_crypto_receive(), and receive_packet_secure_with_timeout().

◆ protocol_version_packet_init()

void protocol_version_packet_init ( protocol_version_packet_t * version,
uint8_t supports_encryption )

#include <include/ascii-chat/network/packet/packet.h>

Initialize a protocol version packet with the current wire version.

Parameters
versionPacket to initialize.
supports_encryptionACIP encryption/authentication capability mask.

Definition at line 37 of file packet.c.

37 {
38 if (!version)
39 return;
40 memset(version, 0, sizeof(*version));
44 version->supports_encryption = supports_encryption;
45}
#define PROTOCOL_VERSION_MAJOR
Major protocol version number.
#define PROTOCOL_VERSION_MINOR
Minor protocol version number.
#define PROTOCOL_VERSION_PATCH
Patch protocol version number.
uint16_t protocol_patch
Patch protocol revision.
Definition packet.h:866
uint16_t protocol_revision
Minor protocol revision (server can be newer).
Definition packet.h:864
uint16_t protocol_version
Major protocol version (must match for compatibility).
Definition packet.h:862
uint8_t supports_encryption
Encryption & authentication mode bitmask (ACIP_CRYPTO_* constants: NONE, ENCRYPT, AUTH,...
Definition packet.h:868

References HOST_TO_NET_U16, protocol_version_packet_t::protocol_patch, protocol_version_packet_t::protocol_revision, protocol_version_packet_t::protocol_version, PROTOCOL_VERSION_MAJOR, PROTOCOL_VERSION_MINOR, PROTOCOL_VERSION_PATCH, and protocol_version_packet_t::supports_encryption.

Referenced by acds_handshake_begin(), and acip_security_start().

◆ receive_packet()

int receive_packet ( socket_t sockfd,
packet_type_t * type,
void ** data,
size_t * len )

#include <include/ascii-chat/network/packet/packet.h>

Receive a basic packet without encryption.

Parameters
sockfdSocket file descriptor
typeOutput: Packet type
dataOutput: Packet payload data (allocated by function)
lenOutput: Payload data length in bytes
Returns
0 on success, -1 on error

Receives a plaintext packet without decryption. Allocates buffer for payload data which must be freed by caller.

Note
Use receive_packet_secure() if decryption support is needed.
Warning
Allocated data buffer must be freed by caller.
Parameters
sockfdSocket file descriptor
typeOutput: packet type
dataOutput: packet data
lenOutput: data length
Returns
0 on success, -1 on error

Definition at line 620 of file packet.c.

620 {
621 asciichat_error_t result = packet_receive(sockfd, type, data, len);
622 return result == ASCIICHAT_OK ? 0 : -1;
623}
asciichat_error_t packet_receive(socket_t sockfd, packet_type_t *type, void **data, size_t *len)
Receive a packet with proper header validation and CRC32 checking.
Definition packet.c:407

References ASCIICHAT_OK, and packet_receive().

◆ receive_packet_secure()

packet_recv_result_t receive_packet_secure ( socket_t sockfd,
void * crypto_ctx,
bool enforce_encryption,
packet_envelope_t * envelope )

#include <include/ascii-chat/network/packet/packet.h>

Receive a packet with decryption and decompression support.

Parameters
sockfdSocket file descriptor
crypto_ctxCryptographic context for decryption (NULL for plaintext)
enforce_encryptionIf true, reject unencrypted packets (except handshake)
envelopeOutput: Received packet envelope with all metadata
Returns
PACKET_RECV_SUCCESS on success, error code on failure

Receives a packet with automatic decryption (if encrypted) and decompression (if compressed). Validates encryption policy and packet integrity.

Note
Decryption is applied automatically when packet is encrypted and crypto_ctx is provided.
Decompression is applied automatically when packet is compressed.
If enforce_encryption is true, unencrypted packets (except handshake packets) cause PACKET_RECV_SECURITY_VIOLATION.
Warning
Envelope's allocated_buffer must be freed by caller to prevent memory leaks.
Parameters
sockfdSocket file descriptor
crypto_ctxCrypto context for decryption
enforce_encryptionWhether to require encryption
envelopeOutput: received packet envelope
Returns
Packet receive result

Definition at line 587 of file packet.c.

588 {
589 return receive_packet_secure_with_timeout(sockfd, crypto_ctx, enforce_encryption, envelope,
591}
packet_recv_result_t receive_packet_secure_with_timeout(socket_t sockfd, void *crypto_ctx, bool enforce_encryption, packet_envelope_t *envelope, uint64_t timeout_ns)
Definition packet.c:1114

References NS_PER_SEC_INT, receive_packet_secure_with_timeout(), and RECV_TIMEOUT.

◆ receive_packet_secure_with_timeout()

packet_recv_result_t receive_packet_secure_with_timeout ( socket_t sockfd,
void * crypto_ctx,
bool enforce_encryption,
packet_envelope_t * envelope,
uint64_t timeout_ns )

#include <include/ascii-chat/network/packet/packet.h>

Definition at line 1114 of file packet.c.

1115 {
1116 if (!envelope) {
1117 SET_ERRNO(ERROR_INVALID_PARAM, "Missing packet envelope");
1118 return PACKET_RECV_ERROR;
1119 }
1120 *envelope = (packet_envelope_t){0};
1121 packet_header_t header;
1122 ssize_t received = recv_with_timeout(sockfd, &header, sizeof(header), timeout_ns);
1123 if (!received)
1124 return PACKET_RECV_EOF;
1125 if (received != (ssize_t)sizeof(header))
1126 return PACKET_RECV_ERROR;
1127 size_t payload_length = NET_TO_HOST_U32(header.length);
1128 if (NET_TO_HOST_U64(header.magic) != PACKET_MAGIC || payload_length > MAX_PACKET_SIZE) {
1129 SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid packet header");
1130 return PACKET_RECV_ERROR;
1131 }
1132 size_t length = sizeof(header) + payload_length;
1133 uint8_t *packet = buffer_alloc(NULL, length);
1134 if (!packet) {
1135 SET_ERRNO(ERROR_MEMORY, "Cannot allocate received packet");
1136 return PACKET_RECV_ERROR;
1137 }
1138 memcpy(packet, &header, sizeof(header));
1139 if (payload_length &&
1140 recv_with_timeout(sockfd, packet + sizeof(header), payload_length,
1141 MAX(timeout_ns, packet_send_timeout_ns(payload_length))) != (ssize_t)payload_length) {
1142 buffer_release(packet);
1143 // The header has already been consumed, so even an idle payload timeout
1144 // leaves the byte stream out of sync and must close the connection.
1145 SET_ERRNO(ERROR_NETWORK, "Incomplete packet payload after receiving header");
1146 return PACKET_RECV_ERROR;
1147 }
1148 asciichat_error_t result = packet_validate(packet, length, MAX_PACKET_SIZE, NULL);
1149 packet_type_t type = NET_TO_HOST_U16(header.type);
1150 if (result == ASCIICHAT_OK && enforce_encryption && type != PACKET_TYPE_ENCRYPTED &&
1151 !packet_is_initial_handshake_type(type))
1152 result = SET_ERRNO(ERROR_CRYPTO, "Unencrypted packet in encrypted session");
1153 if (result != ASCIICHAT_OK) {
1154 buffer_release(packet);
1155 return PACKET_RECV_ERROR;
1156 }
1157 *envelope = (packet_envelope_t){
1158 .type = type, .data = packet, .len = length, .allocated_buffer = packet, .allocated_size = length};
1159 if (type == PACKET_TYPE_ENCRYPTED) {
1160 envelope->data = packet + sizeof(header);
1161 envelope->len = payload_length;
1162 result = packet_decrypt_envelope(envelope, crypto_ctx);
1163 if (result != ASCIICHAT_OK) {
1164 buffer_release(envelope->allocated_buffer);
1165 *envelope = (packet_envelope_t){0};
1166 return PACKET_RECV_ERROR;
1167 }
1168 }
1169 return PACKET_RECV_SUCCESS;
1170}
#define MAX(a, b)
Return the maximum of two values.
Definition common.h:175
asciichat_error_t packet_validate(const void *packet, size_t length, size_t limit, packet_header_t *output)
Definition packet.c:86
asciichat_error_t packet_decrypt_envelope(packet_envelope_t *envelope, void *crypto_ctx)
Send a clear console packet.
Definition packet.c:665
Packet envelope containing received packet data.
Definition packet.h:1185

References ASCIICHAT_OK, buffer_alloc(), buffer_release, ERROR_CRYPTO, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK, ERROR_NETWORK_PROTOCOL, MAX, MAX_PACKET_SIZE, NET_TO_HOST_U16, NET_TO_HOST_U32, NET_TO_HOST_U64, packet_decrypt_envelope(), PACKET_MAGIC, PACKET_RECV_EOF, PACKET_RECV_ERROR, PACKET_RECV_SUCCESS, packet_send_timeout_ns(), PACKET_TYPE_ENCRYPTED, packet_validate(), recv_with_timeout(), and SET_ERRNO.

Referenced by receive_packet_secure().

◆ recv_with_timeout()

ssize_t recv_with_timeout ( socket_t sockfd,
void * buf,
size_t len,
uint64_t timeout_ns )

#include <include/ascii-chat/network/network.h>

Receive data with timeout.

Parameters
sockfdSocket file descriptor
bufBuffer to receive data
lenLength of buffer in bytes
timeout_nsTimeout in nanoseconds
Returns
Number of bytes received on success, -1 on error

Receives data from socket with timeout support. Waits up to timeout_ns for data to arrive.

Note
Partial receives are possible - function returns number of bytes actually received.
Returns -1 on timeout or error. Use network_error_string() to get human-readable error description.
Parameters
sockfdSocket file descriptor
bufBuffer to receive data
lenLength of buffer
timeout_secondsTimeout in seconds
Returns
Number of bytes received, or -1 on error

Definition at line 178 of file network/network.c.

178 {
179 if (sockfd == INVALID_SOCKET_VALUE) {
180 errno = EBADF;
181 return -1;
182 }
183
184 ssize_t total_received = 0;
185 char *data = (char *)buf;
186 uint64_t receive_start = time_get_ns();
187
188 while (total_received < (ssize_t)len) {
189 // Set up poll for read timeout (in nanoseconds)
190 struct pollfd pfd;
191 pfd.fd = sockfd;
192 pfd.events = POLLIN;
193 pfd.revents = 0;
194
195 // Use provided timeout for recv() calls - don't artificially limit data packets in test mode
196 // Timeouts should only apply to initial handshake, not to receiving data on active connections
197 uint64_t elapsed = time_get_ns() - receive_start;
198 if (elapsed >= timeout_ns) {
199 SET_ERRNO(total_received ? ERROR_NETWORK : ERROR_NETWORK_TIMEOUT, "Receive deadline expired");
200 return -1;
201 }
202 int result = socket_poll(&pfd, 1, (int64_t)(timeout_ns - elapsed));
203 if (result <= 0) {
204 if (result == 0) {
205 /* A read timeout is the ordinary idle state for signaling sockets.
206 * Do not emit an error on every polling interval. */
207 errno_set_with_system_error(ERROR_NETWORK_TIMEOUT, __FILE__, __LINE__, __func__, ETIMEDOUT);
208 if (total_received > 0)
209 SET_ERRNO(ERROR_NETWORK, "Receive timed out after consuming %zd/%zu bytes", total_received, len);
210 return -1;
211 }
212 if (network_handle_select_error(result)) {
213 continue; // Retry
214 }
215 return -1; // Fatal error
216 }
217
218 // Poll can return readiness for a hangup or socket error without POLLIN.
219 // Treat those as terminal network errors so callers tear down the dead
220 // connection instead of retrying it as an idle timeout in a tight loop.
221 // Linux zerocopy completions also raise POLLERR with SO_ERROR == 0.
222 // The serialized sender owns the error queue and will consume those events.
223 if ((pfd.revents & POLLERR) && !(pfd.revents & (POLLHUP | POLLNVAL))) {
224 int socket_error = 0;
225 socklen_t error_len = sizeof(socket_error);
226 if (socket_getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &socket_error, &error_len) == 0 && !socket_error) {
227 pfd.revents &= (short)~POLLERR;
228 if (!(pfd.revents & POLLIN)) {
229 platform_sleep_ns(NS_PER_MS_INT);
230 continue;
231 }
232 }
233 }
234 if (pfd.revents & (POLLERR | POLLNVAL)) {
235 SET_ERRNO(ERROR_NETWORK, "poll reported socket flags 0x%x", pfd.revents);
236 return -1;
237 }
238 // A drained asynchronous writer may close while readable bytes remain.
239 // Consume POLLIN before treating POLLHUP as EOF (notably WSAPoll).
240 if ((pfd.revents & POLLHUP) && !(pfd.revents & POLLIN))
241 return total_received;
242
243 // Other readiness flags without POLLIN are an ordinary no-data result.
244 if (!(pfd.revents & POLLIN)) {
245 errno_set_with_system_error(ERROR_NETWORK_TIMEOUT, __FILE__, __LINE__, __func__, ETIMEDOUT);
246 if (total_received > 0)
247 SET_ERRNO(ERROR_NETWORK, "Receive stalled after consuming %zd/%zu bytes", total_received, len);
248 return -1;
249 }
250
251 // Calculate how much we still need to receive
252 size_t bytes_to_recv = len - (size_t)total_received;
253 ssize_t received = socket_recv(sockfd, data + total_received, bytes_to_recv, 0);
254
255 if (received < 0) {
256 int error = errno;
257 if (network_handle_recv_error(error)) {
258 continue; // Retry
259 }
260 return -1; // Fatal error
261 }
262
263 if (received == 0) {
264 // Connection closed by peer
265 log_debug("Connection closed by peer during recv");
266 return total_received; // Return what we got so far
267 }
268
269 total_received += received;
270 }
271
272 return total_received;
273}
#define NS_PER_MS_INT
Definition time.h:168

References EBADF, errno, errno_set_with_system_error(), ERROR_NETWORK, ERROR_NETWORK_TIMEOUT, ETIMEDOUT, log_debug, NS_PER_MS_INT, and SET_ERRNO.

Referenced by packet_receive(), and receive_packet_secure_with_timeout().

◆ send_crypto_capabilities_packet()

int send_crypto_capabilities_packet ( socket_t sockfd,
const crypto_capabilities_packet_t * caps )

#include <include/ascii-chat/network/packet/packet.h>

Send crypto capabilities packet.

Parameters
sockfdSocket file descriptor
capsCrypto capabilities packet structure
Returns
0 on success, -1 on error

Sends a PACKET_TYPE_CRYPTO_CAPABILITIES packet to advertise supported cryptographic algorithms (key exchange, authentication, cipher).

Note
Crypto capabilities packets are always sent unencrypted (handshake packets).
Parameters
sockfdSocket file descriptor
capsCrypto capabilities packet
Returns
0 on success, -1 on error

Definition at line 958 of file packet.c.

958 {
959 if (!caps) {
960 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: caps=%p", caps);
961 return -1;
962 }
963 return send_packet(sockfd, PACKET_TYPE_CRYPTO_CAPABILITIES, caps, sizeof(*caps));
964}
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:607

References ERROR_INVALID_PARAM, PACKET_TYPE_CRYPTO_CAPABILITIES, send_packet(), and SET_ERRNO.

◆ send_crypto_parameters_packet()

int send_crypto_parameters_packet ( socket_t sockfd,
const crypto_parameters_packet_t * params )

#include <include/ascii-chat/network/packet/packet.h>

Send crypto parameters packet.

Parameters
sockfdSocket file descriptor
paramsCrypto parameters packet structure
Returns
0 on success, -1 on error

Sends a PACKET_TYPE_CRYPTO_PARAMETERS packet containing chosen cryptographic algorithms and data sizes for handshake continuation.

Note
Crypto parameters packets are always sent unencrypted (handshake packets).
Parameters
sockfdSocket file descriptor
paramsCrypto parameters packet
Returns
0 on success, -1 on error

Definition at line 972 of file packet.c.

972 {
973 if (!params) {
974 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: params=%p", params);
975 return -1;
976 }
977
978 // Create a copy and convert uint16_t fields to network byte order
979 crypto_parameters_packet_t net_params = *params;
980 log_debug("NETWORK_DEBUG: Before htons: kex=%u, auth=%u, sig=%u, secret=%u", params->kex_public_key_size,
981 params->auth_public_key_size, params->signature_size, params->shared_secret_size);
984 net_params.signature_size = HOST_TO_NET_U16(params->signature_size);
986 log_debug("NETWORK_DEBUG: After htons: kex=%u, auth=%u, sig=%u, secret=%u", net_params.kex_public_key_size,
987 net_params.auth_public_key_size, net_params.signature_size, net_params.shared_secret_size);
988
989 return send_packet(sockfd, PACKET_TYPE_CRYPTO_PARAMETERS, &net_params, sizeof(net_params));
990}
Crypto parameters packet structure (Packet Type 15).
Definition packet.h:1037
uint16_t auth_public_key_size
Authentication public key size in bytes (e.g., 32 for Ed25519, 1952 for Dilithium3).
Definition packet.h:1049
uint16_t signature_size
Signature size in bytes (e.g., 64 for Ed25519, 3309 for Dilithium3).
Definition packet.h:1051
uint16_t shared_secret_size
Shared secret size in bytes (e.g., 32 for X25519).
Definition packet.h:1053
uint16_t kex_public_key_size
Key exchange public key size in bytes (e.g., 32 for X25519, 1568 for Kyber1024).
Definition packet.h:1047

References crypto_parameters_packet_t::auth_public_key_size, ERROR_INVALID_PARAM, HOST_TO_NET_U16, crypto_parameters_packet_t::kex_public_key_size, log_debug, PACKET_TYPE_CRYPTO_PARAMETERS, send_packet(), SET_ERRNO, crypto_parameters_packet_t::shared_secret_size, and crypto_parameters_packet_t::signature_size.

◆ send_image_frame_packet()

asciichat_error_t send_image_frame_packet ( socket_t sockfd,
const void * image_data,
uint16_t width,
uint16_t height,
uint8_t format )

#include <include/ascii-chat/network/packet/packet.h>

Send image frame packet.

Parameters
sockfdSocket file descriptor
image_dataImage pixel data buffer
widthImage width in pixels
heightImage height in pixels
formatPixel format (0=RGB24)
Returns
ASCIICHAT_OK on success, error code on failure

Sends PACKET_TYPE_IMAGE_FRAME with RGB image data. Used for transmitting camera frames or video input.

Note
Maximum dimensions: 4096x4096 pixels (~25MB per frame).
Pixel data must be in RGB24 format (3 bytes per pixel).

Definition at line 1055 of file packet.c.

1056 {
1057 if (!image_data || width == 0 || height == 0) {
1058 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: image_data=%p, width=%u, height=%u", image_data, width,
1059 height);
1060 }
1061
1062 // Validate dimensions to prevent integer overflow
1063 // Max reasonable dimensions: 4K (3840x2160) = ~25MB per frame
1064 if (width > 4096 || height > 4096) {
1065 return SET_ERRNO(ERROR_INVALID_PARAM, "Image dimensions too large: %ux%u (max 4096x4096)", width, height);
1066 }
1067
1068 // Create image frame packet
1069 image_frame_packet_t packet;
1070 packet.width = HOST_TO_NET_U32(width);
1071 packet.height = HOST_TO_NET_U32(height);
1072 packet.pixel_format = HOST_TO_NET_U32(format);
1073 packet.compressed_size = 0;
1074 packet.checksum = 0;
1075 packet.timestamp = 0; // Will be set by receiver
1076
1077 // Calculate total packet size
1078 // Cast to size_t before multiplication to prevent integer overflow
1079 // Use overflow-checked multiplication
1080 size_t width_times_height;
1081 if (checked_size_mul((size_t)width, (size_t)height, &width_times_height) != ASCIICHAT_OK) {
1082 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Image dimensions too large: %d x %d", width, height);
1083 }
1084
1085 size_t frame_size;
1086 if (checked_size_mul(width_times_height, 3u, &frame_size) != ASCIICHAT_OK) {
1087 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Frame size overflow for RGB format");
1088 }
1089
1090 size_t total_size;
1091 if (checked_size_add(sizeof(image_frame_packet_t), frame_size, &total_size) != ASCIICHAT_OK) {
1092 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Total packet size overflow");
1093 }
1094
1095 // Allocate buffer for complete packet
1096 void *packet_data = buffer_alloc(NULL, total_size);
1097 if (!packet_data) {
1098 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for image frame packet: %zu bytes", total_size);
1099 }
1100
1101 // Copy packet header and image data
1102 memcpy(packet_data, &packet, sizeof(image_frame_packet_t));
1103 memcpy((char *)packet_data + sizeof(image_frame_packet_t), image_data, frame_size);
1104
1105 // Send packet
1106 asciichat_error_t result = packet_send(sockfd, PACKET_TYPE_IMAGE_FRAME, packet_data, total_size);
1107
1108 // Clean up
1109 buffer_release(packet_data);
1110
1111 return result;
1112}
Describes one compressed image sent through ACIP. width, height, and pixel_format tell the decoder ho...
Definition packet.h:931
uint32_t pixel_format
Pixel format enum (1=RGB24, 2=RGBA32, 3=BGR24, 4=BGRA32).
Definition packet.h:937
uint32_t timestamp
Timestamp when frame was captured (milliseconds since epoch).
Definition packet.h:943
uint32_t compressed_size
Compressed data size (0 = not compressed, >0 = compressed).
Definition packet.h:939
uint32_t checksum
CRC32 checksum of pixel data.
Definition packet.h:941
uint32_t height
Image height in pixels.
Definition packet.h:935
uint32_t width
Image width in pixels.
Definition packet.h:933

References ASCIICHAT_OK, buffer_alloc(), buffer_release, image_frame_packet_t::checksum, image_frame_packet_t::compressed_size, ERROR_BUFFER_OVERFLOW, ERROR_INVALID_PARAM, ERROR_MEMORY, height, image_frame_packet_t::height, HOST_TO_NET_U32, packet_send(), PACKET_TYPE_IMAGE_FRAME, image_frame_packet_t::pixel_format, SET_ERRNO, image_frame_packet_t::timestamp, image_frame_packet_t::width, and width.

◆ send_packet()

int send_packet ( socket_t sockfd,
packet_type_t type,
const void * data,
size_t len )

#include <include/ascii-chat/network/packet/packet.h>

Send a basic packet without encryption.

Parameters
sockfdSocket file descriptor
typePacket type (from packet_types.h)
dataPacket payload data
lenPayload data length in bytes
Returns
0 on success, -1 on error

Sends a plaintext packet without encryption. Equivalent to packet_send() but returns int instead of asciichat_error_t for compatibility.

Note
Use send_packet_secure() if encryption support is needed.
Parameters
sockfdSocket file descriptor
typePacket type
dataPacket data
lenData length
Returns
0 on success, -1 on error

Definition at line 607 of file packet.c.

607 {
608 asciichat_error_t result = packet_send(sockfd, type, data, len);
609 return result == ASCIICHAT_OK ? 0 : -1;
610}

References ASCIICHAT_OK, and packet_send().

Referenced by acip_server_send_error(), send_crypto_capabilities_packet(), send_crypto_parameters_packet(), send_error_packet_message(), send_ping_packet(), send_pong_packet(), and send_protocol_version_packet().

◆ send_packet_secure()

asciichat_error_t send_packet_secure ( socket_t sockfd,
packet_type_t type,
const void * data,
size_t len,
crypto_context_t * crypto_ctx )

#include <include/ascii-chat/network/packet/packet.h>

Send a packet with encryption and compression support.

Parameters
sockfdSocket file descriptor
typePacket type (from packet_types.h)
dataPacket payload data
lenPayload data length in bytes
crypto_ctxCryptographic context for encryption (NULL for plaintext)
Returns
0 on success, negative on error

Sends a packet with automatic encryption (if crypto context provided) and compression (if packet is large enough). Handshake packets are never encrypted.

Note
Encryption is applied automatically when crypto_ctx is provided and packet type is not a handshake packet.
Compression is applied automatically to large packets based on size thresholds and compression ratios.
Handshake packets (packet_is_handshake_type(type) == true) are always sent unencrypted, even when crypto_ctx is provided.
Parameters
sockfdSocket file descriptor
typePacket type
dataPacket data
lenData length
crypto_ctxCrypto context for encryption
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 492 of file packet.c.

493 {
494 if (sockfd == INVALID_SOCKET_VALUE)
495 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid packet socket");
496 if (len > MAX_PACKET_SIZE) {
497 return SET_ERRNO(ERROR_NETWORK_SIZE, "Packet too large: %zu > %d", len, MAX_PACKET_SIZE);
498 }
499
500 if (!data && len > 0) {
501 return SET_ERRNO(ERROR_INVALID_PARAM, "Packet payload is NULL with nonzero length");
502 }
503
504 // Handshake packets are ALWAYS sent unencrypted
505 if (packet_is_initial_handshake_type(type)) {
506 return packet_send(sockfd, type, data, len);
507 }
508
509 // Apply compression if beneficial for large packets
510 const void *final_data = data;
511 size_t final_len = len;
512 void *compressed_data = NULL;
513
514 // Skip compression for pre-compressed data (Opus audio) or if --no-compress flag is set
515 bool should_skip_compression = packet_is_precompressed(type) || GET_OPTION(no_compress);
516 if (!should_skip_compression && len > COMPRESSION_MIN_SIZE && should_compress(len, len)) {
517 void *temp_compressed = NULL;
518 size_t compressed_size = 0;
519
520 // Use configured compression level from options (default: 1 for fastest compression)
521 int compression_level = (GET_OPTION(compression_level) > 0) ? GET_OPTION(compression_level) : 1;
522 asciichat_error_t compress_result = compress_data(data, len, &temp_compressed, &compressed_size, compression_level);
523 if (compress_result == ASCIICHAT_OK) {
524 double ratio = (double)compressed_size / (double)len;
525 if (ratio < COMPRESSION_RATIO_THRESHOLD) {
526 final_data = temp_compressed;
527 final_len = compressed_size;
528 compressed_data = temp_compressed;
529 log_debug_every(US_PER_SEC_INT, "Compressed packet: %zu -> %zu bytes (%.1f%%)", len, compressed_size,
530 ratio * 100.0);
531 } else {
532 buffer_release(temp_compressed);
533 }
534 }
535 }
536
537 // If no crypto context or crypto not ready, send unencrypted
538 bool ready = crypto_ctx ? crypto_is_ready(crypto_ctx) : false;
539 if (!crypto_ctx || !ready || !crypto_ctx->encrypt_data) {
540 log_warn_every(LOG_RATE_FAST, "CRYPTO_DEBUG: Sending packet type %d UNENCRYPTED (crypto_ctx=%p, ready=%d)", type,
541 (void *)crypto_ctx, ready);
542 asciichat_error_t result = packet_send(sockfd, type, final_data, final_len);
543 if (compressed_data) {
544 buffer_release(compressed_data);
545 }
546 if (result != ASCIICHAT_OK) {
547 SET_ERRNO(ERROR_NETWORK, "Failed to send packet: %s", asciichat_error_string(result));
548 }
549 return result;
550 }
551
552 socket_buffer_t payload = {final_data, final_len};
553 packet_header_t header;
554 asciichat_error_t result = packet_header_build(&header, type, &payload, 1, 0);
555 socket_buffer_t slices[] = {{&header, sizeof(header)}, payload};
557 if (result == ASCIICHAT_OK)
558 result = acip_packet_plan(crypto_ctx, slices, 2, &plan);
559 uint8_t *ciphertext = NULL;
560 if (result == ASCIICHAT_OK) {
561 ciphertext = buffer_alloc(plan.domain, plan.ciphertext_capacity);
562 if (!ciphertext)
563 result = SET_ERRNO(ERROR_MEMORY, "Cannot allocate packet ciphertext");
564 }
565 if (result == ASCIICHAT_OK) {
566 packet_header_t outer;
567 size_t length = 0;
568 result = acip_packet_encrypt(crypto_ctx, &plan, plan.domain, ciphertext, plan.ciphertext_capacity, &outer, &length);
569 if (result == ASCIICHAT_OK) {
570 socket_buffer_t wire[] = {{&outer, sizeof(outer)}, {ciphertext, length}};
571 result = socket_sendv_all(sockfd, wire, 2, packet_send_timeout_ns(sizeof(outer) + length));
572 }
573 }
574 buffer_release(ciphertext);
575 buffer_release(compressed_data);
576 return result;
577}
#define COMPRESSION_MIN_SIZE
Minimum packet size to attempt compression (1KB).
Definition compression.h:62
bool should_compress(size_t original_size, size_t compressed_size)
Determine if compression should be used for given data sizes.
asciichat_error_t compress_data(const void *input, size_t input_size, void **output, size_t *output_size, int compression_level)
Compress data using zstd with configurable compression level.
Definition compression.c:30
#define COMPRESSION_RATIO_THRESHOLD
Compression ratio threshold - only use if <80% original size.
Definition compression.h:59
#define LOG_RATE_FAST
Log rate limit: 1 second (1,000,000 microseconds).
Definition log_rates.h:26
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
#define log_warn_every(interval_us, fmt,...)
Rate-limited WARN logging.
Definition log/log.h:755
asciichat_error_t acip_packet_plan(crypto_context_t *crypto, const socket_buffer_t *buffers, size_t count, acip_packet_plan_t *plan)
Definition prepare.c:9
asciichat_error_t acip_packet_encrypt(crypto_context_t *crypto, const acip_packet_plan_t *plan, buffer_domain_t *domain, void *ciphertext, size_t capacity, packet_header_t *header, size_t *length)
The useful part of acip packet plan is in buffers, count, plaintext_size, ciphertext_capacity: togeth...
Definition prepare.c:64
size_t ciphertext_capacity
Definition prepare.h:20
buffer_domain_t * domain
Borrowed control reserve for handshake/error traffic; NULL otherwise.
Definition prepare.h:25

References acip_packet_encrypt(), acip_packet_plan(), ASCIICHAT_OK, buffer_alloc(), buffer_release, acip_packet_plan_t::ciphertext_capacity, compress_data(), COMPRESSION_MIN_SIZE, COMPRESSION_RATIO_THRESHOLD, crypto_is_ready(), acip_packet_plan_t::domain, crypto_context_t::encrypt_data, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK, ERROR_NETWORK_SIZE, GET_OPTION, log_debug_every, LOG_RATE_FAST, log_warn_every, MAX_PACKET_SIZE, packet_header_build(), packet_send(), packet_send_timeout_ns(), SET_ERRNO, should_compress(), and US_PER_SEC_INT.

Referenced by av_send_audio_opus_batch(), packet_send_error(), and packet_send_remote_log().

◆ send_ping_packet()

int send_ping_packet ( socket_t sockfd)

#include <include/ascii-chat/network/packet/packet.h>

Send a ping packet (keepalive).

Parameters
sockfdSocket file descriptor
Returns
0 on success, -1 on error

Sends a PACKET_TYPE_PING packet to keep connection alive. Server/client responds with PACKET_TYPE_PONG.

Note
Ping packets are always sent unencrypted (handshake packets).

Send a ping packet (keepalive).

Parameters
sockfdSocket file descriptor
Returns
0 on success, -1 on error

Definition at line 636 of file packet.c.

636 {
637 return send_packet(sockfd, PACKET_TYPE_PING, NULL, 0);
638}

References PACKET_TYPE_PING, and send_packet().

◆ send_pong_packet()

int send_pong_packet ( socket_t sockfd)

#include <include/ascii-chat/network/packet/packet.h>

Send a pong packet (keepalive response).

Parameters
sockfdSocket file descriptor
Returns
0 on success, -1 on error

Sends a PACKET_TYPE_PONG packet in response to PACKET_TYPE_PING. Indicates that connection is alive and responsive.

Note
Pong packets are always sent unencrypted (handshake packets).

Send a pong packet (keepalive response).

Parameters
sockfdSocket file descriptor
Returns
0 on success, -1 on error

Definition at line 645 of file packet.c.

645 {
646 return send_packet(sockfd, PACKET_TYPE_PONG, NULL, 0);
647}

References PACKET_TYPE_PONG, and send_packet().

◆ send_protocol_version_packet()

int send_protocol_version_packet ( socket_t sockfd,
const protocol_version_packet_t * version )

#include <include/ascii-chat/network/packet/packet.h>

Send protocol version negotiation packet.

Parameters
sockfdSocket file descriptor
versionProtocol version packet structure
Returns
0 on success, -1 on error

Sends a PACKET_TYPE_PROTOCOL_VERSION packet for protocol capability negotiation. Both client and server send this during handshake.

Note
Protocol version packets are always sent unencrypted (handshake packets).

Send protocol version negotiation packet.

Parameters
sockfdSocket file descriptor
versionProtocol version packet
Returns
0 on success, -1 on error

Definition at line 944 of file packet.c.

944 {
945 if (!version) {
946 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: version=%p", version);
947 return -1;
948 }
949 return send_packet(sockfd, PACKET_TYPE_PROTOCOL_VERSION, version, sizeof(*version));
950}

References ERROR_INVALID_PARAM, PACKET_TYPE_PROTOCOL_VERSION, send_packet(), and SET_ERRNO.

◆ send_with_timeout()

ssize_t send_with_timeout ( socket_t sockfd,
const void * data,
size_t len,
uint64_t timeout_ns )

#include <include/ascii-chat/network/network.h>

Send data with timeout using chunked transmission.

Parameters
sockfdSocket file descriptor
dataData to send
lenLength of data in bytes
timeout_nsTimeout in nanoseconds
Returns
Number of bytes sent on success, -1 on error

Sends data to socket with timeout support. Uses chunked transmission for large data to prevent blocking and enable progress tracking.

Note
Chunked transmission automatically handles large frames by sending in smaller chunks, preventing timeout issues.
Partial sends are handled automatically - function retries until all data is sent or timeout occurs.
Parameters
sockfdSocket file descriptor
dataData to send
lenLength of data
timeout_secondsTimeout in seconds
Returns
Number of bytes sent, or -1 on error

Definition at line 109 of file network/network.c.

109 {
110 if (sockfd == INVALID_SOCKET_VALUE) {
111 errno = EBADF;
112 return -1;
113 }
114
115 size_t total_sent = 0;
116 const char *data_ptr = (const char *)data;
117
118 while (total_sent < len) {
119 // Calculate chunk size
120 size_t bytes_to_send = len - total_sent;
121 const size_t MAX_CHUNK_SIZE = 65536; // 64KB chunks for reliable TCP transmission
122 if (bytes_to_send > MAX_CHUNK_SIZE) {
123 bytes_to_send = MAX_CHUNK_SIZE;
124 }
125
126 // Set up poll for write timeout (in nanoseconds)
127 struct pollfd pfd;
128 pfd.fd = sockfd;
129 pfd.events = POLLOUT;
130 pfd.revents = 0;
131
132 int64_t effective_timeout_ns = network_is_test_environment() ? (100LL * NS_PER_MS_INT) : (int64_t)timeout_ns;
133 int result = socket_poll(&pfd, 1, effective_timeout_ns);
134 if (result <= 0) {
135 if (result == 0) {
136 SET_ERRNO_SYS(ERROR_NETWORK_TIMEOUT, "timed out after %llu nanoseconds", (unsigned long long)timeout_ns);
137 return -1;
138 }
139 if (network_handle_select_error(result)) {
140 continue; // Retry
141 }
142 return -1; // Fatal error
143 }
144
145 // Check if socket is ready for writing
146 if (!(pfd.revents & POLLOUT)) {
147 SET_ERRNO_SYS(ERROR_NETWORK_TIMEOUT, "socket not ready for writing after poll");
148 return -1;
149 }
150
151 // Use platform-specific send
152 ssize_t sent = socket_send(sockfd, data_ptr + total_sent, bytes_to_send, 0);
153
154 if (sent < 0) {
155 int error = errno;
156 if (network_handle_send_error(error)) {
157 continue; // Retry
158 }
159 return -1; // Fatal error
160 }
161
162 if (sent > 0) {
163 total_sent += (size_t)sent;
164 }
165 }
166
167 return (ssize_t)total_sent;
168}
#define network_is_test_environment()
Check if we're in a test environment.

References EBADF, errno, ERROR_NETWORK_TIMEOUT, network_is_test_environment, NS_PER_MS_INT, and SET_ERRNO_SYS.

◆ set_socket_keepalive()

asciichat_error_t set_socket_keepalive ( socket_t sockfd)

#include <include/ascii-chat/network/network.h>

Enable TCP keepalive on socket.

Parameters
sockfdSocket file descriptor
Returns
ASCIICHAT_OK on success, error code on failure

Enables TCP keepalive probes on socket using KEEPALIVE_IDLE, KEEPALIVE_INTERVAL, and KEEPALIVE_COUNT settings.

Note
Keepalive helps detect dead connections (broken network path) without application-level heartbeats.

Enable TCP keepalive on socket.

Parameters
sockfdSocket file descriptor
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 380 of file network/network.c.

380 {
381 if (sockfd == INVALID_SOCKET_VALUE) {
382 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket file descriptor");
383 }
384 int result = socket_set_keepalive_params(sockfd, true, KEEPALIVE_IDLE, KEEPALIVE_INTERVAL, KEEPALIVE_COUNT);
385 if (result != 0) {
386 return SET_ERRNO_SYS(ERROR_NETWORK, "Failed to set socket keepalive parameters");
387 }
388 return ASCIICHAT_OK;
389}
#define KEEPALIVE_COUNT
Keepalive probe count (8 probes).
#define KEEPALIVE_INTERVAL
Keepalive interval in seconds (10 seconds).
#define KEEPALIVE_IDLE
Keepalive idle time in seconds (60 seconds).

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK, KEEPALIVE_COUNT, KEEPALIVE_IDLE, KEEPALIVE_INTERVAL, SET_ERRNO, and SET_ERRNO_SYS.

◆ set_socket_nonblocking()

asciichat_error_t set_socket_nonblocking ( socket_t sockfd)

#include <include/ascii-chat/network/network.h>

Set socket to non-blocking mode.

Parameters
sockfdSocket file descriptor
Returns
ASCIICHAT_OK on success, error code on failure

Sets socket to non-blocking mode. I/O operations return immediately with error if data is not available.

Note
Non-blocking sockets require different error handling - EAGAIN/EWOULDBLOCK indicates operation would block.
Useful for asynchronous I/O patterns with select/poll/epoll.

Set socket to non-blocking mode.

Parameters
sockfdSocket file descriptor
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 396 of file network/network.c.

396 {
397 if (sockfd == INVALID_SOCKET_VALUE) {
398 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket file descriptor");
399 }
400 int result = socket_set_nonblocking(sockfd, true);
401 if (result != 0) {
402 return SET_ERRNO_SYS(ERROR_NETWORK, "Failed to set socket non-blocking mode");
403 }
404 return ASCIICHAT_OK;
405}

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK, SET_ERRNO, and SET_ERRNO_SYS.

Referenced by connect_with_timeout().

◆ set_socket_timeout()

asciichat_error_t set_socket_timeout ( socket_t sockfd,
uint64_t timeout_ns )

#include <include/ascii-chat/network/network.h>

Set socket timeout for send/receive operations.

Parameters
sockfdSocket file descriptor
timeout_nsTimeout in nanoseconds (0 to disable timeout)
Returns
ASCIICHAT_OK on success, error code on failure

Configures socket-level timeout for send and receive operations. This sets SO_SNDTIMEO and SO_RCVTIMEO socket options.

Note
Socket-level timeouts work in addition to application-level timeouts in send_with_timeout() and recv_with_timeout().

Set socket timeout for send/receive operations.

Parameters
sockfdSocket file descriptor
timeout_nsTimeout in nanoseconds (converted to milliseconds for socket level)
Returns
ASCIICHAT_OK on success, error code on failure

Sets socket-level timeouts (SO_RCVTIMEO/SO_SNDTIMEO) as a safety net fallback. Actual precision depends on platform: milliseconds on most systems, microseconds on some. Works in conjunction with application-level timeouts via socket_poll.

Definition at line 345 of file network/network.c.

345 {
346 if (sockfd == INVALID_SOCKET_VALUE) {
347 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket file descriptor");
348 }
349
350 // Convert nanoseconds to milliseconds for socket-level timeout
351 // Note: socket-level timeouts have ~millisecond granularity on most platforms
352 uint64_t timeout_ms = timeout_ns / (1000 * 1000);
353 if (timeout_ms == 0 && timeout_ns > 0) {
354 timeout_ms = 1; // Ensure at least 1ms for non-zero timeouts
355 }
356
357 // Set both receive and send timeouts using struct timeval
358 struct timeval tv;
359 tv.tv_sec = (time_t)(timeout_ms / 1000);
360 tv.tv_usec = (long)((timeout_ms % 1000) * 1000);
361
362 // Set receive timeout
363 if (socket_setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
364 return SET_ERRNO_SYS(ERROR_NETWORK, "Failed to set socket receive timeout");
365 }
366
367 // Set send timeout
368 if (socket_setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
369 return SET_ERRNO_SYS(ERROR_NETWORK, "Failed to set socket send timeout");
370 }
371
372 return ASCIICHAT_OK;
373}

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK, SET_ERRNO, and SET_ERRNO_SYS.

◆ socket_configure_buffers()

asciichat_error_t socket_configure_buffers ( socket_t sockfd)

#include <include/ascii-chat/network/network.h>

Configure socket buffers and TCP_NODELAY for optimal performance.

Parameters
sockfdSocket file descriptor
Returns
ASCIICHAT_OK on success, or ERROR_NETWORK if all options fail

Configures socket with large send/receive buffers (1MB each) and enables TCP_NODELAY for low-latency real-time video streaming.

CONFIGURATION:

  • Send buffer: 1MB (SO_SNDBUF)
  • Receive buffer: 1MB (SO_RCVBUF)
  • TCP_NODELAY: enabled (disables Nagle's algorithm)

BEHAVIOR: Attempts to set all three socket options independently. Individual failures are logged as warnings but do not prevent attempting other options. This ensures TCP_NODELAY is always set even if buffer size configuration fails (common on systems with strict limits). Only returns error if ALL options fail.

Note
Large buffers prevent packet loss during frame bursts
TCP_NODELAY ensures frames are sent immediately without buffering
Call this immediately after socket creation/connection
Parameters
sockfdSocket file descriptor
Returns
ASCIICHAT_OK on success, ERROR_NETWORK_CONFIG on failure

Definition at line 412 of file network/network.c.

412 {
413 if (sockfd == INVALID_SOCKET_VALUE) {
414 return SET_ERRNO_SYS(ERROR_NETWORK, "Invalid socket file descriptor");
415 }
416
417 int failed_options = 0;
418
419 // Attempt to configure 1MB send buffer for optimal frame transmission
420 int send_buffer_size = 1024 * 1024;
421 if (socket_setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &send_buffer_size, sizeof(send_buffer_size)) < 0) {
422 log_warn("Failed to set send buffer size to 1MB: %s", network_error_string());
423 failed_options++;
424 }
425
426 // Attempt to configure 1MB receive buffer for optimal frame reception
427 int recv_buffer_size = 1024 * 1024;
428 if (socket_setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recv_buffer_size, sizeof(recv_buffer_size)) < 0) {
429 log_warn("Failed to set receive buffer size to 1MB: %s", network_error_string());
430 failed_options++;
431 }
432
433 // Attempt to enable TCP_NODELAY to disable Nagle's algorithm for low-latency transmission
434 // This must be set even if buffer configuration fails, as it's essential for real-time video.
435 int tcp_nodelay = 1;
436 if (socket_setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay, sizeof(tcp_nodelay)) < 0) {
437 log_warn("Failed to set TCP_NODELAY: %s", network_error_string());
438 failed_options++;
439 }
440
441 // Return error only if ALL options failed
442 if (failed_options >= 3) {
443 return SET_ERRNO_SYS(ERROR_NETWORK, "Failed to configure all socket options");
444 }
445
446 return ASCIICHAT_OK;
447}
const char * network_error_string()
Get human-readable error string for network errors.

References ASCIICHAT_OK, ERROR_NETWORK, log_warn, network_error_string(), and SET_ERRNO_SYS.

◆ update_check_detect_install_method()

install_method_t update_check_detect_install_method ( void )

#include <include/ascii-chat/network/update_checker.h>

Detect installation method.

Checks binary path and system files to determine how ascii-chat was installed.

Returns
Installation method enum

Definition at line 454 of file update_checker.c.

454 {
455#if defined(ASCIICHAT_INSTALLED_BY_HOMEBREW)
457#elif defined(ASCIICHAT_INSTALLED_BY_SCOOP)
459#elif defined(ASCIICHAT_INSTALLED_BY_AUR)
461#elif defined(_WIN32)
463#else
464 if (is_homebrew_install()) {
466 }
467
468 if (is_arch_linux()) {
470 }
471
472 // The published Unix installer is the default outside package-managed installs.
474#endif
475}

References INSTALL_METHOD_ARCH_AUR, INSTALL_METHOD_HOMEBREW, INSTALL_METHOD_INSTALLER, and INSTALL_METHOD_SCOOP.

Referenced by update_banner_install_update(), update_banner_set_result(), and update_check_format_notification().

◆ update_check_dismiss_for_a_week()

void update_check_dismiss_for_a_week ( update_check_result_t * result)

#include <include/ascii-chat/network/update_checker.h>

Persist a seven-day dismissal for the currently cached release.

Definition at line 242 of file update_checker.c.

242 {
243 if (!result) {
244 return;
245 }
247 (void)update_check_save_cache(result);
248}
asciichat_error_t update_check_save_cache(const update_check_result_t *result)
Save update check result to cache.
time_t dismissed_until
Suppress this available release until this time.
#define UPDATE_CHECK_CACHE_MAX_AGE_SECONDS

References update_check_result_t::dismissed_until, UPDATE_CHECK_CACHE_MAX_AGE_SECONDS, and update_check_save_cache().

Referenced by update_banner_show_prompt().

◆ update_check_format_notification()

void update_check_format_notification ( const update_check_result_t * result,
char * buffer,
size_t buffer_size )

#include <include/ascii-chat/network/update_checker.h>

Format update notification message.

Creates human-readable update notification with version comparison and upgrade suggestion. Example: "Update available: v0.8.1 (f8dc35e1) β†’ v0.9.0 (a1b2c3d4). Run: brew upgrade ascii-chat"

Parameters
resultUpdate check result
[out]bufferOutput buffer for formatted message
buffer_sizeSize of output buffer

Definition at line 533 of file update_checker.c.

533 {
534 if (!result || !buffer || buffer_size == 0) {
535 return;
536 }
537
538 // Get upgrade suggestion
541 update_check_get_upgrade_suggestion(method, result->latest_version, suggestion, sizeof(suggestion));
542
543 // Keep the notification useful even when it is displayed in a log line:
544 // identify the versions, provide an actionable command or download URL, and
545 // point to the exact GitHub release that was returned by the API.
546 snprintf(buffer, buffer_size, "Update available: %s β†’ %s. %s%s. Release notes: %s", result->current_version,
547 result->latest_version,
548 (method == INSTALL_METHOD_GITHUB || method == INSTALL_METHOD_UNKNOWN) ? "Download: " : "Run: ", suggestion,
549 result->release_url);
550}
int buffer_size
Size of circular buffer.
Definition grep.c:68
void update_check_get_upgrade_suggestion(install_method_t method, const char *latest_version, char *buffer, size_t buffer_size)
Get upgrade suggestion string.
install_method_t update_check_detect_install_method(void)
Detect installation method.
char latest_version[64]
Latest version tag (e.g., "v0.9.0").
char current_version[64]
Current version string.
char release_url[512]
GitHub release page URL.
#define NETWORK_UPDATE_CHECKER_SUGGESTION_BUFFER_SIZE

References buffer_size, update_check_result_t::current_version, INSTALL_METHOD_GITHUB, INSTALL_METHOD_UNKNOWN, update_check_result_t::latest_version, NETWORK_UPDATE_CHECKER_SUGGESTION_BUFFER_SIZE, update_check_result_t::release_url, update_check_detect_install_method(), and update_check_get_upgrade_suggestion().

Referenced by action_check_update_immediate().

◆ update_check_get_aur_helper()

const char * update_check_get_aur_helper ( void )

#include <include/ascii-chat/network/update_checker.h>

Return the detected AUR helper (paru or yay), or NULL if unavailable.

Definition at line 477 of file update_checker.c.

477 {
478 static _Atomic int cached_helper = -1;
479 int helper = atomic_load(&cached_helper);
480 if (helper == -1) {
481 const char *paru[] = {"paru", "--version", NULL};
482 const char *yay[] = {"yay", "--version", NULL};
483 helper = platform_execute_subprocess("paru", paru, NULL, 0, NULL, NULL) == 0 ? 1
484 : platform_execute_subprocess("yay", yay, NULL, 0, NULL, NULL) == 0 ? 2
485 : 0;
486 atomic_store(&cached_helper, helper);
487 }
488 return helper == 1 ? "paru" : helper == 2 ? "yay" : NULL;
489}
int platform_execute_subprocess(const char *executable, const char **argv, char *output_buffer, size_t output_size, bool(*cancel)(void *), void *data)
Execute a subprocess and optionally capture its output.

References platform_execute_subprocess().

Referenced by update_banner_install_update(), update_banner_set_result(), and update_check_get_upgrade_suggestion().

◆ update_check_get_upgrade_suggestion()

void update_check_get_upgrade_suggestion ( install_method_t method,
const char * latest_version,
char * buffer,
size_t buffer_size )

#include <include/ascii-chat/network/update_checker.h>

Get upgrade suggestion string.

Returns appropriate upgrade command or URL based on installation method.

Parameters
methodInstallation method
latest_versionLatest version tag for GitHub URL
[out]bufferOutput buffer for suggestion string
buffer_sizeSize of output buffer

Definition at line 491 of file update_checker.c.

492 {
493 if (!buffer || buffer_size == 0) {
494 return;
495 }
496
497 switch (method) {
499 snprintf(buffer, buffer_size, "brew upgrade ascii-chat");
500 break;
501
503 snprintf(buffer, buffer_size, "scoop update ascii-chat");
504 break;
505
507#ifdef _WIN32
508 snprintf(buffer, buffer_size, "irm https://ascii-chat.com/install.ps1 | iex");
509#else
510 snprintf(buffer, buffer_size, "curl -fsSL https://ascii-chat.com/install.sh | bash");
511#endif
512 break;
513
515 const char *helper = update_check_get_aur_helper();
516 if (helper) {
517 snprintf(buffer, buffer_size, "%s -S ascii-chat", helper);
518 } else {
519 snprintf(buffer, buffer_size, "<your-aur-helper> -Ss ascii-chat");
520 }
521 break;
522 }
523
526 default:
527 snprintf(buffer, buffer_size, "https://github.com/zfogg/ascii-chat/releases/tag/%s",
528 latest_version ? latest_version : "latest");
529 break;
530 }
531}
const char * update_check_get_aur_helper(void)

References buffer_size, INSTALL_METHOD_ARCH_AUR, INSTALL_METHOD_GITHUB, INSTALL_METHOD_HOMEBREW, INSTALL_METHOD_INSTALLER, INSTALL_METHOD_SCOOP, INSTALL_METHOD_UNKNOWN, and update_check_get_aur_helper().

Referenced by update_check_format_notification().

◆ update_check_is_cache_fresh()

bool update_check_is_cache_fresh ( const update_check_result_t * result)

#include <include/ascii-chat/network/update_checker.h>

Check if cache is fresh (< 7 days old).

Parameters
resultCached result to check
Returns
true if cache is < 7 days old, false otherwise

Definition at line 231 of file update_checker.c.

231 {
232 if (!result || result->last_check_time == 0) {
233 return false;
234 }
235
236 time_t now = time(NULL);
237 time_t age = now - result->last_check_time;
238
240}
time_t last_check_time
Timestamp of last check.

References update_check_result_t::last_check_time, and UPDATE_CHECK_CACHE_MAX_AGE_SECONDS.

Referenced by update_check_startup(), and update_check_try_fresh_cache().

◆ update_check_is_dismissed()

bool update_check_is_dismissed ( const update_check_result_t * result)

#include <include/ascii-chat/network/update_checker.h>

True when the cached available release remains dismissed.

Definition at line 250 of file update_checker.c.

250 {
251 return result && result->dismissed_until > time(NULL);
252}

References update_check_result_t::dismissed_until.

Referenced by update_banner_show_prompt().

◆ update_check_load_cache()

asciichat_error_t update_check_load_cache ( update_check_result_t * result)

#include <include/ascii-chat/network/update_checker.h>

Load cached update check result.

Reads cache file from ~/.config/ascii-chat/last_update_check. Cache format (4 lines):

  • Line 1: Unix timestamp
  • Line 2: Latest version tag (or empty if check failed)
  • Line 3: Latest SHA (full 40 chars, or empty if check failed)
  • Line 4: Exact GitHub release URL
Parameters
[out]resultOutput structure with cached results
Returns
ASCIICHAT_OK if cache valid, error if missing/corrupt

Definition at line 95 of file update_checker.c.

95 {
96 if (!result) {
97 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL result pointer");
98 }
99
100 memset(result, 0, sizeof(*result));
101
102 char *cache_path = get_cache_file_path();
103 if (!cache_path) {
104 return SET_ERRNO(ERROR_FILE_OPERATION, "Could not determine cache file path");
105 }
106 defer(SAFE_FREE(cache_path));
107
108 /* A cache miss is expected on first launch. Check existence through the
109 * platform layer before opening so Windows does not turn the missing file
110 * into a stale errno-stack notification. */
111 if (platform_access(cache_path, PLATFORM_ACCESS_EXISTS) != 0)
112 return ASCIICHAT_OK;
113
114 FILE *f = platform_fopen("file_stream", cache_path, "r");
115 if (!f) {
116 const char *error_msg = file_read_error_message(cache_path);
117 return SET_ERRNO(ERROR_FILE_OPERATION, "%s", error_msg);
118 }
119 defer(SAFE_FCLOSE(f));
120
121 // Read line 1: timestamp
123 if (!fgets(line, sizeof(line), f)) {
124 return SET_ERRNO(ERROR_FILE_OPERATION, "Failed to read timestamp from cache");
125 }
126
127 // Parse timestamp safely with error checking
128 errno = 0;
129 char *endptr = NULL;
130 long long timestamp_ll = strtoll(line, &endptr, 10);
131 if (errno != 0 || endptr == line) {
132 log_error("Failed to parse timestamp from cache: invalid value '%s'", line);
133 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid timestamp in cache file");
134 }
135 result->last_check_time = (time_t)timestamp_ll;
136
137 // Read line 2: latest version (may be empty if check failed)
138 if (fgets(line, sizeof(line), f)) {
139 trim_line_ending(line);
140 SAFE_STRNCPY(result->latest_version, line, sizeof(result->latest_version));
141 }
142
143 // Read line 3: latest SHA (may be empty if check failed)
144 if (fgets(line, sizeof(line), f)) {
145 trim_line_ending(line);
146 SAFE_STRNCPY(result->latest_sha, line, sizeof(result->latest_sha));
147 }
148
149 // Read line 4: release URL. This field is required in the current cache
150 // format; an older or partially-written cache must be refreshed.
151 if (!fgets(line, sizeof(line), f)) {
152 SAFE_FCLOSE(f);
153 // Treat a legacy cache exactly like a missing cache. Close the file before
154 // removing it so this also works on Windows, where open files cannot be
155 // deleted.
156 (void)remove(cache_path);
157 return SET_ERRNO(ERROR_FORMAT, "Update cache format is obsolete");
158 }
159 trim_line_ending(line);
160 SAFE_STRNCPY(result->release_url, line, sizeof(result->release_url));
161
162 // Older four-line cache files remain valid; they simply have no dismissal.
163 if (fgets(line, sizeof(line), f)) {
164 result->dismissed_until = (time_t)strtoll(line, NULL, 10);
165 }
166
167 SAFE_FCLOSE(f);
168
169 // Fill in current version/SHA
170 SAFE_STRNCPY(result->current_version, ASCII_CHAT_VERSION_STRING, sizeof(result->current_version));
171 SAFE_STRNCPY(result->current_sha, ASCII_CHAT_GIT_COMMIT_HASH, sizeof(result->current_sha));
172
173 // Determine if update is available using version comparison (if we have cached data)
174 if (result->latest_version[0] != '\0') {
175 semantic_version_t current_ver = version_parse(result->current_version);
176 semantic_version_t latest_ver = version_parse(result->latest_version);
177
178 if (current_ver.valid && latest_ver.valid) {
179 int cmp = version_compare(latest_ver, current_ver);
180 result->update_available = (cmp > 0); // Update available if latest > current
181 result->check_succeeded = true;
182 } else {
183 // Cache contains invalid version data - delete it
184 log_warn("Update cache contains invalid version data (current:%s, latest:%s) - deleting corrupted cache",
185 result->current_version, result->latest_version);
186 if (remove(cache_path) != 0) {
187 log_warn("Failed to delete corrupted cache file: %s", cache_path);
188 }
189 return SET_ERRNO(ERROR_FORMAT, "Corrupted cache file deleted");
190 }
191 }
192
193 return ASCIICHAT_OK;
194}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
#define SAFE_FCLOSE(fp)
Definition common.h:395
@ ERROR_FILE_OPERATION
@ ERROR_FORMAT
char current_sha[41]
Current commit SHA.
bool update_available
True if newer version exists.
bool check_succeeded
True if check completed (even if no update).
char latest_sha[41]
Latest release commit SHA (full 40 chars + null).
#define NETWORK_UPDATE_CHECKER_LINE_BUFFER_SIZE
int version_compare(semantic_version_t a, semantic_version_t b)
Definition version.c:112
semantic_version_t version_parse(const char *version_string)
Definition version.c:49

References ASCIICHAT_OK, update_check_result_t::check_succeeded, update_check_result_t::current_sha, update_check_result_t::current_version, update_check_result_t::dismissed_until, errno, ERROR_FILE_OPERATION, ERROR_FORMAT, ERROR_INVALID_PARAM, update_check_result_t::last_check_time, update_check_result_t::latest_sha, update_check_result_t::latest_version, log_error, log_warn, NETWORK_UPDATE_CHECKER_LINE_BUFFER_SIZE, update_check_result_t::release_url, SAFE_FCLOSE, SAFE_FREE, SAFE_STRNCPY, SET_ERRNO, update_check_result_t::update_available, version_compare(), and version_parse().

◆ update_check_perform()

asciichat_error_t update_check_perform ( update_check_result_t * result)

#include <include/ascii-chat/network/update_checker.h>

Check for updates from GitHub releases API.

Performs DNS connectivity test, fetches latest release from GitHub API, compares SHA with current build, and caches the result.

Parameters
[out]resultOutput structure with check results
Returns
ASCIICHAT_OK on success (check completed), error otherwise
Note
On timeout or network error, logs warning and marks as checked in cache
On DNS failure, does not update cache (will retry when online)
Timeout is 2 seconds for the entire operation

Definition at line 311 of file update_checker.c.

311 {
312 if (!result) {
313 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL result pointer");
314 }
315
316 update_check_result_t cached = {0};
317 bool have_cached = load_optional_cache(&cached) == ASCIICHAT_OK;
318 memset(result, 0, sizeof(*result));
319
320 // Fill in current version and SHA
321 SAFE_STRNCPY(result->current_version, ASCII_CHAT_VERSION_STRING, sizeof(result->current_version));
322 SAFE_STRNCPY(result->current_sha, ASCII_CHAT_GIT_COMMIT_HASH, sizeof(result->current_sha));
323 result->last_check_time = time(NULL);
324
325#ifndef NDEBUG
326 // Deterministic local seam for PTY smoke coverage. Release builds never
327 // consult it, and ordinary debug runs continue through the network path.
328 const char *test_latest = SAFE_GETENV("ASCII_CHAT_TEST_UPDATE_LATEST_VERSION");
329 if (test_latest && test_latest[0]) {
330 SAFE_STRNCPY(result->latest_version, test_latest, sizeof(result->latest_version));
331 const char *test_slow = SAFE_GETENV("ASCII_CHAT_TEST_UPDATE_SLOW");
332 if (test_slow && strcmp(test_slow, "1") == 0)
333 platform_sleep_ms(1000);
334 const char *test_gate = SAFE_GETENV("ASCII_CHAT_TEST_UPDATE_GATE");
335 if (test_gate && test_gate[0]) {
336 uint64_t deadline = time_get_ns() + 30 * NS_PER_SEC_INT;
337 while (platform_access(test_gate, PLATFORM_ACCESS_EXISTS) != 0 && time_get_ns() < deadline)
339 }
340 SAFE_STRNCPY(result->release_url, "https://github.com/zfogg/ascii-chat/releases/latest",
341 sizeof(result->release_url));
342 result->check_succeeded = true;
343 semantic_version_t current_ver = version_parse(result->current_version);
344 semantic_version_t latest_ver = version_parse(result->latest_version);
345 result->update_available = current_ver.valid && latest_ver.valid && version_compare(latest_ver, current_ver) > 0;
346 if (have_cached && strcmp(cached.latest_version, result->latest_version) == 0) {
347 result->dismissed_until = cached.dismissed_until;
348 }
349 (void)update_check_save_cache(result);
350 return ASCIICHAT_OK;
351 }
352#endif
353
354 // Fetch latest release from GitHub API
355 log_info("Checking for updates from GitHub releases...");
357 if (!response) {
358 log_warn("Failed to fetch GitHub releases API (timeout or network error)");
359 // Don't save cache on network failure β€” retry next startup
360 result->check_succeeded = false;
361 return SET_ERRNO(ERROR_NETWORK, "Failed to fetch GitHub releases");
362 }
363
364 defer(SAFE_FREE(response));
365
366 // Parse JSON response
369
370 if (!parse_github_release_json(response, latest_tag, sizeof(latest_tag), release_url, sizeof(release_url))) {
371 log_error("Failed to parse GitHub API response");
372 // Don't save cache on parse failure β€” retry next startup
373 result->check_succeeded = false;
374 return SET_ERRNO(ERROR_FORMAT, "Failed to parse GitHub API JSON");
375 }
376
377 // Fill in result
378 SAFE_STRNCPY(result->latest_version, latest_tag, sizeof(result->latest_version));
379 SAFE_STRNCPY(result->release_url, release_url, sizeof(result->release_url));
380 result->latest_sha[0] = '\0';
381 result->check_succeeded = true;
382 if (have_cached && strcmp(cached.latest_version, result->latest_version) == 0) {
383 result->dismissed_until = cached.dismissed_until;
384 }
385
386 // Compare versions semantically
387 semantic_version_t current_ver = version_parse(result->current_version);
388 semantic_version_t latest_ver = version_parse(result->latest_version);
389
390 if (!current_ver.valid || !latest_ver.valid) {
391 log_warn("Failed to parse version strings for comparison (current: %s, latest: %s)", result->current_version,
392 result->latest_version);
393 result->update_available = false;
394 } else {
395 int cmp = version_compare(latest_ver, current_ver);
396 result->update_available = (cmp > 0); // Update available if latest > current
397 }
398
399 if (result->update_available) {
400 log_info("Update available: %s β†’ %s", result->current_version, result->latest_version);
401 } else {
402 log_info("Already on latest version: %s", result->current_version);
403 }
404
405 // Save to cache
407
408 return ASCIICHAT_OK;
409}
#define SAFE_GETENV(name)
Definition common.h:449
#define NETWORK_UPDATE_CHECKER_RELEASE_URL_BUFFER_SIZE
char * https_get(const char *hostname, const char *path)
Perform HTTPS GET request.
void platform_sleep_ms(unsigned int ms)
Sleep for a specified number of milliseconds.
update check result keeps the values that move through the subsystem: update_available,...
#define GITHUB_API_HOSTNAME
#define GITHUB_RELEASES_PATH
#define NETWORK_UPDATE_CHECKER_LATEST_TAG_BUFFER_SIZE

References ASCIICHAT_OK, update_check_result_t::check_succeeded, update_check_result_t::current_sha, update_check_result_t::current_version, update_check_result_t::dismissed_until, ERROR_FORMAT, ERROR_INVALID_PARAM, ERROR_NETWORK, GITHUB_API_HOSTNAME, GITHUB_RELEASES_PATH, https_get(), update_check_result_t::last_check_time, update_check_result_t::latest_sha, update_check_result_t::latest_version, log_error, log_info, log_warn, NETWORK_UPDATE_CHECKER_LATEST_TAG_BUFFER_SIZE, NETWORK_UPDATE_CHECKER_RELEASE_URL_BUFFER_SIZE, NS_PER_SEC_INT, platform_sleep_ms(), update_check_result_t::release_url, SAFE_FREE, SAFE_GETENV, SAFE_STRNCPY, SET_ERRNO, update_check_result_t::update_available, update_check_save_cache(), version_compare(), and version_parse().

Referenced by action_check_update_immediate(), and update_check_startup().

◆ update_check_save_cache()

asciichat_error_t update_check_save_cache ( const update_check_result_t * result)

#include <include/ascii-chat/network/update_checker.h>

Save update check result to cache.

Writes cache file to ~/.config/ascii-chat/last_update_check.

Parameters
resultResult to cache
Returns
ASCIICHAT_OK on success, error otherwise

Definition at line 196 of file update_checker.c.

196 {
197 if (!result) {
198 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL result pointer");
199 }
200
201 char *cache_path = get_cache_file_path();
202 if (!cache_path) {
203 return SET_ERRNO(ERROR_FILE_OPERATION, "Could not determine cache file path");
204 }
205 defer(SAFE_FREE(cache_path));
206
207 FILE *f = platform_fopen("file_stream", cache_path, "w");
208 if (!f) {
209 const char *error_msg = file_write_error_message(cache_path);
210 return SET_ERRNO(ERROR_FILE_OPERATION, "%s", error_msg);
211 }
212
213 // Write timestamp
214 fprintf(f, "%lld\n", (long long)result->last_check_time);
215
216 // Write version (may be empty if check failed)
217 fprintf(f, "%s\n", result->latest_version);
218
219 // Write SHA (may be empty if check failed)
220 fprintf(f, "%s\n", result->latest_sha);
221
222 // Write the release URL so cached notifications retain the exact GitHub
223 // source returned by the API.
224 fprintf(f, "%s\n", result->release_url);
225 fprintf(f, "%lld\n", (long long)result->dismissed_until);
226
227 fclose(f);
228 return ASCIICHAT_OK;
229}

References ASCIICHAT_OK, update_check_result_t::dismissed_until, ERROR_FILE_OPERATION, ERROR_INVALID_PARAM, update_check_result_t::last_check_time, update_check_result_t::latest_sha, update_check_result_t::latest_version, update_check_result_t::release_url, SAFE_FREE, and SET_ERRNO.

Referenced by update_check_dismiss_for_a_week(), and update_check_perform().

◆ update_check_startup()

asciichat_error_t update_check_startup ( update_check_result_t * result)

#include <include/ascii-chat/network/update_checker.h>

Perform startup update check with caching.

Designed for automatic startup checks. Uses cache if fresh (< 7 days), otherwise performs fresh check. Non-blocking and silent on failure.

Parameters
[out]resultOutput structure with check results (can be NULL if you only want logging)
Returns
ASCIICHAT_OK if check succeeded, error otherwise
Note
Does nothing if cache is fresh
Logs warning on network failure but does not fail
Updates cache only if check succeeds

Definition at line 552 of file update_checker.c.

552 {
553 update_check_result_t local_result;
554 update_check_result_t *target = result ? result : &local_result;
555
556 // Try to load from cache first
557 asciichat_error_t cache_err = load_optional_cache(target);
558 if (cache_err == ASCIICHAT_OK && update_check_is_cache_fresh(target)) {
559 // Cache is fresh, use it
560 log_debug("Using cached update check result (age: %.1f days)",
561 (time(NULL) - target->last_check_time) / (double)SEC_PER_DAY);
562 return ASCIICHAT_OK;
563 }
564
565 // Cache is stale or missing, perform fresh check
566 log_debug("Performing automatic update check (cache %s)", cache_err == ASCIICHAT_OK ? "stale" : "missing");
567 asciichat_error_t check_err = update_check_perform(target);
568 if (check_err != ASCIICHAT_OK) {
569 // Check failed, but don't fail startup
570 log_debug("Automatic update check failed (continuing startup)");
571 return check_err;
572 }
573
574 return ASCIICHAT_OK;
575}
#define SEC_PER_DAY
Definition time.h:181
bool update_check_is_cache_fresh(const update_check_result_t *result)
Check if cache is fresh (< 7 days old).
asciichat_error_t update_check_perform(update_check_result_t *result)
Check for updates from GitHub releases API.

References ASCIICHAT_OK, update_check_result_t::last_check_time, log_debug, SEC_PER_DAY, update_check_is_cache_fresh(), and update_check_perform().

◆ update_check_try_fresh_cache()

bool update_check_try_fresh_cache ( update_check_result_t * result)

#include <include/ascii-chat/network/update_checker.h>

Load a fresh cached result without treating a cache miss as an error.

Definition at line 307 of file update_checker.c.

307 {
308 return result && load_optional_cache(result) == ASCIICHAT_OK && update_check_is_cache_fresh(result);
309}

References ASCIICHAT_OK, and update_check_is_cache_fresh().