This source file includes following definitions.
- IsHostAllowed
- write_pending_
- Create
- CreateWithLauncher
- LaunchHostProcess
- OnHostProcessLaunched
- Send
- OnFileCanReadWithoutBlocking
- OnFileCanWriteWithoutBlocking
- ReadNowForTesting
- WaitRead
- DoRead
- OnRead
- HandleReadResult
- ProcessIncomingData
- DoWrite
- HandleWriteResult
- OnWritten
- Close
#include "chrome/browser/extensions/api/messaging/native_message_process_host.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/platform_file.h"
#include "base/prefs/pref_service.h"
#include "base/process/kill.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
#include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
#include "chrome/common/chrome_version_info.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/constants.h"
#include "extensions/common/features/feature.h"
#include "net/base/file_stream.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "url/gurl.h"
namespace {
const size_t kMaximumMessageSize = 1024 * 1024;
const size_t kMessageHeaderSize = 4;
const size_t kReadBufferSize = 4096;
const char kFailedToStartError[] = "Failed to start native messaging host.";
const char kInvalidNameError[] =
"Invalid native messaging host name specified.";
const char kNativeHostExited[] = "Native host has exited.";
const char kNotFoundError[] = "Specified native messaging host not found.";
const char kForbiddenError[] =
"Access to the specified native messaging host is forbidden.";
const char kHostInputOuputError[] =
"Error when communicating with the native messaging host.";
}
namespace extensions {
NativeMessageProcessHost::PolicyPermission
NativeMessageProcessHost::IsHostAllowed(const PrefService* pref_service,
const std::string& native_host_name) {
NativeMessageProcessHost::PolicyPermission allow_result = ALLOW_ALL;
if (pref_service->IsManagedPreference(
pref_names::kNativeMessagingUserLevelHosts)) {
if (!pref_service->GetBoolean(pref_names::kNativeMessagingUserLevelHosts))
allow_result = ALLOW_SYSTEM_ONLY;
}
if (!pref_service->IsManagedPreference(pref_names::kNativeMessagingBlacklist))
return allow_result;
const base::ListValue* blacklist =
pref_service->GetList(pref_names::kNativeMessagingBlacklist);
if (!blacklist)
return allow_result;
base::StringValue name_value(native_host_name);
base::StringValue wildcard_value("*");
if (blacklist->Find(name_value) == blacklist->end() &&
blacklist->Find(wildcard_value) == blacklist->end()) {
return allow_result;
}
if (pref_service->IsManagedPreference(
pref_names::kNativeMessagingWhitelist)) {
const base::ListValue* whitelist =
pref_service->GetList(pref_names::kNativeMessagingWhitelist);
if (whitelist && whitelist->Find(name_value) != whitelist->end())
return allow_result;
}
return DISALLOW;
}
NativeMessageProcessHost::NativeMessageProcessHost(
base::WeakPtr<Client> weak_client_ui,
const std::string& source_extension_id,
const std::string& native_host_name,
int destination_port,
scoped_ptr<NativeProcessLauncher> launcher)
: weak_client_ui_(weak_client_ui),
source_extension_id_(source_extension_id),
native_host_name_(native_host_name),
destination_port_(destination_port),
launcher_(launcher.Pass()),
closed_(false),
process_handle_(base::kNullProcessHandle),
#if defined(OS_POSIX)
read_file_(base::kInvalidPlatformFileValue),
#endif
read_pending_(false),
write_pending_(false) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
base::Bind(&NativeMessageProcessHost::LaunchHostProcess,
base::Unretained(this)));
}
NativeMessageProcessHost::~NativeMessageProcessHost() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
Close(std::string());
}
scoped_ptr<NativeMessageProcessHost> NativeMessageProcessHost::Create(
gfx::NativeView native_view,
base::WeakPtr<Client> weak_client_ui,
const std::string& source_extension_id,
const std::string& native_host_name,
int destination_port,
bool allow_user_level) {
return CreateWithLauncher(weak_client_ui, source_extension_id,
native_host_name, destination_port,
NativeProcessLauncher::CreateDefault(
allow_user_level, native_view));
}
scoped_ptr<NativeMessageProcessHost>
NativeMessageProcessHost::CreateWithLauncher(
base::WeakPtr<Client> weak_client_ui,
const std::string& source_extension_id,
const std::string& native_host_name,
int destination_port,
scoped_ptr<NativeProcessLauncher> launcher) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
scoped_ptr<NativeMessageProcessHost> process(new NativeMessageProcessHost(
weak_client_ui, source_extension_id, native_host_name,
destination_port, launcher.Pass()));
return process.Pass();
}
void NativeMessageProcessHost::LaunchHostProcess() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
GURL origin(std::string(kExtensionScheme) + "://" + source_extension_id_);
launcher_->Launch(origin, native_host_name_,
base::Bind(&NativeMessageProcessHost::OnHostProcessLaunched,
base::Unretained(this)));
}
void NativeMessageProcessHost::OnHostProcessLaunched(
NativeProcessLauncher::LaunchResult result,
base::ProcessHandle process_handle,
base::File read_file,
base::File write_file) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
switch (result) {
case NativeProcessLauncher::RESULT_INVALID_NAME:
Close(kInvalidNameError);
return;
case NativeProcessLauncher::RESULT_NOT_FOUND:
Close(kNotFoundError);
return;
case NativeProcessLauncher::RESULT_FORBIDDEN:
Close(kForbiddenError);
return;
case NativeProcessLauncher::RESULT_FAILED_TO_START:
Close(kFailedToStartError);
return;
case NativeProcessLauncher::RESULT_SUCCESS:
break;
}
process_handle_ = process_handle;
#if defined(OS_POSIX)
read_file_ = read_file.GetPlatformFile();
#endif
scoped_refptr<base::TaskRunner> task_runner(
content::BrowserThread::GetBlockingPool()->
GetTaskRunnerWithShutdownBehavior(
base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
read_stream_.reset(new net::FileStream(
read_file.TakePlatformFile(),
base::PLATFORM_FILE_READ | base::PLATFORM_FILE_ASYNC, NULL,
task_runner));
write_stream_.reset(new net::FileStream(
write_file.TakePlatformFile(),
base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC, NULL,
task_runner));
WaitRead();
DoWrite();
}
void NativeMessageProcessHost::Send(const std::string& json) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (closed_)
return;
scoped_refptr<net::IOBufferWithSize> buffer =
new net::IOBufferWithSize(json.size() + kMessageHeaderSize);
COMPILE_ASSERT(sizeof(uint32) == kMessageHeaderSize, incorrect_header_size);
*reinterpret_cast<uint32*>(buffer->data()) = json.size();
memcpy(buffer->data() + kMessageHeaderSize, json.data(), json.size());
write_queue_.push(buffer);
if (write_stream_)
DoWrite();
}
#if defined(OS_POSIX)
void NativeMessageProcessHost::OnFileCanReadWithoutBlocking(int fd) {
DCHECK_EQ(fd, read_file_);
DoRead();
}
void NativeMessageProcessHost::OnFileCanWriteWithoutBlocking(int fd) {
NOTREACHED();
}
#endif
void NativeMessageProcessHost::ReadNowForTesting() {
DoRead();
}
void NativeMessageProcessHost::WaitRead() {
if (closed_)
return;
DCHECK(!read_pending_);
#if defined(OS_POSIX)
base::MessageLoopForIO::current()->WatchFileDescriptor(
read_file_, false ,
base::MessageLoopForIO::WATCH_READ, &read_watcher_, this);
#else
DoRead();
#endif
}
void NativeMessageProcessHost::DoRead() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
while (!closed_ && !read_pending_) {
read_buffer_ = new net::IOBuffer(kReadBufferSize);
int result = read_stream_->Read(
read_buffer_.get(),
kReadBufferSize,
base::Bind(&NativeMessageProcessHost::OnRead, base::Unretained(this)));
HandleReadResult(result);
}
}
void NativeMessageProcessHost::OnRead(int result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
DCHECK(read_pending_);
read_pending_ = false;
HandleReadResult(result);
WaitRead();
}
void NativeMessageProcessHost::HandleReadResult(int result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (closed_)
return;
if (result > 0) {
ProcessIncomingData(read_buffer_->data(), result);
} else if (result == net::ERR_IO_PENDING) {
read_pending_ = true;
} else if (result == 0 || result == net::ERR_CONNECTION_RESET) {
Close(kNativeHostExited);
} else {
LOG(ERROR) << "Error when reading from Native Messaging host: " << result;
Close(kHostInputOuputError);
}
}
void NativeMessageProcessHost::ProcessIncomingData(
const char* data, int data_size) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
incoming_data_.append(data, data_size);
while (true) {
if (incoming_data_.size() < kMessageHeaderSize)
return;
size_t message_size =
*reinterpret_cast<const uint32*>(incoming_data_.data());
if (message_size > kMaximumMessageSize) {
LOG(ERROR) << "Native Messaging host tried sending a message that is "
<< message_size << " bytes long.";
Close(kHostInputOuputError);
return;
}
if (incoming_data_.size() < message_size + kMessageHeaderSize)
return;
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
base::Bind(&Client::PostMessageFromNativeProcess, weak_client_ui_,
destination_port_,
incoming_data_.substr(kMessageHeaderSize, message_size)));
incoming_data_.erase(0, kMessageHeaderSize + message_size);
}
}
void NativeMessageProcessHost::DoWrite() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
while (!write_pending_ && !closed_) {
if (!current_write_buffer_.get() ||
!current_write_buffer_->BytesRemaining()) {
if (write_queue_.empty())
return;
current_write_buffer_ = new net::DrainableIOBuffer(
write_queue_.front().get(), write_queue_.front()->size());
write_queue_.pop();
}
int result =
write_stream_->Write(current_write_buffer_.get(),
current_write_buffer_->BytesRemaining(),
base::Bind(&NativeMessageProcessHost::OnWritten,
base::Unretained(this)));
HandleWriteResult(result);
}
}
void NativeMessageProcessHost::HandleWriteResult(int result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (result <= 0) {
if (result == net::ERR_IO_PENDING) {
write_pending_ = true;
} else {
LOG(ERROR) << "Error when writing to Native Messaging host: " << result;
Close(kHostInputOuputError);
}
return;
}
current_write_buffer_->DidConsume(result);
}
void NativeMessageProcessHost::OnWritten(int result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
DCHECK(write_pending_);
write_pending_ = false;
HandleWriteResult(result);
DoWrite();
}
void NativeMessageProcessHost::Close(const std::string& error_message) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!closed_) {
closed_ = true;
read_stream_.reset();
write_stream_.reset();
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
base::Bind(&Client::CloseChannel, weak_client_ui_,
destination_port_, error_message));
}
if (process_handle_ != base::kNullProcessHandle) {
#if defined(OS_MACOSX)
content::BrowserThread::PostBlockingPoolTask(
FROM_HERE, base::Bind(&base::EnsureProcessTerminated, process_handle_));
#else
base::EnsureProcessTerminated(process_handle_);
#endif
process_handle_ = base::kNullProcessHandle;
}
}
}