ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
tcp/transport.c
Go to the documentation of this file.
1
13#include "network/packet.h"
14#include "network/network.h"
15#include "log/logging.h"
16#include "platform/socket.h"
17#include "crypto/crypto.h"
18#include "buffer_pool.h"
19#include "util/endian.h"
20#include "network/crc32.h"
21#include <string.h>
22
30
31// =============================================================================
32// Helper Functions
33// =============================================================================
34
38static asciichat_error_t tcp_send_all(socket_t sockfd, const void *data, size_t len) {
39 const uint8_t *ptr = (const uint8_t *)data;
40 size_t remaining = len;
41
42 while (remaining > 0) {
43 ssize_t sent = socket_send(sockfd, ptr, remaining, 0);
44 if (sent < 0) {
45 return SET_ERRNO_SYS(ERROR_NETWORK, "Socket send failed (tried to send %zu bytes, %zu remaining)", len,
46 remaining);
47 }
48 if (sent == 0) {
49 return SET_ERRNO(ERROR_NETWORK, "Socket closed (tried to send %zu bytes, %zu remaining)", len, remaining);
50 }
51 ptr += sent;
52 remaining -= (size_t)sent;
53 }
54
55 return ASCIICHAT_OK;
56}
57
58// =============================================================================
59// TCP Transport Methods
60// =============================================================================
61
62static asciichat_error_t tcp_send(acip_transport_t *transport, const void *data, size_t len) {
64
65 if (!tcp->is_connected) {
66 return SET_ERRNO(ERROR_NETWORK, "TCP transport not connected");
67 }
68
69 // Data already has packet header from send.c, so we need to extract the type
70 // to determine if this is a handshake packet (which should NOT be encrypted)
71 if (len < sizeof(packet_header_t)) {
72 return SET_ERRNO(ERROR_NETWORK, "Packet too small: %zu < %zu", len, sizeof(packet_header_t));
73 }
74
75 // Extract packet type from header
76 const packet_header_t *header = (const packet_header_t *)data;
77 uint16_t packet_type = NET_TO_HOST_U16(header->type);
78
79 // Check if encryption is needed
80 bool should_encrypt = false;
81 if (transport->crypto_ctx && crypto_is_ready(transport->crypto_ctx)) {
82 // Handshake packets are ALWAYS sent unencrypted
83 if (!packet_is_handshake_type((packet_type_t)packet_type)) {
84 should_encrypt = true;
85 }
86 }
87
88 // If no encryption needed, send raw data
89 if (!should_encrypt) {
90 return tcp_send_all(tcp->sockfd, data, len);
91 }
92
93 // Encrypt the entire packet (header + payload)
94 size_t ciphertext_size = len + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE;
95 uint8_t *ciphertext = buffer_pool_alloc(NULL, ciphertext_size);
96 if (!ciphertext) {
97 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate ciphertext buffer");
98 }
99
100 size_t ciphertext_len;
101 crypto_result_t result =
102 crypto_encrypt(transport->crypto_ctx, data, len, ciphertext, ciphertext_size, &ciphertext_len);
103 if (result != CRYPTO_OK) {
104 buffer_pool_free(NULL, ciphertext, ciphertext_size);
105 return SET_ERRNO(ERROR_CRYPTO, "Failed to encrypt packet: %s", crypto_result_to_string(result));
106 }
107
108 // Build PACKET_TYPE_ENCRYPTED header
109 packet_header_t encrypted_header;
110 encrypted_header.magic = HOST_TO_NET_U32(PACKET_MAGIC);
111 encrypted_header.type = HOST_TO_NET_U16(PACKET_TYPE_ENCRYPTED);
112 encrypted_header.length = HOST_TO_NET_U32((uint32_t)ciphertext_len);
113 encrypted_header.crc32 = HOST_TO_NET_U32(asciichat_crc32(ciphertext, ciphertext_len));
114 encrypted_header.client_id = 0;
115
116 // Send encrypted packet: header + ciphertext
117 asciichat_error_t send_result = ASCIICHAT_OK;
118 send_result = tcp_send_all(tcp->sockfd, &encrypted_header, sizeof(encrypted_header));
119 if (send_result == ASCIICHAT_OK) {
120 send_result = tcp_send_all(tcp->sockfd, ciphertext, ciphertext_len);
121 }
122
123 buffer_pool_free(NULL, ciphertext, ciphertext_size);
124
125 log_debug_every(LOG_RATE_SLOW, "Sent encrypted packet (original type %d as PACKET_TYPE_ENCRYPTED)", packet_type);
126 return send_result;
127}
128
129static asciichat_error_t tcp_recv(acip_transport_t *transport, void **buffer, size_t *out_len,
130 void **out_allocated_buffer) {
132
133 if (!tcp->is_connected) {
134 return SET_ERRNO(ERROR_NETWORK, "TCP transport not connected");
135 }
136
137 // Use secure packet receive with envelope
138 packet_envelope_t envelope;
139 bool enforce_encryption = (transport->crypto_ctx != NULL);
140 packet_recv_result_t result =
141 receive_packet_secure(tcp->sockfd, transport->crypto_ctx, enforce_encryption, &envelope);
142
143 if (result != PACKET_RECV_SUCCESS) {
144 if (result == PACKET_RECV_EOF) {
145 return SET_ERRNO(ERROR_NETWORK, "Connection closed");
146 } else if (result == PACKET_RECV_SECURITY_VIOLATION) {
147 return SET_ERRNO(ERROR_CRYPTO, "Security violation");
148 } else {
149 return SET_ERRNO(ERROR_NETWORK, "Failed to receive packet");
150 }
151 }
152
153 *buffer = envelope.data;
154 *out_len = envelope.len;
155 *out_allocated_buffer = envelope.allocated_buffer;
156
157 return ASCIICHAT_OK;
158}
159
160static asciichat_error_t tcp_close(acip_transport_t *transport) {
162
163 if (!tcp->is_connected) {
164 return ASCIICHAT_OK; // Already closed
165 }
166
167 // Note: We do NOT close the socket - caller owns it
168 // We just mark ourselves as disconnected
169 tcp->is_connected = false;
170
171 log_debug("TCP transport marked as disconnected (socket not closed)");
172 return ASCIICHAT_OK;
173}
174
175static acip_transport_type_t tcp_get_type(acip_transport_t *transport) {
176 (void)transport;
177 return ACIP_TRANSPORT_TCP;
178}
179
180static socket_t tcp_get_socket(acip_transport_t *transport) {
182 return tcp->sockfd;
183}
184
185static bool tcp_is_connected(acip_transport_t *transport) {
187 return tcp->is_connected;
188}
189
190// =============================================================================
191// TCP Transport Method Table
192// =============================================================================
193
194static const acip_transport_methods_t tcp_methods = {
195 .send = tcp_send,
196 .recv = tcp_recv,
197 .close = tcp_close,
198 .get_type = tcp_get_type,
199 .get_socket = tcp_get_socket,
200 .is_connected = tcp_is_connected,
201 .destroy_impl = NULL, // No custom cleanup needed
202};
203
204// =============================================================================
205// TCP Transport Creation
206// =============================================================================
207
209 if (sockfd == INVALID_SOCKET_VALUE) {
210 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket descriptor");
211 return NULL;
212 }
213
214 // Allocate transport structure
216 if (!transport) {
217 SET_ERRNO(ERROR_MEMORY, "Failed to allocate TCP transport");
218 return NULL;
219 }
220
221 // Allocate TCP-specific data
223 if (!tcp_data) {
224 SAFE_FREE(transport);
225 SET_ERRNO(ERROR_MEMORY, "Failed to allocate TCP transport data");
226 return NULL;
227 }
228
229 // Initialize TCP data
230 tcp_data->sockfd = sockfd;
231 tcp_data->is_connected = true;
232
233 // Initialize transport
234 transport->methods = &tcp_methods;
235 transport->crypto_ctx = crypto_ctx;
236 transport->impl_data = tcp_data;
237
238 log_debug("Created TCP transport for socket %d (crypto: %s)", sockfd, crypto_ctx ? "enabled" : "disabled");
239
240 return transport;
241}
242
243// =============================================================================
244// Transport Destroy (shared implementation)
245// =============================================================================
246
248 if (!transport) {
249 return;
250 }
251
252 // Close if still connected
253 if (transport->methods && transport->methods->close && transport->methods->is_connected &&
254 transport->methods->is_connected(transport)) {
255 transport->methods->close(transport);
256 }
257
258 // Call custom destroy implementation if provided
259 if (transport->methods && transport->methods->destroy_impl) {
260 transport->methods->destroy_impl(transport);
261 }
262
263 // Free implementation data
264 if (transport->impl_data) {
265 SAFE_FREE(transport->impl_data);
266 }
267
268 // Free transport structure
269 SAFE_FREE(transport);
270
271 log_debug("Destroyed ACIP transport");
272}
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
Hardware-Accelerated CRC32 Checksum Computation.
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
Definition endian.h:101
#define HOST_TO_NET_U32(val)
Definition endian.h:71
#define NET_TO_HOST_U16(val)
Definition endian.h:116
void buffer_pool_free(buffer_pool_t *pool, 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:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
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.
@ CRYPTO_OK
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ERROR_NETWORK
Definition error_codes.h:69
@ ERROR_MEMORY
Definition error_codes.h:53
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_CRYPTO
Definition error_codes.h:88
@ ERROR_INVALID_PARAM
#define LOG_RATE_SLOW
Log rate limit: 10 seconds (10,000,000 microseconds)
Definition log_rates.h:35
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
#define log_debug(...)
Log a DEBUG message.
uint32_t magic
Magic number (PACKET_MAGIC) for packet validation.
Definition packet.h:492
uint32_t client_id
Client ID (0 = server, >0 = client identifier)
Definition packet.h:500
uint32_t length
Payload data length in bytes (0 for header-only packets)
Definition packet.h:496
void * allocated_buffer
Buffer that needs to be freed by caller (may be NULL if not allocated)
Definition packet.h:990
size_t len
Length of payload data in bytes.
Definition packet.h:986
void * data
Packet payload data (decrypted and decompressed if applicable)
Definition packet.h:984
packet_recv_result_t receive_packet_secure(socket_t sockfd, void *crypto_ctx, bool enforce_encryption, packet_envelope_t *envelope)
Receive a packet with decryption and decompression support.
Definition packet.c:568
uint16_t type
Packet type (packet_type_t enumeration)
Definition packet.h:494
uint32_t crc32
CRC32 checksum of payload data (0 if length == 0)
Definition packet.h:498
packet_recv_result_t
Packet reception result codes.
Definition packet.h:1003
@ PACKET_RECV_EOF
Connection closed (EOF)
Definition packet.h:1007
@ PACKET_RECV_SECURITY_VIOLATION
Encryption policy violation (e.g., unencrypted packet when encryption required)
Definition packet.h:1011
@ PACKET_RECV_SUCCESS
Packet received successfully.
Definition packet.h:1005
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:281
#define PACKET_MAGIC
Packet magic number (0xDEADBEEF)
Definition packet.h:251
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion)
Definition packet.h:333
int socket_t
Socket handle type (POSIX: int)
Definition socket.h:50
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:52
ssize_t socket_send(socket_t sock, const void *buf, size_t len, int flags)
Send data on a socket.
#define asciichat_crc32(data, len)
Main CRC32 dispatcher macro - use this in application code.
Definition crc32.h:144
📝 Logging API with multiple log levels and terminal output control
🌐 Core network I/O operations with timeout support
Packet protocol implementation with encryption and compression support.
Cross-platform socket interface for ascii-chat.
Transport method table (virtual function table)
Definition transport.h:81
asciichat_error_t(* send)(acip_transport_t *transport, const void *data, size_t len)
Send data through this transport.
Definition transport.h:95
bool(* is_connected)(acip_transport_t *transport)
Check if transport is connected.
Definition transport.h:149
asciichat_error_t(* close)(acip_transport_t *transport)
Close this transport.
Definition transport.h:122
void(* destroy_impl)(acip_transport_t *transport)
Custom destroy implementation (optional)
Definition transport.h:160
Transport instance structure.
Definition transport.h:169
void * impl_data
Transport-specific state.
Definition transport.h:172
const acip_transport_methods_t * methods
Method table (virtual functions)
Definition transport.h:170
crypto_context_t * crypto_ctx
Optional encryption context.
Definition transport.h:171
Cryptographic context structure.
Packet envelope containing received packet data.
Definition packet.h:980
Network packet header structure.
Definition packet.h:490
TCP transport implementation data.
socket_t sockfd
Socket descriptor (NOT owned - don't close)
bool is_connected
Connection state.
acip_transport_t * acip_tcp_transport_create(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:59
@ ACIP_TRANSPORT_TCP
Raw TCP socket.
Definition transport.h:60