Moved client data passing to a dedicated ClientSession class instead of passing through a bunch of pointers at init
This commit is contained in:
71
include/columnlynx/client/client_session.hpp
Normal file
71
include/columnlynx/client/client_session.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// client_session.hpp - Client Session data for ColumnLynx
|
||||
// Copyright (C) 2026 DcruBro
|
||||
// Distributed under the terms of the GNU General Public License, either version 2 only or version 3. See LICENSES/ for details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <columnlynx/common/libsodium_wrapper.hpp>
|
||||
#include <array>
|
||||
#include <columnlynx/common/net/virtual_interface.hpp>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace ColumnLynx {
|
||||
struct ClientState {
|
||||
std::shared_ptr<Utils::LibSodiumWrapper> sodiumWrapper;
|
||||
SymmetricKey aesKey;
|
||||
bool insecureMode;
|
||||
std::string configPath;
|
||||
std::shared_ptr<Net::VirtualInterface> virtualInterface;
|
||||
uint64_t sessionID;
|
||||
|
||||
~ClientState() { sodium_memzero(aesKey.data(), aesKey.size()); }
|
||||
ClientState(const ClientState&) = delete;
|
||||
ClientState& operator=(const ClientState&) = delete;
|
||||
ClientState(ClientState&&) = default;
|
||||
ClientState& operator=(ClientState&&) = default;
|
||||
|
||||
explicit ClientState() = default;
|
||||
|
||||
explicit ClientState(std::shared_ptr<Utils::LibSodiumWrapper> sodium, SymmetricKey& k, bool insecure,
|
||||
std::string& config, std::shared_ptr<Net::VirtualInterface> tun, uint64_t session)
|
||||
: sodiumWrapper(sodium), aesKey(k), insecureMode(insecure), configPath(config), virtualInterface(tun), sessionID(session) {}
|
||||
};
|
||||
|
||||
class ClientSession {
|
||||
public:
|
||||
// Return a reference to the Client Session instance
|
||||
static ClientSession& getInstance() { static ClientSession instance; return instance; }
|
||||
|
||||
// Return the current client state
|
||||
std::shared_ptr<ClientState> getClientState() const;
|
||||
|
||||
// Set the client state
|
||||
void setClientState(std::shared_ptr<ClientState> state);
|
||||
|
||||
// Get the wrapper for libsodium
|
||||
const std::shared_ptr<Utils::LibSodiumWrapper>& getSodiumWrapper() const;
|
||||
// Get the AES key
|
||||
const SymmetricKey& getAESKey() const;
|
||||
// Get whether insecure mode is enabled
|
||||
bool isInsecureMode() const;
|
||||
// Get the config path
|
||||
const std::string& getConfigPath() const;
|
||||
// Get the virtual interface
|
||||
const std::shared_ptr<Net::VirtualInterface>& getVirtualInterface() const;
|
||||
// Get the session ID
|
||||
uint64_t getSessionID() const;
|
||||
|
||||
// Setters
|
||||
void setSodiumWrapper(std::shared_ptr<Utils::LibSodiumWrapper> sodiumWrapper);
|
||||
void setAESKey(const SymmetricKey& aesKey);
|
||||
void setInsecureMode(bool insecureMode);
|
||||
void setConfigPath(const std::string& configPath);
|
||||
void setVirtualInterface(std::shared_ptr<Net::VirtualInterface> virtualInterface);
|
||||
void setSessionID(uint64_t sessionID);
|
||||
|
||||
private:
|
||||
mutable std::shared_mutex mMutex;
|
||||
std::shared_ptr<struct ClientState> mClientState{nullptr};
|
||||
};
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <string>
|
||||
#include <columnlynx/common/net/protocol_structs.hpp>
|
||||
#include <columnlynx/common/net/virtual_interface.hpp>
|
||||
#include <columnlynx/client/client_session.hpp>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
@@ -25,28 +26,20 @@ namespace ColumnLynx::Net::TCP {
|
||||
public:
|
||||
TCPClient(asio::io_context& ioContext,
|
||||
const std::string& host,
|
||||
const std::string& port,
|
||||
std::shared_ptr<Utils::LibSodiumWrapper> sodiumWrapper,
|
||||
std::shared_ptr<std::array<uint8_t, 32>> aesKey,
|
||||
std::shared_ptr<uint64_t> sessionIDRef,
|
||||
bool insecureMode,
|
||||
std::string& configPath,
|
||||
std::shared_ptr<VirtualInterface> tun = nullptr)
|
||||
const std::string& port)
|
||||
:
|
||||
mResolver(ioContext),
|
||||
mSocket(ioContext),
|
||||
mHost(host),
|
||||
mPort(port),
|
||||
mLibSodiumWrapper(sodiumWrapper),
|
||||
mGlobalKeyRef(aesKey),
|
||||
mSessionIDRef(sessionIDRef),
|
||||
mInsecureMode(insecureMode),
|
||||
mHeartbeatTimer(mSocket.get_executor()),
|
||||
mLastHeartbeatReceived(std::chrono::steady_clock::now()),
|
||||
mLastHeartbeatSent(std::chrono::steady_clock::now()),
|
||||
mTun(tun),
|
||||
mConfigDirPath(configPath)
|
||||
mLastHeartbeatSent(std::chrono::steady_clock::now())
|
||||
{
|
||||
// Get initial client config
|
||||
std::string configPath = ClientSession::getInstance().getConfigPath();
|
||||
std::shared_ptr<Utils::LibSodiumWrapper> mLibSodiumWrapper = ClientSession::getInstance().getSodiumWrapper();
|
||||
|
||||
// Preload the config map
|
||||
mRawClientConfig = Utils::getConfigMap(configPath + "client_config");
|
||||
|
||||
@@ -104,20 +97,14 @@ namespace ColumnLynx::Net::TCP {
|
||||
std::string mHost, mPort;
|
||||
uint8_t mServerPublicKey[32]; // Assuming 256-bit public key
|
||||
std::array<uint8_t, 32> mSubmittedChallenge{};
|
||||
std::shared_ptr<Utils::LibSodiumWrapper> mLibSodiumWrapper;
|
||||
uint64_t mConnectionSessionID;
|
||||
SymmetricKey mConnectionAESKey;
|
||||
std::shared_ptr<std::array<uint8_t, 32>> mGlobalKeyRef; // Reference to global AES key
|
||||
std::shared_ptr<uint64_t> mSessionIDRef; // Reference to global Session ID
|
||||
bool mInsecureMode; // Insecure mode flag
|
||||
asio::steady_timer mHeartbeatTimer;
|
||||
std::chrono::steady_clock::time_point mLastHeartbeatReceived;
|
||||
std::chrono::steady_clock::time_point mLastHeartbeatSent;
|
||||
int mMissedHeartbeats = 0;
|
||||
bool mIsHostDomain;
|
||||
Protocol::TunConfig mTunConfig;
|
||||
std::shared_ptr<VirtualInterface> mTun = nullptr;
|
||||
std::unordered_map<std::string, std::string> mRawClientConfig;
|
||||
std::string mConfigDirPath;
|
||||
};
|
||||
}
|
||||
@@ -10,17 +10,15 @@
|
||||
#include <columnlynx/common/libsodium_wrapper.hpp>
|
||||
#include <array>
|
||||
#include <columnlynx/common/net/virtual_interface.hpp>
|
||||
#include <columnlynx/client/client_session.hpp>
|
||||
|
||||
namespace ColumnLynx::Net::UDP {
|
||||
class UDPClient {
|
||||
public:
|
||||
UDPClient(asio::io_context& ioContext,
|
||||
const std::string& host,
|
||||
const std::string& port,
|
||||
std::shared_ptr<std::array<uint8_t, 32>> aesKeyRef,
|
||||
std::shared_ptr<uint64_t> sessionIDRef,
|
||||
std::shared_ptr<VirtualInterface> tunRef = nullptr)
|
||||
: mSocket(ioContext), mResolver(ioContext), mHost(host), mPort(port), mAesKeyRef(aesKeyRef), mSessionIDRef(sessionIDRef), mTunRef(tunRef)
|
||||
const std::string& port)
|
||||
: mSocket(ioContext), mResolver(ioContext), mHost(host), mPort(port)
|
||||
{
|
||||
mStartReceive();
|
||||
}
|
||||
@@ -43,9 +41,6 @@ namespace ColumnLynx::Net::UDP {
|
||||
asio::ip::udp::endpoint mRemoteEndpoint;
|
||||
std::string mHost;
|
||||
std::string mPort;
|
||||
std::shared_ptr<std::array<uint8_t, 32>> mAesKeyRef;
|
||||
std::shared_ptr<uint64_t> mSessionIDRef;
|
||||
std::shared_ptr<VirtualInterface> mTunRef = nullptr;
|
||||
std::array<uint8_t, 2048> mRecvBuffer; // Adjust size as needed
|
||||
};
|
||||
}
|
||||
71
src/client/client_session.cpp
Normal file
71
src/client/client_session.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// client_session.cpp - Client Session data for ColumnLynx
|
||||
// Copyright (C) 2026 DcruBro
|
||||
// Distributed under the terms of the GNU General Public License, either version 2 only or version 3. See LICENSES/ for details.
|
||||
|
||||
#include <columnlynx/client/client_session.hpp>
|
||||
|
||||
namespace ColumnLynx {
|
||||
std::shared_ptr<ClientState> ClientSession::getClientState() const {
|
||||
std::shared_lock lock(mMutex);
|
||||
return mClientState;
|
||||
}
|
||||
|
||||
void ClientSession::setClientState(std::shared_ptr<ClientState> state) {
|
||||
std::unique_lock lock(mMutex);
|
||||
mClientState = state;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Utils::LibSodiumWrapper>& ClientSession::getSodiumWrapper() const {
|
||||
return getClientState()->sodiumWrapper;
|
||||
}
|
||||
|
||||
const SymmetricKey& ClientSession::getAESKey() const {
|
||||
return getClientState()->aesKey;
|
||||
}
|
||||
|
||||
bool ClientSession::isInsecureMode() const {
|
||||
return getClientState()->insecureMode;
|
||||
}
|
||||
|
||||
const std::string& ClientSession::getConfigPath() const {
|
||||
return getClientState()->configPath;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Net::VirtualInterface>& ClientSession::getVirtualInterface() const {
|
||||
return getClientState()->virtualInterface;
|
||||
}
|
||||
|
||||
uint64_t ClientSession::getSessionID() const {
|
||||
return getClientState()->sessionID;
|
||||
}
|
||||
|
||||
void ClientSession::setSodiumWrapper(std::shared_ptr<Utils::LibSodiumWrapper> sodiumWrapper) {
|
||||
std::unique_lock lock(mMutex);
|
||||
mClientState->sodiumWrapper = sodiumWrapper;
|
||||
}
|
||||
|
||||
void ClientSession::setAESKey(const SymmetricKey& aesKey) {
|
||||
std::unique_lock lock(mMutex);
|
||||
mClientState->aesKey = aesKey;
|
||||
}
|
||||
|
||||
void ClientSession::setInsecureMode(bool insecureMode) {
|
||||
std::unique_lock lock(mMutex);
|
||||
mClientState->insecureMode = insecureMode;
|
||||
}
|
||||
|
||||
void ClientSession::setConfigPath(const std::string& configPath) {
|
||||
std::unique_lock lock(mMutex);
|
||||
mClientState->configPath = configPath;
|
||||
}
|
||||
|
||||
void ClientSession::setVirtualInterface(std::shared_ptr<Net::VirtualInterface> virtualInterface) {
|
||||
std::unique_lock lock(mMutex);
|
||||
mClientState->virtualInterface = virtualInterface;
|
||||
}
|
||||
|
||||
void ClientSession::setSessionID(uint64_t sessionID) {
|
||||
std::unique_lock lock(mMutex);
|
||||
mClientState->sessionID = sessionID;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <columnlynx/client/net/udp/udp_client.hpp>
|
||||
#include <cxxopts.hpp>
|
||||
#include <columnlynx/common/net/virtual_interface.hpp>
|
||||
#include <columnlynx/client/client_session.hpp>
|
||||
#include <thread>
|
||||
|
||||
#if defined(__WIN32__)
|
||||
#include <windows.h>
|
||||
@@ -99,24 +101,34 @@ int main(int argc, char** argv) {
|
||||
#endif
|
||||
}
|
||||
|
||||
struct ClientState initialState{};
|
||||
initialState.configPath = configPath;
|
||||
initialState.insecureMode = insecureMode;
|
||||
|
||||
std::shared_ptr<VirtualInterface> tun = std::make_shared<VirtualInterface>(optionsObj["interface"].as<std::string>());
|
||||
log("Using virtual interface: " + tun->getName());
|
||||
initialState.virtualInterface = tun;
|
||||
|
||||
std::shared_ptr<LibSodiumWrapper> sodiumWrapper = std::make_shared<LibSodiumWrapper>();
|
||||
debug("Public Key: " + Utils::bytesToHexString(sodiumWrapper->getPublicKey(), 32));
|
||||
debug("Private Key: " + Utils::bytesToHexString(sodiumWrapper->getPrivateKey(), 64));
|
||||
initialState.sodiumWrapper = sodiumWrapper;
|
||||
|
||||
std::shared_ptr<std::array<uint8_t, 32>> aesKey = std::make_shared<std::array<uint8_t, 32>>();
|
||||
aesKey->fill(0); // Defualt zeroed state until modified by handshake
|
||||
std::shared_ptr<uint64_t> sessionID = std::make_shared<uint64_t>(0);
|
||||
std::array<uint8_t, 32> aesKey = std::array<uint8_t, 32>();
|
||||
aesKey.fill(0); // Defualt zeroed state until modified by handshake
|
||||
uint64_t sessionID = 0;
|
||||
initialState.aesKey = aesKey;
|
||||
initialState.sessionID = sessionID;
|
||||
|
||||
ColumnLynx::ClientSession::getInstance().setClientState(std::make_shared<ClientState>(std::move(initialState))); // Set initial state
|
||||
|
||||
if (insecureMode) {
|
||||
warn("You have started the client with the --ignore-whitelist. This means that the client will NOT attempt to verify the server's public key. This is INSECURE and SHOULDN'T be used!");
|
||||
}
|
||||
|
||||
asio::io_context io;
|
||||
auto client = std::make_shared<ColumnLynx::Net::TCP::TCPClient>(io, host, port, sodiumWrapper, aesKey, sessionID, insecureMode, configPath, tun);
|
||||
auto udpClient = std::make_shared<ColumnLynx::Net::UDP::UDPClient>(io, host, port, aesKey, sessionID, tun);
|
||||
auto client = std::make_shared<ColumnLynx::Net::TCP::TCPClient>(io, host, port); // TODO: Move to ClientSession state
|
||||
auto udpClient = std::make_shared<ColumnLynx::Net::UDP::UDPClient>(io, host, port);
|
||||
|
||||
client->start();
|
||||
udpClient->start();
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace ColumnLynx::Net::TCP {
|
||||
mLibSodiumWrapper->getXPublicKey(),
|
||||
mLibSodiumWrapper->getXPublicKey() + crypto_box_PUBLICKEYBYTES
|
||||
);*/
|
||||
|
||||
const auto& mLibSodiumWrapper = ClientSession::getInstance().getSodiumWrapper();
|
||||
payload.insert(payload.end(),
|
||||
mLibSodiumWrapper->getPublicKey(),
|
||||
mLibSodiumWrapper->getPublicKey() + crypto_sign_PUBLICKEYBYTES
|
||||
@@ -131,10 +133,8 @@ namespace ColumnLynx::Net::TCP {
|
||||
mHandler->socket().close(ec);
|
||||
mConnected = false;
|
||||
|
||||
mGlobalKeyRef = nullptr;
|
||||
if (mSessionIDRef) {
|
||||
*mSessionIDRef = 0;
|
||||
}
|
||||
ClientSession::getInstance().setAESKey({}); // Clear AES key with all zeros
|
||||
ClientSession::getInstance().setSessionID(0);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -155,9 +155,10 @@ namespace ColumnLynx::Net::TCP {
|
||||
Utils::log("Received server identity. Public Key: " + hexServerPub);
|
||||
|
||||
// Verify pubkey against whitelisted_keys
|
||||
std::vector<std::string> whitelistedKeys = Utils::getWhitelistedKeys(mConfigDirPath);
|
||||
const std::string& configPath = ClientSession::getInstance().getConfigPath();
|
||||
std::vector<std::string> whitelistedKeys = Utils::getWhitelistedKeys(configPath);
|
||||
if (std::find(whitelistedKeys.begin(), whitelistedKeys.end(), Utils::bytesToHexString(mServerPublicKey, 32)) == whitelistedKeys.end()) { // Key verification is handled in later steps of the handshake
|
||||
if (!mInsecureMode) {
|
||||
if (!ClientSession::getInstance().isInsecureMode()) {
|
||||
Utils::error("Server public key not in whitelisted_keys. Terminating connection.");
|
||||
disconnect();
|
||||
return;
|
||||
@@ -193,16 +194,14 @@ namespace ColumnLynx::Net::TCP {
|
||||
|
||||
// Generate AES key and send confirmation
|
||||
mConnectionAESKey = Utils::LibSodiumWrapper::generateRandom256Bit();
|
||||
if (mGlobalKeyRef) { // Copy to the global reference
|
||||
std::copy(mConnectionAESKey.begin(), mConnectionAESKey.end(), mGlobalKeyRef->begin());
|
||||
}
|
||||
ClientSession::getInstance().setAESKey(mConnectionAESKey);
|
||||
AsymNonce nonce{};
|
||||
randombytes_buf(nonce.data(), nonce.size());
|
||||
|
||||
// TODO: This is pretty redundant, it should return the required type directly
|
||||
std::array<uint8_t, 32> arrayPrivateKey;
|
||||
std::copy(mLibSodiumWrapper->getXPrivateKey(),
|
||||
mLibSodiumWrapper->getXPrivateKey() + 32,
|
||||
std::copy(ClientSession::getInstance().getSodiumWrapper()->getXPrivateKey(),
|
||||
ClientSession::getInstance().getSodiumWrapper()->getXPrivateKey() + 32,
|
||||
arrayPrivateKey.begin());
|
||||
|
||||
std::vector<uint8_t> encr = Utils::LibSodiumWrapper::encryptAsymmetric(
|
||||
@@ -249,15 +248,14 @@ namespace ColumnLynx::Net::TCP {
|
||||
|
||||
Utils::log("Connection established with Session ID: " + std::to_string(mConnectionSessionID));
|
||||
|
||||
if (mSessionIDRef) { // Copy to the global reference
|
||||
*mSessionIDRef = mConnectionSessionID;
|
||||
}
|
||||
ClientSession::getInstance().setSessionID(mConnectionSessionID);
|
||||
|
||||
uint32_t clientIP = ntohl(mTunConfig.clientIP);
|
||||
uint32_t serverIP = ntohl(mTunConfig.serverIP);
|
||||
uint8_t prefixLen = mTunConfig.prefixLength;
|
||||
uint16_t mtu = mTunConfig.mtu;
|
||||
|
||||
const auto& mTun = ClientSession::getInstance().getVirtualInterface();
|
||||
if (mTun) {
|
||||
mTun->configureIP(clientIP, serverIP, prefixLen, mtu);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace ColumnLynx::Net::UDP {
|
||||
UDPPacketHeader hdr{};
|
||||
randombytes_buf(hdr.nonce.data(), hdr.nonce.size());
|
||||
|
||||
if (mAesKeyRef == nullptr || mSessionIDRef == nullptr) {
|
||||
if (ClientSession::getInstance().getAESKey().empty() || ClientSession::getInstance().getSessionID() == 0) {
|
||||
Utils::error("UDP Client AES key or Session ID reference is null!");
|
||||
return;
|
||||
}
|
||||
@@ -57,7 +57,7 @@ namespace ColumnLynx::Net::UDP {
|
||||
|
||||
auto encryptedPayload = Utils::LibSodiumWrapper::encryptMessage(
|
||||
reinterpret_cast<const uint8_t*>(data.data()), data.size(),
|
||||
*mAesKeyRef, hdr.nonce, "udp-data"
|
||||
ClientSession::getInstance().getAESKey(), hdr.nonce, "udp-data"
|
||||
//std::string(reinterpret_cast<const char*>(&mSessionIDRef), sizeof(uint64_t))
|
||||
);
|
||||
|
||||
@@ -67,9 +67,10 @@ namespace ColumnLynx::Net::UDP {
|
||||
reinterpret_cast<uint8_t*>(&hdr),
|
||||
reinterpret_cast<uint8_t*>(&hdr) + sizeof(UDPPacketHeader)
|
||||
);
|
||||
uint64_t sessionID = ClientSession::getInstance().getSessionID();
|
||||
packet.insert(packet.end(),
|
||||
reinterpret_cast<uint8_t*>(mSessionIDRef.get()),
|
||||
reinterpret_cast<uint8_t*>(mSessionIDRef.get()) + sizeof(uint64_t)
|
||||
reinterpret_cast<uint8_t*>(&sessionID),
|
||||
reinterpret_cast<uint8_t*>(&sessionID) + sizeof(uint64_t)
|
||||
);
|
||||
packet.insert(packet.end(), encryptedPayload.begin(), encryptedPayload.end());
|
||||
|
||||
@@ -120,8 +121,8 @@ namespace ColumnLynx::Net::UDP {
|
||||
uint64_t sessionID;
|
||||
std::memcpy(&sessionID, mRecvBuffer.data() + sizeof(UDPPacketHeader), sizeof(uint64_t));
|
||||
|
||||
if (sessionID != *mSessionIDRef) {
|
||||
Utils::warn("Got packet that isn't for me! Dropping!");
|
||||
if (sessionID != ClientSession::getInstance().getSessionID()) {
|
||||
Utils::warn("This packet that isn't for me! Dropping!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -131,13 +132,13 @@ namespace ColumnLynx::Net::UDP {
|
||||
mRecvBuffer.begin() + bytes
|
||||
);
|
||||
|
||||
if (mAesKeyRef == nullptr) {
|
||||
if (ClientSession::getInstance().getAESKey().empty()) {
|
||||
Utils::error("UDP Client AES key reference is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> plaintext = Utils::LibSodiumWrapper::decryptMessage(
|
||||
ciphertext.data(), ciphertext.size(), *mAesKeyRef, hdr.nonce, "udp-data"
|
||||
ciphertext.data(), ciphertext.size(), ClientSession::getInstance().getAESKey(), hdr.nonce, "udp-data"
|
||||
//std::string(reinterpret_cast<const char*>(&mSessionIDRef), sizeof(uint64_t))
|
||||
);
|
||||
|
||||
@@ -149,6 +150,7 @@ namespace ColumnLynx::Net::UDP {
|
||||
Utils::debug("UDP Client received packet from " + mRemoteEndpoint.address().to_string() + " - Packet size: " + std::to_string(bytes));
|
||||
|
||||
// Write to TUN
|
||||
const auto& mTunRef = ClientSession::getInstance().getVirtualInterface();
|
||||
if (mTunRef) {
|
||||
mTunRef->writePacket(plaintext);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user