• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TCPSocketResourceBase(Connection connection,PP_Instance instance,TCPSocketVersion version)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 
TCPSocketResourceBase(Connection connection,PP_Instance instance,TCPSocketVersion version,const PP_NetAddress_Private & local_addr,const PP_NetAddress_Private & remote_addr)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 
~TCPSocketResourceBase()66 TCPSocketResourceBase::~TCPSocketResourceBase() {
67   CloseImpl();
68 }
69 
BindImpl(const PP_NetAddress_Private * addr,scoped_refptr<TrackedCallback> callback)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 
ConnectImpl(const char * host,uint16_t port,scoped_refptr<TrackedCallback> callback)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 
ConnectWithNetAddressImpl(const PP_NetAddress_Private * addr,scoped_refptr<TrackedCallback> callback)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 
GetLocalAddressImpl(PP_NetAddress_Private * local_addr)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 
GetRemoteAddressImpl(PP_NetAddress_Private * remote_addr)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 
SSLHandshakeImpl(const char * server_name,uint16_t server_port,scoped_refptr<TrackedCallback> callback)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 
GetServerCertificateImpl()183 PP_Resource TCPSocketResourceBase::GetServerCertificateImpl() {
184   if (!server_certificate_.get())
185     return 0;
186   return server_certificate_->GetReference();
187 }
188 
AddChainBuildingCertificateImpl(PP_Resource certificate,PP_Bool trusted)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 
ReadImpl(char * buffer,int32_t bytes_to_read,scoped_refptr<TrackedCallback> callback)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 
WriteImpl(const char * buffer,int32_t bytes_to_write,scoped_refptr<TrackedCallback> callback)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 
ListenImpl(int32_t backlog,scoped_refptr<TrackedCallback> callback)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 
AcceptImpl(PP_Resource * accepted_tcp_socket,scoped_refptr<TrackedCallback> callback)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 
CloseImpl()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 
SetOptionImpl(PP_TCPSocket_Option name,const PP_Var & value,scoped_refptr<TrackedCallback> callback)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 
PostAbortIfNecessary(scoped_refptr<TrackedCallback> * callback)360 void TCPSocketResourceBase::PostAbortIfNecessary(
361     scoped_refptr<TrackedCallback>* callback) {
362   if (TrackedCallback::IsPending(*callback))
363     (*callback)->PostAbort();
364 }
365 
OnPluginMsgBindReply(const ResourceMessageReplyParams & params,const PP_NetAddress_Private & local_addr)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 
OnPluginMsgConnectReply(const ResourceMessageReplyParams & params,const PP_NetAddress_Private & local_addr,const PP_NetAddress_Private & remote_addr)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 
OnPluginMsgSSLHandshakeReply(const ResourceMessageReplyParams & params,const PPB_X509Certificate_Fields & certificate_fields)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 
OnPluginMsgReadReply(const ResourceMessageReplyParams & params,const std::string & data)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 
OnPluginMsgWriteReply(const ResourceMessageReplyParams & params)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 
OnPluginMsgListenReply(const ResourceMessageReplyParams & params)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 
OnPluginMsgAcceptReply(const ResourceMessageReplyParams & params,int pending_host_id,const PP_NetAddress_Private & local_addr,const PP_NetAddress_Private & remote_addr)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 
OnPluginMsgSetOptionReply(const ResourceMessageReplyParams & params)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 
RunCallback(scoped_refptr<TrackedCallback> callback,int32_t pp_result)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