schannel SSL: Made send method handle unexpected cases better

Implemented timeout loop in schannel_send while sending data.  This
is as close as I think we can get to write buffering; I put a big
comment in to explain my thinking.

With some committer adjustments
This commit is contained in:
Mark Salisbury 2012-06-20 00:14:17 +02:00 committed by Yang Tse
parent 7d2abe27dd
commit a15378e073
1 changed files with 72 additions and 7 deletions

View File

@ -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;
}