ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
protocol.cpp
Go to the documentation of this file.
1#include <emscripten/bind.h>
2#include "client_bytes.h"
3extern "C" {
4#include "../client.h"
7#include <ascii-chat/common.h>
9}
10#include <sodium.h>
11#include <cstdint>
12#include <optional>
13#include <string>
14#include <vector>
15
16static std::optional<std::string> parse_packet(ClientBytes packet) {
17 auto input = binding_copy_input(packet);
18 if (input.size() < sizeof(packet_header_t)) {
19 SET_ERRNO(ERROR_INVALID_PARAM, "Client packet is shorter than its header");
20 return std::nullopt;
21 }
22 packet_header_t header;
23 if (packet_validate(input.data(), input.size(), 0, &header) != ASCIICHAT_OK)
24 return std::nullopt;
25 emscripten::val result = emscripten::val::object();
26 result.set("type", NET_TO_HOST_U16(header.type));
27 result.set("length", NET_TO_HOST_U32(header.length));
28 result.set("client_id", NET_TO_HOST_U32(header.client_id));
29 result.set("crc32", NET_TO_HOST_U32(header.crc32));
30 return emscripten::val::global("JSON").call<std::string>("stringify", result);
31}
32static ClientPacketResult serialize_packet(unsigned type, ClientBytes payload, unsigned client_id) {
33 if (type > UINT16_MAX) {
34 const int code = SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client packet type: %u", type);
35 return packet_result(code, nullptr, 0);
36 }
37 auto input = binding_copy_input(payload);
38 std::vector<uint8_t> output(input.size() + 256);
39 size_t length = 0;
40 const int code = packet_encode(static_cast<packet_type_t>(type), input.empty() ? nullptr : input.data(), input.size(),
41 client_id, output.data(), output.size(), &length);
42 if (code)
43 SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Cannot serialize client packet");
44 return packet_result(code, output.data(), code == 0 ? length : 0);
45}
46static int handle_handshake(int stage, ClientBytes packet) {
47 auto bytes = binding_copy_input(packet);
48 if (stage >= 0 && stage <= 3 && bytes.size() < sizeof(packet_header_t)) {
49 if (!bytes.empty())
50 sodium_memzero(bytes.data(), bytes.size());
51 return SET_ERRNO(ERROR_INVALID_PARAM, "Client handshake packet is shorter than its header");
52 }
53 int result = -1;
54 switch (stage) {
55 case 0:
56 result = client_handle_crypto_parameters(bytes.data(), bytes.size());
57 break;
58 case 1:
59 result = client_handle_key_exchange_init(bytes.data(), bytes.size());
60 break;
61 case 2:
62 result = client_handle_auth_challenge(bytes.data(), bytes.size());
63 break;
64 case 3:
65 result = client_handle_handshake_complete(bytes.data(), bytes.size());
66 break;
67 default:
68 result = SET_ERRNO(ERROR_INVALID_PARAM, "Invalid handshake stage: %d", stage);
69 break;
70 }
71 if (!bytes.empty())
72 sodium_memzero(bytes.data(), bytes.size());
73 if (result && stage >= 0 && stage <= 3)
74 SET_ERRNO(ERROR_CRYPTO_HANDSHAKE, "Client handshake stage %d failed", stage);
75 return result;
76}
77static int receive_handshake(bool discovery, unsigned type, ClientBytes payload) {
78 auto bytes = binding_copy_input(payload);
79 uint8_t empty = 0;
80 const void *data = bytes.empty() ? &empty : bytes.data();
81 const int result = discovery ? client_acds_handshake_receive(type, data, bytes.size())
82 : client_handshake_receive(type, data, bytes.size());
83 if (!bytes.empty())
84 sodium_memzero(bytes.data(), bytes.size());
85 return result;
86}
87static ClientPacketResult crypt_packet(bool encrypt, ClientBytes packet) {
88 auto input = binding_copy_input(packet);
89 std::vector<uint8_t> output(input.size() + (encrypt ? 72 : 0));
90 size_t length = 0;
91 const int code = encrypt ? client_encrypt_packet(input.data(), input.size(), output.data(), output.size(), &length)
92 : client_decrypt_packet(input.data(), input.size(), output.data(), output.size(), &length);
93 ClientPacketResult result = packet_result(code, output.data(), code == 0 ? length : 0);
94 if (!input.empty())
95 sodium_memzero(input.data(), input.size());
96 if (!output.empty())
97 sodium_memzero(output.data(), output.size());
98 return result;
99}
100
101EMSCRIPTEN_BINDINGS(ascii_chat_client_protocol) {
102 emscripten::register_type<ClientBytes>("ClientBytes", "Uint8Array");
103 emscripten::register_type<ClientPacketResult>("ClientPacketResult", "{ code: number; bytes: Uint8Array }");
104 emscripten::function("handleHandshake", &handle_handshake);
105 emscripten::function("receiveHandshake", &receive_handshake);
106 emscripten::function("cryptPacket", &crypt_packet);
107
108 emscripten::function("parsePacketJson", &parse_packet);
109 emscripten::function("serializePacket", &serialize_packet);
110}
#define SET_ERRNO(code,...)
std::vector< uint8_t > binding_copy_input(emscripten::val bytes)
Definition byte_arrays.h:6
ClientPacketResult packet_result(int code, const uint8_t *data, size_t length)
⚙️ Common definitions, error codes, macros, and types shared throughout the application
🔄 Network byte order conversion helpers
#define NET_TO_HOST_U16(val)
Definition endian.h:111
#define NET_TO_HOST_U32(val)
Definition endian.h:81
unsigned char uint8_t
Definition common.h:60
@ ERROR_CRYPTO_HANDSHAKE
Definition error_codes.h:96
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
asciichat_error_t packet_validate(const void *packet, size_t length, size_t limit, packet_header_t *output)
Definition packet.c:86
asciichat_error_t packet_encode(packet_type_t type, const void *payload, size_t length, uint32_t client_id, void *output, size_t capacity, size_t *written)
Definition packet.c:67
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:301
Network error handling utilities.
EMSCRIPTEN_BINDINGS(ascii_chat_client_protocol)
Definition protocol.cpp:101
asciichat_error_t client_handshake_receive(uint32_t type, const void *payload, size_t length)
int client_handle_handshake_complete(const uint8_t *packet, size_t packet_len)
int client_handle_auth_challenge(const uint8_t *packet, size_t packet_len)
asciichat_error_t client_acds_handshake_receive(uint32_t type, const void *payload, size_t length)
int client_decrypt_packet(const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t plaintext_size, size_t *out_len)
int client_encrypt_packet(const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t ciphertext_size, size_t *out_len)
int client_handle_crypto_parameters(const uint8_t *packet, size_t packet_len)
int client_handle_key_exchange_init(const uint8_t *packet, size_t packet_len)
Fixed prefix shared by every ACIP packet on the wire.
Definition packet.h:628