Setup SSL record structure in ssl_parse_record_header()

This commit makes a first step towards modularizing the incoming record
processing by having it operate on instances of the structure mbedtls_record
representing SSL records.

So far, only record encryption/decryption operate in terms of record
instances, but the rest of the parsing doesn't. In particular,
ssl_parse_record_header() operates directly on the fixed input buffer,
setting the various ssl->in_xxx pointers and fields, and only directly
before/after calling ssl_decrypt_buf() these fields a converted to/from
mbedtls_record instances.

This commit does not yet remove the ssl->in_xxx fields, but makes a step
towards extending the lifetime of mbedtls_record structure representing
incoming records, by modifying ssl_parse_record_header() to setup an
instance of mbedtls_record, and setting the ssl->in_xxx fields from that
instance. The instance so-constructed isn't used further so far, and in
particular it is not yet consolidated with the instance set up for use
in ssl_decrypt_record(). That's for a later commit.
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 036fba7..0ae2f1b 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -4722,20 +4722,75 @@
  * Point 2 is needed when the peer is resending, and we have already received
  * the first record from a datagram but are still waiting for the others.
  */
-static int ssl_parse_record_header( mbedtls_ssl_context *ssl )
+static int ssl_parse_record_header( mbedtls_ssl_context *ssl,
+                                    unsigned char *buf,
+                                    size_t len,
+                                    mbedtls_record *rec )
 {
     int major_ver, minor_ver;
 
-    /* Parse and validate record content type and version */
+    size_t const rec_hdr_type_offset    = 0;
+    size_t const rec_hdr_type_len       = 1;
 
-    ssl->in_msgtype =  ssl->in_hdr[0];
-    mbedtls_ssl_read_version( &major_ver, &minor_ver, ssl->conf->transport, ssl->in_hdr + 1 );
+    size_t const rec_hdr_version_offset = rec_hdr_type_offset +
+                                          rec_hdr_type_len;
+    size_t const rec_hdr_version_len    = 2;
 
-    /* Check record type */
+    size_t const rec_hdr_ctr_len        = 8;
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+    uint32_t rec_epoch;
+    size_t const rec_hdr_ctr_offset     = rec_hdr_version_offset +
+                                          rec_hdr_version_len;
+
 #if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
+    size_t const rec_hdr_cid_offset     = rec_hdr_ctr_offset +
+                                          rec_hdr_ctr_len;
+    size_t rec_hdr_cid_len              = 0;
+#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */
+#endif /* MBEDTLS_SSL_PROTO_DTLS */
+
+    size_t       rec_hdr_len_offset; /* To be determined */
+    size_t const rec_hdr_len_len    = 2;
+
+    /*
+     * Check minimum lengths for record header.
+     */
+
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) )
+    {
+        rec_hdr_len_offset = rec_hdr_ctr_offset + rec_hdr_ctr_len;
+    }
+    MBEDTLS_SSL_TRANSPORT_ELSE
+#endif /* MBEDTLS_SSL_PROTO_DTLS */
+#if defined(MBEDTLS_SSL_PROTO_TLS)
+    {
+        rec_hdr_len_offset = rec_hdr_version_offset + rec_hdr_version_len;
+    }
+#endif /* MBEDTLS_SSL_PROTO_DTLS */
+
+    if( len < rec_hdr_len_offset + rec_hdr_len_len )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 1, ( "datagram of length %u too small to hold DTLS record header of length %u",
+                 (unsigned) len,
+                 (unsigned)( rec_hdr_len_len + rec_hdr_len_len ) ) );
+        return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+    }
+
+    /*
+     * Parse and validate record content type
+     */
+
+    rec->type = buf[ rec_hdr_type_offset ];
+    ssl->in_msgtype = rec->type;
+
+    /* Check record content type */
+#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
+    rec->cid_len = 0;
+
     if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) &&
-        ssl->in_msgtype      == MBEDTLS_SSL_MSG_CID            &&
-        mbedtls_ssl_conf_get_cid_len( ssl->conf ) != 0 )
+        mbedtls_ssl_conf_get_cid_len( ssl->conf ) != 0        &&
+        rec->type == MBEDTLS_SSL_MSG_CID )
     {
         /* Shift pointers to account for record header including CID
          * struct {
@@ -4752,30 +4807,45 @@
 
         /* So far, we only support static CID lengths
          * fixed in the configuration. */
-        ssl->in_len = ssl->in_cid + mbedtls_ssl_conf_get_cid_len( ssl->conf );
-        ssl->in_iv  = ssl->in_msg = ssl->in_len + 2;
+        rec_hdr_cid_len = mbedtls_ssl_conf_get_cid_len( ssl->conf );
+        rec_hdr_len_offset += rec_hdr_cid_len;
 
-        /* Now that the total length of the record header is known, ensure
-         * that the current datagram is large enough to hold it.
-         * This would fail, for example, if we received a datagram of
-         * size 13 + n Bytes where n is less than the size of incoming CIDs.
-         */
-        if( ssl->in_left < mbedtls_ssl_in_hdr_len( ssl ) )
+        if( len < rec_hdr_len_offset + rec_hdr_len_len )
         {
-            MBEDTLS_SSL_DEBUG_MSG( 1, ( "datagram too short to contain DTLS record header including CID of length %u.",
-                                        (unsigned) mbedtls_ssl_conf_get_cid_len( ssl->conf ) ) );
+            MBEDTLS_SSL_DEBUG_MSG( 1, ( "datagram of length %u too small to hold DTLS record header including CID, length %u",
+                (unsigned) len,
+                (unsigned)( rec_hdr_len_offset + rec_hdr_len_len ) ) );
             return( MBEDTLS_ERR_SSL_INVALID_RECORD );
         }
+
+        rec->cid_len = rec_hdr_cid_len;
+        memcpy( rec->cid, buf + rec_hdr_cid_offset, rec_hdr_cid_len );
+
+        ssl->in_len = ssl->in_cid + mbedtls_ssl_conf_get_cid_len( ssl->conf );
+        ssl->in_iv  = ssl->in_msg = ssl->in_len + 2;
     }
     else
 #endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */
