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/shared_impl/tcp_socket_shared.h" 6 7 #include <string.h> 8 9 #include <algorithm> 10 11 #include "base/basictypes.h" 12 #include "base/bind.h" 13 #include "base/logging.h" 14 #include "ppapi/c/pp_bool.h" 15 #include "ppapi/c/pp_completion_callback.h" 16 #include "ppapi/c/pp_errors.h" 17 #include "ppapi/shared_impl/ppapi_globals.h" 18 #include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h" 19 #include "ppapi/shared_impl/socket_option_data.h" 20 #include "ppapi/shared_impl/var_tracker.h" 21 #include "ppapi/shared_impl/var.h" 22 #include "ppapi/thunk/enter.h" 23 #include "ppapi/thunk/ppb_x509_certificate_private_api.h" 24 25 namespace ppapi { 26 27 const int32_t TCPSocketShared::kMaxReadSize = 1024 * 1024; 28 const int32_t TCPSocketShared::kMaxWriteSize = 1024 * 1024; 29 const int32_t TCPSocketShared::kMaxSendBufferSize = 30 1024 * TCPSocketShared::kMaxWriteSize; 31 const int32_t TCPSocketShared::kMaxReceiveBufferSize = 32 1024 * TCPSocketShared::kMaxReadSize; 33 34 TCPSocketShared::TCPSocketShared(ResourceObjectType resource_type, 35 uint32 socket_id) 36 : resource_type_(resource_type) { 37 Init(socket_id); 38 } 39 40 TCPSocketShared::~TCPSocketShared() { 41 } 42 43 void TCPSocketShared::OnConnectCompleted( 44 int32_t result, 45 const PP_NetAddress_Private& local_addr, 46 const PP_NetAddress_Private& remote_addr) { 47 // It is possible that |connect_callback_| is pending while 48 // |connection_state_| is not BEFORE_CONNECT: DisconnectImpl() has been 49 // called, but a ConnectCompleted notification came earlier than the task to 50 // abort |connect_callback_|. We don't want to update |connection_state_| or 51 // other members in that case. 52 if (connection_state_ != BEFORE_CONNECT || 53 !TrackedCallback::IsPending(connect_callback_)) { 54 return; 55 } 56 57 result = OverridePPError(result); 58 if (result == PP_OK) { 59 local_addr_ = local_addr; 60 remote_addr_ = remote_addr; 61 connection_state_ = CONNECTED; 62 } 63 connect_callback_->Run(result); 64 } 65 66 void TCPSocketShared::OnSSLHandshakeCompleted( 67 bool succeeded, 68 const PPB_X509Certificate_Fields& certificate_fields) { 69 // It is possible that |ssl_handshake_callback_| is pending while 70 // |connection_state_| is not CONNECT: DisconnectImpl() has been 71 // called, but a SSLHandshakeCompleted notification came earlier than the task 72 // to abort |ssl_handshake_callback_|. We don't want to update 73 // |connection_state_| or other members in that case. 74 if (connection_state_ != CONNECTED || 75 !TrackedCallback::IsPending(ssl_handshake_callback_)) { 76 return; 77 } 78 79 if (succeeded) { 80 connection_state_ = SSL_CONNECTED; 81 server_certificate_ = new PPB_X509Certificate_Private_Shared( 82 resource_type_, 83 GetOwnerResource()->pp_instance(), 84 certificate_fields); 85 ssl_handshake_callback_->Run(PP_OK); 86 } else { 87 // The resource might be released in the callback so we need to hold 88 // a reference so we can Disconnect() first. 89 GetOwnerResource()->AddRef(); 90 ssl_handshake_callback_->Run(PP_ERROR_FAILED); 91 DisconnectImpl(); 92 GetOwnerResource()->Release(); 93 } 94 } 95 96 void TCPSocketShared::OnReadCompleted(int32_t result, 97 const std::string& data) { 98 // It is possible that |read_callback_| is pending while |read_buffer_| is 99 // NULL: DisconnectImpl() has been called, but a ReadCompleted notification 100 // came earlier than the task to abort |read_callback_|. We shouldn't access 101 // the buffer in that case. The user may have released it. 102 if (!TrackedCallback::IsPending(read_callback_) || !read_buffer_) 103 return; 104 105 result = OverridePPError(result); 106 bool succeeded = result == PP_OK; 107 if (succeeded) { 108 CHECK_LE(static_cast<int32_t>(data.size()), bytes_to_read_); 109 if (!data.empty()) 110 memcpy(read_buffer_, data.c_str(), data.size()); 111 } 112 read_buffer_ = NULL; 113 bytes_to_read_ = -1; 114 115 read_callback_->Run( 116 succeeded ? static_cast<int32_t>(data.size()) : result); 117 } 118 119 void TCPSocketShared::OnWriteCompleted(int32_t result) { 120 if (!TrackedCallback::IsPending(write_callback_)) 121 return; 122 123 result = OverridePPError(result); 124 write_callback_->Run(result); 125 } 126 127 void TCPSocketShared::OnSetOptionCompleted(int32_t result) { 128 if (set_option_callbacks_.empty()) { 129 NOTREACHED(); 130 return; 131 } 132 133 result = OverridePPError(result); 134 scoped_refptr<TrackedCallback> callback = set_option_callbacks_.front(); 135 set_option_callbacks_.pop(); 136 137 if (TrackedCallback::IsPending(callback)) 138 callback->Run(result); 139 } 140 141 int32_t TCPSocketShared::OverridePPError(int32_t pp_error) { 142 return pp_error; 143 } 144 145 int32_t TCPSocketShared::ConnectImpl(const char* host, 146 uint16_t port, 147 scoped_refptr<TrackedCallback> callback) { 148 if (!host) 149 return PP_ERROR_BADARGUMENT; 150 if (connection_state_ != BEFORE_CONNECT) 151 return PP_ERROR_FAILED; 152 if (TrackedCallback::IsPending(connect_callback_)) 153 return PP_ERROR_INPROGRESS; // Can only have one pending request. 154 155 connect_callback_ = callback; 156 // Send the request, the browser will call us back via ConnectACK. 157 SendConnect(host, port); 158 return PP_OK_COMPLETIONPENDING; 159 } 160 161 int32_t TCPSocketShared::ConnectWithNetAddressImpl( 162 const PP_NetAddress_Private* addr, 163 scoped_refptr<TrackedCallback> callback) { 164 if (!addr) 165 return PP_ERROR_BADARGUMENT; 166 if (connection_state_ != BEFORE_CONNECT) 167 return PP_ERROR_FAILED; 168 if (TrackedCallback::IsPending(connect_callback_)) 169 return PP_ERROR_INPROGRESS; // Can only have one pending request. 170 171 connect_callback_ = callback; 172 // Send the request, the browser will call us back via ConnectACK. 173 SendConnectWithNetAddress(*addr); 174 return PP_OK_COMPLETIONPENDING; 175 } 176 177 PP_Bool TCPSocketShared::GetLocalAddressImpl( 178 PP_NetAddress_Private* local_addr) { 179 if (!IsConnected() || !local_addr) 180 return PP_FALSE; 181 182 *local_addr = local_addr_; 183 return PP_TRUE; 184 } 185 186 PP_Bool TCPSocketShared::GetRemoteAddressImpl( 187 PP_NetAddress_Private* remote_addr) { 188 if (!IsConnected() || !remote_addr) 189 return PP_FALSE; 190 191 *remote_addr = remote_addr_; 192 return PP_TRUE; 193 } 194 195 int32_t TCPSocketShared::SSLHandshakeImpl( 196 const char* server_name, 197 uint16_t server_port, 198 scoped_refptr<TrackedCallback> callback) { 199 if (!server_name) 200 return PP_ERROR_BADARGUMENT; 201 202 if (connection_state_ != CONNECTED) 203 return PP_ERROR_FAILED; 204 if (TrackedCallback::IsPending(ssl_handshake_callback_) || 205 TrackedCallback::IsPending(read_callback_) || 206 TrackedCallback::IsPending(write_callback_)) 207 return PP_ERROR_INPROGRESS; 208 209 ssl_handshake_callback_ = callback; 210 211 // Send the request, the browser will call us back via SSLHandshakeACK. 212 SendSSLHandshake(server_name, server_port, trusted_certificates_, 213 untrusted_certificates_); 214 return PP_OK_COMPLETIONPENDING; 215 } 216 217 PP_Resource TCPSocketShared::GetServerCertificateImpl() { 218 if (!server_certificate_.get()) 219 return 0; 220 return server_certificate_->GetReference(); 221 } 222 223 PP_Bool TCPSocketShared::AddChainBuildingCertificateImpl( 224 PP_Resource certificate, 225 PP_Bool trusted) { 226 // TODO(raymes): The plumbing for this functionality is implemented but the 227 // certificates aren't yet used for the connection, so just return false for 228 // now. 229 return PP_FALSE; 230 231 thunk::EnterResourceNoLock<thunk::PPB_X509Certificate_Private_API> 232 enter_cert(certificate, true); 233 if (enter_cert.failed()) 234 return PP_FALSE; 235 236 PP_Var der_var = enter_cert.object()->GetField( 237 PP_X509CERTIFICATE_PRIVATE_RAW); 238 ArrayBufferVar* der_array_buffer = ArrayBufferVar::FromPPVar(der_var); 239 PP_Bool success = PP_FALSE; 240 if (der_array_buffer) { 241 const char* der_bytes = static_cast<const char*>(der_array_buffer->Map()); 242 uint32_t der_length = der_array_buffer->ByteLength(); 243 std::vector<char> der(der_bytes, der_bytes + der_length); 244 if (PP_ToBool(trusted)) 245 trusted_certificates_.push_back(der); 246 else 247 untrusted_certificates_.push_back(der); 248 success = PP_TRUE; 249 } 250 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(der_var); 251 return success; 252 } 253 254 int32_t TCPSocketShared::ReadImpl(char* buffer, 255 int32_t bytes_to_read, 256 scoped_refptr<TrackedCallback> callback) { 257 if (!buffer || bytes_to_read <= 0) 258 return PP_ERROR_BADARGUMENT; 259 260 if (!IsConnected()) 261 return PP_ERROR_FAILED; 262 if (TrackedCallback::IsPending(read_callback_) || 263 TrackedCallback::IsPending(ssl_handshake_callback_)) 264 return PP_ERROR_INPROGRESS; 265 read_buffer_ = buffer; 266 bytes_to_read_ = std::min(bytes_to_read, kMaxReadSize); 267 read_callback_ = callback; 268 269 // Send the request, the browser will call us back via ReadACK. 270 SendRead(bytes_to_read_); 271 return PP_OK_COMPLETIONPENDING; 272 } 273 274 int32_t TCPSocketShared::WriteImpl(const char* buffer, 275 int32_t bytes_to_write, 276 scoped_refptr<TrackedCallback> callback) { 277 if (!buffer || bytes_to_write <= 0) 278 return PP_ERROR_BADARGUMENT; 279 280 if (!IsConnected()) 281 return PP_ERROR_FAILED; 282 if (TrackedCallback::IsPending(write_callback_) || 283 TrackedCallback::IsPending(ssl_handshake_callback_)) 284 return PP_ERROR_INPROGRESS; 285 286 if (bytes_to_write > kMaxWriteSize) 287 bytes_to_write = kMaxWriteSize; 288 289 write_callback_ = callback; 290 291 // Send the request, the browser will call us back via WriteACK. 292 SendWrite(std::string(buffer, bytes_to_write)); 293 return PP_OK_COMPLETIONPENDING; 294 } 295 296 void TCPSocketShared::DisconnectImpl() { 297 if (connection_state_ == DISCONNECTED) 298 return; 299 300 connection_state_ = DISCONNECTED; 301 302 SendDisconnect(); 303 socket_id_ = 0; 304 305 PostAbortIfNecessary(&connect_callback_); 306 PostAbortIfNecessary(&ssl_handshake_callback_); 307 PostAbortIfNecessary(&read_callback_); 308 PostAbortIfNecessary(&write_callback_); 309 read_buffer_ = NULL; 310 bytes_to_read_ = -1; 311 server_certificate_ = NULL; 312 } 313 314 int32_t TCPSocketShared::SetOptionImpl( 315 PP_TCPSocket_Option name, 316 const PP_Var& value, 317 scoped_refptr<TrackedCallback> callback) { 318 if (!IsConnected()) 319 return PP_ERROR_FAILED; 320 321 SocketOptionData option_data; 322 switch (name) { 323 case PP_TCPSOCKET_OPTION_NO_DELAY: { 324 if (value.type != PP_VARTYPE_BOOL) 325 return PP_ERROR_BADARGUMENT; 326 option_data.SetBool(PP_ToBool(value.value.as_bool)); 327 break; 328 } 329 case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE: 330 case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: { 331 if (value.type != PP_VARTYPE_INT32) 332 return PP_ERROR_BADARGUMENT; 333 option_data.SetInt32(value.value.as_int); 334 break; 335 } 336 default: { 337 NOTREACHED(); 338 return PP_ERROR_BADARGUMENT; 339 } 340 } 341 342 set_option_callbacks_.push(callback); 343 SendSetOption(name, option_data); 344 return PP_OK_COMPLETIONPENDING; 345 } 346 347 void TCPSocketShared::Init(uint32 socket_id) { 348 DCHECK(socket_id != 0); 349 socket_id_ = socket_id; 350 connection_state_ = BEFORE_CONNECT; 351 read_buffer_ = NULL; 352 bytes_to_read_ = -1; 353 354 local_addr_.size = 0; 355 memset(local_addr_.data, 0, 356 arraysize(local_addr_.data) * sizeof(*local_addr_.data)); 357 remote_addr_.size = 0; 358 memset(remote_addr_.data, 0, 359 arraysize(remote_addr_.data) * sizeof(*remote_addr_.data)); 360 } 361 362 bool TCPSocketShared::IsConnected() const { 363 return connection_state_ == CONNECTED || connection_state_ == SSL_CONNECTED; 364 } 365 366 void TCPSocketShared::PostAbortIfNecessary( 367 scoped_refptr<TrackedCallback>* callback) { 368 if (TrackedCallback::IsPending(*callback)) 369 (*callback)->PostAbort(); 370 } 371 372 } // namespace ppapi 373