Skip to content

Commit cae50d2

Browse files
committed
qir-failures
1 parent aeb22c2 commit cae50d2

15 files changed

Lines changed: 160 additions & 55 deletions

File tree

.sai.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@
321321
},
322322

323323
"qir": {
324-
"cmake": "cd /opt/quic-interop-runner ; source venv/bin/activate ; rm -rf logs_* ; export QIR_LWS_BRANCH=main ; cd /opt/quic-interop-runner/lws ; if [ \"`docker ps -q`\" ] ; then docker kill $(docker ps -q) ; fi ; docker system prune -a --volumes -f && docker build --build-arg QIR_LWS_BRANCH=${QIR_LWS_BRANCH} -t lws-quic-interop . --no-cache && cd .. && python3 run.py -c lws -s lws --delete-successful-logs ; python3 run.py -p webtransport -c lws -s lws --delete-successful-logs -t handshake,transfer-unidirectional-receive,transfer-unidirectional-send,transfer-bidirectional-receive,transfer-bidirectional-send,transfer-datagram-receive,transfer-datagram-send",
324+
"cmake": "cd /opt/quic-interop-runner ; source venv/bin/activate ; rm -rf logs_* ; export QIR_LWS_BRANCH=main ; cd /opt/quic-interop-runner/lws ; if [ \"`docker ps -q`\" ] ; then docker kill $(docker ps -q) ; fi ; docker system prune -f && docker build --build-arg QIR_LWS_BRANCH=${QIR_LWS_BRANCH} -t lws-quic-interop . --no-cache && cd .. && python3 run.py -c lws -s lws --delete-successful-logs ; python3 run.py -p webtransport -c lws -s lws --delete-successful-logs -t handshake,transfer-unidirectional-receive,transfer-unidirectional-send,transfer-bidirectional-receive,transfer-bidirectional-send,transfer-datagram-receive,transfer-datagram-send",
325325
"platforms": "none, linux-debian13/x86_64-amd/gcc"
326326
}
327327

lib/core-net/client/sort-dns.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,9 @@ lws_sort_dns(struct lws *wsi, const struct addrinfo *result)
640640
#if defined(LWS_WITH_IPV6)
641641
if (!wsi->ipv6 && ai->ai_family == AF_INET6)
642642
goto next;
643+
#else
644+
if (ai->ai_family == AF_INET6)
645+
goto next;
643646
#endif
644647

645648
ds = lws_zalloc(sizeof(*ds), __func__);

