#include #include #include #include #include #include #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(&on), sizeof(int)); if (local->ai_family == AF_INET6) { int off = 0; setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&off), sizeof(int)); } if (bind(socketDescriptor, local->ai_addr, local->ai_addrlen)) { // Save our actual local address for later use. getsockname(socketDescriptor, reinterpret_cast(&localAddr.addr), &localAddr.addrlen); } } void ServerListener::onReadable(struct ev_loop *, ev_io * watcher, int) { auto self = static_cast(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(len)) != 0) { return; } auto new_conn = std::make_unique(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(); } }