Resolve a TODO in ssl3_dispatch_alert
[openssl.git] / ssl / record / rec_layer_s3.c
index 8ea16672b673111f7ff47b7b11965d3fd9d9a0e7..b4435bf0201b3458ad23760ad3a1ac9d90ff52af 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
 #include <openssl/evp.h>
 #include <openssl/buffer.h>
 #include <openssl/rand.h>
+#include <openssl/core_names.h>
 #include "record_local.h"
 #include "internal/packet.h"
 
-#if     defined(OPENSSL_SMALL_FOOTPRINT) || \
-        !(      defined(AES_ASM) &&     ( \
-                defined(__x86_64)       || defined(__x86_64__)  || \
-                defined(_M_AMD64)       || defined(_M_X64)      ) \
-        )
-# undef EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
-# define EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK 0
-#endif
-
-void RECORD_LAYER_init(RECORD_LAYER *rl, SSL *s)
+void RECORD_LAYER_init(RECORD_LAYER *rl, SSL_CONNECTION *s)
 {
     rl->s = s;
-    RECORD_LAYER_set_first_record(&s->rlayer);
-    SSL3_RECORD_clear(rl->rrec, SSL_MAX_PIPELINES);
 }
 
 void RECORD_LAYER_clear(RECORD_LAYER *rl)
 {
-    rl->rstate = SSL_ST_READ_HEADER;
-
-    /*
-     * Do I need to clear read_ahead? As far as I can tell read_ahead did not
-     * previously get reset by SSL_clear...so I'll keep it that way..but is
-     * that right?
-     */
-
-    rl->packet = NULL;
-    rl->packet_length = 0;
     rl->wnum = 0;
     memset(rl->handshake_fragment, 0, sizeof(rl->handshake_fragment));
     rl->handshake_fragment_len = 0;
@@ -53,75 +33,122 @@ void RECORD_LAYER_clear(RECORD_LAYER *rl)
     rl->wpend_ret = 0;
     rl->wpend_buf = NULL;
 
-    SSL3_BUFFER_clear(&rl->rbuf);
-    ssl3_release_write_buffer(rl->s);
-    rl->numrpipes = 0;
-    SSL3_RECORD_clear(rl->rrec, SSL_MAX_PIPELINES);
-
-    RECORD_LAYER_reset_read_sequence(rl);
-    RECORD_LAYER_reset_write_sequence(rl);
+    if (rl->rrlmethod != NULL)
+        rl->rrlmethod->free(rl->rrl); /* Ignore return value */
+    if (rl->wrlmethod != NULL)
+        rl->wrlmethod->free(rl->wrl); /* Ignore return value */
+    BIO_free(rl->rrlnext);
+    rl->rrlmethod = NULL;
+    rl->wrlmethod = NULL;
+    rl->rrlnext = NULL;
+    rl->rrl = NULL;
+    rl->wrl = NULL;
 
     if (rl->d)
         DTLS_RECORD_LAYER_clear(rl);
 }
 
-void RECORD_LAYER_release(RECORD_LAYER *rl)
-{
-    if (SSL3_BUFFER_is_initialised(&rl->rbuf))
-        ssl3_release_read_buffer(rl->s);
-    if (rl->numwpipes > 0)
-        ssl3_release_write_buffer(rl->s);
-    SSL3_RECORD_release(rl->rrec, SSL_MAX_PIPELINES);
-}
-
 /* Checks if we have unprocessed read ahead data pending */
 int RECORD_LAYER_read_pending(const RECORD_LAYER *rl)
 {
-    return SSL3_BUFFER_get_left(&rl->rbuf) != 0;
+    return rl->rrlmethod->unprocessed_read_pending(rl->rrl);
 }
 
 /* Checks if we have decrypted unread record data pending */
 int RECORD_LAYER_processed_read_pending(const RECORD_LAYER *rl)
 {
-    size_t curr_rec = 0, num_recs = RECORD_LAYER_get_numrpipes(rl);
-    const SSL3_RECORD *rr = rl->rrec;
-
-    while (curr_rec < num_recs && SSL3_RECORD_is_read(&rr[curr_rec]))
-        curr_rec++;
-
-    return curr_rec < num_recs;
+    return (rl->curr_rec < rl->num_recs)
+           || rl->rrlmethod->processed_read_pending(rl->rrl);
 }
 
 int RECORD_LAYER_write_pending(const RECORD_LAYER *rl)
 {
-    return (rl->numwpipes > 0)
-        && SSL3_BUFFER_get_left(&rl->wbuf[rl->numwpipes - 1]) != 0;
+    return rl->wpend_tot > 0;
 }
 
-void RECORD_LAYER_reset_read_sequence(RECORD_LAYER *rl)
+static uint32_t ossl_get_max_early_data(SSL_CONNECTION *s)
 {
-    memset(rl->read_sequence, 0, sizeof(rl->read_sequence));
+    uint32_t max_early_data;
+    SSL_SESSION *sess = s->session;
+
+    /*
+     * If we are a client then we always use the max_early_data from the
+     * session/psksession. Otherwise we go with the lowest out of the max early
+     * data set in the session and the configured max_early_data.
+     */
+    if (!s->server && sess->ext.max_early_data == 0) {
+        if (!ossl_assert(s->psksession != NULL
+                         && s->psksession->ext.max_early_data > 0)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+        sess = s->psksession;
+    }
+
+    if (!s->server)
+        max_early_data = sess->ext.max_early_data;
+    else if (s->ext.early_data != SSL_EARLY_DATA_ACCEPTED)
+        max_early_data = s->recv_max_early_data;
+    else
+        max_early_data = s->recv_max_early_data < sess->ext.max_early_data
+                         ? s->recv_max_early_data : sess->ext.max_early_data;
+
+    return max_early_data;
 }
 
-void RECORD_LAYER_reset_write_sequence(RECORD_LAYER *rl)
+static int ossl_early_data_count_ok(SSL_CONNECTION *s, size_t length,
+                                    size_t overhead, int send)
 {
-    memset(rl->write_sequence, 0, sizeof(rl->write_sequence));
+    uint32_t max_early_data;
+
+    max_early_data = ossl_get_max_early_data(s);
+
+    if (max_early_data == 0) {
+        SSLfatal(s, send ? SSL_AD_INTERNAL_ERROR : SSL_AD_UNEXPECTED_MESSAGE,
+                 SSL_R_TOO_MUCH_EARLY_DATA);
+        return 0;
+    }
+
+    /* If we are dealing with ciphertext we need to allow for the overhead */
+    max_early_data += overhead;
+
+    if (s->early_data_count + length > max_early_data) {
+        SSLfatal(s, send ? SSL_AD_INTERNAL_ERROR : SSL_AD_UNEXPECTED_MESSAGE,
+                 SSL_R_TOO_MUCH_EARLY_DATA);
+        return 0;
+    }
+    s->early_data_count += length;
+
+    return 1;
 }
 
 size_t ssl3_pending(const SSL *s)
 {
     size_t i, num = 0;
+    const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s);
 
-    if (s->rlayer.rstate == SSL_ST_READ_BODY)
+    if (sc == NULL)
         return 0;
 
-    for (i = 0; i < RECORD_LAYER_get_numrpipes(&s->rlayer); i++) {
-        if (SSL3_RECORD_get_type(&s->rlayer.rrec[i])
-            != SSL3_RT_APPLICATION_DATA)
-            return 0;
-        num += SSL3_RECORD_get_length(&s->rlayer.rrec[i]);
+    if (SSL_CONNECTION_IS_DTLS(sc)) {
+        TLS_RECORD *rdata;
+        pitem *item, *iter;
+
+        iter = pqueue_iterator(sc->rlayer.d->buffered_app_data.q);
+        while ((item = pqueue_next(&iter)) != NULL) {
+            rdata = item->data;
+            num += rdata->length;
+        }
+    }
+
+    for (i = 0; i < sc->rlayer.num_recs; i++) {
+        if (sc->rlayer.tlsrecs[i].type != SSL3_RT_APPLICATION_DATA)
+            return num;
+        num += sc->rlayer.tlsrecs[i].length;
     }
 
+    num += sc->rlayer.rrlmethod->app_data_pending(sc->rlayer.rrl);
+
     return num;
 }
 
@@ -132,216 +159,59 @@ void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len)
 
 void SSL_set_default_read_buffer_len(SSL *s, size_t len)
 {
-    SSL3_BUFFER_set_default_len(RECORD_LAYER_get_rbuf(&s->rlayer), len);
+    SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
+
+    if (sc == NULL)
+        return;
+    sc->rlayer.default_read_buf_len = len;
 }
 
 const char *SSL_rstate_string_long(const SSL *s)
 {
-    switch (s->rlayer.rstate) {
-    case SSL_ST_READ_HEADER:
-        return "read header";
-    case SSL_ST_READ_BODY:
-        return "read body";
-    case SSL_ST_READ_DONE:
-        return "read done";
-    default:
-        return "unknown";
-    }
-}
+    const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s);
+    const char *lng;
 
-const char *SSL_rstate_string(const SSL *s)
-{
-    switch (s->rlayer.rstate) {
-    case SSL_ST_READ_HEADER:
-        return "RH";
-    case SSL_ST_READ_BODY:
-        return "RB";
-    case SSL_ST_READ_DONE:
-        return "RD";
-    default:
-        return "unknown";
-    }
-}
+    if (sc == NULL)
+        return NULL;
 
-/*
- * Return values are as per SSL_read()
- */
-int ssl3_read_n(SSL *s, size_t n, size_t max, int extend, int clearold,
-                size_t *readbytes)
-{
-    /*
-     * If extend == 0, obtain new n-byte packet; if extend == 1, increase
-     * packet by another n bytes. The packet will be in the sub-array of
-     * s->s3.rbuf.buf specified by s->packet and s->packet_length. (If
-     * s->rlayer.read_ahead is set, 'max' bytes may be stored in rbuf [plus
-     * s->packet_length bytes if extend == 1].)
-     * if clearold == 1, move the packet to the start of the buffer; if
-     * clearold == 0 then leave any old packets where they were
-     */
-    size_t len, left, align = 0;
-    unsigned char *pkt;
-    SSL3_BUFFER *rb;
+    if (sc->rlayer.rrlmethod == NULL || sc->rlayer.rrl == NULL)
+        return "unknown";
 
-    if (n == 0)
-        return 0;
+    sc->rlayer.rrlmethod->get_state(sc->rlayer.rrl, NULL, &lng);
 
-    rb = &s->rlayer.rbuf;
-    if (rb->buf == NULL)
-        if (!ssl3_setup_read_buffer(s)) {
-            /* SSLfatal() already called */
-            return -1;
-        }
+    return lng;
+}
 
-    left = rb->left;
-#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
-    align = (size_t)rb->buf + SSL3_RT_HEADER_LENGTH;
-    align = SSL3_ALIGN_PAYLOAD - 1 - ((align - 1) % SSL3_ALIGN_PAYLOAD);
-#endif
+const char *SSL_rstate_string(const SSL *s)
+{
+    const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s);
+    const char *shrt;
 
