ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
client.c File Reference

Go to the source code of this file.

Enumerations

enum  connection_state_t {
  CONNECTION_STATE_DISCONNECTED = 0 , CONNECTION_STATE_CONNECTING = 1 , CONNECTION_STATE_HANDSHAKE = 2 , CONNECTION_STATE_CONNECTED = 3 ,
  CONNECTION_STATE_ERROR = 4
}
 

Functions

 EM_JS (void, js_send_raw_packet,(const uint8_t *packet_data, size_t packet_len), { if(!Module.sendPacketCallback) { console.error('[WASM] sendPacketCallback not registered - cannot send packet');return;} const packetArray=new Uint8Array(HEAPU8.buffer, packet_data, packet_len);const packetCopy=new Uint8Array(packetArray);var pktType=packetCopy.length >=10 ?((packetCopy[8]<< 8)|packetCopy[9]) :-1;console.error('[WASM->JS] Sending raw packet:', packetCopy.length, 'bytes, type=0x'+pktType.toString(16));Module.sendPacketCallback(packetCopy);})
 
const unsigned char * get_font_default_ptr (void)
 
unsigned int get_font_default_size (void)
 
EMSCRIPTEN_KEEPALIVE int client_init_with_args (const char *args_json)
 
EMSCRIPTEN_KEEPALIVE void client_cleanup (void)
 
EMSCRIPTEN_KEEPALIVE asciichat_error_t client_acds_join_request (const char *session, const char *password, uint8_t *output)
 
EMSCRIPTEN_KEEPALIVE int client_generate_keypair (void)
 
EMSCRIPTEN_KEEPALIVE int client_set_server_address (const char *server_host, int server_port)
 
EMSCRIPTEN_KEEPALIVE char * client_get_public_key_hex (void)
 
EMSCRIPTEN_KEEPALIVE int client_handle_key_exchange_init (const uint8_t *packet, size_t packet_len)
 
EMSCRIPTEN_KEEPALIVE int client_handle_crypto_parameters (const uint8_t *packet, size_t packet_len)
 
EMSCRIPTEN_KEEPALIVE int client_handle_auth_challenge (const uint8_t *packet, size_t packet_len)
 
EMSCRIPTEN_KEEPALIVE int client_handle_handshake_complete (const uint8_t *packet, size_t packet_len)
 
EMSCRIPTEN_KEEPALIVE int client_encrypt_packet (const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size, size_t *out_len)
 
EMSCRIPTEN_KEEPALIVE int client_decrypt_packet (const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size, size_t *out_len)
 
EMSCRIPTEN_KEEPALIVE char * client_parse_packet (const uint8_t *raw_packet, size_t packet_len)
 
EMSCRIPTEN_KEEPALIVE int client_serialize_packet (uint16_t packet_type, const uint8_t *payload, size_t payload_len, uint32_t client_id, uint8_t *output, size_t *out_len)
 
EMSCRIPTEN_KEEPALIVE int client_send_video_frame (const uint8_t *rgba_data, int width, int height)
 
EMSCRIPTEN_KEEPALIVE int client_get_connection_state (void)
 
EMSCRIPTEN_KEEPALIVE void client_free_string (char *ptr)
 
EMSCRIPTEN_KEEPALIVE int client_opus_encoder_init (int sample_rate, int channels, int bitrate)
 
EMSCRIPTEN_KEEPALIVE int client_opus_decoder_init (int sample_rate, int channels)
 
EMSCRIPTEN_KEEPALIVE int client_opus_encode (const int16_t *pcm_data, int frame_size, uint8_t *opus_data, int max_opus_bytes)
 
EMSCRIPTEN_KEEPALIVE int client_opus_decode (const uint8_t *opus_data, int opus_bytes, int16_t *pcm_data, int frame_size, int decode_fec)
 
EMSCRIPTEN_KEEPALIVE void client_opus_encoder_cleanup (void)
 
EMSCRIPTEN_KEEPALIVE void client_opus_decoder_cleanup (void)
 

Enumeration Type Documentation

◆ connection_state_t

Enumerator
CONNECTION_STATE_DISCONNECTED 
CONNECTION_STATE_CONNECTING 
CONNECTION_STATE_HANDSHAKE 
CONNECTION_STATE_CONNECTED 
CONNECTION_STATE_ERROR 

Definition at line 129 of file src/web/client.c.

