Home | History | Annotate | Download | only in proxy
      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/proxy/tcp_socket_resource_base.h"
      6 
      7 #include <cstring>
      8 
      9 #include "base/bind.h"
     10 #include "base/logging.h"
     11 #include "ppapi/c/pp_bool.h"
     12 #include "ppapi/c/pp_errors.h"
     13 #include "ppapi/proxy/error_conversion.h"
     14 #include "ppapi/proxy/ppapi_messages.h"
     15 #include "ppapi/shared_impl/ppapi_globals.h"
     16 #include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h"
     17 #include "ppapi/shared_impl/socket_option_data.h"
     18 #include "ppapi/shared_impl/var.h"
     19 #include "ppapi/shared_impl/var_tracker.h"
     20 #include "ppapi/thunk/enter.h"
     21 #include "ppapi/thunk/ppb_x509_certificate_private_api.h"
     22 
     23 namespace ppapi {
     24 namespace proxy {
     25 
     26 const int32_t TCPSocketResourceBase::kMaxReadSize = 1024 * 1024;
     27 const int32_t TCPSocketResourceBase::kMaxWriteSize = 1024 * 1024;
     28 const int32_t TCPSocketResourceBase::kMaxSendBufferSize =
     29     1024 * TCPSocketResourceBase::kMaxWriteSize;
     30 const int32_t TCPSocketResourceBase::kMaxReceiveBufferSize =
     31     1024 * TCPSocketResourceBase::kMaxReadSize;
     32 
     33 TCPSocketResourceBase::TCPSocketResourceBase(Connection connection,
     34                                              PP_Instance instance,
     35                                              TCPSocketVersion version)
     36     : PluginResource(connection, instance),
     37       state_(TCPSocketState::INITIAL),
     38       read_buffer_(NULL),
     39       bytes_to_read_(-1),
     40       accepted_tcp_socket_(NULL),
     41       version_(version) {
     42   local_addr_.size = 0;
     43   memset(local_addr_.data, 0,
     44          arraysize(local_addr_.data) * sizeof(*local_addr_.data));
     45   remote_addr_.size = 0;
     46   memset(remote_addr_.data, 0,
     47          arraysize(remote_addr_.data) * sizeof(*remote_addr_.data));
     48 }
     49 
     50 TCPSocketResourceBase::TCPSocketResourceBase(
     51     Connection connection,
     52     PP_Instance instance,
     53     TCPSocketVersion version,
     54     const PP_NetAddress_Private& local_addr,
     55     const PP_NetAddress_Private& remote_addr)
     56     : PluginResource(connection, instance),
     57       state_(TCPSocketState::CONNECTED),
     58       read_buffer_(NULL),
     59       bytes_to_read_(-1),
     60       local_addr_(local_addr),
     61       remote_addr_(remote_addr),
     62       accepted_tcp_socket_(NULL),
     63       version_(version) {
     64 }
     65 
     66 TCPSocketResourceBase::~TCPSocketResourceBase() {
     67   CloseImpl();
     68 }
     69 
     70 int32_t TCPSocketResourceBase::BindImpl(
     71     const PP_NetAddress_Private* addr,
     72     scoped_refptr<TrackedCallback> callback) {
     73   if (!addr)
     74     return PP_ERROR_BADARGUMENT;
     75   if (state_.IsPending(TCPSocketState::BIND))
     76     return PP_ERROR_INPROGRESS;
     77   if (!state_.IsValidTransition(TCPSocketState::BIND))
     78     return PP_ERROR_FAILED;
     79 
     80   bind_callback_ = callback;
     81   state_.SetPendingTransition(TCPSocketState::BIND);
     82 
     83   Call<PpapiPluginMsg_TCPSocket_BindReply>(
     84       BROWSER,
     85       PpapiHostMsg_TCPSocket_Bind(*addr),
     86       base::Bind(&TCPSocketResourceBase::OnPluginMsgBindReply,
     87                  base::Unretained(this)),
     88       callback);
     89   return PP_OK_COMPLETIONPENDING;
     90 }
     91 
     92 int32_t TCPSocketResourceBase::ConnectImpl(
     93     const char* host,
     94     uint16_t port,
     95     scoped_refptr<TrackedCallback> callback) {
     96   if (!host)
     97     return PP_ERROR_BADARGUMENT;
     98   if (state_.IsPending(TCPSocketState::CONNECT))
     99     return PP_ERROR_INPROGRESS;
    100   if (!state_.IsValidTransition(TCPSocketState::CONNECT))
    101     return PP_ERROR_FAILED;
    102 
    103   connect_callback_ = callback;
    104   state_.SetPendingTransition(TCPSocketState::CONNECT);
    105 
    106   Call<PpapiPluginMsg_TCPSocket_ConnectReply>(
    107       BROWSER,
    108       PpapiHostMsg_TCPSocket_Connect(host, port),
    109       base::Bind(&TCPSocketResourceBase::OnPluginMsgConnectReply,
    110                  base::Unretained(this)),
    111       callback);
    112   return PP_OK_COMPLETIONPENDING;
    113 }
    114 
    115 int32_t TCPSocketResourceBase::ConnectWithNetAddressImpl(
    116     const PP_NetAddress_Private* addr,
    117     scoped_refptr<TrackedCallback> callback) {
    118   if (!addr)
    119     return PP_ERROR_BADARGUMENT;
    120   if (state_.IsPending(TCPSocketState::CONNECT))
    121     return PP_ERROR_INPROGRESS;
    122   if (!state_.IsValidTransition(TCPSocketState::CONNECT))
    123     return PP_ERROR_FAILED;
    124 
    125   connect_callback_ = callback;
    126   state_.SetPendingTransition(TCPSocketState::CONNECT);
    127 
    128   Call<PpapiPluginMsg_TCPSocket_ConnectReply>(
    129       BROWSER,
    130       PpapiHostMsg_TCPSocket_ConnectWithNetAddress(*addr),
    131       base::Bind(&TCPSocketResourceBase::OnPluginMsgConnectReply,
    132                  base::Unretained(this)),
    133       callback);
    134   return PP_OK_COMPLETIONPENDING;
    135 }
    136 
    137 PP_Bool TCPSocketResourceBase::GetLocalAddressImpl(
    138     PP_NetAddress_Private* local_addr) {
    139   if (!state_.IsBound() || !local_addr)
    140     return PP_FALSE;
    141   *local_addr = local_addr_;
    142   return PP_TRUE;
    143 }
    144 
    145 PP_Bool TCPSocketResourceBase::GetRemoteAddressImpl(
    146     PP_NetAddress_Private* remote_addr) {
    147   if (!state_.IsConnected() || !remote_addr)
    148     return PP_FALSE;
    149   *remote_addr = remote_addr_;
    150   return PP_TRUE;
    151 }
    152 
    153 int32_t TCPSocketResourceBase::SSLHandshakeImpl(
    154     const char* server_name,
    155     uint16_t server_port,
    156     scoped_refptr<TrackedCallback> callback) {
    157   if (!server_name)
    158     return PP_ERROR_BADARGUMENT;
    159 
    160   if (state_.IsPending(TCPSocketState::SSL_CONNECT) ||
    161       TrackedCallback::IsPending(read_callback_) ||
    162       TrackedCallback::IsPending(write_callback_)) {
    163     return PP_ERROR_INPROGRESS;
    164   }
    165   if (!state_.IsValidTransition(TCPSocketState::SSL_CONNECT))
    166     return PP_ERROR_FAILED;
    167 
    168   ssl_handshake_callback_ = callback;
    169   state_.SetPendingTransition(TCPSocketState::SSL_CONNECT);
    170 
    171   Call<PpapiPluginMsg_TCPSocket_SSLHandshakeReply>(
    172       BROWSER,
    173       PpapiHostMsg_TCPSocket_SSLHandshake(server_name,
    174                                           server_port,
    175                                           trusted_certificates_,
    176                                           untrusted_certificates_),
    177       base::Bind(&TCPSocketResourceBase::OnPluginMsgSSLHandshakeReply,
    178                  base::Unretained(this)),
    179       callback);
    180   return PP_OK_COMPLETIONPENDING;
    181 }
    182 
    183 PP_Resource TCPSocketResourceBase::GetServerCertificateImpl() {
    184   if (!server_certificate_.get())
    185     return 0;
    186   return server_certificate_->GetReference();
    187 }
    188 
    189 PP_Bool TCPSocketResourceBase::AddChainBuildingCertificateImpl(
    190     PP_Resource certificate,
    191     PP_Bool trusted) {
    192   // TODO(raymes): This is exposed in the private PPB_TCPSocket_Private
    193   // interface for Flash but isn't currently implemented due to security
    194   // implications. It is exposed so that it can be hooked up on the Flash side
    195   // and if we decide to implement it we can do so without modifying the Flash
    196   // codebase.
    197   NOTIMPLEMENTED();
    198   return PP_FALSE;
    199 }
    200 
    201 int32_t TCPSocketResourceBase::ReadImpl(
    202     char* buffer,
    203     int32_t bytes_to_read,
    204     scoped_refptr<TrackedCallback> callback) {
    205   if (!buffer || bytes_to_read <= 0)
    206     return PP_ERROR_BADARGUMENT;
    207 
    208   if (!state_.IsConnected())
    209     return PP_ERROR_FAILED;
    210   if (TrackedCallback::IsPending(read_callback_) ||
    211       state_.IsPending(TCPSocketState::SSL_CONNECT))
    212     return PP_ERROR_INPROGRESS;
    213   read_buffer_ = buffer;
    214   bytes_to_read_ = std::min(bytes_to_read, kMaxReadSize);
    215   read_callback_ = callback;
    216 
    217   Call<PpapiPluginMsg_TCPSocket_ReadReply>(
    218       BROWSER,
    219       PpapiHostMsg_TCPSocket_Read(bytes_to_read_),
    220       base::Bind(&TCPSocketResourceBase::OnPluginMsgReadReply,
    221                  base::Unretained(this)),
    222       callback);
    223   return PP_OK_COMPLETIONPENDING;
    224 }
    225 
    226 int32_t TCPSocketResourceBase::WriteImpl(
    227     const char* buffer,
    228     int32_t bytes_to_write,
    229     scoped_refptr<TrackedCallback> callback) {
    230   if (!buffer || bytes_to_write <= 0)
    231     return PP_ERROR_BADARGUMENT;
    232 
    233   if (!state_.IsConnected())
    234     return PP_ERROR_FAILED;
    235   if (TrackedCallback::IsPending(write_callback_) ||
    236       state_.IsPending(TCPSocketState::SSL_CONNECT))
    237     return PP_ERROR_INPROGRESS;
    238 
    239   if (bytes_to_write > kMaxWriteSize)
    240     bytes_to_write = kMaxWriteSize;
    241 
    242   write_callback_ = callback;
    243 
    244   Call<PpapiPluginMsg_TCPSocket_WriteReply>(
    245       BROWSER,
    246       PpapiHostMsg_TCPSocket_Write(std::string(buffer, bytes_to_write)),
    247       base::Bind(&TCPSocketResourceBase::OnPluginMsgWriteReply,
    248                  base::Unretained(this)),
    249       callback);
    250   return PP_OK_COMPLETIONPENDING;
    251 }
    252 
    253 int32_t TCPSocketResourceBase::ListenImpl(
    254     int32_t backlog,
    255     scoped_refptr<TrackedCallback> callback) {
    256   if (backlog <= 0)
    257     return PP_ERROR_BADARGUMENT;
    258   if (state_.IsPending(TCPSocketState::LISTEN))
    259     return PP_ERROR_INPROGRESS;
    260   if (!state_.IsValidTransition(TCPSocketState::LISTEN))
    261     return PP_ERROR_FAILED;
    262 
    263   listen_callback_ = callback;
    264   state_.SetPendingTransition(TCPSocketState::LISTEN);
    265 
    266   Call<PpapiPluginMsg_TCPSocket_ListenReply>(
    267       BROWSER,
    268       PpapiHostMsg_TCPSocket_Listen(backlog),
    269       base::Bind(&TCPSocketResourceBase::OnPluginMsgListenReply,
    270                  base::Unretained(this)),
    271       callback);
    272   return PP_OK_COMPLETIONPENDING;
    273 }
    274 
    275 int32_t TCPSocketResourceBase::AcceptImpl(
    276     PP_Resource* accepted_tcp_socket,
    277     scoped_refptr<TrackedCallback> callback) {
    278   if (!accepted_tcp_socket)
    279     return PP_ERROR_BADARGUMENT;
    280   if (TrackedCallback::IsPending(accept_callback_))
    281     return PP_ERROR_INPROGRESS;
    282   if (state_.state() != TCPSocketState::LISTENING)
    283     return PP_ERROR_FAILED;
    284 
    285   accept_callback_ = callback;
    286   accepted_tcp_socket_ = accepted_tcp_socket;
    287 
    288   Call<PpapiPluginMsg_TCPSocket_AcceptReply>(
    289       BROWSER,
    290       PpapiHostMsg_TCPSocket_Accept(),
    291       base::Bind(&TCPSocketResourceBase::OnPluginMsgAcceptReply,
    292                  base::Unretained(this)),
    293       callback);
    294   return PP_OK_COMPLETIONPENDING;
    295 }
    296 
    297 void TCPSocketResourceBase::CloseImpl() {
    298   if (state_.state() == TCPSocketState::CLOSED)
    299     return;
    300 
    301   state_.DoTransition(TCPSocketState::CLOSE, true);
    302 
    303   Post(BROWSER, PpapiHostMsg_TCPSocket_Close());
    304 
    305   PostAbortIfNecessary(&bind_callback_);
    306   PostAbortIfNecessary(&connect_callback_);
    307   PostAbortIfNecessary(&ssl_handshake_callback_);
    308   PostAbortIfNecessary(&read_callback_);
    309   PostAbortIfNecessary(&write_callback_);
    310   PostAbortIfNecessary(&listen_callback_);
    311   PostAbortIfNecessary(&accept_callback_);
    312   read_buffer_ = NULL;
    313   bytes_to_read_ = -1;
    314   server_certificate_ = NULL;
    315   accepted_tcp_socket_ = NULL;
    316 }
    317 
    318 int32_t TCPSocketResourceBase::SetOptionImpl(
    319     PP_TCPSocket_Option name,
    320     const PP_Var& value,
    321     scoped_refptr<TrackedCallback> callback) {
    322   SocketOptionData option_data;
    323   switch (name) {
    324     case PP_TCPSOCKET_OPTION_NO_DELAY: {
    325       if (!state_.IsConnected())
    326         return PP_ERROR_FAILED;
    327 
    328       if (value.type != PP_VARTYPE_BOOL)
    329         return PP_ERROR_BADARGUMENT;
    330       option_data.SetBool(PP_ToBool(value.value.as_bool));
    331       break;
    332     }
    333     case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE:
    334     case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: {
    335       if (!state_.IsConnected())
    336         return PP_ERROR_FAILED;
    337 
    338       if (value.type != PP_VARTYPE_INT32)
    339         return PP_ERROR_BADARGUMENT;
    340       option_data.SetInt32(value.value.as_int);
    341       break;
    342     }
    343     default: {
    344       NOTREACHED();
    345       return PP_ERROR_BADARGUMENT;
    346     }
    347   }
    348 
    349   set_option_callbacks_.push(callback);
    350 
    351   Call<PpapiPluginMsg_TCPSocket_SetOptionReply>(
    352       BROWSER,
    353       PpapiHostMsg_TCPSocket_SetOption(name, option_data),
    354       base::Bind(&TCPSocketResourceBase::OnPluginMsgSetOptionReply,
    355                  base::Unretained(this)),
    356       callback);
    357   return PP_OK_COMPLETIONPENDING;
    358 }
    359 
    360 void TCPSocketResourceBase::PostAbortIfNecessary(
    361     scoped_refptr<TrackedCallback>* callback) {
    362   if (TrackedCallback::IsPending(*callback))
    363     (*callback)->PostAbort();
    364 }
    365 
    366 void TCPSocketResourceBase::OnPluginMsgBindReply(
    367     const ResourceMessageReplyParams& params,
    368     const PP_NetAddress_Private& local_addr) {
    369   // It is possible that CloseImpl() has been called. We don't want to update
    370   // class members in this case.
    371   if (!state_.IsPending(TCPSocketState::BIND))
    372     return;
    373 
    374   DCHECK(TrackedCallback::IsPending(bind_callback_));
    375   if (params.result() == PP_OK) {
    376     local_addr_ = local_addr;
    377     state_.CompletePendingTransition(true);
    378   } else {
    379     state_.CompletePendingTransition(false);
    380   }
    381   RunCallback(bind_callback_, params.result());
    382 }
    383 
    384 void TCPSocketResourceBase::OnPluginMsgConnectReply(
    385     const ResourceMessageReplyParams& params,
    386     const PP_NetAddress_Private& local_addr,
    387     const PP_NetAddress_Private& remote_addr) {
    388   // It is possible that CloseImpl() has been called. We don't want to update
    389   // class members in this case.
    390   if (!state_.IsPending(TCPSocketState::CONNECT))
    391     return;
    392 
    393   DCHECK(TrackedCallback::IsPending(connect_callback_));
    394   if (params.result() == PP_OK) {
    395     local_addr_ = local_addr;
    396     remote_addr_ = remote_addr;
    397     state_.CompletePendingTransition(true);
    398   } else {
    399     if (version_ == TCP_SOCKET_VERSION_1_1_OR_ABOVE) {
    400       state_.CompletePendingTransition(false);
    401     } else {
    402       // In order to maintain backward compatibility, allow to connect the
    403       // socket again.
    404       state_ = TCPSocketState(TCPSocketState::INITIAL);
    405     }
    406   }
    407   RunCallback(connect_callback_, params.result());
    408 }
    409 
    410 void TCPSocketResourceBase::OnPluginMsgSSLHandshakeReply(
    411       const ResourceMessageReplyParams& params,
    412       const PPB_X509Certificate_Fields& certificate_fields) {
    413   // It is possible that CloseImpl() has been called. We don't want to
    414   // update class members in this case.
    415   if (!state_.IsPending(TCPSocketState::SSL_CONNECT))
    416     return;
    417 
    418   DCHECK(TrackedCallback::IsPending(ssl_handshake_callback_));
    419   if (params.result() == PP_OK) {
    420     state_.CompletePendingTransition(true);
    421     server_certificate_ = new PPB_X509Certificate_Private_Shared(
    422         OBJECT_IS_PROXY,
    423         pp_instance(),
    424         certificate_fields);
    425   } else {
    426     state_.CompletePendingTransition(false);
    427   }
    428   RunCallback(ssl_handshake_callback_, params.result());
    429 }
    430 
    431 void TCPSocketResourceBase::OnPluginMsgReadReply(
    432     const ResourceMessageReplyParams& params,
    433     const std::string& data) {
    434   // It is possible that CloseImpl() has been called. We shouldn't access the
    435   // buffer in that case. The user may have released it.
    436   if (!state_.IsConnected() || !TrackedCallback::IsPending(read_callback_) ||
    437       !read_buffer_) {
    438     return;
    439   }
    440 
    441   const bool succeeded = params.result() == PP_OK;
    442   if (succeeded) {
    443     CHECK_LE(static_cast<int32_t>(data.size()), bytes_to_read_);
    444     if (!data.empty())
    445       memmove(read_buffer_, data.c_str(), data.size());
    446   }
    447   read_buffer_ = NULL;
    448   bytes_to_read_ = -1;
    449 
    450   RunCallback(read_callback_,
    451               succeeded ? static_cast<int32_t>(data.size()) : params.result());
    452 }
    453 
    454 void TCPSocketResourceBase::OnPluginMsgWriteReply(
    455     const ResourceMessageReplyParams& params) {
    456   if (!state_.IsConnected() || !TrackedCallback::IsPending(write_callback_))
    457     return;
    458   RunCallback(write_callback_, params.result());
    459 }
    460 
    461 void TCPSocketResourceBase::OnPluginMsgListenReply(
    462     const ResourceMessageReplyParams& params) {
    463   if (!state_.IsPending(TCPSocketState::LISTEN))
    464     return;
    465 
    466   DCHECK(TrackedCallback::IsPending(listen_callback_));
    467   state_.CompletePendingTransition(params.result() == PP_OK);
    468 
    469   RunCallback(listen_callback_, params.result());
    470 }
    471 
    472 void TCPSocketResourceBase::OnPluginMsgAcceptReply(
    473     const ResourceMessageReplyParams& params,
    474     int pending_host_id,
    475     const PP_NetAddress_Private& local_addr,
    476     const PP_NetAddress_Private& remote_addr) {
    477   // It is possible that CloseImpl() has been called. We shouldn't access the
    478   // output parameter in that case. The user may have released it.
    479   if (state_.state() != TCPSocketState::LISTENING ||
    480       !TrackedCallback::IsPending(accept_callback_) || !accepted_tcp_socket_) {
    481     return;
    482   }
    483 
    484   if (params.result() == PP_OK) {
    485     *accepted_tcp_socket_ = CreateAcceptedSocket(pending_host_id, local_addr,
    486                                                  remote_addr);
    487   }
    488   accepted_tcp_socket_ = NULL;
    489   RunCallback(accept_callback_, params.result());
    490 }
    491 
    492 void TCPSocketResourceBase::OnPluginMsgSetOptionReply(
    493     const ResourceMessageReplyParams& params) {
    494   if (set_option_callbacks_.empty()) {
    495     NOTREACHED();
    496     return;
    497   }
    498   scoped_refptr<TrackedCallback> callback = set_option_callbacks_.front();
    499   set_option_callbacks_.pop();
    500   if (TrackedCallback::IsPending(callback))
    501     RunCallback(callback, params.result());
    502 }
    503 
    504 void TCPSocketResourceBase::RunCallback(scoped_refptr<TrackedCallback> callback,
    505                                         int32_t pp_result) {
    506   callback->Run(ConvertNetworkAPIErrorForCompatibility(
    507       pp_result, version_ == TCP_SOCKET_VERSION_PRIVATE));
    508 }
    509 
    510 }  // namespace ppapi
    511 }  // namespace proxy
    512