ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
tcp/transport.c
Go to the documentation of this file.
1
15#include <ascii-chat/log/log.h>
22#include <string.h>
23
32
33// =============================================================================
34// Helper Functions
35// =============================================================================
36
40static asciichat_error_t tcp_send_all(socket_t sockfd, const void *data, size_t len) {
41 const uint8_t *ptr = (const uint8_t *)data;
42 size_t remaining = len;
43 size_t total_sent = 0;
44 uint64_t send_start_ns = time_get_ns();
45
46 log_debug("★ TCP_SEND_ALL: sockfd=%d, len=%zu", sockfd, len);
47
48 while (remaining > 0) {
49 uint64_t iteration_start_ns = time_get_ns();
50 ssize_t sent = socket_send(sockfd, ptr, remaining, 0);
51 uint64_t iteration_ns = time_elapsed_ns(iteration_start_ns, time_get_ns());
52
53 if (iteration_ns > 100 * NS_PER_MS_INT) {
54 log_warn("★ TCP_SEND_ALL: SLOW SEND - took %.1fms to send %zu bytes (may indicate full buffer)",
55 (double)iteration_ns / 1e6, remaining);
56 }
57
58 if (sent < 0) {
59 log_error("★ TCP_SEND_ALL: socket_send FAILED at offset %zu/%zu, errno=%d (%s)", total_sent, len, errno,
62 "Socket send failed: %s (tried to send %zu bytes, %zu remaining, already sent %zu)",
63 SAFE_STRERROR(errno), len, remaining, total_sent);
64 }
65 if (sent == 0) {
66 log_error("★ TCP_SEND_ALL: socket closed at offset %zu/%zu", total_sent, len);
67 return SET_ERRNO(ERROR_NETWORK, "Socket closed (tried to send %zu bytes, %zu remaining, already sent %zu)", len,
68 remaining, total_sent);
69 }
70 ptr += sent;
71 remaining -= (size_t)sent;
72 total_sent += (size_t)sent;
73 log_debug("★ TCP_SEND_ALL: sent %zd bytes, total=%zu/%zu, remaining=%zu", sent, total_sent, len, remaining);
74 }
75
76 uint64_t total_send_ns = time_elapsed_ns(send_start_ns, time_get_ns());
77 if (total_send_ns > 500 * NS_PER_MS_INT) {
78 log_warn("★ TCP_SEND_ALL: TOTAL SEND TIME %.1fms for %zu bytes (%.1f MB/s)", (double)total_send_ns / 1e6, len,
79 (double)(len * NS_PER_SEC_INT) / (double)total_send_ns / 1e6);
80 }
81
82 log_debug("★ TCP_SEND_ALL: SUCCESS - sent all %zu bytes in %.1fms", len, (double)total_send_ns / 1e6);
83 return ASCIICHAT_OK;
84}
85
86// =============================================================================
87// TCP Transport Methods
88// =============================================================================
89
90static asciichat_error_t tcp_send(acip_transport_t *transport, const void *data, size_t len) {
92
93 // Lock send mutex to prevent concurrent sends from corrupting packet data
94 mutex_lock(&tcp->send_mutex);
95
96 log_dev("[TCP_SEND_STATE] Entry: transport=%p, sockfd=%d, len=%zu, is_connected=%s, data=%p", (void *)transport,
97 tcp->sockfd, len, tcp->is_connected ? "true" : "false", data);
98
99 if (!tcp->is_connected) {
100 log_error("[TCP_SEND_STATE] ❌ DISCONNECTED: Cannot send - transport marked disconnected! sockfd=%d, len=%zu",
101 tcp->sockfd, len);
102 mutex_unlock(&tcp->send_mutex);
103 return SET_ERRNO(ERROR_NETWORK, "TCP transport not connected");
104 }
105
106 // Data already has packet header from send.c, so we need to extract the type
107 // to determine if this is a handshake packet (which should NOT be encrypted)
108 if (len < sizeof(packet_header_t)) {
109 log_error("[TCP_SEND_STATE] ❌ PACKET_TOO_SMALL: len=%zu < header_size=%zu", len, sizeof(packet_header_t));
110 mutex_unlock(&tcp->send_mutex);
111 return SET_ERRNO(ERROR_NETWORK, "Packet too small: %zu < %zu", len, sizeof(packet_header_t));
112 }
113
114 // Extract packet type from header
115 const packet_header_t *header = (const packet_header_t *)data;
116 uint16_t packet_type = NET_TO_HOST_U16(header->type);
117 log_dev("[TCP_SEND_STATE] 📦 PACKET_TYPE: type=%d (0x%04x), len=%zu, magic_check=%p", packet_type, packet_type, len,
118 (void *)header);
119
120 // Check if encryption is needed
121 bool should_encrypt = false;
122 bool crypto_ready =
123 (transport->crypto_ctx && transport->crypto_ctx->encrypt_data && crypto_is_ready(transport->crypto_ctx));
124 bool is_handshake = packet_is_handshake_type((packet_type_t)packet_type);
125
126 if (crypto_ready) {
127 // Handshake packets are ALWAYS sent unencrypted
128 if (!is_handshake) {
129 should_encrypt = true;
130 }
131 }
132
133 log_dev("[TCP_SEND_STATE] 🔐 CRYPTO_CHECK: crypto_ready=%s, is_handshake=%s, will_encrypt=%s",
134 crypto_ready ? "yes" : "no", is_handshake ? "yes" : "no", should_encrypt ? "yes" : "no");
135
136 // If no encryption needed, send raw data
137 if (!should_encrypt) {
138 log_dev("[TCP_SEND_STATE] 📤 PLAINTEXT_SEND: sockfd=%d, len=%zu bytes (packet_type=%d)", tcp->sockfd, len,
139 packet_type);
140 asciichat_error_t result = tcp_send_all(tcp->sockfd, data, len);
141 if (result == ASCIICHAT_OK) {
142 log_dev("[TCP_SEND_STATE] ✅ PLAINTEXT_SEND_OK: sockfd=%d, %zu bytes sent successfully", tcp->sockfd, len);
143 } else {
144 log_error("[TCP_SEND_STATE] ❌ PLAINTEXT_SEND_FAILED: sockfd=%d, len=%zu - error code set", tcp->sockfd, len);
145 }
146 mutex_unlock(&tcp->send_mutex);
147 return result;
148 }
149
150 // Encrypt the entire packet (header + payload)
151 size_t ciphertext_size = len + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE;
152 log_info("[TCP_SEND_STATE] 🔐 ENCRYPT_START: original_len=%zu, with_nonce=%zu, will_allocate=%zu", len,
153 CRYPTO_NONCE_SIZE, ciphertext_size);
154
155 uint8_t *ciphertext = buffer_pool_alloc(NULL, ciphertext_size);
156 if (!ciphertext) {
157 log_error("[TCP_SEND_STATE] ❌ ENCRYPT_ALLOC_FAILED: needed %zu bytes", ciphertext_size);
158 mutex_unlock(&tcp->send_mutex);
159 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate ciphertext buffer");
160 }
161
162 log_debug("[TCP_SEND_STATE] 🔐 ENCRYPT_BUFFER_ALLOCATED: ciphertext=%p, capacity=%zu", (void *)ciphertext,
163 ciphertext_size);
164
165 size_t ciphertext_len;
166 crypto_result_t result =
167 crypto_encrypt(transport->crypto_ctx, data, len, ciphertext, ciphertext_size, &ciphertext_len);
168 log_info("[TCP_SEND_STATE] 🔐 ENCRYPT_RESULT: code=%d, input=%zu, output=%zu", result, len, ciphertext_len);
169
170 if (result != CRYPTO_OK) {
171 log_error("[TCP_SEND_STATE] ❌ ENCRYPT_FAILED: %s (code=%d)", crypto_result_to_string(result), result);
172 buffer_pool_free(NULL, ciphertext, ciphertext_size);
173 mutex_unlock(&tcp->send_mutex);
174 return SET_ERRNO(ERROR_CRYPTO, "Failed to encrypt packet: %s", crypto_result_to_string(result));
175 }
176
177 // Build PACKET_TYPE_ENCRYPTED header
178 packet_header_t encrypted_header;
179 encrypted_header.magic = HOST_TO_NET_U64(PACKET_MAGIC);
180 encrypted_header.type = HOST_TO_NET_U16(PACKET_TYPE_ENCRYPTED);
181 encrypted_header.length = HOST_TO_NET_U32((uint32_t)ciphertext_len);
182 encrypted_header.crc32 = HOST_TO_NET_U32(asciichat_crc32(ciphertext, ciphertext_len));
183 encrypted_header.client_id = 0;
184
185 log_info("[TCP_SEND_STATE] 🔐 ENCRYPTED_HEADER_BUILT: magic=0x%llx, type=%d, len=%zu, crc=0x%x",
186 (unsigned long long)HOST_TO_NET_U64(PACKET_MAGIC), PACKET_TYPE_ENCRYPTED, ciphertext_len,
187 encrypted_header.crc32);
188
189 // Build combined packet: header + ciphertext (must be atomic to prevent TCP stream desynchronization)
190 size_t combined_size = sizeof(encrypted_header) + ciphertext_len;
191 uint8_t *combined = SAFE_MALLOC(combined_size, uint8_t *);
192 if (!combined) {
193 log_error("[TCP_SEND_STATE] ❌ ENCRYPTED_ALLOC_FAILED: needed %zu bytes for header+payload", combined_size);
194 buffer_pool_free(NULL, ciphertext, ciphertext_size);
195 mutex_unlock(&tcp->send_mutex);
196 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate combined packet buffer");
197 }
198
199 // Copy header and payload into single buffer
200 memcpy(combined, &encrypted_header, sizeof(encrypted_header));
201 memcpy(combined + sizeof(encrypted_header), ciphertext, ciphertext_len);
202
203 // Send as single atomic operation to prevent stream desynchronization
204 log_info("[TCP_SEND_STATE] 📤 ENCRYPTED_SEND: sockfd=%d, total=%zu (header=%zu + payload=%zu)", tcp->sockfd,
205 combined_size, sizeof(encrypted_header), ciphertext_len);
206 asciichat_error_t send_result = tcp_send_all(tcp->sockfd, combined, combined_size);
207
208 if (send_result == ASCIICHAT_OK) {
209 log_info("[TCP_SEND_STATE] ✅ ENCRYPTED_SEND_OK: sockfd=%d, total=%zu bytes sent atomically", tcp->sockfd,
210 combined_size);
211 } else {
212 log_error("[TCP_SEND_STATE] ❌ ENCRYPTED_SEND_FAILED: sockfd=%d, tried %zu bytes", tcp->sockfd, combined_size);
213 }
214
215 SAFE_FREE(combined);
216
217 buffer_pool_free(NULL, ciphertext, ciphertext_size);
218
219 log_debug_every(LOG_RATE_SLOW, "Sent encrypted packet (original type %d as PACKET_TYPE_ENCRYPTED)", packet_type);
220 mutex_unlock(&tcp->send_mutex);
221 return send_result;
222}
223
224static asciichat_error_t tcp_recv(acip_transport_t *transport, void **buffer, size_t *out_len,
225 void **out_allocated_buffer) {
227
228 log_debug("[TCP_RECV_STATE] Entry: transport=%p, sockfd=%d, is_connected=%s", (void *)transport, tcp->sockfd,
229 tcp->is_connected ? "true" : "false");
230
231 if (!tcp->is_connected) {
232 log_error("[TCP_RECV_STATE] ❌ DISCONNECTED: Cannot recv - transport marked disconnected! sockfd=%d", tcp->sockfd);
233 return SET_ERRNO(ERROR_NETWORK, "TCP transport not connected");
234 }
235
236 // Use secure packet receive with envelope
237 packet_envelope_t envelope;
238 bool enforce_encryption = (transport->crypto_ctx != NULL && transport->crypto_ctx->encrypt_data);
239 log_debug("[TCP_RECV_STATE] 📥 RECV_WAITING: sockfd=%d, enforce_encryption=%s", tcp->sockfd,
240 enforce_encryption ? "yes" : "no");
241
242 uint64_t receive_timeout_ns =
243 transport->receive_timeout_ns != 0 ? transport->receive_timeout_ns : RECV_TIMEOUT * NS_PER_SEC_INT;
244 packet_recv_result_t result = receive_packet_secure_with_timeout(tcp->sockfd, transport->crypto_ctx,
245 enforce_encryption, &envelope, receive_timeout_ns);
246 log_debug("[TCP_RECV_STATE] 📥 RECV_RESULT: code=%d (0=success, -1=eof, -2=error, -3=security), data_size=%zu",
247 result, result == PACKET_RECV_SUCCESS ? envelope.len : 0);
248
249 if (result != PACKET_RECV_SUCCESS) {
250 if (result == PACKET_RECV_EOF) {
251 tcp->is_connected = false;
252 log_warn("[TCP_RECV_STATE] ⚠️ RECV_EOF: Connection closed by remote (sockfd=%d)", tcp->sockfd);
253 return SET_ERRNO(ERROR_NETWORK, "Connection closed");
254 } else if (result == PACKET_RECV_SECURITY_VIOLATION) {
255 log_error("[TCP_RECV_STATE] ❌ RECV_SECURITY_VIOLATION: Crypto error on sockfd=%d", tcp->sockfd);
256 return SET_ERRNO(ERROR_CRYPTO, "Security violation");
257 } else {
258 asciichat_error_context_t error_context;
259 if (HAS_ERRNO(&error_context) && error_context.code == ERROR_NETWORK_TIMEOUT) {
261 }
262 // A non-timeout receive error is terminal for this stream. Retaining a
263 // connected state after the peer has gone away causes callers to retry a
264 // socket that can no longer deliver packets.
265 tcp->is_connected = false;
266 log_error("[TCP_RECV_STATE] ❌ RECV_FAILED: result=%d on sockfd=%d", result, tcp->sockfd);
267 return SET_ERRNO(ERROR_NETWORK, "Failed to receive packet");
268 }
269 }
270
271 *buffer = envelope.data;
272 *out_len = envelope.len;
273 *out_allocated_buffer = envelope.allocated_buffer;
274
275 /*
276 * `receive_packet_secure()` returns two different buffer shapes:
277 * - plaintext packets: [header][payload]
278 * - encrypted packets: [payload only] with the decrypted header still
279 * present at the start of the allocated buffer.
280 *
281 * The ACIP client code and transport-agnostic packet helpers expect a
282 * normalized packet buffer that always begins with the ACIP header, so
283 * copy encrypted packets into a compact [header][payload] buffer here.
284 */
285 if (envelope.data != envelope.allocated_buffer) {
286 size_t packet_size = sizeof(packet_header_t) + envelope.len;
287 uint8_t *normalized = buffer_pool_alloc(NULL, packet_size);
288 if (!normalized) {
289 buffer_pool_free(NULL, envelope.allocated_buffer, envelope.allocated_size);
290 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate normalized packet buffer");
291 }
292
293 memcpy(normalized, envelope.allocated_buffer, packet_size);
294 buffer_pool_free(NULL, envelope.allocated_buffer, envelope.allocated_size);
295
296 *buffer = normalized;
297 *out_len = packet_size;
298 *out_allocated_buffer = normalized;
299 }
300
301 if (envelope.len >= sizeof(packet_header_t)) {
302 const packet_header_t *hdr = (const packet_header_t *)envelope.data;
303 uint16_t pkt_type = NET_TO_HOST_U16(hdr->type);
304 log_debug("[TCP_RECV_STATE] ✅ RECV_OK: sockfd=%d, packet_type=%d (0x%04x), len=%zu", tcp->sockfd, pkt_type,
305 pkt_type, envelope.len);
306 } else {
307 log_warn("[TCP_RECV_STATE] ⚠️ RECV_OK_SMALL_PACKET: sockfd=%d, len=%zu (< header)", tcp->sockfd, envelope.len);
308 }
309
310 return ASCIICHAT_OK;
311}
312
313static asciichat_error_t tcp_close(acip_transport_t *transport) {
315
316 log_warn("[TCP_CLOSE_STATE] 🔴 CLOSE_REQUESTED: transport=%p, sockfd=%d, was_connected=%s", (void *)transport,
317 tcp->sockfd, tcp->is_connected ? "yes" : "no");
318
319 if (!tcp->is_connected) {
320 log_debug("[TCP_CLOSE_STATE] ✅ CLOSE_IDEMPOTENT: transport already disconnected (sockfd=%d)", tcp->sockfd);
321 return ASCIICHAT_OK; // Already closed
322 }
323
324 // Note: We do NOT close the socket - caller owns it
325 // We just mark ourselves as disconnected
326 log_warn("[TCP_CLOSE_STATE] 🔴 MARKING_DISCONNECTED: transport=%p, sockfd=%d (socket NOT closed - caller owns it)",
327 (void *)transport, tcp->sockfd);
328 tcp->is_connected = false;
329
330 log_warn("[TCP_CLOSE_STATE] ✅ CLOSE_COMPLETE: TCP transport marked as disconnected. transport=%p, sockfd=%d",
331 (void *)transport, tcp->sockfd);
332 return ASCIICHAT_OK;
333}
334
335static acip_transport_type_t tcp_get_type(acip_transport_t *transport) {
336 (void)transport;
337 return ACIP_TRANSPORT_TCP;
338}
339
340static socket_t tcp_get_socket(acip_transport_t *transport) {
342 return tcp->sockfd;
343}
344
345static bool tcp_is_connected(acip_transport_t *transport) {
347 bool result = tcp->is_connected;
348 log_debug_every(LOG_RATE_FAST, "[TCP_STATE_CHECK] tcp_is_connected(): transport=%p, sockfd=%d, result=%s",
349 (void *)transport, tcp->sockfd, result ? "true" : "false");
350 return result;
351}
352
353static bool tcp_has_pending_data(acip_transport_t *transport) {
355 if (!tcp || !socket_is_valid(tcp->sockfd)) {
356 return false;
357 }
358
359 fd_set readfds;
360 struct timeval tv = {0};
361 socket_fd_zero(&readfds);
362 socket_fd_set(tcp->sockfd, &readfds);
363
364 int select_result = socket_select(tcp->sockfd, &readfds, NULL, NULL, &tv);
365 return select_result > 0 && socket_fd_isset(tcp->sockfd, &readfds);
366}
367
368static void tcp_destroy_impl(acip_transport_t *transport) {
370 if (tcp) {
371 // Destroy the send mutex
372 int result = mutex_destroy(&tcp->send_mutex);
373 if (result != 0) {
374 log_warn("[TCP_DESTROY] ⚠️ MUTEX_DESTROY_FAILED: result=%d", result);
375 }
376 }
377}
378
379// =============================================================================
380// TCP Transport Method Table
381// =============================================================================
382
383static const acip_transport_methods_t tcp_methods = {
384 .send = tcp_send,
385 .recv = tcp_recv,
386 .close = tcp_close,
387 .get_type = tcp_get_type,
388 .get_socket = tcp_get_socket,
389 .is_connected = tcp_is_connected,
390 .has_pending_data = tcp_has_pending_data,
391 .destroy_impl = tcp_destroy_impl, // Destroy send_mutex
392};
393
394// =============================================================================
395// TCP Transport Creation
396// =============================================================================
397
399 if (!name) {
400 SET_ERRNO(ERROR_INVALID_STATE, "Transport name is required");
401 return NULL;
402 }
403
404 if (sockfd == INVALID_SOCKET_VALUE) {
405 log_error("[TCP_CREATE_STATE] ❌ INVALID_SOCKET: sockfd=%d", sockfd);
406 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket descriptor");
407 return NULL;
408 }
409
410 log_info("[TCP_CREATE_STATE] 🟢 CREATE_START: sockfd=%d, crypto=%s", sockfd, crypto_ctx ? "yes" : "no");
411
412 // Allocate transport structure
414 if (!transport) {
415 log_error("[TCP_CREATE_STATE] ❌ ALLOC_TRANSPORT_FAILED: size=%zu", sizeof(acip_transport_t));
416 SET_ERRNO(ERROR_MEMORY, "Failed to allocate TCP transport");
417 return NULL;
418 }
419
420 log_debug("[TCP_CREATE_STATE] 🟢 TRANSPORT_ALLOCATED: transport=%p", (void *)transport);
421
422 // Allocate TCP-specific data
424 if (!tcp_data) {
425 log_error("[TCP_CREATE_STATE] ❌ ALLOC_TCP_DATA_FAILED: size=%zu", sizeof(tcp_transport_data_t));
426 SAFE_FREE(transport);
427 SET_ERRNO(ERROR_MEMORY, "Failed to allocate TCP transport data");
428 return NULL;
429 }
430
431 log_debug("[TCP_CREATE_STATE] 🟢 TCP_DATA_ALLOCATED: tcp_data=%p", (void *)tcp_data);
432
433 // Initialize TCP data
434 tcp_data->sockfd = sockfd;
435 tcp_data->is_connected = true;
436
437 // Initialize send mutex to protect concurrent sends from multiple threads
438 int mutex_result = mutex_init(&tcp_data->send_mutex, "tcp_send");
439 if (mutex_result != 0) {
440 log_error("[TCP_CREATE_STATE] ❌ MUTEX_INIT_FAILED: result=%d", mutex_result);
441 SAFE_FREE(tcp_data);
442 SAFE_FREE(transport);
443 SET_ERRNO(ERROR_MEMORY, "Failed to initialize send mutex");
444 return NULL;
445 }
446
447 log_info("[TCP_CREATE_STATE] 🟢 TCP_DATA_INITIALIZED: sockfd=%d, is_connected=true, send_mutex=initialized", sockfd);
448
449 // Enable TCP_NODELAY to disable Nagle's algorithm
450 // This ensures small packets are sent immediately instead of being buffered
451 int nodelay = 1;
452 if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (const char *)&nodelay, sizeof(nodelay)) < 0) {
453 log_warn("[TCP_CREATE_STATE] ⚠️ TCP_NODELAY_FAILED: sockfd=%d, %s", sockfd, SAFE_STRERROR(errno));
454 // Continue anyway - this is not fatal
455 } else {
456 log_debug("[TCP_CREATE_STATE] 🟢 TCP_NODELAY_SET: sockfd=%d", sockfd);
457 }
458
459 // Initialize transport
460 transport->methods = &tcp_methods;
461 transport->crypto_ctx = crypto_ctx;
462 transport->impl_data = tcp_data;
463
464 // Register transport and impl_data before logging so named replacements work
465 NAMED_REGISTER_TRANSPORT(transport, name, NULL);
466 NAMED_REGISTER(tcp_data, "impl", "tcp_impl", "0x%tx", (uintptr_t)(const void *)(transport));
467
468 log_info("[TCP_CREATE_STATE] ✅ CREATE_COMPLETE: transport=%p, sockfd=%d, is_connected=true, crypto=%s",
469 (void *)transport, sockfd, crypto_ctx ? "enabled" : "disabled");
470
471 return transport;
472}
473
474// =============================================================================
475// Transport Destroy (shared implementation)
476// =============================================================================
477
479 if (!transport) {
480 log_debug("[TRANSPORT_DESTROY] ⚠️ NULL_TRANSPORT: nothing to destroy");
481 return;
482 }
483
484 log_warn("[TRANSPORT_DESTROY] 🔴 DESTROY_START: transport=%p, impl_data=%p", (void *)transport, transport->impl_data);
485
486 // Get type before we destroy for logging
487 acip_transport_type_t type = 0;
488 if (transport->methods && transport->methods->get_type) {
489 type = transport->methods->get_type(transport);
490 log_info("[TRANSPORT_DESTROY] 📋 TRANSPORT_TYPE: type=%d (1=TCP, 2=WebSocket, 3=WebRTC, ...)", type);
491 }
492
493 // Close if still connected
494 if (transport->methods && transport->methods->close && transport->methods->is_connected &&
495 transport->methods->is_connected(transport)) {
496 log_warn("[TRANSPORT_DESTROY] 🔴 STILL_CONNECTED: calling close() first (type=%d)", type);
497 asciichat_error_t close_result = transport->methods->close(transport);
498 log_info("[TRANSPORT_DESTROY] 🔴 CLOSE_CALLED: result=%d", close_result != ASCIICHAT_OK ? -1 : 0);
499 } else {
500 log_debug("[TRANSPORT_DESTROY] ✅ ALREADY_CLOSED: skipping close (type=%d)", type);
501 }
502
503 // Call custom destroy implementation if provided
504 if (transport->methods && transport->methods->destroy_impl) {
505 log_info("[TRANSPORT_DESTROY] 🔧 CALLING_DESTROY_IMPL: type=%d", type);
506 transport->methods->destroy_impl(transport);
507 log_info("[TRANSPORT_DESTROY] ✅ DESTROY_IMPL_COMPLETE: type=%d", type);
508 } else {
509 log_debug("[TRANSPORT_DESTROY] ⏭️ NO_CUSTOM_DESTROY: type=%d (using generic cleanup)", type);
510 }
511
512 // Free implementation data
513 if (transport->impl_data) {
514 log_debug("[TRANSPORT_DESTROY] 🗑️ FREEING_IMPL_DATA: %p", transport->impl_data);
515 NAMED_UNREGISTER(transport->impl_data);
516 SAFE_FREE(transport->impl_data);
517 log_debug("[TRANSPORT_DESTROY] ✅ IMPL_DATA_FREED");
518 }
519
520 // Free transport structure
521 log_debug("[TRANSPORT_DESTROY] 🗑️ FREEING_TRANSPORT: %p", (void *)transport);
522 SAFE_FREE(transport);
523
524 log_warn("[TRANSPORT_DESTROY] ✅ DESTROY_COMPLETE: type=%d", type);
525}
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
Hardware-Accelerated CRC32 Checksum Computation.
Named object registry for debugging — log identifiable resource names.
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
Definition endian.h:96
#define HOST_TO_NET_U32(val)
Definition endian.h:66
#define HOST_TO_NET_U64(val)
Definition endian.h:213
#define NET_TO_HOST_U16(val)
Definition endian.h:111
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 short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define SAFE_STRERROR(errnum)
Definition common.h:465
unsigned long long uint64_t
Definition common.h:59
unsigned char uint8_t
Definition common.h:56
crypto_result_t crypto_encrypt(crypto_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext_out, size_t ciphertext_out_size, size_t *ciphertext_len_out)
Encrypt data using XSalsa20-Poly1305.
#define CRYPTO_NONCE_SIZE
Nonce size (XSalsa20)
const char * crypto_result_to_string(crypto_result_t result)
Convert crypto result to human-readable string.
crypto_result_t
Cryptographic operation result codes.
#define CRYPTO_MAC_SIZE
MAC size (Poly1305)
bool crypto_is_ready(const crypto_context_t *ctx)
Check if key exchange is complete and ready for encryption.
#define NAMED_REGISTER_TRANSPORT(transport, name, parent_ptr)
Register a transport with automatic format specifier.
#define NAMED_REGISTER(ptr, name, type, fmt, parent_ptr)
Register any pointer with base name, type, format spec, and location (auto-suffix)
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context, returning the error code.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
#define HAS_ERRNO(var)
Check if an error occurred and get full context.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_INVALID_STATE
@ ERROR_NETWORK
Definition error_codes.h:77
@ ERROR_MEMORY
Definition error_codes.h:56
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_CRYPTO
Definition error_codes.h:96
@ ERROR_INVALID_PARAM
@ ERROR_NETWORK_TIMEOUT
Definition error_codes.h:80
#define LOG_RATE_FAST
Log rate limit: 1 second (1,000,000 microseconds)
Definition log_rates.h:26
#define LOG_RATE_SLOW
Log rate limit: 10 seconds (10,000,000 microseconds)
Definition log_rates.h:35
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
uint64_t time_get_ns(void)
Get current monotonic time in nanoseconds.
Definition util/time.c:108
#define NS_PER_SEC_INT
Definition time.h:157
#define NS_PER_MS_INT
Definition time.h:156
uint64_t time_elapsed_ns(uint64_t start_ns, uint64_t end_ns)
Calculate elapsed time with wraparound safety.
Definition util/time.c:150
#define RECV_TIMEOUT
Receive timeout in seconds (1 second)
packet_recv_result_t
Packet reception result codes.
Definition packet.h:1152
@ PACKET_RECV_EOF
Connection closed (EOF)
Definition packet.h:1156
@ PACKET_RECV_SECURITY_VIOLATION
Encryption policy violation (e.g., unencrypted packet when encryption required)
Definition packet.h:1160
@ PACKET_RECV_SUCCESS
Packet received successfully.
Definition packet.h:1154
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
#define PACKET_MAGIC
Packet magic number (alias for MAGIC_PACKET_VALID)
Definition packet.h:255
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion)
Definition packet.h:333
void socket_fd_zero(fd_set *set)
Clear an fd_set.
bool socket_is_valid(socket_t sock)
Check if a socket handle is valid.
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:278
void socket_fd_set(socket_t sock, fd_set *set)
Add a socket to an fd_set.
int mutex_init(mutex_t *mutex, const char *name)
Initialize a mutex with a name.
Definition threading.c:16
ssize_t socket_send(socket_t sock, const void *buf, size_t len, int flags)
Send data on a socket.
int socket_select(socket_t max_fd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
Select sockets for I/O readiness.
int errno
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
int socket_fd_isset(socket_t sock, fd_set *set)
Check if a socket is in an fd_set.
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
Definition threading.c:22
#define asciichat_crc32(data, len)
Main CRC32 dispatcher macro - use this in application code.
Definition crc32.h:144
int socket_t
📝 Logging API with multiple log levels and terminal output control
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
Definition log/log.h:702
🌐 Core network I/O operations with timeout support
packet_recv_result_t receive_packet_secure_with_timeout(socket_t sockfd, void *crypto_ctx, bool enforce_encryption, packet_envelope_t *envelope, uint64_t timeout_ns)
Definition packet.c:575
Cross-platform socket interface for ascii-chat.
Transport method table (virtual function table)
Definition transport.h:115
asciichat_error_t(* send)(acip_transport_t *transport, const void *data, size_t len)
Send data through this transport.
Definition transport.h:129
bool(* is_connected)(acip_transport_t *transport)
Check if transport is connected.
Definition transport.h:183
asciichat_error_t(* close)(acip_transport_t *transport)
Close this transport.
Definition transport.h:156
void(* destroy_impl)(acip_transport_t *transport)
Custom destroy implementation (optional)
Definition transport.h:205
acip_transport_type_t(* get_type)(acip_transport_t *transport)
Get transport type.
Definition transport.h:164
Transport instance structure.
Definition transport.h:214
void * impl_data
Transport-specific state.
Definition transport.h:218
const acip_transport_methods_t * methods
Method table (virtual functions)
Definition transport.h:215
Error context structure.
asciichat_error_t code
Error code (asciichat_error_t enum value)
Cryptographic context structure.
Mutex type (POSIX: pthread_mutex_t with debug tracking)
Packet envelope containing received packet data.
Definition packet.h:1129
void * allocated_buffer
Buffer that needs to be freed by caller (may be NULL if not allocated)
Definition packet.h:1139
size_t allocated_size
Size of allocated buffer in bytes.
Definition packet.h:1141
size_t len
Length of payload data in bytes.
Definition packet.h:1135
void * data
Packet payload data (decrypted and decompressed if applicable)
Definition packet.h:1133
Network packet header structure.
Definition packet.h:598
uint32_t client_id
Client ID (0 = server, >0 = client identifier)
Definition packet.h:608
uint32_t length
Payload data length in bytes (0 for header-only packets)
Definition packet.h:604
uint64_t magic
Magic number (PACKET_MAGIC = 0xA5C11C4A1 "ASCIICHAT" in hex) for packet validation.
Definition packet.h:600
uint16_t type
Packet type (packet_type_t enumeration)
Definition packet.h:602
uint32_t crc32
CRC32 checksum of payload data (0 if length == 0)
Definition packet.h:606
TCP transport implementation data.
socket_t sockfd
Socket descriptor (NOT owned - don't close)
bool is_connected
Connection state.
mutex_t send_mutex
Mutex to protect concurrent sends (multiple threads may send packets)
acip_transport_t * acip_tcp_transport_create(const char *name, socket_t sockfd, crypto_context_t *crypto_ctx)
Create TCP transport from existing socket.
void acip_transport_destroy(acip_transport_t *transport)
Destroy transport and free all resources.
Transport abstraction layer for ACIP protocol.
acip_transport_type_t
Transport type enumeration.
Definition transport.h:93
@ ACIP_TRANSPORT_TCP
Raw TCP socket.
Definition transport.h:94