129 {
connection_state_t
@ CONNECTION_STATE_ERROR
@ CONNECTION_STATE_DISCONNECTED
@ CONNECTION_STATE_CONNECTED
@ CONNECTION_STATE_HANDSHAKE
@ CONNECTION_STATE_CONNECTING

Function Documentation

◆ client_acds_join_request()

EMSCRIPTEN_KEEPALIVE asciichat_error_t client_acds_join_request ( const char *  session,
const char *  password,
uint8_t *  output 
)

Build a signed ACDS join request without exporting the ephemeral signing key.

Definition at line 212 of file src/web/client.c.

212 {
213 if (!session || !password || !output || strlen(session) == 0 || strlen(session) > 47 || strlen(password) > 127) {
214 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid discovery session or password");
215 }
216 uint8_t public_key[crypto_sign_PUBLICKEYBYTES];
217 uint8_t secret_key[crypto_sign_SECRETKEYBYTES];
218 if (crypto_sign_keypair(public_key, secret_key) != 0) {
219 return SET_ERRNO(ERROR_CRYPTO, "Failed to create discovery identity");
220 }
221 memset(output, 0, 282);
222 size_t session_len = strlen(session);
223 output[0] = (uint8_t)session_len;
224 memcpy(output + 1, session, session_len);
225 memcpy(output + 49, public_key, sizeof(public_key));
226 uint64_t timestamp = (uint64_t)emscripten_date_now();
227 // Packed ACDS structs use host (little-endian) fields; signatures use network order.
228 memcpy(output + 145, &timestamp, sizeof(timestamp));
229 uint8_t message[9 + 48];
230 message[0] = (uint8_t)6004;
231 for (size_t i = 0; i < 8; ++i)
232 message[1 + i] = (uint8_t)(timestamp >> (56 - i * 8));
233 memcpy(message + 9, session, session_len);
234 int result = crypto_sign_detached(output + 81, NULL, message, 9 + session_len, secret_key);
235 sodium_memzero(secret_key, sizeof(secret_key));
236 if (result != 0)
237 return SET_ERRNO(ERROR_CRYPTO, "Failed to sign discovery join");
238 output[153] = password[0] != '\0';
239 memcpy(output + 154, password, strlen(password));
240 return ASCIICHAT_OK;
241}
unsigned long long uint64_t
Definition common.h:59
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_OK
Definition error_codes.h:51
@ ERROR_CRYPTO
Definition error_codes.h:96
@ ERROR_INVALID_PARAM

References ASCIICHAT_OK, ERROR_CRYPTO, ERROR_INVALID_PARAM, and SET_ERRNO.

◆ client_cleanup()

EMSCRIPTEN_KEEPALIVE void client_cleanup ( void  )

Definition at line 193 of file src/web/client.c.

193 {
194
195 // Clean up crypto handshake context
196 crypto_handshake_destroy(&g_crypto_handshake_ctx);
197 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
198
199 g_handshake_complete = false;
200 g_connection_state = CONNECTION_STATE_DISCONNECTED;
201 g_initialized = false;
204}
void crypto_handshake_destroy(crypto_handshake_context_t *ctx)
Cleanup crypto handshake context with secure memory wiping.
void platform_destroy(void)
Cleanup platform-specific subsystems.
void options_state_destroy(void)
Shutdown RCU options system.
Definition rcu.c:461

References CONNECTION_STATE_DISCONNECTED, crypto_handshake_destroy(), options_state_destroy(), and platform_destroy().

◆ client_decrypt_packet()

EMSCRIPTEN_KEEPALIVE int client_decrypt_packet ( const uint8_t *  ciphertext,
size_t  ciphertext_len,
uint8_t *  plaintext,
size_t  plaintext_size,
size_t *  out_len 
)

Decrypt a ciphertext packet

Parameters
ciphertextInput ciphertext data
ciphertext_lenLength of ciphertext
plaintextOutput buffer (allocated by caller, must be at least ciphertext_len)
plaintext_sizeSize of plaintext buffer
out_lenOutput parameter for plaintext length
Returns
0 on success, -1 on error

Definition at line 597 of file src/web/client.c.

598 {
599 if (!g_handshake_complete) {
600 return -1;
601 }
602
603 // Decrypt the packet using handshake context's crypto context
604 size_t plaintext_len = 0;
605 crypto_result_t result = crypto_decrypt(&g_crypto_handshake_ctx.crypto_ctx, ciphertext, ciphertext_len, plaintext,
606 plaintext_size, &plaintext_len);
607 if (result != CRYPTO_OK) {
608 return -1;
609 }
610
611 *out_len = plaintext_len;
612 return 0;
613}
crypto_result_t
Cryptographic operation result codes.
crypto_result_t crypto_decrypt(crypto_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext_out, size_t plaintext_out_size, size_t *plaintext_len_out)
Decrypt data using XSalsa20-Poly1305.

References crypto_handshake_context_t::crypto_ctx, crypto_decrypt(), and CRYPTO_OK.

◆ client_encrypt_packet()

EMSCRIPTEN_KEEPALIVE int client_encrypt_packet ( const uint8_t *  plaintext,
size_t  plaintext_len,
uint8_t *  ciphertext,
size_t  ciphertext_size,
size_t *  out_len 
)

Encrypt a plaintext packet

Parameters
plaintextInput plaintext data
plaintext_lenLength of plaintext
ciphertextOutput buffer (allocated by caller, must be at least plaintext_len + crypto_aead_overhead)
ciphertext_sizeSize of ciphertext buffer
out_lenOutput parameter for ciphertext length
Returns
0 on success, -1 on error

Definition at line 569 of file src/web/client.c.

570 {
571 if (!g_handshake_complete) {
572 return -1;
573 }
574
575 // Encrypt the packet using handshake context's crypto context
576 size_t ciphertext_len = 0;
577 crypto_result_t result = crypto_encrypt(&g_crypto_handshake_ctx.crypto_ctx, plaintext, plaintext_len, ciphertext,
578 ciphertext_size, &ciphertext_len);
579 if (result != CRYPTO_OK) {
580 return -1;
581 }
582
583 *out_len = ciphertext_len;
584 return 0;
585}
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.

References crypto_handshake_context_t::crypto_ctx, crypto_encrypt(), and CRYPTO_OK.

◆ client_free_string()

EMSCRIPTEN_KEEPALIVE void client_free_string ( char *  ptr)

Definition at line 737 of file src/web/client.c.

737 {
738 SAFE_FREE(ptr);
739}
#define SAFE_FREE(ptr)
Definition common.h:376

References SAFE_FREE.

◆ client_generate_keypair()

EMSCRIPTEN_KEEPALIVE int client_generate_keypair ( void  )

Generate client keypair for handshake

Returns
0 on success, -1 on error

Definition at line 248 of file src/web/client.c.

248 {
249
250 if (!g_initialized) {
251 return -1;
252 }
253
254 // Reset handshake context if previously used (reconnection / React Strict Mode remount)
255 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_DISABLED) {
256 crypto_handshake_destroy(&g_crypto_handshake_ctx);
257 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
258 g_handshake_complete = false;
259 }
260
261 // Initialize crypto handshake context
262 asciichat_error_t result = crypto_handshake_init("wasm-client", &g_crypto_handshake_ctx, false /* is_server */);
263 if (result != ASCIICHAT_OK) {
264 return -1;
265 }
266
267 g_connection_state = CONNECTION_STATE_DISCONNECTED;
268 return 0;
269}
asciichat_error_t crypto_handshake_init(const char *name, crypto_handshake_context_t *ctx, bool is_server)
Initialize named crypto handshake context.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ CRYPTO_HANDSHAKE_DISABLED
crypto_handshake_state_t state

