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
23namespace ppapi {
24namespace proxy {
25
26const int32_t TCPSocketResourceBase::kMaxReadSize = 1024 * 1024;
27const int32_t TCPSocketResourceBase::kMaxWriteSize = 1024 * 1024;
28const int32_t TCPSocketResourceBase::kMaxSendBufferSize =
29    1024 * TCPSocketResourceBase::kMaxWriteSize;
30const int32_t TCPSocketResourceBase::kMaxReceiveBufferSize =
31    1024 * TCPSocketResourceBase::kMaxReadSize;
32
33TCPSocketResourceBase::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
50TCPSocketResourceBase::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
66TCPSocketResourceBase::~TCPSocketResourceBase() {
67  CloseImpl();
68}
69
70int32_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
92int32_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
115int32_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
137PP_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
145PP_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
153int32_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
183PP_Resource TCPSocketResourceBase::GetServerCertificateImpl() {
184  if (!server_certificate_.get())
185    return 0;
186  return server_certificate_->GetReference();
187}
188
189PP_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
201int32_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
226int32_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
253int32_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
275int32_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
297void 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
318int32_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
360void TCPSocketResourceBase::PostAbortIfNecessary(
361    scoped_refptr<TrackedCallback>* callback) {
362  if (TrackedCallback::IsPending(*callback))
363    (*callback)->PostAbort();
364}
365
366void 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
384void 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
410void 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
431void 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
454void TCPSocketResourceBase::OnPluginMsgWriteReply(
455    const ResourceMessageReplyParams& params) {
456  if (!state_.IsConnected() || !TrackedCallback::IsPending(write_callback_))
457    return;
458  RunCallback(write_callback_, params.result());
459}
460
461void 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
472void 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
492void 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
504void 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