Skip to content

Commit

Permalink
Add support for UDPClientSocket::ConnectAsync() to P2PSocketManager.
Browse files Browse the repository at this point in the history
This change also adds a unit test suite to P2PSocketManager.

Bug: 1295460
Change-Id: I5648493cd6a325ec61f9ef6ff5cced3da6b9dfd7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4071785
Reviewed-by: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: Matt Menke <mmenke@chromium.org>
Commit-Queue: Liza Burakova <liza@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1099035}
  • Loading branch information
Liza Burakova authored and Chromium LUCI CQ committed Jan 31, 2023
1 parent f80d846 commit 271a319
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 61 deletions.
1 change: 1 addition & 0 deletions services/network/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ source_set("tests") {

if (is_p2p_enabled) {
sources += [
"p2p/socket_manager_unittest.cc",
"p2p/socket_tcp_unittest.cc",
"p2p/socket_test_utils.cc",
"p2p/socket_test_utils.h",
Expand Down
147 changes: 97 additions & 50 deletions services/network/p2p/socket_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
#include "net/base/sys_addrinfo.h"
#include "net/dns/dns_util.h"
#include "net/dns/host_resolver.h"
#include "net/http/http_network_session.h"
#include "net/log/net_log_source.h"
#include "net/log/net_log_with_source.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/datagram_client_socket.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
Expand Down Expand Up @@ -80,6 +80,9 @@ net::DnsQueryType FamilyToDnsQueryType(int family) {

} // namespace

DefaultLocalAddresses::DefaultLocalAddresses() = default;
DefaultLocalAddresses::~DefaultLocalAddresses() = default;

class P2PSocketManager::DnsRequest {
public:
using DoneCallback = base::OnceCallback<void(const net::IPAddressList&)>;
Expand Down Expand Up @@ -206,11 +209,10 @@ void P2PSocketManager::OnNetworkChanged(
}

// Notify the renderer about changes to list of network interfaces.
network_list_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&P2PSocketManager::DoGetNetworkList,
weak_factory_.GetWeakPtr(),
base::SingleThreadTaskRunner::GetCurrentDefault()));
network_list_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&P2PSocketManager::DoGetNetworkList),
base::BindOnce(&P2PSocketManager::DoGetDefaultLocalAddresses,
weak_factory_.GetWeakPtr()));
}

void P2PSocketManager::PauseNetworkChangeNotifications() {
Expand Down Expand Up @@ -269,26 +271,103 @@ void P2PSocketManager::DumpPacket(base::span<const uint8_t> packet,
incoming);
}

