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