<commit_before>// SPDX-License-Identifier: BSD-2-Clause
//
// Copyright (c) 2016-2020, NetApp, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#ifndef NO_TLS_LOG
#include <stdarg.h>
#include <stdio.h>
#endif

#ifndef NO_SERVER
#include <sys/param.h>
#endif

#ifdef WITH_OPENSSL
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/ossl_typ.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <picotls/fusion.h>
#include <picotls/openssl.h>

#define aes128gcmsha256 ptls_openssl_aes128gcmsha256
#define secp256r1 ptls_openssl_secp256r1
#ifndef MINIMAL_CIPHERS
#define x25519 ptls_openssl_x25519
#endif
#else
#include <picotls/minicrypto.h>

#ifndef MINIMAL_CIPHERS
#define cipher_suite ptls_minicrypto_cipher_suites
#define x25519 ptls_minicrypto_x25519
#endif
#define aes128gcmsha256 ptls_minicrypto_aes128gcmsha256
#define secp256r1 ptls_minicrypto_secp256r1
#endif

#include <quant/quant.h>


#if defined(PARTICLE) || defined(RIOT_VERSION)
#if defined(PARTICLE)
#define MAXPATHLEN 8
#endif

#include <uECC.h>

void ptls_minicrypto_random_bytes(void * buf, size_t len)
{
    while (len >= sizeof(uint32_t)) {
        *((uint32_t *)buf) = w_rand32();
        buf += sizeof(uint32_t);
        len -= sizeof(uint32_t);
    }
    while (len > 0) {
        *((uint8_t *)buf) = w_rand32();
        buf += sizeof(uint8_t);
        len -= sizeof(uint8_t);
    }
}


static int uecc_rng(uint8_t * dest, unsigned size)
{
    ptls_minicrypto_random_bytes(dest, size);
    return 1;
}
#else
#include <errno.h>
#endif


#include "bitset.h"
#include "cid.h"
#include "conn.h"
#include "frame.h"
#include "marshall.h"
#include "pkt.h"
#include "pn.h"
#include "quic.h"
#include "recovery.h"
#include "stream.h"
#include "tls.h"
#include "tree.h"

#ifndef NO_SERVER
#include "diet.h"
#endif


#ifndef NO_ERR_REASONS
static const char * const ptls_err_str[] = {
    [0] = "CLOSE_NOTIFY",
    [10] = "UNEXPECTED_MESSAGE",
    [20] = "BAD_RECORD_MAC",
    [40] = "HANDSHAKE_FAILURE",
    [42] = "BAD_CERTIFICATE",
    [44] = "CERTIFICATE_REVOKED",
    [45] = "CERTIFICATE_EXPIRED",
    [46] = "CERTIFICATE_UNKNOWN",
    [47] = "ILLEGAL_PARAMETER",
    [48] = "UNKNOWN_CA",
    [50] = "DECODE_ERROR",
    [51] = "DECRYPT_ERROR",
    [70] = "PROTOCOL_VERSION",
    [80] = "INTERNAL_ERROR",
    [90] = "USER_CANCELED",
    [109] = "MISSING_EXTENSION",
    [112] = "UNRECOGNIZED_NAME",
    [116] = "CERTIFICATE_REQUIRED",
    [120] = "NO_APPLICATION_PROTOCOL",
};
#endif


struct tls_ticket {
#if !defined(PARTICLE) && !defined(RIOT_VERSION)
    splay_entry(tls_ticket) node;
#endif
    char * sni;
    char * alpn;
    uint8_t * ticket;
    size_t ticket_len;
    struct transport_params tp;
    uint32_t vers;
    uint8_t _unused[12];
};


struct tickets_by_peer {
#if !defined(PARTICLE) && !defined(RIOT_VERSION)
    splay_head(, tls_ticket);
#else
    struct tls_ticket last_ticket;
#endif
};

static struct tickets_by_peer tickets;


#if !defined(PARTICLE) && !defined(RIOT_VERSION)
static int __attribute__((nonnull))
tls_ticket_cmp(const struct tls_ticket * const a,
               const struct tls_ticket * const b)
{
    int diff = strcmp(a->sni, b->sni);
    if (diff)
        return diff;
    if (*a->alpn == 0 || *b->alpn == 0)
        return 0;
    diff = strcmp(a->alpn, b->alpn);
    return diff;
}


SPLAY_PROTOTYPE(tickets_by_peer, tls_ticket, node, tls_ticket_cmp)
SPLAY_GENERATE(tickets_by_peer, tls_ticket, node, tls_ticket_cmp)
#endif


#define ALPN_LEN 16

// first entry is client default, if not otherwise specified
static const ptls_iovec_t alpn[] = {
    {(uint8_t[ALPN_LEN]){"hq-" DRAFT_VERSION_STRING}, 5},
    {(uint8_t[ALPN_LEN]){"hq-interop"}, 10}};
static const size_t alpn_cnt = sizeof(alpn) / sizeof(alpn[0]);


#define QUIC_TP 0x39

#define TP_DCID_O 0x00 ///< original_destination_connection_id
#define TP_IDTO 0x01 ///< idle_timeout
#define TP_SRT 0x02 ///< stateless_reset_token
#define TP_MUPS 0x03 ///< max_udp_payload_size
#define TP_IMD 0x04 ///< initial_max_data
#define TP_IMSD_BL 0x05 ///< initial_max_stream_data_bidi_local
#define TP_IMSD_BR 0x06 ///< initial_max_stream_data_bidi_remote
#define TP_IMSD_U 0x07 ///< initial_max_stream_data_uni
#define TP_IMSB 0x08 ///< initial_max_streams_bidi
#define TP_IMSU 0x09 ///< initial_max_streams_uni
#define TP_ADE 0x0a ///< ack_delay_exponent
#define TP_MAD 0x0b ///< max_ack_delay
#define TP_DMIG 0x0c ///< disable_active_migration
#define TP_PRFA 0x0d ///< preferred_address
#define TP_ACIL 0x0e ///< active_connection_id_limit
#define TP_SCID_I 0x0f ///< initial_source_connection_id
#define TP_SCID_R 0x10 ///< retry_source_connection_id
#define TP_MAX (TP_SCID_R + 1)

#define TP_QBG 0x2ab2 // grease_quic_bit
#define TP_QR 3127


#define err_close_return(...)                                                  \
    do {                                                                       \
        err_close(__VA_ARGS__);                                                \
        return 1;                                                              \
    } while (0)


// quicly shim
#define AEAD_BASE_LABEL PTLS_HKDF_EXPAND_LABEL_PREFIX "quic "
#define st_quicly_cipher_context_t cipher_ctx


