Group d1_both.c by sending and receiving handshake messages.
This file is still kind of a mess, but put the two halves together at least.
Change-Id: Ib21d9c4a7f4864cf80e521f7d0ebec029e5955a1
Reviewed-on: https://boringssl-review.googlesource.com/8502
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index e23d294..2ad8fdd 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -139,6 +139,9 @@
* the underlying BIO supplies one. */
static const unsigned int kDefaultMTU = 1500 - 28;
+
+/* Receiving handshake messages. */
+
static void dtls1_hm_fragment_free(hm_fragment *frag) {
if (frag == NULL) {
return;
@@ -235,6 +238,268 @@
frag->reassembly = NULL;
}
+/* dtls1_is_next_message_complete returns one if the next handshake message is
+ * complete and zero otherwise. */
+static int dtls1_is_next_message_complete(SSL *ssl) {
+ hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
+ SSL_MAX_HANDSHAKE_FLIGHT];
+ return frag != NULL && frag->reassembly == NULL;
+}
+
+/* dtls1_get_incoming_message returns the incoming message corresponding to
+ * |msg_hdr|. If none exists, it creates a new one and inserts it in the
+ * queue. Otherwise, it checks |msg_hdr| is consistent with the existing one. It
+ * returns NULL on failure. The caller does not take ownership of the result. */
+static hm_fragment *dtls1_get_incoming_message(
+ SSL *ssl, const struct hm_header_st *msg_hdr) {
+ if (msg_hdr->seq < ssl->d1->handshake_read_seq ||
+ msg_hdr->seq - ssl->d1->handshake_read_seq >= SSL_MAX_HANDSHAKE_FLIGHT) {
+ return NULL;
+ }
+
+ size_t idx = msg_hdr->seq % SSL_MAX_HANDSHAKE_FLIGHT;
+ hm_fragment *frag = ssl->d1->incoming_messages[idx];
+ if (frag != NULL) {
+ assert(frag->msg_header.seq == msg_hdr->seq);
+ /* The new fragment must be compatible with the previous fragments from this
+ * message. */
+ if (frag->msg_header.type != msg_hdr->type ||
+ frag->msg_header.msg_len != msg_hdr->msg_len) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_FRAGMENT_MISMATCH);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ return NULL;
+ }
+ return frag;
+ }
+
+ /* This is the first fragment from this message. */
+ frag = dtls1_hm_fragment_new(msg_hdr->msg_len);
+ if (frag == NULL) {
+ return NULL;
+ }
+ memcpy(&frag->msg_header, msg_hdr, sizeof(*msg_hdr));
+ ssl->d1->incoming_messages[idx] = frag;
+ return frag;
+}
+
+/* dtls1_process_handshake_record reads a handshake record and processes it. It
+ * returns one if the record was successfully processed and 0 or -1 on error. */
+static int dtls1_process_handshake_record(SSL *ssl) {
+ SSL3_RECORD *rr = &ssl->s3->rrec;
+
+start:
+ if (rr->length == 0) {
+ int ret = dtls1_get_record(ssl);
+ if (ret <= 0) {
+ return ret;
+ }
+ }
+
+ /* 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.
+ *
+ * However, only allow the out-of-order records in the correct epoch.
+ * Application data must come in the encrypted epoch, and ChangeCipherSpec in
+ * the unencrypted epoch (we never renegotiate). Other cases fall through and
+ * fail with a fatal error. */
+ if ((rr->type == SSL3_RT_APPLICATION_DATA &&
+ ssl->s3->aead_read_ctx != NULL) ||
+ (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC &&
+ ssl->s3->aead_read_ctx == NULL)) {
+ rr->length = 0;
+ goto start;
+ }
+
+ if (rr->type != SSL3_RT_HANDSHAKE) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
+ return -1;
+ }
+
+ CBS cbs;
+ CBS_init(&cbs, rr->data, rr->length);
+
+ while (CBS_len(&cbs) > 0) {
+ /* Read a handshake fragment. */
+ struct hm_header_st msg_hdr;
+ CBS body;
+ if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ return -1;
+ }
+
+ const size_t frag_off = msg_hdr.frag_off;
+ const size_t frag_len = msg_hdr.frag_len;
+ const size_t msg_len = msg_hdr.msg_len;
+ if (frag_off > msg_len || frag_off + frag_len < frag_off ||
+ frag_off + frag_len > msg_len ||
+ msg_len > ssl_max_handshake_message_len(ssl)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ return -1;
+ }
+
+ if (msg_hdr.seq < ssl->d1->handshake_read_seq ||
+ msg_hdr.seq >
+ (unsigned)ssl->d1->handshake_read_seq + SSL_MAX_HANDSHAKE_FLIGHT) {
+ /* Ignore fragments from the past, or ones too far in the future. */
+ continue;
+ }
+
+ hm_fragment *frag = dtls1_get_incoming_message(ssl, &msg_hdr);
+ if (frag == NULL) {
+ return -1;
+ }
+ assert(frag->msg_header.msg_len == msg_len);
+
+ if (frag->reassembly == NULL) {
+ /* The message is already assembled. */
+ continue;
+ }
+ assert(msg_len > 0);
+
+ /* Copy the body into the fragment. */
+ memcpy(frag->fragment + frag_off, CBS_data(&body), CBS_len(&body));
+ dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len);
+ }
+
+ rr->length = 0;
+ ssl_read_buffer_discard(ssl);
+ return 1;
+}
+
+/* dtls1_get_message reads a handshake message of message type |msg_type| (any
+ * if |msg_type| == -1). Read an entire handshake message. Handshake messages
+ * arrive in fragments. */
+long dtls1_get_message(SSL *ssl, int msg_type,
+ enum ssl_hash_message_t hash_message, int *ok) {
+ hm_fragment *frag = NULL;
+ int al;
+
+ /* s3->tmp is used to store messages that are unexpected, caused
+ * by the absence of an optional handshake message */
+ if (ssl->s3->tmp.reuse_message) {
+ /* A ssl_dont_hash_message call cannot be combined with reuse_message; the
+ * ssl_dont_hash_message would have to have been applied to the previous
+ * call. */
+ assert(hash_message == ssl_hash_message);
+ ssl->s3->tmp.reuse_message = 0;
+ if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
+ al = SSL_AD_UNEXPECTED_MESSAGE;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+ goto f_err;
+ }
+ *ok = 1;
+ assert(ssl->init_buf->length >= DTLS1_HM_HEADER_LENGTH);
+ ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH;
+ ssl->init_num = (int)ssl->init_buf->length - DTLS1_HM_HEADER_LENGTH;
+ return ssl->init_num;
+ }
+
+ /* Process handshake records until the next message is ready. */
+ while (!dtls1_is_next_message_complete(ssl)) {
+ int ret = dtls1_process_handshake_record(ssl);
+ if (ret <= 0) {
+ *ok = 0;
+ return ret;
+ }
+ }
+
+ /* Pop an entry from the ring buffer. */
+ frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
+ SSL_MAX_HANDSHAKE_FLIGHT];
+ ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
+ SSL_MAX_HANDSHAKE_FLIGHT] = NULL;
+
+ assert(frag != NULL);
+ assert(frag->reassembly == NULL);
+ assert(ssl->d1->handshake_read_seq == frag->msg_header.seq);
+
+ ssl->d1->handshake_read_seq++;
+
+ /* Reconstruct the assembled message. */
+ CBB cbb;
+ CBB_zero(&cbb);
+ if (!BUF_MEM_reserve(ssl->init_buf, (size_t)frag->msg_header.msg_len +
+ DTLS1_HM_HEADER_LENGTH) ||
+ !CBB_init_fixed(&cbb, (uint8_t *)ssl->init_buf->data,
+ ssl->init_buf->max) ||
+ !CBB_add_u8(&cbb, frag->msg_header.type) ||
+ !CBB_add_u24(&cbb, frag->msg_header.msg_len) ||
+ !CBB_add_u16(&cbb, frag->msg_header.seq) ||
+ !CBB_add_u24(&cbb, 0 /* frag_off */) ||
+ !CBB_add_u24(&cbb, frag->msg_header.msg_len) ||
+ !CBB_add_bytes(&cbb, frag->fragment, frag->msg_header.msg_len) ||
+ !CBB_finish(&cbb, NULL, &ssl->init_buf->length)) {
+ CBB_cleanup(&cbb);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ assert(ssl->init_buf->length ==
+ (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
+
+ /* TODO(davidben): This function has a lot of implicit outputs. Simplify the
+ * |ssl_get_message| API. */
+ ssl->s3->tmp.message_type = frag->msg_header.type;
+ ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH;
+ ssl->init_num = frag->msg_header.msg_len;
+
+ if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
+ al = SSL_AD_UNEXPECTED_MESSAGE;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+ goto f_err;
+ }
+ if (hash_message == ssl_hash_message && !ssl3_hash_current_message(ssl)) {
+ goto err;
+ }
+
+ ssl_do_msg_callback(ssl, 0 /* read */, ssl->version, SSL3_RT_HANDSHAKE,
+ ssl->init_buf->data,
+ ssl->init_num + DTLS1_HM_HEADER_LENGTH);
+
+ dtls1_hm_fragment_free(frag);
+
+ *ok = 1;
+ return ssl->init_num;
+
+f_err:
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
+err:
+ dtls1_hm_fragment_free(frag);
+ *ok = 0;
+ return -1;
+}
+
+void dtls_clear_incoming_messages(SSL *ssl) {
+ size_t i;
+ for (i = 0; i < SSL_MAX_HANDSHAKE_FLIGHT; i++) {
+ dtls1_hm_fragment_free(ssl->d1->incoming_messages[i]);
+ ssl->d1->incoming_messages[i] = NULL;
+ }
+}
+
+int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr,
+ CBS *out_body) {
+ memset(out_hdr, 0x00, sizeof(struct hm_header_st));
+
+ if (!CBS_get_u8(cbs, &out_hdr->type) ||
+ !CBS_get_u24(cbs, &out_hdr->msg_len) ||
+ !CBS_get_u16(cbs, &out_hdr->seq) ||
+ !CBS_get_u24(cbs, &out_hdr->frag_off) ||
+ !CBS_get_u24(cbs, &out_hdr->frag_len) ||
+ !CBS_get_bytes(cbs, out_body, out_hdr->frag_len)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* 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() &&
@@ -509,241 +774,6 @@
return ret;
}
-/* dtls1_is_next_message_complete returns one if the next handshake message is
- * complete and zero otherwise. */
-static int dtls1_is_next_message_complete(SSL *ssl) {
- hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
- SSL_MAX_HANDSHAKE_FLIGHT];
- return frag != NULL && frag->reassembly == NULL;
-}
-
-/* dtls1_get_incoming_message returns the incoming message corresponding to
- * |msg_hdr|. If none exists, it creates a new one and inserts it in the
- * queue. Otherwise, it checks |msg_hdr| is consistent with the existing one. It
- * returns NULL on failure. The caller does not take ownership of the result. */
-static hm_fragment *dtls1_get_incoming_message(
- SSL *ssl, const struct hm_header_st *msg_hdr) {
- if (msg_hdr->seq < ssl->d1->handshake_read_seq ||
- msg_hdr->seq - ssl->d1->handshake_read_seq >= SSL_MAX_HANDSHAKE_FLIGHT) {
- return NULL;
- }
-
- size_t idx = msg_hdr->seq % SSL_MAX_HANDSHAKE_FLIGHT;
- hm_fragment *frag = ssl->d1->incoming_messages[idx];
- if (frag != NULL) {
- assert(frag->msg_header.seq == msg_hdr->seq);
- /* The new fragment must be compatible with the previous fragments from this
- * message. */
- if (frag->msg_header.type != msg_hdr->type ||
- frag->msg_header.msg_len != msg_hdr->msg_len) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_FRAGMENT_MISMATCH);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- return NULL;
- }
- return frag;
- }
-
- /* This is the first fragment from this message. */
- frag = dtls1_hm_fragment_new(msg_hdr->msg_len);
- if (frag == NULL) {
- return NULL;
- }
- memcpy(&frag->msg_header, msg_hdr, sizeof(*msg_hdr));
- ssl->d1->incoming_messages[idx] = frag;
- return frag;
-}
-
-/* dtls1_process_handshake_record reads a handshake record and processes it. It
- * returns one if the record was successfully processed and 0 or -1 on error. */
-static int dtls1_process_handshake_record(SSL *ssl) {
- SSL3_RECORD *rr = &ssl->s3->rrec;
-
-start:
- if (rr->length == 0) {
- int ret = dtls1_get_record(ssl);
- if (ret <= 0) {
- return ret;
- }
- }
-
- /* 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.
- *
- * However, only allow the out-of-order records in the correct epoch.
- * Application data must come in the encrypted epoch, and ChangeCipherSpec in
- * the unencrypted epoch (we never renegotiate). Other cases fall through and
- * fail with a fatal error. */
- if ((rr->type == SSL3_RT_APPLICATION_DATA &&
- ssl->s3->aead_read_ctx != NULL) ||
- (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC &&
- ssl->s3->aead_read_ctx == NULL)) {
- rr->length = 0;
- goto start;
- }
-
- if (rr->type != SSL3_RT_HANDSHAKE) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
- return -1;
- }
-
- CBS cbs;
- CBS_init(&cbs, rr->data, rr->length);
-
- while (CBS_len(&cbs) > 0) {
- /* Read a handshake fragment. */
- struct hm_header_st msg_hdr;
- CBS body;
- if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
- return -1;
- }
-
- const size_t frag_off = msg_hdr.frag_off;
- const size_t frag_len = msg_hdr.frag_len;
- const size_t msg_len = msg_hdr.msg_len;
- if (frag_off > msg_len || frag_off + frag_len < frag_off ||
- frag_off + frag_len > msg_len ||
- msg_len > ssl_max_handshake_message_len(ssl)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- return -1;
- }
-
- if (msg_hdr.seq < ssl->d1->handshake_read_seq ||
- msg_hdr.seq >
- (unsigned)ssl->d1->handshake_read_seq + SSL_MAX_HANDSHAKE_FLIGHT) {
- /* Ignore fragments from the past, or ones too far in the future. */
- continue;
- }
-
- hm_fragment *frag = dtls1_get_incoming_message(ssl, &msg_hdr);
- if (frag == NULL) {
- return -1;
- }
- assert(frag->msg_header.msg_len == msg_len);
-
- if (frag->reassembly == NULL) {
- /* The message is already assembled. */
- continue;
- }
- assert(msg_len > 0);
-
- /* Copy the body into the fragment. */
- memcpy(frag->fragment + frag_off, CBS_data(&body), CBS_len(&body));
- dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len);
- }
-
- rr->length = 0;
- ssl_read_buffer_discard(ssl);
- return 1;
-}
-
-/* dtls1_get_message reads a handshake message of message type |msg_type| (any
- * if |msg_type| == -1). Read an entire handshake message. Handshake messages
- * arrive in fragments. */
-long dtls1_get_message(SSL *ssl, int msg_type,
- enum ssl_hash_message_t hash_message, int *ok) {
- hm_fragment *frag = NULL;
- int al;
-
- /* s3->tmp is used to store messages that are unexpected, caused
- * by the absence of an optional handshake message */
- if (ssl->s3->tmp.reuse_message) {
- /* A ssl_dont_hash_message call cannot be combined with reuse_message; the
- * ssl_dont_hash_message would have to have been applied to the previous
- * call. */
- assert(hash_message == ssl_hash_message);
- ssl->s3->tmp.reuse_message = 0;
- if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
- al = SSL_AD_UNEXPECTED_MESSAGE;
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
- goto f_err;
- }
- *ok = 1;
- assert(ssl->init_buf->length >= DTLS1_HM_HEADER_LENGTH);
- ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH;
- ssl->init_num = (int)ssl->init_buf->length - DTLS1_HM_HEADER_LENGTH;
- return ssl->init_num;
- }
-
- /* Process handshake records until the next message is ready. */
- while (!dtls1_is_next_message_complete(ssl)) {
- int ret = dtls1_process_handshake_record(ssl);
- if (ret <= 0) {
- *ok = 0;
- return ret;
- }
- }
-
- /* Pop an entry from the ring buffer. */
- frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
- SSL_MAX_HANDSHAKE_FLIGHT];
- ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
- SSL_MAX_HANDSHAKE_FLIGHT] = NULL;
-
- assert(frag != NULL);
- assert(frag->reassembly == NULL);
- assert(ssl->d1->handshake_read_seq == frag->msg_header.seq);
-
- ssl->d1->handshake_read_seq++;
-
- /* Reconstruct the assembled message. */
- CBB cbb;
- CBB_zero(&cbb);
- if (!BUF_MEM_reserve(ssl->init_buf, (size_t)frag->msg_header.msg_len +
- DTLS1_HM_HEADER_LENGTH) ||
- !CBB_init_fixed(&cbb, (uint8_t *)ssl->init_buf->data,
- ssl->init_buf->max) ||
- !CBB_add_u8(&cbb, frag->msg_header.type) ||
- !CBB_add_u24(&cbb, frag->msg_header.msg_len) ||
- !CBB_add_u16(&cbb, frag->msg_header.seq) ||
- !CBB_add_u24(&cbb, 0 /* frag_off */) ||
- !CBB_add_u24(&cbb, frag->msg_header.msg_len) ||
- !CBB_add_bytes(&cbb, frag->fragment, frag->msg_header.msg_len) ||
- !CBB_finish(&cbb, NULL, &ssl->init_buf->length)) {
- CBB_cleanup(&cbb);
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- goto err;
- }
- assert(ssl->init_buf->length ==
- (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
-
- /* TODO(davidben): This function has a lot of implicit outputs. Simplify the
- * |ssl_get_message| API. */
- ssl->s3->tmp.message_type = frag->msg_header.type;
- ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH;
- ssl->init_num = frag->msg_header.msg_len;
-
- if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
- al = SSL_AD_UNEXPECTED_MESSAGE;
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
- goto f_err;
- }
- if (hash_message == ssl_hash_message && !ssl3_hash_current_message(ssl)) {
- goto err;
- }
-
- ssl_do_msg_callback(ssl, 0 /* read */, ssl->version, SSL3_RT_HANDSHAKE,
- ssl->init_buf->data,
- ssl->init_num + DTLS1_HM_HEADER_LENGTH);
-
- dtls1_hm_fragment_free(frag);
-
- *ok = 1;
- return ssl->init_num;
-
-f_err:
- ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-err:
- dtls1_hm_fragment_free(frag);
- *ok = 0;
- return -1;
-}
-
static int dtls1_retransmit_message(SSL *ssl,
const DTLS_OUTGOING_MESSAGE *msg) {
/* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
@@ -807,30 +837,6 @@
return dtls1_write_change_cipher_spec(ssl, dtls1_use_current_epoch);
}
-void dtls_clear_incoming_messages(SSL *ssl) {
- size_t i;
- for (i = 0; i < SSL_MAX_HANDSHAKE_FLIGHT; i++) {
- dtls1_hm_fragment_free(ssl->d1->incoming_messages[i]);
- ssl->d1->incoming_messages[i] = NULL;
- }
-}
-
unsigned int dtls1_min_mtu(void) {
return kMinMTU;
}
-
-int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr,
- CBS *out_body) {
- memset(out_hdr, 0x00, sizeof(struct hm_header_st));
-
- if (!CBS_get_u8(cbs, &out_hdr->type) ||
- !CBS_get_u24(cbs, &out_hdr->msg_len) ||
- !CBS_get_u16(cbs, &out_hdr->seq) ||
- !CBS_get_u24(cbs, &out_hdr->frag_off) ||
- !CBS_get_u24(cbs, &out_hdr->frag_len) ||
- !CBS_get_bytes(cbs, out_body, out_hdr->frag_len)) {
- return 0;
- }
-
- return 1;
-}