Home | History | Annotate | Download | only in messaging
      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