// from quicly
void dispose_cipher(struct st_quicly_cipher_context_t * ctx)
{
    if (ctx->aead) {
        ptls_aead_free(ctx->aead);
        ctx->aead = 0;
    }
    if (ctx->header_protection) {
        ptls_cipher_free(ctx->header_protection);
        ctx->header_protection = 0;
    }
}


// from quicly (with mods for key update)
static int setup_cipher(ptls_cipher_context_t ** hp_ctx,
                        ptls_aead_context_t ** aead_ctx,
                        ptls_aead_algorithm_t * aead,
                        ptls_hash_algorithm_t * hash,
                        int is_enc,
                        const void * secret)
{
    uint8_t hpkey[PTLS_MAX_SECRET_SIZE] = {0};
    int ret;

    if (hp_ctx) {
        if ((ret = ptls_hkdf_expand_label(
                 hash, hpkey, aead->ctr_cipher->key_size,
                 ptls_iovec_init(secret, hash->digest_size), "quic hp",
                 ptls_iovec_init(NULL, 0), NULL)) != 0)
            goto Exit;
        if ((*hp_ctx = ptls_cipher_new(aead->ctr_cipher, is_enc, hpkey)) ==
            NULL) {
            ret = PTLS_ERROR_NO_MEMORY;
            goto Exit;
        }
    }
    ensure(aead_ctx, "aead_ctx is zero");
    if ((*aead_ctx = ptls_aead_new(aead, hash, is_enc, secret,
                                   AEAD_BASE_LABEL)) == NULL) {
        ret = PTLS_ERROR_NO_MEMORY;
        goto Exit;
    }

#ifdef DEBUG_PROT
    warn(NTE, "aead-secret: %s, hp-key: %s",
         hex2str(secret, hash->digest_size,
                 (char[hex_str_len(PTLS_MAX_DIGEST_SIZE)]){""},
                 hex_str_len(PTLS_MAX_DIGEST_SIZE)),
         hex2str(hpkey, aead->ctr_cipher->key_size,
                 (char[hex_str_len(PTLS_MAX_DIGEST_SIZE)]){""},
                 hex_str_len(PTLS_MAX_DIGEST_SIZE)));
#endif

    ret = 0;
Exit:
    if (ret != 0) {
        if (*aead_ctx != NULL) {
            ptls_aead_free(*aead_ctx);
            *aead_ctx = NULL;
        }
        if (hp_ctx && *hp_ctx != NULL) {
            ptls_cipher_free(*hp_ctx);
            *hp_ctx = NULL;
        }
    }
    ptls_clear_memory(hpkey, sizeof(hpkey));
    return ret;
}


// from quicly (with mods for key update)
static int setup_initial_key(struct st_quicly_cipher_context_t * ctx,
                             ptls_cipher_suite_t * cs,
                             const void * master_secret,
                             const char * label,
                             int is_enc,
                             void * new_secret)
{
    uint8_t _aead_secret[PTLS_MAX_DIGEST_SIZE];
    uint8_t * const aead_secret = new_secret ? new_secret : _aead_secret;

    int ret;

    if ((ret = ptls_hkdf_expand_label(
             cs->hash, aead_secret, cs->hash->digest_size,
             ptls_iovec_init(master_secret, cs->hash->digest_size), label,
             ptls_iovec_init(NULL, 0), NULL)) != 0)
        goto Exit;
    if ((ret =
             setup_cipher(new_secret ? 0 : &ctx->header_protection, &ctx->aead,
                          cs->aead, cs->hash, is_enc, aead_secret)) != 0)
        goto Exit;

Exit:
    ptls_clear_memory(_aead_secret, sizeof(_aead_secret));
    return ret;
}


// from quicly (with mods)
static int setup_initial_encryption(struct st_quicly_cipher_context_t * ingress,
                                    struct st_quicly_cipher_context_t * egress,
                                    ptls_iovec_t cid,
                                    int is_client)
{
    static const uint8_t salt[] = {0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34,
                                   0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
                                   0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a};
    static const char * labels[2] = {"client in", "server in"};
    ptls_cipher_suite_t * const cs = &aes128gcmsha256;
    uint8_t secret[PTLS_MAX_DIGEST_SIZE];
    int ret;

    /* extract master secret */
    if ((ret = ptls_hkdf_extract(
             cs->hash, secret, ptls_iovec_init(salt, sizeof(salt)), cid)) != 0)
        goto Exit;

    /* create aead contexts */
    if ((ret = setup_initial_key(ingress, cs, secret, labels[is_client], 0,
                                 0)) != 0)
        goto Exit;
    if ((ret = setup_initial_key(egress, cs, secret, labels[!is_client], 1,
                                 0)) != 0)
        goto Exit;

Exit:
    ptls_clear_memory(secret, sizeof(secret));
    return ret;
}


static int __attribute__((nonnull))
on_ch(ptls_on_client_hello_t * const self __attribute__((unused)),
      ptls_t * const tls,
      ptls_on_client_hello_parameters_t * const params)
{
    if (params->server_name.len) {
        // TODO verify the SNI instead of accepting whatever the client sent
        warn(INF, "\tSNI = %.*s", (int)params->server_name.len,
             params->server_name.base);
        ensure(ptls_set_server_name(tls, (const char *)params->server_name.base,
                                    params->server_name.len) == 0,
               "ptls_set_server_name");
    } else
        warn(INF, "\tSNI = ");

    if (params->negotiated_protocols.count == 0) {
        warn(WRN, "\tALPN = ");
        return 0;
    }

    size_t j;
    for (j = 0; j < alpn_cnt; j++)
        for (size_t i = 0; i < params->negotiated_protocols.count; i++)
            if (params->negotiated_protocols.list[i].len == alpn[j].len &&
                memcmp(params->negotiated_protocols.list[i].base, alpn[j].base,
                       alpn[j].len) == 0)
                goto done;

    if (j == alpn_cnt) {
        warn(WRN, RED "\tALPN = %.*s (and maybe others, none supported)" NRM,
             (int)params->negotiated_protocols.list[0].len,
             params->negotiated_protocols.list[0].base);
        return PTLS_ALERT_NO_APPLICATION_PROTOCOL;
    }

done:
    // mark this ALPN as negotiated
    ptls_set_negotiated_protocol(tls, (char *)alpn[j].base, alpn[j].len);
    warn(INF, "\tALPN = %.*s", (int)alpn[j].len, alpn[j].base);

    return 0;
}


