ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
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(&(client)->is_sending_audio)) { \
disconnect_client_for_bad_data((client), "%s received before audio stream enabled", (packet_name)); \
return; \
} \
} while (0)

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(&(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 157 of file server/protocol.c.

157 {
158 if (!client) {
159 return;
160 }
161
162 protocol_cleanup_thread_locals();
163
164 bool already_requested = atomic_exchange(&client->protocol_disconnect_requested, true);
165 if (already_requested) {
166 return;
167 }
168
169 char reason[256] = {0};
170 if (format) {
171 va_list args;
172 va_start(args, format);
173 (void)vsnprintf(reason, sizeof(reason), format, args);
174 va_end(args);
175 } else {
176 SAFE_STRNCPY(reason, "Protocol violation", sizeof(reason));
177 }
178
179 const char *reason_str = reason[0] != '\0' ? reason : "Protocol violation";
180 uint32_t client_id = atomic_load(&client->client_id);
181
182 socket_t socket_snapshot = INVALID_SOCKET_VALUE;
183 const crypto_context_t *crypto_ctx = NULL;
184
186 if (client->socket != INVALID_SOCKET_VALUE) {
187 socket_snapshot = client->socket;
188 if (client->crypto_initialized) {
190 }
191 }
193
194 // NOTE: Disconnecting a client due to the client's own bad behavior isn't an
195 // error for us, it's desired behavior for us, so we simply warn and do not
196 // have a need for asciichat_errno here.
197 log_warn("Disconnecting client %u due to protocol violation: %s", client_id, reason_str);
198
199 if (socket_snapshot != INVALID_SOCKET_VALUE) {
200 // CRITICAL: Protect socket writes with send_mutex to prevent race with send_thread
201 // This receive_thread and send_thread both write to same socket
202 mutex_lock(&client->send_mutex);
203
204 asciichat_error_t log_result =
205 log_network_message(socket_snapshot, (const struct crypto_context_t *)crypto_ctx, LOG_ERROR,
206 REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT, "Protocol violation: %s", reason_str);
207 if (log_result != ASCIICHAT_OK) {
208 log_warn("Failed to send remote log to client %u: %s", client_id, asciichat_error_string(log_result));
209 }
210
211 asciichat_error_t send_result = packet_send_error(socket_snapshot, crypto_ctx, ERROR_NETWORK_PROTOCOL, reason_str);
212 if (send_result != ASCIICHAT_OK) {
213 log_warn("Failed to send error packet to client %u: %s", client_id, asciichat_error_string(send_result));
214 }
215
216 mutex_unlock(&client->send_mutex);
217 }
218
220
221 atomic_store(&client->active, false);
222 atomic_store(&client->shutting_down, true);
223 atomic_store(&client->send_thread_running, false);
224 atomic_store(&client->video_render_thread_running, false);
225 atomic_store(&client->audio_render_thread_running, false);
226
227 if (client->audio_queue) {
229 }
230
232 if (client->socket != INVALID_SOCKET_VALUE) {
233 socket_shutdown(client->socket, 2);
234 socket_close(client->socket);
236 }
238}
const crypto_context_t * crypto_handshake_get_context(const crypto_handshake_context_t *ctx)
Get the crypto context for encryption/decryption.
unsigned int uint32_t
Definition common.h:58
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:73
@ ASCIICHAT_OK
Definition error_codes.h:48
#define log_warn(...)
Log a WARN message.
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.
@ REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT
@ LOG_ERROR
Definition log/logging.h:64
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:807
void packet_queue_shutdown(packet_queue_t *queue)
Signal queue shutdown (causes dequeue to return NULL)
int socket_shutdown(socket_t sock, int how)
Shutdown socket I/O.
int socket_t
Socket handle type (POSIX: int)
Definition socket.h:50
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
Definition mutex.h:140
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:52
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)
Definition mutex.h:175
atomic_bool video_render_thread_running
atomic_uint client_id
crypto_handshake_context_t crypto_handshake_ctx
atomic_bool protocol_disconnect_requested
mutex_t client_state_mutex
atomic_bool audio_render_thread_running
packet_queue_t * audio_queue
atomic_bool shutting_down
atomic_bool active
atomic_bool send_thread_running
Cryptographic context structure.

References client_info::active, ASCIICHAT_OK, client_info::audio_queue, client_info::audio_render_thread_running, 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_ERROR, log_network_message(), log_warn, mutex_lock, mutex_unlock, packet_queue_shutdown(), packet_send_error(), platform_sleep_ms(), client_info::protocol_disconnect_requested, REMOTE_LOG_DIRECTION_SERVER_TO_CLIENT, SAFE_STRNCPY, client_info::send_mutex, client_info::send_thread_running, client_info::shutting_down, client_info::socket, socket_close(), socket_shutdown(), and client_info::video_render_thread_running.

Referenced by process_decrypted_packet().