Shaw Graham George wrote:
The sequence of events goes like this:
1. The SSL handshake proceeds as normal.
2. The client puts an HTTP request
3. The server gets the HTTP request
4. The client then executes an (unexpected) SSL shutdown
5. The server puts the HTTP response
Here I might expect the put to fail, but all appears normal.
6. As an HTTP Keep-Alive request has been made, the server enters a
wait for the next read event.
7. The read event occurs (presumably due to the SSL shutdown), and now
the get fails.
SSL_get_error() returns SSL_ERROR_SYSCALL (I would expect
SSL_ERROR_ZERO_RETURN for an SSL shutdown).
ERR_get_error() returns 0.
(SSL_get_shutdown() & SSL_RECEIVED_SHUTDOWN) returns 0.
Is this the expected behaviour?
What I am looking for is a way of identifying the shutdown at the
server, so that I can close the connection softly.
I.e. you dont want your Keep-Alive's hanging around when the other end
has gone.
The client will take one of these actions:
* It will simply close the socket. This can be picked up by your
normal read() / write() failure scenarios.
* It will write out a "SSL shutdown notify" packet, and then close the
connection anyway. The server may or may not get the "SSL shutdown
notify packet" due to send buffering, socket linger options,
retransmission timeouts etc... But at server end will either receive
the "SSL shutdown notify" before the socket close or it will just see a
socket close. So again there is no special trickey here.
* It will write out a "SSL shutdown notify" packet and wait for the
acknowledgment from the server. This is 100% graceful and conceptually
correct way to finish things off. To handle this your SSL_read() will
signal a shutdown has been received (and from this point on no further
SSL data will be received on that session that just closed).
From seeing this you have a number of options at the server:
* ACK the shutdown and issue your own shutdown (but only after you
have finished writing all your application data with SSL_write()). A
HTTP client can often send the request and then shut the socket down,
the server can observe this right after it pulled the request off to
process it in another thread, this does not mean you want to shut the
socket down at your end until you have processed that request and
finished writing the Content body back the other way, only then should
you take action in response to seeing the client's "SSL shutdown notify".
* Once your "SSL shutdown notify" has hit the kernel write socket
buffer, issue a TCP level shutdown(fd, SHUTDOWN_SEND).
* Then wait to receive your shutdown ack or for the socket to
close/timeout. This wait does not have to be forever, I have an
implementation that will allow the client socket a configurable amount
of time to provide the "SSL shutdown notify ACK" back to me, if it does
not happen within that time I close the socket.
* Ultimately all this work leads to closing the socket.
If you implement everything above then your server is at least complying
with all the available mechanisms within SSL during the shutdown
sequence. I'm sure many implementations skip all the more complex
points and jump right to the "close the socket".
I don't fully see the problem with your code, nor the concern of having
a solution that works on Unix and Win32 because most people in the
situation you are in would put the socket into idle state but listening
for more data (an active Keep-Alive idle list).
You will always get an event from listening for more data that will
indicate the socket has been closed, or the socket has received a "SSL
shutdown notify" request. You won't miss it.
Here is a random fragment of my read code from Unix, it interests me if
the there is a difference for Win32 but I would expect SSL_read() to
return < 0, when you can do your "WSAGetLastError() returns
WSAECONNABORTED" to see why.
int n = SSL_read(client->ssl, buffer, sizeof(buffer));
if(n < 0) {
int err = SSL_get_error(client->ssl, n);
switch(err) {
case SSL_ERROR_WANT_READ:
... // put the 'fd' on the read interested list
break;
case SSL_ERROR_WANT_WRITE:
... // put the 'fd' on the write interested list
break;
case SSL_ERROR_ZERO_RETURN:
... // peer change their mind on connecting to us
// unrecoverable error so cleanup
break;
case SSL_ERROR_XXXX_WHATEVER:
... // unrecoverable error so cleanup
break;
default:
... // unrecoverable error so cleanup
// This is where I'd expect you to put your:
// int wsa_errno = WSAGetLastError()
// if you really want to know the exact cause of the error,
// but the corrective action by the application at this
// point is all the same (no matter what WSAGetLastError()
// actually is.
break;
}
} else if(n == 0) {
if((SSL_get_shutdown(client->ssl) & SSL_RECEIVED_SHUTDOWN) != 0) {
/* recv_shutdown_notify */
// Peer signalled he is not going to send us any more data
// so we can take action accodingly, this might mean we
// keep the socket open because we have not flushed all
// the data we wish to write out yet.
// If there is no more data for use to write out then we
// start a graceful shutdown by issuing a (or two):
// SSL_shutdown(client->ssl)
// If that packet hit the kernel buffer then we can also
// issue a TCP level shutdown:
// shutdown(SSL_get_fd(client->ssl), SHUTDOWN_SEND);
// Then ultimately we can setup to close the socket,
// possibly setting the SO_LINGER setsockopt on the way out.
// close(SSL_get_fd(client->ssl));
} else {
int err = SSL_get_error(client->ssl, n);
if(err == SSL_ERROR_ZERO_RETURN) {
/* transport_aborted */
// I often see this case when the client peer does a lazy/quick
// shutdown as opposed to a proper official SSL shutdown
} else {
/* rare error */
}
}
}
Darryl
______________________________________________________________________
OpenSSL Project http://www.openssl.org
User Support Mailing List openssl-users@openssl.org
Automated List Manager [EMAIL PROTECTED]