Thanks for the response.

You'll have to bear with me, as I'm not really a low-level sockets
programmer.  But maybe you misunderstood the nature of my problem.

The problem is that, on Windows at least, my server doesn't appear to
get the "SSL shutdown notify" packet, for some reason.  So, if that is
to be expected, I'm looking for an alternative way of detecting the
closure.

I've now tested it on Linux, and the existing code works fine.  In other
words, when trying the SSL_Get:

1.  It fails
2.  SSL_get_error() returns SSL_ERROR_ZERO_RETURN
3.  (SSL_get_shutdown() & SSL_RECEIVED_SHUTDOWN) is true

On Windows this is not the case.

But I guess if this problem is restricted to Windows, then I can:

1.  Add the call to WSAGetLastError() just for that platform
2.  Use it to detect the socket closure and ...
3.  Softly close the server socket that way

Unless somebody has any better ideas ...

G.
 

-----Original Message-----
From: [EMAIL PROTECTED]
[mailto:[EMAIL PROTECTED] On Behalf Of Darryl Miles
Sent: 15 August 2007 15:40
To: openssl-users@openssl.org
Subject: Re: Problem handling unexpected SSL shutdown

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]
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    openssl-users@openssl.org
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to