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. 4 5 #include "chrome/browser/extensions/api/messaging/native_message_process_host.h" 6 7 #include "base/bind.h" 8 #include "base/files/file_path.h" 9 #include "base/logging.h" 10 #include "base/platform_file.h" 11 #include "base/threading/sequenced_worker_pool.h" 12 #include "base/values.h" 13 #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h" 14 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h" 15 #include "chrome/common/chrome_version_info.h" 16 #include "chrome/common/extensions/features/feature.h" 17 #include "extensions/common/constants.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" 22 #include "url/gurl.h" 23 24 namespace { 25 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; 30 31 // Message header contains 4-byte integer size of the message. 32 const size_t kMessageHeaderSize = 4; 33 34 // Size of the buffer to be allocated for each read. 35 const size_t kReadBufferSize = 4096; 36 37 const char kFailedToStartError[] = "Failed to start native messaging host."; 38 const char kInvalidNameError[] = 39 "Invalid native messaging host name specified."; 40 const char kNotFoundError[] = "Specified native messaging host not found."; 41 const char kForbiddenError[] = 42 "Access to the specified native messaging host is forbidden."; 43 const char kHostInputOuputError[] = 44 "Error when communicating with the native messaging host."; 45 46 } // namespace 47 48 namespace extensions { 49 50 NativeMessageProcessHost::NativeMessageProcessHost( 51 base::WeakPtr<Client> weak_client_ui, 52 const std::string& source_extension_id, 53 const std::string& native_host_name, 54 int destination_port, 55 scoped_ptr<NativeProcessLauncher> launcher) 56 : weak_client_ui_(weak_client_ui), 57 source_extension_id_(source_extension_id), 58 native_host_name_(native_host_name), 59 destination_port_(destination_port), 60 launcher_(launcher.Pass()), 61 closed_(false), 62 read_pending_(false), 63 read_eof_(false), 64 write_pending_(false) { 65 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 66 67 // It's safe to use base::Unretained() here because NativeMessagePort always 68 // deletes us on the IO thread. 69 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, 70 base::Bind(&NativeMessageProcessHost::LaunchHostProcess, 71 base::Unretained(this))); 72 } 73 74 NativeMessageProcessHost::~NativeMessageProcessHost() { 75 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 76 Close(std::string()); 77 } 78 79 // static 80 scoped_ptr<NativeMessageProcessHost> NativeMessageProcessHost::Create( 81 gfx::NativeView native_view, 82 base::WeakPtr<Client> weak_client_ui, 83 const std::string& source_extension_id, 84 const std::string& native_host_name, 85 int destination_port) { 86 return CreateWithLauncher(weak_client_ui, source_extension_id, 87 native_host_name, destination_port, 88 NativeProcessLauncher::CreateDefault(native_view)); 89 } 90 91 // static 92 scoped_ptr<NativeMessageProcessHost> 93 NativeMessageProcessHost::CreateWithLauncher( 94 base::WeakPtr<Client> weak_client_ui, 95 const std::string& source_extension_id, 96 const std::string& native_host_name, 97 int destination_port, 98 scoped_ptr<NativeProcessLauncher> launcher) { 99 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 100 101 scoped_ptr<NativeMessageProcessHost> process(new NativeMessageProcessHost( 102 weak_client_ui, source_extension_id, native_host_name, 103 destination_port, launcher.Pass())); 104 105 return process.Pass(); 106 } 107 108 void NativeMessageProcessHost::LaunchHostProcess() { 109 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 110 111 GURL origin(std::string(kExtensionScheme) + "://" + source_extension_id_); 112 launcher_->Launch(origin, native_host_name_, 113 base::Bind(&NativeMessageProcessHost::OnHostProcessLaunched, 114 base::Unretained(this))); 115 } 116 117 void NativeMessageProcessHost::OnHostProcessLaunched( 118 NativeProcessLauncher::LaunchResult result, 119 base::PlatformFile read_file, 120 base::PlatformFile write_file) { 121 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 122 123 switch (result) { 124 case NativeProcessLauncher::RESULT_INVALID_NAME: 125 Close(kInvalidNameError); 126 return; 127 case NativeProcessLauncher::RESULT_NOT_FOUND: 128 Close(kNotFoundError); 129 return; 130 case NativeProcessLauncher::RESULT_FORBIDDEN: 131 Close(kForbiddenError); 132 return; 133 case NativeProcessLauncher::RESULT_FAILED_TO_START: 134 Close(kFailedToStartError); 135 return; 136 case NativeProcessLauncher::RESULT_SUCCESS: 137 break; 138 } 139 140 read_file_ = read_file; 141 142 scoped_refptr<base::TaskRunner> task_runner( 143 content::BrowserThread::GetBlockingPool()-> 144 GetTaskRunnerWithShutdownBehavior( 145 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)); 146 147 read_stream_.reset(new net::FileStream( 148 read_file, base::PLATFORM_FILE_READ | base::PLATFORM_FILE_ASYNC, NULL, 149 task_runner)); 150 write_stream_.reset(new net::FileStream( 151 write_file, base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC, NULL, 152 task_runner)); 153 154 WaitRead(); 155 DoWrite(); 156 } 157 158 void NativeMessageProcessHost::Send(const std::string& json) { 159 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 160 161 if (closed_) 162 return; 163 164 // Allocate new buffer for the message. 165 scoped_refptr<net::IOBufferWithSize> buffer = 166 new net::IOBufferWithSize(json.size() + kMessageHeaderSize); 167 168 // Copy size and content of the message to the buffer. 169 COMPILE_ASSERT(sizeof(uint32) == kMessageHeaderSize, incorrect_header_size); 170 *reinterpret_cast<uint32*>(buffer->data()) = json.size(); 171 memcpy(buffer->data() + kMessageHeaderSize, json.data(), json.size()); 172 173 // Push new message to the write queue. 174 write_queue_.push(buffer); 175 176 // Send() may be called before the host process is started. In that case the 177 // message will be written when OnHostProcessLaunched() is called. If it's 178 // already started then write the message now. 179 if (write_stream_) 180 DoWrite(); 181 } 182 183 #if defined(OS_POSIX) 184 void NativeMessageProcessHost::OnFileCanReadWithoutBlocking(int fd) { 185 DCHECK_EQ(fd, read_file_); 186 DoRead(); 187 } 188 189 void NativeMessageProcessHost::OnFileCanWriteWithoutBlocking(int fd) { 190 NOTREACHED(); 191 } 192 #endif // !defined(OS_POSIX) 193 194 void NativeMessageProcessHost::ReadNowForTesting() { 195 DoRead(); 196 } 197 198 void NativeMessageProcessHost::WaitRead() { 199 if (closed_ || read_eof_) 200 return; 201 202 DCHECK(!read_pending_); 203 204 // On POSIX FileStream::Read() uses blocking thread pool, so it's better to 205 // wait for the file to become readable before calling DoRead(). Otherwise it 206 // would always be consuming one thread in the thread pool. On Windows 207 // FileStream uses overlapped IO, so that optimization isn't necessary there. 208 #if defined(OS_POSIX) 209 base::MessageLoopForIO::current()->WatchFileDescriptor( 210 read_file_, false /* persistent */, base::MessageLoopForIO::WATCH_READ, 211 &read_watcher_, this); 212 #else // defined(OS_POSIX) 213 DoRead(); 214 #endif // defined(!OS_POSIX) 215 } 216 217 void NativeMessageProcessHost::DoRead() { 218 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 219 220 while (!closed_ && !read_eof_ && !read_pending_) { 221 read_buffer_ = new net::IOBuffer(kReadBufferSize); 222 int result = read_stream_->Read( 223 read_buffer_.get(), 224 kReadBufferSize, 225 base::Bind(&NativeMessageProcessHost::OnRead, base::Unretained(this))); 226 HandleReadResult(result); 227 } 228 } 229 230 void NativeMessageProcessHost::OnRead(int result) { 231 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 232 DCHECK(read_pending_); 233 read_pending_ = false; 234 235 HandleReadResult(result); 236 WaitRead(); 237 } 238 239 void NativeMessageProcessHost::HandleReadResult(int result) { 240 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 241 242 if (closed_) 243 return; 244 245 if (result > 0) { 246 ProcessIncomingData(read_buffer_->data(), result); 247 } else if (result == 0) { 248 read_eof_ = true; 249 } else if (result == net::ERR_IO_PENDING) { 250 read_pending_ = true; 251 } else { 252 LOG(ERROR) << "Error when reading from Native Messaging host: " << result; 253 Close(kHostInputOuputError); 254 } 255 } 256 257 void NativeMessageProcessHost::ProcessIncomingData( 258 const char* data, int data_size) { 259 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 260 261 incoming_data_.append(data, data_size); 262 263 while (true) { 264 if (incoming_data_.size() < kMessageHeaderSize) 265 return; 266 267 size_t message_size = 268 *reinterpret_cast<const uint32*>(incoming_data_.data()); 269 270 if (message_size > kMaximumMessageSize) { 271 LOG(ERROR) << "Native Messaging host tried sending a message that is " 272 << message_size << " bytes long."; 273 Close(kHostInputOuputError); 274 return; 275 } 276 277 if (incoming_data_.size() < message_size + kMessageHeaderSize) 278 return; 279 280 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 281 base::Bind(&Client::PostMessageFromNativeProcess, weak_client_ui_, 282 destination_port_, 283 incoming_data_.substr(kMessageHeaderSize, message_size))); 284 285 incoming_data_.erase(0, kMessageHeaderSize + message_size); 286 } 287 } 288 289 void NativeMessageProcessHost::DoWrite() { 290 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 291 292 while (!write_pending_ && !closed_) { 293 if (!current_write_buffer_.get() || 294 !current_write_buffer_->BytesRemaining()) { 295 if (write_queue_.empty()) 296 return; 297 current_write_buffer_ = new net::DrainableIOBuffer( 298 write_queue_.front().get(), write_queue_.front()->size()); 299 write_queue_.pop(); 300 } 301 302 int result = 303 write_stream_->Write(current_write_buffer_.get(), 304 current_write_buffer_->BytesRemaining(), 305 base::Bind(&NativeMessageProcessHost::OnWritten, 306 base::Unretained(this))); 307 HandleWriteResult(result); 308 } 309 } 310 311 void NativeMessageProcessHost::HandleWriteResult(int result) { 312 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 313 314 if (result <= 0) { 315 if (result == net::ERR_IO_PENDING) { 316 write_pending_ = true; 317 } else { 318 LOG(ERROR) << "Error when writing to Native Messaging host: " << result; 319 Close(kHostInputOuputError); 320 } 321 return; 322 } 323 324 current_write_buffer_->DidConsume(result); 325 } 326 327 void NativeMessageProcessHost::OnWritten(int result) { 328 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 329 330 DCHECK(write_pending_); 331 write_pending_ = false; 332 333 HandleWriteResult(result); 334 DoWrite(); 335 } 336 337 void NativeMessageProcessHost::Close(const std::string& error_message) { 338 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 339 340 if (!closed_) { 341 closed_ = true; 342 read_stream_.reset(); 343 write_stream_.reset(); 344 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 345 base::Bind(&Client::CloseChannel, weak_client_ui_, 346 destination_port_, error_message)); 347 } 348 } 349 350 } // namespace extensions 351