ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
acds_protocol.h File Reference

Go to the source code of this file.

Data Structures

struct  acds_protocol

Macros

#define NETWORK_ACIP_ACDS_PROTOCOL_ERROR_BUFFER_SIZE   256
#define NETWORK_ACIP_ACDS_PROTOCOL_NAME_BUFFER_SIZE   49

Typedefs

typedef asciichat_error_t(* acds_protocol_send_fn) (void *, packet_type_t, const void *, size_t)
typedef struct acds_protocol acds_protocol_t

Enumerations

enum  acds_protocol_state_t {
  ACDS_IDLE , ACDS_CREATING , ACDS_LOOKING_UP , ACDS_JOINING ,
  ACDS_CREATED , ACDS_FOUND , ACDS_JOINED , ACDS_ENDED ,
  ACDS_FAILED
}

Functions

void acds_protocol_init (acds_protocol_t *protocol, acds_protocol_send_fn send, void *user_data)
void acds_protocol_destroy (acds_protocol_t *protocol)
asciichat_error_t acds_protocol_create (acds_protocol_t *protocol, const acds_session_create_params_t *params, uint8_t total_keys, uint8_t key_index)
asciichat_error_t acds_protocol_lookup (acds_protocol_t *protocol, const char *name)
asciichat_error_t acds_protocol_join (acds_protocol_t *protocol, const acds_session_join_params_t *params)
asciichat_error_t acds_protocol_enter (acds_protocol_t *protocol, bool create, const char *name, const char *password)
asciichat_error_t acds_protocol_receive (acds_protocol_t *protocol, packet_type_t type, const void *payload, size_t length)
asciichat_error_t acds_protocol_leave (acds_protocol_t *protocol)
asciichat_error_t acds_handshake_begin (acip_transport_t *transport, uint8_t mode)
asciichat_error_t acds_handshake_receive (crypto_handshake_context_t *context, acip_transport_t *transport, packet_type_t type, const void *payload, size_t length)
asciichat_error_t acds_receive_packet (acip_transport_t *transport, packet_type_t *type, void **payload, size_t *length, void **allocation)

Macro Definition Documentation

◆ NETWORK_ACIP_ACDS_PROTOCOL_ERROR_BUFFER_SIZE

#define NETWORK_ACIP_ACDS_PROTOCOL_ERROR_BUFFER_SIZE   256

Definition at line 4 of file acds_protocol.h.

◆ NETWORK_ACIP_ACDS_PROTOCOL_NAME_BUFFER_SIZE

#define NETWORK_ACIP_ACDS_PROTOCOL_NAME_BUFFER_SIZE   49

Definition at line 5 of file acds_protocol.h.

Typedef Documentation

◆ acds_protocol_send_fn

typedef asciichat_error_t(* acds_protocol_send_fn) (void *, packet_type_t, const void *, size_t)

Event-driven discovery protocol. Callbacks consume borrowed data before returning. The caller owns transport I/O, deadlines, and serialization of calls to this object. No function waits for input or owns the transport.

Definition at line 13 of file acds_protocol.h.

◆ acds_protocol_t

Enumeration Type Documentation

◆ acds_protocol_state_t

Enumerator
ACDS_IDLE 
ACDS_CREATING 
ACDS_LOOKING_UP 
ACDS_JOINING 
ACDS_CREATED 
ACDS_FOUND 
ACDS_JOINED 
ACDS_ENDED 
ACDS_FAILED 

Definition at line 14 of file acds_protocol.h.

14 {
acds_protocol_state_t
@ ACDS_IDLE
@ ACDS_FAILED
@ ACDS_FOUND
@ ACDS_JOINED
@ ACDS_ENDED
@ ACDS_JOINING
@ ACDS_CREATING
@ ACDS_LOOKING_UP
@ ACDS_CREATED

Function Documentation

◆ acds_handshake_begin()

asciichat_error_t acds_handshake_begin ( acip_transport_t * transport,
uint8_t mode )

Handshake event steps shared by native receive loops and browser socket events. Context must already be initialized and configured with any identity options. Input remains caller-owned.

Definition at line 451 of file acds_client.c.

451 {
453 protocol_version_packet_init(&version, mode);
454 return packet_send_via_transport(transport, PACKET_TYPE_PROTOCOL_VERSION, &version, sizeof(version), 0);
455}
void protocol_version_packet_init(protocol_version_packet_t *version, uint8_t supports_encryption)
Initialize a protocol version packet with the current wire version.
Definition packet.c:37
@ PACKET_TYPE_PROTOCOL_VERSION
Protocol version and capabilities negotiation.
Definition packet.h:303
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)
Definition send.c:190
Protocol version negotiation packet structure (Packet Type 1).
Definition packet.h:860