lib/core-net/wsi.c

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,6 @@ struct lws *lws_get_network_wsi(struct lws *wsi) {
118118
if (!wsi)
119119
return NULL;
120120

121-
#if defined(LWS_ROLE_QUIC)
122-
if (wsi->quic.qn)
123-
return wsi;
124-
#endif
125-
126121
#if defined(LWS_WITH_HTTP2) || defined(LWS_ROLE_MQTT) || defined(LWS_ROLE_QUIC)
127122
if (!wsi->mux_substream
128123
#if defined(LWS_WITH_CLIENT)
@@ -131,13 +126,8 @@ struct lws *lws_get_network_wsi(struct lws *wsi) {
131126
)
132127
return wsi;
133128

134-
while (wsi->mux.parent_wsi) {
135-
#if defined(LWS_ROLE_QUIC)
136-
if (wsi->quic.qn)
137-
return wsi;
138-
#endif
129+
while (wsi->mux.parent_wsi)
139130
wsi = wsi->mux.parent_wsi;
140-
}
141131
#endif
142132

143133
return wsi;

lib/roles/http/server/interceptor.c

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,29 @@ lws_interceptor_inject_header(struct lws *wsi, struct vhd_interceptor *vhd, cons
159159
p[cur_len + n] = '\0';
160160
}
161161

162+
static int
163+
lws_interceptor_redirect(struct lws *wsi, const char *uri)
164+
{
165+
char buf[LWS_PRE + 1024], *p = buf + LWS_PRE, *end = buf + sizeof(buf) - 1;
166+
167+
if (lws_add_http_header_status(wsi, HTTP_STATUS_SEE_OTHER,
168+
(unsigned char **)&p, (unsigned char *)end))
169+
return 1;
170+
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION,
171+
(unsigned char *)uri, (int)strlen(uri),
172+
(unsigned char **)&p, (unsigned char *)end))
173+
return 1;
174+
if (lws_finalize_http_header(wsi, (unsigned char **)&p,
175+
(unsigned char *)end))
176+
return 1;
177+
178+
lws_write(wsi, (unsigned char *)buf + LWS_PRE,
179+
lws_ptr_diff_size_t(p, buf + LWS_PRE),
180+
LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END);
181+
182+
return lws_http_transaction_completed(wsi);
183+
}
184+
162185
static int
163186
lws_interceptor_issue_cookie(struct lws *wsi)
164187
{
@@ -522,35 +545,38 @@ lws_interceptor_handle_http(struct lws *wsi, void *user, const struct lws_interc
522545
}
523546

524547
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
548+
unsigned long pre_delay_sec = (unsigned long)(vhd->pre_delay_ms / 1000);
549+
unsigned long min_req_sec = pre_delay_sec > 1 ? pre_delay_sec - 1 : 0;
550+
525551
vs = sizeof(vbuf);
526552

527553
lws_interceptor_init_jwt_cookie(&vck, vhd, ip, "lws_interceptor_v");
528554

529555
if (lws_jwt_get_http_cookie_validate_jwt(wsi, &vck, vbuf, &vs)) {
530556
lwsl_vhost_notice(vhd->vhost, "%s: POST: missing or invalid visit cookie", __func__);
531-
return 1;
557+
return lws_interceptor_redirect(wsi, uri);
532558
}
533559

534560
iat_p = lws_json_simple_find(vbuf, vs, "\"iat\":", &iat_len);
535561
if (!iat_p) {
536562
lwsl_vhost_notice(vhd->vhost, "%s: POST: visit cookie missing iat", __func__);
537-
return 1;
563+
return lws_interceptor_redirect(wsi, uri);
538564
}
539565
iat = atoll(iat_p);
540566

541567
if (ops && ops->verify) {
542568
lws_interceptor_result_t res = ops->verify(wsi, NULL, 0);
543569
if (res == LWS_INTERCEPTOR_RET_REJECT)
544-
return 1;
570+
return lws_interceptor_redirect(wsi, uri);
545571
if (res == LWS_INTERCEPTOR_RET_PASS)
546572
return lws_interceptor_issue_cookie(wsi);
547573

548574
/* RET_DELAYED falls through to timer setup */
549575
}
550576

551-
if (lws_now_secs() < (unsigned long)iat + (unsigned long)(vhd->pre_delay_ms / 1000)) {
577+
if (lws_now_secs() < (unsigned long)iat + min_req_sec) {
552578
lwsl_vhost_notice(vhd->vhost, "%s: POST: pre-delay not met", __func__);
553-
return 1;
579+
return lws_interceptor_redirect(wsi, uri);
554580
}
555581

556582
lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, 25);

lib/roles/quic/crypto-quic.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ lws_quic_set_keys(struct lws *wsi, enum lws_tls_quic_secret_type type, const uin
259259
}
260260

261261
if (is_rx) {
262-
if (level == LWS_QUIC_LEVEL_APP && k->valid && k->secret_rx[0] && qn->handshake_done) {
262+
if (level == LWS_QUIC_LEVEL_APP && k->valid && k->secret_rx[0]) {
263263
lwsl_notice("%s: ignoring post-handshake TLS secret_rx update for APP level\n", __func__);
264264
return 0;
265265
}
@@ -270,7 +270,7 @@ lws_quic_set_keys(struct lws *wsi, enum lws_tls_quic_secret_type type, const uin
270270
&k->el_aead_rx, k->key_aead_rx, &k->el_hp_rx, k->key_hp_rx))
271271
return -1;
272272
} else {
273-
if (level == LWS_QUIC_LEVEL_APP && k->valid && k->secret_tx[0] && qn->handshake_done) {
273+
if (level == LWS_QUIC_LEVEL_APP && k->valid && k->secret_tx[0]) {
274274
lwsl_notice("%s: ignoring post-handshake TLS secret_tx update for APP level\n", __func__);
275275
return 0;
276276
}

lib/roles/quic/ops-quic.c

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,28 +1148,6 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
11481148
break;
11491149
}
11501150

1151-
/* Check reserved bits AFTER unmasking! */
1152-
if (p[0] & 0x80) {
1153-
/* Long header: Bits 0x0c MUST be zero */
1154-
if (p[0] & 0x0c) {
1155-
lwsl_wsi_notice(wsi, "QUIC RX: Reserved bits non-zero in long header");
1156-
if (nwsi && nwsi != wsi) {
1157-
lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0);
1158-
goto next_packet;
1159-
}
1160-
return LWS_HPI_RET_PLEASE_CLOSE_ME;
1161-
}
1162-
} else {
1163-
/* Short header: Bits 0x18 MUST be zero */
1164-
if (p[0] & 0x18) {
1165-
lwsl_wsi_notice(wsi, "QUIC RX: Reserved bits non-zero in short header");
1166-
if (nwsi && nwsi != wsi) {
1167-
lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0);
1168-
goto next_packet;
1169-
}
1170-
return LWS_HPI_RET_PLEASE_CLOSE_ME;
1171-
}
1172-
}
11731151

