ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
Shared Packet Parsing Utilities

📡 Shared protocol parsing utilities for server and client packet handlers More...

Files

file  packet_parsing.c
 ðŸ“¡ Shared packet parsing utilities implementation
 
file  packet_parsing.h
 Shared packet parsing utilities to eliminate duplication between server and client handlers.
 

Macros

#define PACKET_MAX_FRAME_SIZE   (256 * 1024 * 1024)
 Maximum frame size (256MB) - prevents memory exhaustion attacks.
 
#define PACKET_MAX_DIMENSION   32768
 Maximum frame dimension (32768x32768) - prevents overflow.
 

Frame Decoding Functions

Unified frame data decoding for both compressed and uncompressed formats

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)
 
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)
 

Frame Dimension Validation

Validation helpers for frame dimensions with overflow checking

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.
 

Opus Audio Batch Parsing

Helpers for parsing Opus audio batch packets

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.
 

Detailed Description

📡 Shared protocol parsing utilities for server and client packet handlers

This module provides reusable utilities for parsing and validating protocol packets, used by both server (src/server/protocol.c) and client (src/client/protocol.c) handlers.

CORE RESPONSIBILITIES:

  1. Frame data decoding (handles compressed and uncompressed formats)
  2. Network byte order conversions and validation
  3. Audio batch header parsing and validation
  4. Frame dimension validation with overflow checking
  5. Generic packet payload validation helpers

DESIGN PRINCIPLES:

USAGE PATTERNS:

Server handlers use these for incoming client packets:

// Decode compressed/uncompressed frame data
frame_buffer_t *decoded = packet_decode_frame_data(
frame_data_ptr, frame_data_len, is_compressed,
expected_size, compressed_size);
// Validate audio batch header
result = packet_parse_audio_batch_header(data, len, &batch);
Parsed audio batch packet header information.

Client handlers use these for incoming server packets:

// Same frame decoding for server->client frames
frame_data, frame_data_len, is_compressed,
original_size, compressed_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)

INTEGRATION POINTS:

OVERFLOW SAFETY:

All integer calculations use safe_size_mul() and overflow checking to prevent buffer overflows from malicious or malformed packets.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
December 2025

Macro Definition Documentation

◆ PACKET_MAX_DIMENSION

#define PACKET_MAX_DIMENSION   32768

#include <packet_parsing.c>

Maximum frame dimension (32768x32768) - prevents overflow.

Definition at line 31 of file packet_parsing.c.

◆ PACKET_MAX_FRAME_SIZE

#define PACKET_MAX_FRAME_SIZE   (256 * 1024 * 1024)

#include <packet_parsing.c>

Maximum frame size (256MB) - prevents memory exhaustion attacks.

Definition at line 25 of file packet_parsing.c.

Function Documentation

◆ packet_decode_frame_data_buffer()

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 
)

#include <packet_parsing.h>

Decode frame data (fixed buffer version for server handlers)

Decodes frame data into a pre-allocated fixed-size buffer. Used when buffer allocation is managed separately (e.g., ring buffers).

Parameters
frame_data_ptrPointer to frame data (compressed or uncompressed)
frame_data_lenActual size of frame_data_ptr buffer
is_compressedTrue if data is zstd-compressed
output_bufferPre-allocated output buffer
output_sizeSize of output_buffer in bytes
original_sizeExpected decompressed/uncompressed size
compressed_sizeExpected compressed size (if is_compressed=true)
Returns
ASCIICHAT_OK on success, error code on failure Errors set asciichat_errno with context message
Note
Used by server handlers for client->server IMAGE_FRAME packets
See also
packet_decode_frame_data_malloc() For malloc version

Definition at line 89 of file packet_parsing.c.

91 {
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}
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_COMPRESSION
@ ERROR_BUFFER_FULL
Definition error_codes.h:97
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_NETWORK_SIZE
Definition error_codes.h:74
@ ERROR_INVALID_PARAM
#define log_debug(...)
Log a DEBUG message.

References ASCIICHAT_OK, decompress_data(), ERROR_BUFFER_FULL, ERROR_COMPRESSION, ERROR_INVALID_PARAM, ERROR_NETWORK_SIZE, log_debug, and SET_ERRNO.

◆ packet_decode_frame_data_malloc()

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 
)

#include <packet_parsing.h>

Decode frame data (malloc version for client handlers)

Handles both compressed (zstd) and uncompressed frame formats. Allocates buffer using SAFE_MALLOC that caller must free. Validates sizes to prevent memory exhaustion attacks.

Frame formats supported:

  • Uncompressed: Raw pixel/text data at expected_size bytes
  • Compressed: zstd-compressed data, decompresses to expected_size bytes

SIZE VALIDATION:

  • Validates original_size <= 100MB (prevents excessive allocation)
  • Validates expected_size == uncompressed size
  • Validates compressed_size <= frame_data_len (if compressed)

ERROR HANDLING:

  • Returns NULL on allocation failure
  • Returns NULL on decompression failure
  • Returns NULL on size validation failure
  • Sets asciichat_errno for detailed error reporting
