ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
send.h File Reference

ACIP shared/bidirectional packet sending functions. More...

Go to the source code of this file.

Functions

asciichat_error_t packet_send_via_transport (acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
 Send arbitrary packet via transport (generic packet sender)
 
asciichat_error_t packet_receive_via_transport (acip_transport_t *transport, packet_type_t *type, void **payload, size_t *payload_len, void **alloc_buffer)
 Receive a packet via transport with header parsing and validation.
 
asciichat_error_t acip_send_audio_batch (acip_transport_t *transport, const float *samples, uint32_t num_samples, uint32_t batch_count)
 Send audio batch packet.
 
asciichat_error_t acip_send_audio_opus (acip_transport_t *transport, const void *opus_data, size_t opus_len)
 Send Opus-encoded audio packet.
 
asciichat_error_t acip_send_audio_opus_batch (acip_transport_t *transport, const void *opus_data, size_t opus_len, const uint16_t *frame_sizes, uint32_t frame_count, uint32_t sample_rate, uint32_t frame_duration)
 Send batched Opus-encoded audio frames.
 
asciichat_error_t acip_send_ping (acip_transport_t *transport)
 Send ping packet.
 
asciichat_error_t acip_send_pong (acip_transport_t *transport)
 Send pong packet.
 
asciichat_error_t acip_send_error (acip_transport_t *transport, uint32_t error_code, const char *message)
 Send error message packet.
 
asciichat_error_t acip_send_remote_log (acip_transport_t *transport, uint8_t log_level, uint8_t direction, const char *message)
 Send remote log packet.
 
asciichat_error_t acip_send_session_created (acip_transport_t *transport, const acip_session_created_t *response)
 Send SESSION_CREATED response packet.
 
asciichat_error_t acip_send_session_info (acip_transport_t *transport, const acip_session_info_t *info)
 Send SESSION_INFO response packet.
 
asciichat_error_t acip_send_session_joined (acip_transport_t *transport, const acip_session_joined_t *response)
 Send SESSION_JOINED response packet.
 

Detailed Description

ACIP shared/bidirectional packet sending functions.

Provides bidirectional send functions (used by both client and server):

  • Audio sending (PCM, Opus, batched)
  • Ping/Pong keepalive
  • Error messages and remote logging
  • Low-level packet_send_via_transport() utility

For client-specific sends (image frames, join/leave, capabilities): → See lib/network/acip/client.h

For server-specific sends (ASCII frames, state updates, console control): → See lib/network/acip/server.h

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

Definition in file send.h.

Function Documentation

◆ acip_send_audio_batch()

asciichat_error_t acip_send_audio_batch ( acip_transport_t *  transport,
const float *  samples,
uint32_t  num_samples,
uint32_t  batch_count 
)

Send audio batch packet.

Parameters
transportTransport instance
samplesAudio samples (float array)
num_samplesNumber of samples
batch_countNumber of batches aggregated
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 180 of file send.c.

181 {
182 if (!transport || !samples || num_samples == 0) {
183 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
184 }
185
186 // Build batch header
188 header.batch_count = batch_count;
189 header.total_samples = (uint32_t)num_samples;
191 header.channels = 1;
192
193 // Calculate total size (header + float samples)
194 size_t samples_size = num_samples * sizeof(float);
195 size_t total_size = sizeof(header) + samples_size;
196
197 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
198 if (!buffer) {
199 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
200 }
201
202 // Copy header and samples
203 memcpy(buffer, &header, sizeof(header));
204 memcpy(buffer + sizeof(header), samples, samples_size);
205
206 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_AUDIO_BATCH, buffer, total_size, 0);
207
208 buffer_pool_free(NULL, buffer, total_size);
209 return result;
210}
#define AUDIO_SAMPLE_RATE
Audio sample rate (48kHz professional quality, Opus-compatible)
void buffer_pool_free(buffer_pool_t *pool, const void *data, size_t size)
Free a buffer back to the pool (lock-free)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
unsigned int uint32_t
Definition common.h:58
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_MEMORY
Definition error_codes.h:56
@ ERROR_INVALID_PARAM
@ PACKET_TYPE_AUDIO_BATCH
Batched audio packets for efficiency.
Definition packet.h:372
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:41
Audio batch packet structure (Packet Type 28)
Definition packet.h:904
uint32_t channels
Number of audio channels (1=mono, 2=stereo)
Definition packet.h:912
uint32_t batch_count
Number of audio chunks in this batch (usually AUDIO_BATCH_COUNT = 32)
Definition packet.h:906
uint32_t sample_rate
Sample rate in Hz (e.g., 44100, 48000)
Definition packet.h:910
uint32_t total_samples
Total audio samples across all chunks (typically 8192)
Definition packet.h:908

