chromium/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h

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

#ifndef CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_UDP_SOCKET_MESSAGE_FILTER_H_
#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_UDP_SOCKET_MESSAGE_FILTER_H_

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <string>
#include <vector>

#include "base/containers/queue.h"
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "content/common/content_export.h"
#include "content/public/common/process_type.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_stdint.h"
#include "ppapi/c/ppb_udp_socket.h"
#include "ppapi/host/resource_message_filter.h"
#include "services/network/public/mojom/udp_socket.mojom.h"

struct PP_NetAddress_Private;

namespace ppapi {

class SocketOptionData;

namespace host {
struct ReplyMessageContext;
}
}  // namespace ppapi

namespace network {
namespace mojom {
class NetworkContext;
}
}  // namespace network

namespace chromeos {
class FirewallHole;
}  // namespace chromeos

namespace content {

class BrowserPpapiHostImpl;

class CONTENT_EXPORT PepperUDPSocketMessageFilter
    : public ppapi::host::ResourceMessageFilter,
      public network::mojom::UDPSocketListener {
 public:
  PepperUDPSocketMessageFilter(BrowserPpapiHostImpl* host,
                               PP_Instance instance,
                               bool private_api);

  PepperUDPSocketMessageFilter(const PepperUDPSocketMessageFilter&) = delete;
  PepperUDPSocketMessageFilter& operator=(const PepperUDPSocketMessageFilter&) =
      delete;

  using CreateUDPSocketCallback = base::RepeatingCallback<void(
      network::mojom::NetworkContext* network_context,
      mojo::PendingReceiver<network::mojom::UDPSocket> socket_receiver,
      mojo::PendingRemote<network::mojom::UDPSocketListener> socket_listener)>;

  static void SetCreateUDPSocketCallbackForTesting(
      const CreateUDPSocketCallback* create_udp_socket_callback);

  static size_t GetNumInstances();

 protected:
  ~PepperUDPSocketMessageFilter() override;

 private:
  enum SocketOption {
    SOCKET_OPTION_ADDRESS_REUSE = 1 << 0,
    SOCKET_OPTION_BROADCAST = 1 << 1,
    SOCKET_OPTION_RCVBUF_SIZE = 1 << 2,
    SOCKET_OPTION_SNDBUF_SIZE = 1 << 3,
    SOCKET_OPTION_MULTICAST_LOOP = 1 << 4,
    SOCKET_OPTION_MULTICAST_TTL = 1 << 5
  };

  struct PendingSend {
    PendingSend(const net::IPAddress& address,
                int port,
                std::vector<uint8_t> data,
                const ppapi::host::ReplyMessageContext& context);
    PendingSend(const PendingSend& other);
    ~PendingSend();

    net::IPAddress address;
    int port;
    std::vector<uint8_t> data;
    ppapi::host::ReplyMessageContext context;
  };

  // ppapi::host::ResourceMessageFilter overrides.
  void OnFilterDestroyed() override;
  scoped_refptr<base::SequencedTaskRunner> OverrideTaskRunnerForMessage(
      const IPC::Message& message) override;
  int32_t OnResourceMessageReceived(
      const IPC::Message& msg,
      ppapi::host::HostMessageContext* context) override;

  int32_t OnMsgSetOption(const ppapi::host::HostMessageContext* context,
                         PP_UDPSocket_Option name,
                         const ppapi::SocketOptionData& value);
  int32_t OnMsgBind(const ppapi::host::HostMessageContext* context,
                    const PP_NetAddress_Private& addr);
  int32_t OnMsgSendTo(const ppapi::host::HostMessageContext* context,
                      const std::string& data,
                      const PP_NetAddress_Private& addr);
  int32_t OnMsgClose(const ppapi::host::HostMessageContext* context);
  int32_t OnMsgRecvSlotAvailable(
      const ppapi::host::HostMessageContext* context);
  int32_t OnMsgJoinGroup(const ppapi::host::HostMessageContext* context,
                         const PP_NetAddress_Private& addr);
  int32_t OnMsgLeaveGroup(const ppapi::host::HostMessageContext* context,
                          const PP_NetAddress_Private& addr);

  void DoBindCallback(mojo::PendingReceiver<network::mojom::UDPSocketListener>
                          listener_receiver,
                      const ppapi::host::ReplyMessageContext& context,
                      int result,
                      const std::optional<net::IPEndPoint>& local_addr_out);
  void OnBindComplete(mojo::PendingReceiver<network::mojom::UDPSocketListener>
                          listener_receiver,
                      const ppapi::host::ReplyMessageContext& context,
                      const PP_NetAddress_Private& net_address);
#if BUILDFLAG(IS_CHROMEOS)
  void OnFirewallHoleOpened(
      mojo::PendingReceiver<network::mojom::UDPSocketListener>
          listener_receiver,
      const ppapi::host::ReplyMessageContext& context,
      const PP_NetAddress_Private& net_address,
      std::unique_ptr<chromeos::FirewallHole> hole);
#endif  // BUILDFLAG(IS_CHROMEOS)
  void StartPendingSend();
  void Close();

  // network::mojom::UDPSocketListener override:
  void OnReceived(int result,
                  const std::optional<net::IPEndPoint>& src_addr,
                  std::optional<base::span<const uint8_t>> data) override;

  void OnSendToCompleted(int net_result);
  void FinishPendingSend(int net_result);

  void SendBindReply(const ppapi::host::ReplyMessageContext& context,
                     int32_t result,
                     const PP_NetAddress_Private& addr);
  void SendRecvFromResult(int32_t result,
                          const std::string& data,
                          const PP_NetAddress_Private& addr);
  void SendRecvFromResultOnIOThread(int32_t result,
                                    const std::string& data,
                                    const PP_NetAddress_Private& addr);
  void SendSendToReply(const ppapi::host::ReplyMessageContext& context,
                       int32_t result,
                       int32_t bytes_written);

  void SendBindError(const ppapi::host::ReplyMessageContext& context,
                     int32_t result);
  void SendRecvFromError(int32_t result);
  void SendSendToError(const ppapi::host::ReplyMessageContext& context,
                       int32_t result);
  void PipeClosed();

  int32_t CanUseMulticastAPI(const PP_NetAddress_Private& addr);

  template <class ReturnMessage>
  base::OnceCallback<void(int result)> CreateCompletionCallback(
      const ppapi::host::HostMessageContext* context);

  template <class ReturnMessage>
  void ReturnResult(const ppapi::host::ReplyMessageContext& context,
                    std::unique_ptr<int> result);

  // Bitwise-or of SocketOption flags. This stores the state about whether
  // each option is set before Bind() is called.
  int socket_options_;

  // Locally cached value of buffer size.
  int32_t rcvbuf_size_;
  int32_t sndbuf_size_;

  // Multicast options, if socket hasn't been bound
  int multicast_ttl_;
  int32_t can_use_multicast_;

  bool closed_;

  base::queue<PendingSend> pending_sends_;

  size_t remaining_recv_slots_;

  bool external_plugin_;
  bool private_api_;

  int render_process_id_;
  int render_frame_id_;

  const bool is_potentially_secure_plugin_context_;

  // Bound (in a Mojo sense) when binding (in a network sense) starts. Closed in
  // Close() and on Mojo pipe errors. Must only be accessed (and destroyed) on
  // UI thread.
  mojo::Remote<network::mojom::UDPSocket> socket_;

  // Bound (in a Mojo sense) when binding (in a network sense) completes.
  // Binding late avoids receiving data when still setting up the socket. Closed
  // in Close() and on Mojo pipe errors. Must only be accessed (and destroyed)
  // on UI thread.
  mojo::Receiver<network::mojom::UDPSocketListener> receiver_{this};

#if BUILDFLAG(IS_CHROMEOS)
  std::unique_ptr<chromeos::FirewallHole> firewall_hole_;
  // Allows for cancellation of opening a hole in the firewall in the case the
  // network service crashes.
  base::WeakPtrFactory<PepperUDPSocketMessageFilter>
      firewall_hole_weak_ptr_factory_{this};
#endif  // BUILDFLAG(IS_CHROMEOS)
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_UDP_SOCKET_MESSAGE_FILTER_H_