aboutsummaryrefslogtreecommitdiff
path: root/lib/curl_darwinssl.c
diff options
context:
space:
mode:
authorNick Zitzmann <nickzman@gmail.com>2013-02-12 13:02:36 -0700
committerNick Zitzmann <nickzman@gmail.com>2013-02-12 13:02:36 -0700
commit66aa9bf52d3ea56d7c5dea8c4c6a2e27f0c8db21 (patch)
tree2597eeb0a4cec42608d476190d54bfd32775f395 /lib/curl_darwinssl.c
parentbd93062ee52edec4869416ab8306032658010900 (diff)
darwinssl: Fix send glitchiness with data > 32 or so KB
An ambiguity in the SSLWrite() documentation lead to a bad inference in the code where we assumed SSLWrite() returned the amount of bytes written to the socket, when that is not actually true; it returns the amount of data that is buffered for writing to the socket if it returns errSSLWouldBlock. Now darwinssl_send() returns CURLE_AGAIN if data is buffered but not written. Reference URL: http://curl.haxx.se/mail/lib-2013-02/0145.html
Diffstat (limited to 'lib/curl_darwinssl.c')
-rw-r--r--lib/curl_darwinssl.c65
1 files changed, 51 insertions, 14 deletions
diff --git a/lib/curl_darwinssl.c b/lib/curl_darwinssl.c
index d660deb2d..523370e6d 100644
--- a/lib/curl_darwinssl.c
+++ b/lib/curl_darwinssl.c
@@ -739,6 +739,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
return CURLE_OUT_OF_MEMORY;
}
#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */
+ connssl->ssl_write_buffered_length = 0UL; /* reset buffered write length */
/* check to see if we've been told to use an explicit SSL/TLS version */
#if defined(__MAC_10_8) || defined(__IPHONE_5_0)
@@ -1424,22 +1425,58 @@ static ssize_t darwinssl_send(struct connectdata *conn,
/*struct SessionHandle *data = conn->data;*/
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
size_t processed = 0UL;
- OSStatus err = SSLWrite(connssl->ssl_ctx, mem, len, &processed);
+ OSStatus err;
- if(err != noErr) {
+ /* The SSLWrite() function works a little differently than expected. The
+ fourth argument (processed) is currently documented in Apple's
+ documentation as: "On return, the length, in bytes, of the data actually
+ written."
+
+ Now, one could interpret that as "written to the socket," but actually,
+ it returns the amount of data that was written to a buffer internal to
+ the SSLContextRef instead. So it's possible for SSLWrite() to return
+ errSSLWouldBlock and a number of bytes "written" because those bytes were
+ encrypted and written to a buffer, not to the socket.
+
+ So if this happens, then we need to keep calling SSLWrite() over and
+ over again with no new data until it quits returning errSSLWouldBlock. */
+
+ /* Do we have buffered data to write from the last time we were called? */
+ if(connssl->ssl_write_buffered_length) {
+ /* Write the buffered data: */
+ err = SSLWrite(connssl->ssl_ctx, NULL, 0UL, &processed);
switch (err) {
- case errSSLWouldBlock: /* return how much we sent (if anything) */
- if(processed)
- return (ssize_t)processed;
- *curlcode = CURLE_AGAIN;
- return -1;
+ case noErr:
+ /* processed is always going to be 0 because we didn't write to
+ the buffer, so return how much was written to the socket */
+ processed = connssl->ssl_write_buffered_length;
+ connssl->ssl_write_buffered_length = 0UL;
break;
-
+ case errSSLWouldBlock: /* argh, try again */
+ *curlcode = CURLE_AGAIN;
+ return -1L;
default:
- failf(conn->data, "SSLWrite() return error %d", err);
+ failf(conn->data, "SSLWrite() returned error %d", err);
*curlcode = CURLE_SEND_ERROR;
- return -1;
- break;
+ return -1L;
+ }
+ }
+ else {
+ /* We've got new data to write: */
+ err = SSLWrite(connssl->ssl_ctx, mem, len, &processed);
+ if(err != noErr) {
+ switch (err) {
+ case errSSLWouldBlock:
+ /* Data was buffered but not sent, we have to tell the caller
+ to try sending again, and remember how much was buffered */
+ connssl->ssl_write_buffered_length = len;
+ *curlcode = CURLE_AGAIN;
+ return -1L;
+ default:
+ failf(conn->data, "SSLWrite() returned error %d", err);
+ *curlcode = CURLE_SEND_ERROR;
+ return -1L;
+ }
}
}
return (ssize_t)processed;
@@ -1462,18 +1499,18 @@ static ssize_t darwinssl_recv(struct connectdata *conn,
if(processed)
return (ssize_t)processed;
*curlcode = CURLE_AGAIN;
- return -1;
+ return -1L;
break;
case errSSLClosedGraceful: /* they're done; fail gracefully */
*curlcode = CURLE_OK;
- return -1;
+ return -1L;
break;
default:
failf(conn->data, "SSLRead() return error %d", err);
*curlcode = CURLE_RECV_ERROR;
- return -1;
+ return -1L;
break;
}
}