-    if (!extend) {
-        /* start with empty packet ... */
-        if (left == 0)
-            rb->offset = align;
-        else if (align != 0 && left >= SSL3_RT_HEADER_LENGTH) {
-            /*
-             * check if next packet length is large enough to justify payload
-             * alignment...
-             */
-            pkt = rb->buf + rb->offset;
-            if (pkt[0] == SSL3_RT_APPLICATION_DATA
-                && (pkt[3] << 8 | pkt[4]) >= 128) {
-                /*
-                 * Note that even if packet is corrupted and its length field
-                 * is insane, we can only be led to wrong decision about
-                 * whether memmove will occur or not. Header values has no
-                 * effect on memmove arguments and therefore no buffer
-                 * overrun can be triggered.
-                 */
-                memmove(rb->buf + align, pkt, left);
-                rb->offset = align;
-            }
-        }
-        s->rlayer.packet = rb->buf + rb->offset;
-        s->rlayer.packet_length = 0;
-        /* ... now we can act as if 'extend' was set */
-    }
+    if (sc == NULL)
+        return NULL;
 
-    len = s->rlayer.packet_length;
-    pkt = rb->buf + align;
-    /*
-     * Move any available bytes to front of buffer: 'len' bytes already
-     * pointed to by 'packet', 'left' extra ones at the end
-     */
-    if (s->rlayer.packet != pkt && clearold == 1) {
-        memmove(pkt, s->rlayer.packet, len + left);
-        s->rlayer.packet = pkt;
-        rb->offset = len + align;
-    }
+    if (sc->rlayer.rrlmethod == NULL || sc->rlayer.rrl == NULL)
+        return "unknown";
 
-    /*
-     * For DTLS/UDP reads should not span multiple packets because the read
-     * operation returns the whole packet at once (as long as it fits into
-     * the buffer).
-     */
-    if (SSL_IS_DTLS(s)) {
-        if (left == 0 && extend)
-            return 0;
-        if (left > 0 && n > left)
-            n = left;
-    }
+    sc->rlayer.rrlmethod->get_state(sc->rlayer.rrl, &shrt, NULL);
 
-    /* if there is enough in the buffer from a previous read, take some */
-    if (left >= n) {
-        s->rlayer.packet_length += n;
-        rb->left = left - n;
-        rb->offset += n;
-        *readbytes = n;
-        return 1;
-    }
+    return shrt;
+}
 
-    /* else we need to read more data */
+static int tls_write_check_pending(SSL_CONNECTION *s, int type,
+                                   const unsigned char *buf, size_t len)
+{
+    if (s->rlayer.wpend_tot == 0)
+        return 0;
 
-    if (n > rb->len - rb->offset) {
-        /* does not happen */
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_READ_N,
-                 ERR_R_INTERNAL_ERROR);
+    /* We have pending data, so do some sanity checks */
+    if ((s->rlayer.wpend_tot > len)
+        || (!(s->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)
+            && (s->rlayer.wpend_buf != buf))
+        || (s->rlayer.wpend_type != type)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_WRITE_RETRY);
         return -1;
     }
-
-    /*
-     * Ktls always reads full records.
-     * Also, we always act like read_ahead is set for DTLS.
-     */
-    if (!BIO_get_ktls_recv(s->rbio) && !s->rlayer.read_ahead
-        && !SSL_IS_DTLS(s)) {
-        /* ignore max parameter */
-        max = n;
-    } else {
-        if (max < n)
-            max = n;
-        if (max > rb->len - rb->offset)
-            max = rb->len - rb->offset;
-    }
-
-    while (left < n) {
-        size_t bioread = 0;
-        int ret;
-
-        /*
-         * 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
-         * possible)
-         */
-
-        clear_sys_error();
-        if (s->rbio != NULL) {
-            s->rwstate = SSL_READING;
-            /* TODO(size_t): Convert this function */
-            ret = BIO_read(s->rbio, pkt + len + left, max - left);
-            if (ret >= 0)
-                bioread = ret;
-            if (ret <= 0
-                    && !BIO_should_retry(s->rbio)
-                    && BIO_eof(s->rbio)) {
-                if (s->options & SSL_OP_IGNORE_UNEXPECTED_EOF) {
-                    SSL_set_shutdown(s, SSL_RECEIVED_SHUTDOWN);
-                    s->s3.warn_alert = SSL_AD_CLOSE_NOTIFY;
-                } else {
-                    SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_SSL3_READ_N,
-                             SSL_R_UNEXPECTED_EOF_WHILE_READING);
-                }
-            }
-        } else {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_READ_N,
-                     SSL_R_READ_BIO_NOT_SET);
-            ret = -1;
-        }
-
-        if (ret <= 0) {
-            rb->left = left;
-            if (s->mode & SSL_MODE_RELEASE_BUFFERS && !SSL_IS_DTLS(s))
-                if (len + left == 0)
-                    ssl3_release_read_buffer(s);
-            return ret;
-        }
-        left += bioread;
-        /*
-         * reads should *never* span multiple packets for DTLS because the
-         * underlying transport protocol is message oriented as opposed to
-         * byte oriented as in the TLS case.
-         */
-        if (SSL_IS_DTLS(s)) {
-            if (n > left)
-                n = left;       /* makes the while condition false */
-        }
-    }
-
-    /* done reading, now the book-keeping */
-    rb->offset += n;
-    rb->left = left - n;
-    s->rlayer.packet_length += n;
-    s->rwstate = SSL_NOTHING;
-    *readbytes = n;
     return 1;
 }
 
@@ -349,18 +219,19 @@ int ssl3_read_n(SSL *s, size_t n, size_t max, int extend, int clearold,
  * Call this to write data in records of type 'type' It will return <= 0 if
  * not all data has been sent or non-blocking IO.
  */
-int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len,
+int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len,
                      size_t *written)
 {
     const unsigned char *buf = buf_;
     size_t tot;
     size_t n, max_send_fragment, split_send_fragment, maxpipes;
-#if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
-    size_t nw;
-#endif
-    SSL3_BUFFER *wb = &s->rlayer.wbuf[0];
     int i;
-    size_t tmpwrit;
+    SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL_ONLY(ssl);
+    OSSL_RECORD_TEMPLATE tmpls[SSL_MAX_PIPELINES];
+    unsigned int recversion;
+
+    if (s == NULL)
+        return -1;
 
     s->rwstate = SSL_NOTHING;
     tot = s->rlayer.wnum;
@@ -368,20 +239,20 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len,
      * ensure that if we end up with a smaller value of data to write out
      * than the original len from a write which didn't complete for
      * non-blocking I/O and also somehow ended up avoiding the check for
-     * this in ssl3_write_pending/SSL_R_BAD_WRITE_RETRY as it must never be
+     * this in tls_write_check_pending/SSL_R_BAD_WRITE_RETRY as it must never be
      * possible to end up with (len-tot) as a large number that will then
      * promptly send beyond the end of the users buffer ... so we trap and
      * report the error in a way the user will notice
      */
     if ((len < s->rlayer.wnum)
-        || ((wb->left != 0) && (len < (s->rlayer.wnum + s->rlayer.wpend_tot)))) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_WRITE_BYTES,
-                 SSL_R_BAD_LENGTH);
+        || ((s->rlayer.wpend_tot != 0)
+            && (len < (s->rlayer.wnum + s->rlayer.wpend_tot)))) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_LENGTH);
         return -1;
     }
 
     if (s->early_data_state == SSL_EARLY_DATA_WRITING
-            && !early_data_count_ok(s, len, 0, 1)) {
+            && !ossl_early_data_count_ok(s, len, 0, 1)) {
         /* SSLfatal() already called */
         return -1;
     }
@@ -393,8 +264,8 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len,
      * into init unless we have writes pending - in which case we should finish
      * doing that first.
      */
-    if (wb->left == 0 && (s->key_update != SSL_KEY_UPDATE_NONE
-                          || s->ext.extra_tickets_expected > 0))
+    if (s->rlayer.wpend_tot == 0 && (s->key_update != SSL_KEY_UPDATE_NONE
+                                     || s->ext.extra_tickets_expected > 0))
         ossl_statem_set_in_init(s, 1);
 
     /*
@@ -402,9 +273,9 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len,
      * between receiving the EoED and the CF - but we don't want to handle those
      * messages yet.
      */
-    if (SSL_in_init(s) && !ossl_statem_get_in_handshake(s)
+    if (SSL_in_init(ssl) && !ossl_statem_get_in_handshake(s)
             && s->early_data_state != SSL_EARLY_DATA_UNAUTH_WRITING) {
-        i = s->handshake_func(s);
+        i = s->handshake_func(ssl);
         /* SSLfatal() already called */
         if (i < 0)
             return i;
@@ -413,845 +284,228 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len,
         }
     }
 
