[email protected] | 0b45559b | 2009-06-12 21:45:11 | [diff] [blame] | 1 | // Copyright (c) 2008-2009 The Chromium Authors. All rights reserved. |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | f7984fc6 | 2009-06-22 23:26:44 | [diff] [blame] | 5 | #include "net/socket/ssl_client_socket_mac.h" |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 6 | |
| 7 | #include "base/singleton.h" |
| 8 | #include "base/string_util.h" |
[email protected] | 597cf6e | 2009-05-29 09:43:26 | [diff] [blame] | 9 | #include "net/base/io_buffer.h" |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 10 | #include "net/base/net_errors.h" |
| 11 | #include "net/base/ssl_info.h" |
| 12 | |
| 13 | // Welcome to Mac SSL. We've been waiting for you. |
| 14 | // |
| 15 | // The Mac SSL implementation is, like the Windows and NSS implementations, a |
| 16 | // giant state machine. This design constraint is due to the asynchronous nature |
| 17 | // of our underlying transport mechanism. We can call down to read/write on the |
| 18 | // network, but what happens is that either it completes immediately or returns |
| 19 | // saying that we'll get a callback sometime in the future. In that case, we |
| 20 | // have to return to our caller but pick up where we left off when we |
| 21 | // resume. Thus the fun. |
| 22 | // |
| 23 | // On Windows, we use Security Contexts, which are driven by us. We fetch data |
| 24 | // from the network, we call the context to decrypt the data, and so on. On the |
| 25 | // Mac, however, we provide Secure Transport with callbacks to get data from the |
| 26 | // network, and it calls us back to fetch the data from the network for |
| 27 | // it. Therefore, there are different sets of states in our respective state |
| 28 | // machines, fewer on the Mac because Secure Transport keeps a lot of its own |
| 29 | // state. The discussion about what each of the states means lives in comments |
| 30 | // in the DoLoop() function. |
| 31 | // |
| 32 | // Secure Transport is designed for use by either blocking or non-blocking |
| 33 | // network I/O. If, for example, you called SSLRead() to fetch data, Secure |
| 34 | // Transport will, unless it has some cached data, issue a read to your network |
| 35 | // callback read function to fetch it some more encrypted data. It's expecting |
| 36 | // one of two things. If your function is hooked up to a blocking source, then |
| 37 | // it'll block pending receipt of the data from the other end. That's fine, as |
| 38 | // when you return with the data, Secure Transport will do its thing. On the |
| 39 | // other hand, suppose that your socket is non-blocking and tells your function |
| 40 | // that it would block. Then you let Secure Transport know, and it'll tell the |
| 41 | // original caller that it would have blocked and that they need to call it |
| 42 | // "later." |
| 43 | // |
| 44 | // When's "later," though? We have fully-asynchronous networking, so we get a |
| 45 | // callback when our data's ready. But Secure Transport has no way for us to |
| 46 | // tell it that data has arrived, so we must re-execute the call that triggered |
| 47 | // the I/O (we rely on our state machine to do this). When we do so Secure |
| 48 | // Transport will ask once again for the data. Chances are that it'll be the |
| 49 | // same request as the previous time, but that's not actually guaranteed. But as |
| 50 | // long as we buffer what we have and keep track of where we were, it works |
| 51 | // quite well. |
| 52 | // |
| 53 | // Except for network writes. They shoot this plan straight to hell. |
| 54 | // |
| 55 | // Faking a blocking connection with an asynchronous connection (theoretically |
| 56 | // more powerful) simply doesn't work for writing. Suppose that Secure Transport |
| 57 | // requests a write of data to the network. With blocking I/O, we'd just block |
| 58 | // until the write completed, and with non-blocking I/O we'd know how many bytes |
| 59 | // we wrote before we would have blocked. But with the asynchronous I/O, the |
| 60 | // transport underneath us can tell us that it'll let us know sometime "later" |
| 61 | // whether or not things succeeded, and how many bytes were written. What do we |
| 62 | // return to Secure Transport? We can't return a byte count, but we can't return |
| 63 | // "later" as we're not guaranteed to be called in the future with the same data |
| 64 | // to write. |
| 65 | // |
| 66 | // So, like in any good relationship, we're forced to lie. Whenever Secure |
| 67 | // Transport asks for data to be written, we take it all and lie about it always |
| 68 | // being written. We spin in a loop (see SSLWriteCallback() and |
| 69 | // OnWriteComplete()) independent of the main state machine writing the data to |
| 70 | // the network, and get the data out. The main consequence of this independence |
| 71 | // from the state machine is that we require a full-duplex transport underneath |
| 72 | // us since we can't use it to keep our reading and writing |
| 73 | // straight. Fortunately, the NSS implementation also has this issue to deal |
| 74 | // with, so we share the same Libevent-based full-duplex TCP socket. |
| 75 | // |
| 76 | // A side comment on return values might be in order. Those who haven't taken |
| 77 | // the time to read the documentation (ahem, header comments) in our various |
| 78 | // files might be a bit surprised to see result values being treated as both |
| 79 | // lengths and errors. Like Shimmer, they are both. In both the case of |
| 80 | // immediate results as well as results returned in callbacks, a negative return |
| 81 | // value indicates an error, a zero return value indicates end-of-stream (for |
| 82 | // reads), and a positive return value indicates the number of bytes read or |
| 83 | // written. Thus, many functions start off with |if (result < 0) return |
| 84 | // result;|. That gets the error condition out of the way, and from that point |
| 85 | // forward the result can be treated as a length. |
| 86 | |
| 87 | namespace net { |
| 88 | |
| 89 | namespace { |
| 90 | |
| 91 | int NetErrorFromOSStatus(OSStatus status) { |
| 92 | switch (status) { |
| 93 | case errSSLWouldBlock: |
| 94 | return ERR_IO_PENDING; |
| 95 | case errSSLIllegalParam: |
| 96 | case errSSLBadCipherSuite: |
| 97 | case errSSLBadConfiguration: |
| 98 | return ERR_INVALID_ARGUMENT; |
| 99 | case errSSLClosedNoNotify: |
| 100 | return ERR_CONNECTION_RESET; |
| 101 | case errSSLConnectionRefused: |
| 102 | return ERR_CONNECTION_REFUSED; |
| 103 | case errSSLClosedAbort: |
| 104 | return ERR_CONNECTION_ABORTED; |
| 105 | case errSSLInternal: |
| 106 | case errSSLCrypto: |
| 107 | case errSSLFatalAlert: |
| 108 | case errSSLProtocol: |
| 109 | return ERR_SSL_PROTOCOL_ERROR; |
| 110 | case errSSLHostNameMismatch: |
| 111 | return ERR_CERT_COMMON_NAME_INVALID; |
| 112 | case errSSLCertExpired: |
| 113 | case errSSLCertNotYetValid: |
| 114 | return ERR_CERT_DATE_INVALID; |
| 115 | case errSSLNoRootCert: |
| 116 | case errSSLUnknownRootCert: |
| 117 | return ERR_CERT_AUTHORITY_INVALID; |
| 118 | case errSSLXCertChainInvalid: |
| 119 | case errSSLBadCert: |
| 120 | return ERR_CERT_INVALID; |
| 121 | case errSSLPeerCertRevoked: |
| 122 | return ERR_CERT_REVOKED; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 123 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 124 | case errSSLClosedGraceful: |
| 125 | case noErr: |
| 126 | return OK; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 127 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 128 | case errSSLBadRecordMac: |
| 129 | case errSSLBufferOverflow: |
| 130 | case errSSLDecryptionFail: |
| 131 | case errSSLModuleAttach: |
| 132 | case errSSLNegotiation: |
| 133 | case errSSLRecordOverflow: |
| 134 | case errSSLSessionNotFound: |
| 135 | default: |
| 136 | LOG(WARNING) << "Unknown error " << status << |
| 137 | " mapped to net::ERR_FAILED"; |
| 138 | return ERR_FAILED; |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | OSStatus OSStatusFromNetError(int net_error) { |
| 143 | switch (net_error) { |
| 144 | case ERR_IO_PENDING: |
| 145 | return errSSLWouldBlock; |
| 146 | case ERR_INTERNET_DISCONNECTED: |
| 147 | case ERR_TIMED_OUT: |
| 148 | case ERR_CONNECTION_ABORTED: |
| 149 | case ERR_CONNECTION_RESET: |
| 150 | case ERR_CONNECTION_REFUSED: |
| 151 | case ERR_ADDRESS_UNREACHABLE: |
| 152 | case ERR_ADDRESS_INVALID: |
| 153 | return errSSLClosedAbort; |
| 154 | case OK: |
| 155 | return noErr; |
| 156 | default: |
| 157 | LOG(WARNING) << "Unknown error " << net_error << |
| 158 | " mapped to errSSLIllegalParam"; |
| 159 | return errSSLIllegalParam; |
| 160 | } |
| 161 | } |
| 162 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 163 | // Converts from a cipher suite to its key size. If the suite is marked with a |
| 164 | // **, it's not actually implemented in Secure Transport and won't be returned |
| 165 | // (but we'll code for it anyway). The reference here is |
| 166 | // http://www.opensource.apple.com/darwinsource/10.5.5/libsecurity_ssl-32463/lib/cipherSpecs.c |
| 167 | // Seriously, though, there has to be an API for this, but I can't find one. |
| 168 | // Anybody? |
| 169 | int KeySizeOfCipherSuite(SSLCipherSuite suite) { |
| 170 | switch (suite) { |
| 171 | // SSL 2 only |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 172 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 173 | case SSL_RSA_WITH_DES_CBC_MD5: |
| 174 | return 56; |
| 175 | case SSL_RSA_WITH_3DES_EDE_CBC_MD5: |
| 176 | return 112; |
| 177 | case SSL_RSA_WITH_RC2_CBC_MD5: |
| 178 | case SSL_RSA_WITH_IDEA_CBC_MD5: // ** |
| 179 | return 128; |
| 180 | case SSL_NO_SUCH_CIPHERSUITE: // ** |
| 181 | return 0; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 182 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 183 | // SSL 2, 3, TLS |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 184 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 185 | case SSL_NULL_WITH_NULL_NULL: |
| 186 | case SSL_RSA_WITH_NULL_MD5: |
| 187 | case SSL_RSA_WITH_NULL_SHA: // ** |
| 188 | case SSL_FORTEZZA_DMS_WITH_NULL_SHA: // ** |
| 189 | return 0; |
| 190 | case SSL_RSA_EXPORT_WITH_RC4_40_MD5: |
| 191 | case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5: |
| 192 | case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: |
| 193 | case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: // ** |
| 194 | case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: // ** |
| 195 | case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: |
| 196 | case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: |
| 197 | case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5: |
| 198 | case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA: |
| 199 | return 40; |
| 200 | case SSL_RSA_WITH_DES_CBC_SHA: |
| 201 | case SSL_DH_DSS_WITH_DES_CBC_SHA: // ** |
| 202 | case SSL_DH_RSA_WITH_DES_CBC_SHA: // ** |
| 203 | case SSL_DHE_DSS_WITH_DES_CBC_SHA: |
| 204 | case SSL_DHE_RSA_WITH_DES_CBC_SHA: |
| 205 | case SSL_DH_anon_WITH_DES_CBC_SHA: |
| 206 | return 56; |
| 207 | case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA: // ** |
| 208 | return 80; |
| 209 | case SSL_RSA_WITH_3DES_EDE_CBC_SHA: |
| 210 | case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA: // ** |
| 211 | case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA: // ** |
| 212 | case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA: |
| 213 | case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA: |
| 214 | case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: |
| 215 | return 112; |
| 216 | case SSL_RSA_WITH_RC4_128_MD5: |
| 217 | case SSL_RSA_WITH_RC4_128_SHA: |
| 218 | case SSL_RSA_WITH_IDEA_CBC_SHA: // ** |
| 219 | case SSL_DH_anon_WITH_RC4_128_MD5: |
| 220 | return 128; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 221 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 222 | // TLS AES options (see RFC 3268) |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 223 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 224 | case TLS_RSA_WITH_AES_128_CBC_SHA: |
| 225 | case TLS_DH_DSS_WITH_AES_128_CBC_SHA: // ** |
| 226 | case TLS_DH_RSA_WITH_AES_128_CBC_SHA: // ** |
| 227 | case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: |
| 228 | case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: |
| 229 | case TLS_DH_anon_WITH_AES_128_CBC_SHA: |
| 230 | return 128; |
| 231 | case TLS_RSA_WITH_AES_256_CBC_SHA: |
| 232 | case TLS_DH_DSS_WITH_AES_256_CBC_SHA: // ** |
| 233 | case TLS_DH_RSA_WITH_AES_256_CBC_SHA: // ** |
| 234 | case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: |
| 235 | case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: |
| 236 | case TLS_DH_anon_WITH_AES_256_CBC_SHA: |
| 237 | return 256; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 238 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 239 | default: |
| 240 | return -1; |
| 241 | } |
| 242 | } |
| 243 | |
[email protected] | 12701787 | 2009-08-13 17:54:42 | [diff] [blame^] | 244 | // Returns the server's certificate. The caller must release a reference |
| 245 | // to the return value when done. Returns NULL on failure. |
| 246 | X509Certificate* GetServerCert(SSLContextRef ssl_context) { |
| 247 | CFArrayRef certs; |
| 248 | OSStatus status = SSLCopyPeerCertificates(ssl_context, &certs); |
| 249 | if (status != noErr) |
| 250 | return NULL; |
| 251 | |
| 252 | DCHECK_GT(CFArrayGetCount(certs), 0); |
| 253 | |
| 254 | SecCertificateRef server_cert = static_cast<SecCertificateRef>( |
| 255 | const_cast<void*>(CFArrayGetValueAtIndex(certs, 0))); |
| 256 | CFRetain(server_cert); |
| 257 | CFRelease(certs); |
| 258 | return X509Certificate::CreateFromHandle( |
| 259 | server_cert, X509Certificate::SOURCE_FROM_NETWORK); |
| 260 | } |
| 261 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 262 | } // namespace |
| 263 | |
| 264 | //----------------------------------------------------------------------------- |
| 265 | |
| 266 | SSLClientSocketMac::SSLClientSocketMac(ClientSocket* transport_socket, |
| 267 | const std::string& hostname, |
| 268 | const SSLConfig& ssl_config) |
| 269 | : io_callback_(this, &SSLClientSocketMac::OnIOComplete), |
| 270 | write_callback_(this, &SSLClientSocketMac::OnWriteComplete), |
| 271 | transport_(transport_socket), |
| 272 | hostname_(hostname), |
| 273 | ssl_config_(ssl_config), |
| 274 | user_callback_(NULL), |
| 275 | next_state_(STATE_NONE), |
| 276 | next_io_state_(STATE_NONE), |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 277 | server_cert_status_(0), |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 278 | completed_handshake_(false), |
| 279 | ssl_context_(NULL), |
| 280 | pending_send_error_(OK), |
| 281 | recv_buffer_head_slop_(0), |
| 282 | recv_buffer_tail_slop_(0) { |
| 283 | } |
| 284 | |
| 285 | SSLClientSocketMac::~SSLClientSocketMac() { |
| 286 | Disconnect(); |
| 287 | } |
| 288 | |
| 289 | int SSLClientSocketMac::Connect(CompletionCallback* callback) { |
| 290 | DCHECK(transport_.get()); |
| 291 | DCHECK(next_state_ == STATE_NONE); |
| 292 | DCHECK(!user_callback_); |
| 293 | |
[email protected] | 0ef0bcf | 2009-04-03 20:39:43 | [diff] [blame] | 294 | OSStatus status = noErr; |
| 295 | |
| 296 | status = SSLNewContext(false, &ssl_context_); |
| 297 | if (status) |
| 298 | return NetErrorFromOSStatus(status); |
| 299 | |
| 300 | status = SSLSetProtocolVersionEnabled(ssl_context_, |
| 301 | kSSLProtocol2, |
| 302 | ssl_config_.ssl2_enabled); |
| 303 | if (status) |
| 304 | return NetErrorFromOSStatus(status); |
| 305 | |
| 306 | status = SSLSetProtocolVersionEnabled(ssl_context_, |
| 307 | kSSLProtocol3, |
| 308 | ssl_config_.ssl3_enabled); |
| 309 | if (status) |
| 310 | return NetErrorFromOSStatus(status); |
| 311 | |
| 312 | status = SSLSetProtocolVersionEnabled(ssl_context_, |
| 313 | kTLSProtocol1, |
| 314 | ssl_config_.tls1_enabled); |
| 315 | if (status) |
| 316 | return NetErrorFromOSStatus(status); |
| 317 | |
| 318 | status = SSLSetIOFuncs(ssl_context_, SSLReadCallback, SSLWriteCallback); |
| 319 | if (status) |
| 320 | return NetErrorFromOSStatus(status); |
| 321 | |
| 322 | status = SSLSetConnection(ssl_context_, this); |
| 323 | if (status) |
| 324 | return NetErrorFromOSStatus(status); |
| 325 | |
[email protected] | 12701787 | 2009-08-13 17:54:42 | [diff] [blame^] | 326 | if (ssl_config_.allowed_bad_certs.empty()) { |
| 327 | // We're going to use the default certificate verification that the system |
| 328 | // does, and accept its answer for the cert status. |
| 329 | status = SSLSetPeerDomainName(ssl_context_, hostname_.data(), |
| 330 | hostname_.length()); |
| 331 | if (status) |
| 332 | return NetErrorFromOSStatus(status); |
| 333 | |
| 334 | // TODO(wtc): for now, always check revocation. |
| 335 | server_cert_status_ = CERT_STATUS_REV_CHECKING_ENABLED; |
| 336 | } else { |
| 337 | // Disable certificate chain validation. We will only allow the certs in |
| 338 | // ssl_config_.allowed_bad_certs. |
| 339 | status = SSLSetEnableCertVerify(ssl_context_, false); |
| 340 | if (status) |
| 341 | return NetErrorFromOSStatus(status); |
| 342 | } |
[email protected] | 0ef0bcf | 2009-04-03 20:39:43 | [diff] [blame] | 343 | |
| 344 | next_state_ = STATE_HANDSHAKE; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 345 | int rv = DoLoop(OK); |
| 346 | if (rv == ERR_IO_PENDING) |
| 347 | user_callback_ = callback; |
| 348 | return rv; |
| 349 | } |
| 350 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 351 | void SSLClientSocketMac::Disconnect() { |
| 352 | completed_handshake_ = false; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 353 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 354 | if (ssl_context_) { |
| 355 | SSLClose(ssl_context_); |
| 356 | SSLDisposeContext(ssl_context_); |
| 357 | ssl_context_ = NULL; |
| 358 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 359 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 360 | transport_->Disconnect(); |
| 361 | } |
| 362 | |
| 363 | bool SSLClientSocketMac::IsConnected() const { |
| 364 | // Ideally, we should also check if we have received the close_notify alert |
| 365 | // message from the server, and return false in that case. We're not doing |
| 366 | // that, so this function may return a false positive. Since the upper |
| 367 | // layer (HttpNetworkTransaction) needs to handle a persistent connection |
| 368 | // closed by the server when we send a request anyway, a false positive in |
| 369 | // exchange for simpler code is a good trade-off. |
| 370 | return completed_handshake_ && transport_->IsConnected(); |
| 371 | } |
| 372 | |
[email protected] | b219785 | 2009-02-19 23:27:33 | [diff] [blame] | 373 | bool SSLClientSocketMac::IsConnectedAndIdle() const { |
| 374 | // Unlike IsConnected, this method doesn't return a false positive. |
| 375 | // |
| 376 | // Strictly speaking, we should check if we have received the close_notify |
| 377 | // alert message from the server, and return false in that case. Although |
| 378 | // the close_notify alert message means EOF in the SSL layer, it is just |
| 379 | // bytes to the transport layer below, so transport_->IsConnectedAndIdle() |
| 380 | // returns the desired false when we receive close_notify. |
| 381 | return completed_handshake_ && transport_->IsConnectedAndIdle(); |
| 382 | } |
| 383 | |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 384 | int SSLClientSocketMac::Read(IOBuffer* buf, int buf_len, |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 385 | CompletionCallback* callback) { |
| 386 | DCHECK(completed_handshake_); |
| 387 | DCHECK(next_state_ == STATE_NONE); |
| 388 | DCHECK(!user_callback_); |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 389 | DCHECK(!user_buf_); |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 390 | |
| 391 | user_buf_ = buf; |
| 392 | user_buf_len_ = buf_len; |
| 393 | |
| 394 | next_state_ = STATE_PAYLOAD_READ; |
| 395 | int rv = DoLoop(OK); |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 396 | if (rv == ERR_IO_PENDING) { |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 397 | user_callback_ = callback; |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 398 | } else { |
| 399 | user_buf_ = NULL; |
| 400 | } |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 401 | return rv; |
| 402 | } |
| 403 | |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 404 | int SSLClientSocketMac::Write(IOBuffer* buf, int buf_len, |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 405 | CompletionCallback* callback) { |
| 406 | DCHECK(completed_handshake_); |
| 407 | DCHECK(next_state_ == STATE_NONE); |
| 408 | DCHECK(!user_callback_); |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 409 | DCHECK(!user_buf_); |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 410 | |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 411 | user_buf_ = buf; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 412 | user_buf_len_ = buf_len; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 413 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 414 | next_state_ = STATE_PAYLOAD_WRITE; |
| 415 | int rv = DoLoop(OK); |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 416 | if (rv == ERR_IO_PENDING) { |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 417 | user_callback_ = callback; |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 418 | } else { |
| 419 | user_buf_ = NULL; |
| 420 | } |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 421 | return rv; |
| 422 | } |
| 423 | |
| 424 | void SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) { |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 425 | ssl_info->Reset(); |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 426 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 427 | // set cert |
[email protected] | 12701787 | 2009-08-13 17:54:42 | [diff] [blame^] | 428 | ssl_info->cert = server_cert_; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 429 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 430 | // update status |
| 431 | ssl_info->cert_status = server_cert_status_; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 432 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 433 | // security info |
| 434 | SSLCipherSuite suite; |
[email protected] | 12701787 | 2009-08-13 17:54:42 | [diff] [blame^] | 435 | OSStatus status = SSLGetNegotiatedCipher(ssl_context_, &suite); |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 436 | if (!status) |
| 437 | ssl_info->security_bits = KeySizeOfCipherSuite(suite); |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 438 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 439 | |
[email protected] | 0b45559b | 2009-06-12 21:45:11 | [diff] [blame] | 440 | void SSLClientSocketMac::GetSSLCertRequestInfo( |
| 441 | SSLCertRequestInfo* cert_request_info) { |
| 442 | // TODO(wtc): implement this. |
| 443 | } |
| 444 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 445 | void SSLClientSocketMac::DoCallback(int rv) { |
| 446 | DCHECK(rv != ERR_IO_PENDING); |
| 447 | DCHECK(user_callback_); |
| 448 | |
| 449 | // since Run may result in Read being called, clear user_callback_ up front. |
| 450 | CompletionCallback* c = user_callback_; |
| 451 | user_callback_ = NULL; |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 452 | user_buf_ = NULL; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 453 | c->Run(rv); |
| 454 | } |
| 455 | |
| 456 | void SSLClientSocketMac::OnIOComplete(int result) { |
| 457 | if (next_io_state_ != STATE_NONE) { |
| 458 | State next_state = next_state_; |
| 459 | next_state_ = next_io_state_; |
| 460 | next_io_state_ = STATE_NONE; |
| 461 | result = DoLoop(result); |
| 462 | next_state_ = next_state; |
| 463 | } |
| 464 | if (next_state_ != STATE_NONE) { |
| 465 | int rv = DoLoop(result); |
| 466 | if (rv != ERR_IO_PENDING) |
| 467 | DoCallback(rv); |
| 468 | } |
| 469 | } |
| 470 | |
| 471 | // This is the main loop driving the state machine. Most calls coming from the |
| 472 | // outside just set up a few variables and jump into here. |
| 473 | int SSLClientSocketMac::DoLoop(int last_io_result) { |
| 474 | DCHECK(next_state_ != STATE_NONE); |
| 475 | int rv = last_io_result; |
| 476 | do { |
| 477 | State state = next_state_; |
| 478 | next_state_ = STATE_NONE; |
| 479 | switch (state) { |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 480 | case STATE_HANDSHAKE: |
| 481 | // Do the SSL/TLS handshake. |
| 482 | rv = DoHandshake(); |
| 483 | break; |
| 484 | case STATE_READ_COMPLETE: |
| 485 | // A read off the network is complete; do the paperwork. |
| 486 | rv = DoReadComplete(rv); |
| 487 | break; |
| 488 | case STATE_PAYLOAD_READ: |
| 489 | // Do a read of data from the network. |
| 490 | rv = DoPayloadRead(); |
| 491 | break; |
| 492 | case STATE_PAYLOAD_WRITE: |
| 493 | // Do a write of data to the network. |
| 494 | rv = DoPayloadWrite(); |
| 495 | break; |
| 496 | default: |
| 497 | rv = ERR_UNEXPECTED; |
| 498 | NOTREACHED() << "unexpected state"; |
| 499 | break; |
| 500 | } |
| 501 | } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); |
| 502 | return rv; |
| 503 | } |
| 504 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 505 | int SSLClientSocketMac::DoHandshake() { |
| 506 | OSStatus status = SSLHandshake(ssl_context_); |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 507 | int net_error = NetErrorFromOSStatus(status); |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 508 | |
[email protected] | 12701787 | 2009-08-13 17:54:42 | [diff] [blame^] | 509 | if (status == errSSLWouldBlock) { |
| 510 | next_state_ = STATE_HANDSHAKE; |
| 511 | } else if (status == noErr) { |
| 512 | completed_handshake_ = true; // We have a connection. |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 513 | |
[email protected] | 12701787 | 2009-08-13 17:54:42 | [diff] [blame^] | 514 | server_cert_ = GetServerCert(ssl_context_); |
| 515 | DCHECK(server_cert_); |
| 516 | if (!ssl_config_.allowed_bad_certs.empty()) { |
| 517 | // Check server_cert_ because SecureTransport didn't verify it. |
| 518 | // TODO(wtc): If server_cert_ is not one of the allowed bad certificates, |
| 519 | // we should verify server_cert_ ourselves. Since we don't know how to |
| 520 | // do that yet, treat it as an invalid certificate. |
| 521 | net_error = ERR_CERT_INVALID; |
| 522 | server_cert_status_ |= CERT_STATUS_INVALID; |
| 523 | |
| 524 | for (size_t i = 0; i < ssl_config_.allowed_bad_certs.size(); ++i) { |
| 525 | if (server_cert_ == ssl_config_.allowed_bad_certs[i].cert) { |
| 526 | net_error = OK; |
| 527 | server_cert_status_ = ssl_config_.allowed_bad_certs[i].cert_status; |
| 528 | break; |
| 529 | } |
| 530 | } |
| 531 | } |
| 532 | } else if (IsCertStatusError(net_error)) { |
| 533 | server_cert_ = GetServerCert(ssl_context_); |
| 534 | DCHECK(server_cert_); |
[email protected] | dedb5943 | 2009-02-03 16:51:15 | [diff] [blame] | 535 | server_cert_status_ |= MapNetErrorToCertStatus(net_error); |
[email protected] | 12701787 | 2009-08-13 17:54:42 | [diff] [blame^] | 536 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 537 | |
[email protected] | e159339 | 2008-10-20 19:46:33 | [diff] [blame] | 538 | return net_error; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 539 | } |
| 540 | |
| 541 | int SSLClientSocketMac::DoReadComplete(int result) { |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 542 | if (result < 0) { |
| 543 | read_io_buf_ = NULL; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 544 | return result; |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 545 | } |
| 546 | |
| 547 | char* buffer = &recv_buffer_[recv_buffer_.size() - recv_buffer_tail_slop_]; |
| 548 | memcpy(buffer, read_io_buf_->data(), result); |
| 549 | read_io_buf_ = NULL; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 550 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 551 | recv_buffer_tail_slop_ -= result; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 552 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 553 | return result; |
| 554 | } |
| 555 | |
| 556 | void SSLClientSocketMac::OnWriteComplete(int result) { |
| 557 | if (result < 0) { |
| 558 | pending_send_error_ = result; |
| 559 | return; |
| 560 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 561 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 562 | send_buffer_.erase(send_buffer_.begin(), |
| 563 | send_buffer_.begin() + result); |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 564 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 565 | if (!send_buffer_.empty()) |
| 566 | SSLWriteCallback(this, NULL, NULL); |
| 567 | } |
| 568 | |
| 569 | int SSLClientSocketMac::DoPayloadRead() { |
[email protected] | e05c55d | 2009-08-06 18:21:14 | [diff] [blame] | 570 | size_t processed = 0; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 571 | OSStatus status = SSLRead(ssl_context_, |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 572 | user_buf_->data(), |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 573 | user_buf_len_, |
| 574 | &processed); |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 575 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 576 | // There's a subtle difference here in semantics of the "would block" errors. |
| 577 | // In our code, ERR_IO_PENDING means the whole operation is async, while |
| 578 | // errSSLWouldBlock means that the stream isn't ending (and is often returned |
| 579 | // along with partial data). So even though "would block" is returned, if we |
| 580 | // have data, let's just return it. |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 581 | |
[email protected] | e05c55d | 2009-08-06 18:21:14 | [diff] [blame] | 582 | if (processed > 0) |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 583 | return processed; |
[email protected] | e05c55d | 2009-08-06 18:21:14 | [diff] [blame] | 584 | |
| 585 | if (status == errSSLClosedNoNotify) { |
| 586 | // TODO(wtc): Unless we have received the close_notify alert, we need to |
| 587 | // return an error code indicating that the SSL connection ended |
| 588 | // uncleanly, a potential truncation attack. See http://crbug.com/18586. |
| 589 | return OK; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 590 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 591 | |
[email protected] | e05c55d | 2009-08-06 18:21:14 | [diff] [blame] | 592 | if (status == errSSLWouldBlock) |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 593 | next_state_ = STATE_PAYLOAD_READ; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 594 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 595 | return NetErrorFromOSStatus(status); |
| 596 | } |
| 597 | |
| 598 | int SSLClientSocketMac::DoPayloadWrite() { |
[email protected] | e05c55d | 2009-08-06 18:21:14 | [diff] [blame] | 599 | size_t processed = 0; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 600 | OSStatus status = SSLWrite(ssl_context_, |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 601 | user_buf_->data(), |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 602 | user_buf_len_, |
| 603 | &processed); |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 604 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 605 | if (processed > 0) |
| 606 | return processed; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 607 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 608 | return NetErrorFromOSStatus(status); |
| 609 | } |
| 610 | |
| 611 | // Handling the reading from the network is one of those things that should be |
| 612 | // simpler than it is. Ideally, we'd have some kind of ring buffer. For now, a |
| 613 | // std::vector<char> will have to do. |
| 614 | // |
| 615 | // The need for a buffer at all comes from the difference between an |
| 616 | // asynchronous connection (which is what we have) and a non-blocking connection |
| 617 | // (which is what we fake for Secure Transport). When Secure Transport calls us |
| 618 | // to read data, we call our underlying transport, which will likely tell us |
| 619 | // that it'll do a callback. When that happens, we need to tell Secure Transport |
| 620 | // that we've "blocked". When the callback happens, we have a chunk of data that |
| 621 | // we need to feed to Secure Transport, but it's not interested. It'll ask for |
| 622 | // it again when we call it again, so we need to hold on to the data. |
| 623 | // |
| 624 | // Why keep our own buffer? Well, when we execute a read and the underlying |
| 625 | // transport says that it'll do a callback, it keeps the pointer to the |
| 626 | // buffer. We can't pass it the buffer that Secure Transport gave us to fill, as |
| 627 | // we can't guarantee its lifetime. |
| 628 | // |
| 629 | // The basic idea, then, is this: we have a buffer filled with the data that |
| 630 | // we've read from the network but haven't given to Secure Transport |
| 631 | // yet. Whenever we read from the network the first thing we do is ensure we |
| 632 | // have enough room in the buffer for the read. We enlarge the buffer to be big |
| 633 | // enough to hold both our existing data and the new data, and then we mark the |
| 634 | // extra space at the end as "tail slop." Slop is just space at the ends of the |
| 635 | // buffer that's going to be used for data but isn't (yet). A diagram: |
| 636 | // |
| 637 | // +--------------------------------------+--------------------------------+ |
| 638 | // | existing good data ~~~~~~~~~~~~~~~~~ | tail slop area ~~~~~~~~~~~~~~~ | |
| 639 | // +--------------------------------------+--------------------------------+ |
| 640 | // |
| 641 | // When executing a read, we pass a pointer to the beginning of the tail slop |
| 642 | // area (guaranteed to be contiguous space because it's a vector, unlike, say, a |
| 643 | // deque (sigh)) and the size of the tail slop. When we get data (either here in |
| 644 | // SSLReadCallback() or above in DoReadComplete()) we subtract the number of |
| 645 | // bytes received from the tail slop value. That moves those bytes |
| 646 | // (conceptually, not physically) from the tail slop area to the area containing |
| 647 | // real data. |
| 648 | // |
| 649 | // The idea is still pretty simple. We enlarge the tail slop, call our |
| 650 | // underlying network, get data, shrink the slop area to match, copy requested |
| 651 | // data back into our caller's buffer, and delete the data from the head of the |
| 652 | // vector. |
| 653 | // |
| 654 | // Except for a nasty little problem. Asynchronous I/O calls keep the buffer |
| 655 | // pointer. |
| 656 | // |
| 657 | // This leads to the following scenario: we have a few bytes of good data in our |
| 658 | // buffer. But our caller requests more than that. We oblige by enlarging the |
| 659 | // tail slop, and calling our underlying provider, but the provider says that |
| 660 | // it'll call us back later. So we shrug our shoulders, copy what we do have |
| 661 | // into our caller's buffer and... |
| 662 | // |
| 663 | // Wait. We can't delete the data from the head of our vector. That would |
| 664 | // invalidate the pointer that we just gave to our provider. So instead, in that |
| 665 | // case we keep track of where the good data starts by keeping a "head slop" |
| 666 | // value, which just notes what data we've already sent and that is useless to |
| 667 | // us but that we can't delete because we have I/O in flight depending on us |
| 668 | // leaving the buffer alone. |
| 669 | // |
| 670 | // I hear what you're saying. "We need to use a ring buffer!" You write it, |
| 671 | // then, and I'll use it. Here are the features it needs. First, it needs to be |
| 672 | // able to have contiguous segments of arbitrary length attached to it to create |
| 673 | // read buffers. Second, each of those segments must have a "used" length |
| 674 | // indicator, so if it was half-filled by a previous data read, but the next |
| 675 | // data read is for more than there's space left, a new segment can be created |
| 676 | // for the new read without leaving an internal gap. |
| 677 | // |
| 678 | // Get to it. |
| 679 | // |
| 680 | // (sigh) Who am I kidding? TODO(avi): write the aforementioned ring buffer |
| 681 | |
| 682 | // static |
| 683 | OSStatus SSLClientSocketMac::SSLReadCallback(SSLConnectionRef connection, |
| 684 | void* data, |
| 685 | size_t* data_length) { |
| 686 | DCHECK(data); |
| 687 | DCHECK(data_length); |
| 688 | SSLClientSocketMac* us = |
| 689 | const_cast<SSLClientSocketMac*>( |
| 690 | static_cast<const SSLClientSocketMac*>(connection)); |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 691 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 692 | // If we have I/O in flight, promise we'll get back to them and use the |
| 693 | // existing callback to do so |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 694 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 695 | if (us->next_io_state_ == STATE_READ_COMPLETE) { |
| 696 | *data_length = 0; |
| 697 | return errSSLWouldBlock; |
| 698 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 699 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 700 | // Start with what's in the buffer |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 701 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 702 | size_t total_read = us->recv_buffer_.size() - us->recv_buffer_head_slop_ - |
| 703 | us->recv_buffer_tail_slop_; |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 704 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 705 | // Resize the buffer if needed |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 706 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 707 | if (us->recv_buffer_.size() - us->recv_buffer_head_slop_ < *data_length) { |
| 708 | us->recv_buffer_.resize(us->recv_buffer_head_slop_ + *data_length); |
| 709 | us->recv_buffer_tail_slop_ = *data_length - total_read; |
| 710 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 711 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 712 | int rv = 1; // any old value to spin the loop below |
| 713 | while (rv > 0 && total_read < *data_length) { |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 714 | char* buffer = &us->recv_buffer_[us->recv_buffer_head_slop_ + total_read]; |
| 715 | us->read_io_buf_ = new IOBuffer(*data_length - total_read); |
| 716 | rv = us->transport_->Read(us->read_io_buf_, |
| 717 | *data_length - total_read, |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 718 | &us->io_callback_); |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 719 | |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 720 | if (rv >= 0) { |
| 721 | memcpy(buffer, us->read_io_buf_->data(), rv); |
| 722 | us->read_io_buf_ = NULL; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 723 | total_read += rv; |
| 724 | us->recv_buffer_tail_slop_ -= rv; |
| 725 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 726 | } |
| 727 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 728 | *data_length = total_read; |
| 729 | if (total_read) { |
| 730 | memcpy(data, &us->recv_buffer_[us->recv_buffer_head_slop_], total_read); |
| 731 | if (rv == ERR_IO_PENDING) { |
| 732 | // We have I/O in flight which is going to land in our buffer. We can't |
| 733 | // shuffle things around, so we need to just fiddle with pointers. |
| 734 | us->recv_buffer_head_slop_ += total_read; |
| 735 | } else { |
| 736 | us->recv_buffer_.erase(us->recv_buffer_.begin(), |
| 737 | us->recv_buffer_.begin() + |
| 738 | total_read + |
| 739 | us->recv_buffer_head_slop_); |
| 740 | us->recv_buffer_head_slop_ = 0; |
| 741 | } |
| 742 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 743 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 744 | if (rv == ERR_IO_PENDING) { |
| 745 | us->next_io_state_ = STATE_READ_COMPLETE; |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 746 | } else { |
| 747 | us->read_io_buf_ = NULL; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 748 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 749 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 750 | if (rv < 0) |
| 751 | return OSStatusFromNetError(rv); |
[email protected] | 34c9dff | 2009-07-16 12:25:32 | [diff] [blame] | 752 | else if (rv == 0) // stream closed |
| 753 | return errSSLClosedGraceful; |
| 754 | else |
| 755 | return noErr; |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 756 | } |
| 757 | |
| 758 | // static |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 759 | OSStatus SSLClientSocketMac::SSLWriteCallback(SSLConnectionRef connection, |
| 760 | const void* data, |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 761 | size_t* data_length) { |
| 762 | SSLClientSocketMac* us = |
| 763 | const_cast<SSLClientSocketMac*>( |
| 764 | static_cast<const SSLClientSocketMac*>(connection)); |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 765 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 766 | if (us->pending_send_error_ != OK) { |
| 767 | OSStatus status = OSStatusFromNetError(us->pending_send_error_); |
| 768 | us->pending_send_error_ = OK; |
| 769 | return status; |
| 770 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 771 | |
[email protected] | a482bb0 | 2009-07-29 19:57:46 | [diff] [blame] | 772 | bool send_pending = !us->send_buffer_.empty(); |
| 773 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 774 | if (data) |
| 775 | us->send_buffer_.insert(us->send_buffer_.end(), |
| 776 | static_cast<const char*>(data), |
| 777 | static_cast<const char*>(data) + *data_length); |
[email protected] | a482bb0 | 2009-07-29 19:57:46 | [diff] [blame] | 778 | |
| 779 | if (send_pending) { |
| 780 | // If we have I/O in flight, just add the data to the end of the buffer and |
| 781 | // return to our caller. The existing callback will trigger the write of the |
| 782 | // new data when it sees that data remains in the buffer after removing the |
| 783 | // sent data. As always, lie to our caller. |
| 784 | return noErr; |
| 785 | } |
| 786 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 787 | int rv; |
| 788 | do { |
[email protected] | ffeb088 | 2009-04-30 21:51:25 | [diff] [blame] | 789 | scoped_refptr<IOBuffer> buffer = new IOBuffer(us->send_buffer_.size()); |
| 790 | memcpy(buffer->data(), &us->send_buffer_[0], us->send_buffer_.size()); |
| 791 | rv = us->transport_->Write(buffer, |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 792 | us->send_buffer_.size(), |
| 793 | &us->write_callback_); |
| 794 | if (rv > 0) { |
| 795 | us->send_buffer_.erase(us->send_buffer_.begin(), |
| 796 | us->send_buffer_.begin() + rv); |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 797 | } |
| 798 | } while (rv > 0 && !us->send_buffer_.empty()); |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 799 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 800 | if (rv < 0 && rv != ERR_IO_PENDING) { |
| 801 | return OSStatusFromNetError(rv); |
| 802 | } |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 803 | |
[email protected] | b75523f | 2008-10-17 14:49:07 | [diff] [blame] | 804 | // always lie to our caller |
| 805 | return noErr; |
| 806 | } |
| 807 | |
| 808 | } // namespace net |