| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <algorithm> | 5 #include <algorithm> |
| 6 #include <cstddef> | 6 #include <cstddef> |
| 7 #include <string> | 7 #include <string> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "jingle/notifier/communicator/single_login_attempt.h" | 10 #include "jingle/notifier/communicator/single_login_attempt.h" |
| 11 | 11 |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "jingle/notifier/base/chrome_async_socket.h" | 13 #include "jingle/notifier/base/chrome_async_socket.h" |
| 14 #include "jingle/notifier/base/xmpp_client_socket_factory.h" | 14 #include "jingle/notifier/base/xmpp_client_socket_factory.h" |
| 15 #include "jingle/notifier/communicator/connection_options.h" | 15 #include "jingle/notifier/communicator/connection_options.h" |
| 16 #include "jingle/notifier/communicator/connection_settings.h" | 16 #include "jingle/notifier/communicator/connection_settings.h" |
| 17 #include "jingle/notifier/communicator/const_communicator.h" | 17 #include "jingle/notifier/communicator/const_communicator.h" |
| 18 #include "jingle/notifier/communicator/gaia_token_pre_xmpp_auth.h" | 18 #include "jingle/notifier/communicator/gaia_token_pre_xmpp_auth.h" |
| 19 #include "jingle/notifier/communicator/login_failure.h" | 19 #include "jingle/notifier/communicator/login_failure.h" |
| 20 #include "jingle/notifier/communicator/login_settings.h" | 20 #include "jingle/notifier/communicator/login_settings.h" |
| 21 #include "jingle/notifier/communicator/product_info.h" |
| 21 #include "jingle/notifier/communicator/xmpp_connection_generator.h" | 22 #include "jingle/notifier/communicator/xmpp_connection_generator.h" |
| 23 #include "jingle/notifier/communicator/xmpp_socket_adapter.h" |
| 22 #include "net/base/ssl_config_service.h" | 24 #include "net/base/ssl_config_service.h" |
| 23 #include "net/socket/client_socket_factory.h" | 25 #include "net/socket/client_socket_factory.h" |
| 26 #include "talk/base/asynchttprequest.h" |
| 27 #include "talk/base/firewallsocketserver.h" |
| 28 #include "talk/base/signalthread.h" |
| 29 #include "talk/base/taskrunner.h" |
| 30 #include "talk/base/win32socketinit.h" |
| 24 #include "talk/xmllite/xmlelement.h" | 31 #include "talk/xmllite/xmlelement.h" |
| 25 #include "talk/xmpp/xmppclient.h" | 32 #include "talk/xmpp/xmppclient.h" |
| 26 #include "talk/xmpp/xmppclientsettings.h" | 33 #include "talk/xmpp/xmppclientsettings.h" |
| 27 #include "talk/xmpp/constants.h" | 34 #include "talk/xmpp/constants.h" |
| 28 | 35 |
| 29 namespace net { | 36 namespace net { |
| 30 class NetLog; | 37 class NetLog; |
| 31 } // namespace net | 38 } // namespace net |
| 32 | 39 |
| 33 namespace notifier { | 40 namespace notifier { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 47 *stream_error = NULL; | 54 *stream_error = NULL; |
| 48 if (*error == buzz::XmppEngine::ERROR_STREAM) { | 55 if (*error == buzz::XmppEngine::ERROR_STREAM) { |
| 49 const buzz::XmlElement* error_element = client->GetStreamError(); | 56 const buzz::XmlElement* error_element = client->GetStreamError(); |
| 50 if (error_element) { | 57 if (error_element) { |
| 51 *stream_error = new buzz::XmlElement(*error_element); | 58 *stream_error = new buzz::XmlElement(*error_element); |
| 52 } | 59 } |
| 53 } | 60 } |
| 54 } | 61 } |
| 55 | 62 |
| 56 SingleLoginAttempt::SingleLoginAttempt(talk_base::TaskParent* parent, | 63 SingleLoginAttempt::SingleLoginAttempt(talk_base::TaskParent* parent, |
| 57 LoginSettings* login_settings) | 64 LoginSettings* login_settings, |
| 65 bool use_chrome_async_socket, |
| 66 bool successful_connection) |
| 58 : talk_base::Task(parent), | 67 : talk_base::Task(parent), |
| 68 use_chrome_async_socket_(use_chrome_async_socket), |
| 59 state_(buzz::XmppEngine::STATE_NONE), | 69 state_(buzz::XmppEngine::STATE_NONE), |
| 60 code_(buzz::XmppEngine::ERROR_NONE), | 70 code_(buzz::XmppEngine::ERROR_NONE), |
| 61 subcode_(0), | 71 subcode_(0), |
| 62 need_authentication_(false), | 72 need_authentication_(false), |
| 63 certificate_expired_(false), | 73 certificate_expired_(false), |
| 74 cookie_refreshed_(false), |
| 75 successful_connection_(successful_connection), |
| 64 login_settings_(login_settings), | 76 login_settings_(login_settings), |
| 65 client_(NULL) { | 77 client_(NULL) { |
| 78 #if defined(OS_WIN) |
| 79 talk_base::EnsureWinsockInit(); |
| 80 #endif |
| 66 connection_generator_.reset(new XmppConnectionGenerator( | 81 connection_generator_.reset(new XmppConnectionGenerator( |
| 67 this, | 82 this, |
| 68 login_settings_->host_resolver(), | 83 login_settings_->host_resolver(), |
| 69 &login_settings_->connection_options(), | 84 &login_settings_->connection_options(), |
| 70 login_settings_->try_ssltcp_first(), | 85 login_settings_->try_ssltcp_first(), |
| 71 login_settings_->proxy_only(), | 86 login_settings_->proxy_only(), |
| 72 login_settings_->server_list(), | 87 login_settings_->server_list(), |
| 73 login_settings_->server_count())); | 88 login_settings_->server_count())); |
| 74 | 89 |
| 75 connection_generator_->SignalExhaustedSettings.connect( | 90 connection_generator_->SignalExhaustedSettings.connect( |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 return; | 147 return; |
| 133 } | 148 } |
| 134 | 149 |
| 135 if (!successfully_resolved_dns) { | 150 if (!successfully_resolved_dns) { |
| 136 code_ = buzz::XmppEngine::ERROR_SOCKET; | 151 code_ = buzz::XmppEngine::ERROR_SOCKET; |
| 137 subcode_ = first_dns_error; | 152 subcode_ = first_dns_error; |
| 138 } | 153 } |
| 139 | 154 |
| 140 LOG(INFO) << "Connection failed with error " << code_; | 155 LOG(INFO) << "Connection failed with error " << code_; |
| 141 | 156 |
| 142 SignalNeedAutoReconnect(); | 157 // We were connected and we had a problem. |
| 158 if (successful_connection_) { |
| 159 SignalNeedAutoReconnect(); |
| 160 // Expect to be deleted at this point. |
| 161 return; |
| 162 } |
| 163 |
| 164 DiagnoseConnectionError(); |
| 143 } | 165 } |
| 144 | 166 |
| 145 void SingleLoginAttempt::UseNextConnection() { | 167 void SingleLoginAttempt::UseNextConnection() { |
| 146 DCHECK(connection_generator_.get()); | 168 DCHECK(connection_generator_.get()); |
| 147 ClearClient(); | 169 ClearClient(); |
| 148 connection_generator_->UseNextConnection(); | 170 connection_generator_->UseNextConnection(); |
| 149 } | 171 } |
| 150 | 172 |
| 151 void SingleLoginAttempt::UseCurrentConnection() { | 173 void SingleLoginAttempt::UseCurrentConnection() { |
| 152 DCHECK(connection_generator_.get()); | 174 DCHECK(connection_generator_.get()); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 196 need_authentication_ = true; | 218 need_authentication_ = true; |
| 197 } | 219 } |
| 198 | 220 |
| 199 void SingleLoginAttempt::OnCertificateExpired() { | 221 void SingleLoginAttempt::OnCertificateExpired() { |
| 200 // We can check this flag later if all connection options fail. | 222 // We can check this flag later if all connection options fail. |
| 201 certificate_expired_ = true; | 223 certificate_expired_ = true; |
| 202 } | 224 } |
| 203 | 225 |
| 204 buzz::AsyncSocket* SingleLoginAttempt::CreateSocket( | 226 buzz::AsyncSocket* SingleLoginAttempt::CreateSocket( |
| 205 const buzz::XmppClientSettings& xcs) { | 227 const buzz::XmppClientSettings& xcs) { |
| 206 bool use_fake_ssl_client_socket = | 228 if (use_chrome_async_socket_) { |
| 207 (xcs.protocol() == cricket::PROTO_SSLTCP); | 229 bool use_fake_ssl_client_socket = |
| 208 net::ClientSocketFactory* const client_socket_factory = | 230 (xcs.protocol() == cricket::PROTO_SSLTCP); |
| 209 new XmppClientSocketFactory( | 231 net::ClientSocketFactory* const client_socket_factory = |
| 210 net::ClientSocketFactory::GetDefaultFactory(), | 232 new XmppClientSocketFactory( |
| 211 use_fake_ssl_client_socket); | 233 net::ClientSocketFactory::GetDefaultFactory(), |
| 212 // The default SSLConfig is good enough for us for now. | 234 use_fake_ssl_client_socket); |
| 213 const net::SSLConfig ssl_config; | 235 // The default SSLConfig is good enough for us for now. |
| 214 // A read buffer of 64k ought to be sufficient. | 236 const net::SSLConfig ssl_config; |
| 215 const size_t kReadBufSize = 64U * 1024U; | 237 // A read buffer of 64k ought to be sufficient. |
| 216 // This number was taken from a similar number in | 238 const size_t kReadBufSize = 64U * 1024U; |
| 217 // XmppSocketAdapter. | 239 // This number was taken from a similar number in |
| 218 const size_t kWriteBufSize = 64U * 1024U; | 240 // XmppSocketAdapter. |
| 219 // TODO(akalin): Use a real NetLog. | 241 const size_t kWriteBufSize = 64U * 1024U; |
| 220 net::NetLog* const net_log = NULL; | 242 // TODO(akalin): Use a real NetLog. |
| 221 return new ChromeAsyncSocket( | 243 net::NetLog* const net_log = NULL; |
| 222 client_socket_factory, ssl_config, | 244 return new ChromeAsyncSocket( |
| 223 kReadBufSize, kWriteBufSize, net_log); | 245 client_socket_factory, ssl_config, |
| 246 kReadBufSize, kWriteBufSize, net_log); |
| 247 } |
| 248 // TODO(akalin): Always use ChromeAsyncSocket and get rid of this |
| 249 // code. |
| 250 bool allow_unverified_certs = |
| 251 login_settings_->connection_options().allow_unverified_certs(); |
| 252 XmppSocketAdapter* adapter = new XmppSocketAdapter(xcs, |
| 253 allow_unverified_certs); |
| 254 adapter->SignalAuthenticationError.connect( |
| 255 this, |
| 256 &SingleLoginAttempt::OnAuthenticationError); |
| 257 if (login_settings_->firewall()) { |
| 258 adapter->set_firewall(true); |
| 259 } |
| 260 return adapter; |
| 224 } | 261 } |
| 225 | 262 |
| 226 buzz::PreXmppAuth* SingleLoginAttempt::CreatePreXmppAuth( | 263 buzz::PreXmppAuth* SingleLoginAttempt::CreatePreXmppAuth( |
| 227 const buzz::XmppClientSettings& xcs) { | 264 const buzz::XmppClientSettings& xcs) { |
| 228 buzz::Jid jid(xcs.user(), xcs.host(), buzz::STR_EMPTY); | 265 buzz::Jid jid(xcs.user(), xcs.host(), buzz::STR_EMPTY); |
| 229 return new GaiaTokenPreXmppAuth( | 266 return new GaiaTokenPreXmppAuth( |
| 230 jid.Str(), xcs.auth_cookie(), xcs.token_service()); | 267 jid.Str(), xcs.auth_cookie(), xcs.token_service()); |
| 231 } | 268 } |
| 232 | 269 |
| 270 void SingleLoginAttempt::OnFreshAuthCookie(const std::string& auth_cookie) { |
| 271 // Remember this is a fresh cookie. |
| 272 cookie_refreshed_ = true; |
| 273 |
| 274 // TODO(sync): do the cookie logic (part of which is in the #if 0 below). |
| 275 |
| 276 // The following code is what PhoneWindow does for the equivalent method. |
| 277 #if 0 |
| 278 // Save cookie |
| 279 AccountInfo current(account_history_.current()); |
| 280 current.set_auth_cookie(auth_cookie); |
| 281 account_history_.set_current(current); |
| 282 |
| 283 // Calc next time to refresh cookie, between 5 and 10 days. The cookie has |
| 284 // 14 days of life; this gives at least 4 days of retries before the current |
| 285 // cookie expires, maximizing the chance of having a valid cookie next time |
| 286 // the connection servers go down. |
| 287 FTULL now; |
| 288 |
| 289 // NOTE: The following line is win32. Address this when implementing this |
| 290 // code (doing "the cookie logic"). |
| 291 GetSystemTimeAsFileTime(&(now.ft)); |
| 292 ULONGLONG five_days = (ULONGLONG)10000 * 1000 * 60 * 60 * 24 * 5; // 5 days |
| 293 ULONGLONG random = (ULONGLONG)10000 * // get to 100 ns units |
| 294 ((rand() % (5 * 24 * 60)) * (60 * 1000) + // random min. in 5 day period |
| 295 (rand() % 1000) * 60); // random 1/1000th of a minute |
| 296 next_cookie_refresh_ = now.ull + five_days + random; // 5-10 days |
| 297 #endif |
| 298 } |
| 299 |
| 300 void SingleLoginAttempt::DiagnoseConnectionError() { |
| 301 switch (code_) { |
| 302 case buzz::XmppEngine::ERROR_MISSING_USERNAME: |
| 303 case buzz::XmppEngine::ERROR_NETWORK_TIMEOUT: |
| 304 case buzz::XmppEngine::ERROR_DOCUMENT_CLOSED: |
| 305 case buzz::XmppEngine::ERROR_BIND: |
| 306 case buzz::XmppEngine::ERROR_AUTH: |
| 307 case buzz::XmppEngine::ERROR_TLS: |
| 308 case buzz::XmppEngine::ERROR_UNAUTHORIZED: |
| 309 case buzz::XmppEngine::ERROR_VERSION: |
| 310 case buzz::XmppEngine::ERROR_STREAM: |
| 311 case buzz::XmppEngine::ERROR_XML: |
| 312 case buzz::XmppEngine::ERROR_NONE: |
| 313 default: { |
| 314 LoginFailure failure(LoginFailure::XMPP_ERROR, code_, subcode_); |
| 315 SignalLoginFailure(failure); |
| 316 return; |
| 317 } |
| 318 |
| 319 // The following errors require diagnosistics: |
| 320 // * spurious close of connection |
| 321 // * socket errors after auth |
| 322 case buzz::XmppEngine::ERROR_CONNECTION_CLOSED: |
| 323 case buzz::XmppEngine::ERROR_SOCKET: |
| 324 break; |
| 325 } |
| 326 |
| 327 talk_base::AsyncHttpRequest *http_request = |
| 328 new talk_base::AsyncHttpRequest(GetUserAgentString()); |
| 329 http_request->set_host("www.google.com"); |
| 330 http_request->set_port(80); |
| 331 http_request->set_secure(false); |
| 332 http_request->request().path = "/"; |
| 333 http_request->request().verb = talk_base::HV_GET; |
| 334 |
| 335 talk_base::ProxyInfo proxy; |
| 336 DCHECK(connection_generator_.get()); |
| 337 if (connection_generator_.get()) { |
| 338 proxy = connection_generator_->proxy(); |
| 339 } |
| 340 http_request->set_proxy(proxy); |
| 341 http_request->set_firewall(login_settings_->firewall()); |
| 342 |
| 343 http_request->SignalWorkDone.connect(this, |
| 344 &SingleLoginAttempt::OnHttpTestDone); |
| 345 http_request->Start(); |
| 346 http_request->Release(); |
| 347 } |
| 348 |
| 349 void SingleLoginAttempt::OnHttpTestDone(talk_base::SignalThread* thread) { |
| 350 DCHECK(thread); |
| 351 |
| 352 talk_base::AsyncHttpRequest* request = |
| 353 static_cast<talk_base::AsyncHttpRequest*>(thread); |
| 354 |
| 355 if (request->response().scode == 200) { |
| 356 // We were able to do an HTTP GET of www.google.com:80 |
| 357 |
| 358 // |
| 359 // The original error should be reported |
| 360 // |
| 361 LoginFailure failure(LoginFailure::XMPP_ERROR, code_, subcode_); |
| 362 SignalLoginFailure(failure); |
| 363 return; |
| 364 } |
| 365 |
| 366 // Otherwise lets transmute the error into ERROR_SOCKET, and put the subcode |
| 367 // as an indicator of what we think the problem might be. |
| 368 |
| 369 #if 0 |
| 370 // TODO(sync): determine if notifier has an analogous situation. |
| 371 |
| 372 // |
| 373 // We weren't able to do an HTTP GET of www.google.com:80 |
| 374 // |
| 375 GAutoupdater::Version version_logged_in(g_options.version_logged_in()); |
| 376 GAutoupdater::Version version_installed(GetProductVersion().c_str()); |
| 377 if (version_logged_in < version_installed) { |
| 378 // |
| 379 // Google Talk has been updated and can no longer connect to the Google |
| 380 // Talk Service. Your firewall is probably not allowing the new version of |
| 381 // Google Talk to connect to the internet. Please adjust your firewall |
| 382 // settings to allow the new version of Google Talk to connect to the |
| 383 // internet. |
| 384 // |
| 385 // We'll use the "error=1" to help figure this out for now. |
| 386 // |
| 387 LoginFailure failure(LoginFailure::XMPP_ERROR, |
| 388 buzz::XmppEngine::ERROR_SOCKET, |
| 389 1); |
| 390 SignalLoginFailure(failure); |
| 391 return; |
| 392 } |
| 393 #endif |
| 394 |
| 395 // |
| 396 // Any other checking we can add here? |
| 397 // |
| 398 |
| 399 // |
| 400 // Google Talk is unable to use your internet connection. Either your network |
| 401 // isn't configured or Google Talk is being blocked by a local firewall. |
| 402 // |
| 403 // We'll use the "error=0" to help figure this out for now |
| 404 // |
| 405 LoginFailure failure(LoginFailure::XMPP_ERROR, |
| 406 buzz::XmppEngine::ERROR_SOCKET, |
| 407 0); |
| 408 SignalLoginFailure(failure); |
| 409 } |
| 410 |
| 233 void SingleLoginAttempt::OnClientStateChange(buzz::XmppEngine::State state) { | 411 void SingleLoginAttempt::OnClientStateChange(buzz::XmppEngine::State state) { |
| 234 if (state_ == state) | 412 if (state_ == state) |
| 235 return; | 413 return; |
| 236 | 414 |
| 237 buzz::XmppEngine::State previous_state = state_; | 415 buzz::XmppEngine::State previous_state = state_; |
| 238 state_ = state; | 416 state_ = state; |
| 239 | 417 |
| 240 switch (state) { | 418 switch (state) { |
| 241 case buzz::XmppEngine::STATE_NONE: | 419 case buzz::XmppEngine::STATE_NONE: |
| 242 case buzz::XmppEngine::STATE_START: | 420 case buzz::XmppEngine::STATE_START: |
| 243 case buzz::XmppEngine::STATE_OPENING: | 421 case buzz::XmppEngine::STATE_OPENING: |
| 422 // Do nothing. |
| 423 break; |
| 244 case buzz::XmppEngine::STATE_OPEN: | 424 case buzz::XmppEngine::STATE_OPEN: |
| 245 // Do nothing. | 425 successful_connection_ = true; |
| 246 break; | 426 break; |
| 247 case buzz::XmppEngine::STATE_CLOSED: | 427 case buzz::XmppEngine::STATE_CLOSED: |
| 248 OnClientStateChangeClosed(previous_state); | 428 OnClientStateChangeClosed(previous_state); |
| 249 break; | 429 break; |
| 250 } | 430 } |
| 251 SignalClientStateChange(state); | 431 SignalClientStateChange(state); |
| 252 if (state_ == buzz::XmppEngine::STATE_CLOSED) { | 432 if (state_ == buzz::XmppEngine::STATE_CLOSED) { |
| 253 OnClientStateChange(buzz::XmppEngine::STATE_NONE); | 433 OnClientStateChange(buzz::XmppEngine::STATE_NONE); |
| 254 } | 434 } |
| 255 } | 435 } |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 312 // There was a problem with credentials (username/password). | 492 // There was a problem with credentials (username/password). |
| 313 HandleConnectionPasswordError(); | 493 HandleConnectionPasswordError(); |
| 314 return; | 494 return; |
| 315 } | 495 } |
| 316 | 496 |
| 317 // Unexpected disconnect, | 497 // Unexpected disconnect, |
| 318 // Unreachable host, | 498 // Unreachable host, |
| 319 // Or internal server binding error - | 499 // Or internal server binding error - |
| 320 // All these are temporary problems, so continue reconnecting. | 500 // All these are temporary problems, so continue reconnecting. |
| 321 | 501 |
| 502 // GaiaAuth signals this directly via SignalCertificateExpired, but |
| 503 // SChannelAdapter propagates the error through SocketWindow as a socket |
| 504 // error. |
| 505 if (code_ == buzz::XmppEngine::ERROR_SOCKET && |
| 506 subcode_ == SEC_E_CERT_EXPIRED) { |
| 507 certificate_expired_ = true; |
| 508 } |
| 509 |
| 322 login_settings_->modifiable_user_settings()->set_resource(""); | 510 login_settings_->modifiable_user_settings()->set_resource(""); |
| 323 | 511 |
| 324 // Look for stream::error server redirection stanza "see-other-host". | 512 // Look for stream::error server redirection stanza "see-other-host". |
| 325 if (stream_error) { | 513 if (stream_error) { |
| 326 const buzz::XmlElement* other = | 514 const buzz::XmlElement* other = |
| 327 stream_error->FirstNamed(buzz::QN_XSTREAM_SEE_OTHER_HOST); | 515 stream_error->FirstNamed(buzz::QN_XSTREAM_SEE_OTHER_HOST); |
| 328 if (other) { | 516 if (other) { |
| 329 const buzz::XmlElement* text = | 517 const buzz::XmlElement* text = |
| 330 stream_error->FirstNamed(buzz::QN_XSTREAM_TEXT); | 518 stream_error->FirstNamed(buzz::QN_XSTREAM_TEXT); |
| 331 if (text) { | 519 if (text) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 357 DCHECK(connection_generator_.get()); | 545 DCHECK(connection_generator_.get()); |
| 358 if (!connection_generator_.get()) { | 546 if (!connection_generator_.get()) { |
| 359 return; | 547 return; |
| 360 } | 548 } |
| 361 | 549 |
| 362 // Iterate to the next possible connection (still trying to connect). | 550 // Iterate to the next possible connection (still trying to connect). |
| 363 UseNextConnection(); | 551 UseNextConnection(); |
| 364 } | 552 } |
| 365 | 553 |
| 366 } // namespace notifier | 554 } // namespace notifier |
| OLD | NEW |