Use the record layer msg_callback not the SSL object msg_callback
[openssl.git] / ssl / record / methods / tls_common.c
index 8b541061ba18f9fbf27f58d21e30eeca48cafa75..0b63f046a0cc1ab50460211bc184a7d1bed86760 100644 (file)
@@ -17,8 +17,6 @@
 #include "../record_local.h"
 #include "recmethod_local.h"
 
-# define SSL_AD_NO_ALERT    -1
-
 static void tls_int_free(OSSL_RECORD_LAYER *rl);
 
 void ossl_rlayer_fatal(OSSL_RECORD_LAYER *rl, int al, int reason,
@@ -86,16 +84,17 @@ char ssl3_cbc_record_digest_supported(const EVP_MD_CTX *ctx)
 }
 
 #ifndef OPENSSL_NO_COMP
-static int rlayer_allow_compression(OSSL_RECORD_LAYER *rl)
+static int tls_allow_compression(OSSL_RECORD_LAYER *rl)
 {
     if (rl->options & SSL_OP_NO_COMPRESSION)
         return 0;
 
-    return rl->security(rl->cbarg, SSL_SECOP_COMPRESSION, 0, 0, NULL);
+    return rl->security == NULL
+           || rl->security(rl->cbarg, SSL_SECOP_COMPRESSION, 0, 0, NULL);
 }
 #endif
 
-int rlayer_setup_read_buffer(OSSL_RECORD_LAYER *rl)
+int tls_setup_read_buffer(OSSL_RECORD_LAYER *rl)
 {
     unsigned char *p;
     size_t len, align = 0, headerlen;
@@ -108,7 +107,7 @@ int rlayer_setup_read_buffer(OSSL_RECORD_LAYER *rl)
     else
         headerlen = SSL3_RT_HEADER_LENGTH;
 
-#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
+#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0
     align = (-SSL3_RT_HEADER_LENGTH) & (SSL3_ALIGN_PAYLOAD - 1);
 #endif
 
@@ -116,7 +115,7 @@ int rlayer_setup_read_buffer(OSSL_RECORD_LAYER *rl)
         len = SSL3_RT_MAX_PLAIN_LENGTH
             + SSL3_RT_MAX_ENCRYPTED_OVERHEAD + headerlen + align;
 #ifndef OPENSSL_NO_COMP
-        if (rlayer_allow_compression(rl))
+        if (tls_allow_compression(rl))
             len += SSL3_RT_MAX_COMPRESSED_OVERHEAD;
 #endif
         if (b->default_len > len)
@@ -137,12 +136,12 @@ int rlayer_setup_read_buffer(OSSL_RECORD_LAYER *rl)
     return 1;
 }
 
