ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
Validation Helpers

Reusable validation macros for protocol handlers. More...

Modules

 Image Validation Functions
 

Files

file  validation.h
 Common validation macros to reduce duplication in protocol handlers.
 

Macros

#define VALIDATE_NOTNULL_DATA(client, data, packet_name)
 
#define VALIDATE_MIN_SIZE(client, len, min_size, packet_name)
 
#define VALIDATE_EXACT_SIZE(client, len, expected_size, packet_name)
 
#define VALIDATE_AUDIO_STREAM_ENABLED(client, packet_name)
 
#define VALIDATE_AUDIO_SAMPLE_COUNT(client, num_samples, max_samples, packet_name)
 
#define VALIDATE_AUDIO_ALIGNMENT(client, len, sample_size, packet_name)
 
#define VALIDATE_RESOURCE_INITIALIZED(client, resource, resource_name)
 
#define VALIDATE_PACKET_SIZE(client, data, len, expected_size, packet_name)
 
#define VALIDATE_NONZERO(client, value, value_name, packet_name)
 
#define VALIDATE_RANGE(client, value, min_val, max_val, value_name, packet_name)
 
#define VALIDATE_CAPABILITY_FLAGS(client, flags, valid_mask, packet_name)
 
#define VALIDATE_FLAGS_MASK(client, flags, valid_mask, packet_name)
 
#define VALIDATE_PACKET_NOT_NULL(client, data, packet_name)
 

Typedefs

typedef struct client_info client_info_t
 

Functions

void disconnect_client_for_bad_data (client_info_t *client, const char *format,...)
 

Detailed Description

Reusable validation macros for protocol handlers.

Provides standardized validation macros for protocol handlers to:

All macros call disconnect_client_for_bad_data() on validation failure and return from the calling function immediately.

Usage:

void handle_packet(client_info_t *client, const void *data, size_t len) {
VALIDATE_NOTNULL_DATA(client, data, "PACKET_TYPE");
VALIDATE_MIN_SIZE(client, len, 16, "PACKET_TYPE");
VALIDATE_AUDIO_STREAM_ENABLED(client, "PACKET_TYPE");
// ... continue processing ...
}
#define VALIDATE_AUDIO_STREAM_ENABLED(client, packet_name)
#define VALIDATE_MIN_SIZE(client, len, min_size, packet_name)
#define VALIDATE_NOTNULL_DATA(client, data, packet_name)
Per-client state structure for server-side client management.

Macro Definition Documentation

◆ VALIDATE_AUDIO_ALIGNMENT

#define VALIDATE_AUDIO_ALIGNMENT (   client,
  len,
  sample_size,
  packet_name 
)

#include <validation.h>

Value:
do { \
if ((len) % (sample_size) != 0) { \
disconnect_client_for_bad_data((client), "%s payload not aligned (len=%zu, sample_size=%zu)", (packet_name), \
(len), (sample_size)); \
return; \
} \
} while (0)

Validate that audio sample alignment is correct (must be multiple of sample size). Disconnects client and returns if alignment is incorrect.

Definition at line 111 of file util/validation.h.

112 { \
113 if ((len) % (sample_size) != 0) { \
114 disconnect_client_for_bad_data((client), "%s payload not aligned (len=%zu, sample_size=%zu)", (packet_name), \
115 (len), (sample_size)); \
116 return; \
117 } \
118 } while (0)

◆ VALIDATE_AUDIO_SAMPLE_COUNT

#define VALIDATE_AUDIO_SAMPLE_COUNT (   client,
  num_samples,
  max_samples,
  packet_name 
)

#include <validation.h>

Value:
do { \
if ((num_samples) <= 0 || (num_samples) > (max_samples)) { \
disconnect_client_for_bad_data((client), "%s invalid sample count: %d (max %d)", (packet_name), (num_samples), \
(max_samples)); \
return; \
} \
} while (0)

Validate that audio sample count is within acceptable bounds. Disconnects client and returns if sample count is invalid.

Definition at line 98 of file util/validation.h.