-    /*
-     * first check if there is a SSL3_BUFFER still being written out.  This
-     * will happen with non blocking IO
-     */
-    if (wb->left != 0) {
-        /* SSLfatal() already called if appropriate */
-        i = ssl3_write_pending(s, type, &buf[tot], s->rlayer.wpend_tot,
-                               &tmpwrit);
-        if (i <= 0) {
-            /* XXX should we ssl3_release_write_buffer if i<0? */
-            s->rlayer.wnum = tot;
+    i = tls_write_check_pending(s, type, buf, len);
+    if (i < 0) {
+        /* SSLfatal() already called */
+        return i;
+    } else if (i > 0) {
+        /* Retry needed */
+        i = HANDLE_RLAYER_WRITE_RETURN(s,
+                s->rlayer.wrlmethod->retry_write_records(s->rlayer.wrl));
+        if (i <= 0)
             return i;
-        }
-        tot += tmpwrit;               /* this might be last fragment */
-    }
-#if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
-    /*
-     * Depending on platform multi-block can deliver several *times*
-     * better performance. Downside is that it has to allocate
-     * jumbo buffer to accommodate up to 8 records, but the
-     * compromise is considered worthy.
-     */
-    if (type == SSL3_RT_APPLICATION_DATA &&
-        len >= 4 * (max_send_fragment = ssl_get_max_send_fragment(s)) &&
-        s->compress == NULL && s->msg_callback == NULL &&
-        !SSL_WRITE_ETM(s) && SSL_USE_EXPLICIT_IV(s) &&
-        (BIO_get_ktls_send(s->wbio) == 0) &&
-        EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(s->enc_write_ctx)) &
-        EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK) {
-        unsigned char aad[13];
-        EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param;
-        size_t packlen;
-        int packleni;
-
-        /* minimize address aliasing conflicts */
-        if ((max_send_fragment & 0xfff) == 0)
-            max_send_fragment -= 512;
-
-        if (tot == 0 || wb->buf == NULL) { /* allocate jumbo buffer */
-            ssl3_release_write_buffer(s);
-
-            packlen = EVP_CIPHER_CTX_ctrl(s->enc_write_ctx,
-                                          EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE,
-                                          (int)max_send_fragment, NULL);
-
-            if (len >= 8 * max_send_fragment)
-                packlen *= 8;
-            else
-                packlen *= 4;
-
-            if (!ssl3_setup_write_buffer(s, 1, packlen)) {
-                /* SSLfatal() already called */
-                return -1;
-            }
-        } else if (tot == len) { /* done? */
-            /* free jumbo buffer */
-            ssl3_release_write_buffer(s);
-            *written = tot;
-            return 1;
-        }
-
-        n = (len - tot);
-        for (;;) {
-            if (n < 4 * max_send_fragment) {
-                /* free jumbo buffer */
-                ssl3_release_write_buffer(s);
-                break;
-            }
-
-            if (s->s3.alert_dispatch) {
-                i = s->method->ssl_dispatch_alert(s);
-                if (i <= 0) {
-                    /* SSLfatal() already called if appropriate */
-                    s->rlayer.wnum = tot;
-                    return i;
-                }
-            }
-
-            if (n >= 8 * max_send_fragment)
-                nw = max_send_fragment * (mb_param.interleave = 8);
-            else
-                nw = max_send_fragment * (mb_param.interleave = 4);
-
-            memcpy(aad, s->rlayer.write_sequence, 8);
-            aad[8] = type;
-            aad[9] = (unsigned char)(s->version >> 8);
-            aad[10] = (unsigned char)(s->version);
-            aad[11] = 0;
-            aad[12] = 0;
-            mb_param.out = NULL;
-            mb_param.inp = aad;
-            mb_param.len = nw;
-
-            packleni = EVP_CIPHER_CTX_ctrl(s->enc_write_ctx,
-                                          EVP_CTRL_TLS1_1_MULTIBLOCK_AAD,
-                                          sizeof(mb_param), &mb_param);
-            packlen = (size_t)packleni;
-            if (packleni <= 0 || packlen > wb->len) { /* never happens */
-                /* free jumbo buffer */
-                ssl3_release_write_buffer(s);
-                break;
-            }
-
-            mb_param.out = wb->buf;
-            mb_param.inp = &buf[tot];
-            mb_param.len = nw;
-
-            if (EVP_CIPHER_CTX_ctrl(s->enc_write_ctx,
-                                    EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT,
-                                    sizeof(mb_param), &mb_param) <= 0)
-                return -1;
-
-            s->rlayer.write_sequence[7] += mb_param.interleave;
-            if (s->rlayer.write_sequence[7] < mb_param.interleave) {
-                int j = 6;
-                while (j >= 0 && (++s->rlayer.write_sequence[j--]) == 0) ;
-            }
+        tot += s->rlayer.wpend_tot;
+        s->rlayer.wpend_tot = 0;
+    } /* else no retry required */
 
-            wb->offset = 0;
-            wb->left = packlen;
-
-            s->rlayer.wpend_tot = nw;
-            s->rlayer.wpend_buf = &buf[tot];
-            s->rlayer.wpend_type = type;
-            s->rlayer.wpend_ret = nw;
+    if (tot == 0) {
+        /*
+         * We've not previously sent any data for this write so memorize
+         * arguments so that we can detect bad write retries later
+         */
+        s->rlayer.wpend_tot = 0;
+        s->rlayer.wpend_type = type;
+        s->rlayer.wpend_buf = buf;
+        s->rlayer.wpend_ret = len;
+    }
 
-            i = ssl3_write_pending(s, type, &buf[tot], nw, &tmpwrit);
-            if (i <= 0) {
-                /* SSLfatal() already called if appropriate */
-                if (i < 0 && (!s->wbio || !BIO_should_retry(s->wbio))) {
-                    /* free jumbo buffer */
-                    ssl3_release_write_buffer(s);
-                }
-                s->rlayer.wnum = tot;
-                return i;
-            }
-            if (tmpwrit == n) {
-                /* free jumbo buffer */
-                ssl3_release_write_buffer(s);
-                *written = tot + tmpwrit;
-                return 1;
-            }
-            n -= tmpwrit;
-            tot += tmpwrit;
-        }
-    } else
-#endif  /* !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK */
     if (tot == len) {           /* done? */
-        if (s->mode & SSL_MODE_RELEASE_BUFFERS && !SSL_IS_DTLS(s))
-            ssl3_release_write_buffer(s);
-
         *written = tot;
         return 1;
     }
 
+    /* If we have an alert to send, lets send it */
+    if (s->s3.alert_dispatch > 0) {
+        i = ssl->method->ssl_dispatch_alert(ssl);
+        if (i <= 0) {
+            /* SSLfatal() already called if appropriate */
+            return i;
+        }
+        /* if it went, fall through and send more stuff */
+    }
+
     n = (len - tot);
 
     max_send_fragment = ssl_get_max_send_fragment(s);
     split_send_fragment = ssl_get_split_send_fragment(s);
-    /*
-     * If max_pipelines is 0 then this means "undefined" and we default to
-     * 1 pipeline. Similarly if the cipher does not support pipelined
-     * processing then we also only use 1 pipeline, or if we're not using
-     * explicit IVs
-     */
-    maxpipes = s->max_pipelines;
-    if (maxpipes > SSL_MAX_PIPELINES) {
-        /*
-         * We should have prevented this when we set max_pipelines so we
-         * shouldn't get here
-         */
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_WRITE_BYTES,
-                 ERR_R_INTERNAL_ERROR);
-        return -1;
-    }
-    if (maxpipes == 0
-        || s->enc_write_ctx == NULL
-        || !(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(s->enc_write_ctx))
-             & EVP_CIPH_FLAG_PIPELINE)
-        || !SSL_USE_EXPLICIT_IV(s))
-        maxpipes = 1;
-    if (max_send_fragment == 0 || split_send_fragment == 0
-        || split_send_fragment > max_send_fragment) {
+
+    if (max_send_fragment == 0
+            || split_send_fragment == 0
+            || split_send_fragment > max_send_fragment) {
         /*
          * We should have prevented this when we set/get the split and max send
          * fragments so we shouldn't get here
          */
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_WRITE_BYTES,
-                 ERR_R_INTERNAL_ERROR);
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
         return -1;
     }
 
+    /*
+     * Some servers hang if initial client hello is larger than 256 bytes
+     * and record version number > TLS 1.0
+     */
+    recversion = (s->version == TLS1_3_VERSION) ? TLS1_2_VERSION : s->version;
+    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)
+        recversion = TLS1_VERSION;
+
     for (;;) {
-        size_t pipelens[SSL_MAX_PIPELINES], tmppipelen, remain;
-        size_t numpipes, j;
+        size_t tmppipelen, remain;
+        size_t j, lensofar = 0;
 
-        if (n == 0)
-            numpipes = 1;
-        else
-            numpipes = ((n - 1) / split_send_fragment) + 1;
-        if (numpipes > maxpipes)
-            numpipes = maxpipes;
+        /*
+        * Ask the record layer how it would like to split the amount of data
+        * that we have, and how many of those records it would like in one go.
+        */
+        maxpipes = s->rlayer.wrlmethod->get_max_records(s->rlayer.wrl, type, n,
+                                                        max_send_fragment,
+                                                        &split_send_fragment);
+        /*
+        * If max_pipelines is 0 then this means "undefined" and we default to
+        * whatever the record layer wants to do. Otherwise we use the smallest
+        * value from the number requested by the record layer, and max number
+        * configured by the user.
+        */
+        if (s->max_pipelines > 0 && maxpipes > s->max_pipelines)
+            maxpipes = s->max_pipelines;
+
+        if (maxpipes > SSL_MAX_PIPELINES)
+            maxpipes = SSL_MAX_PIPELINES;
+
+        if (split_send_fragment > max_send_fragment) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            return -1;
+        }
 
