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