it2me_cli_host.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright 2019 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. #include "remoting/test/it2me_cli_host.h"
  5. #include "base/bind.h"
  6. #include "base/command_line.h"
  7. #include "base/json/json_reader.h"
  8. #include "base/json/json_writer.h"
  9. #include "base/run_loop.h"
  10. #include "base/threading/thread_task_runner_handle.h"
  11. #include "base/time/time.h"
  12. #include "net/base/network_change_notifier.h"
  13. #include "remoting/base/auto_thread_task_runner.h"
  14. #include "remoting/base/logging.h"
  15. #include "remoting/host/chromoting_host_context.h"
  16. #include "remoting/host/it2me/it2me_constants.h"
  17. #include "remoting/host/it2me/it2me_native_messaging_host.h"
  18. #include "remoting/host/policy_watcher.h"
  19. #include "remoting/test/test_oauth_token_getter.h"
  20. #include "remoting/test/test_token_storage.h"
  21. namespace remoting {
  22. namespace {
  23. constexpr char kCRDDebugLog[] = "_debug_log";
  24. constexpr char kSwitchNameHelp[] = "help";
  25. constexpr char kSwitchNameUsername[] = "username";
  26. constexpr char kSwitchNameStoragePath[] = "storage-path";
  27. std::unique_ptr<It2MeNativeMessagingHost> CreateNativeMessagingHost(
  28. scoped_refptr<AutoThreadTaskRunner> ui_task_runner) {
  29. auto context = ChromotingHostContext::Create(ui_task_runner);
  30. std::unique_ptr<PolicyWatcher> policy_watcher =
  31. PolicyWatcher::CreateWithTaskRunner(context->file_task_runner(),
  32. context->management_service());
  33. auto factory = std::make_unique<It2MeHostFactory>();
  34. return std::make_unique<It2MeNativeMessagingHost>(
  35. /* needs_elevation */ false, std::move(policy_watcher),
  36. std::move(context), std::move(factory));
  37. }
  38. } // namespace
  39. // static
  40. bool It2MeCliHost::ShouldPrintHelp() {
  41. return base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchNameHelp);
  42. }
  43. // static
  44. void It2MeCliHost::PrintHelp() {
  45. fprintf(stderr,
  46. "Usage: %s [--storage-path=<storage-path>] "
  47. "[--username=<example@gmail.com>]\n ",
  48. base::CommandLine::ForCurrentProcess()
  49. ->GetProgram()
  50. .AsUTF8Unsafe()
  51. .c_str());
  52. }
  53. It2MeCliHost::It2MeCliHost() {}
  54. It2MeCliHost::~It2MeCliHost() = default;
  55. void It2MeCliHost::Start() {
  56. base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
  57. std::string username = cmd_line->GetSwitchValueASCII(kSwitchNameUsername);
  58. base::FilePath storage_path =
  59. cmd_line->GetSwitchValuePath(kSwitchNameStoragePath);
  60. storage_ = test::TestTokenStorage::OnDisk(username, storage_path);
  61. token_getter_ = std::make_unique<test::TestOAuthTokenGetter>(storage_.get());
  62. base::RunLoop initialize_token_getter_loop;
  63. token_getter_->Initialize(initialize_token_getter_loop.QuitClosure());
  64. initialize_token_getter_loop.Run();
  65. base::RunLoop ui_loop;
  66. ui_task_runner_ = new AutoThreadTaskRunner(
  67. base::ThreadTaskRunnerHandle::Get(), ui_loop.QuitClosure());
  68. token_getter_->CallWithToken(base::BindOnce(
  69. &It2MeCliHost::StartCRDHostAndGetCode, base::Unretained(this)));
  70. std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier(
  71. net::NetworkChangeNotifier::CreateIfNeeded());
  72. ui_loop.Run();
  73. }
  74. void It2MeCliHost::PostMessageFromNativeHost(const std::string& message) {
  75. auto message_value = base::JSONReader::Read(message);
  76. if (!message_value || !message_value->is_dict()) {
  77. OnProtocolBroken("Message is not a dictionary");
  78. return;
  79. }
  80. auto* type_value =
  81. message_value->FindKeyOfType(kMessageType, base::Value::Type::STRING);
  82. if (!type_value) {
  83. OnProtocolBroken("Message without type");
  84. return;
  85. }
  86. std::string type = type_value->GetString();
  87. if (type == kHelloResponse) {
  88. OnHelloResponse();
  89. } else if (type == kConnectResponse) {
  90. // Ok, just ignore.
  91. } else if (type == kDisconnectResponse) {
  92. OnDisconnectResponse();
  93. } else if (type == kHostStateChangedMessage) {
  94. // Handle CRD host state changes
  95. auto* state_value =
  96. message_value->FindKeyOfType(kState, base::Value::Type::STRING);
  97. if (!state_value) {
  98. OnProtocolBroken("No state in message");
  99. return;
  100. }
  101. std::string state = state_value->GetString();
  102. if (state == kHostStateReceivedAccessCode) {
  103. OnStateReceivedAccessCode(*message_value);
  104. } else if (state == kHostStateConnected) {
  105. OnStateRemoteConnected(*message_value);
  106. } else if (state == kHostStateDisconnected) {
  107. OnStateRemoteDisconnected();
  108. } else if (state == kHostStateError || state == kHostStateDomainError) {
  109. OnStateError(state, *message_value);
  110. } else if (state == kHostStateStarting ||
  111. state == kHostStateRequestedAccessCode) {
  112. // Just ignore these states.
  113. } else {
  114. LOG(WARNING) << "Unhandled state: " << state;
  115. }
  116. } else if (type == kCRDDebugLog) {
  117. // The It2Me host already prints the log to stdout/stderr.
  118. } else {
  119. LOG(WARNING) << "Unknown message type: " << type;
  120. }
  121. }
  122. void It2MeCliHost::CloseChannel(const std::string& error_message) {
  123. LOG(ERROR) << "CRD Host closed channel: " << error_message;
  124. command_awaiting_crd_access_code_ = false;
  125. ShutdownHost();
  126. }
  127. void It2MeCliHost::SendMessageToHost(const std::string& type,
  128. base::Value params) {
  129. std::string message_json;
  130. params.SetKey(kMessageType, base::Value(type));
  131. base::JSONWriter::Write(params, &message_json);
  132. base::ThreadTaskRunnerHandle::Get()->PostTask(
  133. FROM_HERE, base::BindOnce(&It2MeCliHost::DoSendMessage,
  134. weak_factory_.GetWeakPtr(), message_json));
  135. }
  136. void It2MeCliHost::DoSendMessage(const std::string& json) {
  137. if (!host_)
  138. return;
  139. host_->OnMessage(json);
  140. }
  141. void It2MeCliHost::OnProtocolBroken(const std::string& message) {
  142. LOG(ERROR) << "Error communicating with CRD Host : " << message;
  143. command_awaiting_crd_access_code_ = false;
  144. ShutdownHost();
  145. }
  146. void It2MeCliHost::StartCRDHostAndGetCode(OAuthTokenGetter::Status status,
  147. const std::string& user_email,
  148. const std::string& access_token) {
  149. DCHECK(!host_);
  150. // Store all parameters for future connect call.
  151. base::Value connect_params(base::Value::Type::DICTIONARY);
  152. connect_params.SetKey(kUserName, base::Value(user_email));
  153. connect_params.SetKey(kAuthServiceWithToken,
  154. base::Value("oauth2:" + access_token));
  155. connect_params.SetKey(kSuppressUserDialogs, base::Value(true));
  156. connect_params.SetKey(kSuppressNotifications, base::Value(true));
  157. connect_params_ = std::move(connect_params);
  158. remote_connected_ = false;
  159. command_awaiting_crd_access_code_ = true;
  160. host_ = CreateNativeMessagingHost(ui_task_runner_);
  161. host_->Start(this);
  162. base::Value params(base::Value::Type::DICTIONARY);
  163. SendMessageToHost(kHelloMessage, std::move(params));
  164. }
  165. void It2MeCliHost::ShutdownHost() {
  166. if (!host_)
  167. return;
  168. base::ThreadTaskRunnerHandle::Get()->PostTask(
  169. FROM_HERE, base::BindOnce(&It2MeCliHost::DoShutdownHost,
  170. weak_factory_.GetWeakPtr()));
  171. }
  172. void It2MeCliHost::DoShutdownHost() {
  173. host_.reset();
  174. ui_task_runner_.reset();
  175. }
  176. void It2MeCliHost::OnHelloResponse() {
  177. // Host is initialized, start connection.
  178. SendMessageToHost(kConnectMessage, std::move(connect_params_));
  179. }
  180. void It2MeCliHost::OnDisconnectResponse() {
  181. // Should happen only when remoting session finished and we
  182. // have requested host to shut down, or when we have got second auth code
  183. // without receiving connection.
  184. DCHECK(!command_awaiting_crd_access_code_);
  185. DCHECK(!remote_connected_);
  186. ShutdownHost();
  187. }
  188. void It2MeCliHost::OnStateError(const std::string& error_state,
  189. const base::Value& message) {
  190. std::string error_message;
  191. if (error_state == kHostStateDomainError) {
  192. error_message = "CRD Error : Invalid domain";
  193. } else {
  194. auto* error_code_value =
  195. message.FindKeyOfType(kErrorMessageCode, base::Value::Type::STRING);
  196. if (error_code_value)
  197. error_message = error_code_value->GetString();
  198. else
  199. error_message = "Unknown CRD Error";
  200. }
  201. // Notify callback if command is still running.
  202. if (command_awaiting_crd_access_code_) {
  203. command_awaiting_crd_access_code_ = false;
  204. LOG(ERROR) << "CRD Error state " + error_state;
  205. }
  206. // Shut down host, if any
  207. ShutdownHost();
  208. }
  209. void It2MeCliHost::OnStateRemoteConnected(const base::Value& message) {
  210. remote_connected_ = true;
  211. auto* client_value =
  212. message.FindKeyOfType(kClient, base::Value::Type::STRING);
  213. if (client_value) {
  214. HOST_LOG << "Remote connection by " << client_value->GetString();
  215. }
  216. }
  217. void It2MeCliHost::OnStateRemoteDisconnected() {
  218. // There could be a connection attempt that was not successful, we will
  219. // receive "disconnected" message without actually receiving "connected".
  220. if (!remote_connected_)
  221. return;
  222. remote_connected_ = false;
  223. // Remote has disconnected, time to send "disconnect" that would result
  224. // in shutting down the host.
  225. base::Value params(base::Value::Type::DICTIONARY);
  226. SendMessageToHost(kDisconnectMessage, std::move(params));
  227. }
  228. void It2MeCliHost::OnStateReceivedAccessCode(const base::Value& message) {
  229. if (!command_awaiting_crd_access_code_) {
  230. if (!remote_connected_) {
  231. // We have already sent the access code back to the server which initiated
  232. // this CRD session through a remote command, and we can not send a new
  233. // access code. Assuming that the old access code is no longer valid, we
  234. // can only terminate the current CRD session.
  235. base::Value params(base::Value::Type::DICTIONARY);
  236. SendMessageToHost(kDisconnectMessage, std::move(params));
  237. }
  238. return;
  239. }
  240. auto* code_value =
  241. message.FindKeyOfType(kAccessCode, base::Value::Type::STRING);
  242. auto* code_lifetime_value =
  243. message.FindKeyOfType(kAccessCodeLifetime, base::Value::Type::INTEGER);
  244. if (!code_value || !code_lifetime_value) {
  245. OnProtocolBroken("Can not obtain access code");
  246. return;
  247. }
  248. command_awaiting_crd_access_code_ = false;
  249. // Prints the access code.
  250. base::TimeDelta expires_in = base::Seconds(code_lifetime_value->GetInt());
  251. HOST_LOG << "It2Me access code is generated: " << code_value->GetString();
  252. HOST_LOG << "Expires at: " << (base::Time::Now() + expires_in);
  253. }
  254. } // namespace remoting