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_iquic_common.c
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
/*
* Parsing routines shared by all IETF QUIC versions.
*/
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <openssl/rand.h>
#include <openssl/aead.h>
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_in.h"
#include "lsquic_parse_common.h"
#include "lsquic_parse.h"
#include "lsquic_version.h"
#include "lsquic.h"
#include "lsquic_logger.h"
#include "lsquic_byteswap.h"
#include "lsquic_varint.h"
#include "lsquic_enc_sess.h"
#include "lsquic_tokgen.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_ietf.h"
/* [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,
};
int
lsquic_Q046_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;
lsquic_ver_tag_t tag;
enum header_type header_type;
unsigned dcil, scil, packet_len;
int verneg;
unsigned char first_byte;
lsquic_packno_t packno;
if (length < 6)
return -1;
first_byte = *p++;
memcpy(&tag, p, 4);
p += 4;
verneg = 0 == tag;
if (!verneg)
header_type = bits2ht[ (first_byte >> 4) & 3 ];
else
header_type = HETY_VERNEG;
packet_in->pi_header_type = header_type;
dcil = p[0] >> 4;
if (dcil)
dcil += 3;
scil = p[0] & 0xF;
if (scil)
scil += 3;
++p;
/* Chromium comments state that the client sends packets with destination
* CID of 8 bytes and source CID of 0 bytes and the server does it the
* other way around.
*/
if (is_server)
{
if (!(dcil == cid_len && scil == 0))
return -1;
}
else
if (!(dcil == 0 && scil == cid_len))
return -1;
if (!verneg)
{
packet_in->pi_flags |= (first_byte & 3) << PIBIT_BITS_SHIFT;
packet_len = 1 + (first_byte & 3);
if (end - p < (ptrdiff_t) (dcil + scil + packet_len))
return -1;
}
else
{
/* Need at least one version in the version array: add 4 */
if (end - p < (ptrdiff_t) (dcil + scil + 4))
return -1;
#ifdef WIN32
/* Useless initialization: */
packet_len = 0;
#endif
}
memcpy(&packet_in->pi_dcid.idbuf, p, cid_len);
packet_in->pi_dcid.len = cid_len;
p += cid_len;
packet_in->pi_flags |= PI_CONN_ID;
if (!verneg)
{
READ_UINT(packno, 64, p, packet_len);
packet_in->pi_packno = packno;
p += packet_len;
packet_in->pi_quic_ver = 1;
if (is_server || HETY_0RTT != header_type)
packet_in->pi_nonce = 0;
else
{
packet_in->pi_nonce = p - packet_in->pi_data;
p += 32;
}
}
else
{
if (p >= end || (3 & (uintptr_t) (end - p)))
return -1;
packet_in->pi_quic_ver = p - packet_in->pi_data;
p = end;
}
packet_in->pi_header_sz = p - packet_in->pi_data;
packet_in->pi_frame_types = 0;
packet_in->pi_data_sz = length;
packet_in->pi_refcnt = 0;
packet_in->pi_received = 0;
return 0;
}
int
lsquic_Q046_parse_packet_in_short_begin (lsquic_packet_in_t *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 pend = packet_in->pi_data + length;
unsigned packet_len, header_len;
lsquic_packno_t packno;
if (*p & 0x40) /* Q046 and higher */
packet_len = 1 + (*p & 3);
else
return -1;
if (is_server)
header_len = 1 + cid_len + packet_len;
else
header_len = 1 + packet_len;
if (pend - p < (ptrdiff_t) header_len)
return -1;
packet_in->pi_flags |= (*p & 3) << PIBIT_BITS_SHIFT;
++p;
if (is_server)
{
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;
p += cid_len;
}
READ_UINT(packno, 64, p, packet_len);
packet_in->pi_packno = packno;
p += packet_len;
packet_in->pi_header_type = HETY_SHORT;
packet_in->pi_quic_ver = 0;
packet_in->pi_nonce = 0;
packet_in->pi_header_sz = p - packet_in->pi_data;
packet_in->pi_frame_types = 0;
packet_in->pi_data_sz = length;
packet_in->pi_refcnt = 0;
packet_in->pi_received = 0;
return 0;
}
/* This is a bare-bones version of lsquic_Q046_parse_packet_in_long_begin()
*/
int
lsquic_is_valid_iquic_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;
unsigned dcil, scil, packet_len;
unsigned char first_byte;
const unsigned cid_len = 8;
if (length < 6)
return 0;
first_byte = *p++;
memcpy(&tag, p, 4);
p += 4;
if (0 == tag)
return 0; /* Client never sends version negotiation */
dcil = p[0] >> 4;
if (dcil)
dcil += 3;
scil = p[0] & 0xF;
if (scil)
scil += 3;
++p;
if (!(dcil == cid_len && scil == 0))
return 0;
packet_len = first_byte & 3;
if (end - p >= (ptrdiff_t) (dcil + scil + packet_len))
{
*tagp = tag;
return 1;
}
else
return 0;
}
const enum quic_frame_type lsquic_iquic_byte2type[0x40] =
{
[0x00] = QUIC_FRAME_PADDING,
[0x01] = QUIC_FRAME_PING,
[0x02] = QUIC_FRAME_ACK,
[0x03] = QUIC_FRAME_ACK,
[0x04] = QUIC_FRAME_RST_STREAM,
[0x05] = QUIC_FRAME_STOP_SENDING,
[0x06] = QUIC_FRAME_CRYPTO,
[0x07] = QUIC_FRAME_NEW_TOKEN,
[0x08] = QUIC_FRAME_STREAM,
[0x09] = QUIC_FRAME_STREAM,
[0x0A] = QUIC_FRAME_STREAM,
[0x0B] = QUIC_FRAME_STREAM,
[0x0C] = QUIC_FRAME_STREAM,
[0x0D] = QUIC_FRAME_STREAM,
[0x0E] = QUIC_FRAME_STREAM,
[0x0F] = QUIC_FRAME_STREAM,
[0x10] = QUIC_FRAME_MAX_DATA,
[0x11] = QUIC_FRAME_MAX_STREAM_DATA,
[0x12] = QUIC_FRAME_MAX_STREAMS,
[0x13] = QUIC_FRAME_MAX_STREAMS,
[0x14] = QUIC_FRAME_BLOCKED,
[0x15] = QUIC_FRAME_STREAM_BLOCKED,
[0x16] = QUIC_FRAME_STREAMS_BLOCKED,
[0x17] = QUIC_FRAME_STREAMS_BLOCKED,
[0x18] = QUIC_FRAME_NEW_CONNECTION_ID,
[0x19] = QUIC_FRAME_RETIRE_CONNECTION_ID,
[0x1A] = QUIC_FRAME_PATH_CHALLENGE,
[0x1B] = QUIC_FRAME_PATH_RESPONSE,
[0x1C] = QUIC_FRAME_CONNECTION_CLOSE,
[0x1D] = QUIC_FRAME_CONNECTION_CLOSE,
[0x1E] = QUIC_FRAME_HANDSHAKE_DONE,
[0x1F] = QUIC_FRAME_INVALID,
[0x20] = QUIC_FRAME_INVALID,
[0x21] = QUIC_FRAME_INVALID,
[0x22] = QUIC_FRAME_INVALID,
[0x23] = QUIC_FRAME_INVALID,
[0x24] = QUIC_FRAME_INVALID,
[0x25] = QUIC_FRAME_INVALID,
[0x26] = QUIC_FRAME_INVALID,
[0x27] = QUIC_FRAME_INVALID,
[0x28] = QUIC_FRAME_INVALID,
[0x29] = QUIC_FRAME_INVALID,
[0x2A] = QUIC_FRAME_INVALID,
[0x2B] = QUIC_FRAME_INVALID,
[0x2C] = QUIC_FRAME_INVALID,
[0x2D] = QUIC_FRAME_INVALID,
[0x2E] = QUIC_FRAME_INVALID,
[0x2F] = QUIC_FRAME_INVALID,
[0x30] = QUIC_FRAME_DATAGRAM,
[0x31] = QUIC_FRAME_DATAGRAM,
[0x32] = QUIC_FRAME_INVALID,
[0x33] = QUIC_FRAME_INVALID,
[0x34] = QUIC_FRAME_INVALID,
[0x35] = QUIC_FRAME_INVALID,
[0x36] = QUIC_FRAME_INVALID,
[0x37] = QUIC_FRAME_INVALID,
[0x38] = QUIC_FRAME_INVALID,
[0x39] = QUIC_FRAME_INVALID,
[0x3A] = QUIC_FRAME_INVALID,
[0x3B] = QUIC_FRAME_INVALID,
[0x3C] = QUIC_FRAME_INVALID,
[0x3D] = QUIC_FRAME_INVALID,
[0x3E] = QUIC_FRAME_INVALID,
[0x3F] = QUIC_FRAME_INVALID,
};
#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_Q046_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)
{
unsigned slen, dlen;
size_t need;
int r;
need = 1 /* Type */ + 4 /* Packet number */ + 1 /* SCIL */
+ scid->len + dcid->len + popcount(versions) * 4;
if (need > bufsz)
return -1;
*buf++ = 0x80 | 0x40 | rand;
memset(buf, 0, 4);
buf += 4;
/* From [draft-ietf-quic-transport-11], Section 4.3:
*
* 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.
*/
dlen = dcid->len;
if (dlen)
dlen -= 3;
slen = scid->len;
if (slen)
slen -= 3;
*buf++ = (dlen << 4) | slen;
memcpy(buf, dcid->idbuf, dcid->len);
buf += dcid->len;
memcpy(buf, scid->idbuf, scid->len);
buf += scid->len;
r = lsquic_gen_ver_tags(buf, bufsz - 1 - 4 - 1 - dcid->len - scid->len,
versions);
if (r < 0)
return -1;
assert((unsigned) r == popcount(versions) * 4u);
return need;
}
int
lsquic_iquic_gen_retry_pkt (unsigned char *buf, size_t bufsz,
const struct lsquic_engine_public *enpub,
const lsquic_cid_t *scid, const lsquic_cid_t *dcid,
enum lsquic_version version, const struct sockaddr *sockaddr,
uint8_t random_nybble)
{
struct token_generator *const tokgen = enpub->enp_tokgen;
const unsigned our_scid_len = enpub->enp_settings.es_scid_len;
unsigned char *const end = buf + bufsz;
unsigned char *p = buf;
lsquic_ver_tag_t ver_tag;
size_t ad_len, out_len;
unsigned ret_ver;
ssize_t sz;
#define INTEGRITY_TAG_LEN 16
/* [draft-ietf-quic-tls-25] Section 5.8 specifies the layout of the
* Retry Pseudo-Packet:
*/
unsigned char ad_buf[
1 + MAX_CID_LEN /* ODCID */
+ 1 + 4 /* Type and version */
+ 1 + MAX_CID_LEN /* DCID */
+ 1 + MAX_CID_LEN /* SCID */
+ MAX_RETRY_TOKEN_LEN /* Retry token */
];
unsigned char tag[INTEGRITY_TAG_LEN];
/* See [draft-ietf-quic-transport-25], Section 17.2.5 */
if (bufsz < 1 + sizeof(ver_tag) + 1 + our_scid_len + 1 + dcid->len
+ MAX_RETRY_TOKEN_LEN + INTEGRITY_TAG_LEN)
return -1;
p = ad_buf;
*p++ = dcid->len;
memcpy(p, dcid->idbuf, dcid->len);
p += dcid->len;
*p++ = 0xC0
| (3 << 4)
| random_nybble
;
ver_tag = lsquic_ver2tag(version);
memcpy(p, &ver_tag, sizeof(ver_tag));
p += sizeof(ver_tag);
*p++ = scid->len;
memcpy(p, scid->idbuf, scid->len);
p += scid->len;
*p++ = our_scid_len;
RAND_bytes(p, our_scid_len);
p += our_scid_len;
sz = lsquic_tg_generate_retry(tokgen, p, end - p,
p - our_scid_len, our_scid_len, sockaddr, dcid);
if (sz < 0)
return -1;
p += sz;
ad_len = p - ad_buf;
ret_ver = lsquic_version_2_retryver(version);
out_len = sizeof(tag);
if (!(1 == EVP_AEAD_CTX_seal(&enpub->enp_retry_aead_ctx[ret_ver], tag,
&out_len, out_len, lsquic_retry_nonce_buf[ret_ver],
IETF_RETRY_NONCE_SZ,
NULL, 0, ad_buf, ad_len) && out_len == sizeof(tag)))
return -1;
memcpy(buf, ad_buf + 1 + dcid->len, ad_len - 1 - dcid->len);
memcpy(buf + ad_len - 1 - dcid->len, tag, sizeof(tag));
return ad_len - 1 - dcid->len + sizeof(tag);
}