15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef REMOTING_HOST_HEARTBEAT_SENDER_H_
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define REMOTING_HOST_HEARTBEAT_SENDER_H_
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/compiler_specific.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/gtest_prod_util.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/ref_counted.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/timer/timer.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "remoting/base/rsa_key_pair.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "remoting/jingle_glue/signal_strategy.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MessageLoopProxy;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace base
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace buzz {
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class XmlElement;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace buzz
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace remoting {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class RsaKeyPair;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class IqRequest;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class IqSender;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// HeartbeatSender periodically sends heartbeat stanzas to the Chromoting Bot.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Each heartbeat stanza looks as follows:
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// <iq type="set" to="remoting@bot.talk.google.com"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//     from="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client">
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   <rem:heartbeat rem:hostid="a1ddb11e-8aef-11df-bccf-18a905b9cb5a"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//                  rem:sequence-id="456"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//                  xmlns:rem="google:remoting">
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//     <rem:signature>.signature.</rem:signature>
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   </rem:heartbeat>
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// </iq>
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The sequence-id attribute of the heartbeat is a zero-based incrementally
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// increasing integer unique to each heartbeat from a single host.
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The Bot checks the value, and if it is incorrect, includes the
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// correct value in the result stanza. The host should then send another
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// heartbeat, with the correct sequence-id, and increment the sequence-id in
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// susbequent heartbeats.
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The signature is a base-64 encoded SHA-1 hash, signed with the host's
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// private RSA key. The message being signed is the full Jid concatenated with
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the sequence-id, separated by one space. For example, for the heartbeat
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// stanza above, the message that is signed is
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// "user@gmail.com/chromoting123123 456".
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The Bot sends the following result stanza in response to each successful
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// heartbeat:
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  <iq type="set" from="remoting@bot.talk.google.com"
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//      to="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client">
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//    <rem:heartbeat-result xmlns:rem="google:remoting">
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//      <rem:set-interval>300</rem:set-interval>
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//    </rem:heartbeat-result>
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  </iq>
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The set-interval tag is used to specify desired heartbeat interval
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// in seconds. The heartbeat-result and the set-interval tags are
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// optional. Host uses default heartbeat interval if it doesn't find
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// set-interval tag in the result Iq stanza it receives from the
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// server.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// If the heartbeat's sequence-id was incorrect, the Bot sends a result
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// stanza of this form:
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  <iq type="set" from="remoting@bot.talk.google.com"
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//      to="user@gmail.com/chromoting123123" id="5" xmlns="jabber:client">
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//    <rem:heartbeat-result xmlns:rem="google:remoting">
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//      <rem:expected-sequence-id>654</rem:expected-sequence-id>
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//    </rem:heartbeat-result>
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  </iq>
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class HeartbeatSender : public SignalStrategy::Listener {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  class Listener {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   public:
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    virtual ~Listener() { }
857d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
867d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    // Invoked after the first successful heartbeat.
877d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    virtual void OnHeartbeatSuccessful() = 0;
887d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Invoked when the host ID is permanently not recognized by the server.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    virtual void OnUnknownHostIdError() = 0;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |signal_strategy| and |delegate| must outlive this
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // object. Heartbeats will start when the supplied SignalStrategy
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // enters the CONNECTED state.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HeartbeatSender(Listener* listener,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  const std::string& host_id,
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  SignalStrategy* signal_strategy,
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  scoped_refptr<RsaKeyPair> key_pair,
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  const std::string& directory_bot_jid);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~HeartbeatSender();
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // SignalStrategy::Listener interface.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnSignalStrategyStateChange(
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SignalStrategy::State state) OVERRIDE;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual bool OnSignalStrategyIncomingStanza(
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const buzz::XmlElement* stanza) OVERRIDE;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, DoSendStanza);
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest,
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           DoSendStanzaWithExpectedSequenceId);
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, CreateHeartbeatMessage);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest, ProcessResponseSetInterval);
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FRIEND_TEST_ALL_PREFIXES(HeartbeatSenderTest,
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           ProcessResponseExpectedSequenceId);
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SendStanza();
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ResendStanza();
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void DoSendStanza();
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ProcessResponse(IqRequest* request, const buzz::XmlElement* response);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetInterval(int interval);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetSequenceId(int sequence_id);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Helper methods used by DoSendStanza() to generate heartbeat stanzas.
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<buzz::XmlElement> CreateHeartbeatMessage();
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<buzz::XmlElement> CreateSignature();
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Listener* listener_;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string host_id_;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SignalStrategy* signal_strategy_;
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<RsaKeyPair> key_pair_;
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string directory_bot_jid_;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<IqSender> iq_sender_;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<IqRequest> request_;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int interval_ms_;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::RepeatingTimer<HeartbeatSender> timer_;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::OneShotTimer<HeartbeatSender> timer_resend_;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int sequence_id_;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool sequence_id_was_set_;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int sequence_id_recent_set_num_;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool heartbeat_succeeded_;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int failed_startup_heartbeat_count_;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(HeartbeatSender);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace remoting
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // REMOTING_HOST_HEARTBEAT_SENDER_H_
151