-static int rlayer_release_read_buffer(OSSL_RECORD_LAYER *rl)
+static int tls_release_read_buffer(OSSL_RECORD_LAYER *rl)
 {
     SSL3_BUFFER *b;
 
     b = &rl->rbuf;
-    if (rl->options & SSL_OP_CLEANSE_PLAINTEXT)
+    if ((rl->options & SSL_OP_CLEANSE_PLAINTEXT) != 0)
         OPENSSL_cleanse(b->buf, b->len);
     OPENSSL_free(b->buf);
     b->buf = NULL;
@@ -264,8 +263,8 @@ int tls_default_read_n(OSSL_RECORD_LAYER *rl, size_t n, size_t max, int extend,
         BIO *bio = rl->prev != NULL ? rl->prev : rl->bio;
 
         /*
-         * Now we have len+left bytes at the front of s->s3.rbuf.buf and
-         * need to read in more until we have len+n (up to len+max if
+         * Now we have len+left bytes at the front of rl->rbuf.buf and
+         * need to read in more until we have len + n (up to len + max if
          * possible)
          */
 
@@ -298,9 +297,9 @@ int tls_default_read_n(OSSL_RECORD_LAYER *rl, size_t n, size_t max, int extend,
 
         if (ret <= OSSL_RECORD_RETURN_RETRY) {
             rb->left = left;
-            if (rl->mode & SSL_MODE_RELEASE_BUFFERS && !rl->isdtls)
+            if ((rl->mode & SSL_MODE_RELEASE_BUFFERS) != 0 && !rl->isdtls)
                 if (len + left == 0)
-                    rlayer_release_read_buffer(rl);
+                    tls_release_read_buffer(rl);
             return ret;
         }
         left += bioread;
@@ -369,7 +368,7 @@ static int rlayer_early_data_count_ok(OSSL_RECORD_LAYER *rl, size_t length,
 
     if (max_early_data == 0) {
         RLAYERfatal(rl, send ? SSL_AD_INTERNAL_ERROR : SSL_AD_UNEXPECTED_MESSAGE,
-                 SSL_R_TOO_MUCH_EARLY_DATA);
+                    SSL_R_TOO_MUCH_EARLY_DATA);
         return 0;
     }
 
@@ -378,7 +377,7 @@ static int rlayer_early_data_count_ok(OSSL_RECORD_LAYER *rl, size_t length,
 
     if (rl->early_data_count + length > max_early_data) {
         RLAYERfatal(rl, send ? SSL_AD_INTERNAL_ERROR : SSL_AD_UNEXPECTED_MESSAGE,
-                 SSL_R_TOO_MUCH_EARLY_DATA);
+                    SSL_R_TOO_MUCH_EARLY_DATA);
         return 0;
     }
     rl->early_data_count += length;
@@ -388,9 +387,9 @@ static int rlayer_early_data_count_ok(OSSL_RECORD_LAYER *rl, size_t length,
 
 /*
  * MAX_EMPTY_RECORDS defines the number of consecutive, empty records that
- * will be processed per call to ssl3_get_record. Without this limit an
+ * will be processed per call to tls_get_more_records. Without this limit an
  * attacker could send empty records at a faster rate than we can process and
- * cause ssl3_get_record to loop forever.
+ * cause tls_get_more_records to loop forever.
  */
 #define MAX_EMPTY_RECORDS 32
 
@@ -428,7 +427,7 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
     rr = rl->rrec;
     rbuf = &rl->rbuf;
     if (rbuf->buf == NULL) {
-        if (!rlayer_setup_read_buffer(rl)) {
+        if (!tls_setup_read_buffer(rl)) {
             /* RLAYERfatal() already called */
             return OSSL_RECORD_RETURN_FATAL;
         }
@@ -490,7 +489,7 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
                 thisrr->length = sslv2len & 0x7fff;
 
                 if (thisrr->length > SSL3_BUFFER_get_len(rbuf)
-                    - SSL2_RT_HEADER_LENGTH) {
+                                     - SSL2_RT_HEADER_LENGTH) {
                     RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW,
                                 SSL_R_PACKET_LENGTH_TOO_LONG);
                     return OSSL_RECORD_RETURN_FATAL;
@@ -502,7 +501,8 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
                 if (!PACKET_get_1(&pkt, &type)
                         || !PACKET_get_net_2(&pkt, &version)
                         || !PACKET_get_net_2_len(&pkt, &thisrr->length)) {
-                    rl->msg_callback(0, 0, SSL3_RT_HEADER, p, 5, rl->cbarg);
+                    if (rl->msg_callback != NULL)
+                        rl->msg_callback(0, 0, SSL3_RT_HEADER, p, 5, rl->cbarg);
                     RLAYERfatal(rl, SSL_AD_DECODE_ERROR, ERR_R_INTERNAL_ERROR);
                     return OSSL_RECORD_RETURN_FATAL;
                 }
@@ -521,7 +521,8 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
                     return OSSL_RECORD_RETURN_FATAL;
                 }
 
-                rl->msg_callback(0, version, SSL3_RT_HEADER, p, 5, rl->cbarg);
+                if (rl->msg_callback != NULL)
+                    rl->msg_callback(0, version, SSL3_RT_HEADER, p, 5, rl->cbarg);
 
                 if (thisrr->length >
                     SSL3_BUFFER_get_len(rbuf) - SSL3_RT_HEADER_LENGTH) {
@@ -545,7 +546,7 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
          */
         if (thisrr->rec_version == SSL2_VERSION) {
             more = thisrr->length + SSL2_RT_HEADER_LENGTH
-                - SSL3_RT_HEADER_LENGTH;
+                   - SSL3_RT_HEADER_LENGTH;
         } else {
             more = thisrr->length;
         }
@@ -588,9 +589,6 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
         thisrr->data = thisrr->input;
         thisrr->orig_len = thisrr->length;
 
-        /* Mark this record as not read by upper layers yet */
-        thisrr->read = 0;
-
         num_recs++;
 
         /* we have pulled in a full packet so zero things */
@@ -627,7 +625,6 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
                         SSL_R_UNEXPECTED_CCS_MESSAGE);
             return OSSL_RECORD_RETURN_FATAL;
         }
-        thisrr->read = 1;
         rl->num_recs = 0;
         rl->curr_rec = 0;
         rl->num_released = 0;
@@ -641,8 +638,8 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
         if (tmpmd != NULL) {
             imac_size = EVP_MD_get_size(tmpmd);
             if (!ossl_assert(imac_size >= 0 && imac_size <= EVP_MAX_MD_SIZE)) {
-                    RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB);
-                    return OSSL_RECORD_RETURN_FATAL;
+                RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB);
+                return OSSL_RECORD_RETURN_FATAL;
             }
             mac_size = (size_t)imac_size;
         }
@@ -695,11 +692,13 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
      *    1: Success or MTE decryption failed (MAC will be randomised)
      */
     if (enc_err == 0) {
-        if (rl->alert != 0) {
+        if (rl->alert != SSL_AD_NO_ALERT) {
             /* RLAYERfatal() already got called */
             goto end;
         }
-        if (num_recs == 1 && rl->skip_early_data(rl->cbarg)) {
+        if (num_recs == 1
+                && rl->skip_early_data != NULL
+                && rl->skip_early_data(rl->cbarg)) {
             /*
              * Valid early_data that we cannot decrypt will fail here. We treat
              * it like an empty record.
@@ -714,7 +713,6 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
             }
 
             thisrr->length = 0;
-            thisrr->read = 1;
             rl->num_recs = 0;
             rl->curr_rec = 0;
             rl->num_released = 0;
@@ -740,6 +738,7 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
 
         for (j = 0; j < num_recs; j++) {
             SSL_MAC_BUF *thismb = &macbufs[j];
+
             thisrr = &rr[j];
 
             i = rl->funcs->mac(rl, thisrr, md, 0 /* not send */);
@@ -752,7 +751,7 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
     }
 
     if (enc_err == 0) {
-        if (rl->alert != 0) {
+        if (rl->alert != SSL_AD_NO_ALERT) {
             /* We already called RLAYERfatal() */
             goto end;
         }
@@ -798,7 +797,8 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl)
         /* just read a 0 length packet */
         if (thisrr->length == 0) {
             if (++(rl->empty_record_count) > MAX_EMPTY_RECORDS) {
-                RLAYERfatal(rl, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_RECORD_TOO_SMALL);
+                RLAYERfatal(rl, SSL_AD_UNEXPECTED_MESSAGE,
+                            SSL_R_RECORD_TOO_SMALL);
                 goto end;
             }
         } else {
@@ -842,9 +842,9 @@ int tls_default_validate_record_header(OSSL_RECORD_LAYER *rl, SSL3_RECORD *rec)
 
 #ifndef OPENSSL_NO_COMP
     /*
-        * If OPENSSL_NO_COMP is defined then SSL3_RT_MAX_ENCRYPTED_LENGTH
-        * does not include the compression overhead anyway.
-        */
+     * If OPENSSL_NO_COMP is defined then SSL3_RT_MAX_ENCRYPTED_LENGTH
+     * does not include the compression overhead anyway.
+     */
     if (rl->expand == NULL)
         len -= SSL3_RT_MAX_COMPRESSED_OVERHEAD;
 #endif
@@ -870,8 +870,8 @@ int tls_do_uncompress(OSSL_RECORD_LAYER *rl, SSL3_RECORD *rec)
     if (rec->comp == NULL)
         return 0;
 
-    i = COMP_expand_block(rl->expand, rec->comp,
-                          SSL3_RT_MAX_PLAIN_LENGTH, rec->data, (int)rec->length);
+    i = COMP_expand_block(rl->expand, rec->comp, SSL3_RT_MAX_PLAIN_LENGTH,
+                          rec->data, (int)rec->length);
     if (i < 0)
         return 0;
     else
@@ -917,15 +917,15 @@ int tls13_common_post_process_record(OSSL_RECORD_LAYER *rl, SSL3_RECORD *rec)
         return 0;
     }
 
-    rl->msg_callback(0, rl->version, SSL3_RT_INNER_CONTENT_TYPE, &rec->type,
+    if (rl->msg_callback != NULL)
+        rl->msg_callback(0, rl->version, SSL3_RT_INNER_CONTENT_TYPE, &rec->type,
                         1, rl->cbarg);
 
     /*
      * TLSv1.3 alert and handshake records are required to be non-zero in
      * length.
      */
-    if ((rec->type == SSL3_RT_HANDSHAKE
-                || rec->type == SSL3_RT_ALERT)
+    if ((rec->type == SSL3_RT_HANDSHAKE || rec->type == SSL3_RT_ALERT)
             && rec->length == 0) {
         RLAYERfatal(rl, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_BAD_LENGTH);
         return 0;
@@ -934,7 +934,7 @@ int tls13_common_post_process_record(OSSL_RECORD_LAYER *rl, SSL3_RECORD *rec)
     return 1;
 }
 
-int tls_read_record(OSSL_RECORD_LAYER *rl, void **rechandle,  int *rversion,
+int tls_read_record(OSSL_RECORD_LAYER *rl, void **rechandle, int *rversion,
                     int *type, unsigned char **data, size_t *datalen,
                     uint16_t *epoch, unsigned char *seq_num)
 {
@@ -972,7 +972,7 @@ int tls_read_record(OSSL_RECORD_LAYER *rl, void **rechandle,  int *rversion,
     *data = rec->data + rec->off;
     *datalen = rec->length;
     if (rl->isdtls) {
-        *epoch = (uint16_t)rec->epoch;
+        *epoch = rec->epoch;
         memcpy(seq_num, rec->seq_num, sizeof(rec->seq_num));
     }
 
@@ -990,9 +990,55 @@ int tls_release_record(OSSL_RECORD_LAYER *rl, void *rechandle)
 
     rl->num_released++;
 
+    if (rl->curr_rec == rl->num_released
+            && (rl->mode & SSL_MODE_RELEASE_BUFFERS) != 0
+            && SSL3_BUFFER_get_left(&rl->rbuf) == 0)
+        tls_release_read_buffer(rl);
+
     return OSSL_RECORD_RETURN_SUCCESS;
 }
 
+int tls_set_options(OSSL_RECORD_LAYER *rl, const OSSL_PARAM *options)
+{
+    const OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate_const(options, OSSL_LIBSSL_RECORD_LAYER_PARAM_OPTIONS);
+    if (p != NULL && !OSSL_PARAM_get_uint64(p, &rl->options)) {
+        ERR_raise(ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER);
+        return 0;
+    }
+
+    p = OSSL_PARAM_locate_const(options, OSSL_LIBSSL_RECORD_LAYER_PARAM_MODE);
+    if (p != NULL && !OSSL_PARAM_get_uint32(p, &rl->mode)) {
+        ERR_raise(ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER);
+        return 0;
+    }
+
+    p = OSSL_PARAM_locate_const(options,
+                                OSSL_LIBSSL_RECORD_LAYER_READ_BUFFER_LEN);
+    if (p != NULL && !OSSL_PARAM_get_size_t(p, &rl->rbuf.default_len)) {
+        ERR_raise(ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER);
+        return 0;
+    }
+
+    if (rl->level == OSSL_RECORD_PROTECTION_LEVEL_APPLICATION) {
+        /*
+         * We ignore any read_ahead setting prior to the application protection
+         * level. Otherwise we may read ahead data in a lower protection level
+         * that is destined for a higher protection level. To simplify the logic
+         * we don't support that at this stage.
+         */
+        p = OSSL_PARAM_locate_const(options,
+                                    OSSL_LIBSSL_RECORD_LAYER_PARAM_READ_AHEAD);
+        if (p != NULL && !OSSL_PARAM_get_int(p, &rl->read_ahead)) {
+            ERR_raise(ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
 int
 tls_int_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
                          int role, int direction, int level, unsigned char *key,
@@ -1013,81 +1059,46 @@ tls_int_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
     *retrl = NULL;
 
     if (rl == NULL) {
-        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_SSL, ERR_R_MALLOC_FAILURE);
         return OSSL_RECORD_RETURN_FATAL;
     }
 
-    /*
-     * TODO(RECLAYER): Need to handle the case where the params are updated
-     * after the record layer has been created.
-     */
-    p = OSSL_PARAM_locate_const(options, OSSL_LIBSSL_RECORD_LAYER_PARAM_OPTIONS);
-    if (p != NULL && !OSSL_PARAM_get_uint64(p, &rl->options)) {
-        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
-        goto err;
-    }
-
-    p = OSSL_PARAM_locate_const(options, OSSL_LIBSSL_RECORD_LAYER_PARAM_MODE);
-    if (p != NULL && !OSSL_PARAM_get_uint32(p, &rl->mode)) {
-        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
-        goto err;
-    }
-
-    p = OSSL_PARAM_locate_const(options, OSSL_LIBSSL_RECORD_LAYER_READ_BUFFER_LEN);
-    if (p != NULL && !OSSL_PARAM_get_size_t(p, &rl->rbuf.default_len)) {
-        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
-        goto err;
-    }
-
     /* Loop through all the settings since they must all be understood */
-    for (p = settings; p->key != NULL; p++) {
-        if (strcmp(p->key, OSSL_LIBSSL_RECORD_LAYER_PARAM_USE_ETM) == 0) {
-            if (!OSSL_PARAM_get_int(p, &rl->use_etm)) {
-                RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
-                goto err;
-            }
-        } else if (strcmp(p->key, OSSL_LIBSSL_RECORD_LAYER_PARAM_MAX_FRAG_LEN) == 0) {
-            if (!OSSL_PARAM_get_uint(p, &rl->max_frag_len)) {
-                RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
-                goto err;
-            }
-        } else if (strcmp(p->key, OSSL_LIBSSL_RECORD_LAYER_PARAM_MAX_EARLY_DATA) == 0) {
-            if (!OSSL_PARAM_get_uint32(p, &rl->max_early_data)) {
-                RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
-                goto err;
-            }
-        } else if (strcmp(p->key, OSSL_LIBSSL_RECORD_LAYER_PARAM_STREAM_MAC) == 0) {
-            if (!OSSL_PARAM_get_int(p, &rl->stream_mac)) {
-                RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
-                goto err;
-            }
-        } else if (strcmp(p->key, OSSL_LIBSSL_RECORD_LAYER_PARAM_TLSTREE) == 0) {
-            if (!OSSL_PARAM_get_int(p, &rl->tlstree)) {
-                RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
+    if (settings != NULL) {
+        for (p = settings; p->key != NULL; p++) {
+            if (strcmp(p->key, OSSL_LIBSSL_RECORD_LAYER_PARAM_USE_ETM) == 0) {
+                if (!OSSL_PARAM_get_int(p, &rl->use_etm)) {
+                    ERR_raise(ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER);
+                    goto err;
+                }
+            } else if (strcmp(p->key,
+                              OSSL_LIBSSL_RECORD_LAYER_PARAM_MAX_FRAG_LEN) == 0) {
+                if (!OSSL_PARAM_get_uint(p, &rl->max_frag_len)) {
+                    ERR_raise(ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER);
+                    goto err;
+                }
+            } else if (strcmp(p->key,
+                              OSSL_LIBSSL_RECORD_LAYER_PARAM_MAX_EARLY_DATA) == 0) {
+                if (!OSSL_PARAM_get_uint32(p, &rl->max_early_data)) {
+                    ERR_raise(ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER);
+                    goto err;
+                }
+            } else if (strcmp(p->key,
+                              OSSL_LIBSSL_RECORD_LAYER_PARAM_STREAM_MAC) == 0) {
+                if (!OSSL_PARAM_get_int(p, &rl->stream_mac)) {
+                    ERR_raise(ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER);
+                    goto err;
+                }
+            } else if (strcmp(p->key,
+                              OSSL_LIBSSL_RECORD_LAYER_PARAM_TLSTREE) == 0) {
+                if (!OSSL_PARAM_get_int(p, &rl->tlstree)) {
+                    ERR_raise(ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER);
+                    goto err;
+                }
+            } else {
+                ERR_raise(ERR_LIB_SSL, SSL_R_UNKNOWN_MANDATORY_PARAMETER);
                 goto err;
             }
-        } else {
-            RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_UNKNOWN_MANDATORY_PARAMETER);
-            goto err;
-        }
-    }
-
-
-    if (level == OSSL_RECORD_PROTECTION_LEVEL_APPLICATION) {
-        /*
-         * We ignore any read_ahead setting prior to the application protection
-         * level. Otherwise we may read ahead data in a lower protection level
-         * that is destined for a higher protection level. To simplify the logic
-         * we don't support that at this stage.
-         */
-        /*
-         * TODO(RECLAYER): Handle the case of read_ahead at the application
-         * level and a key update/reneg occurs.
-         */
-        p = OSSL_PARAM_locate_const(options, OSSL_LIBSSL_RECORD_LAYER_PARAM_READ_AHEAD);
-        if (p != NULL && !OSSL_PARAM_get_int(p, &rl->read_ahead)) {
-            RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, SSL_R_FAILED_TO_GET_PARAMETER);
-            goto err;
         }
     }
 
@@ -1099,6 +1110,8 @@ tls_int_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
     rl->direction = direction;
     rl->level = level;
 
+    rl->alert = SSL_AD_NO_ALERT;
+
     if (level == OSSL_RECORD_PROTECTION_LEVEL_NONE)
         rl->is_first_record = 1;
 
@@ -1114,23 +1127,30 @@ tls_int_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
     rl->next = next;
 
     rl->cbarg = cbarg;
-    for (; fns->function_id != 0; fns++) {
-        switch (fns->function_id) {
-        case OSSL_FUNC_RLAYER_SKIP_EARLY_DATA:
-            rl->skip_early_data = OSSL_FUNC_rlayer_skip_early_data(fns);
-            break;
-        case OSSL_FUNC_RLAYER_MSG_CALLBACK:
-            rl->msg_callback = OSSL_FUNC_rlayer_msg_callback(fns);
-            break;
-        case OSSL_FUNC_RLAYER_SECURITY:
-            rl->security = OSSL_FUNC_rlayer_security(fns);
-            break;
-        default:
-            /* Just ignore anything we don't understand */
-            break;
+    if (fns != NULL) {
+        for (; fns->function_id != 0; fns++) {
+            switch (fns->function_id) {
+            case OSSL_FUNC_RLAYER_SKIP_EARLY_DATA:
+                rl->skip_early_data = OSSL_FUNC_rlayer_skip_early_data(fns);
+                break;
+            case OSSL_FUNC_RLAYER_MSG_CALLBACK:
+                rl->msg_callback = OSSL_FUNC_rlayer_msg_callback(fns);
+                break;
+            case OSSL_FUNC_RLAYER_SECURITY:
+                rl->security = OSSL_FUNC_rlayer_security(fns);
+                break;
+            default:
+                /* Just ignore anything we don't understand */
+                break;
+            }
         }
     }
 
+    if (!tls_set_options(rl, options)) {
+        ERR_raise(ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER);
+        goto err;
+    }
+
     *retrl = rl;
     return OSSL_RECORD_RETURN_SUCCESS;
  err:
@@ -1140,19 +1160,19 @@ tls_int_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
 
 static int
 tls_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
-                     int role, int direction, int level, unsigned int epoch,
+                     int role, int direction, int level, uint16_t epoch,
                      unsigned char *key, size_t keylen, unsigned char *iv,
                      size_t ivlen, unsigned char *mackey, size_t mackeylen,
                      const EVP_CIPHER *ciph, size_t taglen,
                      int mactype,
-                     const EVP_MD *md, const SSL_COMP *comp, BIO *prev, 
+                     const EVP_MD *md, const SSL_COMP *comp, BIO *prev,
                      BIO *transport, BIO *next, BIO_ADDR *local, BIO_ADDR *peer,
                      const OSSL_PARAM *settings, const OSSL_PARAM *options,
                      const OSSL_DISPATCH *fns, void *cbarg,
                      OSSL_RECORD_LAYER **retrl)
 {
     int ret;
-    
+
     ret = tls_int_new_record_layer(libctx, propq, vers, role, direction, level,
                                    key, keylen, iv, ivlen, mackey, mackeylen,
                                    ciph, taglen, mactype, md, comp, prev,
@@ -1185,8 +1205,8 @@ tls_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
     }
 
     ret = (*retrl)->funcs->set_crypto_state(*retrl, level, key, keylen, iv,
-                                             ivlen, mackey, mackeylen, ciph,
-                                             taglen, mactype, md, comp);
+                                            ivlen, mackey, mackeylen, ciph,
+                                            taglen, mactype, md, comp);
 
  err:
     if (ret != OSSL_RECORD_RETURN_SUCCESS) {
@@ -1212,6 +1232,8 @@ static void tls_int_free(OSSL_RECORD_LAYER *rl)
     if (rl->version == SSL3_VERSION)
         OPENSSL_cleanse(rl->mac_secret, sizeof(rl->mac_secret));
 
+    SSL3_RECORD_release(rl->rrec, SSL_MAX_PIPELINES);
+
     OPENSSL_free(rl);
 }
 
@@ -1236,7 +1258,6 @@ int tls_free(OSSL_RECORD_LAYER *rl)
     return ret;
 }
 
-
 int tls_reset(OSSL_RECORD_LAYER *rl)
 {
     memset(rl, 0, sizeof(*rl));
@@ -1258,7 +1279,7 @@ size_t tls_app_data_pending(OSSL_RECORD_LAYER *rl)
     size_t i;
     size_t num = 0;
 
-    for (i = rl->curr_rec; i <rl->num_recs; i++) {
+    for (i = rl->curr_rec; i < rl->num_recs; i++) {
         if (rl->rrec[i].type != SSL3_RT_APPLICATION_DATA)
             return num;
         num += rl->rrec[i].length;
@@ -1281,18 +1302,543 @@ size_t tls_get_max_records(OSSL_RECORD_LAYER *rl)
     return 0;
 }
 
-int tls_write_records(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE **templates,
-                      size_t numtempl, size_t allowance, size_t *sent)
+int tls_write_records(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE *templates,
+                      size_t numtempl)
 {
-    return 0;
+    WPACKET pkt[SSL_MAX_PIPELINES + 1];
+    SSL3_RECORD wr[SSL_MAX_PIPELINES + 1];
+    WPACKET *thispkt;
+    SSL3_RECORD *thiswr;
+    unsigned char *recordstart;
+    int mac_size, clear = 0;
+    int eivlen = 0;
+    size_t align = 0;
+    SSL3_BUFFER *wb;
+    SSL_SESSION *sess;
+    size_t totlen = 0, len, wpinited = 0;
+    size_t j, prefix = 0;
+    int using_ktls;
+    /* TODO(RECLAYER): REMOVE ME */
+    SSL_CONNECTION *s = rl->cbarg;
+    SSL *ssl = SSL_CONNECTION_GET_SSL(s);
+    OSSL_RECORD_TEMPLATE prefixtempl;
+    OSSL_RECORD_TEMPLATE *thistempl;
+
+    if (!ossl_assert(!RECORD_LAYER_write_pending(&s->rlayer))) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        goto err;
+    }
+
+    sess = s->session;
+
+    if ((sess == NULL)
+            || (s->enc_write_ctx == NULL)
+            || (EVP_MD_CTX_get0_md(s->write_hash) == NULL)) {
+        clear = s->enc_write_ctx ? 0 : 1; /* must be AEAD cipher */
+        mac_size = 0;
+    } else {
+        mac_size = EVP_MD_CTX_get_size(s->write_hash);
+        if (mac_size < 0) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+
+    /*
+     * 'create_empty_fragment' is true only when we have recursively called
+     * ourselves.
+     * Do we need to do that recursion in order to add an empty record prefix?
+     */
+    prefix = s->s3.need_empty_fragments
+             && !clear
+             && !s->s3.empty_fragment_done
+             && templates[0].type == SSL3_RT_APPLICATION_DATA;
+
+    if (s->rlayer.numwpipes < numtempl + prefix) {
+        /*
+         * TODO(RECLAYER): In the prefix case the first buffer can be a lot
+         * smaller. It is wasteful to allocate a full sized buffer here
+         */
+        if (!ssl3_setup_write_buffer(s, numtempl + prefix, 0)) {
+            /* SSLfatal() already called */
+            return -1;
+        }
+    }
+
+    using_ktls = BIO_get_ktls_send(s->wbio);
+    if (!ossl_assert(!using_ktls || !prefix)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    if (prefix) {
+        /*
+         * countermeasure against known-IV weakness in CBC ciphersuites (see
+         * http://www.openssl.org/~bodo/tls-cbc.txt)
+         */
+        prefixtempl.buf = NULL;
+        prefixtempl.buflen = 0;
+        prefixtempl.type = SSL3_RT_APPLICATION_DATA;
+        wpinited = 1;
+
+        /* TODO(RECLAYER): Do we actually need this? */
+        s->s3.empty_fragment_done = 1;
+
+        wb = &s->rlayer.wbuf[0];
+        /* TODO(RECLAYER): This alignment calculation no longer seems right */
+#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
+        /*
+         * extra fragment would be couple of cipher blocks, which would be
+         * multiple of SSL3_ALIGN_PAYLOAD, so if we want to align the real
+         * payload, then we can just pretend we simply have two headers.
+         */
+        align = (size_t)SSL3_BUFFER_get_buf(wb) + 2 * SSL3_RT_HEADER_LENGTH;
+        align = SSL3_ALIGN_PAYLOAD - 1 - ((align - 1) % SSL3_ALIGN_PAYLOAD);
+#endif
+        SSL3_BUFFER_set_offset(wb, align);
+        if (!WPACKET_init_static_len(&pkt[0], SSL3_BUFFER_get_buf(wb),
+                                     SSL3_BUFFER_get_len(wb), 0)
+                || !WPACKET_allocate_bytes(&pkt[0], align, NULL)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+        wpinited = 1;
+    }
+    for (j = 0; j < numtempl; j++) {
+        thispkt = &pkt[prefix + j];
+
+        wb = &s->rlayer.wbuf[prefix + j];
+        wb->type = templates[j].type;
+
+        if (using_ktls) {
+            /*
+            * ktls doesn't modify the buffer, but to avoid a warning we need
+            * to discard the const qualifier.
+            * This doesn't leak memory because the buffers have been
+            * released when switching to ktls.
+            */
+            SSL3_BUFFER_set_buf(wb, (unsigned char *)templates[j].buf);
+            SSL3_BUFFER_set_offset(wb, 0);
+            SSL3_BUFFER_set_app_buffer(wb, 1);
+        } else {
+#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0
+            align = (size_t)SSL3_BUFFER_get_buf(wb) + SSL3_RT_HEADER_LENGTH;
+            align = SSL3_ALIGN_PAYLOAD - 1
+                    - ((align - 1) % SSL3_ALIGN_PAYLOAD);
+#endif
+            /* TODO(RECLAYER): Is this alignment actually used somewhere? */
+            SSL3_BUFFER_set_offset(wb, align);
+            if (!WPACKET_init_static_len(thispkt, SSL3_BUFFER_get_buf(wb),
+                                        SSL3_BUFFER_get_len(wb), 0)
+                    || !WPACKET_allocate_bytes(thispkt, align, NULL)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+            wpinited++;
+        }
+    }
+
+    if (!using_ktls) {
+        /* Explicit IV length, block ciphers appropriate version flag */
+        if (s->enc_write_ctx && SSL_USE_EXPLICIT_IV(s)
+            && !SSL_CONNECTION_TREAT_AS_TLS13(s)) {
+            int mode = EVP_CIPHER_CTX_get_mode(s->enc_write_ctx);
+            if (mode == EVP_CIPH_CBC_MODE) {
+                eivlen = EVP_CIPHER_CTX_get_iv_length(s->enc_write_ctx);
+                if (eivlen < 0) {
+                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_LIBRARY_BUG);
+                    goto err;
+                }
+                if (eivlen <= 1)
+                    eivlen = 0;
+            } else if (mode == EVP_CIPH_GCM_MODE) {
+                /* Need explicit part of IV for GCM mode */
+                eivlen = EVP_GCM_TLS_EXPLICIT_IV_LEN;
+            } else if (mode == EVP_CIPH_CCM_MODE) {
+                eivlen = EVP_CCM_TLS_EXPLICIT_IV_LEN;
+            }
+        }
+    }
+
+    totlen = 0;
+    /* Clear our SSL3_RECORD structures */
+    memset(wr, 0, sizeof(wr));
+    for (j = 0; j < numtempl + prefix; j++) {
+        unsigned int version = (s->version == TLS1_3_VERSION) ? TLS1_2_VERSION
+                                                              : s->version;
+        unsigned char *compressdata = NULL;
+        size_t maxcomplen;
+        unsigned int rectype;
+
+        thispkt = &pkt[j];
+        thiswr = &wr[j];
+        thistempl = (j == 0 && prefix == 1) ? &prefixtempl :
+                                              &templates[j - prefix];
+
+        /*
+         * In TLSv1.3, once encrypting, we always use application data for the
+         * record type
+         */
+        if (SSL_CONNECTION_TREAT_AS_TLS13(s)
+                && s->enc_write_ctx != NULL
+                && (s->statem.enc_write_state != ENC_WRITE_STATE_WRITE_PLAIN_ALERTS
+                    || thistempl->type != SSL3_RT_ALERT))
+            rectype = SSL3_RT_APPLICATION_DATA;
+        else
+            rectype = thistempl->type;
+
+        SSL3_RECORD_set_type(thiswr, rectype);
+
+        /*
+         * Some servers hang if initial client hello is larger than 256 bytes
+         * and record version number > TLS 1.0
+         */
+        if (SSL_get_state(ssl) == TLS_ST_CW_CLNT_HELLO
+                && !s->renegotiate
+                && TLS1_get_version(ssl) > TLS1_VERSION
+                && s->hello_retry_request == SSL_HRR_NONE)
+            version = TLS1_VERSION;
+        SSL3_RECORD_set_rec_version(thiswr, version);
+
+        maxcomplen = thistempl->buflen;
+        if (s->compress != NULL)
+            maxcomplen += SSL3_RT_MAX_COMPRESSED_OVERHEAD;
+
+        /*
+         * When using offload kernel will write the header.
+         * Otherwise write the header now
+         */
+        if (!using_ktls
+                && (!WPACKET_put_bytes_u8(thispkt, rectype)
+                || !WPACKET_put_bytes_u16(thispkt, version)
+                || !WPACKET_start_sub_packet_u16(thispkt)
+                || (eivlen > 0
+                    && !WPACKET_allocate_bytes(thispkt, eivlen, NULL))
+                || (maxcomplen > 0
+                    && !WPACKET_reserve_bytes(thispkt, maxcomplen,
+                                              &compressdata)))) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+
+        /* lets setup the record stuff. */
+        SSL3_RECORD_set_data(thiswr, compressdata);
+        SSL3_RECORD_set_length(thiswr, thistempl->buflen);
+        /*
+         * TODO(RECLAYER): Cast away the const. Should be safe - by why is this
+         * necessary?
+         */
+        SSL3_RECORD_set_input(thiswr, (unsigned char *)thistempl->buf);
+        totlen += thistempl->buflen;
+
+        /*
+         * we now 'read' from thiswr->input, thiswr->length bytes into
+         * thiswr->data
+         */
+
+        /* first we compress */
+        if (s->compress != NULL) {
+            if (!ssl3_do_compress(s, thiswr)
+                    || !WPACKET_allocate_bytes(thispkt, thiswr->length, NULL)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_COMPRESSION_FAILURE);
+                goto err;
+            }
+        } else {
+            if (using_ktls) {
+                SSL3_RECORD_reset_data(&wr[j]);
+            } else {
+                if (!WPACKET_memcpy(thispkt, thiswr->input, thiswr->length)) {
+                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+                    goto err;
+                }
+                SSL3_RECORD_reset_input(&wr[j]);
+            }
+        }
+
+        if (SSL_CONNECTION_TREAT_AS_TLS13(s)
+                && !using_ktls
+                && s->enc_write_ctx != NULL
+                && (s->statem.enc_write_state != ENC_WRITE_STATE_WRITE_PLAIN_ALERTS
+                    || thistempl->type != SSL3_RT_ALERT)) {
+            size_t rlen, max_send_fragment;
+
+            if (!WPACKET_put_bytes_u8(thispkt, thistempl->type)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+            SSL3_RECORD_add_length(thiswr, 1);
+
+            /* Add TLS1.3 padding */
+            max_send_fragment = ssl_get_max_send_fragment(s);
+            rlen = SSL3_RECORD_get_length(thiswr);
+            if (rlen < max_send_fragment) {
+                size_t padding = 0;
+                size_t max_padding = max_send_fragment - rlen;
+                if (s->record_padding_cb != NULL) {
+                    padding = s->record_padding_cb(ssl, thistempl->type, rlen,
+                                                   s->record_padding_arg);
+                } else if (s->block_padding > 0) {
+                    size_t mask = s->block_padding - 1;
+                    size_t remainder;
+
+                    /* optimize for power of 2 */
+                    if ((s->block_padding & mask) == 0)
+                        remainder = rlen & mask;
+                    else
+                        remainder = rlen % s->block_padding;
+                    /* don't want to add a block of padding if we don't have to */
+                    if (remainder == 0)
+                        padding = 0;
+                    else
+                        padding = s->block_padding - remainder;
+                }
+                if (padding > 0) {
+                    /* do not allow the record to exceed max plaintext length */
+                    if (padding > max_padding)
+                        padding = max_padding;
+                    if (!WPACKET_memset(thispkt, 0, padding)) {
+                        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                                 ERR_R_INTERNAL_ERROR);
+                        goto err;
+                    }
+                    SSL3_RECORD_add_length(thiswr, padding);
+                }
+            }
+        }
+
+        /*
+         * we should still have the output to thiswr->data and the input from
+         * wr->input. Length should be thiswr->length. thiswr->data still points
+         * in the wb->buf
+         */
+
+        if (!using_ktls && !SSL_WRITE_ETM(s) && mac_size != 0) {
+            unsigned char *mac;
+
+            if (!WPACKET_allocate_bytes(thispkt, mac_size, &mac)
+                    || !ssl->method->ssl3_enc->mac(s, thiswr, mac, 1)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+        }
+
+        /*
+         * Reserve some bytes for any growth that may occur during encryption.
+         * This will be at most one cipher block or the tag length if using
+         * AEAD. SSL_RT_MAX_CIPHER_BLOCK_SIZE covers either case.
+         */
+        if (!using_ktls) {
+            if (!WPACKET_reserve_bytes(thispkt,
+                                        SSL_RT_MAX_CIPHER_BLOCK_SIZE,
+                                        NULL)
+                /*
+                 * We also need next the amount of bytes written to this
+                 * sub-packet
+                 */
+                || !WPACKET_get_length(thispkt, &len)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            goto err;
+            }
+
+            /* Get a pointer to the start of this record excluding header */
+            recordstart = WPACKET_get_curr(thispkt) - len;
+            SSL3_RECORD_set_data(thiswr, recordstart);
+            SSL3_RECORD_reset_input(thiswr);
+            SSL3_RECORD_set_length(thiswr, len);
+        }
+    }
+
+    if (s->statem.enc_write_state == ENC_WRITE_STATE_WRITE_PLAIN_ALERTS) {
+        /*
+         * We haven't actually negotiated the version yet, but we're trying to
+         * send early data - so we need to use the tls13enc function.
+         */
+        if (tls13_enc(s, wr, numtempl, 1, NULL, mac_size) < 1) {
+            if (!ossl_statem_in_error(s)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            }
+            goto err;
+        }
+    } else {
+        if (!using_ktls) {
+            if (prefix) {
+                if (ssl->method->ssl3_enc->enc(s, wr, 1, 1, NULL, mac_size) < 1) {
+                    if (!ossl_statem_in_error(s)) {
+                        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+                    }
+                    goto err;
+                }
+            }
+            if (ssl->method->ssl3_enc->enc(s, wr + prefix, numtempl, 1, NULL,
+                                           mac_size) < 1) {
+                if (!ossl_statem_in_error(s)) {
+                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+                }
+                goto err;
+            }
+        }
+    }
+
+    for (j = 0; j < prefix + numtempl; j++) {
+        size_t origlen;
+
+        thispkt = &pkt[j];
+        thiswr = &wr[j];
+        thistempl = (prefix == 1 && j == 0) ? &prefixtempl
+                                            : &templates[j - prefix];
+
+        if (using_ktls)
+            goto mac_done;
+
+        /* Allocate bytes for the encryption overhead */
+        if (!WPACKET_get_length(thispkt, &origlen)
+                   /* Encryption should never shrink the data! */
+                || origlen > thiswr->length
+                || (thiswr->length > origlen
+                    && !WPACKET_allocate_bytes(thispkt,
+                                               thiswr->length - origlen,
+                                               NULL))) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+        if (SSL_WRITE_ETM(s) && mac_size != 0) {
+            unsigned char *mac;
+
+            if (!WPACKET_allocate_bytes(thispkt, mac_size, &mac)
+                    || !ssl->method->ssl3_enc->mac(s, thiswr, mac, 1)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+                goto err;
+            }
+            SSL3_RECORD_add_length(thiswr, mac_size);
+        }
+
+        if (!WPACKET_get_length(thispkt, &len)
+                || !WPACKET_close(thispkt)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+
+        if (rl->msg_callback) {
+            recordstart = WPACKET_get_curr(thispkt) - len
+                          - SSL3_RT_HEADER_LENGTH;
+            rl->msg_callback(1, thiswr->rec_version, SSL3_RT_HEADER, recordstart,
+                             SSL3_RT_HEADER_LENGTH, rl->cbarg);
+
+            if (SSL_CONNECTION_TREAT_AS_TLS13(s) && s->enc_write_ctx != NULL) {
+                unsigned char ctype = thistempl->type;
+
+                rl->msg_callback(1, thiswr->rec_version, SSL3_RT_INNER_CONTENT_TYPE,
+                                 &ctype, 1, rl->cbarg);
+            }
+        }
+
+        if (!WPACKET_finish(thispkt)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+
+        /* header is added by the kernel when using offload */
+        SSL3_RECORD_add_length(thiswr, SSL3_RT_HEADER_LENGTH);
+
+ mac_done:
+        /*
+         * we should now have thiswr->data pointing to the encrypted data, which
+         * is thiswr->length long.
+         * Setting the type is not needed but helps for debugging
+         */
+        SSL3_RECORD_set_type(thiswr, thistempl->type);
+
+        /* now let's set up wb */
+        SSL3_BUFFER_set_left(&s->rlayer.wbuf[j], SSL3_RECORD_get_length(thiswr));
+    }
+
+    /* we now just need to write the buffers */
+    return tls_retry_write_records(rl);
+ err:
+    for (j = 0; j < wpinited; j++)
+        WPACKET_cleanup(&pkt[j]);
+    return -1;
 }
 
-int tls_retry_write_records(OSSL_RECORD_LAYER *rl, size_t allowance,
-                            size_t *sent)
+/* if SSL3_BUFFER_get_left() != 0, we need to call this
+ *
+ * Return values are as per SSL_write()
+ */
+int tls_retry_write_records(OSSL_RECORD_LAYER *rl)
 {
-    return 0;
-}
+    int i;
+    SSL3_BUFFER *thiswb;
+    size_t currbuf = 0;
+    size_t tmpwrit = 0;
+    SSL_CONNECTION *s = rl->cbarg;
+
+    for (;;) {
+        thiswb = &s->rlayer.wbuf[currbuf];
+        /* Loop until we find a buffer we haven't written out yet */
+        if (SSL3_BUFFER_get_left(thiswb) == 0
+            && currbuf < s->rlayer.numwpipes - 1) {
+            currbuf++;
+            continue;
+        }
+        clear_sys_error();
+        if (s->wbio != NULL) {
+            s->rwstate = SSL_WRITING;
 
+            /*
+             * To prevent coalescing of control and data messages,
+             * such as in buffer_write, we flush the BIO
+             */
+            if (BIO_get_ktls_send(s->wbio)
+                    && thiswb->type != SSL3_RT_APPLICATION_DATA) {
+                i = BIO_flush(s->wbio);
+                if (i <= 0)
+                    return i;
+                BIO_set_ktls_ctrl_msg(s->wbio, thiswb->type);
+            }
+            i = BIO_write(s->wbio, (char *)
+                          &(SSL3_BUFFER_get_buf(thiswb)
+                            [SSL3_BUFFER_get_offset(thiswb)]),
+                          (unsigned int)SSL3_BUFFER_get_left(thiswb));
+            if (i >= 0)
+                tmpwrit = i;
+        } else {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BIO_NOT_SET);
+            i = -1;
+        }
+
+        /*
+         * When an empty fragment is sent on a connection using KTLS,
+         * it is sent as a write of zero bytes.  If this zero byte
+         * write succeeds, i will be 0 rather than a non-zero value.
+         * Treat i == 0 as success rather than an error for zero byte
+         * writes to permit this case.
+         */
+        if (i >= 0 && tmpwrit == SSL3_BUFFER_get_left(thiswb)) {
+            SSL3_BUFFER_set_left(thiswb, 0);
+            SSL3_BUFFER_add_offset(thiswb, tmpwrit);
+            if (currbuf + 1 < s->rlayer.numwpipes)
+                continue;
+            s->rwstate = SSL_NOTHING;
+            /*
+             * Next chunk of data should get another prepended empty fragment
+             * in ciphersuites with known-IV weakness:
+             */
+            s->s3.empty_fragment_done = 0;
+            return 1;
+        } else if (i <= 0) {
+            if (SSL_CONNECTION_IS_DTLS(s)) {
+                /*
+                 * For DTLS, just drop it. That's kind of the whole point in
+                 * using a datagram service
+                 */
+                SSL3_BUFFER_set_left(thiswb, 0);
+            }
+            return i;
+        }
+        SSL3_BUFFER_add_offset(thiswb, tmpwrit);
+        SSL3_BUFFER_sub_left(thiswb, tmpwrit);
+    }
+}
 
 int tls_get_alert_code(OSSL_RECORD_LAYER *rl)
 {
@@ -1340,6 +1886,30 @@ void tls_set_max_pipelines(OSSL_RECORD_LAYER *rl, size_t max_pipelines)
         rl->read_ahead = 1;
 }
 
+void tls_get_state(OSSL_RECORD_LAYER *rl, const char **shortstr,
+                   const char **longstr)
+{
+    const char *shrt, *lng;
+
+    switch (rl->rstate) {
+    case SSL_ST_READ_HEADER:
+        shrt = "RH";
+        lng = "read header";
+        break;
+    case SSL_ST_READ_BODY:
+        shrt = "RB";
+        lng = "read body";
+        break;
+    default:
+        shrt = lng = "unknown";
+        break;
+    }
+    if (shortstr != NULL)
+        *shortstr = shrt;
+    if (longstr != NULL)
+        *longstr = lng;
+}
+
 const OSSL_RECORD_METHOD ossl_tls_record_method = {
     tls_new_record_layer,
     tls_free,
@@ -1360,5 +1930,7 @@ const OSSL_RECORD_METHOD ossl_tls_record_method = {
     tls_set_plain_alerts,
     tls_set_first_handshake,
     tls_set_max_pipelines,
-    NULL
+    NULL,
+    tls_get_state,
+    tls_set_options
 };