| // Copyright 2016 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 "ppapi/proxy/vpn_provider_resource.h" |
| |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "ppapi/c/pp_errors.h" |
| #include "ppapi/proxy/dispatch_reply_message.h" |
| #include "ppapi/proxy/ppapi_messages.h" |
| #include "ppapi/shared_impl/array_var.h" |
| #include "ppapi/shared_impl/ppapi_globals.h" |
| #include "ppapi/shared_impl/var_tracker.h" |
| |
| namespace ppapi { |
| namespace proxy { |
| |
| VpnProviderResource::VpnProviderResource(Connection connection, |
| PP_Instance instance) |
| : PluginResource(connection, instance), |
| bind_callback_(nullptr), |
| send_packet_callback_(nullptr), |
| receive_packet_callback_(nullptr), |
| receive_packet_callback_var_(nullptr), |
| bound_(false) { |
| SendCreate(BROWSER, PpapiHostMsg_VpnProvider_Create()); |
| } |
| |
| VpnProviderResource::~VpnProviderResource() {} |
| |
| thunk::PPB_VpnProvider_API* VpnProviderResource::AsPPB_VpnProvider_API() { |
| return this; |
| } |
| |
| void VpnProviderResource::OnReplyReceived( |
| const ResourceMessageReplyParams& params, |
| const IPC::Message& msg) { |
| PPAPI_BEGIN_MESSAGE_MAP(VpnProviderResource, msg) |
| PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_VpnProvider_OnUnbind, |
| OnPluginMsgOnUnbindReceived) |
| PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( |
| PpapiPluginMsg_VpnProvider_OnPacketReceived, |
| OnPluginMsgOnPacketReceived) |
| PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED( |
| PluginResource::OnReplyReceived(params, msg)) |
| PPAPI_END_MESSAGE_MAP() |
| } |
| |
| int32_t VpnProviderResource::Bind( |
| const PP_Var& configuration_id, |
| const PP_Var& configuration_name, |
| const scoped_refptr<TrackedCallback>& callback) { |
| if (TrackedCallback::IsPending(bind_callback_)) |
| return PP_ERROR_INPROGRESS; |
| |
| StringVar* configuration_id_var = StringVar::FromPPVar(configuration_id); |
| if (!configuration_id_var) |
| return PP_ERROR_BADARGUMENT; |
| StringVar* configuration_name_var = StringVar::FromPPVar(configuration_name); |
| if (!configuration_name_var) |
| return PP_ERROR_BADARGUMENT; |
| |
| bind_callback_ = callback; |
| |
| Call<PpapiPluginMsg_VpnProvider_BindReply>( |
| BROWSER, PpapiHostMsg_VpnProvider_Bind(configuration_id_var->value(), |
| configuration_name_var->value()), |
| base::Bind(&VpnProviderResource::OnPluginMsgBindReply, this)); |
| |
| return PP_OK_COMPLETIONPENDING; |
| } |
| |
| int32_t VpnProviderResource::SendPacket( |
| const PP_Var& packet, |
| const scoped_refptr<TrackedCallback>& callback) { |
| if (!bound_) |
| return PP_ERROR_FAILED; |
| if (TrackedCallback::IsPending(send_packet_callback_)) |
| return PP_ERROR_INPROGRESS; |
| if (!ArrayBufferVar::FromPPVar(packet)) |
| return PP_ERROR_BADARGUMENT; |
| |
| uint32_t id; |
| if (send_packet_buffer_.get() && send_packet_buffer_->GetAvailable(&id)) { |
| // Send packet immediately |
| send_packet_buffer_->SetAvailable(id, false); |
| return DoSendPacket(packet, id); |
| } else { |
| // Packet will be sent later |
| send_packet_callback_ = callback; |
| PpapiGlobals::Get()->GetVarTracker()->AddRefVar(packet); |
| send_packets_.push(packet); |
| |
| return PP_OK_COMPLETIONPENDING; |
| } |
| } |
| |
| int32_t VpnProviderResource::DoSendPacket(const PP_Var& packet, uint32_t id) { |
| // Convert packet to std::vector<char>, then send it. |
| scoped_refptr<ArrayBufferVar> packet_arraybuffer = |
| ArrayBufferVar::FromPPVar(packet); |
| if (!packet_arraybuffer.get()) |
| return PP_ERROR_BADARGUMENT; |
| |
| uint32_t packet_size = packet_arraybuffer->ByteLength(); |
| if (packet_size > send_packet_buffer_->max_packet_size()) |
| return PP_ERROR_MESSAGE_TOO_BIG; |
| |
| char* packet_pointer = static_cast<char*>(packet_arraybuffer->Map()); |
| memcpy(send_packet_buffer_->GetBuffer(id), packet_pointer, packet_size); |
| packet_arraybuffer->Unmap(); |
| |
| Call<PpapiPluginMsg_VpnProvider_SendPacketReply>( |
| BROWSER, PpapiHostMsg_VpnProvider_SendPacket(packet_size, id), |
| base::Bind(&VpnProviderResource::OnPluginMsgSendPacketReply, this)); |
| |
| return PP_OK; |
| } |
| |
| int32_t VpnProviderResource::ReceivePacket( |
| PP_Var* packet, |
| const scoped_refptr<TrackedCallback>& callback) { |
| if (TrackedCallback::IsPending(receive_packet_callback_)) |
| return PP_ERROR_INPROGRESS; |
| |
| // Return previously received packet. |
| if (!received_packets_.empty()) { |
| receive_packet_callback_var_ = packet; |
| WritePacket(); |
| return PP_OK; |
| } |
| |
| // Or retain packet var and install callback. |
| receive_packet_callback_var_ = packet; |
| receive_packet_callback_ = callback; |
| |
| return PP_OK_COMPLETIONPENDING; |
| } |
| |
| void VpnProviderResource::OnPluginMsgOnUnbindReceived( |
| const ResourceMessageReplyParams& params) { |
| bound_ = false; |
| |
| // Cleanup in-flight packets. |
| while (!received_packets_.empty()) { |
| received_packets_.pop(); |
| } |
| while (!send_packets_.empty()) { |
| PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(send_packets_.front()); |
| send_packets_.pop(); |
| } |
| |
| send_packet_buffer_.reset(); |
| recv_packet_buffer_.reset(); |
| } |
| |
| void VpnProviderResource::OnPluginMsgOnPacketReceived( |
| const ResourceMessageReplyParams& params, |
| uint32_t packet_size, |
| uint32_t id) { |
| DCHECK_LE(packet_size, recv_packet_buffer_->max_packet_size()); |
| if (!bound_) { |
| // Ignore packet and mark shared memory as available |
| Post(BROWSER, PpapiHostMsg_VpnProvider_OnPacketReceivedReply(id)); |
| return; |
| } |
| |
| // Append received packet to queue. |
| void* packet_pointer = recv_packet_buffer_->GetBuffer(id); |
| scoped_refptr<Var> packet_var( |
| PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferVar(packet_size, |
| packet_pointer)); |
| received_packets_.push(packet_var); |
| |
| // Mark shared memory as available for next packet |
| Post(BROWSER, PpapiHostMsg_VpnProvider_OnPacketReceivedReply(id)); |
| |
| if (!TrackedCallback::IsPending(receive_packet_callback_) || |
| TrackedCallback::IsScheduledToRun(receive_packet_callback_)) { |
| return; |
| } |
| |
| scoped_refptr<TrackedCallback> callback; |
| callback.swap(receive_packet_callback_); |
| WritePacket(); |
| callback->Run(PP_OK); |
| } |
| |
| void VpnProviderResource::OnPluginMsgBindReply( |
| const ResourceMessageReplyParams& params, |
| uint32_t queue_size, |
| uint32_t max_packet_size, |
| int32_t result) { |
| if (!TrackedCallback::IsPending(bind_callback_)) |
| return; |
| |
| if (params.result() == PP_OK) { |
| std::vector<base::SharedMemoryHandle> shm_handles; |
| params.TakeAllSharedMemoryHandles(&shm_handles); |
| std::unique_ptr<base::SharedMemory> send_shm( |
| new base::SharedMemory(shm_handles[0], false)); |
| std::unique_ptr<base::SharedMemory> receive_shm( |
| new base::SharedMemory(shm_handles[1], false)); |
| size_t buffer_size = queue_size * max_packet_size; |
| if (!send_shm->Map(buffer_size) || !receive_shm->Map(buffer_size)) { |
| NOTREACHED(); |
| return; |
| } |
| send_packet_buffer_ = std::make_unique<ppapi::VpnProviderSharedBuffer>( |
| queue_size, max_packet_size, std::move(send_shm)); |
| recv_packet_buffer_ = std::make_unique<ppapi::VpnProviderSharedBuffer>( |
| queue_size, max_packet_size, std::move(receive_shm)); |
| |
| bound_ = (result == PP_OK); |
| } |
| |
| scoped_refptr<TrackedCallback> callback; |
| callback.swap(bind_callback_); |
| callback->Run(params.result() ? params.result() : result); |
| } |
| |
| void VpnProviderResource::OnPluginMsgSendPacketReply( |
| const ResourceMessageReplyParams& params, |
| uint32_t id) { |
| if (!send_packets_.empty() && bound_) { |
| // Process remaining packets |
| DoSendPacket(send_packets_.front(), id); |
| PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(send_packets_.front()); |
| send_packets_.pop(); |
| } else { |
| send_packet_buffer_->SetAvailable(id, true); |
| |
| // Available slots - Run callback to process new packets. |
| if (TrackedCallback::IsPending(send_packet_callback_)) { |
| scoped_refptr<TrackedCallback> callback; |
| callback.swap(send_packet_callback_); |
| callback->Run(PP_OK); |
| } |
| } |
| } |
| |
| void VpnProviderResource::WritePacket() { |
| if (!receive_packet_callback_var_) |
| return; |
| |
| *receive_packet_callback_var_ = received_packets_.front()->GetPPVar(); |
| received_packets_.pop(); |
| receive_packet_callback_var_ = nullptr; |
| } |
| |
| } // namespace proxy |
| } // namespace ppapi |