Packages
hackney
3.0.1
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_trans_params.c
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_trans_params.c
*/
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#ifndef WIN32
#include <arpa/inet.h>
#include <sys/socket.h>
#else
#include "vc_compat.h"
#include "Ws2tcpip.h"
#endif
#include "lsquic.h"
#include "lsquic_byteswap.h"
#include "lsquic_int_types.h"
#include "lsquic_types.h"
#include "lsquic_version.h"
#include "lsquic_sizes.h"
#include "lsquic_trans_params.h"
#include "lsquic_util.h"
#include "lsquic_varint.h"
#define LSQUIC_LOGGER_MODULE LSQLM_TRAPA
#include "lsquic_logger.h"
static enum transport_param_id
tpi_val_2_enum (uint64_t tpi_val)
{
switch (tpi_val)
{
case 0: return TPI_ORIGINAL_DEST_CID;
case 1: return TPI_MAX_IDLE_TIMEOUT;
case 2: return TPI_STATELESS_RESET_TOKEN;
case 3: return TPI_MAX_UDP_PAYLOAD_SIZE;
case 4: return TPI_INIT_MAX_DATA;
case 5: return TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL;
case 6: return TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE;
case 7: return TPI_INIT_MAX_STREAM_DATA_UNI;
case 8: return TPI_INIT_MAX_STREAMS_BIDI;
case 9: return TPI_INIT_MAX_STREAMS_UNI;
case 10: return TPI_ACK_DELAY_EXPONENT;
case 11: return TPI_MAX_ACK_DELAY;
case 12: return TPI_DISABLE_ACTIVE_MIGRATION;
case 13: return TPI_PREFERRED_ADDRESS;
case 14: return TPI_ACTIVE_CONNECTION_ID_LIMIT;
case 15: return TPI_INITIAL_SOURCE_CID;
case 16: return TPI_RETRY_SOURCE_CID;
case 17: return TPI_VERSION_INFORMATION;
case 0x20: return TPI_MAX_DATAGRAM_FRAME_SIZE;
#if LSQUIC_TEST_QUANTUM_READINESS
case 0xC37: return TPI_QUANTUM_READINESS;
#endif
case 0x1057: return TPI_LOSS_BITS;
case 0x2AB2: return TPI_GREASE_QUIC_BIT;
case 0xDE1A: return TPI_MIN_ACK_DELAY;
case 0xFF02DE1A:return TPI_MIN_ACK_DELAY_02;
case 0x7158: return TPI_TIMESTAMPS;
default: return INT_MAX;
}
}
static const unsigned enum_2_tpi_val[LAST_TPI + 1] =
{
[TPI_ORIGINAL_DEST_CID] = 0x0,
[TPI_MAX_IDLE_TIMEOUT] = 0x1,
[TPI_STATELESS_RESET_TOKEN] = 0x2,
[TPI_MAX_UDP_PAYLOAD_SIZE] = 0x3,
[TPI_INIT_MAX_DATA] = 0x4,
[TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL] = 0x5,
[TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE] = 0x6,
[TPI_INIT_MAX_STREAM_DATA_UNI] = 0x7,
[TPI_INIT_MAX_STREAMS_BIDI] = 0x8,
[TPI_INIT_MAX_STREAMS_UNI] = 0x9,
[TPI_ACK_DELAY_EXPONENT] = 0xA,
[TPI_MAX_ACK_DELAY] = 0xB,
[TPI_DISABLE_ACTIVE_MIGRATION] = 0xC,
[TPI_PREFERRED_ADDRESS] = 0xD,
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = 0xE,
[TPI_INITIAL_SOURCE_CID] = 0xF,
[TPI_RETRY_SOURCE_CID] = 0x10,
[TPI_VERSION_INFORMATION] = 0x11,
[TPI_MAX_DATAGRAM_FRAME_SIZE] = 0x20,
#if LSQUIC_TEST_QUANTUM_READINESS
[TPI_QUANTUM_READINESS] = 0xC37,
#endif
[TPI_LOSS_BITS] = 0x1057,
[TPI_MIN_ACK_DELAY] = 0xDE1A,
[TPI_MIN_ACK_DELAY_02] = 0xFF02DE1A,
[TPI_TIMESTAMPS] = 0x7158,
[TPI_GREASE_QUIC_BIT] = 0x2AB2,
};
const char * const lsquic_tpi2str[LAST_TPI + 1] =
{
[TPI_ORIGINAL_DEST_CID] = "original_destination_connection_id",
[TPI_MAX_IDLE_TIMEOUT] = "max_idle_timeout",
[TPI_STATELESS_RESET_TOKEN] = "stateless_reset_token",
[TPI_MAX_UDP_PAYLOAD_SIZE] = "max_udp_payload_size",
[TPI_INIT_MAX_DATA] = "init_max_data",
[TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL] = "init_max_stream_data_bidi_local",
[TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE] = "init_max_stream_data_bidi_remote",
[TPI_INIT_MAX_STREAM_DATA_UNI] = "init_max_stream_data_uni",
[TPI_INIT_MAX_STREAMS_BIDI] = "init_max_streams_bidi",
[TPI_INIT_MAX_STREAMS_UNI] = "init_max_streams_uni",
[TPI_ACK_DELAY_EXPONENT] = "ack_delay_exponent",
[TPI_MAX_ACK_DELAY] = "max_ack_delay",
[TPI_DISABLE_ACTIVE_MIGRATION] = "disable_active_migration",
[TPI_PREFERRED_ADDRESS] = "preferred_address",
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = "active_connection_id_limit",
[TPI_INITIAL_SOURCE_CID] = "initial_source_connection_id",
[TPI_RETRY_SOURCE_CID] = "retry_source_connection_id",
[TPI_MAX_DATAGRAM_FRAME_SIZE] = "max_datagram_frame_size",
#if LSQUIC_TEST_QUANTUM_READINESS
[TPI_QUANTUM_READINESS] = "quantum_readiness",
#endif
[TPI_LOSS_BITS] = "loss_bits",
[TPI_MIN_ACK_DELAY] = "min_ack_delay",
[TPI_MIN_ACK_DELAY_02] = "min_ack_delay_02",
[TPI_TIMESTAMPS] = "timestamps",
[TPI_GREASE_QUIC_BIT] = "grease_quic_bit",
};
#define tpi2str lsquic_tpi2str
static const uint64_t def_vals[MAX_NUM_WITH_DEF_TPI + 1] =
{
[TPI_MAX_UDP_PAYLOAD_SIZE] = TP_DEF_MAX_UDP_PAYLOAD_SIZE,
[TPI_ACK_DELAY_EXPONENT] = TP_DEF_ACK_DELAY_EXP,
[TPI_INIT_MAX_STREAMS_UNI] = TP_DEF_INIT_MAX_STREAMS_UNI,
[TPI_INIT_MAX_STREAMS_BIDI] = TP_DEF_INIT_MAX_STREAMS_BIDI,
[TPI_INIT_MAX_DATA] = TP_DEF_INIT_MAX_DATA,
[TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL] = TP_DEF_INIT_MAX_STREAM_DATA_BIDI_LOCAL,
[TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE] = TP_DEF_INIT_MAX_STREAM_DATA_BIDI_REMOTE,
[TPI_INIT_MAX_STREAM_DATA_UNI] = TP_DEF_INIT_MAX_STREAM_DATA_UNI,
[TPI_MAX_IDLE_TIMEOUT] = TP_DEF_MAX_IDLE_TIMEOUT,
[TPI_MAX_ACK_DELAY] = TP_DEF_MAX_ACK_DELAY,
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = TP_DEF_ACTIVE_CONNECTION_ID_LIMIT,
};
static const uint64_t max_vals[MAX_NUMERIC_TPI + 1] =
{
/* We don't enforce the maximum practical UDP payload value of 65527, as
* it is not required by the spec and is not necessary.
*/
[TPI_MAX_UDP_PAYLOAD_SIZE] = VINT_MAX_VALUE,
[TPI_ACK_DELAY_EXPONENT] = TP_MAX_ACK_DELAY_EXP,
[TPI_INIT_MAX_STREAMS_UNI] = 1ull << 60,
[TPI_INIT_MAX_STREAMS_BIDI] = 1ull << 60,
[TPI_INIT_MAX_DATA] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE] = VINT_MAX_VALUE,
[TPI_INIT_MAX_STREAM_DATA_UNI] = VINT_MAX_VALUE,
[TPI_MAX_IDLE_TIMEOUT] = VINT_MAX_VALUE,
[TPI_MAX_ACK_DELAY] = TP_MAX_MAX_ACK_DELAY,
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = VINT_MAX_VALUE,
[TPI_LOSS_BITS] = 1,
[TPI_MIN_ACK_DELAY] = (1u << 24) - 1u,
[TPI_MIN_ACK_DELAY_02] = (1u << 24) - 1u,
[TPI_TIMESTAMPS] = TS_WANT_THEM|TS_GENERATE_THEM,
[TPI_MAX_DATAGRAM_FRAME_SIZE] = VINT_MAX_VALUE,
};
static const uint64_t min_vals[MAX_NUMERIC_TPI + 1] =
{
/* On the other hand, we do enforce the lower bound. */
[TPI_MAX_UDP_PAYLOAD_SIZE] = 1200,
[TPI_MIN_ACK_DELAY] = 1,
[TPI_MIN_ACK_DELAY_02] = 1,
[TPI_ACTIVE_CONNECTION_ID_LIMIT] = 2,
[TPI_TIMESTAMPS] = TS_WANT_THEM,
};
static size_t
preferred_address_size (const struct transport_params *params)
{
return sizeof(params->tp_preferred_address.ipv4_addr)
+ sizeof(params->tp_preferred_address.ipv4_port)
+ sizeof(params->tp_preferred_address.ipv6_addr)
+ sizeof(params->tp_preferred_address.ipv6_port)
+ 1 + params->tp_preferred_address.cid.len
+ sizeof(params->tp_preferred_address.srst)
;
}
int
lsquic_tp_has_pref_ipv4 (const struct transport_params *params)
{
return (params->tp_set & (1 << TPI_PREFERRED_ADDRESS))
&& params->tp_preferred_address.ipv4_port
&& !lsquic_is_zero(params->tp_preferred_address.ipv4_addr,
sizeof(params->tp_preferred_address.ipv4_addr));
}
int
lsquic_tp_has_pref_ipv6 (const struct transport_params *params)
{
return (params->tp_set & (1 << TPI_PREFERRED_ADDRESS))
&& params->tp_preferred_address.ipv6_port
&& !lsquic_is_zero(params->tp_preferred_address.ipv6_addr,
sizeof(params->tp_preferred_address.ipv6_addr));
}
#if LSQUIC_TEST_QUANTUM_READINESS
#include <stdlib.h>
size_t
lsquic_tp_get_quantum_sz (void)
{
const char *str;
str = getenv("LSQUIC_QUANTUM_SZ");
if (str)
return atoi(str);
else
/* https://github.com/quicwg/base-drafts/wiki/Quantum-Readiness-test */
return 1200;
}
#endif
static size_t
update_cid_bits (unsigned bits[][3], enum transport_param_id tpi,
const lsquic_cid_t *cid)
{
bits[tpi][0] = vint_val2bits(enum_2_tpi_val[tpi]);
#if __GNUC__
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#if __clang__
#pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#else
#pragma GCC diagnostic ignored "-Wtype-limits"
#endif
#endif
bits[tpi][1] = vint_val2bits(cid->len);
#if __GNUC__
#pragma GCC diagnostic pop
#pragma GCC diagnostic pop
#endif
return (1u << bits[tpi][0]) + (1u << bits[tpi][1]) + cid->len;
}
int
lsquic_tp_encode (const struct transport_params *params, int is_server,
unsigned char *const buf, size_t bufsz)
{
unsigned char *p;
size_t need;
uint16_t u16;
enum transport_param_id tpi;
unsigned set;
unsigned bits[LAST_TPI + 1][3 /* ID, length, value */];
#if LSQUIC_TEST_QUANTUM_READINESS
const size_t quantum_sz = lsquic_tp_get_quantum_sz();
#endif
lsquic_ver_tag_t tag;
int i;
need = 0;
set = params->tp_set; /* Will turn bits off for default values */
if (set & (1 << TPI_INITIAL_SOURCE_CID))
need += update_cid_bits(bits, TPI_INITIAL_SOURCE_CID,
¶ms->tp_initial_source_cid);
if (is_server)
{
if (set & (1 << TPI_ORIGINAL_DEST_CID))
need += update_cid_bits(bits, TPI_ORIGINAL_DEST_CID,
¶ms->tp_original_dest_cid);
if (set & (1 << TPI_RETRY_SOURCE_CID))
need += update_cid_bits(bits, TPI_RETRY_SOURCE_CID,
¶ms->tp_retry_source_cid);
if (set & (1 << TPI_STATELESS_RESET_TOKEN))
{
bits[TPI_STATELESS_RESET_TOKEN][0]
= vint_val2bits(enum_2_tpi_val[TPI_STATELESS_RESET_TOKEN]);
bits[TPI_STATELESS_RESET_TOKEN][1]
= vint_val2bits(sizeof(params->tp_stateless_reset_token));
need += (1 << bits[TPI_STATELESS_RESET_TOKEN][0])
+ (1 << bits[TPI_STATELESS_RESET_TOKEN][1])
+ sizeof(params->tp_stateless_reset_token);
}
if (set & (1 << TPI_PREFERRED_ADDRESS))
{
bits[TPI_PREFERRED_ADDRESS][0]
= vint_val2bits(enum_2_tpi_val[TPI_PREFERRED_ADDRESS]);
bits[TPI_PREFERRED_ADDRESS][1] = vint_val2bits(
preferred_address_size(params));
need += (1 << bits[TPI_PREFERRED_ADDRESS][0])
+ (1 << bits[TPI_PREFERRED_ADDRESS][1])
+ preferred_address_size(params);
}
}
#if LSQUIC_TEST_QUANTUM_READINESS
if (set & (1 << TPI_QUANTUM_READINESS))
{
bits[TPI_QUANTUM_READINESS][0]
= vint_val2bits(enum_2_tpi_val[TPI_QUANTUM_READINESS]);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(quantum_sz);
need += (1 << bits[TPI_QUANTUM_READINESS][0])
+ (1 << bits[TPI_QUANTUM_READINESS][1])
+ quantum_sz;
}
#endif
for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi)
if (set & (1 << tpi))
{
if (tpi > MAX_NUM_WITH_DEF_TPI
|| params->tp_numerics[tpi] != def_vals[tpi])
{
if (params->tp_numerics[tpi] >= min_vals[tpi]
&& params->tp_numerics[tpi] <= max_vals[tpi])
{
bits[tpi][0] = vint_val2bits(enum_2_tpi_val[tpi]);
bits[tpi][2] = vint_val2bits(params->tp_numerics[tpi]);
bits[tpi][1] = vint_val2bits(bits[tpi][2]);
need += (1 << bits[tpi][0])
+ (1 << bits[tpi][1])
+ (1 << bits[tpi][2]);
}
else if (params->tp_numerics[tpi] > max_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too large (%"PRIu64" vs "
"maximum of %"PRIu64")", tpi2str[tpi],
params->tp_numerics[tpi], max_vals[tpi]);
return -1;
}
else
{
LSQ_DEBUG("numeric value of %s is too small (%"PRIu64" vs "
"minimum " "of %"PRIu64")",
tpi2str[tpi], params->tp_numerics[tpi], min_vals[tpi]);
return -1;
}
}
else
set &= ~(1 << tpi); /* Don't write default value */
}
for (; tpi <= MAX_EMPTY_TPI; ++tpi)
if (set & (1 << tpi))
{
bits[tpi][0] = vint_val2bits(enum_2_tpi_val[tpi]);
need += (1 << bits[tpi][0]) + 1 /* Zero length byte */;
}
if (set & (1 << TPI_VERSION_INFORMATION))
{
tpi = TPI_VERSION_INFORMATION;
bits[tpi][0] = vint_val2bits(enum_2_tpi_val[tpi]);
bits[tpi][1] = vint_val2bits(params->tp_version_cnt << 2);
need += (1 << bits[tpi][0])
+ (1 << bits[tpi][1])
+ (params->tp_version_cnt << 2);
}
if (need > bufsz || need > UINT16_MAX)
{
errno = ENOBUFS;
return -1;
}
p = buf;
#define WRITE_TO_P(src, len) do { \
memcpy(p, src, len); \
p += len; \
} while (0)
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define WRITE_UINT_TO_P(val, width) do { \
u##width = bswap_##width(val); \
WRITE_TO_P(&u##width, sizeof(u##width)); \
} while (0)
#else
#define WRITE_UINT_TO_P(val, width) do { \
u##width = val; \
WRITE_TO_P(&u##width, sizeof(u##width)); \
} while (0)
#endif
for (tpi = 0; tpi <= LAST_TPI; ++tpi)
if (set & (1 << tpi))
{
vint_write(p, enum_2_tpi_val[tpi], bits[tpi][0],
1 << bits[tpi][0]);
p += 1 << bits[tpi][0];
switch (tpi)
{
case TPI_MAX_IDLE_TIMEOUT:
case TPI_MAX_UDP_PAYLOAD_SIZE:
case TPI_INIT_MAX_DATA:
case TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL:
case TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE:
case TPI_INIT_MAX_STREAM_DATA_UNI:
case TPI_INIT_MAX_STREAMS_BIDI:
case TPI_INIT_MAX_STREAMS_UNI:
case TPI_ACK_DELAY_EXPONENT:
case TPI_MAX_ACK_DELAY:
case TPI_ACTIVE_CONNECTION_ID_LIMIT:
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_MIN_ACK_DELAY_02:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
vint_write(p, 1 << bits[tpi][2], bits[tpi][1],
1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
vint_write(p, params->tp_numerics[tpi], bits[tpi][2],
1 << bits[tpi][2]);
p += 1 << bits[tpi][2];
break;
case TPI_ORIGINAL_DEST_CID:
case TPI_INITIAL_SOURCE_CID:
case TPI_RETRY_SOURCE_CID:
vint_write(p, params->tp_cids[TP_CID_IDX(tpi)].len, bits[tpi][1],
1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
WRITE_TO_P(params->tp_cids[TP_CID_IDX(tpi)].idbuf,
params->tp_cids[TP_CID_IDX(tpi)].len);
break;
case TPI_STATELESS_RESET_TOKEN:
vint_write(p, sizeof(params->tp_stateless_reset_token),
bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
WRITE_TO_P(params->tp_stateless_reset_token,
sizeof(params->tp_stateless_reset_token));
break;
case TPI_PREFERRED_ADDRESS:
vint_write(p, preferred_address_size(params),
bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
WRITE_TO_P(¶ms->tp_preferred_address.ipv4_addr,
sizeof(params->tp_preferred_address.ipv4_addr));
WRITE_UINT_TO_P(params->tp_preferred_address.ipv4_port, 16);
WRITE_TO_P(¶ms->tp_preferred_address.ipv6_addr,
sizeof(params->tp_preferred_address.ipv6_addr));
WRITE_UINT_TO_P(params->tp_preferred_address.ipv6_port, 16);
*p++ = params->tp_preferred_address.cid.len;
WRITE_TO_P(params->tp_preferred_address.cid.idbuf,
params->tp_preferred_address.cid.len);
WRITE_TO_P(params->tp_preferred_address.srst,
sizeof(params->tp_preferred_address.srst));
break;
case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_GREASE_QUIC_BIT:
*p++ = 0;
break;
#if LSQUIC_TEST_QUANTUM_READINESS
case TPI_QUANTUM_READINESS:
LSQ_DEBUG("encoded %zd bytes of quantum readiness", quantum_sz);
vint_write(p, quantum_sz, bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
memset(p, 'Q', quantum_sz);
p += quantum_sz;
break;
#endif
case TPI_VERSION_INFORMATION:
//FIXME: generate supported version info.
vint_write(p, params->tp_version_cnt << 2,
bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
for(i = 0; i < params->tp_version_cnt; ++i)
{
tag = lsquic_ver2tag(params->tp_version_info[i]);
WRITE_TO_P(&tag, 4);
}
break;
}
}
assert(buf + need == p);
return (int) (p - buf);
#undef WRITE_TO_P
#undef WRITE_UINT_TO_P
}
int
lsquic_tp_decode (const unsigned char *const buf, size_t bufsz,
int is_server,
struct transport_params *params)
{
const unsigned char *p, *end, *q;
uint64_t len, param_id;
uint16_t tlen;
enum transport_param_id tpi;
unsigned set_of_ids;
int s;
lsquic_ver_tag_t tag;
p = buf;
end = buf + bufsz;
*params = TP_INITIALIZER();
#define EXPECT_LEN(expected_len) do { \
if (expected_len != len) \
return -1; \
} while (0)
#define EXPECT_AT_LEAST(expected_len) do { \
if ((expected_len) > (uintptr_t) (p + len - q)) \
return -1; \
} while (0)
set_of_ids = 0;
while (p < end)
{
s = vint_read(p, end, ¶m_id);
if (s < 0)
return -1;
LSQ_DEBUG("read TP 0x%"PRIX64, param_id);
p += s;
s = vint_read(p, end, &len);
if (s < 0)
return -1;
p += s;
if ((ptrdiff_t) len > end - p)
return -1;
tpi = tpi_val_2_enum(param_id);
if (tpi <= LAST_TPI)
{
if (set_of_ids & (1 << tpi))
return -1;
set_of_ids |= 1 << tpi;
}
switch (tpi)
{
case TPI_MAX_IDLE_TIMEOUT:
case TPI_MAX_UDP_PAYLOAD_SIZE:
case TPI_INIT_MAX_DATA:
case TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL:
case TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE:
case TPI_INIT_MAX_STREAM_DATA_UNI:
case TPI_INIT_MAX_STREAMS_BIDI:
case TPI_INIT_MAX_STREAMS_UNI:
case TPI_ACK_DELAY_EXPONENT:
case TPI_MAX_ACK_DELAY:
case TPI_ACTIVE_CONNECTION_ID_LIMIT:
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_MIN_ACK_DELAY_02:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
switch (len)
{
case 1:
case 2:
case 4:
case 8:
s = vint_read(p, p + len, ¶ms->tp_numerics[tpi]);
if (s == (int) len)
{
if (params->tp_numerics[tpi] > max_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too large "
"(%"PRIu64" vs maximum of %"PRIu64, tpi2str[tpi],
params->tp_numerics[tpi], max_vals[tpi]);
return -1;
}
else if (params->tp_numerics[tpi] < min_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too small "
"(%"PRIu64" vs minimum of %"PRIu64, tpi2str[tpi],
params->tp_numerics[tpi], min_vals[tpi]);
return -1;
}
break;
}
else
{
LSQ_DEBUG("cannot read the value of numeric transport "
"param %s of length %"PRIu64, tpi2str[tpi], len);
return -1;
}
default:
LSQ_DEBUG("invalid length=%"PRIu64" for numeric transport "
"parameter %s", len, tpi2str[tpi]);
return -1;
}
break;
case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_GREASE_QUIC_BIT:
EXPECT_LEN(0);
break;
case TPI_STATELESS_RESET_TOKEN:
/* Client MUST not include reset token,
* see [draft-ietf-quic-transport-11], Section 6.4.1
*/
if (!is_server)
return -1;
EXPECT_LEN(sizeof(params->tp_stateless_reset_token));
memcpy(params->tp_stateless_reset_token, p,
sizeof(params->tp_stateless_reset_token));
break;
case TPI_ORIGINAL_DEST_CID:
case TPI_RETRY_SOURCE_CID:
/* [draft-ietf-quic-transport-28] Section 18.2:
" A client MUST NOT include any server-only transport parameter:
" original_destination_connection_id, preferred_address,
" retry_source_connection_id, or stateless_reset_token.
*/
if (!is_server)
return -1;
/* fallthru */
case TPI_INITIAL_SOURCE_CID:
if (len > MAX_CID_LEN)
return -1;
memcpy(params->tp_cids[TP_CID_IDX(tpi)].idbuf, p, len);
params->tp_cids[TP_CID_IDX(tpi)].len = len;
break;
case TPI_PREFERRED_ADDRESS:
/* Client MUST not include preferred address,
* see [draft-ietf-quic-transport-12], Section 6.4.1
*/
if (!is_server)
return -1;
q = p;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_addr));
memcpy(params->tp_preferred_address.ipv4_addr, q,
sizeof(params->tp_preferred_address.ipv4_addr));
q += sizeof(params->tp_preferred_address.ipv4_addr);
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_port));
READ_UINT(params->tp_preferred_address.ipv4_port, 16, q, 2);
q += 2;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_addr));
memcpy(params->tp_preferred_address.ipv6_addr, q,
sizeof(params->tp_preferred_address.ipv6_addr));
q += sizeof(params->tp_preferred_address.ipv6_addr);
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_port));
READ_UINT(params->tp_preferred_address.ipv6_port, 16, q, 2);
q += 2;
EXPECT_AT_LEAST(1);
tlen = *q;
q += 1;
if (tlen > MAX_CID_LEN)
{
LSQ_DEBUG("preferred server address contains invalid "
"CID length of %"PRIu16" bytes", tlen);
return -1;
}
EXPECT_AT_LEAST(tlen);
memcpy(params->tp_preferred_address.cid.idbuf, q, tlen);
params->tp_preferred_address.cid.len = tlen;
q += tlen;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.srst));
memcpy(params->tp_preferred_address.srst, q,
sizeof(params->tp_preferred_address.srst));
q += sizeof(params->tp_preferred_address.srst);
if (q != p + len)
return -1;
break;
case TPI_VERSION_INFORMATION:
if (len & 0x3)
return -1;
q = p;
while(q < p + len)
{
memmove(&tag, q, 4);
if (tag == 0)
return -1;
int ver = lsquic_tag2ver(tag);
if (ver != -1)
{
if (params->tp_version_cnt > 0)
params->tp_versions |= 1 << ver;
if (params->tp_version_cnt < sizeof(params->tp_version_info))
params->tp_version_info[params->tp_version_cnt++] = ver;
}
else if (params->tp_version_cnt == 0)
return -1;
q += 4;
}
if (!is_server && (params->tp_versions
& (1 << params->tp_chosen_version)) == 0)
return -1;
break;
default:
/* Do nothing: skip this transport parameter */
break;
}
p += len;
if (tpi <= LAST_TPI)
{
params->tp_set |= 1 << tpi;
params->tp_decoded |= 1 << tpi;
}
}
if (p != end)
return -1;
if ((params->tp_set & (1 << TPI_MIN_ACK_DELAY))
&& params->tp_numerics[TPI_MIN_ACK_DELAY]
> params->tp_numerics[TPI_MAX_ACK_DELAY] * 1000)
{
LSQ_DEBUG("min_ack_delay (%"PRIu64" usec) is larger than "
"max_ack_delay (%"PRIu64" ms)",
params->tp_numerics[TPI_MIN_ACK_DELAY],
params->tp_numerics[TPI_MAX_ACK_DELAY]);
return -1;
}
if ((params->tp_set & (1 << TPI_MIN_ACK_DELAY_02))
&& params->tp_numerics[TPI_MIN_ACK_DELAY_02]
> params->tp_numerics[TPI_MAX_ACK_DELAY] * 1000)
{
LSQ_DEBUG("min_ack_delay_02 (%"PRIu64" usec) is larger than "
"max_ack_delay (%"PRIu64" ms)",
params->tp_numerics[TPI_MIN_ACK_DELAY_02],
params->tp_numerics[TPI_MAX_ACK_DELAY]);
return -1;
}
return (int) (end - buf);
#undef EXPECT_LEN
}
void
lsquic_tp_to_str (const struct transport_params *params, char *buf, size_t sz)
{
char *const end = buf + sz;
int nw;
enum transport_param_id tpi;
char tok_str[sizeof(params->tp_stateless_reset_token) * 2 + 1];
char addr_str[INET6_ADDRSTRLEN];
int i;
for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi)
if (params->tp_set & (1 << tpi))
{
nw = snprintf(buf, end - buf, "%.*s%s: %"PRIu64,
(buf + sz > end) << 1, "; ", tpi2str[tpi],
params->tp_numerics[tpi]);
buf += nw;
if (buf >= end)
return;
}
for (; tpi <= MAX_EMPTY_TPI; ++tpi)
if (params->tp_set & (1 << tpi))
{
nw = snprintf(buf, end - buf, "%.*s%s",
(buf + sz > end) << 1, "; ", tpi2str[tpi]);
buf += nw;
if (buf >= end)
return;
}
#if LSQUIC_TEST_QUANTUM_READINESS
if (params->tp_set & (1 << TPI_QUANTUM_READINESS))
{
nw = snprintf(buf, end - buf, "%.*s%s",
(buf + sz > end) << 1, "; ", tpi2str[TPI_QUANTUM_READINESS]);
buf += nw;
if (buf >= end)
return;
}
#endif
if (params->tp_set & (1 << TPI_STATELESS_RESET_TOKEN))
{
lsquic_hexstr(params->tp_stateless_reset_token,
sizeof(params->tp_stateless_reset_token), tok_str, sizeof(tok_str));
nw = snprintf(buf, end - buf, "; stateless_reset_token: %s", tok_str);
buf += nw;
if (buf >= end)
return;
}
for (tpi = FIRST_TP_CID; tpi <= LAST_TP_CID; ++tpi)
if (params->tp_set & (1 << tpi))
{
char cidbuf_[MAX_CID_LEN * 2 + 1];
nw = snprintf(buf, end - buf, "; %s: %"CID_FMT, tpi2str[tpi],
CID_BITS(¶ms->tp_cids[TP_CID_IDX(tpi)]));
buf += nw;
if (buf >= end)
return;
}
if (lsquic_tp_has_pref_ipv4(params))
{
if (inet_ntop(AF_INET, params->tp_preferred_address.ipv4_addr,
addr_str, sizeof(addr_str)))
{
nw = snprintf(buf, end - buf, "; IPv4 preferred address: %s:%u",
addr_str, params->tp_preferred_address.ipv4_port);
buf += nw;
if (buf >= end)
return;
}
}
if (lsquic_tp_has_pref_ipv6(params))
{
if (inet_ntop(AF_INET6, params->tp_preferred_address.ipv6_addr,
addr_str, sizeof(addr_str)))
{
nw = snprintf(buf, end - buf, "; IPv6 preferred address: %s:%u",
addr_str, params->tp_preferred_address.ipv6_port);
buf += nw;
if (buf >= end)
return;
}
}
if (params->tp_set & (1 << TPI_VERSION_INFORMATION))
{
nw = snprintf(buf, end - buf, "; version information: chosen: %s, available:",
lsquic_ver2str[params->tp_chosen_version]);
buf += nw;
if (buf >= end)
return;
for(i = 1; i < params->tp_version_cnt; ++i)
{
nw = snprintf(buf, end - buf, " %s",
lsquic_ver2str[params->tp_version_info[i]]);
buf += nw;
if (buf >= end)
return;
}
}
}
int
lsquic_tp_encode_27 (const struct transport_params *params, int is_server,
unsigned char *const buf, size_t bufsz)
{
unsigned char *p;
size_t need;
uint16_t u16;
enum transport_param_id tpi;
unsigned set;
unsigned bits[LAST_TPI + 1][3 /* ID, length, value */];
#if LSQUIC_TEST_QUANTUM_READINESS
const size_t quantum_sz = lsquic_tp_get_quantum_sz();
#endif
need = 0;
set = params->tp_set; /* Will turn bits off for default values */
if (is_server)
{
if (set & (1 << TPI_ORIGINAL_DEST_CID))
{
bits[TPI_ORIGINAL_DEST_CID][0]
= vint_val2bits(enum_2_tpi_val[TPI_ORIGINAL_DEST_CID]);
#if __GNUC__
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#if __clang__
#pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#else
#pragma GCC diagnostic ignored "-Wtype-limits"
#endif
#endif
bits[TPI_ORIGINAL_DEST_CID][1]
= vint_val2bits(params->tp_original_dest_cid.len);
#if __GNUC__
#pragma GCC diagnostic pop
#pragma GCC diagnostic pop
#endif
need += (1 << bits[TPI_ORIGINAL_DEST_CID][0])
+ (1 << bits[TPI_ORIGINAL_DEST_CID][1])
+ params->tp_original_dest_cid.len;
}
if (set & (1 << TPI_STATELESS_RESET_TOKEN))
{
bits[TPI_STATELESS_RESET_TOKEN][0]
= vint_val2bits(enum_2_tpi_val[TPI_STATELESS_RESET_TOKEN]);
bits[TPI_STATELESS_RESET_TOKEN][1]
= vint_val2bits(sizeof(params->tp_stateless_reset_token));
need += (1 << bits[TPI_STATELESS_RESET_TOKEN][0])
+ (1 << bits[TPI_STATELESS_RESET_TOKEN][1])
+ sizeof(params->tp_stateless_reset_token);
}
if (set & (1 << TPI_PREFERRED_ADDRESS))
{
bits[TPI_PREFERRED_ADDRESS][0]
= vint_val2bits(enum_2_tpi_val[TPI_PREFERRED_ADDRESS]);
bits[TPI_PREFERRED_ADDRESS][1] = vint_val2bits(
preferred_address_size(params));
need += (1 << bits[TPI_PREFERRED_ADDRESS][0])
+ (1 << bits[TPI_PREFERRED_ADDRESS][1])
+ preferred_address_size(params);
}
}
#if LSQUIC_TEST_QUANTUM_READINESS
else if (set & (1 << TPI_QUANTUM_READINESS))
{
bits[TPI_QUANTUM_READINESS][0]
= vint_val2bits(enum_2_tpi_val[TPI_QUANTUM_READINESS]);
bits[TPI_QUANTUM_READINESS][1] = vint_val2bits(quantum_sz);
need += (1 << bits[TPI_QUANTUM_READINESS][0])
+ (1 << bits[TPI_QUANTUM_READINESS][1])
+ quantum_sz;
}
#endif
for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi)
if (set & (1 << tpi))
{
if (tpi > MAX_NUM_WITH_DEF_TPI
|| params->tp_numerics[tpi] != def_vals[tpi])
{
if (params->tp_numerics[tpi] >= min_vals[tpi]
&& params->tp_numerics[tpi] <= max_vals[tpi])
{
bits[tpi][0] = vint_val2bits(enum_2_tpi_val[tpi]);
bits[tpi][2] = vint_val2bits(params->tp_numerics[tpi]);
bits[tpi][1] = vint_val2bits(bits[tpi][2]);
need += (1 << bits[tpi][0])
+ (1 << bits[tpi][1])
+ (1 << bits[tpi][2]);
}
else if (params->tp_numerics[tpi] > max_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too large (%"PRIu64" vs "
"maximum of %"PRIu64")", tpi2str[tpi],
params->tp_numerics[tpi], max_vals[tpi]);
return -1;
}
else
{
LSQ_DEBUG("numeric value of %s is too small (%"PRIu64" vs "
"minimum " "of %"PRIu64")",
tpi2str[tpi], params->tp_numerics[tpi], min_vals[tpi]);
return -1;
}
}
else
set &= ~(1 << tpi); /* Don't write default value */
}
for (; tpi <= MAX_EMPTY_TPI; ++tpi)
if (set & (1 << tpi))
{
bits[tpi][0] = vint_val2bits(enum_2_tpi_val[tpi]);
need += (1 << bits[tpi][0]) + 1 /* Zero length byte */;
}
if (need > bufsz || need > UINT16_MAX)
{
errno = ENOBUFS;
return -1;
}
p = buf;
#define WRITE_TO_P(src, len) do { \
memcpy(p, src, len); \
p += len; \
} while (0)
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define WRITE_UINT_TO_P(val, width) do { \
u##width = bswap_##width(val); \
WRITE_TO_P(&u##width, sizeof(u##width)); \
} while (0)
#else
#define WRITE_UINT_TO_P(val, width) do { \
u##width = val; \
WRITE_TO_P(&u##width, sizeof(u##width)); \
} while (0)
#endif
for (tpi = 0; tpi <= LAST_TPI; ++tpi)
if (set & (1 << tpi))
{
vint_write(p, enum_2_tpi_val[tpi], bits[tpi][0],
1 << bits[tpi][0]);
p += 1 << bits[tpi][0];
switch (tpi)
{
case TPI_MAX_IDLE_TIMEOUT:
case TPI_MAX_UDP_PAYLOAD_SIZE:
case TPI_INIT_MAX_DATA:
case TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL:
case TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE:
case TPI_INIT_MAX_STREAM_DATA_UNI:
case TPI_INIT_MAX_STREAMS_BIDI:
case TPI_INIT_MAX_STREAMS_UNI:
case TPI_ACK_DELAY_EXPONENT:
case TPI_MAX_ACK_DELAY:
case TPI_ACTIVE_CONNECTION_ID_LIMIT:
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_MIN_ACK_DELAY_02:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
vint_write(p, 1 << bits[tpi][2], bits[tpi][1],
1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
vint_write(p, params->tp_numerics[tpi], bits[tpi][2],
1 << bits[tpi][2]);
p += 1 << bits[tpi][2];
break;
case TPI_INITIAL_SOURCE_CID:
case TPI_RETRY_SOURCE_CID:
assert(0);
return -1;
case TPI_ORIGINAL_DEST_CID:
vint_write(p, params->tp_original_dest_cid.len, bits[tpi][1],
1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
WRITE_TO_P(params->tp_original_dest_cid.idbuf,
params->tp_original_dest_cid.len);
break;
case TPI_STATELESS_RESET_TOKEN:
vint_write(p, sizeof(params->tp_stateless_reset_token),
bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
WRITE_TO_P(params->tp_stateless_reset_token,
sizeof(params->tp_stateless_reset_token));
break;
case TPI_PREFERRED_ADDRESS:
vint_write(p, preferred_address_size(params),
bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
WRITE_TO_P(¶ms->tp_preferred_address.ipv4_addr,
sizeof(params->tp_preferred_address.ipv4_addr));
WRITE_UINT_TO_P(params->tp_preferred_address.ipv4_port, 16);
WRITE_TO_P(¶ms->tp_preferred_address.ipv6_addr,
sizeof(params->tp_preferred_address.ipv6_addr));
WRITE_UINT_TO_P(params->tp_preferred_address.ipv6_port, 16);
*p++ = params->tp_preferred_address.cid.len;
WRITE_TO_P(params->tp_preferred_address.cid.idbuf,
params->tp_preferred_address.cid.len);
WRITE_TO_P(params->tp_preferred_address.srst,
sizeof(params->tp_preferred_address.srst));
break;
case TPI_DISABLE_ACTIVE_MIGRATION:
case TPI_GREASE_QUIC_BIT:
*p++ = 0;
break;
#if LSQUIC_TEST_QUANTUM_READINESS
case TPI_QUANTUM_READINESS:
LSQ_DEBUG("encoded %zd bytes of quantum readiness", quantum_sz);
vint_write(p, quantum_sz, bits[tpi][1], 1 << bits[tpi][1]);
p += 1 << bits[tpi][1];
memset(p, 'Q', quantum_sz);
p += quantum_sz;
break;
#endif
case TPI_VERSION_INFORMATION:
break;
}
}
assert(buf + need == p);
return (int) (p - buf);
#undef WRITE_TO_P
#undef WRITE_UINT_TO_P
}
int
lsquic_tp_decode_27 (const unsigned char *const buf, size_t bufsz,
int is_server,
struct transport_params *params)
{
const unsigned char *p, *end, *q;
uint64_t len, param_id;
uint16_t tlen;
enum transport_param_id tpi;
unsigned set_of_ids;
int s;
p = buf;
end = buf + bufsz;
*params = TP_INITIALIZER();
#define EXPECT_LEN(expected_len) do { \
if (expected_len != len) \
return -1; \
} while (0)
#define EXPECT_AT_LEAST(expected_len) do { \
if ((expected_len) > (uintptr_t) (p + len - q)) \
return -1; \
} while (0)
set_of_ids = 0;
while (p < end)
{
s = vint_read(p, end, ¶m_id);
if (s < 0)
return -1;
p += s;
s = vint_read(p, end, &len);
if (s < 0)
return -1;
p += s;
if ((ptrdiff_t) len > end - p)
return -1;
tpi = tpi_val_2_enum(param_id);
if (tpi <= LAST_TPI)
{
if (set_of_ids & (1 << tpi))
return -1;
set_of_ids |= 1 << tpi;
}
switch (tpi)
{
case TPI_MAX_IDLE_TIMEOUT:
case TPI_MAX_UDP_PAYLOAD_SIZE:
case TPI_INIT_MAX_DATA:
case TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL:
case TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE:
case TPI_INIT_MAX_STREAM_DATA_UNI:
case TPI_INIT_MAX_STREAMS_BIDI:
case TPI_INIT_MAX_STREAMS_UNI:
case TPI_ACK_DELAY_EXPONENT:
case TPI_MAX_ACK_DELAY:
case TPI_ACTIVE_CONNECTION_ID_LIMIT:
case TPI_LOSS_BITS:
case TPI_MIN_ACK_DELAY:
case TPI_MIN_ACK_DELAY_02:
case TPI_TIMESTAMPS:
case TPI_MAX_DATAGRAM_FRAME_SIZE:
switch (len)
{
case 1:
case 2:
case 4:
case 8:
s = vint_read(p, p + len, ¶ms->tp_numerics[tpi]);
if (s == (int) len)
{
if (params->tp_numerics[tpi] > max_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too large "
"(%"PRIu64" vs maximum of %"PRIu64, tpi2str[tpi],
params->tp_numerics[tpi], max_vals[tpi]);
return -1;
}
else if (params->tp_numerics[tpi] < min_vals[tpi])
{
LSQ_DEBUG("numeric value of %s is too small "
"(%"PRIu64" vs minimum of %"PRIu64, tpi2str[tpi],
params->tp_numerics[tpi], min_vals[tpi]);
return -1;
}
break;
}
else
{
LSQ_DEBUG("cannot read the value of numeric transport "
"param %s of length %"PRIu64, tpi2str[tpi], len);
return -1;
}
default:
LSQ_DEBUG("invalid length=%"PRIu64" for numeric transport "
"parameter %s", len, tpi2str[tpi]);
return -1;
}
break;
case TPI_DISABLE_ACTIVE_MIGRATION:
EXPECT_LEN(0);
break;
case TPI_STATELESS_RESET_TOKEN:
/* Client MUST not include reset token,
* see [draft-ietf-quic-transport-11], Section 6.4.1
*/
if (!is_server)
return -1;
EXPECT_LEN(sizeof(params->tp_stateless_reset_token));
memcpy(params->tp_stateless_reset_token, p,
sizeof(params->tp_stateless_reset_token));
break;
case TPI_ORIGINAL_DEST_CID:
/* Client MUST not original connecti ID,
* see [draft-ietf-quic-transport-15], Section 6.6.1
*/
if (!is_server)
return -1;
if (len > MAX_CID_LEN)
return -1;
memcpy(params->tp_original_dest_cid.idbuf, p, len);
params->tp_original_dest_cid.len = len;
break;
case TPI_PREFERRED_ADDRESS:
/* Client MUST not include preferred address,
* see [draft-ietf-quic-transport-12], Section 6.4.1
*/
if (!is_server)
return -1;
q = p;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_addr));
memcpy(params->tp_preferred_address.ipv4_addr, q,
sizeof(params->tp_preferred_address.ipv4_addr));
q += sizeof(params->tp_preferred_address.ipv4_addr);
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv4_port));
READ_UINT(params->tp_preferred_address.ipv4_port, 16, q, 2);
q += 2;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_addr));
memcpy(params->tp_preferred_address.ipv6_addr, q,
sizeof(params->tp_preferred_address.ipv6_addr));
q += sizeof(params->tp_preferred_address.ipv6_addr);
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.ipv6_port));
READ_UINT(params->tp_preferred_address.ipv6_port, 16, q, 2);
q += 2;
EXPECT_AT_LEAST(1);
tlen = *q;
q += 1;
if (tlen > MAX_CID_LEN)
{
LSQ_DEBUG("preferred server address contains invalid "
"CID length of %"PRIu16" bytes", tlen);
return -1;
}
EXPECT_AT_LEAST(tlen);
memcpy(params->tp_preferred_address.cid.idbuf, q, tlen);
params->tp_preferred_address.cid.len = tlen;
q += tlen;
EXPECT_AT_LEAST(sizeof(params->tp_preferred_address.srst));
memcpy(params->tp_preferred_address.srst, q,
sizeof(params->tp_preferred_address.srst));
q += sizeof(params->tp_preferred_address.srst);
if (q != p + len)
return -1;
break;
default:
/* Do nothing: skip this transport parameter */
break;
}
p += len;
if (tpi <= LAST_TPI)
{
params->tp_set |= 1 << tpi;
params->tp_decoded |= 1 << tpi;
}
}
if (p != end)
return -1;
if ((params->tp_set & (1 << TPI_MIN_ACK_DELAY))
&& params->tp_numerics[TPI_MIN_ACK_DELAY]
> params->tp_numerics[TPI_MAX_ACK_DELAY] * 1000)
{
LSQ_DEBUG("min_ack_delay (%"PRIu64" usec) is larger than "
"max_ack_delay (%"PRIu64" ms)",
params->tp_numerics[TPI_MIN_ACK_DELAY],
params->tp_numerics[TPI_MAX_ACK_DELAY]);
return -1;
}
if ((params->tp_set & (1 << TPI_MIN_ACK_DELAY_02))
&& params->tp_numerics[TPI_MIN_ACK_DELAY_02]
> params->tp_numerics[TPI_MAX_ACK_DELAY] * 1000)
{
LSQ_DEBUG("min_ack_delay_02 (%"PRIu64" usec) is larger than "
"max_ack_delay (%"PRIu64" ms)",
params->tp_numerics[TPI_MIN_ACK_DELAY_02],
params->tp_numerics[TPI_MAX_ACK_DELAY]);
return -1;
}
return (int) (end - buf);
#undef EXPECT_LEN
}
void
lsquic_tp_to_str_27 (const struct transport_params *params, char *buf, size_t sz)
{
char *const end = buf + sz;
int nw;
enum transport_param_id tpi;
char tok_str[sizeof(params->tp_stateless_reset_token) * 2 + 1];
char addr_str[INET6_ADDRSTRLEN];
for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi)
if (params->tp_set & (1 << tpi))
{
nw = snprintf(buf, end - buf, "%.*s%s: %"PRIu64,
(buf + sz > end) << 1, "; ", tpi2str[tpi],
params->tp_numerics[tpi]);
buf += nw;
if (buf >= end)
return;
}
for (; tpi <= MAX_EMPTY_TPI; ++tpi)
if (params->tp_set & (1 << tpi))
{
nw = snprintf(buf, end - buf, "%.*s%s",
(buf + sz > end) << 1, "; ", tpi2str[tpi]);
buf += nw;
if (buf >= end)
return;
}
#if LSQUIC_TEST_QUANTUM_READINESS
if (params->tp_set & (1 << TPI_QUANTUM_READINESS))
{
nw = snprintf(buf, end - buf, "%.*s%s",
(buf + sz > end) << 1, "; ", tpi2str[TPI_QUANTUM_READINESS]);
buf += nw;
if (buf >= end)
return;
}
#endif
if (params->tp_set & (1 << TPI_STATELESS_RESET_TOKEN))
{
lsquic_hexstr(params->tp_stateless_reset_token,
sizeof(params->tp_stateless_reset_token), tok_str, sizeof(tok_str));
nw = snprintf(buf, end - buf, "; stateless_reset_token: %s", tok_str);
buf += nw;
if (buf >= end)
return;
}
if (params->tp_set & (1 << TPI_ORIGINAL_DEST_CID))
{
char cidbuf_[MAX_CID_LEN * 2 + 1];
nw = snprintf(buf, end - buf, "; original DCID (ODCID): %"CID_FMT,
CID_BITS(¶ms->tp_original_dest_cid));
buf += nw;
if (buf >= end)
return;
}
if (lsquic_tp_has_pref_ipv4(params))
{
if (inet_ntop(AF_INET, params->tp_preferred_address.ipv4_addr,
addr_str, sizeof(addr_str)))
{
nw = snprintf(buf, end - buf, "; IPv4 preferred address: %s:%u",
addr_str, params->tp_preferred_address.ipv4_port);
buf += nw;
if (buf >= end)
return;
}
}
if (lsquic_tp_has_pref_ipv6(params))
{
if (inet_ntop(AF_INET6, params->tp_preferred_address.ipv6_addr,
addr_str, sizeof(addr_str)))
{
nw = snprintf(buf, end - buf, "; IPv6 preferred address: %s:%u",
addr_str, params->tp_preferred_address.ipv6_port);
buf += nw;
if (buf >= end)
return;
}
}
}