Move numwpipes in the write record layer
[openssl.git] / ssl / record / rec_layer_d1.c
index 9e043f5df0b51834a579e0bd134fd29e9f1522b9..cad2ddda2dedb8afa23346fc00e0642bb42cad93 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2005-2021 The OpenSSL Project Authors. All Rights Reserved.
  *
- * Licensed under the OpenSSL license (the "License").  You may not use
+ * 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
  * in the file LICENSE in the source distribution or at
  * https://www.openssl.org/source/license.html
@@ -9,35 +9,30 @@
 
 #include <stdio.h>
 #include <errno.h>
-#define USE_SOCKETS
-#include "../ssl_locl.h"
+#include "../ssl_local.h"
 #include <openssl/evp.h>
 #include <openssl/buffer.h>
-#include <openssl/rand.h>
-#include "record_locl.h"
+#include "record_local.h"
+#include "internal/packet.h"
+#include "internal/cryptlib.h"
 
 int DTLS_RECORD_LAYER_new(RECORD_LAYER *rl)
 {
     DTLS_RECORD_LAYER *d;
-    
-    if ((d = OPENSSL_malloc(sizeof(*d))) == NULL)
-        return (0);
 
+    if ((d = OPENSSL_malloc(sizeof(*d))) == NULL) {
+        ERR_raise(ERR_LIB_SSL, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
 
     rl->d = d;
 
-    d->unprocessed_rcds.q = pqueue_new();
-    d->processed_rcds.q = pqueue_new();
     d->buffered_app_data.q = pqueue_new();
 
-    if (d->unprocessed_rcds.q == NULL || d->processed_rcds.q == NULL
-        || d->buffered_app_data.q == NULL) {
-        pqueue_free(d->unprocessed_rcds.q);
-        pqueue_free(d->processed_rcds.q);
-        pqueue_free(d->buffered_app_data.q);
+    if (d->buffered_app_data.q == NULL) {
         OPENSSL_free(d);
         rl->d = NULL;
-        return (0);
+        return 0;
     }
 
     return 1;
@@ -45,9 +40,10 @@ int DTLS_RECORD_LAYER_new(RECORD_LAYER *rl)
 
 void DTLS_RECORD_LAYER_free(RECORD_LAYER *rl)
 {
+    if (rl->d == NULL)
+        return;
+
     DTLS_RECORD_LAYER_clear(rl);
-    pqueue_free(rl->d->unprocessed_rcds.q);
-    pqueue_free(rl->d->processed_rcds.q);
     pqueue_free(rl->d->buffered_app_data.q);
     OPENSSL_free(rl->d);
     rl->d = NULL;
@@ -57,40 +53,22 @@ void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl)
 {
     DTLS_RECORD_LAYER *d;
     pitem *item = NULL;
-    DTLS1_RECORD_DATA *rdata;
-    pqueue *unprocessed_rcds;
-    pqueue *processed_rcds;
+    TLS_RECORD *rec;
     pqueue *buffered_app_data;
 
     d = rl->d;
-    
-    while ((item = pqueue_pop(d->unprocessed_rcds.q)) != NULL) {
-        rdata = (DTLS1_RECORD_DATA *)item->data;
-        OPENSSL_free(rdata->rbuf.buf);
-        OPENSSL_free(item->data);
-        pitem_free(item);
-    }
-
-    while ((item = pqueue_pop(d->processed_rcds.q)) != NULL) {
-        rdata = (DTLS1_RECORD_DATA *)item->data;
-        OPENSSL_free(rdata->rbuf.buf);
-        OPENSSL_free(item->data);
-        pitem_free(item);
-    }
 
     while ((item = pqueue_pop(d->buffered_app_data.q)) != NULL) {
-        rdata = (DTLS1_RECORD_DATA *)item->data;
-        OPENSSL_free(rdata->rbuf.buf);
+        rec = (TLS_RECORD *)item->data;
+        if (rl->s->options & SSL_OP_CLEANSE_PLAINTEXT)
+            OPENSSL_cleanse(rec->data, rec->length);
+        OPENSSL_free(rec->data);
         OPENSSL_free(item->data);
         pitem_free(item);
     }
 
-    unprocessed_rcds = d->unprocessed_rcds.q;
-    processed_rcds = d->processed_rcds.q;
     buffered_app_data = d->buffered_app_data.q;
     memset(d, 0, sizeof(*d));
-    d->unprocessed_rcds.q = unprocessed_rcds;
-    d->processed_rcds.q = processed_rcds;
     d->buffered_app_data.q = buffered_app_data;
 }
 
@@ -98,174 +76,118 @@ void DTLS_RECORD_LAYER_set_saved_w_epoch(RECORD_LAYER *rl, unsigned short e)
 {
     if (e == rl->d->w_epoch - 1) {
         memcpy(rl->d->curr_write_sequence,
-               rl->write_sequence,
-               sizeof(rl->write_sequence));
+               rl->write_sequence, sizeof(rl->write_sequence));
         memcpy(rl->write_sequence,
-               rl->d->last_write_sequence,
-               sizeof(rl->write_sequence));
+               rl->d->last_write_sequence, sizeof(rl->write_sequence));
     } else if (e == rl->d->w_epoch + 1) {
         memcpy(rl->d->last_write_sequence,
-               rl->write_sequence,
-               sizeof(unsigned char[8]));
+               rl->write_sequence, sizeof(unsigned char[8]));
         memcpy(rl->write_sequence,
-               rl->d->curr_write_sequence,
-               sizeof(rl->write_sequence));
+               rl->d->curr_write_sequence, sizeof(rl->write_sequence));
     }
     rl->d->w_epoch = e;
 }
 
-void DTLS_RECORD_LAYER_resync_write(RECORD_LAYER *rl)
-{
-    memcpy(rl->write_sequence, rl->read_sequence, sizeof(rl->write_sequence));
-}
-
-
 void DTLS_RECORD_LAYER_set_write_sequence(RECORD_LAYER *rl, unsigned char *seq)
 {
     memcpy(rl->write_sequence, seq, SEQ_NUM_SIZE);
 }
 