99 { \
100 if ((num_samples) <= 0 || (num_samples) > (max_samples)) { \
101 disconnect_client_for_bad_data((client), "%s invalid sample count: %d (max %d)", (packet_name), (num_samples), \
102 (max_samples)); \
103 return; \
104 } \
105 } while (0)

◆ VALIDATE_AUDIO_STREAM_ENABLED

#define VALIDATE_AUDIO_STREAM_ENABLED (   client,
  packet_name 
)

#include <validation.h>

Value:
do { \
if (!atomic_load_bool(&(client)->is_sending_audio)) { \
disconnect_client_for_bad_data((client), "%s received before audio stream enabled", (packet_name)); \
return; \
} \
} while (0)
bool atomic_load_bool(atomic_t *a)
Atomically load a boolean value.
Definition atomic.c:169

Validate that audio stream is enabled for this client. Disconnects client and returns if audio stream is not enabled.

Definition at line 86 of file util/validation.h.

87 { \
88 if (!atomic_load_bool(&(client)->is_sending_audio)) { \
89 disconnect_client_for_bad_data((client), "%s received before audio stream enabled", (packet_name)); \
90 return; \
91 } \
92 } while (0)

◆ VALIDATE_CAPABILITY_FLAGS

#define VALIDATE_CAPABILITY_FLAGS (   client,
  flags,
  valid_mask,
  packet_name 
)

#include <validation.h>

Value:
do { \
if (((flags) & (valid_mask)) == 0) { \
disconnect_client_for_bad_data((client), "%s no valid capability flags set (flags=0x%x, valid=0x%x)", \
(packet_name), (unsigned)(flags), (unsigned)(valid_mask)); \
return; \
} \
} while (0)

Validate that at least one capability flag is set. Disconnects client and returns if all flags are zero or invalid.

Definition at line 178 of file util/validation.h.

179 { \
180 if (((flags) & (valid_mask)) == 0) { \
181 disconnect_client_for_bad_data((client), "%s no valid capability flags set (flags=0x%x, valid=0x%x)", \
182 (packet_name), (unsigned)(flags), (unsigned)(valid_mask)); \
183 return; \
184 } \
185 } while (0)

◆ VALIDATE_EXACT_SIZE

#define VALIDATE_EXACT_SIZE (   client,
  len,
  expected_size,
  packet_name 
)

#include <validation.h>

Value:
do { \
if ((len) != (expected_size)) { \
disconnect_client_for_bad_data((client), "%s payload size mismatch (len=%zu, expected=%zu)", (packet_name), \
(len), (expected_size)); \
return; \
} \
} while (0)

Validate that payload size is exactly the expected size. Disconnects client and returns if len != expected_size.

Definition at line 73 of file util/validation.h.

74 { \
75 if ((len) != (expected_size)) { \
76 disconnect_client_for_bad_data((client), "%s payload size mismatch (len=%zu, expected=%zu)", (packet_name), \
77 (len), (expected_size)); \
78 return; \
79 } \
80 } while (0)

◆ VALIDATE_FLAGS_MASK

#define VALIDATE_FLAGS_MASK (   client,
  flags,
  valid_mask,
  packet_name 
)

#include <validation.h>

Value:
do { \
if (((flags) & ~(valid_mask)) != 0) { \
disconnect_client_for_bad_data((client), "%s unknown flags set (flags=0x%x, valid=0x%x)", (packet_name), \
(unsigned)(flags), (unsigned)(valid_mask)); \
return; \
} \
} while (0)

Validate that only known/valid flags are set. Disconnects client and returns if unknown flags are present.

Definition at line 191 of file util/validation.h.

192 { \
193 if (((flags) & ~(valid_mask)) != 0) { \
194 disconnect_client_for_bad_data((client), "%s unknown flags set (flags=0x%x, valid=0x%x)", (packet_name), \
195 (unsigned)(flags), (unsigned)(valid_mask)); \
196 return; \
197 } \
198 } while (0)

◆ VALIDATE_MIN_SIZE

#define VALIDATE_MIN_SIZE (   client,
  len,
  min_size,
  packet_name 
)

#include <validation.h>

Value:
do { \
if ((len) < (min_size)) { \
disconnect_client_for_bad_data((client), "%s payload too small (len=%zu, min=%zu)", (packet_name), (len), \
(min_size)); \
return; \
} \
} while (0)

