#include "content/browser/direct_sockets/direct_sockets_service_impl.h"
#include <optional>
#include "base/feature_list.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "build/build_config.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/direct_sockets_delegate.h"
#include "content/public/browser/document_service.h"
#include "content/public/browser/isolated_context_util.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_client.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/unique_receiver_set.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/base/network_anonymization_key.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/simple_host_resolver.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/restricted_udp_socket.mojom.h"
#include "services/network/public/mojom/tcp_socket.mojom.h"
#include "services/network/public/mojom/udp_socket.mojom.h"
#include "third_party/blink/public/common/features_generated.h"
#include "third_party/blink/public/mojom/direct_sockets/direct_sockets.mojom.h"
#include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
#if BUILDFLAG(IS_WIN)
#include <winsock2.h>
#endif
#if BUILDFLAG(IS_POSIX)
#include <sys/socket.h>
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/components/firewall_hole/firewall_hole.h"
#include "services/network/public/mojom/socket_connection_tracker.mojom.h"
#endif
namespace content {
namespace {
#if BUILDFLAG(IS_CHROMEOS)
bool g_always_open_firewall_hole_for_testing = false;
#endif
constexpr net::NetworkTrafficAnnotationTag kDirectSocketsTrafficAnnotation = …;
network::mojom::NetworkContext*& GetNetworkContextForTesting() { … }
bool IsAPIAccessAllowed(RenderFrameHost& rfh) { … }
bool ValidateAddressAndPort(RenderFrameHost& rfh,
const std::string& address,
uint16_t port,
DirectSocketsDelegate::ProtocolType protocol) { … }
bool ValidateAddressAndPort(RenderFrameHost& rfh,
const net::IPEndPoint& ip_endpoint,
DirectSocketsDelegate::ProtocolType protocol) { … }
bool ValidateAddressAndPort(RenderFrameHost& rfh,
const net::HostPortPair& host_port_pair,
DirectSocketsDelegate::ProtocolType protocol) { … }
#if BUILDFLAG(IS_CHROMEOS)
bool ShouldOpenFirewallHole(const net::IPAddress& address) {
if (g_always_open_firewall_hole_for_testing) {
return true;
}
return !address.IsLoopback();
}
#endif
}
#if BUILDFLAG(IS_CHROMEOS)
class DirectSocketsServiceImpl::FirewallHoleDelegate
: public network::mojom::SocketConnectionTracker {
public:
void OpenTCPFirewallHole(
mojo::PendingReceiver<network::mojom::SocketConnectionTracker>
connection_tracker,
OpenTCPServerSocketCallback callback,
int32_t result,
const std::optional<net::IPEndPoint>& local_addr) {
if (result != net::OK) {
std::move(callback).Run(result, std::nullopt);
return;
}
if (!ShouldOpenFirewallHole(local_addr->address())) {
std::move(callback).Run(net::OK, *local_addr);
return;
}
auto [callback_a, callback_b] =
base::SplitOnceCallback(std::move(callback));
chromeos::FirewallHole::Open(
chromeos::FirewallHole::PortType::kTcp, local_addr->port(),
"" ,
base::BindOnce(
&FirewallHoleDelegate::OnFirewallHoleOpened, GetWeakPtr(),
std::move(connection_tracker),
base::BindOnce(std::move(callback_a), net::OK, *local_addr),
base::BindOnce(std::move(callback_b),
net::ERR_NETWORK_ACCESS_DENIED, std::nullopt)));
}
void OpenUDPFirewallHole(
mojo::PendingReceiver<network::mojom::SocketConnectionTracker>
connection_tracker,
OpenBoundUDPSocketCallback callback,
int32_t result,
const std::optional<net::IPEndPoint>& local_addr) {
if (result != net::OK) {
std::move(callback).Run(result, std::nullopt);
return;
}
if (!ShouldOpenFirewallHole(local_addr->address())) {
std::move(callback).Run(net::OK, *local_addr);
return;
}
auto [callback_a, callback_b] =
base::SplitOnceCallback(std::move(callback));
chromeos::FirewallHole::Open(
chromeos::FirewallHole::PortType::kUdp, local_addr->port(),
"" ,
base::BindOnce(
&FirewallHoleDelegate::OnFirewallHoleOpened, GetWeakPtr(),
std::move(connection_tracker),
base::BindOnce(std::move(callback_a), net::OK, *local_addr),
base::BindOnce(std::move(callback_b),
net::ERR_NETWORK_ACCESS_DENIED, std::nullopt)));
}
base::WeakPtr<FirewallHoleDelegate> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
private:
void OnFirewallHoleOpened(
mojo::PendingReceiver<network::mojom::SocketConnectionTracker>
connection_tracker,
base::OnceClosure on_success,
base::OnceClosure on_failure,
std::unique_ptr<chromeos::FirewallHole> firewall_hole) {
if (!firewall_hole) {
std::move(on_failure).Run();
return;
}
receivers_.Add(this, std::move(connection_tracker),
std::move(firewall_hole));
std::move(on_success).Run();
}
mojo::ReceiverSet<network::mojom::SocketConnectionTracker,
std::unique_ptr<chromeos::FirewallHole>>
receivers_;
base::WeakPtrFactory<FirewallHoleDelegate> weak_factory_{this};
};
#endif
DirectSocketsServiceImpl::DirectSocketsServiceImpl(
RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::DirectSocketsService> receiver)
: … { … }
DirectSocketsServiceImpl::~DirectSocketsServiceImpl() = default;
void DirectSocketsServiceImpl::CreateForFrame(
RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::DirectSocketsService> receiver) { … }
void DirectSocketsServiceImpl::OpenTCPSocket(
blink::mojom::DirectTCPSocketOptionsPtr options,
mojo::PendingReceiver<network::mojom::TCPConnectedSocket> receiver,
mojo::PendingRemote<network::mojom::SocketObserver> observer,
OpenTCPSocketCallback callback) { … }
void DirectSocketsServiceImpl::OpenConnectedUDPSocket(
blink::mojom::DirectConnectedUDPSocketOptionsPtr options,
mojo::PendingReceiver<network::mojom::RestrictedUDPSocket> receiver,
mojo::PendingRemote<network::mojom::UDPSocketListener> listener,
OpenConnectedUDPSocketCallback callback) { … }
void DirectSocketsServiceImpl::OpenBoundUDPSocket(
blink::mojom::DirectBoundUDPSocketOptionsPtr options,
mojo::PendingReceiver<network::mojom::RestrictedUDPSocket> receiver,
mojo::PendingRemote<network::mojom::UDPSocketListener> listener,
OpenBoundUDPSocketCallback callback) { … }
void DirectSocketsServiceImpl::OpenTCPServerSocket(
blink::mojom::DirectTCPServerSocketOptionsPtr options,
mojo::PendingReceiver<network::mojom::TCPServerSocket> socket,
OpenTCPServerSocketCallback callback) { … }
void DirectSocketsServiceImpl::SetNetworkContextForTesting(
network::mojom::NetworkContext* network_context) { … }
#if BUILDFLAG(IS_CHROMEOS)
void DirectSocketsServiceImpl::SetAlwaysOpenFirewallHoleForTesting() {
g_always_open_firewall_hole_for_testing = true;
}
#endif
network::mojom::NetworkContext* DirectSocketsServiceImpl::GetNetworkContext()
const { … }
void DirectSocketsServiceImpl::OnResolveCompleteForTCPSocket(
blink::mojom::DirectTCPSocketOptionsPtr options,
mojo::PendingReceiver<network::mojom::TCPConnectedSocket> socket,
mojo::PendingRemote<network::mojom::SocketObserver> observer,
OpenTCPSocketCallback callback,
int result,
const net::ResolveErrorInfo&,
const std::optional<net::AddressList>& resolved_addresses,
const std::optional<net::HostResolverEndpointResults>&) { … }
void DirectSocketsServiceImpl::OnResolveCompleteForUDPSocket(
blink::mojom::DirectConnectedUDPSocketOptionsPtr options,
mojo::PendingReceiver<network::mojom::RestrictedUDPSocket>
restricted_udp_socket_receiver,
mojo::PendingRemote<network::mojom::UDPSocketListener> listener,
OpenConnectedUDPSocketCallback callback,
int result,
const net::ResolveErrorInfo&,
const std::optional<net::AddressList>& resolved_addresses,
const std::optional<net::HostResolverEndpointResults>&) { … }
}