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