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())); }