References AUDIO_SAMPLE_RATE, audio_batch_packet_t::batch_count, buffer_pool_alloc(), buffer_pool_free(), audio_batch_packet_t::channels, ERROR_INVALID_PARAM, ERROR_MEMORY, packet_send_via_transport(), PACKET_TYPE_AUDIO_BATCH, audio_batch_packet_t::sample_rate, SET_ERRNO, and audio_batch_packet_t::total_samples.

Referenced by threaded_send_audio_batch_packet().

◆ acip_send_audio_opus()

asciichat_error_t acip_send_audio_opus ( acip_transport_t *  transport,
const void *  opus_data,
size_t  opus_len 
)

Send Opus-encoded audio packet.

Parameters
transportTransport instance
opus_dataOpus-encoded audio data
opus_lenLength of encoded data
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 212 of file send.c.

212 {
213 if (!transport || !opus_data || opus_len == 0) {
214 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
215 }
216
217 // Convert single opus frame to batch format (frame_count=1) with standard parameters
218 // Standard: 48kHz sample rate, 20ms frame duration
219 uint16_t frame_sizes[1] = {(uint16_t)opus_len};
220 return acip_send_audio_opus_batch(transport, opus_data, opus_len, frame_sizes, 1, 48000, 20);
221}
unsigned short uint16_t
Definition common.h:57
asciichat_error_t acip_send_audio_opus_batch(acip_transport_t *transport, const void *opus_data, size_t opus_len, const uint16_t *frame_sizes, uint32_t frame_count, uint32_t sample_rate, uint32_t frame_duration)
Send batched Opus-encoded audio frames.
Definition send.c:223

References acip_send_audio_opus_batch(), ERROR_INVALID_PARAM, and SET_ERRNO.

◆ acip_send_audio_opus_batch()

asciichat_error_t acip_send_audio_opus_batch ( acip_transport_t *  transport,
const void *  opus_data,
size_t  opus_len,
const uint16_t *  frame_sizes,
uint32_t  frame_count,
uint32_t  sample_rate,
uint32_t  frame_duration 
)

Send batched Opus-encoded audio frames.

Parameters
transportTransport instance
opus_dataOpus-encoded data (multiple frames concatenated)
opus_lenTotal length of encoded data
frame_sizesArray of individual frame sizes
frame_countNumber of frames in batch
sample_rateSample rate in Hz
frame_durationDuration per frame in milliseconds
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 223 of file send.c.