-        if (n / numpipes >= max_send_fragment) {
+        if (n / maxpipes >= split_send_fragment) {
             /*
              * We have enough data to completely fill all available
              * pipelines
              */
-            for (j = 0; j < numpipes; j++) {
-                pipelens[j] = max_send_fragment;
+            for (j = 0; j < maxpipes; j++) {
+                tmpls[j].type = type;
+                tmpls[j].version = recversion;
+                tmpls[j].buf = &(buf[tot]) + (j * split_send_fragment);
+                tmpls[j].buflen = split_send_fragment;
             }
+            /* Remember how much data we are going to be sending */
+            s->rlayer.wpend_tot = maxpipes * split_send_fragment;
         } else {
             /* We can partially fill all available pipelines */
-            tmppipelen = n / numpipes;
-            remain = n % numpipes;
-            for (j = 0; j < numpipes; j++) {
-                pipelens[j] = tmppipelen;
-                if (j < remain)
-                    pipelens[j]++;
+            tmppipelen = n / maxpipes;
+            remain = n % maxpipes;
+            /*
+             * If there is a remainder we add an extra byte to the first few
+             * pipelines
+             */
+            if (remain > 0)
+                tmppipelen++;
+            for (j = 0; j < maxpipes; j++) {
+                tmpls[j].type = type;
+                tmpls[j].version = recversion;
+                tmpls[j].buf = &(buf[tot]) + lensofar;
+                tmpls[j].buflen = tmppipelen;
+                lensofar += tmppipelen;
+                if (j + 1 == remain)
+                    tmppipelen--;
             }
+            /* Remember how much data we are going to be sending */
+            s->rlayer.wpend_tot = n;
         }
 
-        i = do_ssl3_write(s, type, &(buf[tot]), pipelens, numpipes, 0,
-                          &tmpwrit);
+        i = HANDLE_RLAYER_WRITE_RETURN(s,
+            s->rlayer.wrlmethod->write_records(s->rlayer.wrl, tmpls, maxpipes));
         if (i <= 0) {
             /* SSLfatal() already called if appropriate */
-            /* XXX should we ssl3_release_write_buffer if i<0? */
             s->rlayer.wnum = tot;
             return i;
         }
 
-        if (tmpwrit == n ||
-            (type == SSL3_RT_APPLICATION_DATA &&
-             (s->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) {
-            /*
-             * next chunk of data should get another prepended empty fragment
-             * in ciphersuites with known-IV weakness:
-             */
-            s->s3.empty_fragment_done = 0;
-
-            if (tmpwrit == n
-                    && (s->mode & SSL_MODE_RELEASE_BUFFERS) != 0
-                    && !SSL_IS_DTLS(s))
-                ssl3_release_write_buffer(s);
-
-            *written = tot + tmpwrit;
+        if (s->rlayer.wpend_tot == n
+                || (type == SSL3_RT_APPLICATION_DATA
+                    && (s->mode & SSL_MODE_ENABLE_PARTIAL_WRITE) != 0)) {
+            *written = tot + s->rlayer.wpend_tot;
+            s->rlayer.wpend_tot = 0;
             return 1;
         }
 
-        n -= tmpwrit;
-        tot += tmpwrit;
+        n -= s->rlayer.wpend_tot;
+        tot += s->rlayer.wpend_tot;
     }
 }
 
-int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
-                  size_t *pipelens, size_t numpipes,
-                  int create_empty_fragment, size_t *written)
+int ossl_tls_handle_rlayer_return(SSL_CONNECTION *s, int writing, int ret,
+                                  char *file, int line)
 {
-    WPACKET pkt[SSL_MAX_PIPELINES];
-    SSL3_RECORD wr[SSL_MAX_PIPELINES];
-    WPACKET *thispkt;
-    SSL3_RECORD *thiswr;
-    unsigned char *recordstart;
-    int i, mac_size, clear = 0;
-    size_t prefix_len = 0;
-    int eivlen = 0;
-    size_t align = 0;
-    SSL3_BUFFER *wb;
-    SSL_SESSION *sess;
-    size_t totlen = 0, len, wpinited = 0;
-    size_t j;
-
-    for (j = 0; j < numpipes; j++)
-        totlen += pipelens[j];
-    /*
-     * first check if there is a SSL3_BUFFER still being written out.  This
-     * will happen with non blocking IO
-     */
-    if (RECORD_LAYER_write_pending(&s->rlayer)) {
-        /* Calls SSLfatal() as required */
-        return ssl3_write_pending(s, type, buf, totlen, written);
-    }
-
-    /* If we have an alert to send, lets send it */
-    if (s->s3.alert_dispatch) {
-        i = s->method->ssl_dispatch_alert(s);
-        if (i <= 0) {
-            /* SSLfatal() already called if appropriate */
-            return i;
-        }
-        /* if it went, fall through and send more stuff */
-    }
-
-    if (s->rlayer.numwpipes < numpipes) {
-        if (!ssl3_setup_write_buffer(s, numpipes, 0)) {
-            /* SSLfatal() already called */
-            return -1;
-        }
-    }
-
-    if (totlen == 0 && !create_empty_fragment)
-        return 0;
+    SSL *ssl = SSL_CONNECTION_GET_SSL(s);
 
-    sess = s->session;
-
-    if ((sess == NULL) ||
-        (s->enc_write_ctx == NULL) || (EVP_MD_CTX_md(s->write_hash) == NULL)) {
-        clear = s->enc_write_ctx ? 0 : 1; /* must be AEAD cipher */
-        mac_size = 0;
+    if (ret == OSSL_RECORD_RETURN_RETRY) {
+        s->rwstate = writing ? SSL_WRITING : SSL_READING;
+        ret = -1;
     } else {
-        /* TODO(siz_t): Convert me */
-        mac_size = EVP_MD_CTX_size(s->write_hash);
-        if (mac_size < 0) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                     ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-    }
-
-    /*
-     * 'create_empty_fragment' is true only when this function calls itself
-     */
-    if (!clear && !create_empty_fragment && !s->s3.empty_fragment_done) {
-        /*
-         * countermeasure against known-IV weakness in CBC ciphersuites (see
-         * http://www.openssl.org/~bodo/tls-cbc.txt)
-         */
-
-        if (s->s3.need_empty_fragments && type == SSL3_RT_APPLICATION_DATA) {
-            /*
-             * recursive function call with 'create_empty_fragment' set; this
-             * prepares and buffers the data for an empty fragment (these
-             * 'prefix_len' bytes are sent out later together with the actual
-             * payload)
-             */
-            size_t tmppipelen = 0;
-            int ret;
-
-            ret = do_ssl3_write(s, type, buf, &tmppipelen, 1, 1, &prefix_len);
-            if (ret <= 0) {
-                /* SSLfatal() already called if appropriate */
-                goto err;
-            }
-
-            if (prefix_len >
-                (SSL3_RT_HEADER_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD)) {
-                /* insufficient space */
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                         ERR_R_INTERNAL_ERROR);
-                goto err;
-            }
-        }
-
-        s->s3.empty_fragment_done = 1;
-    }
-
-    if (BIO_get_ktls_send(s->wbio)) {
-        /*
-         * 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(&s->rlayer.wbuf[0], (unsigned char *)buf);
-        SSL3_BUFFER_set_offset(&s->rlayer.wbuf[0], 0);
-        SSL3_BUFFER_set_app_buffer(&s->rlayer.wbuf[0], 1);
-        goto wpacket_init_complete;
-    }
-
-    if (create_empty_fragment) {
-        wb = &s->rlayer.wbuf[0];
-#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, SSL_F_DO_SSL3_WRITE,
-                     ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-        wpinited = 1;
-    } else if (prefix_len) {
-        wb = &s->rlayer.wbuf[0];
-        if (!WPACKET_init_static_len(&pkt[0],
-                                     SSL3_BUFFER_get_buf(wb),
-                                     SSL3_BUFFER_get_len(wb), 0)
-                || !WPACKET_allocate_bytes(&pkt[0], SSL3_BUFFER_get_offset(wb)
-                                                    + prefix_len, NULL)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                     ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-        wpinited = 1;
-    } else {
-        for (j = 0; j < numpipes; j++) {
-            thispkt = &pkt[j];
-
-            wb = &s->rlayer.wbuf[j];
-#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
-            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, SSL_F_DO_SSL3_WRITE,
-                         ERR_R_INTERNAL_ERROR);
-                goto err;
-            }
-            wpinited++;
-        }
-    }
-
-    /* Explicit IV length, block ciphers appropriate version flag */
-    if (s->enc_write_ctx && SSL_USE_EXPLICIT_IV(s) && !SSL_TREAT_AS_TLS13(s)) {
-        int mode = EVP_CIPHER_CTX_mode(s->enc_write_ctx);
-        if (mode == EVP_CIPH_CBC_MODE) {
-            /* TODO(size_t): Convert me */
-            eivlen = EVP_CIPHER_CTX_iv_length(s->enc_write_ctx);
-            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;
-        }
-    }
-
- wpacket_init_complete:
-
-    totlen = 0;
-    /* Clear our SSL3_RECORD structures */
-    memset(wr, 0, sizeof(wr));
-    for (j = 0; j < numpipes; 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];
-
-        /*
-         * In TLSv1.3, once encrypting, we always use application data for the
-         * record type
-         */
-        if (SSL_TREAT_AS_TLS13(s)
-                && s->enc_write_ctx != NULL
-                && (s->statem.enc_write_state != ENC_WRITE_STATE_WRITE_PLAIN_ALERTS
-                    || type != SSL3_RT_ALERT))
-            rectype = SSL3_RT_APPLICATION_DATA;
-        else
-            rectype = 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(s) == TLS_ST_CW_CLNT_HELLO
-                && !s->renegotiate
-                && TLS1_get_version(s) > TLS1_VERSION
-                && s->hello_retry_request == SSL_HRR_NONE)
-            version = TLS1_VERSION;
-        SSL3_RECORD_set_rec_version(thiswr, version);
-
-        maxcomplen = pipelens[j];
-        if (s->compress != NULL)
-            maxcomplen += SSL3_RT_MAX_COMPRESSED_OVERHEAD;
-
-        /*
-         * When using offload kernel will write the header.
-         * Otherwise write the header now
-         */
-        if (!BIO_get_ktls_send(s->wbio)
-                && (!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, SSL_F_DO_SSL3_WRITE,
-                     ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-
-        /* lets setup the record stuff. */
-        SSL3_RECORD_set_data(thiswr, compressdata);
-        SSL3_RECORD_set_length(thiswr, pipelens[j]);
-        SSL3_RECORD_set_input(thiswr, (unsigned char *)&buf[totlen]);
-        totlen += pipelens[j];
-
-        /*
-         * 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_F_DO_SSL3_WRITE,
-                         SSL_R_COMPRESSION_FAILURE);
-                goto err;
-            }
-        } else {
-            if (BIO_get_ktls_send(s->wbio)) {
-                SSL3_RECORD_reset_data(&wr[j]);
-            } else {
-                if (!WPACKET_memcpy(thispkt, thiswr->input, thiswr->length)) {
-                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                            ERR_R_INTERNAL_ERROR);
-                    goto err;
-                }
-                SSL3_RECORD_reset_input(&wr[j]);
-            }
-        }
-
-        if (SSL_TREAT_AS_TLS13(s)
-                && !BIO_get_ktls_send(s->wbio)
-                && s->enc_write_ctx != NULL
-                && (s->statem.enc_write_state != ENC_WRITE_STATE_WRITE_PLAIN_ALERTS
-                    || type != SSL3_RT_ALERT)) {
-            size_t rlen, max_send_fragment;
-
-            if (!WPACKET_put_bytes_u8(thispkt, type)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                         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(s, 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, SSL_F_DO_SSL3_WRITE,
-                                 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 (!BIO_get_ktls_send(s->wbio) && !SSL_WRITE_ETM(s) && mac_size != 0) {
-            unsigned char *mac;
-
-            if (!WPACKET_allocate_bytes(thispkt, mac_size, &mac)
-                    || !s->method->ssl3_enc->mac(s, thiswr, mac, 1)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                         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 (!BIO_get_ktls_send(s->wbio)) {
-            if (!WPACKET_reserve_bytes(thispkt,
-                                        SSL_RT_MAX_CIPHER_BLOCK_SIZE,
-                                        NULL)
+        s->rwstate = SSL_NOTHING;
+        if (ret == OSSL_RECORD_RETURN_EOF) {
+            if (writing) {
                 /*
-                 * We also need next the amount of bytes written to this
-                 * sub-packet
+                 * This shouldn't happen with a writing operation. We treat it
+                 * as fatal.
                  */
-                || !WPACKET_get_length(thispkt, &len)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                     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, numpipes, 1, NULL, mac_size) < 1) {
-            if (!ossl_statem_in_error(s)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                         ERR_R_INTERNAL_ERROR);
-            }
-            goto err;
-        }
-    } else {
-        if (!BIO_get_ktls_send(s->wbio)) {
-            if (s->method->ssl3_enc->enc(s, wr, numpipes, 1, NULL,
-                                         mac_size) < 1) {
-                if (!ossl_statem_in_error(s)) {
-                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                            ERR_R_INTERNAL_ERROR);
-                }
-                goto err;
-            }
-        }
-    }
-
-    for (j = 0; j < numpipes; j++) {
-        size_t origlen;
-
-        thispkt = &pkt[j];
-        thiswr = &wr[j];
-
-        if (BIO_get_ktls_send(s->wbio))
-            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, SSL_F_DO_SSL3_WRITE,
-                     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)
-                    || !s->method->ssl3_enc->mac(s, thiswr, mac, 1)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                         ERR_R_INTERNAL_ERROR);
-                goto err;
+                ERR_new();
+                ERR_set_debug(file, line, 0);
+                ossl_statem_fatal(s, SSL_AD_INTERNAL_ERROR,
+                                  ERR_R_INTERNAL_ERROR, NULL);
+                ret = OSSL_RECORD_RETURN_FATAL;
+            } else if ((s->options & SSL_OP_IGNORE_UNEXPECTED_EOF) != 0) {
+                SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+                s->s3.warn_alert = SSL_AD_CLOSE_NOTIFY;
+            } else {
+                ERR_new();
+                ERR_set_debug(file, line, 0);
+                ossl_statem_fatal(s, SSL_AD_DECODE_ERROR,
+                                  SSL_R_UNEXPECTED_EOF_WHILE_READING, NULL);
             }
-            SSL3_RECORD_add_length(thiswr, mac_size);
-        }
-
-        if (!WPACKET_get_length(thispkt, &len)
-                || !WPACKET_close(thispkt)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                     ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-
-        if (s->msg_callback) {
-            recordstart = WPACKET_get_curr(thispkt) - len
-                          - SSL3_RT_HEADER_LENGTH;
-            s->msg_callback(1, 0, SSL3_RT_HEADER, recordstart,
-                            SSL3_RT_HEADER_LENGTH, s,
-                            s->msg_callback_arg);
+        } else if (ret == OSSL_RECORD_RETURN_FATAL) {
+            int al = s->rlayer.rrlmethod->get_alert_code(s->rlayer.rrl);
 
-            if (SSL_TREAT_AS_TLS13(s) && s->enc_write_ctx != NULL) {
-                unsigned char ctype = type;
-
-                s->msg_callback(1, s->version, SSL3_RT_INNER_CONTENT_TYPE,
-                                &ctype, 1, s, s->msg_callback_arg);
+            if (al != SSL_AD_NO_ALERT) {
+                ERR_new();
+                ERR_set_debug(file, line, 0);
+                ossl_statem_fatal(s, al, SSL_R_RECORD_LAYER_FAILURE, NULL);
             }
-        }
-
-        if (!WPACKET_finish(thispkt)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                     ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-
-        /* header is added by the kernel when using offload */
-        SSL3_RECORD_add_length(&wr[j], SSL3_RT_HEADER_LENGTH);
-
-        if (create_empty_fragment) {
             /*
-             * we are in a recursive call; just return the length, don't write
-             * out anything here
+             * else some failure but there is no alert code. We don't log an
+             * error for this. The record layer should have logged an error
+             * already or, if not, its due to some sys call error which will be
+             * reported via SSL_ERROR_SYSCALL and errno.
              */
-            if (j > 0) {
-                /* We should never be pipelining an empty fragment!! */
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
-                         ERR_R_INTERNAL_ERROR);
-                goto err;
-            }
-            *written = SSL3_RECORD_get_length(thiswr);
-            return 1;
         }
-
- mac_done:
         /*
-         * we should now have thiswr->data pointing to the encrypted data, which
-         * is thiswr->length long
+         * The record layer distinguishes the cases of EOF, non-fatal
+         * err and retry. Upper layers do not.
+         * If we got a retry or success then *ret is already correct,
+         * otherwise we need to convert the return value.
          */
-        SSL3_RECORD_set_type(thiswr, type); /* not needed but helps for
-                                             * debugging */
-
-        /* now let's set up wb */
-        SSL3_BUFFER_set_left(&s->rlayer.wbuf[j],
-                             prefix_len + SSL3_RECORD_get_length(thiswr));
+        if (ret == OSSL_RECORD_RETURN_NON_FATAL_ERR || ret == OSSL_RECORD_RETURN_EOF)
+            ret = 0;
+        else if (ret < OSSL_RECORD_RETURN_NON_FATAL_ERR)
+            ret = -1;
     }
 
-    /*
-     * memorize arguments so that ssl3_write_pending can detect bad write
-     * retries later
-     */
-    s->rlayer.wpend_tot = totlen;
-    s->rlayer.wpend_buf = buf;
-    s->rlayer.wpend_type = type;
-    s->rlayer.wpend_ret = totlen;
-
-    /* we now just need to write the buffer */
-    return ssl3_write_pending(s, type, buf, totlen, written);
- err:
-    for (j = 0; j < wpinited; j++)
-        WPACKET_cleanup(&pkt[j]);
-    return -1;
+    return ret;
 }
 
-/* if s->s3.wbuf.left != 0, we need to call this
- *
- * Return values are as per SSL_write()
- */
-int ssl3_write_pending(SSL *s, int type, const unsigned char *buf, size_t len,
-                       size_t *written)
+void ssl_release_record(SSL_CONNECTION *s, TLS_RECORD *rr)
 {
-    int i;
-    SSL3_BUFFER *wb = s->rlayer.wbuf;
-    size_t currbuf = 0;
-    size_t tmpwrit = 0;
-
-    if ((s->rlayer.wpend_tot > len)
-        || (!(s->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)
-            && (s->rlayer.wpend_buf != buf))
-        || (s->rlayer.wpend_type != type)) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_WRITE_PENDING,
-                 SSL_R_BAD_WRITE_RETRY);
-        return -1;
-    }
-
-    for (;;) {
-        /* Loop until we find a buffer we haven't written out yet */
-        if (SSL3_BUFFER_get_left(&wb[currbuf]) == 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) && type != SSL3_RT_APPLICATION_DATA) {
-                i = BIO_flush(s->wbio);
-                if (i <= 0)
-                    return i;
-            }
-
-            if (BIO_get_ktls_send(s->wbio)
-                && type != SSL3_RT_APPLICATION_DATA) {
-                BIO_set_ktls_ctrl_msg(s->wbio, type);
-            }
-            /* TODO(size_t): Convert this call */
-            i = BIO_write(s->wbio, (char *)
-                          &(SSL3_BUFFER_get_buf(&wb[currbuf])
-                            [SSL3_BUFFER_get_offset(&wb[currbuf])]),
-                          (unsigned int)SSL3_BUFFER_get_left(&wb[currbuf]));
-            if (i >= 0)
-                tmpwrit = i;
-        } else {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_WRITE_PENDING,
-                     SSL_R_BIO_NOT_SET);
-            i = -1;
-        }
-        if (i > 0 && tmpwrit == SSL3_BUFFER_get_left(&wb[currbuf])) {
-            SSL3_BUFFER_set_left(&wb[currbuf], 0);
-            SSL3_BUFFER_add_offset(&wb[currbuf], tmpwrit);
-            if (currbuf + 1 < s->rlayer.numwpipes)
-                continue;
-            s->rwstate = SSL_NOTHING;
-            *written = s->rlayer.wpend_ret;
-            return 1;
-        } else if (i <= 0) {
-            if (SSL_IS_DTLS(s)) {
-                /*
-                 * For DTLS, just drop it. That's kind of the whole point in
-                 * using a datagram service
-                 */
-                SSL3_BUFFER_set_left(&wb[currbuf], 0);
-            }
-            return i;
-        }
-        SSL3_BUFFER_add_offset(&wb[currbuf], tmpwrit);
-        SSL3_BUFFER_sub_left(&wb[currbuf], tmpwrit);
+    if (rr->rechandle != NULL) {
+        /* The record layer allocated the buffers for this record */
+        s->rlayer.rrlmethod->release_record(s->rlayer.rrl, rr->rechandle);
+    } else {
+        /* We allocated the buffers for this record (only happens with DTLS) */
+        OPENSSL_free(rr->data);
     }
+    s->rlayer.curr_rec++;
 }
 
 /*-
@@ -1267,7 +521,7 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf, size_t len,
  *
  * This function must handle any surprises the peer may have for us, such as
  * Alert records (e.g. close_notify) or renegotiation requests. ChangeCipherSpec
- * messages are treated as if they were handshake messages *if* the |recd_type|
+ * messages are treated as if they were handshake messages *if* the |recvd_type|
  * argument is non NULL.
  * Also if record payloads contain fragments too small to process, we store
  * them until there is enough for the respective protocol (the record protocol
@@ -1283,32 +537,23 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf, size_t len,
  *     Application data protocol
  *             none of our business
  */
-int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
+int ssl3_read_bytes(SSL *ssl, int type, int *recvd_type, unsigned char *buf,
                     size_t len, int peek, size_t *readbytes)
 {
     int i, j, ret;
-    size_t n, curr_rec, num_recs, totalbytes;
-    SSL3_RECORD *rr;
-    SSL3_BUFFER *rbuf;
+    size_t n, curr_rec, totalbytes;
+    TLS_RECORD *rr;
     void (*cb) (const SSL *ssl, int type2, int val) = NULL;
-    int is_tls13 = SSL_IS_TLS13(s);
+    int is_tls13;
+    SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL_ONLY(ssl);
 
-    rbuf = &s->rlayer.rbuf;
+    is_tls13 = SSL_CONNECTION_IS_TLS13(s);
 
-    if (!SSL3_BUFFER_is_initialised(rbuf)) {
-        /* Not initialized yet */
-        if (!ssl3_setup_read_buffer(s)) {
-            /* SSLfatal() already called */
-            return -1;
-        }
-    }
-
-    if ((type && (type != SSL3_RT_APPLICATION_DATA)
-         && (type != SSL3_RT_HANDSHAKE)) || (peek
-                                             && (type !=
-                                                 SSL3_RT_APPLICATION_DATA))) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_READ_BYTES,
-                 ERR_R_INTERNAL_ERROR);
+    if ((type != 0
+            && (type != SSL3_RT_APPLICATION_DATA)
+            && (type != SSL3_RT_HANDSHAKE))
+        || (peek && (type != SSL3_RT_APPLICATION_DATA))) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
         return -1;
     }
 
@@ -1342,9 +587,9 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
      * Now s->rlayer.handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE.
      */
 
-    if (!ossl_statem_get_in_handshake(s) && SSL_in_init(s)) {
+    if (!ossl_statem_get_in_handshake(s) && SSL_in_init(ssl)) {
         /* type == SSL3_RT_APPLICATION_DATA */
-        i = s->handshake_func(s);
+        i = s->handshake_func(ssl);
         /* SSLfatal() already called */
         if (i < 0)
             return i;
@@ -1361,41 +606,33 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
      * rr[i].off,     - offset into 'data' for next read
      * rr[i].length,  - number of bytes.
      */
-    rr = s->rlayer.rrec;
-    num_recs = RECORD_LAYER_get_numrpipes(&s->rlayer);
-
-    do {
-        /* get new records if necessary */
-        if (num_recs == 0) {
-            ret = ssl3_get_record(s);
+    /* get new records if necessary */
+    if (s->rlayer.curr_rec >= s->rlayer.num_recs) {
+        s->rlayer.curr_rec = s->rlayer.num_recs = 0;
+        do {
+            rr = &s->rlayer.tlsrecs[s->rlayer.num_recs];
+
+            ret = HANDLE_RLAYER_READ_RETURN(s,
+                    s->rlayer.rrlmethod->read_record(s->rlayer.rrl,
+                                                     &rr->rechandle,
+                                                     &rr->version, &rr->type,
+                                                     &rr->data, &rr->length,
+                                                     NULL, NULL));
             if (ret <= 0) {
                 /* SSLfatal() already called if appropriate */
                 return ret;
             }
-            num_recs = RECORD_LAYER_get_numrpipes(&s->rlayer);
-            if (num_recs == 0) {
-                /* Shouldn't happen */
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_READ_BYTES,
-                         ERR_R_INTERNAL_ERROR);
-                return -1;
-            }
-        }
-        /* Skip over any records we have already read */
-        for (curr_rec = 0;
-             curr_rec < num_recs && SSL3_RECORD_is_read(&rr[curr_rec]);
-             curr_rec++) ;
-        if (curr_rec == num_recs) {
-            RECORD_LAYER_set_numrpipes(&s->rlayer, 0);
-            num_recs = 0;
-            curr_rec = 0;
-        }
-    } while (num_recs == 0);
-    rr = &rr[curr_rec];
+            rr->off = 0;
+            s->rlayer.num_recs++;
+        } while (s->rlayer.rrlmethod->processed_read_pending(s->rlayer.rrl)
+                 && s->rlayer.num_recs < SSL_MAX_PIPELINES);
+    }
+    rr = &s->rlayer.tlsrecs[s->rlayer.curr_rec];
 
     if (s->rlayer.handshake_fragment_len > 0
-            && SSL3_RECORD_get_type(rr) != SSL3_RT_HANDSHAKE
-            && SSL_IS_TLS13(s)) {
-        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
+            && rr->type != SSL3_RT_HANDSHAKE
+            && SSL_CONNECTION_IS_TLS13(s)) {
+        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE,
                  SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA);
         return -1;
     }
@@ -1404,16 +641,15 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
      * Reset the count of consecutive warning alerts if we've got a non-empty
      * record that isn't an alert.
      */
-    if (SSL3_RECORD_get_type(rr) != SSL3_RT_ALERT
-            && SSL3_RECORD_get_length(rr) != 0)
+    if (rr->type != SSL3_RT_ALERT && rr->length != 0)
         s->rlayer.alert_count = 0;
 
     /* we now have a packet which can be read and processed */
 
     if (s->s3.change_cipher_spec /* set when we receive ChangeCipherSpec,
                                   * reset by ssl3_get_finished */
-        && (SSL3_RECORD_get_type(rr) != SSL3_RT_HANDSHAKE)) {
-        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
+        && (rr->type != SSL3_RT_HANDSHAKE)) {
+        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE,
                  SSL_R_DATA_BETWEEN_CCS_AND_FINISHED);
         return -1;
     }
@@ -1423,13 +659,13 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
      * 'peek' mode)
      */
     if (s->shutdown & SSL_RECEIVED_SHUTDOWN) {
-        SSL3_RECORD_set_length(rr, 0);
+        s->rlayer.curr_rec++;
         s->rwstate = SSL_NOTHING;
         return 0;
     }
 
-    if (type == SSL3_RECORD_get_type(rr)
-        || (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC
+    if (type == rr->type
+        || (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC
             && type == SSL3_RT_HANDSHAKE && recvd_type != NULL
             && !is_tls13)) {
         /*
@@ -1441,39 +677,39 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
          * make sure that we are not getting application data when we are
          * doing a handshake for the first time
          */
-        if (SSL_in_init(s) && (type == SSL3_RT_APPLICATION_DATA) &&
-            (s->enc_read_ctx == NULL)) {
-            SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
-                     SSL_R_APP_DATA_IN_HANDSHAKE);
+        if (SSL_in_init(ssl) && type == SSL3_RT_APPLICATION_DATA
+                && SSL_IS_FIRST_HANDSHAKE(s)) {
+            SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_APP_DATA_IN_HANDSHAKE);
             return -1;
         }
 
         if (type == SSL3_RT_HANDSHAKE
-            && SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC
+            && rr->type == SSL3_RT_CHANGE_CIPHER_SPEC
             && s->rlayer.handshake_fragment_len > 0) {
-            SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
-                     SSL_R_CCS_RECEIVED_EARLY);
+            SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_CCS_RECEIVED_EARLY);
             return -1;
         }
 
         if (recvd_type != NULL)
-            *recvd_type = SSL3_RECORD_get_type(rr);
+            *recvd_type = rr->type;
 
         if (len == 0) {
             /*
-             * Mark a zero length record as read. This ensures multiple calls to
+             * Skip a zero length record. This ensures multiple calls to
              * SSL_read() with a zero length buffer will eventually cause
              * SSL_pending() to report data as being available.
              */
-            if (SSL3_RECORD_get_length(rr) == 0)
-                SSL3_RECORD_set_read(rr);
+            if (rr->length == 0)
+                ssl_release_record(s, rr);
+
             return 0;
         }
 
         totalbytes = 0;
+        curr_rec = s->rlayer.curr_rec;
         do {
-            if (len - totalbytes > SSL3_RECORD_get_length(rr))
-                n = SSL3_RECORD_get_length(rr);
+            if (len - totalbytes > rr->length)
+                n = rr->length;
             else
                 n = len - totalbytes;
 
@@ -1481,33 +717,29 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
             buf += n;
             if (peek) {
                 /* Mark any zero length record as consumed CVE-2016-6305 */
-                if (SSL3_RECORD_get_length(rr) == 0)
-                    SSL3_RECORD_set_read(rr);
+                if (rr->length == 0)
+                    ssl_release_record(s, rr);
             } else {
-                SSL3_RECORD_sub_length(rr, n);
-                SSL3_RECORD_add_off(rr, n);
-                if (SSL3_RECORD_get_length(rr) == 0) {
-                    s->rlayer.rstate = SSL_ST_READ_HEADER;
-                    SSL3_RECORD_set_off(rr, 0);
-                    SSL3_RECORD_set_read(rr);
-                }
+                if (s->options & SSL_OP_CLEANSE_PLAINTEXT)
+                    OPENSSL_cleanse(&(rr->data[rr->off]), n);
+                rr->length -= n;
+                rr->off += n;
+                if (rr->length == 0)
+                    ssl_release_record(s, rr);
             }
-            if (SSL3_RECORD_get_length(rr) == 0
-                || (peek && n == SSL3_RECORD_get_length(rr))) {
-                curr_rec++;
+            if (rr->length == 0
+                || (peek && n == rr->length)) {
                 rr++;
+                curr_rec++;
             }
             totalbytes += n;
-        } while (type == SSL3_RT_APPLICATION_DATA && curr_rec < num_recs
-                 && totalbytes < len);
+        } while (type == SSL3_RT_APPLICATION_DATA
+                    && curr_rec < s->rlayer.num_recs
+                    && totalbytes < len);
         if (totalbytes == 0) {
             /* We must have read empty records. Get more data */
             goto start;
         }
-        if (!peek && curr_rec == num_recs
-            && (s->mode & SSL_MODE_RELEASE_BUFFERS)
-            && SSL3_BUFFER_get_left(rbuf) == 0)
-            ssl3_release_read_buffer(s);
         *readbytes = totalbytes;
         return 1;
     }
@@ -1521,19 +753,18 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
     /*
      * Lets just double check that we've not got an SSLv2 record
      */
-    if (rr->rec_version == SSL2_VERSION) {
+    if (rr->version == SSL2_VERSION) {
         /*
          * Should never happen. ssl3_get_record() should only give us an SSLv2
          * record back if this is the first packet and we are looking for an
          * initial ClientHello. Therefore |type| should always be equal to
          * |rr->type|. If not then something has gone horribly wrong
          */
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_READ_BYTES,
-                 ERR_R_INTERNAL_ERROR);
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
         return -1;
     }
 
-    if (s->method->version == TLS_ANY_VERSION
+    if (ssl->method->version == TLS_ANY_VERSION
         && (s->server || rr->type != SSL3_RT_ALERT)) {
         /*
          * If we've got this far and still haven't decided on what version
@@ -1541,9 +772,8 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
          * with. We shouldn't be receiving anything other than a ClientHello
          * if we are a server.
          */
-        s->version = rr->rec_version;
-        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
-                 SSL_R_UNEXPECTED_MESSAGE);
+        s->version = rr->version;
+        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_UNEXPECTED_MESSAGE);
         return -1;
     }
 
@@ -1552,43 +782,42 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
      * (Possibly rr is 'empty' now, i.e. rr->length may be 0.)
      */
 
-    if (SSL3_RECORD_get_type(rr) == SSL3_RT_ALERT) {
+    if (rr->type == SSL3_RT_ALERT) {
         unsigned int alert_level, alert_descr;
-        unsigned char *alert_bytes = SSL3_RECORD_get_data(rr)
-                                     + SSL3_RECORD_get_off(rr);
+        unsigned char *alert_bytes = rr->data
+                                     + rr->off;
         PACKET alert;
 
-        if (!PACKET_buf_init(&alert, alert_bytes, SSL3_RECORD_get_length(rr))
+        if (!PACKET_buf_init(&alert, alert_bytes, rr->length)
                 || !PACKET_get_1(&alert, &alert_level)
                 || !PACKET_get_1(&alert, &alert_descr)
                 || PACKET_remaining(&alert) != 0) {
-            SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
-                     SSL_R_INVALID_ALERT);
+            SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_INVALID_ALERT);
             return -1;
         }
 
         if (s->msg_callback)
-            s->msg_callback(0, s->version, SSL3_RT_ALERT, alert_bytes, 2, s,
+            s->msg_callback(0, s->version, SSL3_RT_ALERT, alert_bytes, 2, ssl,
                             s->msg_callback_arg);
 
         if (s->info_callback != NULL)
             cb = s->info_callback;
-        else if (s->ctx->info_callback != NULL)
-            cb = s->ctx->info_callback;
+        else if (ssl->ctx->info_callback != NULL)
+            cb = ssl->ctx->info_callback;
 
         if (cb != NULL) {
             j = (alert_level << 8) | alert_descr;
-            cb(s, SSL_CB_READ_ALERT, j);
+            cb(ssl, SSL_CB_READ_ALERT, j);
         }
 
-        if (alert_level == SSL3_AL_WARNING
+        if ((!is_tls13 && alert_level == SSL3_AL_WARNING)
                 || (is_tls13 && alert_descr == SSL_AD_USER_CANCELLED)) {
             s->s3.warn_alert = alert_descr;
-            SSL3_RECORD_set_read(rr);
+            ssl_release_record(s, rr);
 
             s->rlayer.alert_count++;
             if (s->rlayer.alert_count == MAX_WARN_ALERT_COUNT) {
-                SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
+                SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE,
                          SSL_R_TOO_MANY_WARN_ALERTS);
                 return -1;
             }
@@ -1605,16 +834,13 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
             s->shutdown |= SSL_RECEIVED_SHUTDOWN;
             return 0;
         } else if (alert_level == SSL3_AL_FATAL || is_tls13) {
-            char tmp[16];
-
             s->rwstate = SSL_NOTHING;
             s->s3.fatal_alert = alert_descr;
-            SSLfatal(s, SSL_AD_NO_ALERT, SSL_F_SSL3_READ_BYTES,
-                     SSL_AD_REASON_OFFSET + alert_descr);
-            BIO_snprintf(tmp, sizeof tmp, "%d", alert_descr);
-            ERR_add_error_data(2, "SSL alert number ", tmp);
+            SSLfatal_data(s, SSL_AD_NO_ALERT,
+                          SSL_AD_REASON_OFFSET + alert_descr,
+                          "SSL alert number %d", alert_descr);
             s->shutdown |= SSL_RECEIVED_SHUTDOWN;
-            SSL3_RECORD_set_read(rr);
+            ssl_release_record(s, rr);
             SSL_CTX_remove_session(s->session_ctx, s->session);
             return 0;
         } else if (alert_descr == SSL_AD_NO_RENEGOTIATION) {
@@ -1626,21 +852,19 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
              * future we might have a renegotiation where we don't care if
              * the peer refused it where we carry on.
              */
-            SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_F_SSL3_READ_BYTES,
-                     SSL_R_NO_RENEGOTIATION);
+            SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_NO_RENEGOTIATION);
             return -1;
         } else if (alert_level == SSL3_AL_WARNING) {
             /* We ignore any other warning alert in TLSv1.2 and below */
             goto start;
         }
 
