From 715729fc0221f657fea5dbafc48043c1bc7bb62c Mon Sep 17 00:00:00 2001 From: Lynix Date: Sun, 1 Apr 2018 20:48:50 +0200 Subject: [PATCH] Network: Add support for dual-stack sockets --- ChangeLog.md | 1 + include/Nazara/Network/ENetHost.hpp | 1 + include/Nazara/Network/ENetHost.inl | 8 ++++--- include/Nazara/Network/UdpSocket.inl | 4 ++-- src/Nazara/Network/AbstractSocket.cpp | 17 +++++++++++++-- src/Nazara/Network/ENetHost.cpp | 2 +- src/Nazara/Network/Posix/SocketImpl.cpp | 23 ++++++++++++++++++-- src/Nazara/Network/Posix/SocketImpl.hpp | 1 + src/Nazara/Network/Win32/SocketImpl.cpp | 28 ++++++++++++++++++++++++- src/Nazara/Network/Win32/SocketImpl.hpp | 1 + 10 files changed, 75 insertions(+), 11 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 042e68cec..2c6552db1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -77,6 +77,7 @@ Nazara Engine: - ⚠️ Added a type tag parameter to Serialize and Unserialize functions, to prevent implicit conversions with overloads - Added Collider3D::ForEachPolygon method, allowing construction of a debug mesh based on the physics collider - Fixed ConvexCollider3D::GetType returning Compound instead of ConvexHull. +- Dual-stack sockets are now supported (by using NetProtocol_Any at creation/opening) Nazara Development Kit: - Added ImageWidget (#139) diff --git a/include/Nazara/Network/ENetHost.hpp b/include/Nazara/Network/ENetHost.hpp index 39bc0be62..d6e1a8018 100644 --- a/include/Nazara/Network/ENetHost.hpp +++ b/include/Nazara/Network/ENetHost.hpp @@ -156,6 +156,7 @@ namespace Nz UInt64 m_totalReceivedData; bool m_allowsIncomingConnections; bool m_continueSending; + bool m_isUsingDualStack; bool m_isSimulationEnabled; bool m_recalculateBandwidthLimits; diff --git a/include/Nazara/Network/ENetHost.inl b/include/Nazara/Network/ENetHost.inl index 667750ff0..11ee91c80 100644 --- a/include/Nazara/Network/ENetHost.inl +++ b/include/Nazara/Network/ENetHost.inl @@ -9,6 +9,7 @@ namespace Nz { inline ENetHost::ENetHost() : m_packetPool(sizeof(ENetPacket)), + m_isUsingDualStack(false), m_isSimulationEnabled(false) { } @@ -20,21 +21,22 @@ namespace Nz inline bool ENetHost::Create(NetProtocol protocol, UInt16 port, std::size_t peerCount, std::size_t channelCount) { - NazaraAssert(protocol != NetProtocol_Any, "Any protocol not supported for Listen"); //< TODO NazaraAssert(protocol != NetProtocol_Unknown, "Invalid protocol"); IpAddress any; switch (protocol) { - case NetProtocol_Any: case NetProtocol_Unknown: - NazaraInternalError("Invalid protocol Any at this point"); + NazaraInternalError("Invalid protocol"); return false; case NetProtocol_IPv4: any = IpAddress::AnyIpV4; break; + case NetProtocol_Any: + m_isUsingDualStack = true; + // fallthrough case NetProtocol_IPv6: any = IpAddress::AnyIpV6; break; diff --git a/include/Nazara/Network/UdpSocket.inl b/include/Nazara/Network/UdpSocket.inl index 2925c1935..ed43b3428 100644 --- a/include/Nazara/Network/UdpSocket.inl +++ b/include/Nazara/Network/UdpSocket.inl @@ -51,15 +51,15 @@ namespace Nz IpAddress any; switch (m_protocol) { - case NetProtocol_Any: case NetProtocol_Unknown: - NazaraInternalError("Invalid protocol Any at this point"); + NazaraInternalError("Invalid protocol"); return SocketState_NotConnected; case NetProtocol_IPv4: any = IpAddress::AnyIpV4; break; + case NetProtocol_Any: case NetProtocol_IPv6: any = IpAddress::AnyIpV6; break; diff --git a/src/Nazara/Network/AbstractSocket.cpp b/src/Nazara/Network/AbstractSocket.cpp index b070a93cb..bf8542741 100644 --- a/src/Nazara/Network/AbstractSocket.cpp +++ b/src/Nazara/Network/AbstractSocket.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #if defined(NAZARA_PLATFORM_WINDOWS) #include @@ -14,6 +14,8 @@ #error Missing implementation: Socket #endif +#include + namespace Nz { /*! @@ -187,10 +189,21 @@ namespace Nz { if (m_handle == SocketImpl::InvalidHandle || m_protocol != protocol) { - SocketHandle handle = SocketImpl::Create(protocol, m_type, &m_lastError); + SocketHandle handle = SocketImpl::Create((protocol == NetProtocol_Any) ? NetProtocol_IPv6 : protocol, m_type, &m_lastError); if (handle == SocketImpl::InvalidHandle) return false; + if (protocol == NetProtocol_Any) + { + if (!SocketImpl::SetIPv6Only(handle, false, &m_lastError)) + { + SocketImpl::Close(handle); + + NazaraError("Failed to open a dual-stack socket: " + Nz::String(ErrorToString(m_lastError))); + return false; + } + } + m_protocol = protocol; Open(handle); } diff --git a/src/Nazara/Network/ENetHost.cpp b/src/Nazara/Network/ENetHost.cpp index 1bc3d0832..5eb8d5862 100644 --- a/src/Nazara/Network/ENetHost.cpp +++ b/src/Nazara/Network/ENetHost.cpp @@ -320,7 +320,7 @@ namespace Nz bool ENetHost::InitSocket(const IpAddress& address) { - if (!m_socket.Create(address.GetProtocol())) + if (!m_socket.Create((m_isUsingDualStack) ? NetProtocol_Any : address.GetProtocol())) return false; m_socket.EnableBlocking(false); diff --git a/src/Nazara/Network/Posix/SocketImpl.cpp b/src/Nazara/Network/Posix/SocketImpl.cpp index 1d3d34e99..2d034d202 100644 --- a/src/Nazara/Network/Posix/SocketImpl.cpp +++ b/src/Nazara/Network/Posix/SocketImpl.cpp @@ -809,8 +809,27 @@ namespace Nz { NazaraAssert(handle != InvalidHandle, "Invalid handle"); - bool option = broadcasting; - if (setsockopt(handle, SOL_SOCKET, SO_BROADCAST, reinterpret_cast(&option), sizeof(option)) == SOCKET_ERROR) + int option = broadcasting; + if (setsockopt(handle, IPPROTO_IPV6, SO_BROADCAST, reinterpret_cast(&option), sizeof(option)) == SOCKET_ERROR) + { + if (error) + *error = TranslateErrnoToResolveError(GetLastErrorCode()); + + return false; //< Error + } + + if (error) + *error = SocketError_NoError; + + return true; + } + + bool SocketImpl::SetIPv6Only(SocketHandle handle, bool ipv6Only, SocketError* error) + { + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + int option = broadcasting; + if (setsockopt(handle, SOL_SOCKET, IPV6_V6ONLY, reinterpret_cast(&option), sizeof(option)) == SOCKET_ERROR) { if (error) *error = TranslateErrnoToResolveError(GetLastErrorCode()); diff --git a/src/Nazara/Network/Posix/SocketImpl.hpp b/src/Nazara/Network/Posix/SocketImpl.hpp index e9f094cc3..4bd38380d 100644 --- a/src/Nazara/Network/Posix/SocketImpl.hpp +++ b/src/Nazara/Network/Posix/SocketImpl.hpp @@ -72,6 +72,7 @@ namespace Nz static bool SetBlocking(SocketHandle handle, bool blocking, SocketError* error = nullptr); static bool SetBroadcasting(SocketHandle handle, bool broadcasting, SocketError* error = nullptr); + static bool SetIPv6Only(SocketHandle handle, bool ipv6only, SocketError* error = nullptr); static bool SetKeepAlive(SocketHandle handle, bool enabled, UInt64 msTime, UInt64 msInterval, SocketError* error = nullptr); static bool SetNoDelay(SocketHandle handle, bool nodelay, SocketError* error = nullptr); static bool SetReceiveBufferSize(SocketHandle handle, std::size_t size, SocketError* error = nullptr); diff --git a/src/Nazara/Network/Win32/SocketImpl.cpp b/src/Nazara/Network/Win32/SocketImpl.cpp index b0f057ad2..a0ebe5948 100644 --- a/src/Nazara/Network/Win32/SocketImpl.cpp +++ b/src/Nazara/Network/Win32/SocketImpl.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 Jérôme Leclercq +// Copyright (C) 2018 Jérôme Leclercq // This file is part of the "Nazara Engine - Network module" // For conditions of distribution and use, see copyright notice in Config.hpp @@ -827,6 +827,32 @@ namespace Nz return true; } + bool SocketImpl::SetIPv6Only(SocketHandle handle, bool ipv6Only, SocketError* error) + { +#if NAZARA_CORE_WINDOWS_NT6 + NazaraAssert(handle != InvalidHandle, "Invalid handle"); + + DWORD option = ipv6Only; + if (setsockopt(handle, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&option), sizeof(option)) == SOCKET_ERROR) + { + if (error) + *error = TranslateWSAErrorToSocketError(WSAGetLastError()); + + return false; //< Error + } + + if (error) + *error = SocketError_NoError; + + return true; +#else + if (error) + *error = SocketError_NotSupported; + + return false; +#endif + } + bool SocketImpl::SetKeepAlive(SocketHandle handle, bool enabled, UInt64 msTime, UInt64 msInterval, SocketError* error) { NazaraAssert(handle != InvalidHandle, "Invalid handle"); diff --git a/src/Nazara/Network/Win32/SocketImpl.hpp b/src/Nazara/Network/Win32/SocketImpl.hpp index 864e629dd..1aa4a0495 100644 --- a/src/Nazara/Network/Win32/SocketImpl.hpp +++ b/src/Nazara/Network/Win32/SocketImpl.hpp @@ -72,6 +72,7 @@ namespace Nz static bool SetBlocking(SocketHandle handle, bool blocking, SocketError* error = nullptr); static bool SetBroadcasting(SocketHandle handle, bool broadcasting, SocketError* error = nullptr); + static bool SetIPv6Only(SocketHandle handle, bool ipv6Only, SocketError* error = nullptr); static bool SetKeepAlive(SocketHandle handle, bool enabled, UInt64 msTime, UInt64 msInterval, SocketError* error = nullptr); static bool SetNoDelay(SocketHandle handle, bool nodelay, SocketError* error = nullptr); static bool SetReceiveBufferSize(SocketHandle handle, std::size_t size, SocketError* error = nullptr);