Packages
hackney
3.0.0
4.7.2
4.7.1
4.7.0
4.6.1
4.6.0
4.5.2
4.5.1
4.5.0
4.4.5
4.4.3
4.4.2
4.4.1
4.4.0
4.3.0
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.3
4.0.2
4.0.1
4.0.0
3.2.1
3.2.0
3.1.2
3.1.1
3.1.0
3.0.3
3.0.2
3.0.1
3.0.0
retired
2.0.1
2.0.0
2.0.0-beta.1
1.25.0
1.24.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.0
1.15.2
1.15.1
1.15.0
1.14.3
1.14.2
1.14.0
1.13.0
1.12.1
1.12.0
1.11.0
1.10.1
1.10.0
1.9.0
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.0
1.7.1
1.7.0
1.6.6
retired
1.6.5
1.6.4
retired
1.6.3
1.6.2
1.6.1
1.6.0
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.10
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.0
1.0.6
1.0.5
1.0.2
1.0.1
0.15.2
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
Simple HTTP client with HTTP/1.1, HTTP/2, and HTTP/3 support
Retired package: Release invalid - Use 3.0.1 instead
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
c_src/boringssl/ssl/d1_pkt.cc
// Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <openssl/ssl.h>
#include <assert.h>
#include <string.h>
#include <algorithm>
#include <openssl/bio.h>
#include <openssl/bytestring.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/rand.h>
#include "../crypto/internal.h"
#include "internal.h"
BSSL_NAMESPACE_BEGIN
ssl_open_record_t dtls1_process_ack(SSL *ssl, uint8_t *out_alert,
DTLSRecordNumber ack_record_number,
Span<const uint8_t> data) {
// As a DTLS-1.3-capable client, it is possible to receive an ACK before we
// receive ServerHello and learned the server picked DTLS 1.3. Thus, tolerate
// but ignore ACKs before the version is set.
if (!ssl_has_final_version(ssl)) {
return ssl_open_record_discard;
}
// ACKs are only allowed in DTLS 1.3. Reject them if we've negotiated a
// version and it's not 1.3.
if (ssl_protocol_version(ssl) < TLS1_3_VERSION) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
*out_alert = SSL_AD_UNEXPECTED_MESSAGE;
return ssl_open_record_error;
}
CBS cbs = data, record_numbers;
if (!CBS_get_u16_length_prefixed(&cbs, &record_numbers) ||
CBS_len(&cbs) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
*out_alert = SSL_AD_DECODE_ERROR;
return ssl_open_record_error;
}
while (CBS_len(&record_numbers) != 0) {
uint64_t epoch, seq;
if (!CBS_get_u64(&record_numbers, &epoch) ||
!CBS_get_u64(&record_numbers, &seq)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
*out_alert = SSL_AD_DECODE_ERROR;
return ssl_open_record_error;
}
// During the handshake, records must be ACKed at the same or higher epoch.
// See https://www.rfc-editor.org/errata/eid8108. Additionally, if the
// record does not fit in DTLSRecordNumber, it is definitely not a record
// number that we sent.
if ((ack_record_number.epoch() < ssl_encryption_application &&
epoch > ack_record_number.epoch()) ||
epoch > UINT16_MAX || seq > DTLSRecordNumber::kMaxSequence) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
*out_alert = SSL_AD_ILLEGAL_PARAMETER;
return ssl_open_record_error;
}
// Find the sent record that matches this ACK.
DTLSRecordNumber number(static_cast<uint16_t>(epoch), seq);
DTLSSentRecord *sent_record = nullptr;
if (ssl->d1->sent_records != nullptr) {
for (size_t i = 0; i < ssl->d1->sent_records->size(); i++) {
if ((*ssl->d1->sent_records)[i].number == number) {
sent_record = &(*ssl->d1->sent_records)[i];
break;
}
}
}
if (sent_record == nullptr) {
// We may have sent this record and forgotten it, so this is not an error.
continue;
}
// Mark each message as ACKed.
if (sent_record->first_msg == sent_record->last_msg) {
ssl->d1->outgoing_messages[sent_record->first_msg].acked.MarkRange(
sent_record->first_msg_start, sent_record->last_msg_end);
} else {
ssl->d1->outgoing_messages[sent_record->first_msg].acked.MarkRange(
sent_record->first_msg_start, SIZE_MAX);
for (size_t i = size_t{sent_record->first_msg} + 1;
i < sent_record->last_msg; i++) {
ssl->d1->outgoing_messages[i].acked.MarkRange(0, SIZE_MAX);
}
if (sent_record->last_msg_end != 0) {
ssl->d1->outgoing_messages[sent_record->last_msg].acked.MarkRange(
0, sent_record->last_msg_end);
}
}
// Clear the state so we don't bother re-marking the messages next time.
sent_record->first_msg = 0;
sent_record->first_msg_start = 0;
sent_record->last_msg = 0;
sent_record->last_msg_end = 0;
}
// If the outgoing flight is now fully ACKed, we are done retransmitting.
if (std::all_of(ssl->d1->outgoing_messages.begin(),
ssl->d1->outgoing_messages.end(),
[](const auto &msg) { return msg.IsFullyAcked(); })) {
dtls1_stop_timer(ssl);
dtls_clear_outgoing_messages(ssl);
// DTLS 1.3 defers the key update to when the message is ACKed.
if (ssl->s3->key_update_pending) {
if (!tls13_rotate_traffic_key(ssl, evp_aead_seal)) {
return ssl_open_record_error;
}
ssl->s3->key_update_pending = false;
}
// Check for deferred messages.
if (ssl->d1->queued_key_update != QueuedKeyUpdate::kNone) {
int request_type =
ssl->d1->queued_key_update == QueuedKeyUpdate::kUpdateRequested
? SSL_KEY_UPDATE_REQUESTED
: SSL_KEY_UPDATE_NOT_REQUESTED;
ssl->d1->queued_key_update = QueuedKeyUpdate::kNone;
if (!tls13_add_key_update(ssl, request_type)) {
return ssl_open_record_error;
}
}
} else {
// We may still be able to drop unused write epochs.
dtls_clear_unused_write_epochs(ssl);
// TODO(crbug.com/42290594): Schedule a retransmit. The peer will have
// waited before sending the ACK, so a partial ACK suggests packet loss.
}
ssl_do_msg_callback(ssl, /*is_write=*/0, SSL3_RT_ACK, data);
return ssl_open_record_discard;
}
ssl_open_record_t dtls1_open_app_data(SSL *ssl, Span<uint8_t> *out,
size_t *out_consumed, uint8_t *out_alert,
Span<uint8_t> in) {
assert(!SSL_in_init(ssl));
uint8_t type;
DTLSRecordNumber record_number;
Span<uint8_t> record;
auto ret = dtls_open_record(ssl, &type, &record_number, &record, out_consumed,
out_alert, in);
if (ret != ssl_open_record_success) {
return ret;
}
if (type == SSL3_RT_HANDSHAKE) {
// Process handshake fragments for DTLS 1.3 post-handshake messages.
if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
if (!dtls1_process_handshake_fragments(ssl, out_alert, record_number,
record)) {
return ssl_open_record_error;
}
return ssl_open_record_discard;
}
// Parse the first fragment header to determine if this is a pre-CCS or
// post-CCS handshake record. DTLS resets handshake message numbers on each
// handshake, so renegotiations and retransmissions are ambiguous.
//
// TODO(crbug.com/42290594): Move this logic into
// |dtls1_process_handshake_fragments| and integrate it into DTLS 1.3
// retransmit conditions.
CBS cbs, body;
struct hm_header_st msg_hdr;
CBS_init(&cbs, record.data(), record.size());
if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD);
*out_alert = SSL_AD_DECODE_ERROR;
return ssl_open_record_error;
}
if (msg_hdr.type == SSL3_MT_FINISHED &&
msg_hdr.seq == ssl->d1->handshake_read_seq - 1) {
if (!ssl->d1->sending_flight && msg_hdr.frag_off == 0) {
// Retransmit our last flight of messages. If the peer sends the second
// Finished, they may not have received ours. Only do this for the
// first fragment, in case the Finished was fragmented.
//
// This is not really a timeout, but increment the timeout count so we
// eventually give up.
ssl->d1->num_timeouts++;
ssl->d1->sending_flight = true;
}
return ssl_open_record_discard;
}
// Otherwise, this is a pre-CCS handshake message from an unsupported
// renegotiation attempt. Fall through to the error path.
}
if (type == SSL3_RT_ACK) {
return dtls1_process_ack(ssl, out_alert, record_number, record);
}
if (type != SSL3_RT_APPLICATION_DATA) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
*out_alert = SSL_AD_UNEXPECTED_MESSAGE;
return ssl_open_record_error;
}
if (record.empty()) {
return ssl_open_record_discard;
}
*out = record;
return ssl_open_record_success;
}
int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake,
size_t *out_bytes_written, Span<const uint8_t> in) {
assert(!SSL_in_init(ssl));
*out_needs_handshake = false;
if (ssl->s3->write_shutdown != ssl_shutdown_none) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
return -1;
}
// DTLS does not split the input across records.
if (in.size() > SSL3_RT_MAX_PLAIN_LENGTH) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DTLS_MESSAGE_TOO_BIG);
return -1;
}
if (in.empty()) {
*out_bytes_written = 0;
return 1;
}
// TODO(crbug.com/381113363): Use the 0-RTT epoch if writing 0-RTT.
int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, in,
ssl->d1->write_epoch.epoch());
if (ret <= 0) {
return ret;
}
*out_bytes_written = in.size();
return 1;
}
int dtls1_write_record(SSL *ssl, int type, Span<const uint8_t> in,
uint16_t epoch) {
SSLBuffer *buf = &ssl->s3->write_buffer;
assert(in.size() <= SSL3_RT_MAX_PLAIN_LENGTH);
// There should never be a pending write buffer in DTLS. One can't write half
// a datagram, so the write buffer is always dropped in
// |ssl_write_buffer_flush|.
assert(buf->empty());
if (in.size() > SSL3_RT_MAX_PLAIN_LENGTH) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return -1;
}
DTLSRecordNumber record_number;
size_t ciphertext_len;
if (!buf->EnsureCap(dtls_seal_prefix_len(ssl, epoch),
in.size() + SSL_max_seal_overhead(ssl)) ||
!dtls_seal_record(ssl, &record_number, buf->remaining().data(),
&ciphertext_len, buf->remaining().size(), type,
in.data(), in.size(), epoch)) {
buf->Clear();
return -1;
}
buf->DidWrite(ciphertext_len);
int ret = ssl_write_buffer_flush(ssl);
if (ret <= 0) {
return ret;
}
return 1;
}
int dtls1_dispatch_alert(SSL *ssl) {
int ret = dtls1_write_record(ssl, SSL3_RT_ALERT, ssl->s3->send_alert,
ssl->d1->write_epoch.epoch());
if (ret <= 0) {
return ret;
}
ssl->s3->alert_dispatch = false;
// If the alert is fatal, flush the BIO now.
if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
BIO_flush(ssl->wbio.get());
}
ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, ssl->s3->send_alert);
int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, alert);
return 1;
}
BSSL_NAMESPACE_END