Merged renegotiation refactoring
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index 57379f2..e51e507 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -200,7 +200,9 @@
 #define SSL_VERIFY_REQUIRED             2
 
 #define SSL_INITIAL_HANDSHAKE           0
-#define SSL_RENEGOTIATION               1
+#define SSL_RENEGOTIATION               1   /* In progress */
+#define SSL_RENEGOTIATION_DONE          2   /* Done */
+#define SSL_RENEGOTIATION_PENDING       3   /* Requested (server only) */
 
 #define SSL_LEGACY_RENEGOTIATION        0
 #define SSL_SECURE_RENEGOTIATION        1
@@ -1421,7 +1423,10 @@
 int ssl_handshake_step( ssl_context *ssl );
 
 /**
- * \brief          Perform an SSL renegotiation on the running connection
+ * \brief          Initiate an SSL renegotiation on the running connection.
+ *                 Client: perform the renegotiation right now.
+ *                 Server: request renegotiation, which will be performed
+ *                 during the next call to ssl_read() if honored by client.
  *
  * \param ssl      SSL context
  *
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 3d9e5a3..055798f 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -1930,7 +1930,8 @@
         ssl->out_msg[2] = (unsigned char)( ( len - 4 ) >>  8 );
         ssl->out_msg[3] = (unsigned char)( ( len - 4 )       );
 
-        ssl->handshake->update_checksum( ssl, ssl->out_msg, len );
+        if( ssl->out_msg[0] != SSL_HS_HELLO_REQUEST )
+            ssl->handshake->update_checksum( ssl, ssl->out_msg, len );
     }
 
 #if defined(POLARSSL_ZLIB_SUPPORT)
@@ -3022,6 +3023,9 @@
     polarssl_free( ssl->handshake );
     ssl->handshake = NULL;
 
+    if( ssl->renegotiation == SSL_RENEGOTIATION )
+        ssl->renegotiation =  SSL_RENEGOTIATION_DONE;
+
     /*
      * Switch in our now active transform context
      */
@@ -3276,6 +3280,10 @@
     ecdh_init( &ssl->handshake->ecdh_ctx );
 #endif
 
+#if defined(POLARSSL_X509_CRT_PARSE_C)
+    ssl->handshake->key_cert = ssl->key_cert;
+#endif
+
     return( 0 );
 }
 
@@ -3950,10 +3958,6 @@
 
     SSL_DEBUG_MSG( 2, ( "=> handshake" ) );
 
-#if defined(POLARSSL_X509_CRT_PARSE_C)
-    ssl->handshake->key_cert = ssl->key_cert;
-#endif
-
     while( ssl->state != SSL_HANDSHAKE_OVER )
     {
         ret = ssl_handshake_step( ssl );
@@ -3967,24 +3971,55 @@
     return( ret );
 }
 
+#if defined(POLARSSL_SSL_SRV_C)
 /*
- * Renegotiate current connection
+ * Write HelloRequest to request renegotiation on server
  */
-int ssl_renegotiate( ssl_context *ssl )
+static int ssl_write_hello_request( ssl_context *ssl )
+{
+    int ret;
+
+    SSL_DEBUG_MSG( 2, ( "=> write hello request" ) );
+
+    ssl->out_msglen  = 4;
+    ssl->out_msgtype = SSL_MSG_HANDSHAKE;
+    ssl->out_msg[0]  = SSL_HS_HELLO_REQUEST;
+
+    if( ( ret = ssl_write_record( ssl ) ) != 0 )
+    {
+        SSL_DEBUG_RET( 1, "ssl_write_record", ret );
+        return( ret );
+    }
+
+    ssl->renegotiation = SSL_RENEGOTIATION_PENDING;
+
+    SSL_DEBUG_MSG( 2, ( "<= write hello request" ) );
+
+    return( 0 );
+}
+#endif /* POLARSSL_SSL_SRV_C */
+
+/*
+ * Actually renegotiate current connection, triggered by either:
+ * - calling ssl_renegotiate() on client,
+ * - receiving a HelloRequest on client during ssl_read(),
+ * - receiving any handshake message on server during ssl_read() after the
+ *   initial handshake is completed
+ * If the handshake doesn't complete due to waiting for I/O, it will continue
+ * during the next calls to ssl_renegotiate() or ssl_read() respectively.
+ */
+static int ssl_start_renegotiation( ssl_context *ssl )
 {
     int ret;
 
     SSL_DEBUG_MSG( 2, ( "=> renegotiate" ) );
 
-    if( ssl->state != SSL_HANDSHAKE_OVER )
-        return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+    if( ( ret = ssl_handshake_init( ssl ) ) != 0 )
+        return( ret );
 
     ssl->state = SSL_HELLO_REQUEST;
     ssl->renegotiation = SSL_RENEGOTIATION;
 
-    if( ( ret = ssl_handshake_init( ssl ) ) != 0 )
-        return( ret );
-
     if( ( ret = ssl_handshake( ssl ) ) != 0 )
     {
         SSL_DEBUG_RET( 1, "ssl_handshake", ret );
@@ -3997,6 +4032,54 @@
 }
 
 /*
+ * Renegotiate current connection on client,
+ * or request renegotiation on server
+ */
+int ssl_renegotiate( ssl_context *ssl )
+{
+    int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE;
+
+#if defined(POLARSSL_SSL_SRV_C)
+    /* On server, just send the request */
+    if( ssl->endpoint == SSL_IS_SERVER )
+    {
+        if( ssl->state != SSL_HANDSHAKE_OVER )
+            return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+        return( ssl_write_hello_request( ssl ) );
+    }
+#endif /* POLARSSL_SSL_SRV_C */
+
+#if defined(POLARSSL_SSL_CLI_C)
+    /*
+     * On client, either start the renegotiation process or,
+     * if already in progress, continue the handshake
+     */
+    if( ssl->renegotiation != SSL_RENEGOTIATION )
+    {
+        if( ssl->state != SSL_HANDSHAKE_OVER )
+            return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );
+
+        if( ( ret = ssl_start_renegotiation( ssl ) ) != 0 )
+        {
+            SSL_DEBUG_RET( 1, "ssl_start_renegotiation", ret );
+            return( ret );
+        }
+    }
+    else
+    {
+        if( ( ret = ssl_handshake( ssl ) ) != 0 )
+        {
+            SSL_DEBUG_RET( 1, "ssl_handshake", ret );
+            return( ret );
+        }
+    }
+#endif /* POLARSSL_SSL_CLI_C */
+
+    return( ret );
+}
+
+/*
  * Receive application data decrypted from the SSL layer
  */
 int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len )
