chromium/content/browser/direct_sockets/direct_sockets_service_impl.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_POSIX)
#include <sys/socket.h>
#endif  // BUILDFLAG(IS_POSIX)

#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/components/firewall_hole/firewall_hole.h"
#include "services/network/public/mojom/socket_connection_tracker.mojom.h"
#endif  // BUILDFLAG(IS_CHROMEOS)

namespace content {

namespace {

#if BUILDFLAG(IS_CHROMEOS)
bool g_always_open_firewall_hole_for_testing = false;
#endif  // BUILDFLAG(IS_CHROMEOS)

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  // BUILDFLAG(IS_CHROMEOS)

}  // namespace

#if BUILDFLAG(IS_CHROMEOS)
// This class inherits from SocketConnectionTracker so that all stored firewall
// hole handles reference |this| in the internal ReceiverSet.
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, /*local_addr=*/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(),
        "" /*all interfaces*/,
        base::BindOnce(
            &FirewallHoleDelegate::OnFirewallHoleOpened, GetWeakPtr(),
            std::move(connection_tracker),
            /*on_success=*/
            base::BindOnce(std::move(callback_a), net::OK, *local_addr),
            /*on_failure=*/
            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, /*local_addr=*/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(),
        "" /*all interfaces*/,
        base::BindOnce(
            &FirewallHoleDelegate::OnFirewallHoleOpened, GetWeakPtr(),
            std::move(connection_tracker),
            /*on_success=*/
            base::BindOnce(std::move(callback_a), net::OK, *local_addr),
            /*on_failure=*/
            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  // BUILDFLAG(IS_CHROMEOS)

DirectSocketsServiceImpl::DirectSocketsServiceImpl(
    RenderFrameHost* render_frame_host,
    mojo::PendingReceiver<blink::mojom::DirectSocketsService> receiver)
    :{}

DirectSocketsServiceImpl::~DirectSocketsServiceImpl() = default;

// static
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) {}

// static
void DirectSocketsServiceImpl::SetNetworkContextForTesting(
    network::mojom::NetworkContext* network_context) {}

#if BUILDFLAG(IS_CHROMEOS)
// static
void DirectSocketsServiceImpl::SetAlwaysOpenFirewallHoleForTesting() {
  g_always_open_firewall_hole_for_testing = true;
}
#endif  // BUILDFLAG(IS_CHROMEOS)

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>&) {}

}  // namespace content