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