References packet_send_via_transport(), PACKET_TYPE_PROTOCOL_VERSION, and protocol_version_packet_init().

Referenced by client_acds_handshake_begin(), and client_handshake_begin().

◆ acds_handshake_receive()

asciichat_error_t acds_handshake_receive ( crypto_handshake_context_t * context,
acip_transport_t * transport,
packet_type_t type,
const void * payload,
size_t length )

<No encryption (plaintext mode)

Definition at line 456 of file acds_client.c.

457 {
458 if (!ctx || !transport || (!payload && length))
459 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid handshake packet");
460 switch (type) {
462 if (ctx->state != CRYPTO_HANDSHAKE_INIT || length != sizeof(crypto_parameters_packet_t))
463 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid discovery crypto parameters");
464 if ((((const crypto_parameters_packet_t *)payload)->selected_cipher != CIPHER_ALGO_NONE) !=
465 ctx->crypto_ctx.encrypt_data)
466 return SET_ERRNO(ERROR_CRYPTO, "Discovery server changed requested encryption");
467 return crypto_handshake_set_parameters(ctx, payload);
469 return crypto_handshake_client_key_exchange(ctx, transport, type, payload, length);
471 return crypto_handshake_client_auth_response(ctx, transport, type, payload, length);
475 return crypto_handshake_client_complete(ctx, transport, type, payload, length);
476 default:
477 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Unexpected discovery handshake packet: %u", type);
478 }
479}
#define SET_ERRNO(code,...)
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.
@ CRYPTO_HANDSHAKE_INIT
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ERROR_CRYPTO
Definition error_codes.h:93
@ ERROR_INVALID_PARAM
#define CIPHER_ALGO_NONE
No encryption (plaintext mode).
Definition packet.h:1124
@ PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE
Server -> Client: "encryption ready" (UNENCRYPTED).
Definition packet.h:338
@ PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT
Server -> Client: {server_pubkey[32]} (UNENCRYPTED).
Definition packet.h:326
@ PACKET_TYPE_CRYPTO_AUTH_FAILED
Server -> Client: "authentication failed" (UNENCRYPTED).
Definition packet.h:334
@ PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP
Server -> Client: {HMAC[32]} server proves knowledge (UNENCRYPTED).
Definition packet.h:336
@ PACKET_TYPE_CRYPTO_AUTH_CHALLENGE
Server -> Client: {nonce[32]} (UNENCRYPTED).
Definition packet.h:330
@ PACKET_TYPE_CRYPTO_PARAMETERS
Server -> Client: Chosen algorithms + data sizes (UNENCRYPTED).
Definition packet.h:324
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.
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.
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.
Crypto parameters packet structure (Packet Type 15).
Definition packet.h:1037

References CIPHER_ALGO_NONE, crypto_handshake_context_t::crypto_ctx, crypto_handshake_client_auth_response(), crypto_handshake_client_complete(), crypto_handshake_client_key_exchange(), CRYPTO_HANDSHAKE_INIT, crypto_handshake_set_parameters(), crypto_context_t::encrypt_data, ERROR_CRYPTO, ERROR_INVALID_PARAM, ERROR_NETWORK_PROTOCOL, PACKET_TYPE_CRYPTO_AUTH_CHALLENGE, PACKET_TYPE_CRYPTO_AUTH_FAILED, PACKET_TYPE_CRYPTO_HANDSHAKE_COMPLETE, PACKET_TYPE_CRYPTO_KEY_EXCHANGE_INIT, PACKET_TYPE_CRYPTO_PARAMETERS, PACKET_TYPE_CRYPTO_SERVER_AUTH_RESP, SET_ERRNO, and crypto_handshake_context_t::state.

Referenced by client_acds_handshake_receive().

◆ acds_protocol_create()

asciichat_error_t acds_protocol_create ( acds_protocol_t * protocol,
const acds_session_create_params_t * params,
uint8_t total_keys,
uint8_t key_index )

Definition at line 240 of file acds_client.c.