11741152
/*
11751153
* Reconstruct full 62-bit PN.
@@ -1221,6 +1199,29 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12211199
goto next_packet;
12221200
}
12231201

1202+
/* Check reserved bits AFTER successful AEAD decryption (RFC 9000 5.4.1 & 12.2) */
1203+
if (p[0] & 0x80) {
1204+
/* Long header: Bits 0x0c MUST be zero */
1205+
if (p[0] & 0x0c) {
1206+
lwsl_wsi_notice(wsi, "QUIC RX: Reserved bits non-zero in long header");
1207+
if (nwsi && nwsi != wsi) {
1208+
lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0);
1209+
goto next_packet;
1210+
}
1211+
return LWS_HPI_RET_PLEASE_CLOSE_ME;
1212+
}
1213+
} else {
1214+
/* Short header: Bits 0x18 MUST be zero */
1215+
if (p[0] & 0x18) {
1216+
lwsl_wsi_notice(wsi, "QUIC RX: Reserved bits non-zero in short header");
1217+
if (nwsi && nwsi != wsi) {
1218+
lws_quic_enter_closing_state(nwsi, LWS_QUIC_ERR_PROTOCOL_VIOLATION, 0, 0);
1219+
goto next_packet;
1220+
}
1221+
return LWS_HPI_RET_PLEASE_CLOSE_ME;
1222+
}
1223+
}
1224+
12241225
/* Decryption succeeded! Commit key update if pending */
12251226
if (is_key_update) {
12261227
lws_quic_keys_release_aead_rx(k);
@@ -1272,6 +1273,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12721273
}
12731274

12741275
/* Check for duplicate/replayed packet numbers (Security Fix) */
1276+
int is_out_of_order = 0;
12751277
if (nwsi->quic.qn) {
12761278
uint64_t highest = nwsi->quic.qn->highest_rx_pn[pn_space];
12771279
if ((nwsi->quic.qn->rx_pn_bitmask[pn_space] != 0 || highest != 0) && full_pn <= highest) {
@@ -1281,6 +1283,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12811283
goto next_packet;
12821284
}
12831285
nwsi->quic.qn->rx_pn_bitmask[pn_space] |= (1ULL << diff);
1286+
is_out_of_order = 1;
12841287
} else {
12851288
if (nwsi->quic.qn->rx_pn_bitmask[pn_space] != 0 || highest != 0) {
12861289
uint64_t diff = full_pn - highest;
@@ -1295,6 +1298,11 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
12951298

12961299
/* Connection Migration: Execute pending migration now that the packet is cryptographically verified */
12971300
if (pending_migration) {
1301+
if (is_out_of_order) {
1302+
lwsl_notice("QUIC: Ignoring connection migration from out-of-order packet (PN %llu <= highest %llu)\n",
1303+
(unsigned long long)full_pn, (unsigned long long)highest);
1304+
pending_migration = 0;
1305+
} else {
12981306
pending_migration = 0;
12991307
#if (_LWS_ENABLED_LOGS & LLL_NOTICE)
13001308
char buf_old[64], buf_new[64];
@@ -1318,7 +1326,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
13181326
buf_old, (unsigned int)ntohs(port_old),
13191327
buf_new, (unsigned int)ntohs(port_new));
13201328
#endif
1321-
/* F-60: Do NOT commit nwsi->udp->sa46 yet! Wait for PATH_RESPONSE! */
1329+
nwsi->quic.qn->rx_has_non_probing = 0;
13221330
nwsi->quic.qn->probing_sa46 = migration_sa46;
13231331
nwsi->quic.qn->probing_sa46_valid = 1;
13241332

@@ -1381,6 +1389,7 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
13811389
lws_callback_on_writable(nwsi);
13821390
}
13831391
}
1392+
}
13841393
}
13851394

