Don't use the buffer BIO in DTLS.
Instead, "writing" a message merely adds it to the outgoing_messages
structure. The code to write the flight then loops over it all and now
shares code with retransmission. The verbs here are all a little odd,
but they'll be fixed in later commits.
In doing so, this fixes a slight miscalculation of the record-layer
overhead when retransmitting a flight that spans two epochs. (We'd use
the encrypted epoch's overhead for the unencrypted epoch.)
BUG=72
Change-Id: I8ac897c955cc74799f8b5ca6923906e97d6dad17
Reviewed-on: https://boringssl-review.googlesource.com/13223
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 1491a53..df227fb 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -4126,10 +4126,6 @@
/* init_num is the length of the current handshake message body. */
uint32_t init_num;
- /* init_off, in DTLS, is the number of bytes of the current message that have
- * been written. */
- uint32_t init_off;
-
struct ssl3_state_st *s3; /* SSLv3 variables */
struct dtls1_state_st *d1; /* DTLSv1 variables */
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index d3e4a92..1d89636 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -122,6 +122,7 @@
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/rand.h>
+#include <openssl/type_check.h>
#include <openssl/x509.h>
#include "../crypto/internal.h"
@@ -311,9 +312,9 @@
}
/* Cross-epoch records are discarded, but we may receive out-of-order
- * application data between ChangeCipherSpec and Finished or a ChangeCipherSpec
- * before the appropriate point in the handshake. Those must be silently
- * discarded.
+ * application data between ChangeCipherSpec and Finished or a
+ * ChangeCipherSpec before the appropriate point in the handshake. Those must
+ * be silently discarded.
*
* However, only allow the out-of-order records in the correct epoch.
* Application data must come in the encrypted epoch, and ChangeCipherSpec in
@@ -384,8 +385,8 @@
assert(msg_len > 0);
/* Copy the body into the fragment. */
- OPENSSL_memcpy(frag->data + DTLS1_HM_HEADER_LENGTH + frag_off, CBS_data(&body),
- CBS_len(&body));
+ OPENSSL_memcpy(frag->data + DTLS1_HM_HEADER_LENGTH + frag_off,
+ CBS_data(&body), CBS_len(&body));
dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len);
}
@@ -507,219 +508,14 @@
/* Sending handshake messages. */
-static void dtls1_update_mtu(SSL *ssl) {
- /* TODO(davidben): What is this code doing and do we need it? */
- if (ssl->d1->mtu < dtls1_min_mtu() &&
- !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
- long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
- if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
- ssl->d1->mtu = (unsigned)mtu;
- } else {
- ssl->d1->mtu = kDefaultMTU;
- BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
- }
- }
-
- /* The MTU should be above the minimum now. */
- assert(ssl->d1->mtu >= dtls1_min_mtu());
-}
-
-/* dtls1_max_record_size returns the maximum record body length that may be
- * written without exceeding the MTU. It accounts for any buffering installed on
- * the write BIO. If no record may be written, it returns zero. */
-static size_t dtls1_max_record_size(const SSL *ssl) {
- size_t ret = ssl->d1->mtu;
-
- size_t overhead = SSL_max_seal_overhead(ssl);
- if (ret <= overhead) {
- return 0;
- }
- ret -= overhead;
-
- size_t pending = BIO_wpending(ssl->wbio);
- if (ret <= pending) {
- return 0;
- }
- ret -= pending;
-
- return ret;
-}
-
-static int dtls1_write_change_cipher_spec(SSL *ssl,
- enum dtls1_use_epoch_t use_epoch) {
- dtls1_update_mtu(ssl);
-
- /* During the handshake, wbio is buffered to pack messages together. Flush the
- * buffer if the ChangeCipherSpec would not fit in a packet. */
- if (dtls1_max_record_size(ssl) == 0) {
- int ret = BIO_flush(ssl->wbio);
- if (ret <= 0) {
- ssl->rwstate = SSL_WRITING;
- return ret;
- }
- }
-
- static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
- int ret =
- dtls1_write_record(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
- sizeof(kChangeCipherSpec), use_epoch);
- if (ret <= 0) {
- return ret;
- }
-
- ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC,
- kChangeCipherSpec, sizeof(kChangeCipherSpec));
- return 1;
-}
-
-/* dtls1_do_handshake_write writes handshake message |in| using the given epoch,
- * starting |offset| bytes into the message body. It returns one on success. On
- * error, it returns <= 0 and sets |*out_offset| to the number of bytes of body
- * that were successfully written. This may be used to retry the write
- * later. |in| must be a reassembled handshake message with the full DTLS
- * handshake header. */
-static int dtls1_do_handshake_write(SSL *ssl, size_t *out_offset,
- const uint8_t *in, size_t offset,
- size_t len,
- enum dtls1_use_epoch_t use_epoch) {
- dtls1_update_mtu(ssl);
-
- int ret = -1;
- CBB cbb;
- CBB_zero(&cbb);
- /* Allocate a temporary buffer to hold the message fragments to avoid
- * clobbering the message. */
- uint8_t *buf = OPENSSL_malloc(ssl->d1->mtu);
- if (buf == NULL) {
- goto err;
- }
-
- /* Although it may be sent as multiple fragments, a DTLS message must be sent
- * serialized as a single fragment for purposes of |ssl_do_msg_callback| and
- * the handshake hash. */
- CBS cbs, body;
- struct hm_header_st hdr;
- CBS_init(&cbs, in, len);
- if (!dtls1_parse_fragment(&cbs, &hdr, &body) ||
- hdr.frag_off != 0 ||
- hdr.frag_len != CBS_len(&body) ||
- hdr.msg_len != CBS_len(&body) ||
- !CBS_skip(&body, offset) ||
- CBS_len(&cbs) != 0) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- goto err;
- }
-
- do {
- /* During the handshake, wbio is buffered to pack messages together. Flush
- * the buffer if there isn't enough room to make progress. */
- if (dtls1_max_record_size(ssl) < DTLS1_HM_HEADER_LENGTH + 1) {
- int flush_ret = BIO_flush(ssl->wbio);
- if (flush_ret <= 0) {
- ssl->rwstate = SSL_WRITING;
- ret = flush_ret;
- goto err;
- }
- assert(BIO_wpending(ssl->wbio) == 0);
- }
-
- size_t todo = dtls1_max_record_size(ssl);
- if (todo < DTLS1_HM_HEADER_LENGTH + 1) {
- /* To make forward progress, the MTU must, at minimum, fit the handshake
- * header and one byte of handshake body. */
- OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
- goto err;
- }
- todo -= DTLS1_HM_HEADER_LENGTH;
-
- if (todo > CBS_len(&body)) {
- todo = CBS_len(&body);
- }
- if (todo >= (1u << 24)) {
- todo = (1u << 24) - 1;
- }
-
- size_t buf_len;
- if (!CBB_init_fixed(&cbb, buf, ssl->d1->mtu) ||
- !CBB_add_u8(&cbb, hdr.type) ||
- !CBB_add_u24(&cbb, hdr.msg_len) ||
- !CBB_add_u16(&cbb, hdr.seq) ||
- !CBB_add_u24(&cbb, offset) ||
- !CBB_add_u24(&cbb, todo) ||
- !CBB_add_bytes(&cbb, CBS_data(&body), todo) ||
- !CBB_finish(&cbb, NULL, &buf_len)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- goto err;
- }
-
- int write_ret =
- dtls1_write_record(ssl, SSL3_RT_HANDSHAKE, buf, buf_len, use_epoch);
- if (write_ret <= 0) {
- ret = write_ret;
- goto err;
- }
-
- if (!CBS_skip(&body, todo)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- offset += todo;
- } while (CBS_len(&body) != 0);
-
- ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, in, len);
-
- ret = 1;
-
-err:
- *out_offset = offset;
- CBB_cleanup(&cbb);
- OPENSSL_free(buf);
- return ret;
-}
-
void dtls_clear_outgoing_messages(SSL *ssl) {
for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) {
OPENSSL_free(ssl->d1->outgoing_messages[i].data);
ssl->d1->outgoing_messages[i].data = NULL;
}
ssl->d1->outgoing_messages_len = 0;
-}
-
-/* dtls1_add_change_cipher_spec adds a ChangeCipherSpec to the current
- * handshake flight. */
-static int dtls1_add_change_cipher_spec(SSL *ssl) {
- if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
-
- DTLS_OUTGOING_MESSAGE *msg =
- &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
- msg->data = NULL;
- msg->len = 0;
- msg->epoch = ssl->d1->w_epoch;
- msg->is_ccs = 1;
-
- ssl->d1->outgoing_messages_len++;
- return 1;
-}
-
-static int dtls1_add_message(SSL *ssl, uint8_t *data, size_t len) {
- if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- OPENSSL_free(data);
- return 0;
- }
-
- DTLS_OUTGOING_MESSAGE *msg =
- &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
- msg->data = data;
- msg->len = len;
- msg->epoch = ssl->d1->w_epoch;
- msg->is_ccs = 0;
-
- ssl->d1->outgoing_messages_len++;
- return 1;
+ ssl->d1->outgoing_written = 0;
+ ssl->d1->outgoing_offset = 0;
}
int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
@@ -752,36 +548,87 @@
return 1;
}
-int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len) {
- ssl3_update_handshake_hash(ssl, msg, len);
+/* add_outgoing adds a new handshake message or ChangeCipherSpec to the current
+ * outgoing flight. It returns one on success and zero on error. In both cases,
+ * it takes ownership of |data| and releases it with |OPENSSL_free| when
+ * done. */
+static int add_outgoing(SSL *ssl, int is_ccs, uint8_t *data, size_t len) {
+ OPENSSL_COMPILE_ASSERT(SSL_MAX_HANDSHAKE_FLIGHT <
+ (1 << 8 * sizeof(ssl->d1->outgoing_messages_len)),
+ outgoing_messages_len_is_too_small);
+ if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
+ assert(0);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ OPENSSL_free(data);
+ return 0;
+ }
- ssl->d1->handshake_write_seq++;
- ssl->init_off = 0;
- return dtls1_add_message(ssl, msg, len);
+ if (!is_ccs) {
+ ssl3_update_handshake_hash(ssl, data, len);
+ ssl->d1->handshake_write_seq++;
+ }
+
+ DTLS_OUTGOING_MESSAGE *msg =
+ &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
+ msg->data = data;
+ msg->len = len;
+ msg->epoch = ssl->d1->w_epoch;
+ msg->is_ccs = is_ccs;
+
+ ssl->d1->outgoing_messages_len++;
+ return 1;
+}
+
+int dtls1_queue_message(SSL *ssl, uint8_t *data, size_t len) {
+ return add_outgoing(ssl, 0 /* handshake */, data, len);
}
int dtls1_write_message(SSL *ssl) {
- if (ssl->d1->outgoing_messages_len == 0) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return -1;
- }
-
- const DTLS_OUTGOING_MESSAGE *msg =
- &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len - 1];
- if (msg->is_ccs) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return -1;
- }
-
- size_t offset = ssl->init_off;
- int ret = dtls1_do_handshake_write(ssl, &offset, msg->data, offset, msg->len,
- dtls1_use_current_epoch);
- ssl->init_off = offset;
- return ret;
+ /* The message is written in |dtls1_flush_flight|. */
+ return 1;
}
-static int dtls1_retransmit_message(SSL *ssl,
- const DTLS_OUTGOING_MESSAGE *msg) {
+int dtls1_send_change_cipher_spec(SSL *ssl) {
+ return add_outgoing(ssl, 1 /* ChangeCipherSpec */, NULL, 0);
+}
+
+/* dtls1_update_mtu updates the current MTU from the BIO, ensuring it is above
+ * the minimum. */
+static void dtls1_update_mtu(SSL *ssl) {
+ /* TODO(davidben): No consumer implements |BIO_CTRL_DGRAM_SET_MTU| and the
+ * only |BIO_CTRL_DGRAM_QUERY_MTU| implementation could use
+ * |SSL_set_mtu|. Does this need to be so complex? */
+ if (ssl->d1->mtu < dtls1_min_mtu() &&
+ !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
+ long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
+ if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
+ ssl->d1->mtu = (unsigned)mtu;
+ } else {
+ ssl->d1->mtu = kDefaultMTU;
+ BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
+ }
+ }
+
+ /* The MTU should be above the minimum now. */
+ assert(ssl->d1->mtu >= dtls1_min_mtu());
+}
+
+enum seal_result_t {
+ seal_error,
+ seal_no_progress,
+ seal_partial,
+ seal_success,
+};
+
+/* seal_next_message seals |msg|, which must be the next message, to |out|. If
+ * progress was made, it returns |seal_partial| or |seal_success| and sets
+ * |*out_len| to the number of bytes written. */
+static enum seal_result_t seal_next_message(SSL *ssl, uint8_t *out,
+ size_t *out_len, size_t max_out,
+ const DTLS_OUTGOING_MESSAGE *msg) {
+ assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len);
+ assert(msg == &ssl->d1->outgoing_messages[ssl->d1->outgoing_written]);
+
/* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
* (negotiated cipher) exist. */
assert(ssl->d1->w_epoch == 0 || ssl->d1->w_epoch == 1);
@@ -790,32 +637,156 @@
if (ssl->d1->w_epoch == 1 && msg->epoch == 0) {
use_epoch = dtls1_use_previous_epoch;
}
+ size_t overhead = dtls_max_seal_overhead(ssl, use_epoch);
+ size_t prefix = dtls_seal_prefix_len(ssl, use_epoch);
- /* TODO(davidben): This cannot handle non-blocking writes. */
- int ret;
if (msg->is_ccs) {
- ret = dtls1_write_change_cipher_spec(ssl, use_epoch);
- } else {
- size_t offset = 0;
- ret = dtls1_do_handshake_write(ssl, &offset, msg->data, offset, msg->len,
- use_epoch);
+ /* Check there is room for the ChangeCipherSpec. */
+ static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
+ if (max_out < sizeof(kChangeCipherSpec) + overhead) {
+ return seal_no_progress;
+ }
+
+ if (!dtls_seal_record(ssl, out, out_len, max_out,
+ SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
+ sizeof(kChangeCipherSpec), use_epoch)) {
+ return seal_error;
+ }
+
+ ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC,
+ kChangeCipherSpec, sizeof(kChangeCipherSpec));
+ return seal_success;
}
- return ret;
+ /* DTLS messages are serialized as a single fragment in |msg|. */
+ CBS cbs, body;
+ struct hm_header_st hdr;
+ CBS_init(&cbs, msg->data, msg->len);
+ if (!dtls1_parse_fragment(&cbs, &hdr, &body) ||
+ hdr.frag_off != 0 ||
+ hdr.frag_len != CBS_len(&body) ||
+ hdr.msg_len != CBS_len(&body) ||
+ !CBS_skip(&body, ssl->d1->outgoing_offset) ||
+ CBS_len(&cbs) != 0) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return seal_error;
+ }
+
+ /* Determine how much progress can be made. */
+ if (max_out < DTLS1_HM_HEADER_LENGTH + 1 + overhead || max_out < prefix) {
+ return seal_no_progress;
+ }
+ size_t todo = CBS_len(&body);
+ if (todo > max_out - DTLS1_HM_HEADER_LENGTH - overhead) {
+ todo = max_out - DTLS1_HM_HEADER_LENGTH - overhead;
+ }
+
+ /* Assemble a fragment, to be sealed in-place. */
+ CBB cbb;
+ uint8_t *frag = out + prefix;
+ size_t max_frag = max_out - prefix, frag_len;
+ if (!CBB_init_fixed(&cbb, frag, max_frag) ||
+ !CBB_add_u8(&cbb, hdr.type) ||
+ !CBB_add_u24(&cbb, hdr.msg_len) ||
+ !CBB_add_u16(&cbb, hdr.seq) ||
+ !CBB_add_u24(&cbb, ssl->d1->outgoing_offset) ||
+ !CBB_add_u24(&cbb, todo) ||
+ !CBB_add_bytes(&cbb, CBS_data(&body), todo) ||
+ !CBB_finish(&cbb, NULL, &frag_len)) {
+ CBB_cleanup(&cbb);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return seal_error;
+ }
+
+ ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, frag, frag_len);
+
+ if (!dtls_seal_record(ssl, out, out_len, max_out, SSL3_RT_HANDSHAKE,
+ out + prefix, frag_len, use_epoch)) {
+ return seal_error;
+ }
+
+ if (todo == CBS_len(&body)) {
+ /* The next message is complete. */
+ ssl->d1->outgoing_offset = 0;
+ return seal_success;
+ }
+
+ ssl->d1->outgoing_offset += todo;
+ return seal_partial;
}
-int dtls1_retransmit_outgoing_messages(SSL *ssl) {
- /* Ensure we are packing handshake messages. */
- const int was_buffered = ssl_is_wbio_buffered(ssl);
- assert(was_buffered == SSL_in_init(ssl));
- if (!was_buffered && !ssl_init_wbio_buffer(ssl)) {
- return -1;
+/* seal_next_packet writes as much of the next flight as possible to |out| and
+ * advances |ssl->d1->outgoing_written| and |ssl->d1->outgoing_offset| as
+ * appropriate. */
+static int seal_next_packet(SSL *ssl, uint8_t *out, size_t *out_len,
+ size_t max_out) {
+ int made_progress = 0;
+ size_t total = 0;
+ assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len);
+ for (; ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len;
+ ssl->d1->outgoing_written++) {
+ const DTLS_OUTGOING_MESSAGE *msg =
+ &ssl->d1->outgoing_messages[ssl->d1->outgoing_written];
+ size_t len;
+ enum seal_result_t ret = seal_next_message(ssl, out, &len, max_out, msg);
+ switch (ret) {
+ case seal_error:
+ return 0;
+
+ case seal_no_progress:
+ goto packet_full;
+
+ case seal_partial:
+ case seal_success:
+ out += len;
+ max_out -= len;
+ total += len;
+ made_progress = 1;
+
+ if (ret == seal_partial) {
+ goto packet_full;
+ }
+ break;
+ }
}
- assert(ssl_is_wbio_buffered(ssl));
+
+packet_full:
+ /* The MTU was too small to make any progress. */
+ if (!made_progress) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
+ return 0;
+ }
+
+ *out_len = total;
+ return 1;
+}
+
+int dtls1_flush_flight(SSL *ssl) {
+ dtls1_update_mtu(ssl);
int ret = -1;
- for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) {
- if (dtls1_retransmit_message(ssl, &ssl->d1->outgoing_messages[i]) <= 0) {
+ uint8_t *packet = OPENSSL_malloc(ssl->d1->mtu);
+ if (packet == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ while (ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len) {
+ uint8_t old_written = ssl->d1->outgoing_written;
+ uint32_t old_offset = ssl->d1->outgoing_offset;
+
+ size_t packet_len;
+ if (!seal_next_packet(ssl, packet, &packet_len, ssl->d1->mtu)) {
+ goto err;
+ }
+
+ int bio_ret = BIO_write(ssl->wbio, packet, packet_len);
+ if (bio_ret <= 0) {
+ /* Retry this packet the next time around. */
+ ssl->d1->outgoing_written = old_written;
+ ssl->d1->outgoing_offset = old_offset;
+ ssl->rwstate = SSL_WRITING;
+ ret = bio_ret;
goto err;
}
}
@@ -823,23 +794,22 @@
ret = BIO_flush(ssl->wbio);
if (ret <= 0) {
ssl->rwstate = SSL_WRITING;
- goto err;
}
err:
- if (!was_buffered) {
- ssl_free_wbio_buffer(ssl);
- }
+ OPENSSL_free(packet);
return ret;
}
-int dtls1_send_change_cipher_spec(SSL *ssl) {
- int ret = dtls1_write_change_cipher_spec(ssl, dtls1_use_current_epoch);
- if (ret <= 0) {
- return ret;
- }
- dtls1_add_change_cipher_spec(ssl);
- return 1;
+int dtls1_retransmit_outgoing_messages(SSL *ssl) {
+ /* Rewind to the start of the flight and write it again.
+ *
+ * TODO(davidben): This does not allow retransmits to be resumed on
+ * non-blocking write. */
+ ssl->d1->outgoing_written = 0;
+ ssl->d1->outgoing_offset = 0;
+
+ return dtls1_flush_flight(ssl);
}
unsigned int dtls1_min_mtu(void) {
diff --git a/ssl/dtls_method.c b/ssl/dtls_method.c
index 702b3c0..a774c82 100644
--- a/ssl/dtls_method.c
+++ b/ssl/dtls_method.c
@@ -99,14 +99,6 @@
return cipher->algorithm_enc != SSL_eNULL;
}
-static int dtls1_flush_flight(SSL *ssl) {
- int ret = BIO_flush(ssl->wbio);
- if (ret <= 0) {
- ssl->rwstate = SSL_WRITING;
- }
- return ret;
-}
-
static void dtls1_expect_flight(SSL *ssl) { dtls1_start_timer(ssl); }
static void dtls1_received_flight(SSL *ssl) { dtls1_stop_timer(ssl); }
diff --git a/ssl/dtls_record.c b/ssl/dtls_record.c
index 35c08b0..879706d 100644
--- a/ssl/dtls_record.c
+++ b/ssl/dtls_record.c
@@ -249,16 +249,27 @@
return ssl_open_record_success;
}
-size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch) {
- const SSL_AEAD_CTX *aead = ssl->s3->aead_write_ctx;
+static const SSL_AEAD_CTX *get_write_aead(const SSL *ssl,
+ enum dtls1_use_epoch_t use_epoch) {
if (use_epoch == dtls1_use_previous_epoch) {
/* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
* (negotiated cipher) exist. */
assert(ssl->d1->w_epoch == 1);
- aead = NULL;
+ return NULL;
}
- return DTLS1_RT_HEADER_LENGTH + SSL_AEAD_CTX_explicit_nonce_len(aead);
+ return ssl->s3->aead_write_ctx;
+}
+
+size_t dtls_max_seal_overhead(const SSL *ssl,
+ enum dtls1_use_epoch_t use_epoch) {
+ return DTLS1_RT_HEADER_LENGTH +
+ SSL_AEAD_CTX_max_overhead(get_write_aead(ssl, use_epoch));
+}
+
+size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch) {
+ return DTLS1_RT_HEADER_LENGTH +
+ SSL_AEAD_CTX_explicit_nonce_len(get_write_aead(ssl, use_epoch));
}
int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
diff --git a/ssl/internal.h b/ssl/internal.h
index a320e72..ffe4d62 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -433,6 +433,10 @@
dtls1_use_current_epoch,
};
+/* dtls_max_seal_overhead returns the maximum overhead, in bytes, of sealing a
+ * record. */
+size_t dtls_max_seal_overhead(const SSL *ssl, enum dtls1_use_epoch_t use_epoch);
+
/* dtls_seal_prefix_len returns the number of bytes of prefix to reserve in
* front of the plaintext when sealing a record in-place. */
size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch);
@@ -1645,6 +1649,13 @@
DTLS_OUTGOING_MESSAGE outgoing_messages[SSL_MAX_HANDSHAKE_FLIGHT];
uint8_t outgoing_messages_len;
+ /* outgoing_written is the number of outgoing messages that have been
+ * written. */
+ uint8_t outgoing_written;
+ /* outgoing_offset is the number of bytes of the next outgoing message have
+ * been written. */
+ uint32_t outgoing_offset;
+
unsigned int mtu; /* max DTLS packet size */
/* num_timeouts is the number of times the retransmit timer has fired since
@@ -1779,6 +1790,8 @@
size_t *out_len);
int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len);
int dtls1_write_message(SSL *ssl);
+int dtls1_send_change_cipher_spec(SSL *ssl);
+int dtls1_flush_flight(SSL *ssl);
/* ssl_complete_message calls |finish_message| and |queue_message| on |cbb| to
* queue the message for writing. */
@@ -1805,7 +1818,6 @@
int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len,
enum dtls1_use_epoch_t use_epoch);
-int dtls1_send_change_cipher_spec(SSL *ssl);
int dtls1_send_finished(SSL *ssl, int a, int b, const char *sender, int slen);
int dtls1_retransmit_outgoing_messages(SSL *ssl);
void dtls1_clear_record_buffer(SSL *ssl);
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index ba1ee8f..d653378 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2028,6 +2028,13 @@
}
int ssl_init_wbio_buffer(SSL *ssl) {
+ if (SSL_is_dtls(ssl)) {
+ /* DTLS does not use the BIO buffer.
+ * TODO(davidben): Remove this altogether when TLS no longer uses it.
+ * https://crbug.com/boringssl/72. */
+ return 1;
+ }
+
if (ssl->bbio != NULL) {
/* Already buffered. */
assert(ssl->bbio == ssl->wbio);
diff --git a/ssl/tls_record.c b/ssl/tls_record.c
index 362b0c2..6ff79c4 100644
--- a/ssl/tls_record.c
+++ b/ssl/tls_record.c
@@ -205,20 +205,19 @@
}
size_t SSL_max_seal_overhead(const SSL *ssl) {
- size_t ret = SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
if (SSL_is_dtls(ssl)) {
- ret += DTLS1_RT_HEADER_LENGTH;
- } else if (ssl_uses_short_header(ssl, evp_aead_seal)) {
- ret += 2;
- } else {
- ret += SSL3_RT_HEADER_LENGTH;
+ return dtls_max_seal_overhead(ssl, dtls1_use_current_epoch);
}
+
+ size_t ret =
+ ssl_uses_short_header(ssl, evp_aead_seal) ? 2 : SSL3_RT_HEADER_LENGTH;
+ ret += SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
/* TLS 1.3 needs an extra byte for the encrypted record type. */
if (ssl->s3->have_version &&
ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
ret += 1;
}
- if (!SSL_is_dtls(ssl) && ssl_needs_record_splitting(ssl)) {
+ if (ssl_needs_record_splitting(ssl)) {
ret *= 2;
}
return ret;