1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/messaging/native_message_process_host.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/process/kill.h"
11 #include "base/threading/sequenced_worker_pool.h"
12 #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
13 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
14 #include "chrome/common/chrome_version_info.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "extensions/common/constants.h"
17 #include "extensions/common/features/feature.h"
18 #include "net/base/file_stream.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/net_util.h"
26 // Maximum message size in bytes for messages received from Native Messaging
27 // hosts. Message size is limited mainly to prevent Chrome from crashing when
28 // native application misbehaves (e.g. starts writing garbage to the pipe).
29 const size_t kMaximumMessageSize = 1024 * 1024;
31 // Message header contains 4-byte integer size of the message.
32 const size_t kMessageHeaderSize = 4;
34 // Size of the buffer to be allocated for each read.
35 const size_t kReadBufferSize = 4096;
39 namespace extensions {
41 NativeMessageProcessHost::NativeMessageProcessHost(
42 const std::string& source_extension_id,
43 const std::string& native_host_name,
44 scoped_ptr<NativeProcessLauncher> launcher)
46 source_extension_id_(source_extension_id),
47 native_host_name_(native_host_name),
48 launcher_(launcher.Pass()),
50 process_handle_(base::kNullProcessHandle),
55 write_pending_(false),
57 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
59 task_runner_ = content::BrowserThread::GetMessageLoopProxyForThread(
60 content::BrowserThread::IO);
63 NativeMessageProcessHost::~NativeMessageProcessHost() {
64 DCHECK(task_runner_->BelongsToCurrentThread());
68 scoped_ptr<NativeMessageHost> NativeMessageHost::Create(
69 gfx::NativeView native_view,
70 const std::string& source_extension_id,
71 const std::string& native_host_name,
72 bool allow_user_level,
73 std::string* error_message) {
74 return NativeMessageProcessHost::CreateWithLauncher(
77 NativeProcessLauncher::CreateDefault(allow_user_level, native_view));
81 scoped_ptr<NativeMessageHost> NativeMessageProcessHost::CreateWithLauncher(
82 const std::string& source_extension_id,
83 const std::string& native_host_name,
84 scoped_ptr<NativeProcessLauncher> launcher) {
85 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
87 scoped_ptr<NativeMessageHost> process(
88 new NativeMessageProcessHost(source_extension_id,
92 return process.Pass();
95 void NativeMessageProcessHost::LaunchHostProcess() {
96 DCHECK(task_runner_->BelongsToCurrentThread());
98 GURL origin(std::string(kExtensionScheme) + "://" + source_extension_id_);
99 launcher_->Launch(origin, native_host_name_,
100 base::Bind(&NativeMessageProcessHost::OnHostProcessLaunched,
101 weak_factory_.GetWeakPtr()));
104 void NativeMessageProcessHost::OnHostProcessLaunched(
105 NativeProcessLauncher::LaunchResult result,
106 base::ProcessHandle process_handle,
107 base::File read_file,
108 base::File write_file) {
109 DCHECK(task_runner_->BelongsToCurrentThread());
112 case NativeProcessLauncher::RESULT_INVALID_NAME:
113 Close(kInvalidNameError);
115 case NativeProcessLauncher::RESULT_NOT_FOUND:
116 Close(kNotFoundError);
118 case NativeProcessLauncher::RESULT_FORBIDDEN:
119 Close(kForbiddenError);
121 case NativeProcessLauncher::RESULT_FAILED_TO_START:
122 Close(kFailedToStartError);
124 case NativeProcessLauncher::RESULT_SUCCESS:
128 process_handle_ = process_handle;
129 #if defined(OS_POSIX)
130 // This object is not the owner of the file so it should not keep an fd.
131 read_file_ = read_file.GetPlatformFile();
134 scoped_refptr<base::TaskRunner> task_runner(
135 content::BrowserThread::GetBlockingPool()->
136 GetTaskRunnerWithShutdownBehavior(
137 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
139 read_stream_.reset(new net::FileStream(read_file.Pass(), task_runner));
140 write_stream_.reset(new net::FileStream(write_file.Pass(), task_runner));
146 void NativeMessageProcessHost::OnMessage(const std::string& json) {
147 DCHECK(task_runner_->BelongsToCurrentThread());
152 // Allocate new buffer for the message.
153 scoped_refptr<net::IOBufferWithSize> buffer =
154 new net::IOBufferWithSize(json.size() + kMessageHeaderSize);
156 // Copy size and content of the message to the buffer.
157 COMPILE_ASSERT(sizeof(uint32) == kMessageHeaderSize, incorrect_header_size);
158 *reinterpret_cast<uint32*>(buffer->data()) = json.size();
159 memcpy(buffer->data() + kMessageHeaderSize, json.data(), json.size());
161 // Push new message to the write queue.
162 write_queue_.push(buffer);
164 // Send() may be called before the host process is started. In that case the
165 // message will be written when OnHostProcessLaunched() is called. If it's
166 // already started then write the message now.
171 void NativeMessageProcessHost::Start(Client* client) {
172 DCHECK(task_runner_->BelongsToCurrentThread());
175 // It's safe to use base::Unretained() here because NativeMessagePort always
176 // deletes us on the IO thread.
177 task_runner_->PostTask(
179 base::Bind(&NativeMessageProcessHost::LaunchHostProcess,
180 weak_factory_.GetWeakPtr()));
183 scoped_refptr<base::SingleThreadTaskRunner>
184 NativeMessageProcessHost::task_runner() const {
188 #if defined(OS_POSIX)
189 void NativeMessageProcessHost::OnFileCanReadWithoutBlocking(int fd) {
190 DCHECK_EQ(fd, read_file_);
194 void NativeMessageProcessHost::OnFileCanWriteWithoutBlocking(int fd) {
197 #endif // !defined(OS_POSIX)
199 void NativeMessageProcessHost::ReadNowForTesting() {
203 void NativeMessageProcessHost::WaitRead() {
207 DCHECK(!read_pending_);
209 // On POSIX FileStream::Read() uses blocking thread pool, so it's better to
210 // wait for the file to become readable before calling DoRead(). Otherwise it
211 // would always be consuming one thread in the thread pool. On Windows
212 // FileStream uses overlapped IO, so that optimization isn't necessary there.
213 #if defined(OS_POSIX)
214 base::MessageLoopForIO::current()->WatchFileDescriptor(
215 read_file_, false /* persistent */,
216 base::MessageLoopForIO::WATCH_READ, &read_watcher_, this);
217 #else // defined(OS_POSIX)
219 #endif // defined(!OS_POSIX)
222 void NativeMessageProcessHost::DoRead() {
223 DCHECK(task_runner_->BelongsToCurrentThread());
225 while (!closed_ && !read_pending_) {
226 read_buffer_ = new net::IOBuffer(kReadBufferSize);
228 read_stream_->Read(read_buffer_.get(), kReadBufferSize,
229 base::Bind(&NativeMessageProcessHost::OnRead,
230 weak_factory_.GetWeakPtr()));
231 HandleReadResult(result);
235 void NativeMessageProcessHost::OnRead(int result) {
236 DCHECK(task_runner_->BelongsToCurrentThread());
237 DCHECK(read_pending_);
238 read_pending_ = false;
240 HandleReadResult(result);
244 void NativeMessageProcessHost::HandleReadResult(int result) {
245 DCHECK(task_runner_->BelongsToCurrentThread());
251 ProcessIncomingData(read_buffer_->data(), result);
252 } else if (result == net::ERR_IO_PENDING) {
253 read_pending_ = true;
254 } else if (result == 0 || result == net::ERR_CONNECTION_RESET) {
255 // On Windows we get net::ERR_CONNECTION_RESET for a broken pipe, while on
256 // Posix read() returns 0 in that case.
257 Close(kNativeHostExited);
259 LOG(ERROR) << "Error when reading from Native Messaging host: " << result;
260 Close(kHostInputOuputError);
264 void NativeMessageProcessHost::ProcessIncomingData(
265 const char* data, int data_size) {
266 DCHECK(task_runner_->BelongsToCurrentThread());
268 incoming_data_.append(data, data_size);
271 if (incoming_data_.size() < kMessageHeaderSize)
274 size_t message_size =
275 *reinterpret_cast<const uint32*>(incoming_data_.data());
277 if (message_size > kMaximumMessageSize) {
278 LOG(ERROR) << "Native Messaging host tried sending a message that is "
279 << message_size << " bytes long.";
280 Close(kHostInputOuputError);
284 if (incoming_data_.size() < message_size + kMessageHeaderSize)
287 client_->PostMessageFromNativeHost(
288 incoming_data_.substr(kMessageHeaderSize, message_size));
290 incoming_data_.erase(0, kMessageHeaderSize + message_size);
294 void NativeMessageProcessHost::DoWrite() {
295 DCHECK(task_runner_->BelongsToCurrentThread());
297 while (!write_pending_ && !closed_) {
298 if (!current_write_buffer_.get() ||
299 !current_write_buffer_->BytesRemaining()) {
300 if (write_queue_.empty())
302 current_write_buffer_ = new net::DrainableIOBuffer(
303 write_queue_.front().get(), write_queue_.front()->size());
308 write_stream_->Write(current_write_buffer_.get(),
309 current_write_buffer_->BytesRemaining(),
310 base::Bind(&NativeMessageProcessHost::OnWritten,
311 weak_factory_.GetWeakPtr()));
312 HandleWriteResult(result);
316 void NativeMessageProcessHost::HandleWriteResult(int result) {
317 DCHECK(task_runner_->BelongsToCurrentThread());
320 if (result == net::ERR_IO_PENDING) {
321 write_pending_ = true;
323 LOG(ERROR) << "Error when writing to Native Messaging host: " << result;
324 Close(kHostInputOuputError);
329 current_write_buffer_->DidConsume(result);
332 void NativeMessageProcessHost::OnWritten(int result) {
333 DCHECK(task_runner_->BelongsToCurrentThread());
335 DCHECK(write_pending_);
336 write_pending_ = false;
338 HandleWriteResult(result);
342 void NativeMessageProcessHost::Close(const std::string& error_message) {
343 DCHECK(task_runner_->BelongsToCurrentThread());
347 read_stream_.reset();
348 write_stream_.reset();
349 client_->CloseChannel(error_message);
352 if (process_handle_ != base::kNullProcessHandle) {
353 // Kill the host process if necessary to make sure we don't leave zombies.
354 // On OSX base::EnsureProcessTerminated() may block, so we have to post a
355 // task on the blocking pool.
356 #if defined(OS_MACOSX)
357 content::BrowserThread::PostBlockingPoolTask(
358 FROM_HERE, base::Bind(&base::EnsureProcessTerminated, process_handle_));
360 base::EnsureProcessTerminated(process_handle_);
362 process_handle_ = base::kNullProcessHandle;
366 } // namespace extensions