-static int have_handshake_fragment(SSL *s, int type, unsigned char *buf,
-                                   int len);
-
-/* copy buffered record into SSL structure */
-static int dtls1_copy_record(SSL *s, pitem *item)
+int dtls_buffer_record(SSL_CONNECTION *s, TLS_RECORD *rec)
 {
-    DTLS1_RECORD_DATA *rdata;
-
-    rdata = (DTLS1_RECORD_DATA *)item->data;
-
-    SSL3_BUFFER_release(&s->rlayer.rbuf);
-
-    s->rlayer.packet = rdata->packet;
-    s->rlayer.packet_length = rdata->packet_length;
-    memcpy(&s->rlayer.rbuf, &(rdata->rbuf), sizeof(SSL3_BUFFER));
-    memcpy(&s->rlayer.rrec, &(rdata->rrec), sizeof(SSL3_RECORD));
-
-    /* Set proper sequence number for mac calculation */
-    memcpy(&(s->rlayer.read_sequence[2]), &(rdata->packet[5]), 6);
-
-    return (1);
-}
-
-int dtls1_buffer_record(SSL *s, record_pqueue *queue, unsigned char *priority)
-{
-    DTLS1_RECORD_DATA *rdata;
+    TLS_RECORD *rdata;
     pitem *item;
+    record_pqueue *queue = &(s->rlayer.d->buffered_app_data);
 
     /* Limit the size of the queue to prevent DOS attacks */
     if (pqueue_size(queue->q) >= 100)
         return 0;
 
+    /* We don't buffer partially read records */
+    if (!ossl_assert(rec->off == 0))
+        return -1;
+
     rdata = OPENSSL_malloc(sizeof(*rdata));
-    item = pitem_new(priority, rdata);
+    item = pitem_new(rec->seq_num, rdata);
     if (rdata == NULL || item == NULL) {
         OPENSSL_free(rdata);
         pitem_free(item);
-        SSLerr(SSL_F_DTLS1_BUFFER_RECORD, ERR_R_INTERNAL_ERROR);
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
         return -1;
     }
 
-    rdata->packet = s->rlayer.packet;
-    rdata->packet_length = s->rlayer.packet_length;
-    memcpy(&(rdata->rbuf), &s->rlayer.rbuf, sizeof(SSL3_BUFFER));
-    memcpy(&(rdata->rrec), &s->rlayer.rrec, sizeof(SSL3_RECORD));
+    *rdata = *rec;
+    /*
+     * We will release the record from the record layer soon, so we take a copy
+     * now. Copying data isn't good - but this should be infrequent so we
+     * accept it here.
+     */
+    rdata->data = OPENSSL_memdup(rec->data, rec->length);
+    if (rdata->data == NULL) {
+        OPENSSL_free(rdata);
+        pitem_free(item);
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    /*
+     * We use a NULL rechandle to indicate that the data field has been
+     * allocated by us.
+     */
+    rdata->rechandle = NULL;
 
     item->data = rdata;
 
 #ifndef OPENSSL_NO_SCTP
     /* Store bio_dgram_sctp_rcvinfo struct */
-    if (BIO_dgram_is_sctp(SSL_get_rbio(s)) &&
-        (SSL_get_state(s) == TLS_ST_SR_FINISHED
-         || SSL_get_state(s) == TLS_ST_CR_FINISHED)) {
-        BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SCTP_GET_RCVINFO,
+    if (BIO_dgram_is_sctp(SSL_get_rbio(ssl)) &&
+        (SSL_get_state(ssl) == TLS_ST_SR_FINISHED
+         || SSL_get_state(ssl) == TLS_ST_CR_FINISHED)) {
+        BIO_ctrl(SSL_get_rbio(ssl), BIO_CTRL_DGRAM_SCTP_GET_RCVINFO,
                  sizeof(rdata->recordinfo), &rdata->recordinfo);
     }
 #endif
 
-    s->rlayer.packet = NULL;
-    s->rlayer.packet_length = 0;
-    memset(&s->rlayer.rbuf, 0, sizeof(s->rlayer.rbuf));
-    memset(&s->rlayer.rrec, 0, sizeof(s->rlayer.rrec));
-
-    if (!ssl3_setup_buffers(s)) {
-        SSLerr(SSL_F_DTLS1_BUFFER_RECORD, ERR_R_INTERNAL_ERROR);
-        OPENSSL_free(rdata->rbuf.buf);
-        OPENSSL_free(rdata);
-        pitem_free(item);
-        return (-1);
-    }
-
-    /* insert should not fail, since duplicates are dropped */
     if (pqueue_insert(queue->q, item) == NULL) {
-        SSLerr(SSL_F_DTLS1_BUFFER_RECORD, ERR_R_INTERNAL_ERROR);
-        OPENSSL_free(rdata->rbuf.buf);
+        /* Must be a duplicate so ignore it */
+        OPENSSL_free(rdata->data);
         OPENSSL_free(rdata);
         pitem_free(item);
-        return (-1);
     }
 
-    return (1);
+    return 1;
 }
 
-int dtls1_retrieve_buffered_record(SSL *s, record_pqueue *queue)
+/* Unbuffer a previously buffered TLS_RECORD structure if any */
+static void dtls_unbuffer_record(SSL_CONNECTION *s)
 {
+    TLS_RECORD *rdata;
     pitem *item;
 
-    item = pqueue_pop(queue->q);
-    if (item) {
-        dtls1_copy_record(s, item);
+    /* If we already have records to handle then do nothing */
+    if (s->rlayer.curr_rec < s->rlayer.num_recs)
+        return;
 
-        OPENSSL_free(item->data);
-        pitem_free(item);
+    item = pqueue_pop(s->rlayer.d->buffered_app_data.q);
+    if (item != NULL) {
+        rdata = (TLS_RECORD *)item->data;
 
-        return (1);
-    }
-
-    return (0);
-}
-
-/*
- * retrieve a buffered record that belongs to the new epoch, i.e., not
- * processed yet
- */
-#define dtls1_get_unprocessed_record(s) \
-                   dtls1_retrieve_buffered_record((s), \
-                   &((s)->rlayer.d->unprocessed_rcds))
+        s->rlayer.tlsrecs[0] = *rdata;
+        s->rlayer.num_recs = 1;
+        s->rlayer.curr_rec = 0;
 
-
-int dtls1_process_buffered_records(SSL *s)
-{
-    pitem *item;
-
-    item = pqueue_peek(s->rlayer.d->unprocessed_rcds.q);
-    if (item) {
-        /* Check if epoch is current. */
-        if (s->rlayer.d->unprocessed_rcds.epoch != s->rlayer.d->r_epoch)
-            return (1);         /* Nothing to do. */
-
-        /* Process all the records. */
-        while (pqueue_peek(s->rlayer.d->unprocessed_rcds.q)) {
-            dtls1_get_unprocessed_record(s);
-            if (!dtls1_process_record(s))
-                return (0);
-            if (dtls1_buffer_record(s, &(s->rlayer.d->processed_rcds),
-                SSL3_RECORD_get_seq_num(s->rlayer.rrec)) < 0)
-                return -1;
+#ifndef OPENSSL_NO_SCTP
+        /* Restore bio_dgram_sctp_rcvinfo struct */
+        if (BIO_dgram_is_sctp(SSL_get_rbio(s))) {
+            BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SCTP_SET_RCVINFO,
+                        sizeof(rdata->recordinfo), &rdata->recordinfo);
         }
-    }
-
-    /*
-     * sync epoch numbers once all the unprocessed records have been
-     * processed
-     */
-    s->rlayer.d->processed_rcds.epoch = s->rlayer.d->r_epoch;
-    s->rlayer.d->unprocessed_rcds.epoch = s->rlayer.d->r_epoch + 1;
+#endif
 
-    return (1);
+        OPENSSL_free(item->data);
+        pitem_free(item);
+    }
 }
 
-
 /*-
  * Return up to 'len' payload bytes received in 'type' records.
  * 'type' is one of the following:
@@ -296,129 +218,104 @@ int dtls1_process_buffered_records(SSL *s)
  *             none of our business
  */
 int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
-                     int len, int peek)
+                     size_t len, int peek, size_t *readbytes)
 {
-    int al, i, j, ret;
-    unsigned int n;
-    SSL3_RECORD *rr;
+    int i, j, ret;
+    size_t n;
+    TLS_RECORD *rr;
     void (*cb) (const SSL *ssl, int type2, int val) = NULL;
+    SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
 
-    if (!SSL3_BUFFER_is_initialised(&s->rlayer.rbuf)) {
-        /* Not initialized yet */
-        if (!ssl3_setup_buffers(s))
-            return (-1);
-    }
+    if (sc == NULL)
+        return -1;
 
     if ((type && (type != SSL3_RT_APPLICATION_DATA) &&
          (type != SSL3_RT_HANDSHAKE)) ||
         (peek && (type != SSL3_RT_APPLICATION_DATA))) {
-        SSLerr(SSL_F_DTLS1_READ_BYTES, ERR_R_INTERNAL_ERROR);
+        SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
         return -1;
     }
 
-    /*
-     * check whether there's a handshake message (client hello?) waiting
-     */
-    if ((ret = have_handshake_fragment(s, type, buf, len)))
-        return ret;
-
-    /*
-     * Now s->rlayer.d->handshake_fragment_len == 0 if
-     * type == SSL3_RT_HANDSHAKE.
-     */
-
-#ifndef OPENSSL_NO_SCTP
-    /*
-     * Continue handshake if it had to be interrupted to read app data with
-     * SCTP.
-     */
-    if ((!ossl_statem_get_in_handshake(s) && SSL_in_init(s)) ||
-        (BIO_dgram_is_sctp(SSL_get_rbio(s))
-         && ossl_statem_in_sctp_read_sock(s)
-         && s->s3->in_read_app_data != 2))
-#else
-    if (!ossl_statem_get_in_handshake(s) && SSL_in_init(s))
-#endif
-    {
+    if (!ossl_statem_get_in_handshake(sc) && SSL_in_init(s)) {
         /* type == SSL3_RT_APPLICATION_DATA */
-        i = s->handshake_func(s);
+        i = sc->handshake_func(s);
+        /* SSLfatal() already called if appropriate */
         if (i < 0)
-            return (i);
-        if (i == 0) {
-            SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_SSL_HANDSHAKE_FAILURE);
-            return (-1);
-        }
+            return i;
+        if (i == 0)
+            return -1;
     }
 
  start:
-    s->rwstate = SSL_NOTHING;
-
-    /*-
-     * s->s3->rrec.type         - is the type of record
-     * s->s3->rrec.data,    - data
-     * s->s3->rrec.off,     - offset into 'data' for next read
-     * s->s3->rrec.length,  - number of bytes.
-     */
-    rr = s->rlayer.rrec;
+    sc->rwstate = SSL_NOTHING;
 
     /*
      * We are not handshaking and have no data yet, so process data buffered
      * during the last handshake in advance, if any.
      */
-    if (SSL_is_init_finished(s) && SSL3_RECORD_get_length(rr) == 0) {
-        pitem *item;
-        item = pqueue_pop(s->rlayer.d->buffered_app_data.q);
-        if (item) {
-#ifndef OPENSSL_NO_SCTP
-            /* Restore bio_dgram_sctp_rcvinfo struct */
-            if (BIO_dgram_is_sctp(SSL_get_rbio(s))) {
-                DTLS1_RECORD_DATA *rdata = (DTLS1_RECORD_DATA *)item->data;
-                BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SCTP_SET_RCVINFO,
-                         sizeof(rdata->recordinfo), &rdata->recordinfo);
-            }
-#endif
-
-            dtls1_copy_record(s, item);
-
-            OPENSSL_free(item->data);
-            pitem_free(item);
-        }
-    }
+    if (SSL_is_init_finished(s))
+        dtls_unbuffer_record(sc);
 
     /* Check for timeout */