References ASCIICHAT_OK, CONNECTION_STATE_DISCONNECTED, crypto_handshake_destroy(), CRYPTO_HANDSHAKE_DISABLED, crypto_handshake_init(), and crypto_handshake_context_t::state.

◆ client_get_connection_state()

EMSCRIPTEN_KEEPALIVE int client_get_connection_state ( void  )

Get current connection state

Returns
Connection state enum value

Definition at line 728 of file src/web/client.c.

728 {
729 return (int)g_connection_state;
730}

◆ client_get_public_key_hex()

EMSCRIPTEN_KEEPALIVE char * client_get_public_key_hex ( void  )

Get client public key as hex string

Returns
Allocated hex string (caller must free with client_free_string), or NULL on error

Definition at line 299 of file src/web/client.c.

299 {
300 if (g_crypto_handshake_ctx.state == CRYPTO_HANDSHAKE_DISABLED) {
301 return NULL;
302 }
303
304 // Get public key from handshake context
305 const uint8_t *pubkey = g_crypto_handshake_ctx.crypto_ctx.public_key;
306 if (!pubkey) {
307 return NULL;
308 }
309
310 // Allocate buffer for hex string (32 bytes = 64 hex chars + null terminator)
311 char *hex_buffer = SAFE_MALLOC(65, char *);
312 if (!hex_buffer) {
313 return NULL;
314 }
315
316 // Convert public key to hex
317 for (int i = 0; i < 32; i++) {
318 snprintf(hex_buffer + (i * 2), 3, "%02x", pubkey[i]);
319 }
320 hex_buffer[64] = '\0';
321
322 return hex_buffer;
323}
#define SAFE_MALLOC(size, cast)
Definition common.h:264

References crypto_handshake_context_t::crypto_ctx, CRYPTO_HANDSHAKE_DISABLED, crypto_context_t::public_key, SAFE_MALLOC, and crypto_handshake_context_t::state.

◆ client_handle_auth_challenge()

EMSCRIPTEN_KEEPALIVE int client_handle_auth_challenge ( const uint8_t *  packet,
size_t  packet_len 
)

Handle CRYPTO_AUTH_CHALLENGE packet from server

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 464 of file src/web/client.c.

