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

ACIP server-side protocol implementation. More...

Go to the source code of this file.

Functions

asciichat_error_t acip_server_receive_and_dispatch (acip_transport_t *transport, void *client_ctx, const acip_server_callbacks_t *callbacks)
 
asciichat_error_t acip_send_ascii_frame (acip_transport_t *transport, const char *frame_data, size_t frame_size, uint32_t width, uint32_t height, uint32_t client_id)
 
asciichat_error_t acip_send_clear_console (acip_transport_t *transport)
 
asciichat_error_t acip_send_server_state (acip_transport_t *transport, const server_state_packet_t *state)
 

Detailed Description

ACIP server-side protocol implementation.

Provides server-side ACIP protocol utilities for:

  • Packet validation
  • Error response generation
  • Protocol compliance checking

ACIP (ascii-chat IP Protocol) is the wire protocol for session discovery and WebRTC signaling. This module provides reusable server-side utilities for any ACIP server implementation.

Server-side packet sending and receiving for ascii-chat protocol. Handles server→client communication (ASCII frames, state updates, console control).

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

Definition in file lib/network/acip/server.c.

Function Documentation

◆ acip_send_ascii_frame()

asciichat_error_t acip_send_ascii_frame ( acip_transport_t *  transport,
const char *  frame_data,
size_t  frame_size,
uint32_t  width,
uint32_t  height,
uint32_t  client_id 
)

Definition at line 182 of file lib/network/acip/server.c.

183 {
184 if (!transport || !frame_data) {
185 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or frame_data");
186 }
187
188 if (frame_size == 0) {
189 return SET_ERRNO(ERROR_INVALID_PARAM, "Empty frame data");
190 }
191
192 // Calculate CRC32 checksum of frame data for integrity verification
193 uint32_t checksum_value = asciichat_crc32(frame_data, frame_size);
194
195 // Create ASCII frame packet header
196 ascii_frame_packet_t header;
197 header.width = HOST_TO_NET_U32(width);
198 header.height = HOST_TO_NET_U32(height);
199 header.original_size = HOST_TO_NET_U32((uint32_t)frame_size);
200 header.compressed_size = 0;
201 header.checksum = HOST_TO_NET_U32(checksum_value);
202 header.flags = 0;
203
204 // Calculate total packet size
205 size_t total_size;
206 if (checked_size_add(sizeof(header), frame_size, &total_size) != ASCIICHAT_OK) {
207 return SET_ERRNO(ERROR_INVALID_PARAM, "Packet size overflow");
208 }
209
210 // Allocate buffer
211 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
212 if (!buffer) {
213 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer: %zu bytes", total_size);
214 }
215
216 // Build packet: header + data
217 memcpy(buffer, &header, sizeof(header));
218 memcpy(buffer + sizeof(header), frame_data, frame_size);
219
220 // Send via transport with client_id
221 asciichat_error_t result =
222 packet_send_via_transport(transport, PACKET_TYPE_ASCII_FRAME, buffer, total_size, client_id);
223
224 buffer_pool_free(NULL, buffer, total_size);
225 return result;
226}
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Definition buffer_pool.c:99
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:41

References buffer_pool_alloc(), buffer_pool_free(), and packet_send_via_transport().

Referenced by client_send_thread_func().

◆ acip_send_clear_console()

asciichat_error_t acip_send_clear_console ( acip_transport_t *  transport)

Definition at line 228 of file lib/network/acip/server.c.

228 {
229 if (!transport) {
230 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
231 }
232
233 return packet_send_via_transport(transport, PACKET_TYPE_CLEAR_CONSOLE, NULL, 0, 0);
234}

References packet_send_via_transport().

Referenced by client_send_thread_func().

◆ acip_send_server_state()

asciichat_error_t acip_send_server_state ( acip_transport_t *  transport,
const server_state_packet_t *  state 
)

Definition at line 236 of file lib/network/acip/server.c.

236 {
237 if (!transport || !state) {
238 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or state");
239 }
240
241 return packet_send_via_transport(transport, PACKET_TYPE_SERVER_STATE, state, sizeof(*state), 0);
242}

References packet_send_via_transport().

Referenced by add_webrtc_client(), broadcast_server_state_to_all_clients(), and send_server_state_to_client().

◆ acip_server_receive_and_dispatch()

asciichat_error_t acip_server_receive_and_dispatch ( acip_transport_t *  transport,
void *  client_ctx,
const acip_server_callbacks_t *  callbacks 
)