-    if (dtls1_handle_timeout(s) > 0)
+    if (dtls1_handle_timeout(sc) > 0) {
         goto start;
+    } else if (ossl_statem_in_error(sc)) {
+        /* dtls1_handle_timeout() has failed with a fatal error */
+        return -1;
+    }
 
     /* get new packet if necessary */
-    if ((SSL3_RECORD_get_length(rr) == 0)
-            || (s->rlayer.rstate == SSL_ST_READ_BODY)) {
-        ret = dtls1_get_record(s);
-        if (ret <= 0) {
-            ret = dtls1_read_failed(s, ret);
-            /* anything other than a timeout is an error */
-            if (ret <= 0)
-                return (ret);
-            else
-                goto start;
-        }
+    if (sc->rlayer.curr_rec >= sc->rlayer.num_recs) {
+        sc->rlayer.curr_rec = sc->rlayer.num_recs = 0;
+        do {
+            rr = &sc->rlayer.tlsrecs[sc->rlayer.num_recs];
+
+            ret = HANDLE_RLAYER_RETURN(sc,
+                    sc->rlayer.rrlmethod->read_record(sc->rlayer.rrl,
+                                                      &rr->rechandle,
+                                                      &rr->version, &rr->type,
+                                                      &rr->data, &rr->length,
+                                                      &rr->epoch, rr->seq_num));
+            if (ret <= 0) {
+                ret = dtls1_read_failed(sc, ret);
+                /*
+                * Anything other than a timeout is an error. SSLfatal() already
+                * called if appropriate.
+                */
+                if (ret <= 0)
+                    return ret;
+                else
+                    goto start;
+            }
+            rr->off = 0;
+            sc->rlayer.num_recs++;
+        } while (sc->rlayer.rrlmethod->processed_read_pending(sc->rlayer.rrl)
+                 && sc->rlayer.num_recs < SSL_MAX_PIPELINES);
     }
+    rr = &sc->rlayer.tlsrecs[sc->rlayer.curr_rec];
+
+    /*
+     * Reset the count of consecutive warning alerts if we've got a non-empty
+     * record that isn't an alert.
+     */
+    if (rr->type != SSL3_RT_ALERT && rr->length != 0)
+        sc->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)) {
+    if (sc->s3.change_cipher_spec /* set when we receive ChangeCipherSpec,
+                                  * reset by ssl3_get_finished */
+        && (rr->type != SSL3_RT_HANDSHAKE)) {
         /*
          * We now have application data between CCS and Finished. Most likely
          * the packets were reordered on their way, so buffer the application
          * data for later processing rather than dropping the connection.
          */
-        if (dtls1_buffer_record(s, &(s->rlayer.d->buffered_app_data),
-            SSL3_RECORD_get_seq_num(rr)) < 0) {
-            SSLerr(SSL_F_DTLS1_READ_BYTES, ERR_R_INTERNAL_ERROR);
+        if (dtls_buffer_record(sc, rr) < 0) {
+            /* SSLfatal() already called */
             return -1;
         }
-        SSL3_RECORD_set_length(rr, 0);
+        ssl_release_record(sc, rr);
         goto start;
     }
 
@@ -426,15 +323,15 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
      * If the other end has shut down, throw anything we read away (even in
      * 'peek' mode)
      */
-    if (s->shutdown & SSL_RECEIVED_SHUTDOWN) {
-        SSL3_RECORD_set_length(rr, 0);
-        s->rwstate = SSL_NOTHING;
-        return (0);
+    if (sc->shutdown & SSL_RECEIVED_SHUTDOWN) {
+        ssl_release_record(sc, rr);
+        sc->rwstate = SSL_NOTHING;
+        return 0;
     }
 
-    if (type == SSL3_RECORD_get_type(rr)
-            || (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC
-                && type == SSL3_RT_HANDSHAKE && recvd_type != NULL)) {
+    if (type == rr->type
+        || (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC
+            && type == SSL3_RT_HANDSHAKE && recvd_type != NULL)) {
         /*
          * SSL3_RT_APPLICATION_DATA or
          * SSL3_RT_HANDSHAKE or
@@ -445,58 +342,58 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
          * doing a handshake for the first time
          */
         if (SSL_in_init(s) && (type == SSL3_RT_APPLICATION_DATA) &&
-            (s->enc_read_ctx == NULL)) {
-            al = SSL_AD_UNEXPECTED_MESSAGE;
-            SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_APP_DATA_IN_HANDSHAKE);
-            goto f_err;
+            (sc->enc_read_ctx == NULL)) {
+            SSLfatal(sc, SSL_AD_UNEXPECTED_MESSAGE,
+                     SSL_R_APP_DATA_IN_HANDSHAKE);
+            return -1;
         }
 
         if (recvd_type != NULL)
-            *recvd_type = SSL3_RECORD_get_type(rr);
+            *recvd_type = rr->type;
 
-        if (len <= 0)
-            return (len);
+        if (len == 0) {
+            /*
+             * Release 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 (rr->length == 0)
+                ssl_release_record(sc, rr);
+            return 0;
+        }
 
-        if ((unsigned int)len > SSL3_RECORD_get_length(rr))
-            n = SSL3_RECORD_get_length(rr);
+        if (len > rr->length)
+            n = rr->length;
         else
-            n = (unsigned int)len;
-
-        memcpy(buf, &(SSL3_RECORD_get_data(rr)[SSL3_RECORD_get_off(rr)]), n);
-        if (!peek) {
-            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);
-            }
+            n = len;
+
+        memcpy(buf, &(rr->data[rr->off]), n);
+        if (peek) {
+            if (rr->length == 0)
+                ssl_release_record(sc, rr);
+        } else {
+            if (sc->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(sc, rr);
         }
 #ifndef OPENSSL_NO_SCTP
-        /*
-         * We were about to renegotiate but had to read belated application
-         * data first, so retry.
-         */
-        if (BIO_dgram_is_sctp(SSL_get_rbio(s)) &&
-            SSL3_RECORD_get_type(rr) == SSL3_RT_APPLICATION_DATA &&
-            ossl_statem_in_sctp_read_sock(s)) {
-            s->rwstate = SSL_READING;
-            BIO_clear_retry_flags(SSL_get_rbio(s));
-            BIO_set_retry_read(SSL_get_rbio(s));
-        }
-
         /*
          * We might had to delay a close_notify alert because of reordered
          * app data. If there was an alert and there is no message to read
          * anymore, finally set shutdown.
          */
         if (BIO_dgram_is_sctp(SSL_get_rbio(s)) &&
-            s->d1->shutdown_received
-            && !BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) {
-            s->shutdown |= SSL_RECEIVED_SHUTDOWN;
-            return (0);
+            sc->d1->shutdown_received
+            && BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s)) <= 0) {
+            sc->shutdown |= SSL_RECEIVED_SHUTDOWN;
+            return 0;
         }
 #endif
-        return (n);
+        *readbytes = n;
+        return 1;
     }
 
     /*
@@ -504,175 +401,25 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
      * then it was unexpected (Hello Request or Client Hello).
      */
 
-    /*
-     * In case of record types for which we have 'fragment' storage, fill
-     * that so that we can process the data at a fixed place.
-     */
-    {
-        unsigned int k, dest_maxlen = 0;
-        unsigned char *dest = NULL;
-        unsigned int *dest_len = NULL;
-
-        if (SSL3_RECORD_get_type(rr) == SSL3_RT_HANDSHAKE) {
-            dest_maxlen = sizeof s->rlayer.d->handshake_fragment;
-            dest = s->rlayer.d->handshake_fragment;
-            dest_len = &s->rlayer.d->handshake_fragment_len;
-        } else if (SSL3_RECORD_get_type(rr) == SSL3_RT_ALERT) {
-            dest_maxlen = sizeof(s->rlayer.d->alert_fragment);
-            dest = s->rlayer.d->alert_fragment;
-            dest_len = &s->rlayer.d->alert_fragment_len;
-        }
-#ifndef OPENSSL_NO_HEARTBEATS
-        else if (SSL3_RECORD_get_type(rr) == DTLS1_RT_HEARTBEAT) {
-            /* We allow a 0 return */
-            if (dtls1_process_heartbeat(s, SSL3_RECORD_get_data(rr),
-                    SSL3_RECORD_get_length(rr)) < 0) {
-                return -1;
-            }
-            /* Exit and notify application to read again */
-            SSL3_RECORD_set_length(rr, 0);
-            s->rwstate = SSL_READING;
-            BIO_clear_retry_flags(SSL_get_rbio(s));
-            BIO_set_retry_read(SSL_get_rbio(s));
-            return (-1);
-        }
-#endif
-        /* else it's a CCS message, or application data or wrong */
-        else if (SSL3_RECORD_get_type(rr) != SSL3_RT_CHANGE_CIPHER_SPEC) {
-            /*
-             * Application data while renegotiating is allowed. Try again
-             * reading.
-             */
-            if (SSL3_RECORD_get_type(rr)  == SSL3_RT_APPLICATION_DATA) {
-                BIO *bio;
-                s->s3->in_read_app_data = 2;
-                bio = SSL_get_rbio(s);
-                s->rwstate = SSL_READING;
-                BIO_clear_retry_flags(bio);
-                BIO_set_retry_read(bio);
-                return (-1);
-            }
-
-            /* Not certain if this is the right error handling */
-            al = SSL_AD_UNEXPECTED_MESSAGE;
-            SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_UNEXPECTED_RECORD);
-            goto f_err;
-        }
-
-        if (dest_maxlen > 0) {
-            /*
-             * XDTLS: In a pathological case, the Client Hello may be
-             * fragmented--don't always expect dest_maxlen bytes
-             */
-            if (SSL3_RECORD_get_length(rr)  < dest_maxlen) {
-#ifdef DTLS1_AD_MISSING_HANDSHAKE_MESSAGE
-                /*
-                 * for normal alerts rr->length is 2, while
-                 * dest_maxlen is 7 if we were to handle this
-                 * non-existing alert...
-                 */
-                FIX ME
-#endif
-                s->rlayer.rstate = SSL_ST_READ_HEADER;
-                SSL3_RECORD_set_length(rr, 0);
-                goto start;
-            }
+    if (rr->type == SSL3_RT_ALERT) {
+        unsigned int alert_level, alert_descr;
+        unsigned char *alert_bytes = rr->data + rr->off;
+        PACKET alert;
 
-            /* now move 'n' bytes: */
-            for (k = 0; k < dest_maxlen; k++) {
-                dest[k] = SSL3_RECORD_get_data(rr)[SSL3_RECORD_get_off(rr)];
-                SSL3_RECORD_add_off(rr, 1);
-                SSL3_RECORD_add_length(rr, -1);
-            }
-            *dest_len = dest_maxlen;
-        }
-    }
-
-    /*-
-     * s->rlayer.d->handshake_fragment_len == 12  iff  rr->type == SSL3_RT_HANDSHAKE;
-     * s->rlayer.d->alert_fragment_len == 7      iff  rr->type == SSL3_RT_ALERT.
-     * (Possibly rr is 'empty' now, i.e. rr->length may be 0.)
-     */
-
-    /* If we are a client, check for an incoming 'Hello Request': */
-    if ((!s->server) &&
-        (s->rlayer.d->handshake_fragment_len >= DTLS1_HM_HEADER_LENGTH) &&
-        (s->rlayer.d->handshake_fragment[0] == SSL3_MT_HELLO_REQUEST) &&
-        (s->session != NULL) && (s->session->cipher != NULL)) {
-        s->rlayer.d->handshake_fragment_len = 0;
-
-        if ((s->rlayer.d->handshake_fragment[1] != 0) ||
-            (s->rlayer.d->handshake_fragment[2] != 0) ||
-            (s->rlayer.d->handshake_fragment[3] != 0)) {
-            al = SSL_AD_DECODE_ERROR;
-            SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_BAD_HELLO_REQUEST);
-            goto f_err;
+        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(sc, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_INVALID_ALERT);
+            return -1;
         }
 
-        /*
-         * no need to check sequence number on HELLO REQUEST messages
-         */
+        if (sc->msg_callback)
+            sc->msg_callback(0, sc->version, SSL3_RT_ALERT, alert_bytes, 2, s,
+                             sc->msg_callback_arg);
 
-        if (s->msg_callback)
-            s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE,
-                            s->rlayer.d->handshake_fragment, 4, s,
-                            s->msg_callback_arg);
-
-        if (SSL_is_init_finished(s) &&
-            !(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS) &&
-            !s->s3->renegotiate) {
-            s->d1->handshake_read_seq++;
-            s->new_session = 1;
-            ssl3_renegotiate(s);
-            if (ssl3_renegotiate_check(s)) {
-                i = s->handshake_func(s);
-                if (i < 0)
-                    return (i);
-                if (i == 0) {
-                    SSLerr(SSL_F_DTLS1_READ_BYTES,
-                           SSL_R_SSL_HANDSHAKE_FAILURE);
-                    return (-1);
-                }
-
-                if (!(s->mode & SSL_MODE_AUTO_RETRY)) {
-                    if (SSL3_BUFFER_get_left(&s->rlayer.rbuf) == 0) {
-                        /* no read-ahead left? */
-                        BIO *bio;
-                        /*
-                         * In the case where we try to read application data,
-                         * but we trigger an SSL handshake, we return -1 with
-                         * the retry option set.  Otherwise renegotiation may
-                         * cause nasty problems in the blocking world
-                         */
-                        s->rwstate = SSL_READING;
-                        bio = SSL_get_rbio(s);
-                        BIO_clear_retry_flags(bio);
-                        BIO_set_retry_read(bio);
-                        return (-1);
-                    }
-                }
-            }
-        }
-        /*
-         * we either finished a handshake or ignored the request, now try
-         * again to obtain the (application) data we were asked for
-         */
-        goto start;
-    }
-
-    if (s->rlayer.d->alert_fragment_len >= DTLS1_AL_HEADER_LENGTH) {
-        int alert_level = s->rlayer.d->alert_fragment[0];
-        int alert_descr = s->rlayer.d->alert_fragment[1];
-
-        s->rlayer.d->alert_fragment_len = 0;
-
-        if (s->msg_callback)
-            s->msg_callback(0, s->version, SSL3_RT_ALERT,
-                            s->rlayer.d->alert_fragment, 2, s,
-                            s->msg_callback_arg);
-
-        if (s->info_callback != NULL)
-            cb = s->info_callback;
+        if (sc->info_callback != NULL)
+            cb = sc->info_callback;
         else if (s->ctx->info_callback != NULL)
             cb = s->ctx->info_callback;
 
@@ -682,7 +429,16 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
         }
 
         if (alert_level == SSL3_AL_WARNING) {
-            s->s3->warn_alert = alert_descr;
+            sc->s3.warn_alert = alert_descr;
+            ssl_release_record(sc, rr);
+
+            sc->rlayer.alert_count++;
+            if (sc->rlayer.alert_count == MAX_WARN_ALERT_COUNT) {
+                SSLfatal(sc, SSL_AD_UNEXPECTED_MESSAGE,
+                         SSL_R_TOO_MANY_WARN_ALERTS);
+                return -1;
+            }
+
             if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
 #ifndef OPENSSL_NO_SCTP
                 /*
@@ -691,125 +447,123 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
                  * that nothing gets discarded.
                  */
                 if (BIO_dgram_is_sctp(SSL_get_rbio(s)) &&
-                    BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s))) {
-                    s->d1->shutdown_received = 1;
-                    s->rwstate = SSL_READING;
+                    BIO_dgram_sctp_msg_waiting(SSL_get_rbio(s)) > 0) {
+                    sc->d1->shutdown_received = 1;
+                    sc->rwstate = SSL_READING;
                     BIO_clear_retry_flags(SSL_get_rbio(s));
                     BIO_set_retry_read(SSL_get_rbio(s));
                     return -1;
                 }
 #endif
