ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
lib/network/acip/client.c
Go to the documentation of this file.
1
13#include <ascii-chat/network/acip/client.h>
14#include <ascii-chat/network/acip/send.h>
15#include <ascii-chat/network/acip/handlers.h>
16#include <ascii-chat/network/packet.h>
17#include <ascii-chat/buffer_pool.h>
18#include <ascii-chat/log/logging.h>
19#include <ascii-chat/common.h>
20#include <ascii-chat/util/endian.h>
21#include <ascii-chat/util/overflow.h>
22#include <string.h>
23
24// =============================================================================
25// Client Receive (from server)
26// =============================================================================
27
28asciichat_error_t acip_client_receive_and_dispatch(acip_transport_t *transport,
29 const acip_client_callbacks_t *callbacks) {
30 if (!transport || !callbacks) {
31 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or callbacks");
32 }
33
34 // Check if transport is connected
35 if (!transport->methods->is_connected(transport)) {
36 return SET_ERRNO(ERROR_NETWORK, "Transport not connected");
37 }
38
39 packet_envelope_t envelope;
40 bool enforce_encryption = (transport->crypto_ctx != NULL);
41
42 // Try to get socket from transport
43 socket_t sock = transport->methods->get_socket(transport);
44
45 if (sock != INVALID_SOCKET_VALUE) {
46 // Socket-based transport (TCP): use receive_packet_secure() for socket I/O + parsing
47 packet_recv_result_t result = receive_packet_secure(sock, transport->crypto_ctx, enforce_encryption, &envelope);
48
49 // Handle receive errors
50 if (result != PACKET_RECV_SUCCESS) {
51 if (result == PACKET_RECV_EOF) {
52 return SET_ERRNO(ERROR_NETWORK, "Connection closed (EOF)");
53 } else if (result == PACKET_RECV_SECURITY_VIOLATION) {
54 return SET_ERRNO(ERROR_CRYPTO, "Security violation: unencrypted packet when encryption required");
55 } else {
56 return SET_ERRNO(ERROR_NETWORK, "Failed to receive packet");
57 }
58 }
59 } else {
60 // Non-socket transport (WebRTC): use transport's recv() method to get complete packet
61 void *packet_data = NULL;
62 void *allocated_buffer = NULL;
63 size_t packet_len = 0;
64
65 asciichat_error_t recv_result = transport->methods->recv(transport, &packet_data, &packet_len, &allocated_buffer);
66 if (recv_result != ASCIICHAT_OK) {
67 return SET_ERRNO(ERROR_NETWORK, "Transport recv() failed");
68 }
69
70 // Parse packet header
71 if (packet_len < sizeof(packet_header_t)) {
72 buffer_pool_free(NULL, allocated_buffer, packet_len);
73 return SET_ERRNO(ERROR_NETWORK, "Packet too small: %zu < %zu", packet_len, sizeof(packet_header_t));
74 }
75
76 const packet_header_t *header = (const packet_header_t *)packet_data;
77 envelope.type = NET_TO_HOST_U16(header->type);
78 envelope.len = NET_TO_HOST_U32(header->length);
79 envelope.data = (uint8_t *)packet_data + sizeof(packet_header_t);
80 envelope.allocated_buffer = allocated_buffer;
81 envelope.allocated_size = packet_len;
82 envelope.was_encrypted = false; // WebRTC currently doesn't support encryption in this path
83
84 log_dev_every(4500 * US_PER_MS_INT, "WebRTC received packet: type=%u, len=%u, total_size=%zu", envelope.type,
85 envelope.len, packet_len);
86
87 // Validate packet length
88 if (envelope.len != packet_len - sizeof(packet_header_t)) {
89 buffer_pool_free(NULL, allocated_buffer, packet_len);
90 return SET_ERRNO(ERROR_NETWORK, "Packet length mismatch: header says %u, actual %zu", envelope.len,
91 packet_len - sizeof(packet_header_t));
92 }
93 }
94
95 // Dispatch packet to appropriate ACIP handler
96 asciichat_error_t dispatch_result =
97 acip_handle_client_packet(transport, envelope.type, envelope.data, envelope.len, callbacks);
98
99 if (dispatch_result != ASCIICHAT_OK) {
100 log_error("Packet dispatch failed for type %u: error %d", envelope.type, dispatch_result);
101 } else {
102 log_debug("Packet type %u dispatched successfully", envelope.type);
103 }
104
105 // Always free the allocated buffer (even if handler failed)
106 if (envelope.allocated_buffer) {
107 buffer_pool_free(NULL, envelope.allocated_buffer, envelope.allocated_size);
108 }
109
110 // Return handler result
111 return dispatch_result;
112}
113
114// =============================================================================
115// Client Send (to server)
116// =============================================================================
117
118asciichat_error_t acip_send_image_frame(acip_transport_t *transport, const void *pixel_data, uint32_t width,
119 uint32_t height, uint32_t pixel_format) {
120 log_debug("★ ACIP_SEND_IMAGE_FRAME: Called with %ux%u, transport=%p, pixel_data=%p", width, height, (void *)transport,
121 pixel_data);
122
123 if (!transport || !pixel_data) {
124 log_debug("★ ACIP_SEND_IMAGE_FRAME: Invalid params");
125 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or pixel_data");
126 }
127
128 // Calculate pixel data size (3 bytes per pixel for RGB24)
129 size_t pixel_size = (size_t)width * height * 3;
130 log_debug("★ ACIP_SEND_IMAGE_FRAME: pixel_size=%zu", pixel_size);
131
132 // Create image frame packet header
133 image_frame_packet_t header;
134 header.width = HOST_TO_NET_U32(width);
135 header.height = HOST_TO_NET_U32(height);
136 header.pixel_format = HOST_TO_NET_U32(pixel_format);
137 header.compressed_size = 0;
138 header.checksum = 0;
139 header.timestamp = 0;
140
141 // Calculate total size
142 size_t total_size;
143 if (checked_size_add(sizeof(header), pixel_size, &total_size) != ASCIICHAT_OK) {
144 log_debug("★ ACIP_SEND_IMAGE_FRAME: Overflow in size calculation");
145 return SET_ERRNO(ERROR_INVALID_PARAM, "Packet size overflow");
146 }
147
148 log_debug("★ ACIP_SEND_IMAGE_FRAME: total_size=%zu, allocating buffer", total_size);
149
150 // Allocate buffer
151 uint8_t *buffer = buffer_pool_alloc(NULL, total_size);
152 if (!buffer) {
153 log_debug("★ ACIP_SEND_IMAGE_FRAME: Failed to allocate %zu bytes", total_size);
154 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate buffer: %zu bytes", total_size);
155 }
156
157 // Build packet
158 memcpy(buffer, &header, sizeof(header));
159 memcpy(buffer + sizeof(header), pixel_data, pixel_size);
160
161 log_debug("★ ACIP_SEND_IMAGE_FRAME: About to send packet");
162 // Send via transport
163 asciichat_error_t result = packet_send_via_transport(transport, PACKET_TYPE_IMAGE_FRAME, buffer, total_size, 0);
164
165 log_debug("★ ACIP_SEND_IMAGE_FRAME: packet_send_via_transport returned %d", result);
166
167 buffer_pool_free(NULL, buffer, total_size);
168 return result;
169}
170
171asciichat_error_t acip_send_client_join(acip_transport_t *transport, uint8_t capabilities) {
172 if (!transport) {
173 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
174 }
175
176 // Simple capability byte payload
177 return packet_send_via_transport(transport, PACKET_TYPE_CLIENT_JOIN, &capabilities, sizeof(capabilities), 0);
178}
179
180asciichat_error_t acip_send_client_leave(acip_transport_t *transport) {
181 if (!transport) {
182 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
183 }
184
185 // Leave has no payload
186 return packet_send_via_transport(transport, PACKET_TYPE_CLIENT_LEAVE, NULL, 0, 0);
187}
188
189asciichat_error_t acip_send_stream_start(acip_transport_t *transport, uint8_t stream_types) {
190 if (!transport) {
191 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
192 }
193
194 // Server expects uint32_t (4 bytes), not uint8_t
195 uint32_t stream_types_net = HOST_TO_NET_U32((uint32_t)stream_types);
196 return packet_send_via_transport(transport, PACKET_TYPE_STREAM_START, &stream_types_net, sizeof(stream_types_net), 0);
197}
198
199asciichat_error_t acip_send_stream_stop(acip_transport_t *transport, uint8_t stream_types) {
200 if (!transport) {
201 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport");
202 }
203
204 // Server expects uint32_t (4 bytes), not uint8_t
205 uint32_t stream_types_net = HOST_TO_NET_U32((uint32_t)stream_types);
206 return packet_send_via_transport(transport, PACKET_TYPE_STREAM_STOP, &stream_types_net, sizeof(stream_types_net), 0);
207}
208
209asciichat_error_t acip_send_capabilities(acip_transport_t *transport, const void *cap_data, size_t cap_len) {
210 if (!transport || !cap_data) {
211 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or cap_data");
212 }
213
214 return packet_send_via_transport(transport, PACKET_TYPE_CLIENT_CAPABILITIES, cap_data, cap_len, 0);
215}
216
217asciichat_error_t acip_send_protocol_version(acip_transport_t *transport, const protocol_version_packet_t *version) {
218 if (!transport || !version) {
219 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid transport or version");
220 }
221
222 return packet_send_via_transport(transport, PACKET_TYPE_PROTOCOL_VERSION, version, sizeof(*version), 0);
223}
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Definition buffer_pool.c:99
int socket_t
asciichat_error_t acip_send_image_frame(acip_transport_t *transport, const void *pixel_data, uint32_t width, uint32_t height, uint32_t pixel_format)
asciichat_error_t acip_send_stream_stop(acip_transport_t *transport, uint8_t stream_types)
asciichat_error_t acip_client_receive_and_dispatch(acip_transport_t *transport, const acip_client_callbacks_t *callbacks)
asciichat_error_t acip_send_stream_start(acip_transport_t *transport, uint8_t stream_types)
asciichat_error_t acip_send_capabilities(acip_transport_t *transport, const void *cap_data, size_t cap_len)
asciichat_error_t acip_send_client_leave(acip_transport_t *transport)
asciichat_error_t acip_send_protocol_version(acip_transport_t *transport, const protocol_version_packet_t *version)
asciichat_error_t acip_send_client_join(acip_transport_t *transport, uint8_t capabilities)
asciichat_error_t acip_handle_client_packet(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, const acip_client_callbacks_t *callbacks)
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:566
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