OLD | NEW |
| (Empty) |
1 // Copyright (c) 2008-2009 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 "net/base/ssl_client_socket_mac.h" | |
6 | |
7 #include "base/singleton.h" | |
8 #include "base/string_util.h" | |
9 #include "net/base/io_buffer.h" | |
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; | |
123 | |
124 case errSSLClosedGraceful: | |
125 case noErr: | |
126 return OK; | |
127 | |
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 | |
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 | |
172 | |
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; | |
182 | |
183 // SSL 2, 3, TLS | |
184 | |
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; | |
221 | |
222 // TLS AES options (see RFC 3268) | |
223 | |
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; | |
238 | |
239 default: | |
240 return -1; | |
241 } | |
242 } | |
243 | |
244 } // namespace | |
245 | |
246 //----------------------------------------------------------------------------- | |
247 | |
248 SSLClientSocketMac::SSLClientSocketMac(ClientSocket* transport_socket, | |
249 const std::string& hostname, | |
250 const SSLConfig& ssl_config) | |
251 : io_callback_(this, &SSLClientSocketMac::OnIOComplete), | |
252 write_callback_(this, &SSLClientSocketMac::OnWriteComplete), | |
253 transport_(transport_socket), | |
254 hostname_(hostname), | |
255 ssl_config_(ssl_config), | |
256 user_callback_(NULL), | |
257 next_state_(STATE_NONE), | |
258 next_io_state_(STATE_NONE), | |
259 server_cert_status_(0), | |
260 completed_handshake_(false), | |
261 ssl_context_(NULL), | |
262 pending_send_error_(OK), | |
263 recv_buffer_head_slop_(0), | |
264 recv_buffer_tail_slop_(0) { | |
265 } | |
266 | |
267 SSLClientSocketMac::~SSLClientSocketMac() { | |
268 Disconnect(); | |
269 } | |
270 | |
271 int SSLClientSocketMac::Connect(CompletionCallback* callback) { | |
272 DCHECK(transport_.get()); | |
273 DCHECK(next_state_ == STATE_NONE); | |
274 DCHECK(!user_callback_); | |
275 | |
276 OSStatus status = noErr; | |
277 | |
278 status = SSLNewContext(false, &ssl_context_); | |
279 if (status) | |
280 return NetErrorFromOSStatus(status); | |
281 | |
282 status = SSLSetProtocolVersionEnabled(ssl_context_, | |
283 kSSLProtocol2, | |
284 ssl_config_.ssl2_enabled); | |
285 if (status) | |
286 return NetErrorFromOSStatus(status); | |
287 | |
288 status = SSLSetProtocolVersionEnabled(ssl_context_, | |
289 kSSLProtocol3, | |
290 ssl_config_.ssl3_enabled); | |
291 if (status) | |
292 return NetErrorFromOSStatus(status); | |
293 | |
294 status = SSLSetProtocolVersionEnabled(ssl_context_, | |
295 kTLSProtocol1, | |
296 ssl_config_.tls1_enabled); | |
297 if (status) | |
298 return NetErrorFromOSStatus(status); | |
299 | |
300 status = SSLSetIOFuncs(ssl_context_, SSLReadCallback, SSLWriteCallback); | |
301 if (status) | |
302 return NetErrorFromOSStatus(status); | |
303 | |
304 status = SSLSetConnection(ssl_context_, this); | |
305 if (status) | |
306 return NetErrorFromOSStatus(status); | |
307 | |
308 status = SSLSetPeerDomainName(ssl_context_, hostname_.c_str(), | |
309 hostname_.length()); | |
310 if (status) | |
311 return NetErrorFromOSStatus(status); | |
312 | |
313 next_state_ = STATE_HANDSHAKE; | |
314 int rv = DoLoop(OK); | |
315 if (rv == ERR_IO_PENDING) | |
316 user_callback_ = callback; | |
317 return rv; | |
318 } | |
319 | |
320 void SSLClientSocketMac::Disconnect() { | |
321 completed_handshake_ = false; | |
322 | |
323 if (ssl_context_) { | |
324 SSLClose(ssl_context_); | |
325 SSLDisposeContext(ssl_context_); | |
326 ssl_context_ = NULL; | |
327 } | |
328 | |
329 transport_->Disconnect(); | |
330 } | |
331 | |
332 bool SSLClientSocketMac::IsConnected() const { | |
333 // Ideally, we should also check if we have received the close_notify alert | |
334 // message from the server, and return false in that case. We're not doing | |
335 // that, so this function may return a false positive. Since the upper | |
336 // layer (HttpNetworkTransaction) needs to handle a persistent connection | |
337 // closed by the server when we send a request anyway, a false positive in | |
338 // exchange for simpler code is a good trade-off. | |
339 return completed_handshake_ && transport_->IsConnected(); | |
340 } | |
341 | |
342 bool SSLClientSocketMac::IsConnectedAndIdle() const { | |
343 // Unlike IsConnected, this method doesn't return a false positive. | |
344 // | |
345 // Strictly speaking, we should check if we have received the close_notify | |
346 // alert message from the server, and return false in that case. Although | |
347 // the close_notify alert message means EOF in the SSL layer, it is just | |
348 // bytes to the transport layer below, so transport_->IsConnectedAndIdle() | |
349 // returns the desired false when we receive close_notify. | |
350 return completed_handshake_ && transport_->IsConnectedAndIdle(); | |
351 } | |
352 | |
353 int SSLClientSocketMac::Read(IOBuffer* buf, int buf_len, | |
354 CompletionCallback* callback) { | |
355 DCHECK(completed_handshake_); | |
356 DCHECK(next_state_ == STATE_NONE); | |
357 DCHECK(!user_callback_); | |
358 DCHECK(!user_buf_); | |
359 | |
360 user_buf_ = buf; | |
361 user_buf_len_ = buf_len; | |
362 | |
363 next_state_ = STATE_PAYLOAD_READ; | |
364 int rv = DoLoop(OK); | |
365 if (rv == ERR_IO_PENDING) { | |
366 user_callback_ = callback; | |
367 } else { | |
368 user_buf_ = NULL; | |
369 } | |
370 return rv; | |
371 } | |
372 | |
373 int SSLClientSocketMac::Write(IOBuffer* buf, int buf_len, | |
374 CompletionCallback* callback) { | |
375 DCHECK(completed_handshake_); | |
376 DCHECK(next_state_ == STATE_NONE); | |
377 DCHECK(!user_callback_); | |
378 DCHECK(!user_buf_); | |
379 | |
380 user_buf_ = buf; | |
381 user_buf_len_ = buf_len; | |
382 | |
383 next_state_ = STATE_PAYLOAD_WRITE; | |
384 int rv = DoLoop(OK); | |
385 if (rv == ERR_IO_PENDING) { | |
386 user_callback_ = callback; | |
387 } else { | |
388 user_buf_ = NULL; | |
389 } | |
390 return rv; | |
391 } | |
392 | |
393 void SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) { | |
394 ssl_info->Reset(); | |
395 | |
396 // set cert | |
397 CFArrayRef certs; | |
398 OSStatus status = SSLCopyPeerCertificates(ssl_context_, &certs); | |
399 if (!status) { | |
400 DCHECK(CFArrayGetCount(certs) > 0); | |
401 | |
402 SecCertificateRef client_cert = | |
403 static_cast<SecCertificateRef>( | |
404 const_cast<void*>(CFArrayGetValueAtIndex(certs, 0))); | |
405 CFRetain(client_cert); | |
406 ssl_info->cert = X509Certificate::CreateFromHandle( | |
407 client_cert, X509Certificate::SOURCE_FROM_NETWORK); | |
408 CFRelease(certs); | |
409 } | |
410 | |
411 // update status | |
412 ssl_info->cert_status = server_cert_status_; | |
413 | |
414 // security info | |
415 SSLCipherSuite suite; | |
416 status = SSLGetNegotiatedCipher(ssl_context_, &suite); | |
417 if (!status) | |
418 ssl_info->security_bits = KeySizeOfCipherSuite(suite); | |
419 } | |
420 | |
421 void SSLClientSocketMac::GetSSLCertRequestInfo( | |
422 SSLCertRequestInfo* cert_request_info) { | |
423 // TODO(wtc): implement this. | |
424 } | |
425 | |
426 void SSLClientSocketMac::DoCallback(int rv) { | |
427 DCHECK(rv != ERR_IO_PENDING); | |
428 DCHECK(user_callback_); | |
429 | |
430 // since Run may result in Read being called, clear user_callback_ up front. | |
431 CompletionCallback* c = user_callback_; | |
432 user_callback_ = NULL; | |
433 user_buf_ = NULL; | |
434 c->Run(rv); | |
435 } | |
436 | |
437 void SSLClientSocketMac::OnIOComplete(int result) { | |
438 if (next_io_state_ != STATE_NONE) { | |
439 State next_state = next_state_; | |
440 next_state_ = next_io_state_; | |
441 next_io_state_ = STATE_NONE; | |
442 result = DoLoop(result); | |
443 next_state_ = next_state; | |
444 } | |
445 if (next_state_ != STATE_NONE) { | |
446 int rv = DoLoop(result); | |
447 if (rv != ERR_IO_PENDING) | |
448 DoCallback(rv); | |
449 } | |
450 } | |
451 | |
452 // This is the main loop driving the state machine. Most calls coming from the | |
453 // outside just set up a few variables and jump into here. | |
454 int SSLClientSocketMac::DoLoop(int last_io_result) { | |
455 DCHECK(next_state_ != STATE_NONE); | |
456 int rv = last_io_result; | |
457 do { | |
458 State state = next_state_; | |
459 next_state_ = STATE_NONE; | |
460 switch (state) { | |
461 case STATE_HANDSHAKE: | |
462 // Do the SSL/TLS handshake. | |
463 rv = DoHandshake(); | |
464 break; | |
465 case STATE_READ_COMPLETE: | |
466 // A read off the network is complete; do the paperwork. | |
467 rv = DoReadComplete(rv); | |
468 break; | |
469 case STATE_PAYLOAD_READ: | |
470 // Do a read of data from the network. | |
471 rv = DoPayloadRead(); | |
472 break; | |
473 case STATE_PAYLOAD_WRITE: | |
474 // Do a write of data to the network. | |
475 rv = DoPayloadWrite(); | |
476 break; | |
477 default: | |
478 rv = ERR_UNEXPECTED; | |
479 NOTREACHED() << "unexpected state"; | |
480 break; | |
481 } | |
482 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
483 return rv; | |
484 } | |
485 | |
486 int SSLClientSocketMac::DoHandshake() { | |
487 OSStatus status = SSLHandshake(ssl_context_); | |
488 | |
489 if (status == errSSLWouldBlock) | |
490 next_state_ = STATE_HANDSHAKE; | |
491 | |
492 if (status == noErr) | |
493 completed_handshake_ = true; | |
494 | |
495 int net_error = NetErrorFromOSStatus(status); | |
496 | |
497 // At this point we have a connection. For now, we're going to use the default | |
498 // certificate verification that the system does, and accept its answer for | |
499 // the cert status. In the future, we'll need to call SSLSetEnableCertVerify | |
500 // to disable cert verification and do the verification ourselves. This allows | |
501 // very fine-grained control over what we'll accept for certification. | |
502 // TODO(avi): ditto | |
503 | |
504 // TODO(wtc): for now, always check revocation. | |
505 server_cert_status_ = CERT_STATUS_REV_CHECKING_ENABLED; | |
506 if (net_error) | |
507 server_cert_status_ |= MapNetErrorToCertStatus(net_error); | |
508 | |
509 return net_error; | |
510 } | |
511 | |
512 int SSLClientSocketMac::DoReadComplete(int result) { | |
513 if (result < 0) { | |
514 read_io_buf_ = NULL; | |
515 return result; | |
516 } | |
517 | |
518 char* buffer = &recv_buffer_[recv_buffer_.size() - recv_buffer_tail_slop_]; | |
519 memcpy(buffer, read_io_buf_->data(), result); | |
520 read_io_buf_ = NULL; | |
521 | |
522 recv_buffer_tail_slop_ -= result; | |
523 | |
524 return result; | |
525 } | |
526 | |
527 void SSLClientSocketMac::OnWriteComplete(int result) { | |
528 if (result < 0) { | |
529 pending_send_error_ = result; | |
530 return; | |
531 } | |
532 | |
533 send_buffer_.erase(send_buffer_.begin(), | |
534 send_buffer_.begin() + result); | |
535 | |
536 if (!send_buffer_.empty()) | |
537 SSLWriteCallback(this, NULL, NULL); | |
538 } | |
539 | |
540 int SSLClientSocketMac::DoPayloadRead() { | |
541 size_t processed; | |
542 OSStatus status = SSLRead(ssl_context_, | |
543 user_buf_->data(), | |
544 user_buf_len_, | |
545 &processed); | |
546 | |
547 // There's a subtle difference here in semantics of the "would block" errors. | |
548 // In our code, ERR_IO_PENDING means the whole operation is async, while | |
549 // errSSLWouldBlock means that the stream isn't ending (and is often returned | |
550 // along with partial data). So even though "would block" is returned, if we | |
551 // have data, let's just return it. | |
552 | |
553 if (processed > 0) { | |
554 next_state_ = STATE_NONE; | |
555 return processed; | |
556 } | |
557 | |
558 if (status == errSSLWouldBlock) { | |
559 next_state_ = STATE_PAYLOAD_READ; | |
560 } | |
561 | |
562 return NetErrorFromOSStatus(status); | |
563 } | |
564 | |
565 int SSLClientSocketMac::DoPayloadWrite() { | |
566 size_t processed; | |
567 OSStatus status = SSLWrite(ssl_context_, | |
568 user_buf_->data(), | |
569 user_buf_len_, | |
570 &processed); | |
571 | |
572 if (processed > 0) | |
573 return processed; | |
574 | |
575 return NetErrorFromOSStatus(status); | |
576 } | |
577 | |
578 // Handling the reading from the network is one of those things that should be | |
579 // simpler than it is. Ideally, we'd have some kind of ring buffer. For now, a | |
580 // std::vector<char> will have to do. | |
581 // | |
582 // The need for a buffer at all comes from the difference between an | |
583 // asynchronous connection (which is what we have) and a non-blocking connection | |
584 // (which is what we fake for Secure Transport). When Secure Transport calls us | |
585 // to read data, we call our underlying transport, which will likely tell us | |
586 // that it'll do a callback. When that happens, we need to tell Secure Transport | |
587 // that we've "blocked". When the callback happens, we have a chunk of data that | |
588 // we need to feed to Secure Transport, but it's not interested. It'll ask for | |
589 // it again when we call it again, so we need to hold on to the data. | |
590 // | |
591 // Why keep our own buffer? Well, when we execute a read and the underlying | |
592 // transport says that it'll do a callback, it keeps the pointer to the | |
593 // buffer. We can't pass it the buffer that Secure Transport gave us to fill, as | |
594 // we can't guarantee its lifetime. | |
595 // | |
596 // The basic idea, then, is this: we have a buffer filled with the data that | |
597 // we've read from the network but haven't given to Secure Transport | |
598 // yet. Whenever we read from the network the first thing we do is ensure we | |
599 // have enough room in the buffer for the read. We enlarge the buffer to be big | |
600 // enough to hold both our existing data and the new data, and then we mark the | |
601 // extra space at the end as "tail slop." Slop is just space at the ends of the | |
602 // buffer that's going to be used for data but isn't (yet). A diagram: | |
603 // | |
604 // +--------------------------------------+--------------------------------+ | |
605 // | existing good data ~~~~~~~~~~~~~~~~~ | tail slop area ~~~~~~~~~~~~~~~ | | |
606 // +--------------------------------------+--------------------------------+ | |
607 // | |
608 // When executing a read, we pass a pointer to the beginning of the tail slop | |
609 // area (guaranteed to be contiguous space because it's a vector, unlike, say, a | |
610 // deque (sigh)) and the size of the tail slop. When we get data (either here in | |
611 // SSLReadCallback() or above in DoReadComplete()) we subtract the number of | |
612 // bytes received from the tail slop value. That moves those bytes | |
613 // (conceptually, not physically) from the tail slop area to the area containing | |
614 // real data. | |
615 // | |
616 // The idea is still pretty simple. We enlarge the tail slop, call our | |
617 // underlying network, get data, shrink the slop area to match, copy requested | |
618 // data back into our caller's buffer, and delete the data from the head of the | |
619 // vector. | |
620 // | |
621 // Except for a nasty little problem. Asynchronous I/O calls keep the buffer | |
622 // pointer. | |
623 // | |
624 // This leads to the following scenario: we have a few bytes of good data in our | |
625 // buffer. But our caller requests more than that. We oblige by enlarging the | |
626 // tail slop, and calling our underlying provider, but the provider says that | |
627 // it'll call us back later. So we shrug our shoulders, copy what we do have | |
628 // into our caller's buffer and... | |
629 // | |
630 // Wait. We can't delete the data from the head of our vector. That would | |
631 // invalidate the pointer that we just gave to our provider. So instead, in that | |
632 // case we keep track of where the good data starts by keeping a "head slop" | |
633 // value, which just notes what data we've already sent and that is useless to | |
634 // us but that we can't delete because we have I/O in flight depending on us | |
635 // leaving the buffer alone. | |
636 // | |
637 // I hear what you're saying. "We need to use a ring buffer!" You write it, | |
638 // then, and I'll use it. Here are the features it needs. First, it needs to be | |
639 // able to have contiguous segments of arbitrary length attached to it to create | |
640 // read buffers. Second, each of those segments must have a "used" length | |
641 // indicator, so if it was half-filled by a previous data read, but the next | |
642 // data read is for more than there's space left, a new segment can be created | |
643 // for the new read without leaving an internal gap. | |
644 // | |
645 // Get to it. | |
646 // | |
647 // (sigh) Who am I kidding? TODO(avi): write the aforementioned ring buffer | |
648 | |
649 // static | |
650 OSStatus SSLClientSocketMac::SSLReadCallback(SSLConnectionRef connection, | |
651 void* data, | |
652 size_t* data_length) { | |
653 DCHECK(data); | |
654 DCHECK(data_length); | |
655 SSLClientSocketMac* us = | |
656 const_cast<SSLClientSocketMac*>( | |
657 static_cast<const SSLClientSocketMac*>(connection)); | |
658 | |
659 // If we have I/O in flight, promise we'll get back to them and use the | |
660 // existing callback to do so | |
661 | |
662 if (us->next_io_state_ == STATE_READ_COMPLETE) { | |
663 *data_length = 0; | |
664 return errSSLWouldBlock; | |
665 } | |
666 | |
667 // Start with what's in the buffer | |
668 | |
669 size_t total_read = us->recv_buffer_.size() - us->recv_buffer_head_slop_ - | |
670 us->recv_buffer_tail_slop_; | |
671 | |
672 // Resize the buffer if needed | |
673 | |
674 if (us->recv_buffer_.size() - us->recv_buffer_head_slop_ < *data_length) { | |
675 us->recv_buffer_.resize(us->recv_buffer_head_slop_ + *data_length); | |
676 us->recv_buffer_tail_slop_ = *data_length - total_read; | |
677 } | |
678 | |
679 int rv = 1; // any old value to spin the loop below | |
680 while (rv > 0 && total_read < *data_length) { | |
681 char* buffer = &us->recv_buffer_[us->recv_buffer_head_slop_ + total_read]; | |
682 us->read_io_buf_ = new IOBuffer(*data_length - total_read); | |
683 rv = us->transport_->Read(us->read_io_buf_, | |
684 *data_length - total_read, | |
685 &us->io_callback_); | |
686 | |
687 if (rv >= 0) { | |
688 memcpy(buffer, us->read_io_buf_->data(), rv); | |
689 us->read_io_buf_ = NULL; | |
690 total_read += rv; | |
691 us->recv_buffer_tail_slop_ -= rv; | |
692 } | |
693 } | |
694 | |
695 *data_length = total_read; | |
696 if (total_read) { | |
697 memcpy(data, &us->recv_buffer_[us->recv_buffer_head_slop_], total_read); | |
698 if (rv == ERR_IO_PENDING) { | |
699 // We have I/O in flight which is going to land in our buffer. We can't | |
700 // shuffle things around, so we need to just fiddle with pointers. | |
701 us->recv_buffer_head_slop_ += total_read; | |
702 } else { | |
703 us->recv_buffer_.erase(us->recv_buffer_.begin(), | |
704 us->recv_buffer_.begin() + | |
705 total_read + | |
706 us->recv_buffer_head_slop_); | |
707 us->recv_buffer_head_slop_ = 0; | |
708 } | |
709 } | |
710 | |
711 if (rv == ERR_IO_PENDING) { | |
712 us->next_io_state_ = STATE_READ_COMPLETE; | |
713 } else { | |
714 us->read_io_buf_ = NULL; | |
715 } | |
716 | |
717 if (rv < 0) | |
718 return OSStatusFromNetError(rv); | |
719 | |
720 return noErr; | |
721 } | |
722 | |
723 // static | |
724 OSStatus SSLClientSocketMac::SSLWriteCallback(SSLConnectionRef connection, | |
725 const void* data, | |
726 size_t* data_length) { | |
727 SSLClientSocketMac* us = | |
728 const_cast<SSLClientSocketMac*>( | |
729 static_cast<const SSLClientSocketMac*>(connection)); | |
730 | |
731 if (us->pending_send_error_ != OK) { | |
732 OSStatus status = OSStatusFromNetError(us->pending_send_error_); | |
733 us->pending_send_error_ = OK; | |
734 return status; | |
735 } | |
736 | |
737 if (data) | |
738 us->send_buffer_.insert(us->send_buffer_.end(), | |
739 static_cast<const char*>(data), | |
740 static_cast<const char*>(data) + *data_length); | |
741 int rv; | |
742 do { | |
743 scoped_refptr<IOBuffer> buffer = new IOBuffer(us->send_buffer_.size()); | |
744 memcpy(buffer->data(), &us->send_buffer_[0], us->send_buffer_.size()); | |
745 rv = us->transport_->Write(buffer, | |
746 us->send_buffer_.size(), | |
747 &us->write_callback_); | |
748 if (rv > 0) { | |
749 us->send_buffer_.erase(us->send_buffer_.begin(), | |
750 us->send_buffer_.begin() + rv); | |
751 } | |
752 } while (rv > 0 && !us->send_buffer_.empty()); | |
753 | |
754 if (rv < 0 && rv != ERR_IO_PENDING) { | |
755 return OSStatusFromNetError(rv); | |
756 } | |
757 | |
758 // always lie to our caller | |
759 return noErr; | |
760 } | |
761 | |
762 } // namespace net | |
OLD | NEW |