225 {
226 if (!transport || !opus_data || !frame_sizes || frame_count == 0) {
227 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
228 }
229
230 // Build batch header (16 bytes)
231 uint8_t header[16];
232 *(uint32_t *)(header + 0) = HOST_TO_NET_U32(sample_rate);
233 *(uint32_t *)(header + 4) = HOST_TO_NET_U32(frame_duration);
234 *(uint32_t *)(header + 8) = HOST_TO_NET_U32(frame_count);
235 *(uint32_t *)(header + 12) = 0; // reserved
236
237 // Calculate sizes array size
238 size_t sizes_len = frame_count * sizeof(uint16_t);
239 size_t total_size = sizeof(header) + sizes_len + opus_len;
240
241 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
242 if (!buffer) {
243 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
244 }
245
246 // Build packet: header + sizes + opus_data
247 memcpy(buffer, header, sizeof(header));
248
249 // Convert frame sizes to network byte order before copying
250 // IMPORTANT: Server expects network byte order and will apply NET_TO_HOST_U16()
251 uint16_t *sizes_buf = (uint16_t *)(buffer + sizeof(header));
252 for (uint32_t i = 0; i < frame_count; i++) {
253 sizes_buf[i] = HOST_TO_NET_U16(frame_sizes[i]);
254 }
255
256 memcpy(buffer + sizeof(header) + sizes_len, opus_data, opus_len);
257
258 log_dev("★ OPUS_BATCH_SEND_DEBUG: header=16, frame_count=%u, sizes_len=%zu, opus_len=%zu, TOTAL=%zu", frame_count,
259 sizes_len, opus_len, total_size);
260
261 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_AUDIO_OPUS_BATCH, buffer, total_size, 0);
262
263 buffer_pool_free(NULL, buffer, total_size);
264 return result;
265}
#define HOST_TO_NET_U16(val)
Definition endian.h:96
#define HOST_TO_NET_U32(val)
Definition endian.h:66
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
@ PACKET_TYPE_AUDIO_OPUS_BATCH
Batched Opus-encoded audio frames.
Definition packet.h:374

References buffer_pool_alloc(), buffer_pool_free(), ERROR_INVALID_PARAM, ERROR_MEMORY, HOST_TO_NET_U16, HOST_TO_NET_U32, log_dev, packet_send_via_transport(), PACKET_TYPE_AUDIO_OPUS_BATCH, and SET_ERRNO.

Referenced by acip_send_audio_opus(), and threaded_send_audio_opus_batch().

◆ acip_send_error()

asciichat_error_t acip_send_error ( acip_transport_t *  transport,
uint32_t  error_code,
const char *  message 
)

Send error message packet.

Parameters
transportTransport instance
error_codeError code from asciichat_error_t
messageError message (will be truncated to MAX_ERROR_MESSAGE_LENGTH)
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 301 of file send.c.

301 {
302 if (!transport) {
303 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
304 }
305
306 // Calculate message length (max MAX_ERROR_MESSAGE_LENGTH)
307 size_t msg_len = 0;
308 if (message) {
309 msg_len = strlen(message);
310 if (msg_len > MAX_ERROR_MESSAGE_LENGTH) {
311 msg_len = MAX_ERROR_MESSAGE_LENGTH;
312 }
313 }
314
315 error_packet_t header;
317 header.message_length = HOST_TO_NET_U32((uint32_t)msg_len);
318
319 // Allocate buffer for header + message
320 size_t total_size = sizeof(header) + msg_len;
321 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
322 if (!buffer) {
323 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
324 }
325
326 // Build packet
327 memcpy(buffer, &header, sizeof(header));
328 if (msg_len > 0) {
329 memcpy(buffer + sizeof(header), message, msg_len);
330 }
331
332 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_ERROR_MESSAGE, buffer, total_size, 0);
333
334 buffer_pool_free(NULL, buffer, total_size);
335 return result;
336}
asciichat_error_t error_code
#define MAX_ERROR_MESSAGE_LENGTH
Maximum error message length (512 bytes)
Definition packet.h:124
@ PACKET_TYPE_ERROR_MESSAGE
Error packet with asciichat_error_t code and human-readable message.
Definition packet.h:352
Error packet structure carrying error code and textual description.
Definition packet.h:727
uint32_t message_length
Length of message payload in bytes (0-512)
Definition packet.h:731
uint32_t error_code
Error code from asciichat_error_t enumeration.
Definition packet.h:729

References buffer_pool_alloc(), buffer_pool_free(), error_packet_t::error_code, error_code, ERROR_INVALID_PARAM, ERROR_MEMORY, HOST_TO_NET_U32, MAX_ERROR_MESSAGE_LENGTH, error_packet_t::message_length, packet_send_via_transport(), PACKET_TYPE_ERROR_MESSAGE, and SET_ERRNO.

Referenced by acds_client_handler(), acds_websocket_client_handler(), and disconnect_client_for_bad_data().

◆ acip_send_ping()

asciichat_error_t acip_send_ping ( acip_transport_t *  transport)