static int filter_tp(ptls_t * tls __attribute__((unused)),
                     struct st_ptls_handshake_properties_t * properties
                     __attribute__((unused)),
                     uint16_t type)
{
    return type == QUIC_TP;
}


static bool __attribute__((nonnull))
dectp(uint_t * const val, const uint8_t ** pos, const uint8_t * const end)
{
    uint64_t len;
    if (decv(&len, pos, end) == false)
        return false;
    if (len) {
        uint64_t v = 0;
        decv(&v, pos, end);
        *val = (uint_t)v;
    }
    return true;
}


#define dec_chk(op, ...)                                                       \
    do {                                                                       \
        if (unlikely(dec##op(__VA_ARGS__) == false))                           \
            err_close_return(c, ERR_TP, FRM_CRY, "tp dec" #op " err");         \
    } while (0)


static bool __attribute__((nonnull))
deccid(struct cid * const id, const uint8_t ** pos, const uint8_t * const end)
{
    uint64_t len = 0;
    if (decv(&len, pos, end) == false)
        return false;
    id->len = (uint8_t)len;
    if (id->len)
        decb(id->id, pos, end, id->len);
    return true;
}


#ifndef NDEBUG
static bool __attribute__((const)) is_grease_tp(const uint64_t tp)
{
    if (tp < 27)
        return false;
    const uint64_t n = (tp - 27) / 31;
    return n * 31 + 27 == tp;
}
#endif


static int chk_tp(ptls_t * tls __attribute__((unused)),
                  ptls_handshake_properties_t * properties,
                  ptls_raw_extension_t * slots)
{
    // get connection based on properties pointer
    struct q_conn * const c =
        (void *)((char *)properties - offsetof(struct tls, tls_hshk_prop) -
                 offsetof(struct q_conn, tls));

    if (unlikely(slots[0].type != QUIC_TP))
        err_close_return(c, ERR_TP, FRM_CRY, "slots[0].type = 0x%04x",
                         slots[0].type);

    if (unlikely(slots[1].type != UINT16_MAX))
        err_close_return(c, ERR_TP, FRM_CRY, "slots[1].type = 0x%04x",
                         slots[1].type);

    // set up parsing
    const uint8_t * pos = (const uint8_t *)slots[0].data.base;
    const uint8_t * const end = pos + slots[0].data.len;

    // keep track of which transport parameters we've seen before
    bitset_define(tp_list, TP_MAX);
    struct tp_list tp_list = bitset_t_initializer(0);

    struct cid orig_dcid = {.len = UINT8_MAX};
    struct cid ini_scid = {.len = UINT8_MAX};
    struct cid rtry_scid = {.len = UINT8_MAX};
    c->tp_peer.act_cid_lim = UINT_T_MAX;
    c->tp_peer.max_ups = MAX_UPS;
    while (pos < end) {
        uint64_t tp;
        dec_chk(v, &tp, &pos, end);

        // deal with unknown TPs and TPs > TP_MAX
        if (tp >= TP_MAX) {
            uint64_t unknown_len;
            dec_chk(v, &unknown_len, &pos, end);

            switch (tp) {
            case TP_QBG:
                warn(INF, "\t" BLD YEL "grease_quic_bit" NRM " = true");
                c->tp_peer.grease_quic_bit = true;
                break;

            case TP_QR:
                warn(INF,
                     "\t" BLD YEL "quantum_ready" NRM " w/len %" PRIu ") = %s",
                     (uint_t)unknown_len,
                     hex2str(pos, unknown_len, (char[16]){""}, 16));
                break;

            default:
                warn(INF,
                     "\t" BLD "%s" NRM " (0x%" PRIx " w/len %" PRIu ") = %s",
                     is_grease_tp(tp) ? YEL "grease" : RED "unknown",
                     (uint_t)tp, (uint_t)unknown_len,
                     hex2str(pos, unknown_len, (char[16]){""}, 16));
                break;
            }

            pos += unknown_len;
            continue;
        }

        // check if this transport parameter is a duplicate
        if (tp < TP_MAX) {
            if (bit_isset(TP_MAX, tp, &tp_list))
                err_close_return(c, ERR_TP, FRM_CRY, "duplicate tp 0x%04lx",
                                 tp);
            bit_set(TP_MAX, tp, &tp_list);
        }

        switch (tp) {
        case TP_IMSD_U:
            dec_chk(tp, &c->tp_peer.max_strm_data_uni, &pos, end);
            warn(INF, "\tinitial_max_stream_data_uni = %" PRIu " [bytes]",
                 c->tp_peer.max_strm_data_uni);
            break;

        case TP_IMSD_BL:
            dec_chk(tp, &c->tp_peer.max_strm_data_bidi_remote, &pos, end);
            warn(INF,
                 "\tinitial_max_stream_data_bidi_local = %" PRIu " [bytes]",
                 c->tp_peer.max_strm_data_bidi_remote);
            break;

        case TP_IMSD_BR:
            // this is RX'ed as _remote, but applies to streams we open, so:
            dec_chk(tp, &c->tp_peer.max_strm_data_bidi_local, &pos, end);
            warn(INF,
                 "\tinitial_max_stream_data_bidi_remote = %" PRIu " [bytes]",
                 c->tp_peer.max_strm_data_bidi_local);
            break;

        case TP_IMD:
            dec_chk(tp, &c->tp_peer.max_data, &pos, end);
            warn(INF, "\tinitial_max_data = %" PRIu " [bytes]",
                 c->tp_peer.max_data);
            break;

        case TP_IMSB:
            dec_chk(tp, &c->tp_peer.max_strms_bidi, &pos, end);
            warn(INF, "\tinitial_max_streams_bidi = %" PRIu,
                 c->tp_peer.max_strms_bidi);
            break;

        case TP_IMSU:
            dec_chk(tp, &c->tp_peer.max_strms_uni, &pos, end);
            warn(INF, "\tinitial_max_streams_uni = %" PRIu,
                 c->tp_peer.max_strms_uni);
            break;

        case TP_IDTO:
            dec_chk(tp, &c->tp_peer.max_idle_to, &pos, end);
            warn(INF, "\tmax_idle_timeout = %" PRIu " [ms]",
                 c->tp_peer.max_idle_to);
            break;

        case TP_MUPS:
            dec_chk(tp, &c->tp_peer.max_ups, &pos, end);
            warn(INF, "\tmax_udp_payload_size = %" PRIu " [bytes]",
                 c->tp_peer.max_ups);
            if (c->tp_peer.max_ups < MIN_INI_LEN)
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "tp_peer.max_ups %" PRIu " invalid (< %u)",
                                 c->tp_peer.max_ups, MIN_INI_LEN);
            break;

        case TP_ADE:
            dec_chk(tp, &c->tp_peer.ack_del_exp, &pos, end);
            warn(INF, "\tack_delay_exponent = %" PRIu, c->tp_peer.ack_del_exp);
            if (c->tp_peer.ack_del_exp > 20)
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "ack_delay_exponent %" PRIu " invalid",
                                 c->tp_peer.ack_del_exp);
            break;

        case TP_MAD:
            dec_chk(tp, &c->tp_peer.max_ack_del, &pos, end);
            warn(INF, "\tmax_ack_delay = %" PRIu " [ms]",
                 c->tp_peer.max_ack_del);
            if (c->tp_peer.max_ack_del > (1 << 14))
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "max_ack_delay %" PRIu " invalid",
                                 c->tp_peer.max_ack_del);
            break;

        case TP_DCID_O:
            if (is_clnt(c) == false)
                err_close_return(
                    c, ERR_TP, FRM_CRY,
                    "serv got original_destination_connection_id tp");
            dec_chk(cid, &orig_dcid, &pos, end);
            warn(INF, "\toriginal_destination_connection_id = %s",
                 cid_str(&orig_dcid));
            break;

        case TP_DMIG:;
            uint_t dmig;
            dec_chk(tp, &dmig, &pos, end);
            warn(INF, "\tdisable_active_migration = true");
            c->tp_peer.disable_active_migration = true;
            break;

        case TP_SRT:
            if (is_clnt(c) == false)
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "rx stateless_reset_token tp at serv");
            uint64_t l;
            dec_chk(v, &l, &pos, end);
#ifndef NO_SRT_MATCHING
            uint8_t * srt = c->dcid->srt;
#else
            uint8_t srt[SRT_LEN];
#endif
            if (l != SRT_LEN)
                err_close_return(c, ERR_TP, FRM_CRY, "illegal srt len %" PRIu,
                                 (uint_t)l);
            decb(srt, &pos, end, SRT_LEN);
            warn(INF, "\tstateless_reset_token = %s", srt_str(srt));
#ifndef NO_SRT_MATCHING
            c->dcid->has_srt = true;
            if (unlikely(conns_by_srt_ins(c, c->dcid->srt) == false))
                err_close_return(c, ERR_TP, FRM_CRY, "cannot ins srt %s",
                                 srt_str(c->dcid->srt));
#endif
            break;

        case TP_PRFA:
            dec_chk(v, &l, &pos, end);
            const uint8_t * const e = pos + l;

            struct pref_addr * const pa = &c->tp_peer.pref_addr;
            struct w_sockaddr * const pa4 = &pa->addr4;
            struct w_sockaddr * const pa6 = &pa->addr6;

            // use decb, since these need to be in network byte-order
            pa4->addr.af = AF_INET;
            decb((uint8_t *)&pa4->addr.ip4, &pos, e, sizeof(pa4->addr.ip4));
            decb((uint8_t *)&pa4->port, &pos, e, sizeof(pa4->port));

            pa6->addr.af = AF_INET6;
            decb((uint8_t *)&pa6->addr.ip6, &pos, e, sizeof(pa6->addr.ip6));
            decb((uint8_t *)&pa6->port, &pos, e, sizeof(pa6->port));

            if (unlikely(dec1(&pa->cid.len, &pos, e) == false))
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "cannot decode tp 0x%" PRIx, (uint_t)tp);
            decb(pa->cid.id, &pos, e, pa->cid.len);
            pa->cid.seq = 1;

#ifndef NO_SRT_MATCHING
            srt = pa->cid.srt;
            pa->cid.has_srt = true;
#endif
            decb(srt, &pos, e, SRT_LEN);
            if (c->tp_mine.disable_active_migration == false) {
#ifndef NO_SRT_MATCHING
                struct cid * const dcid =
#endif
                    cid_ins(&c->dcids, &pa->cid);
#ifndef NO_SRT_MATCHING
                if (unlikely(conns_by_srt_ins(c, dcid->srt) == false))
                    err_close_return(c, ERR_TP, FRM_CRY, "cannot ins srt %s",
                                     srt_str(dcid->srt));
#endif
            }

            warn(INF,
                 "\tpreferred_address = IPv4=%s:%u IPv6=[%s]:%u cid=%s srt=%s",
                 w_ntop(&pa4->addr, ip_tmp), bswap16(pa4->port),
                 w_ntop(&pa6->addr, ip_tmp), bswap16(pa6->port),
                 cid_str(&pa->cid), srt_str(srt));
            break;

        case TP_ACIL:
            dec_chk(tp, &c->tp_peer.act_cid_lim, &pos, end);
            warn(INF, "\tactive_connection_id_limit = %" PRIu,
                 c->tp_peer.act_cid_lim);
            break;

        case TP_SCID_I:
            dec_chk(cid, &ini_scid, &pos, end);
            warn(INF, "\tinitial_source_connection_id = %s",
                 cid_str(&ini_scid));
            break;

        case TP_SCID_R:
            dec_chk(cid, &rtry_scid, &pos, end);
            warn(INF, "\tretry_source_connection_id = %s", cid_str(&rtry_scid));
            break;

        default:
            err_close_return(c, ERR_TP, FRM_CRY, "unsupported tp 0x%" PRIx,
                             (uint_t)tp);
        }
    }

    // authenticate CIDs
    if (ini_scid.len == UINT8_MAX)
        err_close_return(c, ERR_TP, FRM_CRY,
                         "no initial_source_connection_id tp");

    if (is_clnt(c) && orig_dcid.len == UINT8_MAX)
        err_close_return(c, ERR_TP, FRM_CRY,
                         "no original_destination_connection_id tp");

    if (cid_cmp(&ini_scid, c->dcid)) {
        mk_cid_str(ERR, &ini_scid, ini_scid_str);
        mk_cid_str(ERR, c->dcid, dcid_str);
        warn(ERR, "initial_source_connection_id mismatch, %s != %s",
             ini_scid_str, dcid_str);
        err_close_return(c, ERR_TP, FRM_CRY,
                         "initial_source_connection_id mismatch");
    }
