mirror of
https://gitea.invidious.io/iv-org/litespeed-quic.git
synced 2024-08-15 00:53:43 +00:00
Release 2.6.2
- [BUGFIX] SCID!=ODCID rule applies to Retry packets, not regular packets. - [BUGFIX] Zero-RTT: BoringSSL no longer flips read/write secrets. - [BUGFIX] Truncate ACK frame rather instead of aborting IETF connection. - [BUGFIX] Client: don't send duplicate reset tokens. - [BUGFIX] Remove invalid assertion in H3 framing code. - Silence a warning in send ctl by restructuring switch() statement.
This commit is contained in:
parent
84dbbb75d5
commit
767cf6112c
12 changed files with 200 additions and 502 deletions
11
CHANGELOG
11
CHANGELOG
|
@ -1,3 +1,14 @@
|
|||
2019-11-11
|
||||
- 2.6.2
|
||||
- [BUGFIX] SCID!=ODCID rule applies to Retry packets, not regular
|
||||
packets.
|
||||
- [BUGFIX] Zero-RTT: BoringSSL no longer flips read/write secrets.
|
||||
- [BUGFIX] Truncate ACK frame rather instead of aborting IETF
|
||||
connection.
|
||||
- [BUGFIX] Client: don't send duplicate reset tokens.
|
||||
- [BUGFIX] Remove invalid assertion in H3 framing code.
|
||||
- Silence a warning in send ctl by restructuring switch() statement.
|
||||
|
||||
2019-11-08
|
||||
- 2.6.1
|
||||
- [BUGFIX] set retry token on all resubmitted packets.
|
||||
|
|
|
@ -15,8 +15,8 @@ and OpenLiteSpeed. We think it is free of major problems. Nevertheless, do
|
|||
not hesitate to report bugs back to us. Even better, send us fixes and
|
||||
improvements!
|
||||
|
||||
Currently supported QUIC versions are Q039, Q043, Q046, and ID-23. Support
|
||||
for newer versions will be added soon after they are released.
|
||||
Currently supported QUIC versions are Q039, Q043, Q046, ID-23, and ID-24.
|
||||
Support for newer versions will be added soon after they are released.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
|
|
@ -25,7 +25,7 @@ extern "C" {
|
|||
|
||||
#define LSQUIC_MAJOR_VERSION 2
|
||||
#define LSQUIC_MINOR_VERSION 6
|
||||
#define LSQUIC_PATCH_VERSION 1
|
||||
#define LSQUIC_PATCH_VERSION 2
|
||||
|
||||
/**
|
||||
* Engine flags:
|
||||
|
|
|
@ -2362,19 +2362,9 @@ cry_sm_set_encryption_secret (SSL *ssl, enum ssl_encryption_level_t level,
|
|||
return 0;
|
||||
|
||||
if (enc_sess->esi_flags & ESI_SERVER)
|
||||
{
|
||||
if (enc_level != ENC_LEV_EARLY)
|
||||
secrets[0] = read_secret, secrets[1] = write_secret;
|
||||
else
|
||||
secrets[1] = read_secret, secrets[0] = write_secret;
|
||||
}
|
||||
secrets[0] = read_secret, secrets[1] = write_secret;
|
||||
else
|
||||
{
|
||||
if (enc_level != ENC_LEV_EARLY)
|
||||
secrets[0] = write_secret, secrets[1] = read_secret;
|
||||
else
|
||||
secrets[1] = write_secret, secrets[0] = read_secret;
|
||||
}
|
||||
secrets[0] = write_secret, secrets[1] = read_secret;
|
||||
|
||||
if (enc_level < ENC_LEV_FORW)
|
||||
{
|
||||
|
|
|
@ -536,11 +536,11 @@ lsquic_engine_new (unsigned flags,
|
|||
if (hash_conns_by_addr(engine))
|
||||
engine->flags |= ENG_CONNS_BY_ADDR;
|
||||
engine->conns_hash = lsquic_hash_create();
|
||||
engine->pub.enp_tokgen = lsquic_tg_new(&engine->pub);
|
||||
if (!engine->pub.enp_tokgen)
|
||||
return NULL;
|
||||
if (flags & ENG_SERVER)
|
||||
{
|
||||
engine->pub.enp_tokgen = lsquic_tg_new(&engine->pub);
|
||||
if (!engine->pub.enp_tokgen)
|
||||
return NULL;
|
||||
engine->pr_queue = prq_create(
|
||||
10000 /* TODO: make configurable */, MAX_OUT_BATCH_SIZE,
|
||||
&engine->pub);
|
||||
|
|
|
@ -1512,11 +1512,8 @@ generate_new_cid_frame (struct ietf_full_conn *conn, lsquic_time_t now)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (conn->ifc_flags & IFC_SERVER)
|
||||
lsquic_tg_generate_sreset(conn->ifc_enpub->enp_tokgen, &cce->cce_cid,
|
||||
lsquic_tg_generate_sreset(conn->ifc_enpub->enp_tokgen, &cce->cce_cid,
|
||||
token_buf);
|
||||
else
|
||||
memset(token_buf, 0, sizeof(token_buf));
|
||||
|
||||
if (0 != lsquic_engine_add_cid(conn->ifc_enpub, &conn->ifc_conn,
|
||||
cce - conn->ifc_cces))
|
||||
|
@ -4880,6 +4877,7 @@ process_new_connection_id_frame (struct ietf_full_conn *conn,
|
|||
lsquic_cid_t cid;
|
||||
uint64_t seqno, retire_prior_to;
|
||||
int parsed_len, update_cur_dcid;
|
||||
char tokstr[IQUIC_SRESET_TOKEN_SZ * 2 + 1];
|
||||
|
||||
parsed_len = conn->ifc_conn.cn_pf->pf_parse_new_conn_id(p, len,
|
||||
&seqno, &retire_prior_to, &cid, &token);
|
||||
|
@ -4943,6 +4941,18 @@ process_new_connection_id_frame (struct ietf_full_conn *conn,
|
|||
"numbers %u and %"PRIu64, (*el)->de_seqno, seqno);
|
||||
return 0;
|
||||
}
|
||||
else if (((*el)->de_flags & DE_SRST)
|
||||
&& 0 == memcmp((*el)->de_srst, token,
|
||||
IQUIC_SRESET_TOKEN_SZ))
|
||||
{
|
||||
ABORT_QUIETLY(0, TEC_PROTOCOL_VIOLATION,
|
||||
"NEW_CONNECTION_ID: received second instance of reset "
|
||||
"token %s in seqno %"PRIu64", same as in seqno %u",
|
||||
(lsquic_hexstr(token, IQUIC_SRESET_TOKEN_SZ, tokstr,
|
||||
sizeof(tokstr)), tokstr),
|
||||
seqno, (*el)->de_seqno);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (!dce)
|
||||
dce = el;
|
||||
|
@ -5458,6 +5468,21 @@ process_retry_packet (struct ietf_full_conn *conn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (CUR_DCID(conn)->len == packet_in->pi_scid_len
|
||||
&& 0 == memcmp(CUR_DCID(conn)->idbuf,
|
||||
packet_in->pi_data + packet_in->pi_scid_off,
|
||||
packet_in->pi_scid_len))
|
||||
{
|
||||
/*
|
||||
* [draft-ietf-quic-transport-24] Section 17.2.5:
|
||||
" A client MUST discard a Retry packet that contains a Source
|
||||
" Connection ID field that is identical to the Destination
|
||||
" Connection ID field of its Initial packet.
|
||||
*/
|
||||
LSQ_DEBUG("server provided same SCID as ODCID: discard packet");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(CUR_DCID(conn)->len == packet_in->pi_odcid_len
|
||||
&& 0 == memcmp(CUR_DCID(conn)->idbuf,
|
||||
packet_in->pi_data + packet_in->pi_odcid,
|
||||
|
@ -5730,20 +5755,6 @@ process_regular_packet (struct ietf_full_conn *conn,
|
|||
if (!(conn->ifc_flags & (IFC_SERVER|IFC_DCID_SET))
|
||||
&& (packet_in->pi_scid_len))
|
||||
{
|
||||
if (CUR_DCID(conn)->len == packet_in->pi_scid_len
|
||||
&& 0 == memcmp(CUR_DCID(conn)->idbuf,
|
||||
packet_in->pi_data + packet_in->pi_scid_off,
|
||||
packet_in->pi_scid_len))
|
||||
{
|
||||
/*
|
||||
* [draft-ietf-quic-transport-24] Section 17.2.5:
|
||||
" A client MUST discard a Retry packet that contains a Source
|
||||
" Connection ID field that is identical to the Destination
|
||||
" Connection ID field of its Initial packet.
|
||||
*/
|
||||
LSQ_DEBUG("server provided same SCID as ODCID: discard packet");
|
||||
return 0;
|
||||
}
|
||||
conn->ifc_flags |= IFC_DCID_SET;
|
||||
lsquic_scid_from_packet_in(packet_in, CUR_DCID(conn));
|
||||
LSQ_DEBUGC("set DCID to %"CID_FMT,
|
||||
|
|
|
@ -915,7 +915,7 @@ ietf_v1_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
|
|||
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;
|
||||
unsigned a, b, c, addl_ack_blocks, ecn_needs;
|
||||
unsigned bits[4];
|
||||
enum ecn ecn;
|
||||
|
||||
|
@ -954,6 +954,15 @@ ietf_v1_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
|
|||
|
||||
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;
|
||||
|
||||
|
@ -975,13 +984,14 @@ ietf_v1_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
|
|||
rsize = range->high - range->low;
|
||||
a = vint_val2bits(gap - 1);
|
||||
b = vint_val2bits(rsize);
|
||||
if (ecn_needs + (1 << a) + (1 << b) > 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;
|
||||
}
|
||||
CHECKOUT((1 << a) + (1 << b));
|
||||
vint_write(p, gap - 1, a, 1 << a);
|
||||
p += 1 << a;
|
||||
vint_write(p, rsize, b, 1 << b);
|
||||
|
@ -999,9 +1009,7 @@ ietf_v1_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz,
|
|||
|
||||
if (ecn_counts)
|
||||
{
|
||||
for (ecn = 1; ecn <= 3; ++ecn)
|
||||
bits[ecn] = vint_val2bits(ecn_counts[ecn]);
|
||||
CHECKOUT((1 << bits[1]) + (1 << bits[2]) + (1 << bits[3]));
|
||||
assert(ecn_needs <= AVAIL());
|
||||
for (ecn = 1; ecn <= 3; ++ecn)
|
||||
{
|
||||
vint_write(p, ecn_counts[ecnmap[ecn]], bits[ecnmap[ecn]], 1 << bits[ecnmap[ecn]]);
|
||||
|
|
|
@ -1373,17 +1373,14 @@ send_ctl_expire (struct lsquic_send_ctl *ctl, enum packnum_space pns,
|
|||
&next);
|
||||
}
|
||||
break;
|
||||
case EXFI_LAST:
|
||||
default:
|
||||
assert(filter == EXFI_LAST);
|
||||
packet_out = send_ctl_last_unacked_retx_packet(ctl, pns);
|
||||
if (packet_out)
|
||||
n_resubmitted = send_ctl_handle_lost_packet(ctl, packet_out, NULL);
|
||||
else
|
||||
n_resubmitted = 0;
|
||||
break;
|
||||
#ifdef WIN32
|
||||
default:
|
||||
n_resubmitted = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
LSQ_DEBUG("consider %s packets lost: %d resubmitted",
|
||||
|
|
|
@ -2943,7 +2943,6 @@ maybe_close_varsize_hq_frame (struct lsquic_stream *stream)
|
|||
}
|
||||
else if (!size)
|
||||
{
|
||||
assert(!shf->shf_frame_ptr);
|
||||
LSQ_WARN("discard zero-sized HQ frame type 0x%X (off: %"PRIu64")",
|
||||
shf->shf_frame_type, shf->shf_off);
|
||||
stream_hq_frame_put(stream, shf);
|
||||
|
|
|
@ -24,6 +24,7 @@ SET(TESTS
|
|||
ack
|
||||
ackgen_gquic_be
|
||||
ackparse_gquic_be
|
||||
ackparse_ietf
|
||||
alarmset
|
||||
alt_svc_ver
|
||||
arr
|
||||
|
|
|
@ -1,455 +0,0 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "lsquic_types.h"
|
||||
#include "lsquic_parse.h"
|
||||
#include "lsquic_rechist.h"
|
||||
#include "lsquic_util.h"
|
||||
#include "lsquic.h"
|
||||
|
||||
static const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_041);
|
||||
|
||||
|
||||
static lsquic_packno_t
|
||||
n_acked (const ack_info_t *acki)
|
||||
{
|
||||
lsquic_packno_t n = 0;
|
||||
unsigned i;
|
||||
for (i = 0; i < acki->n_ranges; ++i)
|
||||
n += acki->ranges[i].high - acki->ranges[i].low + 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test1 (void)
|
||||
{
|
||||
/* Test taken from quic_framer_test.cc -- NewAckFrameOneAckBlock */
|
||||
unsigned char ack_buf[] = {
|
||||
/* TYPE N LL MM */
|
||||
0xA0 | 0 << 4 | 1 << 2 | 1,
|
||||
0x00, /* Number of timestamps */
|
||||
0x12, 0x34, /* Largest acked */
|
||||
0x00, 0x00, /* Zero delta time */
|
||||
0x12, 0x34, /* first ack block length */
|
||||
};
|
||||
|
||||
ack_info_t acki;
|
||||
memset(&acki, 0xF1, sizeof(acki));
|
||||
|
||||
int len = pf->pf_parse_ack_frame(ack_buf, sizeof(ack_buf), &acki);
|
||||
assert(("Parsed length is correct (8)", len == sizeof(ack_buf)));
|
||||
assert(("Number of ranges is 1", acki.n_ranges == 1));
|
||||
assert(("Largest acked is 0x1234", acki.ranges[0].high == 0x1234));
|
||||
assert(("Lowest acked is 1", acki.ranges[0].low == 1));
|
||||
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
|
||||
unsigned n = n_acked(&acki);
|
||||
assert(("Number of acked packets is 0x1234", n == 0x1234));
|
||||
|
||||
{
|
||||
size_t sz;
|
||||
for (sz = 1; sz < sizeof(ack_buf); ++sz)
|
||||
{
|
||||
len = pf->pf_parse_ack_frame(ack_buf, sz, &acki);
|
||||
assert(("Parsing truncated frame failed", len < 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test2 (void)
|
||||
{
|
||||
/* Test taken from quic_framer_test.cc -- NewAckFrameOneAckBlock */
|
||||
unsigned char ack_buf[] = {
|
||||
/* TYPE N LL MM */
|
||||
0xA0 | 1 << 4 | 1 << 2 | 1,
|
||||
0x04, /* Num Blocks */
|
||||
0x02, /* Num TS */
|
||||
0x12, 0x34, /* Largest acked */
|
||||
0x00, 0x00, /* Zero delta time. */
|
||||
0x00, 0x01, /* First ack block length. */
|
||||
0x01, /* Gap to next block. */
|
||||
0x0e, 0xaf, /* Ack block length. */
|
||||
0xff, /* Gap to next block. */
|
||||
0x00, 0x00, /* Ack block length. */
|
||||
0x91, /* Gap to next block. */
|
||||
0x01, 0xea, /* Ack block length. */
|
||||
0x05, /* Gap to next block. */
|
||||
0x00, 0x04, /* Ack block length. */
|
||||
0x01, /* Delta from largest observed. */
|
||||
0x10, 0x32, 0x54, 0x76, /* Delta time. */ /* XXX do we use this at all? */
|
||||
0x02, /* Delta from largest observed. */
|
||||
0x10, 0x32, /* Delta time. */
|
||||
};
|
||||
|
||||
/* We should get the following array of ranges:
|
||||
* high low
|
||||
* 0x1234 0x1234
|
||||
* 0x1232 0x384
|
||||
* 0x1F3 0xA
|
||||
* 0x4 0x1
|
||||
*/
|
||||
static const struct { unsigned high, low; } ranges[] = {
|
||||
{ 0x1234, 0x1234 },
|
||||
{ 0x1232, 0x384 },
|
||||
{ 0x1F3, 0xA },
|
||||
{ 0x4, 0x1 },
|
||||
};
|
||||
|
||||
ack_info_t acki;
|
||||
memset(&acki, 0xF1, sizeof(acki));
|
||||
|
||||
int len = pf->pf_parse_ack_frame(ack_buf, sizeof(ack_buf), &acki);
|
||||
assert(("Parsed length is correct (29)", len == sizeof(ack_buf)));
|
||||
assert(("Number of ranges is 4", acki.n_ranges == 4));
|
||||
assert(("Largest acked is 0x1234", acki.ranges[0].high == 0x1234));
|
||||
assert(("Number of timestamps is 2", acki.n_timestamps == 2));
|
||||
unsigned n = n_acked(&acki);
|
||||
assert(("Number of acked packets is 4254", n == 4254));
|
||||
|
||||
for (n = 0; n < 4; ++n)
|
||||
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
|
||||
&& ranges[n].low == acki.ranges[n].low));
|
||||
|
||||
{
|
||||
size_t sz;
|
||||
for (sz = 1; sz < sizeof(ack_buf); ++sz)
|
||||
{
|
||||
len = pf->pf_parse_ack_frame(ack_buf, sz, &acki);
|
||||
assert(("Parsing truncated frame failed", len < 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test3 (void)
|
||||
{
|
||||
/* Generated by our own code, but failed to parse... */
|
||||
unsigned char ack_buf[] = {
|
||||
/* TYPE N LL MM */
|
||||
0xA0 | 1 << 4 | 0 << 2 | 0,
|
||||
0x01, /* Num ACK block ranges */
|
||||
0x00, /* Number of timestamps */
|
||||
0x06, /* Largest ACKed */
|
||||
0x00, 0x00, /* Delta time */
|
||||
0x01, /* First ACK block length */
|
||||
0x02, /* Gap to next block */
|
||||
0x03, /* Ack block length */
|
||||
};
|
||||
|
||||
/* We should get the following array of ranges:
|
||||
* high low
|
||||
* 6 6
|
||||
* 3 1
|
||||
*/
|
||||
static const struct { unsigned high, low; } ranges[] = {
|
||||
{ 6, 6, },
|
||||
{ 3, 1, },
|
||||
};
|
||||
|
||||
ack_info_t acki;
|
||||
memset(&acki, 0xF1, sizeof(acki));
|
||||
|
||||
int len = pf->pf_parse_ack_frame(ack_buf, sizeof(ack_buf), &acki);
|
||||
assert(("Parsed length is correct (9)", len == sizeof(ack_buf)));
|
||||
assert(("Number of ranges is 2", acki.n_ranges == 2));
|
||||
assert(("Largest acked is 6", acki.ranges[0].high == 6));
|
||||
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
|
||||
unsigned n = n_acked(&acki);
|
||||
assert(("Number of acked packets is 4", n == 4));
|
||||
|
||||
for (n = 0; n < 2; ++n)
|
||||
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
|
||||
&& ranges[n].low == acki.ranges[n].low));
|
||||
|
||||
{
|
||||
size_t sz;
|
||||
for (sz = 1; sz < sizeof(ack_buf); ++sz)
|
||||
{
|
||||
len = pf->pf_parse_ack_frame(ack_buf, sz, &acki);
|
||||
assert(("Parsing truncated frame failed", len < 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test4 (void)
|
||||
{
|
||||
unsigned char ack_buf[] = {
|
||||
/* TYPE N LL MM */
|
||||
0xA0 | 1 << 4 | 0 << 2 | 0,
|
||||
0x01, /* Num ACK block ranges */
|
||||
0x00, /* Number of timestamps */
|
||||
0x03, /* Largest ACKed */
|
||||
0x23, 0x00, /* Delta time */
|
||||
0x01, /* First ack block length */
|
||||
0x01, /* Gap */
|
||||
0x01, /* Ack block length */
|
||||
};
|
||||
|
||||
/* We should get the following array of ranges:
|
||||
* high low
|
||||
* 6 6
|
||||
* 3 1
|
||||
*/
|
||||
static const struct { unsigned high, low; } ranges[] = {
|
||||
{ 3, 3, },
|
||||
{ 1, 1, },
|
||||
};
|
||||
|
||||
ack_info_t acki;
|
||||
memset(&acki, 0xF1, sizeof(acki));
|
||||
|
||||
int len = pf->pf_parse_ack_frame(ack_buf, sizeof(ack_buf), &acki);
|
||||
assert(("Parsed length is correct (9)", len == sizeof(ack_buf)));
|
||||
assert(("Number of ranges is 2", acki.n_ranges == 2));
|
||||
assert(("Largest acked is 3", acki.ranges[0].high == 3));
|
||||
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
|
||||
unsigned n = n_acked(&acki);
|
||||
assert(("Number of acked packets is 2", n == 2));
|
||||
|
||||
for (n = 0; n < 2; ++n)
|
||||
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
|
||||
&& ranges[n].low == acki.ranges[n].low));
|
||||
|
||||
{
|
||||
size_t sz;
|
||||
for (sz = 1; sz < sizeof(ack_buf); ++sz)
|
||||
{
|
||||
len = pf->pf_parse_ack_frame(ack_buf, sz, &acki);
|
||||
assert(("Parsing truncated frame failed", len < 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Four-byte packet numbers */
|
||||
static void
|
||||
test5 (void)
|
||||
{
|
||||
unsigned char ack_buf[] = {
|
||||
/* TYPE N LL MM */
|
||||
0xA0 | 1 << 4 | 2 << 2 | 2,
|
||||
0x01, /* Num ack blocks ranges. */
|
||||
0x00, /* Number of timestamps. */
|
||||
0x23, 0x45, 0x67, 0x89,
|
||||
0x00, 0x00, /* Zero delta time. */
|
||||
0x00, 0x00, 0x00, 0x01, /* First ack block length. */
|
||||
33 - 1, /* Gap to next block. */
|
||||
0x23, 0x45, 0x67, 0x68, /* Ack block length. */
|
||||
};
|
||||
|
||||
/* We should get the following array of ranges:
|
||||
* high low
|
||||
* 6 6
|
||||
* 3 1
|
||||
*/
|
||||
static const struct { unsigned high, low; } ranges[] = {
|
||||
{ 0x23456789, 0x23456789, },
|
||||
{ 0x23456768, 1, },
|
||||
};
|
||||
|
||||
ack_info_t acki;
|
||||
memset(&acki, 0xF1, sizeof(acki));
|
||||
|
||||
int len = pf->pf_parse_ack_frame(ack_buf, sizeof(ack_buf), &acki);
|
||||
assert(("Parsed length is correct (9)", len == sizeof(ack_buf)));
|
||||
assert(("Number of ranges is 2", acki.n_ranges == 2));
|
||||
assert(("Largest acked is 0x23456789", acki.ranges[0].high == 0x23456789));
|
||||
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
|
||||
lsquic_packno_t n = n_acked(&acki);
|
||||
assert(("Number of acked packets is correct", n == 0x23456768 + 1));
|
||||
|
||||
for (n = 0; n < 2; ++n)
|
||||
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
|
||||
&& ranges[n].low == acki.ranges[n].low));
|
||||
|
||||
{
|
||||
size_t sz;
|
||||
for (sz = 1; sz < sizeof(ack_buf); ++sz)
|
||||
{
|
||||
len = pf->pf_parse_ack_frame(ack_buf, sz, &acki);
|
||||
assert(("Parsing truncated frame failed", len < 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Six-byte packet numbers */
|
||||
static void
|
||||
test6 (void)
|
||||
{
|
||||
unsigned char ack_buf[] = {
|
||||
/* TYPE N LL MM */
|
||||
0xA0 | 1 << 4 | 3 << 2 | 3,
|
||||
0x01, /* Num ack blocks ranges. */
|
||||
0x00, /* Number of timestamps. */
|
||||
0x00, 0x00, 0xAB, 0xCD, 0x23, 0x45, 0x67, 0x89,
|
||||
0x00, 0x00, /* Zero delta time. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* First ack block length. */
|
||||
33 - 1, /* Gap to next block. */
|
||||
0x00, 0x00, 0xAB, 0xCD, 0x23, 0x45, 0x67, 0x68, /* Ack block length. */
|
||||
};
|
||||
|
||||
static const struct { lsquic_packno_t high, low; } ranges[] = {
|
||||
{ 0xABCD23456789, 0xABCD23456789, },
|
||||
{ 0xABCD23456768, 1, },
|
||||
};
|
||||
|
||||
ack_info_t acki;
|
||||
memset(&acki, 0xF1, sizeof(acki));
|
||||
|
||||
int len = pf->pf_parse_ack_frame(ack_buf, sizeof(ack_buf), &acki);
|
||||
assert(("Parsed length is correct", len == sizeof(ack_buf)));
|
||||
assert(("Number of ranges is 2", acki.n_ranges == 2));
|
||||
assert(("Largest acked is 0xABCD23456789", acki.ranges[0].high == 0xABCD23456789));
|
||||
assert(("Number of timestamps is 0", acki.n_timestamps == 0));
|
||||
lsquic_packno_t n = n_acked(&acki);
|
||||
assert(("Number of acked packets is correct", n == 0xABCD23456768 + 1));
|
||||
|
||||
for (n = 0; n < 2; ++n)
|
||||
assert(("Range checks out", ranges[n].high == acki.ranges[n].high
|
||||
&& ranges[n].low == acki.ranges[n].low));
|
||||
|
||||
{
|
||||
size_t sz;
|
||||
for (sz = 1; sz < sizeof(ack_buf); ++sz)
|
||||
{
|
||||
len = pf->pf_parse_ack_frame(ack_buf, sz, &acki);
|
||||
assert(("Parsing truncated frame failed", len < 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_max_ack (void)
|
||||
{
|
||||
lsquic_rechist_t rechist;
|
||||
lsquic_time_t now;
|
||||
unsigned i;
|
||||
int has_missing, sz[2];
|
||||
const struct lsquic_packno_range *range;
|
||||
unsigned char buf[1500];
|
||||
struct ack_info acki;
|
||||
lsquic_cid_t cid = { .len = 0, };
|
||||
|
||||
lsquic_rechist_init(&rechist, &cid);
|
||||
now = lsquic_time_now();
|
||||
|
||||
for (i = 1; i <= 300; ++i)
|
||||
{
|
||||
lsquic_rechist_received(&rechist, i * 10, now);
|
||||
now += i * 1000;
|
||||
}
|
||||
|
||||
memset(buf, 0xAA, sizeof(buf));
|
||||
|
||||
lsquic_packno_t largest = 0;
|
||||
sz[0] = pf->pf_gen_ack_frame(buf, sizeof(buf),
|
||||
(gaf_rechist_first_f) lsquic_rechist_first,
|
||||
(gaf_rechist_next_f) lsquic_rechist_next,
|
||||
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
|
||||
&rechist, now, &has_missing, &largest);
|
||||
assert(sz[0] > 0);
|
||||
assert(sz[0] <= (int) sizeof(buf));
|
||||
assert(has_missing);
|
||||
|
||||
assert(0 == buf[ 2 ]); /* Number of timestamps */
|
||||
assert(0xAA == buf[ sz[0] ]);
|
||||
|
||||
sz[1] = pf->pf_parse_ack_frame(buf, sizeof(buf), &acki);
|
||||
assert(sz[1] == sz[0]);
|
||||
assert(256 == acki.n_ranges);
|
||||
|
||||
for (range = lsquic_rechist_first(&rechist), i = 0;
|
||||
range && i < acki.n_ranges;
|
||||
range = lsquic_rechist_next(&rechist), ++i)
|
||||
{
|
||||
assert(range->high == acki.ranges[i].high);
|
||||
assert(range->low == acki.ranges[i].low);
|
||||
}
|
||||
assert(i == 256);
|
||||
|
||||
lsquic_rechist_cleanup(&rechist);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_ack_truncation (void)
|
||||
{
|
||||
lsquic_rechist_t rechist;
|
||||
lsquic_time_t now;
|
||||
unsigned i;
|
||||
int has_missing, sz[2];
|
||||
const struct lsquic_packno_range *range;
|
||||
unsigned char buf[1500];
|
||||
struct ack_info acki;
|
||||
size_t bufsz;
|
||||
lsquic_cid_t cid = { .len = 0, };
|
||||
|
||||
lsquic_rechist_init(&rechist, &cid);
|
||||
now = lsquic_time_now();
|
||||
|
||||
for (i = 1; i <= 300; ++i)
|
||||
{
|
||||
lsquic_rechist_received(&rechist, i * 10, now);
|
||||
now += i * 1000;
|
||||
}
|
||||
|
||||
for (bufsz = 200; bufsz < 210; ++bufsz)
|
||||
{
|
||||
memset(buf, 0xAA, sizeof(buf));
|
||||
lsquic_packno_t largest = 0;
|
||||
sz[0] = pf->pf_gen_ack_frame(buf, bufsz,
|
||||
(gaf_rechist_first_f) lsquic_rechist_first,
|
||||
(gaf_rechist_next_f) lsquic_rechist_next,
|
||||
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
|
||||
&rechist, now, &has_missing, &largest);
|
||||
assert(sz[0] > 0);
|
||||
assert(sz[0] <= (int) bufsz);
|
||||
assert(has_missing);
|
||||
|
||||
assert(0 == buf[ 2 ]); /* Number of timestamps */
|
||||
assert(0xAA == buf[ sz[0] ]);
|
||||
|
||||
sz[1] = pf->pf_parse_ack_frame(buf, sizeof(buf), &acki);
|
||||
assert(sz[1] == sz[0]);
|
||||
assert(acki.n_ranges < 256);
|
||||
|
||||
for (range = lsquic_rechist_first(&rechist), i = 0;
|
||||
range && i < acki.n_ranges;
|
||||
range = lsquic_rechist_next(&rechist), ++i)
|
||||
{
|
||||
assert(range->high == acki.ranges[i].high);
|
||||
assert(range->low == acki.ranges[i].low);
|
||||
}
|
||||
}
|
||||
|
||||
lsquic_rechist_cleanup(&rechist);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
lsquic_global_init(LSQUIC_GLOBAL_SERVER);
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
test6();
|
||||
test_max_ack();
|
||||
test_ack_truncation();
|
||||
return 0;
|
||||
}
|
136
test/unittests/test_ackparse_ietf.c
Normal file
136
test/unittests/test_ackparse_ietf.c
Normal file
|
@ -0,0 +1,136 @@
|
|||
/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "lsquic_types.h"
|
||||
#include "lsquic_parse.h"
|
||||
#include "lsquic_rechist.h"
|
||||
#include "lsquic_util.h"
|
||||
#include "lsquic.h"
|
||||
#include "lsquic_hash.h"
|
||||
#include "lsquic_conn.h"
|
||||
|
||||
static struct lsquic_conn lconn = LSCONN_INITIALIZER_CIDLEN(lconn, 0);
|
||||
|
||||
static const struct parse_funcs *const pf = select_pf_by_ver(LSQVER_ID24);
|
||||
|
||||
|
||||
static void
|
||||
test_max_ack (void)
|
||||
{
|
||||
lsquic_rechist_t rechist;
|
||||
lsquic_time_t now;
|
||||
unsigned i;
|
||||
int has_missing, sz[2];
|
||||
const struct lsquic_packno_range *range;
|
||||
unsigned char buf[1500];
|
||||
struct ack_info acki;
|
||||
|
||||
lsquic_rechist_init(&rechist, &lconn, 0);
|
||||
now = lsquic_time_now();
|
||||
|
||||
for (i = 1; i <= 300; ++i)
|
||||
{
|
||||
lsquic_rechist_received(&rechist, i * 10, now);
|
||||
now += i * 1000;
|
||||
}
|
||||
|
||||
memset(buf, 0xAA, sizeof(buf));
|
||||
|
||||
lsquic_packno_t largest = 0;
|
||||
sz[0] = pf->pf_gen_ack_frame(buf, sizeof(buf),
|
||||
(gaf_rechist_first_f) lsquic_rechist_first,
|
||||
(gaf_rechist_next_f) lsquic_rechist_next,
|
||||
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
|
||||
&rechist, now, &has_missing, &largest, NULL);
|
||||
assert(sz[0] > 0);
|
||||
assert(sz[0] <= (int) sizeof(buf));
|
||||
assert(has_missing);
|
||||
|
||||
assert(0 == buf[ sz[0] - 1 ]); /* Number of timestamps */
|
||||
assert(0xAA == buf[ sz[0] ]);
|
||||
|
||||
sz[1] = pf->pf_parse_ack_frame(buf, sizeof(buf), &acki, 0);
|
||||
assert(sz[1] == sz[0]);
|
||||
assert(256 == acki.n_ranges);
|
||||
|
||||
for (range = lsquic_rechist_first(&rechist), i = 0;
|
||||
range && i < acki.n_ranges;
|
||||
range = lsquic_rechist_next(&rechist), ++i)
|
||||
{
|
||||
assert(range->high == acki.ranges[i].high);
|
||||
assert(range->low == acki.ranges[i].low);
|
||||
}
|
||||
assert(i == 256);
|
||||
|
||||
lsquic_rechist_cleanup(&rechist);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_ack_truncation (void)
|
||||
{
|
||||
lsquic_rechist_t rechist;
|
||||
lsquic_time_t now;
|
||||
unsigned i;
|
||||
int has_missing, sz[2];
|
||||
const struct lsquic_packno_range *range;
|
||||
unsigned char buf[1500];
|
||||
struct ack_info acki;
|
||||
size_t bufsz;
|
||||
|
||||
lsquic_rechist_init(&rechist, &lconn, 0);
|
||||
now = lsquic_time_now();
|
||||
|
||||
for (i = 1; i <= 300; ++i)
|
||||
{
|
||||
lsquic_rechist_received(&rechist, i * 10, now);
|
||||
now += i * 1000;
|
||||
}
|
||||
|
||||
for (bufsz = 200; bufsz < 210; ++bufsz)
|
||||
{
|
||||
memset(buf, 0xAA, sizeof(buf));
|
||||
lsquic_packno_t largest = 0;
|
||||
sz[0] = pf->pf_gen_ack_frame(buf, bufsz,
|
||||
(gaf_rechist_first_f) lsquic_rechist_first,
|
||||
(gaf_rechist_next_f) lsquic_rechist_next,
|
||||
(gaf_rechist_largest_recv_f) lsquic_rechist_largest_recv,
|
||||
&rechist, now, &has_missing, &largest, NULL);
|
||||
assert(sz[0] > 0);
|
||||
assert(sz[0] <= (int) bufsz);
|
||||
assert(has_missing);
|
||||
|
||||
assert(0 == buf[ sz[0] - 1 ]); /* Number of timestamps */
|
||||
assert(0xAA == buf[ sz[0] ]);
|
||||
|
||||
sz[1] = pf->pf_parse_ack_frame(buf, sizeof(buf), &acki, 0);
|
||||
assert(sz[1] == sz[0]);
|
||||
assert(acki.n_ranges < 256);
|
||||
|
||||
for (range = lsquic_rechist_first(&rechist), i = 0;
|
||||
range && i < acki.n_ranges;
|
||||
range = lsquic_rechist_next(&rechist), ++i)
|
||||
{
|
||||
assert(range->high == acki.ranges[i].high);
|
||||
assert(range->low == acki.ranges[i].low);
|
||||
}
|
||||
}
|
||||
|
||||
lsquic_rechist_cleanup(&rechist);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
lsquic_global_init(LSQUIC_GLOBAL_SERVER);
|
||||
test_max_ack();
|
||||
test_ack_truncation();
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue