Convert dtls_write_records to use standard record layer functions
[openssl.git] / ssl / record / rec_layer_s3.c
index c2e6e91e4172d5195285fe4be453aa351e07aba2..04f130bc2eba140185c0c5205945f124bda295bd 100644 (file)
 #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_CONNECTION *s)
 {
     rl->s = s;
@@ -48,9 +39,14 @@ void RECORD_LAYER_clear(RECORD_LAYER *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);
@@ -77,8 +73,11 @@ int RECORD_LAYER_processed_read_pending(const RECORD_LAYER *rl)
 
 int RECORD_LAYER_write_pending(const RECORD_LAYER *rl)
 {
-    return (rl->numwpipes > 0)
-        && SSL3_BUFFER_get_left(&rl->wbuf[rl->numwpipes - 1]) != 0;
+    /* TODO(RECLAYER): Remove me when DTLS is moved to the write record layer */
+    if (SSL_CONNECTION_IS_DTLS(rl->s))
+        return (rl->numwpipes > 0)
+            && SSL3_BUFFER_get_left(&rl->wbuf[rl->numwpipes - 1]) != 0;
+    return rl->wpend_tot > 0;
 }
 
 void RECORD_LAYER_reset_write_sequence(RECORD_LAYER *rl)
@@ -189,19 +188,14 @@ int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len,
     const unsigned char *buf = buf_;
     size_t tot;
     size_t n, max_send_fragment, split_send_fragment, maxpipes;
-    /* TODO(RECLAYER): Re-enable multiblock code */
-#if 0 && !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
-    size_t nw;
-#endif
-    SSL3_BUFFER *wb;
     int i;
     SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL_ONLY(ssl);
     OSSL_RECORD_TEMPLATE tmpls[SSL_MAX_PIPELINES];
+    unsigned int recversion;
 
     if (s == NULL)
         return -1;
 
-    wb = &s->rlayer.wbuf[0];
     s->rwstate = SSL_NOTHING;
     tot = s->rlayer.wnum;
     /*
@@ -214,7 +208,8 @@ int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len,
      * 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)))) {
+        || ((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;
     }
@@ -232,8 +227,8 @@ int ssl3_write_bytes(SSL *ssl, 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);
 
     /*
@@ -258,7 +253,8 @@ int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len,
         return i;
     } else if (i > 0) {
         /* Retry needed */
-        i = tls_retry_write_records(s);
+        i = HANDLE_RLAYER_WRITE_RETURN(s,
+                s->rlayer.wrlmethod->retry_write_records(s->rlayer.wrl));
         if (i <= 0)
             return i;
         tot += s->rlayer.wpend_tot;
@@ -276,184 +272,26 @@ int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len,
         s->rlayer.wpend_ret = len;
     }
 
-/* TODO(RECLAYER): Re-enable multiblock code */
-#if 0 && !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)
-            && (EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(s->enc_write_ctx))
-                & EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK) != 0) {
-        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 = ssl->method->ssl_dispatch_alert(ssl);
-                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) ;
-            }
-
-            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;
-
-            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_CONNECTION_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) {
+        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);
-    /*
-     * TODO(RECLAYER): This comment is now out-of-date and probably needs to
-     * move somewhere else
-     *
-     * 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, ERR_R_INTERNAL_ERROR);
-        return -1;
-    }
-    /* If no explicit maxpipes configuration - default to 1 */
-    /* TODO(RECLAYER): Should we ask the record layer how many pipes it supports? */
-    if (maxpipes <= 0)
-        maxpipes = 1;
-#if 0
-    /* TODO(RECLAYER): FIX ME */
-    if (maxpipes == 0
-        || s->enc_write_ctx == NULL
-        || (EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(s->enc_write_ctx))
-            & EVP_CIPH_FLAG_PIPELINE) == 0
-        || !SSL_USE_EXPLICIT_IV(s))
-        maxpipes = 1;
-#endif
+
     if (max_send_fragment == 0
             || split_send_fragment == 0
             || split_send_fragment > max_send_fragment) {
@@ -465,41 +303,72 @@ int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len,
         return -1;
     }
 