Validate that payload size is at least the minimum required. Disconnects client and returns if len < min_size.

Definition at line 60 of file util/validation.h.

61 { \
62 if ((len) < (min_size)) { \
63 disconnect_client_for_bad_data((client), "%s payload too small (len=%zu, min=%zu)", (packet_name), (len), \
64 (min_size)); \
65 return; \
66 } \
67 } while (0)

◆ VALIDATE_NONZERO

#define VALIDATE_NONZERO (   client,
  value,
  value_name,
  packet_name 
)

#include <validation.h>

Value:
do { \
if ((value) == 0) { \
disconnect_client_for_bad_data((client), "%s %s cannot be zero", (packet_name), (value_name)); \
return; \
} \
} while (0)

Validate that a numeric value is non-zero (for dimensions, counts, etc). Disconnects client and returns if value is zero.

Definition at line 153 of file util/validation.h.

154 { \
155 if ((value) == 0) { \
156 disconnect_client_for_bad_data((client), "%s %s cannot be zero", (packet_name), (value_name)); \
157 return; \
158 } \
159 } while (0)

◆ VALIDATE_NOTNULL_DATA

#define VALIDATE_NOTNULL_DATA (   client,
  data,
  packet_name 
)

#include <validation.h>

Value:
do { \
if (!(data)) { \
disconnect_client_for_bad_data((client), "%s payload missing", (packet_name)); \
return; \
} \
} while (0)

Validate that payload data pointer is not NULL. Disconnects client and returns if data is NULL.

Definition at line 48 of file util/validation.h.

49 { \
50 if (!(data)) { \
51 disconnect_client_for_bad_data((client), "%s payload missing", (packet_name)); \
52 return; \
53 } \
54 } while (0)

◆ VALIDATE_PACKET_NOT_NULL

#define VALIDATE_PACKET_NOT_NULL (   client,
  data,
  packet_name 
)

#include <validation.h>

Value:
({ \
int _validation_failed = 0; \
if (!(data)) { \
disconnect_client_for_bad_data((client), packet_name " payload missing"); \
_validation_failed = 1; \
} \
_validation_failed; \
})

Validate packet payload pointer is not NULL. Returns true if validation failed (data is NULL), false if valid.

Definition at line 204 of file util/validation.h.

205 { \
206 int _validation_failed = 0; \
207 if (!(data)) { \
208 disconnect_client_for_bad_data((client), packet_name " payload missing"); \
209 _validation_failed = 1; \
210 } \
211 _validation_failed; \
212 })

◆ VALIDATE_PACKET_SIZE

#define VALIDATE_PACKET_SIZE (   client,
  data,
  len,
  expected_size,
  packet_name 
)

#include <validation.h>

Value:
do { \
if (!(data)) { \
disconnect_client_for_bad_data((client), packet_name " payload missing"); \
return; \
} \
if ((len) != (expected_size)) { \
disconnect_client_for_bad_data((client), packet_name " payload size %zu (expected %zu)", (len), \
(expected_size)); \
return; \
} \
} while (0)

Validate packet payload size and presence. Checks if data pointer is non-NULL and payload matches expected size.

Definition at line 136 of file util/validation.h.

137 { \
138 if (!(data)) { \
139 disconnect_client_for_bad_data((client), packet_name " payload missing"); \
140 return; \
141 } \
142 if ((len) != (expected_size)) { \
143 disconnect_client_for_bad_data((client), packet_name " payload size %zu (expected %zu)", (len), \
144 (expected_size)); \
145 return; \
146 } \
147 } while (0)

◆ VALIDATE_RANGE

#define VALIDATE_RANGE (   client,
  value,
  min_val,
  max_val,
  value_name,
  packet_name 
)

#include <validation.h>

Value:
do { \
if ((value) < (min_val) || (value) > (max_val)) { \
disconnect_client_for_bad_data((client), "%s %s out of range: %u (valid: %u-%u)", (packet_name), (value_name), \
(unsigned)(value), (unsigned)(min_val), (unsigned)(max_val)); \
return; \
} \
} while (0)

