1 // Copyright 2013 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/host_status_sender.h"
6
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/stringize_macros.h"
9 #include "base/time/time.h"
10 #include "remoting/base/constants.h"
11 #include "remoting/base/logging.h"
12 #include "remoting/host/server_log_entry.h"
13 #include "remoting/jingle_glue/iq_sender.h"
14 #include "remoting/jingle_glue/signal_strategy.h"
15 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
16 #include "third_party/libjingle/source/talk/xmpp/constants.h"
17
18 using buzz::QName;
19 using buzz::XmlElement;
20
21 namespace remoting {
22
23 namespace {
24
25 const char kHostStatusTag[] = "host-status";
26 const char kHostIdAttr[] = "hostid";
27 const char kExitCodeAttr[] = "exit-code";
28 const char kHostVersionTag[] = "host-version";
29 const char kSignatureTag[] = "signature";
30 const char kStatusAttr[] = "status";
31 const char kSignatureTimeAttr[] = "time";
32
33 } // namespace
34
35 const char* const HostStatusSender::host_status_strings_[] =
36 {"OFFLINE", "ONLINE"};
37
HostStatusSender(const std::string & host_id,SignalStrategy * signal_strategy,scoped_refptr<RsaKeyPair> key_pair,const std::string & directory_bot_jid)38 HostStatusSender::HostStatusSender(
39 const std::string& host_id,
40 SignalStrategy* signal_strategy,
41 scoped_refptr<RsaKeyPair> key_pair,
42 const std::string& directory_bot_jid)
43 : host_id_(host_id),
44 signal_strategy_(signal_strategy),
45 key_pair_(key_pair),
46 directory_bot_jid_(directory_bot_jid) {
47 DCHECK(signal_strategy_);
48 DCHECK(key_pair_.get());
49
50 signal_strategy_->AddListener(this);
51 }
52
~HostStatusSender()53 HostStatusSender::~HostStatusSender() {
54 signal_strategy_->RemoveListener(this);
55 }
56
OnSignalStrategyStateChange(SignalStrategy::State state)57 void HostStatusSender::OnSignalStrategyStateChange(
58 SignalStrategy::State state) {
59 if (state == SignalStrategy::CONNECTED)
60 iq_sender_.reset(new IqSender(signal_strategy_));
61 else if (state == SignalStrategy::DISCONNECTED)
62 iq_sender_.reset();
63 }
64
OnSignalStrategyIncomingStanza(const XmlElement * stanza)65 bool HostStatusSender::OnSignalStrategyIncomingStanza(
66 const XmlElement* stanza) {
67 return false;
68 }
69
SendOfflineStatus(HostExitCodes exit_code)70 void HostStatusSender::SendOfflineStatus(HostExitCodes exit_code) {
71 SendHostStatus(OFFLINE, exit_code);
72 }
73
SendOnlineStatus()74 void HostStatusSender::SendOnlineStatus() {
75 SendHostStatus(ONLINE, kSuccessExitCode);
76 }
77
SendHostStatus(HostStatus status,HostExitCodes exit_code)78 void HostStatusSender::SendHostStatus(HostStatus status,
79 HostExitCodes exit_code) {
80 SignalStrategy::State state = signal_strategy_->GetState();
81 if (state == SignalStrategy::CONNECTED) {
82 HOST_LOG << "Sending host status '"
83 << HostStatusToString(status)
84 << "' to "
85 << directory_bot_jid_;
86
87 iq_sender_->SendIq(buzz::STR_SET,
88 directory_bot_jid_,
89 CreateHostStatusMessage(status, exit_code),
90 IqSender::ReplyCallback());
91 } else {
92 HOST_LOG << "Cannot send host status to '"
93 << directory_bot_jid_
94 << " ' because the state of the SignalStrategy is "
95 << state;
96 }
97 }
98
CreateHostStatusMessage(HostStatus status,HostExitCodes exit_code)99 scoped_ptr<XmlElement> HostStatusSender::CreateHostStatusMessage(
100 HostStatus status, HostExitCodes exit_code) {
101 // Create host status stanza.
102 scoped_ptr<XmlElement> host_status(new XmlElement(
103 QName(kChromotingXmlNamespace, kHostStatusTag)));
104 host_status->AddAttr(
105 QName(kChromotingXmlNamespace, kHostIdAttr), host_id_);
106 host_status->AddAttr(
107 QName(kChromotingXmlNamespace, kStatusAttr), HostStatusToString(status));
108
109 if (status == OFFLINE) {
110 host_status->AddAttr(
111 QName(kChromotingXmlNamespace, kExitCodeAttr),
112 ExitCodeToString(exit_code));
113 }
114
115 host_status->AddElement(CreateSignature(status, exit_code).release());
116
117 // Append host version.
118 scoped_ptr<XmlElement> version_tag(new XmlElement(
119 QName(kChromotingXmlNamespace, kHostVersionTag)));
120 version_tag->AddText(STRINGIZE(VERSION));
121 host_status->AddElement(version_tag.release());
122
123 // Append log message (which isn't signed).
124 scoped_ptr<XmlElement> log(ServerLogEntry::MakeStanza());
125 scoped_ptr<ServerLogEntry> log_entry(
126 ServerLogEntry::MakeForHostStatus(status, exit_code));
127 log_entry->AddHostFields();
128 log->AddElement(log_entry->ToStanza().release());
129 host_status->AddElement(log.release());
130 return host_status.Pass();
131 }
132
CreateSignature(HostStatus status,HostExitCodes exit_code)133 scoped_ptr<XmlElement> HostStatusSender::CreateSignature(
134 HostStatus status, HostExitCodes exit_code) {
135 scoped_ptr<XmlElement> signature_tag(new XmlElement(
136 QName(kChromotingXmlNamespace, kSignatureTag)));
137
138 // Number of seconds since epoch (Jan 1, 1970).
139 int64 time = static_cast<int64>(base::Time::Now().ToDoubleT());
140 std::string time_str(base::Int64ToString(time));
141
142 signature_tag->AddAttr(
143 QName(kChromotingXmlNamespace, kSignatureTimeAttr), time_str);
144
145 // Add a time stamp to the signature to prevent replay attacks.
146 std::string message =
147 signal_strategy_->GetLocalJid() +
148 " " +
149 time_str +
150 " " +
151 HostStatusToString(status);
152
153 if (status == OFFLINE)
154 message += std::string(" ") + ExitCodeToString(exit_code);
155
156 std::string signature(key_pair_->SignMessage(message));
157 signature_tag->AddText(signature);
158
159 return signature_tag.Pass();
160 }
161
162 } // namespace remoting
163