ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
packet_parsing.c
Go to the documentation of this file.
1
13#include "packet_parsing.h"
14#include "network/compression.h"
15#include "util/bytes.h"
16#include "util/format.h"
17#include "util/endian.h"
18#include "logging.h"
19#include <string.h>
20
25#define PACKET_MAX_FRAME_SIZE (256 * 1024 * 1024)
26
31#define PACKET_MAX_DIMENSION 32768
32
33char *packet_decode_frame_data_malloc(const char *frame_data_ptr, size_t frame_data_len, bool is_compressed,
34 uint32_t original_size, uint32_t compressed_size) {
35 // Validate size before allocation to prevent excessive memory usage
36 if (original_size > PACKET_MAX_FRAME_SIZE) {
37 char size_str[32];
38 format_bytes_pretty(original_size, size_str, sizeof(size_str));
39 SET_ERRNO(ERROR_NETWORK_SIZE, "Frame size exceeds maximum: %s (max %d MB)", size_str,
40 PACKET_MAX_FRAME_SIZE / (1024 * 1024));
41 return NULL;
42 }
43
44 // Allocate buffer with extra byte for null terminator
45 char *frame_data = SAFE_MALLOC(original_size + 1, char *);
46 if (!frame_data) {
47 SET_ERRNO(ERROR_MEMORY, "Failed to allocate %u bytes for frame decode", original_size);
48 return NULL;
49 }
50
51 if (is_compressed) {
52 // Validate compressed frame size
53 if (frame_data_len != compressed_size) {
54 SET_ERRNO(ERROR_NETWORK_SIZE, "Compressed frame size mismatch: expected %u, got %zu", compressed_size,
55 frame_data_len);
56 SAFE_FREE(frame_data);
57 return NULL;
58 }
59
60 // Decompress using compression API
61 asciichat_error_t decompress_result = decompress_data(frame_data_ptr, frame_data_len, frame_data, original_size);
62
63 if (decompress_result != ASCIICHAT_OK) {
64 SET_ERRNO(ERROR_COMPRESSION, "Decompression failed for expected size %u: %s", original_size,
65 asciichat_error_string(decompress_result));
66 SAFE_FREE(frame_data);
67 return NULL;
68 }
69
70 log_debug("Decompressed frame: %zu -> %u bytes", frame_data_len, original_size);
71 } else {
72 // Uncompressed frame - validate size
73 if (frame_data_len != original_size) {
74 log_error("Uncompressed frame size mismatch: expected %u, got %zu", original_size, frame_data_len);
75 SAFE_FREE(frame_data);
76 return NULL;
77 }
78
79 // Only copy the actual amount of data we received
80 size_t copy_size = (frame_data_len > original_size) ? original_size : frame_data_len;
81 memcpy(frame_data, frame_data_ptr, copy_size);
82 }
83
84 // Null-terminate the frame data
85 frame_data[original_size] = '\0';
86 return frame_data;
87}
88
89asciichat_error_t packet_decode_frame_data_buffer(const char *frame_data_ptr, size_t frame_data_len, bool is_compressed,
90 void *output_buffer, size_t output_size, uint32_t original_size,
91 uint32_t compressed_size) {
92 if (!frame_data_ptr || !output_buffer) {
93 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL pointer in frame decode");
94 }
95
96 if (output_size < original_size) {
97 return SET_ERRNO(ERROR_BUFFER_FULL, "Output buffer too small: %zu < %u", output_size, original_size);
98 }
99
100 if (is_compressed) {
101 // Validate compressed frame size
102 if (frame_data_len != compressed_size) {
103 return SET_ERRNO(ERROR_NETWORK_SIZE, "Compressed frame size mismatch: expected %u, got %zu", compressed_size,
104 frame_data_len);
105 }
106
107 // Decompress using compression API
108 asciichat_error_t decompress_result = decompress_data(frame_data_ptr, frame_data_len, output_buffer, original_size);
109
110 if (decompress_result != ASCIICHAT_OK) {
111 return SET_ERRNO(ERROR_COMPRESSION, "Decompression failed for expected size %u: %s", original_size,
112 asciichat_error_string(decompress_result));
113 }
114
115 log_debug("Decompressed frame to buffer: %zu -> %u bytes", frame_data_len, original_size);
116 } else {
117 // Uncompressed frame - validate size
118 if (frame_data_len != original_size) {
119 return SET_ERRNO(ERROR_NETWORK_SIZE, "Uncompressed frame size mismatch: expected %u, got %zu", original_size,
120 frame_data_len);
121 }
122
123 // Copy data to output buffer
124 memcpy(output_buffer, frame_data_ptr, original_size);
125 }
126
127 return ASCIICHAT_OK;
128}
129
131 if (!out_rgb_size) {
132 return SET_ERRNO(ERROR_INVALID_PARAM, "out_rgb_size pointer is NULL");
133 }
134
135 // Check dimensions are non-zero
136 if (width == 0 || height == 0) {
137 return SET_ERRNO(ERROR_INVALID_STATE, "Frame dimensions cannot be zero: %ux%u", width, height);
138 }
139
140 // Check dimensions are within reasonable bounds
141 if (width > PACKET_MAX_DIMENSION || height > PACKET_MAX_DIMENSION) {
142 return SET_ERRNO(ERROR_INVALID_STATE, "Frame dimensions exceed maximum: %ux%u (max %u)", width, height,
144 }
145
146 // Calculate RGB buffer size with overflow checking (width * height * 3 bytes per pixel)
147 size_t pixel_count = 0;
148 if (safe_size_mul(width, height, &pixel_count) != 0) {
149 return SET_ERRNO(ERROR_MEMORY, "Frame dimension multiplication overflow: %u * %u", width, height);
150 }
151
152 size_t rgb_size = 0;
153 if (safe_size_mul(pixel_count, 3, &rgb_size) != 0) {
154 return SET_ERRNO(ERROR_MEMORY, "RGB buffer size overflow: %zu * 3", pixel_count);
155 }
156
157 // Validate final buffer size against maximum
158 if (rgb_size > PACKET_MAX_FRAME_SIZE) {
159 char size_str[32];
160 format_bytes_pretty(rgb_size, size_str, sizeof(size_str));
161 return SET_ERRNO(ERROR_MEMORY, "Frame buffer size exceeds maximum: %s (max %d MB)", size_str,
162 PACKET_MAX_FRAME_SIZE / (1024 * 1024));
163 }
164
165 *out_rgb_size = rgb_size;
166 return ASCIICHAT_OK;
167}
168
169// NOTE: packet_parse_audio_batch_header has been moved to lib/audio/audio.c as audio_parse_batch_header
170// This module now provides a wrapper inline function in packet_parsing.h for backwards compatibility
171// See lib/audio/audio.h for the canonical implementation
172
173asciichat_error_t packet_parse_opus_batch(const void *packet_data, size_t packet_len, const uint8_t **out_opus_data,
174 size_t *out_opus_size, const uint16_t **out_frame_sizes, int *out_sample_rate,
175 int *out_frame_duration, int *out_frame_count) {
176 if (!packet_data || !out_opus_data || !out_opus_size || !out_frame_sizes || !out_sample_rate || !out_frame_duration ||
177 !out_frame_count) {
178 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL parameter in Opus batch parsing");
179 }
180
181 // Verify minimum packet size (16-byte header)
182 const size_t header_size = 16;
183 if (packet_len < header_size) {
184 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Opus batch packet too small: %zu < %zu", packet_len, header_size);
185 }
186
187 // Parse header (convert from network byte order)
188 const uint8_t *buf = (const uint8_t *)packet_data;
189 uint32_t sr, fd, fc;
190 memcpy(&sr, buf, 4);
191 memcpy(&fd, buf + 4, 4);
192 memcpy(&fc, buf + 8, 4);
193 *out_sample_rate = (int)NET_TO_HOST_U32(sr);
194 *out_frame_duration = (int)NET_TO_HOST_U32(fd);
195 int frame_count = (int)NET_TO_HOST_U32(fc);
196 *out_frame_count = frame_count;
197
198 // Validate frame count to prevent overflow
199 if (frame_count < 0 || frame_count > 1000) {
200 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid Opus frame count: %d (must be 0-1000)", frame_count);
201 }
202
203 // Extract frame sizes array (after 16-byte header)
204 size_t frame_sizes_bytes = (size_t)frame_count * sizeof(uint16_t);
205 if (packet_len < header_size + frame_sizes_bytes) {
206 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Opus batch packet too small for frame sizes: %zu < %zu", packet_len,
207 header_size + frame_sizes_bytes);
208 }
209 // NOTE: Frame sizes are in network byte order - caller must use NET_TO_HOST_U16() when reading
210 *out_frame_sizes = (const uint16_t *)(buf + header_size);
211
212 // Extract Opus data (after header + frame sizes)
213 *out_opus_data = buf + header_size + frame_sizes_bytes;
214 *out_opus_size = packet_len - header_size - frame_sizes_bytes;
215
216 return ASCIICHAT_OK;
217}
🔢 Byte-Level Access and Arithmetic Utilities
📦 Network Packet Compression Utilities
🔄 Network byte order conversion helpers
#define NET_TO_HOST_U32(val)
Definition endian.h:86
📊 String Formatting Utilities
unsigned short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
#define safe_size_mul
Definition common.h:408
unsigned char uint8_t
Definition common.h:56
asciichat_error_t decompress_data(const void *input, size_t input_size, void *output, size_t output_size)
Decompress data using zstd.
Definition compression.c:58
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ERROR_INVALID_STATE
@ ERROR_COMPRESSION
@ ERROR_BUFFER_FULL
Definition error_codes.h:97
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:73
@ ERROR_MEMORY
Definition error_codes.h:53
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_NETWORK_SIZE
Definition error_codes.h:74
@ ERROR_INVALID_PARAM
#define log_error(...)
Log an ERROR message.
#define log_debug(...)
Log a DEBUG message.
asciichat_error_t packet_parse_opus_batch(const void *packet_data, size_t packet_len, const uint8_t **out_opus_data, size_t *out_opus_size, const uint16_t **out_frame_sizes, int *out_sample_rate, int *out_frame_duration, int *out_frame_count)
Parse Opus audio batch packet header and extract frame data.
#define PACKET_MAX_DIMENSION
Maximum frame dimension (32768x32768) - prevents overflow.
asciichat_error_t packet_decode_frame_data_buffer(const char *frame_data_ptr, size_t frame_data_len, bool is_compressed, void *output_buffer, size_t output_size, uint32_t original_size, uint32_t compressed_size)
Decode frame data (fixed buffer version for server handlers)
asciichat_error_t packet_validate_frame_dimensions(uint32_t width, uint32_t height, size_t *out_rgb_size)
Validate frame dimensions and calculate RGB buffer size.
char * packet_decode_frame_data_malloc(const char *frame_data_ptr, size_t frame_data_len, bool is_compressed, uint32_t original_size, uint32_t compressed_size)
Decode frame data (malloc version for client handlers)
#define PACKET_MAX_FRAME_SIZE
Maximum frame size (256MB) - prevents memory exhaustion attacks.
void format_bytes_pretty(size_t bytes, char *out, size_t out_capacity)
Format byte count into human-readable string.
Definition format.c:10
Shared packet parsing utilities to eliminate duplication between server and client handlers.
Test logging control utilities.