Add "exchanges" option to test server and client

Goal is to test renegotiation better: we need more than one exchange for
server-initiated renego to work reliably (the previous hack for this wouldn't
work with non-blocking I/O and probably not with DTLS either).

Also check message termination in a semi-realistic way.
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index dda4263..f235b4c 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -85,6 +85,7 @@
 #define DFL_RENEGOTIATION       SSL_RENEGOTIATION_DISABLED
 #define DFL_ALLOW_LEGACY        SSL_LEGACY_NO_RENEGOTIATION
 #define DFL_RENEGOTIATE         0
+#define DFL_EXCHANGES           1
 #define DFL_MIN_VERSION         -1
 #define DFL_MAX_VERSION         -1
 #define DFL_AUTH_MODE           SSL_VERIFY_REQUIRED
@@ -120,6 +121,8 @@
     int renegotiation;          /* enable / disable renegotiation           */
     int allow_legacy;           /* allow legacy renegotiation               */
     int renegotiate;            /* attempt renegotiation?                   */
+    int renego_delay;           /* delay before enforcing renegotiation     */
+    int exchanges;              /* number of data exchanges                 */
     int min_version;            /* minimum protocol version accepted        */
     int max_version;            /* maximum protocol version accepted        */
     int auth_mode;              /* verify mode for connection               */
@@ -303,6 +306,7 @@
     "    renegotiation=%%d    default: 1 (enabled)\n"       \
     "    allow_legacy=%%d     default: 0 (disabled)\n"      \
     "    renegotiate=%%d      default: 0 (disabled)\n"      \
+    "    exchanges=%%d        default: 1\n"                 \
     "    reconnect=%%d        default: 0 (disabled)\n"      \
     USAGE_TIME                                              \
     USAGE_TICKETS                                           \
@@ -399,6 +403,7 @@
     opt.renegotiation       = DFL_RENEGOTIATION;
     opt.allow_legacy        = DFL_ALLOW_LEGACY;
     opt.renegotiate         = DFL_RENEGOTIATE;
+    opt.exchanges           = DFL_EXCHANGES;
     opt.min_version         = DFL_MIN_VERSION;
     opt.max_version         = DFL_MAX_VERSION;
     opt.auth_mode           = DFL_AUTH_MODE;
@@ -486,6 +491,12 @@
             if( opt.renegotiate < 0 || opt.renegotiate > 1 )
                 goto usage;
         }
+        else if( strcmp( p, "exchanges" ) == 0 )
+        {
+            opt.exchanges = atoi( q );
+            if( opt.exchanges < 1 )
+                goto usage;
+        }
         else if( strcmp( p, "reconnect" ) == 0 )
         {
             opt.reconnect = atoi( q );
@@ -1136,11 +1147,25 @@
         len = ret;
         buf[len] = '\0';
         printf( " %d bytes read\n\n%s", len, (char *) buf );
+
+        /* End of message should be detected according to the syntax of the
+         * application protocol (eg HTTP), just use a dummy test here. */
+        if( ret > 0 && buf[len-1] == '\n' )
+        {
+            ret = 0;
+            break;
+        }
     }
     while( 1 );
 
     /*
-     * 9. Reconnect?
+     * 7b. Continue doing data exchanges?
+     */
+    if( --opt.exchanges > 0 )
+        goto send_request;
+
+    /*
+     * 7c. Reconnect?
      */
 reconnect:
     if( opt.reconnect != 0 )
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index ae512c0..721dab4 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -587,7 +587,7 @@
 
 int main( int argc, char *argv[] )
 {
-    int ret = 0, len, written, frags;
+    int ret = 0, len, written, frags, exchanges;
     int client_fd = -1;
     int version_suites[4][2];
     unsigned char buf[IO_BUF_LEN];
@@ -1502,6 +1502,8 @@
     }
 #endif /* POLARSSL_X509_CRT_PARSE_C */
 
+    exchanges = opt.exchanges;
+data_exchange:
     /*
      * 6. Read the HTTP Request
      */
@@ -1510,6 +1512,7 @@
 
     do
     {
+        int terminated = 0;
         len = sizeof( buf ) - 1;
         memset( buf, 0, sizeof( buf ) );
         ret = ssl_read( &ssl, buf, len );
@@ -1534,7 +1537,7 @@
 
                 default:
                     printf( " ssl_read returned -0x%x\n", -ret );
-                    goto close_notify;
+                    goto reset;
             }
         }
 
@@ -1543,6 +1546,11 @@
             len = ret;
             buf[len] = '\0';
             printf( " %d bytes read\n\n%s\n", len, (char *) buf );