241 {
242 if (!p || !params)
243 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid discovery create parameters");
244 if (total_keys > MAX_SESSION_KEYS || key_index >= (total_keys ? total_keys : 1) || params->max_participants == 0 ||
246 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid session key count/index or participant limit");
247 if (params->reserved_string && params->reserved_string[0])
248 return protocol_fail(p, ERROR_NOT_SUPPORTED, "Reserved session names are not supported", true);
249 acip_session_create_t req = {0};
250 memcpy(req.identity_pubkey, params->identity_pubkey, sizeof(req.identity_pubkey));
251 req.timestamp = time_ns_to_ms(time_get_realtime_ns());
252 req.capabilities = params->capabilities;
253 req.max_participants = params->max_participants;
254 req.session_type = params->session_type;
255 req.total_keys = total_keys;
256 req.key_index = key_index;
257 req.has_password = params->has_password;
258 req.expose_ip_publicly = params->acds_expose_ip;
259 SAFE_STRNCPY(req.server_address, params->server_address, sizeof(req.server_address));
260 req.server_port = params->server_port;
261 asciichat_error_t result = acds_sign_session_create(params->identity_seckey, req.timestamp, req.capabilities,
262 req.max_participants, req.signature);
263 if (result != ASCIICHAT_OK)
264 return protocol_fail(p, result, "Could not sign session creation", true);
265 if (params->has_password &&
266 crypto_pwhash_str((char *)req.password_hash, params->password,
267 strnlen(params->password, sizeof(params->password)), crypto_pwhash_OPSLIMIT_INTERACTIVE,
268 crypto_pwhash_MEMLIMIT_INTERACTIVE) != 0)
269 return protocol_fail(p, ERROR_CRYPTO, "Could not hash session password", true);
270 p->state = ACDS_CREATING;
271 size_t endpoint_length = strnlen(params->webtransport_url, sizeof(params->webtransport_url));
272 if (endpoint_length == sizeof(params->webtransport_url) ||
273 (endpoint_length &&
274 (endpoint_length < 9 || strncmp(params->webtransport_url, "https://", sizeof("https://") - 1)))) {
275 sodium_memzero(&req, sizeof(req));
276 return protocol_fail(p, ERROR_INVALID_PARAM, "Invalid public WebTransport endpoint", true);
277 }
278 if (endpoint_length || params->quic_port) {
279 uint8_t payload[sizeof(req) + 10 + sizeof(params->webtransport_url)];
280 memcpy(payload, &req, sizeof(req));
281 uint8_t *extension = payload + sizeof(req);
282 memcpy(extension, "ACQT", sizeof("ACQT") - 1);
283 extension[4] = 1;
284 extension[5] = 0;
285 extension[6] = (uint8_t)(params->quic_port >> 8);
286 extension[7] = (uint8_t)params->quic_port;
287 extension[8] = (uint8_t)(endpoint_length >> 8);
288 extension[9] = (uint8_t)endpoint_length;
289 memcpy(extension + 10, params->webtransport_url, endpoint_length);
290 result = protocol_send(p, PACKET_TYPE_ACIP_SESSION_CREATE, payload, sizeof(req) + 10 + endpoint_length);
291 sodium_memzero(payload, sizeof(payload));
292 } else {
293 result = protocol_send(p, PACKET_TYPE_ACIP_SESSION_CREATE, &req, sizeof(req));
294 }
295 sodium_memzero(&req, sizeof(req));
296 return result;
297}
asciichat_error_t acds_sign_session_create(const uint8_t identity_seckey[64], uint64_t timestamp, uint8_t capabilities, uint8_t max_participants, uint8_t signature_out[64])
Sign a SESSION_CREATE message.
Definition acds_client.c:45
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
unsigned char uint8_t
Definition common.h:60
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_NOT_SUPPORTED
Definition error_codes.h:69
#define MAX_SESSION_PARTICIPANTS
#define MAX_SESSION_KEYS
acip_session_create_t
Definition acds.h:226
@ PACKET_TYPE_ACIP_SESSION_CREATE
Create new session (Client -> Discovery Server).
Definition packet.h:431
const char * reserved_string
Optional reserved string (NULL = auto-generate).
uint16_t server_port
Server port where clients should connect.
bool acds_expose_ip
Explicitly allow public IP disclosure (–acds-expose-ip opt-in).
uint8_t identity_seckey[64]
Ed25519 secret key (for signing).
uint8_t max_participants
Maximum participants (1-128).
uint8_t identity_pubkey[32]
Ed25519 public key (host identity).
char server_address[64]
connect
bool has_password
Password protection enabled.
uint8_t capabilities
Bit 0: video, Bit 1: audio.
char password[128]
Optional password (if has_password).
uint8_t session_type
acds_session_type_t: 0=DIRECT_TCP (default), 1=WEBRTC

References ACDS_CREATING, acds_session_create_params_t::acds_expose_ip, acds_sign_session_create(), acip_session_create_t, ASCIICHAT_OK, acds_session_create_params_t::capabilities, ERROR_CRYPTO, ERROR_INVALID_PARAM, ERROR_NOT_SUPPORTED, acds_session_create_params_t::has_password, acds_session_create_params_t::identity_pubkey, acds_session_create_params_t::identity_seckey, acds_session_create_params_t::max_participants, MAX_SESSION_KEYS, MAX_SESSION_PARTICIPANTS, PACKET_TYPE_ACIP_SESSION_CREATE, acds_session_create_params_t::password, acds_session_create_params_t::quic_port, acds_session_create_params_t::reserved_string, SAFE_STRNCPY, acds_session_create_params_t::server_address, acds_session_create_params_t::server_port, acds_session_create_params_t::session_type, SET_ERRNO, acds_protocol::state, and acds_session_create_params_t::webtransport_url.

Referenced by acds_protocol_enter(), and acds_session_create().

◆ acds_protocol_destroy()

void acds_protocol_destroy ( acds_protocol_t * protocol)

Definition at line 219 of file acds_client.c.

219 {
220 if (p)
221 sodium_memzero(p, sizeof(*p));
222}

Referenced by acds_session_create(), acds_session_join(), acds_session_lookup(), and DiscoveryBinding::~DiscoveryBinding().

◆ acds_protocol_enter()

asciichat_error_t acds_protocol_enter ( acds_protocol_t * protocol,
bool create,
const char * name,
const char * password )

Create then join, or look up then join, using the same identity throughout.

Definition at line 332 of file acds_client.c.

332 {
333 if (!p || !password)
334 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid discovery parameters");
335 if (strlen(password) > 127)
336 return protocol_fail(p, ERROR_INVALID_PARAM, "Password is too long", true);
337 if (crypto_sign_keypair(p->join.identity_pubkey, p->join.identity_seckey) != 0)
338 return protocol_fail(p, ERROR_CRYPTO, "Could not generate discovery identity", true);
339 p->auto_join = true;
340 p->require_webrtc = true;
341 p->join.has_password = password[0] != 0;
342 SAFE_STRNCPY(p->join.password, password, sizeof(p->join.password));
343 p->join.session_string = p->name;
344 if (!create)
345 return acds_protocol_lookup(p, name);
346 acds_session_create_params_t params = {.capabilities = 3,
347 .max_participants = MAX_SESSION_PARTICIPANTS,
348 .session_type = SESSION_TYPE_WEBRTC,
349 .has_password = p->join.has_password};
350 memcpy(params.identity_pubkey, p->join.identity_pubkey, sizeof(params.identity_pubkey));
351 memcpy(params.identity_seckey, p->join.identity_seckey, sizeof(params.identity_seckey));
352 memcpy(params.password, p->join.password, sizeof(params.password));
353 asciichat_error_t result = acds_protocol_create(p, &params, 0, 0);
354 sodium_memzero(&params, sizeof(params));
355 return result;
356}
asciichat_error_t acds_protocol_lookup(acds_protocol_t *p, const char *name)
asciichat_error_t acds_protocol_create(acds_protocol_t *p, const acds_session_create_params_t *params, uint8_t total_keys, uint8_t key_index)
@ SESSION_TYPE_WEBRTC
WebRTC P2P mesh with STUN/TURN relay.
Definition acds.h:151
Session creation request parameters.

References acds_protocol_create(), acds_protocol_lookup(), acds_protocol::auto_join, ERROR_CRYPTO, ERROR_INVALID_PARAM, acds_session_join_params_t::has_password, acds_session_create_params_t::identity_pubkey, acds_session_join_params_t::identity_pubkey, acds_session_create_params_t::identity_seckey, acds_session_join_params_t::identity_seckey, acds_protocol::join, MAX_SESSION_PARTICIPANTS, acds_protocol::name, acds_session_create_params_t::password, acds_session_join_params_t::password, acds_protocol::require_webrtc, SAFE_STRNCPY, acds_session_join_params_t::session_string, SESSION_TYPE_WEBRTC, and SET_ERRNO.

Referenced by DiscoveryBinding::enter().

◆ acds_protocol_init()

void acds_protocol_init ( acds_protocol_t * protocol,
acds_protocol_send_fn send,
void * user_data )

Definition at line 214 of file acds_client.c.

214 {
215 memset(p, 0, sizeof(*p));
216 p->send = send;
217 p->user_data = user_data;
218}

References acds_protocol::send, and acds_protocol::user_data.

Referenced by acds_session_create(), acds_session_join(), acds_session_lookup(), and DiscoveryBinding::DiscoveryBinding().

◆ acds_protocol_join()

asciichat_error_t acds_protocol_join ( acds_protocol_t * protocol,
const acds_session_join_params_t * params )

Definition at line 310 of file acds_client.c.

310 {
311 if (!p || !params)
312 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid discovery join parameters");
313 if (!valid_name(params->session_string))
314 return protocol_fail(p, ERROR_INVALID_PARAM, "Invalid session name", true);
315 acip_session_join_t req = {0};
316 req.session_string_len = (uint8_t)strlen(params->session_string);
317 memcpy(req.session_string, params->session_string, req.session_string_len);
318 memcpy(req.identity_pubkey, params->identity_pubkey, sizeof(req.identity_pubkey));
319 req.timestamp = time_ns_to_ms(time_get_realtime_ns());
320 req.has_password = params->has_password;
321 if (params->has_password)
322 SAFE_STRNCPY(req.password, params->password, sizeof(req.password));
323 asciichat_error_t result =
324 acds_sign_session_join(params->identity_seckey, req.timestamp, params->session_string, req.signature);
325 if (result != ASCIICHAT_OK)
326 return protocol_fail(p, result, "Could not sign session join", true);
327 p->state = ACDS_JOINING;
328 result = protocol_send(p, PACKET_TYPE_ACIP_SESSION_JOIN, &req, sizeof(req));
329 sodium_memzero(&req, sizeof(req));
330 return result;
331}
asciichat_error_t acds_sign_session_join(const uint8_t identity_seckey[64], uint64_t timestamp, const char *session_string, uint8_t signature_out[64])
Sign a SESSION_JOIN message.
acip_session_join_t
Definition acds.h:408
@ PACKET_TYPE_ACIP_SESSION_JOIN
Join existing session (Client -> Discovery Server).
Definition packet.h:439
uint8_t identity_pubkey[32]
Participant's Ed25519 public key.
bool has_password
Password provided.
char password[128]
Password (if has_password).
const char * session_string
Session to join.
uint8_t identity_seckey[64]
Ed25519 secret key (for signing).

References ACDS_JOINING, acds_sign_session_join(), acip_session_join_t, ASCIICHAT_OK, ERROR_INVALID_PARAM, acds_session_join_params_t::has_password, acds_session_join_params_t::identity_pubkey, acds_session_join_params_t::identity_seckey, PACKET_TYPE_ACIP_SESSION_JOIN, acds_session_join_params_t::password, SAFE_STRNCPY, acds_session_join_params_t::session_string, SET_ERRNO, and acds_protocol::state.

Referenced by acds_protocol_receive(), and acds_session_join().

◆ acds_protocol_leave()

asciichat_error_t acds_protocol_leave ( acds_protocol_t * protocol)

Definition at line 440 of file acds_client.c.

440 {
441 if (!p)
442 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing discovery protocol");
443 if (p->state != ACDS_JOINED)
444 return ASCIICHAT_OK;
445 acip_session_leave_t req = {0};
446 memcpy(req.session_id, p->joined.session_id, sizeof(req.session_id));
447 memcpy(req.participant_id, p->joined.participant_id, sizeof(req.participant_id));
448 p->state = ACDS_ENDED;
449 return protocol_send(p, PACKET_TYPE_ACIP_SESSION_LEAVE, &req, sizeof(req));
450}
acip_session_leave_t
Definition acds.h:529
@ PACKET_TYPE_ACIP_SESSION_LEAVE
Leave session (Client -> Discovery Server).
Definition packet.h:443

References ACDS_ENDED, ACDS_JOINED, acip_session_leave_t, ASCIICHAT_OK, ERROR_INVALID_PARAM, acds_protocol::joined, PACKET_TYPE_ACIP_SESSION_LEAVE, SET_ERRNO, and acds_protocol::state.

Referenced by DiscoveryBinding::leave().

◆ acds_protocol_lookup()

asciichat_error_t acds_protocol_lookup ( acds_protocol_t * protocol,
const char * name )

Definition at line 298 of file acds_client.c.

298 {
299 if (!p)
300 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing discovery protocol");
301 if (!valid_name(name))
302 return protocol_fail(p, ERROR_INVALID_PARAM, "Invalid session name", true);
303 acip_session_lookup_t req = {0};
304 req.session_string_len = (uint8_t)strlen(name);
305 memcpy(req.session_string, name, req.session_string_len);
306 SAFE_STRNCPY(p->name, name, sizeof(p->name));
307 p->state = ACDS_LOOKING_UP;
308 return protocol_send(p, PACKET_TYPE_ACIP_SESSION_LOOKUP, &req, sizeof(req));
309}
acip_session_lookup_t
Definition acds.h:309
@ PACKET_TYPE_ACIP_SESSION_LOOKUP
Lookup session by string (Client -> Discovery Server).
Definition packet.h:435

References ACDS_LOOKING_UP, acip_session_lookup_t, ERROR_INVALID_PARAM, acds_protocol::name, PACKET_TYPE_ACIP_SESSION_LOOKUP, SAFE_STRNCPY, SET_ERRNO, and acds_protocol::state.

Referenced by acds_protocol_enter(), and acds_session_lookup().

◆ acds_protocol_receive()

asciichat_error_t acds_protocol_receive ( acds_protocol_t * protocol,
packet_type_t type,
const void * payload,
size_t length )

Definition at line 357 of file acds_client.c.

357 {
358 if (!p || (!payload && length))
359 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid discovery packet");
360 if (p->state == ACDS_FAILED || p->state == ACDS_ENDED)
361 return SET_ERRNO(ERROR_INVALID_STATE, "Discovery session has ended");
362 if (type == PACKET_TYPE_ACIP_ERROR || type == PACKET_TYPE_ERROR_MESSAGE) {
363 asciichat_error_t result = network_error_decode(type, payload, length);
364 return protocol_fail(p, result, asciichat_error_string(result), true);
365 }
366 if (type == PACKET_TYPE_ACIP_SESSION_END) {
367 p->state = ACDS_ENDED;
368 p->terminal = true;
369 SAFE_STRNCPY(p->error, "Session ended", sizeof(p->error));
370 return SET_ERRNO(ERROR_NETWORK, "Session ended");
371 }
372 if (type == PACKET_TYPE_ACIP_SESSION_CREATED && p->state == ACDS_CREATING) {
373 if (length < sizeof(p->created))
374 return protocol_fail(p, ERROR_NETWORK_PROTOCOL, "Truncated created session", true);
375 memcpy(&p->created, payload, sizeof(p->created));
376 if (!p->created.session_string_len || p->created.session_string_len > 47)
377 return protocol_fail(p, ERROR_NETWORK_PROTOCOL, "Invalid created session name", true);
378 memcpy(p->name, p->created.session_string, p->created.session_string_len);
379 p->name[p->created.session_string_len] = 0;
380 p->state = ACDS_CREATED;
381 if (p->auto_join)
382 return acds_protocol_join(p, &p->join);
383 return ASCIICHAT_OK;
384 }
385 if (type == PACKET_TYPE_ACIP_SESSION_INFO && p->state == ACDS_LOOKING_UP) {
386 if (length < sizeof(p->info))
387 return protocol_fail(p, ERROR_NETWORK_PROTOCOL, "Truncated session information", true);
388 memcpy(&p->info, payload, sizeof(p->info));
389 p->state = ACDS_FOUND;
390 if (!p->auto_join)
391 return ASCIICHAT_OK;
392 if (!p->info.found)
393 return protocol_fail(p, ERROR_NETWORK, "Session not found", true);
394 if (p->info.require_server_verify || p->info.require_client_verify)
395 return protocol_fail(p, ERROR_NOT_SUPPORTED, "This session requires an authorized identity", true);
396 if (p->info.has_password && !p->join.has_password)
397 return protocol_fail(p, ERROR_CRYPTO_AUTH, "This session requires a password", true);
398 return acds_protocol_join(p, &p->join);
399 }
400 if (type == PACKET_TYPE_ACIP_SESSION_JOINED && p->state == ACDS_JOINING) {
401 if (length < sizeof(p->joined))
402 return protocol_fail(p, ERROR_NETWORK_PROTOCOL, "Truncated discovery join response", true);
403 memcpy(&p->joined, payload, sizeof(p->joined));
404 p->quic_port = 0;
405 p->webtransport_url[0] = 0;
406 if (length > sizeof(p->joined)) {
407 const uint8_t *extension = (const uint8_t *)payload + sizeof(p->joined);
408 size_t extra = length - sizeof(p->joined);
409 if (extra < 10 || memcmp(extension, "ACQT", sizeof("ACQT") - 1) || extension[4] != 1 || extension[5])
410 return protocol_fail(p, ERROR_NETWORK_PROTOCOL, "Invalid discovery transport extension", true);
411 size_t url_length = ((size_t)extension[8] << 8) | extension[9];
412 if (url_length >= sizeof(p->webtransport_url) || extra != 10 + url_length ||
413 (url_length && (url_length < 9 || memcmp(extension + 10, "https://", sizeof("https://") - 1) ||
414 memchr(extension + 10, 0, url_length))))
415 return protocol_fail(p, ERROR_NETWORK_PROTOCOL, "Invalid discovery WebTransport endpoint", true);
416 p->quic_port = (uint16_t)((uint16_t)extension[6] << 8 | extension[7]);
417 memcpy(p->webtransport_url, extension + 10, url_length);
418 p->webtransport_url[url_length] = 0;
419 }
420 p->joined.error_message[sizeof(p->joined.error_message) - 1] = 0;
421 p->joined.server_address[sizeof(p->joined.server_address) - 1] = 0;
422 p->joined.turn_username[sizeof(p->joined.turn_username) - 1] = 0;
423 p->joined.turn_password[sizeof(p->joined.turn_password) - 1] = 0;
424 if (!p->joined.success)
425 return protocol_fail(p, network_error_from_acip(p->joined.error_code),
426 p->joined.error_message[0] ? p->joined.error_message : "Session join rejected", true);
427 if (p->require_webrtc && p->joined.session_type != SESSION_TYPE_WEBRTC && !p->webtransport_url[0])
428 return protocol_fail(p, ERROR_NOT_SUPPORTED, "This session does not offer WebTransport or WebRTC", true);
429 p->state = ACDS_JOINED;
430 sodium_memzero(p->join.identity_seckey, sizeof(p->join.identity_seckey));
431 sodium_memzero(p->join.password, sizeof(p->join.password));
432 return ASCIICHAT_OK;
433 }
434 // Signaling and participant notifications are delivered by the caller's transport adapter.
435 if (p->state == ACDS_JOINED && type != PACKET_TYPE_ACIP_SESSION_CREATED && type != PACKET_TYPE_ACIP_SESSION_INFO &&
437 return ASCIICHAT_OK;
438 return protocol_fail(p, ERROR_NETWORK_PROTOCOL, "Unexpected discovery response", true);
439}
asciichat_error_t acds_protocol_join(acds_protocol_t *p, const acds_session_join_params_t *params)
unsigned short uint16_t
Definition common.h:61
@ ERROR_INVALID_STATE
@ ERROR_NETWORK
Definition error_codes.h:72
@ ERROR_CRYPTO_AUTH
Definition error_codes.h:95
@ PACKET_TYPE_ACIP_SESSION_INFO
Session info response (Discovery Server -> Client).
Definition packet.h:437
@ PACKET_TYPE_ACIP_ERROR
Generic error response (Discovery Server -> Client).
Definition packet.h:533
@ PACKET_TYPE_ACIP_SESSION_CREATED
Session created response (Discovery Server -> Client).
Definition packet.h:433
@ PACKET_TYPE_ERROR_MESSAGE
Error packet with asciichat_error_t code and human-readable message.
Definition packet.h:374
@ PACKET_TYPE_ACIP_SESSION_JOINED
Session joined response (Discovery Server -> Client).
Definition packet.h:441
@ PACKET_TYPE_ACIP_SESSION_END
End session (Host -> Discovery Server).
Definition packet.h:445
asciichat_error_t network_error_decode(packet_type_t type, const void *payload, size_t size)
asciichat_error_t network_error_from_acip(uint8_t code)

References ACDS_CREATED, ACDS_CREATING, ACDS_ENDED, ACDS_FAILED, ACDS_FOUND, ACDS_JOINED, ACDS_JOINING, ACDS_LOOKING_UP, acds_protocol_join(), ASCIICHAT_OK, acds_protocol::auto_join, acds_protocol::created, acds_protocol::error, ERROR_CRYPTO_AUTH, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_NETWORK, ERROR_NETWORK_PROTOCOL, ERROR_NOT_SUPPORTED, acds_session_join_params_t::has_password, acds_session_join_params_t::identity_seckey, acds_protocol::info, acds_protocol::join, acds_protocol::joined, acds_protocol::name, network_error_decode(), network_error_from_acip(), PACKET_TYPE_ACIP_ERROR, PACKET_TYPE_ACIP_SESSION_CREATED, PACKET_TYPE_ACIP_SESSION_END, PACKET_TYPE_ACIP_SESSION_INFO, PACKET_TYPE_ACIP_SESSION_JOINED, PACKET_TYPE_ERROR_MESSAGE, acds_session_join_params_t::password, acds_protocol::quic_port, acds_protocol::require_webrtc, SAFE_STRNCPY, SESSION_TYPE_WEBRTC, SET_ERRNO, acds_protocol::state, acds_protocol::terminal, and acds_protocol::webtransport_url.

Referenced by DiscoveryBinding::receive().

◆ acds_receive_packet()

asciichat_error_t acds_receive_packet ( acip_transport_t * transport,
packet_type_t * type,
void ** payload,
size_t * length,
void ** allocation )

Receive discovery traffic, including WS application encryption. TCP decrypts in its transport.

Definition at line 481 of file acds_client.c.

482 {
483 asciichat_error_t result = packet_receive_via_transport(transport, type, payload, length, allocation);
484 if (result != ASCIICHAT_OK || acip_transport_get_type(transport) != ACIP_TRANSPORT_WEBSOCKET ||
485 !transport->crypto_ctx || !transport->crypto_ctx->encrypt_data)
486 return result;
487 if (*type != PACKET_TYPE_ENCRYPTED)
488 return SET_ERRNO(ERROR_CRYPTO, "Discovery signaling requires encrypted packets");
489 packet_envelope_t envelope = {.type = *type, .data = *payload, .len = *length, .allocated_buffer = *allocation};
490 result = packet_decrypt_envelope(&envelope, transport->crypto_ctx);
491 *allocation = envelope.allocated_buffer;
492 *type = envelope.type;
493 *payload = envelope.data;
494 *length = envelope.len;
495 if (result == ASCIICHAT_OK) {
496 const packet_header_t *inner = envelope.allocated_buffer;
497 if (NET_TO_HOST_U64(inner->magic) != PACKET_MAGIC || envelope.type == PACKET_TYPE_ENCRYPTED ||
498 asciichat_crc32(envelope.data, envelope.len) != NET_TO_HOST_U32(inner->crc32))
499 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid encrypted discovery packet");
500 }
501 return result;
502}
#define NET_TO_HOST_U64(val)
Definition endian.h:228
#define NET_TO_HOST_U32(val)
Definition endian.h:81
asciichat_error_t packet_decrypt_envelope(packet_envelope_t *envelope, void *crypto_ctx)
Send a clear console packet.
Definition packet.c:665
#define PACKET_MAGIC
Packet magic number (alias for MAGIC_PACKET_VALID).
Definition packet.h:270
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion).
Definition packet.h:355
#define asciichat_crc32(data, len)
Main CRC32 dispatcher macro - use this in application code.
Definition crc32.h:147
@ ACIP_TRANSPORT_WEBSOCKET
WebSocket over TCP.
asciichat_error_t packet_receive_via_transport(acip_transport_t *transport, packet_type_t *type, void **payload, size_t *payload_len, void **alloc_buffer)
Receive a packet via transport with header parsing and validation.
Definition send.c:200
crypto_context_t * crypto_ctx
Optional encryption context.
Packet envelope containing received packet data.
Definition packet.h:1185
Fixed prefix shared by every ACIP packet on the wire.
Definition packet.h:628
uint64_t magic
Magic number (PACKET_MAGIC = 0xA5C11C4A1 "ASCIICHAT" in hex) for packet validation.
Definition packet.h:630
uint32_t crc32
CRC32 checksum of payload data (0 if length == 0).
Definition packet.h:636

References ACIP_TRANSPORT_WEBSOCKET, asciichat_crc32, ASCIICHAT_OK, packet_header_t::crc32, acip_transport::crypto_ctx, crypto_context_t::encrypt_data, ERROR_CRYPTO, ERROR_NETWORK_PROTOCOL, packet_header_t::magic, NET_TO_HOST_U32, NET_TO_HOST_U64, packet_decrypt_envelope(), PACKET_MAGIC, packet_receive_via_transport(), PACKET_TYPE_ENCRYPTED, and SET_ERRNO.