Validate that a numeric value is within a specified range (inclusive). Disconnects client and returns if value is outside range.

Definition at line 165 of file util/validation.h.

166 { \
167 if ((value) < (min_val) || (value) > (max_val)) { \
168 disconnect_client_for_bad_data((client), "%s %s out of range: %u (valid: %u-%u)", (packet_name), (value_name), \
169 (unsigned)(value), (unsigned)(min_val), (unsigned)(max_val)); \
170 return; \
171 } \
172 } while (0)

◆ VALIDATE_RESOURCE_INITIALIZED

#define VALIDATE_RESOURCE_INITIALIZED (   client,
  resource,
  resource_name 
)

#include <validation.h>

Value:
do { \
if (!(resource)) { \
disconnect_client_for_bad_data((client), "%s not initialized", (resource_name)); \
return; \
} \
} while (0)

Validate that a required resource/buffer is initialized. Disconnects client and returns if resource is NULL.

Definition at line 124 of file util/validation.h.

125 { \
126 if (!(resource)) { \
127 disconnect_client_for_bad_data((client), "%s not initialized", (resource_name)); \
128 return; \
129 } \
130 } while (0)

Typedef Documentation

◆ client_info_t

typedef struct client_info client_info_t

#include <validation.h>

Definition at line 39 of file util/validation.h.

Function Documentation

◆ disconnect_client_for_bad_data()

void disconnect_client_for_bad_data ( client_info_t *  client,
const char *  format,
  ... 
)

#include <validation.h>

Definition at line 156 of file server/protocol.c.

