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 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 54 HostStatusSender::~HostStatusSender() { 55 signal_strategy_->RemoveListener(this); 56 } 57 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 66 bool HostStatusSender::OnSignalStrategyIncomingStanza( 67 const XmlElement* stanza) { 68 return false; 69 } 70 71 void HostStatusSender::SendOfflineStatus(HostExitCodes exit_code) { 72 SendHostStatus(OFFLINE, exit_code); 73 } 74 75 void HostStatusSender::SendOnlineStatus() { 76 SendHostStatus(ONLINE, kSuccessExitCode); 77 } 78 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 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 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