13861395
/* 5. Parse and handle all frames in the payload */
@@ -1400,6 +1409,46 @@ rops_handle_POLLIN_quic(struct lws_context_per_thread *pt, struct lws *wsi,
14001409
nwsi = lws_get_quic_network_wsi(nwsi);
14011410
}
14021411

1412+
/* RFC 9000 Section 9.3: Receiving non-probing frames (STREAM, ACK, etc.) from a new address
1413+
* indicates that the peer has migrated (e.g. due to NAT rebinding or active client migration).
1414+
* The server MUST commit its active path to the new address. */
1415+
if (nwsi && nwsi->quic.qn && nwsi->quic.qn->is_server &&
1416+
nwsi->quic.qn->probing_sa46_valid && nwsi->quic.qn->rx_has_non_probing) {
1417+
#if (_LWS_ENABLED_LOGS & LLL_NOTICE)
1418+
char buf_old[64], buf_new[64];
1419+
uint16_t port_old, port_new;
1420+
lws_sa46_write_numeric_address(&nwsi->udp->sa46, buf_old, sizeof(buf_old));
1421+
lws_sa46_write_numeric_address(&nwsi->quic.qn->probing_sa46, buf_new, sizeof(buf_new));
1422+
#if defined(LWS_WITH_IPV6)
1423+
port_old = nwsi->udp->sa46.sa4.sin_family == AF_INET ? nwsi->udp->sa46.sa4.sin_port : nwsi->udp->sa46.sa6.sin6_port;
1424+
port_new = nwsi->quic.qn->probing_sa46.sa4.sin_family == AF_INET ? nwsi->quic.qn->probing_sa46.sa4.sin_port : nwsi->quic.qn->probing_sa46.sa6.sin6_port;
1425+
#else
1426+
port_old = nwsi->udp->sa46.sa4.sin_port;
1427+
port_new = nwsi->quic.qn->probing_sa46.sa4.sin_port;
1428+
#endif
1429+
lwsl_notice("QUIC Server: Connection Migration committed via non-probing packet! Peer address updated from %s:%u to %s:%u\n",
1430+
buf_old, (unsigned int)ntohs(port_old),
1431+
buf_new, (unsigned int)ntohs(port_new));
1432+
#endif
1433+
1434+
nwsi->udp->sa46 = nwsi->quic.qn->probing_sa46;
1435+
nwsi->quic.qn->probing_sa46_valid = 0;
1436+
1437+
/* Reset Congestion Control State (RFC 9000 9.3.3) */
1438+
if (nwsi->quic.qn->cc_ops && nwsi->quic.qn->cc_ops->init)
1439+
nwsi->quic.qn->cc_ops->init(nwsi);
1440+
1441+
/* Reset RTT estimator */
1442+
nwsi->quic.qn->smoothed_rtt = 0;
1443+
nwsi->quic.qn->rttvar = 0;
1444+
nwsi->quic.qn->latest_rtt = 0;
1445+
1446+
/* Reset PMTUD */
1447+
nwsi->quic.qn->current_mtu = 1280;
1448+
nwsi->quic.qn->probed_mtu = 1380;
1449+
nwsi->quic.qn->pmtud_state = 1;
1450+
}
1451+
14031452
if (nwsi) {
14041453
struct lws *w = nwsi->mux.child_list;
14051454
while (w) {
@@ -2648,6 +2697,9 @@ rops_client_bind_quic(struct lws *wsi, const struct lws_client_connect_info *i)
26482697

26492698
lws_quic_queue_path_challenge(wsi);
26502699

2700+
lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_quic);
2701+
lws_callback_on_writable(wsi);
2702+
26512703
/* Skip all initialization and key derivation, just return */
26522704
return 1;
26532705
}