-        SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_SSL3_READ_BYTES,
-                 SSL_R_UNKNOWN_ALERT_TYPE);
+        SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_UNKNOWN_ALERT_TYPE);
         return -1;
     }
 
     if ((s->shutdown & SSL_SENT_SHUTDOWN) != 0) {
-        if (SSL3_RECORD_get_type(rr) == SSL3_RT_HANDSHAKE) {
+        if (rr->type == SSL3_RT_HANDSHAKE) {
             BIO *rbio;
 
             /*
@@ -1650,15 +874,14 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
              * because we are unable to write any response due to having already
              * sent close_notify.
              */
-            if (!SSL_IS_TLS13(s)) {
-                SSL3_RECORD_set_length(rr, 0);
-                SSL3_RECORD_set_read(rr);
+            if (!SSL_CONNECTION_IS_TLS13(s)) {
+                ssl_release_record(s, rr);
 
                 if ((s->mode & SSL_MODE_AUTO_RETRY) != 0)
                     goto start;
 
                 s->rwstate = SSL_READING;
-                rbio = SSL_get_rbio(s);
+                rbio = SSL_get_rbio(ssl);
                 BIO_clear_retry_flags(rbio);
                 BIO_set_retry_read(rbio);
                 return -1;
@@ -1671,9 +894,8 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
              * above.
              * No alert sent because we already sent close_notify
              */
-            SSL3_RECORD_set_length(rr, 0);
-            SSL3_RECORD_set_read(rr);
-            SSLfatal(s, SSL_AD_NO_ALERT, SSL_F_SSL3_READ_BYTES,
+            ssl_release_record(s, rr);
+            SSLfatal(s, SSL_AD_NO_ALERT,
                      SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY);
             return -1;
         }
@@ -1685,31 +907,29 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
      * "SHUTDOWN" code above to avoid filling the fragment storage with data
      * that we're just going to discard.
      */
-    if (SSL3_RECORD_get_type(rr) == SSL3_RT_HANDSHAKE) {
+    if (rr->type == SSL3_RT_HANDSHAKE) {
         size_t dest_maxlen = sizeof(s->rlayer.handshake_fragment);
         unsigned char *dest = s->rlayer.handshake_fragment;
         size_t *dest_len = &s->rlayer.handshake_fragment_len;
 
         n = dest_maxlen - *dest_len; /* available space in 'dest' */
-        if (SSL3_RECORD_get_length(rr) < n)
-            n = SSL3_RECORD_get_length(rr); /* available bytes */
+        if (rr->length < n)
+            n = rr->length; /* available bytes */
 
         /* now move 'n' bytes: */
-        memcpy(dest + *dest_len,
-               SSL3_RECORD_get_data(rr) + SSL3_RECORD_get_off(rr), n);
-        SSL3_RECORD_add_off(rr, n);
-        SSL3_RECORD_sub_length(rr, n);
+        memcpy(dest + *dest_len, rr->data + rr->off, n);
+        rr->off += n;
+        rr->length -= n;
         *dest_len += n;
-        if (SSL3_RECORD_get_length(rr) == 0)
-            SSL3_RECORD_set_read(rr);
+        if (rr->length == 0)
+            ssl_release_record(s, rr);
 
         if (*dest_len < dest_maxlen)
             goto start;     /* fragment was too small */
     }
 
-    if (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC) {
-        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
-                 SSL_R_CCS_RECEIVED_EARLY);
+    if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) {
+        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_CCS_RECEIVED_EARLY);
         return -1;
     }
 
@@ -1724,7 +944,7 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
         /* We found handshake data, so we're going back into init */
         ossl_statem_set_in_init(s, 1);
 
-        i = s->handshake_func(s);
+        i = s->handshake_func(ssl);
         /* SSLfatal() already called if appropriate */
         if (i < 0)
             return i;
@@ -1741,8 +961,7 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
             return -1;
 
         if (!(s->mode & SSL_MODE_AUTO_RETRY)) {
-            if (SSL3_BUFFER_get_left(rbuf) == 0) {
-                /* no read-ahead left? */
+            if (!RECORD_LAYER_read_pending(&s->rlayer)) {
                 BIO *bio;
                 /*
                  * In the case where we try to read application data, but we
@@ -1751,7 +970,7 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
                  * problems in the blocking world
                  */
                 s->rwstate = SSL_READING;
-                bio = SSL_get_rbio(s);
+                bio = SSL_get_rbio(ssl);
                 BIO_clear_retry_flags(bio);
                 BIO_set_retry_read(bio);
                 return -1;
@@ -1760,7 +979,7 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
         goto start;
     }
 
-    switch (SSL3_RECORD_get_type(rr)) {
+    switch (rr->type) {
     default:
         /*
          * TLS 1.0 and 1.1 say you SHOULD ignore unrecognised record types, but
@@ -1769,8 +988,7 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
          * no progress is being made and the peer continually sends unrecognised
          * record types, using up resources processing them.
          */
-        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
-                 SSL_R_UNEXPECTED_RECORD);
+        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_UNEXPECTED_RECORD);
         return -1;
     case SSL3_RT_CHANGE_CIPHER_SPEC:
     case SSL3_RT_ALERT:
@@ -1780,8 +998,7 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
          * SSL3_RT_HANDSHAKE when ossl_statem_get_in_handshake(s) is true, but
          * that should not happen when type != rr->type
          */
-        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
-                 ERR_R_INTERNAL_ERROR);
+        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, ERR_R_INTERNAL_ERROR);
         return -1;
     case SSL3_RT_APPLICATION_DATA:
         /*
@@ -1804,45 +1021,368 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
              * decryption was applied. We just skip it and move on to the next
              * record.
              */
-            if (!early_data_count_ok(s, rr->length,
-                                     EARLY_DATA_CIPHERTEXT_OVERHEAD, 0)) {
+            if (!ossl_early_data_count_ok(s, rr->length,
+                                          EARLY_DATA_CIPHERTEXT_OVERHEAD, 0)) {
                 /* SSLfatal() already called */
                 return -1;
             }
-            SSL3_RECORD_set_read(rr);
+            ssl_release_record(s, rr);
             goto start;
         } else {
-            SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_READ_BYTES,
-                     SSL_R_UNEXPECTED_RECORD);
+            SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_UNEXPECTED_RECORD);
             return -1;
         }
     }
 }
 
-void ssl3_record_sequence_update(unsigned char *seq)
-{
-    int i;
-
-    for (i = 7; i >= 0; i--) {
-        ++seq[i];
-        if (seq[i] != 0)
-            break;
-    }
-}
-
 /*
  * Returns true if the current rrec was sent in SSLv2 backwards compatible
  * format and false otherwise.
  */
 int RECORD_LAYER_is_sslv2_record(RECORD_LAYER *rl)
 {
-    return SSL3_RECORD_is_sslv2_record(&rl->rrec[0]);
+    if (SSL_CONNECTION_IS_DTLS(rl->s))
+        return 0;
+    return rl->tlsrecs[0].version == SSL2_VERSION;
 }
 
