ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
lib/network/acip/server.c
Go to the documentation of this file.
1
13#include <ascii-chat/network/acip/server.h>
14#include <ascii-chat/network/acip/send.h>
15#include <ascii-chat/network/acip/handlers.h>
16#include <ascii-chat/network/packet.h>
17#include <ascii-chat/buffer_pool.h>
18#include <ascii-chat/log/logging.h>
19#include <ascii-chat/common.h>
20#include <ascii-chat/util/endian.h>
21#include <ascii-chat/util/overflow.h>
22#include <ascii-chat/util/time.h>
23#include <ascii-chat/network/crc32.h>
24#include <ascii-chat/crypto/crypto.h>
25#include <string.h>
26
27// =============================================================================
28// Server Receive (from client)
29// =============================================================================
30
31asciichat_error_t acip_server_receive_and_dispatch(acip_transport_t *transport, void *client_ctx,
32 const acip_server_callbacks_t *callbacks) {
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}
177
178// =============================================================================
179// Server Send (to client)
180// =============================================================================
181
182asciichat_error_t acip_send_ascii_frame(acip_transport_t *transport, const char *frame_data, size_t frame_size,
183 uint32_t width, uint32_t height, uint32_t client_id) {
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}
227
228asciichat_error_t acip_send_clear_console(acip_transport_t *transport) {
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}
235
236asciichat_error_t acip_send_server_state(acip_transport_t *transport, const server_state_packet_t *state) {
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}
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
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_send_server_state(acip_transport_t *transport, const server_state_packet_t *state)
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_clear_console(acip_transport_t *transport)
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_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
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
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