<commit_msg>Send more TRANSPORT_PARAMETER_ERRORs. Thanks h3spec!<commit_after>// SPDX-License-Identifier: BSD-2-Clause
//
// Copyright (c) 2016-2020, NetApp, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#ifndef NO_TLS_LOG
#include <stdarg.h>
#include <stdio.h>
#endif

#ifndef NO_SERVER
#include <sys/param.h>
#endif

#ifdef WITH_OPENSSL
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/ossl_typ.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <picotls/fusion.h>
#include <picotls/openssl.h>

#define aes128gcmsha256 ptls_openssl_aes128gcmsha256
#define secp256r1 ptls_openssl_secp256r1
#ifndef MINIMAL_CIPHERS
#define x25519 ptls_openssl_x25519
#endif
#else
#include <picotls/minicrypto.h>

#ifndef MINIMAL_CIPHERS
#define cipher_suite ptls_minicrypto_cipher_suites
#define x25519 ptls_minicrypto_x25519
#endif
#define aes128gcmsha256 ptls_minicrypto_aes128gcmsha256
#define secp256r1 ptls_minicrypto_secp256r1
#endif

#include <quant/quant.h>


#if defined(PARTICLE) || defined(RIOT_VERSION)
#if defined(PARTICLE)
#define MAXPATHLEN 8
#endif