-                s->shutdown |= SSL_RECEIVED_SHUTDOWN;
-                return (0);
-            }
-#if 0
-            /* XXX: this is a possible improvement in the future */
-            /* now check if it's a missing record */
-            if (alert_descr == DTLS1_AD_MISSING_HANDSHAKE_MESSAGE) {
-                unsigned short seq;
-                unsigned int frag_off;
-                unsigned char *p = &(s->rlayer.d->alert_fragment[2]);
-
-                n2s(p, seq);
-                n2l3(p, frag_off);
-
-                dtls1_retransmit_message(s,
-                                         dtls1_get_queue_priority
-                                         (frag->msg_header.seq, 0), frag_off,
-                                         &found);
-                if (!found && SSL_in_init(s)) {
-                    /*
-                     * fprintf( stderr,"in init = %d\n", SSL_in_init(s));
-                     */
-                    /*
-                     * requested a message not yet sent, send an alert
-                     * ourselves
-                     */
-                    ssl3_send_alert(s, SSL3_AL_WARNING,
-                                    DTLS1_AD_MISSING_HANDSHAKE_MESSAGE);
-                }
+                sc->shutdown |= SSL_RECEIVED_SHUTDOWN;
+                return 0;
             }
-#endif
         } else if (alert_level == SSL3_AL_FATAL) {
-            char tmp[16];
-
-            s->rwstate = SSL_NOTHING;
-            s->s3->fatal_alert = alert_descr;
-            SSLerr(SSL_F_DTLS1_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);
-            s->shutdown |= SSL_RECEIVED_SHUTDOWN;
-            SSL_CTX_remove_session(s->session_ctx, s->session);
-            return (0);
+            sc->rwstate = SSL_NOTHING;
+            sc->s3.fatal_alert = alert_descr;
+            SSLfatal_data(sc, SSL_AD_NO_ALERT,
+                          SSL_AD_REASON_OFFSET + alert_descr,
+                          "SSL alert number %d", alert_descr);
+            sc->shutdown |= SSL_RECEIVED_SHUTDOWN;
+            ssl_release_record(sc, rr);
+            SSL_CTX_remove_session(sc->session_ctx, sc->session);
+            return 0;
         } else {
-            al = SSL_AD_ILLEGAL_PARAMETER;
-            SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_UNKNOWN_ALERT_TYPE);
-            goto f_err;
+            SSLfatal(sc, SSL_AD_ILLEGAL_PARAMETER, SSL_R_UNKNOWN_ALERT_TYPE);
+            return -1;
         }
 
         goto start;
     }
 
