aboutsummaryrefslogtreecommitdiff
path: root/lib/curl_schannel.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/curl_schannel.c')
-rw-r--r--lib/curl_schannel.c79
1 files changed, 72 insertions, 7 deletions
diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index ba010171e..8b2f5ea72 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -40,7 +40,6 @@
/*
* TODO list for TLS/SSL implementation:
- * - implement write buffering
* - implement client certificate authentication
* - implement custom server certificate validation
* - implement cipher/algorithm option
@@ -681,14 +680,75 @@ schannel_send(struct connectdata *conn, int sockindex,
/* check if the message was encrypted */
if(sspi_status == SEC_E_OK) {
+ written = 0;
+
/* send the encrypted message including header, data and trailer */
len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
- code = Curl_write_plain(conn, conn->sock[sockindex], data, len, &written);
- if((code != CURLE_OK) || (len != (size_t)written))
- *err = CURLE_SEND_ERROR;
- if(code != CURLE_OK)
- written = -1;
- /* TODO: implement write buffering */
+
+ /*
+ It's important to send the full message which includes the header,
+ encrypted payload, and trailer. Until the client receives all the
+ data a coherent message has not been delivered and the client
+ can't read any of it.
+
+ If we wanted to buffer the unwritten encrypted bytes, we would
+ tell the client that all data it has requested to be sent has been
+ sent. The unwritten encrypted bytes would be the first bytes to
+ send on the next invocation.
+ Here's the catch with this - if we tell the client that all the
+ bytes have been sent, will the client call this method again to
+ send the buffered data? Looking at who calls this function, it
+ seems the answer is NO.
+ */
+
+ /* send entire message or fail */
+ while(len > (size_t)written) {
+ ssize_t this_write;
+ long timeleft;
+ int what;
+
+ this_write = 0;
+
+ timeleft = Curl_timeleft(conn->data, NULL, TRUE);
+ if(timeleft < 0) {
+ /* we already got the timeout */
+ failf(conn->data, "schannel: timed out sending data "
+ "(bytes sent: %zd)", written);
+ *err = CURLE_OPERATION_TIMEDOUT;
+ written = -1;
+ break;
+ }
+
+ what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex],
+ timeleft);
+ if(what < 0) {
+ /* fatal error */
+ failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ *err = CURLE_SEND_ERROR;
+ written = -1;
+ break;
+ }
+ else if(0 == what) {
+ failf(conn->data, "schannel: timed out sending data "
+ "(bytes sent: %zd)", written);
+ *err = CURLE_OPERATION_TIMEDOUT;
+ written = -1;
+ break;
+ }
+ /* socket is writable */
+
+ code = Curl_write_plain(conn, conn->sock[sockindex], data + written,
+ len - written, &this_write);
+ if(code == CURLE_AGAIN)
+ continue;
+ else if(code != CURLE_OK) {
+ *err = code;
+ written = -1;
+ break;
+ }
+
+ written += this_write;
+ }
}
else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
*err = CURLE_OUT_OF_MEMORY;
@@ -699,6 +759,11 @@ schannel_send(struct connectdata *conn, int sockindex,
free(data);
+ if(len == (size_t)written)
+ /* Encrypted message including header, data and trailer entirely sent.
+ The return value is the number of unencrypted bytes that were sent. */
+ written = outbuf[1].cbBuffer;
+
return written;
}