Parameters
frame_data_ptrPointer to frame data (compressed or uncompressed)
frame_data_lenActual size of frame_data_ptr buffer
is_compressedTrue if data is zstd-compressed, false if raw
original_sizeExpected decompressed/uncompressed size in bytes
compressed_sizeExpected compressed size (validated only if is_compressed=true)
Returns
Allocated buffer with decoded data (caller must SAFE_FREE), or NULL on error
Note
Caller is responsible for freeing returned buffer with SAFE_FREE()
Added null terminator to decoded data (buffer is original_size + 1)
Used by client handlers for server->client frame packets
See also
packet_decode_frame_data_buffer() For fixed-size buffer variant

Definition at line 33 of file packet_parsing.c.

34 {
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}
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
@ ERROR_MEMORY
Definition error_codes.h:53
#define log_error(...)
Log an ERROR message.
#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

References ASCIICHAT_OK, decompress_data(), ERROR_COMPRESSION, ERROR_MEMORY, ERROR_NETWORK_SIZE, format_bytes_pretty(), log_debug, log_error, PACKET_MAX_FRAME_SIZE, SAFE_FREE, SAFE_MALLOC, and SET_ERRNO.

◆ packet_parse_opus_batch()

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 
)

#include <packet_parsing.h>

Parse Opus audio batch packet header and extract frame data.

Parses PACKET_TYPE_AUDIO_OPUS_BATCH packet and extracts metadata and Opus data. The Opus data and frame_sizes pointers point into the packet_data buffer (NOT copied), so packet_data must remain valid while using these outputs.

Packet format:

  • Offset 0-3: sample_rate (uint32_t, network byte order)
  • Offset 4-7: frame_duration (uint32_t, network byte order)
  • Offset 8-11: frame_count (uint32_t, network byte order)
  • Offset 12-15: reserved (4 bytes, zeroed)
  • Offset 16+: frame_sizes array (uint16_t[], network byte order)
  • After sizes: Opus-encoded data (concatenated frames)
Parameters
packet_dataPacket payload data
packet_lenPacket payload length
out_opus_dataOutput: Pointer to Opus-encoded data within packet (NOT copied)
out_opus_sizeOutput: Size of Opus-encoded data
out_frame_sizesOutput: Pointer to frame sizes array within packet (NOT copied)
out_sample_rateOutput: Sample rate in Hz
out_frame_durationOutput: Frame duration in milliseconds
out_frame_countOutput: Number of frames in batch
Returns
ASCIICHAT_OK on success, error code on failure:
  • ERROR_INVALID_PARAM: NULL pointers
  • ERROR_NETWORK_PROTOCOL: Packet too small or malformed
Note
out_opus_data and out_frame_sizes point into packet_data - do not free separately
Frame sizes in out_frame_sizes are in NETWORK byte order - use NET_TO_HOST_U16() when reading
Warning
packet_data must remain valid while using out_opus_data and out_frame_sizes

Definition at line 173 of file packet_parsing.c.

175 {
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}
#define NET_TO_HOST_U32(val)
Definition endian.h:86
unsigned short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58
unsigned char uint8_t
Definition common.h:56
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:73

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_NETWORK_PROTOCOL, NET_TO_HOST_U32, and SET_ERRNO.

Referenced by handle_audio_opus_batch_packet().

◆ packet_validate_frame_dimensions()

asciichat_error_t packet_validate_frame_dimensions ( uint32_t  width,
uint32_t  height,
size_t *  out_rgb_size 
)

#include <packet_parsing.h>

Validate frame dimensions and calculate RGB buffer size.

Performs comprehensive validation of frame dimensions:

  1. Checks width and height are non-zero and reasonable (< 32768)
  2. Calculates RGB buffer size (width * height * 3) with overflow checking
  3. Validates result size doesn't exceed max image size (256MB)

OVERFLOW PROTECTION:

  • Uses safe_size_mul() for dimension multiplication
  • Checks for integer overflow at each step
  • Returns error instead of wrapping around
Parameters
widthFrame width in pixels
heightFrame height in pixels
out_rgb_sizeOutput parameter: calculated RGB size (width*height*3)
Returns
ASCIICHAT_OK on success, error code on failure:
  • ERROR_INVALID_STATE: width or height is zero
  • ERROR_INVALID_STATE: dimensions exceed max (32768)
  • ERROR_MEMORY: Calculated size exceeds max (256MB)
  • ERROR_MEMORY: Integer overflow in multiplication
Note
Critical for preventing integer overflow attacks
Used by both server and client frame handlers

Definition at line 130 of file packet_parsing.c.

130 {
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}
#define safe_size_mul
Definition common.h:408
@ ERROR_INVALID_STATE
#define PACKET_MAX_DIMENSION
Maximum frame dimension (32768x32768) - prevents overflow.

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_MEMORY, format_bytes_pretty(), PACKET_MAX_DIMENSION, PACKET_MAX_FRAME_SIZE, safe_size_mul, and SET_ERRNO.