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