blob: f943f39eb9e2ed7173cb89348a8bfec828474df6 [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_tcp_socket_message_filter.h"
#include <cstring>
#include "base/bind.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
#include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h"
#include "content/browser/renderer_host/pepper/pepper_socket_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/resource_context.h"
#include "content/public/common/socket_permission_request.h"
#include "net/base/address_family.h"
#include "net/base/host_port_pair.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/dns/single_request_host_resolver.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/ssl_client_socket.h"
#include "net/socket/tcp_client_socket.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/error_conversion.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/host/resource_host.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/tcp_socket_resource_base.h"
#include "ppapi/shared_impl/private/net_address_private_impl.h"
using ppapi::NetAddressPrivateImpl;
using ppapi::host::NetErrorToPepperError;
using ppapi::proxy::TCPSocketResourceBase;
using ppapi::TCPSocketState;
using ppapi::TCPSocketVersion;
namespace {
size_t g_num_instances = 0;
} // namespace
namespace content {
PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter(
ContentBrowserPepperHostFactory* factory,
BrowserPpapiHostImpl* host,
PP_Instance instance,
TCPSocketVersion version)
: version_(version),
external_plugin_(host->external_plugin()),
render_process_id_(0),
render_view_id_(0),
ppapi_host_(host->GetPpapiHost()),
factory_(factory),
instance_(instance),
state_(TCPSocketState::INITIAL),
end_of_file_reached_(false),
bind_input_addr_(NetAddressPrivateImpl::kInvalidNetAddress),
address_index_(0),
socket_(new net::TCPSocket(NULL, net::NetLog::Source())),
ssl_context_helper_(host->ssl_context_helper()),
pending_accept_(false) {
DCHECK(host);
++g_num_instances;
if (!host->GetRenderViewIDsForInstance(instance,
&render_process_id_,
&render_view_id_)) {
NOTREACHED();
}
}
PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter(
BrowserPpapiHostImpl* host,
PP_Instance instance,
TCPSocketVersion version,
scoped_ptr<net::TCPSocket> socket)
: version_(version),
external_plugin_(host->external_plugin()),
render_process_id_(0),
render_view_id_(0),
ppapi_host_(host->GetPpapiHost()),
factory_(NULL),
instance_(instance),
state_(TCPSocketState::CONNECTED),
end_of_file_reached_(false),
bind_input_addr_(NetAddressPrivateImpl::kInvalidNetAddress),
address_index_(0),
socket_(socket.Pass()),
ssl_context_helper_(host->ssl_context_helper()),
pending_accept_(false) {
DCHECK(host);
DCHECK_NE(version, ppapi::TCP_SOCKET_VERSION_1_0);
++g_num_instances;
if (!host->GetRenderViewIDsForInstance(instance,
&render_process_id_,
&render_view_id_)) {
NOTREACHED();
}
}
PepperTCPSocketMessageFilter::~PepperTCPSocketMessageFilter() {
if (socket_)
socket_->Close();
if (ssl_socket_)
ssl_socket_->Disconnect();
--g_num_instances;
}
// static
size_t PepperTCPSocketMessageFilter::GetNumInstances() {
return g_num_instances;
}
scoped_refptr<base::TaskRunner>
PepperTCPSocketMessageFilter::OverrideTaskRunnerForMessage(
const IPC::Message& message) {
switch (message.type()) {
case PpapiHostMsg_TCPSocket_Bind::ID:
case PpapiHostMsg_TCPSocket_Connect::ID:
case PpapiHostMsg_TCPSocket_ConnectWithNetAddress::ID:
case PpapiHostMsg_TCPSocket_Listen::ID:
return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
case PpapiHostMsg_TCPSocket_SSLHandshake::ID:
case PpapiHostMsg_TCPSocket_Read::ID:
case PpapiHostMsg_TCPSocket_Write::ID:
case PpapiHostMsg_TCPSocket_Accept::ID:
case PpapiHostMsg_TCPSocket_Close::ID:
case PpapiHostMsg_TCPSocket_SetOption::ID:
return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
}
return NULL;
}
int32_t PepperTCPSocketMessageFilter::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
IPC_BEGIN_MESSAGE_MAP(PepperTCPSocketMessageFilter, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_TCPSocket_Bind, OnMsgBind)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_TCPSocket_Connect, OnMsgConnect)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_TCPSocket_ConnectWithNetAddress,
OnMsgConnectWithNetAddress)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_TCPSocket_SSLHandshake, OnMsgSSLHandshake)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_TCPSocket_Read, OnMsgRead)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_TCPSocket_Write, OnMsgWrite)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_TCPSocket_Listen, OnMsgListen)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
PpapiHostMsg_TCPSocket_Accept, OnMsgAccept)
PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
PpapiHostMsg_TCPSocket_Close, OnMsgClose)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_TCPSocket_SetOption, OnMsgSetOption)
IPC_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
int32_t PepperTCPSocketMessageFilter::OnMsgBind(
const ppapi::host::HostMessageContext* context,
const PP_NetAddress_Private& net_addr) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// This is only supported by PPB_TCPSocket v1.1 or above.
if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) {
NOTREACHED();
return PP_ERROR_NOACCESS;
}
if (!pepper_socket_utils::CanUseSocketAPIs(
external_plugin_, false /* private_api */, NULL, render_process_id_,
render_view_id_)) {
return PP_ERROR_NOACCESS;
}
bind_input_addr_ = net_addr;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PepperTCPSocketMessageFilter::DoBind, this,
context->MakeReplyMessageContext(), net_addr));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPSocketMessageFilter::OnMsgConnect(
const ppapi::host::HostMessageContext* context,
const std::string& host,
uint16_t port) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// This is only supported by PPB_TCPSocket_Private.
if (!IsPrivateAPI()) {
NOTREACHED();
return PP_ERROR_NOACCESS;
}
SocketPermissionRequest request(SocketPermissionRequest::TCP_CONNECT,
host,
port);
if (!pepper_socket_utils::CanUseSocketAPIs(
external_plugin_, true /* private_api */, &request,
render_process_id_, render_view_id_)) {
return PP_ERROR_NOACCESS;
}
RenderProcessHost* render_process_host =
RenderProcessHost::FromID(render_process_id_);
if (!render_process_host)
return PP_ERROR_FAILED;
BrowserContext* browser_context = render_process_host->GetBrowserContext();
if (!browser_context || !browser_context->GetResourceContext())
return PP_ERROR_FAILED;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PepperTCPSocketMessageFilter::DoConnect, this,
context->MakeReplyMessageContext(),
host, port, browser_context->GetResourceContext()));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPSocketMessageFilter::OnMsgConnectWithNetAddress(
const ppapi::host::HostMessageContext* context,
const PP_NetAddress_Private& net_addr) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
content::SocketPermissionRequest request =
pepper_socket_utils::CreateSocketPermissionRequest(
content::SocketPermissionRequest::TCP_CONNECT, net_addr);
if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, IsPrivateAPI(),
&request, render_process_id_,
render_view_id_)) {
return PP_ERROR_NOACCESS;
}
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PepperTCPSocketMessageFilter::DoConnectWithNetAddress, this,
context->MakeReplyMessageContext(), net_addr));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPSocketMessageFilter::OnMsgSSLHandshake(
const ppapi::host::HostMessageContext* context,
const std::string& server_name,
uint16_t server_port,
const std::vector<std::vector<char> >& trusted_certs,
const std::vector<std::vector<char> >& untrusted_certs) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Allow to do SSL handshake only if currently the socket has been connected
// and there isn't pending read or write.
if (!state_.IsValidTransition(TCPSocketState::SSL_CONNECT) ||
read_buffer_.get() || write_buffer_base_.get() || write_buffer_.get()) {
return PP_ERROR_FAILED;
}
// TODO(raymes,rsleevi): Use trusted/untrusted certificates when connecting.
net::IPEndPoint peer_address;
if (socket_->GetPeerAddress(&peer_address) != net::OK)
return PP_ERROR_FAILED;
scoped_ptr<net::ClientSocketHandle> handle(new net::ClientSocketHandle());
handle->SetSocket(make_scoped_ptr<net::StreamSocket>(
new net::TCPClientSocket(socket_.Pass(), peer_address)));
net::ClientSocketFactory* factory =
net::ClientSocketFactory::GetDefaultFactory();
net::HostPortPair host_port_pair(server_name, server_port);
net::SSLClientSocketContext ssl_context;
ssl_context.cert_verifier = ssl_context_helper_->GetCertVerifier();
ssl_context.transport_security_state =
ssl_context_helper_->GetTransportSecurityState();
ssl_socket_ = factory->CreateSSLClientSocket(
handle.Pass(), host_port_pair, ssl_context_helper_->ssl_config(),
ssl_context);
if (!ssl_socket_) {
LOG(WARNING) << "Failed to create an SSL client socket.";
state_.CompletePendingTransition(false);
return PP_ERROR_FAILED;
}
state_.SetPendingTransition(TCPSocketState::SSL_CONNECT);
const ppapi::host::ReplyMessageContext reply_context(
context->MakeReplyMessageContext());
int net_result = ssl_socket_->Connect(
base::Bind(&PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted,
base::Unretained(this), reply_context));
if (net_result != net::ERR_IO_PENDING)
OnSSLHandshakeCompleted(reply_context, net_result);
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPSocketMessageFilter::OnMsgRead(
const ppapi::host::HostMessageContext* context,
int32_t bytes_to_read) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!state_.IsConnected() || end_of_file_reached_)
return PP_ERROR_FAILED;
if (read_buffer_.get())
return PP_ERROR_INPROGRESS;
if (bytes_to_read <= 0 ||
bytes_to_read > TCPSocketResourceBase::kMaxReadSize) {
return PP_ERROR_BADARGUMENT;
}
ppapi::host::ReplyMessageContext reply_context(
context->MakeReplyMessageContext());
read_buffer_ = new net::IOBuffer(bytes_to_read);
int net_result = net::ERR_FAILED;
if (socket_) {
DCHECK_EQ(state_.state(), TCPSocketState::CONNECTED);
net_result = socket_->Read(
read_buffer_.get(),
bytes_to_read,
base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted,
base::Unretained(this), reply_context));
} else if (ssl_socket_) {
DCHECK_EQ(state_.state(), TCPSocketState::SSL_CONNECTED);
net_result = ssl_socket_->Read(
read_buffer_.get(),
bytes_to_read,
base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted,
base::Unretained(this), reply_context));
}
if (net_result != net::ERR_IO_PENDING)
OnReadCompleted(reply_context, net_result);
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPSocketMessageFilter::OnMsgWrite(
const ppapi::host::HostMessageContext* context,
const std::string& data) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!state_.IsConnected())
return PP_ERROR_FAILED;
if (write_buffer_base_.get() || write_buffer_.get())
return PP_ERROR_INPROGRESS;
size_t data_size = data.size();
if (data_size == 0 ||
data_size > static_cast<size_t>(TCPSocketResourceBase::kMaxWriteSize)) {
return PP_ERROR_BADARGUMENT;
}
write_buffer_base_ = new net::IOBuffer(data_size);
memcpy(write_buffer_base_->data(), data.data(), data_size);
write_buffer_ =
new net::DrainableIOBuffer(write_buffer_base_.get(), data_size);
DoWrite(context->MakeReplyMessageContext());
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPSocketMessageFilter::OnMsgListen(
const ppapi::host::HostMessageContext* context,
int32_t backlog) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// This is only supported by PPB_TCPSocket v1.1 or above.
if (version_ != ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) {
NOTREACHED();
return PP_ERROR_NOACCESS;
}
content::SocketPermissionRequest request =
pepper_socket_utils::CreateSocketPermissionRequest(
content::SocketPermissionRequest::TCP_LISTEN, bind_input_addr_);
if (!pepper_socket_utils::CanUseSocketAPIs(
external_plugin_, false /* private_api */, &request,
render_process_id_, render_view_id_)) {
return PP_ERROR_NOACCESS;
}
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PepperTCPSocketMessageFilter::DoListen, this,
context->MakeReplyMessageContext(), backlog));
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPSocketMessageFilter::OnMsgAccept(
const ppapi::host::HostMessageContext* context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (pending_accept_)
return PP_ERROR_INPROGRESS;
if (state_.state() != TCPSocketState::LISTENING)
return PP_ERROR_FAILED;
pending_accept_ = true;
ppapi::host::ReplyMessageContext reply_context(
context->MakeReplyMessageContext());
int net_result = socket_->Accept(
&accepted_socket_,
&accepted_address_,
base::Bind(&PepperTCPSocketMessageFilter::OnAcceptCompleted,
base::Unretained(this), reply_context));
if (net_result != net::ERR_IO_PENDING)
OnAcceptCompleted(reply_context, net_result);
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperTCPSocketMessageFilter::OnMsgClose(
const ppapi::host::HostMessageContext* context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (state_.state() == TCPSocketState::CLOSED)
return PP_OK;
state_.DoTransition(TCPSocketState::CLOSE, true);
// Make sure we get no further callbacks from |socket_| or |ssl_socket_|.
if (socket_) {
socket_->Close();
} else if (ssl_socket_) {
ssl_socket_->Disconnect();
}
return PP_OK;
}
int32_t PepperTCPSocketMessageFilter::OnMsgSetOption(
const ppapi::host::HostMessageContext* context,
PP_TCPSocket_Option name,
const ppapi::SocketOptionData& value) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
switch (name) {
case PP_TCPSOCKET_OPTION_NO_DELAY: {
if (state_.state() != TCPSocketState::CONNECTED)
return PP_ERROR_FAILED;
bool boolean_value = false;
if (!value.GetBool(&boolean_value))
return PP_ERROR_BADARGUMENT;
return socket_->SetNoDelay(boolean_value) ? PP_OK : PP_ERROR_FAILED;
}
case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE:
case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: {
if (state_.state() != TCPSocketState::CONNECTED)
return PP_ERROR_FAILED;
int32_t integer_value = 0;
if (!value.GetInt32(&integer_value) || integer_value <= 0)
return PP_ERROR_BADARGUMENT;
bool result = false;
if (name == PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE) {
if (integer_value > TCPSocketResourceBase::kMaxSendBufferSize)
return PP_ERROR_BADARGUMENT;
result = socket_->SetSendBufferSize(integer_value);
} else {
if (integer_value > TCPSocketResourceBase::kMaxReceiveBufferSize)
return PP_ERROR_BADARGUMENT;
result = socket_->SetReceiveBufferSize(integer_value);
}
return result ? PP_OK : PP_ERROR_FAILED;
}
default: {
NOTREACHED();
return PP_ERROR_BADARGUMENT;
}
}
}
void PepperTCPSocketMessageFilter::DoBind(
const ppapi::host::ReplyMessageContext& context,
const PP_NetAddress_Private& net_addr) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (state_.IsPending(TCPSocketState::BIND)) {
SendBindError(context, PP_ERROR_INPROGRESS);
return;
}
if (!state_.IsValidTransition(TCPSocketState::BIND)) {
SendBindError(context, PP_ERROR_FAILED);
return;
}
int pp_result = PP_OK;
do {
net::IPAddressNumber address;
int port;
if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address,
&port)) {
pp_result = PP_ERROR_ADDRESS_INVALID;
break;
}
net::IPEndPoint bind_addr(address, port);
DCHECK(!socket_->IsValid());
pp_result = NetErrorToPepperError(socket_->Open(bind_addr.GetFamily()));
if (pp_result != PP_OK)
break;
pp_result = NetErrorToPepperError(socket_->SetDefaultOptionsForServer());
if (pp_result != PP_OK)
break;
pp_result = NetErrorToPepperError(socket_->Bind(bind_addr));
if (pp_result != PP_OK)
break;
net::IPEndPoint ip_end_point_local;
pp_result = NetErrorToPepperError(
socket_->GetLocalAddress(&ip_end_point_local));
if (pp_result != PP_OK)
break;
PP_NetAddress_Private local_addr =
NetAddressPrivateImpl::kInvalidNetAddress;
if (!NetAddressPrivateImpl::IPEndPointToNetAddress(
ip_end_point_local.address(),
ip_end_point_local.port(),
&local_addr)) {
pp_result = PP_ERROR_ADDRESS_INVALID;
break;
}
SendBindReply(context, PP_OK, local_addr);
state_.DoTransition(TCPSocketState::BIND, true);
return;
} while (false);
if (socket_->IsValid())
socket_->Close();
SendBindError(context, pp_result);
state_.DoTransition(TCPSocketState::BIND, false);
}
void PepperTCPSocketMessageFilter::DoConnect(
const ppapi::host::ReplyMessageContext& context,
const std::string& host,
uint16_t port,
ResourceContext* resource_context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!state_.IsValidTransition(TCPSocketState::CONNECT)) {
SendConnectError(context, PP_ERROR_FAILED);
return;
}
state_.SetPendingTransition(TCPSocketState::CONNECT);
address_index_ = 0;
address_list_.clear();
net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port));
resolver_.reset(new net::SingleRequestHostResolver(
resource_context->GetHostResolver()));
int net_result = resolver_->Resolve(
request_info,
net::DEFAULT_PRIORITY,
&address_list_,
base::Bind(&PepperTCPSocketMessageFilter::OnResolveCompleted,
base::Unretained(this), context),
net::BoundNetLog());
if (net_result != net::ERR_IO_PENDING)
OnResolveCompleted(context, net_result);
}
void PepperTCPSocketMessageFilter::DoConnectWithNetAddress(
const ppapi::host::ReplyMessageContext& context,
const PP_NetAddress_Private& net_addr) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!state_.IsValidTransition(TCPSocketState::CONNECT)) {
SendConnectError(context, PP_ERROR_FAILED);
return;
}
state_.SetPendingTransition(TCPSocketState::CONNECT);
net::IPAddressNumber address;
int port;
if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address,
&port)) {
state_.CompletePendingTransition(false);
SendConnectError(context, PP_ERROR_ADDRESS_INVALID);
return;
}
// Copy the single IPEndPoint to address_list_.
address_index_ = 0;
address_list_.clear();
address_list_.push_back(net::IPEndPoint(address, port));
StartConnect(context);
}
void PepperTCPSocketMessageFilter::DoWrite(
const ppapi::host::ReplyMessageContext& context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(write_buffer_base_.get());
DCHECK(write_buffer_.get());
DCHECK_GT(write_buffer_->BytesRemaining(), 0);
DCHECK(state_.IsConnected());
int net_result = net::ERR_FAILED;
if (socket_) {
DCHECK_EQ(state_.state(), TCPSocketState::CONNECTED);
net_result = socket_->Write(
write_buffer_.get(),
write_buffer_->BytesRemaining(),
base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted,
base::Unretained(this), context));
} else if (ssl_socket_) {
DCHECK_EQ(state_.state(), TCPSocketState::SSL_CONNECTED);
net_result = ssl_socket_->Write(
write_buffer_.get(),
write_buffer_->BytesRemaining(),
base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted,
base::Unretained(this), context));
}
if (net_result != net::ERR_IO_PENDING)
OnWriteCompleted(context, net_result);
}
void PepperTCPSocketMessageFilter::DoListen(
const ppapi::host::ReplyMessageContext& context,
int32_t backlog) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (state_.IsPending(TCPSocketState::LISTEN)) {
SendListenReply(context, PP_ERROR_INPROGRESS);
return;
}
if (!state_.IsValidTransition(TCPSocketState::LISTEN)) {
SendListenReply(context, PP_ERROR_FAILED);
return;
}
int32_t pp_result = NetErrorToPepperError(socket_->Listen(backlog));
SendListenReply(context, pp_result);
state_.DoTransition(TCPSocketState::LISTEN, pp_result == PP_OK);
}
void PepperTCPSocketMessageFilter::OnResolveCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!state_.IsPending(TCPSocketState::CONNECT)) {
DCHECK(state_.state() == TCPSocketState::CLOSED);
SendConnectError(context, PP_ERROR_FAILED);
return;
}
if (net_result != net::OK) {
SendConnectError(context, NetErrorToPepperError(net_result));
state_.CompletePendingTransition(false);
return;
}
StartConnect(context);
}
void PepperTCPSocketMessageFilter::StartConnect(
const ppapi::host::ReplyMessageContext& context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(state_.IsPending(TCPSocketState::CONNECT));
DCHECK_LT(address_index_, address_list_.size());
int net_result = net::OK;
if (!socket_->IsValid())
net_result = socket_->Open(address_list_[address_index_].GetFamily());
if (net_result == net::OK) {
net_result = socket_->Connect(
address_list_[address_index_],
base::Bind(&PepperTCPSocketMessageFilter::OnConnectCompleted,
base::Unretained(this), context));
}
if (net_result != net::ERR_IO_PENDING)
OnConnectCompleted(context, net_result);
}
void PepperTCPSocketMessageFilter::OnConnectCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!state_.IsPending(TCPSocketState::CONNECT)) {
DCHECK(state_.state() == TCPSocketState::CLOSED);
SendConnectError(context, PP_ERROR_FAILED);
return;
}
int32_t pp_result = NetErrorToPepperError(net_result);
do {
if (pp_result != PP_OK)
break;
net::IPEndPoint ip_end_point_local;
net::IPEndPoint ip_end_point_remote;
pp_result = NetErrorToPepperError(
socket_->GetLocalAddress(&ip_end_point_local));
if (pp_result != PP_OK)
break;
pp_result = NetErrorToPepperError(
socket_->GetPeerAddress(&ip_end_point_remote));
if (pp_result != PP_OK)
break;
PP_NetAddress_Private local_addr =
NetAddressPrivateImpl::kInvalidNetAddress;
PP_NetAddress_Private remote_addr =
NetAddressPrivateImpl::kInvalidNetAddress;
if (!NetAddressPrivateImpl::IPEndPointToNetAddress(
ip_end_point_local.address(),
ip_end_point_local.port(),
&local_addr) ||
!NetAddressPrivateImpl::IPEndPointToNetAddress(
ip_end_point_remote.address(),
ip_end_point_remote.port(),
&remote_addr)) {
pp_result = PP_ERROR_ADDRESS_INVALID;
break;
}
socket_->SetDefaultOptionsForClient();
SendConnectReply(context, PP_OK, local_addr, remote_addr);
state_.CompletePendingTransition(true);
return;
} while (false);
if (version_ == ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE) {
DCHECK_EQ(1u, address_list_.size());
SendConnectError(context, pp_result);
state_.CompletePendingTransition(false);
} else {
// We have to recreate |socket_| because it doesn't allow a second connect
// attempt. We won't lose any state such as bound address or set options,
// because in the private or v1.0 API, connect must be the first operation.
socket_.reset(new net::TCPSocket(NULL, net::NetLog::Source()));
if (address_index_ + 1 < address_list_.size()) {
DCHECK_EQ(version_, ppapi::TCP_SOCKET_VERSION_PRIVATE);
address_index_++;
StartConnect(context);
} else {
SendConnectError(context, pp_result);
// In order to maintain backward compatibility, allow further attempts to
// connect the socket.
state_ = TCPSocketState(TCPSocketState::INITIAL);
}
}
}
void PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!state_.IsPending(TCPSocketState::SSL_CONNECT)) {
DCHECK(state_.state() == TCPSocketState::CLOSED);
SendSSLHandshakeReply(context, PP_ERROR_FAILED);
return;
}
SendSSLHandshakeReply(context, NetErrorToPepperError(net_result));
state_.CompletePendingTransition(net_result == net::OK);
}
void PepperTCPSocketMessageFilter::OnReadCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(read_buffer_.get());
if (net_result > 0) {
SendReadReply(context,
PP_OK,
std::string(read_buffer_->data(), net_result));
} else if (net_result == 0) {
end_of_file_reached_ = true;
SendReadReply(context, PP_OK, std::string());
} else {
SendReadError(context, NetErrorToPepperError(net_result));
}
read_buffer_ = NULL;
}
void PepperTCPSocketMessageFilter::OnWriteCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(write_buffer_base_.get());
DCHECK(write_buffer_.get());
// Note: For partial writes of 0 bytes, don't continue writing to avoid a
// likely infinite loop.
if (net_result > 0) {
write_buffer_->DidConsume(net_result);
if (write_buffer_->BytesRemaining() > 0 && state_.IsConnected()) {
DoWrite(context);
return;
}
}
if (net_result >= 0)
SendWriteReply(context, write_buffer_->BytesConsumed());
else
SendWriteReply(context, NetErrorToPepperError(net_result));
write_buffer_ = NULL;
write_buffer_base_ = NULL;
}
void PepperTCPSocketMessageFilter::OnAcceptCompleted(
const ppapi::host::ReplyMessageContext& context,
int net_result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(pending_accept_);
pending_accept_ = false;
if (net_result != net::OK) {
SendAcceptError(context, NetErrorToPepperError(net_result));
return;
}
DCHECK(accepted_socket_.get());
net::IPEndPoint ip_end_point_local;
PP_NetAddress_Private local_addr = NetAddressPrivateImpl::kInvalidNetAddress;
PP_NetAddress_Private remote_addr = NetAddressPrivateImpl::kInvalidNetAddress;
int32_t pp_result =
NetErrorToPepperError(accepted_socket_->GetLocalAddress(
&ip_end_point_local));
if (pp_result != PP_OK) {
SendAcceptError(context, pp_result);
return;
}
if (!NetAddressPrivateImpl::IPEndPointToNetAddress(
ip_end_point_local.address(),
ip_end_point_local.port(),
&local_addr) ||
!NetAddressPrivateImpl::IPEndPointToNetAddress(
accepted_address_.address(),
accepted_address_.port(),
&remote_addr)) {
SendAcceptError(context, PP_ERROR_ADDRESS_INVALID);
return;
}
// |factory_| is guaranteed to be non-NULL here. Only those instances created
// in CONNECTED state have a NULL |factory_|, while getting here requires
// LISTENING state.
scoped_ptr<ppapi::host::ResourceHost> host =
factory_->CreateAcceptedTCPSocket(
instance_, version_, accepted_socket_.Pass());
if (!host) {
SendAcceptError(context, PP_ERROR_NOSPACE);
return;
}
int pending_host_id = ppapi_host_->AddPendingResourceHost(host.Pass());
if (pending_host_id)
SendAcceptReply(context, PP_OK, pending_host_id, local_addr, remote_addr);
else
SendAcceptError(context, PP_ERROR_NOSPACE);
}
void PepperTCPSocketMessageFilter::SendBindReply(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_result,
const PP_NetAddress_Private& local_addr) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(pp_result);
SendReply(reply_context, PpapiPluginMsg_TCPSocket_BindReply(local_addr));
}
void PepperTCPSocketMessageFilter::SendBindError(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_error) {
SendBindReply(context, pp_error, NetAddressPrivateImpl::kInvalidNetAddress);
}
void PepperTCPSocketMessageFilter::SendConnectReply(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_result,
const PP_NetAddress_Private& local_addr,
const PP_NetAddress_Private& remote_addr) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(pp_result);
SendReply(reply_context,
PpapiPluginMsg_TCPSocket_ConnectReply(local_addr, remote_addr));
}
void PepperTCPSocketMessageFilter::SendConnectError(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_error) {
SendConnectReply(context,
pp_error,
NetAddressPrivateImpl::kInvalidNetAddress,
NetAddressPrivateImpl::kInvalidNetAddress);
}
void PepperTCPSocketMessageFilter::SendSSLHandshakeReply(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_result) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(pp_result);
ppapi::PPB_X509Certificate_Fields certificate_fields;
if (pp_result == PP_OK) {
// Our socket is guaranteed to be an SSL socket if we get here.
net::SSLInfo ssl_info;
ssl_socket_->GetSSLInfo(&ssl_info);
if (ssl_info.cert.get()) {
pepper_socket_utils::GetCertificateFields(*ssl_info.cert.get(),
&certificate_fields);
}
}
SendReply(reply_context,
PpapiPluginMsg_TCPSocket_SSLHandshakeReply(certificate_fields));
}
void PepperTCPSocketMessageFilter::SendReadReply(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_result,
const std::string& data) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(pp_result);
SendReply(reply_context, PpapiPluginMsg_TCPSocket_ReadReply(data));
}
void PepperTCPSocketMessageFilter::SendReadError(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_error) {
SendReadReply(context, pp_error, std::string());
}
void PepperTCPSocketMessageFilter::SendWriteReply(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_result) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(pp_result);
SendReply(reply_context, PpapiPluginMsg_TCPSocket_WriteReply());
}
void PepperTCPSocketMessageFilter::SendListenReply(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_result) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(pp_result);
SendReply(reply_context, PpapiPluginMsg_TCPSocket_ListenReply());
}
void PepperTCPSocketMessageFilter::SendAcceptReply(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_result,
int pending_host_id,
const PP_NetAddress_Private& local_addr,
const PP_NetAddress_Private& remote_addr) {
ppapi::host::ReplyMessageContext reply_context(context);
reply_context.params.set_result(pp_result);
SendReply(reply_context,
PpapiPluginMsg_TCPSocket_AcceptReply(
pending_host_id, local_addr, remote_addr));
}
void PepperTCPSocketMessageFilter::SendAcceptError(
const ppapi::host::ReplyMessageContext& context,
int32_t pp_error) {
SendAcceptReply(context, pp_error, 0,
NetAddressPrivateImpl::kInvalidNetAddress,
NetAddressPrivateImpl::kInvalidNetAddress);
}
} // namespace content