Add IPv6 dual stack support to NearbyServerSocket.

PiperOrigin-RevId: 820274866
This commit is contained in:
Francis Tsui
2025-10-16 09:46:14 -07:00
committed by Copybara-Service
parent 4c37f90965
commit 98c2b17752
5 changed files with 54 additions and 44 deletions
@@ -252,6 +252,7 @@ cc_library(
deps = [
":crypto", # build_cleaner: keep
":network_info",
":socket_address",
":string_utils",
":types",
"//connections/implementation/flags:connections_flags",
@@ -21,6 +21,7 @@
#include <string>
#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<const char*>(&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<const char*>(&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<const char*>(&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<NearbyClientSocket> 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<NearbyClientSocket>(client_socket);
}
@@ -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<NearbyClientSocket> Accept();
bool Close();
@@ -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<char*>(address_string.data()), AF_INET,
if (WSAStringToAddressA(address_string.data(), AF_INET,
/*lpProtocolInfo=*/nullptr,
reinterpret_cast<sockaddr*>(&address.address_),
&sock_address_size) == 0) {
@@ -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<const sockaddr_in*>(&address_);
}
const sockaddr_in6* v6_address() const {
return reinterpret_cast<const sockaddr_in6*>(&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<sockaddr*>(&address_);
}