blob: dfd384c28ef061d55d91435fe9ade26a6774afe0 [file]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/socket/udp_socket.h"
#include <algorithm>
#include "base/containers/circular_deque.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/posix/eintr_wrapper.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/scoped_clear_last_error.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_view_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/gtest_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "base/test/test_future.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "net/base/features.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/network_interfaces.h"
#include "net/base/port_util.h"
#include "net/base/sockaddr_storage.h"
#include "net/base/test_completion_callback.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_source.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_util.h"
#include "net/socket/extra_socket_defines.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/udp_client_socket.h"
#include "net/socket/udp_server_socket.h"
#include "net/socket/udp_socket_global_limits.h"
#include "net/test/gtest_util.h"
#include "net/test/test_with_task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#if !BUILDFLAG(IS_WIN)
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#else
#include <winsock2.h>
#include <iphlpapi.h>
#endif
#if BUILDFLAG(IS_ANDROID)
#include "net/android/network_change_notifier_factory_android.h"
#include "net/base/network_change_notifier.h"
#endif
#if BUILDFLAG(IS_IOS)
#include <TargetConditionals.h>
#endif
#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#endif // BUILDFLAG(IS_MAC)
using net::test::IsError;
using net::test::IsOk;
using testing::DoAll;
using testing::Not;
namespace net {
namespace {
// Whether Source-Specific Multicast (SSM) is expected to work on this platform.
#if defined(MCAST_JOIN_SOURCE_GROUP) && !BUILDFLAG(IS_ANDROID) && \
!BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_FUCHSIA)
constexpr bool kExpectSSMToWork = true;
#else
constexpr bool kExpectSSMToWork = false;
#endif
// Creates an address from ip address and port and writes it to |*address|.
bool CreateUDPAddress(const std::string& ip_str,
uint16_t port,
IPEndPoint* address) {
IPAddress ip_address;
if (!ip_address.AssignFromIPLiteral(ip_str))
return false;
*address = IPEndPoint(ip_address, port);
return true;
}
class UDPSocketTest : public PlatformTest, public WithTaskEnvironment {
public:
UDPSocketTest() : buffer_(base::MakeRefCounted<IOBufferWithSize>(kMaxRead)) {}
// Blocks until data is read from the socket.
std::string RecvFromSocket(UDPServerSocket* socket) {
return RecvFromSocket(socket, DSCP_DEFAULT, ECN_DEFAULT);
}
std::string RecvFromSocket(UDPServerSocket* socket,
DiffServCodePoint dscp,
EcnCodePoint ecn) {
TestCompletionCallback callback;
int rv = socket->RecvFrom(buffer_.get(), kMaxRead, &recv_from_address_,
callback.callback());
rv = callback.GetResult(rv);
if (rv < 0)
return std::string();
#if BUILDFLAG(IS_WIN)
// The DSCP value is not populated on Windows, in order to avoid incurring
// an extra system call.
EXPECT_EQ(socket->GetLastTos().dscp, DSCP_DEFAULT);
#else
EXPECT_EQ(socket->GetLastTos().dscp, dscp);
#endif
EXPECT_EQ(socket->GetLastTos().ecn, ecn);
return std::string(buffer_->data(), rv);
}
// Sends UDP packet.
// If |address| is specified, then it is used for the destination
// to send to. Otherwise, will send to the last socket this server
// received from.
int SendToSocket(UDPServerSocket* socket, const std::string& msg) {
return SendToSocket(socket, msg, recv_from_address_);
}
int SendToSocket(UDPServerSocket* socket,
std::string msg,
const IPEndPoint& address) {
scoped_refptr<StringIOBuffer> io_buffer =
base::MakeRefCounted<StringIOBuffer>(msg);
TestCompletionCallback callback;
int rv = socket->SendTo(io_buffer.get(), io_buffer->size(), address,
callback.callback());
return callback.GetResult(rv);
}
std::string ReadSocket(UDPClientSocket* socket) {
return ReadSocket(socket, DSCP_DEFAULT, ECN_DEFAULT);
}
std::string ReadSocket(UDPClientSocket* socket,
DiffServCodePoint dscp,
EcnCodePoint ecn) {
TestCompletionCallback callback;
int rv = socket->Read(buffer_.get(), kMaxRead, callback.callback());
rv = callback.GetResult(rv);
if (rv < 0)
return std::string();
#if BUILDFLAG(IS_WIN)
// The DSCP value is not populated on Windows, in order to avoid incurring
// an extra system call.
EXPECT_EQ(socket->GetLastTos().dscp, DSCP_DEFAULT);
#else
EXPECT_EQ(socket->GetLastTos().dscp, dscp);
#endif
EXPECT_EQ(socket->GetLastTos().ecn, ecn);
return std::string(buffer_->data(), rv);
}
// Writes specified message to the socket.
int WriteSocket(UDPClientSocket* socket, const std::string& msg) {
scoped_refptr<StringIOBuffer> io_buffer =
base::MakeRefCounted<StringIOBuffer>(msg);
TestCompletionCallback callback;
int rv = socket->Write(io_buffer.get(), io_buffer->size(),
callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
return callback.GetResult(rv);
}
void WriteSocketIgnoreResult(UDPClientSocket* socket,
const std::string& msg) {
WriteSocket(socket, msg);
}
// And again for a bare socket
int SendToSocket(UDPSocket* socket,
std::string msg,
const IPEndPoint& address) {
auto io_buffer = base::MakeRefCounted<StringIOBuffer>(msg);
TestCompletionCallback callback;
int rv = socket->SendTo(io_buffer.get(), io_buffer->size(), address,
callback.callback());
return callback.GetResult(rv);
}
base::expected<DatagramsMetadata, Error> ReadMultipleExpectedCount(
UDPSocket* socket,
IOBuffer* buffer,
size_t buf_len,
size_t maximum_packet_size,
size_t expected_count) {
CHECK_GE(buf_len, kMinimumReadMultipleBufferSize +
expected_count * maximum_packet_size);
DatagramsMetadata accumulated_metadata;
while (accumulated_metadata.size() < expected_count) {
base::test::ScopedRunLoopTimeout timeout(FROM_HERE, base::Seconds(5));
base::test::TestFuture<base::expected<DatagramsMetadata, Error>> future;
size_t current_offset = accumulated_metadata.size() * maximum_packet_size;
auto sub_span =
buffer->span().subspan(current_offset, buf_len - current_offset);
auto sub_buffer = base::MakeRefCounted<WrappedIOBuffer>(sub_span);
auto rv = socket->ReadMultiple(sub_buffer.get(), sub_span.size(),
maximum_packet_size, future.GetCallback());
base::expected<DatagramsMetadata, Error> result;
if (!rv.has_value() && rv.error() == ERR_IO_PENDING) {
result = future.Get();
} else {
result = std::move(rv);
}
if (!result.has_value()) {
return base::unexpected(result.error());
}
for (const auto& datagram_metadata : result.value()) {
accumulated_metadata.push_back(DatagramMetadata{
.offset = datagram_metadata.offset + current_offset,
.length = datagram_metadata.length,
.tos = datagram_metadata.tos,
});
}
}
return accumulated_metadata;
}
// Run unit test for a connection test.
// |use_nonblocking_io| is used to switch between overlapped and non-blocking
// IO on Windows. It has no effect in other ports.
void ConnectTest(bool use_nonblocking_io, bool use_async);
protected:
static const int kMaxRead = 1024;
scoped_refptr<IOBufferWithSize> buffer_;
IPEndPoint recv_from_address_;
};
const int UDPSocketTest::kMaxRead;
void ReadCompleteCallback(int* result_out,
base::OnceClosure callback,
int result) {
*result_out = result;
std::move(callback).Run();
}
void UDPSocketTest::ConnectTest(bool use_nonblocking_io, bool use_async) {
std::string simple_message("hello world!");
RecordingNetLogObserver net_log_observer;
// Setup the server to listen.
IPEndPoint server_address(IPAddress::IPv4Localhost(), 0 /* port */);
auto server =
std::make_unique<UDPServerSocket>(NetLog::Get(), NetLogSource());
if (use_nonblocking_io)
server->UseNonBlockingIO();
server->AllowAddressReuse();
ASSERT_THAT(server->Listen(server_address), IsOk());
// Get bound port.
ASSERT_THAT(server->GetLocalAddress(&server_address), IsOk());
// Setup the client.
auto client = std::make_unique<UDPClientSocket>(
DatagramSocket::DEFAULT_BIND, NetLog::Get(), NetLogSource(),
// Currently no tests that rely on this test multi-network scenarios.
// This makes it safe to always target the default network. Consider
// exposing a `target_network` parameter to this method if
// this changes.
handles::kInvalidNetworkHandle);
if (use_nonblocking_io)
client->UseNonBlockingIO();
if (!use_async) {
EXPECT_THAT(client->Connect(server_address), IsOk());
} else {
TestCompletionCallback callback;
int rv = client->ConnectAsync(server_address, callback.callback());
if (rv != OK) {
ASSERT_EQ(rv, ERR_IO_PENDING);
rv = callback.WaitForResult();
EXPECT_EQ(rv, OK);
} else {
EXPECT_EQ(rv, OK);
}
}
// Client sends to the server.
EXPECT_EQ(simple_message.length(),
static_cast<size_t>(WriteSocket(client.get(), simple_message)));
// Server waits for message.
std::string str = RecvFromSocket(server.get());
EXPECT_EQ(simple_message, str);
// Server echoes reply.
EXPECT_EQ(simple_message.length(),
static_cast<size_t>(SendToSocket(server.get(), simple_message)));
// Client waits for response.
str = ReadSocket(client.get());
EXPECT_EQ(simple_message, str);
// Test asynchronous read. Server waits for message.
base::RunLoop run_loop;
int read_result = 0;
int rv = server->RecvFrom(buffer_.get(), kMaxRead, &recv_from_address_,
base::BindOnce(&ReadCompleteCallback, &read_result,
run_loop.QuitClosure()));
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Client sends to the server.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&UDPSocketTest::WriteSocketIgnoreResult,
base::Unretained(this), client.get(), simple_message));
run_loop.Run();
EXPECT_EQ(simple_message.length(), static_cast<size_t>(read_result));
EXPECT_EQ(simple_message, std::string(buffer_->data(), read_result));
NetLogSource server_net_log_source = server->NetLog().source();
NetLogSource client_net_log_source = client->NetLog().source();
// Delete sockets so they log their final events.
server.reset();
client.reset();
// Check the server's log.
auto server_entries =
net_log_observer.GetEntriesForSource(server_net_log_source);
ASSERT_EQ(6u, server_entries.size());
EXPECT_TRUE(
LogContainsBeginEvent(server_entries, 0, NetLogEventType::SOCKET_ALIVE));
EXPECT_TRUE(LogContainsEvent(server_entries, 1,
NetLogEventType::UDP_LOCAL_ADDRESS,
NetLogEventPhase::NONE));
EXPECT_TRUE(LogContainsEvent(server_entries, 2,
NetLogEventType::UDP_BYTES_RECEIVED,
NetLogEventPhase::NONE));
EXPECT_TRUE(LogContainsEvent(server_entries, 3,
NetLogEventType::UDP_BYTES_SENT,
NetLogEventPhase::NONE));
EXPECT_TRUE(LogContainsEvent(server_entries, 4,
NetLogEventType::UDP_BYTES_RECEIVED,
NetLogEventPhase::NONE));
EXPECT_TRUE(
LogContainsEndEvent(server_entries, 5, NetLogEventType::SOCKET_ALIVE));
// Check the client's log.
auto client_entries =
net_log_observer.GetEntriesForSource(client_net_log_source);
EXPECT_EQ(7u, client_entries.size());
EXPECT_TRUE(
LogContainsBeginEvent(client_entries, 0, NetLogEventType::SOCKET_ALIVE));
EXPECT_TRUE(
LogContainsBeginEvent(client_entries, 1, NetLogEventType::UDP_CONNECT));
EXPECT_TRUE(
LogContainsEndEvent(client_entries, 2, NetLogEventType::UDP_CONNECT));
EXPECT_TRUE(LogContainsEvent(client_entries, 3,
NetLogEventType::UDP_BYTES_SENT,
NetLogEventPhase::NONE));
EXPECT_TRUE(LogContainsEvent(client_entries, 4,
NetLogEventType::UDP_BYTES_RECEIVED,
NetLogEventPhase::NONE));
EXPECT_TRUE(LogContainsEvent(client_entries, 5,
NetLogEventType::UDP_BYTES_SENT,
NetLogEventPhase::NONE));
EXPECT_TRUE(
LogContainsEndEvent(client_entries, 6, NetLogEventType::SOCKET_ALIVE));
}
TEST_F(UDPSocketTest, Connect) {
// The variable |use_nonblocking_io| has no effect in non-Windows ports.
// Run ConnectTest once with sync connect and once with async connect
ConnectTest(false, false);
ConnectTest(false, true);
}
TEST_F(UDPSocketTest, ConnectRestrictedPort) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList feature_list;
// Setup the server to listen.
UDPServerSocket server(NetLog::Get(), NetLogSource());
server.AllowAddressReuse();
ASSERT_THAT(server.Listen(IPEndPoint(IPAddress::IPv4Localhost(), 0)), IsOk());
// Get bound port.
IPEndPoint server_address;
ASSERT_THAT(server.GetLocalAddress(&server_address), IsOk());
feature_list.InitAndEnableFeatureWithParameters(
features::kRestrictAbusePortsOnLocalhost,
{{"localhost_restrict_ports",
base::NumberToString(server_address.port())}});
ReloadLocalhostRestrictedPortsForTesting();
const IPAddress addresses_to_test[] = {
IPAddress::IPv4Localhost(),
IPAddress(127, 0, 0, 2),
IPAddress::IPv4AllZeros(),
IPAddress::IPv6Localhost(),
IPAddress::IPv6AllZeros(),
ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4Localhost()),
ConvertIPv4ToIPv4MappedIPv6(IPAddress::IPv4AllZeros()),
};
int expected_count = 0;
for (const auto& address : addresses_to_test) {
auto client = std::make_unique<UDPClientSocket>(
DatagramSocket::DEFAULT_BIND, NetLog::Get(), NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(client->Connect(IPEndPoint(address, server_address.port())),
IsError(ERR_UNSAFE_PORT));
expected_count++;
}
histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts",
expected_count);
histogram_tester.ExpectBucketCount("Net.RestrictedLocalhostPorts",
server_address.port(), expected_count);
}
TEST_F(UDPSocketTest, ConnectUsingNetworkRestrictedPort) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList feature_list;
UDPServerSocket server(NetLog::Get(), NetLogSource());
server.AllowAddressReuse();
ASSERT_THAT(server.Listen(IPEndPoint(IPAddress::IPv4Localhost(), 0)), IsOk());
IPEndPoint server_address;
ASSERT_THAT(server.GetLocalAddress(&server_address), IsOk());
feature_list.InitAndEnableFeatureWithParameters(
features::kRestrictAbusePortsOnLocalhost,
{{"localhost_restrict_ports",
base::NumberToString(server_address.port())}});
ReloadLocalhostRestrictedPortsForTesting();
auto client = std::make_unique<UDPClientSocket>(
DatagramSocket::DEFAULT_BIND, NetLog::Get(), NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(client->ConnectUsingNetwork(1234, server_address),
IsError(ERR_UNSAFE_PORT));
histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 1);
}
TEST_F(UDPSocketTest, ConnectUsingDefaultNetworkRestrictedPort) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList feature_list;
UDPServerSocket server(NetLog::Get(), NetLogSource());
server.AllowAddressReuse();
ASSERT_THAT(server.Listen(IPEndPoint(IPAddress::IPv4Localhost(), 0)), IsOk());
IPEndPoint server_address;
ASSERT_THAT(server.GetLocalAddress(&server_address), IsOk());
feature_list.InitAndEnableFeatureWithParameters(
features::kRestrictAbusePortsOnLocalhost,
{{"localhost_restrict_ports",
base::NumberToString(server_address.port())}});
ReloadLocalhostRestrictedPortsForTesting();
auto client = std::make_unique<UDPClientSocket>(
DatagramSocket::DEFAULT_BIND, NetLog::Get(), NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(client->ConnectUsingDefaultNetwork(server_address),
IsError(ERR_UNSAFE_PORT));
histogram_tester.ExpectTotalCount("Net.RestrictedLocalhostPorts", 1);
}
#if BUILDFLAG(IS_WIN)
TEST_F(UDPSocketTest, ConnectNonBlocking) {
ConnectTest(true, false);
ConnectTest(true, true);
}
#endif
TEST_F(UDPSocketTest, PartialRecv) {
UDPServerSocket server_socket(nullptr, NetLogSource());
ASSERT_THAT(server_socket.Listen(IPEndPoint(IPAddress::IPv4Localhost(), 0)),
IsOk());
IPEndPoint server_address;
ASSERT_THAT(server_socket.GetLocalAddress(&server_address), IsOk());
UDPClientSocket client_socket(DatagramSocket::DEFAULT_BIND, nullptr,
NetLogSource(), handles::kInvalidNetworkHandle);
ASSERT_THAT(client_socket.Connect(server_address), IsOk());
std::string test_packet("hello world!");
ASSERT_EQ(static_cast<int>(test_packet.size()),
WriteSocket(&client_socket, test_packet));
TestCompletionCallback recv_callback;
// Read just 2 bytes. Read() is expected to return the first 2 bytes from the
// packet and discard the rest.
const int kPartialReadSize = 2;
auto buffer = base::MakeRefCounted<IOBufferWithSize>(kPartialReadSize);
int rv =
server_socket.RecvFrom(buffer.get(), kPartialReadSize,
&recv_from_address_, recv_callback.callback());
rv = recv_callback.GetResult(rv);
EXPECT_EQ(rv, ERR_MSG_TOO_BIG);
// Send a different message again.
std::string second_packet("Second packet");
ASSERT_EQ(static_cast<int>(second_packet.size()),
WriteSocket(&client_socket, second_packet));
// Read whole packet now.
std::string received = RecvFromSocket(&server_socket);
EXPECT_EQ(second_packet, received);
}
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID)
// - MacOS: requires root permissions on OSX 10.7+.
// - Android: devices attached to testbots don't have default network, so
// broadcasting to 255.255.255.255 returns error -109 (Address not reachable).
// crbug.com/139144.
#define MAYBE_LocalBroadcast DISABLED_LocalBroadcast
#else
#define MAYBE_LocalBroadcast LocalBroadcast
#endif
TEST_F(UDPSocketTest, MAYBE_LocalBroadcast) {
std::string first_message("first message"), second_message("second message");
IPEndPoint listen_address;
ASSERT_TRUE(CreateUDPAddress("0.0.0.0", 0 /* port */, &listen_address));
auto server1 =
std::make_unique<UDPServerSocket>(NetLog::Get(), NetLogSource());
auto server2 =
std::make_unique<UDPServerSocket>(NetLog::Get(), NetLogSource());
server1->AllowAddressReuse();
server1->AllowBroadcast();
server2->AllowAddressReuse();
server2->AllowBroadcast();
EXPECT_THAT(server1->Listen(listen_address), IsOk());
// Get bound port.
EXPECT_THAT(server1->GetLocalAddress(&listen_address), IsOk());
EXPECT_THAT(server2->Listen(listen_address), IsOk());
IPEndPoint broadcast_address;
ASSERT_TRUE(CreateUDPAddress("127.255.255.255", listen_address.port(),
&broadcast_address));
ASSERT_EQ(static_cast<int>(first_message.size()),
SendToSocket(server1.get(), first_message, broadcast_address));
std::string str = RecvFromSocket(server1.get());
ASSERT_EQ(first_message, str);
str = RecvFromSocket(server2.get());
ASSERT_EQ(first_message, str);
ASSERT_EQ(static_cast<int>(second_message.size()),
SendToSocket(server2.get(), second_message, broadcast_address));
str = RecvFromSocket(server1.get());
ASSERT_EQ(second_message, str);
str = RecvFromSocket(server2.get());
ASSERT_EQ(second_message, str);
}
// ConnectRandomBind verifies RANDOM_BIND is handled correctly. It connects
// 1000 sockets and then verifies that the allocated port numbers satisfy the
// following 2 conditions:
// 1. Range from min port value to max is greater than 10000.
// 2. There is at least one port in the 5 buckets in the [min, max] range.
//
// These conditions are not enough to verify that the port numbers are truly
// random, but they are enough to protect from most common non-random port
// allocation strategies (e.g. counter, pool of available ports, etc.) False
// positive result is theoretically possible, but its probability is negligible.
TEST_F(UDPSocketTest, ConnectRandomBind) {
const int kIterations = 1000;
std::vector<int> used_ports;
for (int i = 0; i < kIterations; ++i) {
UDPClientSocket socket(DatagramSocket::RANDOM_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(socket.Connect(IPEndPoint(IPAddress::IPv4Localhost(), 53)),
IsOk());
IPEndPoint client_address;
EXPECT_THAT(socket.GetLocalAddress(&client_address), IsOk());
used_ports.push_back(client_address.port());
}
int min_port = *std::min_element(used_ports.begin(), used_ports.end());
int max_port = *std::max_element(used_ports.begin(), used_ports.end());
int range = max_port - min_port + 1;
// Verify that the range of ports used by the random port allocator is wider
// than 10k. Assuming that socket implementation limits port range to 16k
// ports (default on Fuchsia) probability of false negative is below
// 10^-200.
static int kMinRange = 10000;
EXPECT_GT(range, kMinRange);
static int kBuckets = 5;
std::vector<int> bucket_sizes(kBuckets, 0);
for (int port : used_ports) {
bucket_sizes[(port - min_port) * kBuckets / range] += 1;
}
// Verify that there is at least one value in each bucket. Probability of
// false negative is below (kBuckets * (1 - 1 / kBuckets) ^ kIterations),
// which is less than 10^-96.
for (int size : bucket_sizes) {
EXPECT_GT(size, 0);
}
}
TEST_F(UDPSocketTest, ConnectFail) {
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV4), IsOk());
// Connect to an IPv6 address should fail since the socket was created for
// IPv4.
EXPECT_THAT(socket.Connect(net::IPEndPoint(IPAddress::IPv6Localhost(), 53)),
Not(IsOk()));
// Make sure that UDPSocket actually closed the socket.
EXPECT_FALSE(socket.is_connected());
}
// Similar to ConnectFail but UDPSocket adopts an opened socket instead of
// opening one directly.
TEST_F(UDPSocketTest, AdoptedSocket) {
auto socketfd =
CreatePlatformSocket(ConvertAddressFamily(ADDRESS_FAMILY_IPV4),
SOCK_DGRAM, AF_UNIX ? 0 : IPPROTO_UDP);
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
EXPECT_THAT(socket.AdoptOpenedSocket(ADDRESS_FAMILY_IPV4, socketfd), IsOk());
// Connect to an IPv6 address should fail since the socket was created for
// IPv4.
EXPECT_THAT(socket.Connect(net::IPEndPoint(IPAddress::IPv6Localhost(), 53)),
Not(IsOk()));
// Make sure that UDPSocket actually closed the socket.
EXPECT_FALSE(socket.is_connected());
}
// Tests that UDPSocket updates the global counter correctly.
TEST_F(UDPSocketTest, LimitAdoptSocket) {
ASSERT_EQ(0, GetGlobalUDPSocketCountForTesting());
{
// Creating a platform socket does not increase count.
auto socketfd =
CreatePlatformSocket(ConvertAddressFamily(ADDRESS_FAMILY_IPV4),
SOCK_DGRAM, AF_UNIX ? 0 : IPPROTO_UDP);
ASSERT_EQ(0, GetGlobalUDPSocketCountForTesting());
// Simply allocating a UDPSocket does not increase count.
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
EXPECT_EQ(0, GetGlobalUDPSocketCountForTesting());
// Calling AdoptOpenedSocket() allocates the socket and increases the global
// counter.
EXPECT_THAT(socket.AdoptOpenedSocket(ADDRESS_FAMILY_IPV4, socketfd),
IsOk());
EXPECT_EQ(1, GetGlobalUDPSocketCountForTesting());
// Connect to an IPv6 address should fail since the socket was created for
// IPv4.
EXPECT_THAT(socket.Connect(net::IPEndPoint(IPAddress::IPv6Localhost(), 53)),
Not(IsOk()));
// That Connect() failed doesn't change the global counter.
EXPECT_EQ(1, GetGlobalUDPSocketCountForTesting());
}
// Finally, destroying UDPSocket decrements the global counter.
EXPECT_EQ(0, GetGlobalUDPSocketCountForTesting());
}
// In this test, we verify that connect() on a socket will have the effect
// of filtering reads on this socket only to data read from the destination
// we connected to.
//
// The purpose of this test is that some documentation indicates that connect
// binds the client's sends to send to a particular server endpoint, but does
// not bind the client's reads to only be from that endpoint, and that we need
// to always use recvfrom() to disambiguate.
TEST_F(UDPSocketTest, VerifyConnectBindsAddr) {
std::string simple_message("hello world!");
std::string foreign_message("BAD MESSAGE TO GET!!");
// Setup the first server to listen.
IPEndPoint server1_address(IPAddress::IPv4Localhost(), 0 /* port */);
UDPServerSocket server1(nullptr, NetLogSource());
ASSERT_THAT(server1.Listen(server1_address), IsOk());
// Get the bound port.
ASSERT_THAT(server1.GetLocalAddress(&server1_address), IsOk());
// Setup the second server to listen.
IPEndPoint server2_address(IPAddress::IPv4Localhost(), 0 /* port */);
UDPServerSocket server2(nullptr, NetLogSource());
ASSERT_THAT(server2.Listen(server2_address), IsOk());
// Setup the client, connected to server 1.
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(client.Connect(server1_address), IsOk());
// Client sends to server1.
EXPECT_EQ(simple_message.length(),
static_cast<size_t>(WriteSocket(&client, simple_message)));
// Server1 waits for message.
std::string str = RecvFromSocket(&server1);
EXPECT_EQ(simple_message, str);
// Get the client's address.
IPEndPoint client_address;
EXPECT_THAT(client.GetLocalAddress(&client_address), IsOk());
// Server2 sends reply.
EXPECT_EQ(foreign_message.length(),
static_cast<size_t>(
SendToSocket(&server2, foreign_message, client_address)));
// Server1 sends reply.
EXPECT_EQ(simple_message.length(),
static_cast<size_t>(
SendToSocket(&server1, simple_message, client_address)));
// Client waits for response.
str = ReadSocket(&client);
EXPECT_EQ(simple_message, str);
}
TEST_F(UDPSocketTest, ClientGetLocalPeerAddresses) {
struct TestData {
std::string remote_address;
std::string local_address;
bool may_fail;
} tests[] = {
{"127.0.00.1", "127.0.0.1", false},
{"::1", "::1", true},
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
// Addresses below are disabled on Android. See crbug.com/161248
// They are also disabled on iOS. See https://crbug.com/523225
{"192.168.1.1", "127.0.0.1", false},
{"2001:db8:0::42", "::1", true},
#endif
};
for (const auto& test : tests) {
SCOPED_TRACE(std::string("Connecting from ") + test.local_address +
std::string(" to ") + test.remote_address);
IPAddress ip_address;
EXPECT_TRUE(ip_address.AssignFromIPLiteral(test.remote_address));
IPEndPoint remote_address(ip_address, 80);
EXPECT_TRUE(ip_address.AssignFromIPLiteral(test.local_address));
IPEndPoint local_address(ip_address, 80);
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr,
NetLogSource(), handles::kInvalidNetworkHandle);
int rv = client.Connect(remote_address);
if (test.may_fail && rv == ERR_ADDRESS_UNREACHABLE) {
// Connect() may return ERR_ADDRESS_UNREACHABLE for IPv6
// addresses if IPv6 is not configured.
continue;
}
EXPECT_LE(ERR_IO_PENDING, rv);
IPEndPoint fetched_local_address;
rv = client.GetLocalAddress(&fetched_local_address);
EXPECT_THAT(rv, IsOk());
// TODO(mbelshe): figure out how to verify the IP and port.
// The port is dynamically generated by the udp stack.
// The IP is the real IP of the client, not necessarily
// loopback.
// EXPECT_EQ(local_address.address(), fetched_local_address.address());
IPEndPoint fetched_remote_address;
rv = client.GetPeerAddress(&fetched_remote_address);
EXPECT_THAT(rv, IsOk());
EXPECT_EQ(remote_address, fetched_remote_address);
}
}
TEST_F(UDPSocketTest, ServerGetLocalAddress) {
IPEndPoint bind_address(IPAddress::IPv4Localhost(), 0);
UDPServerSocket server(nullptr, NetLogSource());
int rv = server.Listen(bind_address);
EXPECT_THAT(rv, IsOk());
IPEndPoint local_address;
rv = server.GetLocalAddress(&local_address);
EXPECT_EQ(rv, 0);
// Verify that port was allocated.
EXPECT_GT(local_address.port(), 0);
EXPECT_EQ(local_address.address(), bind_address.address());
}
TEST_F(UDPSocketTest, ServerGetPeerAddress) {
IPEndPoint bind_address(IPAddress::IPv4Localhost(), 0);
UDPServerSocket server(nullptr, NetLogSource());
int rv = server.Listen(bind_address);
EXPECT_THAT(rv, IsOk());
IPEndPoint peer_address;
rv = server.GetPeerAddress(&peer_address);
EXPECT_EQ(rv, ERR_SOCKET_NOT_CONNECTED);
}
TEST_F(UDPSocketTest, ClientSetDoNotFragment) {
for (std::string ip : {"127.0.0.1", "::1"}) {
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr,
NetLogSource(), handles::kInvalidNetworkHandle);
IPAddress ip_address;
EXPECT_TRUE(ip_address.AssignFromIPLiteral(ip));
IPEndPoint remote_address(ip_address, 80);
int rv = client.Connect(remote_address);
// May fail on IPv6 is IPv6 is not configured.
if (ip_address.IsIPv6() && rv == ERR_ADDRESS_UNREACHABLE)
return;
EXPECT_THAT(rv, IsOk());
rv = client.SetDoNotFragment();
#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_FUCHSIA)
// TODO(crbug.com/42050633): IP_MTU_DISCOVER is not implemented on Fuchsia.
EXPECT_THAT(rv, IsError(ERR_NOT_IMPLEMENTED));
#else
EXPECT_THAT(rv, IsOk());
#endif
}
}
TEST_F(UDPSocketTest, ServerSetDoNotFragment) {
for (std::string ip : {"127.0.0.1", "::1"}) {
IPEndPoint bind_address;
ASSERT_TRUE(CreateUDPAddress(ip, 0, &bind_address));
UDPServerSocket server(nullptr, NetLogSource());
int rv = server.Listen(bind_address);
// May fail on IPv6 is IPv6 is not configure
if (bind_address.address().IsIPv6() &&
(rv == ERR_ADDRESS_INVALID || rv == ERR_ADDRESS_UNREACHABLE))
return;
EXPECT_THAT(rv, IsOk());
rv = server.SetDoNotFragment();
#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_FUCHSIA)
// TODO(crbug.com/42050633): IP_MTU_DISCOVER is not implemented on Fuchsia.
EXPECT_THAT(rv, IsError(ERR_NOT_IMPLEMENTED));
#else
EXPECT_THAT(rv, IsOk());
#endif
}
}
// Close the socket while read is pending.
TEST_F(UDPSocketTest, CloseWithPendingRead) {
IPEndPoint bind_address(IPAddress::IPv4Localhost(), 0);
UDPServerSocket server(nullptr, NetLogSource());
int rv = server.Listen(bind_address);
EXPECT_THAT(rv, IsOk());
TestCompletionCallback callback;
IPEndPoint from;
rv = server.RecvFrom(buffer_.get(), kMaxRead, &from, callback.callback());
EXPECT_EQ(rv, ERR_IO_PENDING);
server.Close();
EXPECT_FALSE(callback.have_result());
}
// Some Android devices do not support multicast.
// The ones supporting multicast need WifiManager.MulitcastLock to enable it.
// http://goo.gl/jjAk9
#if !BUILDFLAG(IS_ANDROID)
TEST_F(UDPSocketTest, JoinMulticastGroup) {
#if BUILDFLAG(IS_MAC)
// See https://crbug.com/354933441
if (base::mac::MacOSMajorVersion() >= 15) {
GTEST_SKIP() << "Disabled on macOS Sequoia and later OS versions.";
}
#endif
const char kGroup[] = "237.132.100.17";
IPAddress group_ip;
EXPECT_TRUE(group_ip.AssignFromIPLiteral(kGroup));
// TODO(https://github.com/google/gvisor/issues/3839): don't guard on
// OS_FUCHSIA.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
IPEndPoint bind_address(IPAddress::AllZeros(group_ip.size()), 0 /* port */);
#else
IPEndPoint bind_address(group_ip, 0 /* port */);
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
EXPECT_THAT(socket.Open(bind_address.GetFamily()), IsOk());
EXPECT_THAT(socket.Bind(bind_address), IsOk());
EXPECT_THAT(socket.JoinGroup(group_ip), IsOk());
// Joining group multiple times.
EXPECT_NE(OK, socket.JoinGroup(group_ip));
EXPECT_THAT(socket.LeaveGroup(group_ip), IsOk());
// Leaving group multiple times.
EXPECT_NE(OK, socket.LeaveGroup(group_ip));
socket.Close();
}
// TODO(crbug.com/40620614): failing on device on iOS 12.2.
// TODO(crbug.com/40189274): flaky on Mac 11.
#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_MAC)
#define MAYBE_SharedMulticastAddress DISABLED_SharedMulticastAddress
#else
#define MAYBE_SharedMulticastAddress SharedMulticastAddress
#endif
TEST_F(UDPSocketTest, MAYBE_SharedMulticastAddress) {
const char kGroup[] = "224.0.0.251";
IPAddress group_ip;
ASSERT_TRUE(group_ip.AssignFromIPLiteral(kGroup));
// TODO(https://github.com/google/gvisor/issues/3839): don't guard on
// OS_FUCHSIA.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
IPEndPoint receive_address(IPAddress::AllZeros(group_ip.size()),
0 /* port */);
#else
IPEndPoint receive_address(group_ip, 0 /* port */);
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)
NetworkInterfaceList interfaces;
ASSERT_TRUE(GetNetworkList(&interfaces, 0));
// The test fails with the Hyper-V switch interface (on the host side).
std::erase_if(interfaces, [](const auto& iface) {
return iface.friendly_name.rfind("vEthernet", 0) == 0;
});
ASSERT_FALSE(interfaces.empty());
// Setup first receiving socket.
UDPServerSocket socket1(nullptr, NetLogSource());
socket1.AllowAddressSharingForMulticast();
ASSERT_THAT(socket1.SetMulticastInterface(interfaces[0].interface_index),
IsOk());
ASSERT_THAT(socket1.Listen(receive_address), IsOk());
ASSERT_THAT(socket1.JoinGroup(group_ip), IsOk());
// Get the bound port.
ASSERT_THAT(socket1.GetLocalAddress(&receive_address), IsOk());
// Setup second receiving socket.
UDPServerSocket socket2(nullptr, NetLogSource());
socket2.AllowAddressSharingForMulticast();
ASSERT_THAT(socket2.SetMulticastInterface(interfaces[0].interface_index),
IsOk());
ASSERT_THAT(socket2.Listen(receive_address), IsOk());
ASSERT_THAT(socket2.JoinGroup(group_ip), IsOk());
// Setup client socket.
IPEndPoint send_address(group_ip, receive_address.port());
UDPClientSocket client_socket(DatagramSocket::DEFAULT_BIND, nullptr,
NetLogSource(), handles::kInvalidNetworkHandle);
ASSERT_THAT(client_socket.Connect(send_address), IsOk());
#if !BUILDFLAG(IS_CHROMEOS)
// Send a message via the multicast group. That message is expected be be
// received by both receving sockets.
//
// Skip on ChromeOS where it's known to sometimes not work.
// TODO(crbug.com/898964): If possible, fix and reenable.
const char kMessage[] = "hello!";
ASSERT_GE(WriteSocket(&client_socket, kMessage), 0);
EXPECT_EQ(kMessage, RecvFromSocket(&socket1));
EXPECT_EQ(kMessage, RecvFromSocket(&socket2));
#endif // !BUILDFLAG(IS_CHROMEOS)
}
#endif // !BUILDFLAG(IS_ANDROID)
TEST_F(UDPSocketTest, MulticastOptions) {
IPEndPoint bind_address;
ASSERT_TRUE(CreateUDPAddress("0.0.0.0", 0 /* port */, &bind_address));
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
// Before binding.
EXPECT_THAT(socket.SetMulticastLoopbackMode(false), IsOk());
EXPECT_THAT(socket.SetMulticastLoopbackMode(true), IsOk());
EXPECT_THAT(socket.SetMulticastTimeToLive(0), IsOk());
EXPECT_THAT(socket.SetMulticastTimeToLive(3), IsOk());
EXPECT_NE(OK, socket.SetMulticastTimeToLive(-1));
EXPECT_THAT(socket.SetMulticastInterface(0), IsOk());
EXPECT_THAT(socket.Open(bind_address.GetFamily()), IsOk());
EXPECT_THAT(socket.Bind(bind_address), IsOk());
EXPECT_NE(OK, socket.SetMulticastLoopbackMode(false));
EXPECT_NE(OK, socket.SetMulticastTimeToLive(0));
EXPECT_NE(OK, socket.SetMulticastInterface(0));
socket.Close();
}
// Checking that DSCP bits are set correctly is difficult,
// but let's check that the code doesn't crash at least.
TEST_F(UDPSocketTest, SetDSCP) {
// Setup the server to listen.
IPEndPoint bind_address;
UDPSocket client(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
// We need a real IP, but we won't actually send anything to it.
ASSERT_TRUE(CreateUDPAddress("8.8.8.8", 9999, &bind_address));
int rv = client.Open(bind_address.GetFamily());
EXPECT_THAT(rv, IsOk());
rv = client.Connect(bind_address);
if (rv != OK) {
// Let's try localhost then.
bind_address = IPEndPoint(IPAddress::IPv4Localhost(), 9999);
rv = client.Connect(bind_address);
}
EXPECT_THAT(rv, IsOk());
client.SetDiffServCodePoint(DSCP_NO_CHANGE);
client.SetDiffServCodePoint(DSCP_AF41);
client.SetDiffServCodePoint(DSCP_DEFAULT);
client.SetDiffServCodePoint(DSCP_CS2);
client.SetDiffServCodePoint(DSCP_NO_CHANGE);
client.SetDiffServCodePoint(DSCP_DEFAULT);
client.Close();
}
// Send DSCP + ECN marked packets from server to client and verify the TOS
// bytes that arrive.
TEST_F(UDPSocketTest, VerifyDscpAndEcnExchangeV4) {
IPEndPoint server_address(IPAddress::IPv4Localhost(), 0);
UDPServerSocket server(nullptr, NetLogSource());
server.AllowAddressReuse();
ASSERT_THAT(server.Listen(server_address), IsOk());
// Get bound port.
ASSERT_THAT(server.GetLocalAddress(&server_address), IsOk());
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
client.Connect(server_address);
EXPECT_EQ(client.SetRecvTos(), 0);
EXPECT_EQ(server.SetRecvTos(), 0);
#if BUILDFLAG(IS_WIN)
// Do not exercise the DSCP code because it requires a mock Qwave API.
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, ECN_ECT1), 0);
#else
EXPECT_EQ(client.SetTos(DSCP_AF41, ECN_ECT1), 0);
#endif
std::string client_message = "hello";
EXPECT_EQ(WriteSocket(&client, client_message),
static_cast<int>(client_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_AF41, ECN_ECT1),
client_message.data());
// Server messages
EXPECT_EQ(server.SetTos(DSCP_AF41, ECN_ECT1), 0);
std::string first_message = "foobar";
EXPECT_EQ(SendToSocket(&server, first_message),
static_cast<int>(first_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_AF41, ECN_ECT1), first_message.data());
std::string second_message = "foo";
EXPECT_EQ(server.SetTos(DSCP_CS2, ECN_ECT0), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_CS2, ECN_ECT0), second_message.data());
#if BUILDFLAG(IS_WIN)
// The Windows sendmsg API does not allow setting ECN_CE as the outgoing mark.
EcnCodePoint final_ecn = ECN_ECT1;
#else
EcnCodePoint final_ecn = ECN_CE;
#endif
EXPECT_EQ(server.SetTos(DSCP_NO_CHANGE, final_ecn), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_CS2, final_ecn), second_message.data());
EXPECT_EQ(server.SetTos(DSCP_AF41, ECN_NO_CHANGE), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_AF41, final_ecn), second_message.data());
EXPECT_EQ(server.SetTos(DSCP_NO_CHANGE, ECN_NO_CHANGE), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_AF41, final_ecn), second_message.data());
server.Close();
client.Close();
}
// Send DSCP + ECN marked packets from server to client and verify the TOS
// bytes that arrive.
TEST_F(UDPSocketTest, VerifyDscpAndEcnExchangeV6) {
IPEndPoint server_address(IPAddress::IPv6Localhost(), 0);
UDPServerSocket server(nullptr, NetLogSource());
server.AllowAddressReuse();
ASSERT_THAT(server.Listen(server_address), IsOk());
// Get bound port.
ASSERT_THAT(server.GetLocalAddress(&server_address), IsOk());
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(client.Connect(server_address), IsOk());
EXPECT_EQ(client.SetRecvTos(), 0);
EXPECT_EQ(server.SetRecvTos(), 0);
#if BUILDFLAG(IS_WIN)
// Do not exercise the DSCP code because it requires a mock Qwave API.
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, ECN_ECT1), 0);
#else
EXPECT_EQ(client.SetTos(DSCP_AF41, ECN_ECT1), 0);
#endif
std::string client_message = "hello";
EXPECT_EQ(WriteSocket(&client, client_message),
static_cast<int>(client_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_AF41, ECN_ECT1),
client_message.data());
// Server messages
EXPECT_EQ(server.SetTos(DSCP_AF41, ECN_ECT1), 0);
std::string first_message = "foobar";
EXPECT_EQ(SendToSocket(&server, first_message),
static_cast<int>(first_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_AF41, ECN_ECT1), first_message.data());
std::string second_message = "foo";
EXPECT_EQ(server.SetTos(DSCP_CS2, ECN_ECT0), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_CS2, ECN_ECT0), second_message.data());
#if BUILDFLAG(IS_WIN)
// The Windows sendmsg API does not allow setting ECN_CE as the outgoing mark.
EcnCodePoint final_ecn = ECN_ECT1;
#else
EcnCodePoint final_ecn = ECN_CE;
#endif
EXPECT_EQ(server.SetTos(DSCP_NO_CHANGE, final_ecn), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_CS2, final_ecn), second_message.data());
EXPECT_EQ(server.SetTos(DSCP_AF41, ECN_NO_CHANGE), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_AF41, final_ecn), second_message.data());
EXPECT_EQ(server.SetTos(DSCP_NO_CHANGE, ECN_NO_CHANGE), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_AF41, final_ecn), second_message.data());
server.Close();
client.Close();
}
// Send DSCP + ECN marked packets from client to a dual-stack server and verify
// the TOS bytes that arrive.
TEST_F(UDPSocketTest, VerifyDscpAndEcnExchangeDualStack) {
IPEndPoint server_v6_address(IPAddress::IPv6AllZeros(), 0);
UDPServerSocket server(nullptr, NetLogSource());
server.AllowAddressReuse();
ASSERT_THAT(server.Listen(server_v6_address), IsOk());
// Get bound port.
ASSERT_THAT(server.GetLocalAddress(&server_v6_address), IsOk());
// The server is bound to IPV6_ANY, so it will receive IPv4 packets addressed
// to localhost.
IPEndPoint server_v4_address(IPAddress::IPv4Localhost(),
server_v6_address.port());
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(client.Connect(server_v4_address), IsOk());
EXPECT_EQ(server.SetRecvTos(), 0);
#if BUILDFLAG(IS_WIN)
// Windows requires a Mock QWave API to allow the client to set the DSCP. For
// efficiency reasons, Chromium windows UDP sockets do not provide access to
// incoming DSCP anyway. To avoid all the mocking, don't set the DSCP at all
// for Windows. RecvFromSocket() doesn't check the DSCP for Windows.
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, ECN_ECT1), 0);
#else
EXPECT_EQ(client.SetTos(DSCP_AF41, ECN_ECT1), 0);
#endif //! BUILDFLAG(IS_WIN)
std::string first_message = "foobar";
EXPECT_EQ(WriteSocket(&client, first_message),
static_cast<int>(first_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_AF41, ECN_ECT1), first_message.data());
std::string second_message = "foo";
#if BUILDFLAG(IS_WIN)
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, ECN_ECT0), 0);
#else
EXPECT_EQ(client.SetTos(DSCP_CS2, ECN_ECT0), 0);
#endif
EXPECT_EQ(WriteSocket(&client, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_CS2, ECN_ECT0), second_message.data());
#if BUILDFLAG(IS_WIN)
// The Windows sendmsg API does not allow setting ECN_CE as the outgoing mark.
EcnCodePoint final_ecn = ECN_ECT1;
#else
EcnCodePoint final_ecn = ECN_CE;
#endif
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, final_ecn), 0);
EXPECT_EQ(WriteSocket(&client, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_CS2, final_ecn),
second_message.data());
#if !BUILDFLAG(IS_WIN)
EXPECT_EQ(client.SetTos(DSCP_AF41, ECN_NO_CHANGE), 0);
#endif
EXPECT_EQ(WriteSocket(&client, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_AF41, final_ecn),
second_message.data());
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, ECN_NO_CHANGE), 0);
EXPECT_EQ(WriteSocket(&client, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_AF41, final_ecn),
second_message.data());
server.Close();
client.Close();
}
// Send DSCP + ECN marked packets from client to a dual-stack server and verify
// the TOS bytes that arrive.
TEST_F(UDPSocketTest, VerifyDscpAndEcnExchangeDualStackV4Mapped) {
// Bind to a v4-mapped localhost address
IPEndPoint server_v6_address(*IPAddress::FromIPLiteral("::ffff:7f00:0001"),
0);
UDPServerSocket server(nullptr, NetLogSource());
server.AllowAddressReuse();
ASSERT_THAT(server.Listen(server_v6_address), IsOk());
// Get bound port.
ASSERT_THAT(server.GetLocalAddress(&server_v6_address), IsOk());
IPEndPoint server_v4_address(IPAddress::IPv4Localhost(),
server_v6_address.port());
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(client.Connect(server_v4_address), IsOk());
EXPECT_EQ(server.SetRecvTos(), 0);
#if BUILDFLAG(IS_WIN)
// Windows requires a Mock QWave API to allow the client to set the DSCP. For
// efficiency reasons, Chromium windows UDP sockets do not provide access to
// incoming DSCP anyway. To avoid all the mocking, don't set the DSCP at all
// for Windows. RecvFromSocket() doesn't check the DSCP for Windows.
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, ECN_ECT1), 0);
#else
EXPECT_EQ(client.SetTos(DSCP_AF41, ECN_ECT1), 0);
#endif
std::string first_message = "foobar";
EXPECT_EQ(WriteSocket(&client, first_message),
static_cast<int>(first_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_AF41, ECN_ECT1), first_message.data());
std::string second_message = "foo";
#if BUILDFLAG(IS_WIN)
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, ECN_ECT0), 0);
#else
EXPECT_EQ(client.SetTos(DSCP_CS2, ECN_ECT0), 0);
#endif
EXPECT_EQ(WriteSocket(&client, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_CS2, ECN_ECT0), second_message.data());
#if BUILDFLAG(IS_WIN)
// The Windows sendmsg API does not allow setting ECN_CE as the outgoing mark.
EcnCodePoint final_ecn = ECN_ECT1;
#else
EcnCodePoint final_ecn = ECN_CE;
#endif
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, final_ecn), 0);
EXPECT_EQ(WriteSocket(&client, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_CS2, final_ecn),
second_message.data());
#if !BUILDFLAG(IS_WIN)
EXPECT_EQ(client.SetTos(DSCP_AF41, ECN_NO_CHANGE), 0);
#endif
EXPECT_EQ(WriteSocket(&client, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_AF41, final_ecn),
second_message.data());
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, ECN_NO_CHANGE), 0);
EXPECT_EQ(WriteSocket(&client, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_AF41, final_ecn),
second_message.data());
server.Close();
client.Close();
}
// For windows, test with Nonblocking sockets. For other platforms, this test
// is identical to VerifyDscpAndEcnExchange, above.
TEST_F(UDPSocketTest, VerifyDscpAndEcnExchangeNonBlocking) {
IPEndPoint server_address(IPAddress::IPv4Localhost(), 0);
UDPServerSocket server(nullptr, NetLogSource());
server.UseNonBlockingIO();
server.AllowAddressReuse();
ASSERT_THAT(server.Listen(server_address), IsOk());
// Get bound port.
ASSERT_THAT(server.GetLocalAddress(&server_address), IsOk());
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
client.UseNonBlockingIO();
client.Connect(server_address);
EXPECT_EQ(client.SetRecvTos(), 0);
EXPECT_EQ(server.SetRecvTos(), 0);
#if BUILDFLAG(IS_WIN)
// Do not exercise the DSCP code because it requires a mock Qwave API.
EXPECT_EQ(client.SetTos(DSCP_NO_CHANGE, ECN_ECT1), 0);
#else
EXPECT_EQ(client.SetTos(DSCP_AF41, ECN_ECT1), 0);
#endif
std::string client_message = "hello";
EXPECT_EQ(WriteSocket(&client, client_message),
static_cast<int>(client_message.length()));
EXPECT_EQ(RecvFromSocket(&server, DSCP_AF41, ECN_ECT1),
client_message.data());
// Server messages
EXPECT_EQ(server.SetTos(DSCP_AF41, ECN_ECT1), 0);
std::string first_message = "foobar";
EXPECT_EQ(SendToSocket(&server, first_message),
static_cast<int>(first_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_AF41, ECN_ECT1), first_message.data());
std::string second_message = "foo";
EXPECT_EQ(server.SetTos(DSCP_CS2, ECN_ECT0), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_CS2, ECN_ECT0), second_message.data());
// The Windows sendmsg API does not allow setting ECN_CE as the outgoing mark.
EcnCodePoint final_ecn = ECN_ECT1;
EXPECT_EQ(server.SetTos(DSCP_NO_CHANGE, final_ecn), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_CS2, final_ecn), second_message.data());
EXPECT_EQ(server.SetTos(DSCP_AF41, ECN_NO_CHANGE), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_AF41, final_ecn), second_message.data());
EXPECT_EQ(server.SetTos(DSCP_NO_CHANGE, ECN_NO_CHANGE), 0);
EXPECT_EQ(SendToSocket(&server, second_message),
static_cast<int>(second_message.length()));
EXPECT_EQ(ReadSocket(&client, DSCP_AF41, final_ecn), second_message.data());
server.Close();
client.Close();
}
TEST_F(UDPSocketTest, ConnectUsingNetwork) {
// The specific value of this address doesn't really matter, and no
// server needs to be running here. The test only needs to call
// ConnectUsingNetwork() and won't send any datagrams.
const IPEndPoint fake_server_address(IPAddress::IPv4Localhost(), 8080);
const handles::NetworkHandle wrong_network_handle = 65536;
#if BUILDFLAG(IS_ANDROID)
NetworkChangeNotifierFactoryAndroid ncn_factory;
NetworkChangeNotifier::DisableForTest ncn_disable_for_test;
std::unique_ptr<NetworkChangeNotifier> ncn(ncn_factory.CreateInstance());
if (!NetworkChangeNotifier::AreNetworkHandlesSupported())
GTEST_SKIP() << "Network handles are required to test BindToNetwork.";
{
// Connecting using a not existing network should fail but not report
// ERR_NOT_IMPLEMENTED when network handles are supported.
UDPClientSocket socket(DatagramSocket::RANDOM_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
int rv =
socket.ConnectUsingNetwork(wrong_network_handle, fake_server_address);
EXPECT_NE(ERR_NOT_IMPLEMENTED, rv);
EXPECT_NE(OK, rv);
EXPECT_NE(wrong_network_handle, socket.GetBoundNetwork());
}
{
// Connecting using an existing network should succeed when
// NetworkChangeNotifier returns a valid default network.
UDPClientSocket socket(DatagramSocket::RANDOM_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
const handles::NetworkHandle network_handle =
NetworkChangeNotifier::GetDefaultNetwork();
if (network_handle != handles::kInvalidNetworkHandle) {
EXPECT_EQ(
OK, socket.ConnectUsingNetwork(network_handle, fake_server_address));
EXPECT_EQ(network_handle, socket.GetBoundNetwork());
}
}
#else
UDPClientSocket socket(DatagramSocket::RANDOM_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_EQ(
ERR_NOT_IMPLEMENTED,
socket.ConnectUsingNetwork(wrong_network_handle, fake_server_address));
#endif // BUILDFLAG(IS_ANDROID)
}
TEST_F(UDPSocketTest, ConnectUsingNetworkAsync) {
// The specific value of this address doesn't really matter, and no
// server needs to be running here. The test only needs to call
// ConnectUsingNetwork() and won't send any datagrams.
const IPEndPoint fake_server_address(IPAddress::IPv4Localhost(), 8080);
const handles::NetworkHandle wrong_network_handle = 65536;
#if BUILDFLAG(IS_ANDROID)
NetworkChangeNotifierFactoryAndroid ncn_factory;
NetworkChangeNotifier::DisableForTest ncn_disable_for_test;
std::unique_ptr<NetworkChangeNotifier> ncn(ncn_factory.CreateInstance());
if (!NetworkChangeNotifier::AreNetworkHandlesSupported())
GTEST_SKIP() << "Network handles are required to test BindToNetwork.";
{
// Connecting using a not existing network should fail but not report
// ERR_NOT_IMPLEMENTED when network handles are supported.
UDPClientSocket socket(DatagramSocket::RANDOM_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
TestCompletionCallback callback;
int rv = socket.ConnectUsingNetworkAsync(
wrong_network_handle, fake_server_address, callback.callback());
if (rv == ERR_IO_PENDING) {
rv = callback.WaitForResult();
}
EXPECT_NE(ERR_NOT_IMPLEMENTED, rv);
EXPECT_NE(OK, rv);
}
{
// Connecting using an existing network should succeed when
// NetworkChangeNotifier returns a valid default network.
UDPClientSocket socket(DatagramSocket::RANDOM_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
TestCompletionCallback callback;
const handles::NetworkHandle network_handle =
NetworkChangeNotifier::GetDefaultNetwork();
if (network_handle != handles::kInvalidNetworkHandle) {
int rv = socket.ConnectUsingNetworkAsync(
network_handle, fake_server_address, callback.callback());
if (rv == ERR_IO_PENDING) {
rv = callback.WaitForResult();
}
EXPECT_EQ(OK, rv);
EXPECT_EQ(network_handle, socket.GetBoundNetwork());
}
}
#else
UDPClientSocket socket(DatagramSocket::RANDOM_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
TestCompletionCallback callback;
EXPECT_EQ(ERR_NOT_IMPLEMENTED, socket.ConnectUsingNetworkAsync(
wrong_network_handle, fake_server_address,
callback.callback()));
#endif // BUILDFLAG(IS_ANDROID)
}
} // namespace
#if BUILDFLAG(IS_WIN)
namespace {
const HANDLE kFakeHandle1 = (HANDLE)12;
const HANDLE kFakeHandle2 = (HANDLE)13;
const QOS_FLOWID kFakeFlowId1 = (QOS_FLOWID)27;
const QOS_FLOWID kFakeFlowId2 = (QOS_FLOWID)38;
class TestUDPSocketWin : public UDPSocketWin {
public:
TestUDPSocketWin(QwaveApi* qos,
DatagramSocket::BindType bind_type,
net::NetLog* net_log,
const net::NetLogSource& source)
: UDPSocketWin(bind_type, net_log, source), qos_(qos) {}
TestUDPSocketWin(const TestUDPSocketWin&) = delete;
TestUDPSocketWin& operator=(const TestUDPSocketWin&) = delete;
// Overriding GetQwaveApi causes the test class to use the injected mock
// QwaveApi instance instead of the singleton.
QwaveApi* GetQwaveApi() const override { return qos_; }
private:
raw_ptr<QwaveApi> qos_;
};
class MockQwaveApi : public QwaveApi {
public:
MOCK_CONST_METHOD0(qwave_supported, bool());
MOCK_METHOD0(OnFatalError, void());
MOCK_METHOD2(CreateHandle, BOOL(PQOS_VERSION version, PHANDLE handle));
MOCK_METHOD1(CloseHandle, BOOL(HANDLE handle));
MOCK_METHOD6(AddSocketToFlow,
BOOL(HANDLE handle,
SOCKET socket,
PSOCKADDR addr,
QOS_TRAFFIC_TYPE traffic_type,
DWORD flags,
PQOS_FLOWID flow_id));
MOCK_METHOD4(
RemoveSocketFromFlow,
BOOL(HANDLE handle, SOCKET socket, QOS_FLOWID flow_id, DWORD reserved));
MOCK_METHOD7(SetFlow,
BOOL(HANDLE handle,
QOS_FLOWID flow_id,
QOS_SET_FLOW op,
ULONG size,
PVOID data,
DWORD reserved,
LPOVERLAPPED overlapped));
};
std::unique_ptr<UDPSocket> OpenedDscpTestClient(QwaveApi* api,
IPEndPoint bind_address) {
auto client = std::make_unique<TestUDPSocketWin>(
api, DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
int rv = client->Open(bind_address.GetFamily());
EXPECT_THAT(rv, IsOk());
return client;
}
std::unique_ptr<UDPSocket> ConnectedDscpTestClient(QwaveApi* api) {
IPEndPoint bind_address;
// We need a real IP, but we won't actually send anything to it.
EXPECT_TRUE(CreateUDPAddress("8.8.8.8", 9999, &bind_address));
auto client = OpenedDscpTestClient(api, bind_address);
EXPECT_THAT(client->Connect(bind_address), IsOk());
return client;
}
std::unique_ptr<UDPSocket> UnconnectedDscpTestClient(QwaveApi* api) {
IPEndPoint bind_address;
EXPECT_TRUE(CreateUDPAddress("0.0.0.0", 9999, &bind_address));
auto client = OpenedDscpTestClient(api, bind_address);
EXPECT_THAT(client->Bind(bind_address), IsOk());
return client;
}
} // namespace
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::_;
TEST_F(UDPSocketTest, SetDSCPNoopIfPassedNoChange) {
MockQwaveApi api;
EXPECT_CALL(api, qwave_supported()).WillRepeatedly(Return(true));
EXPECT_CALL(api, AddSocketToFlow(_, _, _, _, _, _)).Times(0);
std::unique_ptr<UDPSocket> client = ConnectedDscpTestClient(&api);
EXPECT_THAT(client->SetDiffServCodePoint(DSCP_NO_CHANGE), IsOk());
}
TEST_F(UDPSocketTest, SetDSCPFailsIfQOSDoesntLink) {
MockQwaveApi api;
EXPECT_CALL(api, qwave_supported()).WillRepeatedly(Return(false));
EXPECT_CALL(api, CreateHandle(_, _)).Times(0);
std::unique_ptr<UDPSocket> client = ConnectedDscpTestClient(&api);
EXPECT_EQ(ERR_NOT_IMPLEMENTED, client->SetDiffServCodePoint(DSCP_AF41));
}
TEST_F(UDPSocketTest, SetDSCPFailsIfHandleCantBeCreated) {
MockQwaveApi api;
EXPECT_CALL(api, qwave_supported()).WillRepeatedly(Return(true));
EXPECT_CALL(api, CreateHandle(_, _)).WillOnce(Return(false));
EXPECT_CALL(api, OnFatalError()).Times(1);
std::unique_ptr<UDPSocket> client = ConnectedDscpTestClient(&api);
EXPECT_EQ(ERR_INVALID_HANDLE, client->SetDiffServCodePoint(DSCP_AF41));
RunUntilIdle();
EXPECT_CALL(api, qwave_supported()).WillRepeatedly(Return(false));
EXPECT_EQ(ERR_NOT_IMPLEMENTED, client->SetDiffServCodePoint(DSCP_AF41));
}
MATCHER_P(DscpPointee, dscp, "") {
return *(DWORD*)arg == (DWORD)dscp;
}
TEST_F(UDPSocketTest, ConnectedSocketDelayedInitAndUpdate) {
MockQwaveApi api;
std::unique_ptr<UDPSocket> client = ConnectedDscpTestClient(&api);
EXPECT_CALL(api, qwave_supported()).WillRepeatedly(Return(true));
EXPECT_CALL(api, CreateHandle(_, _))
.WillOnce(DoAll(SetArgPointee<1>(kFakeHandle1), Return(true)));
EXPECT_CALL(api, AddSocketToFlow(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId1), Return(true)));
EXPECT_CALL(api, SetFlow(_, _, _, _, _, _, _));
// First set on connected sockets will fail since init is async and
// we haven't given the runloop a chance to execute the callback.
EXPECT_EQ(ERR_INVALID_HANDLE, client->SetDiffServCodePoint(DSCP_AF41));
RunUntilIdle();
EXPECT_THAT(client->SetDiffServCodePoint(DSCP_AF41), IsOk());
// New dscp value should reset the flow.
EXPECT_CALL(api, RemoveSocketFromFlow(_, _, kFakeFlowId1, _));
EXPECT_CALL(api, AddSocketToFlow(_, _, _, QOSTrafficTypeBestEffort, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId2), Return(true)));
EXPECT_CALL(api, SetFlow(_, _, QOSSetOutgoingDSCPValue, _,
DscpPointee(DSCP_DEFAULT), _, _));
EXPECT_THAT(client->SetDiffServCodePoint(DSCP_DEFAULT), IsOk());
// Called from DscpManager destructor.
EXPECT_CALL(api, RemoveSocketFromFlow(_, _, kFakeFlowId2, _));
EXPECT_CALL(api, CloseHandle(kFakeHandle1));
}
TEST_F(UDPSocketTest, UnonnectedSocketDelayedInitAndUpdate) {
MockQwaveApi api;
EXPECT_CALL(api, qwave_supported()).WillRepeatedly(Return(true));
EXPECT_CALL(api, CreateHandle(_, _))
.WillOnce(DoAll(SetArgPointee<1>(kFakeHandle1), Return(true)));
// CreateHandle won't have completed yet. Set passes.
std::unique_ptr<UDPSocket> client = UnconnectedDscpTestClient(&api);
EXPECT_THAT(client->SetDiffServCodePoint(DSCP_AF41), IsOk());
RunUntilIdle();
EXPECT_THAT(client->SetDiffServCodePoint(DSCP_AF42), IsOk());
// Called from DscpManager destructor.
EXPECT_CALL(api, CloseHandle(kFakeHandle1));
}
// TODO(zstein): Mocking out DscpManager might be simpler here
// (just verify that DscpManager::Set and DscpManager::PrepareForSend are
// called).
TEST_F(UDPSocketTest, SendToCallsQwaveApis) {
MockQwaveApi api;
std::unique_ptr<UDPSocket> client = UnconnectedDscpTestClient(&api);
EXPECT_CALL(api, qwave_supported()).WillRepeatedly(Return(true));
EXPECT_CALL(api, CreateHandle(_, _))
.WillOnce(DoAll(SetArgPointee<1>(kFakeHandle1), Return(true)));
EXPECT_THAT(client->SetDiffServCodePoint(DSCP_AF41), IsOk());
RunUntilIdle();
EXPECT_CALL(api, AddSocketToFlow(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId1), Return(true)));
EXPECT_CALL(api, SetFlow(_, _, _, _, _, _, _));
std::string simple_message("hello world");
IPEndPoint server_address(IPAddress::IPv4Localhost(), 9438);
int rv = SendToSocket(client.get(), simple_message, server_address);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
// TODO(zstein): Move to second test case (Qwave APIs called once per address)
rv = SendToSocket(client.get(), simple_message, server_address);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
// TODO(zstein): Move to third test case (Qwave APIs called for each
// destination address).
EXPECT_CALL(api, AddSocketToFlow(_, _, _, _, _, _)).WillOnce(Return(true));
IPEndPoint server_address2(IPAddress::IPv4Localhost(), 9439);
rv = SendToSocket(client.get(), simple_message, server_address2);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
// Called from DscpManager destructor.
EXPECT_CALL(api, RemoveSocketFromFlow(_, _, _, _));
EXPECT_CALL(api, CloseHandle(kFakeHandle1));
}
TEST_F(UDPSocketTest, SendToCallsApisAfterDeferredInit) {
MockQwaveApi api;
std::unique_ptr<UDPSocket> client = UnconnectedDscpTestClient(&api);
EXPECT_CALL(api, qwave_supported()).WillRepeatedly(Return(true));
EXPECT_CALL(api, CreateHandle(_, _))
.WillOnce(DoAll(SetArgPointee<1>(kFakeHandle1), Return(true)));
// SetDiffServCodepoint works even if qos api hasn't finished initing.
EXPECT_THAT(client->SetDiffServCodePoint(DSCP_CS7), IsOk());
std::string simple_message("hello world");
IPEndPoint server_address(IPAddress::IPv4Localhost(), 9438);
// SendTo works, but doesn't yet apply TOS
EXPECT_CALL(api, AddSocketToFlow(_, _, _, _, _, _)).Times(0);
int rv = SendToSocket(client.get(), simple_message, server_address);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
RunUntilIdle();
// Now we're initialized, SendTo triggers qos calls with correct codepoint.
EXPECT_CALL(api, AddSocketToFlow(_, _, _, QOSTrafficTypeControl, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId1), Return(true)));
EXPECT_CALL(api, SetFlow(_, _, _, _, _, _, _)).WillOnce(Return(true));
rv = SendToSocket(client.get(), simple_message, server_address);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
// Called from DscpManager destructor.
EXPECT_CALL(api, RemoveSocketFromFlow(_, _, kFakeFlowId1, _));
EXPECT_CALL(api, CloseHandle(kFakeHandle1));
}
class DscpManagerTest : public TestWithTaskEnvironment {
protected:
DscpManagerTest() {
EXPECT_CALL(api_, qwave_supported()).WillRepeatedly(Return(true));
EXPECT_CALL(api_, CreateHandle(_, _))
.WillOnce(DoAll(SetArgPointee<1>(kFakeHandle1), Return(true)));
dscp_manager_ = std::make_unique<DscpManager>(&api_, INVALID_SOCKET);
CreateUDPAddress("1.2.3.4", 9001, &address1_);
CreateUDPAddress("1234:5678:90ab:cdef:1234:5678:90ab:cdef", 9002,
&address2_);
}
MockQwaveApi api_;
std::unique_ptr<DscpManager> dscp_manager_;
IPEndPoint address1_;
IPEndPoint address2_;
};
TEST_F(DscpManagerTest, PrepareForSendIsNoopIfNoSet) {
RunUntilIdle();
dscp_manager_->PrepareForSend(address1_);
}
TEST_F(DscpManagerTest, PrepareForSendCallsQwaveApisAfterSet) {
RunUntilIdle();
dscp_manager_->Set(DSCP_CS2);
// AddSocketToFlow should be called for each address.
// SetFlow should only be called when the flow is first created.
EXPECT_CALL(api_, AddSocketToFlow(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId1), Return(true)));
EXPECT_CALL(api_, SetFlow(_, kFakeFlowId1, _, _, _, _, _));
dscp_manager_->PrepareForSend(address1_);
EXPECT_CALL(api_, AddSocketToFlow(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId1), Return(true)));
EXPECT_CALL(api_, SetFlow(_, _, _, _, _, _, _)).Times(0);
dscp_manager_->PrepareForSend(address2_);
// Called from DscpManager destructor.
EXPECT_CALL(api_, RemoveSocketFromFlow(_, _, kFakeFlowId1, _));
EXPECT_CALL(api_, CloseHandle(kFakeHandle1));
}
TEST_F(DscpManagerTest, PrepareForSendCallsQwaveApisOncePerAddress) {
RunUntilIdle();
dscp_manager_->Set(DSCP_CS2);
EXPECT_CALL(api_, AddSocketToFlow(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId1), Return(true)));
EXPECT_CALL(api_, SetFlow(_, kFakeFlowId1, _, _, _, _, _));
dscp_manager_->PrepareForSend(address1_);
EXPECT_CALL(api_, AddSocketToFlow(_, _, _, _, _, _)).Times(0);
EXPECT_CALL(api_, SetFlow(_, _, _, _, _, _, _)).Times(0);
dscp_manager_->PrepareForSend(address1_);
// Called from DscpManager destructor.
EXPECT_CALL(api_, RemoveSocketFromFlow(_, _, kFakeFlowId1, _));
EXPECT_CALL(api_, CloseHandle(kFakeHandle1));
}
TEST_F(DscpManagerTest, SetDestroysExistingFlow) {
RunUntilIdle();
dscp_manager_->Set(DSCP_CS2);
EXPECT_CALL(api_, AddSocketToFlow(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId1), Return(true)));
EXPECT_CALL(api_, SetFlow(_, kFakeFlowId1, _, _, _, _, _));
dscp_manager_->PrepareForSend(address1_);
// Calling Set should destroy the existing flow.
// TODO(zstein): Verify that RemoveSocketFromFlow with no address
// destroys the flow for all destinations.
EXPECT_CALL(api_, RemoveSocketFromFlow(_, NULL, kFakeFlowId1, _));
dscp_manager_->Set(DSCP_CS5);
EXPECT_CALL(api_, AddSocketToFlow(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId2), Return(true)));
EXPECT_CALL(api_, SetFlow(_, kFakeFlowId2, _, _, _, _, _));
dscp_manager_->PrepareForSend(address1_);
// Called from DscpManager destructor.
EXPECT_CALL(api_, RemoveSocketFromFlow(_, _, kFakeFlowId2, _));
EXPECT_CALL(api_, CloseHandle(kFakeHandle1));
}
TEST_F(DscpManagerTest, SocketReAddedOnRecreateHandle) {
RunUntilIdle();
dscp_manager_->Set(DSCP_CS2);
// First Set and Send work fine.
EXPECT_CALL(api_, AddSocketToFlow(_, _, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId1), Return(true)));
EXPECT_CALL(api_, SetFlow(_, kFakeFlowId1, _, _, _, _, _))
.WillOnce(Return(true));
EXPECT_THAT(dscp_manager_->PrepareForSend(address1_), IsOk());
// Make Second flow operation fail (requires resetting the codepoint).
EXPECT_CALL(api_, RemoveSocketFromFlow(_, _, kFakeFlowId1, _))
.WillOnce(Return(true));
dscp_manager_->Set(DSCP_CS7);
auto error = std::make_unique<base::ScopedClearLastError>();
::SetLastError(ERROR_DEVICE_REINITIALIZATION_NEEDED);
EXPECT_CALL(api_, AddSocketToFlow(_, _, _, _, _, _)).WillOnce(Return(false));
EXPECT_CALL(api_, SetFlow(_, _, _, _, _, _, _)).Times(0);
EXPECT_CALL(api_, CloseHandle(kFakeHandle1));
EXPECT_CALL(api_, CreateHandle(_, _))
.WillOnce(DoAll(SetArgPointee<1>(kFakeHandle2), Return(true)));
EXPECT_EQ(ERR_INVALID_HANDLE, dscp_manager_->PrepareForSend(address1_));
error = nullptr;
RunUntilIdle();
// Next Send should work fine, without requiring another Set
EXPECT_CALL(api_, AddSocketToFlow(_, _, _, QOSTrafficTypeControl, _, _))
.WillOnce(DoAll(SetArgPointee<5>(kFakeFlowId2), Return(true)));
EXPECT_CALL(api_, SetFlow(_, kFakeFlowId2, _, _, _, _, _))
.WillOnce(Return(true));
EXPECT_THAT(dscp_manager_->PrepareForSend(address1_), IsOk());
// Called from DscpManager destructor.
EXPECT_CALL(api_, RemoveSocketFromFlow(_, _, kFakeFlowId2, _));
EXPECT_CALL(api_, CloseHandle(kFakeHandle2));
}
#endif
TEST_F(UDPSocketTest, ReadWithSocketOptimization) {
std::string simple_message("hello world!");
// Setup the server to listen.
IPEndPoint server_address(IPAddress::IPv4Localhost(), 0 /* port */);
UDPServerSocket server(nullptr, NetLogSource());
server.AllowAddressReuse();
ASSERT_THAT(server.Listen(server_address), IsOk());
// Get bound port.
ASSERT_THAT(server.GetLocalAddress(&server_address), IsOk());
// Setup the client, enable experimental optimization and connected to the
// server.
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
client.EnableRecvOptimization();
EXPECT_THAT(client.Connect(server_address), IsOk());
// Get the client's address.
IPEndPoint client_address;
EXPECT_THAT(client.GetLocalAddress(&client_address), IsOk());
// Server sends the message to the client.
EXPECT_EQ(simple_message.length(),
static_cast<size_t>(
SendToSocket(&server, simple_message, client_address)));
// Client receives the message.
std::string str = ReadSocket(&client);
EXPECT_EQ(simple_message, str);
server.Close();
client.Close();
}
// Tests that read from a socket correctly returns
// |ERR_MSG_TOO_BIG| when the buffer is too small and
// returns the actual message when it fits the buffer.
// For the optimized path, the buffer size should be at least
// 1 byte greater than the message.
TEST_F(UDPSocketTest, ReadWithSocketOptimizationTruncation) {
std::string too_long_message(kMaxRead + 1, 'A');
std::string right_length_message(kMaxRead - 1, 'B');
std::string exact_length_message(kMaxRead, 'C');
// Setup the server to listen.
IPEndPoint server_address(IPAddress::IPv4Localhost(), 0 /* port */);
UDPServerSocket server(nullptr, NetLogSource());
server.AllowAddressReuse();
ASSERT_THAT(server.Listen(server_address), IsOk());
// Get bound port.
ASSERT_THAT(server.GetLocalAddress(&server_address), IsOk());
// Setup the client, enable experimental optimization and connected to the
// server.
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
client.EnableRecvOptimization();
EXPECT_THAT(client.Connect(server_address), IsOk());
// Get the client's address.
IPEndPoint client_address;
EXPECT_THAT(client.GetLocalAddress(&client_address), IsOk());
// Send messages to the client.
EXPECT_EQ(too_long_message.length(),
static_cast<size_t>(
SendToSocket(&server, too_long_message, client_address)));
EXPECT_EQ(right_length_message.length(),
static_cast<size_t>(
SendToSocket(&server, right_length_message, client_address)));
EXPECT_EQ(exact_length_message.length(),
static_cast<size_t>(
SendToSocket(&server, exact_length_message, client_address)));
// Client receives the messages.
// 1. The first message is |too_long_message|. Its size exceeds the buffer.
// In that case, the client is expected to get |ERR_MSG_TOO_BIG| when the
// data is read.
TestCompletionCallback callback;
int rv = client.Read(buffer_.get(), kMaxRead, callback.callback());
EXPECT_EQ(ERR_MSG_TOO_BIG, callback.GetResult(rv));
EXPECT_EQ(client.GetLastTos().dscp, DSCP_DEFAULT);
EXPECT_EQ(client.GetLastTos().ecn, ECN_DEFAULT);
// 2. The second message is |right_length_message|. Its size is
// one byte smaller than the size of the buffer. In that case, the client
// is expected to read the whole message successfully.
rv = client.Read(buffer_.get(), kMaxRead, callback.callback());
rv = callback.GetResult(rv);
EXPECT_EQ(static_cast<int>(right_length_message.length()), rv);
EXPECT_EQ(right_length_message, std::string(buffer_->data(), rv));
EXPECT_EQ(client.GetLastTos().dscp, DSCP_DEFAULT);
EXPECT_EQ(client.GetLastTos().ecn, ECN_DEFAULT);
// 3. The third message is |exact_length_message|. Its size is equal to
// the read buffer size. In that case, the client expects to get
// |ERR_MSG_TOO_BIG| when the socket is read. Internally, the optimized
// path uses read() system call that requires one extra byte to detect
// truncated messages; therefore, messages that fill the buffer exactly
// are considered truncated.
// The optimization is only enabled on POSIX platforms. On Windows,
// the optimization is turned off; therefore, the client
// should be able to read the whole message without encountering
// |ERR_MSG_TOO_BIG|.
rv = client.Read(buffer_.get(), kMaxRead, callback.callback());
rv = callback.GetResult(rv);
EXPECT_EQ(client.GetLastTos().dscp, DSCP_DEFAULT);
EXPECT_EQ(client.GetLastTos().ecn, ECN_DEFAULT);
#if BUILDFLAG(IS_POSIX)
EXPECT_EQ(ERR_MSG_TOO_BIG, rv);
#else
EXPECT_EQ(static_cast<int>(exact_length_message.length()), rv);
EXPECT_EQ(exact_length_message, std::string(buffer_->data(), rv));
#endif
server.Close();
client.Close();
}
// On Android, where socket tagging is supported, verify that UDPSocket::Tag
// works as expected.
#if BUILDFLAG(IS_ANDROID)
TEST_F(UDPSocketTest, Tag) {
if (!CanGetTaggedBytes()) {
DVLOG(0) << "Skipping test - GetTaggedBytes unsupported.";
return;
}
UDPServerSocket server(nullptr, NetLogSource());
ASSERT_THAT(server.Listen(IPEndPoint(IPAddress::IPv4Localhost(), 0)), IsOk());
IPEndPoint server_address;
ASSERT_THAT(server.GetLocalAddress(&server_address), IsOk());
UDPClientSocket client(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
ASSERT_THAT(client.Connect(server_address), IsOk());
// Verify UDP packets are tagged and counted properly.
int32_t tag_val1 = 0x12345678;
uint64_t old_traffic = GetTaggedBytes(tag_val1);
SocketTag tag1(SocketTag::UNSET_UID, tag_val1);
client.ApplySocketTag(tag1);
// Client sends to the server.
std::string simple_message("hello world!");
int rv = WriteSocket(&client, simple_message);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
// Server waits for message.
std::string str = RecvFromSocket(&server);
EXPECT_EQ(simple_message, str);
// Server echoes reply.
rv = SendToSocket(&server, simple_message);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
// Client waits for response.
str = ReadSocket(&client);
EXPECT_EQ(simple_message, str);
EXPECT_GT(GetTaggedBytes(tag_val1), old_traffic);
// Verify socket can be retagged with a new value and the current process's
// UID.
int32_t tag_val2 = 0x87654321;
old_traffic = GetTaggedBytes(tag_val2);
SocketTag tag2(getuid(), tag_val2);
client.ApplySocketTag(tag2);
// Client sends to the server.
rv = WriteSocket(&client, simple_message);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
// Server waits for message.
str = RecvFromSocket(&server);
EXPECT_EQ(simple_message, str);
// Server echoes reply.
rv = SendToSocket(&server, simple_message);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
// Client waits for response.
str = ReadSocket(&client);
EXPECT_EQ(simple_message, str);
EXPECT_GT(GetTaggedBytes(tag_val2), old_traffic);
// Verify socket can be retagged with a new value and the current process's
// UID.
old_traffic = GetTaggedBytes(tag_val1);
client.ApplySocketTag(tag1);
// Client sends to the server.
rv = WriteSocket(&client, simple_message);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
// Server waits for message.
str = RecvFromSocket(&server);
EXPECT_EQ(simple_message, str);
// Server echoes reply.
rv = SendToSocket(&server, simple_message);
EXPECT_EQ(simple_message.length(), static_cast<size_t>(rv));
// Client waits for response.
str = ReadSocket(&client);
EXPECT_EQ(simple_message, str);
EXPECT_GT(GetTaggedBytes(tag_val1), old_traffic);
}
TEST_F(UDPSocketTest, BindToNetwork) {
// The specific value of this address doesn't really matter, and no
// server needs to be running here. The test only needs to call
// Connect() and won't send any datagrams.
const IPEndPoint fake_server_address(IPAddress::IPv4Localhost(), 8080);
NetworkChangeNotifierFactoryAndroid ncn_factory;
NetworkChangeNotifier::DisableForTest ncn_disable_for_test;
std::unique_ptr<NetworkChangeNotifier> ncn(ncn_factory.CreateInstance());
if (!NetworkChangeNotifier::AreNetworkHandlesSupported())
GTEST_SKIP() << "Network handles are required to test BindToNetwork.";
// Binding the socket to a not existing network should fail at connect time.
const handles::NetworkHandle wrong_network_handle = 65536;
UDPClientSocket wrong_socket(DatagramSocket::RANDOM_BIND, nullptr,
NetLogSource(), wrong_network_handle);
// Different Android versions might report different errors. Hence, just check
// what shouldn't happen.
int rv = wrong_socket.Connect(fake_server_address);
EXPECT_NE(OK, rv);
EXPECT_NE(ERR_NOT_IMPLEMENTED, rv);
EXPECT_NE(wrong_network_handle, wrong_socket.GetBoundNetwork());
// Binding the socket to an existing network should succeed.
const handles::NetworkHandle network_handle =
NetworkChangeNotifier::GetDefaultNetwork();
if (network_handle != handles::kInvalidNetworkHandle) {
UDPClientSocket correct_socket(DatagramSocket::RANDOM_BIND, nullptr,
NetLogSource(), network_handle);
EXPECT_EQ(OK, correct_socket.Connect(fake_server_address));
EXPECT_EQ(network_handle, correct_socket.GetBoundNetwork());
}
}
#endif // BUILDFLAG(IS_ANDROID)
// Test the behavior of OwnedUDPSocketCount directly. Could be in its own file,
// but seems best to keep it with the more integration-y tests that cover
// UDPSocket usage of the class as well.
TEST_F(UDPSocketTest, OwnedUDPSocketCount) {
std::vector<OwnedUDPSocketCount> owned_counts;
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(), 0);
// The default constructor doesn't increment the count.
owned_counts.resize(OwnedUDPSocketCount::kMaxUdpSockets);
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(), 0);
// Note that this block uses asserts rather than expects to reduce failure log
// size on regression.
for (int i = 0; i < OwnedUDPSocketCount::kMaxUdpSockets; ++i) {
// Creating another owned count should succeed.
auto owned_count = TryAcquireGlobalUDPSocketCount();
ASSERT_FALSE(owned_counts.empty());
ASSERT_EQ(GetGlobalUDPSocketCountForTesting(), i + 1);
// Test that moving an owned count works as expected.
owned_counts[i] = std::move(owned_count);
ASSERT_FALSE(owned_counts[i].empty());
ASSERT_EQ(GetGlobalUDPSocketCountForTesting(), i + 1);
}
// Trying to make another owned count should should fail, since we should
// already be at the max.
auto owned_count_failed = TryAcquireGlobalUDPSocketCount();
EXPECT_TRUE(owned_count_failed.empty());
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets);
// Overwriting an owned count with the empty one should free up a UDP socket.
owned_counts.back() = std::move(owned_count_failed);
EXPECT_TRUE(owned_counts.back().empty());
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets - 1);
// Clearing owned counts should get us back to zero.
owned_counts.clear();
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(), 0);
}
// Tests that UDPClientSocket respects the global UDP socket limits.
TEST_F(UDPSocketTest, LimitClientSocket) {
ASSERT_EQ(0, GetGlobalUDPSocketCountForTesting());
// Use all but 2 sockets of the limit. Don't use UDPClientSockets for this
// because `OwnedUDPSocketCount::kMaxUdpSockets` is rather large.
//
// Note that this block uses asserts rather than expects to reduce failure log
// size on regression.
std::vector<OwnedUDPSocketCount> owned_counts;
for (int i = 0; i < OwnedUDPSocketCount::kMaxUdpSockets - 2; ++i) {
owned_counts.emplace_back(TryAcquireGlobalUDPSocketCount());
ASSERT_FALSE(owned_counts[i].empty());
ASSERT_EQ(GetGlobalUDPSocketCountForTesting(), i + 1);
}
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets - 2);
auto socket1 = std::make_unique<UDPClientSocket>(
DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
auto socket2 = std::make_unique<UDPClientSocket>(
DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
// Simply constructing a UDPClientSocket does not increase the limit (no
// Connect() or Bind() has been called yet).
ASSERT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets - 2);
// The specific value of this address doesn't really matter, and no server
// needs to be running here. The test only needs to call Connect() and won't
// send any datagrams.
IPEndPoint server_address(IPAddress::IPv4Localhost(), 8080);
// Successful Connect() on socket1 increases socket count.
EXPECT_THAT(socket1->Connect(server_address), IsOk());
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets - 1);
// Successful Connect() on socket2 increases socket count.
EXPECT_THAT(socket2->Connect(server_address), IsOk());
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets);
// Attempting a third Connect() should fail with ERR_INSUFFICIENT_RESOURCES.
auto socket3 = std::make_unique<UDPClientSocket>(
DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(socket3->Connect(server_address),
IsError(ERR_INSUFFICIENT_RESOURCES));
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets);
// Check that explicitly closing socket2 free up a count.
socket2->Close();
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets - 1);
// Since the socket was already closed, deleting it will not affect the count.
socket2.reset();
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets - 1);
// Now that the count is below limit, try to connect another socket. This time
// it will work.
auto socket4 = std::make_unique<UDPClientSocket>(
DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(socket4->Connect(server_address), IsOk());
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets);
// Verify that closing the two remaining sockets brings the open count back to
// 0.
socket1.reset();
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets - 1);
socket4.reset();
EXPECT_EQ(GetGlobalUDPSocketCountForTesting(),
OwnedUDPSocketCount::kMaxUdpSockets - 2);
}
// Tests that UDPSocketClient updates the global counter
// correctly when Connect() fails.
TEST_F(UDPSocketTest, LimitConnectFail) {
ASSERT_EQ(0, GetGlobalUDPSocketCountForTesting());
{
// Simply allocating a UDPSocket does not increase count.
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
EXPECT_EQ(0, GetGlobalUDPSocketCountForTesting());
// Calling Open() allocates the socket and increases the global counter.
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV4), IsOk());
EXPECT_EQ(1, GetGlobalUDPSocketCountForTesting());
// Connect to an IPv6 address should fail since the socket was created for
// IPv4.
EXPECT_THAT(socket.Connect(net::IPEndPoint(IPAddress::IPv6Localhost(), 53)),
Not(IsOk()));
// That Connect() failed doesn't change the global counter.
EXPECT_EQ(1, GetGlobalUDPSocketCountForTesting());
}
// Finally, destroying UDPSocket decrements the global counter.
EXPECT_EQ(0, GetGlobalUDPSocketCountForTesting());
}
// Tests allocating UDPClientSockets and Connect()ing them in parallel.
//
// This is primarily intended for coverage under TSAN, to check for races
// enforcing the global socket counter.
TEST_F(UDPSocketTest, LimitConnectMultithreaded) {
ASSERT_EQ(0, GetGlobalUDPSocketCountForTesting());
// Start up some threads.
std::vector<std::unique_ptr<base::Thread>> threads;
for (size_t i = 0; i < 5; ++i) {
threads.push_back(std::make_unique<base::Thread>("Worker thread"));
ASSERT_TRUE(threads.back()->Start());
}
// Post tasks to each of the threads.
for (const auto& thread : threads) {
thread->task_runner()->PostTask(
FROM_HERE, base::BindOnce([] {
// The specific value of this address doesn't really matter, and no
// server needs to be running here. The test only needs to call
// Connect() and won't send any datagrams.
IPEndPoint server_address(IPAddress::IPv4Localhost(), 8080);
UDPClientSocket socket(DatagramSocket::DEFAULT_BIND, nullptr,
NetLogSource(),
handles::kInvalidNetworkHandle);
EXPECT_THAT(socket.Connect(server_address), IsOk());
}));
}
// Complete all the tasks.
threads.clear();
EXPECT_EQ(0, GetGlobalUDPSocketCountForTesting());
}
// Helper to get addresses of a specific family from network interfaces.
// Returns addresses suitable for use as SSM source addresses.
// For IPv6, filters out link-local addresses (fe80::) which don't work
// reliably with SSM on some platforms.
std::vector<IPAddress> GetLocalAddresses(AddressFamily family) {
std::vector<IPAddress> addresses;
NetworkInterfaceList interfaces;
if (!GetNetworkList(&interfaces, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) {
return addresses;
}
const size_t expected_size = (family == ADDRESS_FAMILY_IPV4)
? IPAddress::kIPv4AddressSize
: IPAddress::kIPv6AddressSize;
for (const auto& iface : interfaces) {
if (iface.address.size() == expected_size) {
// Skip link-local addresses for IPv6 as they don't work with SSM.
if (family == ADDRESS_FAMILY_IPV6 && iface.address.IsLinkLocal()) {
continue;
}
addresses.push_back(iface.address);
}
}
return addresses;
}
// Tests for Source-Specific Multicast (SSM)
TEST_F(UDPSocketTest, JoinSourceGroupIPv4) {
IPAddress source_address;
if constexpr (BUILDFLAG(IS_MAC)) {
// macOS requires routable source addresses for SSM.
std::vector<IPAddress> addresses = GetLocalAddresses(ADDRESS_FAMILY_IPV4);
if (addresses.empty()) {
GTEST_SKIP() << "No IPv4 address found for this test on macOS";
}
source_address = addresses[0];
} else {
// Non-macOS platforms: use documentation address (RFC 5737)
EXPECT_TRUE(source_address.AssignFromIPLiteral("192.0.2.1"));
}
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
IPEndPoint local_address(IPAddress::IPv4AllZeros(), 0);
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV4), IsOk());
EXPECT_THAT(socket.Bind(local_address), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("232.1.1.1"));
int rv = socket.JoinSourceGroup(group_address, source_address);
if (kExpectSSMToWork) {
EXPECT_THAT(rv, IsOk());
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source_address), IsOk());
} else {
EXPECT_EQ(ERR_NOT_IMPLEMENTED, rv);
}
}
TEST_F(UDPSocketTest, JoinSourceGroupIPv6) {
IPAddress source_address;
if constexpr (BUILDFLAG(IS_MAC)) {
// macOS requires routable source addresses for SSM.
std::vector<IPAddress> addresses = GetLocalAddresses(ADDRESS_FAMILY_IPV6);
if (addresses.empty()) {
GTEST_SKIP() << "No IPv6 address found for this test on macOS";
}
source_address = addresses[0];
} else {
// Non-macOS platforms: use documentation address (RFC 3849)
EXPECT_TRUE(source_address.AssignFromIPLiteral("2001:db8::1"));
}
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
IPEndPoint local_address(IPAddress::IPv6AllZeros(), 0);
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV6), IsOk());
EXPECT_THAT(socket.Bind(local_address), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("ff3e::1234"));
int rv = socket.JoinSourceGroup(group_address, source_address);
if (kExpectSSMToWork) {
EXPECT_THAT(rv, IsOk());
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source_address), IsOk());
} else {
EXPECT_EQ(ERR_NOT_IMPLEMENTED, rv);
}
}
TEST_F(UDPSocketTest, JoinSourceGroupNotConnected) {
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV4), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("232.1.1.1"));
IPAddress source_address;
EXPECT_TRUE(source_address.AssignFromIPLiteral("192.0.2.1"));
EXPECT_THAT(socket.JoinSourceGroup(group_address, source_address),
IsError(ERR_SOCKET_NOT_CONNECTED));
}
TEST_F(UDPSocketTest, JoinSourceGroupIPv6NotConnected) {
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV6), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("ff3e::1234"));
IPAddress source_address;
EXPECT_TRUE(source_address.AssignFromIPLiteral("2001:db8::1"));
EXPECT_THAT(socket.JoinSourceGroup(group_address, source_address),
IsError(ERR_SOCKET_NOT_CONNECTED));
}
TEST_F(UDPSocketTest, LeaveSourceGroupNotConnected) {
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV4), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("232.1.1.1"));
IPAddress source_address;
EXPECT_TRUE(source_address.AssignFromIPLiteral("192.0.2.1"));
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source_address),
IsError(ERR_SOCKET_NOT_CONNECTED));
}
TEST_F(UDPSocketTest, LeaveSourceGroupIPv6NotConnected) {
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV6), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("ff3e::1234"));
IPAddress source_address;
EXPECT_TRUE(source_address.AssignFromIPLiteral("2001:db8::1"));
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source_address),
IsError(ERR_SOCKET_NOT_CONNECTED));
}
TEST_F(UDPSocketTest, JoinSourceGroupMismatchedIPVersions) {
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
IPEndPoint local_address(IPAddress::IPv4AllZeros(), 0);
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV4), IsOk());
EXPECT_THAT(socket.Bind(local_address), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("232.1.1.1")); // IPv4
IPAddress source_address;
EXPECT_TRUE(source_address.AssignFromIPLiteral("2001:db8::1")); // IPv6
// Should fail because IP versions don't match
EXPECT_THAT(socket.JoinSourceGroup(group_address, source_address),
IsError(ERR_INVALID_ARGUMENT));
}
TEST_F(UDPSocketTest, JoinSourceGroupMultipleSourcesIPv4) {
if (!kExpectSSMToWork) {
GTEST_SKIP() << "SSM not supported on this platform";
}
IPAddress source1;
IPAddress source2;
if constexpr (BUILDFLAG(IS_MAC)) {
// macOS requires routable source addresses for SSM.
std::vector<IPAddress> addresses = GetLocalAddresses(ADDRESS_FAMILY_IPV4);
if (addresses.size() < 2) {
GTEST_SKIP() << "Need at least 2 IPv4 addresses for this test on macOS";
}
source1 = addresses[0];
source2 = addresses[1];
} else {
// Non-macOS platforms: use documentation addresses (RFC 5737)
EXPECT_TRUE(source1.AssignFromIPLiteral("192.0.2.1"));
EXPECT_TRUE(source2.AssignFromIPLiteral("192.0.2.2"));
}
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
IPEndPoint local_address(IPAddress::IPv4AllZeros(), 0);
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV4), IsOk());
EXPECT_THAT(socket.Bind(local_address), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("232.1.1.1"));
// Join same group from two different sources
EXPECT_THAT(socket.JoinSourceGroup(group_address, source1), IsOk());
EXPECT_THAT(socket.JoinSourceGroup(group_address, source2), IsOk());
// Leave both
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source1), IsOk());
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source2), IsOk());
}
TEST_F(UDPSocketTest, LeaveSourceGroupNotJoined) {
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
IPEndPoint local_address(IPAddress::IPv4AllZeros(), 0);
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV4), IsOk());
EXPECT_THAT(socket.Bind(local_address), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("232.1.1.1"));
IPAddress source_address;
EXPECT_TRUE(source_address.AssignFromIPLiteral("192.0.2.1"));
// Try to leave a group we never joined - behavior may vary by platform
// but should not crash
socket.LeaveSourceGroup(group_address, source_address);
}
TEST_F(UDPSocketTest, JoinSourceGroupMultipleSourcesIPv6) {
if (!kExpectSSMToWork) {
GTEST_SKIP() << "SSM not supported on this platform";
}
IPAddress source1;
IPAddress source2;
if constexpr (BUILDFLAG(IS_MAC)) {
// macOS requires routable source addresses for SSM.
std::vector<IPAddress> addresses = GetLocalAddresses(ADDRESS_FAMILY_IPV6);
if (addresses.size() < 2) {
GTEST_SKIP() << "Need at least 2 IPv6 addresses for this test on macOS";
}
source1 = addresses[0];
source2 = addresses[1];
} else {
// Non-macOS platforms: use documentation addresses (RFC 3849)
EXPECT_TRUE(source1.AssignFromIPLiteral("2001:db8::1"));
EXPECT_TRUE(source2.AssignFromIPLiteral("2001:db8::2"));
}
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
IPEndPoint local_address(IPAddress::IPv6AllZeros(), 0);
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV6), IsOk());
EXPECT_THAT(socket.Bind(local_address), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("ff3e::1234"));
// Join same group from two different sources
EXPECT_THAT(socket.JoinSourceGroup(group_address, source1), IsOk());
EXPECT_THAT(socket.JoinSourceGroup(group_address, source2), IsOk());
// Leave both
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source1), IsOk());
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source2), IsOk());
}
// Helper to check if multi-NIC is available for testing.
// Uses net::GetNetworkList() for cross-platform interface enumeration.
// GetNetworkList() already excludes loopback interfaces.
bool HasMultipleNetworkInterfaces() {
NetworkInterfaceList interfaces;
if (!GetNetworkList(&interfaces, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) {
return false;
}
return interfaces.size() >= 2;
}
// Multi-NIC SSM Test for IPv4: Verifies SSM works on systems with multiple interfaces.
TEST_F(UDPSocketTest, SSMSourceFilteringMultiNICIPv4) {
if (!kExpectSSMToWork) {
GTEST_SKIP() << "SSM not supported on this platform";
}
if (!HasMultipleNetworkInterfaces()) {
GTEST_SKIP() << "Multi-NIC not available";
}
std::vector<IPAddress> addresses = GetLocalAddresses(ADDRESS_FAMILY_IPV4);
if (addresses.size() < 2) {
GTEST_SKIP() << "Need at least 2 IPv4 addresses for this test";
}
IPAddress source_address1 = addresses[0];
IPAddress source_address2 = addresses[1];
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
IPEndPoint local_address(IPAddress::IPv4AllZeros(), 0);
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV4), IsOk());
EXPECT_THAT(socket.Bind(local_address), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("232.1.1.1"));
// Join group from two different sources
EXPECT_THAT(socket.JoinSourceGroup(group_address, source_address1), IsOk());
EXPECT_THAT(socket.JoinSourceGroup(group_address, source_address2), IsOk());
// Clean up
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source_address1), IsOk());
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source_address2), IsOk());
}
// Multi-NIC SSM Test for IPv6: Verifies SSM works on systems with multiple interfaces.
TEST_F(UDPSocketTest, SSMSourceFilteringMultiNICIPv6) {
if (!kExpectSSMToWork) {
GTEST_SKIP() << "SSM not supported on this platform";
}
if (!HasMultipleNetworkInterfaces()) {
GTEST_SKIP() << "Multi-NIC not available";
}
std::vector<IPAddress> addresses = GetLocalAddresses(ADDRESS_FAMILY_IPV6);
if (addresses.size() < 2) {
GTEST_SKIP() << "Need at least 2 IPv6 addresses for this test";
}
IPAddress source_address1 = addresses[0];
IPAddress source_address2 = addresses[1];
UDPSocket socket(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
IPEndPoint local_address(IPAddress::IPv6AllZeros(), 0);
EXPECT_THAT(socket.Open(ADDRESS_FAMILY_IPV6), IsOk());
EXPECT_THAT(socket.Bind(local_address), IsOk());
IPAddress group_address;
EXPECT_TRUE(group_address.AssignFromIPLiteral("ff3e::1234"));
// Join group from two different sources
EXPECT_THAT(socket.JoinSourceGroup(group_address, source_address1), IsOk());
EXPECT_THAT(socket.JoinSourceGroup(group_address, source_address2), IsOk());
// Clean up
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source_address1), IsOk());
EXPECT_THAT(socket.LeaveSourceGroup(group_address, source_address2), IsOk());
}
// ReadMultiple is only implemented on POSIX and Fuchsia platforms. On Windows,
// UDPSocketWin::ReadMultiple is not implemented and will hit NOTREACHED().
// On POSIX platforms that do not support recvmmsg (e.g., macOS, iOS, or
// Fuchsia), the implementation falls back to calling recvmsg (via
// InternalRecvFrom).
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
TEST_F(UDPSocketTest, ReadMultiple) {
// Create sender and receiver sockets.
UDPSocket sender(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
UDPSocket receiver(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
// Bind sender to loopback.
IPEndPoint local_address(IPAddress::IPv4Localhost(), 0);
ASSERT_THAT(sender.Open(ADDRESS_FAMILY_IPV4), IsOk());
ASSERT_THAT(sender.Bind(local_address), IsOk());
// Get sender's actual address.
IPEndPoint sender_addr;
ASSERT_THAT(sender.GetLocalAddress(&sender_addr), IsOk());
// Connect receiver to sender. This implicitly binds receiver.
ASSERT_THAT(receiver.Open(ADDRESS_FAMILY_IPV4), IsOk());
ASSERT_THAT(receiver.Connect(sender_addr), IsOk());
// Get receiver's actual address (assigned by system).
IPEndPoint receiver_addr;
ASSERT_THAT(receiver.GetLocalAddress(&receiver_addr), IsOk());
// Enable TOS/ECN propagation on receiver.
ASSERT_THAT(receiver.SetRecvTos(), IsOk());
// Prepare packets to send.
struct PacketToSend {
std::string data;
DiffServCodePoint dscp;
EcnCodePoint ecn;
};
std::vector<PacketToSend> packets = {
{"packet_1", DSCP_CS1, ECN_ECT1},
{"packet_2_longer", DSCP_CS2, ECN_ECT0},
{"pkt3", DSCP_CS3, ECN_CE},
};
// Send packets sequentially.
for (const auto& packet : packets) {
ASSERT_THAT(sender.SetTos(packet.dscp, packet.ecn), IsOk());
auto write_buf = base::MakeRefCounted<StringIOBuffer>(packet.data);
TestCompletionCallback write_callback;
int write_rv = sender.SendTo(write_buf.get(), write_buf->size(),
receiver_addr, write_callback.callback());
ASSERT_EQ(write_callback.GetResult(write_rv), write_buf->size());
}
// Prepare receiver buffer.
constexpr size_t kMaxPacketSize = 1024;
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(
kMinimumReadMultipleBufferSize + packets.size() * kMaxPacketSize);
// Read datagrams.
base::expected<DatagramsMetadata, Error> read_result =
ReadMultipleExpectedCount(&receiver, read_buf.get(),
read_buf->span().size(), kMaxPacketSize,
packets.size());
ASSERT_TRUE(read_result.has_value())
<< "ReadMultiple failed with error: " << read_result.error();
const DatagramsMetadata& datagrams = read_result.value();
size_t expected_total_bytes = 0;
for (const auto& packet : packets) {
expected_total_bytes += packet.data.size();
}
size_t actual_total_bytes = 0;
for (const auto& datagram_metadata : datagrams) {
actual_total_bytes += datagram_metadata.length;
}
EXPECT_EQ(actual_total_bytes, expected_total_bytes);
ASSERT_EQ(datagrams.size(), packets.size());
for (size_t i = 0; i < packets.size(); ++i) {
const auto& expected = packets[i];
const auto& actual = datagrams[i];
EXPECT_EQ(actual.length, expected.data.size());
EXPECT_EQ(actual.offset, i * static_cast<size_t>(kMaxPacketSize));
// Verify TOS/ECN.
uint8_t expected_tos = (expected.dscp << 2) | expected.ecn;
EXPECT_EQ(actual.tos, expected_tos);
// Verify data content.
auto packet_span = read_buf->span().subspan(actual.offset, actual.length);
EXPECT_EQ(base::as_string_view(packet_span), expected.data);
}
}
// This test is only run on platforms that support the recvmmsg-based
// implementation of ReadMultiple (Linux, Android, ChromeOS).
// On fallback POSIX platforms (macOS, iOS, Fuchsia), ReadMultiple delegates
// to the standard RecvFrom method, which uses a large 512-byte control buffer.
// Because this 512-byte buffer is large enough to accommodate the IP_PKTINFO
// control message, the kernel does not set the MSG_CTRUNC flag on those
// platforms. Consequently, the read operation succeeds instead of failing, and
// we cannot test the control message truncation behavior on fallback platforms
// without modifying the general-purpose RecvFrom implementation.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
TEST_F(UDPSocketTest, ReadMultipleControlTruncated) {
// Create sender and receiver sockets.
UDPSocket sender(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
UDPSocket receiver(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
// Bind sender to loopback.
IPEndPoint local_address(IPAddress::IPv4Localhost(), 0);
ASSERT_THAT(sender.Open(ADDRESS_FAMILY_IPV4), IsOk());
ASSERT_THAT(sender.Bind(local_address), IsOk());
// Get sender's actual address.
IPEndPoint sender_addr;
ASSERT_THAT(sender.GetLocalAddress(&sender_addr), IsOk());
// Connect receiver to sender. This implicitly binds receiver.
ASSERT_THAT(receiver.Open(ADDRESS_FAMILY_IPV4), IsOk());
ASSERT_THAT(receiver.Connect(sender_addr), IsOk());
// Get receiver's actual address (assigned by system).
IPEndPoint receiver_addr;
ASSERT_THAT(receiver.GetLocalAddress(&receiver_addr), IsOk());
// Enable TOS/ECN propagation on receiver.
ASSERT_THAT(receiver.SetRecvTos(), IsOk());
// Force MSG_CTRUNC by enabling IP_PKTINFO, which is not accommodated by the
// ReadMultiple control buffer size (CMSG_SPACE(sizeof(int))).
int fd = receiver.SocketDescriptorForTesting();
int opt = 1;
int rv = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
ASSERT_EQ(0, rv);
// Send a packet.
ASSERT_THAT(sender.SetTos(DSCP_CS1, ECN_ECT1), IsOk());
std::string data = "packet";
auto write_buf = base::MakeRefCounted<StringIOBuffer>(data);
TestCompletionCallback write_callback;
int write_rv = sender.SendTo(write_buf.get(), write_buf->size(),
receiver_addr, write_callback.callback());
ASSERT_EQ(write_callback.GetResult(write_rv), write_buf->size());
// Prepare receiver buffer.
constexpr size_t kMaxPacketSize = 1024;
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(
kMinimumReadMultipleBufferSize + kMaxPacketSize);
// Read datagrams. We expect this to fail with ERR_CONTROL_MSG_TOO_BIG because
// of MSG_CTRUNC.
base::expected<DatagramsMetadata, Error> read_result =
ReadMultipleExpectedCount(&receiver, read_buf.get(),
read_buf->span().size(), kMaxPacketSize, 1);
// Verify that the read failed. On Linux/Android/ChromeOS, it fails with
// ERR_CONTROL_MSG_TOO_BIG.
ASSERT_FALSE(read_result.has_value());
EXPECT_EQ(read_result.error(), ERR_CONTROL_MSG_TOO_BIG);
}
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
// BUILDFLAG(IS_ANDROID)
TEST_F(UDPSocketTest, ReadMultiple_TooBig) {
// Create sender and receiver sockets.
UDPSocket sender(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
UDPSocket receiver(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
// Bind sender to loopback.
IPEndPoint local_address(IPAddress::IPv4Localhost(), 0);
ASSERT_THAT(sender.Open(ADDRESS_FAMILY_IPV4), IsOk());
ASSERT_THAT(sender.Bind(local_address), IsOk());
// Get sender's actual address.
IPEndPoint sender_addr;
ASSERT_THAT(sender.GetLocalAddress(&sender_addr), IsOk());
// Connect receiver to sender. This implicitly binds receiver.
ASSERT_THAT(receiver.Open(ADDRESS_FAMILY_IPV4), IsOk());
ASSERT_THAT(receiver.Connect(sender_addr), IsOk());
// Get receiver's actual address (assigned by system).
IPEndPoint receiver_addr;
ASSERT_THAT(receiver.GetLocalAddress(&receiver_addr), IsOk());
// Send a packet that is larger than receiver's max packet size.
std::string large_packet(50, 'a');
auto write_buf = base::MakeRefCounted<StringIOBuffer>(large_packet);
TestCompletionCallback write_callback;
int write_rv = sender.SendTo(write_buf.get(), write_buf->size(),
receiver_addr, write_callback.callback());
ASSERT_EQ(write_callback.GetResult(write_rv), write_buf->size());
// Receiver reads with max packet size smaller than send size.
constexpr size_t kMaxPacketSize = 40;
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(
kMinimumReadMultipleBufferSize + kMaxPacketSize);
base::expected<DatagramsMetadata, Error> read_result =
ReadMultipleExpectedCount(&receiver, read_buf.get(),
read_buf->span().size(), kMaxPacketSize, 1);
ASSERT_FALSE(read_result.has_value());
EXPECT_EQ(read_result.error(), ERR_MSG_TOO_BIG);
}
TEST_F(UDPSocketTest, ReadMultiple_Async) {
// Create sender and receiver sockets.
UDPSocket sender(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
UDPSocket receiver(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
// Bind sender to loopback.
IPEndPoint local_address(IPAddress::IPv4Localhost(), 0);
ASSERT_THAT(sender.Open(ADDRESS_FAMILY_IPV4), IsOk());
ASSERT_THAT(sender.Bind(local_address), IsOk());
// Get sender's actual address.
IPEndPoint sender_addr;
ASSERT_THAT(sender.GetLocalAddress(&sender_addr), IsOk());
// Connect receiver to sender. This implicitly binds receiver.
ASSERT_THAT(receiver.Open(ADDRESS_FAMILY_IPV4), IsOk());
ASSERT_THAT(receiver.Connect(sender_addr), IsOk());
// Get receiver's actual address (assigned by system).
IPEndPoint receiver_addr;
ASSERT_THAT(receiver.GetLocalAddress(&receiver_addr), IsOk());
// Receiver calls ReadMultiple when NO data is available yet.
constexpr size_t kMaxPacketSize = 100;
auto read_buf =
base::MakeRefCounted<IOBufferWithSize>(kMinimumReadMultipleBufferSize);
base::test::TestFuture<base::expected<DatagramsMetadata, Error>> future;
auto rv_read = receiver.ReadMultiple(read_buf.get(), read_buf->span().size(),
kMaxPacketSize, future.GetCallback());
// It MUST be async because we haven't sent anything yet.
ASSERT_FALSE(rv_read.has_value());
ASSERT_EQ(rv_read.error(), ERR_IO_PENDING);
// Now sender sends data.
std::string packet("async_packet");
auto write_buf = base::MakeRefCounted<StringIOBuffer>(packet);
TestCompletionCallback write_callback;
int write_rv = sender.SendTo(write_buf.get(), write_buf->size(),
receiver_addr, write_callback.callback());
ASSERT_EQ(write_callback.GetResult(write_rv), write_buf->size());
// Wait for callback.
base::expected<DatagramsMetadata, Error> read_result = future.Get();
ASSERT_TRUE(read_result.has_value());
ASSERT_GE(read_result.value().size(), 1u);
EXPECT_EQ(read_result.value()[0].length, packet.size());
EXPECT_EQ(read_result.value()[0].offset, 0u);
auto packet_span = read_buf->span().subspan(read_result.value()[0].offset,
read_result.value()[0].length);
EXPECT_EQ(base::as_string_view(packet_span), packet);
}
// Verifies that calling ReadMultiple() on a closed socket returns an explicit
// net error instead of triggering a fatal crash (see
// https://crbug.com/533224376).
TEST_F(UDPSocketTest, ReadMultiple_ClosedSocket) {
UDPSocket receiver(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
ASSERT_THAT(receiver.Open(ADDRESS_FAMILY_IPV4), IsOk());
receiver.Close();
constexpr size_t kMaxPacketSize = 100;
auto read_buf =
base::MakeRefCounted<IOBufferWithSize>(kMinimumReadMultipleBufferSize);
base::test::TestFuture<base::expected<DatagramsMetadata, Error>> future;
auto rv_read = receiver.ReadMultiple(read_buf.get(), read_buf->span().size(),
kMaxPacketSize, future.GetCallback());
ASSERT_FALSE(rv_read.has_value());
EXPECT_EQ(rv_read.error(), ERR_INVALID_HANDLE);
}
TEST_F(UDPSocketTest, ReadFailsWhenGroEnabled) {
UDPSocket receiver(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource());
receiver.set_gro_enabled_for_testing(true);
ASSERT_THAT(receiver.Open(ADDRESS_FAMILY_IPV4), IsOk());
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(100);
TestCompletionCallback callback;
EXPECT_CHECK_DEATH(
receiver.Read(read_buf.get(), read_buf->size(), callback.callback()));
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
class UDPSocketGroTest : public UDPSocketTest {
protected:
UDPSocketGroTest()
: sender_(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource()),
receiver_(DatagramSocket::DEFAULT_BIND, nullptr, NetLogSource()) {}
void SetUp() override {
UDPSocketTest::SetUp();
feature_list_.InitAndEnableFeature(features::kEnableUdpGro);
}
// Configures GRO sockets and verifies that the kernel loopback interface
// actually performs UDP GRO packet coalescing. On some test devices/emulators
// (e.g., android-arm-tests), setting the UDP_GRO and UDP_SEGMENT socket
// options succeeds, but the loopback interface driver does not coalesce
// packets. In those environments, ReadMultiple() falls back to reading
// uncoalesced single datagrams. This method sends a 2-segment payload and
// skips the test if packet coalescing is inactive.
void SetupAndVerifyGroOrSkip() {
IPEndPoint local_address(IPAddress::IPv4Localhost(), 0);
ASSERT_THAT(sender_.Open(ADDRESS_FAMILY_IPV4), IsOk());
ASSERT_THAT(sender_.Bind(local_address), IsOk());
IPEndPoint sender_addr;
ASSERT_THAT(sender_.GetLocalAddress(&sender_addr), IsOk());
ASSERT_THAT(receiver_.Open(ADDRESS_FAMILY_IPV4), IsOk());
receiver_.ConfigureGroSocketOptionForTesting();
if (!receiver_.is_gro_enabled_for_testing()) {
GTEST_SKIP()
<< "UDP GRO is not supported by the kernel on this platform.";
}
ASSERT_THAT(receiver_.Connect(sender_addr), IsOk());
ASSERT_THAT(receiver_.GetLocalAddress(&receiver_addr_), IsOk());
SetGsoSize(100);
if (HasFatalFailure() || IsSkipped()) {
return;
}
SendGroPayload(200, 'z');
// Directly probe OS kernel GRO support using low-level recvmsg() rather
// than calling receiver_.ReadMultiple(), so that a bug in ReadMultiple()
// cannot cause the test suite to be silently skipped.
//
// Note: When GRO coalescing is active, the single recvmsg() call below
// consumes the entire 200-byte coalesced superpacket from the socket
// receive buffer, leaving the queue completely empty for subsequent
// ReadMultiple() test calls. If GRO is inactive, has_gro_cmsg evaluates to
// false and the test is skipped immediately.
char buf[2048];
alignas(struct cmsghdr) char control_buf[512];
struct iovec iov = {.iov_base = buf, .iov_len = sizeof(buf)};
struct msghdr msg = {};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control_buf;
msg.msg_controllen = sizeof(control_buf);
ssize_t bytes_read =
HANDLE_EINTR(recvmsg(receiver_.SocketDescriptorForTesting(), &msg, 0));
ASSERT_GT(bytes_read, 0);
bool has_gro_cmsg = false;
for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) {
has_gro_cmsg = true;
break;
}
}
if (!has_gro_cmsg) {
GTEST_SKIP() << "UDP GRO packet coalescing is not active on this kernel "
"environment.";
}
}
void SetGsoSize(int gso_size) {
if (HasFatalFailure() || IsSkipped()) {
return;
}
if (setsockopt(sender_.SocketDescriptorForTesting(), SOL_UDP, UDP_SEGMENT,
&gso_size, sizeof(gso_size)) < 0) {
GTEST_SKIP()
<< "UDP_SEGMENT is not supported by the kernel on this platform.";
}
}
void SendGroPayload(size_t len, char fill_char = 'a') {
std::string data(len, fill_char);
auto write_buf = base::MakeRefCounted<StringIOBuffer>(data);
TestCompletionCallback write_callback;
int write_rv = sender_.SendTo(write_buf.get(), write_buf->size(),
receiver_addr_, write_callback.callback());
ASSERT_EQ(write_callback.GetResult(write_rv), write_buf->size());
}
UDPSocket sender_;
UDPSocket receiver_;
IPEndPoint receiver_addr_;
base::HistogramTester histogram_tester_;
private:
base::test::ScopedFeatureList feature_list_;
base::MetricsSubSampler::ScopedAlwaysSampleForTesting scoped_always_sample_;
};
TEST_F(UDPSocketGroTest, ReadMultipleGroSyncSuccess) {
SetupAndVerifyGroOrSkip();
if (HasFatalFailure() || IsSkipped()) {
return;
}
SendGroPayload(200, 'a');
constexpr size_t kMaxPacketSize = 1024;
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(
kMinimumReadMultipleBufferSize + 2 * kMaxPacketSize);
base::expected<DatagramsMetadata, Error> read_result =
ReadMultipleExpectedCount(&receiver_, read_buf.get(),
read_buf->span().size(), kMaxPacketSize, 2);
ASSERT_TRUE(read_result.has_value())
<< "ReadMultiple failed: " << read_result.error();
ASSERT_EQ(read_result.value().size(), 2u);
EXPECT_EQ(read_result.value()[0].length, 100u);
EXPECT_EQ(read_result.value()[0].offset, 0u);
EXPECT_EQ(read_result.value()[1].length, 100u);
EXPECT_EQ(read_result.value()[1].offset, 100u);
histogram_tester_.ExpectUniqueSample("Net.UDPSocketPosix.GroPacketsRead", 2,
1);
}
TEST_F(UDPSocketGroTest, ReadMultipleGroSingleUncoalescedPacket) {
SetupAndVerifyGroOrSkip();
if (HasFatalFailure() || IsSkipped()) {
return;
}
SendGroPayload(100, 'a');
constexpr size_t kMaxPacketSize = 1024;
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(
kMinimumReadMultipleBufferSize + kMaxPacketSize);
base::expected<DatagramsMetadata, Error> read_result =
ReadMultipleExpectedCount(&receiver_, read_buf.get(),
read_buf->span().size(), kMaxPacketSize, 1);
ASSERT_TRUE(read_result.has_value())
<< "ReadMultiple failed: " << read_result.error();
ASSERT_EQ(read_result.value().size(), 1u);
EXPECT_EQ(read_result.value()[0].length, 100u);
EXPECT_EQ(read_result.value()[0].offset, 0u);
histogram_tester_.ExpectUniqueSample("Net.UDPSocketPosix.GroPacketsRead", 1,
1);
}
TEST_F(UDPSocketGroTest, ReadMultipleGroSyncFailure) {
SetupAndVerifyGroOrSkip();
SetGsoSize(200);
if (HasFatalFailure() || IsSkipped()) {
return;
}
SendGroPayload(400, 'b');
// maximum_packet_size (100) is smaller than res.gso_size (200), which
// triggers ERR_MSG_TOO_BIG synchronously.
constexpr size_t kMaxPacketSize = 100;
auto read_buf =
base::MakeRefCounted<IOBufferWithSize>(kMinimumReadMultipleBufferSize);
base::test::TestFuture<base::expected<DatagramsMetadata, Error>> future;
auto rv_read = receiver_.ReadMultiple(read_buf.get(), read_buf->span().size(),
kMaxPacketSize, future.GetCallback());
ASSERT_FALSE(rv_read.has_value());
EXPECT_EQ(rv_read.error(), ERR_MSG_TOO_BIG);
histogram_tester_.ExpectTotalCount("Net.UDPSocketPosix.GroPacketsRead", 0);
}
TEST_F(UDPSocketGroTest, ReadMultipleGroAsyncSuccess) {
SetupAndVerifyGroOrSkip();
if (HasFatalFailure() || IsSkipped()) {
return;
}
// Call ReadMultiple before any data is sent to trigger async wait.
constexpr size_t kMaxPacketSize = 1024;
auto read_buf =
base::MakeRefCounted<IOBufferWithSize>(kMinimumReadMultipleBufferSize);
base::test::TestFuture<base::expected<DatagramsMetadata, Error>> future;
auto rv_read = receiver_.ReadMultiple(read_buf.get(), read_buf->span().size(),
kMaxPacketSize, future.GetCallback());
ASSERT_FALSE(rv_read.has_value());
ASSERT_EQ(rv_read.error(), ERR_IO_PENDING);
SendGroPayload(200, 'c');
base::expected<DatagramsMetadata, Error> read_result = future.Get();
ASSERT_TRUE(read_result.has_value())
<< "ReadMultiple failed: " << read_result.error();
ASSERT_EQ(read_result.value().size(), 2u);
EXPECT_EQ(read_result.value()[0].length, 100u);
EXPECT_EQ(read_result.value()[0].offset, 0u);
EXPECT_EQ(read_result.value()[1].length, 100u);
EXPECT_EQ(read_result.value()[1].offset, 100u);
histogram_tester_.ExpectUniqueSample("Net.UDPSocketPosix.GroPacketsRead", 2,
1);
}
TEST_F(UDPSocketGroTest, ReadMultipleGroAsyncFailure) {
SetupAndVerifyGroOrSkip();
SetGsoSize(200);
if (HasFatalFailure() || IsSkipped()) {
return;
}
// maximum_packet_size (100) is smaller than res.gso_size (200), which will
// trigger ERR_MSG_TOO_BIG when the superpacket arrives.
constexpr size_t kMaxPacketSize = 100;
auto read_buf =
base::MakeRefCounted<IOBufferWithSize>(kMinimumReadMultipleBufferSize);
base::test::TestFuture<base::expected<DatagramsMetadata, Error>> future;
auto rv_read = receiver_.ReadMultiple(read_buf.get(), read_buf->span().size(),
kMaxPacketSize, future.GetCallback());
ASSERT_FALSE(rv_read.has_value());
ASSERT_EQ(rv_read.error(), ERR_IO_PENDING);
SendGroPayload(400, 'd');
base::expected<DatagramsMetadata, Error> read_result = future.Get();
ASSERT_FALSE(read_result.has_value());
EXPECT_EQ(read_result.error(), ERR_MSG_TOO_BIG);
histogram_tester_.ExpectTotalCount("Net.UDPSocketPosix.GroPacketsRead", 0);
}
TEST_F(UDPSocketGroTest, ReadMultipleGroZeroSize) {
SetupAndVerifyGroOrSkip();
if (HasFatalFailure() || IsSkipped()) {
return;
}
// Send a 0-size datagram.
SockaddrStorage storage;
ASSERT_TRUE(receiver_addr_.ToSockAddr(storage.addr(), &storage.addr_len));
ssize_t sent = HANDLE_EINTR(sendto(sender_.SocketDescriptorForTesting(), "",
0, 0, storage.addr(), storage.addr_len));
ASSERT_EQ(sent, 0);
constexpr size_t kMaxPacketSize = 1024;
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(
kMinimumReadMultipleBufferSize + kMaxPacketSize);
base::expected<DatagramsMetadata, Error> read_result =
ReadMultipleExpectedCount(&receiver_, read_buf.get(),
read_buf->span().size(), kMaxPacketSize, 1);
ASSERT_TRUE(read_result.has_value())
<< "ReadMultiple failed: " << read_result.error();
ASSERT_EQ(read_result.value().size(), 1u);
EXPECT_EQ(read_result.value()[0].length, 0u);
EXPECT_EQ(read_result.value()[0].offset, 0u);
histogram_tester_.ExpectTotalCount("Net.UDPSocketPosix.GroPacketsRead", 1);
}
TEST_F(UDPSocketGroTest, ReadMultipleGroUnequalSegments) {
SetupAndVerifyGroOrSkip();
if (HasFatalFailure() || IsSkipped()) {
return;
}
// Send 201 bytes. With GSO size 100, this should be split into 100, 100, 1.
SendGroPayload(201, 'b');
constexpr size_t kMaxPacketSize = 1024;
auto read_buf = base::MakeRefCounted<IOBufferWithSize>(
kMinimumReadMultipleBufferSize + 3 * kMaxPacketSize);
base::expected<DatagramsMetadata, Error> read_result =
ReadMultipleExpectedCount(&receiver_, read_buf.get(),
read_buf->span().size(), kMaxPacketSize, 3);
ASSERT_TRUE(read_result.has_value())
<< "ReadMultiple failed: " << read_result.error();
ASSERT_EQ(read_result.value().size(), 3u);
EXPECT_EQ(read_result.value()[0].length, 100u);
EXPECT_EQ(read_result.value()[0].offset, 0u);
EXPECT_EQ(read_result.value()[1].length, 100u);
EXPECT_EQ(read_result.value()[1].offset, 100u);
EXPECT_EQ(read_result.value()[2].length, 1u);
EXPECT_EQ(read_result.value()[2].offset, 200u);
histogram_tester_.ExpectUniqueSample("Net.UDPSocketPosix.GroPacketsRead", 3,
1);
}
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
// BUILDFLAG(IS_ANDROID)
#endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
} // namespace net