+
+            /* End of message should be detected according to the syntax of the
+             * application protocol (eg HTTP), just use a dummy test here. */
+            if( buf[len - 1] == '\n' )
+                terminated = 1;
         }
         else
         {
@@ -1578,22 +1586,31 @@
                     ori_len + extra_len, ori_len, extra_len,
                     (char *) larger_buf );
 
+            /* End of message should be detected according to the syntax of the
+             * application protocol (eg HTTP), just use a dummy test here. */
+            if( larger_buf[ori_len + extra_len - 1] == '\n' )
+                terminated = 1;
+
             polarssl_free( larger_buf );
         }
 
-        if( ret > 0 )
+        if( terminated )
+        {
+            ret = 0;
             break;
+        }
     }
     while( 1 );
 
     /*
-     * 7. Write the 200 Response
+     * 7a. Request renegotiation while client is waiting for input from us.
+     * (only if we're going to exhange more data afterwards)
      */
-    if( opt.renegotiate )
+    if( opt.renegotiate && exchanges > 1 )
     {
-        /* Request renegotiation while the client is waiting for input */
         printf( "  . Requestion renegotiation..." );
         fflush( stdout );
+
         while( ( ret = ssl_renegotiate( &ssl ) ) != 0 )
         {
             if( ret != POLARSSL_ERR_NET_WANT_READ &&
@@ -1603,8 +1620,13 @@
                 goto reset;
             }
         }
+
+        printf( " ok\n" );
     }
 
+    /*
+     * 7. Write the 200 Response
+     */
     printf( "  > Write to client:" );
     fflush( stdout );
 
@@ -1632,45 +1654,24 @@
     buf[written] = '\0';
     printf( " %d bytes written in %d fragments\n\n%s\n", written, frags, (char *) buf );
 
