From 98c2b17752bce6e5d078ca893033a125284b9efb Mon Sep 17 00:00:00 2001 From: Francis Tsui Date: Thu, 16 Oct 2025 09:44:09 -0700 Subject: [PATCH] Add IPv6 dual stack support to NearbyServerSocket. PiperOrigin-RevId: 820274866 --- .../platform/implementation/windows/BUILD | 1 + .../windows/nearby_server_socket.cc | 74 ++++++++++--------- .../windows/nearby_server_socket.h | 2 +- .../implementation/windows/socket_address.cc | 5 +- .../implementation/windows/socket_address.h | 16 ++-- 5 files changed, 54 insertions(+), 44 deletions(-) diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index ca43617e..68bdd69f 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -252,6 +252,7 @@ cc_library( deps = [ ":crypto", # build_cleaner: keep ":network_info", + ":socket_address", ":string_utils", ":types", "//connections/implementation/flags:connections_flags", diff --git a/internal/platform/implementation/windows/nearby_server_socket.cc b/internal/platform/implementation/windows/nearby_server_socket.cc index da469ee6..33b2803b 100644 --- a/internal/platform/implementation/windows/nearby_server_socket.cc +++ b/internal/platform/implementation/windows/nearby_server_socket.cc @@ -21,6 +21,7 @@ #include #include "internal/platform/implementation/windows/nearby_client_socket.h" +#include "internal/platform/implementation/windows/socket_address.h" #include "internal/platform/logging.h" namespace nearby::windows { @@ -42,66 +43,77 @@ NearbyServerSocket::~NearbyServerSocket() { } } -bool NearbyServerSocket::Listen(const std::string& ip_address, int port) { +bool NearbyServerSocket::Listen(const std::string& ip_address, int port, + bool dual_stack) { VLOG(1) << "Listen to socket at " << ip_address << ":" << port; + LOG(INFO) << "Server socket dual stack support: " << dual_stack; if (!is_socket_initiated_) { LOG(ERROR) << "Windows socket is not initiated."; return false; } - socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + socket_ = socket(dual_stack ? AF_INET6 :AF_INET, SOCK_STREAM, IPPROTO_TCP); if (socket_ == INVALID_SOCKET) { LOG(ERROR) << "Failed to create socket."; return false; } BOOL flag = TRUE; - if (setsockopt(/*s=*/socket_, /*level=*/SOL_SOCKET, /*optname=*/SO_KEEPALIVE, - /*optval=*/(const char*)&flag, - /*optlen=*/sizeof(flag)) == SOCKET_ERROR) { + if (setsockopt(socket_, SOL_SOCKET, SO_KEEPALIVE, + reinterpret_cast(&flag), + sizeof(flag)) == SOCKET_ERROR) { LOG(WARNING) << "Failed to set SO_KEEPALIVE with error " << WSAGetLastError(); } - struct sockaddr_in serv_addr; - serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(port); - if (ip_address.empty()) { - serv_addr.sin_addr.s_addr = INADDR_ANY; - } else { - serv_addr.sin_addr.s_addr = inet_addr(ip_address.c_str()); + if (dual_stack) { + // On Windows dual stack is not the default. + // https://learn.microsoft.com/en-us/windows/win32/winsock/dual-stack-sockets#creating-a-dual-stack-socket + DWORD v6_only = 0; + if (setsockopt(socket_, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast(&v6_only), + sizeof(v6_only)) == SOCKET_ERROR) { + LOG(WARNING) << "Failed to set IPV6_V6ONLY with error " + << WSAGetLastError(); + } + } + SocketAddress serv_address(dual_stack); + if (!SocketAddress::FromString(serv_address, ip_address, port)) { + LOG(ERROR) << "Failed to parse address " << ip_address << ":" << port; + return false; } // Set REUSEADDR if a specific port is needed. if (port != 0) { BOOL flag = TRUE; - if (setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, (const char*)&flag, + if (setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&flag), sizeof(flag)) == SOCKET_ERROR) { LOG(WARNING) << "Failed to set SO_REUSEADDR with error " << WSAGetLastError(); } } - if (bind(/*s=*/socket_, /*addr=*/(struct sockaddr*)&serv_addr, - /*namelen=*/sizeof(serv_addr)) == SOCKET_ERROR) { + if (bind(socket_, serv_address.address(), sizeof(sockaddr_storage)) == + SOCKET_ERROR) { LOG(ERROR) << "Failed to bind socket with error " << WSAGetLastError(); closesocket(socket_); return false; } - sockaddr_in local_address; - int address_length = sizeof(local_address); - if (getsockname(/*s=*/socket_, (/*name=*/SOCKADDR*)&local_address, - /*namelen=*/&address_length) == SOCKET_ERROR) { + SocketAddress local_address(dual_stack); + int address_length = sizeof(sockaddr_storage); + if (getsockname(socket_, local_address.address(), &address_length) == + SOCKET_ERROR) { LOG(ERROR) << "Failed to get socket name with error " << WSAGetLastError(); closesocket(socket_); return false; } - port_ = ntohs(local_address.sin_port); + port_ = local_address.port(); - VLOG(1) << "Bound to " << ip_address << ":" << port_; + VLOG(1) << "Bound to " << local_address.ToString(); - if (::listen(/*s=*/socket_, /*backlog=*/SOMAXCONN) == SOCKET_ERROR) { + if (::listen(socket_, /*backlog=*/SOMAXCONN) == SOCKET_ERROR) { LOG(ERROR) << "Failed to listen socket with error " << WSAGetLastError(); closesocket(socket_); return false; @@ -111,28 +123,24 @@ bool NearbyServerSocket::Listen(const std::string& ip_address, int port) { } std::unique_ptr NearbyServerSocket::Accept() { - VLOG(1) << "Accept is called on NearbyServerSocket."; + LOG(INFO) << "Accept is called on NearbyServerSocket."; if (!is_socket_initiated_) { LOG(WARNING) << "Windows socket is not initiated"; return nullptr; } - sockaddr_in peer_address; - int peer_address_length = sizeof(peer_address); + SocketAddress peer_address; + int peer_address_length = sizeof(sockaddr_storage); - SOCKET client_socket = - accept(/*s=*/socket_, /*addr=*/(SOCKADDR*)&peer_address, - /*addrlen=*/&peer_address_length); + SOCKET client_socket = accept(socket_, peer_address.address(), + /*addrlen=*/&peer_address_length); if (client_socket == INVALID_SOCKET) { LOG(ERROR) << "Failed to accept socket with error: " << WSAGetLastError(); return nullptr; } - char client_ip[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(peer_address.sin_addr), client_ip, INET_ADDRSTRLEN); - int client_port = ntohs(peer_address.sin_port); - - LOG(INFO) << "Accepted remote device " << client_ip << ":" << client_port; + LOG(INFO) << "Accepted remote device."; + VLOG(1) << "Remote device address: " << peer_address.ToString(); return std::make_unique(client_socket); } diff --git a/internal/platform/implementation/windows/nearby_server_socket.h b/internal/platform/implementation/windows/nearby_server_socket.h index 19e22161..1d90703e 100644 --- a/internal/platform/implementation/windows/nearby_server_socket.h +++ b/internal/platform/implementation/windows/nearby_server_socket.h @@ -29,7 +29,7 @@ class NearbyServerSocket { NearbyServerSocket(); ~NearbyServerSocket(); - bool Listen(const std::string& ip_address, int port); + bool Listen(const std::string& ip_address, int port, bool dual_stack = false); std::unique_ptr Accept(); bool Close(); diff --git a/internal/platform/implementation/windows/socket_address.cc b/internal/platform/implementation/windows/socket_address.cc index fa949643..b7a3dcc3 100644 --- a/internal/platform/implementation/windows/socket_address.cc +++ b/internal/platform/implementation/windows/socket_address.cc @@ -40,6 +40,7 @@ SocketAddress::SocketAddress(const sockaddr_in6& address) : dual_stack_(true) { SocketAddress::SocketAddress(const sockaddr_storage& address) : dual_stack_(true) { + DCHECK(address.ss_family == AF_INET || address.ss_family == AF_INET6); std::memcpy(&address_, &address, sizeof(sockaddr_storage)); ToMappedIPv6(); } @@ -72,7 +73,7 @@ void SocketAddress::ToMappedIPv6() { } bool SocketAddress::FromString(SocketAddress& address, - const std::string& address_string, int port) { + std::string address_string, int port) { if (address_string.empty()) { if (address.dual_stack_) { address.address_.ss_family = AF_INET6; @@ -95,7 +96,7 @@ bool SocketAddress::FromString(SocketAddress& address, // Try v4 address first. address.address_.ss_family = AF_INET; int sock_address_size = sizeof(sockaddr_storage); - if (WSAStringToAddressA(const_cast(address_string.data()), AF_INET, + if (WSAStringToAddressA(address_string.data(), AF_INET, /*lpProtocolInfo=*/nullptr, reinterpret_cast(&address.address_), &sock_address_size) == 0) { diff --git a/internal/platform/implementation/windows/socket_address.h b/internal/platform/implementation/windows/socket_address.h index d40e0b41..33f407ee 100644 --- a/internal/platform/implementation/windows/socket_address.h +++ b/internal/platform/implementation/windows/socket_address.h @@ -37,6 +37,8 @@ class SocketAddress { } explicit SocketAddress(const sockaddr_in& address, bool dual_stack = false); explicit SocketAddress(const sockaddr_in6& address); + // This constructor assumes dual_stack is enabled, and will convert IPv4 + // addresses to mapped IPv6 addresses. explicit SocketAddress(const sockaddr_storage& address); ~SocketAddress() = default; @@ -46,8 +48,8 @@ class SocketAddress { // If dual_stack is enabled, an IPv4 string will be returned as a mapped IPv6 // address (e.g. [::ffff:192.0.2.1]). // Use empty `address_string` to create and unspecified address ie. ADDR_ANY. - static bool FromString(SocketAddress& address, - const std::string& address_string, int port = 0); + static bool FromString(SocketAddress& address, std::string address_string, + int port = 0); // `Returns port in host byte order. int port() const; @@ -56,12 +58,10 @@ class SocketAddress { std::string ToString() const; - const sockaddr_in* v4_address() const { - return reinterpret_cast(&address_); - } - const sockaddr_in6* v6_address() const { - return reinterpret_cast(&address_); - } + // Returns a pointer to the internal sockaddr_storage. + // This can be used to modify the address directly. However, the dual_stack + // state is not honored, ie. it will not convert IPv4 addresses to mapped IPv6 + // addresses. sockaddr* address() { return reinterpret_cast(&address_); }