diff --git a/include/columnlynx/client/net/tcp/tcp_client.hpp b/include/columnlynx/client/net/tcp/tcp_client.hpp index ae6fbd6..820f534 100644 --- a/include/columnlynx/client/net/tcp/tcp_client.hpp +++ b/include/columnlynx/client/net/tcp/tcp_client.hpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include using asio::ip::tcp; @@ -25,7 +27,8 @@ namespace ColumnLynx::Net::TCP { Utils::LibSodiumWrapper* sodiumWrapper, std::array* aesKey, uint64_t* sessionIDRef, - bool* insecureMode) + bool* insecureMode, + std::shared_ptr tun = nullptr) : mResolver(ioContext), mSocket(ioContext), @@ -37,7 +40,8 @@ namespace ColumnLynx::Net::TCP { mInsecureMode(insecureMode), mHeartbeatTimer(mSocket.get_executor()), mLastHeartbeatReceived(std::chrono::steady_clock::now()), - mLastHeartbeatSent(std::chrono::steady_clock::now()) + mLastHeartbeatSent(std::chrono::steady_clock::now()), + mTun(tun) {} void start(); @@ -70,5 +74,7 @@ namespace ColumnLynx::Net::TCP { std::chrono::steady_clock::time_point mLastHeartbeatSent; int mMissedHeartbeats = 0; bool mIsHostDomain; + Protocol::TunConfig mTunConfig; + std::shared_ptr mTun = nullptr; }; } \ No newline at end of file diff --git a/include/columnlynx/client/net/udp/udp_client.hpp b/include/columnlynx/client/net/udp/udp_client.hpp index 0d804ec..3eed06e 100644 --- a/include/columnlynx/client/net/udp/udp_client.hpp +++ b/include/columnlynx/client/net/udp/udp_client.hpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace ColumnLynx::Net::UDP { class UDPClient { @@ -17,8 +18,12 @@ namespace ColumnLynx::Net::UDP { const std::string& host, const std::string& port, std::array* aesKeyRef, - uint64_t* sessionIDRef) - : mSocket(ioContext), mResolver(ioContext), mHost(host), mPort(port), mAesKeyRef(aesKeyRef), mSessionIDRef(sessionIDRef) { mStartReceive(); } + uint64_t* sessionIDRef, + std::shared_ptr tunRef = nullptr) + : mSocket(ioContext), mResolver(ioContext), mHost(host), mPort(port), mAesKeyRef(aesKeyRef), mSessionIDRef(sessionIDRef), mTunRef(tunRef) + { + mStartReceive(); + } void start(); void sendMessage(const std::string& data = ""); @@ -35,6 +40,7 @@ namespace ColumnLynx::Net::UDP { std::string mPort; std::array* mAesKeyRef; uint64_t* mSessionIDRef; + std::shared_ptr mTunRef = nullptr; std::array mRecvBuffer; // Adjust size as needed }; } \ No newline at end of file diff --git a/include/columnlynx/common/net/protocol_structs.hpp b/include/columnlynx/common/net/protocol_structs.hpp new file mode 100644 index 0000000..dbbc54f --- /dev/null +++ b/include/columnlynx/common/net/protocol_structs.hpp @@ -0,0 +1,21 @@ +// protocol_structs.hpp - Network Protocol Structures +// Copyright (C) 2025 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 + +namespace ColumnLynx::Protocol { + #pragma pack(push, 1) + struct TunConfig { + uint8_t version; + uint8_t prefixLength; + uint16_t mtu; + uint32_t serverIP; + uint32_t clientIP; + uint32_t dns1; + uint32_t dns2; + }; + #pragma pack(pop) +} \ No newline at end of file diff --git a/include/columnlynx/common/net/session_registry.hpp b/include/columnlynx/common/net/session_registry.hpp index ad21a2f..533d0ba 100644 --- a/include/columnlynx/common/net/session_registry.hpp +++ b/include/columnlynx/common/net/session_registry.hpp @@ -21,6 +21,9 @@ namespace ColumnLynx::Net { 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; Nonce base_nonce{}; ~SessionState() { sodium_memzero(aesKey.data(), aesKey.size()); } @@ -29,7 +32,7 @@ namespace ColumnLynx::Net { SessionState(SessionState&&) = default; SessionState& operator=(SessionState&&) = default; - explicit SessionState(const SymmetricKey& k, std::chrono::seconds ttl = std::chrono::hours(24)) : aesKey(k) { + explicit SessionState(const SymmetricKey& k, std::chrono::seconds ttl = std::chrono::hours(24), uint32_t clientIP = 0, uint32_t serverIP = 0, uint64_t id = 0) : aesKey(k), clientTunIP(clientIP), serverTunIP(serverIP), sessionID(id) { expires = created + ttl; } @@ -46,6 +49,7 @@ namespace ColumnLynx::Net { 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 @@ -55,6 +59,12 @@ namespace ColumnLynx::Net { return (it == mSessions.end()) ? nullptr : it->second; } + 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; + } + std::unordered_map> snapshot() const { std::unordered_map> snap; std::shared_lock lock(mMutex); @@ -79,10 +89,50 @@ namespace ColumnLynx::Net { ++it; } } + + for (auto it = mIPSessions.begin(); it != mIPSessions.end(); ) { + if (it->second && it->second->expires <= now) { + it = mIPSessions.erase(it); + } else { + ++it; + } + } + } + + int size() const { + std::shared_lock lock(mMutex); + return static_cast(mSessions.size()); + } + + // IP management (simple for /24 subnet) + + uint32_t getFirstAvailableIP() const { + std::shared_lock lock(mMutex); + uint32_t baseIP = 0x0A0A0002; // 10.10.0.2 + + // TODO: Expand to support larger subnets + for (uint32_t offset = 0; offset < 254; offset++) { + uint32_t candidateIP = baseIP + offset; + if (mSessionIPs.find(candidateIP) == mSessionIPs.end()) { + return candidateIP; + } + } + } + + void lockIP(uint64_t sessionID, uint32_t ip) { + std::unique_lock lock(mMutex); + mSessionIPs[sessionID] = ip; + } + + void deallocIP(uint64_t sessionID) { + std::unique_lock lock(mMutex); + mSessionIPs.erase(sessionID); } private: mutable std::shared_mutex mMutex; std::unordered_map> mSessions; + std::unordered_map mSessionIPs; + std::unordered_map> mIPSessions; }; } \ No newline at end of file diff --git a/include/columnlynx/common/net/virtual_interface.hpp b/include/columnlynx/common/net/virtual_interface.hpp index 6cdf874..a3dcb76 100644 --- a/include/columnlynx/common/net/virtual_interface.hpp +++ b/include/columnlynx/common/net/virtual_interface.hpp @@ -17,6 +17,7 @@ #include #include #include + #include #elif defined(__APPLE__) #include #include @@ -24,8 +25,11 @@ #include #include #include + #include #elif defined(_WIN32) #include + #include + #include #include #pragma comment(lib, "advapi32.lib") #endif @@ -36,12 +40,31 @@ namespace ColumnLynx::Net { explicit VirtualInterface(const std::string& ifName); ~VirtualInterface(); + bool configureIP(uint32_t clientIP, uint32_t serverIP, + uint8_t prefixLen, uint16_t mtu); + std::vector readPacket(); void writePacket(const std::vector& packet); const std::string& getName() const; - int getFd() const; // for ASIO integration (on POSIX) + int getFd() const; // For ASIO integration (on POSIX) + + static inline std::string ipToString(uint32_t ip) { + struct in_addr addr; + addr.s_addr = ip; // expected in network byte order + + char buf[INET_ADDRSTRLEN]; + if (!inet_ntop(AF_INET, &addr, buf, sizeof(buf))) + return "0.0.0.0"; + + return std::string(buf); + } + private: + bool mApplyLinuxIP(uint32_t clientIP, uint32_t serverIP, uint8_t prefixLen, uint16_t mtu); + bool mApplyMacOSIP(uint32_t clientIP, uint32_t serverIP, uint8_t prefixLen, uint16_t mtu); + bool mApplyWindowsIP(uint32_t clientIP, uint32_t serverIP, uint8_t prefixLen, uint16_t mtu); + std::string mIfName; int mFd; // POSIX #if defined(_WIN32) diff --git a/include/columnlynx/server/net/tcp/tcp_connection.hpp b/include/columnlynx/server/net/tcp/tcp_connection.hpp index 7681b97..0ac06bf 100644 --- a/include/columnlynx/server/net/tcp/tcp_connection.hpp +++ b/include/columnlynx/server/net/tcp/tcp_connection.hpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace ColumnLynx::Net::TCP { class TCPConnection : public std::enable_shared_from_this { diff --git a/include/columnlynx/server/net/tcp/tcp_server.hpp b/include/columnlynx/server/net/tcp/tcp_server.hpp index 599a5ad..50a890f 100644 --- a/include/columnlynx/server/net/tcp/tcp_server.hpp +++ b/include/columnlynx/server/net/tcp/tcp_server.hpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace ColumnLynx::Net::TCP { diff --git a/include/columnlynx/server/net/udp/udp_server.hpp b/include/columnlynx/server/net/udp/udp_server.hpp index be7a47a..ab99610 100644 --- a/include/columnlynx/server/net/udp/udp_server.hpp +++ b/include/columnlynx/server/net/udp/udp_server.hpp @@ -8,12 +8,13 @@ #include #include #include +#include namespace ColumnLynx::Net::UDP { class UDPServer { public: - UDPServer(asio::io_context& ioContext, uint16_t port, bool* hostRunning, bool ipv4Only = false) - : mSocket(ioContext), mHostRunning(hostRunning) + UDPServer(asio::io_context& ioContext, uint16_t port, bool* hostRunning, bool ipv4Only = false, std::shared_ptr tun = nullptr) + : mSocket(ioContext), mHostRunning(hostRunning), mTun(tun) { asio::error_code ec; @@ -43,13 +44,15 @@ namespace ColumnLynx::Net::UDP { void stop(); + void sendData(const uint64_t sessionID, const std::string& data); + private: void mStartReceive(); void mHandlePacket(std::size_t bytes); - void mSendData(const uint64_t sessionID, const std::string& data); asio::ip::udp::socket mSocket; asio::ip::udp::endpoint mRemoteEndpoint; std::array mRecvBuffer; // Adjust size as needed bool* mHostRunning; + std::shared_ptr mTun; }; } \ No newline at end of file diff --git a/src/client/main.cpp b/src/client/main.cpp index 33cf9a2..a331bf1 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -15,12 +15,13 @@ using asio::ip::tcp; using namespace ColumnLynx::Utils; using namespace ColumnLynx::Net; +using namespace ColumnLynx; volatile sig_atomic_t done = 0; void signalHandler(int signum) { if (signum == SIGINT || signum == SIGTERM) { - log("Received termination signal. Shutting down client."); + //log("Received termination signal. Shutting down client."); done = 1; } } @@ -62,8 +63,8 @@ int main(int argc, char** argv) { WintunInitialize(); #endif - VirtualInterface tun("columnlynxtun0"); - log("Using virtual interface: " + tun.getName()); + std::shared_ptr tun = std::make_shared("utun0"); + log("Using virtual interface: " + tun->getName()); LibSodiumWrapper sodiumWrapper = LibSodiumWrapper(); @@ -71,8 +72,8 @@ int main(int argc, char** argv) { uint64_t sessionID = 0; asio::io_context io; - auto client = std::make_shared(io, host, port, &sodiumWrapper, &aesKey, &sessionID, &insecureMode); - auto udpClient = std::make_shared(io, host, port, &aesKey, &sessionID); + 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(); @@ -81,30 +82,31 @@ int main(int argc, char** argv) { std::thread ioThread([&io]() { io.run(); }); - ioThread.detach(); + //ioThread.join(); log("Client connected to " + host + ":" + port); - + // Client is running - // TODO: SIGINT or SIGTERM seems to not kill this instantly! while ((client->isConnected() || !client->isHandshakeComplete()) && !done) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Temp wait - - if (client->isHandshakeComplete()) { - // Send a test UDP message every 5 seconds after handshake is complete - static auto lastSendTime = std::chrono::steady_clock::now(); - auto now = std::chrono::steady_clock::now(); - if (std::chrono::duration_cast(now - lastSendTime).count() >= 5) { - udpClient->sendMessage("Hello from UDP client!"); - lastSendTime = now; - } + auto packet = tun->readPacket(); + if (!client->isConnected() || done) { + break; // Bail out if connection died or signal set while blocked } + + if (packet.empty()) { + continue; + } + + udpClient->sendMessage(std::string(packet.begin(), packet.end())); } + log("Client shutting down."); udpClient->stop(); client->disconnect(); io.stop(); - ioThread.join(); + + if (ioThread.joinable()) + ioThread.join(); } catch (const std::exception& e) { error("Client error: " + std::string(e.what())); diff --git a/src/client/net/tcp/tcp_client.cpp b/src/client/net/tcp/tcp_client.cpp index d5388a3..c6185e7 100644 --- a/src/client/net/tcp/tcp_client.cpp +++ b/src/client/net/tcp/tcp_client.cpp @@ -239,19 +239,29 @@ namespace ColumnLynx::Net::TCP { mConnectionAESKey, symNonce ); - if (decrypted.size() != sizeof(mConnectionSessionID)) { - Utils::error("Decrypted session ID has invalid size. Terminating connection."); + if (decrypted.size() != sizeof(mConnectionSessionID) + sizeof(Protocol::TunConfig)) { + Utils::error("Decrypted config has invalid size. Terminating connection."); disconnect(); return; } std::memcpy(&mConnectionSessionID, decrypted.data(), sizeof(mConnectionSessionID)); + std::memcpy(&mTunConfig, decrypted.data() + sizeof(mConnectionSessionID), sizeof(Protocol::TunConfig)); Utils::log("Connection established with Session ID: " + std::to_string(mConnectionSessionID)); if (mSessionIDRef) { // Copy to the global reference *mSessionIDRef = mConnectionSessionID; } + uint32_t clientIP = ntohl(mTunConfig.clientIP); + uint32_t serverIP = ntohl(mTunConfig.serverIP); + uint8_t prefixLen = mTunConfig.prefixLength; + uint16_t mtu = mTunConfig.mtu; + + if (mTun) { + mTun->configureIP(clientIP, serverIP, prefixLen, mtu); + } + mHandshakeComplete = true; } diff --git a/src/client/net/udp/udp_client.cpp b/src/client/net/udp/udp_client.cpp index c5cdc5c..61cf0f8 100644 --- a/src/client/net/udp/udp_client.cpp +++ b/src/client/net/udp/udp_client.cpp @@ -6,6 +6,7 @@ namespace ColumnLynx::Net::UDP { void UDPClient::start() { + // TODO: Add IPv6 auto endpoints = mResolver.resolve(asio::ip::udp::v4(), mHost, mPort); mRemoteEndpoint = *endpoints.begin(); mSocket.open(asio::ip::udp::v4()); @@ -107,5 +108,10 @@ namespace ColumnLynx::Net::UDP { } Utils::log("UDP Client received packet from " + mRemoteEndpoint.address().to_string() + " - Packet size: " + std::to_string(bytes)); + + // Write to TUN + if (mTunRef) { + mTunRef->writePacket(plaintext); + } } } \ No newline at end of file diff --git a/src/common/virtual_interface.cpp b/src/common/virtual_interface.cpp index 2d43a7b..76a0c67 100644 --- a/src/common/virtual_interface.cpp +++ b/src/common/virtual_interface.cpp @@ -14,37 +14,37 @@ namespace ColumnLynx::Net { mFd = open("/dev/net/tun", O_RDWR); if (mFd < 0) throw std::runtime_error("Failed to open /dev/net/tun: " + std::string(strerror(errno))); - + struct ifreq ifr {}; ifr.ifr_flags = IFF_TUN | IFF_NO_PI; std::strncpy(ifr.ifr_name, ifName.c_str(), IFNAMSIZ); - + if (ioctl(mFd, TUNSETIFF, &ifr) < 0) { close(mFd); throw std::runtime_error("TUNSETIFF failed: " + std::string(strerror(errno))); } - + #elif defined(__APPLE__) // ---- macOS: UTUN (system control socket) ---- mFd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); if (mFd < 0) throw std::runtime_error("socket(PF_SYSTEM) failed: " + std::string(strerror(errno))); - + struct ctl_info ctlInfo {}; std::strncpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name)); if (ioctl(mFd, CTLIOCGINFO, &ctlInfo) == -1) throw std::runtime_error("ioctl(CTLIOCGINFO) failed: " + std::string(strerror(errno))); - + struct sockaddr_ctl sc {}; sc.sc_len = sizeof(sc); sc.sc_family = AF_SYSTEM; sc.ss_sysaddr = AF_SYS_CONTROL; sc.sc_id = ctlInfo.ctl_id; sc.sc_unit = 0; // utun0 (0 = auto-assign) - + if (connect(mFd, (struct sockaddr*)&sc, sizeof(sc)) < 0) throw std::runtime_error("connect(AF_SYS_CONTROL) failed: " + std::string(strerror(errno))); - + // Retrieve actual utun device name struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); @@ -54,28 +54,28 @@ namespace ColumnLynx::Net { } else { mIfName = "utunX"; } - + #elif defined(_WIN32) // ---- Windows: Wintun (WireGuard virtual adapter) ---- WINTUN_ADAPTER_HANDLE adapter = WintunOpenAdapter(L"ColumnLynx", std::wstring(ifName.begin(), ifName.end()).c_str()); if (!adapter) throw std::runtime_error("Wintun adapter not found or not installed"); - + WINTUN_SESSION_HANDLE session = WintunStartSession(adapter, 0x200000); // ring buffer size if (!session) throw std::runtime_error("Failed to start Wintun session"); - + mHandle = WintunGetReadWaitEvent(session); mFd = -1; // not used on Windows mIfName = ifName; - + #else throw std::runtime_error("Unsupported platform"); #endif } - + // ------------------------------ Destructor ------------------------------ VirtualInterface::~VirtualInterface() { #if defined(__linux__) || defined(__APPLE__) @@ -87,17 +87,21 @@ namespace ColumnLynx::Net { // WintunEndSession(mSession); #endif } - + // ------------------------------ Read ------------------------------ std::vector VirtualInterface::readPacket() { #if defined(__linux__) || defined(__APPLE__) std::vector buf(4096); ssize_t n = read(mFd, buf.data(), buf.size()); - if (n < 0) + if (n < 0) { + if (errno == EINTR) { + return {}; // Interrupted, return empty + } throw std::runtime_error("read() failed: " + std::string(strerror(errno))); + } buf.resize(n); return buf; - + #elif defined(_WIN32) WINTUN_PACKET* packet = WintunReceivePacket(mSession, nullptr); if (!packet) return {}; @@ -108,14 +112,14 @@ namespace ColumnLynx::Net { return {}; #endif } - + // ------------------------------ Write ------------------------------ void VirtualInterface::writePacket(const std::vector& packet) { #if defined(__linux__) || defined(__APPLE__) ssize_t n = write(mFd, packet.data(), packet.size()); if (n < 0) throw std::runtime_error("write() failed: " + std::string(strerror(errno))); - + #elif defined(_WIN32) WINTUN_PACKET* tx = WintunAllocateSendPacket(mSession, (DWORD)packet.size()); if (!tx) throw std::runtime_error("WintunAllocateSendPacket failed"); @@ -123,9 +127,93 @@ namespace ColumnLynx::Net { WintunSendPacket(mSession, tx); #endif } - + // ------------------------------ Accessors ------------------------------ const std::string& VirtualInterface::getName() const { return mIfName; } - + int VirtualInterface::getFd() const { return mFd; } -} \ No newline at end of file + + // ------------------------------------------------------------ + // IP CONFIGURATION + // ------------------------------------------------------------ + bool VirtualInterface::configureIP(uint32_t clientIP, uint32_t serverIP, + uint8_t prefixLen, uint16_t mtu) + { + #if defined(__linux__) + return mApplyLinuxIP(clientIP, serverIP, prefixLen, mtu); + #elif defined(__APPLE__) + return mApplyMacOSIP(clientIP, serverIP, prefixLen, mtu); + #elif defined(_WIN32) + return mApplyWindowsIP(clientIP, serverIP, prefixLen, mtu); + #else + return false; + #endif + } + + // ------------------------------------------------------------ + // Linux + // ------------------------------------------------------------ + bool VirtualInterface::mApplyLinuxIP(uint32_t clientIP, uint32_t serverIP, + uint8_t prefixLen, uint16_t mtu) + { + char cmd[512]; + + std::string ipStr = ipToString(clientIP); + std::string peerStr = ipToString(serverIP); + + snprintf(cmd, sizeof(cmd), + "ip addr add %s/%d peer %s dev %s", + ipStr.c_str(), prefixLen, peerStr.c_str(), mIfName.c_str()); + system(cmd); + + snprintf(cmd, sizeof(cmd), + "ip link set dev %s up mtu %d", mIfName.c_str(), mtu); + system(cmd); + + return true; + } + + // ------------------------------------------------------------ + // macOS (utun) + // ------------------------------------------------------------ + bool VirtualInterface::mApplyMacOSIP(uint32_t clientIP, uint32_t serverIP, + uint8_t prefixLen, uint16_t mtu) + { + char cmd[512]; + + std::string ipStr = ipToString(clientIP); + std::string peerStr = ipToString(serverIP); + + snprintf(cmd, sizeof(cmd), + "ifconfig utun0 %s %s mtu %d up", + ipStr.c_str(), peerStr.c_str(), mtu); + system(cmd); + + return true; + } + + // ------------------------------------------------------------ + // Windows (Wintun) + // ------------------------------------------------------------ + bool VirtualInterface::mApplyWindowsIP(uint32_t clientIP, uint32_t serverIP, + uint8_t prefixLen, uint16_t mtu) + { + #ifdef _WIN32 + char ip[32], gw[32]; + strcpy(ip, ipToString(clientIP).c_str()); + strcpy(gw, ipToString(serverIP).c_str()); + + char cmd[256]; + snprintf(cmd, sizeof(cmd), + "netsh interface ip set address name=\"%s\" static %s %d.%d.%d.%d", + mIfName.c_str(), ip, + (prefixLen <= 8) ? ((prefixLen << 3) & 255) : 255, + 255, 255, 255); + system(cmd); + + return true; + #else + return false; + #endif + } +} // namespace ColumnLynx::Net \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp index 0477c67..c32074e 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -11,11 +11,14 @@ #include #include #include +#include using asio::ip::tcp; using namespace ColumnLynx::Utils; using namespace ColumnLynx::Net::TCP; using namespace ColumnLynx::Net::UDP; +using namespace ColumnLynx::Net; +using namespace ColumnLynx; volatile sig_atomic_t done = 0; @@ -54,6 +57,13 @@ int main(int argc, char** argv) { log("ColumnLynx Server, Version " + getVersion()); log("This software is licensed under the GPLv2 only OR the GPLv3. See LICENSES/ for details."); +#if defined(__WIN32__) + WintunInitialize(); +#endif + + std::shared_ptr tun = std::make_shared("utun0"); + 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(); log("Server public key: " + bytesToHexString(sodiumWrapper.getPublicKey(), crypto_sign_PUBLICKEYBYTES)); @@ -64,7 +74,7 @@ int main(int argc, char** argv) { asio::io_context io; auto server = std::make_shared(io, serverPort(), &sodiumWrapper, &hostRunning, ipv4Only); - auto udpServer = std::make_shared(io, serverPort(), &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) { @@ -87,7 +97,21 @@ int main(int argc, char** argv) { log("Server started on port " + std::to_string(serverPort())); while (!done) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + auto packet = tun->readPacket(); + if (packet.empty()) { + continue; + } + + const uint8_t* ip = packet.data(); + uint32_t dstIP = ntohl(*(uint32_t*)(ip + 16)); // IPv4 destination address offset in IPv6-mapped header + + auto session = SessionRegistry::getInstance().getByIP(dstIP); + if (!session) { + Utils::warn("TUN: No session found for destination IP " + VirtualInterface::ipToString(dstIP)); + continue; + } + + udpServer->sendData(session->sessionID, std::string(packet.begin(), packet.end())); } log("Shutting down server..."); diff --git a/src/server/server/net/tcp/tcp_connection.cpp b/src/server/server/net/tcp/tcp_connection.cpp index 196a7cd..4c0282f 100644 --- a/src/server/server/net/tcp/tcp_connection.cpp +++ b/src/server/server/net/tcp/tcp_connection.cpp @@ -41,6 +41,9 @@ namespace ColumnLynx::Net::TCP { mHandler->socket().shutdown(asio::ip::tcp::socket::shutdown_both, ec); mHandler->socket().close(ec); + SessionRegistry::getInstance().erase(mConnectionSessionID); + SessionRegistry::getInstance().deallocIP(mConnectionSessionID); + Utils::log("Closed connection to " + ip); if (mOnDisconnect) { @@ -174,16 +177,33 @@ 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 - std::vector encryptedSessionID = Utils::LibSodiumWrapper::encryptMessage( - reinterpret_cast(&mConnectionSessionID), sizeof(mConnectionSessionID), + + uint32_t clientIP = SessionRegistry::getInstance().getFirstAvailableIP(); + Protocol::TunConfig tunConfig{}; + 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.dns1 = htonl(0x08080808); // 8.8.8.8 + tunConfig.dns2 = 0; + + SessionRegistry::getInstance().lockIP(mConnectionSessionID, clientIP); + + std::vector payload(sizeof(uint64_t) + sizeof(tunConfig)); + std::memcpy(payload.data(), &mConnectionSessionID, sizeof(uint64_t)); + std::memcpy(payload.data() + sizeof(uint64_t), &tunConfig, sizeof(tunConfig)); + + std::vector encryptedPayload = Utils::LibSodiumWrapper::encryptMessage( + payload.data(), payload.size(), mConnectionAESKey, symNonce ); - mHandler->sendMessage(ServerMessageType::HANDSHAKE_EXCHANGE_KEY_CONFIRM, Utils::uint8ArrayToString(encryptedSessionID.data(), encryptedSessionID.size())); + mHandler->sendMessage(ServerMessageType::HANDSHAKE_EXCHANGE_KEY_CONFIRM, Utils::uint8ArrayToString(encryptedPayload.data(), encryptedPayload.size())); // Add to session registry Utils::log("Handshake with " + reqAddr + " completed successfully. Session ID assigned."); - auto session = std::make_shared(mConnectionAESKey, std::chrono::hours(12)); + auto session = std::make_shared(mConnectionAESKey, std::chrono::hours(12), clientIP, htonl(0x0A0A0001), mConnectionSessionID); SessionRegistry::getInstance().put(mConnectionSessionID, std::move(session)); } catch (const std::exception& e) { diff --git a/src/server/server/net/udp/udp_server.cpp b/src/server/server/net/udp/udp_server.cpp index 1afa8a7..de5ca67 100644 --- a/src/server/server/net/udp/udp_server.cpp +++ b/src/server/server/net/udp/udp_server.cpp @@ -63,15 +63,16 @@ namespace ColumnLynx::Net::UDP { std::string payloadStr(plaintext.begin(), plaintext.end()); Utils::log("UDP: Received packet from " + mRemoteEndpoint.address().to_string() + " - Payload: " + payloadStr); - // TODO: Process the packet payload, for now just echo back - mSendData(sessionID, std::string(plaintext.begin(), plaintext.end())); + if (mTun) { + mTun->writePacket(plaintext); // Send to virtual interface + } } catch (...) { Utils::warn("UDP: Failed to decrypt payload from " + mRemoteEndpoint.address().to_string()); return; } } - void UDPServer::mSendData(const uint64_t sessionID, const std::string& data) { + void UDPServer::sendData(const uint64_t sessionID, const std::string& data) { // Find the IPv4/IPv6 endpoint for the session std::shared_ptr session = SessionRegistry::getInstance().get(sessionID); if (!session) {