void P2PSocketManager::DoGetNetworkList(
const base::WeakPtr<P2PSocketManager>& socket_manager,
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) {
net::NetworkInterfaceList P2PSocketManager::DoGetNetworkList() {
net::NetworkInterfaceList list;
if (!net::GetNetworkList(&list, net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) {
LOG(ERROR) << "GetNetworkList failed.";
}
return list;
}

void P2PSocketManager::DoGetDefaultLocalAddresses(
net::NetworkInterfaceList list) {
DefaultLocalAddresses* default_local_addresses = new DefaultLocalAddresses();
GetDefaultLocalAddress(
AF_INET,
base::BindOnce(&P2PSocketManager::MaybeFinishDoGetDefaultLocalAddresses,
weak_factory_.GetWeakPtr(), default_local_addresses, list,
AF_INET));
GetDefaultLocalAddress(
AF_INET6,
base::BindOnce(&P2PSocketManager::MaybeFinishDoGetDefaultLocalAddresses,
weak_factory_.GetWeakPtr(), default_local_addresses, list,
AF_INET6));
}

void P2PSocketManager::MaybeFinishDoGetDefaultLocalAddresses(
DefaultLocalAddresses* default_local_addresses,
net::NetworkInterfaceList list,
int family,
net::IPAddress addr) {
if (family == AF_INET) {
default_local_addresses->default_ipv4_local_address = addr;
} else {
default_local_addresses->default_ipv6_local_address = addr;
}

if (!default_local_addresses->default_ipv6_local_address.has_value() ||
!default_local_addresses->default_ipv4_local_address.has_value()) {
return;
}

SendNetworkList(list,
default_local_addresses->default_ipv4_local_address.value(),
default_local_addresses->default_ipv6_local_address.value());
delete default_local_addresses;
}

void P2PSocketManager::GetDefaultLocalAddress(int family,
GetDefaultCallback callback) {
DCHECK(family == AF_INET || family == AF_INET6);

auto socket =
url_request_context_->GetNetworkSessionContext()
->client_socket_factory->CreateDatagramClientSocket(
net::DatagramSocket::DEFAULT_BIND, nullptr, net::NetLogSource());

net::IPAddress ip_address;
if (family == AF_INET) {
ip_address = net::IPAddress(kPublicIPv4Host);
} else {
ip_address = net::IPAddress(kPublicIPv6Host);
}

auto* socket_ptr = socket.get();
auto split_connect_callback = base::SplitOnceCallback(base::BindOnce(
&P2PSocketManager::FinishGetDefaultLocalAddress,
weak_factory_.GetWeakPtr(), std::move(socket), std::move(callback)));
int rv = socket_ptr->ConnectAsync(net::IPEndPoint(ip_address, kPublicPort),
std::move(split_connect_callback.first));
// If ConnectAsync returns synchronously then it will never run the callback
// that was passed in, so run the callback here to make sure
// FinishGetDefaultLocalAddress runs.
if (rv != net::ERR_IO_PENDING) {
std::move(split_connect_callback.second).Run(rv);
}
}

void P2PSocketManager::FinishGetDefaultLocalAddress(
std::unique_ptr<net::DatagramClientSocket> socket,
GetDefaultCallback callback,
int result) {
if (result != net::OK) {
std::move(callback).Run(net::IPAddress());
return;
}

net::IPEndPoint local_address;
if (socket->GetLocalAddress(&local_address) != net::OK) {
std::move(callback).Run(net::IPAddress());
return;
}
net::IPAddress default_ipv4_local_address = GetDefaultLocalAddress(AF_INET);
net::IPAddress default_ipv6_local_address = GetDefaultLocalAddress(AF_INET6);
main_task_runner->PostTask(
FROM_HERE,
base::BindOnce(&P2PSocketManager::SendNetworkList, socket_manager, list,
default_ipv4_local_address, default_ipv6_local_address));

std::move(callback).Run(local_address.address());
}

void P2PSocketManager::SendNetworkList(
const net::NetworkInterfaceList& list,
const net::IPAddress& default_ipv4_local_address,
const net::IPAddress& default_ipv6_local_address) {
net::IPAddress default_ipv4_local_address,
net::IPAddress default_ipv6_local_address) {
network_notification_client_->NetworkListChanged(
list, default_ipv4_local_address, default_ipv6_local_address);
}
Expand All @@ -303,11 +382,7 @@ void P2PSocketManager::StartNetworkNotifications(

net::NetworkChangeNotifier::AddNetworkChangeObserver(this);

network_list_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&P2PSocketManager::DoGetNetworkList,
weak_factory_.GetWeakPtr(),
base::SingleThreadTaskRunner::GetCurrentDefault()));
OnNetworkChanged(net::NetworkChangeNotifier::CONNECTION_NONE);
}