Send ping packet.

Parameters
transportTransport instance
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 271 of file send.c.

271 {
272 if (!transport) {
273 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
274 }
275
276 // Ping has no payload
277 return packet_send_via_transport(transport, PACKET_TYPE_PING, NULL, 0, 0);
278}
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:383

References ERROR_INVALID_PARAM, packet_send_via_transport(), PACKET_TYPE_PING, and SET_ERRNO.

Referenced by discovery_session_process().

◆ acip_send_pong()

asciichat_error_t acip_send_pong ( acip_transport_t *  transport)

Send pong packet.

Parameters
transportTransport instance
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 280 of file send.c.

280 {
281 if (!transport) {
282 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
283 }
284
285 // Pong has no payload
286 return packet_send_via_transport(transport, PACKET_TYPE_PONG, NULL, 0, 0);
287}
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:385

References ERROR_INVALID_PARAM, packet_send_via_transport(), PACKET_TYPE_PONG, and SET_ERRNO.

Referenced by handle_ping_packet().

◆ acip_send_remote_log()

asciichat_error_t acip_send_remote_log ( acip_transport_t *  transport,
uint8_t  log_level,
uint8_t  direction,
const char *  message 
)

Send remote log packet.

Parameters
transportTransport instance
log_levelLog level (0=DEBUG, 1=INFO, 2=WARN, 3=ERROR)
directionDirection hint (0=client->server, 1=server->client)
messageLog message
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 338 of file send.c.

339 {
340 if (!transport || !message) {
341 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or message");
342 }
343
344 size_t msg_len = strlen(message);
345
346 remote_log_packet_t header;
347 header.log_level = log_level;
348 header.direction = direction;
349 header.flags = 0;
350 header.message_length = HOST_TO_NET_U32((uint32_t)msg_len);
351
352 size_t total_size = sizeof(header) + msg_len;
353
354 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
355 if (!buffer) {
356 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer");
357 }
358
359 // Build packet
360 memcpy(buffer, &header, sizeof(header));
361 // NOLINTNEXTLINE(bugprone-not-null-terminated-result) - binary packet data
362 memcpy(buffer + sizeof(header), message, msg_len);
363
364 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_REMOTE_LOG, buffer, total_size, 0);
365
366 buffer_pool_free(NULL, buffer, total_size);
367 return result;
368}
@ PACKET_TYPE_REMOTE_LOG
Bidirectional remote logging packet.
Definition packet.h:354
Remote log packet structure carrying log level and message text.
Definition packet.h:741
uint16_t flags
Additional flags (REMOTE_LOG_FLAG_*)
Definition packet.h:747
uint32_t message_length
Message payload length in bytes (0-512)
Definition packet.h:749
uint8_t log_level
Log level associated with the message (log_level_t cast to uint8_t)
Definition packet.h:743
uint8_t direction
Direction hint so receivers can annotate origin.
Definition packet.h:745

References buffer_pool_alloc(), buffer_pool_free(), remote_log_packet_t::direction, ERROR_INVALID_PARAM, ERROR_MEMORY, remote_log_packet_t::flags, HOST_TO_NET_U32, remote_log_packet_t::log_level, remote_log_packet_t::message_length, packet_send_via_transport(), PACKET_TYPE_REMOTE_LOG, and SET_ERRNO.

◆ acip_send_session_created()

asciichat_error_t acip_send_session_created ( acip_transport_t *  transport,
const acip_session_created_t *  response 
)

Send SESSION_CREATED response packet.

Parameters
transportTransport instance
responseSession created response structure
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 374 of file send.c.

374 {
375 if (!transport || !response) {
376 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or response");
377 }
378
379 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_CREATED, response, sizeof(*response), 0);
380}
@ PACKET_TYPE_ACIP_SESSION_CREATED
Session created response (Discovery Server -> Client)
Definition packet.h:409

References ERROR_INVALID_PARAM, packet_send_via_transport(), PACKET_TYPE_ACIP_SESSION_CREATED, and SET_ERRNO.

◆ acip_send_session_info()

