blob: 248cfcb6b9c40dd7fd1d10b7c6ba3a12367c3d56 [file] [log] [blame]
/** @file
*
* A brief file description
*
* @section license License
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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 <cstdlib>
#include "tscore/Diags.h"
#include "QUICGlobals.h"
#include "QUICIntUtil.h"
#include "QUICTransportParameters.h"
#include "QUICConnection.h"
#include "QUICHandshake.h"
#include "QUICDebugNames.h"
#include "QUICTLS.h"
#include "QUICTypes.h"
static constexpr char tag[] = "quic_handshake";
static constexpr uint32_t TP_ERROR_LENGTH = 0x010000;
static constexpr uint32_t TP_ERROR_VALUE = 0x020000;
// static constexpr uint32_t TP_ERROR_MUST_EXIST = 0x030000;
static constexpr uint32_t TP_ERROR_MUST_NOT_EXIST = 0x040000;
QUICTransportParameters::Value::Value(const uint8_t *data, uint16_t len) : _len(len)
{
this->_data = static_cast<uint8_t *>(ats_malloc(len));
memcpy(this->_data, data, len);
}
QUICTransportParameters::Value::~Value()
{
ats_free(this->_data);
this->_data = nullptr;
}
bool
QUICTransportParameters::is_valid() const
{
return this->_valid;
}
const uint8_t *
QUICTransportParameters::Value::data() const
{
return this->_data;
}
uint16_t
QUICTransportParameters::Value::len() const
{
return this->_len;
}
QUICTransportParameters::~QUICTransportParameters()
{
for (auto p : this->_parameters) {
delete p.second;
}
}
void
QUICTransportParameters::_load(const uint8_t *buf, size_t len)
{
bool has_error = false;
const uint8_t *p = buf;
// Read size of parameters field
uint16_t nbytes = (p[0] << 8) + p[1];
p += 2;
// Read parameters
const uint8_t *end = p + nbytes;
while (p < end) {
// Read ID
uint16_t id = 0;
if (end - p >= 2) {
id = (p[0] << 8) + p[1];
p += 2;
} else {
has_error = true;
break;
}
// Check duplication
// An endpoint MUST treat receipt of duplicate transport parameters as a connection error of type TRANSPORT_PARAMETER_ERROR
if (this->_parameters.find(id) != this->_parameters.end()) {
has_error = true;
break;
}
// Read length of value
uint16_t len = 0;
if (end - p >= 2) {
len = (p[0] << 8) + p[1];
p += 2;
} else {
has_error = true;
break;
}
// Store parameter
if (end - p >= len) {
this->_parameters.insert(std::make_pair(id, new Value(p, len)));
p += len;
} else {
has_error = true;
break;
}
}
if (has_error) {
this->_valid = false;
return;
}
// Validate parameters
int res = this->_validate_parameters();
if (res < 0) {
Debug(tag, "Transport parameter is not valid (err=%d)", res);
this->_valid = false;
} else {
this->_valid = true;
}
}
int
QUICTransportParameters::_validate_parameters() const
{
decltype(this->_parameters)::const_iterator ite;
// MUSTs
// MAYs
if ((ite = this->_parameters.find(QUICTransportParameterId::INITIAL_MAX_DATA)) != this->_parameters.end()) {
}
if ((ite = this->_parameters.find(QUICTransportParameterId::INITIAL_MAX_STREAMS_BIDI)) != this->_parameters.end()) {
}
if ((ite = this->_parameters.find(QUICTransportParameterId::INITIAL_MAX_STREAMS_UNI)) != this->_parameters.end()) {
}
if ((ite = this->_parameters.find(QUICTransportParameterId::IDLE_TIMEOUT)) != this->_parameters.end()) {
}
if ((ite = this->_parameters.find(QUICTransportParameterId::MAX_PACKET_SIZE)) != this->_parameters.end()) {
if (QUICIntUtil::read_nbytes_as_uint(ite->second->data(), ite->second->len()) < 1200) {
return -(TP_ERROR_VALUE | QUICTransportParameterId::MAX_PACKET_SIZE);
}
}
if ((ite = this->_parameters.find(QUICTransportParameterId::ACK_DELAY_EXPONENT)) != this->_parameters.end()) {
if (QUICIntUtil::read_nbytes_as_uint(ite->second->data(), ite->second->len()) > 20) {
return -(TP_ERROR_VALUE | QUICTransportParameterId::ACK_DELAY_EXPONENT);
}
}
// MAYs (initial values for the flow control on each type of stream)
if ((ite = this->_parameters.find(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_BIDI_LOCAL)) != this->_parameters.end()) {
}
if ((ite = this->_parameters.find(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_BIDI_REMOTE)) != this->_parameters.end()) {
}
if ((ite = this->_parameters.find(QUICTransportParameterId::INITIAL_MAX_STREAM_DATA_UNI)) != this->_parameters.end()) {
}
if ((ite = this->_parameters.find(QUICTransportParameterId::DISABLE_ACTIVE_MIGRATION)) != this->_parameters.end()) {
}
if ((ite = this->_parameters.find(QUICTransportParameterId::MAX_ACK_DELAY)) != this->_parameters.end()) {
}
return 0;
}
const uint8_t *
QUICTransportParameters::getAsBytes(QUICTransportParameterId tpid, uint16_t &len) const
{
auto p = this->_parameters.find(tpid);
if (p != this->_parameters.end()) {
len = p->second->len();
return p->second->data();
}
len = 0;
return nullptr;
}
uint64_t
QUICTransportParameters::getAsUInt(QUICTransportParameterId tpid) const
{
uint64_t int_value = 0;
size_t int_value_len = 0;
uint16_t raw_value_len = 0;
const uint8_t *raw_value = this->getAsBytes(tpid, raw_value_len);
if (raw_value) {
QUICVariableInt::decode(int_value, int_value_len, raw_value, raw_value_len);
return int_value;
} else {
return 0;
}
}
bool
QUICTransportParameters::contains(QUICTransportParameterId id) const
{
// Use std::map::contains when C++20 is supported
auto p = this->_parameters.find(id);
return (p != this->_parameters.end());
}
void
QUICTransportParameters::set(QUICTransportParameterId id, const uint8_t *value, uint16_t value_len)
{
if (this->_parameters.find(id) != this->_parameters.end()) {
this->_parameters.erase(id);
}
this->_parameters.insert(std::make_pair(id, new Value(value, value_len)));
}
void
QUICTransportParameters::set(QUICTransportParameterId id, uint64_t value)
{
uint8_t v[8];
size_t n;
QUICIntUtil::write_QUICVariableInt(value, v, &n);
this->set(id, v, n);
}
void
QUICTransportParameters::store(uint8_t *buf, uint16_t *len) const
{
uint8_t *p = buf;
// Write QUIC versions
this->_store(p, len);
p += *len;
// Write parameters
// XXX parameters_size will be written later
uint8_t *parameters_size = p;
p += sizeof(uint16_t);
for (auto &it : this->_parameters) {
// TODO Skip non-MUST parameters that have their default values
p[0] = (it.first & 0xff00) >> 8;
p[1] = it.first & 0xff;
p += 2;
p[0] = (it.second->len() & 0xff00) >> 8;
p[1] = it.second->len() & 0xff;
p += 2;
memcpy(p, it.second->data(), it.second->len());
p += it.second->len();
}
ptrdiff_t n = p - parameters_size - sizeof(uint16_t);
parameters_size[0] = (n & 0xff00) >> 8;
parameters_size[1] = n & 0xff;
*len = (p - buf);
}
void
QUICTransportParameters::_print() const
{
for (auto &p : this->_parameters) {
if (p.second->len() == 0) {
Debug(tag, "%s: (no value)", QUICDebugNames::transport_parameter_id(p.first));
} else if (p.second->len() <= 8) {
uint64_t int_value;
size_t int_value_len;
QUICVariableInt::decode(int_value, int_value_len, p.second->data(), p.second->len());
Debug(tag, "%s (%" PRIu32 "): 0x%" PRIx64 " (%" PRIu64 ")", QUICDebugNames::transport_parameter_id(p.first),
static_cast<uint16_t>(p.first), int_value, int_value);
} else if (p.second->len() <= 24) {
char hex_str[65];
to_hex_str(hex_str, sizeof(hex_str), p.second->data(), p.second->len());
Debug(tag, "%s (%" PRIu32 "): %s", QUICDebugNames::transport_parameter_id(p.first), static_cast<uint16_t>(p.first), hex_str);
} else if (QUICTransportParameterId::PREFERRED_ADDRESS == p.first) {
QUICPreferredAddress pref_addr(p.second->data(), p.second->len());
char cid_hex_str[QUICConnectionId::MAX_HEX_STR_LENGTH];
char token_hex_str[QUICStatelessResetToken::LEN * 2 + 1];
char ep_ipv4_hex_str[512];
char ep_ipv6_hex_str[512];
pref_addr.cid().hex(cid_hex_str, sizeof(cid_hex_str));
to_hex_str(token_hex_str, sizeof(token_hex_str), pref_addr.token().buf(), QUICStatelessResetToken::LEN);
ats_ip_nptop(pref_addr.endpoint_ipv4(), ep_ipv4_hex_str, sizeof(ep_ipv4_hex_str));
ats_ip_nptop(pref_addr.endpoint_ipv6(), ep_ipv6_hex_str, sizeof(ep_ipv6_hex_str));
Debug(tag, "%s: Endpoint(IPv4)=%s, Endpoint(IPv6)=%s, CID=%s, Token=%s", QUICDebugNames::transport_parameter_id(p.first),
ep_ipv4_hex_str, ep_ipv6_hex_str, cid_hex_str, token_hex_str);
} else {
Debug(tag, "%s: (long data)", QUICDebugNames::transport_parameter_id(p.first));
}
}
}
//
// QUICTransportParametersInClientHello
//
QUICTransportParametersInClientHello::QUICTransportParametersInClientHello(const uint8_t *buf, size_t len)
{
this->_load(buf, len);
if (is_debug_tag_set(tag)) {
this->_print();
}
}
void
QUICTransportParametersInClientHello::_store(uint8_t *buf, uint16_t *len) const
{
*len = 0;
}
std::ptrdiff_t
QUICTransportParametersInClientHello::_parameters_offset(const uint8_t *) const
{
return 4; // sizeof(QUICVersion)
}
int
QUICTransportParametersInClientHello::_validate_parameters() const
{
int res = QUICTransportParameters::_validate_parameters();
if (res < 0) {
return res;
}
decltype(this->_parameters)::const_iterator ite;
// MUST NOTs
if ((ite = this->_parameters.find(QUICTransportParameterId::STATELESS_RESET_TOKEN)) != this->_parameters.end()) {
return -(TP_ERROR_MUST_NOT_EXIST | QUICTransportParameterId::STATELESS_RESET_TOKEN);
}
if ((ite = this->_parameters.find(QUICTransportParameterId::PREFERRED_ADDRESS)) != this->_parameters.end()) {
return -(TP_ERROR_MUST_NOT_EXIST | QUICTransportParameterId::PREFERRED_ADDRESS);
}
return 0;
}
//
// QUICTransportParametersInEncryptedExtensions
//
QUICTransportParametersInEncryptedExtensions::QUICTransportParametersInEncryptedExtensions(const uint8_t *buf, size_t len)
{
this->_load(buf, len);
if (is_debug_tag_set(tag)) {
this->_print();
}
}
void
QUICTransportParametersInEncryptedExtensions::_store(uint8_t *buf, uint16_t *len) const
{
*len = 0;
}
void
QUICTransportParametersInEncryptedExtensions::add_version(QUICVersion version)
{
this->_versions[this->_n_versions++] = version;
}
std::ptrdiff_t
QUICTransportParametersInEncryptedExtensions::_parameters_offset(const uint8_t *buf) const
{
return 4 + 1 + buf[4];
}
int
QUICTransportParametersInEncryptedExtensions::_validate_parameters() const
{
int res = QUICTransportParameters::_validate_parameters();
if (res < 0) {
return res;
}
decltype(this->_parameters)::const_iterator ite;
// MUSTs if the server sent a Retry packet
if ((ite = this->_parameters.find(QUICTransportParameterId::ORIGINAL_CONNECTION_ID)) != this->_parameters.end()) {
// We cannot check the length because it's not a fixed length.
} else {
// TODO Need a way that checks if we received a Retry from the server
// return -(TP_ERROR_MUST_EXIST | QUICTransportParameterId::ORIGINAL_CONNECTION_ID);
}
// MAYs
if ((ite = this->_parameters.find(QUICTransportParameterId::STATELESS_RESET_TOKEN)) != this->_parameters.end()) {
if (ite->second->len() != 16) {
return -(TP_ERROR_LENGTH | QUICTransportParameterId::STATELESS_RESET_TOKEN);
}
}
if ((ite = this->_parameters.find(QUICTransportParameterId::PREFERRED_ADDRESS)) != this->_parameters.end()) {
if (ite->second->len() < QUICPreferredAddress::MIN_LEN || QUICPreferredAddress::MAX_LEN < ite->second->len()) {
return -(TP_ERROR_LENGTH | QUICTransportParameterId::PREFERRED_ADDRESS);
}
}
return 0;
}
#ifndef OPENSSL_IS_BORINGSSL
static constexpr int TRANSPORT_PARAMETERS_MAXIMUM_SIZE = 65535;
//
// QUICTransportParametersHandler
//
int
QUICTransportParametersHandler::add(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char **out, size_t *outlen,
X509 *x, size_t chainidx, int *al, void *add_arg)
{
QUICTLS *qtls = static_cast<QUICTLS *>(SSL_get_ex_data(s, QUIC::ssl_quic_tls_index));
*out = reinterpret_cast<const unsigned char *>(ats_malloc(TRANSPORT_PARAMETERS_MAXIMUM_SIZE));
qtls->local_transport_parameters()->store(const_cast<uint8_t *>(*out), reinterpret_cast<uint16_t *>(outlen));
return 1;
}
void
QUICTransportParametersHandler::free(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char *out, void *add_arg)
{
ats_free(const_cast<unsigned char *>(out));
}
int
QUICTransportParametersHandler::parse(SSL *s, unsigned int ext_type, unsigned int context, const unsigned char *in, size_t inlen,
X509 *x, size_t chainidx, int *al, void *parse_arg)
{
QUICTLS *qtls = static_cast<QUICTLS *>(SSL_get_ex_data(s, QUIC::ssl_quic_tls_index));
switch (context) {
case SSL_EXT_CLIENT_HELLO:
qtls->set_remote_transport_parameters(std::make_shared<QUICTransportParametersInClientHello>(in, inlen));
break;
case SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS:
qtls->set_remote_transport_parameters(std::make_shared<QUICTransportParametersInEncryptedExtensions>(in, inlen));
break;
default:
// Do nothing
break;
}
return 1;
}
#endif