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:
Dmitri Tikhonov 2019-11-11 14:05:54 -05:00
parent 84dbbb75d5
commit 767cf6112c
12 changed files with 200 additions and 502 deletions

View file

@ -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.

View file

@ -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
-------------

View file

@ -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:

View file

@ -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)
{

View file

@ -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);

View file

@ -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,

View file

@ -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]]);

View file

@ -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",

View file

@ -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);

View file

@ -24,6 +24,7 @@ SET(TESTS
ack
ackgen_gquic_be
ackparse_gquic_be
ackparse_ietf
alarmset
alt_svc_ver
arr

View file

@ -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;
}

View 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;
}