#include <uECC.h>

void ptls_minicrypto_random_bytes(void * buf, size_t len)
{
    while (len >= sizeof(uint32_t)) {
        *((uint32_t *)buf) = w_rand32();
        buf += sizeof(uint32_t);
        len -= sizeof(uint32_t);
    }
    while (len > 0) {
        *((uint8_t *)buf) = w_rand32();
        buf += sizeof(uint8_t);
        len -= sizeof(uint8_t);
    }
}


static int uecc_rng(uint8_t * dest, unsigned size)
{
    ptls_minicrypto_random_bytes(dest, size);
    return 1;
}
#else
#include <errno.h>
#endif


#include "bitset.h"
#include "cid.h"
#include "conn.h"
#include "frame.h"
#include "marshall.h"
#include "pkt.h"
#include "pn.h"
#include "quic.h"
#include "recovery.h"
#include "stream.h"
#include "tls.h"
#include "tree.h"

#ifndef NO_SERVER
#include "diet.h"
#endif


#ifndef NO_ERR_REASONS
static const char * const ptls_err_str[] = {
    [0] = "CLOSE_NOTIFY",
    [10] = "UNEXPECTED_MESSAGE",
    [20] = "BAD_RECORD_MAC",
    [40] = "HANDSHAKE_FAILURE",
    [42] = "BAD_CERTIFICATE",
    [44] = "CERTIFICATE_REVOKED",
    [45] = "CERTIFICATE_EXPIRED",
    [46] = "CERTIFICATE_UNKNOWN",
    [47] = "ILLEGAL_PARAMETER",
    [48] = "UNKNOWN_CA",
    [50] = "DECODE_ERROR",
    [51] = "DECRYPT_ERROR",
    [70] = "PROTOCOL_VERSION",
    [80] = "INTERNAL_ERROR",
    [90] = "USER_CANCELED",
    [109] = "MISSING_EXTENSION",
    [112] = "UNRECOGNIZED_NAME",
    [116] = "CERTIFICATE_REQUIRED",
    [120] = "NO_APPLICATION_PROTOCOL",
};
#endif


struct tls_ticket {
#if !defined(PARTICLE) && !defined(RIOT_VERSION)
    splay_entry(tls_ticket) node;
#endif
    char * sni;
    char * alpn;
    uint8_t * ticket;
    size_t ticket_len;
    struct transport_params tp;
    uint32_t vers;
    uint8_t _unused[12];
};


struct tickets_by_peer {
#if !defined(PARTICLE) && !defined(RIOT_VERSION)
    splay_head(, tls_ticket);
#else
    struct tls_ticket last_ticket;
#endif
};

static struct tickets_by_peer tickets;


#if !defined(PARTICLE) && !defined(RIOT_VERSION)
static int __attribute__((nonnull))
tls_ticket_cmp(const struct tls_ticket * const a,
               const struct tls_ticket * const b)
{
    int diff = strcmp(a->sni, b->sni);
    if (diff)
        return diff;
    if (*a->alpn == 0 || *b->alpn == 0)
        return 0;
    diff = strcmp(a->alpn, b->alpn);
    return diff;
}


SPLAY_PROTOTYPE(tickets_by_peer, tls_ticket, node, tls_ticket_cmp)
SPLAY_GENERATE(tickets_by_peer, tls_ticket, node, tls_ticket_cmp)
#endif


#define ALPN_LEN 16

// first entry is client default, if not otherwise specified
static const ptls_iovec_t alpn[] = {
    {(uint8_t[ALPN_LEN]){"hq-" DRAFT_VERSION_STRING}, 5},
    {(uint8_t[ALPN_LEN]){"hq-interop"}, 10}};
static const size_t alpn_cnt = sizeof(alpn) / sizeof(alpn[0]);


#define QUIC_TP 0x39

#define TP_DCID_O 0x00 ///< original_destination_connection_id
#define TP_IDTO 0x01 ///< idle_timeout
#define TP_SRT 0x02 ///< stateless_reset_token
#define TP_MUPS 0x03 ///< max_udp_payload_size
#define TP_IMD 0x04 ///< initial_max_data
#define TP_IMSD_BL 0x05 ///< initial_max_stream_data_bidi_local
#define TP_IMSD_BR 0x06 ///< initial_max_stream_data_bidi_remote
#define TP_IMSD_U 0x07 ///< initial_max_stream_data_uni
#define TP_IMSB 0x08 ///< initial_max_streams_bidi
#define TP_IMSU 0x09 ///< initial_max_streams_uni
#define TP_ADE 0x0a ///< ack_delay_exponent
#define TP_MAD 0x0b ///< max_ack_delay
#define TP_DMIG 0x0c ///< disable_active_migration
#define TP_PRFA 0x0d ///< preferred_address
#define TP_ACIL 0x0e ///< active_connection_id_limit
#define TP_SCID_I 0x0f ///< initial_source_connection_id
#define TP_SCID_R 0x10 ///< retry_source_connection_id
#define TP_MAX (TP_SCID_R + 1)

#define TP_QBG 0x2ab2 // grease_quic_bit
#define TP_QR 3127


#define err_close_return(...)                                                  \
    do {                                                                       \
        err_close(__VA_ARGS__);                                                \
        return 1;                                                              \
    } while (0)


// quicly shim
#define AEAD_BASE_LABEL PTLS_HKDF_EXPAND_LABEL_PREFIX "quic "
#define st_quicly_cipher_context_t cipher_ctx