-/*
- * Returns the length in bytes of the current rrec
- */
-size_t RECORD_LAYER_get_rrec_length(RECORD_LAYER *rl)
+static OSSL_FUNC_rlayer_msg_callback_fn rlayer_msg_callback_wrapper;
+static void rlayer_msg_callback_wrapper(int write_p, int version,
+                                        int content_type, const void *buf,
+                                        size_t len, void *cbarg)
+{
+    SSL_CONNECTION *s = cbarg;
+    SSL *ssl = SSL_CONNECTION_GET_SSL(s);
+
+    if (s->msg_callback != NULL)
+        s->msg_callback(write_p, version, content_type, buf, len, ssl,
+                        s->msg_callback_arg);
+}
+
+static OSSL_FUNC_rlayer_security_fn rlayer_security_wrapper;
+static int rlayer_security_wrapper(void *cbarg, int op, int bits, int nid,
+                                   void *other)
+{
+    SSL_CONNECTION *s = cbarg;
+
+    return ssl_security(s, op, bits, nid, other);
+}
+
+static OSSL_FUNC_rlayer_padding_fn rlayer_padding_wrapper;
+static size_t rlayer_padding_wrapper(void *cbarg, int type, size_t len)
+{
+    SSL_CONNECTION *s = cbarg;
+    SSL *ssl = SSL_CONNECTION_GET_SSL(s);
+
+    return s->rlayer.record_padding_cb(ssl, type, len,
+                                       s->rlayer.record_padding_arg);
+}
+
+static const OSSL_DISPATCH rlayer_dispatch[] = {
+    { OSSL_FUNC_RLAYER_SKIP_EARLY_DATA, (void (*)(void))ossl_statem_skip_early_data },
+    { OSSL_FUNC_RLAYER_MSG_CALLBACK, (void (*)(void))rlayer_msg_callback_wrapper },
+    { OSSL_FUNC_RLAYER_SECURITY, (void (*)(void))rlayer_security_wrapper },
+    { OSSL_FUNC_RLAYER_PADDING, (void (*)(void))rlayer_padding_wrapper },
+    { 0, NULL }
+};
+
+static const OSSL_RECORD_METHOD *ssl_select_next_record_layer(SSL_CONNECTION *s,
+                                                              int direction,
+                                                              int level)
+{
+
+    if (level == OSSL_RECORD_PROTECTION_LEVEL_NONE) {
+        if (SSL_CONNECTION_IS_DTLS(s))
+            return &ossl_dtls_record_method;
+
+        return &ossl_tls_record_method;
+    }
+
+#ifndef OPENSSL_NO_KTLS
+    /* KTLS does not support renegotiation */
+    if (level == OSSL_RECORD_PROTECTION_LEVEL_APPLICATION
+            && (s->options & SSL_OP_ENABLE_KTLS) != 0
+            && (SSL_CONNECTION_IS_TLS13(s) || SSL_IS_FIRST_HANDSHAKE(s)))
+        return &ossl_ktls_record_method;
+#endif
+
+    /* Default to the current OSSL_RECORD_METHOD */
+    return direction == OSSL_RECORD_DIRECTION_READ ? s->rlayer.rrlmethod
+                                                   : s->rlayer.wrlmethod;
+}
+
+static int ssl_post_record_layer_select(SSL_CONNECTION *s, int direction)
 {
-    return SSL3_RECORD_get_length(&rl->rrec[0]);
+    const OSSL_RECORD_METHOD *thismethod;
+    OSSL_RECORD_LAYER *thisrl;
+
+    if (direction == OSSL_RECORD_DIRECTION_READ) {
+        thismethod = s->rlayer.rrlmethod;
+        thisrl = s->rlayer.rrl;
+    } else {
+        thismethod = s->rlayer.wrlmethod;
+        thisrl = s->rlayer.wrl;
+    }
+
+#ifndef OPENSSL_NO_KTLS
+    {
+        SSL *ssl = SSL_CONNECTION_GET_SSL(s);
+
+        if (s->rlayer.rrlmethod == &ossl_ktls_record_method) {
+            /* KTLS does not support renegotiation so disallow it */
+            SSL_set_options(ssl, SSL_OP_NO_RENEGOTIATION);
+        }
+    }
+#endif
+    if (SSL_IS_FIRST_HANDSHAKE(s) && thismethod->set_first_handshake != NULL)
+        thismethod->set_first_handshake(thisrl, 1);
+
+    if (s->max_pipelines != 0 && thismethod->set_max_pipelines != NULL)
+        thismethod->set_max_pipelines(thisrl, s->max_pipelines);
+
+    return 1;
+}
+
+int ssl_set_new_record_layer(SSL_CONNECTION *s, int version,
+                             int direction, int level,
+                             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)
+{
+    OSSL_PARAM options[5], *opts = options;
+    OSSL_PARAM settings[6], *set =  settings;
+    const OSSL_RECORD_METHOD **thismethod;
+    OSSL_RECORD_LAYER **thisrl, *newrl = NULL;
+    BIO *thisbio;
+    SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s);
+    const OSSL_RECORD_METHOD *meth;
+    int use_etm, stream_mac = 0, tlstree = 0;
+    unsigned int maxfrag = (direction == OSSL_RECORD_DIRECTION_WRITE)
+                           ? ssl_get_max_send_fragment(s)
+                           : SSL3_RT_MAX_PLAIN_LENGTH;
+    int use_early_data = 0;
+    uint32_t max_early_data;
+    COMP_METHOD *compm = (comp == NULL) ? NULL : comp->method;
+
+    meth = ssl_select_next_record_layer(s, direction, level);
+
+    if (direction == OSSL_RECORD_DIRECTION_READ) {
+        thismethod = &s->rlayer.rrlmethod;
+        thisrl = &s->rlayer.rrl;
+        thisbio = s->rbio;
+    } else {
+        thismethod = &s->rlayer.wrlmethod;
+        thisrl = &s->rlayer.wrl;
+        thisbio = s->wbio;
+    }
+
+    if (meth == NULL)
+        meth = *thismethod;
+
+    if (!ossl_assert(meth != NULL)) {
+        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    /* Parameters that *may* be supported by a record layer if passed */
+    *opts++ = OSSL_PARAM_construct_uint64(OSSL_LIBSSL_RECORD_LAYER_PARAM_OPTIONS,
+                                          &s->options);
+    *opts++ = OSSL_PARAM_construct_uint32(OSSL_LIBSSL_RECORD_LAYER_PARAM_MODE,
+                                          &s->mode);
+    if (direction == OSSL_RECORD_DIRECTION_READ) {
+        *opts++ = OSSL_PARAM_construct_size_t(OSSL_LIBSSL_RECORD_LAYER_READ_BUFFER_LEN,
+                                              &s->rlayer.default_read_buf_len);
+        *opts++ = OSSL_PARAM_construct_int(OSSL_LIBSSL_RECORD_LAYER_PARAM_READ_AHEAD,
+                                           &s->rlayer.read_ahead);
+    } else {
+        *opts++ = OSSL_PARAM_construct_size_t(OSSL_LIBSSL_RECORD_LAYER_PARAM_BLOCK_PADDING,
+                                              &s->rlayer.block_padding);
+    }
+    *opts = OSSL_PARAM_construct_end();
+
+    /* Parameters that *must* be supported by a record layer if passed */
+    if (direction == OSSL_RECORD_DIRECTION_READ) {
+        use_etm = SSL_READ_ETM(s) ? 1 : 0;
+        if ((s->mac_flags & SSL_MAC_FLAG_READ_MAC_STREAM) != 0)
+            stream_mac = 1;
+
+        if ((s->mac_flags & SSL_MAC_FLAG_READ_MAC_TLSTREE) != 0)
+            tlstree = 1;
+    } else {
+        use_etm = SSL_WRITE_ETM(s) ? 1 : 0;
+        if ((s->mac_flags & SSL_MAC_FLAG_WRITE_MAC_STREAM) != 0)
+            stream_mac = 1;
+
+        if ((s->mac_flags & SSL_MAC_FLAG_WRITE_MAC_TLSTREE) != 0)
+            tlstree = 1;
+    }
+
+    if (use_etm)
+        *set++ = OSSL_PARAM_construct_int(OSSL_LIBSSL_RECORD_LAYER_PARAM_USE_ETM,
+                                          &use_etm);
+
+    if (stream_mac)
+        *set++ = OSSL_PARAM_construct_int(OSSL_LIBSSL_RECORD_LAYER_PARAM_STREAM_MAC,
+                                          &stream_mac);
+
+    if (tlstree)
+        *set++ = OSSL_PARAM_construct_int(OSSL_LIBSSL_RECORD_LAYER_PARAM_TLSTREE,
+                                          &tlstree);
+
+    /*
+     * We only need to do this for the read side. The write side should already
+     * have the correct value due to the ssl_get_max_send_fragment() call above
+     */
+    if (direction == OSSL_RECORD_DIRECTION_READ
+            && s->session != NULL
+            && USE_MAX_FRAGMENT_LENGTH_EXT(s->session))
+        maxfrag = GET_MAX_FRAGMENT_LENGTH(s->session);
+
+
+    if (maxfrag != SSL3_RT_MAX_PLAIN_LENGTH)
+        *set++ = OSSL_PARAM_construct_uint(OSSL_LIBSSL_RECORD_LAYER_PARAM_MAX_FRAG_LEN,
+                                           &maxfrag);
+
+    /*
+     * The record layer must check the amount of early data sent or received
+     * using the early keys. A server also needs to worry about rejected early
+     * data that might arrive when the handshake keys are in force.
+     */
+    if (s->server && direction == OSSL_RECORD_DIRECTION_READ) {
+        use_early_data = (level == OSSL_RECORD_PROTECTION_LEVEL_EARLY
+                          || level == OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE);
+    } else if (!s->server && direction == OSSL_RECORD_DIRECTION_WRITE) {
+        use_early_data = (level == OSSL_RECORD_PROTECTION_LEVEL_EARLY);
+    }
+    if (use_early_data) {
+        max_early_data = ossl_get_max_early_data(s);
+
+        if (max_early_data != 0)
+            *set++ = OSSL_PARAM_construct_uint32(OSSL_LIBSSL_RECORD_LAYER_PARAM_MAX_EARLY_DATA,
+                                                 &max_early_data);
+    }
+
+    *set = OSSL_PARAM_construct_end();
+
+    for (;;) {
+        int rlret;
+        BIO *prev = NULL;
+        BIO *next = NULL;
+        unsigned int epoch = 0;
+        OSSL_DISPATCH rlayer_dispatch_tmp[OSSL_NELEM(rlayer_dispatch)];
+        size_t i, j;
+
+        if (direction == OSSL_RECORD_DIRECTION_READ) {
+            prev = s->rlayer.rrlnext;
+            if (SSL_CONNECTION_IS_DTLS(s)
+                    && level != OSSL_RECORD_PROTECTION_LEVEL_NONE)
+                epoch =  DTLS_RECORD_LAYER_get_r_epoch(&s->rlayer) + 1; /* new epoch */
+
+            if (SSL_CONNECTION_IS_DTLS(s))
+                next = BIO_new(BIO_s_dgram_mem());
+            else
+                next = BIO_new(BIO_s_mem());
+
+            if (next == NULL) {
+                BIO_free(prev);
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+                return 0;
+            }
+            s->rlayer.rrlnext = next;
+        } else {
+            if (SSL_CONNECTION_IS_DTLS(s)
+                    && level != OSSL_RECORD_PROTECTION_LEVEL_NONE)
+                epoch =  DTLS_RECORD_LAYER_get_w_epoch(&s->rlayer) + 1; /* new epoch */
+        }
+
+        /*
+         * Create a copy of the dispatch array, missing out wrappers for
+         * callbacks that we don't need.
+         */
+        for (i = 0, j = 0; i < OSSL_NELEM(rlayer_dispatch); i++) {
+            switch (rlayer_dispatch[i].function_id) {
+            case OSSL_FUNC_RLAYER_MSG_CALLBACK:
+                if (s->msg_callback == NULL)
+                    continue;
+                break;
+            case OSSL_FUNC_RLAYER_PADDING:
+                if (s->rlayer.record_padding_cb == NULL)
+                    continue;
+                break;
+            default:
+                break;
+            }
+            rlayer_dispatch_tmp[j++] = rlayer_dispatch[i];
+        }
+
+        rlret = meth->new_record_layer(sctx->libctx, sctx->propq, version,
+                                       s->server, direction, level, epoch,
+                                       key, keylen, iv, ivlen, mackey,
+                                       mackeylen, ciph, taglen, mactype, md,
+                                       compm, prev, thisbio, next, NULL, NULL,
+                                       settings, options, rlayer_dispatch_tmp,
+                                       s, &newrl);
+        BIO_free(prev);
+        switch (rlret) {
+        case OSSL_RECORD_RETURN_FATAL:
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_RECORD_LAYER_FAILURE);
+            return 0;
+
+        case OSSL_RECORD_RETURN_NON_FATAL_ERR:
+            if (*thismethod != meth && *thismethod != NULL) {
+                /*
+                 * We tried a new record layer method, but it didn't work out,
+                 * so we fallback to the original method and try again
+                 */
+                meth = *thismethod;
+                continue;
+            }
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_SUITABLE_RECORD_LAYER);
+            return 0;
+
+        case OSSL_RECORD_RETURN_SUCCESS:
+            break;
+
+        default:
+            /* Should not happen */
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+        break;
+    }
+
+    /*
+     * Free the old record layer if we have one except in the case of DTLS when
+     * writing and there are still buffered sent messages in our queue. In that
+     * case the record layer is still referenced by those buffered messages for
+     * potential retransmit. Only when those buffered messages get freed do we
+     * free the record layer object (see dtls1_hm_fragment_free)
+     */
+    if (!SSL_CONNECTION_IS_DTLS(s)
+            || direction == OSSL_RECORD_DIRECTION_READ
+            || pqueue_peek(s->d1->sent_messages) == NULL) {
+        if (*thismethod != NULL && !(*thismethod)->free(*thisrl)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+    }
+
+    *thisrl = newrl;
+    *thismethod = meth;
+
+    return ssl_post_record_layer_select(s, direction);
+}
+
+int ssl_set_record_protocol_version(SSL_CONNECTION *s, int vers)
+{
+    if (!ossl_assert(s->rlayer.rrlmethod != NULL)
+            || !ossl_assert(s->rlayer.wrlmethod != NULL))
+        return 0;
+    s->rlayer.rrlmethod->set_protocol_version(s->rlayer.rrl, s->version);
+    s->rlayer.wrlmethod->set_protocol_version(s->rlayer.wrl, s->version);
+
+    return 1;
 }