From e695008e109773a1c8265b525987356d8ee20bcc Mon Sep 17 00:00:00 2001 From: DcruBro Date: Wed, 12 Nov 2025 09:07:22 +0100 Subject: [PATCH] Added IPv6 support, added option to disable IPv6 (IPv4-Only mode) --- .../columnlynx/server/net/tcp/tcp_server.hpp | 34 +++++++++++++++++-- .../columnlynx/server/net/udp/udp_server.hpp | 26 ++++++++++++-- src/client/main.cpp | 2 +- src/server/main.cpp | 18 ++++++++-- 4 files changed, 73 insertions(+), 7 deletions(-) diff --git a/include/columnlynx/server/net/tcp/tcp_server.hpp b/include/columnlynx/server/net/tcp/tcp_server.hpp index 57037f2..599a5ad 100644 --- a/include/columnlynx/server/net/tcp/tcp_server.hpp +++ b/include/columnlynx/server/net/tcp/tcp_server.hpp @@ -21,9 +21,39 @@ namespace ColumnLynx::Net::TCP { class TCPServer { public: - TCPServer(asio::io_context& ioContext, uint16_t port, Utils::LibSodiumWrapper* sodiumWrapper, bool* hostRunning) - : mIoContext(ioContext), mAcceptor(ioContext, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)), mSodiumWrapper(sodiumWrapper), mHostRunning(hostRunning) + TCPServer(asio::io_context& ioContext, + uint16_t port, + Utils::LibSodiumWrapper* sodiumWrapper, + bool* hostRunning, bool ipv4Only = false) + : mIoContext(ioContext), + mAcceptor(ioContext), + mSodiumWrapper(sodiumWrapper), + mHostRunning(hostRunning) { + asio::error_code ec; + + if (!ipv4Only) { + // Try IPv6 first (dual-stack check) + 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); + } + } + + // Fallback to IPv4 if anything failed + if (ec || ipv4Only) { + Utils::warn("TCP: IPv6 unavailable (" + ec.message() + "), falling back to IPv4 only"); + + asio::ip::tcp::endpoint endpoint_v4(asio::ip::tcp::v4(), port); + mAcceptor.close(); // ensure clean state + mAcceptor.open(endpoint_v4.protocol()); + mAcceptor.bind(endpoint_v4); + } + + // Start listening + mAcceptor.listen(); Utils::log("Started TCP server on port " + std::to_string(port)); mStartAccept(); } diff --git a/include/columnlynx/server/net/udp/udp_server.hpp b/include/columnlynx/server/net/udp/udp_server.hpp index 99dc3cd..be7a47a 100644 --- a/include/columnlynx/server/net/udp/udp_server.hpp +++ b/include/columnlynx/server/net/udp/udp_server.hpp @@ -12,9 +12,31 @@ namespace ColumnLynx::Net::UDP { class UDPServer { public: - UDPServer(asio::io_context& ioContext, uint16_t port, bool* hostRunning) - : mSocket(ioContext, asio::ip::udp::endpoint(asio::ip::udp::v4(), port)), mHostRunning(hostRunning) + UDPServer(asio::io_context& ioContext, uint16_t port, bool* hostRunning, bool ipv4Only = false) + : mSocket(ioContext), mHostRunning(hostRunning) { + asio::error_code ec; + + 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); + } + } + + // Fallback to IPv4 if anything failed + if (ec || ipv4Only) { + Utils::warn("UDP: IPv6 unavailable (" + ec.message() + "), falling back to IPv4 only"); + + asio::ip::udp::endpoint endpoint_v4(asio::ip::udp::v4(), port); + mSocket.close(); // ensure clean state + mSocket.open(endpoint_v4.protocol()); + mSocket.bind(endpoint_v4); + } + Utils::log("Started UDP server on port " + std::to_string(port)); mStartReceive(); } diff --git a/src/client/main.cpp b/src/client/main.cpp index b832efb..d863422 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -39,7 +39,7 @@ int main(int argc, char** argv) { ("h,help", "Print help") ("s,server", "Server address", cxxopts::value()->default_value("127.0.0.1")) ("p,port", "Server port", cxxopts::value()->default_value(std::to_string(serverPort()))) - ("as,allow-selfsigned", "Allow self-signed certificates", cxxopts::value()->default_value("false")); + ("allow-selfsigned", "Allow self-signed certificates", cxxopts::value()->default_value("false")); bool insecureMode = options.parse(argc, argv).count("allow-selfsigned") > 0; diff --git a/src/server/main.cpp b/src/server/main.cpp index 385dde9..0477c67 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include using asio::ip::tcp; using namespace ColumnLynx::Utils; @@ -33,9 +34,22 @@ int main(int argc, char** argv) { sigaction(SIGINT, &action, nullptr); sigaction(SIGTERM, &action, nullptr); + cxxopts::Options options("columnlynx_server", "ColumnLynx Server Application"); + + options.add_options() + ("h,help", "Print help") + ("4,ipv4-only", "Force IPv4 only operation", cxxopts::value()->default_value("false")); + PanicHandler::init(); try { + auto result = options.parse(argc, argv); + if (result.count("help")) { + std::cout << options.help() << std::endl; + return 0; + } + + bool ipv4Only = result["ipv4-only"].as(); log("ColumnLynx Server, Version " + getVersion()); log("This software is licensed under the GPLv2 only OR the GPLv3. See LICENSES/ for details."); @@ -49,8 +63,8 @@ int main(int argc, char** argv) { asio::io_context io; - auto server = std::make_shared(io, serverPort(), &sodiumWrapper, &hostRunning); - auto udpServer = std::make_shared(io, serverPort(), &hostRunning); + auto server = std::make_shared(io, serverPort(), &sodiumWrapper, &hostRunning, ipv4Only); + auto udpServer = std::make_shared(io, serverPort(), &hostRunning, ipv4Only); asio::signal_set signals(io, SIGINT, SIGTERM); signals.async_wait([&](const std::error_code&, int) {