void P2PSocketManager::GetHostAddress(
Expand Down Expand Up @@ -397,34 +472,6 @@ void P2PSocketManager::NetworkNotificationClientConnectionError() {
net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
}

net::IPAddress P2PSocketManager::GetDefaultLocalAddress(int family) {
DCHECK(family == AF_INET || family == AF_INET6);

// Creation and connection of a UDP socket might be janky.
// DCHECK(network_list_task_runner_->RunsTasksInCurrentSequence());

auto socket =
net::ClientSocketFactory::GetDefaultFactory()->CreateDatagramClientSocket(
net::DatagramSocket::DEFAULT_BIND, nullptr, net::NetLogSource());

net::IPAddress ip_address;
if (family == AF_INET) {
ip_address = net::IPAddress(kPublicIPv4Host);
} else {
ip_address = net::IPAddress(kPublicIPv6Host);
}

if (socket->Connect(net::IPEndPoint(ip_address, kPublicPort)) != net::OK) {
return net::IPAddress();
}

net::IPEndPoint local_address;
if (socket->GetLocalAddress(&local_address) != net::OK)
return net::IPAddress();

return local_address.address();
}

void P2PSocketManager::OnAddressResolved(
DnsRequest* request,
mojom::P2PSocketManager::GetHostAddressCallback callback,
Expand Down
40 changes: 29 additions & 11 deletions services/network/p2p/socket_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
#include "net/base/ip_endpoint.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/network_change_notifier.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/datagram_client_socket.h"
#include "services/network/p2p/socket.h"
#include "services/network/p2p/socket_throttler.h"
#include "services/network/public/cpp/p2p_socket_type.h"
Expand All @@ -45,10 +47,17 @@ class ProxyResolvingClientSocketFactory;

namespace network {

struct DefaultLocalAddresses {
DefaultLocalAddresses();
~DefaultLocalAddresses();

absl::optional<net::IPAddress> default_ipv4_local_address;
absl::optional<net::IPAddress> default_ipv6_local_address;
};

// Owns all the P2P socket instances and dispatches Mojo calls from the
// (untrusted) child and (trusted) browser process.
class P2PSocketManager
class COMPONENT_EXPORT(NETWORK_SERVICE) P2PSocketManager
: public net::NetworkChangeNotifier::NetworkChangeObserver,
public mojom::P2PSocketManager,
public mojom::P2PTrustedSocketManager,
Expand Down Expand Up @@ -85,12 +94,26 @@ class P2PSocketManager
private:
class DnsRequest;

static void DoGetNetworkList(
const base::WeakPtr<P2PSocketManager>& socket_manager,
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner);
static net::NetworkInterfaceList DoGetNetworkList();

using GetDefaultCallback = base::OnceCallback<void(net::IPAddress)>;
void DoGetDefaultLocalAddresses(net::NetworkInterfaceList list);
void MaybeFinishDoGetDefaultLocalAddresses(
DefaultLocalAddresses* default_local_addresses,
net::NetworkInterfaceList list,
int family,
net::IPAddress addr);
// This connects a UDP socket to a public IP address and gets local
// address. Since it binds to the "any" address (0.0.0.0 or ::) internally, it
// retrieves the default local address.
void GetDefaultLocalAddress(int family, GetDefaultCallback callback);
void FinishGetDefaultLocalAddress(
std::unique_ptr<net::DatagramClientSocket> socket,
GetDefaultCallback callback,
int result);
void SendNetworkList(const net::NetworkInterfaceList& list,
const net::IPAddress& default_ipv4_local_address,
const net::IPAddress& default_ipv6_local_address);
net::IPAddress default_ipv4_local_address,
net::IPAddress default_ipv6_local_address);

// P2PSocket::Delegate.
void AddAcceptedConnection(
Expand Down Expand Up @@ -125,11 +148,6 @@ class P2PSocketManager

void NetworkNotificationClientConnectionError();

// This connects a UDP socket to a public IP address and gets local
// address. Since it binds to the "any" address (0.0.0.0 or ::) internally, it
// retrieves the default local address.
static net::IPAddress GetDefaultLocalAddress(int family);

void DoGetHostAddress(
const std::string& host_name,
absl::optional<int> address_family,
Expand Down
93 changes: 93 additions & 0 deletions services/network/p2p/socket_manager_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/network/p2p/socket_manager.h"

#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/base/features.h"
#include "net/log/net_log_with_source.h"
#include "net/socket/datagram_client_socket.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/stream_socket.h"
#include "net/test/test_with_task_environment.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/p2p/socket_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::testing::_;
using ::testing::DeleteArg;
using ::testing::DoAll;
using ::testing::Return;

namespace network {

class P2PSocketManagerTest : public testing::Test {
protected:
P2PSocketManagerTest() = default;

void SetUpSocketManager() {
mojo::PendingRemote<mojom::P2PTrustedSocketManagerClient> client;
mojo::PendingReceiver<mojom::P2PTrustedSocketManager>
trusted_socket_manager;
auto client_receiver = client.InitWithNewPipeAndPassReceiver();
auto trusted_socket_manager_remote =
trusted_socket_manager.InitWithNewPipeAndPassRemote();

auto context_builder = net::CreateTestURLRequestContextBuilder();
context_builder->set_client_socket_factory_for_testing(
&mock_socket_factory_);
url_request_context_ = context_builder->Build();

socket_manager_ = std::make_unique<P2PSocketManager>(
net::NetworkAnonymizationKey(), std::move(client),
std::move(trusted_socket_manager),
socket_manager_remote_.BindNewPipeAndPassReceiver(),
base::DoNothingAs<void(P2PSocketManager*)>(),
url_request_context_.get());
}

std::unique_ptr<P2PSocketManager> socket_manager_;
std::unique_ptr<net::URLRequestContext> url_request_context_;
mojo::Remote<mojom::P2PSocketManager> socket_manager_remote_;

base::test::TaskEnvironment task_environment_;
net::MockClientSocketFactory mock_socket_factory_;
};

// Test to make sure DoGetNetworkList eventually runs SendNetworkList.
TEST_F(P2PSocketManagerTest, DoGetNetworkListTest) {
net::StaticSocketDataProvider socket_data1;
socket_data1.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
net::StaticSocketDataProvider socket_data2;
socket_data2.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
mock_socket_factory_.AddSocketDataProvider(&socket_data1);
mock_socket_factory_.AddSocketDataProvider(&socket_data2);
SetUpSocketManager();

base::RunLoop run_loop;
mojo::PendingRemote<mojom::P2PNetworkNotificationClient> notification_client;
std::unique_ptr<FakeNetworkNotificationClient> fake_notification_client =
std::make_unique<FakeNetworkNotificationClient>(
run_loop.QuitClosure(),
notification_client.InitWithNewPipeAndPassReceiver());

socket_manager_remote_->StartNetworkNotifications(
std::move(notification_client));

run_loop.Run();

EXPECT_TRUE(fake_notification_client->get_network_list_changed());
}

} // namespace network
17 changes: 17 additions & 0 deletions services/network/p2p/socket_test_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,23 @@ FakeSocketClient::FakeSocketClient(

FakeSocketClient::~FakeSocketClient() {}

FakeNetworkNotificationClient::FakeNetworkNotificationClient(
base::OnceClosure closure,
mojo::PendingReceiver<mojom::P2PNetworkNotificationClient>
notification_client)
: notification_client_(this, std::move(notification_client)),
closure_(std::move(closure)) {}

FakeNetworkNotificationClient::~FakeNetworkNotificationClient() = default;

void FakeNetworkNotificationClient::NetworkListChanged(
const std::vector<::net::NetworkInterface>& networks,
const ::net::IPAddress& default_ipv4_local_address,
const ::net::IPAddress& default_ipv6_local_address) {
network_list_changed_ = true;
std::move(closure_).Run();
}

void CreateRandomPacket(std::vector<uint8_t>* packet) {
size_t size = kStunHeaderSize + rand() % 1000;
packet->resize(size);
Expand Down

0 comments on commit 271a319

Please sign in to comment.