1// Copyright (c) 2012 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/signaling_connector.h" 6 7#include "base/bind.h" 8#include "base/callback.h" 9#include "base/strings/string_util.h" 10#include "google_apis/google_api_keys.h" 11#include "net/url_request/url_fetcher.h" 12#include "net/url_request/url_request_context_getter.h" 13#include "remoting/base/logging.h" 14#include "remoting/host/dns_blackhole_checker.h" 15 16namespace remoting { 17 18namespace { 19 20// The delay between reconnect attempts will increase exponentially up 21// to the maximum specified here. 22const int kMaxReconnectDelaySeconds = 10 * 60; 23 24} // namespace 25 26SignalingConnector::SignalingConnector( 27 XmppSignalStrategy* signal_strategy, 28 scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker, 29 const base::Closure& auth_failed_callback) 30 : signal_strategy_(signal_strategy), 31 auth_failed_callback_(auth_failed_callback), 32 dns_blackhole_checker_(dns_blackhole_checker.Pass()), 33 reconnect_attempts_(0) { 34 DCHECK(!auth_failed_callback_.is_null()); 35 DCHECK(dns_blackhole_checker_.get()); 36 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); 37 net::NetworkChangeNotifier::AddIPAddressObserver(this); 38 signal_strategy_->AddListener(this); 39 ScheduleTryReconnect(); 40} 41 42SignalingConnector::~SignalingConnector() { 43 signal_strategy_->RemoveListener(this); 44 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); 45 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); 46} 47 48void SignalingConnector::EnableOAuth(OAuthTokenGetter* oauth_token_getter) { 49 oauth_token_getter_ = oauth_token_getter; 50} 51 52void SignalingConnector::OnSignalStrategyStateChange( 53 SignalStrategy::State state) { 54 DCHECK(CalledOnValidThread()); 55 56 if (state == SignalStrategy::CONNECTED) { 57 HOST_LOG << "Signaling connected."; 58 reconnect_attempts_ = 0; 59 } else if (state == SignalStrategy::DISCONNECTED) { 60 HOST_LOG << "Signaling disconnected."; 61 reconnect_attempts_++; 62 63 // If authentication failed then we have an invalid OAuth token, 64 // inform the upper layer about it. 65 if (signal_strategy_->GetError() == SignalStrategy::AUTHENTICATION_FAILED) { 66 auth_failed_callback_.Run(); 67 } else { 68 ScheduleTryReconnect(); 69 } 70 } 71} 72 73bool SignalingConnector::OnSignalStrategyIncomingStanza( 74 const buzz::XmlElement* stanza) { 75 return false; 76} 77 78void SignalingConnector::OnConnectionTypeChanged( 79 net::NetworkChangeNotifier::ConnectionType type) { 80 DCHECK(CalledOnValidThread()); 81 if (type != net::NetworkChangeNotifier::CONNECTION_NONE && 82 signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) { 83 HOST_LOG << "Network state changed to online."; 84 ResetAndTryReconnect(); 85 } 86} 87 88void SignalingConnector::OnIPAddressChanged() { 89 DCHECK(CalledOnValidThread()); 90 if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) { 91 HOST_LOG << "IP address has changed."; 92 ResetAndTryReconnect(); 93 } 94} 95 96void SignalingConnector::OnAccessToken(OAuthTokenGetter::Status status, 97 const std::string& user_email, 98 const std::string& access_token) { 99 DCHECK(CalledOnValidThread()); 100 101 if (status == OAuthTokenGetter::AUTH_ERROR) { 102 auth_failed_callback_.Run(); 103 return; 104 } else if (status == OAuthTokenGetter::NETWORK_ERROR) { 105 OnNetworkError(); 106 return; 107 } 108 109 DCHECK_EQ(status, OAuthTokenGetter::SUCCESS); 110 HOST_LOG << "Received user info."; 111 112 signal_strategy_->SetAuthInfo(user_email, access_token, "oauth2"); 113 114 // Now that we've refreshed the token and verified that it's for the correct 115 // user account, try to connect using the new token. 116 DCHECK_EQ(signal_strategy_->GetState(), SignalStrategy::DISCONNECTED); 117 signal_strategy_->Connect(); 118} 119 120void SignalingConnector::OnNetworkError() { 121 DCHECK(CalledOnValidThread()); 122 reconnect_attempts_++; 123 ScheduleTryReconnect(); 124} 125 126void SignalingConnector::ScheduleTryReconnect() { 127 DCHECK(CalledOnValidThread()); 128 if (timer_.IsRunning() || net::NetworkChangeNotifier::IsOffline()) 129 return; 130 int delay_s = std::min(1 << reconnect_attempts_, 131 kMaxReconnectDelaySeconds); 132 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay_s), 133 this, &SignalingConnector::TryReconnect); 134} 135 136void SignalingConnector::ResetAndTryReconnect() { 137 DCHECK(CalledOnValidThread()); 138 signal_strategy_->Disconnect(); 139 reconnect_attempts_ = 0; 140 timer_.Stop(); 141 ScheduleTryReconnect(); 142} 143 144void SignalingConnector::TryReconnect() { 145 DCHECK(CalledOnValidThread()); 146 DCHECK(dns_blackhole_checker_.get()); 147 148 // This will check if this machine is allowed to access the chromoting 149 // host talkgadget. 150 dns_blackhole_checker_->CheckForDnsBlackhole( 151 base::Bind(&SignalingConnector::OnDnsBlackholeCheckerDone, 152 base::Unretained(this))); 153} 154 155void SignalingConnector::OnDnsBlackholeCheckerDone(bool allow) { 156 DCHECK(CalledOnValidThread()); 157 158 // Unable to access the host talkgadget. Don't allow the connection, but 159 // schedule a reconnect in case this is a transient problem rather than 160 // an outright block. 161 if (!allow) { 162 reconnect_attempts_++; 163 HOST_LOG << "Talkgadget check failed. Scheduling reconnect. Attempt " 164 << reconnect_attempts_; 165 ScheduleTryReconnect(); 166 return; 167 } 168 169 if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) { 170 HOST_LOG << "Attempting to connect signaling."; 171 oauth_token_getter_->CallWithToken( 172 base::Bind(&SignalingConnector::OnAccessToken, AsWeakPtr())); 173 } 174} 175 176} // namespace remoting 177