-    if( ssl_check_record_type( ssl->in_msgtype ) )
     {
-        MBEDTLS_SSL_DEBUG_MSG( 1, ( "unknown record type" ) );
-        return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+        if( ssl_check_record_type( rec->type ) )
+        {
+            MBEDTLS_SSL_DEBUG_MSG( 1, ( "unknown record type" ) );
+            return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+        }
     }
 
-    /* Check version */
+    /*
+     * Parse and validate record version
+     */
+
+    memcpy( &rec->ver[0],
+            buf + rec_hdr_version_offset,
+            rec_hdr_version_len );
+
+    mbedtls_ssl_read_version( &major_ver, &minor_ver,
+                              ssl->conf->transport,
+                              buf + rec_hdr_version_offset );
+
     if( major_ver != mbedtls_ssl_get_major_ver( ssl ) )
     {
         MBEDTLS_SSL_DEBUG_MSG( 1, ( "major version mismatch" ) );
@@ -4788,18 +4858,46 @@
         return( MBEDTLS_ERR_SSL_INVALID_RECORD );
     }
 
-    MBEDTLS_SSL_DEBUG_BUF( 4, "input record header", ssl->in_hdr, mbedtls_ssl_in_hdr_len( ssl ) );
+    /*
+     * Parse/Copy record sequence number.
+     */
 
-    /* Parse and validate record length
-     * This must happen after the CID parsing because
-     * its position in the record header depends on
-     * the presence of a CID. */
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) )
+    {
+        /* Copy explicit record sequence number from input buffer. */
+        memcpy( &rec->ctr[0], buf + rec_hdr_ctr_offset,
+                rec_hdr_ctr_len );
+    }
+    MBEDTLS_SSL_TRANSPORT_ELSE
+#endif /* MBEDTLS_SSL_PROTO_DTLS */
+#if defined(MBEDTLS_SSL_PROTO_TLS)
+    {
+        /* Copy implicit record sequence number from SSL context structure. */
+        memcpy( &rec->ctr[0], ssl->in_ctr, rec_hdr_ctr_len );
+    }
+#endif /* MBEDTLS_SSL_PROTO_TLS */
 
-    ssl->in_msglen = ( ssl->in_len[0] << 8 ) | ssl->in_len[1];
+    /*
+     * Parse record length.
+     */
+
+#define READ_UINT16_BE( p )                      \
+    ( ( *( (unsigned char*)( p ) + 0 ) << 8 ) |  \
+      ( *( (unsigned char*)( p ) + 1 ) << 0 ) )
+
+    rec->data_offset = rec_hdr_len_offset + rec_hdr_len_len;
+    rec->data_len    = (size_t) READ_UINT16_BE( buf + rec_hdr_len_offset );
+    MBEDTLS_SSL_DEBUG_BUF( 4, "input record header", buf, rec->data_offset );
+
+    ssl->in_msglen   = rec->data_len;
     MBEDTLS_SSL_DEBUG_MSG( 3, ( "input record: msgtype = %d, "
                                 "version = [%d:%d], msglen = %d",
-                                ssl->in_msgtype,
-                                major_ver, minor_ver, ssl->in_msglen ) );
+                                rec->type,
+                                major_ver, minor_ver, rec->data_len ) );
+
+    rec->buf     = buf;
+    rec->buf_len = rec->data_offset + rec->data_len;
 
     /*
      * DTLS-related tests.
@@ -4816,13 +4914,15 @@
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
     if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) )
     {
-        unsigned int rec_epoch = ( ssl->in_ctr[0] << 8 ) | ssl->in_ctr[1];
+        rec_epoch = ( rec->ctr[0] << 8 ) | rec->ctr[1];
 
         /* Check that the datagram is large enough to contain a record
          * of the advertised length. */
-        if( ssl->in_left < mbedtls_ssl_in_hdr_len( ssl ) + ssl->in_msglen )
+        if( len < rec->data_offset + rec->data_len )
         {
-            MBEDTLS_SSL_DEBUG_MSG( 1, ( "Datagram too small to contain record." ) );
+            MBEDTLS_SSL_DEBUG_MSG( 1, ( "Datagram of length %u too small to contain record of advertised length %u.",
+                             (unsigned) len,
+                             (unsigned)( rec->data_offset + rec->data_len ) ) );
             return( MBEDTLS_ERR_SSL_INVALID_RECORD );
         }
 
@@ -5736,6 +5836,7 @@
 static int ssl_get_next_record( mbedtls_ssl_context *ssl )
 {
     int ret;
+    mbedtls_record rec;
 
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
     /* We might have buffered a future record; if so,
@@ -5764,7 +5865,8 @@
         return( ret );
     }
 
-    if( ( ret = ssl_parse_record_header( ssl ) ) != 0 )
+    ret = ssl_parse_record_header( ssl, ssl->in_hdr, ssl->in_left, &rec );
+    if( ret != 0 )
     {
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
         if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) )