Add ability to resend last flight
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 6c89fbe..fff5a1f 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -2297,7 +2297,7 @@
#if defined(POLARSSL_SSL_PROTO_DTLS)
if( ssl->transport == SSL_TRANSPORT_DATAGRAM &&
ssl->handshake != NULL &&
- ssl->handshake->retransmit_state == SSL_RETRANS_PREPARING &&
+ ssl->handshake->retransmit_state != SSL_RETRANS_SENDING &&
( ssl->out_msgtype == SSL_MSG_CHANGE_CIPHER_SPEC ||
ssl->out_msgtype == SSL_MSG_HANDSHAKE ) )
{
@@ -2873,6 +2873,8 @@
return( 0 );
}
+static void ssl_handshake_wrapup_free_hs_transform( ssl_context *ssl );
+
/*
* Read a record.
*
@@ -2986,6 +2988,46 @@
}
/*
+ * When we sent the last flight of the handshake, we MUST respond to a
+ * retransmit of the peer's previous flight with a retransmit. (In
+ * practice, only the Finished message will make it, other messages
+ * including CCS use the old transform so they're dropped as invalid.)
+ *
+ * If the record we received is not a handshake message, however, it
+ * means the peer received our last flight so we can clean up
+ * handshake info.
+ *
+ * This check needs to be done before prepare_handshake() due to an edge
+ * case: if the client immediately requests renegotiation, this
+ * finishes the current handshake first, avoiding the new ClientHello
+ * being mistaken for an ancient message in the current handshake.
+ */
+#if defined(POLARSSL_SSL_PROTO_DTLS)
+ if( ssl->transport == SSL_TRANSPORT_DATAGRAM &&
+ ssl->handshake != NULL &&
+ ssl->handshake->retransmit_state == SSL_RETRANS_FINISHED )
+ {
+ if( ssl->in_msgtype == SSL_MSG_HANDSHAKE &&
+ ssl->in_msg[0] == SSL_HS_FINISHED )
+ {
+ SSL_DEBUG_MSG( 2, ( "received retransmit of last flight" ) );
+
+ if( ( ret = ssl_resend( ssl ) ) != 0 )
+ {
+ SSL_DEBUG_RET( 1, "ssl_resend", ret );
+ return( ret );
+ }
+
+ return( POLARSSL_ERR_NET_WANT_READ );
+ }
+ else
+ {
+ ssl_handshake_wrapup_free_hs_transform( ssl );
+ }
+ }
+#endif
+
+ /*
* Handle particular types of records
*/
if( ssl->in_msgtype == SSL_MSG_HANDSHAKE )
@@ -3859,11 +3901,9 @@
#endif /* POLARSSL_SHA512_C */
#endif /* POLARSSL_SSL_PROTO_TLS1_2 */
-void ssl_handshake_wrapup( ssl_context *ssl )
+static void ssl_handshake_wrapup_free_hs_transform( ssl_context *ssl )
{
- int resume = ssl->handshake->resume;
-
- SSL_DEBUG_MSG( 3, ( "=> handshake wrapup" ) );
+ SSL_DEBUG_MSG( 3, ( "=> handshake wrapup: final free" ) );
/*
* Free our handshake params
@@ -3872,14 +3912,8 @@
polarssl_free( ssl->handshake );
ssl->handshake = NULL;
- if( ssl->renegotiation == SSL_RENEGOTIATION )
- {
- ssl->renegotiation = SSL_RENEGOTIATION_DONE;
- ssl->renego_records_seen = 0;
- }
-
/*
- * Switch in our now active transform context
+ * Free the previous transform and swith in the current one
*/
if( ssl->transform )
{
@@ -3889,6 +3923,24 @@
ssl->transform = ssl->transform_negotiate;
ssl->transform_negotiate = NULL;
+ SSL_DEBUG_MSG( 3, ( "<= handshake wrapup: final free" ) );
+}
+
+void ssl_handshake_wrapup( ssl_context *ssl )
+{
+ int resume = ssl->handshake->resume;
+
+ SSL_DEBUG_MSG( 3, ( "=> handshake wrapup" ) );
+
+ if( ssl->renegotiation == SSL_RENEGOTIATION )
+ {
+ ssl->renegotiation = SSL_RENEGOTIATION_DONE;
+ ssl->renego_records_seen = 0;
+ }
+
+ /*
+ * Free the previous session and switch in the current one
+ */
if( ssl->session )
{
ssl_session_free( ssl->session );
@@ -3908,6 +3960,18 @@
SSL_DEBUG_MSG( 1, ( "cache did not store session" ) );
}
+#if defined(POLARSSL_SSL_PROTO_DTLS)
+ if( ssl->transport == SSL_TRANSPORT_DATAGRAM &&
+ ssl->handshake->flight != NULL )
+ {
+ /* Keep last flight around in case we need to resend it:
+ * we need the handshake and transform structures for that */
+ SSL_DEBUG_MSG( 3, ( "skip freeing handshake and transform" ) );
+ }
+ else
+#endif
+ ssl_handshake_wrapup_free_hs_transform( ssl );
+
ssl->state++;
SSL_DEBUG_MSG( 3, ( "<= handshake wrapup" ) );
@@ -5313,6 +5377,19 @@
SSL_DEBUG_MSG( 2, ( "=> read" ) );
+#if defined(POLARSSL_SSL_PROTO_DTLS)
+ if( ssl->transport == SSL_TRANSPORT_DATAGRAM &&
+ ssl->handshake != NULL &&
+ ssl->handshake->retransmit_state == SSL_RETRANS_SENDING )
+ {
+ if( ( ret = ssl_flush_output( ssl ) ) != 0 )
+ return( ret );
+
+ if( ( ret = ssl_resend( ssl ) ) != 0 )
+ return( ret );
+ }
+#endif
+
if( ssl->state != SSL_HANDSHAKE_OVER )
{
ret = ssl_handshake( ssl );