From 63f943aaed6d6a4d98e630bda9447994c8dad3c0 Mon Sep 17 00:00:00 2001 From: Francis Tsui Date: Tue, 9 Dec 2025 16:20:25 -0800 Subject: [PATCH] Improve usabilty of ServiceAddress. PiperOrigin-RevId: 842439675 --- Package.swift | 1 + internal/platform/BUILD | 15 +++ .../platform/implementation/windows/BUILD | 1 + .../implementation/windows/socket_address.cc | 6 + .../implementation/windows/socket_address.h | 4 + .../windows/socket_address_test.cc | 54 ++++++++ internal/platform/service_address.cc | 45 +++++++ internal/platform/service_address.h | 59 +++++++++ internal/platform/service_address_test.cc | 122 ++++++++++++++++++ internal/platform/wifi_credential.h | 9 +- 10 files changed, 308 insertions(+), 8 deletions(-) create mode 100644 internal/platform/service_address.cc create mode 100644 internal/platform/service_address.h create mode 100644 internal/platform/service_address_test.cc diff --git a/Package.swift b/Package.swift index 8d0cc670..334d91f2 100644 --- a/Package.swift +++ b/Package.swift @@ -427,6 +427,7 @@ let package = Package( "internal/platform/borrowable_test.cc", "internal/platform/implementation/windows/http_loader_test.cc", "internal/platform/blocking_queue_stream_test.cc", + "internal/platform/service_address_test.cc", "internal/network/utils_test.cc", "internal/network/url_test.cc", "internal/network/http_response_test.cc", diff --git a/internal/platform/BUILD b/internal/platform/BUILD index baf2315a..6936352b 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -42,6 +42,7 @@ cc_library( "base64_utils.cc", "input_stream.cc", "prng.cc", + "service_address.cc", ], hdrs = [ "base64_utils.h", @@ -59,6 +60,7 @@ cc_library( "payload_id.h", "prng.h", "runnable.h", + "service_address.h", "socket.h", "types.h", "wifi_credential.h", @@ -70,6 +72,7 @@ cc_library( "//connections:partners", ], deps = [ + "//connections/implementation/proto:offline_wire_formats_cc_proto", "//internal/platform/implementation:wifi_utils", "//proto:connections_enums_cc_proto", "@com_google_absl//absl/base:core_headers", @@ -80,6 +83,7 @@ cc_library( "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", ], @@ -606,3 +610,14 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "service_address_test", + srcs = ["service_address_test.cc"], + deps = [ + ":base", + "//connections/implementation/proto:offline_wire_formats_cc_proto", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index 1d49c96e..048f6116 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -354,6 +354,7 @@ cc_library( "//:__subpackages__", ], deps = [ + "//internal/platform:base", "//internal/platform:logging", "@com_google_absl//absl/types:span", ], diff --git a/internal/platform/implementation/windows/socket_address.cc b/internal/platform/implementation/windows/socket_address.cc index 7965588b..2e42112e 100644 --- a/internal/platform/implementation/windows/socket_address.cc +++ b/internal/platform/implementation/windows/socket_address.cc @@ -23,6 +23,7 @@ #include "absl/types/span.h" #include "internal/platform/logging.h" +#include "internal/platform/service_address.h" namespace nearby::windows { @@ -149,6 +150,11 @@ bool SocketAddress::FromBytes(SocketAddress& address, return true; } +bool SocketAddress::FromServiceAddress(SocketAddress& address, + const ServiceAddress& service_address) { + return FromBytes(address, service_address.address, service_address.port); +} + int SocketAddress::port() const { DCHECK(address_.ss_family == AF_INET || address_.ss_family == AF_INET6); if (address_.ss_family == AF_INET) { diff --git a/internal/platform/implementation/windows/socket_address.h b/internal/platform/implementation/windows/socket_address.h index de2e9a6f..6be083a5 100644 --- a/internal/platform/implementation/windows/socket_address.h +++ b/internal/platform/implementation/windows/socket_address.h @@ -23,6 +23,7 @@ #include #include "absl/types/span.h" +#include "internal/platform/service_address.h" namespace nearby::windows { @@ -63,6 +64,9 @@ class SocketAddress { static bool FromBytes(SocketAddress& address, absl::Span address_bytes, int port = 0); + static bool FromServiceAddress(SocketAddress& address, + const ServiceAddress& service_address); + // Returns true if dual stack support has been enabled. bool dual_stack() const { return dual_stack_; } diff --git a/internal/platform/implementation/windows/socket_address_test.cc b/internal/platform/implementation/windows/socket_address_test.cc index 50e22629..3c80c90b 100644 --- a/internal/platform/implementation/windows/socket_address_test.cc +++ b/internal/platform/implementation/windows/socket_address_test.cc @@ -15,6 +15,7 @@ #include "internal/platform/implementation/windows/socket_address.h" #include "gtest/gtest.h" +#include "internal/platform/service_address.h" namespace nearby::windows { namespace { @@ -201,5 +202,58 @@ TEST(SocketAddressTest, IPv6LinkLocalFail) { EXPECT_FALSE(address.IsV6LinkLocal()); } +TEST(SocketAddressTest, FromServiceAddressIPv4) { + SocketAddress address; + ServiceAddress service_address = { + .address = {192, 168, 1, 1}, + .port = 8080, + }; + EXPECT_TRUE(SocketAddress::FromServiceAddress(address, service_address)); + EXPECT_EQ(address.ToString(), "192.168.1.1:8080"); + EXPECT_EQ(address.port(), 8080); +} + +TEST(SocketAddressTest, FromServiceAddressIPv4DualStack) { + SocketAddress address(/*dual_stack=*/true); + ServiceAddress service_address = { + .address = {192, 168, 1, 1}, + .port = 8080, + }; + EXPECT_TRUE(SocketAddress::FromServiceAddress(address, service_address)); + EXPECT_EQ(address.ToString(), "[::ffff:192.168.1.1]:8080"); + EXPECT_EQ(address.port(), 8080); +} + +TEST(SocketAddressTest, FromServiceAddressIPv6) { + SocketAddress address(/*dual_stack=*/true); + ServiceAddress service_address = { + .address = {0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x4d, 0xb2, 0xb3, 0x5c, 0x22, + 0x03, 0x98, 0xa1}, + .port = 8080, + }; + EXPECT_TRUE(SocketAddress::FromServiceAddress(address, service_address)); + EXPECT_EQ(address.ToString(), "[fe80::4db2:b35c:2203:98a1]:8080"); + EXPECT_EQ(address.port(), 8080); +} + +TEST(SocketAddressTest, FromServiceAddressIPv6NoDualStack) { + SocketAddress address; + ServiceAddress service_address = { + .address = {0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x4d, 0xb2, 0xb3, 0x5c, 0x22, + 0x03, 0x98, 0xa1}, + .port = 8080, + }; + EXPECT_FALSE(SocketAddress::FromServiceAddress(address, service_address)); +} + +TEST(SocketAddressTest, FromServiceAddressInvalidAddress) { + SocketAddress address; + ServiceAddress service_address = { + .address = {192, 168, 1}, + .port = 8080, + }; + EXPECT_FALSE(SocketAddress::FromServiceAddress(address, service_address)); +} + } // namespace } // namespace nearby::windows diff --git a/internal/platform/service_address.cc b/internal/platform/service_address.cc new file mode 100644 index 00000000..90722d31 --- /dev/null +++ b/internal/platform/service_address.cc @@ -0,0 +1,45 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "internal/platform/service_address.h" + +#include + +#include "connections/implementation/proto/offline_wire_formats.pb.h" + +namespace nearby { + +void ServiceAddressToProto( + const ServiceAddress& service_address, + location::nearby::connections::ServiceAddress& proto) { + proto.set_ip_address(std::string(service_address.address.begin(), + service_address.address.end())); + proto.set_port(service_address.port); +} + +bool ServiceAddressFromProto( + const location::nearby::connections::ServiceAddress& proto, + ServiceAddress& service_address) { + // Address must be either 4 or 16 bytes and port must be set. + if ((proto.ip_address().size() != 16 && proto.ip_address().size() != 4) || + proto.port() == 0) { + return false; + } + service_address.address = {proto.ip_address().begin(), + proto.ip_address().end()}; + service_address.port = proto.port(); + return true; +} + +} // namespace nearby diff --git a/internal/platform/service_address.h b/internal/platform/service_address.h new file mode 100644 index 00000000..dfc6d83b --- /dev/null +++ b/internal/platform/service_address.h @@ -0,0 +1,59 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_SERVICE_ADDRESS_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_SERVICE_ADDRESS_H_ + +#include +#include +#include + +#include "absl/strings/str_format.h" +#include "connections/implementation/proto/offline_wire_formats.pb.h" +#include "internal/platform/implementation/wifi_utils.h" + +namespace nearby { + +struct ServiceAddress { + // IP address in MSB-first order. + // IPv4 address is 4 bytes, and IPv6 address is 16 bytes. + std::vector address; + uint16_t port; + + bool operator==(const ServiceAddress& other) const = default; +}; + +// Support logging of ServiceAddress. +template +void AbslStringify(Sink& sink, const ServiceAddress& service_address) { + absl::Format( + &sink, "[%s]:%d", + WifiUtils::GetHumanReadableIpAddress(std::string( + service_address.address.begin(), service_address.address.end())), + service_address.port); +} + +void ServiceAddressToProto( + const ServiceAddress& service_address, + location::nearby::connections::ServiceAddress& proto); + +// Returns false is proto address does not contain an IPv4 or IPv6 address, or +// if the proto port is 0. +bool ServiceAddressFromProto( + const location::nearby::connections::ServiceAddress& proto, + ServiceAddress& service_address); + +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_SERVICE_ADDRESS_H_ diff --git a/internal/platform/service_address_test.cc b/internal/platform/service_address_test.cc new file mode 100644 index 00000000..436a1966 --- /dev/null +++ b/internal/platform/service_address_test.cc @@ -0,0 +1,122 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "internal/platform/service_address.h" + +#include +#include + +#include "gtest/gtest.h" +#include "connections/implementation/proto/offline_wire_formats.pb.h" + +namespace nearby { +namespace { + +using ProtoServiceAddress = ::location::nearby::connections::ServiceAddress; + +TEST(ServiceAddressTest, IPv4ServiceAddressToProto) { + ServiceAddress service_address = { + .address = {127, 0, 0, 1}, + .port = 8080, + }; + ProtoServiceAddress proto; + ServiceAddressToProto(service_address, proto); + EXPECT_EQ(proto.ip_address(), std::string("\x7f\0\0\1", 4)); + EXPECT_EQ(proto.port(), 8080); +} + +TEST(ServiceAddressTest, IPv4ServiceAddressFromProto) { + ProtoServiceAddress proto; + proto.set_ip_address(std::string("\x7f\0\0\1", 4)); + proto.set_port(8080); + ServiceAddress service_address; + EXPECT_TRUE(ServiceAddressFromProto(proto, service_address)); + EXPECT_EQ(service_address.address, std::vector({127, 0, 0, 1})); + EXPECT_EQ(service_address.port, 8080); +} + +TEST(ServiceAddressTest, IPv6ServiceAddressToProto) { + ServiceAddress service_address = { + .address = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + .port = 8080, + }; + ProtoServiceAddress proto; + ServiceAddressToProto(service_address, proto); + EXPECT_EQ(proto.ip_address(), + std::string("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1", 16)); + EXPECT_EQ(proto.port(), 8080); +} + +TEST(ServiceAddressTest, IPv6ServiceAddressFromProto) { + ProtoServiceAddress proto; + proto.set_ip_address(std::string("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1", 16)); + proto.set_port(8080); + ServiceAddress service_address; + EXPECT_TRUE(ServiceAddressFromProto(proto, service_address)); + EXPECT_EQ( + service_address.address, + std::vector({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})); + EXPECT_EQ(service_address.port, 8080); +} + +TEST(ServiceAddressTest, ServiceAddressFromProtoInvalidAddress) { + ProtoServiceAddress proto; + proto.set_ip_address(std::string("\x7f\0\0", 3)); + proto.set_port(8080); + ServiceAddress service_address; + EXPECT_FALSE(ServiceAddressFromProto(proto, service_address)); +} + +TEST(ServiceAddressTest, ServiceAddressFromProtoInvalidPort) { + ProtoServiceAddress proto; + proto.set_ip_address(std::string("\x7f\0\0\1", 4)); + proto.set_port(0); + ServiceAddress service_address; + EXPECT_FALSE(ServiceAddressFromProto(proto, service_address)); +} + +TEST(ServiceAddressTest, ServiceAddressEquality) { + ServiceAddress service_address1 = { + .address = {127, 0, 0, 1}, + .port = 8080, + }; + ServiceAddress service_address2 = { + .address = {127, 0, 0, 1}, + .port = 8080, + }; + ServiceAddress service_address3 = { + .address = {127, 0, 0, 2}, + .port = 8080, + }; + ServiceAddress service_address4 = { + .address = {127, 0, 0, 1}, + .port = 8081, + }; + ServiceAddress service_address5 = { + .address = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + .port = 8080, + }; + ServiceAddress service_address6 = { + .address = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + .port = 8080, + }; + EXPECT_EQ(service_address1, service_address2); + EXPECT_NE(service_address1, service_address3); + EXPECT_NE(service_address1, service_address4); + EXPECT_NE(service_address1, service_address5); + EXPECT_EQ(service_address5, service_address6); +} + +} // namespace +} // namespace nearby diff --git a/internal/platform/wifi_credential.h b/internal/platform/wifi_credential.h index af394908..7618d4af 100644 --- a/internal/platform/wifi_credential.h +++ b/internal/platform/wifi_credential.h @@ -15,22 +15,15 @@ #ifndef PLATFORM_BASE_WIFI_CREDENTIAL_H_ #define PLATFORM_BASE_WIFI_CREDENTIAL_H_ -#include #include #include #include +#include "internal/platform/service_address.h" #include "proto/connections_enums.pb.h" namespace nearby { -struct ServiceAddress { - // IP address in MSB-first order. - // IPv4 address is 4 bytes, and IPv6 address is 16 bytes. - std::vector address; - uint16_t port; -}; - // Credentials for the currently-hosted Wifi hotspot (if any) // Class HotspotCredentials is copyable & movable class HotspotCredentials {