// from quicly
void dispose_cipher(struct st_quicly_cipher_context_t * ctx)
{
    if (ctx->aead) {
        ptls_aead_free(ctx->aead);
        ctx->aead = 0;
    }
    if (ctx->header_protection) {
        ptls_cipher_free(ctx->header_protection);
        ctx->header_protection = 0;
    }
}


// from quicly (with mods for key update)
static int setup_cipher(ptls_cipher_context_t ** hp_ctx,
                        ptls_aead_context_t ** aead_ctx,
                        ptls_aead_algorithm_t * aead,
                        ptls_hash_algorithm_t * hash,
                        int is_enc,
                        const void * secret)
{
    uint8_t hpkey[PTLS_MAX_SECRET_SIZE] = {0};
    int ret;

    if (hp_ctx) {
        if ((ret = ptls_hkdf_expand_label(
                 hash, hpkey, aead->ctr_cipher->key_size,
                 ptls_iovec_init(secret, hash->digest_size), "quic hp",
                 ptls_iovec_init(NULL, 0), NULL)) != 0)
            goto Exit;
        if ((*hp_ctx = ptls_cipher_new(aead->ctr_cipher, is_enc, hpkey)) ==
            NULL) {
            ret = PTLS_ERROR_NO_MEMORY;
            goto Exit;
        }
    }
    ensure(aead_ctx, "aead_ctx is zero");
    if ((*aead_ctx = ptls_aead_new(aead, hash, is_enc, secret,
                                   AEAD_BASE_LABEL)) == NULL) {
        ret = PTLS_ERROR_NO_MEMORY;
        goto Exit;
    }

#ifdef DEBUG_PROT
    warn(NTE, "aead-secret: %s, hp-key: %s",
         hex2str(secret, hash->digest_size,
                 (char[hex_str_len(PTLS_MAX_DIGEST_SIZE)]){""},
                 hex_str_len(PTLS_MAX_DIGEST_SIZE)),
         hex2str(hpkey, aead->ctr_cipher->key_size,
                 (char[hex_str_len(PTLS_MAX_DIGEST_SIZE)]){""},
                 hex_str_len(PTLS_MAX_DIGEST_SIZE)));
#endif

    ret = 0;
Exit:
    if (ret != 0) {
        if (*aead_ctx != NULL) {
            ptls_aead_free(*aead_ctx);
            *aead_ctx = NULL;
        }
        if (hp_ctx && *hp_ctx != NULL) {
            ptls_cipher_free(*hp_ctx);
            *hp_ctx = NULL;
        }
    }
    ptls_clear_memory(hpkey, sizeof(hpkey));
    return ret;
}


// from quicly (with mods for key update)
static int setup_initial_key(struct st_quicly_cipher_context_t * ctx,
                             ptls_cipher_suite_t * cs,
                             const void * master_secret,
                             const char * label,
                             int is_enc,
                             void * new_secret)
{
    uint8_t _aead_secret[PTLS_MAX_DIGEST_SIZE];
    uint8_t * const aead_secret = new_secret ? new_secret : _aead_secret;

    int ret;

    if ((ret = ptls_hkdf_expand_label(
             cs->hash, aead_secret, cs->hash->digest_size,
             ptls_iovec_init(master_secret, cs->hash->digest_size), label,
             ptls_iovec_init(NULL, 0), NULL)) != 0)
        goto Exit;
    if ((ret =
             setup_cipher(new_secret ? 0 : &ctx->header_protection, &ctx->aead,
                          cs->aead, cs->hash, is_enc, aead_secret)) != 0)
        goto Exit;

Exit:
    ptls_clear_memory(_aead_secret, sizeof(_aead_secret));
    return ret;
}


// from quicly (with mods)
static int setup_initial_encryption(struct st_quicly_cipher_context_t * ingress,
                                    struct st_quicly_cipher_context_t * egress,
                                    ptls_iovec_t cid,
                                    int is_client)
{
    static const uint8_t salt[] = {0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34,
                                   0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
                                   0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a};
    static const char * labels[2] = {"client in", "server in"};
    ptls_cipher_suite_t * const cs = &aes128gcmsha256;
    uint8_t secret[PTLS_MAX_DIGEST_SIZE];
    int ret;

    /* extract master secret */
    if ((ret = ptls_hkdf_extract(
             cs->hash, secret, ptls_iovec_init(salt, sizeof(salt)), cid)) != 0)
        goto Exit;

    /* create aead contexts */
    if ((ret = setup_initial_key(ingress, cs, secret, labels[is_client], 0,
                                 0)) != 0)
        goto Exit;
    if ((ret = setup_initial_key(egress, cs, secret, labels[!is_client], 1,
                                 0)) != 0)
        goto Exit;

Exit:
    ptls_clear_memory(secret, sizeof(secret));
    return ret;
}


static int __attribute__((nonnull))
on_ch(ptls_on_client_hello_t * const self __attribute__((unused)),
      ptls_t * const tls,
      ptls_on_client_hello_parameters_t * const params)
{
    if (params->server_name.len) {
        // TODO verify the SNI instead of accepting whatever the client sent
        warn(INF, "\tSNI = %.*s", (int)params->server_name.len,
             params->server_name.base);
        ensure(ptls_set_server_name(tls, (const char *)params->server_name.base,
                                    params->server_name.len) == 0,
               "ptls_set_server_name");
    } else
        warn(INF, "\tSNI = ");

    if (params->negotiated_protocols.count == 0) {
        warn(WRN, "\tALPN = ");
        return 0;
    }

    size_t j;
    for (j = 0; j < alpn_cnt; j++)
        for (size_t i = 0; i < params->negotiated_protocols.count; i++)
            if (params->negotiated_protocols.list[i].len == alpn[j].len &&
                memcmp(params->negotiated_protocols.list[i].base, alpn[j].base,
                       alpn[j].len) == 0)
                goto done;

    if (j == alpn_cnt) {
        warn(WRN, RED "\tALPN = %.*s (and maybe others, none supported)" NRM,
             (int)params->negotiated_protocols.list[0].len,
             params->negotiated_protocols.list[0].base);
        return PTLS_ALERT_NO_APPLICATION_PROTOCOL;
    }

done:
    // mark this ALPN as negotiated
    ptls_set_negotiated_protocol(tls, (char *)alpn[j].base, alpn[j].len);
    warn(INF, "\tALPN = %.*s", (int)alpn[j].len, alpn[j].base);

    return 0;
}


static int filter_tp(ptls_t * tls __attribute__((unused)),
                     struct st_ptls_handshake_properties_t * properties
                     __attribute__((unused)),
                     uint16_t type)
{
    return type == QUIC_TP;
}