464 {
465
466 if (!packet || packet_len == 0) {
467 return -1;
468 }
469
470 // Extract packet type and payload
471 if (packet_len < sizeof(packet_header_t)) {
472 return -1;
473 }
474
475 const packet_header_t *header = (const packet_header_t *)packet;
476 packet_type_t packet_type = ntohs(header->type);
477 const uint8_t *payload_src = packet + sizeof(packet_header_t);
478 size_t payload_len = packet_len - sizeof(packet_header_t);
479
480 // Allocate payload copy from buffer pool (crypto function takes ownership and frees it)
481 uint8_t *payload = NULL;
482 if (payload_len > 0) {
483 payload = buffer_pool_alloc(NULL, payload_len);
484 if (!payload) {
485 g_connection_state = CONNECTION_STATE_ERROR;
486 return -1;
487 }
488 memcpy(payload, payload_src, payload_len);
489 }
490
491 // Process auth challenge
492 asciichat_error_t result = crypto_handshake_client_auth_response(&g_crypto_handshake_ctx, &g_wasm_transport,
493 packet_type, payload, payload_len);
494
495 if (result != ASCIICHAT_OK) {
496 g_connection_state = CONNECTION_STATE_ERROR;
497 return -1;
498 }
499
500 return 0;
501}
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
asciichat_error_t crypto_handshake_client_auth_response(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)
Client: Process auth challenge and send response.
Network packet header structure.
Definition packet.h:598
uint16_t type
Packet type (packet_type_t enumeration)
Definition packet.h:602

References ASCIICHAT_OK, buffer_pool_alloc(), CONNECTION_STATE_ERROR, crypto_handshake_client_auth_response(), and packet_header_t::type.

◆ client_handle_crypto_parameters()

EMSCRIPTEN_KEEPALIVE int client_handle_crypto_parameters ( const uint8_t *  packet,
size_t  packet_len 
)

Handle CRYPTO_PARAMETERS packet from server Sets up the key size negotiation before KEY_EXCHANGE_INIT

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 401 of file src/web/client.c.

401 {
402
403 if (!packet || packet_len == 0) {
404 return -1;
405 }
406
407 // Discovery sends CRYPTO_PARAMETERS before KEY_EXCHANGE_INIT. Initialize
408 // here so the negotiated values survive the subsequent key exchange packet.
409 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_INIT) {
410 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_DISABLED) {
411 crypto_handshake_destroy(&g_crypto_handshake_ctx);
412 }
413 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
414 if (crypto_handshake_init("wasm-client", &g_crypto_handshake_ctx, false) != ASCIICHAT_OK) {
415 return -1;
416 }
417 }
418
419 // Extract packet type and payload
420 if (packet_len < sizeof(packet_header_t)) {
421 return -1;
422 }
423
424 const packet_header_t *header = (const packet_header_t *)packet;
425 packet_type_t packet_type = ntohs(header->type);
426 const uint8_t *payload_src = packet + sizeof(packet_header_t);
427 size_t payload_len = packet_len - sizeof(packet_header_t);
428
429 // Allocate payload copy from buffer pool
430 uint8_t *payload = NULL;
431 if (payload_len > 0) {
432 payload = buffer_pool_alloc(NULL, payload_len);
433 if (!payload) {
434 g_connection_state = CONNECTION_STATE_ERROR;
435 return -1;
436 }
437 memcpy(payload, payload_src, payload_len);
438 }
439
440 // Process crypto parameters
441 asciichat_error_t result =
442 crypto_handshake_set_parameters(&g_crypto_handshake_ctx, (const crypto_parameters_packet_t *)payload);
443
444 // Free payload
445 if (payload) {
446 buffer_pool_free(NULL, payload, payload_len);
447 }
448
449 if (result != ASCIICHAT_OK) {
450 g_connection_state = CONNECTION_STATE_ERROR;
451 return -1;
452 }
453
454 return 0;
455}
asciichat_error_t crypto_handshake_set_parameters(crypto_handshake_context_t *ctx, const crypto_parameters_packet_t *params)
Set crypto parameters from crypto_parameters_packet_t.
void buffer_pool_free(buffer_pool_t *pool, const void *data, size_t size)
Free a buffer back to the pool (lock-free)
@ CRYPTO_HANDSHAKE_INIT
Crypto parameters packet structure (Packet Type 15)
Definition packet.h:981

References ASCIICHAT_OK, buffer_pool_alloc(), buffer_pool_free(), CONNECTION_STATE_ERROR, crypto_handshake_destroy(), CRYPTO_HANDSHAKE_DISABLED, CRYPTO_HANDSHAKE_INIT, crypto_handshake_init(), crypto_handshake_set_parameters(), crypto_handshake_context_t::state, and packet_header_t::type.

◆ client_handle_handshake_complete()

EMSCRIPTEN_KEEPALIVE int client_handle_handshake_complete ( const uint8_t *  packet,
size_t  packet_len 
)

Handle CRYPTO_HANDSHAKE_COMPLETE packet from server

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 510 of file src/web/client.c.

