Commit 60f9ae4d authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

Introduce public PlatformChannel and NamedPlatformChannel

These are cleaned-up versions of the EDK's PlatformChannelPair and
NamedPlatformChannelPair, which will be removed but remain untouched for
the moment. Most of the logic is duplicated verbatim, modulo cleanup.

For test coverage, since actual I/O operations are not being ported to
the public library, the new types are used in place of EDK types within
the invitation API tests.

Bug: 844764
Change-Id: Ieeea396370e37d4fa3a0e9a65b6f9b71855b85fc
Reviewed-on: https://chromium-review.googlesource.com/1068652
Commit-Queue: Ken Rockot <rockot@chromium.org>
Reviewed-by: default avatarJay Civelli <jcivelli@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561624}
parent 37a14eea
...@@ -14,11 +14,11 @@ ...@@ -14,11 +14,11 @@
#include "base/path_service.h" #include "base/path_service.h"
#include "base/test/multiprocess_test.h" #include "base/test/multiprocess_test.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "mojo/edk/embedder/named_platform_channel_pair.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/embedder/platform_handle_utils.h"
#include "mojo/edk/test/mojo_test_base.h" #include "mojo/edk/test/mojo_test_base.h"
#include "mojo/public/c/system/invitation.h" #include "mojo/public/c/system/invitation.h"
#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/platform_handle.h"
namespace mojo { namespace mojo {
namespace edk { namespace edk {
...@@ -93,11 +93,13 @@ TEST_F(InvitationTest, InvalidArguments) { ...@@ -93,11 +93,13 @@ TEST_F(InvitationTest, InvalidArguments) {
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoExtractMessagePipeFromInvitation( EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoExtractMessagePipeFromInvitation(
invitation, 0, nullptr, nullptr)); invitation, 0, nullptr, nullptr));
PlatformChannelPair channel; PlatformChannel channel;
MojoPlatformHandle endpoint_handle; MojoPlatformHandle endpoint_handle;
endpoint_handle.struct_size = sizeof(endpoint_handle); endpoint_handle.struct_size = sizeof(endpoint_handle);
EXPECT_EQ(MOJO_RESULT_OK, ScopedInternalPlatformHandleToMojoPlatformHandle( PlatformHandleToMojoPlatformHandle(
channel.PassServerHandle(), &endpoint_handle)); channel.TakeLocalEndpoint().TakePlatformHandle(), &endpoint_handle);
ASSERT_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID);
MojoInvitationTransportEndpoint valid_endpoint; MojoInvitationTransportEndpoint valid_endpoint;
valid_endpoint.struct_size = sizeof(valid_endpoint); valid_endpoint.struct_size = sizeof(valid_endpoint);
valid_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL; valid_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL;
...@@ -235,44 +237,45 @@ base::Process InvitationTest::LaunchChildTestClient( ...@@ -235,44 +237,45 @@ base::Process InvitationTest::LaunchChildTestClient(
base::GetMultiProcessTestChildBaseCommandLine()); base::GetMultiProcessTestChildBaseCommandLine());
base::LaunchOptions launch_options; base::LaunchOptions launch_options;
base::Optional<PlatformChannelPair> channel; base::Optional<PlatformChannel> channel;
base::Optional<NamedPlatformChannelPair> named_channel; base::Optional<NamedPlatformChannel> named_channel;
ScopedInternalPlatformHandle server_handle; PlatformHandle local_endpoint_handle;
if (transport_type == TransportType::kChannel) { if (transport_type == TransportType::kChannel) {
channel.emplace(); channel.emplace();
#if defined(OS_FUCHSIA) #if defined(OS_FUCHSIA)
channel->PrepareToPassClientHandleToChildProcess( channel->PrepareToPassRemoteEndpoint(&launch_options.handles_to_transfer,
&command_line, &launch_options.handles_to_transfer); &command_line);
#elif defined(OS_POSIX) #elif defined(OS_POSIX)
channel->PrepareToPassClientHandleToChildProcess( channel->PrepareToPassRemoteEndpoint(&launch_options.fds_to_remap,
&command_line, &launch_options.fds_to_remap); &command_line);
#elif defined(OS_WIN) #elif defined(OS_WIN)
launch_options.start_hidden = true; launch_options.start_hidden = true;
channel->PrepareToPassClientHandleToChildProcess( channel->PrepareToPassRemoteEndpoint(&launch_options.handles_to_inherit,
&command_line, &launch_options.handles_to_inherit); &command_line);
#else #else
#error "Platform not yet supported." #error "Platform not yet supported."
#endif #endif
server_handle = channel->PassServerHandle(); local_endpoint_handle = channel->TakeLocalEndpoint().TakePlatformHandle();
} else { } else {
#if defined(OS_FUCHSIA) #if defined(OS_FUCHSIA)
NOTREACHED() << "Named pipe support does not exist for Mojo on Fuchsia."; NOTREACHED() << "Named pipe support does not exist for Mojo on Fuchsia.";
#else #else
NamedPlatformChannelPair::Options named_channel_options; NamedPlatformChannel::Options named_channel_options;
#if !defined(OS_WIN) #if !defined(OS_WIN)
CHECK(base::PathService::Get(base::DIR_TEMP, CHECK(base::PathService::Get(base::DIR_TEMP,
&named_channel_options.socket_dir)); &named_channel_options.socket_dir));
#endif #endif
named_channel.emplace(named_channel_options); named_channel.emplace(named_channel_options);
named_channel->PrepareToPassClientHandleToChildProcess(&command_line); named_channel->PassServerNameOnCommandLine(&command_line);
server_handle = named_channel->PassServerHandle(); local_endpoint_handle =
named_channel->TakeServerEndpoint().TakePlatformHandle();
#endif #endif
} }
MojoPlatformHandle endpoint_handle; MojoPlatformHandle endpoint_handle;
endpoint_handle.struct_size = sizeof(endpoint_handle); PlatformHandleToMojoPlatformHandle(std::move(local_endpoint_handle),
CHECK_EQ(MOJO_RESULT_OK, ScopedInternalPlatformHandleToMojoPlatformHandle( &endpoint_handle);
std::move(server_handle), &endpoint_handle)); CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID);
MojoHandle invitation; MojoHandle invitation;
CHECK_EQ(MOJO_RESULT_OK, MojoCreateInvitation(nullptr, &invitation)); CHECK_EQ(MOJO_RESULT_OK, MojoCreateInvitation(nullptr, &invitation));
...@@ -285,7 +288,7 @@ base::Process InvitationTest::LaunchChildTestClient( ...@@ -285,7 +288,7 @@ base::Process InvitationTest::LaunchChildTestClient(
base::Process child_process = base::SpawnMultiProcessTestChild( base::Process child_process = base::SpawnMultiProcessTestChild(
test_client_name, command_line, launch_options); test_client_name, command_line, launch_options);
if (channel) if (channel)
channel->ChildProcessLaunched(); channel->RemoteProcessLaunched();
MojoPlatformProcessHandle process_handle; MojoPlatformProcessHandle process_handle;
process_handle.struct_size = sizeof(process_handle); process_handle.struct_size = sizeof(process_handle);
...@@ -314,18 +317,16 @@ class TestClientBase : public InvitationTest { ...@@ -314,18 +317,16 @@ class TestClientBase : public InvitationTest {
public: public:
static MojoHandle AcceptInvitation() { static MojoHandle AcceptInvitation() {
const auto& command_line = *base::CommandLine::ForCurrentProcess(); const auto& command_line = *base::CommandLine::ForCurrentProcess();
ScopedInternalPlatformHandle channel_endpoint = PlatformChannelEndpoint channel_endpoint =
NamedPlatformChannelPair::PassClientHandleFromParentProcess( NamedPlatformChannel::ConnectToServer(command_line);
command_line);
if (!channel_endpoint.is_valid()) { if (!channel_endpoint.is_valid()) {
channel_endpoint = PlatformChannelPair::PassClientHandleFromParentProcess( channel_endpoint =
*base::CommandLine::ForCurrentProcess()); PlatformChannel::RecoverPassedEndpointFromCommandLine(command_line);
} }
MojoPlatformHandle endpoint_handle; MojoPlatformHandle endpoint_handle;
endpoint_handle.struct_size = sizeof(endpoint_handle); PlatformHandleToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(),
CHECK_EQ(MOJO_RESULT_OK, &endpoint_handle);
ScopedInternalPlatformHandleToMojoPlatformHandle( CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID);
std::move(channel_endpoint), &endpoint_handle));
MojoInvitationTransportEndpoint transport_endpoint; MojoInvitationTransportEndpoint transport_endpoint;
transport_endpoint.struct_size = sizeof(transport_endpoint); transport_endpoint.struct_size = sizeof(transport_endpoint);
......
...@@ -6,10 +6,19 @@ component("platform") { ...@@ -6,10 +6,19 @@ component("platform") {
output_name = "mojo_cpp_platform" output_name = "mojo_cpp_platform"
public = [ public = [
"named_platform_channel.h",
"platform_channel.h",
"platform_channel_endpoint.h",
"platform_channel_server_endpoint.h",
"platform_handle.h", "platform_handle.h",
] ]
sources = [ sources = [
"named_platform_channel.cc",
"named_platform_channel_win.cc",
"platform_channel.cc",
"platform_channel_endpoint.cc",
"platform_channel_server_endpoint.cc",
"platform_handle.cc", "platform_handle.cc",
] ]
...@@ -17,7 +26,12 @@ component("platform") { ...@@ -17,7 +26,12 @@ component("platform") {
"//base", "//base",
] ]
if (is_posix && (!is_nacl && !is_fuchsia)) {
sources += [ "named_platform_channel_posix.cc" ]
}
if (is_fuchsia) { if (is_fuchsia) {
sources += [ "named_platform_channel_fuchsia.cc" ]
public_deps += [ "//third_party/fuchsia-sdk:fdio" ] public_deps += [ "//third_party/fuchsia-sdk:fdio" ]
} }
......
// Copyright 2018 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/public/cpp/platform/named_platform_channel.h"
#include "base/logging.h"
namespace mojo {
const char NamedPlatformChannel::kNamedHandleSwitch[] =
"mojo-named-platform-channel-pipe";
NamedPlatformChannel::NamedPlatformChannel(const Options& options) {
server_endpoint_ = PlatformChannelServerEndpoint(
CreateServerEndpoint(options, &server_name_));
}
NamedPlatformChannel::~NamedPlatformChannel() = default;
void NamedPlatformChannel::PassServerNameOnCommandLine(
base::CommandLine* command_line) {
command_line->AppendSwitchNative(kNamedHandleSwitch, server_name_);
}
// static
PlatformChannelEndpoint NamedPlatformChannel::ConnectToServer(
const ServerName& server_name) {
DCHECK(!server_name.empty());
return CreateClientEndpoint(server_name);
}
// static
PlatformChannelEndpoint NamedPlatformChannel::ConnectToServer(
const base::CommandLine& command_line) {
ServerName name = command_line.GetSwitchValueNative(kNamedHandleSwitch);
if (name.empty())
return PlatformChannelEndpoint();
return ConnectToServer(name);
}
} // namespace mojo
// Copyright 2018 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_PUBLIC_CPP_PLATFORM_NAMED_PLATFORM_CHANNEL_H_
#define MOJO_PUBLIC_CPP_PLATFORM_NAMED_PLATFORM_CHANNEL_H_
#include "base/command_line.h"
#include "base/component_export.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "build/build_config.h"
#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h"
#if defined(OS_WIN)
#include "base/strings/string16.h"
#elif defined(OS_POSIX)
#include "base/files/file_path.h"
#endif
namespace mojo {
// NamedPlatformChannel encapsulates a Mojo invitation transport channel which
// can listen for inbound connections established by clients connecting to
// a named system resource (i.e. a named pipe server on Windows, a named
// Unix domain socket on POSIX; other platforms not supported).
//
// This can be especially useful when the local process has no way to transfer
// handles to the remote process, e.g. it does not control process launch or
// have any pre-existing communication channel to the process.
class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) NamedPlatformChannel {
public:
static const char kNamedHandleSwitch[];
#if defined(OS_WIN)
using ServerName = base::string16;
#else
using ServerName = std::string;
#endif
struct COMPONENT_EXPORT(MOJO_CPP_PLATFORM) Options {
// Specifies the name to use for the server. If empty, a random name is
// generated.
ServerName server_name;
#if defined(OS_WIN)
// If non-empty, a security descriptor to use when creating the pipe. If
// empty, a default security descriptor will be used. See
// |kDefaultSecurityDescriptor|.
base::string16 security_descriptor;
// If |true|, only a server endpoint will be allowed with the given name and
// only one client will be able to connect. Otherwise many
// NamedPlatformChannel instances can be created with the same name and
// a different client can connect to each one.
bool enforce_uniqueness = true;
#elif defined(OS_POSIX)
// On POSIX, every new unnamed NamedPlatformChannel creates a server socket
// with a random name. This controls the directory where that happens.
// Ignored if |server_name| was set explicitly.
base::FilePath socket_dir;
#endif
};
NamedPlatformChannel(const Options& options);
~NamedPlatformChannel();
const PlatformChannelServerEndpoint& server_endpoint() const {
return server_endpoint_;
}
// Passes the local server endpoint for the channel. On Windows, this is a
// named pipe server; on POSIX it's a bound, listening domain socket. In each
// case it should accept a single new connection.
//
// Use the handle to send or receive an invitation, with the endpoint type as
// |MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER|.
PlatformChannelServerEndpoint TakeServerEndpoint() WARN_UNUSED_RESULT {
return std::move(server_endpoint_);
}
// Returns a name that can be used a remote process to connect to the server
// endpoint.
const ServerName& GetServerName() const { return server_name_; }
// Passes the server name on |*command_line| using the common
// |kNamedHandleSwitch| flag.
void PassServerNameOnCommandLine(base::CommandLine* command_line);
// Recovers a functioning client endpoint handle by creating a new endpoint
// and connecting it to |server_name| if possible.
static PlatformChannelEndpoint ConnectToServer(const ServerName& server_name)
WARN_UNUSED_RESULT;
// Like above, but extracts the server name from |command_line| using the
// common |kNamedHandleSwitch| flag.
static PlatformChannelEndpoint ConnectToServer(
const base::CommandLine& command_line) WARN_UNUSED_RESULT;
private:
static PlatformChannelServerEndpoint CreateServerEndpoint(
const Options& options,
ServerName* server_name);
static PlatformChannelEndpoint CreateClientEndpoint(
const ServerName& server_name);
ServerName server_name_;
PlatformChannelServerEndpoint server_endpoint_;
DISALLOW_COPY_AND_ASSIGN(NamedPlatformChannel);
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_PLATFORM_NAMED_PLATFORM_CHANNEL_H_
// Copyright 2018 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/public/cpp/platform/named_platform_channel.h"
namespace mojo {
// static
PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint(
const Options& options,
ServerName* server_name) {
// TODO(https://crbug.com/754038): Implement, or remove dependencies.
NOTREACHED();
return {};
}
// static
PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint(
const ServerName& server_name) {
// TODO(https://crbug.com/754038): Implement, or remove dependencies.
NOTREACHED();
return {};
}
} // namespace mojo
// Copyright 2018 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/public/cpp/platform/named_platform_channel.h"
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
namespace mojo {
namespace {
NamedPlatformChannel::ServerName GenerateRandomServerName(
const NamedPlatformChannel::Options& options) {
return options.socket_dir
.AppendASCII(base::NumberToString(base::RandUint64()))
.value();
}
// This function fills in |unix_addr| with the appropriate data for the socket,
// and sets |unix_addr_len| to the length of the data therein.
// Returns true on success, or false on failure (typically because |server_name|
// violated the naming rules).
bool MakeUnixAddr(const NamedPlatformChannel::ServerName& server_name,
struct sockaddr_un* unix_addr,
size_t* unix_addr_len) {
DCHECK(unix_addr);
DCHECK(unix_addr_len);
DCHECK(!server_name.empty());
constexpr size_t kMaxSocketNameLength = 104;
// We reject server_name.length() == kMaxSocketNameLength to make room for the
// NUL terminator at the end of the string.
if (server_name.length() >= kMaxSocketNameLength) {
LOG(ERROR) << "Socket name too long: " << server_name;
return false;
}
// Create unix_addr structure.
memset(unix_addr, 0, sizeof(struct sockaddr_un));
unix_addr->sun_family = AF_UNIX;
strncpy(unix_addr->sun_path, server_name.c_str(), kMaxSocketNameLength);
*unix_addr_len =
offsetof(struct sockaddr_un, sun_path) + server_name.length();
return true;
}
// This function creates a unix domain socket, and set it as non-blocking.
// If successful, this returns a PlatformHandle containing the socket.
// Otherwise, this returns an invalid PlatformHandle.
PlatformHandle CreateUnixDomainSocket() {
// Create the unix domain socket.
PlatformHandle handle(base::ScopedFD(socket(AF_UNIX, SOCK_STREAM, 0)));
if (!handle.is_valid()) {
PLOG(ERROR) << "Failed to create AF_UNIX socket.";
return PlatformHandle();
}
// Now set it as non-blocking.
if (!base::SetNonBlocking(handle.GetFD().get())) {
PLOG(ERROR) << "base::SetNonBlocking() failed " << handle.GetFD().get();
return PlatformHandle();
}
return handle;
}
} // namespace
// static
PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint(
const Options& options,
ServerName* server_name) {
ServerName name = options.server_name;
if (name.empty())
name = GenerateRandomServerName(options);
// Make sure the path we need exists.
base::FilePath socket_dir = base::FilePath(name).DirName();
if (!base::CreateDirectory(socket_dir)) {
LOG(ERROR) << "Couldn't create directory: " << socket_dir.value();
return PlatformChannelServerEndpoint();
}
// Delete any old FS instances.
if (unlink(name.c_str()) < 0 && errno != ENOENT) {
PLOG(ERROR) << "unlink " << name;
return PlatformChannelServerEndpoint();
}
struct sockaddr_un unix_addr;
size_t unix_addr_len;
if (!MakeUnixAddr(name, &unix_addr, &unix_addr_len))
return PlatformChannelServerEndpoint();
PlatformHandle handle = CreateUnixDomainSocket();
if (!handle.is_valid())
return PlatformChannelServerEndpoint();
// Bind the socket.
if (bind(handle.GetFD().get(), reinterpret_cast<const sockaddr*>(&unix_addr),
unix_addr_len) < 0) {
PLOG(ERROR) << "bind " << name;
return PlatformChannelServerEndpoint();
}
// Start listening on the socket.
if (listen(handle.GetFD().get(), SOMAXCONN) < 0) {
PLOG(ERROR) << "listen " << name;
unlink(name.c_str());
return PlatformChannelServerEndpoint();
}
*server_name = name;
return PlatformChannelServerEndpoint(std::move(handle));
}
// static
PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint(
const ServerName& server_name) {
DCHECK(!server_name.empty());
struct sockaddr_un unix_addr;
size_t unix_addr_len;
if (!MakeUnixAddr(server_name, &unix_addr, &unix_addr_len))
return PlatformChannelEndpoint();
PlatformHandle handle = CreateUnixDomainSocket();
if (!handle.is_valid())
return PlatformChannelEndpoint();
if (HANDLE_EINTR(connect(handle.GetFD().get(),
reinterpret_cast<sockaddr*>(&unix_addr),
unix_addr_len)) < 0) {
PLOG(ERROR) << "connect " << server_name;
return PlatformChannelEndpoint();
}
return PlatformChannelEndpoint(std::move(handle));
}
} // namespace mojo
// Copyright 2018 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/public/cpp/platform/named_platform_channel.h"
#include <windows.h>
// NOTE: This needs to be included *after* windows.h.
#include <sddl.h>
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
namespace mojo {
namespace {
// A DACL to grant:
// GA = Generic All
// access to:
// SY = LOCAL_SYSTEM
// BA = BUILTIN_ADMINISTRATORS
// OW = OWNER_RIGHTS
constexpr base::char16 kDefaultSecurityDescriptor[] =
L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;OW)";
NamedPlatformChannel::ServerName GenerateRandomServerName() {
return base::UTF8ToUTF16(
base::StringPrintf("%lu.%lu.%I64u", ::GetCurrentProcessId(),
::GetCurrentThreadId(), base::RandUint64()));
}
base::string16 GetPipeNameFromServerName(
const NamedPlatformChannel::ServerName& server_name) {
return L"\\\\.\\pipe\\mojo." + server_name;
}
} // namespace
// static
PlatformChannelServerEndpoint NamedPlatformChannel::CreateServerEndpoint(
const Options& options,
ServerName* server_name) {
ServerName name = options.server_name;
if (name.empty())
name = GenerateRandomServerName();
PSECURITY_DESCRIPTOR security_desc = nullptr;
ULONG security_desc_len = 0;
PCHECK(::ConvertStringSecurityDescriptorToSecurityDescriptor(
options.security_descriptor.empty() ? kDefaultSecurityDescriptor
: options.security_descriptor.c_str(),
SDDL_REVISION_1, &security_desc, &security_desc_len));
std::unique_ptr<void, decltype(::LocalFree)*> p(security_desc, ::LocalFree);
SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
security_desc, FALSE};
const DWORD kOpenMode = options.enforce_uniqueness
? PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
FILE_FLAG_FIRST_PIPE_INSTANCE
: PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED;
const DWORD kPipeMode =
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS;
base::string16 pipe_name = GetPipeNameFromServerName(name);
PlatformHandle handle(base::win::ScopedHandle(::CreateNamedPipeW(
pipe_name.c_str(), kOpenMode, kPipeMode,
options.enforce_uniqueness ? 1 : 255, // Max instances.
4096, // Out buffer size.
4096, // In buffer size.
5000, // Timeout in milliseconds.
&security_attributes)));
*server_name = name;
return PlatformChannelServerEndpoint(std::move(handle));
}
// static
PlatformChannelEndpoint NamedPlatformChannel::CreateClientEndpoint(
const ServerName& server_name) {
base::string16 pipe_name = GetPipeNameFromServerName(server_name);
// Note: This may block.
if (!::WaitNamedPipeW(pipe_name.c_str(), NMPWAIT_USE_DEFAULT_WAIT))
return PlatformChannelEndpoint();
const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
// The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
// the client.
const DWORD kFlags =
SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED;
PlatformHandle handle(base::win::ScopedHandle(
::CreateFileW(pipe_name.c_str(), kDesiredAccess, 0, nullptr,
OPEN_EXISTING, kFlags, nullptr)));
// The server may have stopped accepting a connection between the
// WaitNamedPipe() and CreateFile(). If this occurs, an invalid handle is
// returned.
DPLOG_IF(ERROR, !handle.is_valid())
<< "Named pipe " << pipe_name
<< " could not be opened after WaitNamedPipe succeeded";
return PlatformChannelEndpoint(std::move(handle));
}
} // namespace mojo
// Copyright 2018 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/public/cpp/platform/platform_channel.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#include "base/win/scoped_handle.h"
#elif defined(OS_FUCHSIA)
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include "base/fuchsia/scoped_zx_handle.h"
#elif defined(OS_POSIX)
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include "base/files/scoped_file.h"
#include "base/posix/global_descriptors.h"
#endif
#if defined(OS_POSIX) && !defined(OS_NACL_SFI)
#include <sys/socket.h>
#elif defined(OS_NACL_SFI)
#include "native_client/src/public/imc_syscalls.h"
#endif
namespace mojo {
namespace {
#if defined(OS_WIN)
void CreateChannel(PlatformHandle* local_endpoint,
PlatformHandle* remote_endpoint) {
base::string16 pipe_name = base::UTF8ToUTF16(base::StringPrintf(
"\\\\.\\pipe\\mojo.%lu.%lu.%I64u", ::GetCurrentProcessId(),
::GetCurrentThreadId(), base::RandUint64()));
DWORD kOpenMode =
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE;
const DWORD kPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE;
*local_endpoint = PlatformHandle(base::win::ScopedHandle(
::CreateNamedPipeW(pipe_name.c_str(), kOpenMode, kPipeMode,
1, // Max instances.
4096, // Output buffer size.
4096, // Input buffer size.
5000, // Timeout in ms.
nullptr))); // Default security descriptor.
PCHECK(local_endpoint->is_valid());
const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
// The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
// the client.
DWORD kFlags = SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS;
// Allow the handle to be inherited by child processes.
SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
nullptr, TRUE};
*remote_endpoint = PlatformHandle(base::win::ScopedHandle(
::CreateFileW(pipe_name.c_str(), kDesiredAccess, 0, &security_attributes,
OPEN_EXISTING, kFlags, nullptr)));
PCHECK(remote_endpoint->is_valid());
// Since a client has connected, ConnectNamedPipe() should return zero and
// GetLastError() should return ERROR_PIPE_CONNECTED.
CHECK(!::ConnectNamedPipe(local_endpoint->GetHandle().Get(), nullptr));
PCHECK(::GetLastError() == ERROR_PIPE_CONNECTED);
}
#elif defined(OS_FUCHSIA)
void CreateChannel(PlatformHandle* local_endpoint,
PlatformHandle* remote_endpoint) {
zx_handle_t handles[2] = {};
zx_status_t result = zx_channel_create(0, &handles[0], &handles[1]);
CHECK_EQ(ZX_OK, result);
*local_endpoint = PlatformHandle(base::ScopedZxHandle(handles[0]));
*remote_endpoint = PlatformHandle(base::ScopedZxHandle(handles[1]));
DCHECK(local_endpoint->is_valid());
DCHECK(remote_endpoint->is_valid());
}
#elif defined(OS_POSIX)
#if defined(OS_ANDROID)
// Leave room for any other descriptors defined in content for example.
// TODO(https://crbug.com/676442): Consider changing base::GlobalDescriptors to
// generate a key when setting the file descriptor.
constexpr int kAndroidClientHandleDescriptor =
base::GlobalDescriptors::kBaseDescriptor + 10000;
#else
bool IsTargetDescriptorUsed(const base::FileHandleMappingVector& mapping,
int target_fd) {
for (size_t i = 0; i < mapping.size(); ++i) {
if (mapping[i].second == target_fd)
return true;
}
return false;
}
#endif
void CreateChannel(PlatformHandle* local_endpoint,
PlatformHandle* remote_endpoint) {
int fds[2];
#if defined(OS_NACL_SFI)
PCHECK(imc_socketpair(fds) == 0);
#else
PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
// Set non-blocking on both ends.
PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0);
PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0);
#if defined(OS_MACOSX)
// This turns off |SIGPIPE| when writing to a closed socket, causing the call
// to fail with |EPIPE| instead. On Linux we have to use |send...()| with
// |MSG_NOSIGNAL| instead, which is not supported on Mac.
int no_sigpipe = 1;
PCHECK(setsockopt(fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
sizeof(no_sigpipe)) == 0);
PCHECK(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
sizeof(no_sigpipe)) == 0);
#endif // defined(OS_MACOSX)
#endif // defined(OS_NACL_SFI)
*local_endpoint = PlatformHandle(base::ScopedFD(fds[0]));
*remote_endpoint = PlatformHandle(base::ScopedFD(fds[1]));
DCHECK(local_endpoint->is_valid());
DCHECK(remote_endpoint->is_valid());
}
#else
#error "Unsupported platform."
#endif
} // namespace
const char PlatformChannel::kHandleSwitch[] = "mojo-platform-channel-handle";
PlatformChannel::PlatformChannel() {
PlatformHandle local_handle;
PlatformHandle remote_handle;
CreateChannel(&local_handle, &remote_handle);
local_endpoint_ = PlatformChannelEndpoint(std::move(local_handle));
remote_endpoint_ = PlatformChannelEndpoint(std::move(remote_handle));
}
PlatformChannel::PlatformChannel(PlatformChannel&& other) = default;
PlatformChannel::~PlatformChannel() = default;
PlatformChannel& PlatformChannel::operator=(PlatformChannel&& other) = default;
void PlatformChannel::PrepareToPassRemoteEndpoint(HandlePassingInfo* info,
std::string* value) {
DCHECK(info);
DCHECK(value);
DCHECK(remote_endpoint_.is_valid());
#if defined(OS_WIN)
info->push_back(remote_endpoint_.platform_handle().GetHandle().Get());
*value = base::NumberToString(
HandleToLong(remote_endpoint_.platform_handle().GetHandle().Get()));
#elif defined(OS_FUCHSIA)
const uint32_t id = PA_HND(PA_USER0, 0);
info->push_back({id, remote_endpoint_.platform_handle().GetHandle().get()});
*value = base::NumberToString(id);
#elif defined(OS_ANDROID)
int fd = remote_endpoint_.platform_handle().GetFD().get();
info->emplace_back(fd, kAndroidClientHandleDescriptor);
*value = base::NumberToString(kAndroidClientHandleDescriptor);
#elif defined(OS_POSIX)
// Arbitrary sanity check to ensure the loop below terminates reasonably
// quickly.
CHECK_LT(info->size(), 1000u);
// Find a suitable FD to map the remote endpoint handle to in the child
// process. This has quadratic time complexity in the size of |*info|, but
// |*info| should be very small and is usually empty.
int target_fd = base::GlobalDescriptors::kBaseDescriptor;
while (IsTargetDescriptorUsed(*info, target_fd))
++target_fd;
info->emplace_back(remote_endpoint_.platform_handle().GetFD().get(),
target_fd);
*value = base::NumberToString(target_fd);
#endif
}
void PlatformChannel::PrepareToPassRemoteEndpoint(
HandlePassingInfo* info,
base::CommandLine* command_line) {
std::string value;
PrepareToPassRemoteEndpoint(info, &value);
if (!value.empty())
command_line->AppendSwitchASCII(kHandleSwitch, value);
}
void PlatformChannel::RemoteProcessLaunched() {
#if defined(OS_FUCHSIA)
// Unlike other platforms, Fuchsia just transfers handle ownership to the
// launcher process rather than duplicating it.
DCHECK(remote_endpoint_.platform_handle().is_valid_handle());
ignore_result(remote_endpoint_.TakePlatformHandle().ReleaseHandle());
#else
remote_endpoint_.reset();
#endif
}
// static
PlatformChannelEndpoint PlatformChannel::RecoverPassedEndpointFromString(
base::StringPiece value) {
#if defined(OS_WIN)
int handle_value = 0;
if (value.empty() || !base::StringToInt(value, &handle_value)) {
DLOG(ERROR) << "Invalid PlatformChannel endpoint string.";
return PlatformChannelEndpoint();
}
return PlatformChannelEndpoint(
PlatformHandle(base::win::ScopedHandle(LongToHandle(handle_value))));
#elif defined(OS_FUCHSIA)
unsigned int handle_value = 0;
if (value.empty() || !base::StringToUint(value, &handle_value)) {
DLOG(ERROR) << "Invalid PlatformChannel endpoint string.";
return PlatformChannelEndpoint();
}
return PlatformChannelEndpoint(PlatformHandle(base::ScopedZxHandle(
zx_get_startup_handle(base::checked_cast<uint32_t>(handle_value)))));
#elif defined(OS_ANDROID)
base::GlobalDescriptors::Key key = -1;
if (value.empty() || !base::StringToUint(value, &key)) {
DLOG(ERROR) << "Invalid PlatformChannel endpoint string.";
return PlatformChannelEndpoint();
}
return PlatformChannelEndpoint(PlatformHandle(
base::ScopedFD(base::GlobalDescriptors::GetInstance()->Get(key))));
#elif defined(OS_POSIX)
int fd = -1;
if (value.empty() || !base::StringToInt(value, &fd) ||
fd < base::GlobalDescriptors::kBaseDescriptor) {
DLOG(ERROR) << "Invalid PlatformChannel endpoint string.";
return PlatformChannelEndpoint();
}
return PlatformChannelEndpoint(PlatformHandle(base::ScopedFD(fd)));
#endif
}
// static
PlatformChannelEndpoint PlatformChannel::RecoverPassedEndpointFromCommandLine(
const base::CommandLine& command_line) {
return RecoverPassedEndpointFromString(
command_line.GetSwitchValueASCII(kHandleSwitch));
}
} // namespace mojo
// Copyright 2018 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_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_H_
#define MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_H_
#include "base/command_line.h"
#include "base/component_export.h"
#include "base/macros.h"
#include "base/process/launch.h"
#include "build/build_config.h"
#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
namespace mojo {
// PlatformChannel encapsulates construction and ownership of two entangled
// endpoints of a platform-specific communication primitive, e.g. a Windows pipe
// or a Unix domain socket. One endpoint is designated as the "local" endpoint
// and should be retained by the creating process; the other endpoint is
// designated as the "remote" endpoint and should be passed to an external
// process.
//
// PlatformChannels can be used to bootstrap Mojo IPC between one process and
// another. Typically the other process is a child of this process, and there
// are helper methods for passing the endpoint to a child as such; but this
// arrangement is not strictly necessary on all platforms.
//
// For a channel which allows clients to connect by name (i.e. a named pipe
// or socket server, supported only on Windows and POSIX systems) see
// NamedPlatformChannel.
class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannel {
public:
// A common helper constant that is used to pass handle values on the
// command line when the relevant methods are used on this class.
static const char kHandleSwitch[];
// Unfortunately base process support code has no unified handle-passing
// data pipe, so we have this.
#if defined(OS_WIN)
using HandlePassingInfo = base::HandlesToInheritVector;
#elif defined(OS_FUCHSIA)
using HandlePassingInfo = base::HandlesToTransferVector;
#elif defined(OS_POSIX)
using HandlePassingInfo = base::FileHandleMappingVector;
#else
#error "Unsupported platform."
#endif
PlatformChannel();
PlatformChannel(PlatformChannel&& other);
~PlatformChannel();
PlatformChannel& operator=(PlatformChannel&& other);
const PlatformChannelEndpoint& local_endpoint() const {
return local_endpoint_;
}
const PlatformChannelEndpoint& remote_endpoint() const {
return remote_endpoint_;
}
PlatformChannelEndpoint TakeLocalEndpoint() WARN_UNUSED_RESULT {
return std::move(local_endpoint_);
}
PlatformChannelEndpoint TakeRemoteEndpoint() WARN_UNUSED_RESULT {
return std::move(remote_endpoint_);
}
// Prepares to pass the remote endpoint handle to a process that will soon be
// launched. Returns a string that can be used in the remote process with
// |RecoverPassedEndpointFromString()| (see below). The string can e.g. be
// passed on the new process's command line.
//
// **NOTE**: If this method is called it is important to also call
// |RemoteProcessLaunched()| on this PlatformChanenl *after* the process has
// launched. Failing to do so can result in leaked handles.
void PrepareToPassRemoteEndpoint(HandlePassingInfo* info, std::string* value);
// Like above but modifies |*command_line| to include the endpoint string
// via the |kHandleSwitch| flag.
void PrepareToPassRemoteEndpoint(HandlePassingInfo* info,
base::CommandLine* command_line);
// Must be called after the corresponding process launch if
// |PrepareToPassRemoteEndpoint()| was used.
void RemoteProcessLaunched();
// Recovers and endpoint handle which was passed to the calling process by
// its creator. |value| is a string returned by
// |PrepareToPassRemoteEndpoint()| in the creator's process.
static PlatformChannelEndpoint RecoverPassedEndpointFromString(
base::StringPiece value) WARN_UNUSED_RESULT;
// Like above but extracts the input string from |command_line| via the
// |kHandleSwitch| flag.
static PlatformChannelEndpoint RecoverPassedEndpointFromCommandLine(
const base::CommandLine& command_line) WARN_UNUSED_RESULT;
private:
PlatformChannelEndpoint local_endpoint_;
PlatformChannelEndpoint remote_endpoint_;
DISALLOW_COPY_AND_ASSIGN(PlatformChannel);
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_H_
// Copyright 2018 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/public/cpp/platform/platform_channel_endpoint.h"
namespace mojo {
PlatformChannelEndpoint::PlatformChannelEndpoint() = default;
PlatformChannelEndpoint::PlatformChannelEndpoint(
PlatformChannelEndpoint&& other) = default;
PlatformChannelEndpoint::PlatformChannelEndpoint(PlatformHandle handle)
: handle_(std::move(handle)) {}
PlatformChannelEndpoint::~PlatformChannelEndpoint() = default;
PlatformChannelEndpoint& PlatformChannelEndpoint::operator=(
PlatformChannelEndpoint&& other) = default;
void PlatformChannelEndpoint::reset() {
handle_.reset();
}
PlatformChannelEndpoint PlatformChannelEndpoint::Clone() const {
return PlatformChannelEndpoint(handle_.Clone());
}
} // namespace mojo
// Copyright 2018 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_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_ENDPOINT_H_
#define MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_ENDPOINT_H_
#include "base/component_export.h"
#include "base/macros.h"
#include "mojo/public/cpp/platform/platform_handle.h"
namespace mojo {
// A PlatformHandle with a little extra type information to convey that it's
// a channel endpoint, i.e. a handle that can be used to send or receive
// invitations as |MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL| to a remote
// PlatformChannelEndpoint.
class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannelEndpoint {
public:
PlatformChannelEndpoint();
PlatformChannelEndpoint(PlatformChannelEndpoint&& other);
explicit PlatformChannelEndpoint(PlatformHandle handle);
~PlatformChannelEndpoint();
PlatformChannelEndpoint& operator=(PlatformChannelEndpoint&& other);
bool is_valid() const { return handle_.is_valid(); }
void reset();
PlatformChannelEndpoint Clone() const;
const PlatformHandle& platform_handle() const { return handle_; }
PlatformHandle TakePlatformHandle() WARN_UNUSED_RESULT {
return std::move(handle_);
}
private:
PlatformHandle handle_;
DISALLOW_COPY_AND_ASSIGN(PlatformChannelEndpoint);
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_ENDPOINT_H_
// Copyright 2018 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/public/cpp/platform/platform_channel_server_endpoint.h"
namespace mojo {
PlatformChannelServerEndpoint::PlatformChannelServerEndpoint() = default;
PlatformChannelServerEndpoint::PlatformChannelServerEndpoint(
PlatformChannelServerEndpoint&& other) = default;
PlatformChannelServerEndpoint::PlatformChannelServerEndpoint(
PlatformHandle handle)
: handle_(std::move(handle)) {}
PlatformChannelServerEndpoint::~PlatformChannelServerEndpoint() = default;
PlatformChannelServerEndpoint& PlatformChannelServerEndpoint::operator=(
PlatformChannelServerEndpoint&& other) = default;
void PlatformChannelServerEndpoint::reset() {
handle_.reset();
}
PlatformChannelServerEndpoint PlatformChannelServerEndpoint::Clone() const {
return PlatformChannelServerEndpoint(handle_.Clone());
}
} // namespace mojo
// Copyright 2018 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_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_SERVER_ENDPOINT_H_
#define MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_SERVER_ENDPOINT_H_
#include "base/component_export.h"
#include "base/macros.h"
#include "mojo/public/cpp/platform/platform_handle.h"
namespace mojo {
// A PlatformHandle with a little extra type information to convey that it's
// a channel server endpoint, i.e. a handle that can be used to send invitations
// as |MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER| to a remote
// PlatformChannelEndpoint.
class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannelServerEndpoint {
public:
PlatformChannelServerEndpoint();
PlatformChannelServerEndpoint(PlatformChannelServerEndpoint&& other);
explicit PlatformChannelServerEndpoint(PlatformHandle handle);
~PlatformChannelServerEndpoint();
PlatformChannelServerEndpoint& operator=(
PlatformChannelServerEndpoint&& other);
bool is_valid() const { return handle_.is_valid(); }
void reset();
PlatformChannelServerEndpoint Clone() const;
const PlatformHandle& platform_handle() const { return handle_; }
PlatformHandle TakePlatformHandle() WARN_UNUSED_RESULT {
return std::move(handle_);
}
private:
PlatformHandle handle_;
DISALLOW_COPY_AND_ASSIGN(PlatformChannelServerEndpoint);
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_SERVER_ENDPOINT_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment