OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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/tools/chrome_async_socket.h" |
| 6 |
| 7 #if defined(OS_WIN) |
| 8 #include <winsock2.h> |
| 9 #elif defined(OS_POSIX) |
| 10 #include <arpa/inet.h> |
| 11 #endif |
| 12 |
| 13 #include <algorithm> |
| 14 #include <cstring> |
| 15 #include <cstdlib> |
| 16 |
| 17 #include "base/compiler_specific.h" |
| 18 #include "base/logging.h" |
| 19 #include "net/base/address_list.h" |
| 20 #include "net/base/io_buffer.h" |
| 21 #include "net/base/ssl_config_service.h" |
| 22 #include "net/base/sys_addrinfo.h" |
| 23 #include "net/socket/client_socket_factory.h" |
| 24 #include "net/socket/ssl_client_socket.h" |
| 25 #include "net/socket/tcp_client_socket.h" |
| 26 #include "talk/base/socketaddress.h" |
| 27 |
| 28 namespace sync_tools { |
| 29 |
| 30 ChromeAsyncSocket::ChromeAsyncSocket( |
| 31 net::ClientSocketFactory* client_socket_factory, |
| 32 const net::SSLConfig& ssl_config, |
| 33 size_t read_buf_size, |
| 34 size_t write_buf_size, |
| 35 net::NetLog* net_log) |
| 36 : connect_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this), |
| 37 &ChromeAsyncSocket::ProcessConnectDone), |
| 38 read_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this), |
| 39 &ChromeAsyncSocket::ProcessReadDone), |
| 40 write_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this), |
| 41 &ChromeAsyncSocket::ProcessWriteDone), |
| 42 ssl_connect_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this), |
| 43 &ChromeAsyncSocket::ProcessSSLConnectDone), |
| 44 client_socket_factory_(client_socket_factory), |
| 45 ssl_config_(ssl_config), |
| 46 bound_net_log_( |
| 47 net::BoundNetLog::Make(net_log, net::NetLog::SOURCE_SOCKET)), |
| 48 state_(STATE_CLOSED), |
| 49 error_(ERROR_NONE), |
| 50 net_error_(net::OK), |
| 51 scoped_runnable_method_factory_( |
| 52 ALLOW_THIS_IN_INITIALIZER_LIST(this)), |
| 53 read_state_(IDLE), |
| 54 read_buf_(new net::IOBufferWithSize(read_buf_size)), |
| 55 read_start_(0), |
| 56 read_end_(0), |
| 57 write_state_(IDLE), |
| 58 write_buf_(new net::IOBufferWithSize(write_buf_size)), |
| 59 write_end_(0) { |
| 60 DCHECK(client_socket_factory_); |
| 61 DCHECK_GT(read_buf_size, 0); |
| 62 DCHECK_GT(write_buf_size, 0); |
| 63 } |
| 64 |
| 65 ChromeAsyncSocket::~ChromeAsyncSocket() {} |
| 66 |
| 67 ChromeAsyncSocket::State ChromeAsyncSocket::state() { |
| 68 return state_; |
| 69 } |
| 70 |
| 71 ChromeAsyncSocket::Error ChromeAsyncSocket::error() { |
| 72 return error_; |
| 73 } |
| 74 |
| 75 int ChromeAsyncSocket::GetError() { |
| 76 return net_error_; |
| 77 } |
| 78 |
| 79 bool ChromeAsyncSocket::IsOpen() const { |
| 80 return (state_ == STATE_OPEN) || (state_ == STATE_TLS_OPEN); |
| 81 } |
| 82 |
| 83 void ChromeAsyncSocket::DoNonNetError(Error error) { |
| 84 DCHECK_NE(error, ERROR_NONE); |
| 85 DCHECK_NE(error, ERROR_WINSOCK); |
| 86 error_ = error; |
| 87 net_error_ = net::OK; |
| 88 } |
| 89 |
| 90 void ChromeAsyncSocket::DoNetError(net::Error net_error) { |
| 91 error_ = ERROR_WINSOCK; |
| 92 net_error_ = net_error; |
| 93 } |
| 94 |
| 95 void ChromeAsyncSocket::DoNetErrorFromStatus(int status) { |
| 96 DCHECK_LT(status, net::OK); |
| 97 DoNetError(static_cast<net::Error>(status)); |
| 98 } |
| 99 |
| 100 namespace { |
| 101 |
| 102 net::AddressList SocketAddressToAddressList( |
| 103 const talk_base::SocketAddress& address) { |
| 104 DCHECK_NE(address.ip(), 0); |
| 105 // Use malloc() as net::AddressList uses free(). |
| 106 addrinfo* ai = static_cast<addrinfo*>(std::malloc(sizeof *ai)); |
| 107 memset(ai, 0, sizeof *ai); |
| 108 ai->ai_family = AF_INET; |
| 109 ai->ai_socktype = SOCK_STREAM; |
| 110 ai->ai_addrlen = sizeof(sockaddr_in); |
| 111 |
| 112 sockaddr_in* addr = static_cast<sockaddr_in*>(std::malloc(sizeof *addr)); |
| 113 memset(addr, 0, sizeof *addr); |
| 114 addr->sin_family = AF_INET; |
| 115 addr->sin_addr.s_addr = htonl(address.ip()); |
| 116 addr->sin_port = htons(address.port()); |
| 117 ai->ai_addr = reinterpret_cast<sockaddr*>(addr); |
| 118 |
| 119 net::AddressList address_list; |
| 120 address_list.Adopt(ai); |
| 121 return address_list; |
| 122 } |
| 123 |
| 124 } // namespace |
| 125 |
| 126 // STATE_CLOSED -> STATE_CONNECTING |
| 127 |
| 128 bool ChromeAsyncSocket::Connect(const talk_base::SocketAddress& address) { |
| 129 if (state_ != STATE_CLOSED) { |
| 130 LOG(DFATAL) << "Connect() called on non-closed socket"; |
| 131 DoNonNetError(ERROR_WRONGSTATE); |
| 132 return false; |
| 133 } |
| 134 if (address.ip() == 0) { |
| 135 DoNonNetError(ERROR_DNS); |
| 136 return false; |
| 137 } |
| 138 |
| 139 DCHECK_EQ(state_, buzz::AsyncSocket::STATE_CLOSED); |
| 140 DCHECK_EQ(read_state_, IDLE); |
| 141 DCHECK_EQ(write_state_, IDLE); |
| 142 |
| 143 state_ = STATE_CONNECTING; |
| 144 |
| 145 DCHECK(scoped_runnable_method_factory_.empty()); |
| 146 scoped_runnable_method_factory_.RevokeAll(); |
| 147 |
| 148 net::AddressList address_list = SocketAddressToAddressList(address); |
| 149 transport_socket_.reset( |
| 150 client_socket_factory_-> |
| 151 CreateTCPClientSocket(address_list, bound_net_log_.net_log())); |
| 152 int status = transport_socket_->Connect(&connect_callback_); |
| 153 if (status != net::ERR_IO_PENDING) { |
| 154 // We defer execution of ProcessConnectDone instead of calling it |
| 155 // directly here as the caller may not expect an error/close to |
| 156 // happen here. This is okay, as from the caller's point of view, |
| 157 // the connect always happens asynchronously. |
| 158 MessageLoop* message_loop = MessageLoop::current(); |
| 159 CHECK(message_loop); |
| 160 message_loop->PostTask( |
| 161 FROM_HERE, |
| 162 scoped_runnable_method_factory_.NewRunnableMethod( |
| 163 &ChromeAsyncSocket::ProcessConnectDone, status)); |
| 164 } |
| 165 return true; |
| 166 } |
| 167 |
| 168 // STATE_CONNECTING -> STATE_OPEN |
| 169 // read_state_ == IDLE -> read_state_ == POSTED (via PostDoRead()) |
| 170 |
| 171 void ChromeAsyncSocket::ProcessConnectDone(int status) { |
| 172 DCHECK_NE(status, net::ERR_IO_PENDING); |
| 173 DCHECK_EQ(read_state_, IDLE); |
| 174 DCHECK_EQ(write_state_, IDLE); |
| 175 DCHECK_EQ(state_, STATE_CONNECTING); |
| 176 if (status != net::OK) { |
| 177 DoNetErrorFromStatus(status); |
| 178 DoClose(); |
| 179 return; |
| 180 } |
| 181 state_ = STATE_OPEN; |
| 182 PostDoRead(); |
| 183 // Write buffer should be empty. |
| 184 DCHECK_EQ(write_end_, 0); |
| 185 SignalConnected(); |
| 186 } |
| 187 |
| 188 // read_state_ == IDLE -> read_state_ == POSTED |
| 189 |
| 190 void ChromeAsyncSocket::PostDoRead() { |
| 191 DCHECK(IsOpen()); |
| 192 DCHECK_EQ(read_state_, IDLE); |
| 193 DCHECK_EQ(read_start_, 0); |
| 194 DCHECK_EQ(read_end_, 0); |
| 195 MessageLoop* message_loop = MessageLoop::current(); |
| 196 CHECK(message_loop); |
| 197 message_loop->PostTask( |
| 198 FROM_HERE, |
| 199 scoped_runnable_method_factory_.NewRunnableMethod( |
| 200 &ChromeAsyncSocket::DoRead)); |
| 201 read_state_ = POSTED; |
| 202 } |
| 203 |
| 204 // read_state_ == POSTED -> read_state_ == PENDING |
| 205 |
| 206 void ChromeAsyncSocket::DoRead() { |
| 207 DCHECK(IsOpen()); |
| 208 DCHECK_EQ(read_state_, POSTED); |
| 209 DCHECK_EQ(read_start_, 0); |
| 210 DCHECK_EQ(read_end_, 0); |
| 211 // Once we call Read(), we cannot call StartTls() until the read |
| 212 // finishes. This is okay, as StartTls() is called only from a read |
| 213 // handler (i.e., after a read finishes and before another read is |
| 214 // done). |
| 215 int status = |
| 216 transport_socket_->Read( |
| 217 read_buf_.get(), read_buf_->size(), &read_callback_); |
| 218 read_state_ = PENDING; |
| 219 if (status != net::ERR_IO_PENDING) { |
| 220 ProcessReadDone(status); |
| 221 } |
| 222 } |
| 223 |
| 224 // read_state_ == PENDING -> read_state_ == IDLE |
| 225 |
| 226 void ChromeAsyncSocket::ProcessReadDone(int status) { |
| 227 DCHECK_NE(status, net::ERR_IO_PENDING); |
| 228 DCHECK(IsOpen()); |
| 229 DCHECK_EQ(read_state_, PENDING); |
| 230 DCHECK_EQ(read_start_, 0); |
| 231 DCHECK_EQ(read_end_, 0); |
| 232 read_state_ = IDLE; |
| 233 if (status > 0) { |
| 234 read_end_ = status; |
| 235 SignalRead(); |
| 236 } else if (status == 0) { |
| 237 // Other side closed the connection. |
| 238 error_ = ERROR_NONE; |
| 239 net_error_ = net::OK; |
| 240 DoClose(); |
| 241 } else { // status < 0 |
| 242 DoNetErrorFromStatus(status); |
| 243 DoClose(); |
| 244 } |
| 245 } |
| 246 |
| 247 // (maybe) read_state_ == IDLE -> read_state_ == POSTED (via |
| 248 // PostDoRead()) |
| 249 |
| 250 bool ChromeAsyncSocket::Read(char* data, size_t len, size_t* len_read) { |
| 251 if (!IsOpen() && (state_ != STATE_TLS_CONNECTING)) { |
| 252 LOG(DFATAL) << "Read() called on non-open non-tls-connecting socket"; |
| 253 DoNonNetError(ERROR_WRONGSTATE); |
| 254 return false; |
| 255 } |
| 256 DCHECK_LE(read_start_, read_end_); |
| 257 if ((state_ == STATE_TLS_CONNECTING) || read_end_ == 0) { |
| 258 if (state_ == STATE_TLS_CONNECTING) { |
| 259 DCHECK_EQ(read_state_, IDLE); |
| 260 DCHECK_EQ(read_end_, 0); |
| 261 } else { |
| 262 DCHECK_NE(read_state_, IDLE); |
| 263 } |
| 264 *len_read = 0; |
| 265 return true; |
| 266 } |
| 267 DCHECK_EQ(read_state_, IDLE); |
| 268 *len_read = std::min(len, read_end_ - read_start_); |
| 269 DCHECK_GT(*len_read, 0); |
| 270 std::memcpy(data, read_buf_->data() + read_start_, *len_read); |
| 271 read_start_ += *len_read; |
| 272 if (read_start_ == read_end_) { |
| 273 read_start_ = 0; |
| 274 read_end_ = 0; |
| 275 // We defer execution of DoRead() here for similar reasons as |
| 276 // ProcessConnectDone(). |
| 277 PostDoRead(); |
| 278 } |
| 279 return true; |
| 280 } |
| 281 |
| 282 // (maybe) write_state_ == IDLE -> write_state_ == POSTED (via |
| 283 // PostDoWrite()) |
| 284 |
| 285 bool ChromeAsyncSocket::Write(const char* data, size_t len) { |
| 286 if (!IsOpen() && (state_ != STATE_TLS_CONNECTING)) { |
| 287 LOG(DFATAL) << "Write() called on non-open non-tls-connecting socket"; |
| 288 DoNonNetError(ERROR_WRONGSTATE); |
| 289 return false; |
| 290 } |
| 291 // TODO(akalin): Avoid this check by modifying the interface to have |
| 292 // a "ready for writing" signal. |
| 293 if ((write_buf_->size() - write_end_) < len) { |
| 294 LOG(DFATAL) << "queueing " << len << " bytes would exceed the " |
| 295 << "max write buffer size = " << write_buf_->size() |
| 296 << " by " << (len - write_buf_->size()) << " bytes"; |
| 297 DoNetError(net::ERR_INSUFFICIENT_RESOURCES); |
| 298 return false; |
| 299 } |
| 300 std::memcpy(write_buf_->data() + write_end_, data, len); |
| 301 write_end_ += len; |
| 302 // If we're TLS-connecting, the write buffer will get flushed once |
| 303 // the TLS-connect finishes. Otherwise, start writing if we're not |
| 304 // already writing and we have something to write. |
| 305 if ((state_ != STATE_TLS_CONNECTING) && |
| 306 (write_state_ == IDLE) && (write_end_ > 0)) { |
| 307 // We defer execution of DoWrite() here for similar reasons as |
| 308 // ProcessConnectDone(). |
| 309 PostDoWrite(); |
| 310 } |
| 311 return true; |
| 312 } |
| 313 |
| 314 // write_state_ == IDLE -> write_state_ == POSTED |
| 315 |
| 316 void ChromeAsyncSocket::PostDoWrite() { |
| 317 DCHECK(IsOpen()); |
| 318 DCHECK_EQ(write_state_, IDLE); |
| 319 DCHECK_GT(write_end_, 0); |
| 320 MessageLoop* message_loop = MessageLoop::current(); |
| 321 CHECK(message_loop); |
| 322 message_loop->PostTask( |
| 323 FROM_HERE, |
| 324 scoped_runnable_method_factory_.NewRunnableMethod( |
| 325 &ChromeAsyncSocket::DoWrite)); |
| 326 write_state_ = POSTED; |
| 327 } |
| 328 |
| 329 // write_state_ == POSTED -> write_state_ == PENDING |
| 330 |
| 331 void ChromeAsyncSocket::DoWrite() { |
| 332 DCHECK(IsOpen()); |
| 333 DCHECK_EQ(write_state_, POSTED); |
| 334 DCHECK_GT(write_end_, 0); |
| 335 // Once we call Write(), we cannot call StartTls() until the write |
| 336 // finishes. This is okay, as StartTls() is called only after we |
| 337 // have received a reply to a message we sent to the server and |
| 338 // before we send the next message. |
| 339 int status = |
| 340 transport_socket_->Write( |
| 341 write_buf_.get(), write_end_, &write_callback_); |
| 342 write_state_ = PENDING; |
| 343 if (status != net::ERR_IO_PENDING) { |
| 344 ProcessWriteDone(status); |
| 345 } |
| 346 } |
| 347 |
| 348 // write_state_ == PENDING -> write_state_ == IDLE or POSTED (the |
| 349 // latter via PostDoWrite()) |
| 350 |
| 351 void ChromeAsyncSocket::ProcessWriteDone(int status) { |
| 352 DCHECK_NE(status, net::ERR_IO_PENDING); |
| 353 DCHECK(IsOpen()); |
| 354 DCHECK_EQ(write_state_, PENDING); |
| 355 DCHECK_GT(write_end_, 0); |
| 356 write_state_ = IDLE; |
| 357 if (status < net::OK) { |
| 358 DoNetErrorFromStatus(status); |
| 359 DoClose(); |
| 360 return; |
| 361 } |
| 362 if (status > write_end_) { |
| 363 LOG(DFATAL) << "bytes read = " << status |
| 364 << " exceeds bytes requested = " << write_end_; |
| 365 DoNetError(net::ERR_UNEXPECTED); |
| 366 DoClose(); |
| 367 return; |
| 368 } |
| 369 // TODO(akalin): Figure out a better way to do this; perhaps a queue |
| 370 // of DrainableIOBuffers. This'll also allow us to not have an |
| 371 // artificial buffer size limit. |
| 372 std::memmove(write_buf_->data(), |
| 373 write_buf_->data() + status, |
| 374 write_end_ - status); |
| 375 write_end_ -= status; |
| 376 if (write_end_ > 0) { |
| 377 PostDoWrite(); |
| 378 } |
| 379 } |
| 380 |
| 381 // * -> STATE_CLOSED |
| 382 |
| 383 bool ChromeAsyncSocket::Close() { |
| 384 DoClose(); |
| 385 return true; |
| 386 } |
| 387 |
| 388 // (not STATE_CLOSED) -> STATE_CLOSED |
| 389 |
| 390 void ChromeAsyncSocket::DoClose() { |
| 391 scoped_runnable_method_factory_.RevokeAll(); |
| 392 if (transport_socket_.get()) { |
| 393 transport_socket_->Disconnect(); |
| 394 } |
| 395 transport_socket_.reset(); |
| 396 read_state_ = IDLE; |
| 397 read_start_ = 0; |
| 398 read_end_ = 0; |
| 399 write_state_ = IDLE; |
| 400 write_end_ = 0; |
| 401 if (state_ != STATE_CLOSED) { |
| 402 state_ = STATE_CLOSED; |
| 403 SignalClosed(); |
| 404 } |
| 405 // Reset error variables after SignalClosed() so slots connected |
| 406 // to it can read it. |
| 407 error_ = ERROR_NONE; |
| 408 net_error_ = net::OK; |
| 409 } |
| 410 |
| 411 // STATE_OPEN -> STATE_TLS_CONNECTING |
| 412 |
| 413 bool ChromeAsyncSocket::StartTls(const std::string& domain_name) { |
| 414 if ((state_ != STATE_OPEN) || (read_state_ == PENDING) || |
| 415 (write_state_ != IDLE)) { |
| 416 LOG(DFATAL) << "StartTls() called in wrong state"; |
| 417 DoNonNetError(ERROR_WRONGSTATE); |
| 418 return false; |
| 419 } |
| 420 |
| 421 state_ = STATE_TLS_CONNECTING; |
| 422 read_state_ = IDLE; |
| 423 read_start_ = 0; |
| 424 read_end_ = 0; |
| 425 DCHECK_EQ(write_end_, 0); |
| 426 |
| 427 // Clear out any posted DoRead() tasks. |
| 428 scoped_runnable_method_factory_.RevokeAll(); |
| 429 |
| 430 DCHECK(transport_socket_.get()); |
| 431 transport_socket_.reset( |
| 432 client_socket_factory_->CreateSSLClientSocket( |
| 433 transport_socket_.release(), domain_name, ssl_config_)); |
| 434 int status = transport_socket_->Connect(&ssl_connect_callback_); |
| 435 if (status != net::ERR_IO_PENDING) { |
| 436 MessageLoop* message_loop = MessageLoop::current(); |
| 437 CHECK(message_loop); |
| 438 message_loop->PostTask( |
| 439 FROM_HERE, |
| 440 scoped_runnable_method_factory_.NewRunnableMethod( |
| 441 &ChromeAsyncSocket::ProcessSSLConnectDone, status)); |
| 442 } |
| 443 return true; |
| 444 } |
| 445 |
| 446 // STATE_TLS_CONNECTING -> STATE_TLS_OPEN |
| 447 // read_state_ == IDLE -> read_state_ == POSTED (via PostDoRead()) |
| 448 // (maybe) write_state_ == IDLE -> write_state_ == POSTED (via |
| 449 // PostDoWrite()) |
| 450 |
| 451 void ChromeAsyncSocket::ProcessSSLConnectDone(int status) { |
| 452 DCHECK_NE(status, net::ERR_IO_PENDING); |
| 453 DCHECK_EQ(state_, STATE_TLS_CONNECTING); |
| 454 DCHECK_EQ(read_state_, IDLE); |
| 455 DCHECK_EQ(read_start_, 0); |
| 456 DCHECK_EQ(read_end_, 0); |
| 457 DCHECK_EQ(write_state_, IDLE); |
| 458 if (status != net::OK) { |
| 459 DoNetErrorFromStatus(status); |
| 460 return; |
| 461 } |
| 462 state_ = STATE_TLS_OPEN; |
| 463 PostDoRead(); |
| 464 if (write_end_ > 0) { |
| 465 PostDoWrite(); |
| 466 } |
| 467 SignalSSLConnected(); |
| 468 } |
| 469 |
| 470 } // namespace sync_tools |
OLD | NEW |