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]

Reply via email to