Switch to SocketAddress in Windows impl.

PiperOrigin-RevId: 820862441
This commit is contained in:
Francis Tsui
2025-10-17 16:11:07 -07:00
committed by Copybara-Service
parent 7ccc8ff57e
commit ca82b68dfe
17 changed files with 150 additions and 90 deletions
@@ -320,6 +320,7 @@ cc_library(
],
deps = [
"//internal/platform:logging",
"@com_google_absl//absl/types:span",
],
)
@@ -49,8 +49,7 @@ NearbyClientSocket::~NearbyClientSocket() {
}
}
bool NearbyClientSocket ::Connect(const std::string& ip_address, int port,
bool dual_stack) {
bool NearbyClientSocket ::Connect(const SocketAddress& server_address) {
if (!is_socket_initiated_) {
LOG(WARNING) << "Windows socket is not initiated.";
return false;
@@ -60,19 +59,14 @@ bool NearbyClientSocket ::Connect(const std::string& ip_address, int port,
LOG(ERROR) << "Socket is already connected.";
return false;
}
SocketAddress serv_address(dual_stack);
if (!SocketAddress::FromString(serv_address, ip_address, port)) {
LOG(ERROR) << "Failed to parse address " << ip_address << ":" << port;
return false;
}
socket_ = socket(dual_stack ? AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP);
socket_ = socket(server_address.dual_stack() ? AF_INET6 : AF_INET,
SOCK_STREAM, IPPROTO_TCP);
if (socket_ == INVALID_SOCKET) {
LOG(ERROR) << "Failed to get socket with error " << WSAGetLastError();
return false;
}
if (dual_stack) {
if (server_address.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;
@@ -106,7 +100,7 @@ bool NearbyClientSocket ::Connect(const std::string& ip_address, int port,
setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&flag),
sizeof(flag));
if (connect(socket_, serv_address.address(), sizeof(sockaddr_storage)) ==
if (connect(socket_, server_address.address(), sizeof(sockaddr_storage)) ==
SOCKET_ERROR) {
LOG(ERROR) << "Failed to connect socket with error: " << WSAGetLastError();
closesocket(socket_);
@@ -120,7 +114,7 @@ bool NearbyClientSocket ::Connect(const std::string& ip_address, int port,
int address_length = sizeof(sockaddr_storage);
if (getsockname(socket_, local_address.address(), &address_length) !=
SOCKET_ERROR) {
VLOG(1) << "Connected to " << serv_address.ToString() << " from "
VLOG(1) << "Connected to " << server_address.ToString() << " from "
<< local_address.ToString();
}
}
@@ -19,10 +19,10 @@
#include <cstddef>
#include <cstdint>
#include <string>
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/windows/socket_address.h"
namespace nearby::windows {
@@ -32,8 +32,7 @@ class NearbyClientSocket {
explicit NearbyClientSocket(SOCKET socket);
~NearbyClientSocket();
bool Connect(const std::string& ip_address, int port,
bool dual_stack);
bool Connect(const SocketAddress& server_address);
ExceptionOr<ByteArray> Read(std::int64_t size);
ExceptionOr<size_t> Skip(size_t offset);
Exception Write(const ByteArray& data);
@@ -43,16 +43,16 @@ NearbyServerSocket::~NearbyServerSocket() {
}
}
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;
bool NearbyServerSocket::Listen(const SocketAddress& address) {
VLOG(1) << "Listen to socket at " << address.ToString();
LOG(INFO) << "Server socket dual stack support: " << address.dual_stack();
if (!is_socket_initiated_) {
LOG(ERROR) << "Windows socket is not initiated.";
return false;
}
socket_ = socket(dual_stack ? AF_INET6 :AF_INET, SOCK_STREAM, IPPROTO_TCP);
socket_ = socket(address.dual_stack() ? AF_INET6 : AF_INET, SOCK_STREAM,
IPPROTO_TCP);
if (socket_ == INVALID_SOCKET) {
LOG(ERROR) << "Failed to create socket.";
return false;
@@ -66,7 +66,7 @@ bool NearbyServerSocket::Listen(const std::string& ip_address, int port,
<< WSAGetLastError();
}
if (dual_stack) {
if (address.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;
@@ -77,13 +77,8 @@ bool NearbyServerSocket::Listen(const std::string& ip_address, int port,
<< 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) {
if (address.port() != 0) {
BOOL flag = TRUE;
if (setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char*>(&flag),
@@ -93,14 +88,14 @@ bool NearbyServerSocket::Listen(const std::string& ip_address, int port,
}
}
if (bind(socket_, serv_address.address(), sizeof(sockaddr_storage)) ==
if (bind(socket_, address.address(), sizeof(sockaddr_storage)) ==
SOCKET_ERROR) {
LOG(ERROR) << "Failed to bind socket with error " << WSAGetLastError();
closesocket(socket_);
return false;
}
SocketAddress local_address(dual_stack);
SocketAddress local_address(address.dual_stack());
int address_length = sizeof(sockaddr_storage);
if (getsockname(socket_, local_address.address(), &address_length) ==
SOCKET_ERROR) {
@@ -21,6 +21,7 @@
#include <string>
#include "internal/platform/implementation/windows/nearby_client_socket.h"
#include "internal/platform/implementation/windows/socket_address.h"
namespace nearby::windows {
@@ -29,7 +30,7 @@ class NearbyServerSocket {
NearbyServerSocket();
~NearbyServerSocket();
bool Listen(const std::string& ip_address, int port, bool dual_stack);
bool Listen(const SocketAddress& address);
std::unique_ptr<NearbyClientSocket> Accept();
bool Close();
@@ -20,6 +20,7 @@
#include <cstring>
#include <string>
#include "absl/types/span.h"
#include "internal/platform/logging.h"
namespace nearby::windows {
@@ -122,6 +123,31 @@ bool SocketAddress::FromString(SocketAddress& address,
return false;
}
bool SocketAddress::FromBytes(SocketAddress& address,
absl::Span<const char> address_bytes, int port) {
if (address_bytes.size() != 4 &&
!(address.dual_stack_ && address_bytes.size() == 16)) {
// Invalid address bytes size.
return false;
}
if (address_bytes.size() == 4) {
address.address_.ss_family = AF_INET;
sockaddr_in* v4_address = reinterpret_cast<sockaddr_in*>(&address.address_);
std::memcpy(&v4_address->sin_addr, address_bytes.data(), sizeof(in_addr));
address.set_port(port);
if (address.dual_stack_) {
address.ToMappedIPv6();
}
return true;
}
address.address_.ss_family = AF_INET6;
sockaddr_in6* v6_address =
reinterpret_cast<sockaddr_in6*>(&address.address_);
std::memcpy(&v6_address->sin6_addr, address_bytes.data(), sizeof(in6_addr));
address.set_port(port);
return true;
}
int SocketAddress::port() const {
DCHECK(address_.ss_family == AF_INET || address_.ss_family == AF_INET6);
if (address_.ss_family == AF_INET) {
@@ -21,6 +21,8 @@
#include <cstring>
#include <string>
#include "absl/types/span.h"
namespace nearby::windows {
// A helper class that simplifies handling of both IPv4 and IPv6 addresses.
@@ -48,9 +50,21 @@ 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.
// `port` is in host byte order.
static bool FromString(SocketAddress& address, std::string address_string,
int port = 0);
// The `dual_stack` state of `address` determines whether the address is
// can be parsed as IPv6.
// `addresss_bytes` is 4 bytes if dual_stack is disabled, and 4 or 16 bytes
// for dual_stack. The address must be in network byte order.
// `port` is in host byte order.
static bool FromBytes(SocketAddress& address,
absl::Span<const char> address_bytes, int port = 0);
// Returns true if dual stack support has been enabled.
bool dual_stack() const { return dual_stack_; }
// `Returns port in host byte order.
int port() const;
// `port` is in host byte order.
@@ -66,6 +80,12 @@ class SocketAddress {
return reinterpret_cast<sockaddr*>(&address_);
}
// An overload to return a non-const sockaddr pointer from a const
// SocketAddress. This is a convenience for calling legacy C APIs.
sockaddr* address() const {
return const_cast<sockaddr*>(reinterpret_cast<const sockaddr*>(&address_));
}
private:
// If `address_` is AF_INET, then rewrite into mapped ipv6 address, e.g.
// [::ffff:192.0.2.1].
@@ -146,5 +146,44 @@ TEST(SocketAddressTest, SetInvalidPort) {
EXPECT_EQ(address.port(), 8080);
}
TEST(SocketAddressTest, FromBytesIPv4) {
SocketAddress address;
char bytes[4] = {192, 168, 1, 1};
EXPECT_TRUE(SocketAddress::FromBytes(address, bytes, 8080));
EXPECT_EQ(address.ToString(), "192.168.1.1:8080");
EXPECT_EQ(address.port(), 8080);
}
TEST(SocketAddressTest, FromBytesIPv6Fails) {
SocketAddress address;
char bytes[16] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0x4d, 0xb2, 0xb3, 0x5c, 0x22, 0x03, 0x98, 0xa1};
EXPECT_FALSE(SocketAddress::FromBytes(address, bytes, 8080));
}
TEST(SocketAddressTest, FromBytesIPv4DualStack) {
SocketAddress address(/*dual_stack=*/true);
char bytes[4] = {192, 168, 1, 1};
EXPECT_TRUE(SocketAddress::FromBytes(address, bytes, 8080));
EXPECT_EQ(address.ToString(), "[::ffff:192.168.1.1]:8080");
EXPECT_EQ(address.port(), 8080);
}
TEST(SocketAddressTest, FromBytesIPv6DualStack) {
SocketAddress address(/*dual_stack=*/true);
char bytes[16] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0x4d, 0xb2, 0xb3, 0x5c, 0x22, 0x03, 0x98, 0xa1};
EXPECT_TRUE(SocketAddress::FromBytes(address, bytes, 8080));
EXPECT_EQ(address.ToString(), "[fe80::4db2:b35c:2203:98a1]:8080");
EXPECT_EQ(address.port(), 8080);
}
TEST(SocketAddressTest, DualStack) {
SocketAddress address(/*dual_stack=*/true);
EXPECT_TRUE(address.dual_stack());
SocketAddress address2(/*dual_stack=*/false);
EXPECT_FALSE(address2.dual_stack());
}
} // namespace
} // namespace nearby::windows
@@ -170,14 +170,6 @@ std::vector<std::string> GetIpv4Addresses() {
return result;
}
std::vector<std::string> GetWifiIpv4Addresses() {
std::vector<std::string> result;
std::vector<std::string> ethernet_addresses;
std::vector<std::string> other_addresses;
GetIpv4Addresses(result, ethernet_addresses, other_addresses);
return result;
}
void GetConnectedNetworks(bool& is_wifi_connected, bool& is_ethernet_connected,
bool& is_other_connected) {
std::vector<std::string> wifi_addresses;
@@ -42,7 +42,6 @@ ByteArray Sha256(absl::string_view input, size_t size);
// Reads the IPv4 addresses
std::vector<std::string> GetIpv4Addresses();
std::vector<std::string> GetWifiIpv4Addresses();
void GetIpv4Addresses(std::vector<std::string>& wifi_addresses,
std::vector<std::string>& ethernet_addresses,
std::vector<std::string>& other_addresses);
@@ -42,6 +42,7 @@
#include "internal/platform/implementation/windows/nearby_client_socket.h"
#include "internal/platform/implementation/windows/nearby_server_socket.h"
#include "internal/platform/implementation/windows/scheduled_executor.h"
#include "internal/platform/implementation/windows/socket_address.h"
#include "internal/platform/implementation/windows/wifi_hotspot_native.h"
// WinRT headers
@@ -94,8 +95,8 @@ class WifiHotspotSocket : public api::WifiHotspotSocket {
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() override { return client_socket_->Close(); }
bool Connect(const std::string& ip_address, int port, bool dual_stack) {
return client_socket_->Connect(ip_address, port, dual_stack);
bool Connect(const SocketAddress& server_address) {
return client_socket_->Connect(server_address);
}
private:
@@ -36,6 +36,7 @@
#include "internal/platform/implementation/wifi_utils.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Enumeration.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Security.Credentials.h"
#include "internal/platform/implementation/windows/socket_address.h"
#include "internal/platform/implementation/windows/string_utils.h"
#include "internal/platform/implementation/windows/utils.h"
#include "internal/platform/implementation/windows/wifi_hotspot.h"
@@ -85,16 +86,16 @@ std::unique_ptr<api::WifiHotspotSocket> WifiHotspotMedium::ConnectToService(
return nullptr;
}
std::string ipv4_address;
if (ip_address.length() == 4) {
ipv4_address = ipaddr_4bytes_to_dotdecimal_string(ip_address);
} else {
ipv4_address = std::string(ip_address);
}
if (ipv4_address.empty()) {
LOG(ERROR) << "Invalid IP address parameter.";
bool dual_stack = NearbyFlags::GetInstance().GetBoolFlag(
platform::config_package_nearby::nearby_platform_feature::
kEnableIpv6DualStack);
SocketAddress server_address(dual_stack);
if (!server_address.FromString(server_address, std::string(ip_address),
port)) {
LOG(ERROR) << "no valid service address and port to connect.";
return nullptr;
}
VLOG(1) << "ConnectToService address: " << server_address.ToString();
// Try connecting to the service up to wifi_hotspot_max_connection_retries,
// because it may fail first time if DHCP procedure is not finished yet.
@@ -117,7 +118,7 @@ std::unique_ptr<api::WifiHotspotSocket> WifiHotspotMedium::ConnectToService(
<< "ms, connection timeout="
<< wifi_hotspot_client_socket_connect_timeout_millis << "ms";
LOG(INFO) << "Connect to service " << ipv4_address << ":" << port;
LOG(INFO) << "Connect to service.";
for (int i = 0; i < wifi_hotspot_max_connection_retries; ++i) {
auto wifi_hotspot_socket = std::make_unique<WifiHotspotSocket>();
@@ -126,8 +127,7 @@ std::unique_ptr<api::WifiHotspotSocket> WifiHotspotMedium::ConnectToService(
nullptr;
if (cancellation_flag != nullptr) {
if (cancellation_flag->Cancelled()) {
LOG(INFO) << "connect has been cancelled to service " << ipv4_address
<< ":" << port;
LOG(INFO) << "connect to service has been cancelled.";
return nullptr;
}
@@ -139,21 +139,18 @@ std::unique_ptr<api::WifiHotspotSocket> WifiHotspotMedium::ConnectToService(
});
}
bool dual_stack = NearbyFlags::GetInstance().GetBoolFlag(
platform::config_package_nearby::nearby_platform_feature::
kEnableIpv6DualStack);
bool result = wifi_hotspot_socket->Connect(ipv4_address, port, dual_stack);
bool result = wifi_hotspot_socket->Connect(server_address);
if (!result) {
LOG(WARNING) << "reconnect to service at " << (i + 1) << "th times";
Sleep(wifi_hotspot_retry_interval_millis);
continue;
}
LOG(INFO) << "connected to remote service " << ipv4_address << ":" << port;
LOG(INFO) << "connected to remote service.";
return wifi_hotspot_socket;
}
LOG(ERROR) << "Failed to connect to service " << ipv4_address << ":" << port;
LOG(ERROR) << "Failed to connect to service.";
return nullptr;
}
@@ -34,6 +34,7 @@
#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Connectivity.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h"
#include "internal/platform/implementation/windows/socket_address.h"
#include "internal/platform/implementation/windows/utils.h"
#include "internal/platform/implementation/windows/wifi_hotspot.h"
#include "internal/platform/logging.h"
@@ -117,7 +118,13 @@ bool WifiHotspotServerSocket::Listen(bool dual_stack) {
return false;
}
if (!server_socket_.Listen(hotspot_ipaddr_, port_, dual_stack)) {
SocketAddress address(dual_stack);
if (!SocketAddress::FromString(address, hotspot_ipaddr_, port_)) {
LOG(ERROR) << "Failed to parse hotspot IP address: " << hotspot_ipaddr_
<< " and port: " << port_;
return false;
}
if (!server_socket_.Listen(address)) {
LOG(ERROR) << "Failed to listen socket.";
return false;
}
@@ -46,6 +46,7 @@
#include "internal/platform/implementation/windows/nearby_client_socket.h"
#include "internal/platform/implementation/windows/nearby_server_socket.h"
#include "internal/platform/implementation/windows/scheduled_executor.h"
#include "internal/platform/implementation/windows/socket_address.h"
#include "internal/platform/implementation/windows/wifi_lan_mdns.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/nsd_service_info.h"
@@ -95,7 +96,7 @@ class WifiLanSocket : public api::WifiLanSocket {
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() override;
bool Connect(const std::string& ip_address, int port, bool dual_stack);
bool Connect(const SocketAddress& server_address);
private:
// A simple wrapper to handle input stream of socket
@@ -19,9 +19,7 @@
#include <winsock.h>
// Standard C/C++ headers
#include <codecvt>
#include <cstdint>
#include <locale>
#include <memory>
#include <optional>
#include <string>
@@ -49,6 +47,7 @@
#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Connectivity.h"
#include "internal/platform/implementation/windows/string_utils.h"
#include "internal/platform/implementation/windows/socket_address.h"
#include "internal/platform/implementation/windows/utils.h"
#include "internal/platform/logging.h"
#include "internal/platform/nsd_service_info.h"
@@ -228,26 +227,17 @@ std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
const std::string& ip_address, int port,
CancellationFlag* cancellation_flag) {
LOG(INFO) << "ConnectToService is called.";
if (ip_address.empty() || ip_address.length() != 4 || port == 0) {
bool dual_stack = NearbyFlags::GetInstance().GetBoolFlag(
platform::config_package_nearby::nearby_platform_feature::
kEnableIpv6DualStack);
SocketAddress server_address(dual_stack);
if (!server_address.FromBytes(server_address, ip_address, port)) {
LOG(ERROR) << "no valid service address and port to connect.";
return nullptr;
}
// Converts ip address to x.x.x.x format
in_addr address;
address.S_un.S_un_b.s_b1 = ip_address[0];
address.S_un.S_un_b.s_b2 = ip_address[1];
address.S_un.S_un_b.s_b3 = ip_address[2];
address.S_un.S_un_b.s_b4 = ip_address[3];
char* ipv4_address = inet_ntoa(address);
if (ipv4_address == nullptr) {
LOG(ERROR) << "Invalid IP address parameter.";
return nullptr;
}
VLOG(1) << "ConnectToService address: " << server_address.ToString();
if (cancellation_flag != nullptr && cancellation_flag->Cancelled()) {
LOG(INFO) << "connect has been cancelled to service " << ipv4_address << ":"
<< port;
LOG(INFO) << "connect to service has been cancelled.";
return nullptr;
}
@@ -265,18 +255,13 @@ std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
socket->Close();
});
}
bool dual_stack = NearbyFlags::GetInstance().GetBoolFlag(
platform::config_package_nearby::nearby_platform_feature::
kEnableIpv6DualStack);
bool result = wifi_lan_socket->Connect(ipv4_address, port, dual_stack);
bool result = wifi_lan_socket->Connect(server_address);
if (!result) {
LOG(ERROR) << "failed to connect to service " << ipv4_address << ":"
<< port;
LOG(ERROR) << "failed to connect to service.";
return nullptr;
}
LOG(INFO) << "connected to remote service " << ipv4_address << ":" << port;
LOG(INFO) << "connected to remote service.";
return wifi_lan_socket;
}
@@ -26,6 +26,7 @@
#include "internal/platform/implementation/wifi_lan.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h"
#include "internal/platform/implementation/windows/nearby_server_socket.h"
#include "internal/platform/implementation/windows/socket_address.h"
#include "internal/platform/implementation/windows/utils.h"
#include "internal/platform/implementation/windows/wifi_lan.h"
#include "internal/platform/logging.h"
@@ -122,7 +123,9 @@ Exception WifiLanServerSocket::Close() {
bool WifiLanServerSocket::Listen(bool dual_stack) {
// Listen on all interfaces.
if (!server_socket_.Listen("", port_, dual_stack)) {
SocketAddress address(dual_stack);
SocketAddress::FromString(address, "", port_);
if (!server_socket_.Listen(address)) {
LOG(ERROR) << "Failed to listen socket at port:" << port_;
return false;
}
@@ -26,6 +26,7 @@
#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h"
#include "internal/platform/implementation/windows/generated/winrt/Windows.Storage.Streams.h"
#include "internal/platform/implementation/windows/nearby_client_socket.h"
#include "internal/platform/implementation/windows/socket_address.h"
#include "internal/platform/implementation/windows/wifi_lan.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/logging.h"
@@ -74,9 +75,8 @@ Exception WifiLanSocket::Close() {
return {Exception::kSuccess};
}
bool WifiLanSocket::Connect(const std::string& ip_address,
int port, bool dual_stack) {
return client_socket_->Connect(ip_address, port, dual_stack);
bool WifiLanSocket::Connect(const SocketAddress& server_address) {
return client_socket_->Connect(server_address);
}
// SocketInputStream