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()),
54 write_pending_(false),
56 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
58 task_runner_
= content::BrowserThread::GetMessageLoopProxyForThread(
59 content::BrowserThread::IO
);
62 NativeMessageProcessHost::~NativeMessageProcessHost() {
63 DCHECK(task_runner_
->BelongsToCurrentThread());
65 if (process_
.IsValid()) {
66 // Kill the host process if necessary to make sure we don't leave zombies.
67 // On OSX base::EnsureProcessTerminated() may block, so we have to post a
68 // task on the blocking pool.
69 #if defined(OS_MACOSX)
70 content::BrowserThread::PostBlockingPoolTask(
72 base::Bind(&base::EnsureProcessTerminated
, Passed(&process_
)));
74 base::EnsureProcessTerminated(process_
.Pass());
80 scoped_ptr
<NativeMessageHost
> NativeMessageHost::Create(
81 gfx::NativeView native_view
,
82 const std::string
& source_extension_id
,
83 const std::string
& native_host_name
,
84 bool allow_user_level
,
85 std::string
* error_message
) {
86 return NativeMessageProcessHost::CreateWithLauncher(
89 NativeProcessLauncher::CreateDefault(allow_user_level
, native_view
));
93 scoped_ptr
<NativeMessageHost
> NativeMessageProcessHost::CreateWithLauncher(
94 const std::string
& source_extension_id
,
95 const std::string
& native_host_name
,
96 scoped_ptr
<NativeProcessLauncher
> launcher
) {
97 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
99 scoped_ptr
<NativeMessageHost
> process(
100 new NativeMessageProcessHost(source_extension_id
,
104 return process
.Pass();
107 void NativeMessageProcessHost::LaunchHostProcess() {
108 DCHECK(task_runner_
->BelongsToCurrentThread());
110 GURL
origin(std::string(kExtensionScheme
) + "://" + source_extension_id_
);
111 launcher_
->Launch(origin
, native_host_name_
,
112 base::Bind(&NativeMessageProcessHost::OnHostProcessLaunched
,
113 weak_factory_
.GetWeakPtr()));
116 void NativeMessageProcessHost::OnHostProcessLaunched(
117 NativeProcessLauncher::LaunchResult result
,
118 base::Process process
,
119 base::File read_file
,
120 base::File write_file
) {
121 DCHECK(task_runner_
->BelongsToCurrentThread());
124 case NativeProcessLauncher::RESULT_INVALID_NAME
:
125 Close(kInvalidNameError
);
127 case NativeProcessLauncher::RESULT_NOT_FOUND
:
128 Close(kNotFoundError
);
130 case NativeProcessLauncher::RESULT_FORBIDDEN
:
131 Close(kForbiddenError
);
133 case NativeProcessLauncher::RESULT_FAILED_TO_START
:
134 Close(kFailedToStartError
);
136 case NativeProcessLauncher::RESULT_SUCCESS
:
140 process_
= process
.Pass();
141 #if defined(OS_POSIX)
142 // This object is not the owner of the file so it should not keep an fd.
143 read_file_
= read_file
.GetPlatformFile();
146 scoped_refptr
<base::TaskRunner
> task_runner(
147 content::BrowserThread::GetBlockingPool()->
148 GetTaskRunnerWithShutdownBehavior(
149 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN
));
151 read_stream_
.reset(new net::FileStream(read_file
.Pass(), task_runner
));
152 write_stream_
.reset(new net::FileStream(write_file
.Pass(), task_runner
));
158 void NativeMessageProcessHost::OnMessage(const std::string
& json
) {
159 DCHECK(task_runner_
->BelongsToCurrentThread());
164 // Allocate new buffer for the message.
165 scoped_refptr
<net::IOBufferWithSize
> buffer
=
166 new net::IOBufferWithSize(json
.size() + kMessageHeaderSize
);
168 // Copy size and content of the message to the buffer.
169 static_assert(sizeof(uint32
) == kMessageHeaderSize
,
170 "kMessageHeaderSize is incorrect");
171 *reinterpret_cast<uint32
*>(buffer
->data()) = json
.size();
172 memcpy(buffer
->data() + kMessageHeaderSize
, json
.data(), json
.size());
174 // Push new message to the write queue.
175 write_queue_
.push(buffer
);
177 // Send() may be called before the host process is started. In that case the
178 // message will be written when OnHostProcessLaunched() is called. If it's
179 // already started then write the message now.
184 void NativeMessageProcessHost::Start(Client
* client
) {
185 DCHECK(task_runner_
->BelongsToCurrentThread());
188 // It's safe to use base::Unretained() here because NativeMessagePort always
189 // deletes us on the IO thread.
190 task_runner_
->PostTask(
192 base::Bind(&NativeMessageProcessHost::LaunchHostProcess
,
193 weak_factory_
.GetWeakPtr()));
196 scoped_refptr
<base::SingleThreadTaskRunner
>
197 NativeMessageProcessHost::task_runner() const {
201 #if defined(OS_POSIX)
202 void NativeMessageProcessHost::OnFileCanReadWithoutBlocking(int fd
) {
203 DCHECK_EQ(fd
, read_file_
);
207 void NativeMessageProcessHost::OnFileCanWriteWithoutBlocking(int fd
) {
210 #endif // !defined(OS_POSIX)
212 void NativeMessageProcessHost::ReadNowForTesting() {
216 void NativeMessageProcessHost::WaitRead() {
220 DCHECK(!read_pending_
);
222 // On POSIX FileStream::Read() uses blocking thread pool, so it's better to
223 // wait for the file to become readable before calling DoRead(). Otherwise it
224 // would always be consuming one thread in the thread pool. On Windows
225 // FileStream uses overlapped IO, so that optimization isn't necessary there.
226 #if defined(OS_POSIX)
227 base::MessageLoopForIO::current()->WatchFileDescriptor(
228 read_file_
, false /* persistent */,
229 base::MessageLoopForIO::WATCH_READ
, &read_watcher_
, this);
230 #else // defined(OS_POSIX)
232 #endif // defined(!OS_POSIX)
235 void NativeMessageProcessHost::DoRead() {
236 DCHECK(task_runner_
->BelongsToCurrentThread());
238 while (!closed_
&& !read_pending_
) {
239 read_buffer_
= new net::IOBuffer(kReadBufferSize
);
241 read_stream_
->Read(read_buffer_
.get(), kReadBufferSize
,
242 base::Bind(&NativeMessageProcessHost::OnRead
,
243 weak_factory_
.GetWeakPtr()));
244 HandleReadResult(result
);
248 void NativeMessageProcessHost::OnRead(int result
) {
249 DCHECK(task_runner_
->BelongsToCurrentThread());
250 DCHECK(read_pending_
);
251 read_pending_
= false;
253 HandleReadResult(result
);
257 void NativeMessageProcessHost::HandleReadResult(int result
) {
258 DCHECK(task_runner_
->BelongsToCurrentThread());
264 ProcessIncomingData(read_buffer_
->data(), result
);
265 } else if (result
== net::ERR_IO_PENDING
) {
266 read_pending_
= true;
267 } else if (result
== 0 || result
== net::ERR_CONNECTION_RESET
) {
268 // On Windows we get net::ERR_CONNECTION_RESET for a broken pipe, while on
269 // Posix read() returns 0 in that case.
270 Close(kNativeHostExited
);
272 LOG(ERROR
) << "Error when reading from Native Messaging host: " << result
;
273 Close(kHostInputOuputError
);
277 void NativeMessageProcessHost::ProcessIncomingData(
278 const char* data
, int data_size
) {
279 DCHECK(task_runner_
->BelongsToCurrentThread());
281 incoming_data_
.append(data
, data_size
);
284 if (incoming_data_
.size() < kMessageHeaderSize
)
287 size_t message_size
=
288 *reinterpret_cast<const uint32
*>(incoming_data_
.data());
290 if (message_size
> kMaximumMessageSize
) {
291 LOG(ERROR
) << "Native Messaging host tried sending a message that is "
292 << message_size
<< " bytes long.";
293 Close(kHostInputOuputError
);
297 if (incoming_data_
.size() < message_size
+ kMessageHeaderSize
)
300 client_
->PostMessageFromNativeHost(
301 incoming_data_
.substr(kMessageHeaderSize
, message_size
));
303 incoming_data_
.erase(0, kMessageHeaderSize
+ message_size
);
307 void NativeMessageProcessHost::DoWrite() {
308 DCHECK(task_runner_
->BelongsToCurrentThread());
310 while (!write_pending_
&& !closed_
) {
311 if (!current_write_buffer_
.get() ||
312 !current_write_buffer_
->BytesRemaining()) {
313 if (write_queue_
.empty())
315 current_write_buffer_
= new net::DrainableIOBuffer(
316 write_queue_
.front().get(), write_queue_
.front()->size());
321 write_stream_
->Write(current_write_buffer_
.get(),
322 current_write_buffer_
->BytesRemaining(),
323 base::Bind(&NativeMessageProcessHost::OnWritten
,
324 weak_factory_
.GetWeakPtr()));
325 HandleWriteResult(result
);
329 void NativeMessageProcessHost::HandleWriteResult(int result
) {
330 DCHECK(task_runner_
->BelongsToCurrentThread());
333 if (result
== net::ERR_IO_PENDING
) {
334 write_pending_
= true;
336 LOG(ERROR
) << "Error when writing to Native Messaging host: " << result
;
337 Close(kHostInputOuputError
);
342 current_write_buffer_
->DidConsume(result
);
345 void NativeMessageProcessHost::OnWritten(int result
) {
346 DCHECK(task_runner_
->BelongsToCurrentThread());
348 DCHECK(write_pending_
);
349 write_pending_
= false;
351 HandleWriteResult(result
);
355 void NativeMessageProcessHost::Close(const std::string
& error_message
) {
356 DCHECK(task_runner_
->BelongsToCurrentThread());
360 read_stream_
.reset();
361 write_stream_
.reset();
362 client_
->CloseChannel(error_message
);
366 } // namespace extensions