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