blob: 26c0e44ee34f6277a36309c6dd4b5c2c73a0e4c1 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// 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 "base/compiler_specific.h"
#include "base/logging.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_thread.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/udp/udp_server_socket.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_base.h"
#include "ppapi/shared_impl/private/net_address_private_impl.h"
#include "ppapi/shared_impl/socket_option_data.h"
using ppapi::NetAddressPrivateImpl;
using ppapi::host::NetErrorToPepperError;
namespace {
size_t g_num_instances = 0;
} // namespace
namespace content {
PepperUDPSocketMessageFilter::PepperUDPSocketMessageFilter(
BrowserPpapiHostImpl* host,
PP_Instance instance,
bool private_api)
: allow_address_reuse_(false),
allow_broadcast_(false),
closed_(false),
remaining_recv_slots_(
ppapi::proxy::UDPSocketResourceBase::kPluginReceiveBufferSlots),
external_plugin_(host->external_plugin()),
private_api_(private_api),
render_process_id_(0),
render_frame_id_(0) {
++g_num_instances;
DCHECK(host);
if (!host->GetRenderFrameIDsForInstance(
instance, &render_process_id_, &render_frame_id_)) {
NOTREACHED();
}
}
PepperUDPSocketMessageFilter::~PepperUDPSocketMessageFilter() {
Close();
--g_num_instances;
}
// static
size_t PepperUDPSocketMessageFilter::GetNumInstances() {
return g_num_instances;
}
scoped_refptr<base::TaskRunner>
PepperUDPSocketMessageFilter::OverrideTaskRunnerForMessage(
const IPC::Message& message) {
switch (message.type()) {
case PpapiHostMsg_UDPSocket_SetOption::ID:
case PpapiHostMsg_UDPSocket_Close::ID:
case PpapiHostMsg_UDPSocket_RecvSlotAvailable::ID:
return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
case PpapiHostMsg_UDPSocket_Bind::ID:
case PpapiHostMsg_UDPSocket_SendTo::ID:
return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
}
return NULL;
}
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_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::IO);
if (closed_)
return PP_ERROR_FAILED;
switch (name) {
case PP_UDPSOCKET_OPTION_ADDRESS_REUSE:
case PP_UDPSOCKET_OPTION_BROADCAST: {
if (socket_.get()) {
// They only take effect before the socket is bound.
return PP_ERROR_FAILED;
}
bool boolean_value = false;
if (!value.GetBool(&boolean_value))
return PP_ERROR_BADARGUMENT;
if (name == PP_UDPSOCKET_OPTION_ADDRESS_REUSE)
allow_address_reuse_ = boolean_value;
else
allow_broadcast_ = boolean_value;
return PP_OK;
}
case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE:
case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE: {
if (!socket_.get()) {
// They only take effect after the socket is bound.
return PP_ERROR_FAILED;
}
int32_t integer_value = 0;
if (!value.GetInt32(&integer_value) || integer_value <= 0)
return PP_ERROR_BADARGUMENT;
int net_result = net::ERR_UNEXPECTED;
if (name == PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE) {
if (integer_value >
ppapi::proxy::UDPSocketResourceBase::kMaxSendBufferSize) {
return PP_ERROR_BADARGUMENT;
}
net_result = socket_->SetSendBufferSize(integer_value);
} else {
if (integer_value >
ppapi::proxy::UDPSocketResourceBase::kMaxReceiveBufferSize) {
return PP_ERROR_BADARGUMENT;
}
net_result = socket_->SetReceiveBufferSize(integer_value);
}
// TODO(wtc): Add error mapping code.
return (net_result == net::OK) ? PP_OK : PP_ERROR_FAILED;
}
default: {
NOTREACHED();
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);
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;
}
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&PepperUDPSocketMessageFilter::DoBind,
this,
context->MakeReplyMessageContext(),
addr));
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);
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;
}
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&PepperUDPSocketMessageFilter::DoSendTo,
this,
context->MakeReplyMessageContext(),
data,
addr));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperUDPSocketMessageFilter::OnMsgClose(
const ppapi::host::HostMessageContext* context) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
Close();
return PP_OK;
}
int32_t PepperUDPSocketMessageFilter::OnMsgRecvSlotAvailable(
const ppapi::host::HostMessageContext* context) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (remaining_recv_slots_ <
ppapi::proxy::UDPSocketResourceBase::kPluginReceiveBufferSlots) {
remaining_recv_slots_++;
}
if (!recvfrom_buffer_.get() && !closed_ && socket_.get()) {
DCHECK_EQ(1u, remaining_recv_slots_);
DoRecvFrom();
}
return PP_OK;
}
void PepperUDPSocketMessageFilter::DoBind(
const ppapi::host::ReplyMessageContext& context,
const PP_NetAddress_Private& addr) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (closed_ || socket_.get()) {
SendBindError(context, PP_ERROR_FAILED);
return;
}
scoped_ptr<net::UDPServerSocket> socket(
new net::UDPServerSocket(NULL, net::NetLog::Source()));
net::IPAddressNumber address;
int port;
if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &address, &port)) {
SendBindError(context, PP_ERROR_ADDRESS_INVALID);
return;
}
if (allow_address_reuse_)
socket->AllowAddressReuse();
if (allow_broadcast_)
socket->AllowBroadcast();
int32_t pp_result =
NetErrorToPepperError(socket->Listen(net::IPEndPoint(address, port)));
if (pp_result != PP_OK) {
SendBindError(context, pp_result);
return;
}
net::IPEndPoint bound_address;
pp_result = NetErrorToPepperError(socket->GetLocalAddress(&bound_address));
if (pp_result != PP_OK) {
SendBindError(context, pp_result);
return;
}
PP_NetAddress_Private net_address = NetAddressPrivateImpl::kInvalidNetAddress;
if (!NetAddressPrivateImpl::IPEndPointToNetAddress(
bound_address.address(), bound_address.port(), &net_address)) {
SendBindError(context, PP_ERROR_ADDRESS_INVALID);
return;
}
allow_address_reuse_ = false;
allow_broadcast_ = false;
socket_.swap(socket);
SendBindReply(context, PP_OK, net_address);
DoRecvFrom();
}
void PepperUDPSocketMessageFilter::DoRecvFrom() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(!closed_);
DCHECK(socket_.get());
DCHECK(!recvfrom_buffer_.get());
DCHECK_GT(remaining_recv_slots_, 0u);
recvfrom_buffer_ = new net::IOBuffer(
ppapi::proxy::UDPSocketResourceBase::kMaxReadSize);
// Use base::Unretained(this), so that the lifespan of this object doesn't
// have to last until the callback is called.
// It is safe to do so because |socket_| is owned by this object. If this
// object gets destroyed (and so does |socket_|), the callback won't be
// called.
int net_result = socket_->RecvFrom(
recvfrom_buffer_.get(),
ppapi::proxy::UDPSocketResourceBase::kMaxReadSize,
&recvfrom_address_,
base::Bind(&PepperUDPSocketMessageFilter::OnRecvFromCompleted,
base::Unretained(this)));
if (net_result != net::ERR_IO_PENDING)
OnRecvFromCompleted(net_result);
}
void PepperUDPSocketMessageFilter::DoSendTo(
const ppapi::host::ReplyMessageContext& context,
const std::string& data,
const PP_NetAddress_Private& addr) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(socket_.get());
if (closed_ || !socket_.get()) {
SendSendToError(context, PP_ERROR_FAILED);
return;
}
if (sendto_buffer_.get()) {
SendSendToError(context, PP_ERROR_INPROGRESS);
return;
}
size_t num_bytes = data.size();
if (num_bytes == 0 ||
num_bytes > static_cast<size_t>(
ppapi::proxy::UDPSocketResourceBase::kMaxWriteSize)) {
// Size of |data| is checked on the plugin side.
NOTREACHED();
SendSendToError(context, PP_ERROR_BADARGUMENT);
return;
}
sendto_buffer_ = new net::IOBufferWithSize(num_bytes);
memcpy(sendto_buffer_->data(), data.data(), num_bytes);
net::IPAddressNumber address;
int port;
if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &address, &port)) {
SendSendToError(context, PP_ERROR_ADDRESS_INVALID);
return;
}
// Please see OnMsgRecvFrom() for the reason why we use base::Unretained(this)
// when calling |socket_| methods.
int net_result = socket_->SendTo(
sendto_buffer_.get(),
sendto_buffer_->size(),
net::IPEndPoint(address, port),
base::Bind(&PepperUDPSocketMessageFilter::OnSendToCompleted,
base::Unretained(this),
context));
if (net_result != net::ERR_IO_PENDING)
OnSendToCompleted(context, net_result);
}
void PepperUDPSocketMessageFilter::Close() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (socket_.get() && !closed_)
socket_->Close();
closed_ = true;
}
void PepperUDPSocketMessageFilter::OnRecvFromCompleted(int net_result) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(recvfrom_buffer_.get());
int32_t pp_result = NetErrorToPepperError(net_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 >= 0 &&
!NetAddressPrivateImpl::IPEndPointToNetAddress(
recvfrom_address_.address(), recvfrom_address_.port(), &addr)) {
pp_result = PP_ERROR_ADDRESS_INVALID;
}
if (pp_result >= 0) {
SendRecvFromResult(PP_OK, std::string(recvfrom_buffer_->data(), pp_result),
addr);
} else {
SendRecvFromError(pp_result);
}
recvfrom_buffer_ = NULL;
DCHECK_GT(remaining_recv_slots_, 0u);
remaining_recv_slots_--;
if (remaining_recv_slots_ > 0 && !closed_ && socket_.get())
DoRecvFrom();
}
void PepperUDPSocketMessageFilter::OnSendToCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(sendto_buffer_.get());
int32_t pp_result = NetErrorToPepperError(net_result);
if (pp_result < 0)
SendSendToError(context, pp_result);
else
SendSendToReply(context, PP_OK, pp_result);
sendto_buffer_ = NULL;
}
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) {
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) {
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);
}
} // namespace content