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.
5 #include "ppapi/proxy/tcp_socket_resource_base.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"
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
;
33 TCPSocketResourceBase::TCPSocketResourceBase(Connection connection
,
35 TCPSocketVersion version
)
36 : PluginResource(connection
, instance
),
37 state_(TCPSocketState::INITIAL
),
40 accepted_tcp_socket_(NULL
),
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
));
50 TCPSocketResourceBase::TCPSocketResourceBase(
51 Connection connection
,
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
),
60 local_addr_(local_addr
),
61 remote_addr_(remote_addr
),
62 accepted_tcp_socket_(NULL
),
66 TCPSocketResourceBase::~TCPSocketResourceBase() {
70 int32_t TCPSocketResourceBase::BindImpl(
71 const PP_NetAddress_Private
* addr
,
72 scoped_refptr
<TrackedCallback
> callback
) {
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
;
80 bind_callback_
= callback
;
81 state_
.SetPendingTransition(TCPSocketState::BIND
);
83 Call
<PpapiPluginMsg_TCPSocket_BindReply
>(
85 PpapiHostMsg_TCPSocket_Bind(*addr
),
86 base::Bind(&TCPSocketResourceBase::OnPluginMsgBindReply
,
87 base::Unretained(this)),
89 return PP_OK_COMPLETIONPENDING
;
92 int32_t TCPSocketResourceBase::ConnectImpl(
95 scoped_refptr
<TrackedCallback
> callback
) {
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
;
103 connect_callback_
= callback
;
104 state_
.SetPendingTransition(TCPSocketState::CONNECT
);
106 Call
<PpapiPluginMsg_TCPSocket_ConnectReply
>(
108 PpapiHostMsg_TCPSocket_Connect(host
, port
),
109 base::Bind(&TCPSocketResourceBase::OnPluginMsgConnectReply
,
110 base::Unretained(this)),
112 return PP_OK_COMPLETIONPENDING
;
115 int32_t TCPSocketResourceBase::ConnectWithNetAddressImpl(
116 const PP_NetAddress_Private
* addr
,
117 scoped_refptr
<TrackedCallback
> callback
) {
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
;
125 connect_callback_
= callback
;
126 state_
.SetPendingTransition(TCPSocketState::CONNECT
);
128 Call
<PpapiPluginMsg_TCPSocket_ConnectReply
>(
130 PpapiHostMsg_TCPSocket_ConnectWithNetAddress(*addr
),
131 base::Bind(&TCPSocketResourceBase::OnPluginMsgConnectReply
,
132 base::Unretained(this)),
134 return PP_OK_COMPLETIONPENDING
;
137 PP_Bool
TCPSocketResourceBase::GetLocalAddressImpl(
138 PP_NetAddress_Private
* local_addr
) {
139 if (!state_
.IsBound() || !local_addr
)
141 *local_addr
= local_addr_
;
145 PP_Bool
TCPSocketResourceBase::GetRemoteAddressImpl(
146 PP_NetAddress_Private
* remote_addr
) {
147 if (!state_
.IsConnected() || !remote_addr
)
149 *remote_addr
= remote_addr_
;
153 int32_t TCPSocketResourceBase::SSLHandshakeImpl(
154 const char* server_name
,
155 uint16_t server_port
,
156 scoped_refptr
<TrackedCallback
> callback
) {
158 return PP_ERROR_BADARGUMENT
;
160 if (state_
.IsPending(TCPSocketState::SSL_CONNECT
) ||
161 TrackedCallback::IsPending(read_callback_
) ||
162 TrackedCallback::IsPending(write_callback_
)) {
163 return PP_ERROR_INPROGRESS
;
165 if (!state_
.IsValidTransition(TCPSocketState::SSL_CONNECT
))
166 return PP_ERROR_FAILED
;
168 ssl_handshake_callback_
= callback
;
169 state_
.SetPendingTransition(TCPSocketState::SSL_CONNECT
);
171 Call
<PpapiPluginMsg_TCPSocket_SSLHandshakeReply
>(
173 PpapiHostMsg_TCPSocket_SSLHandshake(server_name
,
175 trusted_certificates_
,
176 untrusted_certificates_
),
177 base::Bind(&TCPSocketResourceBase::OnPluginMsgSSLHandshakeReply
,
178 base::Unretained(this)),
180 return PP_OK_COMPLETIONPENDING
;
183 PP_Resource
TCPSocketResourceBase::GetServerCertificateImpl() {
184 if (!server_certificate_
.get())
186 return server_certificate_
->GetReference();
189 PP_Bool
TCPSocketResourceBase::AddChainBuildingCertificateImpl(
190 PP_Resource certificate
,
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
197 thunk::EnterResourceNoLock
<thunk::PPB_X509Certificate_Private_API
>
198 enter_cert(certificate
, true);
199 if (enter_cert
.failed())
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
);
213 untrusted_certificates_
.push_back(der
);
216 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(der_var
);
220 int32_t TCPSocketResourceBase::ReadImpl(
222 int32_t bytes_to_read
,
223 scoped_refptr
<TrackedCallback
> callback
) {
224 if (!buffer
|| bytes_to_read
<= 0)
225 return PP_ERROR_BADARGUMENT
;
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
;
236 Call
<PpapiPluginMsg_TCPSocket_ReadReply
>(
238 PpapiHostMsg_TCPSocket_Read(bytes_to_read_
),
239 base::Bind(&TCPSocketResourceBase::OnPluginMsgReadReply
,
240 base::Unretained(this)),
242 return PP_OK_COMPLETIONPENDING
;
245 int32_t TCPSocketResourceBase::WriteImpl(
247 int32_t bytes_to_write
,
248 scoped_refptr
<TrackedCallback
> callback
) {
249 if (!buffer
|| bytes_to_write
<= 0)
250 return PP_ERROR_BADARGUMENT
;
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
;
258 if (bytes_to_write
> kMaxWriteSize
)
259 bytes_to_write
= kMaxWriteSize
;
261 write_callback_
= callback
;
263 Call
<PpapiPluginMsg_TCPSocket_WriteReply
>(
265 PpapiHostMsg_TCPSocket_Write(std::string(buffer
, bytes_to_write
)),
266 base::Bind(&TCPSocketResourceBase::OnPluginMsgWriteReply
,
267 base::Unretained(this)),
269 return PP_OK_COMPLETIONPENDING
;
272 int32_t TCPSocketResourceBase::ListenImpl(
274 scoped_refptr
<TrackedCallback
> callback
) {
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
;
282 listen_callback_
= callback
;
283 state_
.SetPendingTransition(TCPSocketState::LISTEN
);
285 Call
<PpapiPluginMsg_TCPSocket_ListenReply
>(
287 PpapiHostMsg_TCPSocket_Listen(backlog
),
288 base::Bind(&TCPSocketResourceBase::OnPluginMsgListenReply
,
289 base::Unretained(this)),
291 return PP_OK_COMPLETIONPENDING
;
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
;
304 accept_callback_
= callback
;
305 accepted_tcp_socket_
= accepted_tcp_socket
;
307 Call
<PpapiPluginMsg_TCPSocket_AcceptReply
>(
309 PpapiHostMsg_TCPSocket_Accept(),
310 base::Bind(&TCPSocketResourceBase::OnPluginMsgAcceptReply
,
311 base::Unretained(this)),
313 return PP_OK_COMPLETIONPENDING
;
316 void TCPSocketResourceBase::CloseImpl() {
317 if (state_
.state() == TCPSocketState::CLOSED
)
320 state_
.DoTransition(TCPSocketState::CLOSE
, true);
322 Post(BROWSER
, PpapiHostMsg_TCPSocket_Close());
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_
);
333 server_certificate_
= NULL
;
334 accepted_tcp_socket_
= NULL
;
337 int32_t TCPSocketResourceBase::SetOptionImpl(
338 PP_TCPSocket_Option name
,
340 scoped_refptr
<TrackedCallback
> callback
) {
341 SocketOptionData option_data
;
343 case PP_TCPSOCKET_OPTION_NO_DELAY
: {
344 if (!state_
.IsConnected())
345 return PP_ERROR_FAILED
;
347 if (value
.type
!= PP_VARTYPE_BOOL
)
348 return PP_ERROR_BADARGUMENT
;
349 option_data
.SetBool(PP_ToBool(value
.value
.as_bool
));
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
;
357 if (value
.type
!= PP_VARTYPE_INT32
)
358 return PP_ERROR_BADARGUMENT
;
359 option_data
.SetInt32(value
.value
.as_int
);
364 return PP_ERROR_BADARGUMENT
;
368 set_option_callbacks_
.push(callback
);
370 Call
<PpapiPluginMsg_TCPSocket_SetOptionReply
>(
372 PpapiHostMsg_TCPSocket_SetOption(name
, option_data
),
373 base::Bind(&TCPSocketResourceBase::OnPluginMsgSetOptionReply
,
374 base::Unretained(this)),
376 return PP_OK_COMPLETIONPENDING
;
379 void TCPSocketResourceBase::PostAbortIfNecessary(
380 scoped_refptr
<TrackedCallback
>* callback
) {
381 if (TrackedCallback::IsPending(*callback
))
382 (*callback
)->PostAbort();
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
))
393 DCHECK(TrackedCallback::IsPending(bind_callback_
));
394 if (params
.result() == PP_OK
) {
395 local_addr_
= local_addr
;
396 state_
.CompletePendingTransition(true);
398 state_
.CompletePendingTransition(false);
400 RunCallback(bind_callback_
, params
.result());
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
))
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);
418 if (version_
== TCP_SOCKET_VERSION_1_1_OR_ABOVE
) {
419 state_
.CompletePendingTransition(false);
421 // In order to maintain backward compatibility, allow to connect the
423 state_
= TCPSocketState(TCPSocketState::INITIAL
);
426 RunCallback(connect_callback_
, params
.result());
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
))
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(
445 state_
.CompletePendingTransition(false);
447 RunCallback(ssl_handshake_callback_
, params
.result());
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_
) ||
460 const bool succeeded
= params
.result() == PP_OK
;
462 CHECK_LE(static_cast<int32_t>(data
.size()), bytes_to_read_
);
464 memmove(read_buffer_
, data
.c_str(), data
.size());
469 RunCallback(read_callback_
,
470 succeeded
? static_cast<int32_t>(data
.size()) : params
.result());
473 void TCPSocketResourceBase::OnPluginMsgWriteReply(
474 const ResourceMessageReplyParams
& params
) {
475 if (!state_
.IsConnected() || !TrackedCallback::IsPending(write_callback_
))
477 RunCallback(write_callback_
, params
.result());
480 void TCPSocketResourceBase::OnPluginMsgListenReply(
481 const ResourceMessageReplyParams
& params
) {
482 if (!state_
.IsPending(TCPSocketState::LISTEN
))
485 DCHECK(TrackedCallback::IsPending(listen_callback_
));
486 state_
.CompletePendingTransition(params
.result() == PP_OK
);
488 RunCallback(listen_callback_
, params
.result());
491 void TCPSocketResourceBase::OnPluginMsgAcceptReply(
492 const ResourceMessageReplyParams
& params
,
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_
) {
503 if (params
.result() == PP_OK
) {
504 *accepted_tcp_socket_
= CreateAcceptedSocket(pending_host_id
, local_addr
,
507 accepted_tcp_socket_
= NULL
;
508 RunCallback(accept_callback_
, params
.result());
511 void TCPSocketResourceBase::OnPluginMsgSetOptionReply(
512 const ResourceMessageReplyParams
& params
) {
513 if (set_option_callbacks_
.empty()) {
517 scoped_refptr
<TrackedCallback
> callback
= set_option_callbacks_
.front();
518 set_option_callbacks_
.pop();
519 if (TrackedCallback::IsPending(callback
))
520 RunCallback(callback
, params
.result());
523 void TCPSocketResourceBase::RunCallback(scoped_refptr
<TrackedCallback
> callback
,
525 callback
->Run(ConvertNetworkAPIErrorForCompatibility(
526 pp_result
, version_
== TCP_SOCKET_VERSION_PRIVATE
));