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): The plumbing for this functionality is implemented but the
    193   // certificates aren't yet used for the connection, so just return false for
    194   // now.
    195   return PP_FALSE;
    196 
    197   thunk::EnterResourceNoLock<thunk::PPB_X509Certificate_Private_API>
    198   enter_cert(certificate, true);
    199   if (enter_cert.failed())
    200     return PP_FALSE;
    201 
    202   PP_Var der_var = enter_cert.object()->GetField(
    203       PP_X509CERTIFICATE_PRIVATE_RAW);
    204   ArrayBufferVar* der_array_buffer = ArrayBufferVar::FromPPVar(der_var);
    205   PP_Bool success = PP_FALSE;
    206   if (der_array_buffer) {
    207     const char* der_bytes = static_cast<const char*>(der_array_buffer->Map());
    208     uint32_t der_length = der_array_buffer->ByteLength();
    209     std::vector<char> der(der_bytes, der_bytes + der_length);
    210     if (PP_ToBool(trusted))
    211       trusted_certificates_.push_back(der);
    212     else
    213       untrusted_certificates_.push_back(der);
    214     success = PP_TRUE;
    215   }
    216   PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(der_var);
    217   return success;
    218 }
    219 
    220 int32_t TCPSocketResourceBase::ReadImpl(
    221     char* buffer,
    222     int32_t bytes_to_read,
    223     scoped_refptr<TrackedCallback> callback) {
    224   if (!buffer || bytes_to_read <= 0)
    225     return PP_ERROR_BADARGUMENT;
    226 
    227   if (!state_.IsConnected())
    228     return PP_ERROR_FAILED;
    229   if (TrackedCallback::IsPending(read_callback_) ||
    230       state_.IsPending(TCPSocketState::SSL_CONNECT))
    231     return PP_ERROR_INPROGRESS;
    232   read_buffer_ = buffer;
    233   bytes_to_read_ = std::min(bytes_to_read, kMaxReadSize);
    234   read_callback_ = callback;
    235 
    236   Call<PpapiPluginMsg_TCPSocket_ReadReply>(
    237       BROWSER,
    238       PpapiHostMsg_TCPSocket_Read(bytes_to_read_),
    239       base::Bind(&TCPSocketResourceBase::OnPluginMsgReadReply,
    240                  base::Unretained(this)),
    241       callback);
    242   return PP_OK_COMPLETIONPENDING;
    243 }
    244 
    245 int32_t TCPSocketResourceBase::WriteImpl(
    246     const char* buffer,
    247     int32_t bytes_to_write,
    248     scoped_refptr<TrackedCallback> callback) {
    249   if (!buffer || bytes_to_write <= 0)
    250     return PP_ERROR_BADARGUMENT;
    251 
    252   if (!state_.IsConnected())
    253     return PP_ERROR_FAILED;
    254   if (TrackedCallback::IsPending(write_callback_) ||
    255       state_.IsPending(TCPSocketState::SSL_CONNECT))
    256     return PP_ERROR_INPROGRESS;
    257 
    258   if (bytes_to_write > kMaxWriteSize)
    259     bytes_to_write = kMaxWriteSize;
    260 
    261   write_callback_ = callback;
    262 
    263   Call<PpapiPluginMsg_TCPSocket_WriteReply>(
    264       BROWSER,
    265       PpapiHostMsg_TCPSocket_Write(std::string(buffer, bytes_to_write)),
    266       base::Bind(&TCPSocketResourceBase::OnPluginMsgWriteReply,
    267                  base::Unretained(this)),
    268       callback);
    269   return PP_OK_COMPLETIONPENDING;
    270 }
    271 
    272 int32_t TCPSocketResourceBase::ListenImpl(
    273     int32_t backlog,
    274     scoped_refptr<TrackedCallback> callback) {
    275   if (backlog <= 0)
    276     return PP_ERROR_BADARGUMENT;
    277   if (state_.IsPending(TCPSocketState::LISTEN))
    278     return PP_ERROR_INPROGRESS;
    279   if (!state_.IsValidTransition(TCPSocketState::LISTEN))
    280     return PP_ERROR_FAILED;
    281 
    282   listen_callback_ = callback;
    283   state_.SetPendingTransition(TCPSocketState::LISTEN);
    284 
    285   Call<PpapiPluginMsg_TCPSocket_ListenReply>(
    286       BROWSER,
    287       PpapiHostMsg_TCPSocket_Listen(backlog),
    288       base::Bind(&TCPSocketResourceBase::OnPluginMsgListenReply,
    289                  base::Unretained(this)),
    290       callback);
    291   return PP_OK_COMPLETIONPENDING;
    292 }
    293 
    294 int32_t TCPSocketResourceBase::AcceptImpl(
    295     PP_Resource* accepted_tcp_socket,
    296     scoped_refptr<TrackedCallback> callback) {
    297   if (!accepted_tcp_socket)
    298     return PP_ERROR_BADARGUMENT;
    299   if (TrackedCallback::IsPending(accept_callback_))
    300     return PP_ERROR_INPROGRESS;
    301   if (state_.state() != TCPSocketState::LISTENING)
    302     return PP_ERROR_FAILED;
    303 
    304   accept_callback_ = callback;
    305   accepted_tcp_socket_ = accepted_tcp_socket;
    306 
    307   Call<PpapiPluginMsg_TCPSocket_AcceptReply>(
    308       BROWSER,
    309       PpapiHostMsg_TCPSocket_Accept(),
    310       base::Bind(&TCPSocketResourceBase::OnPluginMsgAcceptReply,
    311                  base::Unretained(this)),
    312       callback);
    313   return PP_OK_COMPLETIONPENDING;
    314 }
    315 
    316 void TCPSocketResourceBase::CloseImpl() {
    317   if (state_.state() == TCPSocketState::CLOSED)
    318     return;
    319 
    320   state_.DoTransition(TCPSocketState::CLOSE, true);
    321 
    322   Post(BROWSER, PpapiHostMsg_TCPSocket_Close());
    323 
    324   PostAbortIfNecessary(&bind_callback_);
    325   PostAbortIfNecessary(&connect_callback_);
    326   PostAbortIfNecessary(&ssl_handshake_callback_);
    327   PostAbortIfNecessary(&read_callback_);
    328   PostAbortIfNecessary(&write_callback_);
    329   PostAbortIfNecessary(&listen_callback_);
    330   PostAbortIfNecessary(&accept_callback_);
    331   read_buffer_ = NULL;
    332   bytes_to_read_ = -1;
    333   server_certificate_ = NULL;
    334   accepted_tcp_socket_ = NULL;
    335 }
    336 
    337 int32_t TCPSocketResourceBase::SetOptionImpl(
    338     PP_TCPSocket_Option name,
    339     const PP_Var& value,
    340     scoped_refptr<TrackedCallback> callback) {
    341   SocketOptionData option_data;
    342   switch (name) {
    343     case PP_TCPSOCKET_OPTION_NO_DELAY: {
    344       if (!state_.IsConnected())
    345         return PP_ERROR_FAILED;
    346 
    347       if (value.type != PP_VARTYPE_BOOL)
    348         return PP_ERROR_BADARGUMENT;
    349       option_data.SetBool(PP_ToBool(value.value.as_bool));
    350       break;
    351     }
    352     case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE:
    353     case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: {
    354       if (!state_.IsConnected())
    355         return PP_ERROR_FAILED;
    356 
    357       if (value.type != PP_VARTYPE_INT32)
    358         return PP_ERROR_BADARGUMENT;
    359       option_data.SetInt32(value.value.as_int);
    360       break;
    361     }
    362     default: {
    363       NOTREACHED();
    364       return PP_ERROR_BADARGUMENT;
    365     }
    366   }
    367 
    368   set_option_callbacks_.push(callback);
    369 
    370   Call<PpapiPluginMsg_TCPSocket_SetOptionReply>(
    371       BROWSER,
    372       PpapiHostMsg_TCPSocket_SetOption(name, option_data),
    373       base::Bind(&TCPSocketResourceBase::OnPluginMsgSetOptionReply,
    374                  base::Unretained(this)),
    375       callback);
    376   return PP_OK_COMPLETIONPENDING;
    377 }
    378 
    379 void TCPSocketResourceBase::PostAbortIfNecessary(
    380     scoped_refptr<TrackedCallback>* callback) {
    381   if (TrackedCallback::IsPending(*callback))
    382     (*callback)->PostAbort();
    383 }
    384 
    385 void TCPSocketResourceBase::OnPluginMsgBindReply(
    386     const ResourceMessageReplyParams& params,
    387     const PP_NetAddress_Private& local_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::BIND))
    391     return;
    392 
    393   DCHECK(TrackedCallback::IsPending(bind_callback_));
    394   if (params.result() == PP_OK) {
    395     local_addr_ = local_addr;
    396     state_.CompletePendingTransition(true);
    397   } else {
    398     state_.CompletePendingTransition(false);
    399   }
    400   RunCallback(bind_callback_, params.result());
    401 }
    402 
    403 void TCPSocketResourceBase::OnPluginMsgConnectReply(
    404     const ResourceMessageReplyParams& params,
    405     const PP_NetAddress_Private& local_addr,
    406     const PP_NetAddress_Private& remote_addr) {
    407   // It is possible that CloseImpl() has been called. We don't want to update
    408   // class members in this case.
    409   if (!state_.IsPending(TCPSocketState::CONNECT))
    410     return;
    411 
    412   DCHECK(TrackedCallback::IsPending(connect_callback_));
    413   if (params.result() == PP_OK) {
    414     local_addr_ = local_addr;
    415     remote_addr_ = remote_addr;
    416     state_.CompletePendingTransition(true);
    417   } else {
    418     if (version_ == TCP_SOCKET_VERSION_1_1_OR_ABOVE) {
    419       state_.CompletePendingTransition(false);
    420     } else {
    421       // In order to maintain backward compatibility, allow to connect the
    422       // socket again.
    423       state_ = TCPSocketState(TCPSocketState::INITIAL);
    424     }
    425   }
    426   RunCallback(connect_callback_, params.result());
    427 }
    428 
    429 void TCPSocketResourceBase::OnPluginMsgSSLHandshakeReply(
    430       const ResourceMessageReplyParams& params,
    431       const PPB_X509Certificate_Fields& certificate_fields) {
    432   // It is possible that CloseImpl() has been called. We don't want to
    433   // update class members in this case.
    434   if (!state_.IsPending(TCPSocketState::SSL_CONNECT))
    435     return;
    436 
    437   DCHECK(TrackedCallback::IsPending(ssl_handshake_callback_));
    438   if (params.result() == PP_OK) {
    439     state_.CompletePendingTransition(true);
    440     server_certificate_ = new PPB_X509Certificate_Private_Shared(
    441         OBJECT_IS_PROXY,
    442         pp_instance(),
    443         certificate_fields);
    444   } else {
    445     state_.CompletePendingTransition(false);
    446   }
    447   RunCallback(ssl_handshake_callback_, params.result());
    448 }
    449 
    450 void TCPSocketResourceBase::OnPluginMsgReadReply(
    451     const ResourceMessageReplyParams& params,
    452     const std::string& data) {
    453   // It is possible that CloseImpl() has been called. We shouldn't access the
    454   // buffer in that case. The user may have released it.
    455   if (!state_.IsConnected() || !TrackedCallback::IsPending(read_callback_) ||
    456       !read_buffer_) {
    457     return;
    458   }
    459 
    460   const bool succeeded = params.result() == PP_OK;
    461   if (succeeded) {
    462     CHECK_LE(static_cast<int32_t>(data.size()), bytes_to_read_);
    463     if (!data.empty())
    464       memmove(read_buffer_, data.c_str(), data.size());
    465   }
    466   read_buffer_ = NULL;
    467   bytes_to_read_ = -1;
    468 
    469   RunCallback(read_callback_,
    470               succeeded ? static_cast<int32_t>(data.size()) : params.result());
    471 }
    472 
    473 void TCPSocketResourceBase::OnPluginMsgWriteReply(
    474     const ResourceMessageReplyParams& params) {
    475   if (!state_.IsConnected() || !TrackedCallback::IsPending(write_callback_))
    476     return;
    477   RunCallback(write_callback_, params.result());
    478 }
    479 
    480 void TCPSocketResourceBase::OnPluginMsgListenReply(
    481     const ResourceMessageReplyParams& params) {
    482   if (!state_.IsPending(TCPSocketState::LISTEN))
    483     return;
    484 
    485   DCHECK(TrackedCallback::IsPending(listen_callback_));
    486   state_.CompletePendingTransition(params.result() == PP_OK);
    487 
    488   RunCallback(listen_callback_, params.result());
    489 }
    490 
    491 void TCPSocketResourceBase::OnPluginMsgAcceptReply(
    492     const ResourceMessageReplyParams& params,
    493     int pending_host_id,
    494     const PP_NetAddress_Private& local_addr,
    495     const PP_NetAddress_Private& remote_addr) {
    496   // It is possible that CloseImpl() has been called. We shouldn't access the
    497   // output parameter in that case. The user may have released it.
    498   if (state_.state() != TCPSocketState::LISTENING ||
    499       !TrackedCallback::IsPending(accept_callback_) || !accepted_tcp_socket_) {
    500     return;
    501   }
    502 
    503   if (params.result() == PP_OK) {
    504     *accepted_tcp_socket_ = CreateAcceptedSocket(pending_host_id, local_addr,
    505                                                  remote_addr);
    506   }
    507   accepted_tcp_socket_ = NULL;
    508   RunCallback(accept_callback_, params.result());
    509 }
    510 
    511 void TCPSocketResourceBase::OnPluginMsgSetOptionReply(
    512     const ResourceMessageReplyParams& params) {
    513   if (set_option_callbacks_.empty()) {
    514     NOTREACHED();
    515     return;
    516   }
    517   scoped_refptr<TrackedCallback> callback = set_option_callbacks_.front();
    518   set_option_callbacks_.pop();
    519   if (TrackedCallback::IsPending(callback))
    520     RunCallback(callback, params.result());
    521 }
    522 
    523 void TCPSocketResourceBase::RunCallback(scoped_refptr<TrackedCallback> callback,
    524                                         int32_t pp_result) {
    525   callback->Run(ConvertNetworkAPIErrorForCompatibility(
    526       pp_result, version_ == TCP_SOCKET_VERSION_PRIVATE));
    527 }
    528 
    529 }  // namespace ppapi
    530 }  // namespace proxy
    531