510 {
511
512 if (!packet || packet_len == 0) {
513 return -1;
514 }
515
516 // Extract packet type and payload
517 if (packet_len < sizeof(packet_header_t)) {
518 return -1;
519 }
520
521 const packet_header_t *header = (const packet_header_t *)packet;
522 packet_type_t packet_type = ntohs(header->type);
523 const uint8_t *payload_src = packet + sizeof(packet_header_t);
524 size_t payload_len = packet_len - sizeof(packet_header_t);
525
526 // Allocate payload copy from buffer pool (crypto function takes ownership and frees it)
527 uint8_t *payload = NULL;
528 if (payload_len > 0) {
529 payload = buffer_pool_alloc(NULL, payload_len);
530 if (!payload) {
531 g_connection_state = CONNECTION_STATE_ERROR;
532 return -1;
533 }
534 memcpy(payload, payload_src, payload_len);
535 }
536
537 // Complete handshake (takes ownership of payload and will free it)
538 asciichat_error_t result =
539 crypto_handshake_client_complete(&g_crypto_handshake_ctx, &g_wasm_transport, packet_type, payload, payload_len);
540
541 if (result != ASCIICHAT_OK) {
542 g_connection_state = CONNECTION_STATE_ERROR;
543 return -1;
544 }
545
546 g_handshake_complete = true;
547 g_connection_state = CONNECTION_STATE_CONNECTED;
548
549 // Update transport with the crypto context so ACIP can encrypt/decrypt packets
550 g_wasm_transport.crypto_ctx = &g_crypto_handshake_ctx.crypto_ctx;
551
552 return 0;
553}
asciichat_error_t crypto_handshake_client_complete(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)
Client: Wait for handshake complete confirmation.
crypto_context_t * crypto_ctx
Optional encryption context.
Definition transport.h:216

References ASCIICHAT_OK, buffer_pool_alloc(), CONNECTION_STATE_CONNECTED, CONNECTION_STATE_ERROR, crypto_handshake_context_t::crypto_ctx, acip_transport::crypto_ctx, crypto_handshake_client_complete(), and packet_header_t::type.

◆ client_handle_key_exchange_init()

EMSCRIPTEN_KEEPALIVE int client_handle_key_exchange_init ( const uint8_t *  packet,
size_t  packet_len 
)

Handle CRYPTO_KEY_EXCHANGE_INIT packet from server This is the first step of the crypto handshake

Parameters
packetRaw packet data including header
packet_lenTotal packet length
Returns
0 on success, -1 on error

Definition at line 333 of file src/web/client.c.

333 {
334
335 // Safety check: if handshake context is not in INIT state, reinitialize it
336 // This handles cases where previous handshakes weren't properly cleaned up
337 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_INIT) {
338
339 // Destroy and reset
340 if (g_crypto_handshake_ctx.state != CRYPTO_HANDSHAKE_DISABLED) {
341 crypto_handshake_destroy(&g_crypto_handshake_ctx);
342 }
343 memset(&g_crypto_handshake_ctx, 0, sizeof(g_crypto_handshake_ctx));
344
345 // Reinitialize to INIT state
346 asciichat_error_t init_result = crypto_handshake_init("wasm-client", &g_crypto_handshake_ctx, false);
347 if (init_result != ASCIICHAT_OK) {
348 return -1;
349 }
350 }
351
352 if (!packet || packet_len == 0) {
353 return -1;
354 }
355
356 // Extract packet type and payload
357 if (packet_len < sizeof(packet_header_t)) {
358 return -1;
359 }
360
361 const packet_header_t *header = (const packet_header_t *)packet;
362 packet_type_t packet_type = ntohs(header->type);
363 const uint8_t *payload_src = packet + sizeof(packet_header_t);
364 size_t payload_len = packet_len - sizeof(packet_header_t);
365
366 // Allocate payload copy from buffer pool (crypto function takes ownership and frees it)
367 // The raw packet pointer from JS cannot be passed directly because the crypto
368 // handshake function calls buffer_pool_free() on the payload when done.
369 uint8_t *payload = NULL;
370 if (payload_len > 0) {
371 payload = buffer_pool_alloc(NULL, payload_len);
372 if (!payload) {
373 g_connection_state = CONNECTION_STATE_ERROR;
374 return -1;
375 }
376 memcpy(payload, payload_src, payload_len);
377 }
378
379 // Process key exchange using transport-abstracted handshake
380
381 asciichat_error_t result = crypto_handshake_client_key_exchange(&g_crypto_handshake_ctx, &g_wasm_transport,
382 packet_type, payload, payload_len);
383
384 if (result != ASCIICHAT_OK) {
385 g_connection_state = CONNECTION_STATE_ERROR;
386 return -1;
387 }
388
389 g_connection_state = CONNECTION_STATE_HANDSHAKE;
390 return 0;
391}
asciichat_error_t crypto_handshake_client_key_exchange(crypto_handshake_context_t *ctx, acip_transport_t *transport, packet_type_t packet_type, const uint8_t *payload, size_t payload_len)
Client: Process server's public key and send our public key.

