Infrastructure for buffering & resending flights
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index 42b3473..1a7722c 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -243,6 +243,14 @@
#define SSL_SESSION_TICKETS_DISABLED 0
#define SSL_SESSION_TICKETS_ENABLED 1
+/*
+ * DTLS retransmission states, see RFC 6347 4.2.4
+ */
+#define SSL_RETRANS_PREPARING 0
+#define SSL_RETRANS_SENDING 1
+#define SSL_RETRANS_WAITING 2
+#define SSL_RETRANS_FINISHED 3
+
/**
* \name SECTION: Module settings
*
@@ -511,6 +519,9 @@
#if defined(POLARSSL_X509_CRT_PARSE_C)
typedef struct _ssl_key_cert ssl_key_cert;
#endif
+#if defined(POLARSSL_SSL_PROTO_DTLS)
+typedef struct _ssl_flight_item ssl_flight_item;
+#endif
/*
* This structure is used for storing current session data.
@@ -622,11 +633,17 @@
#if defined(POLARSSL_SSL_PROTO_DTLS)
unsigned int out_msg_seq; /*!< Outgoing handshake sequence number */
unsigned int in_msg_seq; /*!< Incoming handshake sequence number */
+
unsigned char *verify_cookie; /*!< Cli: HelloVerifyRequest cookie
Srv: unused */
unsigned char verify_cookie_len; /*!< Cli: cookie length
Srv: flag for sending a cookie */
+
unsigned char *hs_msg; /*!< Reassembled handshake message */
+
+ unsigned char retransmit_state; /*!< Retransmission state */
+ ssl_flight_item *flight; /*!< Current outgoing flight */
+ ssl_flight_item *cur_msg; /*!< Current message in flight */
#endif
/*
@@ -695,6 +712,18 @@
};
#endif /* POLARSSL_X509_CRT_PARSE_C */
+#if defined(POLARSSL_SSL_PROTO_DTLS)
+/*
+ * List of handshake messages kept around for resending
+ */
+struct _ssl_flight_item
+{
+ unsigned char *p; /*!< message, including handshake headers */
+ size_t len; /*!< length of hs_msg */
+ ssl_flight_item *next; /*!< next handshake message(s) */
+};
+#endif /* POLARSSL_SSL_PROTO_DTLS */
+
struct _ssl_context
{
/*
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 8ab2ee3..423bc0b 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -2013,9 +2013,117 @@
return( 0 );
}
+#if defined(POLARSSL_SSL_PROTO_DTLS)
+/*
+ * Append current handshake message to current outgoing flight
+ */
+static int ssl_flight_append( ssl_context *ssl )
+{
+ ssl_flight_item *msg;
+
+ /* Allocate space for current message */
+ if( ( msg = polarssl_malloc( sizeof( ssl_flight_item ) ) ) == NULL )
+ {
+ SSL_DEBUG_MSG( 1, ( "malloc %d bytes failed",
+ sizeof( ssl_flight_item ) ) );
+ return( POLARSSL_ERR_SSL_MALLOC_FAILED );
+ }
+
+ if( ( msg->p = polarssl_malloc( ssl->out_msglen ) ) == NULL )
+ {
+ SSL_DEBUG_MSG( 1, ( "malloc %d bytes failed", ssl->out_msglen ) );
+ return( POLARSSL_ERR_SSL_MALLOC_FAILED );
+ }
+
+ /* Copy current handshake message with headers */
+ memcpy( msg->p, ssl->out_msg, ssl->out_msglen );
+ msg->len = ssl->out_msglen;
+ msg->next = NULL;
+
+ /* Append to the current flight */
+ if( ssl->handshake->flight == NULL )
+ {
+ ssl->handshake->flight = msg;
+ ssl->handshake->cur_msg = msg;
+ }
+ else
+ {
+ ssl_flight_item *cur = ssl->handshake->flight;
+ while( cur->next != NULL )
+ cur = cur->next;
+ cur->next = msg;
+ }
+
+ return( 0 );
+}
+
+/*
+ * Free the current flight of handshake messages
+ */
+static void ssl_flight_free( ssl_flight_item *flight )
+{
+ ssl_flight_item *cur = flight;
+ ssl_flight_item *next;
+
+ while( cur != NULL )
+ {
+ next = cur->next;
+
+ polarssl_free( cur->p );
+ polarssl_free( cur );
+
+ cur = next;
+ }
+}
+
+/*
+ * Send current flight of messages.
+ *
+ * Need to remember the current message in case flush_output returns
+ * WANT_WRITE, causing us to exit this function and come back later.
+ */
+static int ssl_send_current_flight( ssl_context *ssl )
+{
+ ssl->handshake->retransmit_state = SSL_RETRANS_SENDING;
+
+ SSL_DEBUG_MSG( 2, ( "=> ssl_send_current_flight" ) );
+
+ while( ssl->handshake->cur_msg != NULL )
+ {
+ int ret;
+ ssl_flight_item *cur = ssl->handshake->cur_msg;
+
+ ssl->out_msglen = cur->len;
+ memcpy( ssl->out_msg, cur->p, ssl->out_msglen );
+ ssl->out_msgtype = SSL_MSG_HANDSHAKE;
+
+ ssl->handshake->cur_msg = cur->next;
+
+ SSL_DEBUG_BUF( 3, "resent handshake message header", ssl->out_msg, 12 );
+
+ if( ( ret = ssl_write_record( ssl ) ) != 0 )
+ {
+ SSL_DEBUG_RET( 1, "ssl_write_record", ret );
+ return( ret );
+ }
+ }
+
+ ssl->handshake->retransmit_state = SSL_RETRANS_WAITING;
+
+ SSL_DEBUG_MSG( 2, ( "<= ssl_send_current_flight" ) );
+
+ return( 0 );
+}
+#endif /* POLARSSL_SSL_PROTO_DTLS */
+
/*
* Record layer functions
*/
+
+/*
+ * Write current record.
+ * Uses ssl->out_msgtype, ssl->out_msglen and bytes at ssl->out_msg.
+ */
int ssl_write_record( ssl_context *ssl )
{
int ret, done = 0;
@@ -2023,6 +2131,15 @@
SSL_DEBUG_MSG( 2, ( "=> write record" ) );
+#if defined(POLARSSL_SSL_PROTO_DTLS)
+ if( ssl->transport == SSL_TRANSPORT_DATAGRAM &&
+ ssl->handshake != NULL &&
+ ssl->handshake->retransmit_state == SSL_RETRANS_SENDING )
+ {
+ ; /* Skip special handshake treatment when resending */
+ }
+ else
+#endif
if( ssl->out_msgtype == SSL_MSG_HANDSHAKE )
{
ssl->out_msg[1] = (unsigned char)( ( len - 4 ) >> 16 );
@@ -2067,6 +2184,22 @@
ssl->handshake->update_checksum( ssl, ssl->out_msg, len );
}
+ /* Save handshake and CCS messages for resending */
+#if defined(POLARSSL_SSL_PROTO_DTLS)
+ if( ssl->transport == SSL_TRANSPORT_DATAGRAM &&
+ ssl->handshake != NULL &&
+ ssl->handshake->retransmit_state == SSL_RETRANS_PREPARING &&
+ ( ssl->out_msgtype == SSL_MSG_CHANGE_CIPHER_SPEC ||
+ ssl->out_msgtype == SSL_MSG_HANDSHAKE ) )
+ {
+ if( ( ret = ssl_flight_append( ssl ) ) != 0 )
+ {
+ SSL_DEBUG_RET( 1, "ssl_flight_append", ret );
+ return( ret );
+ }
+ }
+#endif
+
#if defined(POLARSSL_ZLIB_SUPPORT)
if( ssl->transform_out != NULL &&
ssl->session_out->compression == SSL_COMPRESS_DEFLATE )
@@ -5372,6 +5505,7 @@
#if defined(POLARSSL_SSL_PROTO_DTLS)
polarssl_free( handshake->verify_cookie );
polarssl_free( handshake->hs_msg );
+ ssl_flight_free( handshake->flight );
#endif
polarssl_zeroize( handshake, sizeof( ssl_handshake_params ) );