From d2242ebbc770237fddf63dc0af2c3af07e1f5121 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Wed, 26 Nov 2025 21:52:01 +0100 Subject: [PATCH] Comment some code --- .../columnlynx/client/net/tcp/tcp_client.hpp | 9 + .../columnlynx/client/net/udp/udp_client.hpp | 5 + .../columnlynx/common/libsodium_wrapper.hpp | 188 +++++++++--------- .../common/net/session_registry.hpp | 36 ++-- .../common/net/udp/udp_message_type.hpp | 1 + include/columnlynx/common/panic_handler.hpp | 3 +- include/columnlynx/common/utils.hpp | 13 +- .../server/net/tcp/tcp_connection.hpp | 8 + .../columnlynx/server/net/tcp/tcp_server.hpp | 3 + .../columnlynx/server/net/udp/udp_server.hpp | 5 + src/common/virtual_interface.cpp | 2 + src/server/server/net/tcp/tcp_connection.cpp | 7 + 12 files changed, 174 insertions(+), 106 deletions(-) diff --git a/include/columnlynx/client/net/tcp/tcp_client.hpp b/include/columnlynx/client/net/tcp/tcp_client.hpp index d27b771..e6516df 100644 --- a/include/columnlynx/client/net/tcp/tcp_client.hpp +++ b/include/columnlynx/client/net/tcp/tcp_client.hpp @@ -63,17 +63,26 @@ namespace ColumnLynx::Net::TCP { } } + // Starts the TCP Client and initiaties the handshake void start(); + // Sends a TCP message to the server void sendMessage(ClientMessageType type, const std::string& data = ""); + // Attempt to gracefully disconnect from the server void disconnect(bool echo = true); + // Get the handshake status bool isHandshakeComplete() const; + // Get the connection status bool isConnected() const; private: + // Start the heartbeat routine void mStartHeartbeat(); + // Handle an incoming TCP message void mHandleMessage(ServerMessageType type, const std::string& data); + // TODO: Move ptrs to smart ptrs + bool mConnected = false; bool mHandshakeComplete = false; tcp::resolver mResolver; diff --git a/include/columnlynx/client/net/udp/udp_client.hpp b/include/columnlynx/client/net/udp/udp_client.hpp index 3eed06e..9d7717a 100644 --- a/include/columnlynx/client/net/udp/udp_client.hpp +++ b/include/columnlynx/client/net/udp/udp_client.hpp @@ -25,12 +25,17 @@ namespace ColumnLynx::Net::UDP { mStartReceive(); } + // Start the UDP client void start(); + // Send a UDP message void sendMessage(const std::string& data = ""); + // Stop the UDP client void stop(); private: + // Start the UDP listener routine void mStartReceive(); + // Handle an incoming UDP message void mHandlePacket(std::size_t bytes); asio::ip::udp::socket mSocket; diff --git a/include/columnlynx/common/libsodium_wrapper.hpp b/include/columnlynx/common/libsodium_wrapper.hpp index ff4b117..9ce7204 100644 --- a/include/columnlynx/common/libsodium_wrapper.hpp +++ b/include/columnlynx/common/libsodium_wrapper.hpp @@ -35,12 +35,14 @@ namespace ColumnLynx::Utils { public: LibSodiumWrapper(); + // These are pretty self-explanatory + uint8_t* getPublicKey(); uint8_t* getPrivateKey(); uint8_t* getXPublicKey() { return mXPublicKey.data(); } uint8_t* getXPrivateKey() { return mXPrivateKey.data(); } - // Dangerous! + // Set the Asymmetric signing keypair. This also regenerates the corresponding encryption keypair; Dangerous! void setKeys(PublicKey pk, PrivateKey sk) { mPublicKey = pk; mPrivateKey = sk; @@ -54,18 +56,14 @@ namespace ColumnLynx::Utils { // Generates a random 256-bit (32-byte) array static std::array generateRandom256Bit(); - + + // Sign a message with the stored private key static inline Signature signMessage(const uint8_t* msg, size_t len, const PrivateKey& sk) { Signature sig{}; crypto_sign_detached(sig.data(), nullptr, msg, len, sk.data()); return sig; } - static inline bool verifyMessage(const uint8_t* msg, size_t len, - const Signature& sig, const PublicKey& pk) { - return crypto_sign_verify_detached(sig.data(), msg, len, pk.data()) == 0; - } - // Overloads for std::string / std::array static inline Signature signMessage(const std::string& msg, const PrivateKey& sk) { return signMessage(reinterpret_cast(msg.data()), msg.size(), sk); @@ -82,6 +80,11 @@ namespace ColumnLynx::Utils { return sig; } + // Verify a message with a given public key + static inline bool verifyMessage(const uint8_t* msg, size_t len, const Signature& sig, const PublicKey& pk) { + return crypto_sign_verify_detached(sig.data(), msg, len, pk.data()) == 0; + } + static inline bool verifyMessage(const std::string& msg, const Signature& sig, const PublicKey& pk) { return verifyMessage(reinterpret_cast(msg.data()), msg.size(), sig, pk); } @@ -96,7 +99,7 @@ namespace ColumnLynx::Utils { return crypto_sign_verify_detached(sig.data(), msg, len, pk_raw) == 0; } - // Encrypt with ChaCha20-Poly1305 (returns ciphertext as bytes) + // Encrypt symmetrically with ChaCha20-Poly1305; returns ciphertext as bytes static inline std::vector encryptMessage( const uint8_t* plaintext, size_t len, const SymmetricKey& key, const Nonce& nonce, @@ -119,7 +122,7 @@ namespace ColumnLynx::Utils { return ciphertext; } - // Decrypt with ChaCha20-Poly1305 (returns plaintext as bytes) + // Decrypt symmetrically with ChaCha20-Poly1305; Returns plaintext as bytes static inline std::vector decryptMessage( const uint8_t* ciphertext, size_t len, const SymmetricKey& key, const Nonce& nonce, @@ -145,12 +148,14 @@ namespace ColumnLynx::Utils { return plaintext; } + // Returns a random nonce static inline Nonce generateNonce() { Nonce n{}; randombytes_buf(n.data(), n.size()); return n; } + // Encrypt message asymmetrically; Returns ciphertext as bytes static inline std::vector encryptAsymmetric( const uint8_t* plaintext, size_t len, const AsymNonce& nonce, @@ -171,6 +176,7 @@ namespace ColumnLynx::Utils { return ciphertext; } + // Decrypt message asymmetrically; Returns plaintext as bytes static inline std::vector decryptAsymmetric( const uint8_t* ciphertext, size_t len, const AsymNonce& nonce, @@ -194,97 +200,99 @@ namespace ColumnLynx::Utils { return plaintext; } - static inline bool verifyCertificateWithSystemCAs(const std::vector& cert_der) { - // Parse DER-encoded certificate - const unsigned char* p = cert_der.data(); - std::unique_ptr cert( - d2i_X509(nullptr, &p, cert_der.size()), X509_free - ); - if (!cert) { - return false; + // Verify a public key (certificate) against system-installed CAs + static inline bool verifyCertificateWithSystemCAs(const std::vector& cert_der) { + // Parse DER-encoded certificate + const unsigned char* p = cert_der.data(); + std::unique_ptr cert( + d2i_X509(nullptr, &p, cert_der.size()), X509_free + ); + if (!cert) { + return false; + } + + // Create a certificate store + std::unique_ptr store( + X509_STORE_new(), X509_STORE_free + ); + if (!store) { + return false; + } + + // Load system default CA paths (/etc/ssl/certs, etc.) + if (X509_STORE_set_default_paths(store.get()) != 1) { + return false; + } + + // Create a verification context + std::unique_ptr ctx( + X509_STORE_CTX_new(), X509_STORE_CTX_free + ); + if (!ctx) { + return false; + } + + // Initialize verification context + if (X509_STORE_CTX_init(ctx.get(), store.get(), cert.get(), nullptr) != 1) { + return false; + } + + // Perform the actual certificate verification + int result = X509_verify_cert(ctx.get()); + return result == 1; } - - // Create a certificate store - std::unique_ptr store( - X509_STORE_new(), X509_STORE_free - ); - if (!store) { - return false; - } - - // Load system default CA paths (/etc/ssl/certs, etc.) - if (X509_STORE_set_default_paths(store.get()) != 1) { - return false; - } - - // Create a verification context - std::unique_ptr ctx( - X509_STORE_CTX_new(), X509_STORE_CTX_free - ); - if (!ctx) { - return false; - } - - // Initialize verification context - if (X509_STORE_CTX_init(ctx.get(), store.get(), cert.get(), nullptr) != 1) { - return false; - } - - // Perform the actual certificate verification - int result = X509_verify_cert(ctx.get()); - return result == 1; - } - static inline std::vector getCertificateHostname(const std::vector& cert_der) { - std::vector names; + // Extract the hostnames (Subject Alternative Names and Common Names) out of a public key (certificate) + static inline std::vector getCertificateHostname(const std::vector& cert_der) { + std::vector names; - if (cert_der.empty()) - return names; + if (cert_der.empty()) + return names; - // Parse DER certificate - const unsigned char* p = cert_der.data(); - X509* cert = d2i_X509(nullptr, &p, cert_der.size()); - if (!cert) - return names; + // Parse DER certificate + const unsigned char* p = cert_der.data(); + X509* cert = d2i_X509(nullptr, &p, cert_der.size()); + if (!cert) + return names; - // --- Subject Alternative Names (SAN) --- - GENERAL_NAMES* san_names = - (GENERAL_NAMES*)X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr); + // --- Subject Alternative Names (SAN) --- + GENERAL_NAMES* san_names = + (GENERAL_NAMES*)X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr); - if (san_names) { - int san_count = sk_GENERAL_NAME_num(san_names); - for (int i = 0; i < san_count; i++) { - const GENERAL_NAME* current = sk_GENERAL_NAME_value(san_names, i); - if (current->type == GEN_DNS) { - const char* dns_name = (const char*)ASN1_STRING_get0_data(current->d.dNSName); - // Safety: ensure no embedded nulls - if (ASN1_STRING_length(current->d.dNSName) == (int)std::strlen(dns_name)) { - names.emplace_back(dns_name); + if (san_names) { + int san_count = sk_GENERAL_NAME_num(san_names); + for (int i = 0; i < san_count; i++) { + const GENERAL_NAME* current = sk_GENERAL_NAME_value(san_names, i); + if (current->type == GEN_DNS) { + const char* dns_name = (const char*)ASN1_STRING_get0_data(current->d.dNSName); + // Safety: ensure no embedded nulls + if (ASN1_STRING_length(current->d.dNSName) == (int)std::strlen(dns_name)) { + names.emplace_back(dns_name); + } + } + } + GENERAL_NAMES_free(san_names); + } + + // --- Fallback: Common Name (CN) --- + if (names.empty()) { + X509_NAME* subject = X509_get_subject_name(cert); + if (subject) { + int idx = X509_NAME_get_index_by_NID(subject, NID_commonName, -1); + if (idx >= 0) { + X509_NAME_ENTRY* entry = X509_NAME_get_entry(subject, idx); + ASN1_STRING* cn_asn1 = X509_NAME_ENTRY_get_data(entry); + const char* cn_str = (const char*)ASN1_STRING_get0_data(cn_asn1); + if (ASN1_STRING_length(cn_asn1) == (int)std::strlen(cn_str)) { + names.emplace_back(cn_str); + } } } } - GENERAL_NAMES_free(san_names); + + X509_free(cert); + return names; } - - // --- Fallback: Common Name (CN) --- - if (names.empty()) { - X509_NAME* subject = X509_get_subject_name(cert); - if (subject) { - int idx = X509_NAME_get_index_by_NID(subject, NID_commonName, -1); - if (idx >= 0) { - X509_NAME_ENTRY* entry = X509_NAME_get_entry(subject, idx); - ASN1_STRING* cn_asn1 = X509_NAME_ENTRY_get_data(entry); - const char* cn_str = (const char*)ASN1_STRING_get0_data(cn_asn1); - if (ASN1_STRING_length(cn_asn1) == (int)std::strlen(cn_str)) { - names.emplace_back(cn_str); - } - } - } - } - - X509_free(cert); - return names; - } private: std::array mPublicKey; diff --git a/include/columnlynx/common/net/session_registry.hpp b/include/columnlynx/common/net/session_registry.hpp index 533d0ba..d4699ff 100644 --- a/include/columnlynx/common/net/session_registry.hpp +++ b/include/columnlynx/common/net/session_registry.hpp @@ -14,16 +14,16 @@ namespace ColumnLynx::Net { struct SessionState { - SymmetricKey aesKey; // Immutable after creation + SymmetricKey aesKey; // Agreed-upon AES-256 kes for that session; Immutable after creation std::atomic send_ctr{0}; // Per-direction counters - std::atomic recv_ctr{0}; - asio::ip::udp::endpoint udpEndpoint; - std::atomic sendCounter{0}; - std::chrono::steady_clock::time_point created = std::chrono::steady_clock::now(); - std::chrono::steady_clock::time_point expires{}; - uint32_t clientTunIP; - uint32_t serverTunIP; - uint64_t sessionID; + std::atomic recv_ctr{0}; // Per-direction counters + asio::ip::udp::endpoint udpEndpoint; // Deducted IP + Port of that session client + std::atomic sendCounter{0}; // Counter of sent messages + std::chrono::steady_clock::time_point created = std::chrono::steady_clock::now(); // Time created + std::chrono::steady_clock::time_point expires{}; // Time of expiry + uint32_t clientTunIP; // Assigned IP + uint32_t serverTunIP; // Server IP + uint64_t sessionID; // Session ID Nonce base_nonce{}; ~SessionState() { sodium_memzero(aesKey.data(), aesKey.size()); } @@ -36,6 +36,7 @@ namespace ColumnLynx::Net { expires = created + ttl; } + // Set the UDP endpoint void setUDPEndpoint(const asio::ip::udp::endpoint& ep) { udpEndpoint = ep; } @@ -43,28 +44,31 @@ namespace ColumnLynx::Net { class SessionRegistry { public: + // Return a reference to the Session Registry instance static SessionRegistry& getInstance() { static SessionRegistry instance; return instance; } - // Insert or replace + // Insert or replace a session entry void put(uint64_t sessionID, std::shared_ptr state) { std::unique_lock lock(mMutex); mSessions[sessionID] = std::move(state); mIPSessions[mSessions[sessionID]->clientTunIP] = mSessions[sessionID]; } - // Lookup + // Lookup a session entry by session ID std::shared_ptr get(uint64_t sessionID) const { std::shared_lock lock(mMutex); auto it = mSessions.find(sessionID); return (it == mSessions.end()) ? nullptr : it->second; } + // Lookup a session entry by IPv4 std::shared_ptr getByIP(uint32_t ip) const { std::shared_lock lock(mMutex); auto it = mIPSessions.find(ip); return (it == mIPSessions.end()) ? nullptr : it->second; } + // Get a snapshot of the Session Registry std::unordered_map> snapshot() const { std::unordered_map> snap; std::shared_lock lock(mMutex); @@ -72,7 +76,7 @@ namespace ColumnLynx::Net { return snap; } - // Remove + // Remove a session by ID void erase(uint64_t sessionID) { std::unique_lock lock(mMutex); mSessions.erase(sessionID); @@ -99,6 +103,7 @@ namespace ColumnLynx::Net { } } + // Get the number of registered sessions int size() const { std::shared_lock lock(mMutex); return static_cast(mSessions.size()); @@ -106,6 +111,7 @@ namespace ColumnLynx::Net { // IP management (simple for /24 subnet) + // Get the lowest available IPv4 address; Returns 0 if none available uint32_t getFirstAvailableIP() const { std::shared_lock lock(mMutex); uint32_t baseIP = 0x0A0A0002; // 10.10.0.2 @@ -115,15 +121,19 @@ namespace ColumnLynx::Net { uint32_t candidateIP = baseIP + offset; if (mSessionIPs.find(candidateIP) == mSessionIPs.end()) { return candidateIP; - } + } + + return 0; // Unavailable } } + // 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; } + // Unlock the IP associated with a given session void deallocIP(uint64_t sessionID) { std::unique_lock lock(mMutex); mSessionIPs.erase(sessionID); diff --git a/include/columnlynx/common/net/udp/udp_message_type.hpp b/include/columnlynx/common/net/udp/udp_message_type.hpp index 5f086cf..85ef240 100644 --- a/include/columnlynx/common/net/udp/udp_message_type.hpp +++ b/include/columnlynx/common/net/udp/udp_message_type.hpp @@ -9,6 +9,7 @@ #include namespace ColumnLynx::Net::UDP { + // @deprecated // Shared between server and client enum class MessageType : uint8_t { PING = 0x01, diff --git a/include/columnlynx/common/panic_handler.hpp b/include/columnlynx/common/panic_handler.hpp index aaaf6ab..8e9867c 100644 --- a/include/columnlynx/common/panic_handler.hpp +++ b/include/columnlynx/common/panic_handler.hpp @@ -184,7 +184,7 @@ namespace ColumnLynx::Utils { out << "----------------------\n"; } - //Panic the main thread and instantly halt execution. This produces a stack trace dump. Do not use by itself, throw an error instead. + // Panic the main thread and instantly halt execution. This produces a stack trace dump. Do not use by itself, throw an error instead. static void panic(const std::string& reason) { std::cerr << "\n***\033[31m MAIN THREAD PANIC! \033[0m***\n"; std::cerr << "Reason: " << reason << "\n"; @@ -204,6 +204,7 @@ namespace ColumnLynx::Utils { std::cerr << "Panic trace written to panic_dump.txt\n"; } + // Gets the current time static std::string currentTime() { std::time_t t = std::time(nullptr); char buf[64]; diff --git a/include/columnlynx/common/utils.hpp b/include/columnlynx/common/utils.hpp index 7ac4e59..83ddbec 100644 --- a/include/columnlynx/common/utils.hpp +++ b/include/columnlynx/common/utils.hpp @@ -23,12 +23,18 @@ #endif namespace ColumnLynx::Utils { + // General log function. Use for logging important information. void log(const std::string &msg); + // General warning function. Use for logging important warnings. void warn(const std::string &msg); + // General error function. Use for logging failures and general errors. void error(const std::string &msg); + // Debug log function. Use for logging non-important information. These will not print unless the binary is compiled with DEBUG=1 void debug(const std::string &msg); + // Returns the hostname of the running platform. std::string getHostname(); + // Returns the version of the running release. std::string getVersion(); unsigned short serverPort(); unsigned char protocolVersion(); @@ -36,6 +42,7 @@ namespace ColumnLynx::Utils { // Raw byte to hex string conversion helper std::string bytesToHexString(const uint8_t* bytes, size_t length); + // Hex string to raw byte conversion helper std::vector hexStringToBytes(const std::string& hex); // uint8_t to raw string conversion helper @@ -59,12 +66,14 @@ namespace ColumnLynx::Utils { ((x & 0xFF00000000000000ULL) >> 56); } + // host -> big-endian (for little-endian hosts) - 64 bit inline constexpr uint64_t chtobe64(uint64_t x) { - return cbswap64(x); // host -> big-endian (for little-endian hosts) + return cbswap64(x); } + // big-endian -> host (for little-endian hosts) - 64 bit inline constexpr uint64_t cbe64toh(uint64_t x) { - return cbswap64(x); // big-endian -> host (for little-endian hosts) + return cbswap64(x); } // Returns the config file in an unordered_map format. This purely reads the config file, you still need to parse it manually. diff --git a/include/columnlynx/server/net/tcp/tcp_connection.hpp b/include/columnlynx/server/net/tcp/tcp_connection.hpp index 0ac06bf..c60b8c8 100644 --- a/include/columnlynx/server/net/tcp/tcp_connection.hpp +++ b/include/columnlynx/server/net/tcp/tcp_connection.hpp @@ -33,12 +33,18 @@ namespace ColumnLynx::Net::TCP { return conn; } + // Start a TCP Connection (Handler for an incoming connection) void start(); + // Send a message to the TCP client void sendMessage(ServerMessageType type, const std::string& data = ""); + // Set callback for disconnects void setDisconnectCallback(std::function)> cb); + // Disconnect the client void disconnect(); + // Get the assigned session ID uint64_t getSessionID() const; + // Get the assigned AES key; You should probably access this via the Session Registry instead std::array getAESKey() const; private: @@ -51,7 +57,9 @@ namespace ColumnLynx::Net::TCP { mLastHeartbeatSent(std::chrono::steady_clock::now()) {} + // Start the heartbeat routine void mStartHeartbeat(); + // Handle an incoming TCP message void mHandleMessage(ClientMessageType type, const std::string& data); std::shared_ptr mHandler; diff --git a/include/columnlynx/server/net/tcp/tcp_server.hpp b/include/columnlynx/server/net/tcp/tcp_server.hpp index d1f059c..74dcfce 100644 --- a/include/columnlynx/server/net/tcp/tcp_server.hpp +++ b/include/columnlynx/server/net/tcp/tcp_server.hpp @@ -62,10 +62,13 @@ namespace ColumnLynx::Net::TCP { mStartAccept(); } + // Stop the TCP Server void stop(); private: + // Start accepting clients via TCP void mStartAccept(); + asio::io_context &mIoContext; asio::ip::tcp::acceptor mAcceptor; std::unordered_set mClients; diff --git a/include/columnlynx/server/net/udp/udp_server.hpp b/include/columnlynx/server/net/udp/udp_server.hpp index ab99610..0963ee3 100644 --- a/include/columnlynx/server/net/udp/udp_server.hpp +++ b/include/columnlynx/server/net/udp/udp_server.hpp @@ -42,13 +42,18 @@ namespace ColumnLynx::Net::UDP { mStartReceive(); } + // Stop the UDP server void stop(); + // Send UDP data to an endpoint; Fetched via the Session Registry void sendData(const uint64_t sessionID, const std::string& data); private: + // Start receiving UDP data void mStartReceive(); + // Handle an incoming UDP packet void mHandlePacket(std::size_t bytes); + asio::ip::udp::socket mSocket; asio::ip::udp::endpoint mRemoteEndpoint; std::array mRecvBuffer; // Adjust size as needed diff --git a/src/common/virtual_interface.cpp b/src/common/virtual_interface.cpp index 42ad8bd..40346aa 100644 --- a/src/common/virtual_interface.cpp +++ b/src/common/virtual_interface.cpp @@ -4,6 +4,8 @@ #include +// This is all fucking voodoo dark magic. + namespace ColumnLynx::Net { // ------------------------------ Constructor ------------------------------ VirtualInterface::VirtualInterface(const std::string& ifName) diff --git a/src/server/server/net/tcp/tcp_connection.cpp b/src/server/server/net/tcp/tcp_connection.cpp index a2bfcbf..cd2433d 100644 --- a/src/server/server/net/tcp/tcp_connection.cpp +++ b/src/server/server/net/tcp/tcp_connection.cpp @@ -196,6 +196,13 @@ namespace ColumnLynx::Net::TCP { Nonce symNonce{}; // All zeros uint32_t clientIP = SessionRegistry::getInstance().getFirstAvailableIP(); + + if (clientIP == 0) { + Utils::warn("Out of available IPs! Disconnecting client " + reqAddr); + disconnect(); + return; + } + Protocol::TunConfig tunConfig{}; tunConfig.version = Utils::protocolVersion(); tunConfig.prefixLength = 24;