-    if( opt.renegotiate )
-    {
-        /*
-         * Should be a while loop, not an if, but here we're not actually
-         * expecting data from the client, and since we're running tests
-         * locally, we can just hope the handshake will finish the during the
-         * first call.
-         */
-        if( ( ret = ssl_read( &ssl, buf, 0 ) ) != 0 )
-        {
-            if( ret != POLARSSL_ERR_NET_WANT_READ &&
-                ret != POLARSSL_ERR_NET_WANT_WRITE )
-            {
-                printf( " failed\n  ! ssl_read returned %d\n\n", ret );
-
-                /* Unexpected message probably means client didn't renegotiate
-                 * as requested */
-                if( ret == POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE )
-                    goto reset;
-                else
-                    goto exit;
-            }
-        }
-
-        printf( " ok\n" );
-    }
 
     /*
-     * 8. Close the connection cleanly
+     * 7b. Continue doing data exchanges?
+     */
+    if( --exchanges > 0 )
+        goto data_exchange;
+
+    /*
+     * 8. Done, cleanly close the connection
      */
 close_notify:
     printf( "  . Closing the connection..." );
 
     while( ( ret = ssl_close_notify( &ssl ) ) < 0 )
     {
-        printf( " ret = %d (-0x%04X)", ret, -ret );
         if( ret == POLARSSL_ERR_NET_CONN_RESET )
         {
-            printf( " connection was reset by peer\n" );
+            printf( " ok (already closed by peer)\n" );
             ret = 0;
             goto reset;
         }
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 63258cc..e05019f 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -564,8 +564,8 @@
 # Tests for renegotiation
 
 run_test    "Renegotiation #0 (none)" \
-            "$P_SRV debug_level=4" \
-            "$P_CLI debug_level=4" \
+            "$P_SRV debug_level=4 exchanges=2" \
+            "$P_CLI debug_level=4 exchanges=2" \
             0 \
             -C "client hello, adding renegotiation extension" \
             -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
@@ -577,8 +577,8 @@
             -S "write hello request"
 
 run_test    "Renegotiation #1 (enabled, client-initiated)" \
-            "$P_SRV debug_level=4 renegotiation=1" \
-            "$P_CLI debug_level=4 renegotiation=1 renegotiate=1" \
+            "$P_SRV debug_level=4 exchanges=2 renegotiation=1" \
+            "$P_CLI debug_level=4 exchanges=2 renegotiation=1 renegotiate=1" \
             0 \
             -c "client hello, adding renegotiation extension" \
             -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
@@ -590,8 +590,8 @@
             -S "write hello request"
 
 run_test    "Renegotiation #2 (enabled, server-initiated)" \
-            "$P_SRV debug_level=4 renegotiation=1 renegotiate=1" \
-            "$P_CLI debug_level=4 renegotiation=1" \
+            "$P_SRV debug_level=4 exchanges=2 renegotiation=1 renegotiate=1" \
+            "$P_CLI debug_level=4 exchanges=2 renegotiation=1" \
             0 \
             -c "client hello, adding renegotiation extension" \
             -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
@@ -603,8 +603,8 @@
             -s "write hello request"
 
 run_test    "Renegotiation #3 (enabled, double)" \
-            "$P_SRV debug_level=4 renegotiation=1 renegotiate=1" \
-            "$P_CLI debug_level=4 renegotiation=1 renegotiate=1" \
+            "$P_SRV debug_level=4 exchanges=2 renegotiation=1 renegotiate=1" \
+            "$P_CLI debug_level=4 exchanges=2 renegotiation=1 renegotiate=1" \
             0 \
             -c "client hello, adding renegotiation extension" \
             -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
@@ -616,8 +616,8 @@
             -s "write hello request"
 
 run_test    "Renegotiation #4 (client-initiated, server-rejected)" \
-            "$P_SRV debug_level=4 renegotiation=0" \
-            "$P_CLI debug_level=4 renegotiation=1 renegotiate=1" \
+            "$P_SRV debug_level=4 exchanges=2 renegotiation=0" \
+            "$P_CLI debug_level=4 exchanges=2 renegotiation=1 renegotiate=1" \
             1 \
             -c "client hello, adding renegotiation extension" \
             -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
@@ -631,8 +631,8 @@
             -c "failed"
 
 run_test    "Renegotiation #5 (server-initiated, client-rejected, default)" \
-            "$P_SRV debug_level=4 renegotiation=1 renegotiate=1" \
-            "$P_CLI debug_level=4 renegotiation=0" \
+            "$P_SRV debug_level=4 exchanges=2 renegotiation=1 renegotiate=1" \
+            "$P_CLI debug_level=4 exchanges=2 renegotiation=0" \
             0 \
             -C "client hello, adding renegotiation extension" \
             -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
@@ -646,9 +646,9 @@
             -S "failed"
 
 run_test    "Renegotiation #6 (server-initiated, client-rejected, not enforced)" \
-            "$P_SRV debug_level=4 renegotiation=1 renegotiate=1 \
+            "$P_SRV debug_level=4 exchanges=2 renegotiation=1 renegotiate=1 \
              renego_delay=-1" \
-            "$P_CLI debug_level=4 renegotiation=0" \
+            "$P_CLI debug_level=4 exchanges=2 renegotiation=0" \
             0 \
             -C "client hello, adding renegotiation extension" \
             -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
@@ -661,10 +661,11 @@
             -S "SSL - An unexpected message was received from our peer" \
             -S "failed"
 
-run_test    "Renegotiation #7 (server-initiated, client-rejected, delay 1)" \
-            "$P_SRV debug_level=4 renegotiation=1 renegotiate=1 \
-             renego_delay=1" \
-            "$P_CLI debug_level=4 renegotiation=0" \
+# delay 2 for 1 alert record + 1 application data record
+run_test    "Renegotiation #7 (server-initiated, client-rejected, delay 2)" \
+            "$P_SRV debug_level=4 exchanges=2 renegotiation=1 renegotiate=1 \
+             renego_delay=2" \
+            "$P_CLI debug_level=4 exchanges=2 renegotiation=0" \
             0 \
             -C "client hello, adding renegotiation extension" \
             -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
@@ -678,9 +679,9 @@
             -S "failed"
 
 run_test    "Renegotiation #8 (server-initiated, client-rejected, delay 0)" \
-            "$P_SRV debug_level=4 renegotiation=1 renegotiate=1 \
+            "$P_SRV debug_level=4 exchanges=2 renegotiation=1 renegotiate=1 \
              renego_delay=0" \
-            "$P_CLI debug_level=4 renegotiation=0" \
+            "$P_CLI debug_level=4 exchanges=2 renegotiation=0" \
             0 \
             -C "client hello, adding renegotiation extension" \
             -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
@@ -690,13 +691,12 @@
             -C "=> renegotiate" \
             -S "=> renegotiate" \
             -s "write hello request" \
-            -s "SSL - An unexpected message was received from our peer" \
-            -s "failed"
+            -s "SSL - An unexpected message was received from our peer"
 
 run_test    "Renegotiation #9 (server-initiated, client-accepted, delay 0)" \
-            "$P_SRV debug_level=4 renegotiation=1 renegotiate=1 \
+            "$P_SRV debug_level=4 exchanges=2 renegotiation=1 renegotiate=1 \
              renego_delay=0" \
-            "$P_CLI debug_level=4 renegotiation=1" \
+            "$P_CLI debug_level=4 exchanges=2 renegotiation=1" \
             0 \
             -c "client hello, adding renegotiation extension" \
             -s "received TLS_EMPTY_RENEGOTIATION_INFO" \