Home | History | Annotate | Download | only in shared_impl
      1 // Copyright 2013 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 "ppapi/shared_impl/tcp_socket_shared.h"
      6 
      7 #include <string.h>
      8 
      9 #include <algorithm>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/bind.h"
     13 #include "base/logging.h"
     14 #include "ppapi/c/pp_bool.h"
     15 #include "ppapi/c/pp_completion_callback.h"
     16 #include "ppapi/c/pp_errors.h"
     17 #include "ppapi/shared_impl/ppapi_globals.h"
     18 #include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h"
     19 #include "ppapi/shared_impl/socket_option_data.h"
     20 #include "ppapi/shared_impl/var_tracker.h"
     21 #include "ppapi/shared_impl/var.h"
     22 #include "ppapi/thunk/enter.h"
     23 #include "ppapi/thunk/ppb_x509_certificate_private_api.h"
     24 
     25 namespace ppapi {
     26 
     27 const int32_t TCPSocketShared::kMaxReadSize = 1024 * 1024;
     28 const int32_t TCPSocketShared::kMaxWriteSize = 1024 * 1024;
     29 const int32_t TCPSocketShared::kMaxSendBufferSize =
     30     1024 * TCPSocketShared::kMaxWriteSize;
     31 const int32_t TCPSocketShared::kMaxReceiveBufferSize =
     32     1024 * TCPSocketShared::kMaxReadSize;
     33 
     34 TCPSocketShared::TCPSocketShared(ResourceObjectType resource_type,
     35                                  uint32 socket_id)
     36     : resource_type_(resource_type) {
     37   Init(socket_id);
     38 }
     39 
     40 TCPSocketShared::~TCPSocketShared() {
     41 }
     42 
     43 void TCPSocketShared::OnConnectCompleted(
     44     int32_t result,
     45     const PP_NetAddress_Private& local_addr,
     46     const PP_NetAddress_Private& remote_addr) {
     47   // It is possible that |connect_callback_| is pending while
     48   // |connection_state_| is not BEFORE_CONNECT: DisconnectImpl() has been
     49   // called, but a ConnectCompleted notification came earlier than the task to
     50   // abort |connect_callback_|. We don't want to update |connection_state_| or
     51   // other members in that case.
     52   if (connection_state_ != BEFORE_CONNECT ||
     53       !TrackedCallback::IsPending(connect_callback_)) {
     54     return;
     55   }
     56 
     57   result = OverridePPError(result);
     58   if (result == PP_OK) {
     59     local_addr_ = local_addr;
     60     remote_addr_ = remote_addr;
     61     connection_state_ = CONNECTED;
     62   }
     63   connect_callback_->Run(result);
     64 }
     65 
     66 void TCPSocketShared::OnSSLHandshakeCompleted(
     67     bool succeeded,
     68     const PPB_X509Certificate_Fields& certificate_fields) {
     69   // It is possible that |ssl_handshake_callback_| is pending while
     70   // |connection_state_| is not CONNECT: DisconnectImpl() has been
     71   // called, but a SSLHandshakeCompleted notification came earlier than the task
     72   // to abort |ssl_handshake_callback_|. We don't want to update
     73   // |connection_state_| or other members in that case.
     74   if (connection_state_ != CONNECTED ||
     75       !TrackedCallback::IsPending(ssl_handshake_callback_)) {
     76     return;
     77   }
     78 
     79   if (succeeded) {
     80     connection_state_ = SSL_CONNECTED;
     81     server_certificate_ = new PPB_X509Certificate_Private_Shared(
     82         resource_type_,
     83         GetOwnerResource()->pp_instance(),
     84         certificate_fields);
     85     ssl_handshake_callback_->Run(PP_OK);
     86   } else {
     87     // The resource might be released in the callback so we need to hold
     88     // a reference so we can Disconnect() first.
     89     GetOwnerResource()->AddRef();
     90     ssl_handshake_callback_->Run(PP_ERROR_FAILED);
     91     DisconnectImpl();
     92     GetOwnerResource()->Release();
     93   }
     94 }
     95 
     96 void TCPSocketShared::OnReadCompleted(int32_t result,
     97                                       const std::string& data) {
     98   // It is possible that |read_callback_| is pending while |read_buffer_| is
     99   // NULL: DisconnectImpl() has been called, but a ReadCompleted notification
    100   // came earlier than the task to abort |read_callback_|. We shouldn't access
    101   // the buffer in that case. The user may have released it.
    102   if (!TrackedCallback::IsPending(read_callback_) || !read_buffer_)
    103     return;
    104 
    105   result = OverridePPError(result);
    106   bool succeeded = result == PP_OK;
    107   if (succeeded) {
    108     CHECK_LE(static_cast<int32_t>(data.size()), bytes_to_read_);
    109     if (!data.empty())
    110       memcpy(read_buffer_, data.c_str(), data.size());
    111   }
    112   read_buffer_ = NULL;
    113   bytes_to_read_ = -1;
    114 
    115   read_callback_->Run(
    116       succeeded ? static_cast<int32_t>(data.size()) : result);
    117 }
    118 
    119 void TCPSocketShared::OnWriteCompleted(int32_t result) {
    120   if (!TrackedCallback::IsPending(write_callback_))
    121     return;
    122 
    123   result = OverridePPError(result);
    124   write_callback_->Run(result);
    125 }
    126 
    127 void TCPSocketShared::OnSetOptionCompleted(int32_t result) {
    128   if (set_option_callbacks_.empty()) {
    129     NOTREACHED();
    130     return;
    131   }
    132 
    133   result = OverridePPError(result);
    134   scoped_refptr<TrackedCallback> callback = set_option_callbacks_.front();
    135   set_option_callbacks_.pop();
    136 
    137   if (TrackedCallback::IsPending(callback))
    138     callback->Run(result);
    139 }
    140 
    141 int32_t TCPSocketShared::OverridePPError(int32_t pp_error) {
    142   return pp_error;
    143 }
    144 
    145 int32_t TCPSocketShared::ConnectImpl(const char* host,
    146                                      uint16_t port,
    147                                      scoped_refptr<TrackedCallback> callback) {
    148   if (!host)
    149     return PP_ERROR_BADARGUMENT;
    150   if (connection_state_ != BEFORE_CONNECT)
    151     return PP_ERROR_FAILED;
    152   if (TrackedCallback::IsPending(connect_callback_))
    153     return PP_ERROR_INPROGRESS;  // Can only have one pending request.
    154 
    155   connect_callback_ = callback;
    156   // Send the request, the browser will call us back via ConnectACK.
    157   SendConnect(host, port);
    158   return PP_OK_COMPLETIONPENDING;
    159 }
    160 
    161 int32_t TCPSocketShared::ConnectWithNetAddressImpl(
    162     const PP_NetAddress_Private* addr,
    163     scoped_refptr<TrackedCallback> callback) {
    164   if (!addr)
    165     return PP_ERROR_BADARGUMENT;
    166   if (connection_state_ != BEFORE_CONNECT)
    167     return PP_ERROR_FAILED;
    168   if (TrackedCallback::IsPending(connect_callback_))
    169     return PP_ERROR_INPROGRESS;  // Can only have one pending request.
    170 
    171   connect_callback_ = callback;
    172   // Send the request, the browser will call us back via ConnectACK.
    173   SendConnectWithNetAddress(*addr);
    174   return PP_OK_COMPLETIONPENDING;
    175 }
    176 
    177 PP_Bool TCPSocketShared::GetLocalAddressImpl(
    178     PP_NetAddress_Private* local_addr) {
    179   if (!IsConnected() || !local_addr)
    180     return PP_FALSE;
    181 
    182   *local_addr = local_addr_;
    183   return PP_TRUE;
    184 }
    185 
    186 PP_Bool TCPSocketShared::GetRemoteAddressImpl(
    187     PP_NetAddress_Private* remote_addr) {
    188   if (!IsConnected() || !remote_addr)
    189     return PP_FALSE;
    190 
    191   *remote_addr = remote_addr_;
    192   return PP_TRUE;
    193 }
    194 
    195 int32_t TCPSocketShared::SSLHandshakeImpl(
    196     const char* server_name,
    197     uint16_t server_port,
    198     scoped_refptr<TrackedCallback> callback) {
    199   if (!server_name)
    200     return PP_ERROR_BADARGUMENT;
    201 
    202   if (connection_state_ != CONNECTED)
    203     return PP_ERROR_FAILED;
    204   if (TrackedCallback::IsPending(ssl_handshake_callback_) ||
    205       TrackedCallback::IsPending(read_callback_) ||
    206       TrackedCallback::IsPending(write_callback_))
    207     return PP_ERROR_INPROGRESS;
    208 
    209   ssl_handshake_callback_ = callback;
    210 
    211   // Send the request, the browser will call us back via SSLHandshakeACK.
    212   SendSSLHandshake(server_name, server_port, trusted_certificates_,
    213       untrusted_certificates_);
    214   return PP_OK_COMPLETIONPENDING;
    215 }
    216 
    217 PP_Resource TCPSocketShared::GetServerCertificateImpl() {
    218   if (!server_certificate_.get())
    219     return 0;
    220   return server_certificate_->GetReference();
    221 }
    222 
    223 PP_Bool TCPSocketShared::AddChainBuildingCertificateImpl(
    224     PP_Resource certificate,
    225     PP_Bool trusted) {
    226   // TODO(raymes): The plumbing for this functionality is implemented but the
    227   // certificates aren't yet used for the connection, so just return false for
    228   // now.
    229   return PP_FALSE;
    230 
    231   thunk::EnterResourceNoLock<thunk::PPB_X509Certificate_Private_API>
    232   enter_cert(certificate, true);
    233   if (enter_cert.failed())
    234     return PP_FALSE;
    235 
    236   PP_Var der_var = enter_cert.object()->GetField(
    237       PP_X509CERTIFICATE_PRIVATE_RAW);
    238   ArrayBufferVar* der_array_buffer = ArrayBufferVar::FromPPVar(der_var);
    239   PP_Bool success = PP_FALSE;
    240   if (der_array_buffer) {
    241     const char* der_bytes = static_cast<const char*>(der_array_buffer->Map());
    242     uint32_t der_length = der_array_buffer->ByteLength();
    243     std::vector<char> der(der_bytes, der_bytes + der_length);
    244     if (PP_ToBool(trusted))
    245       trusted_certificates_.push_back(der);
    246     else
    247       untrusted_certificates_.push_back(der);
    248     success = PP_TRUE;
    249   }
    250   PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(der_var);
    251   return success;
    252 }
    253 
    254 int32_t TCPSocketShared::ReadImpl(char* buffer,
    255                                   int32_t bytes_to_read,
    256                                   scoped_refptr<TrackedCallback> callback) {
    257   if (!buffer || bytes_to_read <= 0)
    258     return PP_ERROR_BADARGUMENT;
    259 
    260   if (!IsConnected())
    261     return PP_ERROR_FAILED;
    262   if (TrackedCallback::IsPending(read_callback_) ||
    263       TrackedCallback::IsPending(ssl_handshake_callback_))
    264     return PP_ERROR_INPROGRESS;
    265   read_buffer_ = buffer;
    266   bytes_to_read_ = std::min(bytes_to_read, kMaxReadSize);
    267   read_callback_ = callback;
    268 
    269   // Send the request, the browser will call us back via ReadACK.
    270   SendRead(bytes_to_read_);
    271   return PP_OK_COMPLETIONPENDING;
    272 }
    273 
    274 int32_t TCPSocketShared::WriteImpl(const char* buffer,
    275                                    int32_t bytes_to_write,
    276                                    scoped_refptr<TrackedCallback> callback) {
    277   if (!buffer || bytes_to_write <= 0)
    278     return PP_ERROR_BADARGUMENT;
    279 
    280   if (!IsConnected())
    281     return PP_ERROR_FAILED;
    282   if (TrackedCallback::IsPending(write_callback_) ||
    283       TrackedCallback::IsPending(ssl_handshake_callback_))
    284     return PP_ERROR_INPROGRESS;
    285 
    286   if (bytes_to_write > kMaxWriteSize)
    287     bytes_to_write = kMaxWriteSize;
    288 
    289   write_callback_ = callback;
    290 
    291   // Send the request, the browser will call us back via WriteACK.
    292   SendWrite(std::string(buffer, bytes_to_write));
    293   return PP_OK_COMPLETIONPENDING;
    294 }
    295 
    296 void TCPSocketShared::DisconnectImpl() {
    297   if (connection_state_ == DISCONNECTED)
    298     return;
    299 
    300   connection_state_ = DISCONNECTED;
    301 
    302   SendDisconnect();
    303   socket_id_ = 0;
    304 
    305   PostAbortIfNecessary(&connect_callback_);
    306   PostAbortIfNecessary(&ssl_handshake_callback_);
    307   PostAbortIfNecessary(&read_callback_);
    308   PostAbortIfNecessary(&write_callback_);
    309   read_buffer_ = NULL;
    310   bytes_to_read_ = -1;
    311   server_certificate_ = NULL;
    312 }
    313 
    314 int32_t TCPSocketShared::SetOptionImpl(
    315     PP_TCPSocket_Option name,
    316     const PP_Var& value,
    317     scoped_refptr<TrackedCallback> callback) {
    318   if (!IsConnected())
    319     return PP_ERROR_FAILED;
    320 
    321   SocketOptionData option_data;
    322   switch (name) {
    323     case PP_TCPSOCKET_OPTION_NO_DELAY: {
    324       if (value.type != PP_VARTYPE_BOOL)
    325         return PP_ERROR_BADARGUMENT;
    326       option_data.SetBool(PP_ToBool(value.value.as_bool));
    327       break;
    328     }
    329     case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE:
    330     case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: {
    331       if (value.type != PP_VARTYPE_INT32)
    332         return PP_ERROR_BADARGUMENT;
    333       option_data.SetInt32(value.value.as_int);
    334       break;
    335     }
    336     default: {
    337       NOTREACHED();
    338       return PP_ERROR_BADARGUMENT;
    339     }
    340   }
    341 
    342   set_option_callbacks_.push(callback);
    343   SendSetOption(name, option_data);
    344   return PP_OK_COMPLETIONPENDING;
    345 }
    346 
    347 void TCPSocketShared::Init(uint32 socket_id) {
    348   DCHECK(socket_id != 0);
    349   socket_id_ = socket_id;
    350   connection_state_ = BEFORE_CONNECT;
    351   read_buffer_ = NULL;
    352   bytes_to_read_ = -1;
    353 
    354   local_addr_.size = 0;
    355   memset(local_addr_.data, 0,
    356          arraysize(local_addr_.data) * sizeof(*local_addr_.data));
    357   remote_addr_.size = 0;
    358   memset(remote_addr_.data, 0,
    359          arraysize(remote_addr_.data) * sizeof(*remote_addr_.data));
    360 }
    361 
    362 bool TCPSocketShared::IsConnected() const {
    363   return connection_state_ == CONNECTED || connection_state_ == SSL_CONNECTED;
    364 }
    365 
    366 void TCPSocketShared::PostAbortIfNecessary(
    367     scoped_refptr<TrackedCallback>* callback) {
    368   if (TrackedCallback::IsPending(*callback))
    369     (*callback)->PostAbort();
    370 }
    371 
    372 }  // namespace ppapi
    373