Definition at line 31 of file lib/network/acip/server.c.

32 {
33 uint64_t dispatch_start_ns = time_get_ns();
34 log_dev_every(4500 * US_PER_MS_INT,
35 "ACIP_SERVER_DISPATCH: Entry, transport=%p, client_ctx=%p, callbacks=%p, timestamp=%llu",
36 (void *)transport, client_ctx, (const void *)callbacks, (unsigned long long)dispatch_start_ns);
37
38 if (!transport) {
39 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport (NULL)");
40 }
41 if (!callbacks) {
42 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid callbacks (NULL)");
43 }
44
45 // Check if transport is connected
46 if (!transport->methods->is_connected(transport)) {
47 return SET_ERRNO(ERROR_NETWORK, "Transport not connected");
48 }
49
50 packet_envelope_t envelope;
51 bool enforce_encryption = (transport->crypto_ctx != NULL);
52
53 // Try to get socket from transport
54 socket_t sock = transport->methods->get_socket(transport);
55 log_dev_every(4500 * US_PER_MS_INT, "ACIP_SERVER_DISPATCH: Socket=%d (INVALID=%d)", sock, INVALID_SOCKET_VALUE);
56
57 if (sock != INVALID_SOCKET_VALUE) {
58 // Socket-based transport (TCP): use receive_packet_secure() for socket I/O + parsing
59 packet_recv_result_t result = receive_packet_secure(sock, transport->crypto_ctx, enforce_encryption, &envelope);
60
61 // Handle receive errors
62 if (result != PACKET_RECV_SUCCESS) {
63 if (result == PACKET_RECV_EOF) {
64 return SET_ERRNO(ERROR_NETWORK, "Connection closed (EOF)");
65 } else if (result == PACKET_RECV_SECURITY_VIOLATION) {
66 return SET_ERRNO(ERROR_CRYPTO, "Security violation: unencrypted packet when encryption required");
67 } else {
68 return SET_ERRNO(ERROR_NETWORK, "Failed to receive packet");
69 }
70 }
71 } else {
72 // Non-socket transport (WebRTC): use transport's recv() method to get complete packet
73 void *packet_data = NULL;
74 void *allocated_buffer = NULL;
75 size_t packet_len = 0;
76
77 uint64_t recv_start_ns = time_get_ns();
78 asciichat_error_t recv_result = transport->methods->recv(transport, &packet_data, &packet_len, &allocated_buffer);
79 uint64_t recv_end_ns = time_get_ns();
80
81 if (recv_result != ASCIICHAT_OK) {
82 char recv_duration_str[32];
83 format_duration_ns((double)(recv_end_ns - recv_start_ns), recv_duration_str, sizeof(recv_duration_str));
84 log_warn("[ACIP_RECV_ERROR] recv() failed after %s (result=%d)", recv_duration_str, recv_result);
85 return SET_ERRNO(ERROR_NETWORK, "Transport recv() failed");
86 }
87
88 char recv_duration_str[32];
89 format_duration_ns((double)(recv_end_ns - recv_start_ns), recv_duration_str, sizeof(recv_duration_str));
90 log_info("[ACIP_RECV_SUCCESS] Received %zu bytes in %s", packet_len, recv_duration_str);
91
92 // Parse packet header
93 if (packet_len < sizeof(packet_header_t)) {
94 buffer_pool_free(NULL, allocated_buffer, packet_len);
95 return SET_ERRNO(ERROR_NETWORK, "Packet too small: %zu < %zu", packet_len, sizeof(packet_header_t));
96 }
97
98 const packet_header_t *header = (const packet_header_t *)packet_data;
99 envelope.type = NET_TO_HOST_U16(header->type);
100 envelope.len = NET_TO_HOST_U32(header->length);
101 envelope.data = (uint8_t *)packet_data + sizeof(packet_header_t);
102 envelope.allocated_buffer = allocated_buffer;
103 envelope.allocated_size = packet_len;
104
105 log_dev_every(4500 * US_PER_MS_INT, "ACIP_SERVER_DISPATCH: WebRTC packet parsed: type=%d, len=%u", envelope.type,
106 envelope.len);
107
108 // Handle PACKET_TYPE_ENCRYPTED from WebSocket clients that encrypt at application layer
109 if (envelope.type == PACKET_TYPE_ENCRYPTED && transport->crypto_ctx) {
110 uint64_t decrypt_start_ns = time_get_ns();
111 uint8_t *ciphertext = (uint8_t *)envelope.data;
112 size_t ciphertext_len = envelope.len;
113
114 // Decrypt to get inner plaintext packet (header + payload)
115 size_t plaintext_size = ciphertext_len + 1024;
116 uint8_t *plaintext = buffer_pool_alloc(NULL, plaintext_size);
117 if (!plaintext) {
118 buffer_pool_free(NULL, allocated_buffer, packet_len);
119 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate plaintext buffer for decryption");
120 }
121
122 size_t plaintext_len;
123 crypto_result_t crypto_result =
124 crypto_decrypt(transport->crypto_ctx, ciphertext, ciphertext_len, plaintext, plaintext_size, &plaintext_len);
125
126 // Free the original encrypted buffer
127 buffer_pool_free(NULL, allocated_buffer, packet_len);
128
129 if (crypto_result != CRYPTO_OK) {
130 buffer_pool_free(NULL, plaintext, plaintext_size);
131 return SET_ERRNO(ERROR_CRYPTO, "Failed to decrypt WebSocket packet: %s",
132 crypto_result_to_string(crypto_result));
133 }
134
135 if (plaintext_len < sizeof(packet_header_t)) {
136 buffer_pool_free(NULL, plaintext, plaintext_size);
137 return SET_ERRNO(ERROR_CRYPTO, "Decrypted packet too small: %zu < %zu", plaintext_len, sizeof(packet_header_t));
138 }
139
140 // Parse the inner (decrypted) header
141 const packet_header_t *inner_header = (const packet_header_t *)plaintext;
142 envelope.type = NET_TO_HOST_U16(inner_header->type);
143 envelope.len = NET_TO_HOST_U32(inner_header->length);
144 envelope.data = plaintext + sizeof(packet_header_t);
145 envelope.allocated_buffer = plaintext;
146 envelope.allocated_size = plaintext_size;
147
148 uint64_t decrypt_end_ns = time_get_ns();
149 char decrypt_duration_str[32];
150 format_duration_ns((double)(decrypt_end_ns - decrypt_start_ns), decrypt_duration_str,
151 sizeof(decrypt_duration_str));
152 log_info("[WS_TIMING] Decrypt %zu bytes → %zu bytes in %s (inner_type=%d)", ciphertext_len, plaintext_len,
153 decrypt_duration_str, envelope.type);
154 }
155 }
156
157 // Dispatch packet to appropriate ACIP handler
158 // Server receives packets FROM clients, so use server packet handler
159 log_info("ACIP_DISPATCH_PKT: type=%d, len=%zu, client_ctx=%p", envelope.type, envelope.len, client_ctx);
160 uint64_t dispatch_handler_start_ns = time_get_ns();
161 asciichat_error_t dispatch_result =
162 acip_handle_server_packet(transport, envelope.type, envelope.data, envelope.len, client_ctx, callbacks);
163 uint64_t dispatch_handler_end_ns = time_get_ns();
164 char handler_duration_str[32];
165 format_duration_ns((double)(dispatch_handler_end_ns - dispatch_handler_start_ns), handler_duration_str,
166 sizeof(handler_duration_str));
167 log_info("[WS_TIMING] Handler for type=%d took %s (result=%d)", envelope.type, handler_duration_str, dispatch_result);
168
169 // Always free the allocated buffer (even if handler failed)
170 if (envelope.allocated_buffer) {
171 buffer_pool_free(NULL, envelope.allocated_buffer, envelope.allocated_size);
172 }
173
174 // Return handler result
175 return dispatch_result;
176}
int socket_t
const char * crypto_result_to_string(crypto_result_t result)
crypto_result_t crypto_decrypt(crypto_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext_out, size_t plaintext_out_size, size_t *plaintext_len_out)
asciichat_error_t acip_handle_server_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, void *client_ctx, const acip_server_callbacks_t *callbacks)
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.
Definition packet.c:566
uint64_t time_get_ns(void)
Definition util/time.c:48
int format_duration_ns(double nanoseconds, char *buffer, size_t buffer_size)
Definition util/time.c:275

References acip_handle_server_packet(), buffer_pool_alloc(), buffer_pool_free(), crypto_decrypt(), crypto_result_to_string(), format_duration_ns(), receive_packet_secure(), and time_get_ns().

Referenced by client_receive_thread().