Author: kgiusti
Date: Thu Sep 27 14:47:34 2012
New Revision: 1391036

URL: http://svn.apache.org/viewvc?rev=1391036&view=rev
Log:
PROTON-36: fix ssl early shutdown bug

Modified:
    qpid/proton/trunk/proton-c/src/ssl/openssl.c

Modified: qpid/proton/trunk/proton-c/src/ssl/openssl.c
URL: 
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/src/ssl/openssl.c?rev=1391036&r1=1391035&r2=1391036&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/src/ssl/openssl.c (original)
+++ qpid/proton/trunk/proton-c/src/ssl/openssl.c Thu Sep 27 14:47:34 2012
@@ -60,14 +60,14 @@ struct pn_ssl_t {
   BIO *bio_ssl;         // i/o from/to SSL socket layer
   BIO *bio_ssl_io;      // SSL "half" of network-facing BIO
   BIO *bio_net_io;      // socket-side "half" of network-facing BIO
-  bool read_stalled;  // SSL has data to read, but client buffer is full.
   bool ssl_shutdown;    // BIO_ssl_shutdown() called on socket.
-  ssize_t app_closed;   // error code returned by upper layer on close
-  ssize_t ssl_closed;   // SSL socket has closed (use upper layer code)
+  bool ssl_closed;      // shutdown complete, or SSL error
+  ssize_t app_input_closed;   // error code returned by upper layer process 
input
+  ssize_t app_output_closed;  // error code returned by upper layer process 
output
 
   bool read_blocked;    // SSL blocked until more network data is read
   bool write_blocked;   // SSL blocked until data is written to network
-  
+
   // buffers for holding I/O from "applications" above SSL
 #define APP_BUF_SIZE    (4*1024)
   char outbuf[APP_BUF_SIZE];
@@ -117,14 +117,24 @@ static void _log(pn_ssl_t *ssl, const ch
 
 static void _log_ssl_error(pn_ssl_t *ssl)
 {
+  char buf[128];        // see "man ERR_error_string_n()"
   unsigned long err = ERR_get_error();
   while (err) {
-    char *msg = ERR_error_string(err, NULL);
-    _log(ssl, "%s\n", msg);
+    ERR_error_string_n(err, buf, sizeof(buf));
+    _log(ssl, "%s\n", buf);
     err = ERR_get_error();
   }
 }
 
+static void _log_clear_data(pn_ssl_t *ssl, const char *data, size_t len)
+{
+  if (PN_TRACE_RAW & ssl->trace) {
+    fprintf(stderr, "SSL decrypted data: \"");
+    pn_fprint_data( stderr, data, len );
+    fprintf(stderr, "\"\n");
+  }
+}
+
 // @todo replace with a "reasonable" default (?), allow application to 
register its own
 // callback.
 #if 0
@@ -490,10 +500,7 @@ static ssize_t process_input_ssl( pn_tra
   if (!ssl) return PN_ERR;
   if (ssl->ssl == NULL && init_ssl_socket(ssl)) return PN_ERR;
 
-  /* if (ssl->write_blocked) { */
-  /*   _log(ssl, "SSL write_blocked, ignoring input data\n" ); */
-  /*   return 0; */
-  /* } */
+  _log( ssl, "process_input_ssl( data size=%d )\n",available );
 
   ssize_t consumed = 0;
 
@@ -510,7 +517,7 @@ static ssize_t process_input_ssl( pn_tra
     }
   }
 
-  // process any work available at the SSL socket
+  // Read all available data from the SSL socket
 
   if (!ssl->ssl_closed) {
     int pending = BIO_pending(ssl->bio_ssl);
@@ -518,14 +525,15 @@ static ssize_t process_input_ssl( pn_tra
     while (available > 0) {
       int written = BIO_read( ssl->bio_ssl, &ssl->inbuf[ssl->in_count], 
available );
       if (written > 0) {
+        _log( ssl, "Read %d bytes from SSL socket for app\n", written );
+        _log_clear_data( ssl, &ssl->inbuf[ssl->in_count], written );
         ssl->in_count += written;
-        _log( ssl, "Read %d bytes from socket for app\n", written );
       } else {
         if (!BIO_should_retry(ssl->bio_ssl)) {
-          _log_ssl_error(ssl);
+          start_ssl_shutdown(ssl);      // KAG: not sure - this may be 
necessary
           _log(ssl, "Read from SSL socket failed - SSL connection closed!!\n");
-          ssl->ssl_closed = (ssl->app_closed) ? ssl->app_closed : PN_EOS;
-          start_ssl_shutdown(ssl);
+          _log_ssl_error(ssl);
+          ssl->ssl_closed = true;
         } else {
           if (BIO_should_write( ssl->bio_ssl )) {
             ssl->write_blocked = true;
@@ -545,9 +553,7 @@ static ssize_t process_input_ssl( pn_tra
 
   // write incoming data to app layer
 
-  if (ssl->app_closed) {
-    ssl->in_count = 0;        // cannot accept more input, drop it
-  } else {
+  if (!ssl->app_input_closed) {
     char *data = ssl->inbuf;
     while (ssl->in_count > 0) {
       ssize_t consumed = transport->process_input(transport, data, 
ssl->in_count);
@@ -557,8 +563,15 @@ static ssize_t process_input_ssl( pn_tra
         _log( ssl, "Application consumed %d bytes from peer\n", (int) consumed 
);
       } else {
         if (consumed < 0) {
-          _log(ssl, "Application layer closed: %d (in_count=%d)\n", (int) 
consumed, (int)ssl->in_count);
-          ssl->app_closed = consumed;
+          _log(ssl, "Application layer closed its input: %d (discarding %d 
bytes)\n",
+               (int) consumed, (int)ssl->in_count);
+          ssl->in_count = 0;    // discard any pending input
+          ssl->app_input_closed = consumed;
+          if (ssl->app_output_closed && ssl->out_count) {
+            // both sides of app closed, and last bit of app output written to 
socket:
+            start_ssl_shutdown(ssl);
+          }
+          /* @todo: fix this - duplicate code - transport does the same */
           if (consumed == PN_EOS) {
             if (transport->disp->trace & (PN_TRACE_RAW | PN_TRACE_FRM))
               pn_dispatcher_trace(transport->disp, 0, "<- EOS\n");
@@ -571,13 +584,15 @@ static ssize_t process_input_ssl( pn_tra
         break;
       }
     }
-    if (!ssl->app_closed && ssl->in_count > 0 && data != ssl->inbuf)
+    if (ssl->in_count > 0 && data != ssl->inbuf)
       memmove( ssl->inbuf, data, ssl->in_count );
   }
 
-  //if (consumed == 0 && ssl->ssl_closed) {
-  if (ssl->ssl_closed) {
-    consumed = ssl->app_closed;
+  // tell transport our input side is closed if the SSL socket cannot be read 
from any
+  // longer, AND any pending input has been written up to the application (or 
the
+  // application is closed)
+  if (ssl->ssl_closed && ssl->in_count == 0) {
+    consumed = ssl->app_input_closed ? ssl->app_input_closed : PN_EOS;
   }
 
   _log(ssl, "process_input_ssl() returning %d\n", (int) consumed);
@@ -590,16 +605,11 @@ static ssize_t process_output_ssl( pn_tr
   if (!ssl) return PN_ERR;
   if (ssl->ssl == NULL && init_ssl_socket(ssl)) return PN_ERR;
 
-  /* if (ssl->read_blocked) { */
-  /*   _log(ssl, "SSL read_blocked, skipping output data\n" ); */
-  /*   return 0; */
-  /* } */
-
   ssize_t written = 0;
 
   // first, get any pending application output, if possible
 
-  if (!ssl->app_closed) {
+  if (!ssl->app_output_closed) {
     while (ssl->out_count < APP_BUF_SIZE) {
       ssize_t app_bytes = transport->process_output(transport, 
&ssl->outbuf[ssl->out_count], APP_BUF_SIZE - ssl->out_count);
       if (app_bytes > 0) {
@@ -607,8 +617,9 @@ static ssize_t process_output_ssl( pn_tr
         _log( ssl, "Gathered %d bytes from app to send to peer\n", app_bytes );
       } else {
         if (app_bytes < 0) {
-          _log(ssl, "Application layer closed: %d (out_count=%d)\n", (int) 
app_bytes, (int) ssl->out_count);
-          ssl->app_closed = app_bytes;
+          _log(ssl, "Application layer closed its output size: %d (%d bytes 
pending send)\n",
+               (int) app_bytes, (int) ssl->out_count);
+          ssl->app_output_closed = app_bytes;
           if (app_bytes == PN_EOS) {
             if (transport->disp->trace & (PN_TRACE_RAW | PN_TRACE_FRM))
               pn_dispatcher_trace(transport->disp, 0, "-> EOS\n");
@@ -625,9 +636,7 @@ static ssize_t process_output_ssl( pn_tr
 
   // now push any pending app data into the socket
 
-  if (ssl->ssl_closed) {
-    ssl->out_count = 0;       // cannot write to socket, so erase app output 
data
-  } else {
+  if (!ssl->ssl_closed) {
     char *data = ssl->outbuf;
     while (ssl->out_count > 0) {
       int written = BIO_write( ssl->bio_ssl, data, ssl->out_count );
@@ -637,10 +646,11 @@ static ssize_t process_output_ssl( pn_tr
         _log( ssl, "Wrote %d bytes from app to socket\n", written );
       } else {
         if (!BIO_should_retry(ssl->bio_ssl)) {
-          _log_ssl_error(ssl);
+          start_ssl_shutdown(ssl);      // KAG: not sure - this may be 
necessary
           _log(ssl, "Write to SSL socket failed - SSL connection closed!!\n");
-          ssl->ssl_closed = (ssl->app_closed) ? ssl->app_closed : PN_EOS;
-          start_ssl_shutdown(ssl);
+          _log_ssl_error(ssl);
+          ssl->out_count = 0;       // can no longer write to socket, so erase 
app output data
+          ssl->ssl_closed = true;
         } else {
           if (BIO_should_read( ssl->bio_ssl )) {
             ssl->read_blocked = true;
@@ -656,7 +666,11 @@ static ssize_t process_output_ssl( pn_tr
     }
 
     if (ssl->out_count == 0) {
-      if (ssl->app_closed) start_ssl_shutdown(ssl);
+      if (ssl->app_input_closed && ssl->app_output_closed) {
+        // application is done sending/receiving data, and all buffered output 
data has
+        // been written to the SSL socket
+        start_ssl_shutdown(ssl);
+      }
     } else if (data != ssl->outbuf) {
       memmove( ssl->outbuf, data, ssl->out_count );
     }
@@ -674,11 +688,10 @@ static ssize_t process_output_ssl( pn_tr
     }
   }
 
-  // once the app closes, and we drain any data it has written, we can 
shutdown the SSL
-  // connection cleanly.
-
+  // Once no more data is available "below" the SSL socket, tell the transport 
we are
+  // done.
   if (written == 0 && ssl->ssl_closed && BIO_pending(ssl->bio_net_io) == 0) {
-    written = ssl->ssl_closed;
+    written = ssl->app_output_closed ? ssl->app_output_closed : PN_EOS;
   }
   _log(ssl, "process_output_ssl() returning %d\n", (int) written);
   return written;



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to