12#include <ascii-chat/network/acip/transport.h>
13#include <ascii-chat/network/packet.h>
14#include <ascii-chat/network/network.h>
15#include <ascii-chat/log/logging.h>
16#include <ascii-chat/platform/socket.h>
17#include <ascii-chat/crypto/crypto.h>
18#include <ascii-chat/buffer_pool.h>
19#include <ascii-chat/util/endian.h>
20#include <ascii-chat/network/crc32.h>
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 size_t total_sent = 0;
43 log_debug(
"★ TCP_SEND_ALL: sockfd=%d, len=%zu", sockfd, len);
45 while (remaining > 0) {
46 ssize_t sent = socket_send(sockfd, ptr, remaining, 0);
48 log_error(
"★ TCP_SEND_ALL: socket_send failed at offset %zu/%zu", total_sent, len);
49 return SET_ERRNO_SYS(ERROR_NETWORK,
50 "Socket send failed (tried to send %zu bytes, %zu remaining, already sent %zu)", len,
51 remaining, total_sent);
54 log_error(
"★ TCP_SEND_ALL: socket closed at offset %zu/%zu", total_sent, len);
55 return SET_ERRNO(ERROR_NETWORK,
"Socket closed (tried to send %zu bytes, %zu remaining, already sent %zu)", len,
56 remaining, total_sent);
59 remaining -= (size_t)sent;
60 total_sent += (size_t)sent;
61 log_debug(
"★ TCP_SEND_ALL: sent %zd bytes, total=%zu/%zu, remaining=%zu", sent, total_sent, len, remaining);
64 log_debug(
"★ TCP_SEND_ALL: SUCCESS - sent all %zu bytes", len);
72static asciichat_error_t tcp_send(acip_transport_t *transport,
const void *data,
size_t len) {
75 if (!tcp->is_connected) {
76 return SET_ERRNO(ERROR_NETWORK,
"TCP transport not connected");
81 if (len <
sizeof(packet_header_t)) {
82 return SET_ERRNO(ERROR_NETWORK,
"Packet too small: %zu < %zu", len,
sizeof(packet_header_t));
86 const packet_header_t *header = (
const packet_header_t *)data;
87 uint16_t packet_type = NET_TO_HOST_U16(header->type);
90 bool should_encrypt =
false;
91 if (transport->crypto_ctx && transport->crypto_ctx->encrypt_data &&
crypto_is_ready(transport->crypto_ctx)) {
93 if (!packet_is_handshake_type((packet_type_t)packet_type)) {
94 should_encrypt =
true;
99 if (!should_encrypt) {
100 return tcp_send_all(tcp->sockfd, data, len);
104 size_t ciphertext_size = len + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE;
107 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate ciphertext buffer");
110 size_t ciphertext_len;
111 crypto_result_t result =
112 crypto_encrypt(transport->crypto_ctx, data, len, ciphertext, ciphertext_size, &ciphertext_len);
113 if (result != CRYPTO_OK) {
119 packet_header_t encrypted_header;
120 encrypted_header.magic = HOST_TO_NET_U64(PACKET_MAGIC);
121 encrypted_header.type = HOST_TO_NET_U16(PACKET_TYPE_ENCRYPTED);
122 encrypted_header.length = HOST_TO_NET_U32((uint32_t)ciphertext_len);
123 encrypted_header.crc32 = HOST_TO_NET_U32(asciichat_crc32(ciphertext, ciphertext_len));
124 encrypted_header.client_id = 0;
127 asciichat_error_t send_result = ASCIICHAT_OK;
128 send_result = tcp_send_all(tcp->sockfd, &encrypted_header,
sizeof(encrypted_header));
129 if (send_result == ASCIICHAT_OK) {
130 send_result = tcp_send_all(tcp->sockfd, ciphertext, ciphertext_len);
135 log_debug_every(LOG_RATE_SLOW,
"Sent encrypted packet (original type %d as PACKET_TYPE_ENCRYPTED)", packet_type);
139static asciichat_error_t tcp_recv(acip_transport_t *transport,
void **buffer,
size_t *out_len,
140 void **out_allocated_buffer) {
143 if (!tcp->is_connected) {
144 return SET_ERRNO(ERROR_NETWORK,
"TCP transport not connected");
148 packet_envelope_t envelope;
149 bool enforce_encryption = (transport->crypto_ctx != NULL && transport->crypto_ctx->encrypt_data);
150 packet_recv_result_t result =
153 if (result != PACKET_RECV_SUCCESS) {
154 if (result == PACKET_RECV_EOF) {
155 return SET_ERRNO(ERROR_NETWORK,
"Connection closed");
156 }
else if (result == PACKET_RECV_SECURITY_VIOLATION) {
157 return SET_ERRNO(ERROR_CRYPTO,
"Security violation");
159 return SET_ERRNO(ERROR_NETWORK,
"Failed to receive packet");
163 *buffer = envelope.data;
164 *out_len = envelope.len;
165 *out_allocated_buffer = envelope.allocated_buffer;
170static asciichat_error_t tcp_close(acip_transport_t *transport) {
173 if (!tcp->is_connected) {
179 tcp->is_connected =
false;
181 log_debug(
"TCP transport marked as disconnected (socket not closed)");
185static acip_transport_type_t tcp_get_type(acip_transport_t *transport) {
187 return ACIP_TRANSPORT_TCP;
190static socket_t tcp_get_socket(acip_transport_t *transport) {
195static bool tcp_is_connected(acip_transport_t *transport) {
197 return tcp->is_connected;
204static const acip_transport_methods_t tcp_methods = {
208 .get_type = tcp_get_type,
209 .get_socket = tcp_get_socket,
210 .is_connected = tcp_is_connected,
211 .destroy_impl = NULL,
219 if (sockfd == INVALID_SOCKET_VALUE) {
220 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid socket descriptor");
225 acip_transport_t *transport = SAFE_MALLOC(
sizeof(acip_transport_t), acip_transport_t *);
227 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate TCP transport");
234 SAFE_FREE(transport);
235 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate TCP transport data");
240 tcp_data->
sockfd = sockfd;
246 if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (
const char *)&nodelay,
sizeof(nodelay)) < 0) {
247 log_warn(
"Failed to set TCP_NODELAY on socket %d: %s", sockfd, SAFE_STRERROR(errno));
252 transport->methods = &tcp_methods;
253 transport->crypto_ctx = crypto_ctx;
254 transport->impl_data = tcp_data;
256 log_debug(
"Created TCP transport for socket %d (crypto: %s)", sockfd, crypto_ctx ?
"enabled" :
"disabled");
271 if (transport->methods && transport->methods->close && transport->methods->is_connected &&
272 transport->methods->is_connected(transport)) {
273 transport->methods->close(transport);
277 if (transport->methods && transport->methods->destroy_impl) {
278 transport->methods->destroy_impl(transport);
282 if (transport->impl_data) {
283 SAFE_FREE(transport->impl_data);
287 SAFE_FREE(transport);
289 log_debug(
"Destroyed ACIP transport");
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
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)
const char * crypto_result_to_string(crypto_result_t result)
bool crypto_is_ready(const crypto_context_t *ctx)
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.
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)
void acip_transport_destroy(acip_transport_t *transport)