References ASCIICHAT_OK, buffer_pool_alloc(), CONNECTION_STATE_ERROR, CONNECTION_STATE_HANDSHAKE, crypto_handshake_client_key_exchange(), crypto_handshake_destroy(), CRYPTO_HANDSHAKE_DISABLED, CRYPTO_HANDSHAKE_INIT, crypto_handshake_init(), crypto_handshake_context_t::state, and packet_header_t::type.

◆ client_init_with_args()

EMSCRIPTEN_KEEPALIVE int client_init_with_args ( const char *  args_json)

Initialize client mode with command-line style arguments

Parameters
args_jsonSpace-separated string of argument strings, e.g. "client --width 80 --height 40"
Returns
0 on success, -1 on error

Definition at line 149 of file src/web/client.c.

149 {
150
151 // Parse space-separated arguments
152 char *args_copy = NULL;
153 char *argv[64] = {NULL};
154 int argc = wasm_parse_args(args_json, argv, 64, &args_copy);
155 if (argc < 0) {
156 return -1;
157 }
158
159 // Initialize options (sets up RCU, defaults, etc.)
160 asciichat_error_t err = options_init(argc, argv);
161 free(args_copy);
162
163 if (g_initialized) {
164 return -1;
165 }
166
167 // Initialize platform layer
168 err = platform_init();
169 if (err != ASCIICHAT_OK) {
170 return -1;
171 }
172
173 // Initialize logging to stderr (console.error in browser)
174 log_init(NULL, LOG_DEBUG, true, false);
175 // Use debug-style format in WASM so browser console shows full log headers
177 log_info("WASM client initialized via logging system");
178
179 if (err != ASCIICHAT_OK) {
180 return -1;
181 }
182
183 // Initialize ANSI color code generation
185
186 g_initialized = true;
187 g_connection_state = CONNECTION_STATE_DISCONNECTED;
188
189 return 0;
190}
void ansi_fast_init(void)
Definition ansi.c:319
void log_init(const char *filename, log_level_t level, bool force_stderr, bool use_mmap)
Initialize the logging system.
Definition log/log.c:533
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
asciichat_error_t log_set_format(const char *format_str, bool console_only)
Set a custom log format string.
Definition log/log.c:744
asciichat_error_t options_init(int argc, char **argv)
Initialize options by parsing command-line arguments with unified mode detection.
#define OPT_LOG_TEMPLATE_DEFAULT_DEBUG
Default log format string - debug mode (verbose with thread name, file relative path,...
asciichat_error_t platform_init(void)
Initialize platform-specific subsystems.
int wasm_parse_args(const char *args_str, char **argv, int max_args, char **out_args_copy)
#define LOG_DEBUG
Definition types.h:39

References ansi_fast_init(), ASCIICHAT_OK, CONNECTION_STATE_DISCONNECTED, LOG_DEBUG, log_info, log_init(), log_set_format(), OPT_LOG_TEMPLATE_DEFAULT_DEBUG, options_init(), platform_init(), and wasm_parse_args().

◆ client_opus_decode()

EMSCRIPTEN_KEEPALIVE int client_opus_decode ( const uint8_t *  opus_data,
int  opus_bytes,
int16_t *  pcm_data,
int  frame_size,
int  decode_fec 
)

Decode Opus audio to PCM

Parameters
opus_dataInput Opus data
opus_bytesLength of Opus data
pcm_dataOutput buffer for PCM samples (Int16)
frame_sizeMaximum number of samples per channel
decode_fecWhether to use FEC
Returns
Number of samples decoded per channel, or -1 on error

Definition at line 825 of file src/web/client.c.

825 {
826 if (!g_opus_decoder) {
827 return -1;
828 }
829
830 int decoded_samples = opus_decode(g_opus_decoder, opus_data, opus_bytes, pcm_data, frame_size, decode_fec);
831 if (decoded_samples < 0) {
832 return -1;
833 }
834
835 return decoded_samples;
836}

◆ client_opus_decoder_cleanup()

EMSCRIPTEN_KEEPALIVE void client_opus_decoder_cleanup ( void  )

Cleanup Opus decoder

Definition at line 853 of file src/web/client.c.

853 {
854 if (g_opus_decoder) {
855 opus_decoder_destroy(g_opus_decoder);
856 g_opus_decoder = NULL;
857 }
858}

◆ client_opus_decoder_init()

EMSCRIPTEN_KEEPALIVE int client_opus_decoder_init ( int  sample_rate,
int  channels 
)

Initialize Opus decoder

Parameters
sample_rateSample rate (8000, 16000, 24000, or 48000)
channelsNumber of channels (1=mono, 2=stereo)
Returns
0 on success, -1 on error

Definition at line 778 of file src/web/client.c.

778 {
779 if (g_opus_decoder) {
780 opus_decoder_destroy(g_opus_decoder);
781 g_opus_decoder = NULL;
782 }
783
784 int error;
785 g_opus_decoder = opus_decoder_create(sample_rate, channels, &error);
786 if (error != OPUS_OK || !g_opus_decoder) {
787 return -1;
788 }
789
790 return 0;
791}

◆ client_opus_encode()

EMSCRIPTEN_KEEPALIVE int client_opus_encode ( const int16_t *  pcm_data,
int  frame_size,
uint8_t *  opus_data,
int  max_opus_bytes 
)

Encode PCM audio to Opus

Parameters
pcm_dataInput PCM data (Int16 samples)
frame_sizeNumber of samples per channel
opus_dataOutput buffer for Opus data
max_opus_bytesMaximum size of output buffer
Returns
Number of bytes encoded, or -1 on error

Definition at line 802 of file src/web/client.c.

802 {
803 if (!g_opus_encoder) {
804 return -1;
805 }
806
807 int encoded_bytes = opus_encode(g_opus_encoder, pcm_data, frame_size, opus_data, max_opus_bytes);
808 if (encoded_bytes < 0) {
809 return -1;
810 }
811
812 return encoded_bytes;
813}

◆ client_opus_encoder_cleanup()

EMSCRIPTEN_KEEPALIVE void client_opus_encoder_cleanup ( void  )

Cleanup Opus encoder

Definition at line 842 of file src/web/client.c.

842 {
843 if (g_opus_encoder) {
844 opus_encoder_destroy(g_opus_encoder);
845 g_opus_encoder = NULL;
846 }
847}

◆ client_opus_encoder_init()

EMSCRIPTEN_KEEPALIVE int client_opus_encoder_init ( int  sample_rate,
int  channels,
int  bitrate 
)

Initialize Opus encoder

Parameters
sample_rateSample rate (8000, 16000, 24000, or 48000)
channelsNumber of channels (1=mono, 2=stereo)
bitrateTarget bitrate in bits/sec
Returns
0 on success, -1 on error

Definition at line 753 of file src/web/client.c.

753 {
754 if (g_opus_encoder) {
755 opus_encoder_destroy(g_opus_encoder);
756 g_opus_encoder = NULL;
757 }
758
759 int error;
760 g_opus_encoder = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_VOIP, &error);
761 if (error != OPUS_OK || !g_opus_encoder) {
762 return -1;
763 }
764
765 // Set bitrate
766 opus_encoder_ctl(g_opus_encoder, OPUS_SET_BITRATE(bitrate));
767
768 return 0;
769}
@ OPUS_APPLICATION_VOIP
Voice over IP (optimized for speech)
Definition opus.h:77

