ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
packet.c
Go to the documentation of this file.
1
9#include <ascii-chat/common.h>
20#include <ascii-chat/options/rcu.h> // For RCU-based options access
21#include <stdint.h>
22#include <errno.h>
23#include <stdbool.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28// Network timeout constants are defined in packet_types.h
29
30/* ============================================================================
31 * Packet Protocol Implementation
32 * ============================================================================
33 * This module handles packet verification, CRC validation, and protocol
34 * compliance checking for all network packets.
35 */
36
37// Use network_network_is_test_environment() from network.h
38
45static uint64_t calculate_packet_timeout(size_t packet_size) {
46 uint64_t base_timeout = SEND_TIMEOUT * NS_PER_SEC_INT;
47
48 // For large packets, increase timeout proportionally
49 if (packet_size > LARGE_PACKET_THRESHOLD) {
50 // Add extra timeout per MB above the threshold
51 uint64_t extra_timeout =
52 (uint64_t)(((double)packet_size - LARGE_PACKET_THRESHOLD) / 1000000.0 * LARGE_PACKET_EXTRA_TIMEOUT_PER_MB) +
54 uint64_t total_timeout = base_timeout + extra_timeout;
55
56 // Ensure client timeout is longer than server's RECV_TIMEOUT (30s) to prevent deadlock
57 // Add 10 seconds buffer to account for server processing delays
58 if (total_timeout < MIN_CLIENT_TIMEOUT) {
59 total_timeout = MIN_CLIENT_TIMEOUT;
60 }
61
62 // Cap at maximum timeout
63 return (total_timeout > MAX_CLIENT_TIMEOUT) ? MAX_CLIENT_TIMEOUT : total_timeout;
64 }
65
66 return base_timeout;
67}
68
77static asciichat_error_t packet_validate_header(const packet_header_t *header, uint16_t *pkt_type, uint32_t *pkt_len,
78 uint32_t *expected_crc) {
79 if (!header || !pkt_type || !pkt_len || !expected_crc) {
80 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: header=%p, pkt_type=%p, pkt_len=%p, expected_crc=%p",
81 header, pkt_type, pkt_len, expected_crc);
82 }
83
84 // First validate packet length BEFORE converting from network byte order
85 // This prevents potential integer overflow issues
86 uint32_t pkt_len_network = header->length;
87 if (pkt_len_network == 0xFFFFFFFF) {
88 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid packet length in network byte order: 0xFFFFFFFF");
89 }
90
91 // Convert from network byte order using safe helpers
92 uint64_t magic = endian_unpack_u64(header->magic);
93 uint16_t type = endian_unpack_u16(header->type);
94 uint32_t len = endian_unpack_u32(pkt_len_network);
95 uint32_t crc = endian_unpack_u32(header->crc32);
96
97 // Validate magic
98 if (magic != PACKET_MAGIC) {
99 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid packet magic: 0x%x (expected 0x%x)", magic, PACKET_MAGIC);
100 }
101
102 // Validate packet size with bounds checking
103 if (len > MAX_PACKET_SIZE) {
104 return SET_ERRNO(ERROR_NETWORK_SIZE, "Packet too large: %u > %d", len, MAX_PACKET_SIZE);
105 }
106
107 // Validate packet type and size constraints
108 switch (type) {
110 // Protocol version packet has fixed size
111 if (len != sizeof(protocol_version_packet_t)) {
112 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid protocol version packet size: %u, expected %zu", len,
114 }
115 break;
117 // ASCII frame contains header + frame data
118 if (len < sizeof(ascii_frame_packet_t)) {
119 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid ASCII frame packet size: %u, minimum %zu", len,
120 sizeof(ascii_frame_packet_t));
121 }
122 break;
124 // Image frame contains header + pixel data
125 if (len < sizeof(image_frame_packet_t)) {
126 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid image frame packet size: %u, minimum %zu", len,
127 sizeof(image_frame_packet_t));
128 }
129 break;
131 // Batch must have at least header + some samples
132 if (len < sizeof(audio_batch_packet_t) + sizeof(float)) {
133 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid audio batch packet size: %u", len);
134 }
135 break;
136 case PACKET_TYPE_PING:
137 case PACKET_TYPE_PONG:
138 if (len != 0) { // Ping/pong are just header packets and no payload.
139 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid ping/pong packet size: %u", len);
140 }
141 break;
143 // Client capabilities packet can be empty or contain capability data
144 if (len > 1024) { // Reasonable limit for capability data
145 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid client capabilities packet size: %u", len);
146 }
147 break;
149 if (len != sizeof(client_info_packet_t)) {
150 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid client join packet size: %u, expected %zu", len,
151 sizeof(client_info_packet_t));
152 }
153 break;
155 // Client leave packet can be empty or contain reason
156 if (len > 256) {
157 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid client leave packet size: %u", len);
158 }
159 break;
162 if (len != sizeof(uint32_t)) {
163 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid stream control packet size: %u, expected %zu", len,
164 sizeof(uint32_t));
165 }
166 break;
168 // Size message format: "SIZE:width,height"
169 if (len == 0 || len > 32) { // Reasonable size for "SIZE:1234,5678"
170 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid size message packet size: %u", len);
171 }
172 break;
174 // Audio message format: "AUDIO:num_samples"
175 if (len == 0 || len > 32) { // Reasonable size for "AUDIO:1234"
176 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid audio message packet size: %u", len);
177 }
178 break;
180 // Text message can be empty or contain message
181 if (len > 1024) { // Reasonable limit for text messages
182 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid text message packet size: %u", len);
183 }
184 break;
186 if (len < sizeof(error_packet_t)) {
187 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid error packet size: %u (minimum %zu)", len,
188 sizeof(error_packet_t));
189 }
190 if (len > sizeof(error_packet_t) + MAX_ERROR_MESSAGE_LENGTH) {
191 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Error packet message too large: %u (max %zu)", len,
192 sizeof(error_packet_t) + (size_t)MAX_ERROR_MESSAGE_LENGTH);
193 }
194 break;
195 // All crypto handshake packet types - validate using session parameters
208 // Crypto packets are validated by the crypto handshake context
209 // This is just a basic sanity check for extremely large packets
210 if (len > 65536) { // 64KB should be enough for even large post-quantum crypto packets
211 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Crypto packet too large: %u bytes (max 65536)", len);
212 }
213 // Note: Proper size validation is done in crypto_handshake_validate_packet_size()
214 // which uses the session's crypto parameters for accurate validation
215 break;
216 // ACIP protocol packets (Discovery Service)
237 // ACIP packets - basic size validation
238 // Discovery service packets can vary in size (variable-length strings, etc.)
239 if (len > 65536) { // 64KB max for discovery packets
240 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "ACIP packet too large: %u bytes (max 65536)", len);
241 }
242 break;
243 default:
244 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Unknown packet type: %u", type);
245 }
246
247 // Return parsed values
248 *pkt_type = type;
249 *pkt_len = len;
250 *expected_crc = crc;
251
252 return ASCIICHAT_OK;
253}
254
262static asciichat_error_t packet_validate_crc32(const void *data, size_t len, uint32_t expected_crc) {
263 if (!data && len > 0) {
264 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: data=%p but len=%zu", data, len);
265 }
266
267 if (len == 0) {
268 // Empty packets should have CRC32 of 0
269 if (expected_crc != 0) {
270 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid CRC32 for empty packet: 0x%x (expected 0)", expected_crc);
271 }
272 return 0;
273 }
274
275 uint32_t calculated_crc = asciichat_crc32(data, len);
276 if (calculated_crc != expected_crc) {
277 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "CRC32 mismatch: calculated 0x%x, expected 0x%x", calculated_crc,
278 expected_crc);
279 }
280
281 return ASCIICHAT_OK;
282}
283
292asciichat_error_t packet_send(socket_t sockfd, packet_type_t type, const void *data, size_t len) {
293 if (sockfd == INVALID_SOCKET_VALUE) {
294 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket descriptor");
295 }
296
297 if (len > MAX_PACKET_SIZE) {
298 return SET_ERRNO(ERROR_NETWORK_SIZE, "Packet too large: %zu > %d", len, MAX_PACKET_SIZE);
299 }
300
302 .type = HOST_TO_NET_U16((uint16_t)type),
303 .length = HOST_TO_NET_U32((uint32_t)len),
304 .crc32 = HOST_TO_NET_U32(len > 0 ? asciichat_crc32(data, len) : 0),
305 .client_id = HOST_TO_NET_U32(0)}; // Always initialize client_id to 0 in network byte order
306
307 // Calculate timeout based on packet size (in nanoseconds)
308 uint64_t timeout = calculate_packet_timeout(len);
309
310 // Send header first
311 ssize_t sent = send_with_timeout(sockfd, &header, sizeof(header), timeout);
312 if (sent < 0) {
313 // Error context is already set by send_with_timeout
314 return ERROR_NETWORK;
315 }
316 if ((size_t)sent != sizeof(header)) {
317 return SET_ERRNO(ERROR_NETWORK, "Failed to fully send packet header. Sent %zd/%zu bytes", sent, sizeof(header));
318 }
319
320 // Send payload if present
321 if (len > 0 && data) {
322 // Check socket validity before sending payload to avoid race conditions
323 if (!socket_is_valid(sockfd)) {
324 return SET_ERRNO(ERROR_NETWORK, "Socket became invalid between header and payload send");
325 }
326 sent = send_with_timeout(sockfd, data, len, timeout);
327 // Check for error first to avoid signed/unsigned comparison issues
328 if (sent < 0) {
329 // Error context is already set by send_with_timeout
330 return ERROR_NETWORK;
331 }
332 if ((size_t)sent != len) {
333 return SET_ERRNO(ERROR_NETWORK, "Failed to fully send packet payload. Sent %zd/%zu bytes", sent, len);
334 }
335 }
336
337#ifdef DEBUG_NETWORK
338 log_debug("Sent packet type=%d, len=%zu, errno=%d (%s)", type, len, errno, SAFE_STRERROR(errno));
339#endif
340
341 return 0;
342}
343
352asciichat_error_t packet_receive(socket_t sockfd, packet_type_t *type, void **data, size_t *len) {
353 if (sockfd == INVALID_SOCKET_VALUE) {
354 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket descriptor");
355 }
356 if (!type || !data || !len) {
357 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: type=%p, data=%p, len=%p", type, data, len);
358 }
359
360 // Read packet header into memory from network socket
361 packet_header_t header;
362 uint64_t header_timeout_ns = RECV_TIMEOUT * NS_PER_SEC_INT;
363 ssize_t received = recv_with_timeout(sockfd, &header, sizeof(header), header_timeout_ns);
364 if (received < 0) {
365 // Error context is already set by recv_with_timeout
366 return ERROR_NETWORK;
367 }
368 if ((size_t)received != sizeof(header)) {
369 if (received == 0) {
370 log_warn("Connection closed while reading packet header");
371 return SET_ERRNO(ERROR_NETWORK, "Connection closed by peer while reading packet header");
372 }
373
374 return SET_ERRNO(ERROR_NETWORK, "Partial packet header received: %zd/%zu bytes", received, sizeof(header));
375 }
376
377 // Validate packet header
378 uint16_t pkt_type;
379 uint32_t pkt_len;
380 uint32_t expected_crc;
381 if (packet_validate_header(&header, &pkt_type, &pkt_len, &expected_crc) != ASCIICHAT_OK) {
382 // Error context is already set by packet_validate_header
384 }
385
386 // Allocate buffer for payload
387 void *payload = NULL;
388 if (pkt_len > 0) {
389 payload = buffer_pool_alloc(NULL, pkt_len);
390
391 // Use adaptive timeout for large packets
392 uint64_t recv_timeout = calculate_packet_timeout(pkt_len);
393 received = recv_with_timeout(sockfd, payload, pkt_len, recv_timeout);
394 if (received < 0) {
395 buffer_pool_free(NULL, payload, pkt_len);
396 return SET_ERRNO_SYS(ERROR_NETWORK, "Failed to receive packet payload");
397 }
398 if (received != (ssize_t)pkt_len) {
399 buffer_pool_free(NULL, payload, pkt_len);
400 return SET_ERRNO(ERROR_NETWORK, "Partial packet payload received: %zd/%u bytes", received, pkt_len);
401 }
402
403 // Validate CRC32
404 if (packet_validate_crc32(payload, pkt_len, expected_crc) != ASCIICHAT_OK) {
405 buffer_pool_free(NULL, payload, pkt_len);
406 // Error context is already set by packet_validate_crc32
408 }
409 }
410
411 // Return results
412 *type = (packet_type_t)pkt_type;
413 *data = payload;
414 *len = pkt_len;
415
416 return ASCIICHAT_OK;
417}
418
419/* ============================================================================
420 * High-Level Secure Packet Functions
421 * ============================================================================
422 * These functions handle encryption and compression for secure communication.
423 */
424
434asciichat_error_t send_packet_secure(socket_t sockfd, packet_type_t type, const void *data, size_t len,
435 crypto_context_t *crypto_ctx) {
436 if (len > MAX_PACKET_SIZE) {
437 return SET_ERRNO(ERROR_NETWORK_SIZE, "Packet too large: %zu > %d", len, MAX_PACKET_SIZE);
438 }
439
440 // Handshake packets are ALWAYS sent unencrypted
441 if (packet_is_handshake_type(type)) {
442 return packet_send(sockfd, type, data, len);
443 }
444
445 // Apply compression if beneficial for large packets
446 const void *final_data = data;
447 size_t final_len = len;
448 void *compressed_data = NULL;
449
450 // Skip compression for pre-compressed data (Opus audio) or if --no-compress flag is set
451 bool should_skip_compression = packet_is_precompressed(type) || GET_OPTION(no_compress);
452 if (!should_skip_compression && len > COMPRESSION_MIN_SIZE && should_compress(len, len)) {
453 void *temp_compressed = NULL;
454 size_t compressed_size = 0;
455
456 // Use configured compression level from options (default: 1 for fastest compression)
457 int compression_level = (GET_OPTION(compression_level) > 0) ? GET_OPTION(compression_level) : 1;
458 asciichat_error_t compress_result = compress_data(data, len, &temp_compressed, &compressed_size, compression_level);
459 if (compress_result == ASCIICHAT_OK) {
460 double ratio = (double)compressed_size / (double)len;
461 if (ratio < COMPRESSION_RATIO_THRESHOLD) {
462 final_data = temp_compressed;
463 final_len = compressed_size;
464 compressed_data = temp_compressed;
465 log_debug("Compressed packet: %zu -> %zu bytes (%.1f%%)", len, compressed_size, ratio * 100.0);
466 } else {
467 SAFE_FREE(temp_compressed);
468 }
469 }
470 }
471
472 // If no crypto context or crypto not ready, send unencrypted
473 bool ready = crypto_ctx ? crypto_is_ready(crypto_ctx) : false;
474 if (!crypto_ctx || !ready) {
475 log_warn_every(LOG_RATE_FAST, "CRYPTO_DEBUG: Sending packet type %d UNENCRYPTED (crypto_ctx=%p, ready=%d)", type,
476 (void *)crypto_ctx, ready);
477 asciichat_error_t result = packet_send(sockfd, type, final_data, final_len);
478 if (compressed_data) {
479 SAFE_FREE(compressed_data);
480 }
481 if (result != ASCIICHAT_OK) {
482 SET_ERRNO(ERROR_NETWORK, "Failed to send packet: %s", asciichat_error_string(result));
483 }
484 return result;
485 }
486
487 // Encrypt the packet: create header + payload, encrypt everything, wrap in PACKET_TYPE_ENCRYPTED
489 .type = HOST_TO_NET_U16((uint16_t)type),
490 .length = HOST_TO_NET_U32((uint32_t)final_len),
491 .crc32 = HOST_TO_NET_U32(final_len > 0 ? asciichat_crc32(final_data, final_len) : 0),
492 .client_id = HOST_TO_NET_U32(0)}; // Always 0 - client_id is not used in practice
493
494 // Combine header + payload for encryption
495 // Check for integer overflow before addition
496 if (final_len > SIZE_MAX - sizeof(header)) {
497 if (compressed_data) {
498 SAFE_FREE(compressed_data);
499 }
500 return SET_ERRNO(ERROR_NETWORK_SIZE, "Packet too large: would overflow plaintext buffer size");
501 }
502 size_t plaintext_len = sizeof(header) + final_len;
503 uint8_t *plaintext = buffer_pool_alloc(NULL, plaintext_len);
504 if (!plaintext) {
505 if (compressed_data) {
506 SAFE_FREE(compressed_data);
507 }
508 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for plaintext packet");
509 }
510
511 memcpy(plaintext, &header, sizeof(header));
512 if (final_len > 0 && final_data) {
513 memcpy(plaintext + sizeof(header), final_data, final_len);
514 }
515
516 // Encrypt
517 // Check for integer overflow before calculating ciphertext size
518 if (plaintext_len > SIZE_MAX - CRYPTO_NONCE_SIZE - CRYPTO_MAC_SIZE) {
519 buffer_pool_free(NULL, plaintext, plaintext_len);
520 if (compressed_data) {
521 SAFE_FREE(compressed_data);
522 }
523 return SET_ERRNO(ERROR_NETWORK_SIZE, "Packet too large: would overflow ciphertext buffer size");
524 }
525 size_t ciphertext_size = plaintext_len + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE;
526 uint8_t *ciphertext = buffer_pool_alloc(NULL, ciphertext_size);
527 if (!ciphertext) {
528 buffer_pool_free(NULL, plaintext, plaintext_len);
529 if (compressed_data) {
530 SAFE_FREE(compressed_data);
531 }
532 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for ciphertext");
533 }
534
535 size_t ciphertext_len;
536 crypto_result_t result =
537 crypto_encrypt(crypto_ctx, plaintext, plaintext_len, ciphertext, ciphertext_size, &ciphertext_len);
538 buffer_pool_free(NULL, plaintext, plaintext_len);
539
540 if (result != CRYPTO_OK) {
541 buffer_pool_free(NULL, ciphertext, ciphertext_size);
542 if (compressed_data) {
543 SAFE_FREE(compressed_data);
544 }
545 return SET_ERRNO(ERROR_CRYPTO, "Failed to encrypt packet: %s", crypto_result_to_string(result));
546 }
547
548 // Send as PACKET_TYPE_ENCRYPTED
549 log_debug_every(LOG_RATE_SLOW, "CRYPTO_DEBUG: Sending encrypted packet (original type %d as PACKET_TYPE_ENCRYPTED)",
550 type);
551 asciichat_error_t send_result = packet_send(sockfd, PACKET_TYPE_ENCRYPTED, ciphertext, ciphertext_len);
552 buffer_pool_free(NULL, ciphertext, ciphertext_size);
553
554 if (compressed_data) {
555 SAFE_FREE(compressed_data);
556 }
557
558 return send_result;
559}
560
569packet_recv_result_t receive_packet_secure(socket_t sockfd, void *crypto_ctx, bool enforce_encryption,
570 packet_envelope_t *envelope) {
571 return receive_packet_secure_with_timeout(sockfd, crypto_ctx, enforce_encryption, envelope,
573}
574
575packet_recv_result_t receive_packet_secure_with_timeout(socket_t sockfd, void *crypto_ctx, bool enforce_encryption,
576 packet_envelope_t *envelope, uint64_t timeout_ns) {
577
578 if (!envelope) {
579 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: envelope=%p", envelope);
580 return PACKET_RECV_ERROR;
581 }
582
583 // Initialize envelope
584 memset(envelope, 0, sizeof(*envelope));
585
586 // Receive packet header
587 packet_header_t header;
588 ssize_t received = recv_with_timeout(sockfd, &header, sizeof(header), timeout_ns);
589
590 // Check for errors first (before comparing signed with unsigned)
591 if (received < 0) {
592 /* Preserve a poll timeout so callers can wait for the next signaling packet
593 * without treating an idle connection as a receive failure. */
594 asciichat_error_context_t error_context;
595 if (HAS_ERRNO(&error_context) && error_context.code == ERROR_NETWORK_TIMEOUT) {
596 return PACKET_RECV_ERROR;
597 }
598 SET_ERRNO(ERROR_NETWORK, "Failed to receive packet header: %zd/%zu bytes", received, sizeof(header));
599 return PACKET_RECV_ERROR;
600 }
601
602 if (received == 0) {
603 return PACKET_RECV_EOF;
604 }
605
606 if ((size_t)received != sizeof(header)) {
607 SET_ERRNO(ERROR_NETWORK, "Failed to receive packet header: %zd/%zu bytes", received, sizeof(header));
608 return PACKET_RECV_ERROR;
609 }
610
611 // Convert from network byte order
612 uint64_t magic = NET_TO_HOST_U64(header.magic);
613 uint16_t pkt_type = NET_TO_HOST_U16(header.type);
614 uint32_t pkt_len = NET_TO_HOST_U32(header.length);
615 uint32_t expected_crc = NET_TO_HOST_U32(header.crc32);
616
617 // DEBUG: Log raw header bytes for debugging packet misalignment
618 char header_hex[95];
619 for (size_t i = 0; i < sizeof(header); i++) {
620 snprintf(&header_hex[i * 2], 3, "%02x", ((uint8_t *)&header)[i]);
621 }
622 header_hex[94] = '\0';
623
624 // DEBUG: Log all received packet types in detail
625 log_info("[RECV_PKT_HEADER] 🔍 Received packet: type=%u (0x%04x), len=%u, client_id=%u, raw=%s", pkt_type, pkt_type,
626 pkt_len, header.client_id, header_hex);
627
628 // Validate magic number
629 if (magic != PACKET_MAGIC) {
630 SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid packet magic: 0x%llx (expected 0x%llx)", magic, PACKET_MAGIC);
631 return PACKET_RECV_ERROR;
632 }
633
634 // Validate packet size
635 if (pkt_len > MAX_PACKET_SIZE) {
636 SET_ERRNO(ERROR_NETWORK_SIZE, "Packet too large: %u > %d", pkt_len, MAX_PACKET_SIZE);
637 return PACKET_RECV_ERROR;
638 }
639
640 // Handle encrypted packets
641 if (pkt_type == PACKET_TYPE_ENCRYPTED) {
642 if (!crypto_ctx) {
643 SET_ERRNO(ERROR_CRYPTO, "Received encrypted packet but no crypto context");
644 return PACKET_RECV_ERROR;
645 }
646
647 // Read encrypted payload
648 uint8_t *ciphertext = buffer_pool_alloc(NULL, pkt_len);
649 if (!ciphertext) {
650 SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for ciphertext");
651 return PACKET_RECV_ERROR;
652 }
653
654 uint64_t recv_timeout = MAX(timeout_ns, calculate_packet_timeout(pkt_len));
655 received = recv_with_timeout(sockfd, ciphertext, pkt_len, recv_timeout);
656 if (received != (ssize_t)pkt_len) {
657 SET_ERRNO(ERROR_NETWORK, "Failed to receive encrypted payload: %zd/%u bytes", received, pkt_len);
658 buffer_pool_free(NULL, ciphertext, pkt_len);
659 return PACKET_RECV_ERROR;
660 }
661
662 // Decrypt - allocate plaintext buffer with extra space
663 // pkt_len is already validated to be <= MAX_PACKET_SIZE, so adding 1024 cannot overflow
664 size_t plaintext_size = (size_t)pkt_len + 1024;
665 uint8_t *plaintext = buffer_pool_alloc(NULL, plaintext_size);
666 if (!plaintext) {
667 SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for plaintext");
668 buffer_pool_free(NULL, ciphertext, pkt_len);
669 return PACKET_RECV_ERROR;
670 }
671
672 size_t plaintext_len;
673 crypto_result_t result = crypto_decrypt(crypto_ctx, ciphertext, pkt_len, plaintext, plaintext_size, &plaintext_len);
674 buffer_pool_free(NULL, ciphertext, pkt_len);
675
676 if (result != CRYPTO_OK) {
677 SET_ERRNO(ERROR_CRYPTO, "Failed to decrypt packet: %s", crypto_result_to_string(result));
678 buffer_pool_free(NULL, plaintext, plaintext_size);
679 return PACKET_RECV_ERROR;
680 }
681
682 if (plaintext_len < sizeof(packet_header_t)) {
683 SET_ERRNO(ERROR_CRYPTO, "Decrypted packet too small: %zu < %zu", plaintext_len, sizeof(packet_header_t));
684 buffer_pool_free(NULL, plaintext, plaintext_size);
685 return PACKET_RECV_ERROR;
686 }
687
688 // Parse decrypted header
689 packet_header_t *decrypted_header = (packet_header_t *)plaintext;
690 pkt_type = NET_TO_HOST_U16(decrypted_header->type);
691
692 // DEBUG: Log decrypted packet type
693 log_info("[RECV_PKT_DECRYPTED] 🔐 After decryption: actual_type=%u (0x%04x), len=%u", pkt_type, pkt_type,
694 NET_TO_HOST_U32(decrypted_header->length));
695 pkt_len = NET_TO_HOST_U32(decrypted_header->length);
696 expected_crc = NET_TO_HOST_U32(decrypted_header->crc32);
697
698 // Extract payload
699 size_t payload_len = plaintext_len - sizeof(packet_header_t);
700 if (payload_len != pkt_len) {
701 SET_ERRNO(ERROR_CRYPTO, "Decrypted payload size mismatch: %zu != %u", payload_len, pkt_len);
702 buffer_pool_free(NULL, plaintext, plaintext_size);
703 return PACKET_RECV_ERROR;
704 }
705
706 // Verify CRC
707 if (pkt_len > 0) {
708 uint32_t actual_crc = asciichat_crc32(plaintext + sizeof(packet_header_t), pkt_len);
709 if (actual_crc != expected_crc) {
710 SET_ERRNO(ERROR_CRYPTO, "Decrypted packet CRC mismatch: 0x%x != 0x%x", actual_crc, expected_crc);
711 buffer_pool_free(NULL, plaintext, plaintext_size);
712 return PACKET_RECV_ERROR;
713 }
714 }
715
716 // Set envelope
717 envelope->type = (packet_type_t)pkt_type;
718 envelope->data = plaintext + sizeof(packet_header_t);
719 envelope->len = pkt_len;
720 envelope->allocated_buffer = plaintext;
721 envelope->allocated_size = plaintext_size;
722
723 return PACKET_RECV_SUCCESS;
724 }
725
726 // Handle unencrypted packets
727 if (enforce_encryption && !packet_is_handshake_type(pkt_type)) {
728 SET_ERRNO(ERROR_CRYPTO, "Received unencrypted packet type %d but encryption is required", pkt_type);
729 return PACKET_RECV_ERROR;
730 }
731
732 // Read payload and return header+payload in envelope (matching encrypted path format)
733 size_t total_size = sizeof(packet_header_t) + pkt_len;
734 uint8_t *packet_buf = buffer_pool_alloc(NULL, total_size);
735 if (!packet_buf) {
736 SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for packet");
737 return PACKET_RECV_ERROR;
738 }
739
740 // Copy header into buffer
741 memcpy(packet_buf, &header, sizeof(packet_header_t));
742
743 if (pkt_len > 0) {
744 uint64_t recv_timeout = calculate_packet_timeout(pkt_len);
745 received = recv_with_timeout(sockfd, packet_buf + sizeof(packet_header_t), pkt_len, recv_timeout);
746 if (received != (ssize_t)pkt_len) {
747 SET_ERRNO(ERROR_NETWORK, "Failed to receive payload: %zd/%u bytes", received, pkt_len);
748 buffer_pool_free(NULL, packet_buf, total_size);
749 return PACKET_RECV_ERROR;
750 }
751
752 // Verify CRC
753 uint32_t actual_crc = asciichat_crc32(packet_buf + sizeof(packet_header_t), pkt_len);
754 if (actual_crc != expected_crc) {
755 SET_ERRNO(ERROR_NETWORK, "Packet CRC mismatch: 0x%x != 0x%x", actual_crc, expected_crc);
756 buffer_pool_free(NULL, packet_buf, total_size);
757 return PACKET_RECV_ERROR;
758 }
759 }
760
761 envelope->data = packet_buf;
762 envelope->allocated_buffer = packet_buf;
763 envelope->allocated_size = total_size;
764 envelope->type = (packet_type_t)pkt_type;
765 envelope->len = total_size;
766
767 return PACKET_RECV_SUCCESS;
768}
769
770/* ============================================================================
771 * Basic Packet Functions (Non-Secure)
772 * ============================================================================
773 * These are the basic packet send/receive functions without encryption.
774 */
775
784int send_packet(socket_t sockfd, packet_type_t type, const void *data, size_t len) {
785 asciichat_error_t result = packet_send(sockfd, type, data, len);
786 return result == ASCIICHAT_OK ? 0 : -1;
787}
788
797int receive_packet(socket_t sockfd, packet_type_t *type, void **data, size_t *len) {
798 asciichat_error_t result = packet_receive(sockfd, type, data, len);
799 return result == ASCIICHAT_OK ? 0 : -1;
800}
801
802/* ============================================================================
803 * Protocol Message Functions
804 * ============================================================================
805 * These functions send specific protocol messages.
806 */
807
814 return send_packet(sockfd, PACKET_TYPE_PING, NULL, 0);
815}
816
823 return send_packet(sockfd, PACKET_TYPE_PONG, NULL, 0);
824}
825
843 if (!envelope) {
844 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid envelope");
845 }
846
847 // Only decrypt if this is an encrypted packet
848 if (envelope->type != PACKET_TYPE_ENCRYPTED) {
849 return ASCIICHAT_OK; // Not encrypted, nothing to do
850 }
851
852 if (!crypto_ctx) {
853 return SET_ERRNO(ERROR_CRYPTO, "Received encrypted packet but no crypto context");
854 }
855
856 uint8_t *ciphertext = (uint8_t *)envelope->data;
857 size_t ciphertext_len = envelope->len;
858
859 // Allocate plaintext buffer with extra space for decryption
860 size_t plaintext_size = ciphertext_len + 1024;
861 uint8_t *plaintext = buffer_pool_alloc(NULL, plaintext_size);
862 if (!plaintext) {
863 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate plaintext buffer for decryption");
864 }
865
866 size_t plaintext_len;
867 crypto_result_t result =
868 crypto_decrypt(crypto_ctx, ciphertext, ciphertext_len, plaintext, plaintext_size, &plaintext_len);
869
870 if (result != CRYPTO_OK) {
871 buffer_pool_free(NULL, plaintext, plaintext_size);
872 return SET_ERRNO(ERROR_CRYPTO, "Failed to decrypt packet: %s", crypto_result_to_string(result));
873 }
874
875 if (plaintext_len < sizeof(packet_header_t)) {
876 buffer_pool_free(NULL, plaintext, plaintext_size);
877 return SET_ERRNO(ERROR_CRYPTO, "Decrypted packet too small: %zu < %zu", plaintext_len, sizeof(packet_header_t));
878 }
879
880 // Parse inner packet header
881 const packet_header_t *inner_header = (const packet_header_t *)plaintext;
882 packet_type_t inner_type = NET_TO_HOST_U16(inner_header->type);
883 uint32_t inner_len = NET_TO_HOST_U32(inner_header->length);
884
885 // Validate inner packet length
886 if (inner_len != plaintext_len - sizeof(packet_header_t)) {
887 buffer_pool_free(NULL, plaintext, plaintext_size);
888 return SET_ERRNO(ERROR_CRYPTO, "Inner packet length mismatch: header=%u, actual=%zu", inner_len,
889 plaintext_len - sizeof(packet_header_t));
890 }
891
892 // Free the original encrypted buffer if it's different from plaintext
893 if (envelope->allocated_buffer && envelope->allocated_buffer != plaintext) {
894 buffer_pool_free(NULL, envelope->allocated_buffer, envelope->allocated_size);
895 }
896
897 // Update envelope with decrypted packet info
898 envelope->type = inner_type;
899 envelope->data = (uint8_t *)plaintext + sizeof(packet_header_t);
900 envelope->len = inner_len;
901 envelope->allocated_buffer = plaintext;
902 envelope->allocated_size = plaintext_size;
903
904 log_info("[PACKET_DECRYPT] 🔐 Decrypted PACKET_TYPE_ENCRYPTED: inner_type=%u (0x%04x), len=%u", inner_type,
905 inner_type, inner_len);
906
907 return ASCIICHAT_OK;
908}
909
911 const char *message) {
912 if (sockfd == INVALID_SOCKET_VALUE) {
913 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket descriptor");
914 }
915
916 if (!message) {
917 message = "";
918 }
919
920 size_t message_len = strnlen(message, MAX_ERROR_MESSAGE_LENGTH);
921 if (message_len == MAX_ERROR_MESSAGE_LENGTH) {
922 log_warn("Error message truncated to %zu bytes", (size_t)MAX_ERROR_MESSAGE_LENGTH);
923 }
924
925 size_t payload_len = sizeof(error_packet_t) + message_len;
926 uint8_t *payload = SAFE_MALLOC(payload_len, uint8_t *);
927 if (!payload) {
928 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate %zu bytes for error packet", payload_len);
929 }
930
931 error_packet_t *packet = (error_packet_t *)payload;
933 packet->message_length = HOST_TO_NET_U32((uint32_t)message_len);
934
935 if (message_len > 0) {
936 memcpy(payload + sizeof(error_packet_t), message, message_len);
937 }
938
939 bool encryption_ready = crypto_ctx && crypto_is_ready(crypto_ctx);
940 asciichat_error_t send_result;
941
942 if (encryption_ready) {
943 send_result =
944 send_packet_secure(sockfd, PACKET_TYPE_ERROR_MESSAGE, payload, payload_len, (crypto_context_t *)crypto_ctx);
945 } else {
946 send_result = packet_send(sockfd, PACKET_TYPE_ERROR_MESSAGE, payload, payload_len);
947 }
948 SAFE_FREE(payload);
949
950 if (send_result != ASCIICHAT_OK) {
951 return SET_ERRNO(ERROR_NETWORK, "Failed to send error packet: %s", asciichat_error_string(send_result));
952 }
953
954 return ASCIICHAT_OK;
955}
956
957asciichat_error_t packet_parse_error_message(const void *data, size_t len, asciichat_error_t *out_error_code,
958 char *message_buffer, size_t message_buffer_size,
959 size_t *out_message_length) {
960 if (!data || len < sizeof(error_packet_t) || !out_error_code || !message_buffer || message_buffer_size == 0) {
962 "Invalid parameters: data=%p len=%zu out_error_code=%p message_buffer=%p buffer_size=%zu", data,
963 len, out_error_code, message_buffer, message_buffer_size);
964 }
965
966 const error_packet_t *packet = (const error_packet_t *)data;
967 uint32_t raw_error_code = NET_TO_HOST_U32(packet->error_code);
968 uint32_t raw_message_length = NET_TO_HOST_U32(packet->message_length);
969
970 if (raw_message_length > MAX_ERROR_MESSAGE_LENGTH) {
971 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Error message length too large: %u", raw_message_length);
972 }
973
974 size_t total_required = sizeof(error_packet_t) + (size_t)raw_message_length;
975 if (total_required > len) {
976 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Error packet truncated: expected %zu bytes, have %zu", total_required,
977 len);
978 }
979
980 const uint8_t *message_bytes = (const uint8_t *)data + sizeof(error_packet_t);
981 size_t copy_len = raw_message_length;
982 if (copy_len >= message_buffer_size) {
983 copy_len = message_buffer_size - 1;
984 }
985
986 if (copy_len > 0) {
987 memcpy(message_buffer, message_bytes, copy_len);
988 }
989 message_buffer[copy_len] = '\0';
990
991 if (out_message_length) {
992 *out_message_length = raw_message_length;
993 }
994
995 *out_error_code = (asciichat_error_t)raw_error_code;
996 return ASCIICHAT_OK;
997}
998
1000 remote_log_direction_t direction, uint16_t flags, const char *message) {
1001 if (sockfd == INVALID_SOCKET_VALUE) {
1002 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket descriptor");
1003 }
1004
1005 if (level < LOG_DEV || level > LOG_FATAL) {
1006 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid log level: %d", level);
1007 }
1008
1009 const char *safe_message = message ? message : "";
1010 bool truncated = false;
1011 size_t message_len = strnlen(safe_message, MAX_REMOTE_LOG_MESSAGE_LENGTH);
1012 if (message_len == MAX_REMOTE_LOG_MESSAGE_LENGTH && safe_message[message_len] != '\0') {
1013 truncated = true;
1014 }
1015
1016 size_t payload_len = sizeof(remote_log_packet_t) + message_len;
1017 uint8_t *payload = SAFE_MALLOC(payload_len, uint8_t *);
1018 if (!payload) {
1019 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate %zu bytes for remote log packet", payload_len);
1020 }
1021
1022 remote_log_packet_t *packet = (remote_log_packet_t *)payload;
1023 packet->log_level = (uint8_t)level;
1024 packet->direction = (uint8_t)direction;
1025 uint16_t final_flags = flags;
1026 if (truncated) {
1027 final_flags |= REMOTE_LOG_FLAG_TRUNCATED;
1028 }
1029 packet->flags = HOST_TO_NET_U16(final_flags);
1030 packet->message_length = HOST_TO_NET_U32((uint32_t)message_len);
1031
1032 if (message_len > 0) {
1033 memcpy(payload + sizeof(remote_log_packet_t), safe_message, message_len);
1034 }
1035
1036 bool encryption_ready = crypto_ctx && crypto_is_ready(crypto_ctx);
1037 asciichat_error_t send_result;
1038
1039 if (encryption_ready) {
1040 int secure_result =
1041 send_packet_secure(sockfd, PACKET_TYPE_REMOTE_LOG, payload, payload_len, (crypto_context_t *)crypto_ctx);
1042 send_result = secure_result == 0 ? ASCIICHAT_OK : ERROR_NETWORK;
1043 } else {
1044 send_result = packet_send(sockfd, PACKET_TYPE_REMOTE_LOG, payload, payload_len);
1045 }
1046
1047 SAFE_FREE(payload);
1048
1049 if (send_result != ASCIICHAT_OK) {
1050 return SET_ERRNO(ERROR_NETWORK, "Failed to send remote log packet: %d", send_result);
1051 }
1052
1053 return ASCIICHAT_OK;
1054}
1055
1056asciichat_error_t packet_parse_remote_log(const void *data, size_t len, log_level_t *out_level,
1057 remote_log_direction_t *out_direction, uint16_t *out_flags,
1058 char *message_buffer, size_t message_buffer_size,
1059 size_t *out_message_length) {
1060 if (!data || len < sizeof(remote_log_packet_t) || !out_level || !out_direction || !out_flags || !message_buffer ||
1061 message_buffer_size == 0) {
1062 return SET_ERRNO(
1064 "Invalid parameters: data=%p len=%zu out_level=%p out_direction=%p out_flags=%p buffer=%p size=%zu", data, len,
1065 out_level, out_direction, out_flags, message_buffer, message_buffer_size);
1066 }
1067
1068 const remote_log_packet_t *packet = (const remote_log_packet_t *)data;
1069 uint8_t raw_level = packet->log_level;
1070 if (raw_level > LOG_FATAL) {
1071 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid remote log level: %u", raw_level);
1072 }
1073 *out_level = (log_level_t)raw_level;
1074
1075 uint8_t raw_direction = packet->direction;
1076 if (raw_direction > REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER) {
1077 raw_direction = REMOTE_LOG_DIRECTION_UNKNOWN;
1078 }
1079 *out_direction = (remote_log_direction_t)raw_direction;
1080
1081 uint16_t raw_flags = NET_TO_HOST_U16(packet->flags);
1082 *out_flags = raw_flags;
1083
1084 uint32_t raw_message_length = NET_TO_HOST_U32(packet->message_length);
1085 if (raw_message_length > MAX_REMOTE_LOG_MESSAGE_LENGTH) {
1086 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Remote log message length too large: %u", raw_message_length);
1087 }
1088
1089 size_t total_required = sizeof(remote_log_packet_t) + (size_t)raw_message_length;
1090 if (total_required > len) {
1091 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Remote log packet truncated: expected %zu bytes, have %zu",
1092 total_required, len);
1093 }
1094
1095 const uint8_t *message_bytes = (const uint8_t *)data + sizeof(remote_log_packet_t);
1096 size_t copy_len = raw_message_length;
1097 if (copy_len >= message_buffer_size) {
1098 copy_len = message_buffer_size - 1;
1099 }
1100
1101 if (copy_len > 0) {
1102 memcpy(message_buffer, message_bytes, copy_len);
1103 }
1104 message_buffer[copy_len] = '\0';
1105
1106 if (out_message_length) {
1107 *out_message_length = raw_message_length;
1108 }
1109
1110 return ASCIICHAT_OK;
1111}
1112
1120 if (!version) {
1121 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: version=%p", version);
1122 return -1;
1123 }
1124 return send_packet(sockfd, PACKET_TYPE_PROTOCOL_VERSION, version, sizeof(*version));
1125}
1126
1134 if (!caps) {
1135 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: caps=%p", caps);
1136 return -1;
1137 }
1138 return send_packet(sockfd, PACKET_TYPE_CRYPTO_CAPABILITIES, caps, sizeof(*caps));
1139}
1140
1148 if (!params) {
1149 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: params=%p", params);
1150 return -1;
1151 }
1152
1153 // Create a copy and convert uint16_t fields to network byte order
1154 crypto_parameters_packet_t net_params = *params;
1155 log_debug("NETWORK_DEBUG: Before htons: kex=%u, auth=%u, sig=%u, secret=%u", params->kex_public_key_size,
1156 params->auth_public_key_size, params->signature_size, params->shared_secret_size);
1159 net_params.signature_size = HOST_TO_NET_U16(params->signature_size);
1161 log_debug("NETWORK_DEBUG: After htons: kex=%u, auth=%u, sig=%u, secret=%u", net_params.kex_public_key_size,
1162 net_params.auth_public_key_size, net_params.signature_size, net_params.shared_secret_size);
1163
1164 return send_packet(sockfd, PACKET_TYPE_CRYPTO_PARAMETERS, &net_params, sizeof(net_params));
1165}
1166
1167// =============================================================================
1168// Audio Batch Packet Sending
1169// =============================================================================
1170// These functions were moved from lib/network/av.c during refactoring.
1171// They provide socket-level audio packet sending with encryption support.
1172// For transport-agnostic sending, use acip_send_audio_batch() instead.
1173// =============================================================================
1174
1175asciichat_error_t av_send_audio_opus_batch(socket_t sockfd, const uint8_t *opus_data, size_t opus_size,
1176 const uint16_t *frame_sizes, int sample_rate, int frame_duration,
1177 int frame_count, crypto_context_t *crypto_ctx) {
1178 if (!opus_data || opus_size == 0 || !frame_sizes || sample_rate <= 0 || frame_duration <= 0 || frame_count <= 0) {
1180 "Invalid Opus batch parameters: opus_data=%p, opus_size=%zu, frame_sizes=%p, sample_rate=%d, "
1181 "frame_duration=%d, frame_count=%d",
1182 (const void *)opus_data, opus_size, (const void *)frame_sizes, sample_rate, frame_duration,
1183 frame_count);
1184 }
1185
1186 // Validate frame_count to prevent integer overflow in size calculations
1187 // Max reasonable frames per batch: ~1 second of 10ms frames = 100 frames
1188 if (frame_count > 1000) {
1189 return SET_ERRNO(ERROR_INVALID_PARAM, "Too many Opus frames: %d (max 1000)", frame_count);
1190 }
1191
1192 // Allocate buffer for header + frame sizes + encoded data
1193 size_t header_size = 16; // sample_rate (4), frame_duration (4), frame_count (4), reserved (4)
1194 size_t frame_sizes_bytes = (size_t)frame_count * sizeof(uint16_t);
1195 size_t total_size = header_size + frame_sizes_bytes + opus_size;
1196 void *packet_data = buffer_pool_alloc(NULL, total_size);
1197 if (!packet_data) {
1198 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for Opus batch packet: %zu bytes", total_size);
1199 }
1200
1201 // Write header (network byte order for cross-platform compatibility)
1202 uint8_t *buf = (uint8_t *)packet_data;
1203 uint32_t sr = HOST_TO_NET_U32((uint32_t)sample_rate);
1204 uint32_t fd = HOST_TO_NET_U32((uint32_t)frame_duration);
1205 uint32_t fc = HOST_TO_NET_U32((uint32_t)frame_count);
1206 memcpy(buf, &sr, 4);
1207 memcpy(buf + 4, &fd, 4);
1208 memcpy(buf + 8, &fc, 4);
1209 memset(buf + 12, 0, 4); // Reserved
1210
1211 // Write frame sizes array (convert each to network byte order)
1212 uint16_t *frame_sizes_out = (uint16_t *)(buf + header_size);
1213 for (int i = 0; i < frame_count; i++) {
1214 frame_sizes_out[i] = HOST_TO_NET_U16(frame_sizes[i]);
1215 }
1216
1217 // Copy Opus data
1218 memcpy(buf + header_size + frame_sizes_bytes, opus_data, opus_size);
1219
1220 // Send packet (with encryption support)
1221 asciichat_error_t result =
1222 send_packet_secure(sockfd, PACKET_TYPE_AUDIO_OPUS_BATCH, packet_data, total_size, crypto_ctx);
1223
1224 // Clean up
1225 buffer_pool_free(NULL, packet_data, total_size);
1226
1227 return result;
1228}
1229
1230asciichat_error_t send_image_frame_packet(socket_t sockfd, const void *image_data, uint16_t width, uint16_t height,
1231 uint8_t format) {
1232 if (!image_data || width == 0 || height == 0) {
1233 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: image_data=%p, width=%u, height=%u", image_data, width,
1234 height);
1235 }
1236
1237 // Validate dimensions to prevent integer overflow
1238 // Max reasonable dimensions: 4K (3840x2160) = ~25MB per frame
1239 if (width > 4096 || height > 4096) {
1240 return SET_ERRNO(ERROR_INVALID_PARAM, "Image dimensions too large: %ux%u (max 4096x4096)", width, height);
1241 }
1242
1243 // Create image frame packet
1244 image_frame_packet_t packet;
1245 packet.width = width;
1246 packet.height = height;
1247 packet.pixel_format = format;
1248 packet.compressed_size = 0;
1249 packet.checksum = 0;
1250 packet.timestamp = 0; // Will be set by receiver
1251
1252 // Calculate total packet size
1253 // Cast to size_t before multiplication to prevent integer overflow
1254 // Use overflow-checked multiplication
1255 size_t width_times_height;
1256 if (checked_size_mul((size_t)width, (size_t)height, &width_times_height) != ASCIICHAT_OK) {
1257 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Image dimensions too large: %d x %d", width, height);
1258 }
1259
1260 size_t frame_size;
1261 if (checked_size_mul(width_times_height, 3u, &frame_size) != ASCIICHAT_OK) {
1262 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Frame size overflow for RGB format");
1263 }
1264
1265 size_t total_size;
1266 if (checked_size_add(sizeof(image_frame_packet_t), frame_size, &total_size) != ASCIICHAT_OK) {
1267 return SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Total packet size overflow");
1268 }
1269
1270 // Allocate buffer for complete packet
1271 void *packet_data = buffer_pool_alloc(NULL, total_size);
1272 if (!packet_data) {
1273 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer for image frame packet: %zu bytes", total_size);
1274 }
1275
1276 // Copy packet header and image data
1277 memcpy(packet_data, &packet, sizeof(image_frame_packet_t));
1278 memcpy((char *)packet_data + sizeof(image_frame_packet_t), image_data, frame_size);
1279
1280 // Send packet
1281 asciichat_error_t result = packet_send(sockfd, PACKET_TYPE_IMAGE_FRAME, packet_data, total_size);
1282
1283 // Clean up
1284 buffer_pool_free(NULL, packet_data, total_size);
1285
1286 return result;
1287}
asciichat_error_t error_code
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
⚙️ Common definitions, error codes, macros, and types shared throughout the application
📦 Network Packet Compression Utilities
Hardware-Accelerated CRC32 Checksum Computation.
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
Definition endian.h:96
#define NET_TO_HOST_U64(val)
Definition endian.h:228
#define HOST_TO_NET_U32(val)
Definition endian.h:66
#define HOST_TO_NET_U64(val)
Definition endian.h:213
#define NET_TO_HOST_U16(val)
Definition endian.h:111
#define NET_TO_HOST_U32(val)
Definition endian.h:81
Cross-platform error handling utilities.
void buffer_pool_free(buffer_pool_t *pool, const void *data, size_t size)
Free a buffer back to the pool (lock-free)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
unsigned short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define SAFE_STRERROR(errnum)
Definition common.h:465
unsigned long long uint64_t
Definition common.h:59
unsigned char uint8_t
Definition common.h:56
#define COMPRESSION_MIN_SIZE
Minimum packet size to attempt compression (1KB)
Definition compression.h:61
bool should_compress(size_t original_size, size_t compressed_size)
Determine if compression should be used for given data sizes.
Definition compression.c:74
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:14
#define COMPRESSION_RATIO_THRESHOLD
Compression ratio threshold - only use if <80% original size.
Definition compression.h:58
crypto_result_t crypto_encrypt(crypto_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext_out, size_t ciphertext_out_size, size_t *ciphertext_len_out)
Encrypt data using XSalsa20-Poly1305.
#define CRYPTO_NONCE_SIZE
Nonce size (XSalsa20)
const char * crypto_result_to_string(crypto_result_t result)
Convert crypto result to human-readable string.
crypto_result_t
Cryptographic operation result codes.
#define CRYPTO_MAC_SIZE
MAC size (Poly1305)
bool crypto_is_ready(const crypto_context_t *ctx)
Check if key exchange is complete and ready for encryption.
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)
Decrypt data using XSalsa20-Poly1305.
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context, returning the error code.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
#define HAS_ERRNO(var)
Check if an error occurred and get full context.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_NETWORK
Definition error_codes.h:77
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:81
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_NETWORK_SIZE
Definition error_codes.h:82
@ ERROR_CRYPTO
Definition error_codes.h:96
@ ERROR_INVALID_PARAM
@ ERROR_BUFFER_OVERFLOW
@ ERROR_NETWORK_TIMEOUT
Definition error_codes.h:80
#define LOG_RATE_FAST
Log rate limit: 1 second (1,000,000 microseconds)
Definition log_rates.h:26
#define LOG_RATE_SLOW
Log rate limit: 10 seconds (10,000,000 microseconds)
Definition log_rates.h:35
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
enum remote_log_direction remote_log_direction_t
Remote log packet direction enumeration.
log_level_t
Logging levels enumeration.
Definition types.h:29
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
@ REMOTE_LOG_DIRECTION_UNKNOWN
Definition network/log.h:21
@ REMOTE_LOG_DIRECTION_CLIENT_TO_SERVER
Definition network/log.h:23
#define NS_PER_SEC_INT
Definition time.h:157
#define NS_PER_MS_INT
Definition time.h:156
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:434
ssize_t recv_with_timeout(socket_t sockfd, void *buf, size_t len, uint64_t timeout_ns)
Receive data with timeout.
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.
Definition packet.c:1230
int send_protocol_version_packet(socket_t sockfd, const protocol_version_packet_t *version)
Send protocol version packet.
Definition packet.c:1119
int send_pong_packet(socket_t sockfd)
Send a pong packet.
Definition packet.c:822
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:352
int receive_packet(socket_t sockfd, packet_type_t *type, void **data, size_t *len)
Receive a basic packet without encryption.
Definition packet.c:797
asciichat_error_t packet_decrypt_envelope(packet_envelope_t *envelope, void *crypto_ctx)
Send a clear console packet.
Definition packet.c:842
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.
Definition packet.c:1175
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:784
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.
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:292
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:569
#define RECV_TIMEOUT
Receive timeout in seconds (1 second)
int send_crypto_capabilities_packet(socket_t sockfd, const crypto_capabilities_packet_t *caps)
Send crypto capabilities packet.
Definition packet.c:1133
int send_ping_packet(socket_t sockfd)
Send a ping packet.
Definition packet.c:813
#define REMOTE_LOG_FLAG_TRUNCATED
Remote log packet flag definitions.
Definition packet.h:736
int send_crypto_parameters_packet(socket_t sockfd, const crypto_parameters_packet_t *params)
Send crypto parameters packet.
Definition packet.c:1147
packet_recv_result_t
Packet reception result codes.
Definition packet.h:1152
#define SEND_TIMEOUT
Send timeout in seconds (5 seconds)
@ PACKET_RECV_ERROR
Network error occurred.
Definition packet.h:1158
@ PACKET_RECV_EOF
Connection closed (EOF)
Definition packet.h:1156
@ PACKET_RECV_SUCCESS
Packet received successfully.
Definition packet.h:1154
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
#define LARGE_PACKET_THRESHOLD
Large packet size threshold (100KB)
Definition packet.h:105
#define MAX_CLIENT_TIMEOUT
Maximum client timeout in nanoseconds (60 seconds)
Definition packet.h:185
#define LARGE_PACKET_EXTRA_TIMEOUT_PER_MB
Extra timeout per MB for large packets (0.8 seconds per MB)
Definition packet.h:165
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
#define MAX_PACKET_SIZE
Maximum packet size (5MB)
Definition packet.h:115
#define MAX_REMOTE_LOG_MESSAGE_LENGTH
Maximum remote log message length (512 bytes)
Definition packet.h:134
#define PACKET_MAGIC
Packet magic number (alias for MAGIC_PACKET_VALID)
Definition packet.h:255
#define MAX_ERROR_MESSAGE_LENGTH
Maximum error message length (512 bytes)
Definition packet.h:124
#define MIN_CLIENT_TIMEOUT
Minimum client timeout in nanoseconds (10 seconds)
Definition packet.h:175
@ PACKET_TYPE_CRYPTO_CLIENT_HELLO
Client -> Server: Expected server key fingerprint for multi-key selection (UNENCRYPTED)
Definition packet.h:299
@ PACKET_TYPE_ACIP_WEBRTC_ICE
WebRTC ICE candidate (bidirectional)
Definition packet.h:428
@ PACKET_TYPE_AUDIO_OPUS_BATCH
Batched Opus-encoded audio frames.
Definition packet.h:374
@ PACKET_TYPE_CLIENT_LEAVE
Clean disconnect notification.
Definition packet.h:389
@ PACKET_TYPE_ACIP_SESSION_INFO
Session info response (Discovery Server -> Client)
Definition packet.h:413
@ PACKET_TYPE_IMAGE_FRAME
Complete RGB image with dimensions.
Definition packet.h:363
@ PACKET_TYPE_CRYPTO_AUTH_RESPONSE
Client -> Server: {HMAC[32]} (UNENCRYPTED)
Definition packet.h:317
@ PACKET_TYPE_TEXT_MESSAGE
Text message.
Definition packet.h:350
@ PACKET_TYPE_AUDIO_MESSAGE
Audio message.
Definition packet.h:348
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:385
@ PACKET_TYPE_ACIP_SESSION_RECONNECT
Reconnect to session (Client -> Discovery Server)
Definition packet.h:423
@ PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE
Server -> Client: "encryption ready" (UNENCRYPTED)
Definition packet.h:323
@ PACKET_TYPE_STREAM_START
Client requests to start sending video/audio.
Definition packet.h:391
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT
Server -> Client: {server_pubkey[32]} (UNENCRYPTED)
Definition packet.h:311
@ PACKET_TYPE_ACIP_ERROR
Generic error response (Discovery Server -> Client)
Definition packet.h:509
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_RESP
Client -> Server: {client_pubkey[32]} (UNENCRYPTED)
Definition packet.h:313
@ PACKET_TYPE_ACIP_NETWORK_QUALITY
NETWORK_QUALITY exchange (unified packet for all quality metrics)
Definition packet.h:455
@ PACKET_TYPE_ACIP_HOST_LOST
Host lost notification - lightweight (Participant -> ACDS)
Definition packet.h:465
@ PACKET_TYPE_ACIP_STRING_RESERVE
Reserve session string (Client -> Discovery Server)
Definition packet.h:431
@ PACKET_TYPE_CRYPTO_AUTH_FAILED
Server -> Client: "authentication failed" (UNENCRYPTED)
Definition packet.h:319
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion)
Definition packet.h:333
@ PACKET_TYPE_SIZE_MESSAGE
Terminal size message.
Definition packet.h:346
@ PACKET_TYPE_REMOTE_LOG
Bidirectional remote logging packet.
Definition packet.h:354
@ PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP
Server -> Client: {HMAC[32]} server proves knowledge (UNENCRYPTED)
Definition packet.h:321
@ PACKET_TYPE_ACIP_HOST_ANNOUNCEMENT
Host announcement (Participant -> ACDS, "I won negotiation")
Definition packet.h:457
@ PACKET_TYPE_PROTOCOL_VERSION
Protocol version and capabilities negotiation.
Definition packet.h:288
@ PACKET_TYPE_CRYPTO_NO_ENCRYPTION
Client -> Server: "I want to proceed without encryption" (UNENCRYPTED)
Definition packet.h:325
@ PACKET_TYPE_ACIP_SESSION_CREATE
Create new session (Client -> Discovery Server)
Definition packet.h:407
@ PACKET_TYPE_ACIP_STRING_RESERVED
String reserved response (Discovery Server -> Client)
Definition packet.h:433
@ PACKET_TYPE_ACIP_DISCOVERY_PING
Discovery server ping (keepalive)
Definition packet.h:507
@ PACKET_TYPE_CRYPTO_AUTH_CHALLENGE
Server -> Client: {nonce[32]} (UNENCRYPTED)
Definition packet.h:315
@ PACKET_TYPE_ASCII_FRAME
Complete ASCII frame with all metadata.
Definition packet.h:361
@ PACKET_TYPE_CRYPTO_PARAMETERS
Server -> Client: Chosen algorithms + data sizes (UNENCRYPTED)
Definition packet.h:309
@ PACKET_TYPE_ACIP_SESSION_LOOKUP
Lookup session by string (Client -> Discovery Server)
Definition packet.h:411
@ PACKET_TYPE_CRYPTO_CAPABILITIES
Client -> Server: Supported crypto algorithms (UNENCRYPTED)
Definition packet.h:307
@ PACKET_TYPE_ACIP_SESSION_LEAVE
Leave session (Client -> Discovery Server)
Definition packet.h:419
@ PACKET_TYPE_ACIP_WEBRTC_SDP
WebRTC SDP offer/answer (bidirectional)
Definition packet.h:426
@ PACKET_TYPE_ACIP_SESSION_JOIN
Join existing session (Client -> Discovery Server)
Definition packet.h:415
@ PACKET_TYPE_ACIP_SESSION_CREATED
Session created response (Discovery Server -> Client)
Definition packet.h:409
@ PACKET_TYPE_ACIP_STRING_RELEASE
Release string reservation (Client -> Discovery Server)
Definition packet.h:437
@ PACKET_TYPE_CLIENT_JOIN
Client announces capability to send media.
Definition packet.h:387
@ PACKET_TYPE_CLIENT_CAPABILITIES
Client reports terminal capabilities.
Definition packet.h:381
@ PACKET_TYPE_ACIP_STRING_RENEW
Renew string reservation (Client -> Discovery Server)
Definition packet.h:435
@ PACKET_TYPE_ERROR_MESSAGE
Error packet with asciichat_error_t code and human-readable message.
Definition packet.h:352
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:383
@ PACKET_TYPE_AUDIO_BATCH
Batched audio packets for efficiency.
Definition packet.h:372
@ PACKET_TYPE_ACIP_SESSION_JOINED
Session joined response (Discovery Server -> Client)
Definition packet.h:417
@ PACKET_TYPE_STREAM_STOP
Client stops sending media.
Definition packet.h:393
@ PACKET_TYPE_ACIP_SESSION_END
End session (Host -> Discovery Server)
Definition packet.h:421
bool socket_is_valid(socket_t sock)
Check if a socket handle is valid.
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:278
int errno
#define asciichat_crc32(data, len)
Main CRC32 dispatcher macro - use this in application code.
Definition crc32.h:144
int socket_t
🔊 Audio Capture and Playback Interface for ascii-chat
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
Definition log/log.h:702
#define log_warn_every(interval_us, fmt,...)
Rate-limited WARN logging.
Definition log/log.h:708
🌐 Core network I/O operations with timeout support
✅ Safe Integer Arithmetic and Overflow Detection
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.
Definition packet.c:1056
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.
Definition packet.c:910
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.
Definition packet.c:999
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.
Definition packet.c:957
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:575
#define MAX(a, b)
Definition param.h:10
Cross-platform socket interface for ascii-chat.
ClangTool/LibTooling compatibility shim for stdbool.h.
ASCII frame packet structure (Packet Type 2)
Definition packet.h:848
Error context structure.
asciichat_error_t code
Error code (asciichat_error_t enum value)
Audio batch packet structure (Packet Type 28)
Definition packet.h:904
Client information packet structure.
Definition packet.h:653
Crypto capabilities packet structure (Packet Type 14)
Definition packet.h:952
Cryptographic context structure.
Crypto parameters packet structure (Packet Type 15)
Definition packet.h:981
uint16_t auth_public_key_size
Authentication public key size in bytes (e.g., 32 for Ed25519, 1952 for Dilithium3)
Definition packet.h:993
uint16_t signature_size
Signature size in bytes (e.g., 64 for Ed25519, 3309 for Dilithium3)
Definition packet.h:995
uint16_t shared_secret_size
Shared secret size in bytes (e.g., 32 for X25519)
Definition packet.h:997
uint16_t kex_public_key_size
Key exchange public key size in bytes (e.g., 32 for X25519, 1568 for Kyber1024)
Definition packet.h:991
Error packet structure carrying error code and textual description.
Definition packet.h:727
uint32_t message_length
Length of message payload in bytes (0-512)
Definition packet.h:731
uint32_t error_code
Error code from asciichat_error_t enumeration.
Definition packet.h:729
Image frame packet structure (Packet Type 3)
Definition packet.h:876
uint32_t pixel_format
Pixel format enum (1=RGB24, 2=RGBA32, 3=BGR24, 4=BGRA32)
Definition packet.h:882
uint32_t timestamp
Timestamp when frame was captured (milliseconds since epoch)
Definition packet.h:888
uint32_t compressed_size
Compressed data size (0 = not compressed, >0 = compressed)
Definition packet.h:884
uint32_t checksum
CRC32 checksum of pixel data.
Definition packet.h:886
uint32_t height
Image height in pixels.
Definition packet.h:880
uint32_t width
Image width in pixels.
Definition packet.h:878
Packet envelope containing received packet data.
Definition packet.h:1129
void * allocated_buffer
Buffer that needs to be freed by caller (may be NULL if not allocated)
Definition packet.h:1139
size_t allocated_size
Size of allocated buffer in bytes.
Definition packet.h:1141
size_t len
Length of payload data in bytes.
Definition packet.h:1135
void * data
Packet payload data (decrypted and decompressed if applicable)
Definition packet.h:1133
packet_type_t type
Packet type (from packet_types.h)
Definition packet.h:1131
Network packet header structure.
Definition packet.h:598
uint32_t client_id
Client ID (0 = server, >0 = client identifier)
Definition packet.h:608
uint32_t length
Payload data length in bytes (0 for header-only packets)
Definition packet.h:604
uint64_t magic
Magic number (PACKET_MAGIC = 0xA5C11C4A1 "ASCIICHAT" in hex) for packet validation.
Definition packet.h:600
uint16_t type
Packet type (packet_type_t enumeration)
Definition packet.h:602
uint32_t crc32
CRC32 checksum of payload data (0 if length == 0)
Definition packet.h:606
Protocol version negotiation packet structure (Packet Type 1)
Definition packet.h:818
Remote log packet structure carrying log level and message text.
Definition packet.h:741
uint16_t flags
Additional flags (REMOTE_LOG_FLAG_*)
Definition packet.h:747
uint32_t message_length
Message payload length in bytes (0-512)
Definition packet.h:749
uint8_t log_level
Log level associated with the message (log_level_t cast to uint8_t)
Definition packet.h:743
uint8_t direction
Direction hint so receivers can annotate origin.
Definition packet.h:745
#define LOG_FATAL
Definition types.h:43