asciichat_error_t acip_send_session_info ( acip_transport_t *  transport,
const acip_session_info_t *  info 
)

Send SESSION_INFO response packet.

Parameters
transportTransport instance
infoSession info structure
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 382 of file send.c.

382 {
383 if (!transport || !info) {
384 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or info");
385 }
386
387 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_INFO, info, sizeof(*info), 0);
388}
@ PACKET_TYPE_ACIP_SESSION_INFO
Session info response (Discovery Server -> Client)
Definition packet.h:413

References ERROR_INVALID_PARAM, packet_send_via_transport(), PACKET_TYPE_ACIP_SESSION_INFO, and SET_ERRNO.

◆ acip_send_session_joined()

asciichat_error_t acip_send_session_joined ( acip_transport_t *  transport,
const acip_session_joined_t *  response 
)

Send SESSION_JOINED response packet.

Parameters
transportTransport instance
responseSession joined response structure
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 390 of file send.c.

390 {
391 if (!transport || !response) {
392 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or response");
393 }
394
395 return packet_send_via_transport(transport, PACKET_TYPE_ACIP_SESSION_JOINED, response, sizeof(*response), 0);
396}
@ PACKET_TYPE_ACIP_SESSION_JOINED
Session joined response (Discovery Server -> Client)
Definition packet.h:417

References ERROR_INVALID_PARAM, packet_send_via_transport(), PACKET_TYPE_ACIP_SESSION_JOINED, and SET_ERRNO.

◆ packet_receive_via_transport()

asciichat_error_t packet_receive_via_transport ( acip_transport_t *  transport,
packet_type_t *  type,
void **  payload,
size_t *  payload_len,
void **  alloc_buffer 
)

Receive a packet via transport with header parsing and validation.

Transport counterpart to packet_receive(). Receives raw bytes via transport, parses the packet header, validates magic/CRC32, and returns the packet type and payload separately.

Parameters
transportTransport instance
[out]typePacket type from header
[out]payloadPointer to payload data within the received buffer
[out]payload_lenPayload length from header
[out]alloc_bufferBuffer to free when done (caller must free with SAFE_FREE)
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 122 of file send.c.

123 {
124 if (!transport || !type || !payload || !payload_len || !alloc_buffer) {
125 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters for packet_receive_via_transport");
126 }
127
128 *type = 0;
129 *payload = NULL;
130 *payload_len = 0;
131 *alloc_buffer = NULL;
132
133 // Receive raw bytes via transport
134 void *recv_buffer = NULL;
135 size_t recv_len = 0;
136 asciichat_error_t result = acip_transport_recv(transport, &recv_buffer, &recv_len, alloc_buffer);
137 if (result != ASCIICHAT_OK) {
138 return result;
139 }
140
141 // Parse packet header
142 if (recv_len < sizeof(packet_header_t)) {
143 log_warn("packet_receive_via_transport: packet too small (%zu bytes, need %zu)", recv_len, sizeof(packet_header_t));
144 if (*alloc_buffer) {
145 buffer_pool_free(NULL, *alloc_buffer, recv_len);
146 *alloc_buffer = NULL;
147 }
148 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Received packet smaller than header");
149 }
150
151 const packet_header_t *header = (const packet_header_t *)recv_buffer;
152 *type = (packet_type_t)NET_TO_HOST_U16(header->type);
153 size_t plen = NET_TO_HOST_U32(header->length);
154
155 if (recv_len < sizeof(packet_header_t) + plen) {
156 log_warn("packet_receive_via_transport: truncated packet (header says %zu payload, got %zu total)", plen, recv_len);
157 if (*alloc_buffer) {
158 buffer_pool_free(NULL, *alloc_buffer, recv_len);
159 *alloc_buffer = NULL;
160 }
161 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Truncated packet payload");
162 }
163
164 if (plen > 0) {
165 *payload = (uint8_t *)recv_buffer + sizeof(packet_header_t);
166 }
167 *payload_len = plen;
168
169 return ASCIICHAT_OK;
170}
#define NET_TO_HOST_U16(val)
Definition endian.h:111
#define NET_TO_HOST_U32(val)
Definition endian.h:81
@ 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
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
Network packet header structure.
Definition packet.h:598
uint32_t length
Payload data length in bytes (0 for header-only packets)
Definition packet.h:604
uint16_t type
Packet type (packet_type_t enumeration)
Definition packet.h:602