-    if (s->shutdown & SSL_SENT_SHUTDOWN) { /* but we have not received a
+    if (sc->shutdown & SSL_SENT_SHUTDOWN) { /* but we have not received a
                                             * shutdown */
-        s->rwstate = SSL_NOTHING;
-        SSL3_RECORD_set_length(rr, 0);
-        return (0);
+        sc->rwstate = SSL_NOTHING;
+        ssl_release_record(sc, rr);
+        return 0;
     }
 
-    if (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC) {
+    if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) {
         /*
          * We can't process a CCS now, because previous handshake messages
          * are still missing, so just drop it.
          */
-        SSL3_RECORD_set_length(rr, 0);
+        ssl_release_record(sc, rr);
         goto start;
     }
 
     /*
      * Unexpected handshake message (Client Hello, or protocol violation)
      */
-    if ((s->rlayer.d->handshake_fragment_len >= DTLS1_HM_HEADER_LENGTH) &&
-        !ossl_statem_get_in_handshake(s)) {
+    if (rr->type == SSL3_RT_HANDSHAKE && !ossl_statem_get_in_handshake(sc)) {
         struct hm_header_st msg_hdr;
 
-        /* this may just be a stale retransmit */
-        dtls1_get_message_header(rr->data, &msg_hdr);
-        if (SSL3_RECORD_get_epoch(rr) != s->rlayer.d->r_epoch) {
-            SSL3_RECORD_set_length(rr, 0);
+        /*
+         * This may just be a stale retransmit. Also sanity check that we have
+         * at least enough record bytes for a message header
+         */
+        if (rr->epoch != sc->rlayer.d->r_epoch
+                || rr->length < DTLS1_HM_HEADER_LENGTH) {
+            ssl_release_record(sc, rr);
             goto start;
         }
 
+        dtls1_get_message_header(rr->data, &msg_hdr);
+
         /*
          * If we are server, we may have a repeated FINISHED of the client
          * here, then retransmit our CCS and FINISHED.
          */
         if (msg_hdr.type == SSL3_MT_FINISHED) {
-            if (dtls1_check_timeout_num(s) < 0)
+            if (dtls1_check_timeout_num(sc) < 0) {
+                /* SSLfatal) already called */
                 return -1;
+            }
 
-            dtls1_retransmit_buffered_messages(s);
-            SSL3_RECORD_set_length(rr, 0);
+            if (dtls1_retransmit_buffered_messages(sc) <= 0) {
+                /* Fail if we encountered a fatal error */
+                if (ossl_statem_in_error(sc))
+                    return -1;
+            }
+            ssl_release_record(sc, rr);
+            if (!(sc->mode & SSL_MODE_AUTO_RETRY)) {
+                if (!sc->rlayer.rrlmethod->unprocessed_read_pending(sc->rlayer.rrl)) {
+                    /* no read-ahead left? */
+                    BIO *bio;
+
+                    sc->rwstate = SSL_READING;
+                    bio = SSL_get_rbio(s);
+                    BIO_clear_retry_flags(bio);
+                    BIO_set_retry_read(bio);
+                    return -1;
+                }
+            }
             goto start;
         }
 
-        if (SSL_is_init_finished(s) &&
-            !(s->s3->flags & SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS)) {
-            ossl_statem_set_in_init(s, 1);
-            s->renegotiate = 1;
-            s->new_session = 1;
+        /*
+         * To get here we must be trying to read app data but found handshake
+         * data. But if we're trying to read app data, and we're not in init
+         * (which is tested for at the top of this function) then init must be
+         * finished
+         */
+        if (!ossl_assert(SSL_is_init_finished(s))) {
+            SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            return -1;
         }
-        i = s->handshake_func(s);
+
+        /* We found handshake data, so we're going back into init */
+        ossl_statem_set_in_init(sc, 1);
+
+        i = sc->handshake_func(s);
+        /* SSLfatal() called if appropriate */
         if (i < 0)
-            return (i);
-        if (i == 0) {
-            SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_SSL_HANDSHAKE_FAILURE);
-            return (-1);
-        }
+            return i;
+        if (i == 0)
+            return -1;
 
-        if (!(s->mode & SSL_MODE_AUTO_RETRY)) {
-            if (SSL3_BUFFER_get_left(&s->rlayer.rbuf) == 0) {
+        if (!(sc->mode & SSL_MODE_AUTO_RETRY)) {
+            if (!sc->rlayer.rrlmethod->unprocessed_read_pending(sc->rlayer.rrl)) {
                 /* no read-ahead left? */
                 BIO *bio;
                 /*
@@ -818,26 +572,20 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
                  * option set.  Otherwise renegotiation may cause nasty
                  * problems in the blocking world
                  */
-                s->rwstate = SSL_READING;
+                sc->rwstate = SSL_READING;
                 bio = SSL_get_rbio(s);
                 BIO_clear_retry_flags(bio);
                 BIO_set_retry_read(bio);
-                return (-1);
+                return -1;
             }
         }
         goto start;
     }
 
-    switch (SSL3_RECORD_get_type(rr)) {
+    switch (rr->type) {
     default:
-        /* TLS just ignores unknown message types */
-        if (s->version == TLS1_VERSION) {
-            SSL3_RECORD_set_length(rr, 0);
-            goto start;
-        }
-        al = SSL_AD_UNEXPECTED_MESSAGE;
-        SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_UNEXPECTED_RECORD);
-        goto f_err;
+        SSLfatal(sc, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_UNEXPECTED_RECORD);
+        return -1;
     case SSL3_RT_CHANGE_CIPHER_SPEC:
     case SSL3_RT_ALERT:
     case SSL3_RT_HANDSHAKE:
@@ -846,9 +594,8 @@ int dtls1_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
          */
-        al = SSL_AD_UNEXPECTED_MESSAGE;
-        SSLerr(SSL_F_DTLS1_READ_BYTES, ERR_R_INTERNAL_ERROR);
-        goto f_err;
+        SSLfatal(sc, SSL_AD_UNEXPECTED_MESSAGE, ERR_R_INTERNAL_ERROR);
+        return -1;
     case SSL3_RT_APPLICATION_DATA:
         /*
          * At this point, we were expecting handshake data, but have
@@ -857,117 +604,172 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
          * application data at this point (session renegotiation not yet
          * started), we will indulge it.
          */
-        if (s->s3->in_read_app_data &&
-            (s->s3->total_renegotiations != 0) &&
-            ossl_statem_app_data_allowed(s)) {
-            s->s3->in_read_app_data = 2;
-            return (-1);
+        if (sc->s3.in_read_app_data &&
+            (sc->s3.total_renegotiations != 0) &&
+            ossl_statem_app_data_allowed(sc)) {
+            sc->s3.in_read_app_data = 2;
+            return -1;
         } else {
-            al = SSL_AD_UNEXPECTED_MESSAGE;
-            SSLerr(SSL_F_DTLS1_READ_BYTES, SSL_R_UNEXPECTED_RECORD);
-            goto f_err;
+            SSLfatal(sc, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_UNEXPECTED_RECORD);
+            return -1;
         }
     }
     /* not reached */
-
- f_err:
-    ssl3_send_alert(s, SSL3_AL_FATAL, al);
-    return (-1);
 }
 
-
-        /*
-         * this only happens when a client hello is received and a handshake
-         * is started.
-         */
-static int have_handshake_fragment(SSL *s, int type, unsigned char *buf,
-                                   int len)
+/*
+ * 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 dtls1_write_bytes(SSL_CONNECTION *s, int type, const void *buf,
+                      size_t len, size_t *written)
 {
+    int i;
 
-    if ((type == SSL3_RT_HANDSHAKE)
-            && (s->rlayer.d->handshake_fragment_len > 0))
-        /* (partially) satisfy request from storage */
-    {
-        unsigned char *src = s->rlayer.d->handshake_fragment;
-        unsigned char *dst = buf;
-        unsigned int k, n;
-
-        /* peek == 0 */
-        n = 0;
-        while ((len > 0) && (s->rlayer.d->handshake_fragment_len > 0)) {
-            *dst++ = *src++;
-            len--;
-            s->rlayer.d->handshake_fragment_len--;
-            n++;
-        }
-        /* move any remaining fragment bytes: */
-        for (k = 0; k < s->rlayer.d->handshake_fragment_len; k++)
-            s->rlayer.d->handshake_fragment[k] = *src++;
-        return n;
+    if (!ossl_assert(len <= SSL3_RT_MAX_PLAIN_LENGTH)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return -1;
     }
-
-    return 0;
+    s->rwstate = SSL_NOTHING;
+    i = do_dtls1_write(s, type, buf, len, 0, written);
+    return i;
 }
 
 /*
- * 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.
+ * TODO(RECLAYER): Temporary copy of the old ssl3_write_pending() function now
+ * replaced by tls_retry_write_records(). Needs to be removed when the DTLS code
+ * is converted
  */
-int dtls1_write_bytes(SSL *s, int type, const void *buf, int len)
+/* if SSL3_BUFFER_get_left() != 0, we need to call this
+ *
+ * Return values are as per SSL_write()
+ */
+static int ssl3_write_pending(SSL_CONNECTION *s, int type,
+                              const unsigned char *buf, size_t len,
+                              size_t *written)
 {
     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_R_BAD_WRITE_RETRY);
+        return -1;
+    }
 
-    OPENSSL_assert(len <= SSL3_RT_MAX_PLAIN_LENGTH);
-    s->rwstate = SSL_NOTHING;
-    i = do_dtls1_write(s, type, buf, len, 0);
-    return i;
+    for (;;) {
+        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;
+                BIO_set_ktls_ctrl_msg(s->wbio, type);
+            }
+            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_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(&wb[currbuf])) {
+            SSL3_BUFFER_set_left(&wb[currbuf], 0);
+            SSL3_BUFFER_add_offset(&wb[currbuf], tmpwrit);
+            s->rwstate = SSL_NOTHING;
+            *written = s->rlayer.wpend_ret;
+            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(&wb[currbuf], 0);
+            }
+            return i;
+        }
+        SSL3_BUFFER_add_offset(&wb[currbuf], tmpwrit);
+        SSL3_BUFFER_sub_left(&wb[currbuf], tmpwrit);
+    }
 }
 