static bool __attribute__((nonnull))
dectp(uint_t * const val, const uint8_t ** pos, const uint8_t * const end)
{
    uint64_t len;
    if (decv(&len, pos, end) == false)
        return false;
    if (len) {
        uint64_t v = 0;
        decv(&v, pos, end);
        *val = (uint_t)v;
    }
    return true;
}


#define dec_chk(op, ...)                                                       \
    do {                                                                       \
        if (unlikely(dec##op(__VA_ARGS__) == false))                           \
            err_close_return(c, ERR_TP, FRM_CRY, "tp dec" #op " err");         \
    } while (0)


static bool __attribute__((nonnull))
deccid(struct cid * const id, const uint8_t ** pos, const uint8_t * const end)
{
    uint64_t len = 0;
    if (decv(&len, pos, end) == false)
        return false;
    id->len = (uint8_t)len;
    if (id->len)
        decb(id->id, pos, end, id->len);
    return true;
}


#ifndef NDEBUG
static bool __attribute__((const)) is_grease_tp(const uint64_t tp)
{
    if (tp < 27)
        return false;
    const uint64_t n = (tp - 27) / 31;
    return n * 31 + 27 == tp;
}
#endif


static int chk_tp(ptls_t * tls __attribute__((unused)),
                  ptls_handshake_properties_t * properties,
                  ptls_raw_extension_t * slots)
{
    // get connection based on properties pointer
    struct q_conn * const c =
        (void *)((char *)properties - offsetof(struct tls, tls_hshk_prop) -
                 offsetof(struct q_conn, tls));

    if (unlikely(slots[0].type != QUIC_TP))
        err_close_return(c, ERR_TP, FRM_CRY, "slots[0].type = 0x%04x",
                         slots[0].type);

    if (unlikely(slots[1].type != UINT16_MAX))
        err_close_return(c, ERR_TP, FRM_CRY, "slots[1].type = 0x%04x",
                         slots[1].type);

    // set up parsing
    const uint8_t * pos = (const uint8_t *)slots[0].data.base;
    const uint8_t * const end = pos + slots[0].data.len;

    // keep track of which transport parameters we've seen before
    bitset_define(tp_list, TP_MAX);
    struct tp_list tp_list = bitset_t_initializer(0);

    struct cid orig_dcid = {.len = UINT8_MAX};
    struct cid ini_scid = {.len = UINT8_MAX};
    struct cid rtry_scid = {.len = UINT8_MAX};
    c->tp_peer.act_cid_lim = UINT_T_MAX;
    c->tp_peer.max_ups = MAX_UPS;
    while (pos < end) {
        uint64_t tp;
        dec_chk(v, &tp, &pos, end);

        // deal with unknown TPs and TPs > TP_MAX
        if (tp >= TP_MAX) {
            uint64_t unknown_len;
            dec_chk(v, &unknown_len, &pos, end);

            switch (tp) {
            case TP_QBG:
                warn(INF, "\t" BLD YEL "grease_quic_bit" NRM " = true");
                c->tp_peer.grease_quic_bit = true;
                break;

            case TP_QR:
                warn(INF,
                     "\t" BLD YEL "quantum_ready" NRM " w/len %" PRIu ") = %s",
                     (uint_t)unknown_len,
                     hex2str(pos, unknown_len, (char[16]){""}, 16));
                break;

            default:
                warn(INF,
                     "\t" BLD "%s" NRM " (0x%" PRIx " w/len %" PRIu ") = %s",
                     is_grease_tp(tp) ? YEL "grease" : RED "unknown",
                     (uint_t)tp, (uint_t)unknown_len,
                     hex2str(pos, unknown_len, (char[16]){""}, 16));
                break;
            }

            pos += unknown_len;
            continue;
        }

        // check if this transport parameter is a duplicate
        if (tp < TP_MAX) {
            if (bit_isset(TP_MAX, tp, &tp_list))
                err_close_return(c, ERR_TP, FRM_CRY, "duplicate tp 0x%04lx",
                                 tp);
            bit_set(TP_MAX, tp, &tp_list);
        }

        switch (tp) {
        case TP_IMSD_U:
            dec_chk(tp, &c->tp_peer.max_strm_data_uni, &pos, end);
            warn(INF, "\tinitial_max_stream_data_uni = %" PRIu " [bytes]",
                 c->tp_peer.max_strm_data_uni);
            break;

        case TP_IMSD_BL:
            dec_chk(tp, &c->tp_peer.max_strm_data_bidi_remote, &pos, end);
            warn(INF,
                 "\tinitial_max_stream_data_bidi_local = %" PRIu " [bytes]",
                 c->tp_peer.max_strm_data_bidi_remote);
            break;

        case TP_IMSD_BR:
            // this is RX'ed as _remote, but applies to streams we open, so:
            dec_chk(tp, &c->tp_peer.max_strm_data_bidi_local, &pos, end);
            warn(INF,
                 "\tinitial_max_stream_data_bidi_remote = %" PRIu " [bytes]",
                 c->tp_peer.max_strm_data_bidi_local);
            break;

        case TP_IMD:
            dec_chk(tp, &c->tp_peer.max_data, &pos, end);
            warn(INF, "\tinitial_max_data = %" PRIu " [bytes]",
                 c->tp_peer.max_data);
            break;

        case TP_IMSB:
            dec_chk(tp, &c->tp_peer.max_strms_bidi, &pos, end);
            warn(INF, "\tinitial_max_streams_bidi = %" PRIu,
                 c->tp_peer.max_strms_bidi);
            break;

        case TP_IMSU:
            dec_chk(tp, &c->tp_peer.max_strms_uni, &pos, end);
            warn(INF, "\tinitial_max_streams_uni = %" PRIu,
                 c->tp_peer.max_strms_uni);
            break;

        case TP_IDTO:
            dec_chk(tp, &c->tp_peer.max_idle_to, &pos, end);
            warn(INF, "\tmax_idle_timeout = %" PRIu " [ms]",
                 c->tp_peer.max_idle_to);
            break;

        case TP_MUPS:
            dec_chk(tp, &c->tp_peer.max_ups, &pos, end);
            warn(INF, "\tmax_udp_payload_size = %" PRIu " [bytes]",
                 c->tp_peer.max_ups);
            if (c->tp_peer.max_ups < MIN_INI_LEN)
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "tp_peer.max_ups %" PRIu " invalid (< %u)",
                                 c->tp_peer.max_ups, MIN_INI_LEN);
            break;

        case TP_ADE:
            dec_chk(tp, &c->tp_peer.ack_del_exp, &pos, end);
            warn(INF, "\tack_delay_exponent = %" PRIu, c->tp_peer.ack_del_exp);
            if (c->tp_peer.ack_del_exp > 20)
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "ack_delay_exponent %" PRIu " invalid",
                                 c->tp_peer.ack_del_exp);
            break;

        case TP_MAD:
            dec_chk(tp, &c->tp_peer.max_ack_del, &pos, end);
            warn(INF, "\tmax_ack_delay = %" PRIu " [ms]",
                 c->tp_peer.max_ack_del);
            if (c->tp_peer.max_ack_del > (1 << 14))
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "max_ack_delay %" PRIu " invalid",
                                 c->tp_peer.max_ack_del);
            break;

        case TP_DCID_O:
            if (is_clnt(c) == false)
                err_close_return(
                    c, ERR_TP, FRM_CRY,
                    "serv rx'ed original_destination_connection_id");
            dec_chk(cid, &orig_dcid, &pos, end);
            warn(INF, "\toriginal_destination_connection_id = %s",
                 cid_str(&orig_dcid));
            break;

        case TP_DMIG:;
            uint_t dmig;
            dec_chk(tp, &dmig, &pos, end);
            warn(INF, "\tdisable_active_migration = true");
            c->tp_peer.disable_active_migration = true;
            break;

        case TP_SRT:
            if (is_clnt(c) == false)
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "serv rx'ed stateless_reset_token");
            uint64_t l;
            dec_chk(v, &l, &pos, end);
#ifndef NO_SRT_MATCHING
            uint8_t * srt = c->dcid->srt;
#else
            uint8_t srt[SRT_LEN];
#endif
            if (l != SRT_LEN)
                err_close_return(c, ERR_TP, FRM_CRY, "illegal srt len %" PRIu,
                                 (uint_t)l);
            decb(srt, &pos, end, SRT_LEN);
            warn(INF, "\tstateless_reset_token = %s", srt_str(srt));
#ifndef NO_SRT_MATCHING
            c->dcid->has_srt = true;
            if (unlikely(conns_by_srt_ins(c, c->dcid->srt) == false))
                err_close_return(c, ERR_TP, FRM_CRY, "cannot ins srt %s",
                                 srt_str(c->dcid->srt));
#endif
            break;

        case TP_PRFA:
            if (is_clnt(c) == false)
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "serv rx'ed preferred_address");
            dec_chk(v, &l, &pos, end);
            const uint8_t * const e = pos + l;

            struct pref_addr * const pa = &c->tp_peer.pref_addr;
            struct w_sockaddr * const pa4 = &pa->addr4;
            struct w_sockaddr * const pa6 = &pa->addr6;

            // use decb, since these need to be in network byte-order
            pa4->addr.af = AF_INET;
            decb((uint8_t *)&pa4->addr.ip4, &pos, e, sizeof(pa4->addr.ip4));
            decb((uint8_t *)&pa4->port, &pos, e, sizeof(pa4->port));

            pa6->addr.af = AF_INET6;
            decb((uint8_t *)&pa6->addr.ip6, &pos, e, sizeof(pa6->addr.ip6));
            decb((uint8_t *)&pa6->port, &pos, e, sizeof(pa6->port));

            if (unlikely(dec1(&pa->cid.len, &pos, e) == false))
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "cannot decode tp 0x%" PRIx, (uint_t)tp);
            decb(pa->cid.id, &pos, e, pa->cid.len);
            pa->cid.seq = 1;

