From 15d13b6f044cbf3cb8b759ab820299ca669830a1 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Mon, 1 Dec 2025 21:13:46 +0100 Subject: [PATCH 01/10] Converted some raw pointers to smart pointers --- CMakeLists.txt | 2 +- .../columnlynx/client/net/tcp/tcp_client.hpp | 16 ++++++++-------- .../columnlynx/client/net/udp/udp_client.hpp | 8 ++++---- .../server/net/tcp/tcp_connection.hpp | 6 +++--- .../columnlynx/server/net/tcp/tcp_server.hpp | 8 ++++---- .../columnlynx/server/net/udp/udp_server.hpp | 6 +++--- src/client/main.cpp | 15 ++++++++------- src/client/net/tcp/tcp_client.cpp | 2 +- src/client/net/udp/udp_client.cpp | 10 +++++++--- src/server/main.cpp | 18 +++++++----------- 10 files changed, 46 insertions(+), 45 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8da7e3..df60fdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ endif() if(WIN32) add_compile_definitions(_WIN32_WINNT=0x0A00 NOMINMAX WIN32_LEAN_AND_MEAN) elseif(UNIX) - add_compile_options(-Wall -Wextra -Wpedantic) + add_compile_options(-Wall -Wextra -Wpedantic -O3) add_link_options(-pthread) endif() diff --git a/include/columnlynx/client/net/tcp/tcp_client.hpp b/include/columnlynx/client/net/tcp/tcp_client.hpp index 6d8720c..6778ebd 100644 --- a/include/columnlynx/client/net/tcp/tcp_client.hpp +++ b/include/columnlynx/client/net/tcp/tcp_client.hpp @@ -25,10 +25,10 @@ namespace ColumnLynx::Net::TCP { TCPClient(asio::io_context& ioContext, const std::string& host, const std::string& port, - Utils::LibSodiumWrapper* sodiumWrapper, - std::array* aesKey, - uint64_t* sessionIDRef, - bool* insecureMode, + std::shared_ptr sodiumWrapper, + std::shared_ptr> aesKey, + std::shared_ptr sessionIDRef, + bool insecureMode, std::shared_ptr tun = nullptr) : mResolver(ioContext), @@ -95,12 +95,12 @@ namespace ColumnLynx::Net::TCP { std::string mHost, mPort; uint8_t mServerPublicKey[32]; // Assuming 256-bit public key std::array mSubmittedChallenge{}; - Utils::LibSodiumWrapper* mLibSodiumWrapper; + std::shared_ptr mLibSodiumWrapper; uint64_t mConnectionSessionID; SymmetricKey mConnectionAESKey; - std::array* mGlobalKeyRef; // Reference to global AES key - uint64_t* mSessionIDRef; // Reference to global Session ID - bool* mInsecureMode; // Reference to insecure mode flag + std::shared_ptr> mGlobalKeyRef; // Reference to global AES key + std::shared_ptr 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; diff --git a/include/columnlynx/client/net/udp/udp_client.hpp b/include/columnlynx/client/net/udp/udp_client.hpp index 701eb37..86d5a68 100644 --- a/include/columnlynx/client/net/udp/udp_client.hpp +++ b/include/columnlynx/client/net/udp/udp_client.hpp @@ -17,8 +17,8 @@ namespace ColumnLynx::Net::UDP { UDPClient(asio::io_context& ioContext, const std::string& host, const std::string& port, - std::array* aesKeyRef, - uint64_t* sessionIDRef, + std::shared_ptr> aesKeyRef, + std::shared_ptr sessionIDRef, std::shared_ptr tunRef = nullptr) : mSocket(ioContext), mResolver(ioContext), mHost(host), mPort(port), mAesKeyRef(aesKeyRef), mSessionIDRef(sessionIDRef), mTunRef(tunRef) { @@ -43,8 +43,8 @@ namespace ColumnLynx::Net::UDP { asio::ip::udp::endpoint mRemoteEndpoint; std::string mHost; std::string mPort; - std::array* mAesKeyRef; - uint64_t* mSessionIDRef; + std::shared_ptr> mAesKeyRef; + std::shared_ptr mSessionIDRef; std::shared_ptr mTunRef = nullptr; std::array mRecvBuffer; // Adjust size as needed }; diff --git a/include/columnlynx/server/net/tcp/tcp_connection.hpp b/include/columnlynx/server/net/tcp/tcp_connection.hpp index 53ced26..771c2e7 100644 --- a/include/columnlynx/server/net/tcp/tcp_connection.hpp +++ b/include/columnlynx/server/net/tcp/tcp_connection.hpp @@ -25,7 +25,7 @@ namespace ColumnLynx::Net::TCP { static pointer create( asio::ip::tcp::socket socket, - Utils::LibSodiumWrapper* sodiumWrapper, + std::shared_ptr sodiumWrapper, std::function onDisconnect) { auto conn = pointer(new TCPConnection(std::move(socket), sodiumWrapper)); @@ -48,7 +48,7 @@ namespace ColumnLynx::Net::TCP { std::array getAESKey() const; private: - TCPConnection(asio::ip::tcp::socket socket, Utils::LibSodiumWrapper* sodiumWrapper) + TCPConnection(asio::ip::tcp::socket socket, std::shared_ptr sodiumWrapper) : mHandler(std::make_shared(std::move(socket))), mLibSodiumWrapper(sodiumWrapper), @@ -64,7 +64,7 @@ namespace ColumnLynx::Net::TCP { std::shared_ptr mHandler; std::function)> mOnDisconnect; - Utils::LibSodiumWrapper *mLibSodiumWrapper; + std::shared_ptr mLibSodiumWrapper; std::array mConnectionAESKey; uint64_t mConnectionSessionID; AsymPublicKey mConnectionPublicKey; diff --git a/include/columnlynx/server/net/tcp/tcp_server.hpp b/include/columnlynx/server/net/tcp/tcp_server.hpp index e45e9b6..5a0fd70 100644 --- a/include/columnlynx/server/net/tcp/tcp_server.hpp +++ b/include/columnlynx/server/net/tcp/tcp_server.hpp @@ -24,8 +24,8 @@ namespace ColumnLynx::Net::TCP { public: TCPServer(asio::io_context& ioContext, uint16_t port, - Utils::LibSodiumWrapper* sodiumWrapper, - bool* hostRunning, bool ipv4Only = false) + std::shared_ptr sodiumWrapper, + std::shared_ptr hostRunning, bool ipv4Only = false) : mIoContext(ioContext), mAcceptor(ioContext), mSodiumWrapper(sodiumWrapper), @@ -72,8 +72,8 @@ namespace ColumnLynx::Net::TCP { asio::io_context &mIoContext; asio::ip::tcp::acceptor mAcceptor; std::unordered_set mClients; - Utils::LibSodiumWrapper *mSodiumWrapper; - bool* mHostRunning; + std::shared_ptr mSodiumWrapper; + std::shared_ptr mHostRunning; std::unordered_map mRawServerConfig; }; diff --git a/include/columnlynx/server/net/udp/udp_server.hpp b/include/columnlynx/server/net/udp/udp_server.hpp index 7dfcbe3..f872fa4 100644 --- a/include/columnlynx/server/net/udp/udp_server.hpp +++ b/include/columnlynx/server/net/udp/udp_server.hpp @@ -13,7 +13,7 @@ namespace ColumnLynx::Net::UDP { class UDPServer { public: - UDPServer(asio::io_context& ioContext, uint16_t port, bool* hostRunning, bool ipv4Only = false, std::shared_ptr tun = nullptr) + UDPServer(asio::io_context& ioContext, uint16_t port, std::shared_ptr hostRunning, bool ipv4Only = false, std::shared_ptr tun = nullptr) : mSocket(ioContext), mHostRunning(hostRunning), mTun(tun) { asio::error_code ec; @@ -56,8 +56,8 @@ namespace ColumnLynx::Net::UDP { asio::ip::udp::socket mSocket; asio::ip::udp::endpoint mRemoteEndpoint; - std::array mRecvBuffer; // Adjust size as needed - bool* mHostRunning; + std::array mRecvBuffer; // 2048 seems stable + std::shared_ptr mHostRunning; std::shared_ptr mTun; }; } \ No newline at end of file diff --git a/src/client/main.cpp b/src/client/main.cpp index 7f620dc..feffdaa 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -74,16 +74,17 @@ int main(int argc, char** argv) { std::shared_ptr tun = std::make_shared(optionsObj["interface"].as()); log("Using virtual interface: " + tun->getName()); - LibSodiumWrapper sodiumWrapper = LibSodiumWrapper(); - debug("Public Key: " + Utils::bytesToHexString(sodiumWrapper.getPublicKey(), 32)); - debug("Private Key: " + Utils::bytesToHexString(sodiumWrapper.getPrivateKey(), 64)); + std::shared_ptr sodiumWrapper = std::make_shared(); + debug("Public Key: " + Utils::bytesToHexString(sodiumWrapper->getPublicKey(), 32)); + debug("Private Key: " + Utils::bytesToHexString(sodiumWrapper->getPrivateKey(), 64)); - std::array aesKey = {0}; // Defualt zeroed state until modified by handshake - uint64_t sessionID = 0; + std::shared_ptr> aesKey = std::make_shared>(); + aesKey->fill(0); // Defualt zeroed state until modified by handshake + std::shared_ptr sessionID = std::make_shared(0); asio::io_context io; - auto client = std::make_shared(io, host, port, &sodiumWrapper, &aesKey, &sessionID, &insecureMode, tun); - auto udpClient = std::make_shared(io, host, port, &aesKey, &sessionID, tun); + auto client = std::make_shared(io, host, port, sodiumWrapper, aesKey, sessionID, insecureMode, tun); + auto udpClient = std::make_shared(io, host, port, aesKey, sessionID, tun); client->start(); udpClient->start(); diff --git a/src/client/net/tcp/tcp_client.cpp b/src/client/net/tcp/tcp_client.cpp index 30a97b4..ef03e3a 100644 --- a/src/client/net/tcp/tcp_client.cpp +++ b/src/client/net/tcp/tcp_client.cpp @@ -145,7 +145,7 @@ namespace ColumnLynx::Net::TCP { // Verify pubkey against whitelisted_keys std::vector whitelistedKeys = Utils::getWhitelistedKeys(); 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 (!mInsecureMode) { Utils::error("Server public key not in whitelisted_keys. Terminating connection."); disconnect(); return; diff --git a/src/client/net/udp/udp_client.cpp b/src/client/net/udp/udp_client.cpp index 0fb0ca9..facd53e 100644 --- a/src/client/net/udp/udp_client.cpp +++ b/src/client/net/udp/udp_client.cpp @@ -36,10 +36,9 @@ namespace ColumnLynx::Net::UDP { reinterpret_cast(&hdr), reinterpret_cast(&hdr) + sizeof(UDPPacketHeader) ); - uint64_t sid = *mSessionIDRef; packet.insert(packet.end(), - reinterpret_cast(&sid), - reinterpret_cast(&sid) + sizeof(sid) + reinterpret_cast(mSessionIDRef.get()), + reinterpret_cast(mSessionIDRef.get()) + sizeof(uint64_t) ); packet.insert(packet.end(), encryptedPayload.begin(), encryptedPayload.end()); @@ -90,6 +89,11 @@ 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!"); + return; + } + // Decrypt payload std::vector ciphertext( mRecvBuffer.begin() + sizeof(UDPPacketHeader) + sizeof(uint64_t), diff --git a/src/server/main.cpp b/src/server/main.cpp index cab6884..3b6214e 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -77,7 +77,7 @@ int main(int argc, char** argv) { log("Using virtual interface: " + tun->getName()); // Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory) - LibSodiumWrapper sodiumWrapper = LibSodiumWrapper(); + std::shared_ptr sodiumWrapper = std::make_shared(); auto itPubkey = config.find("SERVER_PUBLIC_KEY"); auto itPrivkey = config.find("SERVER_PRIVATE_KEY"); @@ -91,27 +91,26 @@ int main(int argc, char** argv) { std::copy_n(Utils::hexStringToBytes(itPrivkey->second).begin(), sk.size(), sk.begin()); std::copy_n(Utils::hexStringToBytes(itPubkey->second).begin(), pk.size(), pk.begin()); - sodiumWrapper.setKeys(pk, sk); + sodiumWrapper->setKeys(pk, sk); } else { warn("No keypair found in config file! Using random key."); } - log("Server public key: " + bytesToHexString(sodiumWrapper.getPublicKey(), crypto_sign_PUBLICKEYBYTES)); - //log("Server private key: " + bytesToHexString(sodiumWrapper.getPrivateKey(), crypto_sign_SECRETKEYBYTES)); // TEMP, remove later + log("Server public key: " + bytesToHexString(sodiumWrapper->getPublicKey(), crypto_sign_PUBLICKEYBYTES)); - bool hostRunning = true; + std::shared_ptr hostRunning = std::make_shared(true); asio::io_context io; - auto server = std::make_shared(io, serverPort(), &sodiumWrapper, &hostRunning, ipv4Only); - auto udpServer = std::make_shared(io, serverPort(), &hostRunning, ipv4Only, tun); + auto server = std::make_shared(io, serverPort(), sodiumWrapper, hostRunning, ipv4Only); + auto udpServer = std::make_shared(io, serverPort(), hostRunning, ipv4Only, tun); asio::signal_set signals(io, SIGINT, SIGTERM); signals.async_wait([&](const std::error_code&, int) { log("Received termination signal. Shutting down server gracefully."); done = 1; asio::post(io, [&]() { - hostRunning = false; + *hostRunning = false; server->stop(); udpServer->stop(); }); @@ -145,9 +144,6 @@ int main(int argc, char** argv) { } log("Shutting down server..."); - /*hostRunning = false; - server->stop(); - udpServer->stop();*/ io.stop(); if (ioThread.joinable()) { From 4dbde290c2d5cbe4100ed3b13a29019a493ebcf5 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 2 Dec 2025 01:18:06 +0100 Subject: [PATCH 02/10] Update README and IPv6 preparations --- README.md | 2 ++ include/columnlynx/common/utils.hpp | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/README.md b/README.md index ab93573..449024b 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ ColumnLynx makes use of both **TCP** and **UDP**. **TCP** is used for the initia It operates on port **48042** for both TCP and UDP. +Generally, all transmission is done in **little-endian byte order**, since pretty much every single modern architecture uses it by default. The only exemption to this is the **transmission of IP addresses** (for the **Virtual Interface**), which is **big-endian**. + ### Handshake Procedure The handshake between the client and server is done over **TCP**. This is to ensure delivery without much hassle. diff --git a/include/columnlynx/common/utils.hpp b/include/columnlynx/common/utils.hpp index 83ddbec..ad84710 100644 --- a/include/columnlynx/common/utils.hpp +++ b/include/columnlynx/common/utils.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -22,6 +23,10 @@ #include #endif +namespace ColumnLynx { + using IPv6Addr = std::array; +} + namespace ColumnLynx::Utils { // General log function. Use for logging important information. void log(const std::string &msg); @@ -76,6 +81,18 @@ namespace ColumnLynx::Utils { return cbswap64(x); } + template + T cbswap128(const T& x) { + static_assert(sizeof(T) == 16, "cbswap128 requires a 128-bit type"); + + T out{}; + const uint8_t* src = reinterpret_cast(&x); + uint8_t* dst = reinterpret_cast(&out); + std::reverse_copy(src, src + 16, dst); + + return out; + } + // Returns the config file in an unordered_map format. This purely reads the config file, you still need to parse it manually. std::unordered_map getConfigMap(std::string path); }; \ No newline at end of file From eb7f4930e218e5f7588cc24b88a15ac34a5c5420 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 2 Dec 2025 16:25:42 +0100 Subject: [PATCH 03/10] Test dynamic IPv4 + Subnet masks --- README.md | 4 ++++ .../columnlynx/common/net/session_registry.hpp | 16 +++++++++------- .../columnlynx/common/net/virtual_interface.hpp | 10 ++++++++++ include/columnlynx/common/utils.hpp | 3 ++- .../server/net/tcp/tcp_connection.hpp | 8 ++++++-- .../columnlynx/server/net/tcp/tcp_server.hpp | 2 +- src/common/utils.cpp | 12 +++++++++++- src/server/server/net/tcp/tcp_connection.cpp | 17 ++++++++++++++--- src/server/server/net/tcp/tcp_server.cpp | 3 ++- 9 files changed, 59 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 449024b..034a632 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,16 @@ Configurating the server and client are are relatively easy. Currently (since th - **SERVER_PUBLIC_KEY** (Hex String): The public key to be used - **SERVER_PRIVATE_KEY** (Hex String): The private key to be used +- **NETWORK** (IPv4 Format): The network IPv4 to be used (Server Interface still needs to be configured manually) +- **SUBNET_MASK** (Integer): The subnet mask to be used (ensure proper length, it will not be checked) **Example:** ``` SERVER_PUBLIC_KEY=787B648046F10DDD0B77A6303BE42D859AA65C52F5708CC3C58EB5691F217C7B SERVER_PRIVATE_KEY=778604245F57B847E63BD85DE8208FF1A127FB559895195928C3987E246B77B8787B648046F10DDD0B77A6303BE42D859AA65C52F5708CC3C58EB5691F217C7B +NETWORK=10.10.0.0 +SUBNET_MASK=24 ```
diff --git a/include/columnlynx/common/net/session_registry.hpp b/include/columnlynx/common/net/session_registry.hpp index 20939ba..25617e6 100644 --- a/include/columnlynx/common/net/session_registry.hpp +++ b/include/columnlynx/common/net/session_registry.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -109,22 +110,23 @@ namespace ColumnLynx::Net { return static_cast(mSessions.size()); } - // IP management (simple for /24 subnet) + // IP management // Get the lowest available IPv4 address; Returns 0 if none available - uint32_t getFirstAvailableIP() const { + uint32_t getFirstAvailableIP(uint32_t baseIP, uint8_t mask) const { std::shared_lock lock(mMutex); - uint32_t baseIP = 0x0A0A0002; // 10.10.0.2 + + uint32_t hostSpace = (1u << (32 - mask)) - 2; // Usable hosts - // TODO: Expand to support larger subnets - for (uint32_t offset = 0; offset < 254; offset++) { + // Skip 0 (network) and 1 (server reserved), start at 2 + for (uint32_t offset = 2; offset <= hostSpace; offset++) { uint32_t candidateIP = baseIP + offset; if (mSessionIPs.find(candidateIP) == mSessionIPs.end()) { return candidateIP; } } - - return 0; // Unavailable + + return 0; // No available IPs } // Lock an IP as assigned to a specific session diff --git a/include/columnlynx/common/net/virtual_interface.hpp b/include/columnlynx/common/net/virtual_interface.hpp index cf3d2d4..1b19bff 100644 --- a/include/columnlynx/common/net/virtual_interface.hpp +++ b/include/columnlynx/common/net/virtual_interface.hpp @@ -60,6 +60,16 @@ namespace ColumnLynx::Net { return std::string(buf); } + static inline uint32_t stringToIpv4(const std::string &ipStr) { + struct in_addr addr; + + if (inet_pton(AF_INET, ipStr.c_str(), &addr) != 1) { + return 0; // "0.0.0.0" + } + + return ntohl(addr.s_addr); + } + static inline uint32_t prefixLengthToNetmask(uint8_t prefixLen) { if (prefixLen == 0) return 0; uint32_t mask = (0xFFFFFFFF << (32 - prefixLen)) & 0xFFFFFFFF; diff --git a/include/columnlynx/common/utils.hpp b/include/columnlynx/common/utils.hpp index ad84710..97014fa 100644 --- a/include/columnlynx/common/utils.hpp +++ b/include/columnlynx/common/utils.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #ifdef _WIN32 @@ -94,5 +95,5 @@ namespace ColumnLynx::Utils { } // Returns the config file in an unordered_map format. This purely reads the config file, you still need to parse it manually. - std::unordered_map getConfigMap(std::string path); + std::unordered_map getConfigMap(std::string path, std::vector requiredKeys = {}); }; \ No newline at end of file diff --git a/include/columnlynx/server/net/tcp/tcp_connection.hpp b/include/columnlynx/server/net/tcp/tcp_connection.hpp index 771c2e7..4b93140 100644 --- a/include/columnlynx/server/net/tcp/tcp_connection.hpp +++ b/include/columnlynx/server/net/tcp/tcp_connection.hpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace ColumnLynx::Net::TCP { class TCPConnection : public std::enable_shared_from_this { @@ -26,9 +27,10 @@ namespace ColumnLynx::Net::TCP { static pointer create( asio::ip::tcp::socket socket, std::shared_ptr sodiumWrapper, + std::unordered_map* serverConfig, std::function onDisconnect) { - auto conn = pointer(new TCPConnection(std::move(socket), sodiumWrapper)); + auto conn = pointer(new TCPConnection(std::move(socket), sodiumWrapper, serverConfig)); conn->mOnDisconnect = std::move(onDisconnect); return conn; } @@ -48,10 +50,11 @@ namespace ColumnLynx::Net::TCP { std::array getAESKey() const; private: - TCPConnection(asio::ip::tcp::socket socket, std::shared_ptr sodiumWrapper) + TCPConnection(asio::ip::tcp::socket socket, std::shared_ptr sodiumWrapper, std::unordered_map* serverConfig) : mHandler(std::make_shared(std::move(socket))), mLibSodiumWrapper(sodiumWrapper), + mRawServerConfig(serverConfig), mHeartbeatTimer(mHandler->socket().get_executor()), mLastHeartbeatReceived(std::chrono::steady_clock::now()), mLastHeartbeatSent(std::chrono::steady_clock::now()) @@ -65,6 +68,7 @@ namespace ColumnLynx::Net::TCP { std::shared_ptr mHandler; std::function)> mOnDisconnect; std::shared_ptr mLibSodiumWrapper; + std::unordered_map* mRawServerConfig; std::array mConnectionAESKey; uint64_t mConnectionSessionID; AsymPublicKey mConnectionPublicKey; diff --git a/include/columnlynx/server/net/tcp/tcp_server.hpp b/include/columnlynx/server/net/tcp/tcp_server.hpp index 5a0fd70..364a727 100644 --- a/include/columnlynx/server/net/tcp/tcp_server.hpp +++ b/include/columnlynx/server/net/tcp/tcp_server.hpp @@ -32,7 +32,7 @@ namespace ColumnLynx::Net::TCP { mHostRunning(hostRunning) { // Preload the config map - mRawServerConfig = Utils::getConfigMap("server_config"); + mRawServerConfig = Utils::getConfigMap("server_config", {"NETWORK", "SUBNET_MASK"}); asio::error_code ec; diff --git a/src/common/utils.cpp b/src/common/utils.cpp index eff056b..03e0797 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -118,7 +118,7 @@ namespace ColumnLynx::Utils { return out; } - std::unordered_map getConfigMap(std::string path) { + std::unordered_map getConfigMap(std::string path, std::vector requiredKeys) { // TODO: Currently re-reads every time. std::vector readLines; @@ -129,6 +129,16 @@ namespace ColumnLynx::Utils { readLines.push_back(line); } + if (!requiredKeys.empty()) { + // Check if they exist using unordered_set magic + std::unordered_set setA(readLines.begin(), readLines.end()); + for (std::string x : requiredKeys) { + if (!setA.count(x)) { + throw std::runtime_error("Config doesn't contain all required keys! (Missing: '" + x + "')"); + } + } + } + // Parse them into the struct std::unordered_map config; char delimiter = '='; diff --git a/src/server/server/net/tcp/tcp_connection.cpp b/src/server/server/net/tcp/tcp_connection.cpp index 891cf63..71d2f0d 100644 --- a/src/server/server/net/tcp/tcp_connection.cpp +++ b/src/server/server/net/tcp/tcp_connection.cpp @@ -202,7 +202,18 @@ namespace ColumnLynx::Net::TCP { // Encrypt the Session ID with the established AES key (using symmetric encryption, nonce can be all zeros for this purpose) Nonce symNonce{}; // All zeros - uint32_t clientIP = SessionRegistry::getInstance().getFirstAvailableIP(); + std::string networkString = mRawServerConfig->find("NETWORK")->second; // The load check guarantees that this value exists + uint8_t configMask = std::stoi(mRawServerConfig->find("SUBNET_MASK")->second); // Same deal here + + uint32_t baseIP = Net::VirtualInterface::stringToIpv4(networkString); + + if (baseIP == 0) { + Utils::warn("Your NETWORK value in the server configuration is malformed! I will not be able to accept connections! (Connection " + reqAddr + " was killed)"); + disconnect(); + return; + } + + uint32_t clientIP = SessionRegistry::getInstance().getFirstAvailableIP(baseIP, configMask); if (clientIP == 0) { Utils::warn("Out of available IPs! Disconnecting client " + reqAddr); @@ -214,8 +225,8 @@ namespace ColumnLynx::Net::TCP { tunConfig.version = Utils::protocolVersion(); tunConfig.prefixLength = 24; tunConfig.mtu = 1420; - tunConfig.serverIP = htonl(0x0A0A0001); // 10.10.0.1 - tunConfig.clientIP = htonl(clientIP); // 10.10.0.X + tunConfig.serverIP = htonl(baseIP + 1); // e.g. 10.10.0.1 + tunConfig.clientIP = htonl(clientIP); // e.g. 10.10.0.X tunConfig.dns1 = htonl(0x08080808); // 8.8.8.8 tunConfig.dns2 = 0; diff --git a/src/server/server/net/tcp/tcp_server.cpp b/src/server/server/net/tcp/tcp_server.cpp index 3f71670..05735a9 100644 --- a/src/server/server/net/tcp/tcp_server.cpp +++ b/src/server/server/net/tcp/tcp_server.cpp @@ -31,10 +31,11 @@ namespace ColumnLynx::Net::TCP { mStartAccept(); return; } - + auto client = TCPConnection::create( std::move(socket), mSodiumWrapper, + &mRawServerConfig, [this](std::shared_ptr c) { mClients.erase(c); Utils::log("Client removed."); From 0ba78d72edf1b93fa7a125becae98f5f3659c900 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 2 Dec 2025 16:30:56 +0100 Subject: [PATCH 04/10] Test - Updated map check --- src/common/utils.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 03e0797..fc9912b 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -129,16 +129,6 @@ namespace ColumnLynx::Utils { readLines.push_back(line); } - if (!requiredKeys.empty()) { - // Check if they exist using unordered_set magic - std::unordered_set setA(readLines.begin(), readLines.end()); - for (std::string x : requiredKeys) { - if (!setA.count(x)) { - throw std::runtime_error("Config doesn't contain all required keys! (Missing: '" + x + "')"); - } - } - } - // Parse them into the struct std::unordered_map config; char delimiter = '='; @@ -155,6 +145,14 @@ namespace ColumnLynx::Utils { config.insert({ key, val }); } + if (!requiredKeys.empty()) { + for (std::string x : requiredKeys) { + if (config.find(x) == config.end()) { + throw std::runtime_error("Config doesn't contain all required keys! (Missing: '" + x + "')"); + } + } + } + return config; } } \ No newline at end of file From 265954d700e68d3bb8ee8e8eb9e5156f2c782b92 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 2 Dec 2025 17:00:13 +0100 Subject: [PATCH 05/10] Test - fixed session ip locking --- .../common/net/session_registry.hpp | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/include/columnlynx/common/net/session_registry.hpp b/include/columnlynx/common/net/session_registry.hpp index 25617e6..6e1589c 100644 --- a/include/columnlynx/common/net/session_registry.hpp +++ b/include/columnlynx/common/net/session_registry.hpp @@ -115,30 +115,36 @@ namespace ColumnLynx::Net { // Get the lowest available IPv4 address; Returns 0 if none available uint32_t getFirstAvailableIP(uint32_t baseIP, uint8_t mask) const { std::shared_lock lock(mMutex); - - uint32_t hostSpace = (1u << (32 - mask)) - 2; // Usable hosts - // Skip 0 (network) and 1 (server reserved), start at 2 - for (uint32_t offset = 2; offset <= hostSpace; offset++) { + uint32_t hostCount = (1u << (32 - mask)); + uint32_t firstHost = 2; + uint32_t lastHost = hostCount - 2; + + for (uint32_t offset = firstHost; offset <= lastHost; offset++) { uint32_t candidateIP = baseIP + offset; - if (mSessionIPs.find(candidateIP) == mSessionIPs.end()) { + if (mIPSessions.find(candidateIP) == mIPSessions.end()) { return candidateIP; } } - return 0; // No available IPs + return 0; } - // Lock an IP as assigned to a specific session void lockIP(uint64_t sessionID, uint32_t ip) { std::unique_lock lock(mMutex); mSessionIPs[sessionID] = ip; + mIPSessions[ip] = mSessions.find(sessionID)->second; } - // Unlock the IP associated with a given session void deallocIP(uint64_t sessionID) { std::unique_lock lock(mMutex); - mSessionIPs.erase(sessionID); + + auto it = mSessionIPs.find(sessionID); + if (it != mSessionIPs.end()) { + uint32_t ip = it->second; + mIPSessions.erase(ip); + mSessionIPs.erase(it); + } } private: From 210f9f2436f9d36d596739dabc56bb634d3ca655 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 2 Dec 2025 17:08:40 +0100 Subject: [PATCH 06/10] Test2 --- include/columnlynx/common/net/session_registry.hpp | 4 ++++ src/server/server/net/tcp/tcp_connection.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/columnlynx/common/net/session_registry.hpp b/include/columnlynx/common/net/session_registry.hpp index 6e1589c..630f088 100644 --- a/include/columnlynx/common/net/session_registry.hpp +++ b/include/columnlynx/common/net/session_registry.hpp @@ -133,6 +133,10 @@ namespace ColumnLynx::Net { void lockIP(uint64_t sessionID, uint32_t ip) { std::unique_lock lock(mMutex); mSessionIPs[sessionID] = ip; + + if (mIPSessions.find(sessionID) == mIPSessions.end()) { + Utils::debug("yikes"); + } mIPSessions[ip] = mSessions.find(sessionID)->second; } diff --git a/src/server/server/net/tcp/tcp_connection.cpp b/src/server/server/net/tcp/tcp_connection.cpp index 71d2f0d..db6bfe1 100644 --- a/src/server/server/net/tcp/tcp_connection.cpp +++ b/src/server/server/net/tcp/tcp_connection.cpp @@ -229,7 +229,7 @@ namespace ColumnLynx::Net::TCP { tunConfig.clientIP = htonl(clientIP); // e.g. 10.10.0.X tunConfig.dns1 = htonl(0x08080808); // 8.8.8.8 tunConfig.dns2 = 0; - + SessionRegistry::getInstance().lockIP(mConnectionSessionID, clientIP); uint64_t sessionIDNet = Utils::chtobe64(mConnectionSessionID); From 3bda0b2ec45d2f2d6619cb244076f09b639f5052 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 2 Dec 2025 17:11:03 +0100 Subject: [PATCH 07/10] Test3 --- include/columnlynx/common/net/session_registry.hpp | 4 ++-- src/server/server/net/tcp/tcp_connection.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/columnlynx/common/net/session_registry.hpp b/include/columnlynx/common/net/session_registry.hpp index 630f088..11449ae 100644 --- a/include/columnlynx/common/net/session_registry.hpp +++ b/include/columnlynx/common/net/session_registry.hpp @@ -134,9 +134,9 @@ namespace ColumnLynx::Net { std::unique_lock lock(mMutex); mSessionIPs[sessionID] = ip; - if (mIPSessions.find(sessionID) == mIPSessions.end()) { + /*if (mIPSessions.find(sessionID) == mIPSessions.end()) { Utils::debug("yikes"); - } + }*/ mIPSessions[ip] = mSessions.find(sessionID)->second; } diff --git a/src/server/server/net/tcp/tcp_connection.cpp b/src/server/server/net/tcp/tcp_connection.cpp index db6bfe1..34d7094 100644 --- a/src/server/server/net/tcp/tcp_connection.cpp +++ b/src/server/server/net/tcp/tcp_connection.cpp @@ -229,8 +229,6 @@ namespace ColumnLynx::Net::TCP { tunConfig.clientIP = htonl(clientIP); // e.g. 10.10.0.X tunConfig.dns1 = htonl(0x08080808); // 8.8.8.8 tunConfig.dns2 = 0; - - SessionRegistry::getInstance().lockIP(mConnectionSessionID, clientIP); uint64_t sessionIDNet = Utils::chtobe64(mConnectionSessionID); @@ -249,6 +247,7 @@ namespace ColumnLynx::Net::TCP { Utils::log("Handshake with " + reqAddr + " completed successfully. Session ID assigned (" + std::to_string(mConnectionSessionID) + ")."); auto session = std::make_shared(mConnectionAESKey, std::chrono::hours(12), clientIP, htonl(0x0A0A0001), mConnectionSessionID); SessionRegistry::getInstance().put(mConnectionSessionID, std::move(session)); + SessionRegistry::getInstance().lockIP(mConnectionSessionID, clientIP); } catch (const std::exception& e) { Utils::error("Failed to decrypt HANDSHAKE_EXCHANGE_KEY from " + reqAddr + ": " + e.what()); From 4f4a0fd38523fb5dbeec586dc00127e242cab53e Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 2 Dec 2025 17:23:02 +0100 Subject: [PATCH 08/10] Wipe tuns before adding --- src/common/virtual_interface.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/common/virtual_interface.cpp b/src/common/virtual_interface.cpp index f6c9666..b19f3a6 100644 --- a/src/common/virtual_interface.cpp +++ b/src/common/virtual_interface.cpp @@ -166,6 +166,13 @@ namespace ColumnLynx::Net { std::string ipStr = ipv4ToString(clientIP); std::string peerStr = ipv4ToString(serverIP); + // Wipe the current config + snprintf(cmd, sizeof(cmd), + "ip addr flush dev %s", + mIfName.c_str() + ); + system(cmd); + snprintf(cmd, sizeof(cmd), "ip addr add %s/%d peer %s dev %s", ipStr.c_str(), prefixLen, peerStr.c_str(), mIfName.c_str()); @@ -190,10 +197,23 @@ namespace ColumnLynx::Net { std::string peerStr = ipv4ToString(serverIP); std::string prefixStr = ipv4ToString(prefixLen); - // Set netmask (/24 CIDR temporarily with raw command, improve later) + // Reset snprintf(cmd, sizeof(cmd), - "ifconfig lynx0 %s %s mtu %d netmask %s up", - ipStr.c_str(), peerStr.c_str(), mtu, prefixStr.c_str()); + "ifconfig %s inet 0.0.0.0 delete", + mIfName.c_str() + ); + system(cmd); + + snprintf(cmd, sizeof(cmd), + "ifconfig %s inet6 :: delete", + mIfName.c_str() + ); + system(cmd); + + // Set + snprintf(cmd, sizeof(cmd), + "ifconfig %s %s %s mtu %d netmask %s up", + mIfName.c_str(), ipStr.c_str(), peerStr.c_str(), mtu, prefixStr.c_str()); system(cmd); Utils::log("Executed command: " + std::string(cmd)); From 8ed90a3bc88768bf28fa25a01e98c686e61d0c0c Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 2 Dec 2025 18:44:57 +0100 Subject: [PATCH 09/10] Alpha 0.6 - Dynamic IPs --- CMakeLists.txt | 4 +++- src/common/utils.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index df60fdc..626a3db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,9 @@ set(CMAKE_CXX_EXTENSIONS OFF) #set(CMAKE_CXX_FLAGS_DEBUG "-g") #add_compile_options(${CMAKE_CXX_FLAGS_DEBUG}) -add_compile_definitions(DEBUG=1) # TODO: Forcing for now, add dymanic based on compile flags later +if(DEBUG) + add_compile_definitions(DEBUG=1) # TODO: Forcing for now, add dymanic based on compile flags later +endif() include(FetchContent) diff --git a/src/common/utils.cpp b/src/common/utils.cpp index fc9912b..7a1c992 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -49,7 +49,7 @@ namespace ColumnLynx::Utils { } std::string getVersion() { - return "a0.5"; + return "a0.6"; } unsigned short serverPort() { From 668b96e8e0b2c216d90611a04bfaba0918144aa2 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 2 Dec 2025 18:45:50 +0100 Subject: [PATCH 10/10] Alpha 0.6 - Dynamic IPs --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 626a3db..d4e640f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.16) # If MAJOR is 0, and MINOR > 0, Version is BETA project(ColumnLynx - VERSION 0.0.5 + VERSION 0.0.6 LANGUAGES CXX )