Packages
hackney
3.0.2
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
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
c_src/lsquic/src/liblsquic/lsquic_parse_ietf_v1.c
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_parse_ietf_v1.c -- Parsing functions specific to IETF QUIC v1
*/
#include <assert.h>
#include <inttypes.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <sys/queue.h>
#ifndef WIN32
#include <sys/types.h>
#else
#include <vc_compat.h>
#endif
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_sizes.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_ietf.h"
#include "lsquic_packet_in.h"
#include "lsquic_packet_out.h"
#include "lsquic_parse.h"
#include "lsquic_parse_common.h"
#include "lsquic_sfcw.h"
#include "lsquic_varint.h"
#include "lsquic_hq.h"
#include "lsquic_hash.h"
#include "lsquic_stream.h"
#include "lsquic_mm.h"
#include "lsquic_malo.h"
#include "lsquic_version.h"
#include "lsquic.h"
#include "lsquic_byteswap.h"
#include "lsquic_conn.h"
#include "lsquic_enc_sess.h"
#include "lsquic_trans_params.h"
#include "lsquic_parse_ietf.h"
#include "lsquic_qtags.h"
#define LSQUIC_LOGGER_MODULE LSQLM_PARSE
#include "lsquic_logger.h"
#define CHECK_SPACE(need, pstart, pend) \
do { if ((intptr_t) (need) > ((pend) - (pstart))) { return -1; } } while (0)
#define FRAME_TYPE_ACK_FREQUENCY 0xAF
#define FRAME_TYPE_TIMESTAMP 0x2F5
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static int
ietf_v1_gen_one_varint (unsigned char *, size_t, unsigned char, uint64_t);
static int
ietf_v1_gen_two_varints (unsigned char *, size_t, unsigned char, uint64_t[2]);
static void
ietf_v1_parse_packet_in_finish (lsquic_packet_in_t *packet_in,
struct packin_parse_state *state)
{
/* Packet number is set to an invalid value. The packet number must
* be decrypted, which happens later.
*/
packet_in->pi_packno = 1ULL << 62;
}
/* Note: token size is not accounted for */
static size_t
ietf_v1_packout_header_size_long_by_flags (const struct lsquic_conn *lconn,
enum header_type header_type, enum packet_out_flags flags,
size_t dcid_len)
{
size_t sz;
enum packno_bits packno_bits;
packno_bits = (flags >> POBIT_SHIFT) & 0x3;
sz = 1 /* Type */
+ 4 /* Version */
+ 1 /* DCIL */
+ dcid_len
+ 1 /* SCIL */
+ CN_SCID(lconn)->len
+ (header_type == HETY_INITIAL) /* Token length */
+ 2 /* Always use two bytes to encode payload length */
+ iquic_packno_bits2len(packno_bits)
;
return sz;
}
static size_t
ietf_v1_packout_header_size_long_by_packet (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out)
{
size_t sz;
unsigned token_len; /* Need intermediate value to quiet compiler warning */
enum packno_bits packno_bits;
packno_bits = lsquic_packet_out_packno_bits(packet_out);
sz = 1 /* Type */
+ 4 /* Version */
+ 1 /* DCIL */
+ packet_out->po_path->np_dcid.len
+ 1 /* SCIL */
+ CN_SCID(lconn)->len
+ (packet_out->po_header_type == HETY_INITIAL ?
(token_len = packet_out->po_token_len,
(1 << vint_val2bits(token_len)) + token_len) : 0)
+ 2 /* Always use two bytes to encode payload length */
+ iquic_packno_bits2len(packno_bits)
;
return sz;
}
static size_t
ietf_v1_packout_header_size_short (enum packet_out_flags flags, size_t dcid_len)
{
enum packno_bits bits;
size_t sz;
bits = (flags >> POBIT_SHIFT) & 0x3;
sz = 1 /* Type */
+ (flags & PO_CONN_ID ? dcid_len : 0)
+ iquic_packno_bits2len(bits)
;
return sz;
}
static size_t
ietf_v1_packout_max_header_size (const struct lsquic_conn *lconn,
enum packet_out_flags flags, size_t dcid_len, enum header_type header_type)
{
if ((lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
&& header_type == HETY_SHORT)
return ietf_v1_packout_header_size_short(flags, dcid_len);
else
return ietf_v1_packout_header_size_long_by_flags(lconn, header_type,
flags, dcid_len);
}
/* [draft-ietf-quic-transport-17] Section-17.2 */
static const unsigned char header_type_to_bin[] = {
[HETY_INITIAL] = 0x0,
[HETY_0RTT] = 0x1,
[HETY_HANDSHAKE] = 0x2,
[HETY_RETRY] = 0x3,
};
/* [draft-ietf-quic-v2] Section-3.2 */
static const unsigned char header_type_to_bin_v2[] = {
[HETY_INITIAL] = 0x1,
[HETY_0RTT] = 0x2,
[HETY_HANDSHAKE] = 0x3,
[HETY_RETRY] = 0x0,
};
static unsigned
write_packno (unsigned char *p, lsquic_packno_t packno,
enum packno_bits bits)
{
unsigned char *const begin = p;
switch (bits)
{
case IQUIC_PACKNO_LEN_4:
*p++ = packno >> 24;
/* fall through */
case IQUIC_PACKNO_LEN_3:
*p++ = packno >> 16;
/* fall through */
case IQUIC_PACKNO_LEN_2:
*p++ = packno >> 8;
/* fall through */
default:
*p++ = packno;
}
return p - begin;
}
static int
gen_long_pkt_header (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out, unsigned char *buf,
size_t bufsz, unsigned *packno_off_p, unsigned *packno_len_p)
{
unsigned payload_len, bits;
enum packno_bits packno_bits;
lsquic_ver_tag_t ver_tag;
unsigned char *p;
unsigned token_len;
size_t need;
need = ietf_v1_packout_header_size_long_by_packet(lconn, packet_out);
if (need > bufsz)
{
errno = EINVAL;
return -1;
}
packno_bits = lsquic_packet_out_packno_bits(packet_out);
p = buf;
if (lconn->cn_version == LSQVER_I002)
*p++ = 0xC0
| ( header_type_to_bin_v2[ packet_out->po_header_type ] << 4)
| packno_bits
;
else
*p++ = 0xC0
| ( header_type_to_bin[ packet_out->po_header_type ] << 4)
| packno_bits
;
ver_tag = lsquic_ver2tag(lconn->cn_version);
memcpy(p, &ver_tag, sizeof(ver_tag));
p += sizeof(ver_tag);
*p++ = packet_out->po_path->np_dcid.len;
memcpy(p, packet_out->po_path->np_dcid.idbuf, packet_out->po_path->np_dcid.len);
p += packet_out->po_path->np_dcid.len;
*p++ = CN_SCID(lconn)->len;
memcpy(p, CN_SCID(lconn)->idbuf, CN_SCID(lconn)->len);
p += CN_SCID(lconn)->len;
if (HETY_INITIAL == packet_out->po_header_type)
{
token_len = packet_out->po_token_len;
bits = vint_val2bits(token_len);
vint_write(p, token_len, bits, 1 << bits);
p += 1 << bits;
memcpy(p, packet_out->po_token, token_len);
p += token_len;
}
payload_len = packet_out->po_data_sz
+ lconn->cn_esf_c->esf_tag_len
+ iquic_packno_bits2len(packno_bits);
bits = 1; /* Always use two bytes to encode payload length */
vint_write(p, payload_len, bits, 1 << bits);
p += 1 << bits;
*packno_off_p = p - buf;
*packno_len_p = iquic_packno_bits2len(packno_bits);
p += write_packno(p, packet_out->po_packno, packno_bits);
return p - buf;
}
static int
gen_short_pkt_header (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out, unsigned char *buf,
size_t bufsz, unsigned *packno_off_p, unsigned *packno_len_p)
{
unsigned packno_len, cid_len, need;
enum packno_bits packno_bits;
packno_bits = lsquic_packet_out_packno_bits(packet_out);
packno_len = iquic_packno_bits2len(packno_bits);
cid_len = packet_out->po_flags & PO_CONN_ID ? packet_out->po_path->np_dcid.len : 0;
need = 1 + cid_len + packno_len;
if (need > bufsz)
return -1;
buf[0] = QUIC_BIT
| (lsquic_packet_out_spin_bit(packet_out) << 5)
| (lsquic_packet_out_square_bit(packet_out) << 4)
| (lsquic_packet_out_loss_bit(packet_out) << 3)
| (lsquic_packet_out_kp(packet_out) << 2)
| packno_bits
;
if (cid_len)
memcpy(buf + 1, packet_out->po_path->np_dcid.idbuf, cid_len);
(void) write_packno(buf + 1 + cid_len, packet_out->po_packno, packno_bits);
*packno_off_p = 1 + cid_len;
*packno_len_p = packno_len;
return need;
}
static int
ietf_v1_gen_reg_pkt_header (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out, unsigned char *buf,
size_t bufsz, unsigned *packno_off, unsigned *packno_len)
{
if (packet_out->po_header_type == HETY_SHORT)
return gen_short_pkt_header(lconn, packet_out, buf, bufsz, packno_off,
packno_len);
else
return gen_long_pkt_header(lconn, packet_out, buf, bufsz, packno_off,
packno_len);
}
static size_t
ietf_v1_packout_size (const struct lsquic_conn *lconn,
const struct lsquic_packet_out *packet_out)
{
size_t sz;
if ((lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
&& packet_out->po_header_type == HETY_SHORT)
sz = ietf_v1_packout_header_size_short(packet_out->po_flags,
packet_out->po_path->np_dcid.len);
else
sz = ietf_v1_packout_header_size_long_by_packet(lconn, packet_out);
sz += packet_out->po_data_sz;
sz += lconn->cn_esf_c->esf_tag_len;
return sz;
}
static int
ietf_v1_gen_stream_frame (unsigned char *buf, size_t buf_len,
lsquic_stream_id_t stream_id, uint64_t offset, int fin, size_t size,
gsf_read_f gsf_read, void *stream)
{
/* 0b00001XXX
* 0x4 OFF
* 0x2 LEN
* 0x1 FIN
*/
unsigned sbits, obits, dbits;
unsigned slen, olen, dlen;
unsigned char *p = buf + 1;
#if _MSC_VER
obits = 0, dbits = 0;
#endif
assert(!!fin || !!size);
/* We do not check that stream_id, offset, and size are smaller
* than 2^62: this is not necessary, as this code will never generate
* this many stream IDs, nor will it even transfer this much data.
* The size is limited by our own code.
*/
sbits = vint_val2bits(stream_id);
slen = 1 << sbits;
if (offset)
{
obits = vint_val2bits(offset);
olen = 1 << obits;
}
else
olen = 0;
if (!fin)
{
unsigned n_avail;
size_t nr;
n_avail = buf_len - (p + slen + olen - buf);
/* If we cannot fill remaining buffer, we need to include data
* length.
*/
if (size < n_avail)
{
dbits = vint_val2bits(size);
dlen = 1 << dbits;
n_avail -= dlen;
if (size > n_avail)
size = n_avail;
}
else
{
dlen = 0;
size = n_avail;
}
CHECK_STREAM_SPACE(1 + olen + slen + dlen +
+ 1 /* We need to write at least 1 byte */, buf, buf + buf_len);
vint_write(p, stream_id, sbits, slen);
p += slen;
if (olen)
vint_write(p, offset, obits, olen);
p += olen;
/* Read as much as we can */
nr = gsf_read(stream, p + dlen, size, &fin);
if (nr == 0)
return 0;
assert(nr <= size);
if (dlen)
vint_write(p, nr, dbits, dlen);
p += dlen + nr;
}
else
{
dlen = 1 + slen + olen < buf_len;
CHECK_STREAM_SPACE(1 + slen + olen + dlen, buf, buf + buf_len);
vint_write(p, stream_id, sbits, slen);
p += slen;
if (olen)
vint_write(p, offset, obits, olen);
p += olen;
if (dlen)
*p++ = 0;
}
buf[0] = 0x08
| (!!olen << 2)
| (!!dlen << 1)
| (!!fin << 0)
;
return p - buf;
}
int
lsquic_ietf_v1_gen_crypto_frame (unsigned char *buf, unsigned char first_byte,
size_t buf_len, lsquic_stream_id_t UNUSED_1, uint64_t offset,
int UNUSED_2, size_t size, gsf_read_f gsf_read, void *stream)
{
unsigned char *const end = buf + buf_len;
unsigned char *p;
unsigned obits, dbits;
unsigned olen, dlen;
size_t nr, n_avail;
int dummy_fin;
obits = vint_val2bits(offset);
olen = 1 << obits;
dbits = vint_val2bits(size);
dlen = 1 << dbits;
CHECK_SPACE(1 + olen + dlen
+ (dlen > 0) /* We need to write at least 1 byte */, buf, end);
n_avail = end - buf - 1 - olen - dlen;
if (n_avail < size)
size = n_avail;
p = buf;
*p++ = first_byte;
vint_write(p, offset, obits, olen);
p += olen;
nr = gsf_read(stream, p + dlen, size, &dummy_fin);
assert(nr != 0); /* This indicates error in the caller */
assert(nr <= size); /* This also indicates an error in the caller */
vint_write(p, nr, dbits, dlen);
p += dlen + nr;
return p - buf;
}
static int
ietf_v1_gen_crypto_frame (unsigned char *buf, size_t buf_len,
lsquic_stream_id_t stream_id, uint64_t offset, int fin,
size_t size, gsf_read_f gsf_read, void *stream)
{
return lsquic_ietf_v1_gen_crypto_frame(buf, 0x6, buf_len, stream_id,
offset, fin, size, gsf_read, stream);
}
static int
ietf_v1_dec_stream_frame_size (unsigned char *p, size_t new_size)
{
/* 0b00001XXX
* 0x4 OFF
* 0x2 LEN
* 0x1 FIN
*/
unsigned bits;
const char type = *p++;
if (!(type & 0x2))
return 1;
/* Stream ID */
bits = *p >> 6;
p += 1 << bits;
if (type & 0x4)
{
/* Offset */
bits = *p >> 6;
p += 1 << bits;
}
/* Write new size */
bits = *p >> 6;
vint_write(p, new_size, bits, 1 << bits);
return 0;
}
/* return parsed (used) buffer length */
static int
ietf_v1_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz,
struct stream_frame *stream_frame)
{
/* 0b00001XXX
* 0x4 OFF
* 0x2 LEN
* 0x1 FIN
*/
const unsigned char *const pend = buf + rem_packet_sz;
const unsigned char *p = buf;
lsquic_stream_id_t stream_id;
uint64_t offset, data_sz;
int r;
CHECK_SPACE(1, p, pend);
const char type = *p++;
r = vint_read(p, pend, &stream_id);
if (r < 0)
return -1;
p += r;
if (type & 0x4)
{
r = vint_read(p, pend, &offset);
if (r < 0)
return -1;
p += r;
}
else
offset = 0;
if (type & 0x2)
{
r = vint_read(p, pend, &data_sz);
if (r < 0)
return -1;
p += r;
CHECK_SPACE(data_sz, p, pend);
}
else
data_sz = pend - p;
/* Largest offset cannot exceed this value and we MUST detect this error */
if (VINT_MAX_VALUE - offset < data_sz)
return -1;
stream_frame->stream_id = stream_id;
stream_frame->data_frame.df_fin = type & 0x1;
stream_frame->data_frame.df_offset = offset;
stream_frame->data_frame.df_size = data_sz;
stream_frame->data_frame.df_data = p;
stream_frame->data_frame.df_read_off= 0;
stream_frame->packet_in = NULL;
assert(p <= pend);
return p + data_sz - (unsigned char *) buf;
}
int
lsquic_ietf_v1_parse_crypto_frame (const unsigned char *buf,
size_t rem_packet_sz, struct stream_frame *stream_frame)
{
const unsigned char *const pend = buf + rem_packet_sz;
const unsigned char *p = buf;
uint64_t offset, data_sz;
int r;
CHECK_SPACE(1, p, pend);
++p;
r = vint_read(p, pend, &offset);
if (r < 0)
return -1;
p += r;
r = vint_read(p, pend, &data_sz);
if (r < 0)
return -1;
p += r;
CHECK_SPACE(data_sz, p, pend);
/* Largest offset cannot exceed this value and we MUST detect this error */
if (VINT_MAX_VALUE - offset < data_sz)
return -1;
stream_frame->stream_id = ~0ULL; /* Unset */
stream_frame->data_frame.df_fin = 0;
stream_frame->data_frame.df_offset = offset;
stream_frame->data_frame.df_size = data_sz;
stream_frame->data_frame.df_data = p;
stream_frame->data_frame.df_read_off= 0;
stream_frame->packet_in = NULL;
assert(p <= pend);
return p + data_sz - (unsigned char *) buf;
}
static int
ietf_v1_parse_crypto_frame (const unsigned char *buf, size_t rem_packet_sz,
struct stream_frame *stream_frame)
{
if (rem_packet_sz > 0)
{
assert(0x06 == buf[0]);
return lsquic_ietf_v1_parse_crypto_frame(buf, rem_packet_sz,
stream_frame);
}
else
return -1;
}
#if __GNUC__
# define UNLIKELY(cond) __builtin_expect(cond, 0)
#else
# define UNLIKELY(cond) cond
#endif
/* Bits 10 (2) is ECT(0); * bits 01 (1) is ECT(1). */
static const int ecnmap[4] = { 0, 2, 1, 3, };
static int
ietf_v1_parse_ack_frame (const unsigned char *const buf, size_t buf_len,
struct ack_info *ack, uint8_t exp)
{
const unsigned char *p = buf;
const unsigned char *const end = buf + buf_len;
uint64_t block_count, gap, block;
enum ecn ecn;
unsigned i;
int r;
++p;
r = vint_read(p, end, &ack->ranges[0].high);
if (UNLIKELY(r < 0))
return -1;
p += r;
r = vint_read(p, end, &ack->lack_delta);
if (UNLIKELY(r < 0))
return -1;
p += r;
ack->lack_delta <<= exp;
r = vint_read(p, end, &block_count);
if (UNLIKELY(r < 0))
return -1;
p += r;
r = vint_read(p, end, &block);
if (UNLIKELY(r < 0))
return -1;
ack->ranges[0].low = ack->ranges[0].high - block;
if (UNLIKELY(ack->ranges[0].high < ack->ranges[0].low))
return -1;
p += r;
for (i = 1; i <= block_count; ++i)
{
r = vint_read(p, end, &gap);
if (UNLIKELY(r < 0))
return -1;
p += r;
r = vint_read(p, end, &block);
if (UNLIKELY(r < 0))
return -1;
p += r;
if (i < sizeof(ack->ranges) / sizeof(ack->ranges[0]))
{
ack->ranges[i].high = ack->ranges[i - 1].low - gap - 2;
ack->ranges[i].low = ack->ranges[i].high - block;
if (UNLIKELY(ack->ranges[i].high >= ack->ranges[i - 1].low
|| ack->ranges[i].high < ack->ranges[i].low))
return -1;
}
}
if (i < sizeof(ack->ranges) / sizeof(ack->ranges[0]))
{
ack->flags = 0;
ack->n_ranges = block_count + 1;
}
else
{
ack->flags = AI_TRUNCATED;
ack->n_ranges = sizeof(ack->ranges) / sizeof(ack->ranges[0]);
}
if (0x03 == buf[0])
{
for (ecn = 1; ecn <= 3; ++ecn)
{
r = vint_read(p, end, &ack->ecn_counts[ecnmap[ecn]]);
if (UNLIKELY(r < 0))
return -1;
p += r;
}
ack->flags |= AI_ECN;
}
return p - buf;
}
static unsigned
ietf_v1_rst_frame_size (lsquic_stream_id_t stream_id, uint64_t error_code,
uint64_t final_size)
{
return 1 /* Type */
+ vint_size(stream_id) /* Stream ID (i) */
+ vint_size(error_code) /* Application Error Code (i) */
+ vint_size(final_size); /* Final Size (i) */
}
static int
ietf_v1_gen_rst_frame (unsigned char *buf, size_t buf_len,
lsquic_stream_id_t stream_id, uint64_t final_size, uint64_t error_code)
{
unsigned vbits;
unsigned char *p;
if (buf_len < ietf_v1_rst_frame_size(stream_id, error_code, final_size))
return -1;
p = buf;
*p++ = 0x04;
/* Stream ID (i) */
vbits = vint_val2bits(stream_id);
vint_write(p, stream_id, vbits, 1 << vbits);
p += 1 << vbits;
/* Application Error Code (i) */
vbits = vint_val2bits(error_code);
vint_write(p, error_code, vbits, 1 << vbits);
p += 1 << vbits;
/* Final Size (i) */
vbits = vint_val2bits(final_size);
vint_write(p, final_size, vbits, 1 << vbits);
p += 1 << vbits;
return p - buf;
}
static int
ietf_v1_parse_rst_frame (const unsigned char *buf, size_t buf_len,
lsquic_stream_id_t *stream_id_p, uint64_t *final_size_p, uint64_t *error_code_p)
{
const unsigned char *p = buf + 1;
const unsigned char *const end = buf + buf_len;
uint64_t stream_id, final_size, error_code;
int r;
/* Stream ID (i) */
r = vint_read(p, end, &stream_id);
if (r < 0)
return r;
p += r;
/* Application Error Code (i) */
r = vint_read(p, end, &error_code);
if (r < 0)
return r;
p += r;
/* Final Size (i) */
r = vint_read(p, end, &final_size);
if (r < 0)
return r;
p += r;
*stream_id_p = stream_id;
*final_size_p = final_size;
*error_code_p = error_code;
return p - buf;
}
static int
ietf_v1_parse_stop_sending_frame (const unsigned char *buf, size_t buf_len,
lsquic_stream_id_t *stream_id, uint64_t *error_code)
{
const unsigned char *p = buf + 1;
const unsigned char *const end = buf + buf_len;
int r;
r = vint_read(p, end, stream_id);
if (r < 0)
return r;
p += r;
r = vint_read(p, end, error_code);
if (r < 0)
return r;
p += r;
return p - buf;
}
static int
ietf_v1_gen_stop_sending_frame (unsigned char *buf, size_t len,
lsquic_stream_id_t stream_id, uint64_t error_code)
{
return ietf_v1_gen_two_varints(buf, len, 0x05, (uint64_t[]){ stream_id,
error_code, });
}
static unsigned
ietf_v1_stop_sending_frame_size (lsquic_stream_id_t val, uint64_t error_code)
{
return 1 + vint_size(val) + vint_size(error_code);
}
static int
ietf_v1_parse_new_token_frame (const unsigned char *buf, size_t buf_len,
const unsigned char **token, size_t *token_size_p)
{
uint64_t token_size;
const unsigned char *p = buf + 1;
const unsigned char *const end = buf + buf_len;
int r;
r = vint_read(p, end, &token_size);
if (r < 0)
return r;
p += r;
if (p + token_size > end)
return -1;
*token = p;
p += token_size;
*token_size_p = token_size;
return p - buf;
}
static int
ietf_v1_gen_ping_frame (unsigned char *buf, int buf_len)
{
if (buf_len > 0)
{
buf[0] = 0x01;
return 1;
}
else
return -1;
}
static size_t
ietf_v1_connect_close_frame_size (int app_error, unsigned error_code,
unsigned frame_type, size_t reason_len)
{
return 1 /* Type */
+ (1 << vint_val2bits(error_code)) /* Error code */
+ (app_error ? 0 : 1 << vint_val2bits(frame_type)) /* Frame type */
+ (1 << vint_val2bits(reason_len)) /* Reason Phrase Length */
+ reason_len
;
}
static int
ietf_v1_gen_connect_close_frame (unsigned char *buf, size_t buf_len,
int app_error, unsigned error_code, const char *reason, int reason_len)
{
size_t needed;
unsigned bits_error, bits_reason;
unsigned char *p;
assert(!!reason == !!reason_len);
bits_reason = vint_val2bits(reason_len);
bits_error = vint_val2bits(error_code);
needed = 1 /* Type */ + (1 << bits_error)
+ (app_error ? 0 : 1) /* Frame type */
/* TODO: frame type instead of just zero */
+ (1 << bits_reason) + reason_len;
if (buf_len < needed)
return -1;
p = buf;
*p = 0x1C + !!app_error;
++p;
vint_write(p, error_code, bits_error, 1 << bits_error);
p += 1 << bits_error;
if (!app_error)
*p++ = 0; /* Frame type */ /* TODO */
vint_write(p, reason_len, bits_reason, 1 << bits_reason);
p += 1 << bits_reason;
if (reason_len)
{
memcpy(p, reason, reason_len);
p += reason_len;
}
assert((unsigned) (p - buf) == needed);
return p - buf;
}
static int
ietf_v1_parse_connect_close_frame (const unsigned char *buf, size_t buf_len,
int *app_error_p, uint64_t *error_code, uint16_t *reason_len,
uint8_t *reason_offset)
{
const unsigned char *const pend = buf + buf_len;
const unsigned char *p = buf + 1;
uint64_t len;
ptrdiff_t off;
int app_error, r;
r = vint_read(p, pend, error_code);
if (r < 0)
return -1;
p += r;
app_error = buf[0] == 0x1D;
if (!app_error)
{
r = vint_read(p, pend, &len);
if (r < 0)
return -1;
p += r;
}
r = vint_read(p, pend, &len);
if (r < 0)
return -1;
if (len > UINT16_MAX)
return -1;
p += r;
off = p - buf;
if (buf_len < off + len)
return -2;
*app_error_p = app_error;
*reason_len = len;
*reason_offset = off;
return off + len;
}
/* Returns number of bytes written or -1 on failure */
/* This function makes an assumption that there is at least one range */
static int
ietf_v1_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
gaf_rechist_first_f rechist_first, gaf_rechist_next_f rechist_next,
gaf_rechist_largest_recv_f rechist_largest_recv,
void *rechist, lsquic_time_t now, int *has_missing,
lsquic_packno_t *largest_received, const uint64_t *ecn_counts)
{
unsigned char *block_count_p, *p = outbuf;
unsigned char *const end = p + outbuf_sz;
lsquic_time_t time_diff;
lsquic_packno_t packno_diff, gap, prev_low, maxno, rsize;
size_t sz;
const struct lsquic_packno_range *range;
unsigned a, b, c, addl_ack_blocks, ecn_needs;
unsigned bits[4];
enum ecn ecn;
#define AVAIL() (end - p)
#define CHECKOUT(sz) do { \
if ((intptr_t) (sz) > AVAIL()) { \
errno = ENOBUFS; \
return -1; \
} \
} while (0)
range = rechist_first(rechist);
if (!range)
{
errno = EINVAL;
return -1;
}
// LSQ_DEBUG("range [%"PRIu64" - %"PRIu64"]", range->high, range->low);
time_diff = now - rechist_largest_recv(rechist);
time_diff >>= TP_DEF_ACK_DELAY_EXP;
maxno = range->high;
packno_diff = maxno - range->low;
a = vint_val2bits(maxno);
b = vint_val2bits(time_diff);
c = vint_val2bits(packno_diff);
sz = 1 /* Type */
+ (1 << a) /* Largest Acknowledged */
+ (1 << b) /* ACK Delay */
+ 1 /* ACK Block Count */
+ (1 << c) /* ACK Blocks */
;
CHECKOUT(sz);
if (ecn_counts)
{
for (ecn = 1; ecn <= 3; ++ecn)
bits[ecn] = vint_val2bits(ecn_counts[ecn]);
ecn_needs = (1 << bits[1]) + (1 << bits[2]) + (1 << bits[3]);
}
else
ecn_needs = 0;
*p = 0x02 + !!ecn_counts;
++p;
vint_write(p, maxno, a, 1 << a);
p += 1 << a;
vint_write(p, time_diff, b, 1 << b);
p += 1 << b;
block_count_p = p;
p += 1; /* Initial guess that we have fewer than 64 additional ACK Blocks */
vint_write(p, packno_diff, c, 1 << c);
p += 1 << c;
prev_low = range->low;
addl_ack_blocks = 0;
while ((range = rechist_next(rechist)))
{
// LSQ_DEBUG("range [%"PRIu64" - %"PRIu64"]", range->high, range->low);
gap = prev_low - range->high - 1;
rsize = range->high - range->low;
a = vint_val2bits(gap - 1);
b = vint_val2bits(rsize);
if (ecn_needs + (1 << a) + (1 << b) > (unsigned)AVAIL())
break;
if (addl_ack_blocks == VINT_MAX_ONE_BYTE)
{
memmove(block_count_p + 2, block_count_p + 1,
p - block_count_p - 1);
++p;
}
vint_write(p, gap - 1, a, 1 << a);
p += 1 << a;
vint_write(p, rsize, b, 1 << b);
p += 1 << b;
++addl_ack_blocks;
prev_low = range->low;
}
/* Here we assume that addl_ack_blocks < (1 << 14), which is a safe
* assumption to make.
*/
vint_write(block_count_p, addl_ack_blocks,
addl_ack_blocks > VINT_MAX_ONE_BYTE,
1 + (addl_ack_blocks > VINT_MAX_ONE_BYTE));
if (ecn_counts)
{
assert(ecn_needs <= (unsigned)AVAIL());
for (ecn = 1; ecn <= 3; ++ecn)
{
vint_write(p, ecn_counts[ecnmap[ecn]], bits[ecnmap[ecn]], 1 << bits[ecnmap[ecn]]);
p += 1 << bits[ecnmap[ecn]];
}
}
*has_missing = addl_ack_blocks > 0;
*largest_received = maxno;
return p - (unsigned char *) outbuf;
#undef CHECKOUT
#undef AVAIL
}
static size_t
ietf_v1_calc_stream_frame_header_sz (lsquic_stream_id_t stream_id,
uint64_t offset, unsigned data_sz)
{
if (offset)
return 1
+ (1 << vint_val2bits(stream_id))
+ (1 << vint_val2bits(data_sz))
+ (1 << vint_val2bits(offset));
else
return 1
+ (1 << vint_val2bits(data_sz))
+ (1 << vint_val2bits(stream_id));
}
/* [draft-ietf-quic-transport-24] Section 19.6 */
static size_t
ietf_v1_calc_crypto_frame_header_sz (uint64_t offset, unsigned data_sz)
{
return 1 /* Frame type */
+ (1 << vint_val2bits(offset))
+ (1 << vint_val2bits(data_sz))
;
}
static enum quic_frame_type
ietf_v1_parse_frame_type (const unsigned char *buf, size_t len)
{
uint64_t val;
int s;
if (len > 0 && buf[0] < 0x40)
return lsquic_iquic_byte2type[buf[0]];
s = vint_read(buf, buf + len, &val);
if (s > 0 && (unsigned) s == (1u << vint_val2bits(val)))
switch (val)
{
case FRAME_TYPE_ACK_FREQUENCY: return QUIC_FRAME_ACK_FREQUENCY;
case FRAME_TYPE_TIMESTAMP: return QUIC_FRAME_TIMESTAMP;
default: break;
}
return QUIC_FRAME_INVALID;
}
static unsigned
ietf_v1_path_chal_frame_size (void)
{
return 1 + sizeof(uint64_t);
}
static int
ietf_v1_gen_path_chal_frame (unsigned char *buf, size_t len, uint64_t chal)
{
if (len >= 1 + sizeof(chal))
{
*buf = 0x1A;
memcpy(buf + 1, &chal, sizeof(chal));
return 1 + sizeof(chal);
}
else
return -1;
}
static int
ietf_v1_parse_path_chal_frame (const unsigned char *buf, size_t len,
uint64_t *chal)
{
if (len >= 9)
{
memcpy(chal, buf + 1, 8);
return 9;
}
else
return -1;
}
static unsigned
ietf_v1_path_resp_frame_size (void)
{
return 1 + sizeof(uint64_t);
}
static int
ietf_v1_gen_path_resp_frame (unsigned char *buf, size_t len, uint64_t resp)
{
if (len >= 1 + sizeof(resp))
{
*buf = 0x1B;
memcpy(buf + 1, &resp, sizeof(resp));
return 1 + sizeof(resp);
}
else
return -1;
}
static int
ietf_v1_parse_path_resp_frame (const unsigned char *buf, size_t len,
uint64_t *resp)
{
return ietf_v1_parse_path_chal_frame(buf, len, resp);
}
static void
ietf_v1_turn_on_fin (unsigned char *stream_frame_header)
{
*stream_frame_header |= 1;
}
static unsigned
ietf_v1_packno_bits2len (enum packno_bits bits)
{
return iquic_packno_bits2len(bits);
}
static enum packno_bits
ietf_v1_calc_packno_bits (lsquic_packno_t packno,
lsquic_packno_t least_unacked, uint64_t n_in_flight)
{
uint64_t delta;
unsigned bits;
delta = packno - least_unacked;
if (n_in_flight > delta)
delta = n_in_flight;
delta *= 4;
bits = (delta >= (1ULL << 8))
+ (delta >= (1ULL << 16))
+ (delta >= (1ULL << 24))
;
return bits;
}
static int
ietf_v1_parse_one_varint (const unsigned char *buf, size_t len, uint64_t *val)
{
int s;
s = vint_read(buf + 1, buf + len, val);
if (s >= 0)
return 1 + s;
else
return s;
}
static int
ietf_v1_gen_one_varint (unsigned char *buf, size_t len,
unsigned char type, uint64_t val)
{
unsigned vbits;
unsigned char *p;
vbits = vint_val2bits(val);
if (1u + (1u << vbits) > len)
return -1;
p = buf;
*p++ = type;
vint_write(p, val, vbits, 1 << vbits);
p += 1 << vbits;
return p - buf;
}
static int
ietf_v1_gen_blocked_frame (unsigned char *buf, size_t buf_len, uint64_t off)
{
return ietf_v1_gen_one_varint(buf, buf_len, 0x14, off);
}
static int
ietf_v1_parse_blocked_frame (const unsigned char *buf, size_t sz, uint64_t *off)
{
return ietf_v1_parse_one_varint(buf, sz, off);
}
static unsigned
ietf_v1_blocked_frame_size (uint64_t off)
{
return 1 + vint_size(off);
}
static int
ietf_v1_parse_max_data (const unsigned char *buf, size_t len, uint64_t *val)
{
return ietf_v1_parse_one_varint(buf, len, val);
}
static int
ietf_v1_gen_max_data_frame (unsigned char *buf, size_t len, uint64_t val)
{
return ietf_v1_gen_one_varint(buf, len, 0x10, val);
}
static unsigned
ietf_v1_max_data_frame_size (uint64_t val)
{
return 1 + vint_size(val);
}
static int
ietf_v1_parse_retire_cid_frame (const unsigned char *buf, size_t len,
uint64_t *val)
{
return ietf_v1_parse_one_varint(buf, len, val);
}
static int
ietf_v1_gen_retire_cid_frame (unsigned char *buf, size_t len, uint64_t val)
{
return ietf_v1_gen_one_varint(buf, len, 0x19, val);
}
static size_t
ietf_v1_retire_cid_frame_size (uint64_t val)
{
return 1 + vint_size(val);
}
static int
ietf_v1_parse_new_conn_id (const unsigned char *buf, size_t len,
uint64_t *seqno, uint64_t *retire_prior_to,
lsquic_cid_t *cid, const unsigned char **reset_token)
{
const unsigned char *p = buf + 1;
const unsigned char *const end = buf + len;
unsigned char cid_len;
int s;
s = vint_read(p, end, seqno);
if (s < 0)
return s;
p += s;
s = vint_read(p, end, retire_prior_to);
if (s < 0)
return s;
p += s;
if (p >= end)
return -1;
cid_len = *p++;
if (cid_len == 0 || cid_len > MAX_CID_LEN)
return -2;
if ((unsigned) (end - p) < cid_len + IQUIC_SRESET_TOKEN_SZ)
return -1;
cid->len = cid_len;
memcpy(cid->idbuf, p, cid_len);
p += cid_len;
if (reset_token)
*reset_token = p;
p += IQUIC_SRESET_TOKEN_SZ;
return p - buf;
}
/* Size of a frame that contains two varints */
static unsigned
ietf_v1_two_varints_size (uint64_t vals[2])
{
unsigned vbits[2];
vbits[0] = vint_val2bits(vals[0]);
vbits[1] = vint_val2bits(vals[1]);
return 1u + (1u << vbits[0]) + (1u << vbits[1]);
}
static int
ietf_v1_gen_two_varints (unsigned char *buf, size_t len,
unsigned char type, uint64_t vals[2])
{
unsigned vbits[2];
unsigned char *p;
vbits[0] = vint_val2bits(vals[0]);
vbits[1] = vint_val2bits(vals[1]);
if (1u + (1u << vbits[0]) + (1u << vbits[1]) > len)
return -1;
p = buf;
*p++ = type;
vint_write(p, vals[0], vbits[0], 1 << vbits[0]);
p += 1 << vbits[0];
vint_write(p, vals[1], vbits[1], 1 << vbits[1]);
p += 1 << vbits[1];
return p - buf;
}
static int
ietf_v1_parse_two_varints (const unsigned char *buf, size_t len, uint64_t *vals[2])
{
const unsigned char *p = buf;
const unsigned char *const end = p + len;
int s;
if (len < 2)
return -1;
++p; /* Type */
s = vint_read(p, end, vals[0]);
if (s < 0)
return s;
p += s;
s = vint_read(p, end, vals[1]);
if (s < 0)
return s;
p += s;
return p - buf;
}
/* vals[0] is the frame type */
static unsigned
ietf_v1_frame_with_varints_size (unsigned n, uint64_t vals[])
{
unsigned vbits, size;
assert(n > 0);
vbits = vint_val2bits(vals[0]);
size = 1 << vbits;
while (--n)
{
vbits = vint_val2bits(vals[n]);
size += 1 << vbits;
}
return size;
}
/* vals[0] is the frame type */
static int
ietf_v1_gen_frame_with_varints (unsigned char *buf, size_t len,
unsigned count, uint64_t vals[])
{
unsigned vbits, n;
unsigned char *p;
if (ietf_v1_frame_with_varints_size(count, vals) > len)
return -1;
p = buf;
for (n = 0; n < count; ++n)
{
vbits = vint_val2bits(vals[n]);
vint_write(p, vals[n], vbits, 1 << vbits);
p += 1 << vbits;
}
return p - buf;
}
/* Frame type is checked when frame type is parsed. The only use here is
* to calculate skip length.
*/
static int
ietf_v1_parse_frame_with_varints (const unsigned char *buf, size_t len,
const uint64_t frame_type, unsigned count, uint64_t *vals[])
{
const unsigned char *p = buf;
const unsigned char *const end = p + len;
unsigned vbits, n;
int s;
vbits = vint_val2bits(frame_type);
p += 1 << vbits;
for (n = 0; n < count; ++n)
{
s = vint_read(p, end, vals[n]);
if (s < 0)
return s;
p += s;
}
return p - buf;
}
static int
ietf_v1_parse_stream_blocked_frame (const unsigned char *buf, size_t len,
lsquic_stream_id_t *stream_id, uint64_t *offset)
{
return ietf_v1_parse_two_varints(buf, len,
(uint64_t *[]) { stream_id, offset, });
}
static unsigned
ietf_v1_stream_blocked_frame_size (lsquic_stream_id_t stream_id, uint64_t off)
{
return ietf_v1_two_varints_size((uint64_t []) { stream_id, off, });
}
static int
ietf_v1_gen_streams_blocked_frame (unsigned char *buf, size_t len,
enum stream_dir sd, uint64_t limit)
{
return ietf_v1_gen_one_varint(buf, len, 0x16 + (sd == SD_UNI), limit);
}
static int
ietf_v1_parse_streams_blocked_frame (const unsigned char *buf, size_t len,
enum stream_dir *sd, uint64_t *limit)
{
int s;
s = ietf_v1_parse_one_varint(buf, len, limit);
if (s > 0)
{
if (buf[0] == 0x16)
*sd = SD_BIDI;
else
*sd = SD_UNI;
}
return s;
}
static unsigned
ietf_v1_streams_blocked_frame_size (uint64_t limit)
{
return 1 + vint_size(limit);
}
static int
ietf_v1_gen_stream_blocked_frame (unsigned char *buf, size_t len,
lsquic_stream_id_t stream_id, uint64_t off)
{
return ietf_v1_gen_two_varints(buf, len, 0x15, (uint64_t[]){ stream_id, off, });
}
static int
ietf_v1_gen_max_stream_data_frame (unsigned char *buf, size_t len,
lsquic_stream_id_t stream_id, uint64_t off)
{
return ietf_v1_gen_two_varints(buf, len, 0x11, (uint64_t[]){ stream_id, off, });
}
static unsigned
ietf_v1_max_stream_data_frame_size (lsquic_stream_id_t stream_id, uint64_t off)
{
return ietf_v1_two_varints_size((uint64_t []) { stream_id, off, });
}
static int
ietf_v1_parse_max_stream_data_frame (const unsigned char *buf, size_t len,
lsquic_stream_id_t *stream_id, uint64_t *off)
{
return ietf_v1_parse_two_varints(buf, len, (uint64_t *[]){ stream_id, off, });
}
static int
ietf_v1_parse_max_streams_frame (const unsigned char *buf, size_t len,
enum stream_dir *sd, uint64_t *max_streams)
{
int s;
s = ietf_v1_parse_one_varint(buf, len, max_streams);
if (s > 0)
*sd = buf[0] == 0x12 ? SD_BIDI : SD_UNI;
return s;
}
static int
ietf_v1_gen_max_streams_frame (unsigned char *buf, size_t len,
enum stream_dir sd, uint64_t limit)
{
return ietf_v1_gen_one_varint(buf, len, 0x12 + (sd == SD_UNI), limit);
}
static unsigned
ietf_v1_max_streams_frame_size (uint64_t limit)
{
return 1 + vint_size(limit);
}
static size_t
ietf_v1_new_token_frame_size (size_t token_sz)
{
unsigned bits;
bits = vint_val2bits(token_sz);
return 1 + (1 << bits) + token_sz;
}
static int
ietf_v1_gen_new_token_frame (unsigned char *buf, size_t buf_sz,
const unsigned char *token, size_t token_sz)
{
unsigned char *p;
unsigned bits;
bits = vint_val2bits(token_sz);
if (buf_sz < 1 + (1 << bits) + token_sz)
{
errno = ENOBUFS;
return -1;
}
p = buf;
*p++ = 0x07;
vint_write(p, token_sz, bits, 1 << bits);
p += 1 << bits;
memcpy(p, token, token_sz);
p += token_sz;
return p - buf;
}
static size_t
ietf_v1_new_connection_id_frame_size (unsigned seqno, unsigned scid_len)
{
unsigned bits;
bits = vint_val2bits(seqno);
return 1 /* Frame Type */
+ (1 << bits) /* Sequence Number */
+ 1 /* Retire Prior To (we always set it to zero */
+ 1 /* CID length */
+ scid_len
+ IQUIC_SRESET_TOKEN_SZ;
}
static int
ietf_v1_gen_new_connection_id_frame (unsigned char *buf, size_t buf_sz,
unsigned seqno, const struct lsquic_cid *cid,
const unsigned char *token, size_t token_sz)
{
unsigned char *p;
unsigned bits;
if (buf_sz < ietf_v1_new_connection_id_frame_size(seqno, cid->len))
return -1;
p = buf;
*p++ = 0x18;
bits = vint_val2bits(seqno);
vint_write(p, seqno, bits, 1 << bits);
p += 1 << bits;
*p++ = 0; /* Retire Prior To */
*p++ = cid->len;
memcpy(p, cid->idbuf, cid->len);
p += cid->len;
memcpy(p, token, token_sz);
p += token_sz;
return p - buf;
}
/* [draft-ietf-quic-transport-17] Section-17.2 */
static const enum header_type bits2ht[4] =
{
[0] = HETY_INITIAL,
[1] = HETY_0RTT,
[2] = HETY_HANDSHAKE,
[3] = HETY_RETRY,
};
static const enum header_type bits2ht_v2[4] =
{
[0] = HETY_RETRY,
[1] = HETY_INITIAL,
[2] = HETY_0RTT,
[3] = HETY_HANDSHAKE,
};
#if LSQUIC_QIR
/* Return true if the parsing function is to enforce the minimum DCID
* length requirement as specified in IETF v1 and the I-Ds.
*/
static int
enforce_initial_dcil (enum lsquic_version version)
{
return version != (enum lsquic_version) -1
&& ((1 << version) & LSQUIC_IETF_VERSIONS);
}
#endif
int
lsquic_ietf_v1_parse_packet_in_long_begin (struct lsquic_packet_in *packet_in,
size_t length, int is_server, unsigned cid_len,
struct packin_parse_state *state)
{
const unsigned char *p = packet_in->pi_data;
const unsigned char *const end = p + length;
enum header_type header_type;
unsigned dcil, scil;
int r;
unsigned char first_byte;
uint64_t payload_len, token_len;
if (length < 6)
return -1;
first_byte = *p++;
if ((packet_in->pi_flags & PI_VER_PARSED) == 0)
{
packet_in->pi_version = lsquic_tag2ver_fast(p);
packet_in->pi_flags |= PI_VER_PARSED;
}
p += 4;
if (packet_in->pi_version != LSQVER_VERNEG)
{
if (packet_in->pi_version == LSQVER_I002)
header_type = bits2ht_v2[ (first_byte >> 4) & 3 ];
else
header_type = bits2ht[ (first_byte >> 4) & 3 ];
}
else
header_type = HETY_VERNEG;
packet_in->pi_header_type = header_type;
dcil = *p++;
if (p + dcil >= end || dcil > MAX_CID_LEN)
return -1;
if (dcil)
{
memcpy(packet_in->pi_dcid.idbuf, p, dcil);
packet_in->pi_flags |= PI_CONN_ID;
p += dcil;
}
packet_in->pi_dcid.len = dcil;
scil = *p++;
if (p + scil > end || scil > MAX_CID_LEN)
return -1;
if (scil)
{
packet_in->pi_scid_off = p - packet_in->pi_data;
p += scil;
}
packet_in->pi_scid_len = scil;
switch (header_type)
{
case HETY_INITIAL:
#if LSQUIC_QIR
if (!enforce_initial_dcil(packet_in->pi_version))
{
/* Count even zero-length DCID as having DCID */
packet_in->pi_flags |= PI_CONN_ID;
}
else
#endif
if (is_server && dcil < MIN_INITIAL_DCID_LEN)
return -1;
r = vint_read(p, end, &token_len);
if (r < 0)
return -1;
if (token_len && !is_server)
{
/* From [draft-ietf-quic-transport-14]:
*
* Token Length: A variable-length integer specifying the
* length of the Token field, in bytes. This value is zero
* if no token is present. Initial packets sent by the
* server MUST set the Token Length field to zero; clients
* that receive an Initial packet with a non-zero Token
* Length field MUST either discard the packet or generate
* a connection error of type PROTOCOL_VIOLATION.
*/
return -1;
}
p += r;
if (token_len)
{
if (token_len >=
1ull << (sizeof(packet_in->pi_token_size) * 8))
return -1;
if (p + token_len > end)
return -1;
packet_in->pi_token = p - packet_in->pi_data;
packet_in->pi_token_size = token_len;
p += token_len;
}
/* fall-through */
case HETY_HANDSHAKE:
case HETY_0RTT:
if (p >= end)
return -1;
r = vint_read(p, end, &payload_len);
if (r < 0)
return -1;
p += r;
if (p - packet_in->pi_data + payload_len > length)
return -1;
length = p - packet_in->pi_data + payload_len;
if (end - p < 4)
return -1;
state->pps_p = p - r;
state->pps_nbytes = r;
packet_in->pi_quic_ver = 1;
break;
case HETY_RETRY:
if (p >= end)
return -1;
if (p
/* [draft-ietf-quic-transport-25] Section 17.2.5 says that "a
* client MUST discard a Retry packet with a zero-length Retry
* Token field." We might as well do it here.
*/
+ 1
/* Integrity tag length: */
+ 16 > end)
return -1;
packet_in->pi_token = p - packet_in->pi_data;
packet_in->pi_token_size = end - p - 16;
/* Tag validation happens later */
p = end;
length = end - packet_in->pi_data;
state->pps_p = NULL;
state->pps_nbytes = 0;
packet_in->pi_quic_ver = 1;
break;
default:
assert(header_type == HETY_VERNEG);
if (p >= end || (3 & (uintptr_t) (end - p)))
return -1;
packet_in->pi_quic_ver = p - packet_in->pi_data;
p = end;
state->pps_p = NULL;
state->pps_nbytes = 0;
break;
}
packet_in->pi_header_sz = p - packet_in->pi_data;
packet_in->pi_data_sz = length;
packet_in->pi_nonce = 0;
packet_in->pi_refcnt = 0;
packet_in->pi_frame_types = 0;
memset(&packet_in->pi_next, 0, sizeof(packet_in->pi_next));
packet_in->pi_refcnt = 0;
packet_in->pi_received = 0;
/* Packet number is set to an invalid value. The packet number must
* be decrypted, which happens later.
*/
packet_in->pi_packno = 1ULL << 62;
return 0;
}
/* Is this a valid Initial packet? We take the perspective of the server. */
int
lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet (const unsigned char *buf,
size_t length, lsquic_ver_tag_t *tagp)
{
const unsigned char *p = buf;
const unsigned char *const end = p + length;
lsquic_ver_tag_t tag;
enum header_type header_type;
unsigned dcil, scil;
int r;
unsigned char first_byte;
uint64_t payload_len, token_len, packet_len;
if (length < 6)
return 0;
first_byte = *p++;
if (*p != 0x6B)
header_type = bits2ht[ (first_byte >> 4) & 3 ];
else
header_type = bits2ht_v2[ (first_byte >> 4) & 3 ];
if (header_type != HETY_INITIAL)
return 0;
memcpy(&tag, p, 4);
p += 4;
switch (tag)
{
case 0:
return 0; /* Client never sends version negotiation packets */
case TAG('Q', '0', '4', '6'):
dcil = p[0] >> 4;
if (dcil)
dcil += 3;
scil = p[0] & 0xF;
if (scil)
scil += 3;
++p;
if (!(dcil == GQUIC_CID_LEN && scil == 0))
return 0;
packet_len = first_byte & 3;
if (end - p < (ptrdiff_t) (dcil + scil + packet_len))
return 0;
break;
case TAG('Q', '0', '5', '0'):
dcil = *p++;
if (dcil != 8)
return 0;
if (p + dcil + 1 >= end)
return 0;
p += dcil;
scil = *p++;
if (scil != 0)
return 0;
goto read_token;
default:
dcil = *p++;
if (dcil < MIN_INITIAL_DCID_LEN || dcil > MAX_CID_LEN)
return 0;
if (p + dcil >= end)
return 0;
p += dcil;
scil = *p++;
if (p + scil > end || scil > MAX_CID_LEN)
return 0;
p += scil;
read_token:
r = vint_read(p, end, &token_len);
if (r < 0)
return 0;
p += r;
p += token_len;
if (p >= end)
return 0;
r = vint_read(p, end, &payload_len);
if (r < 0)
return 0;
p += r;
if (p - buf + payload_len > length)
return 0;
if (end - p < 4)
return 0;
}
*tagp = tag;
return 1;
}
int
lsquic_ietf_v1_parse_packet_in_short_begin (struct lsquic_packet_in *packet_in,
size_t length, int is_server, unsigned cid_len,
struct packin_parse_state *state)
{
unsigned char byte;
unsigned header_sz;
/* By the time this function has been called, we know length is non-zero */
byte = packet_in->pi_data[0];
/* [draft-ietf-quic-transport-17] Section 17.3 */
/* 01SRRKPP */
if (cid_len)
{
header_sz = 1 + cid_len;
if (length < header_sz)
return -1;
memcpy(packet_in->pi_dcid.idbuf, packet_in->pi_data + 1, cid_len);
packet_in->pi_dcid.len = cid_len;
packet_in->pi_flags |= PI_CONN_ID;
}
else
header_sz = 1;
packet_in->pi_flags |= ((byte & 0x20) > 0) << PIBIT_SPIN_SHIFT;
packet_in->pi_flags |= (byte & 3) << PIBIT_BITS_SHIFT;
packet_in->pi_header_sz = header_sz;
packet_in->pi_data_sz = length;
packet_in->pi_quic_ver = 0;
packet_in->pi_nonce = 0;
packet_in->pi_refcnt = 0;
packet_in->pi_frame_types = 0;
memset(&packet_in->pi_next, 0, sizeof(packet_in->pi_next));
packet_in->pi_refcnt = 0;
packet_in->pi_received = 0;
/* This is so that Q046 works, ID-18 code does not use it */
state->pps_p = packet_in->pi_data + header_sz;
state->pps_nbytes = 1 + (byte & 3);
return 0;
}
#if __GNUC__
# define popcount __builtin_popcount
#else
static int
popcount (unsigned v)
{
int count, i;
for (i = 0, count = 0; i < sizeof(v) * 8; ++i)
if (v & (1 << i))
++count;
return count;
}
#endif
int
lsquic_ietf_v1_gen_ver_nego_pkt (unsigned char *buf, size_t bufsz,
const lsquic_cid_t *scid, const lsquic_cid_t *dcid, unsigned versions,
uint8_t rand)
{
size_t need;
int r;
need = 1 /* Type */ + 4 /* Version */ + 1 /* DCIL */
+ dcid->len + 1 /* SCIL */ + scid->len + popcount(versions) * 4;
if (need > bufsz)
return -1;
*buf++ = 0x80 | QUIC_BIT | rand;
memset(buf, 0, 4);
buf += 4;
/* From [draft-ietf-quic-transport-22], Section 17.2.1:
*
* The server MUST include the value from the Source Connection ID field
* of the packet it receives in the Destination Connection ID field.
* The value for Source Connection ID MUST be copied from the
* Destination Connection ID of the received packet, which is initially
* randomly selected by a client. Echoing both connection IDs gives
* clients some assurance that the server received the packet and that
* the Version Negotiation packet was not generated by an off-path
* attacker.
*/
*buf++ = dcid->len;
memcpy(buf, dcid->idbuf, dcid->len);
buf += dcid->len;
*buf++ = scid->len;
memcpy(buf, scid->idbuf, scid->len);
buf += scid->len;
r = lsquic_gen_ver_tags(buf, bufsz - 1 - 4 - 2 - dcid->len - scid->len,
versions);
if (r < 0)
return -1;
assert((unsigned) r == popcount(versions) * 4u);
return need;
}
static int
ietf_v1_gen_handshake_done_frame (unsigned char *buf, size_t buf_len)
{
if (buf_len > 0)
{
*buf = 0x1E;
return 1;
}
else
return -1;
}
static int
ietf_v1_parse_handshake_done_frame (const unsigned char *buf, size_t buf_len)
{
assert(buf[0] == 0x1E);
assert(buf_len > 0);
return 1;
}
static int
ietf_v1_gen_ack_frequency_frame (unsigned char *buf, size_t buf_len,
uint64_t seqno, uint64_t pack_tol, uint64_t upd_mad, int ignore)
{
int sz;
sz = ietf_v1_gen_frame_with_varints(buf, buf_len, 4,
(uint64_t[]){ FRAME_TYPE_ACK_FREQUENCY, seqno, pack_tol, upd_mad });
if (sz > 0 && (size_t) sz < buf_len)
{
buf[sz++] = !!ignore;
return sz;
}
else
return -1;
}
static int
ietf_v1_parse_ack_frequency_frame (const unsigned char *buf, size_t buf_len,
uint64_t *seqno, uint64_t *pack_tol, uint64_t *upd_mad, int *ignore)
{
int sz;
sz = ietf_v1_parse_frame_with_varints(buf, buf_len,
FRAME_TYPE_ACK_FREQUENCY,
3, (uint64_t *[]) { seqno, pack_tol, upd_mad });
if (sz > 0 && (size_t) sz < buf_len && buf[sz] < 2)
{
*ignore = buf[sz++];
return sz;
}
else
return -1;
}
static unsigned
ietf_v1_ack_frequency_frame_size (uint64_t seqno, uint64_t pack_tol,
uint64_t upd_mad)
{
return 1 + ietf_v1_frame_with_varints_size(4,
(uint64_t[]){ FRAME_TYPE_ACK_FREQUENCY, seqno, pack_tol, upd_mad });
}
static unsigned
ietf_v1_handshake_done_frame_size (void)
{
return 1;
}
static int
ietf_v1_gen_timestamp_frame (unsigned char *buf, size_t buf_len,
uint64_t timestamp)
{
return ietf_v1_gen_frame_with_varints(buf, buf_len, 2,
(uint64_t[]){ FRAME_TYPE_TIMESTAMP, timestamp });
}
static int
ietf_v1_parse_timestamp_frame (const unsigned char *buf, size_t buf_len,
uint64_t *timestamp)
{
return ietf_v1_parse_frame_with_varints(buf, buf_len,
FRAME_TYPE_TIMESTAMP, 1, (uint64_t *[]) { timestamp });
}
static int
ietf_v1_parse_datagram_frame (const unsigned char *buf, size_t buf_len,
const void **data, size_t *data_len)
{
uint64_t len;
int s;
/* Length and frame type have been checked already */
assert(buf_len > 0);
assert(buf[0] == 0x30 || buf[0] == 0x31);
if (buf[0] & 1)
{
s = vint_read(buf + 1, buf + buf_len, &len);
if (s > 0 && 1 + s + len <= buf_len)
{
*data = buf + 1 + s;
*data_len = len;
return 1 + s + len;
}
else
return -1;
}
else
{
*data = buf + 1;
*data_len = buf_len - 1;
return buf_len;
}
}
static unsigned
ietf_v1_datagram_frame_size (size_t sz)
{
return 1u + vint_size(sz) + sz;
}
static int
ietf_v1_gen_datagram_frame (unsigned char *buf, size_t bufsz, size_t min_sz,
size_t max_sz,
ssize_t (*user_callback)(struct lsquic_conn *, void *, size_t),
struct lsquic_conn *lconn)
{
unsigned bits, len_sz;
ssize_t nw;
/* We always generate length. A more efficient implementation would
* complicate the API.
*/
if (min_sz)
bits = vint_val2bits(min_sz);
else
bits = vint_val2bits(bufsz);
len_sz = 1u << bits;
if (1 + len_sz + min_sz > bufsz)
{
errno = ENOBUFS;
return -1;
}
nw = user_callback(lconn, buf + 1 + len_sz, min_sz ? min_sz
: MIN(bufsz - 1 - len_sz, max_sz));
if (nw >= 0)
{
buf[0] = 0x31;
vint_write(&buf[1], (uint64_t) nw, bits, len_sz);
return 1 + len_sz + nw;
}
else
return -1;
}
const struct parse_funcs lsquic_parse_funcs_ietf_v1 =
{
.pf_gen_reg_pkt_header = ietf_v1_gen_reg_pkt_header,
.pf_parse_packet_in_finish = ietf_v1_parse_packet_in_finish,
.pf_gen_stream_frame = ietf_v1_gen_stream_frame,
.pf_calc_stream_frame_header_sz = ietf_v1_calc_stream_frame_header_sz,
.pf_parse_stream_frame = ietf_v1_parse_stream_frame,
.pf_dec_stream_frame_size = ietf_v1_dec_stream_frame_size,
.pf_parse_ack_frame = ietf_v1_parse_ack_frame,
.pf_gen_ack_frame = ietf_v1_gen_ack_frame,
.pf_gen_blocked_frame = ietf_v1_gen_blocked_frame,
.pf_parse_blocked_frame = ietf_v1_parse_blocked_frame,
.pf_blocked_frame_size = ietf_v1_blocked_frame_size,
.pf_rst_frame_size = ietf_v1_rst_frame_size,
.pf_gen_rst_frame = ietf_v1_gen_rst_frame,
.pf_parse_rst_frame = ietf_v1_parse_rst_frame,
.pf_connect_close_frame_size = ietf_v1_connect_close_frame_size,
.pf_gen_connect_close_frame = ietf_v1_gen_connect_close_frame,
.pf_parse_connect_close_frame = ietf_v1_parse_connect_close_frame,
.pf_gen_ping_frame = ietf_v1_gen_ping_frame,
.pf_parse_frame_type = ietf_v1_parse_frame_type,
.pf_turn_on_fin = ietf_v1_turn_on_fin,
.pf_packout_size = ietf_v1_packout_size,
.pf_packout_max_header_size = ietf_v1_packout_max_header_size,
.pf_path_chal_frame_size = ietf_v1_path_chal_frame_size,
.pf_parse_path_chal_frame = ietf_v1_parse_path_chal_frame,
.pf_gen_path_chal_frame = ietf_v1_gen_path_chal_frame,
.pf_path_resp_frame_size = ietf_v1_path_resp_frame_size,
.pf_gen_path_resp_frame = ietf_v1_gen_path_resp_frame,
.pf_parse_path_resp_frame = ietf_v1_parse_path_resp_frame,
.pf_calc_packno_bits = ietf_v1_calc_packno_bits,
.pf_packno_bits2len = ietf_v1_packno_bits2len,
.pf_gen_crypto_frame = ietf_v1_gen_crypto_frame,
.pf_parse_crypto_frame = ietf_v1_parse_crypto_frame,
.pf_calc_crypto_frame_header_sz = ietf_v1_calc_crypto_frame_header_sz,
.pf_parse_max_data = ietf_v1_parse_max_data,
.pf_gen_max_data_frame = ietf_v1_gen_max_data_frame,
.pf_max_data_frame_size = ietf_v1_max_data_frame_size,
.pf_parse_new_conn_id = ietf_v1_parse_new_conn_id,
.pf_gen_stream_blocked_frame = ietf_v1_gen_stream_blocked_frame,
.pf_parse_stream_blocked_frame = ietf_v1_parse_stream_blocked_frame,
.pf_stream_blocked_frame_size = ietf_v1_stream_blocked_frame_size,
.pf_gen_max_stream_data_frame = ietf_v1_gen_max_stream_data_frame,
.pf_parse_max_stream_data_frame = ietf_v1_parse_max_stream_data_frame,
.pf_max_stream_data_frame_size = ietf_v1_max_stream_data_frame_size,
.pf_parse_stop_sending_frame = ietf_v1_parse_stop_sending_frame,
.pf_gen_stop_sending_frame = ietf_v1_gen_stop_sending_frame,
.pf_stop_sending_frame_size = ietf_v1_stop_sending_frame_size,
.pf_parse_new_token_frame = ietf_v1_parse_new_token_frame,
.pf_new_connection_id_frame_size = ietf_v1_new_connection_id_frame_size,
.pf_gen_new_connection_id_frame = ietf_v1_gen_new_connection_id_frame,
.pf_new_token_frame_size = ietf_v1_new_token_frame_size,
.pf_gen_new_token_frame = ietf_v1_gen_new_token_frame,
.pf_parse_retire_cid_frame = ietf_v1_parse_retire_cid_frame,
.pf_gen_retire_cid_frame = ietf_v1_gen_retire_cid_frame,
.pf_retire_cid_frame_size = ietf_v1_retire_cid_frame_size,
.pf_gen_streams_blocked_frame = ietf_v1_gen_streams_blocked_frame,
.pf_parse_streams_blocked_frame = ietf_v1_parse_streams_blocked_frame,
.pf_streams_blocked_frame_size = ietf_v1_streams_blocked_frame_size,
.pf_gen_max_streams_frame = ietf_v1_gen_max_streams_frame,
.pf_parse_max_streams_frame = ietf_v1_parse_max_streams_frame,
.pf_max_streams_frame_size = ietf_v1_max_streams_frame_size,
.pf_gen_handshake_done_frame = ietf_v1_gen_handshake_done_frame,
.pf_parse_handshake_done_frame = ietf_v1_parse_handshake_done_frame,
.pf_handshake_done_frame_size = ietf_v1_handshake_done_frame_size,
.pf_gen_ack_frequency_frame = ietf_v1_gen_ack_frequency_frame,
.pf_parse_ack_frequency_frame = ietf_v1_parse_ack_frequency_frame,
.pf_ack_frequency_frame_size = ietf_v1_ack_frequency_frame_size,
.pf_gen_timestamp_frame = ietf_v1_gen_timestamp_frame,
.pf_parse_timestamp_frame = ietf_v1_parse_timestamp_frame,
.pf_parse_datagram_frame = ietf_v1_parse_datagram_frame,
.pf_gen_datagram_frame = ietf_v1_gen_datagram_frame,
.pf_datagram_frame_size = ietf_v1_datagram_frame_size,
};