#ifndef NO_SRT_MATCHING
            srt = pa->cid.srt;
            pa->cid.has_srt = true;
#endif
            decb(srt, &pos, e, SRT_LEN);
            if (c->tp_mine.disable_active_migration == false) {
#ifndef NO_SRT_MATCHING
                struct cid * const dcid =
#endif
                    cid_ins(&c->dcids, &pa->cid);
#ifndef NO_SRT_MATCHING
                if (unlikely(conns_by_srt_ins(c, dcid->srt) == false))
                    err_close_return(c, ERR_TP, FRM_CRY, "cannot ins srt %s",
                                     srt_str(dcid->srt));
#endif
            }

            warn(INF,
                 "\tpreferred_address = IPv4=%s:%u IPv6=[%s]:%u cid=%s srt=%s",
                 w_ntop(&pa4->addr, ip_tmp), bswap16(pa4->port),
                 w_ntop(&pa6->addr, ip_tmp), bswap16(pa6->port),
                 cid_str(&pa->cid), srt_str(srt));
            break;

        case TP_ACIL:
            dec_chk(tp, &c->tp_peer.act_cid_lim, &pos, end);
            warn(INF, "\tactive_connection_id_limit = %" PRIu,
                 c->tp_peer.act_cid_lim);
            break;

        case TP_SCID_I:
            dec_chk(cid, &ini_scid, &pos, end);
            warn(INF, "\tinitial_source_connection_id = %s",
                 cid_str(&ini_scid));
            break;

        case TP_SCID_R:
            if (is_clnt(c) == false)
                err_close_return(c, ERR_TP, FRM_CRY,
                                 "serv rx'ed retry_source_connection_id");
            dec_chk(cid, &rtry_scid, &pos, end);
            warn(INF, "\tretry_source_connection_id = %s", cid_str(&rtry_scid));
            break;

        default:
            err_close_return(c, ERR_TP, FRM_CRY, "unsupported tp 0x%" PRIx,
                             (uint_t)tp);
        }
    }

    // authenticate CIDs
    if (ini_scid.len == UINT8_MAX)
        err_close_return(c, ERR_TP, FRM_CRY,
                         "no initial_source_connection_id tp");

    if (is_clnt(c) && orig_dcid.len == UINT8_MAX)
        err_close_return(c, ERR_TP, FRM_CRY,
                         "no original_destination_connection_id tp");

    if (cid_cmp(&ini_scid, c->dcid)) {
        mk_cid_str(ERR, &ini_scid, ini_scid_str);
        mk_cid_str(ERR, c->dcid, dcid_str);
        warn(ERR, "initial_source_connection_id mismatch, %s != %s",
             ini_scid_str, dcid_str);
        err_close_return(c, ERR_TP, FRM_CRY,
                         "initial_source_connection_id mismatch");
    }

{'fraction_non_alphanumeric': 0.11984123769794662, 'fraction_numerical': 0.01670649224413754, 'mean_word_length': 2.0190744023965275, 'pattern_counts': {'":': 0, '<': 101, '<?xml version=': 0, '>': 291, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 74, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '52807', 'n_tokens_mistral': 18292, 'n_tokens_neox': 17238, 'n_words': 3657}