diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/curl_schannel.c | 79 |
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; } |