References OPUS_APPLICATION_VOIP.

◆ client_parse_packet()

EMSCRIPTEN_KEEPALIVE char * client_parse_packet ( const uint8_t *  raw_packet,
size_t  packet_len 
)

Parse a raw packet and return JSON metadata

Parameters
raw_packetRaw packet bytes including header
packet_lenLength of raw packet
Returns
Allocated JSON string with packet metadata (caller must free), or NULL on error

Definition at line 622 of file src/web/client.c.

622 {
623 if (!raw_packet || packet_len < sizeof(packet_header_t)) {
624 return NULL;
625 }
626
627 // Parse packet header directly from bytes
628 const packet_header_t *header = (const packet_header_t *)raw_packet;
629
630 // Validate magic number (convert from network byte order)
631 uint64_t magic = NET_TO_HOST_U64(header->magic);
632 if (magic != PACKET_MAGIC) {
633 return NULL;
634 }
635
636 // Convert header fields from network byte order
637 uint16_t type = NET_TO_HOST_U16(header->type);
638 uint32_t length = NET_TO_HOST_U32(header->length);
639 uint32_t client_id = NET_TO_HOST_U32(header->client_id);
640 uint32_t crc32 = NET_TO_HOST_U32(header->crc32);
641
642 if (length != packet_len - sizeof(packet_header_t) ||
643 (length > 0 && asciichat_crc32_sw(raw_packet + sizeof(packet_header_t), length) != crc32)) {
644 return NULL;
645 }
646
647 // Build JSON response with packet metadata
648 char *json = SAFE_MALLOC(1024, char *);
649 if (!json) {
650 return NULL;
651 }
652
653 snprintf(json, 1024, "{\"type\":%u,\"length\":%u,\"client_id\":%u,\"crc32\":%u}", type, length, client_id, crc32);
654
655 return json;
656}
#define NET_TO_HOST_U64(val)
Definition endian.h:228
#define NET_TO_HOST_U16(val)
Definition endian.h:111
#define NET_TO_HOST_U32(val)
Definition endian.h:81
unsigned short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58
#define PACKET_MAGIC
Packet magic number (alias for MAGIC_PACKET_VALID)
Definition packet.h:255
uint32_t asciichat_crc32_sw(const void *data, size_t len)
Compute CRC32 checksum using software implementation only.
Definition crc32.c:171
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
uint32_t crc32
CRC32 checksum of payload data (0 if length == 0)
Definition packet.h:606

