blob: f10a69b2f32888af82e78ec5c0b9c237c49c65d9 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/xmpp_register_support_host_request.h"
#include <stdint.h>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringize_macros.h"
#include "base/time/time.h"
#include "remoting/base/constants.h"
#include "remoting/host/host_config.h"
#include "remoting/host/host_details.h"
#include "remoting/protocol/errors.h"
#include "remoting/signaling/iq_sender.h"
#include "remoting/signaling/signal_strategy.h"
#include "remoting/signaling/signaling_address.h"
#include "remoting/signaling/signaling_id_util.h"
#include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
#include "third_party/libjingle_xmpp/xmpp/constants.h"
using jingle_xmpp::QName;
using jingle_xmpp::XmlElement;
namespace remoting {
using protocol::ErrorCode;
namespace {
// Strings used in the request message we send to the bot.
const char kRegisterQueryTag[] = "register-support-host";
const char kPublicKeyTag[] = "public-key";
const char kSignatureTag[] = "signature";
const char kSignatureTimeAttr[] = "time";
const char kHostVersionTag[] = "host-version";
const char kHostOsNameTag[] = "host-os-name";
const char kHostOsVersionTag[] = "host-os-version";
// Strings used to parse responses received from the bot.
const char kRegisterQueryResultTag[] = "register-support-host-result";
const char kSupportIdTag[] = "support-id";
const char kSupportIdLifetimeTag[] = "support-id-lifetime";
// The signaling timeout for register support host requests.
constexpr int kRegisterRequestTimeoutInSeconds = 10;
} // namespace
XmppRegisterSupportHostRequest::XmppRegisterSupportHostRequest(
const std::string& directory_bot_jid)
: directory_bot_jid_(directory_bot_jid) {}
XmppRegisterSupportHostRequest::~XmppRegisterSupportHostRequest() {
if (signal_strategy_)
signal_strategy_->RemoveListener(this);
}
void XmppRegisterSupportHostRequest::StartRequest(
SignalStrategy* signal_strategy,
scoped_refptr<RsaKeyPair> key_pair,
RegisterCallback callback) {
DCHECK(signal_strategy);
DCHECK(key_pair.get());
DCHECK(callback);
signal_strategy_ = signal_strategy;
key_pair_ = key_pair;
callback_ = std::move(callback);
signal_strategy_->AddListener(this);
iq_sender_.reset(new IqSender(signal_strategy_));
}
void XmppRegisterSupportHostRequest::OnSignalStrategyStateChange(
SignalStrategy::State state) {
if (state == SignalStrategy::CONNECTED) {
DCHECK(!callback_.is_null());
// The host_jid will be written to the SupportHostStore for lookup. Use id()
// instead of jid() so that we can write the lcs address instead of the
// remoting bot JID.
std::string host_jid = signal_strategy_->GetLocalAddress().id();
request_ = iq_sender_->SendIq(
jingle_xmpp::STR_SET, directory_bot_jid_,
CreateRegistrationRequest(host_jid),
base::BindRepeating(&XmppRegisterSupportHostRequest::ProcessResponse,
base::Unretained(this)));
if (!request_) {
LOG(ERROR) << "Error sending the register-support-host request.";
CallCallback(std::string(), base::TimeDelta(),
ErrorCode::SIGNALING_ERROR);
return;
}
request_->SetTimeout(
base::TimeDelta::FromSeconds(kRegisterRequestTimeoutInSeconds));
} else if (state == SignalStrategy::DISCONNECTED) {
// We will reach here if signaling fails to connect.
LOG(ERROR) << "Signal strategy disconnected.";
CallCallback(std::string(), base::TimeDelta(), ErrorCode::SIGNALING_ERROR);
}
}
bool XmppRegisterSupportHostRequest::OnSignalStrategyIncomingStanza(
const jingle_xmpp::XmlElement* stanza) {
return false;
}
std::unique_ptr<XmlElement>
XmppRegisterSupportHostRequest::CreateRegistrationRequest(
const std::string& jid) {
auto query = std::make_unique<XmlElement>(
QName(kChromotingXmlNamespace, kRegisterQueryTag));
auto public_key = std::make_unique<XmlElement>(
QName(kChromotingXmlNamespace, kPublicKeyTag));
public_key->AddText(key_pair_->GetPublicKey());
query->AddElement(public_key.release());
query->AddElement(CreateSignature(jid).release());
// Add host version.
auto host_version = std::make_unique<XmlElement>(
QName(kChromotingXmlNamespace, kHostVersionTag));
host_version->AddText(STRINGIZE(VERSION));
query->AddElement(host_version.release());
// Add host os name.
auto host_os_name = std::make_unique<XmlElement>(
QName(kChromotingXmlNamespace, kHostOsNameTag));
host_os_name->AddText(GetHostOperatingSystemName());
query->AddElement(host_os_name.release());
// Add host os version.
auto host_os_version = std::make_unique<XmlElement>(
QName(kChromotingXmlNamespace, kHostOsVersionTag));
host_os_version->AddText(GetHostOperatingSystemVersion());
query->AddElement(host_os_version.release());
return query;
}
std::unique_ptr<XmlElement> XmppRegisterSupportHostRequest::CreateSignature(
const std::string& jid) {
std::unique_ptr<XmlElement> signature_tag(
new XmlElement(QName(kChromotingXmlNamespace, kSignatureTag)));
int64_t time = static_cast<int64_t>(base::Time::Now().ToDoubleT());
std::string time_str(base::NumberToString(time));
signature_tag->AddAttr(QName(kChromotingXmlNamespace, kSignatureTimeAttr),
time_str);
std::string message = NormalizeSignalingId(jid) + ' ' + time_str;
std::string signature(key_pair_->SignMessage(message));
signature_tag->AddText(signature);
return signature_tag;
}
void XmppRegisterSupportHostRequest::ParseResponse(const XmlElement* response,
std::string* support_id,
base::TimeDelta* lifetime,
ErrorCode* error_code) {
if (!response) {
LOG(ERROR) << "register-support-host request timed out.";
*error_code = ErrorCode::SIGNALING_TIMEOUT;
return;
}
std::string type = response->Attr(jingle_xmpp::QN_TYPE);
if (type == jingle_xmpp::STR_ERROR) {
LOG(ERROR) << "Received error in response to heartbeat: "
<< response->Str();
*error_code = ErrorCode::HOST_REGISTRATION_ERROR;
return;
}
// This method must only be called for error or result stanzas.
if (type != jingle_xmpp::STR_RESULT) {
LOG(ERROR) << "Received unexpect stanza of type \"" << type << "\"";
*error_code = ErrorCode::HOST_REGISTRATION_ERROR;
return;
}
const XmlElement* result_element = response->FirstNamed(
QName(kChromotingXmlNamespace, kRegisterQueryResultTag));
if (!result_element) {
LOG(ERROR) << "<" << kRegisterQueryResultTag
<< "> is missing in the host registration response: "
<< response->Str();
*error_code = ErrorCode::HOST_REGISTRATION_ERROR;
return;
}
const XmlElement* support_id_element =
result_element->FirstNamed(QName(kChromotingXmlNamespace, kSupportIdTag));
if (!support_id_element) {
LOG(ERROR) << "<" << kSupportIdTag
<< "> is missing in the host registration response: "
<< response->Str();
*error_code = ErrorCode::HOST_REGISTRATION_ERROR;
return;
}
const XmlElement* lifetime_element = result_element->FirstNamed(
QName(kChromotingXmlNamespace, kSupportIdLifetimeTag));
if (!lifetime_element) {
LOG(ERROR) << "<" << kSupportIdLifetimeTag
<< "> is missing in the host registration response: "
<< response->Str();
*error_code = ErrorCode::HOST_REGISTRATION_ERROR;
return;
}
int lifetime_int;
if (!base::StringToInt(lifetime_element->BodyText(), &lifetime_int) ||
lifetime_int <= 0) {
LOG(ERROR) << "<" << kSupportIdLifetimeTag
<< "> is malformed in the host registration response: "
<< response->Str();
*error_code = ErrorCode::HOST_REGISTRATION_ERROR;
return;
}
*support_id = support_id_element->BodyText();
*lifetime = base::TimeDelta::FromSeconds(lifetime_int);
return;
}
void XmppRegisterSupportHostRequest::ProcessResponse(
IqRequest* request,
const XmlElement* response) {
std::string support_id;
base::TimeDelta lifetime;
ErrorCode error_code = ErrorCode::OK;
ParseResponse(response, &support_id, &lifetime, &error_code);
CallCallback(support_id, lifetime, error_code);
}
void XmppRegisterSupportHostRequest::CallCallback(const std::string& support_id,
base::TimeDelta lifetime,
ErrorCode error_code) {
// Cleanup state before calling the callback.
request_.reset();
iq_sender_.reset();
signal_strategy_->RemoveListener(this);
signal_strategy_ = nullptr;
std::move(callback_).Run(support_id, lifetime, error_code);
}
} // namespace remoting