-int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
-                   unsigned int len, int create_empty_fragment)
+int do_dtls1_write(SSL_CONNECTION *sc, int type, const unsigned char *buf,
+                   size_t len, int create_empty_fragment, size_t *written)
 {
     unsigned char *p, *pseq;
     int i, mac_size, clear = 0;
-    int prefix_len = 0;
+    size_t prefix_len = 0;
     int eivlen;
     SSL3_RECORD wr;
     SSL3_BUFFER *wb;
     SSL_SESSION *sess;
+    SSL *s = SSL_CONNECTION_GET_SSL(sc);
 
-    wb = &s->rlayer.wbuf[0];
+    wb = &sc->rlayer.wbuf[0];
 
     /*
-     * first check if there is a SSL3_BUFFER still being written out.  This
-     * will happen with non blocking IO
+     * DTLS writes whole datagrams, so there can't be anything left in
+     * the buffer.
      */
-    if (SSL3_BUFFER_get_left(wb) != 0) {
-        OPENSSL_assert(0);      /* XDTLS: want to see if we ever get here */
-        return (ssl3_write_pending(s, type, buf, len));
+    if (!ossl_assert(SSL3_BUFFER_get_left(wb) == 0)) {
+        SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
     }
 
     /* If we have an alert to send, lets send it */
-    if (s->s3->alert_dispatch) {
+    if (sc->s3.alert_dispatch) {
         i = s->method->ssl_dispatch_alert(s);
         if (i <= 0)
-            return (i);
+            return i;
         /* if it went, fall through and send more stuff */
     }
 
     if (len == 0 && !create_empty_fragment)
         return 0;
 
-    sess = s->session;
+    if (len > ssl_get_max_send_fragment(sc)) {
+        SSLfatal(sc, SSL_AD_INTERNAL_ERROR, SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE);
+        return 0;
+    }
 
-    if ((sess == NULL) ||
-        (s->enc_write_ctx == NULL) || (EVP_MD_CTX_md(s->write_hash) == NULL))
+    sess = sc->session;
+
+    if ((sess == NULL)
+            || (sc->enc_write_ctx == NULL)
+            || (EVP_MD_CTX_get0_md(sc->write_hash) == NULL))
         clear = 1;
 
     if (clear)
         mac_size = 0;
     else {
-        mac_size = EVP_MD_CTX_size(s->write_hash);
-        if (mac_size < 0)
-            goto err;
+        mac_size = EVP_MD_CTX_get_size(sc->write_hash);
+        if (mac_size < 0) {
+            SSLfatal(sc, SSL_AD_INTERNAL_ERROR,
+                     SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE);
+            return -1;
+        }
     }
 
     p = SSL3_BUFFER_get_buf(wb) + prefix_len;
@@ -981,12 +783,13 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
      * haven't decided which version to use yet send back using version 1.0
      * header: otherwise some clients will ignore it.
      */
-    if (s->method->version == DTLS_ANY_VERSION) {
+    if (s->method->version == DTLS_ANY_VERSION &&
+        sc->max_proto_version != DTLS1_BAD_VER) {
         *(p++) = DTLS1_VERSION >> 8;
         *(p++) = DTLS1_VERSION & 0xff;
     } else {
-        *(p++) = s->version >> 8;
-        *(p++) = s->version & 0xff;
+        *(p++) = sc->version >> 8;
+        *(p++) = sc->version & 0xff;
     }
 
     /* field where we are to write out packet epoch, seq num and len */
@@ -994,10 +797,14 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
     p += 10;
 
     /* Explicit IV length, block ciphers appropriate version flag */
-    if (s->enc_write_ctx) {
-        int mode = EVP_CIPHER_CTX_mode(s->enc_write_ctx);
+    if (sc->enc_write_ctx) {
+        int mode = EVP_CIPHER_CTX_get_mode(sc->enc_write_ctx);
         if (mode == EVP_CIPH_CBC_MODE) {
-            eivlen = EVP_CIPHER_CTX_iv_length(s->enc_write_ctx);
+            eivlen = EVP_CIPHER_CTX_get_iv_length(sc->enc_write_ctx);
+            if (eivlen < 0) {
+                SSLfatal(sc, SSL_AD_INTERNAL_ERROR, SSL_R_LIBRARY_BUG);
+                return -1;
+            }
             if (eivlen <= 1)
                 eivlen = 0;
         }
@@ -1013,7 +820,7 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
 
     /* lets setup the record stuff. */
     SSL3_RECORD_set_data(&wr, p + eivlen); /* make room for IV in case of CBC */
-    SSL3_RECORD_set_length(&wr, (int)len);
+    SSL3_RECORD_set_length(&wr, len);
     SSL3_RECORD_set_input(&wr, (unsigned char *)buf);
 
     /*
@@ -1021,10 +828,10 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
      */
 
     /* first we compress */
-    if (s->compress != NULL) {
-        if (!ssl3_do_compress(s, &wr)) {
-            SSLerr(SSL_F_DO_DTLS1_WRITE, SSL_R_COMPRESSION_FAILURE);
-            goto err;
+    if (sc->compress != NULL) {
+        if (!ssl3_do_compress(sc, &wr)) {
+            SSLfatal(sc, SSL_AD_INTERNAL_ERROR, SSL_R_COMPRESSION_FAILURE);
+            return -1;
         }
     } else {
         memcpy(SSL3_RECORD_get_data(&wr), SSL3_RECORD_get_input(&wr),
@@ -1038,10 +845,13 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
      * wb->buf
      */
 
-    if (mac_size != 0) {
-        if (s->method->ssl3_enc->mac(s, &wr,
-                &(p[SSL3_RECORD_get_length(&wr) + eivlen]), 1) < 0)
-            goto err;
+    if (!SSL_WRITE_ETM(sc) && mac_size != 0) {
+        if (!s->method->ssl3_enc->mac(sc, &wr,
+                                      &(p[SSL3_RECORD_get_length(&wr) + eivlen]),
+                                      1)) {
+            SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            return -1;
+        }
         SSL3_RECORD_add_length(&wr, mac_size);
     }
 
@@ -1052,31 +862,35 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
     if (eivlen)
         SSL3_RECORD_add_length(&wr, eivlen);
 
-    if (s->method->ssl3_enc->enc(s, &wr, 1, 1) < 1)
-        goto err;
+    if (s->method->ssl3_enc->enc(sc, &wr, 1, 1, NULL, mac_size) < 1) {
+        if (!ossl_statem_in_error(sc)) {
+            SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        }
+        return -1;
+    }
+
+    if (SSL_WRITE_ETM(sc) && mac_size != 0) {
+        if (!s->method->ssl3_enc->mac(sc, &wr,
+                                      &(p[SSL3_RECORD_get_length(&wr)]), 1)) {
+            SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            return -1;
+        }
+        SSL3_RECORD_add_length(&wr, mac_size);
+    }
 
     /* record length after mac and block padding */
-    /*
-     * if (type == SSL3_RT_APPLICATION_DATA || (type == SSL3_RT_ALERT && !
-     * SSL_in_init(s)))
-     */
 
     /* there's only one epoch between handshake and app data */
 
-    s2n(s->rlayer.d->w_epoch, pseq);
-
-    /* XDTLS: ?? */
-    /*
-     * else s2n(s->d1->handshake_epoch, pseq);
-     */
+    s2n(sc->rlayer.d->w_epoch, pseq);
 
-    memcpy(pseq, &(s->rlayer.write_sequence[2]), 6);
+    memcpy(pseq, &(sc->rlayer.write_sequence[2]), 6);
     pseq += 6;
     s2n(SSL3_RECORD_get_length(&wr), pseq);
 
-    if (s->msg_callback)
-        s->msg_callback(1, 0, SSL3_RT_HEADER, pseq - DTLS1_RT_HEADER_LENGTH,
-                        DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg);
+    if (sc->msg_callback)
+        sc->msg_callback(1, 0, SSL3_RT_HEADER, pseq - DTLS1_RT_HEADER_LENGTH,
+                         DTLS1_RT_HEADER_LENGTH, s, sc->msg_callback_arg);
 
     /*
      * we should now have wr.data pointing to the encrypted data, which is
@@ -1085,14 +899,15 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
     SSL3_RECORD_set_type(&wr, type); /* not needed but helps for debugging */
     SSL3_RECORD_add_length(&wr, DTLS1_RT_HEADER_LENGTH);
 
-    ssl3_record_sequence_update(&(s->rlayer.write_sequence[0]));
+    ssl3_record_sequence_update(&(sc->rlayer.write_sequence[0]));
 
     if (create_empty_fragment) {
         /*
          * we are in a recursive call; just return the length, don't write
          * out anything here
          */
-        return wr.length;
+        *written = wr.length;
+        return 1;
     }
 
     /* now let's set up wb */
@@ -1103,55 +918,32 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
      * memorize arguments so that ssl3_write_pending can detect bad write
      * retries later
      */
-    s->rlayer.wpend_tot = len;
-    s->rlayer.wpend_buf = buf;
-    s->rlayer.wpend_type = type;
-    s->rlayer.wpend_ret = len;
-
-    /* we now just need to write the buffer */
-    return ssl3_write_pending(s, type, buf, len);
- err:
-    return -1;
-}
-
-DTLS1_BITMAP *dtls1_get_bitmap(SSL *s, SSL3_RECORD *rr,
-                                      unsigned int *is_next_epoch)
-{
+    sc->rlayer.wpend_tot = len;
+    sc->rlayer.wpend_buf = buf;
+    sc->rlayer.wpend_type = type;
+    sc->rlayer.wpend_ret = len;
 
-    *is_next_epoch = 0;
-
-    /* In current epoch, accept HM, CCS, DATA, & ALERT */
-    if (rr->epoch == s->rlayer.d->r_epoch)
-        return &s->rlayer.d->bitmap;
-
-    /* Only HM and ALERT messages can be from the next epoch */
-    else if (rr->epoch == (unsigned long)(s->rlayer.d->r_epoch + 1) &&
-            (rr->type == SSL3_RT_HANDSHAKE || rr->type == SSL3_RT_ALERT)) {
-        *is_next_epoch = 1;
-        return &s->rlayer.d->next_bitmap;
-    }
-
-    return NULL;
+    /* we now just need to write the buffer. Calls SSLfatal() as required. */
+    return ssl3_write_pending(sc, type, buf, len, written);
 }
 
-void dtls1_reset_seq_numbers(SSL *s, int rw)
+void dtls1_reset_seq_numbers(SSL_CONNECTION *s, int rw)
 {
     unsigned char *seq;
-    unsigned int seq_bytes = sizeof(s->rlayer.read_sequence);
 
     if (rw & SSL3_CC_READ) {
-        seq = s->rlayer.read_sequence;
         s->rlayer.d->r_epoch++;
-        memcpy(&s->rlayer.d->bitmap, &s->rlayer.d->next_bitmap,
-               sizeof(s->rlayer.d->bitmap));
-        memset(&s->rlayer.d->next_bitmap, 0,
-               sizeof(s->rlayer.d->next_bitmap));
+
+        /*
+         * We must not use any buffered messages received from the previous
+         * epoch
+         */
+        dtls1_clear_received_buffer(s);
     } else {
         seq = s->rlayer.write_sequence;
         memcpy(s->rlayer.d->last_write_sequence, seq,
                sizeof(s->rlayer.write_sequence));
         s->rlayer.d->w_epoch++;
+        memset(seq, 0, sizeof(s->rlayer.write_sequence));
     }
-
-    memset(seq, 0, seq_bytes);
 }