lib/roles/quic/parse-quic.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,21 @@ lws_quic_rx_reassemble(struct lws *nwsi, struct lws *wsi_child, struct lws_quic_
315315
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, owner->head) {
316316
struct lws_quic_rx_chunk *c = lws_container_of(d, struct lws_quic_rx_chunk, list);
317317

318+
if (c->offset < *expected_offset) {
319+
if (c->offset + c->len <= *expected_offset) {
320+
/* Completely obsolete chunk already delivered, discard */
321+
lws_dll2_remove(&c->list);
322+
lws_free(c);
323+
flushed = 1;
324+
break;
325+
}
326+
/* Trim overlapping prefix */
327+
size_t overlap = (size_t)(*expected_offset - c->offset);
328+
c->data += overlap;
329+
c->len -= overlap;
330+
c->offset = *expected_offset;
331+
}
332+
318333
if (c->offset == *expected_offset) {
319334
/* We found the next contiguous piece! */
320335
if (is_crypto) {
@@ -543,6 +558,14 @@ lws_quic_parse_frames(struct lws *nwsi, int level, uint8_t *payload, size_t payl
543558
if (!consumed) return -1;
544559
pos += consumed;
545560

561+
/* Track non-probing frames (RFC 9000 Section 9.1) */
562+
if (qn && type != LWS_QUIC_FT_PADDING &&
563+
type != LWS_QUIC_FT_PATH_CHALLENGE &&
564+
type != LWS_QUIC_FT_PATH_RESPONSE &&
565+
type != LWS_QUIC_FT_NEW_CONNECTION_ID) {
566+
qn->rx_has_non_probing = 1;
567+
}
568+
546569
/* Epoch Gating (RFC 9000 12.5) */
547570
int is_allowed = 0;
548571
switch (type) {
@@ -1672,16 +1695,21 @@ lws_quic_parse_transport_parameters(struct lws *wsi, const uint8_t *buf, size_t
16721695
}
16731696

16741697
if (port > 0) {
1698+
const char *host_str = (qn->nwsi && qn->nwsi->stash && qn->nwsi->stash->cis[CIS_HOST]) ?
1699+
qn->nwsi->stash->cis[CIS_HOST] : addr_str;
1700+
16751701
lwsl_wsi_notice(wsi, "QUIC TP: Migrating to preferred_address %s:%d", addr_str, port);
16761702
memset(&i, 0, sizeof(i));
16771703
i.context = wsi->a.context;
16781704
i.vhost = wsi->a.vhost;
16791705
i.address = addr_str;
1680-
i.host = addr_str;
1681-
i.origin = addr_str;
1706+
i.host = host_str;
1707+
i.origin = host_str;
16821708
i.port = port;
16831709
i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_ALLOW_INSECURE;
16841710
i.quic_migrate_from_wsi = qn->nwsi;
1711+
i.method = "QUIC";
1712+
i.alpn = wsi->alpn;
16851713

16861714
lws_client_connect_via_info(&i);
16871715
}

lib/roles/quic/private-lib-roles-quic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ struct lws_quic_netconn {
316316
struct lws *migration_probing_wsi;
317317
lws_sockaddr46 probing_sa46;
318318
uint8_t probing_sa46_valid:1;
319+
uint8_t rx_has_non_probing:1;
319320

320321
/* ECN (Explicit Congestion Notification) */
321322
uint64_t ecn_rx_ect0;

lib/tls/gnutls/gnutls-session.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ lws_tls_session_new_gnutls(struct lws *wsi)
174174
struct lws_vhost *vh;
175175
lws_tls_scm_t *ts;
176176
size_t nl;
177-
gnutls_datum_t gd;
177+
gnutls_datum_t gd = { NULL, 0 };
178178
#if (_LWS_ENABLED_LOGS & LLL_INFO)
179179
const char *disposition = "reuse";
180180
#endif

lib/tls/openssl/openssl-client.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,13 +708,11 @@ lws_tls_client_create_vhost_context(struct lws_vhost *vh,
708708
)
709709
{
710710
struct lws_tls_client_reuse *tcr;
711-
X509_STORE *x509_store;
712711
unsigned long error;
713712
SSL_METHOD *method;
714713
EVP_MD_CTX *mdctx;
715714
unsigned int len;
716715
uint8_t hash[32];
717-
X509 *client_CA;
718716
char c;
719717
int n;
720718

0 commit comments

Comments
 (0)