| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.h" | |
| 6 | |
| 7 #include <iomanip> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "base/logging.h" | |
| 11 #include "chrome/browser/sync/notifier/base/ssl_adapter.h" | |
| 12 #include "chrome/browser/sync/notifier/communicator/product_info.h" | |
| 13 #include "talk/base/byteorder.h" | |
| 14 #include "talk/base/common.h" | |
| 15 #include "talk/base/firewallsocketserver.h" | |
| 16 #include "talk/base/logging.h" | |
| 17 #include "talk/base/socketadapters.h" | |
| 18 #include "talk/base/thread.h" | |
| 19 #include "talk/xmpp/xmppengine.h" | |
| 20 | |
| 21 namespace notifier { | |
| 22 | |
| 23 XmppSocketAdapter::XmppSocketAdapter(const buzz::XmppClientSettings& xcs, | |
| 24 bool allow_unverified_certs) | |
| 25 : state_(STATE_CLOSED), | |
| 26 error_(ERROR_NONE), | |
| 27 wsa_error_(0), | |
| 28 socket_(NULL), | |
| 29 protocol_(xcs.protocol()), | |
| 30 firewall_(false), | |
| 31 write_buffer_(NULL), | |
| 32 write_buffer_length_(0), | |
| 33 write_buffer_capacity_(0), | |
| 34 allow_unverified_certs_(allow_unverified_certs) { | |
| 35 proxy_.type = xcs.proxy(); | |
| 36 proxy_.address.SetIP(xcs.proxy_host(), false); | |
| 37 proxy_.address.SetPort(xcs.proxy_port()); | |
| 38 proxy_.username = xcs.proxy_user(); | |
| 39 proxy_.password = xcs.proxy_pass(); | |
| 40 } | |
| 41 | |
| 42 XmppSocketAdapter::~XmppSocketAdapter() { | |
| 43 FreeState(); | |
| 44 | |
| 45 // Clean up any previous socket - cannot delete socket on close because close | |
| 46 // happens during the child socket's stack callback. | |
| 47 if (socket_) { | |
| 48 delete socket_; | |
| 49 socket_ = NULL; | |
| 50 } | |
| 51 } | |
| 52 | |
| 53 bool XmppSocketAdapter::FreeState() { | |
| 54 int code = 0; | |
| 55 | |
| 56 // Clean up the socket. | |
| 57 if (socket_ && !(state_ == STATE_CLOSED || state_ == STATE_CLOSING)) { | |
| 58 code = socket_->Close(); | |
| 59 } | |
| 60 | |
| 61 delete[] write_buffer_; | |
| 62 write_buffer_ = NULL; | |
| 63 write_buffer_length_ = 0; | |
| 64 write_buffer_capacity_ = 0; | |
| 65 | |
| 66 if (code) { | |
| 67 SetWSAError(code); | |
| 68 return false; | |
| 69 } | |
| 70 return true; | |
| 71 } | |
| 72 | |
| 73 bool XmppSocketAdapter::Connect(const talk_base::SocketAddress& addr) { | |
| 74 if (state_ != STATE_CLOSED) { | |
| 75 SetError(ERROR_WRONGSTATE); | |
| 76 return false; | |
| 77 } | |
| 78 | |
| 79 LOG(INFO) << "XmppSocketAdapter::Connect(" << addr.ToString() << ")"; | |
| 80 | |
| 81 // Clean up any previous socket - cannot delete socket on close because close | |
| 82 // happens during the child socket's stack callback. | |
| 83 if (socket_) { | |
| 84 delete socket_; | |
| 85 socket_ = NULL; | |
| 86 } | |
| 87 | |
| 88 talk_base::AsyncSocket* socket = | |
| 89 talk_base::Thread::Current()->socketserver()->CreateAsyncSocket( | |
| 90 SOCK_STREAM); | |
| 91 if (!socket) { | |
| 92 SetWSAError(WSA_NOT_ENOUGH_MEMORY); | |
| 93 return false; | |
| 94 } | |
| 95 | |
| 96 if (firewall_) { | |
| 97 // TODO(sync): Change this to make WSAAsyncSockets support current thread | |
| 98 // socket server. | |
| 99 talk_base::FirewallSocketServer* fw = | |
| 100 static_cast<talk_base::FirewallSocketServer*>( | |
| 101 talk_base::Thread::Current()->socketserver()); | |
| 102 socket = fw->WrapSocket(socket, SOCK_STREAM); | |
| 103 } | |
| 104 | |
| 105 if (proxy_.type) { | |
| 106 talk_base::AsyncSocket* proxy_socket = 0; | |
| 107 if (proxy_.type == talk_base::PROXY_SOCKS5) { | |
| 108 proxy_socket = new talk_base::AsyncSocksProxySocket( | |
| 109 socket, proxy_.address, proxy_.username, proxy_.password); | |
| 110 } else { | |
| 111 // Note: we are trying unknown proxies as HTTPS currently. | |
| 112 proxy_socket = new talk_base::AsyncHttpsProxySocket(socket, | |
| 113 GetUserAgentString(), proxy_.address, proxy_.username, | |
| 114 proxy_.password); | |
| 115 } | |
| 116 if (!proxy_socket) { | |
| 117 SetWSAError(WSA_NOT_ENOUGH_MEMORY); | |
| 118 delete socket; | |
| 119 return false; | |
| 120 } | |
| 121 socket = proxy_socket; // For our purposes the proxy is now the socket. | |
| 122 } | |
| 123 | |
| 124 if (protocol_ == cricket::PROTO_SSLTCP) { | |
| 125 talk_base::AsyncSocket *fake_ssl_socket = | |
| 126 new talk_base::AsyncSSLSocket(socket); | |
| 127 if (!fake_ssl_socket) { | |
| 128 SetWSAError(WSA_NOT_ENOUGH_MEMORY); | |
| 129 delete socket; | |
| 130 return false; | |
| 131 } | |
| 132 socket = fake_ssl_socket; // For our purposes the SSL socket is the socket. | |
| 133 } | |
| 134 | |
| 135 #if defined(FEATURE_ENABLE_SSL) | |
| 136 talk_base::SSLAdapter* ssl_adapter = notifier::CreateSSLAdapter(socket); | |
| 137 socket = ssl_adapter; // For our purposes the SSL adapter is the socket. | |
| 138 #endif | |
| 139 | |
| 140 socket->SignalReadEvent.connect(this, &XmppSocketAdapter::OnReadEvent); | |
| 141 socket->SignalWriteEvent.connect(this, &XmppSocketAdapter::OnWriteEvent); | |
| 142 socket->SignalConnectEvent.connect(this, &XmppSocketAdapter::OnConnectEvent); | |
| 143 socket->SignalCloseEvent.connect(this, &XmppSocketAdapter::OnCloseEvent); | |
| 144 | |
| 145 // The linux implementation of socket::Connect returns an error when the | |
| 146 // connect didn't complete yet. This can be distinguished from a failure | |
| 147 // because socket::IsBlocking is true. Perhaps, the linux implementation | |
| 148 // should be made to behave like the windows version which doesn't do this, | |
| 149 // but it seems to be a pattern with these methods that they return an error | |
| 150 // if the operation didn't complete in a sync fashion and one has to check | |
| 151 // IsBlocking to tell if was a "real" error. | |
| 152 if (socket->Connect(addr) == SOCKET_ERROR && !socket->IsBlocking()) { | |
| 153 SetWSAError(socket->GetError()); | |
| 154 delete socket; | |
| 155 return false; | |
| 156 } | |
| 157 | |
| 158 socket_ = socket; | |
| 159 state_ = STATE_CONNECTING; | |
| 160 return true; | |
| 161 } | |
| 162 | |
| 163 bool XmppSocketAdapter::Read(char* data, size_t len, size_t* len_read) { | |
| 164 if (len_read) | |
| 165 *len_read = 0; | |
| 166 | |
| 167 if (state_ <= STATE_CLOSING) { | |
| 168 SetError(ERROR_WRONGSTATE); | |
| 169 return false; | |
| 170 } | |
| 171 | |
| 172 ASSERT(socket_ != NULL); | |
| 173 | |
| 174 if (IsOpen()) { | |
| 175 int result = socket_->Recv(data, len); | |
| 176 if (result < 0) { | |
| 177 if (!socket_->IsBlocking()) { | |
| 178 SetWSAError(socket_->GetError()); | |
| 179 return false; | |
| 180 } | |
| 181 | |
| 182 result = 0; | |
| 183 } | |
| 184 | |
| 185 if (len_read) | |
| 186 *len_read = result; | |
| 187 } | |
| 188 | |
| 189 return true; | |
| 190 } | |
| 191 | |
| 192 bool XmppSocketAdapter::Write(const char* data, size_t len) { | |
| 193 if (state_ <= STATE_CLOSING) { | |
| 194 // There may be data in a buffer that gets lost. Too bad! | |
| 195 SetError(ERROR_WRONGSTATE); | |
| 196 return false; | |
| 197 } | |
| 198 | |
| 199 ASSERT(socket_ != NULL); | |
| 200 | |
| 201 size_t sent = 0; | |
| 202 | |
| 203 // Try an immediate write when there is no buffer and we aren't in SSL mode | |
| 204 // or opening the connection. | |
| 205 if (write_buffer_length_ == 0 && IsOpen()) { | |
| 206 int result = socket_->Send(data, len); | |
| 207 if (result < 0) { | |
| 208 if (!socket_->IsBlocking()) { | |
| 209 SetWSAError(socket_->GetError()); | |
| 210 return false; | |
| 211 } | |
| 212 result = 0; | |
| 213 } | |
| 214 | |
| 215 sent = static_cast<size_t>(result); | |
| 216 } | |
| 217 | |
| 218 // Buffer what we didn't send. | |
| 219 if (sent < len) { | |
| 220 QueueWriteData(data + sent, len - sent); | |
| 221 } | |
| 222 | |
| 223 // Service the socket right away to push the written data out in SSL mode. | |
| 224 return HandleWritable(); | |
| 225 } | |
| 226 | |
| 227 bool XmppSocketAdapter::Close() { | |
| 228 if (state_ == STATE_CLOSING) { | |
| 229 return false; // Avoid recursion, but not unexpected. | |
| 230 } | |
| 231 if (state_ == STATE_CLOSED) { | |
| 232 // In theory should not be trying to re-InternalClose. | |
| 233 SetError(ERROR_WRONGSTATE); | |
| 234 return false; | |
| 235 } | |
| 236 | |
| 237 // TODO(sync): deal with flushing close (flush, don't do reads, clean ssl). | |
| 238 | |
| 239 // If we've gotten to the point where we really do have a socket underneath | |
| 240 // then close it. It should call us back to tell us it is closed, and | |
| 241 // NotifyClose will be called. We indicate "closing" state so that we | |
| 242 // do not recusively try to keep closing the socket. | |
| 243 if (socket_) { | |
| 244 state_ = STATE_CLOSING; | |
| 245 socket_->Close(); | |
| 246 } | |
| 247 | |
| 248 // If we didn't get the callback, then we better make sure we signal | |
| 249 // closed. | |
| 250 if (state_ != STATE_CLOSED) { | |
| 251 // The socket was closed manually, not directly due to error. | |
| 252 if (error_ != ERROR_NONE) { | |
| 253 LOG(INFO) << "XmppSocketAdapter::Close - previous Error: " << error_ | |
| 254 << " WSAError: " << wsa_error_; | |
| 255 error_ = ERROR_NONE; | |
| 256 wsa_error_ = 0; | |
| 257 } | |
| 258 NotifyClose(); | |
| 259 } | |
| 260 return true; | |
| 261 } | |
| 262 | |
| 263 void XmppSocketAdapter::NotifyClose() { | |
| 264 if (state_ == STATE_CLOSED) { | |
| 265 SetError(ERROR_WRONGSTATE); | |
| 266 } else { | |
| 267 LOG(INFO) << "XmppSocketAdapter::NotifyClose - Error: " << error_ | |
| 268 << " WSAError: " << wsa_error_; | |
| 269 state_ = STATE_CLOSED; | |
| 270 SignalClosed(); | |
| 271 FreeState(); | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 void XmppSocketAdapter::OnConnectEvent(talk_base::AsyncSocket *socket) { | |
| 276 if (state_ == STATE_CONNECTING) { | |
| 277 state_ = STATE_OPEN; | |
| 278 LOG(INFO) << "XmppSocketAdapter::OnConnectEvent - STATE_OPEN"; | |
| 279 SignalConnected(); | |
| 280 #if defined(FEATURE_ENABLE_SSL) | |
| 281 } else if (state_ == STATE_TLS_CONNECTING) { | |
| 282 state_ = STATE_TLS_OPEN; | |
| 283 LOG(INFO) << "XmppSocketAdapter::OnConnectEvent - STATE_TLS_OPEN"; | |
| 284 SignalSSLConnected(); | |
| 285 if (write_buffer_length_ > 0) { | |
| 286 HandleWritable(); | |
| 287 } | |
| 288 #endif // defined(FEATURE_ENABLE_SSL) | |
| 289 } else { | |
| 290 LOG(INFO) << "XmppSocketAdapter::OnConnectEvent - state is " << state_; | |
| 291 ASSERT(false); | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 void XmppSocketAdapter::OnReadEvent(talk_base::AsyncSocket *socket) { | |
| 296 HandleReadable(); | |
| 297 } | |
| 298 | |
| 299 void XmppSocketAdapter::OnWriteEvent(talk_base::AsyncSocket *socket) { | |
| 300 HandleWritable(); | |
| 301 } | |
| 302 | |
| 303 void XmppSocketAdapter::OnCloseEvent(talk_base::AsyncSocket *socket, | |
| 304 int error) { | |
| 305 LOG(INFO) << "XmppSocketAdapter::OnCloseEvent(" << error << ")"; | |
| 306 SetWSAError(error); | |
| 307 if (error == SOCKET_EACCES) { | |
| 308 SignalAuthenticationError(); // Proxy needs authentication. | |
| 309 } | |
| 310 NotifyClose(); | |
| 311 } | |
| 312 | |
| 313 #if defined(FEATURE_ENABLE_SSL) | |
| 314 bool XmppSocketAdapter::StartTls(const std::string& verify_host_name) { | |
| 315 if (state_ != STATE_OPEN) { | |
| 316 SetError(ERROR_WRONGSTATE); | |
| 317 return false; | |
| 318 } | |
| 319 | |
| 320 state_ = STATE_TLS_CONNECTING; | |
| 321 | |
| 322 ASSERT(write_buffer_length_ == 0); | |
| 323 | |
| 324 talk_base::SSLAdapter* ssl_adapter = | |
| 325 static_cast<talk_base::SSLAdapter*>(socket_); | |
| 326 | |
| 327 if (allow_unverified_certs_) { | |
| 328 ssl_adapter->set_ignore_bad_cert(true); | |
| 329 } | |
| 330 | |
| 331 if (ssl_adapter->StartSSL(verify_host_name.c_str(), false) != 0) { | |
| 332 state_ = STATE_OPEN; | |
| 333 SetError(ERROR_SSL); | |
| 334 return false; | |
| 335 } | |
| 336 | |
| 337 return true; | |
| 338 } | |
| 339 #endif // defined(FEATURE_ENABLE_SSL) | |
| 340 | |
| 341 void XmppSocketAdapter::QueueWriteData(const char* data, size_t len) { | |
| 342 // Expand buffer if needed. | |
| 343 if (write_buffer_length_ + len > write_buffer_capacity_) { | |
| 344 size_t new_capacity = 1024; | |
| 345 while (new_capacity < write_buffer_length_ + len) { | |
| 346 new_capacity = new_capacity * 2; | |
| 347 } | |
| 348 char* new_buffer = new char[new_capacity]; | |
| 349 ASSERT(write_buffer_length_ <= 64000); | |
| 350 memcpy(new_buffer, write_buffer_, write_buffer_length_); | |
| 351 delete[] write_buffer_; | |
| 352 write_buffer_ = new_buffer; | |
| 353 write_buffer_capacity_ = new_capacity; | |
| 354 } | |
| 355 | |
| 356 // Copy data into the end of buffer. | |
| 357 memcpy(write_buffer_ + write_buffer_length_, data, len); | |
| 358 write_buffer_length_ += len; | |
| 359 } | |
| 360 | |
| 361 void XmppSocketAdapter::FlushWriteQueue(Error* error, int* wsa_error) { | |
| 362 ASSERT(error && wsa_error); | |
| 363 | |
| 364 size_t flushed = 0; | |
| 365 while (flushed < write_buffer_length_) { | |
| 366 int sent = socket_->Send(write_buffer_ + flushed, | |
| 367 static_cast<int>(write_buffer_length_ - flushed)); | |
| 368 if (sent < 0) { | |
| 369 if (!socket_->IsBlocking()) { | |
| 370 *error = ERROR_WINSOCK; | |
| 371 *wsa_error = socket_->GetError(); | |
| 372 } | |
| 373 break; | |
| 374 } | |
| 375 flushed += static_cast<size_t>(sent); | |
| 376 } | |
| 377 | |
| 378 // Remove flushed memory. | |
| 379 write_buffer_length_ -= flushed; | |
| 380 memmove(write_buffer_, write_buffer_ + flushed, write_buffer_length_); | |
| 381 | |
| 382 // When everything is flushed, deallocate the buffer if it's gotten big. | |
| 383 if (write_buffer_length_ == 0) { | |
| 384 if (write_buffer_capacity_ > 8192) { | |
| 385 delete[] write_buffer_; | |
| 386 write_buffer_ = NULL; | |
| 387 write_buffer_capacity_ = 0; | |
| 388 } | |
| 389 } | |
| 390 } | |
| 391 | |
| 392 void XmppSocketAdapter::SetError(Error error) { | |
| 393 if (error_ == ERROR_NONE) { | |
| 394 error_ = error; | |
| 395 } | |
| 396 } | |
| 397 | |
| 398 void XmppSocketAdapter::SetWSAError(int error) { | |
| 399 if (error_ == ERROR_NONE && error != 0) { | |
| 400 error_ = ERROR_WINSOCK; | |
| 401 wsa_error_ = error; | |
| 402 } | |
| 403 } | |
| 404 | |
| 405 bool XmppSocketAdapter::HandleReadable() { | |
| 406 if (!IsOpen()) | |
| 407 return false; | |
| 408 | |
| 409 SignalRead(); | |
| 410 return true; | |
| 411 } | |
| 412 | |
| 413 bool XmppSocketAdapter::HandleWritable() { | |
| 414 if (!IsOpen()) | |
| 415 return false; | |
| 416 | |
| 417 Error error = ERROR_NONE; | |
| 418 int wsa_error = 0; | |
| 419 FlushWriteQueue(&error, &wsa_error); | |
| 420 if (error != ERROR_NONE) { | |
| 421 Close(); | |
| 422 return false; | |
| 423 } | |
| 424 return true; | |
| 425 } | |
| 426 | |
| 427 } // namespace notifier | |
| OLD | NEW |