156 {
157 if (!client) {
158 return;
159 }
160
161 protocol_cleanup_thread_locals();
162
163 // Atomically set the disconnect flag and get the old value
164 // This ensures only one thread proceeds to disconnect (the first one)
165 bool already_requested = atomic_exchange_bool(&client->protocol_disconnect_requested, true);
166 if (already_requested) {
167 return; // Already disconnecting, don't do it again
168 }
169
170 char reason[BUFFER_SIZE_SMALL] = {0};
171 if (format) {
172 va_list args;
173 va_start(args, format);
174 safe_vsnprintf(reason, sizeof(reason), format, args);
175 va_end(args);
176 } else {
177 SAFE_STRNCPY(reason, "Protocol violation", sizeof(reason));
178 }
179
180 const char *reason_str = reason[0] != '\0' ? reason : "Protocol violation";
181 const char *client_id = client->client_id;
182
183 socket_t socket_snapshot = INVALID_SOCKET_VALUE;
184 const crypto_context_t *crypto_ctx = NULL;
185 acip_transport_t *transport_snapshot = NULL;
186
188 if (client->socket != INVALID_SOCKET_VALUE) {
189 socket_snapshot = client->socket;
190 if (client->crypto_initialized) {
192 }
193 }
195
196 // Get transport reference for WebSocket clients
197 mutex_lock(&client->send_mutex);
198 transport_snapshot = client->transport;
199 mutex_unlock(&client->send_mutex);
200
201 // NOTE: Disconnecting a client due to the client's own bad behavior isn't an
202 // error for us, it's desired behavior for us, so we simply warn and do not
203 // have a need for asciichat_errno here.
204 log_warn("Disconnecting client %u due to protocol violation: %s", client_id, reason_str);
205
206 if (socket_snapshot != INVALID_SOCKET_VALUE) {
207 // Protect socket writes with send_mutex to prevent race with send_thread.
208 // This receive_thread and send_thread both write to same socket.
209 mutex_lock(&client->send_mutex);
210
211 asciichat_error_t log_result =
212 log_network_message(socket_snapshot, (const struct crypto_context_t *)crypto_ctx, LOG_ERROR,
213 REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT, "Protocol violation: %s", reason_str);
214 if (log_result != ASCIICHAT_OK) {
215 log_warn("Failed to send remote log to client %u: %s", client_id, asciichat_error_string(log_result));
216 }
217
218 asciichat_error_t send_result = packet_send_error(socket_snapshot, crypto_ctx, ERROR_NETWORK_PROTOCOL, reason_str);
219 if (send_result != ASCIICHAT_OK) {
220 log_warn("Failed to send error packet to client %u: %s", client_id, asciichat_error_string(send_result));
221 }
222
223 mutex_unlock(&client->send_mutex);
224 } else if (transport_snapshot) {
225 // For WebSocket clients, try to send error via transport
226 log_debug("Sending error to WebSocket client %u via transport", client_id);
227 acip_send_error(transport_snapshot, ERROR_NETWORK_PROTOCOL, reason_str);
228 }
229
230 APP_CALLBACK_VOID(platform_pump_events);
232
233 log_debug("Setting active=false in disconnect_client_for_bad_data (client_id=%u, reason=%s)", client_id, reason_str);
234 atomic_store_bool(&client->active, false);
235 atomic_store_bool(&client->shutting_down, true);
236 atomic_store_bool(&client->send_thread_running, false);
239
240 if (client->audio_queue) {
242 }
243
245 if (client->socket != INVALID_SOCKET_VALUE) {
246 socket_shutdown(client->socket, 2);
247 socket_close(client->socket);
249 }
251}
#define APP_CALLBACK_VOID(callback_name)
void atomic_store_bool(atomic_t *a, bool value)
Atomically store a boolean value.
Definition atomic.c:177
bool atomic_exchange_bool(atomic_t *a, bool new_value)
Atomically exchange a boolean and return the old value.
Definition atomic.c:303
const crypto_context_t * crypto_handshake_get_context(const crypto_handshake_context_t *ctx)
Get the crypto context for encryption/decryption.
#define BUFFER_SIZE_SMALL
Small buffer size (256 bytes)
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:81
@ ASCIICHAT_OK
Definition error_codes.h:51
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
asciichat_error_t log_network_message(socket_t sockfd, const struct crypto_context_t *crypto_ctx, log_level_t level, remote_log_direction_t direction, const char *fmt,...)
Send a formatted log message over the network.
Definition log/log.c:1550
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
@ REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT
Definition network/log.h:22
void packet_queue_stop(packet_queue_t *queue)
Destroy a packet queue and free all resources.
Definition queue.c:616
int socket_shutdown(socket_t sock, int how)
Shutdown socket I/O.
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
int safe_vsnprintf(char *buffer, size_t buffer_size, const char *format, va_list ap)
Safe formatted string printing with va_list.
Definition system.c:199
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:278
int socket_close(socket_t sock)
Close a socket.
void platform_sleep_ms(unsigned int ms)
Sleep for a specified number of milliseconds.
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
int socket_t
action_args_t args
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 acip_send_error(acip_transport_t *transport, uint32_t error_code, const char *message)
Send error message packet.
Definition send.c:301
Transport instance structure.
Definition transport.h:214
crypto_handshake_context_t crypto_handshake_ctx
char client_id[MAX_CLIENT_ID_LEN]
Cryptographic context structure.
#define LOG_ERROR
Definition types.h:42

References acip_send_error(), client_info::active, APP_CALLBACK_VOID, args, ASCIICHAT_OK, atomic_exchange_bool(), atomic_store_bool(), client_info::audio_queue, client_info::audio_render_thread_running, BUFFER_SIZE_SMALL, client_info::client_id, client_info::client_state_mutex, client_info::crypto_handshake_ctx, crypto_handshake_get_context(), client_info::crypto_initialized, ERROR_NETWORK_PROTOCOL, INVALID_SOCKET_VALUE, log_debug, LOG_ERROR, log_network_message(), log_warn, mutex_lock, mutex_unlock, packet_queue_stop(), packet_send_error(), platform_sleep_ms(), client_info::protocol_disconnect_requested, REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT, SAFE_STRNCPY, safe_vsnprintf(), client_info::send_mutex, client_info::send_thread_running, client_info::shutting_down, client_info::socket, socket_close(), socket_shutdown(), client_info::transport, and client_info::video_render_thread_running.

Referenced by handle_audio_batch_packet(), handle_audio_opus_batch_packet(), handle_audio_opus_packet(), handle_client_join_packet(), handle_image_frame_h265_packet(), handle_image_frame_packet(), handle_protocol_version_packet(), handle_remote_log_packet_from_client(), and process_decrypted_packet().