References ASCIICHAT_OK, buffer_pool_free(), ERROR_INVALID_PARAM, ERROR_NETWORK_PROTOCOL, packet_header_t::length, log_warn, NET_TO_HOST_U16, NET_TO_HOST_U32, SET_ERRNO, and packet_header_t::type.

Referenced by __attribute__(), client_crypto_handshake(), discovery_session_process(), and discovery_session_start().

◆ packet_send_via_transport()

asciichat_error_t packet_send_via_transport ( acip_transport_t *  transport,
packet_type_t  type,
const void *  payload,
size_t  payload_len,
uint32_t  client_id 
)

Send arbitrary packet via transport (generic packet sender)

Low-level function that builds packet header and sends via transport. Most code should use the type-specific functions (acip_send_ping, etc.), but this is useful for generic wrappers that handle multiple packet types.

Parameters
transportTransport instance
typePacket type
payloadPacket payload (can be NULL if payload_len==0)
payload_lenPayload length
client_idClient ID to include in packet header
Returns
ASCIICHAT_OK on success, error code on failure

Send arbitrary packet via transport (generic packet sender)

Wraps payload in ACIP packet header and sends via transport. Handles CRC32 calculation and network byte order conversion.

Parameters
transportTransport instance
typePacket type
payloadPayload data (may be NULL if payload_len is 0)
payload_lenPayload length
client_idClient ID to include in packet header
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 41 of file send.c.

42 {
43 if (!transport) {
44 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
45 }
46
47// Sanity check: catch suspiciously large packets (likely a bug)
48#define MAX_REASONABLE_PACKET_SIZE (25 * 1024 * 1024) // 25 MB
49 if (payload_len > MAX_REASONABLE_PACKET_SIZE) {
50 log_error("★ PACKET_SIZE_VALIDATION_FAILURE: Attempting to send %zu bytes (%zu MB) with type=%d (0x%04x), exceeds "
51 "max %d bytes (25 MB) - likely a bug in caller's length calculation",
52 payload_len, payload_len / (1024 * 1024), type, type, MAX_REASONABLE_PACKET_SIZE);
53 return SET_ERRNO(ERROR_INVALID_PARAM, "Packet payload too large: %zu bytes (max 25MB)", payload_len);
54 }
55
56 log_dev("★ PACKET_SEND_VIA_TRANSPORT START: type=%d (0x%04x), payload_len=%zu bytes, client_id=%u, transport=%p",
57 type, type, payload_len, client_id, (void *)transport);
58
59 // Build packet header
60 packet_header_t header;
62 header.type = HOST_TO_NET_U16(type);
63 header.length = HOST_TO_NET_U32((uint32_t)payload_len);
64 header.client_id = client_id;
65
66 // Calculate CRC32 if we have payload
67 if (payload && payload_len > 0) {
68 header.crc32 = HOST_TO_NET_U32(asciichat_crc32((const uint8_t *)payload, payload_len));
69 log_dev("★ PACKET_SEND: CRC32=0x%08x calculated for %zu byte payload", header.crc32, payload_len);
70 } else {
71 header.crc32 = 0;
72 log_dev("★ PACKET_SEND: No payload, CRC32=0");
73 }
74
75 log_dev("★ PACKET_SEND: magic=0x%016llx, type=%d, length=%u bytes, client_id=%u", header.magic, type, header.length,
76 header.client_id);
77
78 // Calculate total packet size
79 size_t total_size = sizeof(header) + payload_len;
80 log_dev("★ PACKET_SEND: Header=%zu bytes + Payload=%zu bytes = Total=%zu bytes", sizeof(header), payload_len,
81 total_size);
82
83 // Allocate buffer for complete packet
84 uint8_t *packet = SAFE_MALLOC(total_size, uint8_t *);
85 if (!packet) {
86 log_error("★ PACKET_SEND: Memory allocation FAILED for %zu byte packet buffer", total_size);
87 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate packet buffer");
88 }
89
90 log_dev("★ PACKET_SEND: Packet buffer allocated at %p", (void *)packet);
91
92 // Build complete packet: header + payload
93 memcpy(packet, &header, sizeof(header));
94 if (payload && payload_len > 0) {
95 memcpy(packet + sizeof(header), payload, payload_len);
96 log_dev("★ PACKET_SEND: Payload copied to buffer offset %zu", sizeof(header));
97 }
98
99 // Send via transport (transport handles encryption if crypto_ctx present)
100 // Transport does NOT take ownership - it only copies data to its own buffers
101 log_dev("★ PACKET_SEND: Calling acip_transport_send with %zu total bytes", total_size);
102 asciichat_error_t result = acip_transport_send(transport, packet, total_size);
103
104 if (result == ASCIICHAT_OK) {
105 log_dev("★ PACKET_SEND_VIA_TRANSPORT COMPLETE: SUCCESS - sent %zu bytes (type=%d, client_id=%u)", total_size, type,
106 client_id);
107 } else {
108 log_error("★ PACKET_SEND_VIA_TRANSPORT FAILED: acip_transport_send returned error %d (%s) when sending %zu bytes "
109 "type=%d to client_id=%u",
110 result, asciichat_error_string(result), total_size, type, client_id);
111 }
112
113 // Always free the packet buffer
114 SAFE_FREE(packet);
115 return result;
116}
#define HOST_TO_NET_U64(val)
Definition endian.h:213
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define PACKET_MAGIC
Packet magic number (alias for MAGIC_PACKET_VALID)
Definition packet.h:255
#define asciichat_crc32(data, len)
Main CRC32 dispatcher macro - use this in application code.
Definition crc32.h:144
#define MAX_REASONABLE_PACKET_SIZE
uint32_t client_id
Client ID (0 = server, >0 = client identifier)
Definition packet.h:608
uint64_t magic
Magic number (PACKET_MAGIC = 0xA5C11C4A1 "ASCIICHAT" in hex) for packet validation.
Definition packet.h:600
uint32_t crc32
CRC32 checksum of payload data (0 if length == 0)
Definition packet.h:606

