From 7d9018043dfa6d8be10fa4a7c9a57d7543d9c8fb Mon Sep 17 00:00:00 2001 From: DcruBro Date: Tue, 9 Dec 2025 20:14:06 +0100 Subject: [PATCH 01/13] Add resetIP() method, need impl --- CMakeLists.txt | 2 +- .../common/net/virtual_interface.hpp | 2 ++ .../server/net/tcp/tcp_connection.hpp | 2 +- src/client/net/tcp/tcp_client.cpp | 6 ++++ src/common/utils.cpp | 2 +- src/common/virtual_interface.cpp | 33 ++++++++++++++++++- src/server/net/tcp/tcp_connection.cpp | 18 ++++++++-- 7 files changed, 59 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bca8dbd..efa5898 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.1.0 + VERSION 0.2.0 LANGUAGES CXX ) diff --git a/include/columnlynx/common/net/virtual_interface.hpp b/include/columnlynx/common/net/virtual_interface.hpp index dca574d..87cfaec 100644 --- a/include/columnlynx/common/net/virtual_interface.hpp +++ b/include/columnlynx/common/net/virtual_interface.hpp @@ -44,6 +44,8 @@ namespace ColumnLynx::Net { bool configureIP(uint32_t clientIP, uint32_t serverIP, uint8_t prefixLen, uint16_t mtu); + void resetIP(); + std::vector readPacket(); void writePacket(const std::vector& packet); diff --git a/include/columnlynx/server/net/tcp/tcp_connection.hpp b/include/columnlynx/server/net/tcp/tcp_connection.hpp index 4b93140..212a470 100644 --- a/include/columnlynx/server/net/tcp/tcp_connection.hpp +++ b/include/columnlynx/server/net/tcp/tcp_connection.hpp @@ -42,7 +42,7 @@ namespace ColumnLynx::Net::TCP { // Set callback for disconnects void setDisconnectCallback(std::function)> cb); // Disconnect the client - void disconnect(); + void disconnect(bool echo = true); // Get the assigned session ID uint64_t getSessionID() const; diff --git a/src/client/net/tcp/tcp_client.cpp b/src/client/net/tcp/tcp_client.cpp index ef03e3a..d2b05e3 100644 --- a/src/client/net/tcp/tcp_client.cpp +++ b/src/client/net/tcp/tcp_client.cpp @@ -269,6 +269,12 @@ namespace ColumnLynx::Net::TCP { disconnect(false); } break; + case ServerMessageType::KILL_CONNECTION: + Utils::warn("Server is killing the connection: " + data); + if (mConnected) { + disconnect(false); + } + break; default: Utils::log("Received unknown message type from server."); break; diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 73c7c51..c7ec622 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -49,7 +49,7 @@ namespace ColumnLynx::Utils { } std::string getVersion() { - return "b0.1"; + return "b0.2"; } unsigned short serverPort() { diff --git a/src/common/virtual_interface.cpp b/src/common/virtual_interface.cpp index e2a860e..01da960 100644 --- a/src/common/virtual_interface.cpp +++ b/src/common/virtual_interface.cpp @@ -237,7 +237,38 @@ namespace ColumnLynx::Net { return false; #endif } - + + void VirtualInterface::resetIP() { + #if defined(__linux__) + char cmd[512]; + snprintf(cmd, sizeof(cmd), + "ip addr flush dev %s", + mIfName.c_str() + ); + system(cmd); + #elif defined(__APPLE__) + char cmd[512]; + snprintf(cmd, sizeof(cmd), + "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); + #elif defined(_WIN32) + char cmd[256]; + snprintf(cmd, sizeof(cmd), + "netsh interface ip set address name=\"%s\" dhcp", + mIfName.c_str() + ); + system(cmd); + #endif + } + // ------------------------------------------------------------ // Linux // ------------------------------------------------------------ diff --git a/src/server/net/tcp/tcp_connection.cpp b/src/server/net/tcp/tcp_connection.cpp index 34d7094..f6b28f6 100644 --- a/src/server/net/tcp/tcp_connection.cpp +++ b/src/server/net/tcp/tcp_connection.cpp @@ -32,14 +32,23 @@ namespace ColumnLynx::Net::TCP { mOnDisconnect = std::move(cb); } - void TCPConnection::disconnect() { + void TCPConnection::disconnect(bool echo) { std::string ip = mHandler->socket().remote_endpoint().address().to_string(); - mHandler->sendMessage(ServerMessageType::GRACEFUL_DISCONNECT, "Server initiated disconnect."); + if (echo) { + mHandler->sendMessage(ServerMessageType::GRACEFUL_DISCONNECT, "Server initiated disconnect."); + } mHeartbeatTimer.cancel(); asio::error_code ec; mHandler->socket().shutdown(asio::ip::tcp::socket::shutdown_both, ec); + if (ec) { + Utils::error("Error during socket shutdown: " + ec.message()); + } + mHandler->socket().close(ec); + if (ec) { + Utils::error("Error during socket close: " + ec.message()); + } SessionRegistry::getInstance().erase(mConnectionSessionID); SessionRegistry::getInstance().deallocIP(mConnectionSessionID); @@ -272,6 +281,11 @@ namespace ColumnLynx::Net::TCP { disconnect(); break; } + case ClientMessageType::KILL_CONNECTION: { + Utils::warn("Received KILL_CONNECTION from " + reqAddr + ": " + data); + disconnect(); + break; + } default: Utils::warn("Unhandled message type from " + reqAddr); break; From 4ff33ffdab0732824bfe1e80f0bb89cf97fe79dc Mon Sep 17 00:00:00 2001 From: DcruBro Date: Wed, 10 Dec 2025 15:08:29 +0100 Subject: [PATCH 02/13] Add IPv6 helpers --- .../common/net/virtual_interface.hpp | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/include/columnlynx/common/net/virtual_interface.hpp b/include/columnlynx/common/net/virtual_interface.hpp index 87cfaec..460040c 100644 --- a/include/columnlynx/common/net/virtual_interface.hpp +++ b/include/columnlynx/common/net/virtual_interface.hpp @@ -77,6 +77,42 @@ namespace ColumnLynx::Net { return ntohl(addr.s_addr); } + static inline std::string ipv6ToString(IPv6Addr &ip, + bool flip = false) + { + struct in6_addr addr; + + if (flip) { + IPv6Addr flipped; + for (size_t i = 0; i < 16; ++i) + flipped[i] = ip[15 - i]; + memcpy(addr.s6_addr, flipped.data(), 16); + } else { + memcpy(addr.s6_addr, ip.data(), 16); + } + + char buf[INET6_ADDRSTRLEN]; + if (!inet_ntop(AF_INET6, &addr, buf, sizeof(buf))) + return "::"; // Fallback + + return std::string(buf); + } + + static inline IPv6Addr stringToIpv6(const std::string &ipStr) + { + IPv6Addr result{}; + struct in6_addr addr; + + if (inet_pton(AF_INET6, ipStr.c_str(), &addr) != 1) { + // "::" + result.fill(0); + return result; + } + + memcpy(result.data(), addr.s6_addr, 16); + return result; + } + static inline uint32_t prefixLengthToNetmask(uint8_t prefixLen) { if (prefixLen == 0) return 0; uint32_t mask = (0xFFFFFFFF << (32 - prefixLen)) & 0xFFFFFFFF; From 5e3aef78a5c988d08a921b2c3877e9b25e989728 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Wed, 10 Dec 2025 16:02:55 +0100 Subject: [PATCH 03/13] Test: proper ipv6 support, still sending v4 to client tun --- .../columnlynx/server/net/tcp/tcp_server.hpp | 33 +++++++++++------ .../columnlynx/server/net/udp/udp_server.hpp | 35 ++++++++++++------ src/client/main.cpp | 9 +++-- src/client/net/udp/udp_client.cpp | 37 +++++++++++++++++-- 4 files changed, 85 insertions(+), 29 deletions(-) diff --git a/include/columnlynx/server/net/tcp/tcp_server.hpp b/include/columnlynx/server/net/tcp/tcp_server.hpp index 364a727..9ff6b5e 100644 --- a/include/columnlynx/server/net/tcp/tcp_server.hpp +++ b/include/columnlynx/server/net/tcp/tcp_server.hpp @@ -34,24 +34,33 @@ namespace ColumnLynx::Net::TCP { // Preload the config map mRawServerConfig = Utils::getConfigMap("server_config", {"NETWORK", "SUBNET_MASK"}); - asio::error_code ec; - + asio::error_code ec_open, ec_v6only, ec_bind; + if (!ipv4Only) { - // Try IPv6 first (dual-stack check) + // Try IPv6 (dual-stack if supported) asio::ip::tcp::endpoint endpoint_v6(asio::ip::tcp::v6(), port); - mAcceptor.open(endpoint_v6.protocol(), ec); - if (!ec) { - mAcceptor.set_option(asio::ip::v6_only(false), ec); // Allow dual-stack if possible - mAcceptor.bind(endpoint_v6, ec); + + mAcceptor.open(endpoint_v6.protocol(), ec_open); + + if (!ec_open) { + // Try enabling dual-stack, but DO NOT treat failure as fatal + mAcceptor.set_option(asio::ip::v6_only(false), ec_v6only); + + // Try binding IPv6 + mAcceptor.bind(endpoint_v6, ec_bind); } } - - // Fallback to IPv4 if anything failed - if (ec || ipv4Only) { - Utils::warn("TCP: IPv6 unavailable (" + ec.message() + "), falling back to IPv4 only"); + + // If IPv6 bind failed OR IPv6 open failed OR forced IPv4-only + if (ipv4Only || ec_open || ec_bind) { + if (!ipv4Only) + Utils::warn("TCP: IPv6 unavailable (open=" + ec_open.message() + + ", bind=" + ec_bind.message() + + "), falling back to IPv4 only"); asio::ip::tcp::endpoint endpoint_v4(asio::ip::tcp::v4(), port); - mAcceptor.close(); // ensure clean state + + mAcceptor.close(); // guarantee clean state mAcceptor.open(endpoint_v4.protocol()); mAcceptor.bind(endpoint_v4); } diff --git a/include/columnlynx/server/net/udp/udp_server.hpp b/include/columnlynx/server/net/udp/udp_server.hpp index f872fa4..40f5e0a 100644 --- a/include/columnlynx/server/net/udp/udp_server.hpp +++ b/include/columnlynx/server/net/udp/udp_server.hpp @@ -16,24 +16,37 @@ namespace ColumnLynx::Net::UDP { 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; + asio::error_code ec_open, ec_v6only, ec_bind; if (!ipv4Only) { - // Try IPv6 first (dual-stack check) asio::ip::udp::endpoint endpoint_v6(asio::ip::udp::v6(), port); - mSocket.open(endpoint_v6.protocol(), ec); - if (!ec) { - mSocket.set_option(asio::ip::v6_only(false), ec); // Allow dual-stack if possible - mSocket.bind(endpoint_v6, ec); + + // Try opening IPv6 socket + mSocket.open(endpoint_v6.protocol(), ec_open); + + if (!ec_open) { + // Try enabling dual-stack (non fatal if it fails) + mSocket.set_option(asio::ip::v6_only(false), ec_v6only); + + // Attempt bind + mSocket.bind(endpoint_v6, ec_bind); } } - // Fallback to IPv4 if anything failed - if (ec || ipv4Only) { - Utils::warn("UDP: IPv6 unavailable (" + ec.message() + "), falling back to IPv4 only"); - + // Fallback to IPv4 if IPv6 is unusable + if (ipv4Only || ec_open || ec_bind) { + if (!ipv4Only) { + Utils::warn( + "UDP: IPv6 unavailable (open=" + ec_open.message() + + ", bind=" + ec_bind.message() + + "), falling back to IPv4 only" + ); + } + asio::ip::udp::endpoint endpoint_v4(asio::ip::udp::v4(), port); - mSocket.close(); // ensure clean state + + mSocket.close(); + mSocket = asio::ip::udp::socket(ioContext); // fully reset internal state mSocket.open(endpoint_v4.protocol()); mSocket.bind(endpoint_v4); } diff --git a/src/client/main.cpp b/src/client/main.cpp index d5666a5..b18bba5 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -21,7 +21,6 @@ volatile sig_atomic_t done = 0; void signalHandler(int signum) { if (signum == SIGINT || signum == SIGTERM) { - //log("Received termination signal. Shutting down client."); done = 1; } } @@ -96,13 +95,17 @@ int main(int argc, char** argv) { //ioThread.join(); log("Client connected to " + host + ":" + port); + debug("Client connection flag: " + std::to_string(client->isConnected())); + debug("Client handshake flag: " + std::to_string(client->isHandshakeComplete())); + debug("isDone flag: " + std::to_string(done)); // Client is running while ((client->isConnected() || !client->isHandshakeComplete()) && !done) { + //debug("Client connection flag: " + std::to_string(client->isConnected())); auto packet = tun->readPacket(); - if (!client->isConnected() || done) { + /*if (!client->isConnected() || done) { break; // Bail out if connection died or signal set while blocked - } + }*/ if (packet.empty()) { continue; diff --git a/src/client/net/udp/udp_client.cpp b/src/client/net/udp/udp_client.cpp index facd53e..90133d5 100644 --- a/src/client/net/udp/udp_client.cpp +++ b/src/client/net/udp/udp_client.cpp @@ -6,10 +6,41 @@ namespace ColumnLynx::Net::UDP { void UDPClient::start() { - // TODO: Add IPv6 - auto endpoints = mResolver.resolve(asio::ip::udp::v4(), mHost, mPort); + asio::error_code ec; + + // Resolve using an unspecified protocol (allows both IPv4 and IPv6) + auto endpoints = mResolver.resolve( + asio::ip::udp::v6(), // Try IPv6 first (dual-stack with v4) + mHost, + mPort, + ec + ); + + if (ec) { + // If IPv6 fails (host has no AAAA), try IPv4 + endpoints = mResolver.resolve( + asio::ip::udp::v4(), + mHost, + mPort, + ec + ); + } + + if (ec) { + Utils::error("UDP resolve failed: " + ec.message()); + return; + } + + // Use whichever endpoint resolved mRemoteEndpoint = *endpoints.begin(); - mSocket.open(asio::ip::udp::v4()); + + // Open socket using the resolved endpoint's protocol + mSocket.open(mRemoteEndpoint.protocol(), ec); + if (ec) { + Utils::error("UDP socket open failed: " + ec.message()); + return; + } + Utils::log("UDP Client ready to send to " + mRemoteEndpoint.address().to_string() + ":" + std::to_string(mRemoteEndpoint.port())); } From c047cb90f0a8fdda17332e5d99bfc2406e8f9c94 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Wed, 17 Dec 2025 18:34:07 +0100 Subject: [PATCH 04/13] Test 1: Make WinTun work --- CMakeLists.txt | 15 +--- .../common/net/virtual_interface.hpp | 8 +- src/common/virtual_interface.cpp | 85 +++++++++++++++---- 3 files changed, 75 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index efa5898..c6a8903 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,15 +80,6 @@ FetchContent_MakeAvailable(Sodium) FetchContent_MakeAvailable(asio) FetchContent_MakeAvailable(cxxopts) -# OpenSSL -find_package(OpenSSL REQUIRED) -if(OPENSSL_FOUND) - message(STATUS "Found OpenSSL version ${OPENSSL_VERSION}") - include_directories(${OPENSSL_INCLUDE_DIR}) -else() - message(FATAL_ERROR "OpenSSL not found") -endif() - # --------------------------------------------------------- # Output directories # --------------------------------------------------------- @@ -108,7 +99,7 @@ endforeach() # --------------------------------------------------------- file(GLOB_RECURSE COMMON_SRC CONFIGURE_DEPENDS src/common/*.cpp) add_library(common STATIC ${COMMON_SRC}) -target_link_libraries(common PUBLIC sodium OpenSSL::SSL OpenSSL::Crypto cxxopts::cxxopts) +target_link_libraries(common PUBLIC sodium cxxopts::cxxopts) target_include_directories(common PUBLIC ${PROJECT_SOURCE_DIR}/include ${sodium_SOURCE_DIR}/src/libsodium/include @@ -122,7 +113,7 @@ target_compile_definitions(common PUBLIC ASIO_STANDALONE) # --------------------------------------------------------- file(GLOB_RECURSE CLIENT_SRC CONFIGURE_DEPENDS src/client/*.cpp) add_executable(client ${CLIENT_SRC}) -target_link_libraries(client PRIVATE common sodium OpenSSL::SSL OpenSSL::Crypto cxxopts::cxxopts) +target_link_libraries(client PRIVATE common sodium cxxopts::cxxopts) target_include_directories(client PRIVATE ${PROJECT_SOURCE_DIR}/include ${sodium_SOURCE_DIR}/src/libsodium/include @@ -137,7 +128,7 @@ set_target_properties(client PROPERTIES OUTPUT_NAME "columnlynx_client") # --------------------------------------------------------- file(GLOB_RECURSE SERVER_SRC CONFIGURE_DEPENDS src/server/*.cpp) add_executable(server ${SERVER_SRC}) -target_link_libraries(server PRIVATE common sodium OpenSSL::SSL OpenSSL::Crypto cxxopts::cxxopts) +target_link_libraries(server PRIVATE common sodium cxxopts::cxxopts) target_include_directories(server PRIVATE ${PROJECT_SOURCE_DIR}/include ${sodium_SOURCE_DIR}/src/libsodium/include diff --git a/include/columnlynx/common/net/virtual_interface.hpp b/include/columnlynx/common/net/virtual_interface.hpp index 460040c..299bbe9 100644 --- a/include/columnlynx/common/net/virtual_interface.hpp +++ b/include/columnlynx/common/net/virtual_interface.hpp @@ -28,11 +28,11 @@ #include #include #elif defined(_WIN32) + #define WIN32_LEAN_AND_MEAN #include - #include #include + #include #include - #pragma comment(lib, "advapi32.lib") #endif namespace ColumnLynx::Net { @@ -127,7 +127,9 @@ namespace ColumnLynx::Net { std::string mIfName; int mFd; // POSIX #if defined(_WIN32) - HANDLE mHandle; // Windows + WINTUN_ADAPTER_HANDLE mAdapter = nullptr; + WINTUN_SESSION_HANDLE mSession = nullptr; + HANDLE mHandle = nullptr; #endif }; } \ No newline at end of file diff --git a/src/common/virtual_interface.cpp b/src/common/virtual_interface.cpp index 01da960..800ec19 100644 --- a/src/common/virtual_interface.cpp +++ b/src/common/virtual_interface.cpp @@ -6,6 +6,47 @@ // This is all fucking voodoo dark magic. +#if defined(_WIN32) + +static HMODULE gWintun = nullptr; + +static WintunOpenAdapterFn WintunOpenAdapter; +static WintunStartSessionFn WintunStartSession; +static WintunEndSessionFn WintunEndSession; +static WintunGetReadWaitEventFn WintunGetReadWaitEvent; +static WintunReceivePacketFn WintunReceivePacket; +static WintunReleaseReceivePacketFn WintunReleaseReceivePacket; +static WintunAllocateSendPacketFn WintunAllocateSendPacket; +static WintunSendPacketFn WintunSendPacket; + +static void InitializeWintun() +{ + if (gWintun) + return; + + gWintun = LoadLibraryExW(L"wintun.dll", nullptr, + LOAD_LIBRARY_SEARCH_APPLICATION_DIR); + if (!gWintun) + throw std::runtime_error("Failed to load wintun.dll"); + +#define RESOLVE(name) \ + name = (name##Fn)GetProcAddress(gWintun, #name); \ + if (!name) throw std::runtime_error("Missing Wintun symbol: " #name); + + RESOLVE(WintunOpenAdapter) + RESOLVE(WintunStartSession) + RESOLVE(WintunEndSession) + RESOLVE(WintunGetReadWaitEvent) + RESOLVE(WintunReceivePacket) + RESOLVE(WintunReleaseReceivePacket) + RESOLVE(WintunAllocateSendPacket) + RESOLVE(WintunSendPacket) + +#undef RESOLVE +} + +#endif // _WIN32 + namespace ColumnLynx::Net { // ------------------------------ Constructor ------------------------------ VirtualInterface::VirtualInterface(const std::string& ifName) @@ -63,20 +104,23 @@ namespace ColumnLynx::Net { Utils::log("VirtualInterface: opened macOS UTUN: " + mIfName); #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) + InitializeWintun(); + + mAdapter = WintunOpenAdapter( + L"ColumnLynx", + std::wstring(ifName.begin(), ifName.end()).c_str() + ); + + if (!mAdapter) + throw std::runtime_error("Wintun adapter not found"); + + mSession = WintunStartSession(mAdapter, 0x200000); + if (!mSession) throw std::runtime_error("Failed to start Wintun session"); - mHandle = WintunGetReadWaitEvent(session); - mFd = -1; // not used on Windows - mIfName = ifName; + mHandle = WintunGetReadWaitEvent(mSession); + mFd = -1; #else throw std::runtime_error("Unsupported platform"); @@ -89,9 +133,8 @@ namespace ColumnLynx::Net { if (mFd >= 0) close(mFd); #elif defined(_WIN32) - // Wintun sessions need explicit stop - // (assuming you stored the session handle as member) - // WintunEndSession(mSession); + if (mSession) + WintunEndSession(mSession); #endif } @@ -158,9 +201,12 @@ namespace ColumnLynx::Net { #elif defined(_WIN32) WINTUN_PACKET* packet = WintunReceivePacket(mSession, nullptr); - if (!packet) return {}; + if (!packet) + return {}; + + std::vector buf(packet->Data, + packet->Data + packet->Length); - std::vector buf(packet->Data, packet->Data + packet->Length); WintunReleaseReceivePacket(mSession, packet); return buf; @@ -206,10 +252,13 @@ namespace ColumnLynx::Net { #elif defined(_WIN32) - WINTUN_PACKET* tx = WintunAllocateSendPacket(mSession, (DWORD)packet.size()); + WINTUN_PACKET* tx = + WintunAllocateSendPacket(mSession, + static_cast(packet.size())); + if (!tx) throw std::runtime_error("WintunAllocateSendPacket failed"); - + memcpy(tx->Data, packet.data(), packet.size()); WintunSendPacket(mSession, tx); From cab1362053524f233a2bf500b4425aaf702b97ce Mon Sep 17 00:00:00 2001 From: DcruBro Date: Wed, 17 Dec 2025 19:11:28 +0100 Subject: [PATCH 05/13] Kinda working Windows version Needs these DLLs: - libgcc_s_seh-1.dll - libstdc++-6.dll - libwinpthread-1.dll - wintun.dll --- CMakeLists.txt | 20 +++++ .../common/net/virtual_interface.hpp | 1 + src/client/main.cpp | 4 +- src/client/net/tcp/tcp_client.cpp | 12 ++- src/common/virtual_interface.cpp | 79 ++++++++++--------- src/server/main.cpp | 4 +- 6 files changed, 77 insertions(+), 43 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6a8903..c9c6b02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,16 @@ endforeach() file(GLOB_RECURSE COMMON_SRC CONFIGURE_DEPENDS src/common/*.cpp) add_library(common STATIC ${COMMON_SRC}) target_link_libraries(common PUBLIC sodium cxxopts::cxxopts) + +if (WIN32) + target_link_libraries(common PUBLIC + ws2_32 + iphlpapi + advapi32 + mswsock + ) +endif() + target_include_directories(common PUBLIC ${PROJECT_SOURCE_DIR}/include ${sodium_SOURCE_DIR}/src/libsodium/include @@ -114,6 +124,11 @@ target_compile_definitions(common PUBLIC ASIO_STANDALONE) file(GLOB_RECURSE CLIENT_SRC CONFIGURE_DEPENDS src/client/*.cpp) add_executable(client ${CLIENT_SRC}) target_link_libraries(client PRIVATE common sodium cxxopts::cxxopts) +if (WIN32) + target_link_libraries(client PRIVATE + dbghelp + ) +endif() target_include_directories(client PRIVATE ${PROJECT_SOURCE_DIR}/include ${sodium_SOURCE_DIR}/src/libsodium/include @@ -129,6 +144,11 @@ set_target_properties(client PROPERTIES OUTPUT_NAME "columnlynx_client") file(GLOB_RECURSE SERVER_SRC CONFIGURE_DEPENDS src/server/*.cpp) add_executable(server ${SERVER_SRC}) target_link_libraries(server PRIVATE common sodium cxxopts::cxxopts) +if (WIN32) + target_link_libraries(server PRIVATE + dbghelp + ) +endif() target_include_directories(server PRIVATE ${PROJECT_SOURCE_DIR}/include ${sodium_SOURCE_DIR}/src/libsodium/include diff --git a/include/columnlynx/common/net/virtual_interface.hpp b/include/columnlynx/common/net/virtual_interface.hpp index 299bbe9..baecd11 100644 --- a/include/columnlynx/common/net/virtual_interface.hpp +++ b/include/columnlynx/common/net/virtual_interface.hpp @@ -29,6 +29,7 @@ #include #elif defined(_WIN32) #define WIN32_LEAN_AND_MEAN + #define WINTUN_STATIC #include #include #include diff --git a/src/client/main.cpp b/src/client/main.cpp index b18bba5..1f73ac6 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -27,11 +27,13 @@ void signalHandler(int signum) { int main(int argc, char** argv) { // Capture SIGINT and SIGTERM for graceful shutdown +#if !defined(_WIN32) struct sigaction action; memset(&action, 0, sizeof(struct sigaction)); action.sa_handler = signalHandler; sigaction(SIGINT, &action, nullptr); sigaction(SIGTERM, &action, nullptr); +#endif PanicHandler::init(); @@ -67,7 +69,7 @@ int main(int argc, char** argv) { log("This software is licensed under the GPLv2 only OR the GPLv3. See LICENSES/ for details."); #if defined(__WIN32__) - WintunInitialize(); + //WintunInitialize(); #endif std::shared_ptr tun = std::make_shared(optionsObj["interface"].as()); diff --git a/src/client/net/tcp/tcp_client.cpp b/src/client/net/tcp/tcp_client.cpp index d2b05e3..dd90faa 100644 --- a/src/client/net/tcp/tcp_client.cpp +++ b/src/client/net/tcp/tcp_client.cpp @@ -3,7 +3,7 @@ // Distributed under the terms of the GNU General Public License, either version 2 only or version 3. See LICENSES/ for details. #include -#include +//#include namespace ColumnLynx::Net::TCP { void TCPClient::start() { @@ -26,9 +26,13 @@ namespace ColumnLynx::Net::TCP { Utils::log("Sending handshake init to server."); // Check if hostname or IPv4/IPv6 - sockaddr_in addr4{}; - sockaddr_in6 addr6{}; - self->mIsHostDomain = inet_pton(AF_INET, mHost.c_str(), (void*)(&addr4)) != 1 && inet_pton(AF_INET6, mHost.c_str(), (void*)(&addr6)) != 1; // Voodoo black magic + try { + asio::ip::make_address(mHost); + self->mIsHostDomain = false; // IPv4 or IPv6 literal + } catch (const asio::system_error&) { + self->mIsHostDomain = true; // hostname / domain + } + std::vector payload; payload.reserve(1 + crypto_box_PUBLICKEYBYTES); diff --git a/src/common/virtual_interface.cpp b/src/common/virtual_interface.cpp index 800ec19..d2caf0f 100644 --- a/src/common/virtual_interface.cpp +++ b/src/common/virtual_interface.cpp @@ -10,37 +10,43 @@ static HMODULE gWintun = nullptr; -static WintunOpenAdapterFn WintunOpenAdapter; -static WintunStartSessionFn WintunStartSession; -static WintunEndSessionFn WintunEndSession; -static WintunGetReadWaitEventFn WintunGetReadWaitEvent; -static WintunReceivePacketFn WintunReceivePacket; -static WintunReleaseReceivePacketFn WintunReleaseReceivePacket; -static WintunAllocateSendPacketFn WintunAllocateSendPacket; -static WintunSendPacketFn WintunSendPacket; +static WINTUN_OPEN_ADAPTER_FUNC* pWintunOpenAdapter; +static WINTUN_START_SESSION_FUNC* pWintunStartSession; +static WINTUN_END_SESSION_FUNC* pWintunEndSession; +static WINTUN_GET_READ_WAIT_EVENT_FUNC* pWintunGetReadWaitEvent; +static WINTUN_RECEIVE_PACKET_FUNC* pWintunReceivePacket; +static WINTUN_RELEASE_RECEIVE_PACKET_FUNC* pWintunReleaseReceivePacket; +static WINTUN_ALLOCATE_SEND_PACKET_FUNC* pWintunAllocateSendPacket; +static WINTUN_SEND_PACKET_FUNC* pWintunSendPacket; static void InitializeWintun() { if (gWintun) return; - gWintun = LoadLibraryExW(L"wintun.dll", nullptr, - LOAD_LIBRARY_SEARCH_APPLICATION_DIR); + gWintun = LoadLibraryExW( + L"wintun.dll", + nullptr, + LOAD_LIBRARY_SEARCH_APPLICATION_DIR + ); + if (!gWintun) throw std::runtime_error("Failed to load wintun.dll"); -#define RESOLVE(name) \ - name = (name##Fn)GetProcAddress(gWintun, #name); \ - if (!name) throw std::runtime_error("Missing Wintun symbol: " #name); +#define RESOLVE(name, type) \ + p##name = reinterpret_cast( \ + GetProcAddress(gWintun, #name)); \ + if (!p##name) \ + throw std::runtime_error("Missing Wintun symbol: " #name); - RESOLVE(WintunOpenAdapter) - RESOLVE(WintunStartSession) - RESOLVE(WintunEndSession) - RESOLVE(WintunGetReadWaitEvent) - RESOLVE(WintunReceivePacket) - RESOLVE(WintunReleaseReceivePacket) - RESOLVE(WintunAllocateSendPacket) - RESOLVE(WintunSendPacket) + RESOLVE(WintunOpenAdapter, WINTUN_OPEN_ADAPTER_FUNC) + RESOLVE(WintunStartSession, WINTUN_START_SESSION_FUNC) + RESOLVE(WintunEndSession, WINTUN_END_SESSION_FUNC) + RESOLVE(WintunGetReadWaitEvent, WINTUN_GET_READ_WAIT_EVENT_FUNC) + RESOLVE(WintunReceivePacket, WINTUN_RECEIVE_PACKET_FUNC) + RESOLVE(WintunReleaseReceivePacket, WINTUN_RELEASE_RECEIVE_PACKET_FUNC) + RESOLVE(WintunAllocateSendPacket, WINTUN_ALLOCATE_SEND_PACKET_FUNC) + RESOLVE(WintunSendPacket, WINTUN_SEND_PACKET_FUNC) #undef RESOLVE } @@ -107,19 +113,18 @@ namespace ColumnLynx::Net { InitializeWintun(); - mAdapter = WintunOpenAdapter( - L"ColumnLynx", + mAdapter = pWintunOpenAdapter( std::wstring(ifName.begin(), ifName.end()).c_str() ); if (!mAdapter) throw std::runtime_error("Wintun adapter not found"); - mSession = WintunStartSession(mAdapter, 0x200000); + mSession = pWintunStartSession(mAdapter, 0x200000); if (!mSession) throw std::runtime_error("Failed to start Wintun session"); - mHandle = WintunGetReadWaitEvent(mSession); + mHandle = pWintunGetReadWaitEvent(mSession); mFd = -1; #else @@ -134,7 +139,7 @@ namespace ColumnLynx::Net { close(mFd); #elif defined(_WIN32) if (mSession) - WintunEndSession(mSession); + pWintunEndSession(mSession); #endif } @@ -200,14 +205,13 @@ namespace ColumnLynx::Net { #elif defined(_WIN32) - WINTUN_PACKET* packet = WintunReceivePacket(mSession, nullptr); + DWORD size = 0; + BYTE* packet = pWintunReceivePacket(mSession, &size); if (!packet) return {}; - std::vector buf(packet->Data, - packet->Data + packet->Length); - - WintunReleaseReceivePacket(mSession, packet); + std::vector buf(packet, packet + size); + pWintunReleaseReceivePacket(mSession, packet); return buf; #else @@ -252,15 +256,16 @@ namespace ColumnLynx::Net { #elif defined(_WIN32) - WINTUN_PACKET* tx = - WintunAllocateSendPacket(mSession, - static_cast(packet.size())); + BYTE* tx = pWintunAllocateSendPacket( + mSession, + static_cast(packet.size()) + ); if (!tx) throw std::runtime_error("WintunAllocateSendPacket failed"); - - memcpy(tx->Data, packet.data(), packet.size()); - WintunSendPacket(mSession, tx); + + memcpy(tx, packet.data(), packet.size()); + pWintunSendPacket(mSession, tx); #endif } diff --git a/src/server/main.cpp b/src/server/main.cpp index 3b6214e..080018b 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -32,11 +32,13 @@ void signalHandler(int signum) { int main(int argc, char** argv) { // Capture SIGINT and SIGTERM for graceful shutdown +#if !defined(_WIN32) struct sigaction action; memset(&action, 0, sizeof(struct sigaction)); action.sa_handler = signalHandler; sigaction(SIGINT, &action, nullptr); sigaction(SIGTERM, &action, nullptr); +#endif cxxopts::Options options("columnlynx_server", "ColumnLynx Server Application"); @@ -68,7 +70,7 @@ int main(int argc, char** argv) { log("This software is licensed under the GPLv2 only OR the GPLv3. See LICENSES/ for details."); #if defined(__WIN32__) - WintunInitialize(); + //WintunInitialize(); #endif std::unordered_map config = Utils::getConfigMap(optionsObj["config"].as()); From 225aa2a55d9ffc069392ee6c8d303acf0ffe2aee Mon Sep 17 00:00:00 2001 From: DcruBro Date: Thu, 18 Dec 2025 07:55:35 +0100 Subject: [PATCH 06/13] Create wintun interface if not found --- CMakeLists.txt | 2 +- src/common/utils.cpp | 2 +- src/common/virtual_interface.cpp | 16 ++++++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c9c6b02..9ae0fe8 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.2.0 + VERSION 0.3.0 LANGUAGES CXX ) diff --git a/src/common/utils.cpp b/src/common/utils.cpp index c7ec622..ebf6612 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -49,7 +49,7 @@ namespace ColumnLynx::Utils { } std::string getVersion() { - return "b0.2"; + return "b0.3"; } unsigned short serverPort() { diff --git a/src/common/virtual_interface.cpp b/src/common/virtual_interface.cpp index d2caf0f..91f70a5 100644 --- a/src/common/virtual_interface.cpp +++ b/src/common/virtual_interface.cpp @@ -18,6 +18,7 @@ static WINTUN_RECEIVE_PACKET_FUNC* pWintunReceivePacket; static WINTUN_RELEASE_RECEIVE_PACKET_FUNC* pWintunReleaseReceivePacket; static WINTUN_ALLOCATE_SEND_PACKET_FUNC* pWintunAllocateSendPacket; static WINTUN_SEND_PACKET_FUNC* pWintunSendPacket; +static WINTUN_CREATE_ADAPTER_FUNC* pWintunCreateAdapter; static void InitializeWintun() { @@ -47,6 +48,7 @@ static void InitializeWintun() RESOLVE(WintunReleaseReceivePacket, WINTUN_RELEASE_RECEIVE_PACKET_FUNC) RESOLVE(WintunAllocateSendPacket, WINTUN_ALLOCATE_SEND_PACKET_FUNC) RESOLVE(WintunSendPacket, WINTUN_SEND_PACKET_FUNC) + RESOLVE(WintunCreateAdapter, WINTUN_CREATE_ADAPTER_FUNC) #undef RESOLVE } @@ -113,12 +115,18 @@ namespace ColumnLynx::Net { InitializeWintun(); - mAdapter = pWintunOpenAdapter( - std::wstring(ifName.begin(), ifName.end()).c_str() - ); + mAdapter = pWintunOpenAdapter(ifaceName); + + if (!mAdapter) { + mAdapter = pWintunCreateAdapter( + ifaceName, + L"ColumnLynx", + nullptr + ); + } if (!mAdapter) - throw std::runtime_error("Wintun adapter not found"); + throw std::runtime_error("Failed to open or create Wintun adapter"); mSession = pWintunStartSession(mAdapter, 0x200000); if (!mSession) From 17cc314c264fbeb5520377e89d09bcfd3e72b5e0 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Thu, 18 Dec 2025 11:12:11 +0100 Subject: [PATCH 07/13] Fix wintun - connects but errors --- include/columnlynx/common/net/virtual_interface.hpp | 2 ++ src/common/virtual_interface.cpp | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/columnlynx/common/net/virtual_interface.hpp b/include/columnlynx/common/net/virtual_interface.hpp index baecd11..f393fcf 100644 --- a/include/columnlynx/common/net/virtual_interface.hpp +++ b/include/columnlynx/common/net/virtual_interface.hpp @@ -33,6 +33,8 @@ #include #include #include + #include + #include #include #endif diff --git a/src/common/virtual_interface.cpp b/src/common/virtual_interface.cpp index 91f70a5..ee571ed 100644 --- a/src/common/virtual_interface.cpp +++ b/src/common/virtual_interface.cpp @@ -113,20 +113,25 @@ namespace ColumnLynx::Net { #elif defined(_WIN32) + // Convert to Windows' wchar_t* thingy + std::wstring_convert> converter; + std::wstring wide_string = converter.from_bytes(mIfName); + const wchar_t* wide_c_str = wide_string.c_str(); + InitializeWintun(); - mAdapter = pWintunOpenAdapter(ifaceName); + mAdapter = pWintunOpenAdapter(wide_c_str); if (!mAdapter) { mAdapter = pWintunCreateAdapter( - ifaceName, + wide_c_str, L"ColumnLynx", nullptr ); } if (!mAdapter) - throw std::runtime_error("Failed to open or create Wintun adapter"); + throw std::runtime_error("Failed to open or create Wintun adapter (run running as admin)"); mSession = pWintunStartSession(mAdapter, 0x200000); if (!mSession) From 68a825b7df9e2bb581086aad6435b083babdd287 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Mon, 29 Dec 2025 18:02:42 +0100 Subject: [PATCH 08/13] test --- .gitignore | 3 +- .../columnlynx/common/libsodium_wrapper.hpp | 6 +--- .../common/net/session_registry.hpp | 2 +- src/client/main.cpp | 2 +- src/client/net/tcp/tcp_client.cpp | 27 ++++++++------ src/common/tcp_message_handler.cpp | 18 +++++++--- src/server/net/tcp/tcp_connection.cpp | 35 ++++++++++--------- 7 files changed, 53 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index da5e8e8..290af9a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ _deps CMakeUserPresets.json build/ -.vscode/ \ No newline at end of file +.vscode/ +.DS_Store diff --git a/include/columnlynx/common/libsodium_wrapper.hpp b/include/columnlynx/common/libsodium_wrapper.hpp index f5904b8..0685151 100644 --- a/include/columnlynx/common/libsodium_wrapper.hpp +++ b/include/columnlynx/common/libsodium_wrapper.hpp @@ -11,10 +11,6 @@ #include #include #include -#include -#include -#include -#include #include #include @@ -211,4 +207,4 @@ namespace ColumnLynx::Utils { std::array mXPublicKey; std::array mXPrivateKey; }; -} \ 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 762dc05..a8b1190 100644 --- a/include/columnlynx/common/net/session_registry.hpp +++ b/include/columnlynx/common/net/session_registry.hpp @@ -89,4 +89,4 @@ namespace ColumnLynx::Net { std::unordered_map mSessionIPs; std::unordered_map> mIPSessions; }; -} \ No newline at end of file +} diff --git a/src/client/main.cpp b/src/client/main.cpp index 1f73ac6..f700ec9 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -96,7 +96,7 @@ int main(int argc, char** argv) { }); //ioThread.join(); - log("Client connected to " + host + ":" + port); + log("Attempting connection to " + host + ":" + port); debug("Client connection flag: " + std::to_string(client->isConnected())); debug("Client handshake flag: " + std::to_string(client->isHandshakeComplete())); debug("isDone flag: " + std::to_string(done)); diff --git a/src/client/net/tcp/tcp_client.cpp b/src/client/net/tcp/tcp_client.cpp index dd90faa..75e78df 100644 --- a/src/client/net/tcp/tcp_client.cpp +++ b/src/client/net/tcp/tcp_client.cpp @@ -13,13 +13,22 @@ namespace ColumnLynx::Net::TCP { if (!ec) { asio::async_connect(mSocket, endpoints, [this, self](asio::error_code ec, const tcp::endpoint&) { - if (!NetHelper::isExpectedDisconnect(ec)) { + if (!ec) { mConnected = true; Utils::log("Client connected."); mHandler = std::make_shared(std::move(mSocket)); mHandler->onMessage([this](AnyMessageType type, const std::string& data) { mHandleMessage(static_cast(MessageHandler::toUint8(type)), data); }); + // Close only after peer FIN to avoid RSTs + mHandler->onDisconnect([this](const asio::error_code& ec) { + asio::error_code ec2; + if (mHandler) { + mHandler->socket().close(ec2); + } + mConnected = false; + Utils::log(std::string("Server disconnected: ") + ec.message()); + }); mHandler->start(); // Init connection handshake @@ -50,7 +59,9 @@ namespace ColumnLynx::Net::TCP { mStartHeartbeat(); } else { - Utils::error("Client connect failed: " + ec.message()); + if (!NetHelper::isExpectedDisconnect(ec)) { + Utils::error("Client connect failed: " + ec.message()); + } } }); } else { @@ -81,18 +92,14 @@ namespace ColumnLynx::Net::TCP { asio::error_code ec; mHeartbeatTimer.cancel(); - mHandler->socket().shutdown(tcp::socket::shutdown_both, ec); + // Half-close: stop sending, keep reading until peer FIN + mHandler->socket().shutdown(tcp::socket::shutdown_send, ec); if (ec) { Utils::error("Error during socket shutdown: " + ec.message()); } - mHandler->socket().close(ec); - if (ec) { - Utils::error("Error during socket close: " + ec.message()); - } - - mConnected = false; - Utils::log("Client disconnected."); + // Do not close immediately; rely on onDisconnect to finalize + Utils::log("Client initiated graceful disconnect (half-close)."); } } diff --git a/src/common/tcp_message_handler.cpp b/src/common/tcp_message_handler.cpp index 79cb1b7..ab09fcf 100644 --- a/src/common/tcp_message_handler.cpp +++ b/src/common/tcp_message_handler.cpp @@ -43,13 +43,19 @@ namespace ColumnLynx::Net::TCP { auto self = shared_from_this(); asio::async_read(mSocket, asio::buffer(mHeader), [this, self](asio::error_code ec, std::size_t) { - if (!NetHelper::isExpectedDisconnect(ec)) { + if (!ec) { mCurrentType = decodeMessageType(mHeader[0]); uint16_t len = (mHeader[1] << 8) | mHeader[2]; mReadBody(len); } else { - Utils::error("Header read failed: " + ec.message()); + if (!NetHelper::isExpectedDisconnect(ec)) { + Utils::error("Header read failed: " + ec.message()); + } + // Connection closed, trigger disconnect handler + if (mOnDisconnect) { + mOnDisconnect(ec); + } } } ); @@ -61,7 +67,7 @@ namespace ColumnLynx::Net::TCP { asio::async_read(mSocket, asio::buffer(mBody), [this, self](asio::error_code ec, std::size_t) { - if (!NetHelper::isExpectedDisconnect(ec)) { + if (!ec) { std::string payload(mBody.begin(), mBody.end()); // Dispatch based on message type @@ -71,8 +77,10 @@ namespace ColumnLynx::Net::TCP { mReadHeader(); // Keep listening } else { - Utils::error("Body read failed: " + ec.message()); - + if (!NetHelper::isExpectedDisconnect(ec)) { + Utils::error("Body read failed: " + ec.message()); + } + // Connection closed, trigger disconnect handler if (mOnDisconnect) { mOnDisconnect(ec); } diff --git a/src/server/net/tcp/tcp_connection.cpp b/src/server/net/tcp/tcp_connection.cpp index f6b28f6..6eaca97 100644 --- a/src/server/net/tcp/tcp_connection.cpp +++ b/src/server/net/tcp/tcp_connection.cpp @@ -11,8 +11,20 @@ namespace ColumnLynx::Net::TCP { }); mHandler->onDisconnect([this](const asio::error_code& ec) { - Utils::log("Client disconnected: " + mHandler->socket().remote_endpoint().address().to_string() + " - " + ec.message()); - disconnect(); + // Peer has closed; finalize locally without sending RST + std::string ip = mHandler->socket().remote_endpoint().address().to_string(); + Utils::log("Client disconnected: " + ip + " - " + ec.message()); + asio::error_code ec2; + mHandler->socket().close(ec2); + + SessionRegistry::getInstance().erase(mConnectionSessionID); + SessionRegistry::getInstance().deallocIP(mConnectionSessionID); + + Utils::log("Closed connection to " + ip); + + if (mOnDisconnect) { + mOnDisconnect(shared_from_this()); + } }); mHandler->start(); @@ -40,24 +52,13 @@ namespace ColumnLynx::Net::TCP { } mHeartbeatTimer.cancel(); asio::error_code ec; - mHandler->socket().shutdown(asio::ip::tcp::socket::shutdown_both, ec); + // Half-close: stop sending, keep reading until peer FIN + mHandler->socket().shutdown(asio::ip::tcp::socket::shutdown_send, ec); if (ec) { Utils::error("Error during socket shutdown: " + ec.message()); } - - mHandler->socket().close(ec); - if (ec) { - Utils::error("Error during socket close: " + ec.message()); - } - - SessionRegistry::getInstance().erase(mConnectionSessionID); - SessionRegistry::getInstance().deallocIP(mConnectionSessionID); - - Utils::log("Closed connection to " + ip); - - if (mOnDisconnect) { - mOnDisconnect(shared_from_this()); - } + // Do not close immediately; final cleanup happens in onDisconnect + Utils::log("Initiated graceful disconnect (half-close) to " + ip); } uint64_t TCPConnection::getSessionID() const { From 16cd980c0a50989f3a43090572e54936c6ecf7de Mon Sep 17 00:00:00 2001 From: DcruBro Date: Mon, 29 Dec 2025 18:11:05 +0100 Subject: [PATCH 09/13] test2 --- src/server/main.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/server/main.cpp b/src/server/main.cpp index 080018b..66211d8 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -67,17 +67,22 @@ int main(int argc, char** argv) { bool ipv4Only = optionsObj["ipv4-only"].as(); 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::unordered_map config = Utils::getConfigMap(optionsObj["config"].as()); + log("This software is licensed under the GPLv2 only OR the GPLv3. See LICENSES/ for details."); std::shared_ptr tun = std::make_shared(optionsObj["interface"].as()); log("Using virtual interface: " + tun->getName()); + // Get network configuration from config file + std::unordered_map config = Utils::getConfigMap(optionsObj["config"].as()); + std::string networkString = config.find("NETWORK") != config.end() ? config.find("NETWORK")->second : "10.10.0.0"; + uint8_t subnetMask = config.find("SUBNET_MASK") != config.end() ? std::stoi(config.find("SUBNET_MASK")->second) : 24; + uint32_t baseIP = VirtualInterface::stringToIpv4(networkString); + uint32_t serverIP = baseIP + 1; // e.g., 10.10.0.1 + + // Configure the server's TUN interface + tun->configureIP(serverIP, serverIP, subnetMask, 1420); + log("Configured TUN interface with IP " + VirtualInterface::ipv4ToString(serverIP) + "/" + std::to_string(subnetMask)); + // Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory) std::shared_ptr sodiumWrapper = std::make_shared(); From 6031d9655a49cf6a346b9bf9c36cb20094d58309 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Mon, 29 Dec 2025 18:29:42 +0100 Subject: [PATCH 10/13] test3 --- src/server/main.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/server/main.cpp b/src/server/main.cpp index 66211d8..d013bd2 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -74,14 +74,14 @@ int main(int argc, char** argv) { // Get network configuration from config file std::unordered_map config = Utils::getConfigMap(optionsObj["config"].as()); - std::string networkString = config.find("NETWORK") != config.end() ? config.find("NETWORK")->second : "10.10.0.0"; - uint8_t subnetMask = config.find("SUBNET_MASK") != config.end() ? std::stoi(config.find("SUBNET_MASK")->second) : 24; - uint32_t baseIP = VirtualInterface::stringToIpv4(networkString); - uint32_t serverIP = baseIP + 1; // e.g., 10.10.0.1 + //std::string networkString = config.find("NETWORK") != config.end() ? config.find("NETWORK")->second : "10.10.0.0"; + //uint8_t subnetMask = config.find("SUBNET_MASK") != config.end() ? std::stoi(config.find("SUBNET_MASK")->second) : 24; + //uint32_t baseIP = VirtualInterface::stringToIpv4(networkString); + //uint32_t serverIP = baseIP + 1; // e.g., 10.10.0.1 // Configure the server's TUN interface - tun->configureIP(serverIP, serverIP, subnetMask, 1420); - log("Configured TUN interface with IP " + VirtualInterface::ipv4ToString(serverIP) + "/" + std::to_string(subnetMask)); + //tun->configureIP(serverIP, serverIP, subnetMask, 1420); + //log("Configured TUN interface with IP " + VirtualInterface::ipv4ToString(serverIP) + "/" + std::to_string(subnetMask)); // Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory) std::shared_ptr sodiumWrapper = std::make_shared(); From 072fb69a4aab2ca83a0f805ba5f47bc4c56166bd Mon Sep 17 00:00:00 2001 From: DcruBro Date: Mon, 29 Dec 2025 18:30:54 +0100 Subject: [PATCH 11/13] test3 --- src/server/main.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/server/main.cpp b/src/server/main.cpp index d013bd2..080018b 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -67,22 +67,17 @@ int main(int argc, char** argv) { bool ipv4Only = optionsObj["ipv4-only"].as(); log("ColumnLynx Server, Version " + getVersion()); - log("This software is licensed under the GPLv2 only OR the GPLv3. See LICENSES/ for details."); + log("This software is licensed under the GPLv2 only OR the GPLv3. See LICENSES/ for details."); + +#if defined(__WIN32__) + //WintunInitialize(); +#endif + + std::unordered_map config = Utils::getConfigMap(optionsObj["config"].as()); std::shared_ptr tun = std::make_shared(optionsObj["interface"].as()); log("Using virtual interface: " + tun->getName()); - // Get network configuration from config file - std::unordered_map config = Utils::getConfigMap(optionsObj["config"].as()); - //std::string networkString = config.find("NETWORK") != config.end() ? config.find("NETWORK")->second : "10.10.0.0"; - //uint8_t subnetMask = config.find("SUBNET_MASK") != config.end() ? std::stoi(config.find("SUBNET_MASK")->second) : 24; - //uint32_t baseIP = VirtualInterface::stringToIpv4(networkString); - //uint32_t serverIP = baseIP + 1; // e.g., 10.10.0.1 - - // Configure the server's TUN interface - //tun->configureIP(serverIP, serverIP, subnetMask, 1420); - //log("Configured TUN interface with IP " + VirtualInterface::ipv4ToString(serverIP) + "/" + std::to_string(subnetMask)); - // Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory) std::shared_ptr sodiumWrapper = std::make_shared(); From ae507c3fb9f16d78419545b7ffd0d7980a20aa18 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Mon, 29 Dec 2025 19:02:22 +0100 Subject: [PATCH 12/13] Test Fix panic on disconnect --- include/columnlynx/common/panic_handler.hpp | 2 +- .../server/net/tcp/tcp_connection.hpp | 1 + src/server/main.cpp | 13 +++++++++++- src/server/net/tcp/tcp_connection.cpp | 21 ++++++++++++------- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/include/columnlynx/common/panic_handler.hpp b/include/columnlynx/common/panic_handler.hpp index 8e9867c..044fdde 100644 --- a/include/columnlynx/common/panic_handler.hpp +++ b/include/columnlynx/common/panic_handler.hpp @@ -186,7 +186,7 @@ namespace ColumnLynx::Utils { // 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 << "\n***\033[31m MASTER THREAD PANIC! \033[0m***\n"; std::cerr << "Reason: " << reason << "\n"; std::cerr << "Dumping panic trace...\n"; diff --git a/include/columnlynx/server/net/tcp/tcp_connection.hpp b/include/columnlynx/server/net/tcp/tcp_connection.hpp index 212a470..3a45b7b 100644 --- a/include/columnlynx/server/net/tcp/tcp_connection.hpp +++ b/include/columnlynx/server/net/tcp/tcp_connection.hpp @@ -76,5 +76,6 @@ namespace ColumnLynx::Net::TCP { std::chrono::steady_clock::time_point mLastHeartbeatReceived; std::chrono::steady_clock::time_point mLastHeartbeatSent; int mMissedHeartbeats = 0; + std::string mRemoteIP; // Cached remote IP to avoid calling remote_endpoint() on closed sockets }; } \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp index 080018b..948df83 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -78,6 +78,17 @@ int main(int argc, char** argv) { std::shared_ptr tun = std::make_shared(optionsObj["interface"].as()); log("Using virtual interface: " + tun->getName()); + // Get network configuration from config file + std::string networkString = config.find("NETWORK") != config.end() ? config.find("NETWORK")->second : "10.10.0.0"; + uint8_t subnetMask = config.find("SUBNET_MASK") != config.end() ? std::stoi(config.find("SUBNET_MASK")->second) : 24; + uint32_t baseIP = VirtualInterface::stringToIpv4(networkString); + uint32_t serverIP = baseIP + 1; // e.g., 10.10.0.1 + + // Configure the server's TUN interface with point-to-point mode + // Server acts as a peer to clients, so use point-to-point configuration + tun->configureIP(serverIP, baseIP, subnetMask, 1420); + log("Configured TUN interface with IP " + VirtualInterface::ipv4ToString(serverIP) + " peer " + VirtualInterface::ipv4ToString(baseIP)); + // Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory) std::shared_ptr sodiumWrapper = std::make_shared(); @@ -134,7 +145,7 @@ int main(int argc, char** argv) { } const uint8_t* ip = packet.data(); - uint32_t dstIP = ntohl(*(uint32_t*)(ip + 16)); // IPv4 destination address offset in IPv6-mapped header + uint32_t dstIP = ntohl(*(uint32_t*)(ip + 16)); // IPv4 destination address auto session = SessionRegistry::getInstance().getByIP(dstIP); if (!session) { diff --git a/src/server/net/tcp/tcp_connection.cpp b/src/server/net/tcp/tcp_connection.cpp index 6eaca97..ffbfbb7 100644 --- a/src/server/net/tcp/tcp_connection.cpp +++ b/src/server/net/tcp/tcp_connection.cpp @@ -6,21 +6,28 @@ namespace ColumnLynx::Net::TCP { void TCPConnection::start() { + try { + // Cache the remote IP early to avoid calling remote_endpoint() on closed sockets later + mRemoteIP = mHandler->socket().remote_endpoint().address().to_string(); + } catch (const std::exception& e) { + mRemoteIP = "unknown"; + Utils::warn("Failed to get remote endpoint: " + std::string(e.what())); + } + mHandler->onMessage([this](AnyMessageType type, const std::string& data) { mHandleMessage(static_cast(MessageHandler::toUint8(type)), data); }); mHandler->onDisconnect([this](const asio::error_code& ec) { // Peer has closed; finalize locally without sending RST - std::string ip = mHandler->socket().remote_endpoint().address().to_string(); - Utils::log("Client disconnected: " + ip + " - " + ec.message()); + Utils::log("Client disconnected: " + mRemoteIP + " - " + ec.message()); asio::error_code ec2; mHandler->socket().close(ec2); SessionRegistry::getInstance().erase(mConnectionSessionID); SessionRegistry::getInstance().deallocIP(mConnectionSessionID); - Utils::log("Closed connection to " + ip); + Utils::log("Closed connection to " + mRemoteIP); if (mOnDisconnect) { mOnDisconnect(shared_from_this()); @@ -31,7 +38,7 @@ namespace ColumnLynx::Net::TCP { mStartHeartbeat(); // Placeholder for message handling setup - Utils::log("Client connected: " + mHandler->socket().remote_endpoint().address().to_string()); + Utils::log("Client connected: " + mRemoteIP); } void TCPConnection::sendMessage(ServerMessageType type, const std::string& data) { @@ -45,8 +52,6 @@ namespace ColumnLynx::Net::TCP { } void TCPConnection::disconnect(bool echo) { - std::string ip = mHandler->socket().remote_endpoint().address().to_string(); - if (echo) { mHandler->sendMessage(ServerMessageType::GRACEFUL_DISCONNECT, "Server initiated disconnect."); } @@ -58,7 +63,7 @@ namespace ColumnLynx::Net::TCP { Utils::error("Error during socket shutdown: " + ec.message()); } // Do not close immediately; final cleanup happens in onDisconnect - Utils::log("Initiated graceful disconnect (half-close) to " + ip); + Utils::log("Initiated graceful disconnect (half-close) to " + mRemoteIP); } uint64_t TCPConnection::getSessionID() const { @@ -102,7 +107,7 @@ namespace ColumnLynx::Net::TCP { } void TCPConnection::mHandleMessage(ClientMessageType type, const std::string& data) { - std::string reqAddr = mHandler->socket().remote_endpoint().address().to_string(); + std::string& reqAddr = mRemoteIP; switch (type) { case ClientMessageType::HANDSHAKE_INIT: { From d5bf741650db9fcc890c5146c303fb75e2782320 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Mon, 29 Dec 2025 19:05:25 +0100 Subject: [PATCH 13/13] Test Fix double SIGTERM --- src/server/main.cpp | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/src/server/main.cpp b/src/server/main.cpp index 948df83..3c3194e 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include #include @@ -23,22 +25,7 @@ using namespace ColumnLynx; volatile sig_atomic_t done = 0; -void signalHandler(int signum) { - if (signum == SIGINT || signum == SIGTERM) { - log("Received termination signal. Shutting down server gracefully."); - done = 1; - } -} - int main(int argc, char** argv) { - // Capture SIGINT and SIGTERM for graceful shutdown -#if !defined(_WIN32) - struct sigaction action; - memset(&action, 0, sizeof(struct sigaction)); - action.sa_handler = signalHandler; - sigaction(SIGINT, &action, nullptr); - sigaction(SIGTERM, &action, nullptr); -#endif cxxopts::Options options("columnlynx_server", "ColumnLynx Server Application"); @@ -78,17 +65,6 @@ int main(int argc, char** argv) { std::shared_ptr tun = std::make_shared(optionsObj["interface"].as()); log("Using virtual interface: " + tun->getName()); - // Get network configuration from config file - std::string networkString = config.find("NETWORK") != config.end() ? config.find("NETWORK")->second : "10.10.0.0"; - uint8_t subnetMask = config.find("SUBNET_MASK") != config.end() ? std::stoi(config.find("SUBNET_MASK")->second) : 24; - uint32_t baseIP = VirtualInterface::stringToIpv4(networkString); - uint32_t serverIP = baseIP + 1; // e.g., 10.10.0.1 - - // Configure the server's TUN interface with point-to-point mode - // Server acts as a peer to clients, so use point-to-point configuration - tun->configureIP(serverIP, baseIP, subnetMask, 1420); - log("Configured TUN interface with IP " + VirtualInterface::ipv4ToString(serverIP) + " peer " + VirtualInterface::ipv4ToString(baseIP)); - // Generate a temporary keypair, replace with actual CA signed keys later (Note, these are stored in memory) std::shared_ptr sodiumWrapper = std::make_shared(); @@ -141,11 +117,13 @@ int main(int argc, char** argv) { while (!done) { auto packet = tun->readPacket(); if (packet.empty()) { + // Small sleep to avoid busy-waiting and to allow signal processing + std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } const uint8_t* ip = packet.data(); - uint32_t dstIP = ntohl(*(uint32_t*)(ip + 16)); // IPv4 destination address + uint32_t dstIP = ntohl(*(uint32_t*)(ip + 16)); // IPv4 destination address offset in IPv6-mapped header auto session = SessionRegistry::getInstance().getByIP(dstIP); if (!session) {