ArtifactEngine/Shared/Network/Connection.cpp
2026-03-11 18:05:19 -04:00

148 lines
5.1 KiB
C++

#include <string>
#include <thread>
#include <ngtcp2/ngtcp2_crypto.h>
#include <ngtcp2/ngtcp2_crypto_gnutls.h>
#include <gnutls/crypto.h>
#include <cista.h>
#include "Connection.h"
namespace Artifact {
Connection::Connection() {
ngtcp2_settings_default(&settings);
ngtcp2_transport_params_default(&transportParams);
transportParams.initial_max_streams_bidi = 1;
transportParams.initial_max_data = 65535;
transportParams.initial_max_stream_data_bidi_local = 65535;
callbacks.encrypt = ngtcp2_crypto_encrypt_cb;
callbacks.decrypt = ngtcp2_crypto_decrypt_cb;
callbacks.hp_mask = ngtcp2_crypto_hp_mask_cb;
callbacks.update_key = ngtcp2_crypto_update_key_cb;
callbacks.delete_crypto_aead_ctx = ngtcp2_crypto_delete_crypto_aead_ctx_cb;
callbacks.delete_crypto_cipher_ctx = ngtcp2_crypto_delete_crypto_cipher_ctx_cb;
callbacks.get_path_challenge_data = ngtcp2_crypto_get_path_challenge_data_cb;
callbacks.rand = [](auto dest, auto len, auto ctx) {
gnutls_rnd(GNUTLS_RND_RANDOM, dest, len);
};
callbacks.get_new_connection_id = [](auto conn, auto cid, auto token, auto cidlen, auto userdata) {
gnutls_rnd(GNUTLS_RND_RANDOM, cid, cidlen);
gnutls_rnd(GNUTLS_RND_RANDOM, token, NGTCP2_STATELESS_RESET_TOKENLEN);
return 0;
};
callbacks.version_negotiation = ngtcp2_crypto_version_negotiation_cb;
gnutls_rnd(GNUTLS_RND_RANDOM, dcid.data, dcid.datalen);
gnutls_rnd(GNUTLS_RND_RANDOM, scid.data, scid.datalen);
}
void readPacket(ngtcp2_pkt_info * info, void * packet) {
}
ClientConnection::ClientConnection(std::string host, std::string port) {
struct addrinfo hints;
struct addrinfo * local = nullptr;
struct addrinfo * remote = nullptr;
getaddrinfo("::", "", &hints, &local);
getaddrinfo(host.c_str(), port.c_str(), &hints, &remote);
ngtcp2_path path = {
.local = {
.addr = local->ai_addr,
.addrlen = local->ai_addrlen
},
.remote = {
.addr = remote->ai_addr,
.addrlen = remote->ai_addrlen
}
};
if (gnutls_init(&session, GNUTLS_CLIENT) != GNUTLS_E_SUCCESS) {
throw std::runtime_error("Failed to initialize gnutls.");
}
gnutls_set_default_priority(session);
ngtcp2_conn_client_new(&conn, &dcid, &scid, &path, NGTCP2_PROTO_VER_V1, &callbacks, &settings, &transportParams, nullptr, this);
ngtcp2_conn_set_tls_native_handle(conn, session);
}
ServerConnection::ServerConnection(ngtcp2_path path) : path(path) {
if (gnutls_init(&session, GNUTLS_CLIENT) != GNUTLS_E_SUCCESS) {
throw std::runtime_error("Failed to initialize gnutls.");
}
gnutls_set_default_priority(session);
ngtcp2_conn_server_new(&conn, &dcid, &scid, &path, NGTCP2_PROTO_VER_V1, &callbacks, &settings, &transportParams, nullptr, this);
ngtcp2_conn_set_tls_native_handle(conn, session);
}
ServerListener::ServerListener(std::string port) {
struct addrinfo hints;
struct addrinfo * local = nullptr;
getaddrinfo("::", "", &hints, &local);
socketDescriptor = socket(local->ai_family, local->ai_socktype, local->ai_protocol);
int on = 1;
setsockopt(socketDescriptor, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&on), sizeof(int));
if (local->ai_family == AF_INET6) {
int off = 0;
setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char *>(&off), sizeof(int));
}
if (bind(socketDescriptor, local->ai_addr, local->ai_addrlen)) {
// Save our actual local address for later use.
getsockname(socketDescriptor, reinterpret_cast<sockaddr *>(&localAddr.addr), &localAddr.addrlen);
}
}
void ServerListener::onReadable(struct ev_loop *, ev_io * watcher, int) {
auto self = static_cast<ServerListener *>(watcher->data);
uint8_t buf[2048];
ngtcp2_addr remote;
auto len = recvfrom(self->socketDescriptor, buf, sizeof(buf), 0, remote.addr, &remote.addrlen);
ngtcp2_pkt_info pi{};
ngtcp2_pkt_hd hd{};
ngtcp2_pkt_decode_hd_long(&hd, buf, len);
auto it = self->connections.find(hd.dcid);
if (it != self->connections.end()) {
ServerConnection * conn = it->second.get();
ngtcp2_conn_read_pkt(conn->conn, &conn->path, &pi, buf, len, std::chrono::steady_clock::now().time_since_epoch().count());
conn->readPacket(&pi, buf);
return;
}
if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM && hd.type == NGTCP2_PKT_INITIAL) {
ngtcp2_pkt_hd accept_hd{};
if (ngtcp2_accept(&accept_hd, buf, static_cast<size_t>(len)) != 0) {
return;
}
auto new_conn = std::make_unique<ServerConnection>(ngtcp2_path {
.local = self->localAddr,
.remote = remote
});
self->connections[accept_hd.dcid] = std::move(new_conn);
}
}
void ServerListener::run() {
std::thread([this]() {
loop = ev_loop_new(EVFLAG_AUTO);
ev_io_init(&watcher, onReadable, socketDescriptor, EV_READ);
watcher.data = this;
ev_io_start(loop, &watcher);
ev_run(loop, 0);
}).detach();
}
}