References asciichat_crc32, ASCIICHAT_OK, packet_header_t::client_id, packet_header_t::crc32, ERROR_INVALID_PARAM, ERROR_MEMORY, HOST_TO_NET_U16, HOST_TO_NET_U32, HOST_TO_NET_U64, packet_header_t::length, log_dev, log_error, packet_header_t::magic, MAX_REASONABLE_PACKET_SIZE, PACKET_MAGIC, SAFE_FREE, SAFE_MALLOC, SET_ERRNO, and packet_header_t::type.

Referenced by acip_send_ascii_frame(), acip_send_audio_batch(), acip_send_audio_opus_batch(), acip_send_capabilities(), acip_send_clear_console(), acip_send_client_join(), acip_send_client_leave(), acip_send_error(), acip_send_image_frame(), acip_send_image_frame_h265(), acip_send_ping(), acip_send_pong(), acip_send_protocol_version(), acip_send_remote_log(), acip_send_server_state(), acip_send_session_created(), acip_send_session_info(), acip_send_session_joined(), acip_send_stream_start(), acip_send_stream_stop(), client_crypto_handshake(), client_send_thread_func(), crypto_handshake_client_key_exchange(), crypto_handshake_rekey_complete(), crypto_handshake_rekey_request(), crypto_handshake_rekey_response(), crypto_handshake_server_auth_challenge(), crypto_handshake_server_complete(), crypto_handshake_server_send_parameters(), crypto_handshake_server_start(), discovery_session_become_host(), discovery_session_handle_host_disconnect(), discovery_session_process(), discovery_session_start(), discovery_session_stop(), signaling_relay_ice(), signaling_relay_sdp(), threaded_send_audio_opus(), threaded_send_image_frame_h265(), threaded_send_packet(), and websocket_client_send_packet().