| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |