summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-03 19:12:01 +0000
committerlambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-03 19:12:01 +0000
commit3f118f750972f1232033243312819b2acee9abab (patch)
tree847a432d52ac7122723e6e4ec6a7f391db88d56d
parent73ff7fe36a24d6366706dfae48dfacfe77a7ddd8 (diff)
downloadchromium_src-3f118f750972f1232033243312819b2acee9abab.zip
chromium_src-3f118f750972f1232033243312819b2acee9abab.tar.gz
chromium_src-3f118f750972f1232033243312819b2acee9abab.tar.bz2
Native Messaging host process for Chromoting Me2Me.
This adds a GYP target to build a Native Messaging host executable for all platforms that currently support Me2Me hosts. BUG=173509 TEST=manual Review URL: https://chromiumcodereview.appspot.com/12852005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@198164 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--remoting/host/setup/daemon_controller_linux.cc11
-rw-r--r--remoting/host/setup/native_messaging_host.cc427
-rw-r--r--remoting/host/setup/native_messaging_reader.cc164
-rw-r--r--remoting/host/setup/native_messaging_reader.h76
-rw-r--r--remoting/host/setup/native_messaging_writer.cc103
-rw-r--r--remoting/host/setup/native_messaging_writer.h37
-rw-r--r--remoting/remoting.gyp21
7 files changed, 837 insertions, 2 deletions
diff --git a/remoting/host/setup/daemon_controller_linux.cc b/remoting/host/setup/daemon_controller_linux.cc
index 216572f..a777346 100644
--- a/remoting/host/setup/daemon_controller_linux.cc
+++ b/remoting/host/setup/daemon_controller_linux.cc
@@ -117,8 +117,15 @@ static bool RunHostScriptWithTimeout(
command_line.AppendArg(args[i]);
}
base::ProcessHandle process_handle;
- if (!base::LaunchProcess(command_line, base::LaunchOptions(),
- &process_handle)) {
+
+ // Redirect the child's stdout to the parent's stderr. In the case where this
+ // parent process is a Native Messaging host, its stdout is used to send
+ // messages to the web-app.
+ base::FileHandleMappingVector fds_to_remap;
+ fds_to_remap.push_back(std::pair<int, int>(STDERR_FILENO, STDOUT_FILENO));
+ base::LaunchOptions options;
+ options.fds_to_remap = &fds_to_remap;
+ if (!base::LaunchProcess(command_line, options, &process_handle)) {
return false;
}
diff --git a/remoting/host/setup/native_messaging_host.cc b/remoting/host/setup/native_messaging_host.cc
new file mode 100644
index 0000000..b665f83
--- /dev/null
+++ b/remoting/host/setup/native_messaging_host.cc
@@ -0,0 +1,427 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/platform_file.h"
+#include "base/run_loop.h"
+#include "base/strings/stringize_macros.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "net/base/net_util.h"
+#include "remoting/base/rsa_key_pair.h"
+#include "remoting/host/logging.h"
+#include "remoting/host/pin_hash.h"
+#include "remoting/host/setup/daemon_controller.h"
+#include "remoting/host/setup/native_messaging_reader.h"
+#include "remoting/host/setup/native_messaging_writer.h"
+
+#if defined(OS_POSIX)
+#include <unistd.h>
+#endif
+
+namespace {
+
+// Helper to extract the "config" part of a message as a DictionaryValue.
+// Returns NULL on failure, and logs an error message.
+scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
+ const base::DictionaryValue& message) {
+ scoped_ptr<base::DictionaryValue> config_dict;
+ std::string config_str;
+ if (!message.GetString("config", &config_str)) {
+ LOG(ERROR) << "'config' not found.";
+ return config_dict.Pass();
+ }
+
+ // TODO(lambroslambrou): Fix the webapp to embed the config dictionary
+ // directly into the request, rather than as a serialized JSON string.
+ scoped_ptr<base::Value> config(
+ base::JSONReader::Read(config_str, base::JSON_ALLOW_TRAILING_COMMAS));
+ if (!config || !config->IsType(base::Value::TYPE_DICTIONARY)) {
+ LOG(ERROR) << "Bad config parameter.";
+ return config_dict.Pass();
+ }
+ config_dict.reset(reinterpret_cast<base::DictionaryValue*>(config.release()));
+ return config_dict.Pass();
+}
+
+} // namespace
+
+namespace remoting {
+
+// Implementation of the NativeMessaging host process.
+class NativeMessagingHost {
+ public:
+ NativeMessagingHost(
+ base::PlatformFile input,
+ base::PlatformFile output,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ const base::Closure& quit_closure);
+ ~NativeMessagingHost();
+
+ // Starts reading and processing messages.
+ void Start();
+
+ // Posts |quit_closure| to |caller_task_runner|. This gets called whenever an
+ // error is encountered during reading and processing messages.
+ void Shutdown();
+
+ private:
+ // Processes a message received from the client app.
+ void ProcessMessage(scoped_ptr<base::Value> message);
+
+ // These "Process.." methods handle specific request types. The |response|
+ // dictionary is pre-filled by ProcessMessage() with the parts of the
+ // response already known ("id" and "type" fields).
+ bool ProcessHello(const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response);
+ bool ProcessGetHostName(const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response);
+ bool ProcessGetPinHash(const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response);
+ bool ProcessGenerateKeyPair(const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response);
+ bool ProcessUpdateDaemonConfig(const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response);
+ bool ProcessGetDaemonConfig(const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response);
+ bool ProcessGetUsageStatsConsent(const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response);
+ bool ProcessStartDaemon(const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response);
+ bool ProcessStopDaemon(const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response);
+ bool ProcessGetDaemonState(const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response);
+
+ // Sends a response back to the client app. This can be called on either the
+ // main message loop or the DaemonController's internal thread, so it
+ // PostTask()s to the main thread if necessary.
+ void SendResponse(scoped_ptr<base::DictionaryValue> response);
+
+ // These Send... methods get called on the DaemonController's internal thread
+ // These methods fill in the |response| dictionary from the other parameters,
+ // and pass it to SendResponse().
+ void SendUpdateConfigResponse(scoped_ptr<base::DictionaryValue> response,
+ DaemonController::AsyncResult result);
+ void SendConfigResponse(scoped_ptr<base::DictionaryValue> response,
+ scoped_ptr<base::DictionaryValue> config);
+ void SendUsageStatsConsentResponse(
+ scoped_ptr<base::DictionaryValue> response,
+ bool supported,
+ bool allowed,
+ bool set_by_policy);
+ void SendAsyncResult(scoped_ptr<base::DictionaryValue> response,
+ DaemonController::AsyncResult result);
+
+ // Callbacks may be invoked by e.g. DaemonController during destruction,
+ // which use |weak_ptr_|, so it's important that it be the last member to be
+ // destroyed.
+ base::WeakPtr<NativeMessagingHost> weak_ptr_;
+
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
+ base::Closure quit_closure_;
+
+ NativeMessagingReader native_messaging_reader_;
+ NativeMessagingWriter native_messaging_writer_;
+
+ // The DaemonController may post tasks to this object during destruction (but
+ // not afterwards), so it needs to be destroyed before other members of this
+ // class (except for |weak_factory_|).
+ scoped_ptr<remoting::DaemonController> daemon_controller_;
+
+ base::WeakPtrFactory<NativeMessagingHost> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeMessagingHost);
+};
+
+NativeMessagingHost::NativeMessagingHost(
+ base::PlatformFile input,
+ base::PlatformFile output,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ const base::Closure& quit_closure)
+ : caller_task_runner_(caller_task_runner),
+ quit_closure_(quit_closure),
+ native_messaging_reader_(input),
+ native_messaging_writer_(output),
+ daemon_controller_(DaemonController::Create()),
+ weak_factory_(this) {
+ weak_ptr_ = weak_factory_.GetWeakPtr();
+}
+
+NativeMessagingHost::~NativeMessagingHost() {
+}
+
+void NativeMessagingHost::Start() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ native_messaging_reader_.Start(
+ base::Bind(&NativeMessagingHost::ProcessMessage, weak_ptr_),
+ base::Bind(&NativeMessagingHost::Shutdown, weak_ptr_));
+}
+
+void NativeMessagingHost::Shutdown() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+ if (!quit_closure_.is_null()) {
+ caller_task_runner_->PostTask(FROM_HERE, quit_closure_);
+ quit_closure_.Reset();
+ }
+}
+
+void NativeMessagingHost::ProcessMessage(scoped_ptr<base::Value> message) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+ const base::DictionaryValue* message_dict;
+ if (!message->GetAsDictionary(&message_dict)) {
+ LOG(ERROR) << "Expected DictionaryValue";
+ Shutdown();
+ }
+
+ scoped_ptr<base::DictionaryValue> response_dict(new base::DictionaryValue());
+
+ // If the client supplies an ID, it will expect it in the response. This
+ // might be a string or a number, so cope with both.
+ const base::Value* id;
+ if (message_dict->Get("id", &id))
+ response_dict->Set("id", id->DeepCopy());
+
+ std::string type;
+ if (!message_dict->GetString("type", &type)) {
+ LOG(ERROR) << "'type' not found";
+ Shutdown();
+ return;
+ }
+
+ response_dict->SetString("type", type + "Response");
+
+ bool success = false;
+ if (type == "hello") {
+ success = ProcessHello(*message_dict, response_dict.Pass());
+ } else if (type == "getHostName") {
+ success = ProcessGetHostName(*message_dict, response_dict.Pass());
+ } else if (type == "getPinHash") {
+ success = ProcessGetPinHash(*message_dict, response_dict.Pass());
+ } else if (type == "generateKeyPair") {
+ success = ProcessGenerateKeyPair(*message_dict, response_dict.Pass());
+ } else if (type == "updateDaemonConfig") {
+ success = ProcessUpdateDaemonConfig(*message_dict, response_dict.Pass());
+ } else if (type == "getDaemonConfig") {
+ success = ProcessGetDaemonConfig(*message_dict, response_dict.Pass());
+ } else if (type == "getUsageStatsConsent") {
+ success = ProcessGetUsageStatsConsent(*message_dict, response_dict.Pass());
+ } else if (type == "startDaemon") {
+ success = ProcessStartDaemon(*message_dict, response_dict.Pass());
+ } else if (type == "stopDaemon") {
+ success = ProcessStopDaemon(*message_dict, response_dict.Pass());
+ } else if (type == "getDaemonState") {
+ success = ProcessGetDaemonState(*message_dict, response_dict.Pass());
+ } else {
+ LOG(ERROR) << "Unsupported request type: " << type;
+ }
+
+ if (!success)
+ Shutdown();
+}
+
+bool NativeMessagingHost::ProcessHello(
+ const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response) {
+ response->SetString("version", STRINGIZE(VERSION));
+ SendResponse(response.Pass());
+ return true;
+}
+
+bool NativeMessagingHost::ProcessGetHostName(
+ const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response) {
+ response->SetString("hostname", net::GetHostName());
+ SendResponse(response.Pass());
+ return true;
+}
+
+bool NativeMessagingHost::ProcessGetPinHash(
+ const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response) {
+ std::string host_id;
+ if (!message.GetString("hostId", &host_id)) {
+ LOG(ERROR) << "'hostId' not found: " << message;
+ return false;
+ }
+ std::string pin;
+ if (!message.GetString("pin", &pin)) {
+ LOG(ERROR) << "'pin' not found: " << message;
+ return false;
+ }
+ response->SetString("hash", remoting::MakeHostPinHash(host_id, pin));
+ SendResponse(response.Pass());
+ return true;
+}
+
+bool NativeMessagingHost::ProcessGenerateKeyPair(
+ const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response) {
+ scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
+ response->SetString("private_key", key_pair->ToString());
+ response->SetString("public_key", key_pair->GetPublicKey());
+ SendResponse(response.Pass());
+ return true;
+}
+
+bool NativeMessagingHost::ProcessUpdateDaemonConfig(
+ const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response) {
+ scoped_ptr<base::DictionaryValue> config_dict =
+ ConfigDictionaryFromMessage(message);
+ if (!config_dict)
+ return false;
+
+ // base::Unretained() is safe because this object owns |daemon_controller_|
+ // which owns the thread that will run the callback.
+ daemon_controller_->UpdateConfig(
+ config_dict.Pass(),
+ base::Bind(&NativeMessagingHost::SendAsyncResult, base::Unretained(this),
+ base::Passed(&response)));
+ return true;
+}
+
+bool NativeMessagingHost::ProcessGetDaemonConfig(
+ const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response) {
+ daemon_controller_->GetConfig(base::Bind(
+ &NativeMessagingHost::SendConfigResponse,
+ base::Unretained(this), base::Passed(&response)));
+ return true;
+}
+
+bool NativeMessagingHost::ProcessGetUsageStatsConsent(
+ const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response) {
+ daemon_controller_->GetUsageStatsConsent(base::Bind(
+ &NativeMessagingHost::SendUsageStatsConsentResponse,
+ base::Unretained(this), base::Passed(&response)));
+ return true;
+}
+
+bool NativeMessagingHost::ProcessStartDaemon(
+ const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response) {
+ bool consent;
+ if (!message.GetBoolean("consent", &consent)) {
+ LOG(ERROR) << "'consent' not found.";
+ return false;
+ }
+
+ scoped_ptr<base::DictionaryValue> config_dict =
+ ConfigDictionaryFromMessage(message);
+ if (!config_dict)
+ return false;
+
+ daemon_controller_->SetConfigAndStart(
+ config_dict.Pass(), consent,
+ base::Bind(&NativeMessagingHost::SendAsyncResult, base::Unretained(this),
+ base::Passed(&response)));
+ return true;
+}
+
+bool NativeMessagingHost::ProcessStopDaemon(
+ const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response) {
+ daemon_controller_->Stop(base::Bind(
+ &NativeMessagingHost::SendAsyncResult, base::Unretained(this),
+ base::Passed(&response)));
+ return true;
+}
+
+bool NativeMessagingHost::ProcessGetDaemonState(
+ const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> response) {
+ // TODO(lambroslambrou): Send the state as a string instead of an integer,
+ // and update the web-app accordingly.
+ DaemonController::State state = daemon_controller_->GetState();
+ response->SetInteger("state", state);
+ SendResponse(response.Pass());
+ return true;
+}
+
+void NativeMessagingHost::SendResponse(
+ scoped_ptr<base::DictionaryValue> response) {
+ if (!caller_task_runner_->BelongsToCurrentThread()) {
+ caller_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &NativeMessagingHost::SendResponse, weak_ptr_,
+ base::Passed(&response)));
+ return;
+ }
+
+ if (!native_messaging_writer_.WriteMessage(*response))
+ Shutdown();
+}
+
+void NativeMessagingHost::SendConfigResponse(
+ scoped_ptr<base::DictionaryValue> response,
+ scoped_ptr<base::DictionaryValue> config) {
+ // TODO(lambroslambrou): Fix the web-app to accept the config dictionary
+ // directly embedded in the response, rather than as serialized JSON. See
+ // http://crbug.com/232135.
+ std::string config_json;
+ base::JSONWriter::Write(config.get(), &config_json);
+ response->SetString("config", config_json);
+ SendResponse(response.Pass());
+}
+
+void NativeMessagingHost::SendUsageStatsConsentResponse(
+ scoped_ptr<base::DictionaryValue> response,
+ bool supported,
+ bool allowed,
+ bool set_by_policy) {
+ response->SetBoolean("supported", supported);
+ response->SetBoolean("allowed", allowed);
+ response->SetBoolean("set_by_policy", set_by_policy);
+ SendResponse(response.Pass());
+}
+
+void NativeMessagingHost::SendAsyncResult(
+ scoped_ptr<base::DictionaryValue> response,
+ DaemonController::AsyncResult result) {
+ // TODO(lambroslambrou): Send the result as a string instead of an integer,
+ // and update the web-app accordingly. See http://crbug.com/232135.
+ response->SetInteger("result", result);
+ SendResponse(response.Pass());
+}
+
+} // namespace remoting
+
+int main(int argc, char** argv) {
+ // This object instance is required by Chrome code (such as MessageLoop).
+ base::AtExitManager exit_manager;
+
+ CommandLine::Init(argc, argv);
+ remoting::InitHostLogging();
+
+#if defined(OS_WIN)
+ base::PlatformFile read_file = GetStdHandle(STD_INPUT_HANDLE);
+ base::PlatformFile write_file = GetStdHandle(STD_OUTPUT_HANDLE);
+#elif defined(OS_POSIX)
+ base::PlatformFile read_file = STDIN_FILENO;
+ base::PlatformFile write_file = STDOUT_FILENO;
+#else
+#error Not implemented.
+#endif
+
+ base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
+ base::RunLoop run_loop;
+ remoting::NativeMessagingHost host(read_file, write_file,
+ message_loop.message_loop_proxy(),
+ run_loop.QuitClosure());
+ host.Start();
+ run_loop.Run();
+ return 0;
+}
diff --git a/remoting/host/setup/native_messaging_reader.cc b/remoting/host/setup/native_messaging_reader.cc
new file mode 100644
index 0000000..e98ac52
--- /dev/null
+++ b/remoting/host/setup/native_messaging_reader.cc
@@ -0,0 +1,164 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/setup/native_messaging_reader.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/values.h"
+#include "net/base/file_stream.h"
+
+namespace {
+
+// uint32 is specified in the protocol as the type for the message header.
+typedef uint32 MessageLengthType;
+
+const int kMessageHeaderSize = sizeof(MessageLengthType);
+
+// Limit the size of received messages, to avoid excessive memory-allocation in
+// this process, and potential overflow issues when casting to a signed 32-bit
+// int.
+const MessageLengthType kMaximumMessageSize = 1024 * 1024;
+
+} // namespace
+
+namespace remoting {
+
+class NativeMessagingReader::Core {
+ public:
+ Core(base::PlatformFile handle,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ scoped_refptr<base::SequencedTaskRunner> read_task_runner,
+ base::WeakPtr<NativeMessagingReader> reader_);
+ ~Core();
+
+ // Reads a message from the Native Messaging client and passes it to
+ // |message_callback_| on the originating thread. Called on the reader thread.
+ void ReadMessage();
+
+ private:
+ // Notify the reader's EOF callback when an error occurs or EOF is reached.
+ void NotifyEof();
+
+ net::FileStream read_stream_;
+
+ base::WeakPtr<NativeMessagingReader> reader_;
+
+ // Used to post the caller-supplied reader callbacks on the caller thread.
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
+
+ // Used to DCHECK that the reader code executes on the correct thread.
+ scoped_refptr<base::SequencedTaskRunner> read_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+NativeMessagingReader::Core::Core(
+ base::PlatformFile handle,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ scoped_refptr<base::SequencedTaskRunner> read_task_runner,
+ base::WeakPtr<NativeMessagingReader> reader)
+ : read_stream_(handle, base::PLATFORM_FILE_READ, NULL),
+ reader_(reader),
+ caller_task_runner_(caller_task_runner),
+ read_task_runner_(read_task_runner) {
+}
+
+NativeMessagingReader::Core::~Core() {
+}
+
+void NativeMessagingReader::Core::ReadMessage() {
+ DCHECK(read_task_runner_->RunsTasksOnCurrentThread());
+
+ // Keep reading messages until the stream is closed or an error occurs.
+ while (true) {
+ MessageLengthType message_length;
+ int read_result = read_stream_.ReadUntilComplete(
+ reinterpret_cast<char*>(&message_length), kMessageHeaderSize);
+ if (read_result != kMessageHeaderSize) {
+ LOG(ERROR) << "Failed to read message header, read returned "
+ << read_result;
+ NotifyEof();
+ return;
+ }
+
+ if (message_length > kMaximumMessageSize) {
+ LOG(ERROR) << "Message size too large: " << message_length;
+ NotifyEof();
+ return;
+ }
+
+ std::string message_json(message_length, '\0');
+ read_result = read_stream_.ReadUntilComplete(
+ string_as_array(&message_json), message_length);
+ if (read_result != static_cast<int>(message_length)) {
+ LOG(ERROR) << "Failed to read message body, read returned "
+ << read_result;
+ NotifyEof();
+ return;
+ }
+
+ scoped_ptr<base::Value> message(base::JSONReader::Read(message_json));
+ if (!message) {
+ LOG(ERROR) << "Failed to parse JSON message: " << message;
+ NotifyEof();
+ return;
+ }
+
+ // Notify callback of new message.
+ caller_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &NativeMessagingReader::InvokeMessageCallback, reader_,
+ base::Passed(&message)));
+ }
+}
+
+void NativeMessagingReader::Core::NotifyEof() {
+ DCHECK(read_task_runner_->RunsTasksOnCurrentThread());
+ caller_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &NativeMessagingReader::InvokeEofCallback, reader_));
+}
+
+NativeMessagingReader::NativeMessagingReader(base::PlatformFile handle)
+ : reader_thread_("Reader"),
+ weak_factory_(this) {
+ reader_thread_.Start();
+ read_task_runner_ = reader_thread_.message_loop_proxy();
+ core_.reset(new Core(handle, base::ThreadTaskRunnerHandle::Get(),
+ read_task_runner_, weak_factory_.GetWeakPtr()));
+}
+
+NativeMessagingReader::~NativeMessagingReader() {
+ read_task_runner_->DeleteSoon(FROM_HERE, core_.release());
+}
+
+void NativeMessagingReader::Start(MessageCallback message_callback,
+ base::Closure eof_callback) {
+ message_callback_ = message_callback;
+ eof_callback_ = eof_callback;
+
+ // base::Unretained is safe since |core_| is only deleted via the
+ // DeleteSoon task which is posted from this class's dtor.
+ read_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &NativeMessagingReader::Core::ReadMessage,
+ base::Unretained(core_.get())));
+}
+
+void NativeMessagingReader::InvokeMessageCallback(
+ scoped_ptr<base::Value> message) {
+ message_callback_.Run(message.Pass());
+}
+
+void NativeMessagingReader::InvokeEofCallback() {
+ eof_callback_.Run();
+}
+
+} // namespace remoting
diff --git a/remoting/host/setup/native_messaging_reader.h b/remoting/host/setup/native_messaging_reader.h
new file mode 100644
index 0000000..b9c81c3
--- /dev/null
+++ b/remoting/host/setup/native_messaging_reader.h
@@ -0,0 +1,76 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_HOST_SETUP_NATIVE_MESSAGING_READER_H_
+#define REMOTING_HOST_SETUP_NATIVE_MESSAGING_READER_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/platform_file.h"
+#include "base/threading/thread.h"
+
+namespace base {
+class SequencedTaskRunner;
+class Value;
+} // namespace base
+
+namespace net {
+class FileStream;
+} // namespace net
+
+namespace remoting {
+
+// This class is used for reading messages from the Native Messaging client
+// webapp.
+class NativeMessagingReader {
+ public:
+ typedef base::Callback<void(scoped_ptr<base::Value>)> MessageCallback;
+
+ explicit NativeMessagingReader(base::PlatformFile handle);
+ ~NativeMessagingReader();
+
+ // Begin reading messages from the Native Messaging client webapp, calling
+ // |message_callback| for each received message, or |eof_callback| if
+ // EOF or error is encountered. This method is asynchronous - the callbacks
+ // will be run on the same thread via PostTask. The caller should be prepared
+ // for these callbacks to be invoked right up until this object is destroyed.
+ void Start(MessageCallback message_callback, base::Closure eof_callback);
+
+ private:
+ class Core;
+ friend class Core;
+
+ // Wrappers posted to by the read thread to trigger the message and EOF
+ // callbacks on the caller thread, and have them safely dropped if the reader
+ // has been deleted before they are processed.
+ void InvokeMessageCallback(scoped_ptr<base::Value> message);
+ void InvokeEofCallback();
+
+ // Holds the information that the read thread needs to access, such as the
+ // FileStream, and the TaskRunner used for posting notifications back to this
+ // class.
+ scoped_ptr<Core> core_;
+
+ // Caller-supplied message and end-of-file callbacks.
+ MessageCallback message_callback_;
+ base::Closure eof_callback_;
+
+ // Separate thread used to read from the stream without blocking the main
+ // thread. net::FileStream's async API cannot be used here because, on
+ // Windows, it requires the file handle to have been opened for overlapped IO.
+ base::Thread reader_thread_;
+ scoped_refptr<base::SequencedTaskRunner> read_task_runner_;
+
+ // Allows the reader to be deleted safely even when tasks may be pending on
+ // it.
+ base::WeakPtrFactory<NativeMessagingReader> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeMessagingReader);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_SETUP_NATIVE_MESSAGING_READER_H_
diff --git a/remoting/host/setup/native_messaging_writer.cc b/remoting/host/setup/native_messaging_writer.cc
new file mode 100644
index 0000000..15d6cd7
--- /dev/null
+++ b/remoting/host/setup/native_messaging_writer.cc
@@ -0,0 +1,103 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/setup/native_messaging_writer.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/json/json_writer.h"
+
+namespace {
+
+// 4-byte type used for the message header.
+typedef uint32 MessageLengthType;
+
+// Defined as an int, for passing to APIs that take an int, to avoid
+// signed/unsigned warnings about implicit cast.
+const int kMessageHeaderSize = sizeof(MessageLengthType);
+
+// Limit the size of sent messages, since Chrome will not accept messages
+// larger than 1MB, and this helps deal with the problem of integer overflow
+// when passing sizes to net::FileStream APIs that take |int| parameters.
+// This is defined as size_t (unsigned type) so it can be compared with the
+// result of std::string::length() without compiler warnings.
+const size_t kMaximumMessageSize = 1024 * 1024;
+
+// Performs the same task as FileStream::WriteSync(), but ensures that exactly
+// |buffer_length| bytes are written. Unlike WriteSync(), a partial write may
+// only occur as a result of end-of-file or fatal error. Returns the number of
+// bytes written (buffer_length) or an error-code <= 0.
+//
+// TODO(lambroslambrou): Add this method to net::FileStream, with unit-tests.
+// See http://crbug.com/232202.
+int WriteUntilComplete(net::FileStream* out,
+ const char* buffer, int buffer_length) {
+ int written = 0;
+ while (written < buffer_length) {
+ int result = out->WriteSync(buffer + written, buffer_length - written);
+ if (result <= 0) {
+ return result;
+ }
+ DCHECK_LE(result, buffer_length - written);
+ written += result;
+ }
+ return written;
+}
+
+} // namespace
+
+namespace remoting {
+
+NativeMessagingWriter::NativeMessagingWriter(base::PlatformFile handle)
+ : write_stream_(handle, base::PLATFORM_FILE_WRITE, NULL),
+ fail_(false) {
+}
+
+NativeMessagingWriter::~NativeMessagingWriter() {
+}
+
+bool NativeMessagingWriter::WriteMessage(const base::Value& message) {
+ if (fail_) {
+ LOG(ERROR) << "Stream marked as corrupt.";
+ return false;
+ }
+
+ std::string message_json;
+ base::JSONWriter::Write(&message, &message_json);
+
+ CHECK_LE(message_json.length(), kMaximumMessageSize);
+
+ // Cast from size_t to the proper header type. The check above ensures this
+ // won't overflow.
+ MessageLengthType message_length =
+ static_cast<MessageLengthType>(message_json.length());
+
+ int result = WriteUntilComplete(
+ &write_stream_, reinterpret_cast<char*>(&message_length),
+ kMessageHeaderSize);
+ if (result != kMessageHeaderSize) {
+ LOG(ERROR) << "Failed to send message header, write returned " << result;
+ fail_ = true;
+ return false;
+ }
+
+ // The length check above ensures that the cast won't overflow a signed
+ // 32-bit int.
+ int message_length_as_int = message_length;
+
+ // CHECK needed since data() is undefined on an empty std::string.
+ CHECK(!message_json.empty());
+ result = WriteUntilComplete(&write_stream_, message_json.data(),
+ message_length_as_int);
+ if (result != message_length_as_int) {
+ LOG(ERROR) << "Failed to send message body, write returned " << result;
+ fail_ = true;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace remoting
diff --git a/remoting/host/setup/native_messaging_writer.h b/remoting/host/setup/native_messaging_writer.h
new file mode 100644
index 0000000..72e5345
--- /dev/null
+++ b/remoting/host/setup/native_messaging_writer.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_HOST_SETUP_NATIVE_MESSAGING_WRITER_H_
+#define REMOTING_HOST_SETUP_NATIVE_MESSAGING_WRITER_H_
+
+#include "base/platform_file.h"
+#include "net/base/file_stream.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+namespace remoting {
+
+// This class is used for sending messages to the Native Messaging client
+// webapp.
+class NativeMessagingWriter {
+ public:
+ explicit NativeMessagingWriter(base::PlatformFile handle);
+ ~NativeMessagingWriter();
+
+ // Sends a message to the Native Messaging client, returning true if
+ // successful.
+ bool WriteMessage(const base::Value& message);
+
+ private:
+ net::FileStream write_stream_;
+ bool fail_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeMessagingWriter);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_SETUP_NATIVE_MESSAGING_WRITER_H_
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index b9fa5b7..791f1e6 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -711,6 +711,27 @@
],
}, # end of target 'remoting_host_plugin'
+ {
+ 'target_name': 'remoting_native_messaging_host',
+ 'type': 'executable',
+ 'variables': { 'enable_wexit_time_destructors': 1, },
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'remoting_host',
+ 'remoting_host_logging',
+ 'remoting_host_setup_base',
+ ],
+ 'defines': [
+ 'VERSION=<(version_full)',
+ ],
+ 'sources': [
+ 'host/setup/native_messaging_host.cc',
+ 'host/setup/native_messaging_reader.cc',
+ 'host/setup/native_messaging_reader.h',
+ 'host/setup/native_messaging_writer.cc',
+ 'host/setup/native_messaging_writer.h',
+ ],
+ }, # end of target 'remoting_native_messaging_host'
], # end of 'targets'
}], # 'enable_remoting_host==1'