+    /*
+     * Some servers hang if initial client hello is larger than 256 bytes
+     * and record version number > TLS 1.0
+     */
+    /* TODO(RECLAYER): Does this also need to be in the DTLS equivalent code? */
+    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 tmppipelen, remain;
-        size_t numpipes, j, lensofar = 0;
+        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++) {
+            for (j = 0; j < maxpipes; j++) {
                 tmpls[j].type = type;
-                tmpls[j].buf = &(buf[tot]) + (j * max_send_fragment);
-                tmpls[j].buflen = max_send_fragment;
+                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 = numpipes * max_send_fragment;
+            s->rlayer.wpend_tot = maxpipes * split_send_fragment;
         } else {
             /* We can partially fill all available pipelines */
-            tmppipelen = n / numpipes;
-            remain = n % numpipes;
+            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 < numpipes; j++) {
+            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;
@@ -510,21 +379,17 @@ int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len,
             s->rlayer.wpend_tot = n;
         }
 
-        i = tls_write_records(s, tmpls, numpipes);
+        i = HANDLE_RLAYER_WRITE_RETURN(s,
+            s->rlayer.wrlmethod->write_records(s->rlayer.wrl, tmpls, maxpipes));
         if (i <= 0) {
             /* SSLfatal() already called if appropriate */
             s->rlayer.wnum = tot;
             return i;
         }
 
-        if (s->rlayer.wpend_tot == n ||
-            (type == SSL3_RT_APPLICATION_DATA &&
-             (s->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) {
-            if (s->rlayer.wpend_tot == n
-                    && (s->mode & SSL_MODE_RELEASE_BUFFERS) != 0
-                    && !SSL_CONNECTION_IS_DTLS(s))
-                ssl3_release_write_buffer(s);
-
+        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;
@@ -535,564 +400,28 @@ int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, size_t len,
     }
 }
 
-int tls_write_records(SSL_CONNECTION *s, OSSL_RECORD_TEMPLATE *templates,
-                      size_t numtempl)
-{
-    WPACKET pkt[SSL_MAX_PIPELINES + 1];
-    SSL3_RECORD wr[SSL_MAX_PIPELINES + 1];
-    WPACKET *thispkt;
-    SSL3_RECORD *thiswr;
-    unsigned char *recordstart;
-    int i, mac_size, clear = 0;
-    int eivlen = 0;
-    size_t align = 0;
-    SSL3_BUFFER *wb;
-    SSL_SESSION *sess;
-    size_t totlen = 0, len, wpinited = 0;
-    size_t j, prefix = 0;
-    int using_ktls;
-    SSL *ssl = SSL_CONNECTION_GET_SSL(s);
-    OSSL_RECORD_TEMPLATE prefixtempl;
-    OSSL_RECORD_TEMPLATE *thistempl;
-
-    if (!ossl_assert(!RECORD_LAYER_write_pending(&s->rlayer))) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-        goto err;
-    }
-
-    /* If we have an alert to send, lets send it */
-    if (s->s3.alert_dispatch) {
-        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 */
-    }
-
-    sess = s->session;
-
-    if ((sess == NULL)
-            || (s->enc_write_ctx == NULL)
-            || (EVP_MD_CTX_get0_md(s->write_hash) == NULL)) {
-        clear = s->enc_write_ctx ? 0 : 1; /* must be AEAD cipher */
-        mac_size = 0;
-    } else {
-        mac_size = EVP_MD_CTX_get_size(s->write_hash);
-        if (mac_size < 0) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-    }
-
-    /*
-     * 'create_empty_fragment' is true only when we have recursively called
-     * ourselves.
-     * Do we need to do that recursion in order to add an empty record prefix?
-     */
-    prefix = s->s3.need_empty_fragments
-             && !clear
-             && !s->s3.empty_fragment_done
-             && templates[0].type == SSL3_RT_APPLICATION_DATA;
-
-    if (s->rlayer.numwpipes < numtempl + prefix) {
-        /*
-         * TODO(RECLAYER): In the prefix case the first buffer can be a lot
-         * smaller. It is wasteful to allocate a full sized buffer here
-         */
-        if (!ssl3_setup_write_buffer(s, numtempl + prefix, 0)) {
-            /* SSLfatal() already called */
-            return -1;
-        }
-    }
-
-    using_ktls = BIO_get_ktls_send(s->wbio);
-    if (!ossl_assert(!using_ktls || !prefix)) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-        goto err;
-    }
-
-    if (prefix) {
-        /*
-         * countermeasure against known-IV weakness in CBC ciphersuites (see
-         * http://www.openssl.org/~bodo/tls-cbc.txt)
-         */
-        prefixtempl.buf = NULL;
-        prefixtempl.buflen = 0;
-        prefixtempl.type = SSL3_RT_APPLICATION_DATA;
-        wpinited = 1;
-
-        /* TODO(RECLAYER): Do we actually need this? */
-        s->s3.empty_fragment_done = 1;
-
-        wb = &s->rlayer.wbuf[0];
-        /* TODO(RECLAYER): This alignment calculation no longer seems right */
-#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
-        /*
-         * extra fragment would be couple of cipher blocks, which would be
-         * multiple of SSL3_ALIGN_PAYLOAD, so if we want to align the real
-         * payload, then we can just pretend we simply have two headers.
-         */
-        align = (size_t)SSL3_BUFFER_get_buf(wb) + 2 * SSL3_RT_HEADER_LENGTH;
-        align = SSL3_ALIGN_PAYLOAD - 1 - ((align - 1) % SSL3_ALIGN_PAYLOAD);
-#endif
-        SSL3_BUFFER_set_offset(wb, align);
-        if (!WPACKET_init_static_len(&pkt[0], SSL3_BUFFER_get_buf(wb),
-                                     SSL3_BUFFER_get_len(wb), 0)
-                || !WPACKET_allocate_bytes(&pkt[0], align, NULL)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-        wpinited = 1;
-    }
-    for (j = 0; j < numtempl; j++) {
-        thispkt = &pkt[prefix + j];
-
-        wb = &s->rlayer.wbuf[prefix + j];
-        wb->type = templates[j].type;
-
-        if (using_ktls) {
-            /*
-            * ktls doesn't modify the buffer, but to avoid a warning we need
-            * to discard the const qualifier.
-            * This doesn't leak memory because the buffers have been
-            * released when switching to ktls.
-            */
-            SSL3_BUFFER_set_buf(wb, (unsigned char *)templates[j].buf);
-            SSL3_BUFFER_set_offset(wb, 0);
-            SSL3_BUFFER_set_app_buffer(wb, 1);
-        } else {
-#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0
-            align = (size_t)SSL3_BUFFER_get_buf(wb) + SSL3_RT_HEADER_LENGTH;
-            align = SSL3_ALIGN_PAYLOAD - 1
-                    - ((align - 1) % SSL3_ALIGN_PAYLOAD);
-#endif
-            /* TODO(RECLAYER): Is this alignment actually used somewhere? */
-            SSL3_BUFFER_set_offset(wb, align);
-            if (!WPACKET_init_static_len(thispkt, SSL3_BUFFER_get_buf(wb),
-                                        SSL3_BUFFER_get_len(wb), 0)
-                    || !WPACKET_allocate_bytes(thispkt, align, NULL)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-                goto err;
-            }
-            wpinited++;
-        }
-    }
-
-    if (!using_ktls) {
-        /* Explicit IV length, block ciphers appropriate version flag */
-        if (s->enc_write_ctx && SSL_USE_EXPLICIT_IV(s)
-            && !SSL_CONNECTION_TREAT_AS_TLS13(s)) {
-            int mode = EVP_CIPHER_CTX_get_mode(s->enc_write_ctx);
-            if (mode == EVP_CIPH_CBC_MODE) {
-                eivlen = EVP_CIPHER_CTX_get_iv_length(s->enc_write_ctx);
-                if (eivlen < 0) {
-                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_LIBRARY_BUG);
-                    goto err;
-            }
-                if (eivlen <= 1)
-                    eivlen = 0;
-            } else if (mode == EVP_CIPH_GCM_MODE) {
-                /* Need explicit part of IV for GCM mode */
-                eivlen = EVP_GCM_TLS_EXPLICIT_IV_LEN;
-            } else if (mode == EVP_CIPH_CCM_MODE) {
-                eivlen = EVP_CCM_TLS_EXPLICIT_IV_LEN;
-            }
-        }
-    }
-
-    totlen = 0;
-    /* Clear our SSL3_RECORD structures */
-    memset(wr, 0, sizeof(wr));
-    for (j = 0; j < numtempl + prefix; j++) {
-        unsigned int version = (s->version == TLS1_3_VERSION) ? TLS1_2_VERSION
-                                                              : s->version;
-        unsigned char *compressdata = NULL;
-        size_t maxcomplen;
-        unsigned int rectype;
-
-        thispkt = &pkt[j];
-        thiswr = &wr[j];
-        thistempl = (j == 0 && prefix == 1) ? &prefixtempl :
-                                              &templates[j - prefix];
-
-        /*
-         * In TLSv1.3, once encrypting, we always use application data for the
-         * record type
-         */
-        if (SSL_CONNECTION_TREAT_AS_TLS13(s)
-                && s->enc_write_ctx != NULL
-                && (s->statem.enc_write_state != ENC_WRITE_STATE_WRITE_PLAIN_ALERTS
-                    || thistempl->type != SSL3_RT_ALERT))
-            rectype = SSL3_RT_APPLICATION_DATA;
-        else
-            rectype = thistempl->type;
-
-        SSL3_RECORD_set_type(thiswr, rectype);
-
-        /*
-         * Some servers hang if initial client hello is larger than 256 bytes
-         * and record version number > TLS 1.0
-         */
-        if (SSL_get_state(ssl) == TLS_ST_CW_CLNT_HELLO
-                && !s->renegotiate
-                && TLS1_get_version(ssl) > TLS1_VERSION
-                && s->hello_retry_request == SSL_HRR_NONE)
-            version = TLS1_VERSION;
-        SSL3_RECORD_set_rec_version(thiswr, version);
-
-        maxcomplen = thistempl->buflen;
-        if (s->compress != NULL)
-            maxcomplen += SSL3_RT_MAX_COMPRESSED_OVERHEAD;
-
-        /*
-         * When using offload kernel will write the header.
-         * Otherwise write the header now
-         */
-        if (!using_ktls
-                && (!WPACKET_put_bytes_u8(thispkt, rectype)
-                || !WPACKET_put_bytes_u16(thispkt, version)
-                || !WPACKET_start_sub_packet_u16(thispkt)
-                || (eivlen > 0
-                    && !WPACKET_allocate_bytes(thispkt, eivlen, NULL))
-                || (maxcomplen > 0
-                    && !WPACKET_reserve_bytes(thispkt, maxcomplen,
-                                              &compressdata)))) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-
-        /* lets setup the record stuff. */
-        SSL3_RECORD_set_data(thiswr, compressdata);
-        SSL3_RECORD_set_length(thiswr, thistempl->buflen);
-        /*
-         * TODO(RECLAYER): Cast away the const. Should be safe - by why is this
-         * necessary?
-         */
-        SSL3_RECORD_set_input(thiswr, (unsigned char *)thistempl->buf);
-        totlen += thistempl->buflen;
-
-        /*
-         * we now 'read' from thiswr->input, thiswr->length bytes into
-         * thiswr->data
-         */
-
-        /* first we compress */
-        if (s->compress != NULL) {
-            if (!ssl3_do_compress(s, thiswr)
-                    || !WPACKET_allocate_bytes(thispkt, thiswr->length, NULL)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_COMPRESSION_FAILURE);
-                goto err;
-            }
-        } else {
-            if (using_ktls) {
-                SSL3_RECORD_reset_data(&wr[j]);
-            } else {
-                if (!WPACKET_memcpy(thispkt, thiswr->input, thiswr->length)) {
-                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-                    goto err;
-                }
-                SSL3_RECORD_reset_input(&wr[j]);
-            }
-        }
-
-        if (SSL_CONNECTION_TREAT_AS_TLS13(s)
-                && !using_ktls
-                && s->enc_write_ctx != NULL
-                && (s->statem.enc_write_state != ENC_WRITE_STATE_WRITE_PLAIN_ALERTS
-                    || thistempl->type != SSL3_RT_ALERT)) {
-            size_t rlen, max_send_fragment;
-
-            if (!WPACKET_put_bytes_u8(thispkt, thistempl->type)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-                goto err;
-            }
-            SSL3_RECORD_add_length(thiswr, 1);
-
-            /* Add TLS1.3 padding */
-            max_send_fragment = ssl_get_max_send_fragment(s);
-            rlen = SSL3_RECORD_get_length(thiswr);
-            if (rlen < max_send_fragment) {
-                size_t padding = 0;
-                size_t max_padding = max_send_fragment - rlen;
-                if (s->record_padding_cb != NULL) {
-                    padding = s->record_padding_cb(ssl, thistempl->type, rlen,
-                                                   s->record_padding_arg);
-                } else if (s->block_padding > 0) {
-                    size_t mask = s->block_padding - 1;
-                    size_t remainder;
-
-                    /* optimize for power of 2 */
-                    if ((s->block_padding & mask) == 0)
-                        remainder = rlen & mask;
-                    else
-                        remainder = rlen % s->block_padding;
-                    /* don't want to add a block of padding if we don't have to */
-                    if (remainder == 0)
-                        padding = 0;
-                    else
-                        padding = s->block_padding - remainder;
-                }
-                if (padding > 0) {
-                    /* do not allow the record to exceed max plaintext length */
-                    if (padding > max_padding)
-                        padding = max_padding;
-                    if (!WPACKET_memset(thispkt, 0, padding)) {
-                        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                                 ERR_R_INTERNAL_ERROR);
-                        goto err;
-                    }
-                    SSL3_RECORD_add_length(thiswr, padding);
-                }
-            }
-        }
-
-        /*
-         * we should still have the output to thiswr->data and the input from
-         * wr->input. Length should be thiswr->length. thiswr->data still points
-         * in the wb->buf
-         */
-
-        if (!using_ktls && !SSL_WRITE_ETM(s) && mac_size != 0) {
-            unsigned char *mac;
-
-            if (!WPACKET_allocate_bytes(thispkt, mac_size, &mac)
-                    || !ssl->method->ssl3_enc->mac(s, thiswr, mac, 1)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-                goto err;
-            }
-        }
-
-        /*
-         * Reserve some bytes for any growth that may occur during encryption.
-         * This will be at most one cipher block or the tag length if using
-         * AEAD. SSL_RT_MAX_CIPHER_BLOCK_SIZE covers either case.
-         */
-        if (!using_ktls) {
-            if (!WPACKET_reserve_bytes(thispkt,
-                                        SSL_RT_MAX_CIPHER_BLOCK_SIZE,
-                                        NULL)
-                /*
-                 * We also need next the amount of bytes written to this
-                 * sub-packet
-                 */
-                || !WPACKET_get_length(thispkt, &len)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-            goto err;
-            }
-
-            /* Get a pointer to the start of this record excluding header */
-            recordstart = WPACKET_get_curr(thispkt) - len;
-            SSL3_RECORD_set_data(thiswr, recordstart);
-            SSL3_RECORD_reset_input(thiswr);
-            SSL3_RECORD_set_length(thiswr, len);
-        }
-    }
-
-    if (s->statem.enc_write_state == ENC_WRITE_STATE_WRITE_PLAIN_ALERTS) {
-        /*
-         * We haven't actually negotiated the version yet, but we're trying to
-         * send early data - so we need to use the tls13enc function.
-         */
-        if (tls13_enc(s, wr, numtempl, 1, NULL, mac_size) < 1) {
-            if (!ossl_statem_in_error(s)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-            }
-            goto err;
-        }
-    } else {
-        if (!using_ktls) {
-            if (prefix) {
-                if (ssl->method->ssl3_enc->enc(s, wr, 1, 1, NULL, mac_size) < 1) {
-                    if (!ossl_statem_in_error(s)) {
-                        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-                    }
-                    goto err;
-                }
-            }
-            if (ssl->method->ssl3_enc->enc(s, wr + prefix, numtempl, 1, NULL,
-                                           mac_size) < 1) {
-                if (!ossl_statem_in_error(s)) {
-                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-                }
-                goto err;
-            }
-        }
-    }
-
-    for (j = 0; j < prefix + numtempl; j++) {
-        size_t origlen;
-
-        thispkt = &pkt[j];
-        thiswr = &wr[j];
-        thistempl = (prefix == 1 && j == 0) ? &prefixtempl
-                                            : &templates[j - prefix];
-
-        if (using_ktls)
-            goto mac_done;
-
-        /* Allocate bytes for the encryption overhead */
-        if (!WPACKET_get_length(thispkt, &origlen)
-                   /* Encryption should never shrink the data! */
-                || origlen > thiswr->length
-                || (thiswr->length > origlen
-                    && !WPACKET_allocate_bytes(thispkt,
-                                               thiswr->length - origlen,
-                                               NULL))) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-        if (SSL_WRITE_ETM(s) && mac_size != 0) {
-            unsigned char *mac;
-
-            if (!WPACKET_allocate_bytes(thispkt, mac_size, &mac)
-                    || !ssl->method->ssl3_enc->mac(s, thiswr, mac, 1)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-                goto err;
-            }
-            SSL3_RECORD_add_length(thiswr, mac_size);
-        }
-
-        if (!WPACKET_get_length(thispkt, &len)
-                || !WPACKET_close(thispkt)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-
-        if (s->msg_callback) {
-            recordstart = WPACKET_get_curr(thispkt) - len
-                          - SSL3_RT_HEADER_LENGTH;
-            s->msg_callback(1, thiswr->rec_version, SSL3_RT_HEADER, recordstart,
-                            SSL3_RT_HEADER_LENGTH, ssl,
-                            s->msg_callback_arg);
-
-            if (SSL_CONNECTION_TREAT_AS_TLS13(s) && s->enc_write_ctx != NULL) {
-                unsigned char ctype = thistempl->type;
-
-                s->msg_callback(1, thiswr->rec_version, SSL3_RT_INNER_CONTENT_TYPE,
-                                &ctype, 1, ssl, s->msg_callback_arg);
-            }
-        }
-
-        if (!WPACKET_finish(thispkt)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-
-        /* header is added by the kernel when using offload */
-        SSL3_RECORD_add_length(thiswr, SSL3_RT_HEADER_LENGTH);
-
- mac_done:
-        /*
-         * we should now have thiswr->data pointing to the encrypted data, which
-         * is thiswr->length long.
-         * Setting the type is not needed but helps for debugging
-         */
-        SSL3_RECORD_set_type(thiswr, thistempl->type);
-
-        /* now let's set up wb */
-        SSL3_BUFFER_set_left(&s->rlayer.wbuf[j], SSL3_RECORD_get_length(thiswr));
-    }
-
-    /* we now just need to write the buffers */
-    return tls_retry_write_records(s);
- err:
-    for (j = 0; j < wpinited; j++)
-        WPACKET_cleanup(&pkt[j]);
-    return -1;
-}
-
-/* if SSL3_BUFFER_get_left() != 0, we need to call this
- *
- * Return values are as per SSL_write()
- */
-int tls_retry_write_records(SSL_CONNECTION *s)
-{
-    int i;
-    SSL3_BUFFER *thiswb;
-    size_t currbuf = 0;
-    size_t tmpwrit = 0;
-
-    for (;;) {
-        thiswb = &s->rlayer.wbuf[currbuf];
-        /* Loop until we find a buffer we haven't written out yet */
-        if (SSL3_BUFFER_get_left(thiswb) == 0
-            && currbuf < s->rlayer.numwpipes - 1) {
-            currbuf++;
-            continue;
-        }
-        clear_sys_error();
-        if (s->wbio != NULL) {
-            s->rwstate = SSL_WRITING;
-
-            /*
-             * To prevent coalescing of control and data messages,
-             * such as in buffer_write, we flush the BIO
-             */
-            if (BIO_get_ktls_send(s->wbio)
-                    && thiswb->type != SSL3_RT_APPLICATION_DATA) {
-                i = BIO_flush(s->wbio);
-                if (i <= 0)
-                    return i;
-                BIO_set_ktls_ctrl_msg(s->wbio, thiswb->type);
-            }
-            i = BIO_write(s->wbio, (char *)
-                          &(SSL3_BUFFER_get_buf(thiswb)
-                            [SSL3_BUFFER_get_offset(thiswb)]),
-                          (unsigned int)SSL3_BUFFER_get_left(thiswb));
-            if (i >= 0)
-                tmpwrit = i;
-        } else {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BIO_NOT_SET);
-            i = -1;
-        }
-
-        /*
-         * When an empty fragment is sent on a connection using KTLS,
-         * it is sent as a write of zero bytes.  If this zero byte
-         * write succeeds, i will be 0 rather than a non-zero value.
-         * Treat i == 0 as success rather than an error for zero byte
-         * writes to permit this case.
-         */
-        if (i >= 0 && tmpwrit == SSL3_BUFFER_get_left(thiswb)) {
-            SSL3_BUFFER_set_left(thiswb, 0);
-            SSL3_BUFFER_add_offset(thiswb, tmpwrit);
-            if (currbuf + 1 < s->rlayer.numwpipes)
-                continue;
-            s->rwstate = SSL_NOTHING;
-            /*
-             * Next chunk of data should get another prepended empty fragment
-             * in ciphersuites with known-IV weakness:
-             */
-            s->s3.empty_fragment_done = 0;
-            return 1;
-        } else if (i <= 0) {
-            if (SSL_CONNECTION_IS_DTLS(s)) {
-                /*
-                 * For DTLS, just drop it. That's kind of the whole point in
-                 * using a datagram service
-                 */
-                SSL3_BUFFER_set_left(thiswb, 0);
-            }
-            return i;
-        }
-        SSL3_BUFFER_add_offset(thiswb, tmpwrit);
-        SSL3_BUFFER_sub_left(thiswb, tmpwrit);
-    }
-}
-
-int ossl_tls_handle_rlayer_return(SSL_CONNECTION *s, int ret, char *file,
-                                  int line)
+int ossl_tls_handle_rlayer_return(SSL_CONNECTION *s, int writing, int ret,
+                                  char *file, int line)
 {
     SSL *ssl = SSL_CONNECTION_GET_SSL(s);
 
     if (ret == OSSL_RECORD_RETURN_RETRY) {
-        s->rwstate = SSL_READING;
+        s->rwstate = writing ? SSL_WRITING : SSL_READING;
         ret = -1;
     } else {
         s->rwstate = SSL_NOTHING;
         if (ret == OSSL_RECORD_RETURN_EOF) {
-            if (s->options & SSL_OP_IGNORE_UNEXPECTED_EOF) {
+            if (writing) {
+                /*
+                 * This shouldn't happen with a writing operation. We treat it
+                 * as fatal.
+                 */
+                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 {
@@ -1247,7 +576,7 @@ int ssl3_read_bytes(SSL *ssl, int type, int *recvd_type, unsigned char *buf,
         do {
             rr = &s->rlayer.tlsrecs[s->rlayer.num_recs];
 
-            ret = HANDLE_RLAYER_RETURN(s,
+            ret = HANDLE_RLAYER_READ_RETURN(s,
                     s->rlayer.rrlmethod->read_record(s->rlayer.rrl,
                                                      &rr->rechandle,
                                                      &rr->version, &rr->type,
@@ -1714,14 +1043,26 @@ static int rlayer_security_wrapper(void *cbarg, int op, int bits, int nid,
     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)
 {
 
@@ -1741,24 +1082,38 @@ static const OSSL_RECORD_METHOD *ssl_select_next_record_layer(SSL_CONNECTION *s,
 #endif
 
     /* Default to the current OSSL_RECORD_METHOD */
-    return s->rlayer.rrlmethod;
+    return direction == OSSL_RECORD_DIRECTION_READ ? s->rlayer.rrlmethod
+                                                   : s->rlayer.wrlmethod;
 }
 
-static int ssl_post_record_layer_select(SSL_CONNECTION *s)
+static int ssl_post_record_layer_select(SSL_CONNECTION *s, int direction)
 {
+    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);
+    {
+        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);
+        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) && s->rlayer.rrlmethod->set_first_handshake != NULL)
-        s->rlayer.rrlmethod->set_first_handshake(s->rlayer.rrl, 1);
+    if (SSL_IS_FIRST_HANDSHAKE(s) && thismethod->set_first_handshake != NULL)
+        thismethod->set_first_handshake(thisrl, 1);
 
-    if (s->max_pipelines != 0 && s->rlayer.rrlmethod->set_max_pipelines != NULL)
-        s->rlayer.rrlmethod->set_max_pipelines(s->rlayer.rrl, s->max_pipelines);
+    if (s->max_pipelines != 0 && thismethod->set_max_pipelines != NULL)
+        thismethod->set_max_pipelines(thisrl, s->max_pipelines);
 
     return 1;
 }
@@ -1774,26 +1129,35 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version,
 {
     OSSL_PARAM options[5], *opts = options;
     OSSL_PARAM settings[6], *set =  settings;
-    const OSSL_RECORD_METHOD *origmeth = s->rlayer.rrlmethod;
+    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 = SSL3_RT_MAX_PLAIN_LENGTH;
+    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, level);
+    meth = ssl_select_next_record_layer(s, direction, level);
 
-    if (s->rlayer.rrlmethod != NULL && !s->rlayer.rrlmethod->free(s->rlayer.rrl)) {
-        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
-        return 0;
+    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;
     }
 
-    s->rlayer.rrl = NULL;
-    if (meth != NULL)
-        s->rlayer.rrlmethod = meth;
+    if (meth == NULL)
+        meth = *thismethod;
 
-    if (!ossl_assert(s->rlayer.rrlmethod != NULL)) {
+    if (!ossl_assert(meth != NULL)) {
         ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
         return 0;
     }
@@ -1803,10 +1167,15 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version,
                                           &s->options);
     *opts++ = OSSL_PARAM_construct_uint32(OSSL_LIBSSL_RECORD_LAYER_PARAM_MODE,
                                           &s->mode);
-    *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);
+    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 */
@@ -1838,9 +1207,16 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version,
         *set++ = OSSL_PARAM_construct_int(OSSL_LIBSSL_RECORD_LAYER_PARAM_TLSTREE,
                                           &tlstree);
 
-    if (s->session != NULL && USE_MAX_FRAGMENT_LENGTH_EXT(s->session))
+    /*
+     * 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);
@@ -1850,7 +1226,6 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version,
      * using the early keys. A server also needs to worry about rejected early
      * data that might arrive when the handshake keys are in force.
      */
-    /* TODO(RECLAYER): Check this when doing the "write" record layer */
     if (s->server && direction == OSSL_RECORD_DIRECTION_READ) {
         use_early_data = (level == OSSL_RECORD_PROTECTION_LEVEL_EARLY
                           || level == OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE);
@@ -1862,43 +1237,69 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version,
 
         if (max_early_data != 0)
             *set++ = OSSL_PARAM_construct_uint(OSSL_LIBSSL_RECORD_LAYER_PARAM_MAX_EARLY_DATA,
-                                            &max_early_data);
+                                               &max_early_data);
     }
 
     *set = OSSL_PARAM_construct_end();
 
     for (;;) {
         int rlret;
-        BIO *prev = s->rlayer.rrlnext;
-        unsigned int epoch = 0;;
-
-        if (SSL_CONNECTION_IS_DTLS(s)
-                && level != OSSL_RECORD_PROTECTION_LEVEL_NONE)
-            epoch =  DTLS_RECORD_LAYER_get_r_epoch(&s->rlayer) + 1; /* new epoch */
+        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 (SSL_CONNECTION_IS_DTLS(s))
-            s->rlayer.rrlnext = BIO_new(BIO_s_dgram_mem());
-        else
-            s->rlayer.rrlnext = 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 */
+        }
 
-        if (s->rlayer.rrlnext == NULL) {
-            BIO_free(prev);
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
-            return 0;
+        /*
+         * 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 = s->rlayer.rrlmethod->new_record_layer(sctx->libctx,
-                                                      sctx->propq,
-                                                      version, s->server,
-                                                      direction, level, epoch,
-                                                      key, keylen, iv, ivlen,
-                                                      mackey, mackeylen, ciph,
-                                                      taglen, mactype, md, comp,
-                                                      prev, s->rbio,
-                                                      s->rlayer.rrlnext, NULL,
-                                                      NULL, settings, options,
-                                                      rlayer_dispatch, s,
-                                                      &s->rlayer.rrl);
+        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:
@@ -1906,12 +1307,12 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version,
             return 0;
 
         case OSSL_RECORD_RETURN_NON_FATAL_ERR:
-            if (s->rlayer.rrlmethod != origmeth && origmeth != NULL) {
+            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
                  */
-                s->rlayer.rrlmethod = origmeth;
+                meth = *thismethod;
                 continue;
             }
             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_SUITABLE_RECORD_LAYER);
@@ -1928,5 +1329,32 @@ int ssl_set_new_record_layer(SSL_CONNECTION *s, int version,
         break;
     }
 
-    return ssl_post_record_layer_select(s);
+    /*
+     * Free the old record layer if we have one except in the case of DTLS when
+     * writing. In that case the record layer is still referenced by 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) {
+        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;
 }