You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
371 lines
14 KiB
371 lines
14 KiB
// 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.
|
|
|
|
#ifndef MOJO_CORE_CHANNEL_H_
|
|
#define MOJO_CORE_CHANNEL_H_
|
|
|
|
#include <vector>
|
|
|
|
#include "base/macros.h"
|
|
#include "base/memory/ref_counted.h"
|
|
#include "base/process/process_handle.h"
|
|
#include "base/task_runner.h"
|
|
#include "build/build_config.h"
|
|
#include "mojo/core/connection_params.h"
|
|
#include "mojo/core/platform_handle_in_transit.h"
|
|
#include "mojo/core/scoped_process_handle.h"
|
|
#include "mojo/public/cpp/platform/platform_handle.h"
|
|
|
|
namespace mojo {
|
|
namespace core {
|
|
|
|
const size_t kChannelMessageAlignment = 8;
|
|
|
|
constexpr bool IsAlignedForChannelMessage(size_t n) {
|
|
return n % kChannelMessageAlignment == 0;
|
|
}
|
|
|
|
// Channel provides a thread-safe interface to read and write arbitrary
|
|
// delimited messages over an underlying I/O channel, optionally transferring
|
|
// one or more platform handles in the process.
|
|
class MOJO_SYSTEM_IMPL_EXPORT Channel
|
|
: public base::RefCountedThreadSafe<Channel> {
|
|
public:
|
|
struct Message;
|
|
|
|
using MessagePtr = std::unique_ptr<Message>;
|
|
|
|
// A message to be written to a channel.
|
|
struct MOJO_SYSTEM_IMPL_EXPORT Message {
|
|
enum class MessageType : uint16_t {
|
|
// An old format normal message, that uses the LegacyHeader.
|
|
// Only used on Android and ChromeOS.
|
|
// TODO(https://crbug.com/695645): remove legacy support when Arc++ has
|
|
// updated to Mojo with normal versioned messages.
|
|
NORMAL_LEGACY = 0,
|
|
#if defined(OS_MACOSX)
|
|
// A control message containing handles to echo back.
|
|
HANDLES_SENT,
|
|
// A control message containing handles that can now be closed.
|
|
HANDLES_SENT_ACK,
|
|
#endif
|
|
// A normal message that uses Header and can contain extra header values.
|
|
NORMAL,
|
|
};
|
|
|
|
#pragma pack(push, 1)
|
|
// Old message wire format for ChromeOS and Android, used by NORMAL_LEGACY
|
|
// messages.
|
|
struct LegacyHeader {
|
|
// Message size in bytes, including the header.
|
|
uint32_t num_bytes;
|
|
|
|
// Number of attached handles.
|
|
uint16_t num_handles;
|
|
|
|
MessageType message_type;
|
|
};
|
|
|
|
// Header used by NORMAL messages.
|
|
// To preserve backward compatibility with LegacyHeader, the num_bytes and
|
|
// message_type field must be at the same offset as in LegacyHeader.
|
|
struct Header {
|
|
// Message size in bytes, including the header.
|
|
uint32_t num_bytes;
|
|
|
|
// Total size of header, including extra header data (i.e. HANDLEs on
|
|
// windows).
|
|
uint16_t num_header_bytes;
|
|
|
|
MessageType message_type;
|
|
|
|
// Number of attached handles. May be less than the reserved handle
|
|
// storage size in this message on platforms that serialise handles as
|
|
// data (i.e. HANDLEs on Windows, Mach ports on OSX).
|
|
uint16_t num_handles;
|
|
|
|
char padding[6];
|
|
};
|
|
|
|
#if defined(OS_MACOSX) && !defined(OS_IOS)
|
|
struct MachPortsEntry {
|
|
// Index of Mach port in the original vector of PlatformHandleInTransits.
|
|
uint16_t index;
|
|
|
|
// Mach port name.
|
|
uint32_t mach_port;
|
|
static_assert(sizeof(mach_port_t) <= sizeof(uint32_t),
|
|
"mach_port_t must be no larger than uint32_t");
|
|
};
|
|
static_assert(sizeof(MachPortsEntry) == 6,
|
|
"sizeof(MachPortsEntry) must be 6 bytes");
|
|
|
|
// Structure of the extra header field when present on OSX.
|
|
struct MachPortsExtraHeader {
|
|
// Actual number of Mach ports encoded in the extra header.
|
|
uint16_t num_ports;
|
|
|
|
// Array of encoded Mach ports. If |num_ports| > 0, |entries[0]| through
|
|
// to |entries[num_ports-1]| inclusive are valid.
|
|
MachPortsEntry entries[0];
|
|
};
|
|
static_assert(sizeof(MachPortsExtraHeader) == 2,
|
|
"sizeof(MachPortsExtraHeader) must be 2 bytes");
|
|
#elif defined(OS_FUCHSIA)
|
|
struct HandleInfoEntry {
|
|
// The FDIO type associated with one or more handles, or zero for handles
|
|
// that do not belong to FDIO.
|
|
uint8_t type;
|
|
// Zero for non-FDIO handles, otherwise the number of handles to consume
|
|
// to generate an FDIO file-descriptor wrapper.
|
|
uint8_t count;
|
|
};
|
|
#elif defined(OS_WIN)
|
|
struct HandleEntry {
|
|
// The windows HANDLE. HANDLEs are guaranteed to fit inside 32-bits.
|
|
// See: https://msdn.microsoft.com/en-us/library/aa384203(VS.85).aspx
|
|
uint32_t handle;
|
|
};
|
|
static_assert(sizeof(HandleEntry) == 4,
|
|
"sizeof(HandleEntry) must be 4 bytes");
|
|
#endif
|
|
#pragma pack(pop)
|
|
|
|
// Allocates and owns a buffer for message data with enough capacity for
|
|
// |payload_size| bytes plus a header, plus |max_handles| platform handles.
|
|
Message(size_t payload_size, size_t max_handles);
|
|
Message(size_t payload_size, size_t max_handles, MessageType message_type);
|
|
Message(size_t capacity, size_t payload_size, size_t max_handles);
|
|
Message(size_t capacity,
|
|
size_t max_handles,
|
|
size_t payload_size,
|
|
MessageType message_type);
|
|
~Message();
|
|
|
|
// Constructs a Message from serialized message data, optionally coming from
|
|
// a known remote process.
|
|
static MessagePtr Deserialize(
|
|
const void* data,
|
|
size_t data_num_bytes,
|
|
base::ProcessHandle from_process = base::kNullProcessHandle);
|
|
|
|
const void* data() const { return data_; }
|
|
size_t data_num_bytes() const { return size_; }
|
|
|
|
// The current capacity of the message buffer, not counting internal header
|
|
// data.
|
|
size_t capacity() const;
|
|
|
|
// Extends the portion of the total message capacity which contains
|
|
// meaningful payload data. Storage capacity which falls outside of this
|
|
// range is not transmitted when the message is sent.
|
|
//
|
|
// If the message's current capacity is not large enough to accommodate the
|
|
// new payload size, it will be reallocated accordingly.
|
|
void ExtendPayload(size_t new_payload_size);
|
|
|
|
const void* extra_header() const;
|
|
void* mutable_extra_header();
|
|
size_t extra_header_size() const;
|
|
|
|
void* mutable_payload();
|
|
const void* payload() const;
|
|
size_t payload_size() const;
|
|
|
|
size_t num_handles() const;
|
|
bool has_handles() const;
|
|
#if defined(OS_MACOSX) && !defined(OS_IOS)
|
|
bool has_mach_ports() const;
|
|
#endif
|
|
|
|
bool is_legacy_message() const;
|
|
LegacyHeader* legacy_header() const;
|
|
Header* header() const;
|
|
|
|
// Note: SetHandles() and TakeHandles() invalidate any previous value of
|
|
// handles().
|
|
void SetHandles(std::vector<PlatformHandle> new_handles);
|
|
void SetHandles(std::vector<PlatformHandleInTransit> new_handles);
|
|
std::vector<PlatformHandleInTransit> TakeHandles();
|
|
// Version of TakeHandles that returns a vector of platform handles suitable
|
|
// for transfer over an underlying OS mechanism. i.e. file descriptors over
|
|
// a unix domain socket. Any handle that cannot be transferred this way,
|
|
// such as Mach ports, will be removed.
|
|
std::vector<PlatformHandleInTransit> TakeHandlesForTransport();
|
|
|
|
void SetVersionForTest(uint16_t version_number);
|
|
|
|
private:
|
|
// The message data buffer.
|
|
char* data_ = nullptr;
|
|
|
|
// The capacity of the buffer at |data_|.
|
|
size_t capacity_ = 0;
|
|
|
|
// The size of the message. This is the portion of |data_| that should
|
|
// be transmitted if the message is written to a channel. Includes all
|
|
// headers and user payload.
|
|
size_t size_ = 0;
|
|
|
|
// Maximum number of handles which may be attached to this message.
|
|
size_t max_handles_ = 0;
|
|
|
|
std::vector<PlatformHandleInTransit> handle_vector_;
|
|
|
|
#if defined(OS_WIN)
|
|
// On Windows, handles are serialised into the extra header section.
|
|
HandleEntry* handles_ = nullptr;
|
|
#elif defined(OS_MACOSX) && !defined(OS_IOS)
|
|
// On OSX, handles are serialised into the extra header section.
|
|
MachPortsExtraHeader* mach_ports_header_ = nullptr;
|
|
#endif
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Message);
|
|
};
|
|
|
|
// Error types which may be reported by a Channel instance to its delegate.
|
|
enum class Error {
|
|
// The remote end of the channel has been closed, either explicitly or
|
|
// because the process which hosted it is gone.
|
|
kDisconnected,
|
|
|
|
// For connection-oriented channels (e.g. named pipes), an unexpected error
|
|
// occurred during channel connection.
|
|
kConnectionFailed,
|
|
|
|
// Some incoming data failed validation, implying either a buggy or
|
|
// compromised sender.
|
|
kReceivedMalformedData,
|
|
};
|
|
|
|
// Delegate methods are called from the I/O task runner with which the Channel
|
|
// was created (see Channel::Create).
|
|
class Delegate {
|
|
public:
|
|
virtual ~Delegate() {}
|
|
|
|
// Notify of a received message. |payload| is not owned and must not be
|
|
// retained; it will be null if |payload_size| is 0. |handles| are
|
|
// transferred to the callee.
|
|
virtual void OnChannelMessage(const void* payload,
|
|
size_t payload_size,
|
|
std::vector<PlatformHandle> handles) = 0;
|
|
|
|
// Notify that an error has occured and the Channel will cease operation.
|
|
virtual void OnChannelError(Error error) = 0;
|
|
};
|
|
|
|
// Creates a new Channel around a |platform_handle|, taking ownership of the
|
|
// handle. All I/O on the handle will be performed on |io_task_runner|.
|
|
// Note that ShutDown() MUST be called on the Channel some time before
|
|
// |delegate| is destroyed.
|
|
static scoped_refptr<Channel> Create(
|
|
Delegate* delegate,
|
|
ConnectionParams connection_params,
|
|
scoped_refptr<base::TaskRunner> io_task_runner);
|
|
|
|
// Request that the channel be shut down. This should always be called before
|
|
// releasing the last reference to a Channel to ensure that it's cleaned up
|
|
// on its I/O task runner's thread.
|
|
//
|
|
// Delegate methods will no longer be invoked after this call.
|
|
void ShutDown();
|
|
|
|
// Sets the process handle of the remote endpoint to which this Channel is
|
|
// connected. If called at all, must be called only once, and before Start().
|
|
void set_remote_process(ScopedProcessHandle remote_process) {
|
|
DCHECK(!remote_process_.is_valid());
|
|
remote_process_ = std::move(remote_process);
|
|
}
|
|
const ScopedProcessHandle& remote_process() const { return remote_process_; }
|
|
|
|
// Begin processing I/O events. Delegate methods must only be invoked after
|
|
// this call.
|
|
virtual void Start() = 0;
|
|
|
|
// Stop processing I/O events.
|
|
virtual void ShutDownImpl() = 0;
|
|
|
|
// Queues an outgoing message on the Channel. This message will either
|
|
// eventually be written or will fail to write and trigger
|
|
// Delegate::OnChannelError.
|
|
virtual void Write(MessagePtr message) = 0;
|
|
|
|
// Causes the platform handle to leak when this channel is shut down instead
|
|
// of closing it.
|
|
virtual void LeakHandle() = 0;
|
|
|
|
protected:
|
|
explicit Channel(Delegate* delegate);
|
|
virtual ~Channel();
|
|
|
|
Delegate* delegate() const { return delegate_; }
|
|
|
|
// Called by the implementation when it wants somewhere to stick data.
|
|
// |*buffer_capacity| may be set by the caller to indicate the desired buffer
|
|
// size. If 0, a sane default size will be used instead.
|
|
//
|
|
// Returns the address of a buffer which can be written to, and indicates its
|
|
// actual capacity in |*buffer_capacity|.
|
|
char* GetReadBuffer(size_t* buffer_capacity);
|
|
|
|
// Called by the implementation when new data is available in the read
|
|
// buffer. Returns false to indicate an error. Upon success,
|
|
// |*next_read_size_hint| will be set to a recommended size for the next
|
|
// read done by the implementation.
|
|
bool OnReadComplete(size_t bytes_read, size_t* next_read_size_hint);
|
|
|
|
// Called by the implementation when something goes horribly wrong. It is NOT
|
|
// OK to call this synchronously from any public interface methods.
|
|
void OnError(Error error);
|
|
|
|
// Retrieves the set of platform handles read for a given message.
|
|
// |extra_header| and |extra_header_size| correspond to the extra header data.
|
|
// Depending on the Channel implementation, this body may encode platform
|
|
// handles, or handles may be stored and managed elsewhere by the
|
|
// implementation.
|
|
//
|
|
// Returns |false| on unrecoverable error (i.e. the Channel should be closed).
|
|
// Returns |true| otherwise. Note that it is possible on some platforms for an
|
|
// insufficient number of handles to be available when this call is made, but
|
|
// this is not necessarily an error condition. In such cases this returns
|
|
// |true| but |*handles| will also be reset to null.
|
|
//
|
|
// If the implementation sets |*deferred| to |true|, it assumes responsibility
|
|
// for dispatching the message eventually. It must copy |payload| to retain
|
|
// it for later transmission.
|
|
virtual bool GetReadPlatformHandles(const void* payload,
|
|
size_t payload_size,
|
|
size_t num_handles,
|
|
const void* extra_header,
|
|
size_t extra_header_size,
|
|
std::vector<PlatformHandle>* handles,
|
|
bool* deferred) = 0;
|
|
|
|
// Handles a received control message. Returns |true| if the message is
|
|
// accepted, or |false| otherwise.
|
|
virtual bool OnControlMessage(Message::MessageType message_type,
|
|
const void* payload,
|
|
size_t payload_size,
|
|
std::vector<PlatformHandle> handles);
|
|
|
|
private:
|
|
friend class base::RefCountedThreadSafe<Channel>;
|
|
|
|
class ReadBuffer;
|
|
|
|
Delegate* delegate_;
|
|
const std::unique_ptr<ReadBuffer> read_buffer_;
|
|
|
|
// Handle to the process on the other end of this Channel, iff known.
|
|
ScopedProcessHandle remote_process_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Channel);
|
|
};
|
|
|
|
} // namespace core
|
|
} // namespace mojo
|
|
|
|
#endif // MOJO_CORE_CHANNEL_H_
|