| // 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 |