ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
network/packet/parsing.c
Go to the documentation of this file.
1
2/* Storage capacities in elements; text buffers include the terminating NUL. */
3#define NETWORK_PACKET_PARSING_SIZE_BUFFER_SIZE 32
4#define NETWORK_PACKET_PARSING_HEX_BUFFER_SIZE 256
5
7#include <defer.h>
20
27#include <string.h>
28
33#define PACKET_MAX_FRAME_SIZE (256 * 1024 * 1024)
34
39#define PACKET_MAX_DIMENSION 32768
40
41static char *decode_frame_data_malloc(const char *frame_data_ptr, size_t frame_data_len, bool is_compressed,
42 uint32_t original_size, uint32_t compressed_size) {
43 // Validate size before allocation to prevent excessive memory usage
44 if (original_size > PACKET_MAX_FRAME_SIZE) {
46 format_bytes_pretty(original_size, size_str, sizeof(size_str));
47 SET_ERRNO(ERROR_NETWORK_SIZE, "Frame size exceeds maximum: %s (max %d MB)", size_str,
48 PACKET_MAX_FRAME_SIZE / (1024 * 1024));
49 return NULL;
50 }
51
52 // Allocate buffer with extra byte for null terminator
53 SAFE_MALLOC_DEFER_FREE(frame_data, original_size + 1, char *);
54 if (!frame_data) {
55 SET_ERRNO(ERROR_MEMORY, "Failed to allocate %u bytes for frame decode", original_size);
56 return NULL;
57 }
58
59 if (is_compressed) {
60 // Validate compressed frame size
61 if (frame_data_len != compressed_size) {
62 SET_ERRNO(ERROR_NETWORK_SIZE, "Compressed frame size mismatch: expected %u, got %zu", compressed_size,
63 frame_data_len);
64 return NULL;
65 }
66
67 // Decompress using compression API
68 asciichat_error_t decompress_result =
69 decompress_data_exact(frame_data_ptr, frame_data_len, frame_data, original_size);
70
71 if (decompress_result != ASCIICHAT_OK) {
72 SET_ERRNO(ERROR_COMPRESSION, "Decompression failed for expected size %u: %s", original_size,
73 asciichat_error_string(decompress_result));
74 return NULL;
75 }
76
77 log_debug_every(US_PER_SEC_INT, "Decompressed frame: %zu -> %u bytes", frame_data_len, original_size);
78 } else {
79 // Uncompressed frame - validate size
80 if (frame_data_len != original_size) {
81 log_error("Uncompressed frame size mismatch: expected %u, got %zu", original_size, frame_data_len);
82 return NULL;
83 }
84
85 // Only copy the actual amount of data we received
86 size_t copy_size = (frame_data_len > original_size) ? original_size : frame_data_len;
87 memcpy(frame_data, frame_data_ptr, copy_size);
88 }
89
90 // Null-terminate the frame data
91 frame_data[original_size] = '\0';
92 char *result = frame_data;
93 frame_data = NULL;
94 return result;
95}
96
97char *packet_decode_frame_data_malloc(const char *data, size_t length, bool compressed, uint32_t original_size,
98 uint32_t compressed_size) {
99 uint64_t started = time_get_ns();
100 char *result = decode_frame_data_malloc(data, length, compressed, original_size, compressed_size);
103 return result;
104}
105
106asciichat_error_t packet_decode_frame_data_buffer(const char *frame_data_ptr, size_t frame_data_len, bool is_compressed,
107 void *output_buffer, size_t output_size, uint32_t original_size,
108 uint32_t compressed_size) {
109 if (!frame_data_ptr || !output_buffer) {
110 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL pointer in frame decode");
111 }
112
113 if (output_size < original_size) {
114 return SET_ERRNO(ERROR_BUFFER_FULL, "Output buffer too small: %zu < %u", output_size, original_size);
115 }
116
117 if (is_compressed) {
118 // Validate compressed frame size
119 if (frame_data_len != compressed_size) {
120 return SET_ERRNO(ERROR_NETWORK_SIZE, "Compressed frame size mismatch: expected %u, got %zu", compressed_size,
121 frame_data_len);
122 }
123
124 // Decompress using compression API
125 asciichat_error_t decompress_result =
126 decompress_data_exact(frame_data_ptr, frame_data_len, output_buffer, original_size);
127
128 if (decompress_result != ASCIICHAT_OK) {
129 return SET_ERRNO(ERROR_COMPRESSION, "Decompression failed for expected size %u: %s", original_size,
130 asciichat_error_string(decompress_result));
131 }
132
133 log_debug_every(US_PER_SEC_INT, "Decompressed frame to buffer: %zu -> %u bytes", frame_data_len, original_size);
134 } else {
135 // Uncompressed frame - validate size
136 if (frame_data_len != original_size) {
137 return SET_ERRNO(ERROR_NETWORK_SIZE, "Uncompressed frame size mismatch: expected %u, got %zu", original_size,
138 frame_data_len);
139 }
140
141 // Copy data to output buffer
142 memcpy(output_buffer, frame_data_ptr, original_size);
143 }
144
145 return ASCIICHAT_OK;
146}
147
149 bool compressed, uint32_t original_size,
150 uint32_t compressed_size) {
151 if (!workspace || original_size > PACKET_MAX_FRAME_SIZE)
152 return SET_ERRNO(ERROR_NETWORK_SIZE, "Invalid decoded ASCII workspace size: %u", original_size);
153 uint64_t started = time_get_ns();
154 asciichat_error_t result = buffer_workspace_reserve(workspace, NULL, (size_t)original_size + 1);
155 if (result == ASCIICHAT_OK)
156 result = packet_decode_frame_data_buffer(data, length, compressed, workspace->data,
157 buffer_capacity(workspace->data), original_size, compressed_size);
158 if (result == ASCIICHAT_OK) {
159 ((char *)workspace->data)[original_size] = '\0';
160 workspace->length = original_size;
161 }
165 return result;
166}
167
169 if (!out_rgb_size) {
170 return SET_ERRNO(ERROR_INVALID_PARAM, "out_rgb_size pointer is NULL");
171 }
172
173 // Check dimensions are non-zero
174 if (width == 0 || height == 0) {
175 return SET_ERRNO(ERROR_INVALID_STATE, "Frame dimensions cannot be zero: %ux%u", width, height);
176 }
177
178 // Check dimensions are within reasonable bounds
180 return SET_ERRNO(ERROR_INVALID_STATE, "Frame dimensions exceed maximum: %ux%u (max %u)", width, height,
182 }
183
184 // Calculate RGB buffer size with overflow checking (width * height * 3 bytes per pixel)
185 size_t pixel_count = 0;
186 if (safe_size_mul(width, height, &pixel_count) != 0) {
187 return SET_ERRNO(ERROR_MEMORY, "Frame dimension multiplication overflow: %u * %u", width, height);
188 }
189
190 size_t rgb_size = 0;
191 if (safe_size_mul(pixel_count, 3, &rgb_size) != 0) {
192 return SET_ERRNO(ERROR_MEMORY, "RGB buffer size overflow: %zu * 3", pixel_count);
193 }
194
195 // Validate final buffer size against maximum
196 if (rgb_size > PACKET_MAX_FRAME_SIZE) {
198 format_bytes_pretty(rgb_size, size_str, sizeof(size_str));
199 return SET_ERRNO(ERROR_MEMORY, "Frame buffer size exceeds maximum: %s (max %d MB)", size_str,
200 PACKET_MAX_FRAME_SIZE / (1024 * 1024));
201 }
202
203 *out_rgb_size = rgb_size;
204 return ASCIICHAT_OK;
205}
206
207// NOTE: packet_parse_audio_batch_header has been moved to lib/audio/audio.c as audio_parse_batch_header
208// This module now provides a wrapper inline function in packet_parsing.h for backwards compatibility
209// See lib/audio/audio.h for the canonical implementation
210
211asciichat_error_t packet_parse_opus_batch(const void *packet_data, size_t packet_len, const uint8_t **out_opus_data,
212 size_t *out_opus_size, const uint16_t **out_frame_sizes, int *out_sample_rate,
213 int *out_frame_duration, int *out_frame_count) {
214 if (!packet_data || !out_opus_data || !out_opus_size || !out_frame_sizes || !out_sample_rate || !out_frame_duration ||
215 !out_frame_count) {
216 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL parameter in Opus batch parsing");
217 }
218
219 // Verify minimum packet size (16-byte header: sample_rate + frame_duration + frame_count + reserved)
220 const size_t header_size = 16;
221 if (packet_len < header_size) {
222 // DEBUG: Log first few bytes of corrupted packet to understand what's happening
224 size_t bytes_to_show = (packet_len < 32) ? packet_len : 32;
225 for (size_t i = 0; i < bytes_to_show; i++) {
226 snprintf(hex_buf + i * 2, 3, "%02x", ((uint8_t *)packet_data)[i]);
227 }
228 hex_buf[bytes_to_show * 2] = '\0';
229 log_error("★ OPUS_BATCH_RCV_DEBUG: packet_len=%zu, expected_min=%zu, first_bytes=%s", packet_len, header_size,
230 hex_buf);
231 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Opus batch packet too small: %zu < %zu", packet_len, header_size);
232 }
233
234 // Parse header (convert from network byte order)
235 const uint8_t *buf = (const uint8_t *)packet_data;
236 uint32_t sr, fd, fc;
237 memcpy(&sr, buf, 4);
238 memcpy(&fd, buf + 4, 4);
239 memcpy(&fc, buf + 8, 4);
240 *out_sample_rate = (int)NET_TO_HOST_U32(sr);
241 *out_frame_duration = (int)NET_TO_HOST_U32(fd);
242 int frame_count = (int)NET_TO_HOST_U32(fc);
243 *out_frame_count = frame_count;
244
245 // Validate frame count to prevent overflow
246 if (frame_count < 0 || frame_count > 1000) {
247 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid Opus frame count: %d (must be 0-1000)", frame_count);
248 }
249
250 // Extract frame sizes array (after 16-byte header)
251 size_t frame_sizes_bytes = (size_t)frame_count * sizeof(uint16_t);
252 if (packet_len < header_size + frame_sizes_bytes) {
253 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Opus batch packet too small for frame sizes: %zu < %zu", packet_len,
254 header_size + frame_sizes_bytes);
255 }
256 // NOTE: Frame sizes are in network byte order - caller must use NET_TO_HOST_U16() when reading
257 *out_frame_sizes = (const uint16_t *)(buf + header_size);
258
259 // Extract Opus data (after header + frame sizes)
260 *out_opus_data = buf + header_size + frame_sizes_bytes;
261 *out_opus_size = packet_len - header_size - frame_sizes_bytes;
262
263 return ASCIICHAT_OK;
264}
#define SET_ERRNO(code,...)
size_t buffer_capacity(const void *data)
Definition buffer.c:280
asciichat_error_t buffer_workspace_reserve(buffer_workspace_t *workspace, buffer_domain_t *domain, size_t capacity)
Definition buffer.c:356
🔢 Byte-Level Access and Arithmetic Utilities
📦 Network Packet Compression Utilities
🔄 Network byte order conversion helpers
#define NET_TO_HOST_U32(val)
Definition endian.h:81
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
unsigned short uint16_t
Definition common.h:61
unsigned int uint32_t
Definition common.h:62
#define SAFE_MALLOC_DEFER_FREE(name, size, cast)
Definition common.h:386
#define safe_size_mul
Definition common.h:503
unsigned long long uint64_t
Definition common.h:63
unsigned char uint8_t
Definition common.h:60
asciichat_error_t decompress_data_exact(const void *input, size_t input_size, void *output, size_t expected_size)
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_INVALID_STATE
@ ERROR_COMPRESSION
@ ERROR_BUFFER_FULL
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
@ ERROR_INVALID_PARAM
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
#define US_PER_SEC_INT
Definition time.h:173
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.
asciichat_error_t packet_decode_frame_data_workspace(buffer_workspace_t *workspace, const void *data, size_t length, bool compressed, uint32_t original_size, uint32_t compressed_size)
char * packet_decode_frame_data_malloc(const char *data, size_t length, bool 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 util/format.c:11
void stats_counter_add(stats_scope_t *scope, stats_counter_id_t id, uint64_t amount)
void stats_duration_record(stats_scope_t *scope, stats_duration_id_t id, uint64_t elapsed_ns)
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
Definition log/log.h:749
#define NETWORK_PACKET_PARSING_SIZE_BUFFER_SIZE
#define NETWORK_PACKET_PARSING_HEX_BUFFER_SIZE
stats_scope_t * stats_runtime_scope(void)
Definition runtime.c:80
🔤 String Manipulation and Shell Escaping Utilities
Owns the temporary storage used by buffer workspace. The data pointers and size or cursor fields desc...
Definition buffer.h:102
⏱️ High-precision timing utilities using sokol_time.h and uthash
📊 String Formatting Utilities