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 16 namespace remoting { 17 18 namespace { 19 20 // The delay between reconnect attempts will increase exponentially up 21 // to the maximum specified here. 22 const int kMaxReconnectDelaySeconds = 10 * 60; 23 24 } // namespace 25 26 SignalingConnector::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 42 SignalingConnector::~SignalingConnector() { 43 signal_strategy_->RemoveListener(this); 44 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); 45 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); 46 } 47 48 void SignalingConnector::EnableOAuth(OAuthTokenGetter* oauth_token_getter) { 49 oauth_token_getter_ = oauth_token_getter; 50 } 51 52 void 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 73 bool SignalingConnector::OnSignalStrategyIncomingStanza( 74 const buzz::XmlElement* stanza) { 75 return false; 76 } 77 78 void 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 88 void SignalingConnector::OnIPAddressChanged() { 89 DCHECK(CalledOnValidThread()); 90 if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) { 91 HOST_LOG << "IP address has changed."; 92 ResetAndTryReconnect(); 93 } 94 } 95 96 void 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 120 void SignalingConnector::OnNetworkError() { 121 DCHECK(CalledOnValidThread()); 122 reconnect_attempts_++; 123 ScheduleTryReconnect(); 124 } 125 126 void 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 136 void SignalingConnector::ResetAndTryReconnect() { 137 DCHECK(CalledOnValidThread()); 138 signal_strategy_->Disconnect(); 139 reconnect_attempts_ = 0; 140 timer_.Stop(); 141 ScheduleTryReconnect(); 142 } 143 144 void 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 155 void 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