References asciichat_crc32_sw(), packet_header_t::client_id, packet_header_t::crc32, packet_header_t::length, packet_header_t::magic, NET_TO_HOST_U16, NET_TO_HOST_U32, NET_TO_HOST_U64, PACKET_MAGIC, SAFE_MALLOC, and packet_header_t::type.

◆ client_send_video_frame()

EMSCRIPTEN_KEEPALIVE int client_send_video_frame ( const uint8_t *  rgba_data,
int  width,
int  height 
)

Process a video frame and prepare it for sending

Parameters
rgba_dataRGBA pixel data from canvas
widthFrame width
heightFrame height
Returns
0 on success, -1 on error

Definition at line 705 of file src/web/client.c.

705 {
706 if (!g_initialized) {
707 return -1;
708 }
709
710 // This is a placeholder - actual implementation would:
711 // 1. Convert RGBA to target format (e.g., compress with libjpeg-turbo)
712 // 2. Build frame packet
713 // 3. Encrypt if handshake complete
714 // 4. Return serialized packet via callback to JS
715
716 return 0;
717}

◆ client_serialize_packet()

EMSCRIPTEN_KEEPALIVE int client_serialize_packet ( uint16_t  packet_type,
const uint8_t *  payload,
size_t  payload_len,
uint32_t  client_id,
uint8_t *  output,
size_t *  out_len 
)

Serialize a packet structure to raw bytes

Parameters
packet_typeType of packet
payloadPacket payload data
payload_lenLength of payload
client_idClient ID to include in header
outputOutput buffer (allocated by caller)
out_lenOutput parameter for total packet length
Returns
0 on success, -1 on error

Definition at line 669 of file src/web/client.c.

670 {
671 if (!output || !out_len) {
672 return -1;
673 }
674
675 // Calculate CRC32 of payload (use software version for WASM)
676 uint32_t crc = payload && payload_len > 0 ? asciichat_crc32_sw(payload, payload_len) : 0;
677
678 // Build packet header (convert to network byte order)
680 .type = HOST_TO_NET_U16(packet_type),
681 .length = HOST_TO_NET_U32((uint32_t)payload_len),
682 .crc32 = HOST_TO_NET_U32(crc),
683 .client_id = HOST_TO_NET_U32(client_id)};
684
685 // Copy header to output
686 memcpy(output, &header, sizeof(packet_header_t));
687
688 // Copy payload to output
689 if (payload && payload_len > 0) {
690 memcpy(output + sizeof(packet_header_t), payload, payload_len);
691 }
692
693 *out_len = sizeof(packet_header_t) + payload_len;
694 return 0;
695}
#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

References asciichat_crc32_sw(), HOST_TO_NET_U16, HOST_TO_NET_U32, HOST_TO_NET_U64, packet_header_t::magic, and PACKET_MAGIC.

◆ client_set_server_address()

EMSCRIPTEN_KEEPALIVE int client_set_server_address ( const char *  server_host,
int  server_port 
)

Set server address for known_hosts verification

Parameters
server_hostServer hostname or IP address
server_portServer port number
Returns
0 on success, -1 on error

Definition at line 278 of file src/web/client.c.

278 {
279 if (!g_initialized) {
280 return -1;
281 }
282
283 if (!server_host || server_port <= 0 || server_port > 65535) {
284 return -1;
285 }
286
287 // Set server IP and port in handshake context
288 SAFE_STRNCPY(g_crypto_handshake_ctx.server_ip, server_host, sizeof(g_crypto_handshake_ctx.server_ip));
289 g_crypto_handshake_ctx.server_port = server_port;
290
291 return 0;
292}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414

References SAFE_STRNCPY, crypto_handshake_context_t::server_ip, and crypto_handshake_context_t::server_port.

◆ EM_JS()

EM_JS ( void  ,
js_send_raw_packet  ,
(const uint8_t *packet_data, size_t packet_len)  ,
{ if(!Module.sendPacketCallback) { console.error('[WASM] sendPacketCallback not registered - cannot send packet');return;} const packetArray=new Uint8Array(HEAPU8.buffer, packet_data, packet_len);const packetCopy=new Uint8Array(packetArray);var pktType=packetCopy.length >=10 ?((packetCopy[8]<< 8)|packetCopy[9]) :-1;console.error('[WASM->JS] Sending raw packet:', packetCopy.length, 'bytes, type=0x'+pktType.toString(16));Module.sendPacketCallback(packetCopy);}   
)

◆ get_font_default_ptr()

const unsigned char * get_font_default_ptr ( void  )

Definition at line 54 of file src/web/client.c.

54 {
55 return g_font_default;
56}
const unsigned char g_font_default[]

References g_font_default.

◆ get_font_default_size()

unsigned int get_font_default_size ( void  )

Definition at line 58 of file src/web/client.c.

58 {
59 return (unsigned int)g_font_default_size;
60}
const size_t g_font_default_size

References g_font_default_size.