@@ -4091,15 +4174,21 @@
             }
             else
             {
-                if( ( ret = ssl_renegotiate( ssl ) ) != 0 )
+                if( ( ret = ssl_start_renegotiation( ssl ) ) != 0 )
                 {
-                    SSL_DEBUG_RET( 1, "ssl_renegotiate", ret );
+                    SSL_DEBUG_RET( 1, "ssl_start_renegotiation", ret );
                     return( ret );
                 }
 
                 return( POLARSSL_ERR_NET_WANT_READ );
             }
         }
+        else if( ssl->renegotiation == SSL_RENEGOTIATION_PENDING )
+        {
+            SSL_DEBUG_MSG( 1, ( "renegotiation requested, "
+                                "but not honored by client" ) );
+            return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE );
+        }
         else if( ssl->in_msgtype != SSL_MSG_APPLICATION_DATA )
         {
             SSL_DEBUG_MSG( 1, ( "bad application data message" ) );
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 247abbe..e4a1426 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -71,6 +71,9 @@
  * longer paquets (for fragmentation purposes) */
 #define GET_REQUEST "GET %s HTTP/1.0\r\n" /* LONG_HEADER */ "\r\n"
 
+/* Uncomment to test client-initiated renegotiation */
+// #define TEST_RENEGO
+
 /*
  * global options
  */
@@ -792,6 +795,24 @@
     }
 #endif /* POLARSSL_X509_CRT_PARSE_C */
 
+#ifdef TEST_RENEGO
+    /*
+     * Perform renegotiation (this must be done when the server is waiting
+     * for input from our side).
+     */
+    printf( "  . Performing renegotiation..." );
+    fflush( stdout );
+    while( ( ret = ssl_renegotiate( &ssl ) ) != 0 )
+    {
+        if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE )
+        {
+            printf( " failed\n  ! ssl_renegotiate returned %d\n\n", ret );
+            goto exit;
+        }
+    }
+    printf( " ok\n" );
+#endif
+
     /*
      * 6. Write the GET request
      */
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 8e7ee0e..2a046a7 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -50,7 +50,6 @@
 #endif
 
 #define DFL_SERVER_PORT         4433
-#define DFL_REQUEST_PAGE        "/"
 #define DFL_DEBUG_LEVEL         0
 #define DFL_CA_FILE             ""
 #define DFL_CA_PATH             ""
@@ -84,6 +83,9 @@
     "<h2>PolarSSL Test Server</h2>\r\n" \
     "<p>Successful connection using: %s</p>\r\n" // LONG_RESPONSE
 
+/* Uncomment to test server-initiated renegotiation */
+// #define TEST_RENEGO
+
 /*
  * global options
  */
@@ -939,6 +941,44 @@
     buf[written] = '\0';
     printf( " %d bytes written in %d fragments\n\n%s\n", written, frags, (char *) buf );
 
+#ifdef TEST_RENEGO
+    /*
+     * Request renegotiation (this must be done when the client is still
+     * waiting for input from our side).
+     */
+    printf( "  . Requestion renegotiation..." );
+    fflush( stdout );
+    while( ( ret = ssl_renegotiate( &ssl ) ) != 0 )
+    {
+        if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE )
+        {
+            printf( " failed\n  ! ssl_renegotiate returned %d\n\n", ret );
+            goto exit;
+        }
+    }
+
+    /*
+     * 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 */
+            if( ret == POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE )
+                goto reset;
+            else
+                goto exit;
+        }
+    }
+
+    printf( " ok\n" );
+#endif
+
     ret = 0;
     goto reset;