// 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.
#include "content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h"
#include <cstring>
#include <utility>
#include "base/compiler_specific.h"
#include "base/containers/to_vector.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
#include "content/browser/renderer_host/pepper/pepper_socket_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/process_type.h"
#include "content/public/common/socket_permission_request.h"
#include "ipc/ipc_message_macros.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/log/net_log_source.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/private/ppb_net_address_private.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/error_conversion.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/host/resource_host.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/udp_socket_resource_constants.h"
#include "ppapi/shared_impl/private/net_address_private_impl.h"
#include "ppapi/shared_impl/socket_option_data.h"
#include "services/network/public/mojom/network_context.mojom.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/components/firewall_hole/firewall_hole.h"
#endif // BUILDFLAG(IS_CHROMEOS)
using ppapi::NetAddressPrivateImpl;
using ppapi::host::NetErrorToPepperError;
using ppapi::proxy::UDPSocketResourceConstants;
namespace content {
namespace {
const PepperUDPSocketMessageFilter::CreateUDPSocketCallback*
g_create_udp_socket_callback_for_testing = nullptr;
size_t g_num_udp_filter_instances = 0;
} // namespace
PepperUDPSocketMessageFilter::PendingSend::PendingSend(
const net::IPAddress& address,
int port,
std::vector<uint8_t> data,
const ppapi::host::ReplyMessageContext& context)
: address(address), port(port), data(std::move(data)), context(context) {}
PepperUDPSocketMessageFilter::PendingSend::PendingSend(
const PendingSend& other) = default;
PepperUDPSocketMessageFilter::PendingSend::~PendingSend() {}
PepperUDPSocketMessageFilter::PepperUDPSocketMessageFilter(
BrowserPpapiHostImpl* host,
PP_Instance instance,
bool private_api)
: socket_options_(0),
rcvbuf_size_(0),
sndbuf_size_(0),
multicast_ttl_(0),
can_use_multicast_(PP_ERROR_FAILED),
closed_(false),
remaining_recv_slots_(
UDPSocketResourceConstants::kPluginReceiveBufferSlots),
external_plugin_(host->external_plugin()),
private_api_(private_api),
render_process_id_(0),
render_frame_id_(0),
is_potentially_secure_plugin_context_(
host->IsPotentiallySecurePluginContext(instance)) {
++g_num_udp_filter_instances;
DCHECK(host);
if (!host->GetRenderFrameIDsForInstance(instance, &render_process_id_,
&render_frame_id_)) {
NOTREACHED_IN_MIGRATION();
}
}
PepperUDPSocketMessageFilter::~PepperUDPSocketMessageFilter() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(closed_);
DCHECK(!socket_);
DCHECK(!receiver_.is_bound());
--g_num_udp_filter_instances;
}
void PepperUDPSocketMessageFilter::SetCreateUDPSocketCallbackForTesting(
const CreateUDPSocketCallback* create_udp_socket_callback) {
DCHECK(!create_udp_socket_callback ||
!g_create_udp_socket_callback_for_testing);
g_create_udp_socket_callback_for_testing = create_udp_socket_callback;
}
// static
size_t PepperUDPSocketMessageFilter::GetNumInstances() {
return g_num_udp_filter_instances;
}
void PepperUDPSocketMessageFilter::OnFilterDestroyed() {
ResourceMessageFilter::OnFilterDestroyed();
// Need to close the socket on the UI thread. Calling Close() also ensures
// that future messages will be ignored, so the mojo pipes won't be
// re-created, so after Close() runs, |this| can be safely deleted on the IO
// thread.
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&PepperUDPSocketMessageFilter::Close, this));
}
scoped_refptr<base::SequencedTaskRunner>
PepperUDPSocketMessageFilter::OverrideTaskRunnerForMessage(
const IPC::Message& message) {
switch (message.type()) {
case PpapiHostMsg_UDPSocket_SetOption::ID:
case PpapiHostMsg_UDPSocket_Close::ID:
case PpapiHostMsg_UDPSocket_RecvSlotAvailable::ID:
case PpapiHostMsg_UDPSocket_Bind::ID:
case PpapiHostMsg_UDPSocket_SendTo::ID:
case PpapiHostMsg_UDPSocket_JoinGroup::ID:
case PpapiHostMsg_UDPSocket_LeaveGroup::ID:
return GetUIThreadTaskRunner({});
}
return nullptr;
}
int32_t PepperUDPSocketMessageFilter::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
PPAPI_BEGIN_MESSAGE_MAP(PepperUDPSocketMessageFilter, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_SetOption,
OnMsgSetOption)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_Bind, OnMsgBind)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_SendTo,
OnMsgSendTo)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_UDPSocket_Close,
OnMsgClose)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
PpapiHostMsg_UDPSocket_RecvSlotAvailable, OnMsgRecvSlotAvailable)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_JoinGroup,
OnMsgJoinGroup)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_UDPSocket_LeaveGroup,
OnMsgLeaveGroup)
PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
int32_t PepperUDPSocketMessageFilter::OnMsgSetOption(
const ppapi::host::HostMessageContext* context,
PP_UDPSocket_Option name,
const ppapi::SocketOptionData& value) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (closed_)
return PP_ERROR_FAILED;
switch (name) {
case PP_UDPSOCKET_OPTION_ADDRESS_REUSE: {
if (socket_) {
// AllowReuseAddress is only effective before Bind().
// Note that this limitation originally comes from Windows, but
// PPAPI tries to provide platform independent APIs.
return PP_ERROR_FAILED;
}
bool boolean_value = false;
if (!value.GetBool(&boolean_value))
return PP_ERROR_BADARGUMENT;
if (boolean_value) {
socket_options_ |= SOCKET_OPTION_ADDRESS_REUSE;
} else {
socket_options_ &= ~SOCKET_OPTION_ADDRESS_REUSE;
}
return PP_OK;
}
case PP_UDPSOCKET_OPTION_BROADCAST: {
bool boolean_value = false;
if (!value.GetBool(&boolean_value))
return PP_ERROR_BADARGUMENT;
if (socket_) {
socket_->SetBroadcast(
boolean_value,
CreateCompletionCallback<PpapiPluginMsg_UDPSocket_SetOptionReply>(
context));
return PP_OK_COMPLETIONPENDING;
}
if (boolean_value) {
socket_options_ |= SOCKET_OPTION_BROADCAST;
} else {
socket_options_ &= ~SOCKET_OPTION_BROADCAST;
}
return PP_OK;
}
case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE: {
int32_t integer_value = 0;
if (!value.GetInt32(&integer_value) || integer_value <= 0 ||
integer_value > UDPSocketResourceConstants::kMaxSendBufferSize)
return PP_ERROR_BADARGUMENT;
socket_options_ |= SOCKET_OPTION_SNDBUF_SIZE;
sndbuf_size_ = integer_value;
// If the socket is already initialized, proxy the value to UDPSocket.
if (socket_) {
socket_->SetSendBufferSize(
integer_value,
CreateCompletionCallback<PpapiPluginMsg_UDPSocket_SetOptionReply>(
context));
return PP_OK_COMPLETIONPENDING;
}
return PP_OK;
}
case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE: {
int32_t integer_value = 0;
if (!value.GetInt32(&integer_value) || integer_value <= 0 ||
integer_value > UDPSocketResourceConstants::kMaxReceiveBufferSize)
return PP_ERROR_BADARGUMENT;
socket_options_ |= SOCKET_OPTION_RCVBUF_SIZE;
rcvbuf_size_ = integer_value;
// If the socket is already initialized, proxy the value to UDPSocket.
if (socket_) {
socket_->SetReceiveBufferSize(
integer_value,
CreateCompletionCallback<PpapiPluginMsg_UDPSocket_SetOptionReply>(
context));
return PP_OK_COMPLETIONPENDING;
}
return PP_OK;
}
case PP_UDPSOCKET_OPTION_MULTICAST_LOOP: {
bool boolean_value = false;
if (!value.GetBool(&boolean_value))
return PP_ERROR_BADARGUMENT;
if (boolean_value) {
socket_options_ |= SOCKET_OPTION_MULTICAST_LOOP;
} else {
socket_options_ &= ~SOCKET_OPTION_MULTICAST_LOOP;
}
// If the socket is already initialized, either fail if permissions
// disallow multicast, or lie and claim it succeeded, to maintain previous
// behavior.
if (socket_) {
if (can_use_multicast_ != PP_OK)
return can_use_multicast_;
return PP_OK;
}
return PP_OK;
}
case PP_UDPSOCKET_OPTION_MULTICAST_TTL: {
int32_t integer_value = 0;
if (!value.GetInt32(&integer_value) || integer_value < 0 ||
integer_value > 255)
return PP_ERROR_BADARGUMENT;
// UDPSocket instance is not yet created, so remember the value here.
socket_options_ |= SOCKET_OPTION_MULTICAST_TTL;
multicast_ttl_ = integer_value;
// If the socket is already initialized, either fail if permissions
// disallow multicast, or lie and claim it succeeded, to maintain previous
// behavior.
if (socket_) {
if (can_use_multicast_ != PP_OK)
return can_use_multicast_;
return PP_OK;
}
return PP_OK;
}
default: {
NOTREACHED_IN_MIGRATION();
return PP_ERROR_BADARGUMENT;
}
}
}
int32_t PepperUDPSocketMessageFilter::OnMsgBind(
const ppapi::host::HostMessageContext* context,
const PP_NetAddress_Private& addr) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(context);
if (closed_ || socket_)
return PP_ERROR_FAILED;
// Check for permissions to use multicast APIS. This check must be done while
// on the UI thread, so we cache the value here to be used later on.
PP_NetAddress_Private any_addr;
NetAddressPrivateImpl::GetAnyAddress(PP_FALSE, &any_addr);
can_use_multicast_ = CanUseMulticastAPI(any_addr);
SocketPermissionRequest request =
pepper_socket_utils::CreateSocketPermissionRequest(
SocketPermissionRequest::UDP_BIND, addr);
if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_,
&request, render_process_id_,
render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
net::IPAddressBytes address;
uint16_t port;
if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &address, &port))
return PP_ERROR_ADDRESS_INVALID;
net::IPEndPoint end_point(net::IPAddress(address), port);
network::mojom::UDPSocketOptionsPtr udp_socket_options =
network::mojom::UDPSocketOptions::New();
udp_socket_options->allow_address_reuse =
!!(socket_options_ & SOCKET_OPTION_ADDRESS_REUSE);
udp_socket_options->allow_broadcast =
!!(socket_options_ & SOCKET_OPTION_BROADCAST);
if (socket_options_ & SOCKET_OPTION_SNDBUF_SIZE)
udp_socket_options->send_buffer_size = sndbuf_size_;
if (socket_options_ & SOCKET_OPTION_RCVBUF_SIZE)
udp_socket_options->receive_buffer_size = rcvbuf_size_;
if (socket_options_ & SOCKET_OPTION_MULTICAST_LOOP) {
if (can_use_multicast_ != PP_OK) {
// TODO(mmenke): The above line implies |can_use_multicast_| is a PP
// error code, but the next line implies it is a net error code. Fix that.
return NetErrorToPepperError(can_use_multicast_);
}
// TODO(mmenke): This doesn't seem to be doing anything - this is the
// default behavior.
udp_socket_options->multicast_loopback_mode = true;
}
if (socket_options_ & SOCKET_OPTION_MULTICAST_TTL) {
if (can_use_multicast_ != PP_OK) {
// TODO(mmenke): The above line implies |can_use_multicast_| is a PP
// error code, but the next line implies it is a net error code. Fix that.
return NetErrorToPepperError(can_use_multicast_);
}
udp_socket_options->multicast_time_to_live = multicast_ttl_;
}
RenderFrameHost* render_frame_host =
RenderFrameHost::FromID(render_process_id_, render_frame_id_);
// If the RenderFrameHost has been closed, just fail the request.
if (!render_frame_host)
return PP_ERROR_NOACCESS;
mojo::PendingRemote<network::mojom::UDPSocketListener> udp_socket_listener;
// Avoid binding the listener until the socket has been successfully Bound (in
// a socket sense), to avoid providing read data to the caller until it has
// been told that the socket was bound.
mojo::PendingReceiver<network::mojom::UDPSocketListener> listener_receiver =
udp_socket_listener.InitWithNewPipeAndPassReceiver();
SiteInstance* site_instance = render_frame_host->GetSiteInstance();
network::mojom::NetworkContext* network_context =
site_instance->GetBrowserContext()
->GetStoragePartition(site_instance)
->GetNetworkContext();
if (g_create_udp_socket_callback_for_testing) {
g_create_udp_socket_callback_for_testing->Run(
network_context, socket_.BindNewPipeAndPassReceiver(),
std::move(udp_socket_listener));
} else {
network_context->CreateUDPSocket(socket_.BindNewPipeAndPassReceiver(),
std::move(udp_socket_listener));
}
ppapi::host::ReplyMessageContext reply_context =
context->MakeReplyMessageContext();
// Watch the socket for errors during the the Bind call.
socket_.set_disconnect_handler(
base::BindOnce(&PepperUDPSocketMessageFilter::SendBindError,
base::Unretained(this), reply_context, PP_ERROR_FAILED));
// This is the actual socket Bind call (i.e., not a Mojo Bind call).
socket_->Bind(end_point, std::move(udp_socket_options),
base::BindOnce(&PepperUDPSocketMessageFilter::DoBindCallback,
base::Unretained(this),
std::move(listener_receiver), reply_context));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperUDPSocketMessageFilter::OnMsgSendTo(
const ppapi::host::HostMessageContext* context,
const std::string& data,
const PP_NetAddress_Private& addr) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(context);
// Check |receiver_| instead of |socket_| because |receiver_| is only set
// after the Bind() call completes.
if (closed_ || !receiver_.is_bound())
return PP_ERROR_FAILED;
SocketPermissionRequest request =
pepper_socket_utils::CreateSocketPermissionRequest(
SocketPermissionRequest::UDP_SEND_TO, addr);
if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_,
&request, render_process_id_,
render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
// Make sure a malicious plugin can't queue up an unlimited number of buffers.
size_t num_pending_sends = pending_sends_.size();
if (num_pending_sends == UDPSocketResourceConstants::kPluginSendBufferSlots) {
return PP_ERROR_FAILED;
}
size_t num_bytes = data.size();
if (num_bytes == 0 ||
num_bytes >
static_cast<size_t>(UDPSocketResourceConstants::kMaxWriteSize)) {
// Size of |data| is checked on the plugin side.
NOTREACHED_IN_MIGRATION();
return PP_ERROR_BADARGUMENT;
}
net::IPAddressBytes address;
uint16_t port;
if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &address, &port)) {
return PP_ERROR_ADDRESS_INVALID;
}
std::vector<uint8_t> data_vector = base::ToVector(base::as_byte_span(data));
pending_sends_.push(PendingSend(net::IPAddress(address), port,
std::move(data_vector),
context->MakeReplyMessageContext()));
// Can only start the send if there isn't another send pending.
if (num_pending_sends == 0)
StartPendingSend();
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperUDPSocketMessageFilter::OnMsgClose(
const ppapi::host::HostMessageContext* context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Close();
return PP_OK;
}
int32_t PepperUDPSocketMessageFilter::OnMsgRecvSlotAvailable(
const ppapi::host::HostMessageContext* context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (remaining_recv_slots_ <
UDPSocketResourceConstants::kPluginReceiveBufferSlots) {
// If the pipe was closed, but the consumer has not yet closed the UDP
// socket, keep the read buffer filled with errors.
if (!receiver_.is_bound()) {
PepperUDPSocketMessageFilter::SendRecvFromError(PP_ERROR_FAILED);
return PP_OK;
}
remaining_recv_slots_++;
socket_->ReceiveMore(1);
}
return PP_OK;
}
int32_t PepperUDPSocketMessageFilter::OnMsgJoinGroup(
const ppapi::host::HostMessageContext* context,
const PP_NetAddress_Private& addr) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
int32_t ret = CanUseMulticastAPI(addr);
if (ret != PP_OK)
return ret;
if (!socket_)
return PP_ERROR_FAILED;
net::IPAddressBytes group;
uint16_t port;
if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &group, &port))
return PP_ERROR_ADDRESS_INVALID;
socket_->JoinGroup(
net::IPAddress(group),
CreateCompletionCallback<PpapiPluginMsg_UDPSocket_SetOptionReply>(
context));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperUDPSocketMessageFilter::OnMsgLeaveGroup(
const ppapi::host::HostMessageContext* context,
const PP_NetAddress_Private& addr) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
int32_t ret = CanUseMulticastAPI(addr);
if (ret != PP_OK)
return ret;
if (!socket_)
return PP_ERROR_FAILED;
net::IPAddressBytes group;
uint16_t port;
if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &group, &port))
return PP_ERROR_ADDRESS_INVALID;
socket_->LeaveGroup(
net::IPAddress(group),
CreateCompletionCallback<PpapiPluginMsg_UDPSocket_LeaveGroupReply>(
context));
return PP_OK_COMPLETIONPENDING;
}
void PepperUDPSocketMessageFilter::DoBindCallback(
mojo::PendingReceiver<network::mojom::UDPSocketListener> listener_receiver,
const ppapi::host::ReplyMessageContext& context,
int result,
const std::optional<net::IPEndPoint>& local_addr_out) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (result != net::OK) {
SendBindError(context, NetErrorToPepperError(result));
return;
}
PP_NetAddress_Private net_address = NetAddressPrivateImpl::kInvalidNetAddress;
if (!local_addr_out || !NetAddressPrivateImpl::IPEndPointToNetAddress(
local_addr_out->address().bytes(),
local_addr_out->port(), &net_address)) {
SendBindError(context, PP_ERROR_ADDRESS_INVALID);
return;
}
#if BUILDFLAG(IS_CHROMEOS)
pepper_socket_utils::OpenUDPFirewallHole(
*local_addr_out,
base::BindOnce(&PepperUDPSocketMessageFilter::OnFirewallHoleOpened,
firewall_hole_weak_ptr_factory_.GetWeakPtr(),
std::move(listener_receiver), context, net_address));
#else // !BUILDFLAG(IS_CHROMEOS)
OnBindComplete(std::move(listener_receiver), context, net_address);
#endif // !BUILDFLAG(IS_CHROMEOS)
}
void PepperUDPSocketMessageFilter::OnBindComplete(
mojo::PendingReceiver<network::mojom::UDPSocketListener> listener_receiver,
const ppapi::host::ReplyMessageContext& context,
const PP_NetAddress_Private& net_address) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(socket_);
SendBindReply(context, PP_OK, net_address);
receiver_.Bind(std::move(listener_receiver));
receiver_.set_disconnect_handler(base::BindOnce(
&PepperUDPSocketMessageFilter::PipeClosed, base::Unretained(this)));
socket_.set_disconnect_handler(base::BindOnce(
&PepperUDPSocketMessageFilter::PipeClosed, base::Unretained(this)));
socket_->ReceiveMore(UDPSocketResourceConstants::kPluginReceiveBufferSlots);
}
#if BUILDFLAG(IS_CHROMEOS)
void PepperUDPSocketMessageFilter::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) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
LOG_IF(WARNING, !hole.get()) << "Firewall hole could not be opened.";
firewall_hole_.reset(hole.release());
OnBindComplete(std::move(listener_receiver), context, net_address);
}
#endif // BUILDFLAG(IS_CHROMEOS)
void PepperUDPSocketMessageFilter::StartPendingSend() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!pending_sends_.empty());
DCHECK(socket_);
const PendingSend& pending_send = pending_sends_.front();
// See OnMsgRecvFrom() for the reason why we use base::Unretained(this)
// when calling |socket_| methods.
socket_->SendTo(
net::IPEndPoint(pending_send.address, pending_send.port),
pending_send.data, pepper_socket_utils::PepperUDPNetworkAnnotationTag(),
base::BindOnce(&PepperUDPSocketMessageFilter::OnSendToCompleted,
base::Unretained(this)));
}
void PepperUDPSocketMessageFilter::Close() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
socket_.reset();
receiver_.reset();
closed_ = true;
}
void PepperUDPSocketMessageFilter::OnReceived(
int result,
const std::optional<net::IPEndPoint>& src_addr,
std::optional<base::span<const uint8_t>> data) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!closed_);
int32_t pp_result = NetErrorToPepperError(result);
// Convert IPEndPoint we get back from RecvFrom to a PP_NetAddress_Private
// to send back.
PP_NetAddress_Private addr = NetAddressPrivateImpl::kInvalidNetAddress;
if (pp_result == PP_OK &&
(!src_addr ||
!NetAddressPrivateImpl::IPEndPointToNetAddress(
src_addr->address().bytes(), src_addr->port(), &addr))) {
pp_result = PP_ERROR_ADDRESS_INVALID;
}
if (pp_result == PP_OK) {
std::string data_string;
if (data) {
data_string = std::string(reinterpret_cast<const char*>(data->data()),
data->size());
}
SendRecvFromResult(PP_OK, data_string, addr);
} else {
SendRecvFromError(pp_result);
}
// This should always be the case, but best to protect against a broken /
// taken over network service.
if (remaining_recv_slots_ > 0)
remaining_recv_slots_--;
}
void PepperUDPSocketMessageFilter::OnSendToCompleted(int net_result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
FinishPendingSend(net_result);
if (!pending_sends_.empty())
StartPendingSend();
}
void PepperUDPSocketMessageFilter::FinishPendingSend(int net_result) {
DCHECK(!pending_sends_.empty());
const PendingSend& pending_send = pending_sends_.front();
int32_t pp_result = NetErrorToPepperError(net_result);
if (pp_result < 0) {
SendSendToError(pending_send.context, pp_result);
} else {
// The cast should be safe because of the
// UDPSocketResourceConstants::kMaxSendBufferSize before enqueuing the send.
SendSendToReply(pending_send.context, PP_OK,
static_cast<int>(pending_send.data.size()));
}
pending_sends_.pop();
}
void PepperUDPSocketMessageFilter::SendBindReply(
const ppapi::host::ReplyMessageContext& context,
int32_t result,
const PP_NetAddress_Private& addr) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(result);
SendReply(reply_context, PpapiPluginMsg_UDPSocket_BindReply(addr));
}
void PepperUDPSocketMessageFilter::SendRecvFromResult(
int32_t result,
const std::string& data,
const PP_NetAddress_Private& addr) {
// Unlike SendReply, which is safe to call on any thread, SendUnsolicitedReply
// calls are only safe to make on the IO thread.
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
&PepperUDPSocketMessageFilter::SendRecvFromResultOnIOThread, this,
result, data, addr));
}
void PepperUDPSocketMessageFilter::SendRecvFromResultOnIOThread(
int32_t result,
const std::string& data,
const PP_NetAddress_Private& addr) {
if (resource_host()) {
resource_host()->host()->SendUnsolicitedReply(
resource_host()->pp_resource(),
PpapiPluginMsg_UDPSocket_PushRecvResult(result, data, addr));
}
}
void PepperUDPSocketMessageFilter::SendSendToReply(
const ppapi::host::ReplyMessageContext& context,
int32_t result,
int32_t bytes_written) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(result);
SendReply(reply_context, PpapiPluginMsg_UDPSocket_SendToReply(bytes_written));
}
void PepperUDPSocketMessageFilter::SendBindError(
const ppapi::host::ReplyMessageContext& context,
int32_t result) {
socket_.reset();
#if BUILDFLAG(IS_CHROMEOS)
// In the unlikely case that this is due to a Mojo error while trying to open
// a hole in the firewall on ChromeOS, abandon opening a hole in the firewall.
firewall_hole_weak_ptr_factory_.InvalidateWeakPtrs();
#endif // BUILDFLAG(IS_CHROMEOS)
SendBindReply(context, result, NetAddressPrivateImpl::kInvalidNetAddress);
}
void PepperUDPSocketMessageFilter::SendRecvFromError(int32_t result) {
SendRecvFromResult(result, std::string(),
NetAddressPrivateImpl::kInvalidNetAddress);
}
void PepperUDPSocketMessageFilter::SendSendToError(
const ppapi::host::ReplyMessageContext& context,
int32_t result) {
SendSendToReply(context, result, 0);
}
void PepperUDPSocketMessageFilter::PipeClosed() {
Close();
while (!pending_sends_.empty())
FinishPendingSend(PP_ERROR_FAILED);
// Any reads should fail, after a pipe error.
while (remaining_recv_slots_ > 0) {
--remaining_recv_slots_;
SendRecvFromError(PP_ERROR_FAILED);
}
}
int32_t PepperUDPSocketMessageFilter::CanUseMulticastAPI(
const PP_NetAddress_Private& addr) {
// Check for plugin permissions.
SocketPermissionRequest request =
pepper_socket_utils::CreateSocketPermissionRequest(
SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, addr);
if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_,
&request, render_process_id_,
render_frame_id_)) {
return PP_ERROR_NOACCESS;
}
return PP_OK;
}
template <class ReturnMessage>
base::OnceCallback<void(int result)>
PepperUDPSocketMessageFilter::CreateCompletionCallback(
const ppapi::host::HostMessageContext* context) {
std::unique_ptr<int> result = std::make_unique<int>(net::ERR_FAILED);
int* result_ptr = result.get();
base::ScopedClosureRunner closure_runner(
base::BindOnce(&PepperUDPSocketMessageFilter::ReturnResult<ReturnMessage>,
base::Unretained(this), context->MakeReplyMessageContext(),
std::move(result)));
return base::BindOnce(
[](base::ScopedClosureRunner closure_runner, int* result_ptr,
int net_result) { *result_ptr = net_result; },
std::move(closure_runner), result_ptr);
}
template <class ReturnMessage>
void PepperUDPSocketMessageFilter::ReturnResult(
const ppapi::host::ReplyMessageContext& context,
std::unique_ptr<int> result) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(NetErrorToPepperError(*result));
SendReply(reply_context, ReturnMessage());
}
} // namespace content