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 
5 #include "remoting/host/heartbeat_sender.h"
6 
7 #include <math.h>
8 
9 #include <cstdint>
10 #include <utility>
11 
12 #include "base/bind.h"
13 #include "base/rand_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringize_macros.h"
16 #include "base/system/sys_info.h"
17 #include "base/time/time.h"
18 #include "build/build_config.h"
19 #include "net/base/network_interfaces.h"
20 #include "net/traffic_annotation/network_traffic_annotation.h"
21 #include "remoting/base/constants.h"
22 #include "remoting/base/logging.h"
23 #include "remoting/base/protobuf_http_client.h"
24 #include "remoting/base/protobuf_http_request.h"
25 #include "remoting/base/protobuf_http_request_config.h"
26 #include "remoting/base/protobuf_http_status.h"
27 #include "remoting/base/service_urls.h"
28 #include "remoting/host/host_config.h"
29 #include "remoting/host/host_details.h"
30 #include "remoting/host/server_log_entry_host.h"
31 #include "remoting/signaling/ftl_signal_strategy.h"
32 #include "remoting/signaling/signaling_address.h"
33 #include "services/network/public/cpp/shared_url_loader_factory.h"
34 
35 #if defined(OS_WIN)
36 #include "base/strings/utf_string_conversions.h"
37 #endif
38 
39 namespace remoting {
40 
41 namespace {
42 
43 constexpr char kHeartbeatPath[] = "/v1/directory:heartbeat";
44 
45 constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
46     net::DefineNetworkTrafficAnnotation("heartbeat_sender",
47                                         R"(
48         semantics {
49           sender: "Chrome Remote Desktop"
50           description:
51             "Sends heartbeat data to the Chrome Remote Desktop backend so that "
52             "the client knows about the presence of the host."
53           trigger:
54             "Starting a Chrome Remote Desktop host."
55           data:
56             "Chrome Remote Desktop Host ID and some non-PII information about "
57             "the host system such as the Chrome Remote Desktop version and the "
58             "OS version."
59           destination: OTHER
60           destination_other: "Chrome Remote Desktop directory service"
61         }
62         policy {
63           cookies_allowed: NO
64           setting:
65             "This request cannot be stopped in settings, but will not be sent "
66             "if the user does not use Chrome Remote Desktop."
67           policy_exception_justification:
68             "Not implemented."
69         })");
70 
71 constexpr base::TimeDelta kMinimumHeartbeatInterval =
72     base::TimeDelta::FromMinutes(3);
73 constexpr base::TimeDelta kHeartbeatResponseTimeout =
74     base::TimeDelta::FromSeconds(30);
75 constexpr base::TimeDelta kResendDelayOnHostNotFound =
76     base::TimeDelta::FromSeconds(10);
77 constexpr base::TimeDelta kResendDelayOnUnauthenticated =
78     base::TimeDelta::FromSeconds(10);
79 
80 constexpr int kMaxResendOnHostNotFoundCount =
81     12;  // 2 minutes (12 x 10 seconds).
82 constexpr int kMaxResendOnUnauthenticatedCount =
83     6;  // 1 minute (10 x 6 seconds).
84 
85 const net::BackoffEntry::Policy kBackoffPolicy = {
86     // Number of initial errors (in sequence) to ignore before applying
87     // exponential back-off rules.
88     0,
89 
90     // Initial delay for exponential back-off in ms. (10s)
91     10000,
92 
93     // Factor by which the waiting time will be multiplied.
94     2,
95 
96     // Fuzzing percentage. ex: 10% will spread requests randomly
97     // between 90%-100% of the calculated time.
98     0.5,
99 
100     // Maximum amount of time we are willing to delay our request in ms. (10m)
101     600000,
102 
103     // Time to keep an entry from being discarded even when it
104     // has no significant state, -1 to never discard.
105     -1,
106 
107     // Starts with initial delay.
108     false,
109 };
110 
GetHostname()111 std::string GetHostname() {
112 #if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_BSD)
113   return net::GetHostName();
114 #elif defined(OS_WIN)
115   wchar_t buffer[MAX_PATH] = {0};
116   DWORD size = MAX_PATH;
117   if (!::GetComputerNameExW(ComputerNameDnsFullyQualified, buffer, &size)) {
118     PLOG(ERROR) << "GetComputerNameExW failed";
119     return std::string();
120   }
121   std::string hostname;
122   if (!base::UTF16ToUTF8(buffer, size, &hostname)) {
123     LOG(ERROR) << "Failed to convert from UTF16 to UTF8";
124     return std::string();
125   }
126   return hostname;
127 #else
128   return std::string();
129 #endif
130 }
131 
132 }  // namespace
133 
134 class HeartbeatSender::HeartbeatClientImpl final
135     : public HeartbeatSender::HeartbeatClient {
136  public:
137   explicit HeartbeatClientImpl(
138       OAuthTokenGetter* oauth_token_getter,
139       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
140   ~HeartbeatClientImpl() override;
141 
142   void Heartbeat(std::unique_ptr<apis::v1::HeartbeatRequest> request,
143                  HeartbeatResponseCallback callback) override;
144   void CancelPendingRequests() override;
145 
146  private:
147   ProtobufHttpClient http_client_;
148   DISALLOW_COPY_AND_ASSIGN(HeartbeatClientImpl);
149 };
150 
HeartbeatClientImpl(OAuthTokenGetter * oauth_token_getter,scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)151 HeartbeatSender::HeartbeatClientImpl::HeartbeatClientImpl(
152     OAuthTokenGetter* oauth_token_getter,
153     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
154     : http_client_(ServiceUrls::GetInstance()->remoting_server_endpoint(),
155                    oauth_token_getter,
156                    url_loader_factory) {}
157 
158 HeartbeatSender::HeartbeatClientImpl::~HeartbeatClientImpl() = default;
159 
Heartbeat(std::unique_ptr<apis::v1::HeartbeatRequest> request,HeartbeatResponseCallback callback)160 void HeartbeatSender::HeartbeatClientImpl::Heartbeat(
161     std::unique_ptr<apis::v1::HeartbeatRequest> request,
162     HeartbeatResponseCallback callback) {
163   std::string host_offline_reason_or_empty_log =
164       request->has_host_offline_reason()
165           ? (", host_offline_reason: " + request->host_offline_reason())
166           : "";
167   HOST_LOG << "Sending outgoing heartbeat. tachyon_id: "
168            << request->tachyon_id() << host_offline_reason_or_empty_log;
169 
170   auto request_config =
171       std::make_unique<ProtobufHttpRequestConfig>(kTrafficAnnotation);
172   request_config->path = kHeartbeatPath;
173   request_config->request_message = std::move(request);
174   auto http_request =
175       std::make_unique<ProtobufHttpRequest>(std::move(request_config));
176   http_request->SetTimeoutDuration(kHeartbeatResponseTimeout);
177   http_request->SetResponseCallback(std::move(callback));
178   http_client_.ExecuteRequest(std::move(http_request));
179 }
180 
CancelPendingRequests()181 void HeartbeatSender::HeartbeatClientImpl::CancelPendingRequests() {
182   http_client_.CancelPendingRequests();
183 }
184 
185 // end of HeartbeatSender::HeartbeatClientImpl
186 
HeartbeatSender(Delegate * delegate,const std::string & host_id,SignalStrategy * signal_strategy,OAuthTokenGetter * oauth_token_getter,Observer * observer,scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,bool is_googler)187 HeartbeatSender::HeartbeatSender(
188     Delegate* delegate,
189     const std::string& host_id,
190     SignalStrategy* signal_strategy,
191     OAuthTokenGetter* oauth_token_getter,
192     Observer* observer,
193     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
194     bool is_googler)
195     : delegate_(delegate),
196       host_id_(host_id),
197       signal_strategy_(signal_strategy),
198       client_(std::make_unique<HeartbeatClientImpl>(oauth_token_getter,
199                                                     url_loader_factory)),
200       oauth_token_getter_(oauth_token_getter),
201       observer_(observer),
202       backoff_(&kBackoffPolicy) {
203   DCHECK(delegate_);
204   DCHECK(signal_strategy_);
205   DCHECK(observer_);
206 
207   signal_strategy_->AddListener(this);
208   OnSignalStrategyStateChange(signal_strategy_->GetState());
209   is_googler_ = is_googler;
210 }
211 
~HeartbeatSender()212 HeartbeatSender::~HeartbeatSender() {
213   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
214   signal_strategy_->RemoveListener(this);
215 }
216 
SetHostOfflineReason(const std::string & host_offline_reason,const base::TimeDelta & timeout,base::OnceCallback<void (bool success)> ack_callback)217 void HeartbeatSender::SetHostOfflineReason(
218     const std::string& host_offline_reason,
219     const base::TimeDelta& timeout,
220     base::OnceCallback<void(bool success)> ack_callback) {
221   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
222   DCHECK(!host_offline_reason_ack_callback_);
223 
224   host_offline_reason_ = host_offline_reason;
225   host_offline_reason_ack_callback_ = std::move(ack_callback);
226   host_offline_reason_timeout_timer_.Start(
227       FROM_HERE, timeout, this, &HeartbeatSender::OnHostOfflineReasonTimeout);
228   if (signal_strategy_->GetState() == SignalStrategy::State::CONNECTED) {
229     SendHeartbeat();
230   }
231 }
232 
OnSignalStrategyStateChange(SignalStrategy::State state)233 void HeartbeatSender::OnSignalStrategyStateChange(SignalStrategy::State state) {
234   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
235   switch (state) {
236     case SignalStrategy::State::CONNECTED:
237       SendHeartbeat();
238       break;
239     case SignalStrategy::State::DISCONNECTED:
240       client_->CancelPendingRequests();
241       heartbeat_timer_.AbandonAndStop();
242       break;
243     default:
244       // Do nothing
245       break;
246   }
247 }
248 
OnSignalStrategyIncomingStanza(const jingle_xmpp::XmlElement * stanza)249 bool HeartbeatSender::OnSignalStrategyIncomingStanza(
250     const jingle_xmpp::XmlElement* stanza) {
251   return false;
252 }
253 
OnHostOfflineReasonTimeout()254 void HeartbeatSender::OnHostOfflineReasonTimeout() {
255   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
256   DCHECK(host_offline_reason_ack_callback_);
257 
258   std::move(host_offline_reason_ack_callback_).Run(false);
259 }
260 
OnHostOfflineReasonAck()261 void HeartbeatSender::OnHostOfflineReasonAck() {
262   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
263   if (!host_offline_reason_ack_callback_) {
264     DCHECK(!host_offline_reason_timeout_timer_.IsRunning());
265     return;
266   }
267 
268   DCHECK(host_offline_reason_timeout_timer_.IsRunning());
269   host_offline_reason_timeout_timer_.AbandonAndStop();
270 
271   std::move(host_offline_reason_ack_callback_).Run(true);
272 }
273 
SendHeartbeat()274 void HeartbeatSender::SendHeartbeat() {
275   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
276 
277   if (signal_strategy_->GetState() != SignalStrategy::State::CONNECTED) {
278     LOG(WARNING) << "Not sending heartbeat because the signal strategy is not "
279                     "connected.";
280     return;
281   }
282 
283   VLOG(1) << "About to send heartbeat.";
284 
285   // Drop previous heartbeat and timer so that it doesn't interfere with the
286   // current one.
287   client_->CancelPendingRequests();
288   heartbeat_timer_.AbandonAndStop();
289 
290   client_->Heartbeat(
291       CreateHeartbeatRequest(),
292       base::BindOnce(&HeartbeatSender::OnResponse, base::Unretained(this)));
293   observer_->OnHeartbeatSent();
294 }
295 
OnResponse(const ProtobufHttpStatus & status,std::unique_ptr<apis::v1::HeartbeatResponse> response)296 void HeartbeatSender::OnResponse(
297     const ProtobufHttpStatus& status,
298     std::unique_ptr<apis::v1::HeartbeatResponse> response) {
299   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
300 
301   if (status.ok()) {
302     backoff_.Reset();
303 
304     // Notify listener of the first successful heartbeat.
305     if (!initial_heartbeat_sent_) {
306       delegate_->OnFirstHeartbeatSuccessful();
307       initial_heartbeat_sent_ = true;
308     }
309 
310     // Notify caller of SetHostOfflineReason that we got an ack and don't
311     // schedule another heartbeat.
312     if (!host_offline_reason_.empty()) {
313       OnHostOfflineReasonAck();
314       return;
315     }
316 
317     if (response->has_remote_command()) {
318       OnRemoteCommand(response->remote_command());
319     }
320   } else {
321     LOG(ERROR) << "Heartbeat failed. Error code: "
322                << static_cast<int>(status.error_code()) << ", "
323                << status.error_message();
324     backoff_.InformOfRequest(false);
325   }
326 
327   if (status.error_code() == ProtobufHttpStatus::Code::DEADLINE_EXCEEDED) {
328     LOG(ERROR) << "Heartbeat timed out.";
329   }
330 
331   // If the host was registered immediately before it sends a heartbeat,
332   // then server-side latency may prevent the server recognizing the
333   // host ID in the heartbeat. So even if all of the first few heartbeats
334   // get a "host ID not found" error, that's not a good enough reason to
335   // exit.
336   if (status.error_code() == ProtobufHttpStatus::Code::NOT_FOUND &&
337       (initial_heartbeat_sent_ ||
338        (backoff_.failure_count() > kMaxResendOnHostNotFoundCount))) {
339     delegate_->OnHostNotFound();
340     return;
341   }
342 
343   if (status.error_code() == ProtobufHttpStatus::Code::UNAUTHENTICATED) {
344     oauth_token_getter_->InvalidateCache();
345     if (backoff_.failure_count() > kMaxResendOnUnauthenticatedCount) {
346       delegate_->OnAuthFailed();
347       return;
348     }
349   }
350 
351   // Calculate delay before sending the next message.
352   base::TimeDelta delay;
353   // See CoreErrorDomainTranslator.java for the mapping
354   switch (status.error_code()) {
355     case ProtobufHttpStatus::Code::OK:
356       delay = base::TimeDelta::FromSeconds(response->set_interval_seconds());
357       if (delay < kMinimumHeartbeatInterval) {
358         LOG(WARNING) << "Received suspicious set_interval_seconds: " << delay
359                      << ". Using minimum interval: "
360                      << kMinimumHeartbeatInterval;
361         delay = kMinimumHeartbeatInterval;
362       }
363       break;
364     case ProtobufHttpStatus::Code::NOT_FOUND:
365       delay = kResendDelayOnHostNotFound;
366       break;
367     case ProtobufHttpStatus::Code::UNAUTHENTICATED:
368       delay = kResendDelayOnUnauthenticated;
369       break;
370     default:
371       delay = backoff_.GetTimeUntilRelease();
372       LOG(ERROR) << "Heartbeat failed due to unexpected error. Will retry in "
373                  << delay;
374       break;
375   }
376 
377   heartbeat_timer_.Start(FROM_HERE, delay, this,
378                          &HeartbeatSender::SendHeartbeat);
379 }
380 
OnRemoteCommand(apis::v1::HeartbeatResponse::RemoteCommand remote_command)381 void HeartbeatSender::OnRemoteCommand(
382     apis::v1::HeartbeatResponse::RemoteCommand remote_command) {
383   HOST_LOG << "Received remote command: "
384            << apis::v1::HeartbeatResponse::RemoteCommand_Name(remote_command)
385            << "(" << remote_command << ")";
386   switch (remote_command) {
387     case apis::v1::HeartbeatResponse::RESTART_HOST:
388       delegate_->OnRemoteRestartHost();
389       break;
390     default:
391       LOG(WARNING) << "Unknown remote command: " << remote_command;
392       break;
393   }
394 }
395 
396 std::unique_ptr<apis::v1::HeartbeatRequest>
CreateHeartbeatRequest()397 HeartbeatSender::CreateHeartbeatRequest() {
398   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
399 
400   auto heartbeat = std::make_unique<apis::v1::HeartbeatRequest>();
401   heartbeat->set_tachyon_id(signal_strategy_->GetLocalAddress().id());
402   heartbeat->set_host_id(host_id_);
403   if (!host_offline_reason_.empty()) {
404     heartbeat->set_host_offline_reason(host_offline_reason_);
405   }
406   heartbeat->set_host_version(STRINGIZE(VERSION));
407   heartbeat->set_host_os_name(GetHostOperatingSystemName());
408   heartbeat->set_host_os_version(GetHostOperatingSystemVersion());
409   heartbeat->set_host_cpu_type(base::SysInfo::OperatingSystemArchitecture());
410   heartbeat->set_is_initial_heartbeat(!initial_heartbeat_sent_);
411 
412   if (is_googler_) {
413     std::string hostname = GetHostname();
414     if (!hostname.empty()) {
415       heartbeat->set_hostname(hostname);
416     }
417   }
418 
419   return heartbeat;
420 }
421 
422 }  // namespace remoting
423