Initial commit.
This commit is contained in:
commit
cb0391ce80
56 changed files with 3485 additions and 0 deletions
148
Shared/Network/Connection.cpp
Normal file
148
Shared/Network/Connection.cpp
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#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();
|
||||
}
|
||||
|
||||
}
|
||||
95
Shared/Network/Connection.h
Normal file
95
Shared/Network/Connection.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <ev.h>
|
||||
|
||||
// Platform-specific socket includes and helpers
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
using socket_t = SOCKET;
|
||||
#define CLOSE_SOCKET closesocket
|
||||
#define SOCKLEN_T int
|
||||
#define IS_INVALID_SOCKET(s) ((s) == INVALID_SOCKET)
|
||||
#define GET_LAST_ERROR() WSAGetLastError()
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
using socket_t = int;
|
||||
#define CLOSE_SOCKET close
|
||||
#define SOCKLEN_T socklen_t
|
||||
#define IS_INVALID_SOCKET(s) ((s) < 0)
|
||||
#define GET_LAST_ERROR() errno
|
||||
#endif
|
||||
|
||||
namespace Artifact {
|
||||
|
||||
namespace {
|
||||
|
||||
struct Ngtcp2CidEqual {
|
||||
bool operator()(const ngtcp2_cid& a, const ngtcp2_cid& b) const noexcept {
|
||||
return a.datalen == b.datalen &&
|
||||
std::memcmp(a.data, b.data, a.datalen) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Ngtcp2CidHash {
|
||||
size_t operator()(const ngtcp2_cid& cid) const noexcept {
|
||||
size_t h = cid.datalen;
|
||||
for (uint8_t i = 0; i < cid.datalen; ++i) {
|
||||
h = (h * 31) ^ cid.data[i]; // or better hash as above
|
||||
}
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class Connection {
|
||||
public:
|
||||
ngtcp2_settings settings;
|
||||
ngtcp2_transport_params transportParams;
|
||||
ngtcp2_callbacks callbacks;
|
||||
ngtcp2_cid dcid, scid;
|
||||
gnutls_session_t session;
|
||||
ngtcp2_conn * conn;
|
||||
|
||||
Connection();
|
||||
|
||||
void readPacket(ngtcp2_pkt_info * info, void * packet);
|
||||
};
|
||||
|
||||
class ClientConnection: public Connection {
|
||||
public:
|
||||
ClientConnection(std::string host, std::string port);
|
||||
};
|
||||
|
||||
class ServerConnection: public Connection {
|
||||
public:
|
||||
ngtcp2_path path;
|
||||
ServerConnection(ngtcp2_path path);
|
||||
};
|
||||
|
||||
class ServerListener {
|
||||
socket_t socketDescriptor;
|
||||
std::unordered_map<ngtcp2_cid, std::unique_ptr<ServerConnection>, Ngtcp2CidHash, Ngtcp2CidEqual> connections;
|
||||
ngtcp2_addr localAddr;
|
||||
|
||||
struct ev_loop * loop;
|
||||
ev_io watcher;
|
||||
public:
|
||||
ServerListener(std::string port);
|
||||
static void onReadable(struct ev_loop *, ev_io * watcher, int);
|
||||
void run();
|
||||
};
|
||||
|
||||
}
|
||||
31
Shared/Network/Network.cpp
Normal file
31
Shared/Network/Network.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#include "Network.h"
|
||||
|
||||
namespace Artifact {
|
||||
|
||||
void NetworkServer::host(NetworkClient * local) {
|
||||
if (active) {
|
||||
unhost();
|
||||
}
|
||||
|
||||
active = true;
|
||||
localClient = local;
|
||||
listener = local->listen<Events::NetworkMessage>([](auto ev) {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
void NetworkServer::unhost() {
|
||||
active = false;
|
||||
if (localClient) {
|
||||
localClient->unlisten<Events::NetworkMessage>(listener);
|
||||
localClient = nullptr;
|
||||
} else if (server) {
|
||||
server = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkClient::connect(NetworkServer * local) {
|
||||
localServer = local;
|
||||
}
|
||||
|
||||
}
|
||||
45
Shared/Network/Network.h
Normal file
45
Shared/Network/Network.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include "Shared.h"
|
||||
#include "Events.h"
|
||||
#include "Connection.h"
|
||||
|
||||
namespace Artifact {
|
||||
|
||||
namespace Events {
|
||||
|
||||
struct NetworkMessage {
|
||||
void * data;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class NetworkClient;
|
||||
|
||||
class NetworkServer: public BaseSubsystem, public EventTarget {
|
||||
NetworkClient * localClient = nullptr;
|
||||
std::unique_ptr<ServerListener> server = nullptr;
|
||||
bool active = false;
|
||||
uint64_t listener;
|
||||
public:
|
||||
|
||||
void host(NetworkClient * client);
|
||||
void host(std::string port);
|
||||
|
||||
void unhost();
|
||||
};
|
||||
|
||||
class NetworkClient: public BaseSubsystem, public EventTarget {
|
||||
NetworkServer * localServer = nullptr;
|
||||
std::unique_ptr<Connection> server = nullptr;
|
||||
bool active = false;
|
||||
uint64_t listener;
|
||||
public:
|
||||
|
||||
void connect(NetworkServer * local);
|
||||
void connect(std::string host, std::string port);
|
||||
|
||||
void disconnect();
|
||||
};
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue