| // 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 "mojo/edk/system/node_channel.h" |
| |
| #include <cstring> |
| #include <limits> |
| #include <sstream> |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "mojo/edk/system/channel.h" |
| #include "mojo/edk/system/request_context.h" |
| |
| #if defined(OS_MACOSX) && !defined(OS_IOS) |
| #include "mojo/edk/system/mach_port_relay.h" |
| #endif |
| |
| namespace mojo { |
| namespace edk { |
| |
| namespace { |
| |
| template <typename T> |
| T Align(T t) { |
| const auto k = kChannelMessageAlignment; |
| return t + (k - (t % k)) % k; |
| } |
| |
| // NOTE: Please ONLY append messages to the end of this enum. |
| enum class MessageType : uint32_t { |
| ACCEPT_CHILD, |
| ACCEPT_PARENT, |
| ADD_BROKER_CLIENT, |
| BROKER_CLIENT_ADDED, |
| ACCEPT_BROKER_CLIENT, |
| PORTS_MESSAGE, |
| REQUEST_PORT_MERGE, |
| REQUEST_INTRODUCTION, |
| INTRODUCE, |
| #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) |
| RELAY_PORTS_MESSAGE, |
| #endif |
| BROADCAST, |
| #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) |
| PORTS_MESSAGE_FROM_RELAY, |
| #endif |
| ACCEPT_PEER, |
| }; |
| |
| struct Header { |
| MessageType type; |
| uint32_t padding; |
| }; |
| |
| static_assert(IsAlignedForChannelMessage(sizeof(Header)), |
| "Invalid header size."); |
| |
| struct AcceptChildData { |
| ports::NodeName parent_name; |
| ports::NodeName token; |
| }; |
| |
| struct AcceptParentData { |
| ports::NodeName token; |
| ports::NodeName child_name; |
| }; |
| |
| struct AcceptPeerData { |
| ports::NodeName token; |
| ports::NodeName peer_name; |
| ports::PortName port_name; |
| }; |
| |
| // This message may include a process handle on plaforms that require it. |
| struct AddBrokerClientData { |
| ports::NodeName client_name; |
| #if !defined(OS_WIN) |
| uint32_t process_handle; |
| uint32_t padding; |
| #endif |
| }; |
| |
| #if !defined(OS_WIN) |
| static_assert(sizeof(base::ProcessHandle) == sizeof(uint32_t), |
| "Unexpected pid size"); |
| static_assert(sizeof(AddBrokerClientData) % kChannelMessageAlignment == 0, |
| "Invalid AddBrokerClientData size."); |
| #endif |
| |
| // This data is followed by a platform channel handle to the broker. |
| struct BrokerClientAddedData { |
| ports::NodeName client_name; |
| }; |
| |
| // This data may be followed by a platform channel handle to the broker. If not, |
| // then the parent is the broker and its channel should be used as such. |
| struct AcceptBrokerClientData { |
| ports::NodeName broker_name; |
| }; |
| |
| // This is followed by arbitrary payload data which is interpreted as a token |
| // string for port location. |
| struct RequestPortMergeData { |
| ports::PortName connector_port_name; |
| }; |
| |
| // Used for both REQUEST_INTRODUCTION and INTRODUCE. |
| // |
| // For INTRODUCE the message also includes a valid platform handle for a channel |
| // the receiver may use to communicate with the named node directly, or an |
| // invalid platform handle if the node is unknown to the sender or otherwise |
| // cannot be introduced. |
| struct IntroductionData { |
| ports::NodeName name; |
| }; |
| |
| #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) |
| // This struct is followed by the full payload of a message to be relayed. |
| struct RelayPortsMessageData { |
| ports::NodeName destination; |
| }; |
| |
| // This struct is followed by the full payload of a relayed message. |
| struct PortsMessageFromRelayData { |
| ports::NodeName source; |
| }; |
| #endif |
| |
| template <typename DataType> |
| Channel::MessagePtr CreateMessage(MessageType type, |
| size_t payload_size, |
| size_t num_handles, |
| DataType** out_data) { |
| Channel::MessagePtr message( |
| new Channel::Message(sizeof(Header) + payload_size, num_handles)); |
| Header* header = reinterpret_cast<Header*>(message->mutable_payload()); |
| header->type = type; |
| header->padding = 0; |
| *out_data = reinterpret_cast<DataType*>(&header[1]); |
| return message; |
| } |
| |
| template <typename DataType> |
| bool GetMessagePayload(const void* bytes, |
| size_t num_bytes, |
| DataType** out_data) { |
| static_assert(sizeof(DataType) > 0, "DataType must have non-zero size."); |
| if (num_bytes < sizeof(Header) + sizeof(DataType)) |
| return false; |
| *out_data = reinterpret_cast<const DataType*>( |
| static_cast<const char*>(bytes) + sizeof(Header)); |
| return true; |
| } |
| |
| } // namespace |
| |
| // static |
| scoped_refptr<NodeChannel> NodeChannel::Create( |
| Delegate* delegate, |
| ConnectionParams connection_params, |
| scoped_refptr<base::TaskRunner> io_task_runner, |
| const ProcessErrorCallback& process_error_callback) { |
| #if defined(OS_NACL_SFI) |
| LOG(FATAL) << "Multi-process not yet supported on NaCl-SFI"; |
| return nullptr; |
| #else |
| return new NodeChannel(delegate, std::move(connection_params), io_task_runner, |
| process_error_callback); |
| #endif |
| } |
| |
| // static |
| Channel::MessagePtr NodeChannel::CreatePortsMessage(size_t payload_size, |
| void** payload, |
| size_t num_handles) { |
| return CreateMessage(MessageType::PORTS_MESSAGE, payload_size, num_handles, |
| payload); |
| } |
| |
| // static |
| void NodeChannel::GetPortsMessageData(Channel::Message* message, |
| void** data, |
| size_t* num_data_bytes) { |
| *data = reinterpret_cast<Header*>(message->mutable_payload()) + 1; |
| *num_data_bytes = message->payload_size() - sizeof(Header); |
| } |
| |
| void NodeChannel::Start() { |
| #if defined(OS_MACOSX) && !defined(OS_IOS) |
| MachPortRelay* relay = delegate_->GetMachPortRelay(); |
| if (relay) |
| relay->AddObserver(this); |
| #endif |
| |
| base::AutoLock lock(channel_lock_); |
| // ShutDown() may have already been called, in which case |channel_| is null. |
| if (channel_) |
| channel_->Start(); |
| } |
| |
| void NodeChannel::ShutDown() { |
| #if defined(OS_MACOSX) && !defined(OS_IOS) |
| MachPortRelay* relay = delegate_->GetMachPortRelay(); |
| if (relay) |
| relay->RemoveObserver(this); |
| #endif |
| |
| base::AutoLock lock(channel_lock_); |
| if (channel_) { |
| channel_->ShutDown(); |
| channel_ = nullptr; |
| } |
| } |
| |
| void NodeChannel::LeakHandleOnShutdown() { |
| base::AutoLock lock(channel_lock_); |
| if (channel_) { |
| channel_->LeakHandle(); |
| } |
| } |
| |
| void NodeChannel::NotifyBadMessage(const std::string& error) { |
| if (!process_error_callback_.is_null()) |
| process_error_callback_.Run("Received bad user message: " + error); |
| } |
| |
| void NodeChannel::SetRemoteProcessHandle(base::ProcessHandle process_handle) { |
| DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); |
| base::AutoLock lock(remote_process_handle_lock_); |
| DCHECK_EQ(base::kNullProcessHandle, remote_process_handle_); |
| CHECK_NE(remote_process_handle_, base::GetCurrentProcessHandle()); |
| remote_process_handle_ = process_handle; |
| #if defined(OS_WIN) |
| DCHECK(!scoped_remote_process_handle_.is_valid()); |
| scoped_remote_process_handle_.reset(PlatformHandle(process_handle)); |
| #endif |
| } |
| |
| bool NodeChannel::HasRemoteProcessHandle() { |
| base::AutoLock lock(remote_process_handle_lock_); |
| return remote_process_handle_ != base::kNullProcessHandle; |
| } |
| |
| base::ProcessHandle NodeChannel::CopyRemoteProcessHandle() { |
| base::AutoLock lock(remote_process_handle_lock_); |
| #if defined(OS_WIN) |
| if (remote_process_handle_ != base::kNullProcessHandle) { |
| // Privileged nodes use this to pass their childrens' process handles to the |
| // broker on launch. |
| HANDLE handle = remote_process_handle_; |
| BOOL result = DuplicateHandle( |
| base::GetCurrentProcessHandle(), remote_process_handle_, |
| base::GetCurrentProcessHandle(), &handle, 0, FALSE, |
| DUPLICATE_SAME_ACCESS); |
| DPCHECK(result); |
| return handle; |
| } |
| return base::kNullProcessHandle; |
| #else |
| return remote_process_handle_; |
| #endif |
| } |
| |
| void NodeChannel::SetRemoteNodeName(const ports::NodeName& name) { |
| DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); |
| remote_node_name_ = name; |
| } |
| |
| void NodeChannel::AcceptChild(const ports::NodeName& parent_name, |
| const ports::NodeName& token) { |
| AcceptChildData* data; |
| Channel::MessagePtr message = CreateMessage( |
| MessageType::ACCEPT_CHILD, sizeof(AcceptChildData), 0, &data); |
| data->parent_name = parent_name; |
| data->token = token; |
| WriteChannelMessage(std::move(message)); |
| } |
| |
| void NodeChannel::AcceptParent(const ports::NodeName& token, |
| const ports::NodeName& child_name) { |
| AcceptParentData* data; |
| Channel::MessagePtr message = CreateMessage( |
| MessageType::ACCEPT_PARENT, sizeof(AcceptParentData), 0, &data); |
| data->token = token; |
| data->child_name = child_name; |
| WriteChannelMessage(std::move(message)); |
| } |
| |
| void NodeChannel::AcceptPeer(const ports::NodeName& sender_name, |
| const ports::NodeName& token, |
| const ports::PortName& port_name) { |
| AcceptPeerData* data; |
| Channel::MessagePtr message = |
| CreateMessage(MessageType::ACCEPT_PEER, sizeof(AcceptPeerData), 0, &data); |
| data->token = token; |
| data->peer_name = sender_name; |
| data->port_name = port_name; |
| WriteChannelMessage(std::move(message)); |
| } |
| |
| void NodeChannel::AddBrokerClient(const ports::NodeName& client_name, |
| base::ProcessHandle process_handle) { |
| AddBrokerClientData* data; |
| ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector()); |
| #if defined(OS_WIN) |
| handles->push_back(PlatformHandle(process_handle)); |
| #endif |
| Channel::MessagePtr message = CreateMessage( |
| MessageType::ADD_BROKER_CLIENT, sizeof(AddBrokerClientData), |
| handles->size(), &data); |
| message->SetHandles(std::move(handles)); |
| data->client_name = client_name; |
| #if !defined(OS_WIN) |
| data->process_handle = process_handle; |
| data->padding = 0; |
| #endif |
| WriteChannelMessage(std::move(message)); |
| } |
| |
| void NodeChannel::BrokerClientAdded(const ports::NodeName& client_name, |
| ScopedPlatformHandle broker_channel) { |
| BrokerClientAddedData* data; |
| ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector()); |
| if (broker_channel.is_valid()) |
| handles->push_back(broker_channel.release()); |
| Channel::MessagePtr message = CreateMessage( |
| MessageType::BROKER_CLIENT_ADDED, sizeof(BrokerClientAddedData), |
| handles->size(), &data); |
| message->SetHandles(std::move(handles)); |
| data->client_name = client_name; |
| WriteChannelMessage(std::move(message)); |
| } |
| |
| void NodeChannel::AcceptBrokerClient(const ports::NodeName& broker_name, |
| ScopedPlatformHandle broker_channel) { |
| AcceptBrokerClientData* data; |
| ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector()); |
| if (broker_channel.is_valid()) |
| handles->push_back(broker_channel.release()); |
| Channel::MessagePtr message = CreateMessage( |
| MessageType::ACCEPT_BROKER_CLIENT, sizeof(AcceptBrokerClientData), |
| handles->size(), &data); |
| message->SetHandles(std::move(handles)); |
| data->broker_name = broker_name; |
| WriteChannelMessage(std::move(message)); |
| } |
| |
| void NodeChannel::PortsMessage(Channel::MessagePtr message) { |
| WriteChannelMessage(std::move(message)); |
| } |
| |
| void NodeChannel::RequestPortMerge(const ports::PortName& connector_port_name, |
| const std::string& token) { |
| RequestPortMergeData* data; |
| Channel::MessagePtr message = CreateMessage( |
| MessageType::REQUEST_PORT_MERGE, |
| sizeof(RequestPortMergeData) + token.size(), 0, &data); |
| data->connector_port_name = connector_port_name; |
| memcpy(data + 1, token.data(), token.size()); |
| WriteChannelMessage(std::move(message)); |
| } |
| |
| void NodeChannel::RequestIntroduction(const ports::NodeName& name) { |
| IntroductionData* data; |
| Channel::MessagePtr message = CreateMessage( |
| MessageType::REQUEST_INTRODUCTION, sizeof(IntroductionData), 0, &data); |
| data->name = name; |
| WriteChannelMessage(std::move(message)); |
| } |
| |
| void NodeChannel::Introduce(const ports::NodeName& name, |
| ScopedPlatformHandle channel_handle) { |
| IntroductionData* data; |
| ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector()); |
| if (channel_handle.is_valid()) |
| handles->push_back(channel_handle.release()); |
| Channel::MessagePtr message = CreateMessage( |
| MessageType::INTRODUCE, sizeof(IntroductionData), handles->size(), &data); |
| message->SetHandles(std::move(handles)); |
| data->name = name; |
| WriteChannelMessage(std::move(message)); |
| } |
| |
| void NodeChannel::Broadcast(Channel::MessagePtr message) { |
| DCHECK(!message->has_handles()); |
| void* data; |
| Channel::MessagePtr broadcast_message = CreateMessage( |
| MessageType::BROADCAST, message->data_num_bytes(), 0, &data); |
| memcpy(data, message->data(), message->data_num_bytes()); |
| WriteChannelMessage(std::move(broadcast_message)); |
| } |
| |
| #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) |
| void NodeChannel::RelayPortsMessage(const ports::NodeName& destination, |
| Channel::MessagePtr message) { |
| #if defined(OS_WIN) |
| DCHECK(message->has_handles()); |
| |
| // Note that this is only used on Windows, and on Windows all platform |
| // handles are included in the message data. We blindly copy all the data |
| // here and the relay node (the parent) will duplicate handles as needed. |
| size_t num_bytes = sizeof(RelayPortsMessageData) + message->data_num_bytes(); |
| RelayPortsMessageData* data; |
| Channel::MessagePtr relay_message = CreateMessage( |
| MessageType::RELAY_PORTS_MESSAGE, num_bytes, 0, &data); |
| data->destination = destination; |
| memcpy(data + 1, message->data(), message->data_num_bytes()); |
| |
| // When the handles are duplicated in the parent, the source handles will |
| // be closed. If the parent never receives this message then these handles |
| // will leak, but that means something else has probably broken and the |
| // sending process won't likely be around much longer. |
| ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); |
| handles->clear(); |
| |
| #else |
| DCHECK(message->has_mach_ports()); |
| |
| // On OSX, the handles are extracted from the relayed message and attached to |
| // the wrapper. The broker then takes the handles attached to the wrapper and |
| // moves them back to the relayed message. This is necessary because the |
| // message may contain fds which need to be attached to the outer message so |
| // that they can be transferred to the broker. |
| ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); |
| size_t num_bytes = sizeof(RelayPortsMessageData) + message->data_num_bytes(); |
| RelayPortsMessageData* data; |
| Channel::MessagePtr relay_message = CreateMessage( |
| MessageType::RELAY_PORTS_MESSAGE, num_bytes, handles->size(), &data); |
| data->destination = destination; |
| memcpy(data + 1, message->data(), message->data_num_bytes()); |
| relay_message->SetHandles(std::move(handles)); |
| #endif // defined(OS_WIN) |
| |
| WriteChannelMessage(std::move(relay_message)); |
| } |
| |
| void NodeChannel::PortsMessageFromRelay(const ports::NodeName& source, |
| Channel::MessagePtr message) { |
| size_t num_bytes = sizeof(PortsMessageFromRelayData) + |
| message->payload_size(); |
| PortsMessageFromRelayData* data; |
| Channel::MessagePtr relayed_message = CreateMessage( |
| MessageType::PORTS_MESSAGE_FROM_RELAY, num_bytes, message->num_handles(), |
| &data); |
| data->source = source; |
| if (message->payload_size()) |
| memcpy(data + 1, message->payload(), message->payload_size()); |
| relayed_message->SetHandles(message->TakeHandles()); |
| WriteChannelMessage(std::move(relayed_message)); |
| } |
| #endif // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) |
| |
| NodeChannel::NodeChannel(Delegate* delegate, |
| ConnectionParams connection_params, |
| scoped_refptr<base::TaskRunner> io_task_runner, |
| const ProcessErrorCallback& process_error_callback) |
| : delegate_(delegate), |
| io_task_runner_(io_task_runner), |
| process_error_callback_(process_error_callback) |
| #if !defined(OS_NACL_SFI) |
| , |
| channel_( |
| Channel::Create(this, std::move(connection_params), io_task_runner_)) |
| #endif |
| { |
| } |
| |
| NodeChannel::~NodeChannel() { |
| ShutDown(); |
| } |
| |
| void NodeChannel::OnChannelMessage(const void* payload, |
| size_t payload_size, |
| ScopedPlatformHandleVectorPtr handles) { |
| DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); |
| |
| RequestContext request_context(RequestContext::Source::SYSTEM); |
| |
| // Ensure this NodeChannel stays alive through the extent of this method. The |
| // delegate may have the only other reference to this object and it may choose |
| // to drop it here in response to, e.g., a malformed message. |
| scoped_refptr<NodeChannel> keepalive = this; |
| |
| #if defined(OS_WIN) |
| // If we receive handles from a known process, rewrite them to our own |
| // process. This can occur when a privileged node receives handles directly |
| // from a privileged descendant. |
| { |
| base::AutoLock lock(remote_process_handle_lock_); |
| if (handles && remote_process_handle_ != base::kNullProcessHandle) { |
| // Note that we explicitly mark the handles as being owned by the sending |
| // process before rewriting them, in order to accommodate RewriteHandles' |
| // internal sanity checks. |
| for (auto& handle : *handles) |
| handle.owning_process = remote_process_handle_; |
| if (!Channel::Message::RewriteHandles(remote_process_handle_, |
| base::GetCurrentProcessHandle(), |
| handles.get())) { |
| DLOG(ERROR) << "Received one or more invalid handles."; |
| } |
| } else if (handles) { |
| // Handles received by an unknown process must already be owned by us. |
| for (auto& handle : *handles) |
| handle.owning_process = base::GetCurrentProcessHandle(); |
| } |
| } |
| #elif defined(OS_MACOSX) && !defined(OS_IOS) |
| // If we're not the root, receive any mach ports from the message. If we're |
| // the root, the only message containing mach ports should be a |
| // RELAY_PORTS_MESSAGE. |
| { |
| MachPortRelay* relay = delegate_->GetMachPortRelay(); |
| if (handles && !relay) { |
| if (!MachPortRelay::ReceivePorts(handles.get())) { |
| LOG(ERROR) << "Error receiving mach ports."; |
| } |
| } |
| } |
| #endif // defined(OS_WIN) |
| |
| |
| if (payload_size <= sizeof(Header)) { |
| delegate_->OnChannelError(remote_node_name_, this); |
| return; |
| } |
| |
| const Header* header = static_cast<const Header*>(payload); |
| switch (header->type) { |
| case MessageType::ACCEPT_CHILD: { |
| const AcceptChildData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| delegate_->OnAcceptChild(remote_node_name_, data->parent_name, |
| data->token); |
| return; |
| } |
| break; |
| } |
| |
| case MessageType::ACCEPT_PARENT: { |
| const AcceptParentData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| delegate_->OnAcceptParent(remote_node_name_, data->token, |
| data->child_name); |
| return; |
| } |
| break; |
| } |
| |
| case MessageType::ADD_BROKER_CLIENT: { |
| const AddBrokerClientData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| ScopedPlatformHandle process_handle; |
| #if defined(OS_WIN) |
| if (!handles || handles->size() != 1) { |
| DLOG(ERROR) << "Dropping invalid AddBrokerClient message."; |
| break; |
| } |
| process_handle = ScopedPlatformHandle(handles->at(0)); |
| handles->clear(); |
| delegate_->OnAddBrokerClient(remote_node_name_, data->client_name, |
| process_handle.release().handle); |
| #else |
| if (handles && handles->size() != 0) { |
| DLOG(ERROR) << "Dropping invalid AddBrokerClient message."; |
| break; |
| } |
| delegate_->OnAddBrokerClient(remote_node_name_, data->client_name, |
| data->process_handle); |
| #endif |
| return; |
| } |
| break; |
| } |
| |
| case MessageType::BROKER_CLIENT_ADDED: { |
| const BrokerClientAddedData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| ScopedPlatformHandle broker_channel; |
| if (!handles || handles->size() != 1) { |
| DLOG(ERROR) << "Dropping invalid BrokerClientAdded message."; |
| break; |
| } |
| broker_channel = ScopedPlatformHandle(handles->at(0)); |
| handles->clear(); |
| delegate_->OnBrokerClientAdded(remote_node_name_, data->client_name, |
| std::move(broker_channel)); |
| return; |
| } |
| break; |
| } |
| |
| case MessageType::ACCEPT_BROKER_CLIENT: { |
| const AcceptBrokerClientData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| ScopedPlatformHandle broker_channel; |
| if (handles && handles->size() > 1) { |
| DLOG(ERROR) << "Dropping invalid AcceptBrokerClient message."; |
| break; |
| } |
| if (handles && handles->size() == 1) { |
| broker_channel = ScopedPlatformHandle(handles->at(0)); |
| handles->clear(); |
| } |
| delegate_->OnAcceptBrokerClient(remote_node_name_, data->broker_name, |
| std::move(broker_channel)); |
| return; |
| } |
| break; |
| } |
| |
| case MessageType::PORTS_MESSAGE: { |
| size_t num_handles = handles ? handles->size() : 0; |
| Channel::MessagePtr message( |
| new Channel::Message(payload_size, num_handles)); |
| message->SetHandles(std::move(handles)); |
| memcpy(message->mutable_payload(), payload, payload_size); |
| delegate_->OnPortsMessage(remote_node_name_, std::move(message)); |
| return; |
| } |
| |
| case MessageType::REQUEST_PORT_MERGE: { |
| const RequestPortMergeData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| // Don't accept an empty token. |
| size_t token_size = payload_size - sizeof(*data) - sizeof(Header); |
| if (token_size == 0) |
| break; |
| std::string token(reinterpret_cast<const char*>(data + 1), token_size); |
| delegate_->OnRequestPortMerge(remote_node_name_, |
| data->connector_port_name, token); |
| return; |
| } |
| break; |
| } |
| |
| case MessageType::REQUEST_INTRODUCTION: { |
| const IntroductionData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| delegate_->OnRequestIntroduction(remote_node_name_, data->name); |
| return; |
| } |
| break; |
| } |
| |
| case MessageType::INTRODUCE: { |
| const IntroductionData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| if (handles && handles->size() > 1) { |
| DLOG(ERROR) << "Dropping invalid introduction message."; |
| break; |
| } |
| ScopedPlatformHandle channel_handle; |
| if (handles && handles->size() == 1) { |
| channel_handle = ScopedPlatformHandle(handles->at(0)); |
| handles->clear(); |
| } |
| delegate_->OnIntroduce(remote_node_name_, data->name, |
| std::move(channel_handle)); |
| return; |
| } |
| break; |
| } |
| |
| #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) |
| case MessageType::RELAY_PORTS_MESSAGE: { |
| base::ProcessHandle from_process; |
| { |
| base::AutoLock lock(remote_process_handle_lock_); |
| from_process = remote_process_handle_; |
| } |
| const RelayPortsMessageData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| // Don't try to relay an empty message. |
| if (payload_size <= sizeof(Header) + sizeof(RelayPortsMessageData)) |
| break; |
| |
| const void* message_start = data + 1; |
| Channel::MessagePtr message = Channel::Message::Deserialize( |
| message_start, payload_size - sizeof(Header) - sizeof(*data)); |
| if (!message) { |
| DLOG(ERROR) << "Dropping invalid relay message."; |
| break; |
| } |
| #if defined(OS_MACOSX) && !defined(OS_IOS) |
| message->SetHandles(std::move(handles)); |
| MachPortRelay* relay = delegate_->GetMachPortRelay(); |
| if (!relay) { |
| LOG(ERROR) << "Receiving mach ports without a port relay from " |
| << remote_node_name_ << ". Dropping message."; |
| break; |
| } |
| { |
| base::AutoLock lock(pending_mach_messages_lock_); |
| if (relay->port_provider()->TaskForPid(from_process) == |
| MACH_PORT_NULL) { |
| pending_relay_messages_.push( |
| std::make_pair(data->destination, std::move(message))); |
| break; |
| } |
| } |
| #endif |
| delegate_->OnRelayPortsMessage(remote_node_name_, from_process, |
| data->destination, std::move(message)); |
| return; |
| } |
| break; |
| } |
| #endif |
| |
| case MessageType::BROADCAST: { |
| if (payload_size <= sizeof(Header)) |
| break; |
| const void* data = static_cast<const void*>( |
| reinterpret_cast<const Header*>(payload) + 1); |
| Channel::MessagePtr message = |
| Channel::Message::Deserialize(data, payload_size - sizeof(Header)); |
| if (!message || message->has_handles()) { |
| DLOG(ERROR) << "Dropping invalid broadcast message."; |
| break; |
| } |
| delegate_->OnBroadcast(remote_node_name_, std::move(message)); |
| return; |
| } |
| |
| #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) |
| case MessageType::PORTS_MESSAGE_FROM_RELAY: |
| const PortsMessageFromRelayData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| size_t num_bytes = payload_size - sizeof(*data); |
| if (num_bytes < sizeof(Header)) |
| break; |
| num_bytes -= sizeof(Header); |
| |
| size_t num_handles = handles ? handles->size() : 0; |
| Channel::MessagePtr message( |
| new Channel::Message(num_bytes, num_handles)); |
| message->SetHandles(std::move(handles)); |
| if (num_bytes) |
| memcpy(message->mutable_payload(), data + 1, num_bytes); |
| delegate_->OnPortsMessageFromRelay( |
| remote_node_name_, data->source, std::move(message)); |
| return; |
| } |
| break; |
| |
| #endif // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) |
| |
| case MessageType::ACCEPT_PEER: { |
| const AcceptPeerData* data; |
| if (GetMessagePayload(payload, payload_size, &data)) { |
| delegate_->OnAcceptPeer(remote_node_name_, data->token, data->peer_name, |
| data->port_name); |
| return; |
| } |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| DLOG(ERROR) << "Received invalid message. Closing channel."; |
| delegate_->OnChannelError(remote_node_name_, this); |
| } |
| |
| void NodeChannel::OnChannelError() { |
| DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); |
| |
| RequestContext request_context(RequestContext::Source::SYSTEM); |
| |
| ShutDown(); |
| // |OnChannelError()| may cause |this| to be destroyed, but still need access |
| // to the name name after that destruction. So may a copy of |
| // |remote_node_name_| so it can be used if |this| becomes destroyed. |
| ports::NodeName node_name = remote_node_name_; |
| delegate_->OnChannelError(node_name, this); |
| } |
| |
| #if defined(OS_MACOSX) && !defined(OS_IOS) |
| void NodeChannel::OnProcessReady(base::ProcessHandle process) { |
| io_task_runner_->PostTask(FROM_HERE, base::Bind( |
| &NodeChannel::ProcessPendingMessagesWithMachPorts, this)); |
| } |
| |
| void NodeChannel::ProcessPendingMessagesWithMachPorts() { |
| MachPortRelay* relay = delegate_->GetMachPortRelay(); |
| DCHECK(relay); |
| |
| base::ProcessHandle remote_process_handle; |
| { |
| base::AutoLock lock(remote_process_handle_lock_); |
| remote_process_handle = remote_process_handle_; |
| } |
| PendingMessageQueue pending_writes; |
| PendingRelayMessageQueue pending_relays; |
| { |
| base::AutoLock lock(pending_mach_messages_lock_); |
| pending_writes.swap(pending_write_messages_); |
| pending_relays.swap(pending_relay_messages_); |
| } |
| |
| while (!pending_writes.empty()) { |
| Channel::MessagePtr message = std::move(pending_writes.front()); |
| pending_writes.pop(); |
| if (!relay->SendPortsToProcess(message.get(), remote_process_handle)) { |
| LOG(ERROR) << "Error on sending mach ports. Remote process is likely " |
| << "gone. Dropping message."; |
| return; |
| } |
| |
| base::AutoLock lock(channel_lock_); |
| if (!channel_) { |
| DLOG(ERROR) << "Dropping message on closed channel."; |
| break; |
| } else { |
| channel_->Write(std::move(message)); |
| } |
| } |
| |
| // Ensure this NodeChannel stays alive while flushing relay messages. |
| scoped_refptr<NodeChannel> keepalive = this; |
| |
| while (!pending_relays.empty()) { |
| ports::NodeName destination = pending_relays.front().first; |
| Channel::MessagePtr message = std::move(pending_relays.front().second); |
| pending_relays.pop(); |
| delegate_->OnRelayPortsMessage(remote_node_name_, remote_process_handle, |
| destination, std::move(message)); |
| } |
| } |
| #endif |
| |
| void NodeChannel::WriteChannelMessage(Channel::MessagePtr message) { |
| #if defined(OS_WIN) |
| // Map handles to the destination process. Note: only messages from a |
| // privileged node should contain handles on Windows. If an unprivileged |
| // node needs to send handles, it should do so via RelayPortsMessage which |
| // stashes the handles in the message in such a way that they go undetected |
| // here (they'll be unpacked and duplicated by a privileged parent.) |
| |
| if (message->has_handles()) { |
| base::ProcessHandle remote_process_handle; |
| { |
| base::AutoLock lock(remote_process_handle_lock_); |
| remote_process_handle = remote_process_handle_; |
| } |
| |
| // Rewrite outgoing handles if we have a handle to the destination process. |
| if (remote_process_handle != base::kNullProcessHandle) { |
| ScopedPlatformHandleVectorPtr handles = message->TakeHandles(); |
| if (!Channel::Message::RewriteHandles(base::GetCurrentProcessHandle(), |
| remote_process_handle, |
| handles.get())) { |
| DLOG(ERROR) << "Failed to duplicate one or more outgoing handles."; |
| } |
| message->SetHandles(std::move(handles)); |
| } |
| } |
| #elif defined(OS_MACOSX) && !defined(OS_IOS) |
| // On OSX, we need to transfer mach ports to the destination process before |
| // transferring the message itself. |
| if (message->has_mach_ports()) { |
| MachPortRelay* relay = delegate_->GetMachPortRelay(); |
| if (relay) { |
| base::ProcessHandle remote_process_handle; |
| { |
| base::AutoLock lock(remote_process_handle_lock_); |
| // Expect that the receiving node is a child. |
| DCHECK(remote_process_handle_ != base::kNullProcessHandle); |
| remote_process_handle = remote_process_handle_; |
| } |
| { |
| base::AutoLock lock(pending_mach_messages_lock_); |
| if (relay->port_provider()->TaskForPid(remote_process_handle) == |
| MACH_PORT_NULL) { |
| // It is also possible for TaskForPid() to return MACH_PORT_NULL when |
| // the process has started, then died. In that case, the queued |
| // message will never be processed. But that's fine since we're about |
| // to die anyway. |
| pending_write_messages_.push(std::move(message)); |
| return; |
| } |
| } |
| |
| if (!relay->SendPortsToProcess(message.get(), remote_process_handle)) { |
| LOG(ERROR) << "Error on sending mach ports. Remote process is likely " |
| << "gone. Dropping message."; |
| return; |
| } |
| } |
| } |
| #endif |
| |
| base::AutoLock lock(channel_lock_); |
| if (!channel_) |
| DLOG(ERROR) << "Dropping message on closed channel."; |
| else |
| channel_->Write(std::move(message)); |
| } |
| |
| } // namespace edk |
| } // namespace mojo |