@amritk/nish-x86_64-linux 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/INSTALL.md +43 -34
  2. package/bin/nish +0 -0
  3. package/package.json +1 -1
  4. package/runtime/nish.d.ts +117 -8
  5. package/runtime/nish.h +50 -13
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +43 -24
  8. package/runtime/runtime-net.c +57 -10
  9. package/runtime/runtime-os.c +62 -20
  10. package/runtime/runtime-simd.c +203 -0
  11. package/runtime/runtime-wasm.c +12 -0
  12. package/runtime/runtime.c +66 -18
  13. package/runtime/shim.mjs +114 -5
  14. package/scripts/build.sh +14 -12
  15. package/std/README.md +168 -36
  16. package/std/crypto/aes.ts +51 -8
  17. package/std/crypto/base64url.ts +25 -12
  18. package/std/crypto/chacha20poly1305.ts +43 -29
  19. package/std/crypto/ct.ts +6 -1
  20. package/std/crypto/hkdf.ts +364 -3
  21. package/std/crypto/hmac.ts +307 -7
  22. package/std/crypto/p256.ts +618 -34
  23. package/std/crypto/sha1.ts +147 -0
  24. package/std/crypto/sha256.ts +3 -2
  25. package/std/crypto/x25519.ts +88 -36
  26. package/std/crypto/x509.ts +130 -47
  27. package/std/json.ts +297 -41
  28. package/std/net/hpack.ts +1094 -0
  29. package/std/net/http-fields.ts +604 -0
  30. package/std/net/http1-server.ts +1389 -0
  31. package/std/net/http1.ts +1492 -0
  32. package/std/net/http2-frame.ts +745 -0
  33. package/std/net/http2-tls.ts +216 -0
  34. package/std/net/http2.ts +1433 -0
  35. package/std/net/http3-frame.ts +457 -0
  36. package/std/net/http3-server.ts +911 -0
  37. package/std/net/http3.ts +2145 -0
  38. package/std/net/qpack.ts +1163 -0
  39. package/std/net/quic-conn-ack.ts +206 -0
  40. package/std/net/quic-conn-cid.ts +425 -0
  41. package/std/net/quic-conn-params.ts +462 -0
  42. package/std/net/quic-datagram.ts +130 -0
  43. package/std/net/quic-frame.ts +1206 -0
  44. package/std/net/quic-listener.ts +929 -0
  45. package/std/net/quic-packet.ts +1698 -0
  46. package/std/net/quic-recovery.ts +849 -0
  47. package/std/net/quic-stream.ts +1609 -0
  48. package/std/net/quic.ts +3578 -0
  49. package/std/net/tls/codec.ts +1089 -0
  50. package/std/net/tls/record-server.ts +1173 -0
  51. package/std/net/tls/record.ts +621 -0
  52. package/std/net/tls/schedule.ts +429 -0
  53. package/std/net/tls-tcp.ts +414 -0
  54. package/std/net/tls.ts +1437 -0
  55. package/std/net/websocket.ts +823 -0
  56. package/std/net/webtransport.ts +1334 -0
  57. package/std/secret.ts +62 -0
  58. package/std/text.ts +77 -0
@@ -0,0 +1,3578 @@
1
+ /**
2
+ * `nish/net/quic` — the server side of a QUIC version 1 connection (RFC 9000,
3
+ * RFC 9001, RFC 9221): the TLS 1.3 handshake carried in CRYPTO frames at
4
+ * three encryption levels, transport parameters both ways, acknowledgements
5
+ * per packet number space, the connection-ID table, streams both ways
6
+ * (`nish/net/quic-stream`) and DATAGRAM frames once the handshake is done.
7
+ * Sans-IO, like the rest of `nish/net`.
8
+ *
9
+ * import { QuicConnection, QuicServerConfig } from "nish/net/quic";
10
+ *
11
+ * const conn = new QuicConnection(config, entropy); // entropy: QUIC_CONN_ENTROPY_SIZE random bytes
12
+ * conn.receiveWindow(buf, off, len, now); // every datagram the client sends; now in ms
13
+ * const input: u8[] | null = conn.signatureInput();
14
+ * if (input !== null) { conn.sign(tlsSignEcdsaP256(key, input)); }
15
+ * let n: i32 = conn.takeDatagramInto(out, 0, now); // out: at least QUIC_CONN_DATAGRAM_SIZE bytes
16
+ * while (n > 0) { … send out[0 .. n) to the client … ; n = conn.takeDatagramInto(out, 0, now); }
17
+ * let id: i64 = conn.nextStreamEvent(); // a stream with news: read it, write it
18
+ * … conn.streamRead(id, buf, 0, size) / conn.streamWrite(id, data, 0, n, fin) …
19
+ * … and at conn.deadline(), conn.handleTimer(now); once done, conn.reset(entropy) for the next peer
20
+ *
21
+ * **What it is.** One connection, from the client's first Initial to a
22
+ * CONNECTION_CLOSE either way. The handshake is `nish/net/tls`'s `TlsServer`,
23
+ * driven over QUIC as RFC 9001 §4 describes: the bytes it writes at each
24
+ * level go out in CRYPTO frames of the matching packet number space, the
25
+ * bytes the client sends in CRYPTO frames are put back in order and handed to
26
+ * it, and each level's packet keys are made with `nish/net/quic-packet`'s
27
+ * `quicKeys` as soon as `TlsServer` knows the secret. The server's transport
28
+ * parameters, `original_destination_connection_id` and
29
+ * `initial_source_connection_id` among them, go into EncryptedExtensions; the
30
+ * client's are parsed and checked (RFC 9000 §7.3, §7.4) before the server's
31
+ * flight leaves.
32
+ *
33
+ * **Streams** are `nish/net/quic-stream`'s: both kinds, opened by either
34
+ * side, with flow control per stream and for the connection raised as the
35
+ * application reads, and the stream limits raised as streams finish. The
36
+ * application sees them through a small surface: `nextStreamEvent` names a
37
+ * stream with something new (opened, readable, finished, reset, stopped, or
38
+ * writable again), `streamRead` and `streamWrite` move bytes a chunk at a
39
+ * time with back-pressure, `openStream` opens one of the server's,
40
+ * `streamReset` and `streamStopSending` abandon a side. `readStream` and
41
+ * `writeStream` are the all-at-once forms, which allocate.
42
+ *
43
+ * **Datagrams** (RFC 9221). With `maxDatagramFrameSize` above 0 the server
44
+ * advertises `max_datagram_frame_size` and takes DATAGRAM frames up to it; a
45
+ * DATAGRAM frame it did not offer to take, or one larger, is
46
+ * PROTOCOL_VIOLATION. `sendDatagram` queues a payload when the client
47
+ * offered to take one that size and it fits a packet; it goes out within the
48
+ * congestion window, and is never sent again. `readDatagram` hands out what
49
+ * arrived. Each way is a fixed ring (`nish/net/quic-datagram`).
50
+ *
51
+ * **Loss recovery** (RFC 9002) is `nish/net/quic-recovery`'s: every
52
+ * ack-eliciting packet is recorded in its space's ring with what it carried
53
+ * (its CRYPTO range, its STREAM chunks, HANDSHAKE_DONE and the control
54
+ * frames), the client's ACKs give the RTT estimate and show what was lost by
55
+ * packet or time threshold, and what a lost packet carried is sent again,
56
+ * from the CRYPTO bytes a level keeps until it is discarded and the bytes a
57
+ * stream keeps until they are acknowledged; a control frame goes again with
58
+ * its current value. When nothing is acknowledged for a probe timeout, with
59
+ * its backoff, everything in flight is queued again and a probe goes out in
60
+ * each space that has packets in flight, a PING if there is nothing to
61
+ * resend (§6.2.4) — two datagrams of them during the handshake, each with
62
+ * the oldest CRYPTO data not acknowledged. A client that shows it lacks the
63
+ * server's handshake data (an Initial repeating CRYPTO data, an
64
+ * ack-eliciting Handshake packet) gets it again at once, a datagram each
65
+ * time, up to `QUIC_CONN_EARLY_RESENDS` times (§6.2.3). NewReno's window
66
+ * holds back everything ack-eliciting but a probe; an ACK always goes.
67
+ * PATH_RESPONSE and DATAGRAM are never sent again (RFC 9000 §13.3, RFC 9221
68
+ * §5.2). Pacing, and GSO batching, are the carrier's:
69
+ * `nish/net/quic-listener`.
70
+ *
71
+ * **Nothing allocated per packet** (QUIC-3). A connection is a slot: every
72
+ * buffer it uses — the packet record, the stream buffers, the CRYPTO
73
+ * reassembly, the connection-ID table, the datagram rings — is made by the
74
+ * constructor and reused, and `reset` makes the slot a new connection for
75
+ * the next peer. A packet is opened (`receiveWindow`) and a datagram built
76
+ * and sealed (`takeDatagramInto`) inside a `using a = arena()` block, so the
77
+ * AEAD's and the header protection's temporaries go when the block ends, and
78
+ * the compiler refuses the block if anything in it could store one. The
79
+ * handshake keeps its state in the slot too: one `TlsServer`, made by the
80
+ * slot's first handshake and `restart`ed by each after it; every level's
81
+ * packet keys, and a key update's, derived in place into `QuicKeysSlot`s
82
+ * (`nish/net/quic-packet`) over the slot's HKDF scratch; the Initial
83
+ * secrets, the stateless reset tokens and the connection IDs computed in
84
+ * arena blocks; the transport parameters encoded and parsed into the
85
+ * slot's own objects. After the first connection in a slot, a connection
86
+ * keeps nothing at all (QUIC-3 in `docs/security/quic.md`).
87
+ *
88
+ * **Time.** The connection has no clock: `receive`, `takeDatagram` and
89
+ * `handleTimer` take the caller's monotonic time in milliseconds, and
90
+ * `deadline()` says when `handleTimer` is next due. Three things run on it.
91
+ * The idle timeout (RFC 9000 §10.1) is the smaller of the two sides'
92
+ * `max_idle_timeout`, never under `QUIC_CONN_IDLE_FLOOR` or three probe
93
+ * timeouts; once it passes with nothing received, the connection closes
94
+ * silently (`QUIC_STATE_TIMED_OUT`) and wipes its keys. Loss recovery's
95
+ * timer declares packets lost by the time threshold or fires the probe
96
+ * timeout. And after a key update the previous read keys are kept for a
97
+ * probe timeout (`recovery.probeTimeout()`), for packets the network
98
+ * reordered, before the next generation's are derived (RFC 9001 §6.5).
99
+ *
100
+ * **Key update** (RFC 9001 §6), both ways. A 1-RTT packet whose Key Phase
101
+ * bit differs from the current one is opened with the next generation's read
102
+ * keys, which are derived ahead of time so that a forged bit costs no
103
+ * derivation and shows no timing difference (§6.3); if it opens, the client
104
+ * has updated, and the server's write keys follow before anything is
105
+ * acknowledged (§6.2). `updateKeys()` starts an update from the server once
106
+ * the client has acknowledged a packet of the current phase (§6.1). The
107
+ * generations take turns in two key slots and two secrets a direction, so an
108
+ * update keeps nothing in the arena; each still costs two key derivations of
109
+ * work, so a connection takes at most `QUIC_CONN_MAX_KEY_UPDATES` updates the
110
+ * client starts and closes on the next with KEY_UPDATE_ERROR (QUIC-4).
111
+ *
112
+ * **Sans-IO and deterministic.** No socket, no clock and no random device:
113
+ * every random choice (the TLS server random and ephemeral key, the server's
114
+ * first connection ID, and the seed later IDs are derived from) comes from
115
+ * the `entropy` the constructor and `reset` take, every stateless reset
116
+ * token from the configuration's static key (RFC 9000 §10.3.2), and every
117
+ * ACK says a delay of zero. So a recorded exchange replays byte for byte.
118
+ *
119
+ * **What a peer cannot do.** Nothing it sends makes this module panic. A
120
+ * datagram that does not parse, a packet that does not authenticate, a
121
+ * duplicate packet number, a packet for keys already discarded and a 1-RTT
122
+ * packet before the handshake is done are dropped and counted in `dropped`.
123
+ * A frame that breaks the protocol closes the connection with the transport
124
+ * error RFC 9000 names (`QUIC_ERROR_*` in `nish/net/quic-frame`), a TLS alert
125
+ * closes it with CRYPTO_ERROR (0x100 + the alert), and the CONNECTION_CLOSE
126
+ * is the next datagram out. Before the client's address is validated — by a
127
+ * Handshake packet, or by a Retry token `nish/net/quic-listener` checked —
128
+ * the server sends at most three times what it received (§8.1). Every buffer
129
+ * a peer can fill is fixed: CRYPTO reassembly by `QUIC_CONN_CRYPTO_WINDOW`,
130
+ * each stream by the credit advertised (its buffer), the connection by
131
+ * `maxData`, the connection-ID table by the limit advertised, the received
132
+ * packet numbers by `QUIC_ACK_MAX_RANGES`, and datagrams by their ring.
133
+ *
134
+ * **Secrets.** The packet keys of each level live in this connection's key
135
+ * slots as long as the level does, and in `TlsServer`'s (TLS-1); so do the
136
+ * 1-RTT secrets the next key generation is derived from, and the next
137
+ * generation's read keys. A `Secret` may not be a field (NL2430), so they are
138
+ * plain bytes; `secureZero` wipes each level's key, IV and header-protection
139
+ * key, and its AES schedules with ordinary stores, when the level is
140
+ * discarded, each generation's key, IV and secret when a key update
141
+ * replaces it, and `release()` (and `reset`) the rest — the 1-RTT keys, the
142
+ * key-update secrets, the traffic secrets `TlsServer` holds, its ephemeral
143
+ * key and the connection-ID seed. What no wipe reaches (`aesKeyInto`'s
144
+ * working copy of a schedule) is recorded as QUIC-2 in
145
+ * `docs/security/quic.md`, with #430, the follow-up that moves these structs
146
+ * onto `nish:secret`.
147
+ *
148
+ * Written from RFC 9000, RFC 9001 and RFC 9221, in this module's own
149
+ * structure; nothing here is ported from another implementation. Private
150
+ * names carry the `quicConn` prefix (`docs/wp26-stdlib.md` §3e).
151
+ */
152
+ import { timingSafeEqualAt } from "nish/crypto/ct"
153
+ import { HkdfScratch } from "nish/crypto/hkdf"
154
+ import { HmacSha256Scratch, hmacSha256 } from "nish/crypto/hmac"
155
+ import {
156
+ QUIC_AEAD_AES_128_GCM,
157
+ QUIC_AEAD_AES_256_GCM,
158
+ QUIC_AEAD_CHACHA20_POLY1305,
159
+ QUIC_AEAD_TAG_SIZE,
160
+ QUIC_ERR_RESERVED_BITS,
161
+ QUIC_MAX_CID_LENGTH,
162
+ QUIC_MAX_VARINT,
163
+ QUIC_PACKET_HANDSHAKE,
164
+ QUIC_PACKET_INITIAL,
165
+ QUIC_PACKET_OK,
166
+ QUIC_PACKET_SHORT,
167
+ QuicHeader,
168
+ QuicKeys,
169
+ QuicKeysSlot,
170
+ QuicPacket,
171
+ quicDecryptPayload,
172
+ quicInitialSecretsInto,
173
+ quicKeyUpdateSecretInto,
174
+ quicKeysInto,
175
+ quicKeysUpdateInto,
176
+ quicPacketNumberLength,
177
+ quicParseHeaderInto,
178
+ quicPutLongHeader,
179
+ quicPutShortHeader,
180
+ quicSealInPlace,
181
+ quicUnprotectHeader,
182
+ } from "nish/net/quic-packet"
183
+ import {
184
+ QUIC_ERROR_APPLICATION,
185
+ QUIC_ERROR_CRYPTO,
186
+ QUIC_ERROR_CRYPTO_BUFFER_EXCEEDED,
187
+ QUIC_ERROR_INTERNAL,
188
+ QUIC_ERROR_KEY_UPDATE,
189
+ QUIC_ERROR_NO_ERROR,
190
+ QUIC_ERROR_PROTOCOL_VIOLATION,
191
+ QUIC_ERROR_TRANSPORT_PARAMETER,
192
+ QUIC_FRAME_ACK,
193
+ QUIC_FRAME_ACK_ECN,
194
+ QUIC_FRAME_CONNECTION_CLOSE,
195
+ QUIC_FRAME_CONNECTION_CLOSE_APP,
196
+ QUIC_FRAME_CRYPTO,
197
+ QUIC_FRAME_DATAGRAM,
198
+ QUIC_FRAME_DATAGRAM_LENGTH,
199
+ QUIC_FRAME_HANDSHAKE_DONE,
200
+ QUIC_FRAME_MAX_DATA,
201
+ QUIC_FRAME_MAX_STREAM_DATA,
202
+ QUIC_FRAME_MAX_STREAMS_BIDI,
203
+ QUIC_FRAME_MAX_STREAMS_UNI,
204
+ QUIC_FRAME_NEW_CONNECTION_ID,
205
+ QUIC_FRAME_NEW_TOKEN,
206
+ QUIC_FRAME_PATH_CHALLENGE,
207
+ QUIC_FRAME_PATH_RESPONSE,
208
+ QUIC_FRAME_PING,
209
+ QUIC_FRAME_RESET_STREAM,
210
+ QUIC_FRAME_RETIRE_CONNECTION_ID,
211
+ QUIC_FRAME_STOP_SENDING,
212
+ QUIC_FRAME_STREAM,
213
+ QUIC_FRAME_STREAM_DATA_BLOCKED,
214
+ QUIC_PATH_DATA_SIZE,
215
+ QUIC_RESET_TOKEN_SIZE,
216
+ QuicFrame,
217
+ quicCryptoOverhead,
218
+ quicDatagramSize,
219
+ quicFrameAckEliciting,
220
+ quicFrameAllowed,
221
+ quicParseFrame,
222
+ quicPutConnectionClose,
223
+ quicPutCrypto,
224
+ quicPutNewConnectionId,
225
+ quicPutPadding,
226
+ quicPutPathData,
227
+ quicPutTypeOnly,
228
+ quicPutValue,
229
+ } from "nish/net/quic-frame"
230
+ import {
231
+ QuicTransportParameters,
232
+ quicEncodeTransportParametersInto,
233
+ quicParseTransportParametersInto,
234
+ } from "nish/net/quic-conn-params"
235
+ import { QuicAckRanges } from "nish/net/quic-conn-ack"
236
+ import {
237
+ QUIC_RECOVERY_APPLICATION_CAPACITY,
238
+ QUIC_RECOVERY_HANDSHAKE_CAPACITY,
239
+ QUIC_RECOVERY_TIMEOUT_LOSS,
240
+ QUIC_RECOVERY_TIMEOUT_PTO,
241
+ QuicRecovery,
242
+ QuicSentPackets,
243
+ } from "nish/net/quic-recovery"
244
+ import { QuicCidEntry, QuicCidTable, quicCidCopy } from "nish/net/quic-conn-cid"
245
+ import {
246
+ QUIC_SEND_RESET_SENT,
247
+ QUIC_STREAM_END,
248
+ QUIC_STREAM_ERR_FINISHED,
249
+ QUIC_STREAM_ERR_FLOW,
250
+ QUIC_STREAM_ERR_STATE,
251
+ QUIC_STREAM_ERR_UNKNOWN,
252
+ QUIC_STREAM_OK,
253
+ QuicStream,
254
+ QuicStreams,
255
+ quicStreamBit,
256
+ quicStreamSetBit,
257
+ } from "nish/net/quic-stream"
258
+ import { QuicDatagramQueue } from "nish/net/quic-datagram"
259
+ import {
260
+ TLS_LEVEL_APPLICATION,
261
+ TLS_LEVEL_HANDSHAKE,
262
+ TLS_LEVEL_INITIAL,
263
+ TLS_STATE_CONNECTED,
264
+ TLS_STATE_WAIT_CLIENT_HELLO,
265
+ TLS_STATE_WAIT_SIGNATURE,
266
+ TlsServer,
267
+ TlsServerConfig,
268
+ } from "nish/net/tls"
269
+ import { TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256 } from "nish/net/tls/schedule"
270
+
271
+ // ---- Sizes ----------------------------------------------------------------------
272
+
273
+ /** The length of every connection ID this server issues; a short header's DCID is read at this length. */
274
+ export const QUIC_CONN_CID_LENGTH: i32 = 8
275
+ /**
276
+ * The largest datagram the server sends, and the smallest a client's first
277
+ * Initial may arrive in (RFC 9000 §14.1): 1200 bytes, which every QUIC path
278
+ * carries, so no path MTU discovery is needed. `takeDatagramInto` needs this
279
+ * much room.
280
+ */
281
+ export const QUIC_CONN_DATAGRAM_SIZE: i32 = 1200
282
+ /**
283
+ * How many random bytes the constructor and `reset` take: 32 for the TLS
284
+ * server random, 32 for the x25519 key, 8 for the first connection ID and
285
+ * 32 for the seed the later IDs and their reset tokens are derived from.
286
+ */
287
+ export const QUIC_CONN_ENTROPY_SIZE: i32 = 104
288
+ /**
289
+ * How far ahead of what it has handed TLS the server buffers CRYPTO data.
290
+ * RFC 9000 §7.5 asks for at least 4096 bytes; a frame past this is
291
+ * CRYPTO_BUFFER_EXCEEDED. One buffer of this size serves every level, since
292
+ * the client's Initial flight is complete before its Handshake one starts.
293
+ */
294
+ export const QUIC_CONN_CRYPTO_WINDOW: i32 = 16384
295
+ /** The most connection IDs the server keeps active for the client to use, its own first one included. */
296
+ export const QUIC_CONN_LOCAL_CIDS: i32 = 4
297
+ /** The largest per-stream credit a configuration may advertise, which is also each stream's buffer each way. */
298
+ export const QUIC_CONN_MAX_STREAM_DATA: i64 = 1048576
299
+ /** The most streams of one kind a configuration may let the client open at once, or open itself. */
300
+ export const QUIC_CONN_MAX_STREAMS: i64 = 1024
301
+ /**
302
+ * The most bytes of stream buffer a configuration may make each connection
303
+ * hold: every stream slot (`maxStreamsBidi + maxStreamsUni + localStreams`)
304
+ * has `maxStreamData` bytes each way, made when the connection is, so a
305
+ * configuration past 64 MiB of them is refused rather than allocated.
306
+ */
307
+ export const QUIC_CONN_MAX_STREAM_BUFFERS: i64 = 67108864
308
+ /** The length of the configuration's static keys: the stateless reset key and the Retry token key. */
309
+ export const QUIC_CONN_STATIC_KEY_SIZE: i32 = 32
310
+ /**
311
+ * The shortest idle timeout the server keeps: three times the probe timeout
312
+ * of a path with no RTT sample yet (RFC 9002 §6.2.2, about a second), and
313
+ * never under three times the current one either. RFC 9000 §10.1 has an
314
+ * endpoint raise a smaller negotiated value to this, so that several probes
315
+ * can be lost before the connection is given up.
316
+ */
317
+ export const QUIC_CONN_IDLE_FLOOR: i64 = 3000
318
+ /**
319
+ * The most STREAM frames one packet carries, which is how many chunks a
320
+ * packet's record keeps for retransmission; the rest of the streams go in
321
+ * the next packet.
322
+ */
323
+ export const QUIC_CONN_PACKET_STREAMS: i32 = 4
324
+ /**
325
+ * The most control frames one packet carries — NEW_CONNECTION_ID,
326
+ * RETIRE_CONNECTION_ID and the flow-control, limit and stream-ending frames
327
+ * of `nish/net/quic-stream` — for the same reason: the three new IDs the
328
+ * server announces once the handshake is done fit one packet, and the rest
329
+ * go over the next few.
330
+ */
331
+ export const QUIC_CONN_PACKET_CONTROL: i32 = 4
332
+ /**
333
+ * How many key updates a connection takes from its client (RFC 9001 §6).
334
+ * Each costs two key derivations of work, though none of their memory, so
335
+ * the cap bounds what a client can make the server derive (QUIC-4); the next one
336
+ * closes the connection with KEY_UPDATE_ERROR. A client also has to wait
337
+ * a probe timeout between two of them.
338
+ */
339
+ export const QUIC_CONN_MAX_KEY_UPDATES: i32 = 64
340
+ /**
341
+ * How many ack-eliciting datagrams a probe timeout sends in the Initial and
342
+ * Handshake spaces (RFC 9002 §6.2.4 allows up to two). The server's first
343
+ * flight is usually one datagram, so with one probe a lossy path loses every
344
+ * copy of it about as often as it loses one datagram per probe timeout; two
345
+ * square that, and stay inside the amplification limit, which a one-datagram
346
+ * flight and two probes fill exactly. The Application Data space sends one.
347
+ */
348
+ export const QUIC_CONN_HANDSHAKE_PROBES: i32 = 2
349
+ /**
350
+ * How many times a connection resends its handshake data before the probe
351
+ * timeout because the client showed it lacks it (RFC 9002 §6.2.3, "for a
352
+ * limited number of times per connection"): a client Initial carrying
353
+ * CRYPTO data the server already read, or an ack-eliciting Handshake packet,
354
+ * while the server's own CRYPTO data at that level is in flight. Each
355
+ * resend is one datagram, so a client cannot make the server answer more
356
+ * than this many packets that way, whatever it sends.
357
+ */
358
+ export const QUIC_CONN_EARLY_RESENDS: i32 = 2
359
+ /**
360
+ * How many connection IDs a connection issues in all, its first included.
361
+ * The client retiring one asks for a replacement (§5.1.1, a SHOULD), and
362
+ * each costs two HMACs whose temporaries stay in the arena, so this bounds
363
+ * what a client can make the server derive by retiring IDs over and over;
364
+ * past it, a retired ID is not replaced.
365
+ */
366
+ export const QUIC_CONN_MAX_ISSUED_CIDS: i64 = 64
367
+ /** How many DATAGRAM payloads each way a connection holds, when it takes any. */
368
+ export const QUIC_CONN_DATAGRAM_QUEUE: i32 = 8
369
+ /**
370
+ * The largest `maxDatagramFrameSize` a configuration may advertise: 1500
371
+ * bytes, an Ethernet path's whole UDP payload, past which no DATAGRAM frame
372
+ * reaches a server on a real path. Each of the receive ring's entries is
373
+ * sized to what is advertised, so every frame taken is held.
374
+ */
375
+ export const QUIC_CONN_MAX_DATAGRAM_FRAME: i64 = 1500
376
+
377
+ // ---- States ---------------------------------------------------------------------
378
+
379
+ /** Waiting for the client's first Initial packet. */
380
+ export const QUIC_STATE_WAIT_INITIAL: i32 = 0
381
+ /** The handshake is under way. */
382
+ export const QUIC_STATE_HANDSHAKE: i32 = 1
383
+ /** The handshake is complete and confirmed; stream data flows. */
384
+ export const QUIC_STATE_CONNECTED: i32 = 2
385
+ /** This side closed the connection: one CONNECTION_CLOSE goes out, then nothing. `error` says why. */
386
+ export const QUIC_STATE_CLOSING: i32 = 3
387
+ /** The client closed the connection: nothing more goes out (RFC 9000 §10.2.2). `error` is its code. */
388
+ export const QUIC_STATE_DRAINING: i32 = 4
389
+ /**
390
+ * The idle timeout passed (RFC 9000 §10.1): the connection closed silently,
391
+ * its keys wiped, and nothing more goes out. Its state is to be discarded:
392
+ * drop the connection (or `reset` the slot), so that a later packet to its
393
+ * IDs reaches `nish/net/quic-listener`, whose stateless reset tells the client.
394
+ */
395
+ export const QUIC_STATE_TIMED_OUT: i32 = 5
396
+
397
+ // ---- What the datagram calls answer ---------------------------------------------
398
+
399
+ /** `sendDatagram` queued the payload. */
400
+ export const QUIC_DATAGRAM_OK: i32 = 0
401
+ /** `readDatagram`: nothing has arrived. */
402
+ export const QUIC_DATAGRAM_NONE: i32 = -1
403
+ /** `readDatagram`: the next datagram does not fit in the room given; it is kept. */
404
+ export const QUIC_DATAGRAM_ERR_ROOM: i32 = -2
405
+ /** `sendDatagram`: the client did not offer to take DATAGRAM frames (RFC 9221 §3), or the connection is not connected. */
406
+ export const QUIC_DATAGRAM_ERR_DISABLED: i32 = -3
407
+ /** `sendDatagram`: the payload is larger than the client takes, or than fits one packet (`maxDatagramPayload`). */
408
+ export const QUIC_DATAGRAM_ERR_TOO_BIG: i32 = -4
409
+ /** `sendDatagram`: the send ring is full; try again once a datagram has gone. */
410
+ export const QUIC_DATAGRAM_ERR_FULL: i32 = -5
411
+
412
+ /** A typed zero for the offsets below: a bare literal is an `f64` under `--number-mode f64`. */
413
+ const QUIC_CONN_FROM: i32 = 0
414
+ /** A space's key slots (`QuicConnSpace.keySlot`): the read keys', the write keys', and where Application Data's write keys start. */
415
+ const QUIC_CONN_READ_SLOT: i32 = 0
416
+ const QUIC_CONN_WRITE_SLOT: i32 = 1
417
+ const QUIC_CONN_APP_WRITE_SLOTS: i32 = 2
418
+ /** The same for the `i64` arguments of the methods below, where a literal is not given its parameter's type. */
419
+ const QUIC_CONN_NONE: i64 = 0
420
+ /** TLS's unexpected_message alert, which CRYPTO data at a level TLS is not reading is (RFC 8446 §6). */
421
+ const QUIC_CONN_ALERT_UNEXPECTED: i64 = 10
422
+
423
+ /** A packet's record: the bit that says it carried HANDSHAKE_DONE, and the kinds of control frame of its own. */
424
+ const QUIC_CONN_SENT_HANDSHAKE_DONE: i32 = 1
425
+ const QUIC_CONN_CONTROL_NEW_CID: i32 = 1
426
+ const QUIC_CONN_CONTROL_RETIRE: i32 = 2
427
+
428
+ /**
429
+ * What a QUIC server is configured with, the same for every connection. The
430
+ * limits are the transport parameters it advertises (RFC 9000 §18.2); the
431
+ * stream limits and `maxStreamData` also size each connection's buffers,
432
+ * which are made once and reused.
433
+ */
434
+ export interface QuicServerConfig {
435
+ /** DER certificates, leaf first, as `TlsServerConfig` takes them. */
436
+ certificateChain: u8[][]
437
+ /** The ALPN protocols the server speaks, most preferred first; QUIC requires one to match (RFC 9001 §8.1). */
438
+ alpn: string[]
439
+ /** The signature scheme the caller's key signs with, as `TlsServerConfig` takes it. */
440
+ signatureScheme: i32
441
+ /** Whether `nish/net/quic-listener` answers a client's first Initial with a Retry (§8.1.2). */
442
+ retry: boolean
443
+ /** `initial_max_data`: the connection's receive window, 0 to 2^62 − 1, raised as the application reads. */
444
+ maxData: i64
445
+ /**
446
+ * `initial_max_stream_data_bidi_remote` (and `_uni`, and `_bidi_local`
447
+ * when the server opens streams): each stream's receive window and the
448
+ * size of its buffer each way, 1 to 1 MiB.
449
+ */
450
+ maxStreamData: i64
451
+ /** `initial_max_streams_bidi`: how many bidirectional streams the client may have open at once, 0 to 1024. */
452
+ maxStreamsBidi: i64
453
+ /** `initial_max_streams_uni`: how many unidirectional streams the client may have open at once, 0 to 1024. */
454
+ maxStreamsUni: i64
455
+ /** How many streams of its own the server may have open at once, 0 to 1024 (`openStream`). */
456
+ localStreams: i64
457
+ /**
458
+ * RFC 9221's `max_datagram_frame_size`: the largest DATAGRAM frame the
459
+ * server takes, 0 (none, and the parameter is not sent) to 1500. The
460
+ * server sends DATAGRAM frames only when it takes them too.
461
+ */
462
+ maxDatagramFrameSize: i64
463
+ /**
464
+ * `max_idle_timeout` in milliseconds: 0 for none, at most 2^62 − 1. The
465
+ * connection times out after the smaller of this and the client's, and
466
+ * never sooner than `QUIC_CONN_IDLE_FLOOR`.
467
+ */
468
+ maxIdleTimeout: i64
469
+ /** `active_connection_id_limit`: how many of its connection IDs the client may give the server, 2 to 8. */
470
+ activeConnectionIdLimit: i64
471
+ /** How long, in milliseconds, a Retry token is accepted after it was issued: 1 to 60000. */
472
+ retryTokenLifetime: i64
473
+ /**
474
+ * The static key every stateless reset token is derived from
475
+ * (`quicStatelessResetToken`, RFC 9000 §10.3.2), `QUIC_CONN_STATIC_KEY_SIZE`
476
+ * bytes. Keep it across restarts: a server that lost a connection resets it
477
+ * with the token this key gives for the connection ID the client sends to.
478
+ */
479
+ statelessResetKey: u8[]
480
+ /** The key `nish/net/quic-listener` authenticates its Retry tokens with (§8.1.4), `QUIC_CONN_STATIC_KEY_SIZE` bytes. */
481
+ retryTokenKey: u8[]
482
+ }
483
+
484
+ /** One run of stream data the client sent, in order, as `readStream` answers it: `fin` when it ends the stream. */
485
+ export class QuicStreamData {
486
+ streamId: i64 = 0
487
+ data: u8[]
488
+ fin: boolean = false
489
+
490
+ constructor(streamId: i64, data: u8[], fin: boolean) {
491
+ this.streamId = streamId
492
+ this.data = data
493
+ this.fin = fin
494
+ }
495
+ }
496
+
497
+ /**
498
+ * CRYPTO bytes put back in order: a ring of `capacity` bytes indexed by
499
+ * stream offset, with a bit per byte saying it arrived, so frames may come
500
+ * in any order and overlap. What continues the stream from `delivered` is
501
+ * handed to TLS from the ring itself. One serves the connection: it follows
502
+ * the level the client is sending at, since the Initial flight is complete
503
+ * before the Handshake one starts.
504
+ */
505
+ class QuicConnReassembly {
506
+ ring: u8[]
507
+ have: u8[]
508
+ /** The level whose bytes the ring holds. */
509
+ level: i32 = 0
510
+ /** How many bytes of the level have been handed to TLS. */
511
+ delivered: i64 = 0
512
+
513
+ constructor(capacity: i32) {
514
+ this.ring = new Array<u8>(capacity)
515
+ this.have = new Array<u8>((capacity + 7) >> 3)
516
+ }
517
+
518
+ /** Empties the ring for level `level`. */
519
+ reset(level: i32): void {
520
+ this.level = level
521
+ this.delivered = 0
522
+ this.have.fill(toU8(0))
523
+ }
524
+
525
+ /** The ring slot of stream offset `offset`. */
526
+ slot(offset: i64): i32 {
527
+ const capacity: i32 = toI32(this.ring.length)
528
+ return capacity > 0 ? toI32(offset % toI64(capacity)) : 0
529
+ }
530
+
531
+ /** Whether the byte in ring slot `s` arrived and is not yet handed out. */
532
+ arrived(s: i32): boolean {
533
+ return quicStreamBit(this.have, s)
534
+ }
535
+
536
+ /** Marks ring slot `s` as holding a byte, or not. */
537
+ mark(s: i32, on: boolean): void {
538
+ quicStreamSetBit(this.have, s, on)
539
+ }
540
+
541
+ /**
542
+ * Takes `data[from .. from + length)`, which sits at stream offset
543
+ * `offset`. Answers `false`, taking nothing, when it reaches `capacity` or
544
+ * more past what was delivered. Bytes already delivered are skipped.
545
+ */
546
+ insert(offset: i64, data: u8[], from: i32, length: i32): boolean {
547
+ const capacity: i32 = toI32(this.ring.length)
548
+ if (offset + toI64(length) > this.delivered + toI64(capacity)) {
549
+ return false
550
+ }
551
+ for (let k: i32 = 0; k < length; k += 1) {
552
+ const position: i64 = offset + toI64(k)
553
+ const at: i32 = from + k
554
+ if (position >= this.delivered && at >= 0 && at < toI32(data.length)) {
555
+ const s: i32 = this.slot(position)
556
+ if (s >= 0 && s < capacity) {
557
+ this.ring[s] = data[at]
558
+ this.mark(s, true)
559
+ }
560
+ }
561
+ }
562
+ return true
563
+ }
564
+
565
+ /** How many bytes from `delivered` have arrived without a gap, up to the end of the ring's array. */
566
+ run(): i32 {
567
+ const capacity: i32 = toI32(this.ring.length)
568
+ const start: i32 = this.slot(this.delivered)
569
+ let n: i32 = 0
570
+ while (start + n < capacity && this.arrived(start + n)) {
571
+ n += 1
572
+ }
573
+ return n
574
+ }
575
+
576
+ /** Hands out the `n` bytes `run` counted: their bits cleared, `delivered` moved past them. */
577
+ consume(n: i32): void {
578
+ const start: i32 = this.slot(this.delivered)
579
+ for (let k: i32 = 0; k < n; k += 1) {
580
+ this.mark(start + k, false)
581
+ }
582
+ this.delivered = this.delivered + toI64(n)
583
+ }
584
+ }
585
+
586
+ /**
587
+ * One packet number space's state (RFC 9000 §12.3): its keys, its numbers,
588
+ * its CRYPTO stream, and what each of its packets in flight carried.
589
+ *
590
+ * The record of a packet is a row of the `sent*` arrays, indexed by the slot
591
+ * `nish/net/quic-recovery` gave it, so it is fixed when the connection is
592
+ * made. One row past the last slot, `staging`, is where the packet being
593
+ * built is described; `recordSent` copies it to the packet's slot. Only the
594
+ * Application Data space has rows for STREAM chunks and control frames,
595
+ * since only a 1-RTT packet carries them.
596
+ */
597
+ class QuicConnSpace {
598
+ readKeys: QuicKeys | null = null
599
+ writeKeys: QuicKeys | null = null
600
+ /** The next packet number to send. */
601
+ nextPn: i64 = 0
602
+ /** The largest packet number the client acknowledged here, or -1. */
603
+ largestAcked: i64 = -1
604
+ received: QuicAckRanges
605
+ /**
606
+ * Every CRYPTO byte TLS wrote at this level, from stream offset 0, its
607
+ * first `cryptoOutLength` bytes, kept until the level is discarded so that
608
+ * a lost range can be sent again; the array is reused by the next
609
+ * connection in the slot. The bytes from `cryptoOutHead` (offset
610
+ * `cryptoOutOffset`) are not sent yet.
611
+ */
612
+ cryptoOut: u8[]
613
+ cryptoOutLength: i32 = 0
614
+ /**
615
+ * How many ack-eliciting probe packets this space still owes, which the
616
+ * window does not hold back: what a probe timeout asked for (RFC 9002
617
+ * §6.2.4), or an early resend (§6.2.3).
618
+ */
619
+ probes: i32 = 0
620
+ cryptoOutOffset: i64 = 0
621
+ /** How many CRYPTO bytes the client sent here that were handed to TLS, for a duplicate arriving after the next level started. */
622
+ cryptoInDelivered: i64 = 0
623
+ /** CRYPTO bytes declared lost, to send before new ones: `[cryptoResendLow, cryptoResendHigh)`, or -1. */
624
+ cryptoResendLow: i64 = -1
625
+ cryptoResendHigh: i64 = -1
626
+ /** Each packet's CRYPTO frame (offset, and length 0 for none), and its `QUIC_CONN_SENT_*` bits. */
627
+ sentCryptoOffset: i64[]
628
+ sentCryptoLength: i32[]
629
+ sentFlags: u8[]
630
+ /** Each packet's STREAM chunks, `QUIC_CONN_PACKET_STREAMS` to a row: how many, then each one's stream, offset, length and FIN. */
631
+ sentStreamCount: i32[]
632
+ sentStreamId: i64[]
633
+ sentStreamOffset: i64[]
634
+ sentStreamLength: i32[]
635
+ sentStreamFin: u8[]
636
+ /** Each packet's control frames, `QUIC_CONN_PACKET_CONTROL` to a row: their kind and value (a sequence number or a stream). */
637
+ sentControlCount: i32[]
638
+ sentControlKind: u8[]
639
+ sentControlValue: i64[]
640
+ /**
641
+ * Where this space's packet keys live, made once for the slot (QUIC-3):
642
+ * the read keys' and the write keys', and for the Application Data space a
643
+ * second of each, which a key update derives the next generation into.
644
+ */
645
+ keySlots: QuicKeysSlot[]
646
+ /** `TLS_LEVEL_INITIAL`, `_HANDSHAKE` or `_APPLICATION`, which is also the space's index. */
647
+ level: i32 = 0
648
+ cryptoOutHead: i32 = 0
649
+ /** The packet `buildDatagram` is putting together here: where it starts in the datagram, or -1 for none, and its payload's end. */
650
+ builtStart: i32 = -1
651
+ builtEnd: i32 = 0
652
+ /** The row the packet being built is described in: one past the last slot. */
653
+ staging: i32 = 0
654
+ /** Whether the keys were discarded (RFC 9001 §4.9): nothing is sent or received here again. */
655
+ discarded: boolean = false
656
+ /** Whether the packet being built elicits an acknowledgement, and so is recorded. */
657
+ stagedEliciting: boolean = false
658
+
659
+ constructor(level: i32) {
660
+ this.level = level
661
+ this.received = new QuicAckRanges()
662
+ this.cryptoOut = []
663
+ this.keySlots = []
664
+ const slots: i32 = level === TLS_LEVEL_APPLICATION ? 4 : 2
665
+ for (let k: i32 = 0; k < slots; k += 1) {
666
+ this.keySlots.push(new QuicKeysSlot())
667
+ }
668
+ const capacity: i32 =
669
+ level === TLS_LEVEL_APPLICATION ? QUIC_RECOVERY_APPLICATION_CAPACITY : QUIC_RECOVERY_HANDSHAKE_CAPACITY
670
+ const rows: i32 = capacity + 1
671
+ // Only 1-RTT packets carry STREAM and control frames (RFC 9000 §12.4).
672
+ const chunkRows: i32 = level === TLS_LEVEL_APPLICATION ? rows : 0
673
+ this.staging = capacity
674
+ this.sentCryptoOffset = new Array<i64>(rows)
675
+ this.sentCryptoLength = new Array<i32>(rows)
676
+ this.sentFlags = new Array<u8>(rows)
677
+ this.sentStreamCount = new Array<i32>(rows)
678
+ this.sentStreamId = new Array<i64>(chunkRows * QUIC_CONN_PACKET_STREAMS)
679
+ this.sentStreamOffset = new Array<i64>(chunkRows * QUIC_CONN_PACKET_STREAMS)
680
+ this.sentStreamLength = new Array<i32>(chunkRows * QUIC_CONN_PACKET_STREAMS)
681
+ this.sentStreamFin = new Array<u8>(chunkRows * QUIC_CONN_PACKET_STREAMS)
682
+ this.sentControlCount = new Array<i32>(rows)
683
+ this.sentControlKind = new Array<u8>(chunkRows * QUIC_CONN_PACKET_CONTROL)
684
+ this.sentControlValue = new Array<i64>(chunkRows * QUIC_CONN_PACKET_CONTROL)
685
+ }
686
+
687
+ /** Puts the space back as a new connection has it, keeping its arrays, for a slot reused for another peer. */
688
+ reset(): void {
689
+ this.readKeys = null
690
+ this.writeKeys = null
691
+ this.nextPn = 0
692
+ this.largestAcked = -1
693
+ this.received.clear()
694
+ this.cryptoOutLength = 0
695
+ this.cryptoOutOffset = 0
696
+ this.cryptoOutHead = 0
697
+ this.cryptoInDelivered = 0
698
+ this.cryptoResendLow = -1
699
+ this.cryptoResendHigh = -1
700
+ this.discarded = false
701
+ this.probes = 0
702
+ this.clearStaged()
703
+ }
704
+
705
+ /**
706
+ * Appends `bytes[0 .. length)` to the CRYPTO stream kept, writing into the
707
+ * array's old room before growing it: TLS's output buffer is read in
708
+ * place, so its flight is not copied on the way.
709
+ */
710
+ appendCrypto(bytes: u8[], length: i32): void {
711
+ for (let k: i32 = 0; k < length && k < toI32(bytes.length); k += 1) {
712
+ const at: i32 = this.cryptoOutLength
713
+ if (at >= 0 && at < toI32(this.cryptoOut.length)) {
714
+ this.cryptoOut[at] = bytes[k]
715
+ } else {
716
+ this.cryptoOut.push(bytes[k])
717
+ }
718
+ this.cryptoOutLength = at + 1
719
+ }
720
+ }
721
+
722
+ /**
723
+ * Key slot `k` of this space: in the Initial and Handshake spaces 0 holds
724
+ * the read keys and 1 the write keys; in Application Data 0 and 1 hold
725
+ * the read keys' generations and 2 and 3 the write keys'.
726
+ */
727
+ keySlot(k: i32): QuicKeysSlot {
728
+ const at: i32 = k >= 0 && k < toI32(this.keySlots.length) ? k : 0
729
+ return this.keySlots[at]
730
+ }
731
+
732
+ /** Zeroes every key this space's slots hold, the AES schedules included. */
733
+ wipeKeySlots(): void {
734
+ for (const slot of this.keySlots) {
735
+ slot.wipe()
736
+ }
737
+ }
738
+
739
+ /** The number of STREAM chunks row `row` holds, or 0. */
740
+ streamCount(row: i32): i32 {
741
+ return row >= 0 && row < toI32(this.sentStreamCount.length) ? this.sentStreamCount[row] : 0
742
+ }
743
+
744
+ /** The number of control frames row `row` holds, or 0. */
745
+ controlCount(row: i32): i32 {
746
+ return row >= 0 && row < toI32(this.sentControlCount.length) ? this.sentControlCount[row] : 0
747
+ }
748
+
749
+ /** The CRYPTO frame's length in row `row`, or 0. */
750
+ cryptoLength(row: i32): i32 {
751
+ return row >= 0 && row < toI32(this.sentCryptoLength.length) ? this.sentCryptoLength[row] : 0
752
+ }
753
+
754
+ /** The CRYPTO frame's offset in row `row`, or 0. */
755
+ cryptoOffset(row: i32): i64 {
756
+ return row >= 0 && row < toI32(this.sentCryptoOffset.length) ? this.sentCryptoOffset[row] : 0
757
+ }
758
+
759
+ /** The `QUIC_CONN_SENT_*` bits of row `row`. */
760
+ flags(row: i32): i32 {
761
+ return row >= 0 && row < toI32(this.sentFlags.length) ? toI32(this.sentFlags[row]) : 0
762
+ }
763
+
764
+ /** Empties the staging row, for the next packet. */
765
+ clearStaged(): void {
766
+ this.stagedEliciting = false
767
+ this.setCrypto(this.staging, QUIC_CONN_NONE, QUIC_CONN_FROM)
768
+ this.setFlags(this.staging, QUIC_CONN_FROM)
769
+ this.setCounts(this.staging, QUIC_CONN_FROM, QUIC_CONN_FROM)
770
+ }
771
+
772
+ /** Sets row `row`'s CRYPTO frame. */
773
+ setCrypto(row: i32, offset: i64, length: i32): void {
774
+ if (row >= 0 && row < toI32(this.sentCryptoOffset.length) && row < toI32(this.sentCryptoLength.length)) {
775
+ this.sentCryptoOffset[row] = offset
776
+ this.sentCryptoLength[row] = length
777
+ }
778
+ }
779
+
780
+ /** Sets row `row`'s bits. */
781
+ setFlags(row: i32, flags: i32): void {
782
+ if (row >= 0 && row < toI32(this.sentFlags.length)) {
783
+ this.sentFlags[row] = toU8(flags)
784
+ }
785
+ }
786
+
787
+ /** Sets how many STREAM chunks and control frames row `row` holds. */
788
+ setCounts(row: i32, streams: i32, control: i32): void {
789
+ if (row >= 0 && row < toI32(this.sentStreamCount.length) && row < toI32(this.sentControlCount.length)) {
790
+ this.sentStreamCount[row] = streams
791
+ this.sentControlCount[row] = control
792
+ }
793
+ }
794
+
795
+ /** Sets STREAM chunk `j` of row `row`. */
796
+ setChunk(row: i32, j: i32, id: i64, offset: i64, length: i32, fin: boolean): void {
797
+ const at: i32 = row * QUIC_CONN_PACKET_STREAMS + j
798
+ if (
799
+ at >= 0 &&
800
+ at < toI32(this.sentStreamId.length) &&
801
+ at < toI32(this.sentStreamOffset.length) &&
802
+ at < toI32(this.sentStreamLength.length) &&
803
+ at < toI32(this.sentStreamFin.length)
804
+ ) {
805
+ this.sentStreamId[at] = id
806
+ this.sentStreamOffset[at] = offset
807
+ this.sentStreamLength[at] = length
808
+ this.sentStreamFin[at] = toU8(fin ? 1 : 0)
809
+ }
810
+ }
811
+
812
+ /** Sets control frame `j` of row `row`. */
813
+ setControl(row: i32, j: i32, kind: i32, value: i64): void {
814
+ const at: i32 = row * QUIC_CONN_PACKET_CONTROL + j
815
+ if (at >= 0 && at < toI32(this.sentControlKind.length) && at < toI32(this.sentControlValue.length)) {
816
+ this.sentControlKind[at] = toU8(kind)
817
+ this.sentControlValue[at] = value
818
+ }
819
+ }
820
+
821
+ /** Stages a CRYPTO frame at `offset`, `length` bytes, in the packet being built. */
822
+ stageCrypto(offset: i64, length: i32): void {
823
+ this.setCrypto(this.staging, offset, length)
824
+ this.stagedEliciting = true
825
+ }
826
+
827
+ /** Stages a STREAM chunk in the packet being built; the caller keeps under `QUIC_CONN_PACKET_STREAMS`. */
828
+ stageChunk(id: i64, offset: i64, length: i32, fin: boolean): void {
829
+ const streams: i32 = this.streamCount(this.staging)
830
+ this.setChunk(this.staging, streams, id, offset, length, fin)
831
+ this.setCounts(this.staging, streams + 1, this.controlCount(this.staging))
832
+ this.stagedEliciting = true
833
+ }
834
+
835
+ /** Stages a control frame in the packet being built; the caller keeps under `QUIC_CONN_PACKET_CONTROL`. */
836
+ stageControl(kind: i32, value: i64): void {
837
+ const control: i32 = this.controlCount(this.staging)
838
+ this.setControl(this.staging, control, kind, value)
839
+ this.setCounts(this.staging, this.streamCount(this.staging), control + 1)
840
+ this.stagedEliciting = true
841
+ }
842
+
843
+ /** Copies the staging row to row `row`, the slot of the packet just sealed. */
844
+ commitStaged(row: i32): void {
845
+ const from: i32 = this.staging
846
+ this.setCrypto(row, this.cryptoOffset(from), this.cryptoLength(from))
847
+ this.setFlags(row, this.flags(from))
848
+ const streams: i32 = this.streamCount(from)
849
+ const control: i32 = this.controlCount(from)
850
+ this.setCounts(row, streams, control)
851
+ for (let j: i32 = 0; j < streams; j += 1) {
852
+ const at: i32 = from * QUIC_CONN_PACKET_STREAMS + j
853
+ if (
854
+ at >= 0 &&
855
+ at < toI32(this.sentStreamId.length) &&
856
+ at < toI32(this.sentStreamOffset.length) &&
857
+ at < toI32(this.sentStreamLength.length) &&
858
+ at < toI32(this.sentStreamFin.length)
859
+ ) {
860
+ this.setChunk(
861
+ row,
862
+ j,
863
+ this.sentStreamId[at],
864
+ this.sentStreamOffset[at],
865
+ this.sentStreamLength[at],
866
+ toI32(this.sentStreamFin[at]) !== 0
867
+ )
868
+ }
869
+ }
870
+ for (let j: i32 = 0; j < control; j += 1) {
871
+ const at: i32 = from * QUIC_CONN_PACKET_CONTROL + j
872
+ if (at >= 0 && at < toI32(this.sentControlKind.length) && at < toI32(this.sentControlValue.length)) {
873
+ this.setControl(row, j, toI32(this.sentControlKind[at]), this.sentControlValue[at])
874
+ }
875
+ }
876
+ }
877
+
878
+ /** Queues CRYPTO bytes `[offset, offset + length)` to be sent again, merged with what is queued already. */
879
+ resendCrypto(offset: i64, length: i32): void {
880
+ if (length <= 0 || this.discarded) {
881
+ return
882
+ }
883
+ const end: i64 = offset + toI64(length)
884
+ if (this.cryptoResendLow < 0) {
885
+ this.cryptoResendLow = offset
886
+ this.cryptoResendHigh = end
887
+ return
888
+ }
889
+ if (offset < this.cryptoResendLow) {
890
+ this.cryptoResendLow = offset
891
+ }
892
+ if (end > this.cryptoResendHigh) {
893
+ this.cryptoResendHigh = end
894
+ }
895
+ }
896
+
897
+ /** How many CRYPTO bytes are waiting to be sent. */
898
+ cryptoUnsent(): i32 {
899
+ return this.cryptoOutLength - this.cryptoOutHead
900
+ }
901
+ }
902
+
903
+ /** The bytes `bytes[from .. from + length)`, copied into an array of exactly that size. */
904
+ const quicConnSlice = (bytes: u8[], from: i32, length: i32): u8[] => {
905
+ const out: u8[] = new Array<u8>(length)
906
+ const n: i32 = toI32(out.length)
907
+ for (let k: i32 = 0; k < n; k += 1) {
908
+ if (from + k >= 0 && from + k < toI32(bytes.length)) {
909
+ out[k] = bytes[from + k]
910
+ }
911
+ }
912
+ return out
913
+ }
914
+
915
+ /** The packet-protection AEAD that goes with a TLS 1.3 suite (RFC 9001 §5.3). */
916
+ const quicConnAead = (suite: i32): i32 => {
917
+ if (suite === TLS_AES_256_GCM_SHA384) {
918
+ return QUIC_AEAD_AES_256_GCM
919
+ }
920
+ if (suite === TLS_CHACHA20_POLY1305_SHA256) {
921
+ return QUIC_AEAD_CHACHA20_POLY1305
922
+ }
923
+ return QUIC_AEAD_AES_128_GCM
924
+ }
925
+
926
+ /** Wipes a level's key, IV and header-protection key; the expanded AES schedules are out of reach (QUIC-2). */
927
+ const quicConnWipeKeys = (keys: QuicKeys | null): void => {
928
+ quicConnWipePacketKey(keys)
929
+ if (keys !== null) {
930
+ secureZero(keys.hp)
931
+ }
932
+ }
933
+
934
+ /**
935
+ * Wipes a key generation's packet key and IV that a key update replaced. The
936
+ * header-protection key is left alone: every generation shares it (RFC 9001
937
+ * §6), so it goes only when the level does.
938
+ */
939
+ const quicConnWipePacketKey = (keys: QuicKeys | null): void => {
940
+ if (keys !== null) {
941
+ secureZero(keys.key)
942
+ secureZero(keys.iv)
943
+ }
944
+ }
945
+
946
+ /**
947
+ * The stateless reset token for the connection ID `cid` (RFC 9000 §10.3.2):
948
+ * the first 16 bytes of HMAC-SHA256 under the server's static `key`. A
949
+ * connection hands it to the client with each ID it issues, and
950
+ * `nish/net/quic-listener` derives the same token from a packet's ID alone
951
+ * once the connection is gone, which is what lets a server that lost its
952
+ * state end the connection.
953
+ */
954
+ export const quicStatelessResetToken = (key: u8[], cid: u8[]): u8[] => {
955
+ const mac: u8[] = hmacSha256(key, cid)
956
+ const token: u8[] = quicConnSlice(mac, 0, QUIC_RESET_TOKEN_SIZE)
957
+ secureZero(mac)
958
+ return token
959
+ }
960
+
961
+ /**
962
+ * `quicStatelessResetToken` of the ID `cid[at .. at + length)` into
963
+ * `out[outAt ..]`, computed in a connection's own HMAC scratch inside an
964
+ * arena block, so issuing an ID keeps nothing (QUIC-3). The scratch and the
965
+ * full MAC are wiped before it returns.
966
+ */
967
+ export const quicConnResetTokenInto = (
968
+ mac: HmacSha256Scratch,
969
+ key: u8[],
970
+ cid: u8[],
971
+ at: i32,
972
+ length: i32,
973
+ out: u8[],
974
+ outAt: i32
975
+ ): void => {
976
+ using _scope = arena()
977
+ const tag: u8[] = new Array<u8>(32)
978
+ mac.begin(key, QUIC_CONN_FROM, toI32(key.length))
979
+ mac.update(cid, at, length)
980
+ mac.finishInto(tag, QUIC_CONN_FROM)
981
+ for (
982
+ let k: i32 = 0;
983
+ k < QUIC_RESET_TOKEN_SIZE && k < toI32(tag.length) && outAt + k < toI32(out.length);
984
+ k += 1
985
+ ) {
986
+ out[outAt + k] = tag[k]
987
+ }
988
+ secureZero(tag)
989
+ mac.wipe()
990
+ }
991
+
992
+ /** Puts `from[at .. at + length)` in `to`, emptied first, in the room `to` already has. */
993
+ const quicConnRefill = (to: u8[], from: u8[], at: i32, length: i32): void => {
994
+ while (to.length > 0) {
995
+ to.pop()
996
+ }
997
+ for (let k: i32 = 0; k < length && at + k >= 0 && at + k < toI32(from.length); k += 1) {
998
+ to.push(from[at + k])
999
+ }
1000
+ }
1001
+
1002
+ /**
1003
+ * Secret `index` (0 or 1) of a direction's pool, of the length `aead`'s hash
1004
+ * gives: what a key update derives the next traffic secret into.
1005
+ */
1006
+ const quicConnSecretAt = (pool: u8[][], aead: i32, index: i32): u8[] => {
1007
+ let wanted: i32 = index === 1 ? 1 : 0
1008
+ if (aead === QUIC_AEAD_AES_256_GCM) {
1009
+ wanted = wanted + 2
1010
+ }
1011
+ const at: i32 = wanted < toI32(pool.length) ? wanted : 0
1012
+ return pool[at]
1013
+ }
1014
+
1015
+ /** Two secrets of SHA-256's length and two of SHA-384's, for one direction's key updates. */
1016
+ const quicConnSecretPool = (): u8[][] => [
1017
+ new Array<u8>(32),
1018
+ new Array<u8>(32),
1019
+ new Array<u8>(48),
1020
+ new Array<u8>(48),
1021
+ ]
1022
+
1023
+ /** Whether the configuration's limits are ones this module can honour. */
1024
+ const quicConnConfigFits = (config: QuicServerConfig): boolean =>
1025
+ config.maxStreamData >= 1 &&
1026
+ config.maxStreamData <= QUIC_CONN_MAX_STREAM_DATA &&
1027
+ config.maxStreamsBidi >= 0 &&
1028
+ config.maxStreamsBidi <= QUIC_CONN_MAX_STREAMS &&
1029
+ config.maxStreamsUni >= 0 &&
1030
+ config.maxStreamsUni <= QUIC_CONN_MAX_STREAMS &&
1031
+ config.localStreams >= 0 &&
1032
+ config.localStreams <= QUIC_CONN_MAX_STREAMS &&
1033
+ (config.maxStreamsBidi + config.maxStreamsUni + config.localStreams) * config.maxStreamData * 2 <=
1034
+ QUIC_CONN_MAX_STREAM_BUFFERS &&
1035
+ config.maxDatagramFrameSize >= 0 &&
1036
+ config.maxDatagramFrameSize <= QUIC_CONN_MAX_DATAGRAM_FRAME &&
1037
+ config.maxData >= 0 &&
1038
+ config.maxData <= QUIC_MAX_VARINT &&
1039
+ config.maxIdleTimeout >= 0 &&
1040
+ config.maxIdleTimeout <= QUIC_MAX_VARINT &&
1041
+ config.activeConnectionIdLimit >= 2 &&
1042
+ config.activeConnectionIdLimit <= 8 &&
1043
+ toI32(config.statelessResetKey.length) === QUIC_CONN_STATIC_KEY_SIZE
1044
+
1045
+ /**
1046
+ * One server connection, which is also a reusable slot. Make one, hand it
1047
+ * every datagram the client sends with `receiveWindow` (or `receive`), sign
1048
+ * when `signatureInput()` asks, send what `takeDatagramInto()` writes until
1049
+ * it answers 0, and use streams and datagrams once `state` is
1050
+ * `QUIC_STATE_CONNECTED`. Once it has closed, `reset` makes it a new
1051
+ * connection for the next client, with its buffers kept.
1052
+ *
1053
+ * The fields are readable: `state`, `error` (the transport error or
1054
+ * application code the connection closed with), `dropped`, `alpn` and
1055
+ * `serverName` once the handshake has read them, `peerParameters`, `streams`
1056
+ * (the stream table), and `cids`, the connection-ID table, which a server
1057
+ * that runs many connections routes datagrams by (`ownsConnectionId`).
1058
+ */
1059
+ export class QuicConnection {
1060
+ config: QuicServerConfig
1061
+ /** The error code the connection closed with: a transport error, `0x100 + alert`, or the application's code. */
1062
+ error: i64 = 0
1063
+ /** For a transport error: the type of the frame that caused it, or 0. */
1064
+ errorFrameType: i64 = 0
1065
+ serverRandom: u8[]
1066
+ ephemeralPrivate: u8[]
1067
+ cidSeed: u8[]
1068
+ /** The DCID of the client's first Initial, which the Initial keys come from: its first `originalDcidLength` bytes. */
1069
+ originalDcid: u8[]
1070
+ /** The SCID of the client's first Initial: where long-header packets go, and what its transport parameters must name. */
1071
+ peerScid: u8[]
1072
+ /** The server's first connection ID, the SCID of every long-header packet it sends. */
1073
+ localScid: u8[]
1074
+ tls: TlsServer | null = null
1075
+ /** The slot's `TlsServer`, made by its first handshake and `restart`ed by every one after (QUIC-3). */
1076
+ tlsSlot: TlsServer | null = null
1077
+ /** Its configuration, made once: the parameters it carries are `localEncoded`, rewritten for each connection. */
1078
+ tlsConfig: TlsServerConfig
1079
+ /** The server's transport parameters, set for each connection, and their encoding. */
1080
+ localParameters: QuicTransportParameters
1081
+ localEncoded: u8[]
1082
+ /** The client's transport parameters, parsed into the slot's own object. */
1083
+ parsedParameters: QuicTransportParameters
1084
+ /** Where every key derivation and HMAC of the connection runs. */
1085
+ kdf: HkdfScratch
1086
+ /** The Initial secrets of RFC 9001 §5.2, the client's and the server's. */
1087
+ initialClient: u8[]
1088
+ initialServer: u8[]
1089
+ /**
1090
+ * The 1-RTT secrets a key update derives, two of each hash's length a
1091
+ * direction (SHA-256's first, then SHA-384's), used in turn: the next
1092
+ * generation's goes in the one the current generation's is not in.
1093
+ */
1094
+ writeSecrets: u8[][]
1095
+ readSecrets: u8[][]
1096
+ /** A connection ID being issued and its stateless reset token, before the table copies them. */
1097
+ cidScratch: u8[]
1098
+ tokenScratch: u8[]
1099
+ initial: QuicConnSpace
1100
+ handshake: QuicConnSpace
1101
+ application: QuicConnSpace
1102
+ cids: QuicCidTable
1103
+ /** Loss detection, the RTT estimate, the congestion window and the pacer (RFC 9002). */
1104
+ recovery: QuicRecovery
1105
+ /** The client's transport parameters, once the handshake has checked them; until then, every default. */
1106
+ peerParameters: QuicTransportParameters
1107
+ /** The defaults `peerParameters` starts from, made once and set back by `reset`. */
1108
+ defaultParameters: QuicTransportParameters
1109
+ /** Bytes received and sent, for the anti-amplification limit (RFC 9000 §8.1). */
1110
+ bytesReceived: i64 = 0
1111
+ bytesSent: i64 = 0
1112
+ /** The ALPN protocol the handshake chose. */
1113
+ alpn: string = ""
1114
+ /** The client's `server_name`, or empty. */
1115
+ serverName: string = ""
1116
+ /** The streams, both ways (`nish/net/quic-stream`). */
1117
+ streams: QuicStreams
1118
+ /** DATAGRAM payloads waiting to be sent, and those the client sent waiting to be read. */
1119
+ datagramsOut: QuicDatagramQueue
1120
+ datagramsIn: QuicDatagramQueue
1121
+ /** The CRYPTO bytes the client sent, put back in order. */
1122
+ cryptoIn: QuicConnReassembly
1123
+ /** PATH_CHALLENGE data to answer: a ring of four, eight bytes each, `pathCount` from `pathHead`. */
1124
+ pathData: u8[]
1125
+ frame: QuicFrame
1126
+ /** The header and the packet numbers of the packet being read, reused for every one. */
1127
+ header: QuicHeader
1128
+ packet: QuicPacket
1129
+ /** For a connection `acceptRetry` set up: the DCID of the client's Initial before the Retry, and the Retry's SCID. */
1130
+ retryOriginalDcid: u8[]
1131
+ retryScid: u8[]
1132
+ /** An empty array, which a cleared secret or ID is set to. */
1133
+ none: u8[]
1134
+ /** The latest time the caller gave, in milliseconds; time never runs backwards here. */
1135
+ now: i64 = 0
1136
+ /** When the idle timer last restarted (RFC 9000 §10.1), or -1 before the first packet. */
1137
+ idleSince: i64 = -1
1138
+ /**
1139
+ * The 1-RTT secrets of the current generation, the next generation's read
1140
+ * secret, and its read keys (RFC 9001 §6.1, §6.3). `otherReadKeys` holds
1141
+ * the next generation's while `otherIsNext`, and for a probe timeout after
1142
+ * an update the previous one's, for reordered packets (§6.5).
1143
+ */
1144
+ appReadSecret: u8[]
1145
+ appWriteSecret: u8[]
1146
+ nextReadSecret: u8[]
1147
+ otherReadKeys: QuicKeys | null = null
1148
+ /** The lowest and highest packet numbers opened with the current read keys, or -1. */
1149
+ readPhaseLowest: i64 = -1
1150
+ readPhaseHighest: i64 = -1
1151
+ /** The first packet number sent with the current write keys. */
1152
+ writePhaseFirst: i64 = 0
1153
+ /** When the previous read keys go and the next are derived, or -1. */
1154
+ keyRetainUntil: i64 = -1
1155
+ /** The packet number of a client key update read but not applied yet, or -1. */
1156
+ phasePending: i64 = -1
1157
+ /** The largest DATAGRAM frame the client takes (its `max_datagram_frame_size`), 0 for none. */
1158
+ peerMaxDatagramFrame: i64 = 0
1159
+ originalDcidLength: i32 = 0
1160
+ peerScidLength: i32 = 0
1161
+ pathHead: i32 = 0
1162
+ pathCount: i32 = 0
1163
+ state: i32 = 0
1164
+ /** Packets dropped without closing the connection: unparseable, unauthenticated, duplicated, or for keys not held. */
1165
+ dropped: i32 = 0
1166
+ /** Key updates the client started, against `QUIC_CONN_MAX_KEY_UPDATES`. */
1167
+ keyUpdates: i32 = 0
1168
+ /** Which of the Application Data space's two read slots, and two write slots, hold the current keys. */
1169
+ appReadSlot: i32 = 0
1170
+ appWriteSlot: i32 = 0
1171
+ /** Which of `readSecrets` and `writeSecrets` (0 or 1) holds the current secret, or -1 for `TlsServer`'s own. */
1172
+ readSecretCurrent: i32 = -1
1173
+ writeSecretCurrent: i32 = -1
1174
+ /** Handshake data sent again early, against `QUIC_CONN_EARLY_RESENDS` (RFC 9002 §6.2.3). */
1175
+ earlyResends: i32 = 0
1176
+ /** The level at which the packet just read showed the client lacks the server's handshake data, or -1. */
1177
+ resendLevel: i32 = -1
1178
+ /** Whether `error` is an application code (CONNECTION_CLOSE 0x1d) rather than a transport error. */
1179
+ errorIsApplication: boolean = false
1180
+ /** Whether the one CONNECTION_CLOSE this side owes has gone out. */
1181
+ closeSent: boolean = false
1182
+ /** Whether the client's address is validated: a Handshake packet from it was processed. */
1183
+ addressValidated: boolean = false
1184
+ /** Whether the handshake is complete, which for a server is also confirmed (RFC 9001 §4.1.2). */
1185
+ handshakeComplete: boolean = false
1186
+ handshakeDonePending: boolean = false
1187
+ /** Whether the connection was set up by `acceptRetry`. */
1188
+ retried: boolean = false
1189
+ /** Whether an ack-eliciting packet went out since the last packet was received. */
1190
+ elicitingSent: boolean = false
1191
+ /** Whether `otherReadKeys` are the next generation's rather than the previous one's. */
1192
+ otherIsNext: boolean = false
1193
+ /** The Key Phase bit of the current read keys, and of the current write keys. */
1194
+ readPhase: boolean = false
1195
+ writePhase: boolean = false
1196
+ /** Whether the packet just read brought CRYPTO bytes for TLS, or retired an ID that wants replacing. */
1197
+ cryptoArrived: boolean = false
1198
+ topUpOwed: boolean = false
1199
+
1200
+ /**
1201
+ * A connection under `config`, with `entropy` its `QUIC_CONN_ENTROPY_SIZE`
1202
+ * random bytes, which are copied out and then wiped in the caller's array.
1203
+ * Every buffer the connection will use is made here, sized by `config`.
1204
+ * Entropy of another length, or limits outside what `QuicServerConfig`
1205
+ * documents, leave the connection closing with INTERNAL_ERROR before it
1206
+ * reads anything; it then sends nothing.
1207
+ */
1208
+ constructor(config: QuicServerConfig, entropy: u8[]) {
1209
+ this.config = config
1210
+ this.serverRandom = new Array<u8>(32)
1211
+ this.ephemeralPrivate = new Array<u8>(32)
1212
+ this.localScid = new Array<u8>(QUIC_CONN_CID_LENGTH)
1213
+ this.cidSeed = new Array<u8>(32)
1214
+ this.originalDcid = new Array<u8>(QUIC_MAX_CID_LENGTH)
1215
+ this.peerScid = new Array<u8>(QUIC_MAX_CID_LENGTH)
1216
+ this.initial = new QuicConnSpace(TLS_LEVEL_INITIAL)
1217
+ this.handshake = new QuicConnSpace(TLS_LEVEL_HANDSHAKE)
1218
+ this.application = new QuicConnSpace(TLS_LEVEL_APPLICATION)
1219
+ this.cids = new QuicCidTable(
1220
+ config.activeConnectionIdLimit >= 2 && config.activeConnectionIdLimit <= 8
1221
+ ? config.activeConnectionIdLimit
1222
+ : 2,
1223
+ QUIC_CONN_LOCAL_CIDS
1224
+ )
1225
+ this.recovery = new QuicRecovery()
1226
+ this.defaultParameters = new QuicTransportParameters()
1227
+ this.peerParameters = this.defaultParameters
1228
+ // A configuration that does not fit closes the connection at once (see
1229
+ // `begin`), so it gets no stream or datagram buffers at all.
1230
+ const fits: boolean = quicConnConfigFits(config)
1231
+ const streams: i64 = fits ? config.maxStreamsBidi : QUIC_CONN_NONE
1232
+ const uni: i64 = fits ? config.maxStreamsUni : QUIC_CONN_NONE
1233
+ const local: i64 = fits ? config.localStreams : QUIC_CONN_NONE
1234
+ const buffer: i32 = fits ? toI32(config.maxStreamData) : QUIC_CONN_FROM
1235
+ this.streams = new QuicStreams(streams, uni, local, buffer, fits ? config.maxData : QUIC_CONN_NONE)
1236
+ // The rings are made only when DATAGRAM frames are offered at all.
1237
+ const datagrams: i32 = fits && config.maxDatagramFrameSize > 0 ? QUIC_CONN_DATAGRAM_QUEUE : 0
1238
+ this.datagramsOut = new QuicDatagramQueue(datagrams, QUIC_CONN_DATAGRAM_SIZE)
1239
+ this.datagramsIn = new QuicDatagramQueue(
1240
+ datagrams,
1241
+ fits ? toI32(config.maxDatagramFrameSize) : QUIC_CONN_FROM
1242
+ )
1243
+ this.cryptoIn = new QuicConnReassembly(QUIC_CONN_CRYPTO_WINDOW)
1244
+ this.pathData = new Array<u8>(QUIC_PATH_DATA_SIZE * 4)
1245
+ this.frame = new QuicFrame()
1246
+ this.header = new QuicHeader()
1247
+ this.packet = new QuicPacket()
1248
+ this.none = []
1249
+ this.retryOriginalDcid = []
1250
+ this.retryScid = []
1251
+ this.localParameters = new QuicTransportParameters()
1252
+ this.localEncoded = []
1253
+ this.parsedParameters = new QuicTransportParameters()
1254
+ const extra: u8[] = []
1255
+ this.tlsConfig = {
1256
+ certificateChain: config.certificateChain,
1257
+ alpn: config.alpn,
1258
+ quicTransportParameters: this.localEncoded,
1259
+ extraExtensions: extra,
1260
+ signatureScheme: config.signatureScheme,
1261
+ quic: true,
1262
+ }
1263
+ this.kdf = new HkdfScratch()
1264
+ this.initialClient = new Array<u8>(32)
1265
+ this.initialServer = new Array<u8>(32)
1266
+ this.writeSecrets = quicConnSecretPool()
1267
+ this.readSecrets = quicConnSecretPool()
1268
+ this.cidScratch = new Array<u8>(QUIC_CONN_CID_LENGTH)
1269
+ this.tokenScratch = new Array<u8>(QUIC_RESET_TOKEN_SIZE)
1270
+ this.appReadSecret = this.none
1271
+ this.appWriteSecret = this.none
1272
+ this.nextReadSecret = this.none
1273
+ this.begin(entropy)
1274
+ }
1275
+
1276
+ /**
1277
+ * Makes this slot a new connection for the next client, with `entropy`
1278
+ * its `QUIC_CONN_ENTROPY_SIZE` fresh random bytes (wiped in the caller's
1279
+ * array): every secret of the last connection is wiped first, as
1280
+ * `release()` does, and every buffer is kept and emptied. The limits are
1281
+ * the constructor's configuration's. A slot reset this way allocates
1282
+ * nothing, and neither does the next handshake (QUIC-3 in
1283
+ * `docs/security/quic.md`).
1284
+ */
1285
+ reset(entropy: u8[]): void {
1286
+ this.wipeAll()
1287
+ this.initial.reset()
1288
+ this.handshake.reset()
1289
+ this.application.reset()
1290
+ this.cids.reset()
1291
+ this.recovery.reset()
1292
+ this.streams.reset()
1293
+ this.datagramsOut.reset()
1294
+ this.datagramsIn.reset()
1295
+ this.cryptoIn.reset(TLS_LEVEL_INITIAL)
1296
+ this.peerParameters = this.defaultParameters
1297
+ // The slot keeps its `TlsServer`, wiped by `wipeAll`; the next handshake restarts it.
1298
+ this.tls = null
1299
+ this.error = 0
1300
+ this.errorFrameType = 0
1301
+ this.bytesReceived = 0
1302
+ this.bytesSent = 0
1303
+ this.alpn = ""
1304
+ this.serverName = ""
1305
+ quicConnRefill(this.retryOriginalDcid, this.none, QUIC_CONN_FROM, QUIC_CONN_FROM)
1306
+ quicConnRefill(this.retryScid, this.none, QUIC_CONN_FROM, QUIC_CONN_FROM)
1307
+ this.appReadSecret = this.none
1308
+ this.appWriteSecret = this.none
1309
+ this.nextReadSecret = this.none
1310
+ this.otherReadKeys = null
1311
+ this.appReadSlot = 0
1312
+ this.appWriteSlot = 0
1313
+ this.readSecretCurrent = -1
1314
+ this.writeSecretCurrent = -1
1315
+ this.earlyResends = 0
1316
+ this.resendLevel = -1
1317
+ this.now = 0
1318
+ this.idleSince = -1
1319
+ this.readPhaseLowest = -1
1320
+ this.readPhaseHighest = -1
1321
+ this.writePhaseFirst = 0
1322
+ this.keyRetainUntil = -1
1323
+ this.phasePending = -1
1324
+ this.peerMaxDatagramFrame = 0
1325
+ this.originalDcidLength = 0
1326
+ this.peerScidLength = 0
1327
+ this.pathHead = 0
1328
+ this.pathCount = 0
1329
+ this.state = QUIC_STATE_WAIT_INITIAL
1330
+ this.dropped = 0
1331
+ this.keyUpdates = 0
1332
+ this.errorIsApplication = false
1333
+ this.closeSent = false
1334
+ this.addressValidated = false
1335
+ this.handshakeComplete = false
1336
+ this.handshakeDonePending = false
1337
+ this.retried = false
1338
+ this.elicitingSent = false
1339
+ this.otherIsNext = false
1340
+ this.readPhase = false
1341
+ this.writePhase = false
1342
+ this.cryptoArrived = false
1343
+ this.topUpOwed = false
1344
+ this.begin(entropy)
1345
+ }
1346
+
1347
+ /** Takes `entropy` (and wipes it), or closes with INTERNAL_ERROR for entropy or a configuration that does not fit. */
1348
+ begin(entropy: u8[]): void {
1349
+ quicCidCopy(this.serverRandom, entropy, 0, 32)
1350
+ quicCidCopy(this.ephemeralPrivate, entropy, 32, 32)
1351
+ quicCidCopy(this.localScid, entropy, 64, QUIC_CONN_CID_LENGTH)
1352
+ quicCidCopy(this.cidSeed, entropy, 72, 32)
1353
+ const fits: boolean = toI32(entropy.length) === QUIC_CONN_ENTROPY_SIZE && quicConnConfigFits(this.config)
1354
+ secureZero(entropy)
1355
+ if (!fits) {
1356
+ this.state = QUIC_STATE_CLOSING
1357
+ this.error = QUIC_ERROR_INTERNAL
1358
+ this.closeSent = true
1359
+ }
1360
+ }
1361
+
1362
+ /** Counts a dropped packet; the count stops at the largest `i32` rather than overflow. */
1363
+ drop(): void {
1364
+ if (this.dropped < 2147483647) {
1365
+ this.dropped += 1
1366
+ }
1367
+ }
1368
+
1369
+ /** Whether the connection has closed, either way. */
1370
+ closed(): boolean {
1371
+ return (
1372
+ this.state === QUIC_STATE_CLOSING ||
1373
+ this.state === QUIC_STATE_DRAINING ||
1374
+ this.state === QUIC_STATE_TIMED_OUT
1375
+ )
1376
+ }
1377
+
1378
+ /**
1379
+ * Sets up a connection whose client came back with a Retry token that
1380
+ * `nish/net/quic-listener` checked (RFC 9000 §8.1.2): `originalDcid` is the
1381
+ * DCID of the client's Initial before the Retry, and `retryScid` the
1382
+ * Retry's SCID, which the client now sends to. The server's transport
1383
+ * parameters then name both (§7.3), and the client's address counts as
1384
+ * validated, so the anti-amplification limit does not apply. Call it
1385
+ * before the first `receive`; answers whether it took: not once the
1386
+ * connection has started, and not for an ID over 20 bytes.
1387
+ */
1388
+ acceptRetry(originalDcid: u8[], retryScid: u8[]): boolean {
1389
+ if (
1390
+ this.state !== QUIC_STATE_WAIT_INITIAL ||
1391
+ toI32(originalDcid.length) > QUIC_MAX_CID_LENGTH ||
1392
+ toI32(retryScid.length) > QUIC_MAX_CID_LENGTH
1393
+ ) {
1394
+ return false
1395
+ }
1396
+ quicConnRefill(this.retryOriginalDcid, originalDcid, QUIC_CONN_FROM, toI32(originalDcid.length))
1397
+ quicConnRefill(this.retryScid, retryScid, QUIC_CONN_FROM, toI32(retryScid.length))
1398
+ this.retried = true
1399
+ return true
1400
+ }
1401
+
1402
+ /** Closes the connection with transport error `code`, caused by a frame of `frameType` (0 for none). */
1403
+ fail(code: i64, frameType: i64): void {
1404
+ if (this.closed()) {
1405
+ return
1406
+ }
1407
+ this.state = QUIC_STATE_CLOSING
1408
+ this.error = code
1409
+ this.errorFrameType = frameType
1410
+ this.errorIsApplication = false
1411
+ }
1412
+
1413
+ /**
1414
+ * Closes the connection from the application, with its own error `code`
1415
+ * (CONNECTION_CLOSE 0x1d; RFC 9000 §10.2). The next `takeDatagram` answers
1416
+ * the close, and nothing after it. Closing twice, or after the client
1417
+ * closed, does nothing.
1418
+ */
1419
+ close(code: i64): void {
1420
+ if (this.closed()) {
1421
+ return
1422
+ }
1423
+ this.state = QUIC_STATE_CLOSING
1424
+ this.error = code
1425
+ this.errorFrameType = 0
1426
+ this.errorIsApplication = true
1427
+ }
1428
+
1429
+ /** The space of packets of `type`, or `null` for a 0-RTT or Retry packet. */
1430
+ spaceOf(type: i32): QuicConnSpace | null {
1431
+ if (type === QUIC_PACKET_INITIAL) {
1432
+ return this.initial
1433
+ }
1434
+ if (type === QUIC_PACKET_HANDSHAKE) {
1435
+ return this.handshake
1436
+ }
1437
+ if (type === QUIC_PACKET_SHORT) {
1438
+ return this.application
1439
+ }
1440
+ return null
1441
+ }
1442
+
1443
+ /** The space of TLS level `level`. */
1444
+ spaceAt(level: i32): QuicConnSpace {
1445
+ if (level === TLS_LEVEL_INITIAL) {
1446
+ return this.initial
1447
+ }
1448
+ return level === TLS_LEVEL_HANDSHAKE ? this.handshake : this.application
1449
+ }
1450
+
1451
+ /**
1452
+ * Whether a datagram whose first packet is sent to `dcid` belongs to this
1453
+ * connection: one of its active connection IDs, or, until the handshake is
1454
+ * done, the DCID of the client's first Initial. A closed connection still
1455
+ * answers for its IDs; the caller drops it once it is done with it.
1456
+ */
1457
+ ownsConnectionId(dcid: u8[]): boolean {
1458
+ return this.ownsConnectionIdAt(dcid, QUIC_CONN_FROM, toI32(dcid.length))
1459
+ }
1460
+
1461
+ /** `ownsConnectionId` of the ID `buf[at .. at + length)`, read in place, so a carrier routes without a copy. */
1462
+ ownsConnectionIdAt(buf: u8[], at: i32, length: i32): boolean {
1463
+ if (this.cids.ownsLocalAt(buf, at, length)) {
1464
+ return true
1465
+ }
1466
+ return (
1467
+ !this.handshakeComplete &&
1468
+ this.originalDcidLength > 0 &&
1469
+ length === this.originalDcidLength &&
1470
+ timingSafeEqualAt(buf, at, this.originalDcid, QUIC_CONN_FROM, length)
1471
+ )
1472
+ }
1473
+
1474
+ /** `receiveWindow` of a whole datagram. */
1475
+ receive(datagram: u8[], now: i64): i64 {
1476
+ return this.receiveWindow(datagram, QUIC_CONN_FROM, toI32(datagram.length), now)
1477
+ }
1478
+
1479
+ /**
1480
+ * Takes one datagram from the client, `buf[off .. off + len)`, which
1481
+ * arrived at `now` (the caller's monotonic time in milliseconds): every
1482
+ * packet in it is opened and its frames acted on, in order. `buf` is read,
1483
+ * never written, so a GRO receive buffer is handed over a datagram at a
1484
+ * time. Answers 0, or the error the connection closed with — in which case
1485
+ * `takeDatagramInto` writes the CONNECTION_CLOSE to send. A packet that
1486
+ * cannot be used is dropped and counted (see the module header). Once the
1487
+ * connection has closed, or its idle timeout has passed by `now`, every
1488
+ * datagram is ignored.
1489
+ */
1490
+ receiveWindow(buf: u8[], off: i32, len: i32, now: i64): i64 {
1491
+ this.runClocks(now)
1492
+ if (this.closed()) {
1493
+ return this.error
1494
+ }
1495
+ if (off < 0 || len < 0 || off > toI32(buf.length) - len) {
1496
+ this.drop()
1497
+ return 0
1498
+ }
1499
+ // §14.1: a client's first Initial comes in a datagram of at least 1200 bytes.
1500
+ if (this.state === QUIC_STATE_WAIT_INITIAL && len < QUIC_CONN_DATAGRAM_SIZE) {
1501
+ this.drop()
1502
+ return 0
1503
+ }
1504
+ const end: i32 = off + len
1505
+ let at: i32 = off
1506
+ let counted: boolean = false
1507
+ while (at < end) {
1508
+ if (quicParseHeaderInto(this.header, buf, at, end, QUIC_CONN_CID_LENGTH) !== QUIC_PACKET_OK) {
1509
+ this.drop()
1510
+ break
1511
+ }
1512
+ counted = this.receivePacket(buf, this.header) || counted
1513
+ const type: i32 = this.header.type
1514
+ const next: i32 = this.header.end
1515
+ if (this.closed() || type === QUIC_PACKET_SHORT || next <= at) {
1516
+ break
1517
+ }
1518
+ at = next
1519
+ }
1520
+ // A datagram counts toward the amplification limit once one of its packets
1521
+ // proves it is this connection's (RFC 9000 §8.1).
1522
+ if (counted) {
1523
+ this.bytesReceived = this.bytesReceived + toI64(len)
1524
+ }
1525
+ // Loss recovery's timer runs after the datagram, so an acknowledgement
1526
+ // it carries settles what it acknowledges before a probe is owed.
1527
+ this.runRecoveryTimer()
1528
+ return this.closed() ? this.error : QUIC_ERROR_NO_ERROR
1529
+ }
1530
+
1531
+ /**
1532
+ * Starts the connection from the client's first Initial, `header`: the
1533
+ * original DCID and the client's SCID, and the Initial keys (RFC 9001
1534
+ * §5.2). Answers whether it could: the client's DCID must be at least 8
1535
+ * bytes (§7.2). The rest of the connection — its first local ID and the
1536
+ * `TlsServer` — is made by `establish` once the Initial has opened, so an
1537
+ * Initial that does not authenticate costs only its keys.
1538
+ */
1539
+ start(buf: u8[], header: QuicHeader): boolean {
1540
+ if (
1541
+ header.dcidLength < 8 ||
1542
+ header.dcidLength > QUIC_MAX_CID_LENGTH ||
1543
+ header.scidLength > QUIC_MAX_CID_LENGTH
1544
+ ) {
1545
+ return false
1546
+ }
1547
+ const secrets: boolean = quicInitialSecretsInto(
1548
+ this.kdf,
1549
+ buf,
1550
+ header.dcidStart,
1551
+ header.dcidLength,
1552
+ this.initialClient,
1553
+ this.initialServer
1554
+ )
1555
+ if (!secrets) {
1556
+ return false
1557
+ }
1558
+ this.initial.readKeys = quicKeysInto(
1559
+ this.kdf,
1560
+ this.initial.keySlot(QUIC_CONN_READ_SLOT),
1561
+ QUIC_AEAD_AES_128_GCM,
1562
+ this.initialClient
1563
+ )
1564
+ this.initial.writeKeys = quicKeysInto(
1565
+ this.kdf,
1566
+ this.initial.keySlot(QUIC_CONN_WRITE_SLOT),
1567
+ QUIC_AEAD_AES_128_GCM,
1568
+ this.initialServer
1569
+ )
1570
+ quicCidCopy(this.originalDcid, buf, header.dcidStart, header.dcidLength)
1571
+ this.originalDcidLength = header.dcidLength
1572
+ quicCidCopy(this.peerScid, buf, header.scidStart, header.scidLength)
1573
+ this.peerScidLength = header.scidLength
1574
+ this.state = QUIC_STATE_HANDSHAKE
1575
+ return true
1576
+ }
1577
+
1578
+ /**
1579
+ * The rest of `start`, once the first Initial has opened: the first local
1580
+ * connection ID, and the slot's `TlsServer` started with transport
1581
+ * parameters that name both IDs (RFC 9000 §7.3), the Retry's when
1582
+ * `acceptRetry` set one up, and the first ID's stateless reset token
1583
+ * (§18.2).
1584
+ */
1585
+ establish(): void {
1586
+ const none: u8[] = this.none
1587
+ const token: u8[] = this.tokenScratch
1588
+ quicConnResetTokenInto(
1589
+ this.kdf.sha256,
1590
+ this.config.statelessResetKey,
1591
+ this.localScid,
1592
+ QUIC_CONN_FROM,
1593
+ QUIC_CONN_CID_LENGTH,
1594
+ token,
1595
+ QUIC_CONN_FROM
1596
+ )
1597
+ this.cids.addLocal(this.localScid, token)
1598
+ this.cids.addPeerAt(
1599
+ QUIC_CONN_NONE,
1600
+ QUIC_CONN_NONE,
1601
+ this.peerScid,
1602
+ QUIC_CONN_FROM,
1603
+ this.peerScidLength,
1604
+ none,
1605
+ QUIC_CONN_FROM,
1606
+ false
1607
+ )
1608
+ // §8.1.2: a Retry token the listener checked has validated the address.
1609
+ this.addressValidated = this.retried
1610
+
1611
+ // The parameters, and their encoding, are rewritten in the slot's own
1612
+ // arrays, which grow only when a connection needs more than any before.
1613
+ const params: QuicTransportParameters = this.localParameters
1614
+ if (this.retried) {
1615
+ quicConnRefill(
1616
+ params.originalDcid,
1617
+ this.retryOriginalDcid,
1618
+ QUIC_CONN_FROM,
1619
+ toI32(this.retryOriginalDcid.length)
1620
+ )
1621
+ } else {
1622
+ quicConnRefill(params.originalDcid, this.originalDcid, QUIC_CONN_FROM, this.originalDcidLength)
1623
+ }
1624
+ params.hasOriginalDcid = true
1625
+ params.initialScid = this.localScid
1626
+ params.hasInitialScid = true
1627
+ quicConnRefill(params.retryScid, this.retryScid, QUIC_CONN_FROM, toI32(this.retryScid.length))
1628
+ params.hasRetryScid = this.retried
1629
+ quicConnRefill(params.statelessResetToken, token, QUIC_CONN_FROM, QUIC_RESET_TOKEN_SIZE)
1630
+ params.hasStatelessResetToken = true
1631
+ secureZero(token)
1632
+ params.maxIdleTimeout = this.config.maxIdleTimeout
1633
+ params.initialMaxData = this.config.maxData
1634
+ params.initialMaxStreamDataBidiRemote = this.config.maxStreamData
1635
+ params.initialMaxStreamsBidi = this.config.maxStreamsBidi
1636
+ // Each of these is advertised only when the configuration uses it, so a
1637
+ // configuration without them sends the parameters it always has.
1638
+ params.initialMaxStreamDataBidiLocal = this.config.localStreams > 0 ? this.config.maxStreamData : 0
1639
+ params.initialMaxStreamsUni = this.config.maxStreamsUni
1640
+ params.initialMaxStreamDataUni = this.config.maxStreamsUni > 0 ? this.config.maxStreamData : 0
1641
+ params.maxDatagramFrameSize = this.config.maxDatagramFrameSize
1642
+ params.activeConnectionIdLimit = this.config.activeConnectionIdLimit
1643
+ // The server never migrates and asks the client not to (§9): path
1644
+ // validation beyond answering PATH_CHALLENGE is not here.
1645
+ params.disableActiveMigration = true
1646
+ quicEncodeTransportParametersInto(params, this.localEncoded)
1647
+ // One `TlsServer` a slot, made by its first handshake (so its flight is
1648
+ // sized for these parameters) and restarted, wiped, by each after it.
1649
+ const tls: TlsServer | null = this.tlsSlot
1650
+ if (tls !== null) {
1651
+ tls.restart(this.serverRandom, this.ephemeralPrivate)
1652
+ this.tls = tls
1653
+ } else {
1654
+ const made: TlsServer = new TlsServer(this.tlsConfig, this.serverRandom, this.ephemeralPrivate)
1655
+ this.tlsSlot = made
1656
+ this.tls = made
1657
+ }
1658
+ }
1659
+
1660
+ /**
1661
+ * One packet of a datagram. Answers whether the packet was used: whether it
1662
+ * authenticated as this connection's.
1663
+ */
1664
+ receivePacket(buf: u8[], header: QuicHeader): boolean {
1665
+ const type: i32 = header.type
1666
+ const first: boolean = this.state === QUIC_STATE_WAIT_INITIAL
1667
+ if (first && (type !== QUIC_PACKET_INITIAL || !this.start(buf, header))) {
1668
+ this.drop()
1669
+ return false
1670
+ }
1671
+ const used: boolean = this.openPacket(buf, header, first)
1672
+ // A first Initial that does not open leaves nothing behind but its keys:
1673
+ // the next datagram is read as a first Initial again.
1674
+ if (first && !used) {
1675
+ this.unstart()
1676
+ }
1677
+ return used
1678
+ }
1679
+
1680
+ /** Undoes `start`, for a first Initial that turned out not to authenticate. */
1681
+ unstart(): void {
1682
+ this.state = QUIC_STATE_WAIT_INITIAL
1683
+ this.addressValidated = false
1684
+ this.idleSince = -1
1685
+ this.initial.reset()
1686
+ this.originalDcidLength = 0
1687
+ this.peerScidLength = 0
1688
+ }
1689
+
1690
+ /**
1691
+ * Opens one packet of a connection already started, and acts on its frames.
1692
+ * The opening and the frames run inside an arena block (`openScoped`), so
1693
+ * their temporaries go when it ends; what may grow a buffer of the slot —
1694
+ * a first Initial's `TlsServer` and parameters, a key update, TLS reading
1695
+ * the CRYPTO bytes and what it answers — runs after it. Answers whether
1696
+ * the packet was used.
1697
+ */
1698
+ openPacket(buf: u8[], header: QuicHeader, first: boolean): boolean {
1699
+ const type: i32 = header.type
1700
+ const space: QuicConnSpace | null = this.spaceOf(type)
1701
+ if (space === null || !this.addressedHere(buf, header)) {
1702
+ this.drop()
1703
+ return false
1704
+ }
1705
+ const keys: QuicKeys | null = space.readKeys
1706
+ // RFC 9001 §5.7: no 1-RTT packet is processed before the handshake is complete.
1707
+ if (keys === null || space.discarded || (type === QUIC_PACKET_SHORT && !this.handshakeComplete)) {
1708
+ this.drop()
1709
+ return false
1710
+ }
1711
+ this.cryptoArrived = false
1712
+ this.topUpOwed = false
1713
+ this.phasePending = -1
1714
+ this.resendLevel = -1
1715
+ let used: boolean = false
1716
+ {
1717
+ using _scope = arena()
1718
+ used = this.openScoped(buf, header, space, keys)
1719
+ }
1720
+ if (!used) {
1721
+ return false
1722
+ }
1723
+ if (first) {
1724
+ this.establish()
1725
+ }
1726
+ if (this.phasePending >= 0 && !this.closed()) {
1727
+ this.applyPhase(this.phasePending)
1728
+ }
1729
+ if (this.closed()) {
1730
+ return true
1731
+ }
1732
+ if (type === QUIC_PACKET_HANDSHAKE) {
1733
+ // §8.1: a Handshake packet proves the client holds its address; RFC
1734
+ // 9001 §4.9.1: the server is then done with the Initial keys.
1735
+ this.addressValidated = true
1736
+ this.discard(this.initial)
1737
+ }
1738
+ if (this.cryptoArrived) {
1739
+ this.feedTls()
1740
+ }
1741
+ this.afterTls()
1742
+ if (this.topUpOwed) {
1743
+ this.topUpConnectionIds()
1744
+ }
1745
+ if (this.resendLevel >= 0 && !this.closed()) {
1746
+ this.resendEarly(this.resendLevel)
1747
+ }
1748
+ return true
1749
+ }
1750
+
1751
+ /**
1752
+ * `openPacket`'s half inside the arena block: header protection off, the
1753
+ * payload decrypted (with the other key generation's keys when the Key
1754
+ * Phase bit says so), and every frame acted on. Nothing it calls stores
1755
+ * an allocation, which is what lets the block release the packet's
1756
+ * temporaries. Answers whether the packet was used rather than dropped.
1757
+ */
1758
+ openScoped(buf: u8[], header: QuicHeader, space: QuicConnSpace, keys: QuicKeys): boolean {
1759
+ const packet: QuicPacket = this.packet
1760
+ // Every key generation shares the header-protection key (RFC 9001 §6),
1761
+ // so the current keys take it off whatever the Key Phase bit says.
1762
+ const clear: u8[] = quicUnprotectHeader(keys, buf, header, space.received.largest, packet)
1763
+ const type: i32 = header.type
1764
+ const other: boolean = type === QUIC_PACKET_SHORT && packet.keyPhase !== this.readPhase
1765
+ const open: QuicKeys | null = other ? this.otherReadKeys : keys
1766
+ if (open === null || packet.packetNumber < 0) {
1767
+ this.drop()
1768
+ return false
1769
+ }
1770
+ const payload: u8[] | null = quicDecryptPayload(open, buf, header, clear, packet)
1771
+ if (payload === null) {
1772
+ this.drop()
1773
+ return false
1774
+ }
1775
+ // §17.2, §17.3.1: reserved bits set under a valid tag close the connection.
1776
+ if (packet.error === QUIC_ERR_RESERVED_BITS) {
1777
+ this.fail(QUIC_ERROR_PROTOCOL_VIOLATION, QUIC_CONN_NONE)
1778
+ return true
1779
+ }
1780
+ const pn: i64 = packet.packetNumber
1781
+ if (space.received.contains(pn)) {
1782
+ this.drop()
1783
+ return true
1784
+ }
1785
+ if (type === QUIC_PACKET_SHORT && !this.notePhase(pn, other)) {
1786
+ return true
1787
+ }
1788
+ const eliciting: boolean = this.receiveFrames(space, type, payload, buf, header)
1789
+ if (this.closed()) {
1790
+ return true
1791
+ }
1792
+ // RFC 9002 §6.2.3: a client that sends an ack-eliciting Handshake packet
1793
+ // while the server's Handshake data is in flight may not have it all.
1794
+ if (eliciting && type === QUIC_PACKET_HANDSHAKE) {
1795
+ this.resendLevel = TLS_LEVEL_HANDSHAKE
1796
+ }
1797
+ space.received.record(pn, eliciting)
1798
+ // §10.1: a packet received and processed restarts the idle timer.
1799
+ this.idleSince = this.now
1800
+ this.elicitingSent = false
1801
+ return true
1802
+ }
1803
+
1804
+ /**
1805
+ * Checks a 1-RTT packet `pn` that opened under the current read keys, or,
1806
+ * when `other`, under the other set (RFC 9001 §6.2, §6.4, §6.5). Under the
1807
+ * next generation's it is a key update, which `applyPhase` carries out
1808
+ * once the packet is read. Under the previous generation's it is a packet
1809
+ * the network delayed. Either is KEY_UPDATE_ERROR when it breaks §6.4's
1810
+ * order — a higher packet number under older keys than a lower one
1811
+ * already had — and an update past `QUIC_CONN_MAX_KEY_UPDATES` is too.
1812
+ * Answers whether the connection is still open.
1813
+ */
1814
+ notePhase(pn: i64, other: boolean): boolean {
1815
+ if (other && !this.otherIsNext) {
1816
+ if (this.readPhaseLowest >= 0 && pn > this.readPhaseLowest) {
1817
+ this.fail(QUIC_ERROR_KEY_UPDATE, QUIC_CONN_NONE)
1818
+ return false
1819
+ }
1820
+ return true
1821
+ }
1822
+ if (!other) {
1823
+ if (this.readPhaseLowest < 0 || pn < this.readPhaseLowest) {
1824
+ this.readPhaseLowest = pn
1825
+ }
1826
+ if (pn > this.readPhaseHighest) {
1827
+ this.readPhaseHighest = pn
1828
+ }
1829
+ return true
1830
+ }
1831
+ if (pn < this.readPhaseHighest) {
1832
+ this.fail(QUIC_ERROR_KEY_UPDATE, QUIC_CONN_NONE)
1833
+ return false
1834
+ }
1835
+ if (this.writePhase === this.readPhase && this.keyUpdates >= QUIC_CONN_MAX_KEY_UPDATES) {
1836
+ this.fail(QUIC_ERROR_KEY_UPDATE, QUIC_CONN_NONE)
1837
+ return false
1838
+ }
1839
+ this.phasePending = pn
1840
+ return true
1841
+ }
1842
+
1843
+ /**
1844
+ * Carries out the client's key update `notePhase` found in packet `pn`
1845
+ * (RFC 9001 §6.2): the write keys follow, unless the server started this
1846
+ * update, before anything acknowledges the packet; the read keys move on,
1847
+ * and the previous ones are kept for a probe timeout.
1848
+ */
1849
+ applyPhase(pn: i64): void {
1850
+ if (this.writePhase === this.readPhase) {
1851
+ this.keyUpdates = this.keyUpdates + 1
1852
+ if (!this.updateWriteKeys()) {
1853
+ this.fail(QUIC_ERROR_INTERNAL, QUIC_CONN_NONE)
1854
+ return
1855
+ }
1856
+ }
1857
+ const previous: QuicKeys | null = this.application.readKeys
1858
+ this.application.readKeys = this.otherReadKeys
1859
+ this.otherReadKeys = previous
1860
+ this.otherIsNext = false
1861
+ this.appReadSlot = 1 - this.appReadSlot
1862
+ secureZero(this.appReadSecret)
1863
+ this.appReadSecret = this.nextReadSecret
1864
+ this.readSecretCurrent = this.readSecretCurrent === 0 ? 1 : 0
1865
+ this.nextReadSecret = this.none
1866
+ this.readPhase = !this.readPhase
1867
+ this.readPhaseLowest = pn
1868
+ this.readPhaseHighest = pn
1869
+ this.keyRetainUntil = this.now + this.recovery.probeTimeout()
1870
+ }
1871
+
1872
+ /**
1873
+ * Moves the write keys to the next generation (RFC 9001 §6.1): the next
1874
+ * secret by `quic ku`, its key and IV, the Key Phase bit toggled; the old
1875
+ * key, IV and secret wiped. Answers whether it could, which it cannot only
1876
+ * for keys `quicKeys` did not make.
1877
+ */
1878
+ updateWriteKeys(): boolean {
1879
+ const keys: QuicKeys | null = this.application.writeKeys
1880
+ if (keys === null) {
1881
+ return false
1882
+ }
1883
+ const target: i32 = this.writeSecretCurrent === 0 ? 1 : 0
1884
+ const secret: u8[] = quicConnSecretAt(this.writeSecrets, keys.aead, target)
1885
+ if (!quicKeyUpdateSecretInto(this.kdf, keys.aead, this.appWriteSecret, secret)) {
1886
+ return false
1887
+ }
1888
+ const slot: QuicKeysSlot = this.application.keySlot(QUIC_CONN_APP_WRITE_SLOTS + 1 - this.appWriteSlot)
1889
+ const next: QuicKeys | null = quicKeysUpdateInto(this.kdf, slot, keys, secret)
1890
+ if (next === null) {
1891
+ secureZero(secret)
1892
+ return false
1893
+ }
1894
+ quicConnWipePacketKey(keys)
1895
+ secureZero(this.appWriteSecret)
1896
+ this.application.writeKeys = next
1897
+ this.appWriteSecret = secret
1898
+ this.writeSecretCurrent = target
1899
+ this.appWriteSlot = 1 - this.appWriteSlot
1900
+ this.writePhase = !this.writePhase
1901
+ this.writePhaseFirst = this.application.nextPn
1902
+ return true
1903
+ }
1904
+
1905
+ /**
1906
+ * Derives the next generation's read secret and keys from the current
1907
+ * ones, into `otherReadKeys`, wiping the previous generation's key and IV
1908
+ * that sat there (RFC 9001 §6.3, §6.5). It runs when the 1-RTT keys are
1909
+ * installed and a probe timeout after each update, never while a packet is
1910
+ * being opened, so what a packet's Key Phase bit says shows in no timing.
1911
+ */
1912
+ prepareNextReadKeys(): void {
1913
+ const keys: QuicKeys | null = this.application.readKeys
1914
+ quicConnWipePacketKey(this.otherReadKeys)
1915
+ this.otherReadKeys = null
1916
+ this.otherIsNext = false
1917
+ this.keyRetainUntil = -1
1918
+ if (keys === null) {
1919
+ return
1920
+ }
1921
+ const secret: u8[] = quicConnSecretAt(this.readSecrets, keys.aead, this.readSecretCurrent === 0 ? 1 : 0)
1922
+ if (!quicKeyUpdateSecretInto(this.kdf, keys.aead, this.appReadSecret, secret)) {
1923
+ return
1924
+ }
1925
+ this.nextReadSecret = secret
1926
+ this.otherReadKeys = quicKeysUpdateInto(
1927
+ this.kdf,
1928
+ this.application.keySlot(1 - this.appReadSlot),
1929
+ keys,
1930
+ secret
1931
+ )
1932
+ this.otherIsNext = this.otherReadKeys !== null
1933
+ }
1934
+
1935
+ /**
1936
+ * Whether `header`'s connection IDs are this connection's: a long header
1937
+ * from the client's first SCID to the server's ID (or, for an Initial,
1938
+ * the original DCID), a short header to an active local ID. Read in place
1939
+ * from `buf`.
1940
+ */
1941
+ addressedHere(buf: u8[], header: QuicHeader): boolean {
1942
+ if (header.type === QUIC_PACKET_SHORT) {
1943
+ return this.cids.ownsLocalAt(buf, header.dcidStart, header.dcidLength)
1944
+ }
1945
+ if (
1946
+ header.scidLength !== this.peerScidLength ||
1947
+ !timingSafeEqualAt(buf, header.scidStart, this.peerScid, QUIC_CONN_FROM, this.peerScidLength)
1948
+ ) {
1949
+ return false
1950
+ }
1951
+ if (
1952
+ header.dcidLength === QUIC_CONN_CID_LENGTH &&
1953
+ timingSafeEqualAt(buf, header.dcidStart, this.localScid, QUIC_CONN_FROM, QUIC_CONN_CID_LENGTH)
1954
+ ) {
1955
+ return true
1956
+ }
1957
+ return (
1958
+ header.type === QUIC_PACKET_INITIAL &&
1959
+ header.dcidLength === this.originalDcidLength &&
1960
+ timingSafeEqualAt(buf, header.dcidStart, this.originalDcid, QUIC_CONN_FROM, this.originalDcidLength)
1961
+ )
1962
+ }
1963
+
1964
+ /**
1965
+ * Acts on every frame of a packet's payload, in order, and answers whether
1966
+ * any asked for an acknowledgement. A payload with no frames, a frame that
1967
+ * does not parse and a frame the packet type may not carry close the
1968
+ * connection (RFC 9000 §12.4).
1969
+ */
1970
+ receiveFrames(
1971
+ space: QuicConnSpace,
1972
+ packetType: i32,
1973
+ payload: u8[],
1974
+ buf: u8[],
1975
+ header: QuicHeader
1976
+ ): boolean {
1977
+ const end: i32 = toI32(payload.length)
1978
+ if (end === 0) {
1979
+ this.fail(QUIC_ERROR_PROTOCOL_VIOLATION, QUIC_CONN_NONE)
1980
+ return false
1981
+ }
1982
+ const frame: QuicFrame = this.frame
1983
+ let eliciting: boolean = false
1984
+ let at: i32 = 0
1985
+ while (at < end) {
1986
+ const error: i64 = quicParseFrame(frame, payload, at, end)
1987
+ if (error !== QUIC_ERROR_NO_ERROR) {
1988
+ this.fail(error, toI64(frame.type))
1989
+ return eliciting
1990
+ }
1991
+ if (!quicFrameAllowed(frame.type, packetType)) {
1992
+ this.fail(QUIC_ERROR_PROTOCOL_VIOLATION, toI64(frame.type))
1993
+ return eliciting
1994
+ }
1995
+ eliciting = eliciting || quicFrameAckEliciting(frame.type)
1996
+ this.receiveFrame(space, frame, payload, at, buf, header)
1997
+ if (this.closed() || frame.end <= at) {
1998
+ return eliciting
1999
+ }
2000
+ at = frame.end
2001
+ }
2002
+ return eliciting
2003
+ }
2004
+
2005
+ /** Closes the connection with `error` from a stream frame of `type`, unless it is 0. */
2006
+ failOn(error: i64, type: i64): void {
2007
+ if (error !== QUIC_ERROR_NO_ERROR) {
2008
+ this.fail(error, type)
2009
+ }
2010
+ }
2011
+
2012
+ /**
2013
+ * One frame, which the packet type is allowed to carry: it starts at
2014
+ * `payload[frameStart]`, and the packet's header is `header` in `buf`.
2015
+ */
2016
+ receiveFrame(
2017
+ space: QuicConnSpace,
2018
+ frame: QuicFrame,
2019
+ payload: u8[],
2020
+ frameStart: i32,
2021
+ buf: u8[],
2022
+ header: QuicHeader
2023
+ ): void {
2024
+ const type: i64 = toI64(frame.type)
2025
+ const streams: QuicStreams = this.streams
2026
+ switch (frame.type) {
2027
+ case QUIC_FRAME_ACK:
2028
+ case QUIC_FRAME_ACK_ECN:
2029
+ this.receiveAck(space, frame)
2030
+ break
2031
+ case QUIC_FRAME_CRYPTO:
2032
+ this.receiveCrypto(space, frame, payload)
2033
+ break
2034
+ case QUIC_FRAME_STREAM:
2035
+ this.failOn(
2036
+ streams.onStream(
2037
+ frame.streamId,
2038
+ frame.offset,
2039
+ payload,
2040
+ frame.dataStart,
2041
+ frame.dataLength,
2042
+ frame.fin
2043
+ ),
2044
+ type
2045
+ )
2046
+ break
2047
+ case QUIC_FRAME_RESET_STREAM:
2048
+ this.failOn(streams.onReset(frame.streamId, frame.errorCode, frame.value), type)
2049
+ break
2050
+ case QUIC_FRAME_STOP_SENDING:
2051
+ this.failOn(streams.onStopSending(frame.streamId, frame.errorCode), type)
2052
+ break
2053
+ case QUIC_FRAME_MAX_STREAM_DATA:
2054
+ this.failOn(streams.onMaxStreamData(frame.streamId, frame.value), type)
2055
+ break
2056
+ case QUIC_FRAME_STREAM_DATA_BLOCKED:
2057
+ this.failOn(streams.onStreamDataBlocked(frame.streamId), type)
2058
+ break
2059
+ case QUIC_FRAME_MAX_DATA:
2060
+ streams.onMaxData(frame.value)
2061
+ break
2062
+ case QUIC_FRAME_MAX_STREAMS_BIDI:
2063
+ streams.onMaxStreams(false, frame.value)
2064
+ break
2065
+ case QUIC_FRAME_MAX_STREAMS_UNI:
2066
+ streams.onMaxStreams(true, frame.value)
2067
+ break
2068
+ case QUIC_FRAME_DATAGRAM:
2069
+ this.receiveDatagram(frame, payload, frameStart)
2070
+ break
2071
+ case QUIC_FRAME_NEW_CONNECTION_ID: {
2072
+ // §19.15: a client that chose a zero-length connection ID has none to give.
2073
+ const error: i64 =
2074
+ this.peerScidLength === 0
2075
+ ? QUIC_ERROR_PROTOCOL_VIOLATION
2076
+ : this.cids.addPeerAt(
2077
+ frame.value,
2078
+ frame.retirePriorTo,
2079
+ payload,
2080
+ frame.connectionIdStart,
2081
+ frame.connectionIdLength,
2082
+ payload,
2083
+ frame.resetTokenStart,
2084
+ true
2085
+ )
2086
+ this.failOn(error, type)
2087
+ break
2088
+ }
2089
+ case QUIC_FRAME_RETIRE_CONNECTION_ID: {
2090
+ const error: i64 = this.cids.retireLocal(frame.value, buf, header.dcidStart, header.dcidLength)
2091
+ this.failOn(error, type)
2092
+ this.topUpOwed = error === QUIC_ERROR_NO_ERROR
2093
+ break
2094
+ }
2095
+ case QUIC_FRAME_PATH_CHALLENGE:
2096
+ this.receivePathChallenge(frame, payload)
2097
+ break
2098
+ // A PATH_RESPONSE answers no PATH_CHALLENGE, since the server sends none
2099
+ // (§19.18); NEW_TOKEN and HANDSHAKE_DONE go only to a client (§19.7,
2100
+ // §19.20). Each is a PROTOCOL_VIOLATION here.
2101
+ case QUIC_FRAME_PATH_RESPONSE:
2102
+ case QUIC_FRAME_NEW_TOKEN:
2103
+ case QUIC_FRAME_HANDSHAKE_DONE:
2104
+ this.fail(QUIC_ERROR_PROTOCOL_VIOLATION, type)
2105
+ break
2106
+ case QUIC_FRAME_CONNECTION_CLOSE:
2107
+ this.drain(frame.errorCode, false)
2108
+ break
2109
+ case QUIC_FRAME_CONNECTION_CLOSE_APP:
2110
+ this.drain(frame.errorCode, true)
2111
+ break
2112
+ default:
2113
+ // PADDING, PING, DATA_BLOCKED and STREAMS_BLOCKED ask nothing: credit
2114
+ // and limits follow what the application reads and finishes, not
2115
+ // what the client asks for.
2116
+ break
2117
+ }
2118
+ }
2119
+
2120
+ /** The client closed the connection: enter the draining state, sending nothing more (RFC 9000 §10.2.2). */
2121
+ drain(code: i64, application: boolean): void {
2122
+ this.state = QUIC_STATE_DRAINING
2123
+ this.error = code
2124
+ this.errorIsApplication = application
2125
+ this.closeSent = true
2126
+ }
2127
+
2128
+ /**
2129
+ * An ACK frame: acknowledging a packet number this space never sent is a
2130
+ * PROTOCOL_VIOLATION (§13.1). Otherwise loss recovery reads it (RFC 9002
2131
+ * §5, §6.1, §7): each packet it newly acknowledges releases what it
2132
+ * carried, and each it shows lost has its frames queued again.
2133
+ */
2134
+ receiveAck(space: QuicConnSpace, frame: QuicFrame): void {
2135
+ if (frame.largest >= space.nextPn) {
2136
+ this.fail(QUIC_ERROR_PROTOCOL_VIOLATION, toI64(frame.type))
2137
+ return
2138
+ }
2139
+ if (frame.largest > space.largestAcked) {
2140
+ space.largestAcked = frame.largest
2141
+ }
2142
+ const sent: QuicSentPackets | null = this.recovery.space(space.level)
2143
+ const delay: i64 = this.ackDelayOf(frame.ackDelay)
2144
+ if (
2145
+ sent === null ||
2146
+ this.recovery.onAck(space.level, frame.ackRanges, frame.ackRangeCount, delay, this.now) <= 0
2147
+ ) {
2148
+ return
2149
+ }
2150
+ for (let k: i32 = 0; k < sent.ackedCount; k += 1) {
2151
+ this.untrack(space, sent.ackedSlot(k))
2152
+ }
2153
+ this.packetsLost(space, sent)
2154
+ }
2155
+
2156
+ /**
2157
+ * An ACK frame's ACK Delay in milliseconds: the field scaled by the
2158
+ * client's `ack_delay_exponent` (RFC 9000 §19.3, §18.2). A value too large
2159
+ * to scale is held at 2^40 microseconds first, which is still far past any
2160
+ * `max_ack_delay` it is then bounded by (RFC 9002 §5.3).
2161
+ */
2162
+ ackDelayOf(field: i64): i64 {
2163
+ let exponent: i64 = this.peerParameters.ackDelayExponent
2164
+ if (exponent < 0 || exponent > 20) {
2165
+ exponent = 3
2166
+ }
2167
+ const ceiling: i64 = 1099511627776
2168
+ const value: i64 = field > ceiling || field < 0 ? ceiling : field
2169
+ return (value << exponent) / 1000
2170
+ }
2171
+
2172
+ /** Every packet of `space` that loss recovery's last call declared lost: what each carried is queued again. */
2173
+ packetsLost(space: QuicConnSpace, sent: QuicSentPackets): void {
2174
+ for (let k: i32 = 0; k < sent.lostCount; k += 1) {
2175
+ this.requeue(space, sent.lostSlot(k))
2176
+ }
2177
+ }
2178
+
2179
+ /**
2180
+ * Queues again what the packet in row `row` of `space` carried (RFC 9000
2181
+ * §13.3): its CRYPTO range, its STREAM chunks with their FIN, HANDSHAKE_DONE,
2182
+ * the NEW_CONNECTION_ID of an ID still active, the RETIRE_CONNECTION_ID of
2183
+ * one not queued already, and each stream control frame that still says
2184
+ * something. A DATAGRAM is never in a record, so it never goes again.
2185
+ */
2186
+ requeue(space: QuicConnSpace, row: i32): void {
2187
+ space.resendCrypto(space.cryptoOffset(row), space.cryptoLength(row))
2188
+ if ((space.flags(row) & QUIC_CONN_SENT_HANDSHAKE_DONE) !== 0) {
2189
+ this.handshakeDonePending = true
2190
+ }
2191
+ const streams: i32 = space.streamCount(row)
2192
+ for (let j: i32 = 0; j < streams; j += 1) {
2193
+ const at: i32 = row * QUIC_CONN_PACKET_STREAMS + j
2194
+ if (
2195
+ at >= 0 &&
2196
+ at < toI32(space.sentStreamId.length) &&
2197
+ at < toI32(space.sentStreamOffset.length) &&
2198
+ at < toI32(space.sentStreamLength.length) &&
2199
+ at < toI32(space.sentStreamFin.length)
2200
+ ) {
2201
+ this.streams.chunkLost(
2202
+ space.sentStreamId[at],
2203
+ space.sentStreamOffset[at],
2204
+ space.sentStreamLength[at],
2205
+ toI32(space.sentStreamFin[at]) !== 0
2206
+ )
2207
+ }
2208
+ }
2209
+ const control: i32 = space.controlCount(row)
2210
+ for (let j: i32 = 0; j < control; j += 1) {
2211
+ const at: i32 = row * QUIC_CONN_PACKET_CONTROL + j
2212
+ if (at >= 0 && at < toI32(space.sentControlKind.length) && at < toI32(space.sentControlValue.length)) {
2213
+ this.requeueControl(toI32(space.sentControlKind[at]), space.sentControlValue[at])
2214
+ }
2215
+ }
2216
+ }
2217
+
2218
+ /** A lost control frame of kind `kind` (a sequence number or a stream in `value`), queued again unless it no longer matters. */
2219
+ requeueControl(kind: i32, value: i64): void {
2220
+ if (kind === QUIC_CONN_CONTROL_NEW_CID) {
2221
+ for (const entry of this.cids.local) {
2222
+ if (entry.used && entry.sequence === value) {
2223
+ entry.announced = false
2224
+ }
2225
+ }
2226
+ return
2227
+ }
2228
+ if (kind === QUIC_CONN_CONTROL_RETIRE) {
2229
+ this.cids.queueRetire(value)
2230
+ return
2231
+ }
2232
+ this.streams.controlLost(kind, value)
2233
+ }
2234
+
2235
+ /**
2236
+ * A probe timeout fired (RFC 9002 §6.2.4): in every space with packets in
2237
+ * flight, everything they carry is queued again and the next packets are
2238
+ * probes, sent whatever the window says, with a PING if nothing is left to
2239
+ * carry. During the handshake that resends the Initial and the Handshake
2240
+ * flight together, which is what a client that lost both needs, and does
2241
+ * it twice (`QUIC_CONN_HANDSHAKE_PROBES`), so that one more lost datagram
2242
+ * does not cost another, doubled, probe timeout.
2243
+ */
2244
+ probe(): void {
2245
+ for (let level: i32 = 0; level < 3; level += 1) {
2246
+ const space: QuicConnSpace = this.spaceAt(level)
2247
+ const sent: QuicSentPackets | null = this.recovery.space(level)
2248
+ if (sent === null || space.discarded || sent.inFlight === 0) {
2249
+ continue
2250
+ }
2251
+ for (let k: i32 = 0; k < sent.count; k += 1) {
2252
+ const slot: i32 = sent.slot(k)
2253
+ if (sent.inFlightAt(slot)) {
2254
+ this.requeue(space, slot)
2255
+ }
2256
+ }
2257
+ space.probes = QUIC_CONN_HANDSHAKE_PROBES
2258
+ if (level === TLS_LEVEL_APPLICATION) {
2259
+ space.probes = 1
2260
+ }
2261
+ }
2262
+ }
2263
+
2264
+ /**
2265
+ * Speeds up the handshake (RFC 9002 §6.2.3): the client showed at `level`
2266
+ * that it lacks the server's handshake data there — an Initial with CRYPTO
2267
+ * data the server already read, or an ack-eliciting Handshake packet —
2268
+ * so, while the server's own packets at that level are in flight, the
2269
+ * oldest one's CRYPTO data at that level and at Handshake goes again now,
2270
+ * as one probe datagram, rather than at the probe timeout. It does so at
2271
+ * most `QUIC_CONN_EARLY_RESENDS` times a connection, and not while an
2272
+ * earlier probe is still owed, so each packet the client sends asks for
2273
+ * one datagram at most. The anti-amplification limit holds it like any
2274
+ * datagram (`takeDatagramInto`).
2275
+ */
2276
+ resendEarly(level: i32): void {
2277
+ if (
2278
+ this.earlyResends >= QUIC_CONN_EARLY_RESENDS ||
2279
+ this.initial.probes > 0 ||
2280
+ this.handshake.probes > 0
2281
+ ) {
2282
+ return
2283
+ }
2284
+ const asked: QuicSentPackets | null = this.recovery.space(level)
2285
+ if (asked === null || asked.inFlight === 0) {
2286
+ return
2287
+ }
2288
+ let queued: boolean = false
2289
+ for (let at: i32 = level; at <= TLS_LEVEL_HANDSHAKE; at += 1) {
2290
+ const space: QuicConnSpace = this.spaceAt(at)
2291
+ const sent: QuicSentPackets | null = this.recovery.space(at)
2292
+ if (sent === null || space.discarded || sent.inFlight === 0) {
2293
+ continue
2294
+ }
2295
+ // Compacted, the ring's oldest slot is the oldest packet in flight.
2296
+ sent.compact()
2297
+ const oldest: i32 = sent.slot(QUIC_CONN_FROM)
2298
+ const length: i32 = space.cryptoLength(oldest)
2299
+ if (length > 0) {
2300
+ space.resendCrypto(space.cryptoOffset(oldest), length)
2301
+ space.probes = 1
2302
+ queued = true
2303
+ }
2304
+ }
2305
+ if (queued) {
2306
+ this.earlyResends += 1
2307
+ }
2308
+ }
2309
+
2310
+ /**
2311
+ * A CRYPTO frame: its data put back in order, for TLS to read once the
2312
+ * packet is (`feedTls`). The buffer follows the level the client is
2313
+ * sending at; data of a level it has moved past is a duplicate of what
2314
+ * TLS already read, or, past that, a message TLS is not reading, which is
2315
+ * its alert unexpected_message (RFC 9001 §4.8). A frame too far ahead is
2316
+ * CRYPTO_BUFFER_EXCEEDED.
2317
+ */
2318
+ receiveCrypto(space: QuicConnSpace, frame: QuicFrame, payload: u8[]): void {
2319
+ const reassembly: QuicConnReassembly = this.cryptoIn
2320
+ if (space.level < reassembly.level) {
2321
+ if (frame.offset + toI64(frame.dataLength) > space.cryptoInDelivered) {
2322
+ this.fail(QUIC_ERROR_CRYPTO + QUIC_CONN_ALERT_UNEXPECTED, toI64(QUIC_FRAME_CRYPTO))
2323
+ }
2324
+ return
2325
+ }
2326
+ if (space.level > reassembly.level) {
2327
+ this.spaceAt(reassembly.level).cryptoInDelivered = reassembly.delivered
2328
+ reassembly.reset(space.level)
2329
+ }
2330
+ // RFC 9002 §6.2.3: an Initial with CRYPTO data the server already read
2331
+ // says the client did not get the server's Initial flight.
2332
+ if (space.level === TLS_LEVEL_INITIAL && frame.offset < reassembly.delivered) {
2333
+ this.resendLevel = TLS_LEVEL_INITIAL
2334
+ }
2335
+ if (!reassembly.insert(frame.offset, payload, frame.dataStart, frame.dataLength)) {
2336
+ this.fail(QUIC_ERROR_CRYPTO_BUFFER_EXCEEDED, toI64(QUIC_FRAME_CRYPTO))
2337
+ return
2338
+ }
2339
+ this.cryptoArrived = true
2340
+ }
2341
+
2342
+ /** Hands TLS what now continues the CRYPTO stream, from the buffer itself; an alert closes with CRYPTO_ERROR. */
2343
+ feedTls(): void {
2344
+ const tls: TlsServer | null = this.tls
2345
+ const reassembly: QuicConnReassembly = this.cryptoIn
2346
+ if (tls === null) {
2347
+ return
2348
+ }
2349
+ // A run stops at the buffer's end, so a stream that wraps is two runs.
2350
+ for (let pass: i32 = 0; pass < 2; pass += 1) {
2351
+ const n: i32 = reassembly.run()
2352
+ if (n === 0) {
2353
+ return
2354
+ }
2355
+ const alert: i32 = tls.receive(
2356
+ reassembly.level,
2357
+ reassembly.ring,
2358
+ reassembly.slot(reassembly.delivered),
2359
+ n
2360
+ )
2361
+ reassembly.consume(n)
2362
+ if (alert !== 0) {
2363
+ this.fail(QUIC_ERROR_CRYPTO + toI64(alert), toI64(QUIC_FRAME_CRYPTO))
2364
+ return
2365
+ }
2366
+ }
2367
+ }
2368
+
2369
+ /**
2370
+ * A DATAGRAM frame (RFC 9221 §4), which starts at `payload[frameStart]`:
2371
+ * one the server did not offer to take, or larger than it offered, is a
2372
+ * PROTOCOL_VIOLATION (§3); otherwise its payload waits for
2373
+ * `readDatagram`, or is dropped when the ring is full.
2374
+ */
2375
+ receiveDatagram(frame: QuicFrame, payload: u8[], frameStart: i32): void {
2376
+ const size: i64 = toI64(frame.end - frameStart)
2377
+ if (this.config.maxDatagramFrameSize === 0 || size > this.config.maxDatagramFrameSize) {
2378
+ // `fin` is how the frame says it was 0x31, the type with a Length.
2379
+ this.fail(
2380
+ QUIC_ERROR_PROTOCOL_VIOLATION,
2381
+ toI64(frame.fin ? QUIC_FRAME_DATAGRAM_LENGTH : QUIC_FRAME_DATAGRAM)
2382
+ )
2383
+ return
2384
+ }
2385
+ this.datagramsIn.push(payload, frame.dataStart, frame.dataLength)
2386
+ }
2387
+
2388
+ /**
2389
+ * A PATH_CHALLENGE (§8.2.2): answered with the same eight bytes. Only the
2390
+ * latest four are kept, so a burst of challenges cannot grow anything.
2391
+ */
2392
+ receivePathChallenge(frame: QuicFrame, payload: u8[]): void {
2393
+ if (this.pathCount >= 4) {
2394
+ this.pathHead = 0
2395
+ this.pathCount = 0
2396
+ }
2397
+ const base: i32 = ((this.pathHead + this.pathCount) % 4) * QUIC_PATH_DATA_SIZE
2398
+ for (let k: i32 = 0; k < QUIC_PATH_DATA_SIZE; k += 1) {
2399
+ const from: i32 = frame.dataStart + k
2400
+ if (
2401
+ base + k >= 0 &&
2402
+ base + k < toI32(this.pathData.length) &&
2403
+ from >= 0 &&
2404
+ from < toI32(payload.length)
2405
+ ) {
2406
+ this.pathData[base + k] = payload[from]
2407
+ }
2408
+ }
2409
+ this.pathCount = this.pathCount + 1
2410
+ }
2411
+
2412
+ /**
2413
+ * Whatever TLS's last step made due: its output moved into each level's
2414
+ * CRYPTO stream, the keys of each level it now has secrets for, the
2415
+ * client's transport parameters checked once it has read them, and, when
2416
+ * the handshake completes, the Handshake keys discarded (RFC 9001 §4.9.2),
2417
+ * HANDSHAKE_DONE queued (§4.1.2) and the client given more connection IDs.
2418
+ */
2419
+ afterTls(): void {
2420
+ const tls: TlsServer | null = this.tls
2421
+ if (tls === null || this.closed()) {
2422
+ return
2423
+ }
2424
+ if (!this.initial.discarded) {
2425
+ this.initial.appendCrypto(tls.outputInitial, tls.outputInitialLength)
2426
+ tls.clearOutput(TLS_LEVEL_INITIAL)
2427
+ }
2428
+ if (!this.handshake.discarded) {
2429
+ this.handshake.appendCrypto(tls.outputHandshake, tls.outputHandshakeLength)
2430
+ tls.clearOutput(TLS_LEVEL_HANDSHAKE)
2431
+ }
2432
+ const aead: i32 = quicConnAead(tls.suite)
2433
+ if (
2434
+ !this.handshake.discarded &&
2435
+ this.handshake.writeKeys === null &&
2436
+ tls.state !== TLS_STATE_WAIT_CLIENT_HELLO
2437
+ ) {
2438
+ if (!this.checkPeerParameters(tls)) {
2439
+ return
2440
+ }
2441
+ this.alpn = tls.alpn
2442
+ this.serverName = tls.serverName
2443
+ this.handshake.readKeys = this.keysFor(
2444
+ this.handshake.keySlot(QUIC_CONN_READ_SLOT),
2445
+ aead,
2446
+ tls.readSecret(TLS_LEVEL_HANDSHAKE)
2447
+ )
2448
+ this.handshake.writeKeys = this.keysFor(
2449
+ this.handshake.keySlot(QUIC_CONN_WRITE_SLOT),
2450
+ aead,
2451
+ tls.writeSecret(TLS_LEVEL_HANDSHAKE)
2452
+ )
2453
+ }
2454
+ if (
2455
+ !this.application.discarded &&
2456
+ this.application.writeKeys === null &&
2457
+ tls.state !== TLS_STATE_WAIT_SIGNATURE
2458
+ ) {
2459
+ const write: u8[] | null = tls.writeSecret(TLS_LEVEL_APPLICATION)
2460
+ const read: u8[] | null = tls.readSecret(TLS_LEVEL_APPLICATION)
2461
+ if (write !== null && read !== null) {
2462
+ this.application.writeKeys = this.keysFor(
2463
+ this.application.keySlot(QUIC_CONN_APP_WRITE_SLOTS + this.appWriteSlot),
2464
+ aead,
2465
+ write
2466
+ )
2467
+ this.application.readKeys = this.keysFor(this.application.keySlot(this.appReadSlot), aead, read)
2468
+ // The key update secrets start from `TlsServer`'s own arrays, so the
2469
+ // first update wipes those too, rather than leaving a copy behind.
2470
+ this.appWriteSecret = write
2471
+ this.appReadSecret = read
2472
+ this.prepareNextReadKeys()
2473
+ }
2474
+ }
2475
+ if (tls.state === TLS_STATE_CONNECTED && !this.handshakeComplete) {
2476
+ this.handshakeComplete = true
2477
+ this.recovery.handshakeConfirmed = true
2478
+ this.handshakeDonePending = true
2479
+ this.state = QUIC_STATE_CONNECTED
2480
+ this.discard(this.handshake)
2481
+ this.topUpConnectionIds()
2482
+ }
2483
+ }
2484
+
2485
+ /** `quicKeysInto` `slot` of a secret TLS has, or `null` when it has none yet. */
2486
+ keysFor(slot: QuicKeysSlot, aead: i32, secret: u8[] | null): QuicKeys | null {
2487
+ if (secret === null) {
2488
+ return null
2489
+ }
2490
+ return quicKeysInto(this.kdf, slot, aead, secret)
2491
+ }
2492
+
2493
+ /**
2494
+ * The client's transport parameters (RFC 9000 §7.3, §7.4, §18.2): they must
2495
+ * parse, and must name as `initial_source_connection_id` the SCID its first
2496
+ * Initial came from. Missing, that is TRANSPORT_PARAMETER_ERROR; wrong, a
2497
+ * PROTOCOL_VIOLATION. Answers whether they hold, closing the connection
2498
+ * when they do not. The streams take the client's limits from them, and
2499
+ * the datagrams its `max_datagram_frame_size`.
2500
+ */
2501
+ checkPeerParameters(tls: TlsServer): boolean {
2502
+ const p: QuicTransportParameters = quicParseTransportParametersInto(
2503
+ this.parsedParameters,
2504
+ tls.clientTransportParameters,
2505
+ false
2506
+ )
2507
+ if (p.error !== QUIC_ERROR_NO_ERROR || !p.hasInitialScid) {
2508
+ this.fail(QUIC_ERROR_TRANSPORT_PARAMETER, toI64(QUIC_FRAME_CRYPTO))
2509
+ return false
2510
+ }
2511
+ if (
2512
+ toI32(p.initialScid.length) !== this.peerScidLength ||
2513
+ !timingSafeEqualAt(p.initialScid, 0, this.peerScid, QUIC_CONN_FROM, this.peerScidLength)
2514
+ ) {
2515
+ this.fail(QUIC_ERROR_PROTOCOL_VIOLATION, toI64(QUIC_FRAME_CRYPTO))
2516
+ return false
2517
+ }
2518
+ this.peerParameters = p
2519
+ this.streams.setPeerLimits(
2520
+ p.initialMaxData,
2521
+ p.initialMaxStreamDataBidiLocal,
2522
+ p.initialMaxStreamDataBidiRemote,
2523
+ p.initialMaxStreamDataUni,
2524
+ p.initialMaxStreamsBidi,
2525
+ p.initialMaxStreamsUni
2526
+ )
2527
+ this.peerMaxDatagramFrame = p.maxDatagramFrameSize
2528
+ this.recovery.maxAckDelay = p.maxAckDelay
2529
+ return true
2530
+ }
2531
+
2532
+ /** Discards a space's keys and CRYPTO state (RFC 9001 §4.9), wiping the keys. */
2533
+ discard(space: QuicConnSpace): void {
2534
+ if (space.discarded) {
2535
+ return
2536
+ }
2537
+ space.discarded = true
2538
+ // RFC 9002 §6.4: the space's packets leave flight, uncounted.
2539
+ this.recovery.discardSpace(space.level)
2540
+ space.cryptoResendLow = -1
2541
+ space.cryptoResendHigh = -1
2542
+ space.probes = 0
2543
+ quicConnWipeKeys(space.readKeys)
2544
+ quicConnWipeKeys(space.writeKeys)
2545
+ space.wipeKeySlots()
2546
+ space.readKeys = null
2547
+ space.writeKeys = null
2548
+ space.cryptoOutLength = 0
2549
+ space.cryptoOutHead = 0
2550
+ }
2551
+
2552
+ /**
2553
+ * The HMAC-SHA256 under the connection's seed of the sequence number
2554
+ * `sequence`, whose first 8 bytes are the local connection ID of that
2555
+ * number, into `cidScratch`: so the IDs are unlinkable to anyone without
2556
+ * the seed and need no more entropy. It runs in the connection's HMAC
2557
+ * scratch inside an arena block, and wipes the MAC and the scratch.
2558
+ */
2559
+ deriveConnectionId(sequence: i64): void {
2560
+ const mac: HmacSha256Scratch = this.kdf.sha256
2561
+ using _scope = arena()
2562
+ const counter: u8[] = new Array<u8>(8)
2563
+ for (let k: i32 = 0; k < 8 && k < toI32(counter.length); k += 1) {
2564
+ counter[k] = toU8(toI32((sequence >> (toI64(7 - k) * 8)) & 255))
2565
+ }
2566
+ const tag: u8[] = new Array<u8>(32)
2567
+ mac.begin(this.cidSeed, QUIC_CONN_FROM, toI32(this.cidSeed.length))
2568
+ mac.update(counter, QUIC_CONN_FROM, toI32(counter.length))
2569
+ mac.finishInto(tag, QUIC_CONN_FROM)
2570
+ for (
2571
+ let k: i32 = 0;
2572
+ k < QUIC_CONN_CID_LENGTH && k < toI32(tag.length) && k < toI32(this.cidScratch.length);
2573
+ k += 1
2574
+ ) {
2575
+ this.cidScratch[k] = tag[k]
2576
+ }
2577
+ secureZero(tag)
2578
+ mac.wipe()
2579
+ }
2580
+
2581
+ /**
2582
+ * Issues local connection IDs until the client holds as many as it said it
2583
+ * would take (its `active_connection_id_limit`), up to
2584
+ * `QUIC_CONN_LOCAL_CIDS` at once and `QUIC_CONN_MAX_ISSUED_CIDS` in all;
2585
+ * each goes out in a NEW_CONNECTION_ID frame with the stateless reset
2586
+ * token the configuration's static key gives it. Each ID and token is made
2587
+ * in the slot's scratch and copied into the table, so issuing keeps
2588
+ * nothing (QUIC-3).
2589
+ */
2590
+ topUpConnectionIds(): void {
2591
+ if (!this.handshakeComplete) {
2592
+ return
2593
+ }
2594
+ let want: i64 = this.peerParameters.activeConnectionIdLimit
2595
+ if (want > toI64(QUIC_CONN_LOCAL_CIDS)) {
2596
+ want = toI64(QUIC_CONN_LOCAL_CIDS)
2597
+ }
2598
+ while (this.cids.nextLocal < QUIC_CONN_MAX_ISSUED_CIDS && toI64(this.cids.activeLocal()) < want) {
2599
+ this.deriveConnectionId(this.cids.nextLocal)
2600
+ quicConnResetTokenInto(
2601
+ this.kdf.sha256,
2602
+ this.config.statelessResetKey,
2603
+ this.cidScratch,
2604
+ QUIC_CONN_FROM,
2605
+ QUIC_CONN_CID_LENGTH,
2606
+ this.tokenScratch,
2607
+ QUIC_CONN_FROM
2608
+ )
2609
+ const added: i64 = this.cids.addLocal(this.cidScratch, this.tokenScratch)
2610
+ secureZero(this.tokenScratch)
2611
+ if (added < 0) {
2612
+ return
2613
+ }
2614
+ }
2615
+ }
2616
+
2617
+ /** What CertificateVerify must sign while the handshake waits for it, else `null` (see `TlsServer.signatureInput`). */
2618
+ signatureInput(): u8[] | null {
2619
+ const tls: TlsServer | null = this.tls
2620
+ if (tls === null || this.closed()) {
2621
+ return null
2622
+ }
2623
+ return tls.signatureInput()
2624
+ }
2625
+
2626
+ /**
2627
+ * Hands the signature over `signatureInput()` to TLS, which writes the
2628
+ * rest of the server's flight. Answers 0, or the CRYPTO_ERROR the
2629
+ * connection closed with when TLS refused it (a call at the wrong time, or
2630
+ * an empty signature: `internal_error`).
2631
+ */
2632
+ sign(signature: u8[]): i64 {
2633
+ const tls: TlsServer | null = this.tls
2634
+ if (tls === null || this.closed()) {
2635
+ return this.error
2636
+ }
2637
+ const alert: i32 = tls.sign(signature)
2638
+ if (alert !== 0) {
2639
+ this.fail(QUIC_ERROR_CRYPTO + toI64(alert), QUIC_CONN_NONE)
2640
+ return this.error
2641
+ }
2642
+ this.afterTls()
2643
+ return QUIC_ERROR_NO_ERROR
2644
+ }
2645
+
2646
+ /**
2647
+ * The effective idle timeout in milliseconds (RFC 9000 §10.1): the smaller
2648
+ * of the two sides' `max_idle_timeout`, or the one that is not 0, raised to
2649
+ * `QUIC_CONN_IDLE_FLOOR` and to three probe timeouts; -1 when both are 0,
2650
+ * for none. Until the client's transport parameters are read, the server's
2651
+ * own alone.
2652
+ */
2653
+ idleTimeout(): i64 {
2654
+ const local: i64 = this.config.maxIdleTimeout
2655
+ const peer: i64 = this.peerParameters.maxIdleTimeout
2656
+ let timeout: i64 = local
2657
+ if (local === 0 || (peer !== 0 && peer < local)) {
2658
+ timeout = peer
2659
+ }
2660
+ if (timeout === 0) {
2661
+ return -1
2662
+ }
2663
+ let floor: i64 = this.recovery.probeTimeout() * 3
2664
+ if (floor < QUIC_CONN_IDLE_FLOOR) {
2665
+ floor = QUIC_CONN_IDLE_FLOOR
2666
+ }
2667
+ return timeout < floor ? floor : timeout
2668
+ }
2669
+
2670
+ /** When the idle timeout passes, or -1 when there is none or the timer has not started. */
2671
+ idleDeadline(): i64 {
2672
+ const timeout: i64 = this.idleTimeout()
2673
+ return timeout >= 0 && this.idleSince >= 0 ? this.idleSince + timeout : -1
2674
+ }
2675
+
2676
+ /**
2677
+ * When `handleTimer` is next due, in the caller's milliseconds: the idle
2678
+ * timeout, loss recovery's timer (a time-threshold loss or the probe
2679
+ * timeout), or the end of a key update's probe timeout, whichever comes
2680
+ * first. -1 when nothing is timed: before the first packet, once the
2681
+ * connection has closed, and with no idle timeout, nothing in flight and
2682
+ * no update pending.
2683
+ */
2684
+ deadline(): i64 {
2685
+ if (this.closed() || this.state === QUIC_STATE_WAIT_INITIAL) {
2686
+ return -1
2687
+ }
2688
+ let due: i64 = this.idleDeadline()
2689
+ if (this.keyRetainUntil >= 0 && (due < 0 || this.keyRetainUntil < due)) {
2690
+ due = this.keyRetainUntil
2691
+ }
2692
+ const recovery: i64 = this.recovery.deadline(this.amplificationBlocked())
2693
+ if (recovery >= 0 && (due < 0 || recovery < due)) {
2694
+ due = recovery
2695
+ }
2696
+ return due
2697
+ }
2698
+
2699
+ /**
2700
+ * Whether the anti-amplification limit stops the server sending (RFC 9000
2701
+ * §8.1): the client's address is not validated and three times what it
2702
+ * sent leaves no room for a whole datagram.
2703
+ */
2704
+ amplificationBlocked(): boolean {
2705
+ return !this.addressValidated && this.bytesReceived * 3 - this.bytesSent < toI64(QUIC_CONN_DATAGRAM_SIZE)
2706
+ }
2707
+
2708
+ /**
2709
+ * Runs whatever is due by `now`, the caller's monotonic time in
2710
+ * milliseconds; call it at `deadline()`, or any time. Past the idle timeout
2711
+ * the connection closes silently (`QUIC_STATE_TIMED_OUT`, RFC 9000 §10.1):
2712
+ * no CONNECTION_CLOSE, every key wiped. Past a key update's probe timeout
2713
+ * the previous read keys are wiped and the next generation's derived (RFC
2714
+ * 9001 §6.5). Past loss recovery's timer, packets lost by the time
2715
+ * threshold have their frames queued again, or a probe timeout queues
2716
+ * every space's packets in flight and asks for a probe (RFC 9002 §6.1.2,
2717
+ * §6.2.4): `takeDatagram` then sends it. `receive` and `takeDatagram` run
2718
+ * it themselves. Time never runs backwards here: an earlier `now` than one
2719
+ * already given counts as that one.
2720
+ */
2721
+ handleTimer(now: i64): void {
2722
+ this.runClocks(now)
2723
+ this.runRecoveryTimer()
2724
+ }
2725
+
2726
+ /** `handleTimer`'s idle timeout and key retention, which `receive` runs before it reads a datagram. */
2727
+ runClocks(now: i64): void {
2728
+ if (now > this.now) {
2729
+ this.now = now
2730
+ }
2731
+ if (this.closed() || this.state === QUIC_STATE_WAIT_INITIAL) {
2732
+ return
2733
+ }
2734
+ const idle: i64 = this.idleDeadline()
2735
+ if (idle >= 0 && this.now >= idle) {
2736
+ this.state = QUIC_STATE_TIMED_OUT
2737
+ this.error = QUIC_ERROR_NO_ERROR
2738
+ this.closeSent = true
2739
+ this.wipeAll()
2740
+ return
2741
+ }
2742
+ if (this.keyRetainUntil >= 0 && this.now >= this.keyRetainUntil) {
2743
+ this.prepareNextReadKeys()
2744
+ }
2745
+ }
2746
+
2747
+ /** Loss recovery's timer, if it is due (RFC 9002 §6.2, A.9). */
2748
+ runRecoveryTimer(): void {
2749
+ if (this.closed() || this.state === QUIC_STATE_WAIT_INITIAL) {
2750
+ return
2751
+ }
2752
+ const kind: i32 = this.recovery.onTimeout(this.now, this.amplificationBlocked())
2753
+ if (kind === QUIC_RECOVERY_TIMEOUT_LOSS) {
2754
+ const level: i32 = this.recovery.timeoutSpace
2755
+ const sent: QuicSentPackets | null = this.recovery.space(level)
2756
+ if (sent !== null) {
2757
+ this.packetsLost(this.spaceAt(level), sent)
2758
+ }
2759
+ } else if (kind === QUIC_RECOVERY_TIMEOUT_PTO) {
2760
+ this.probe()
2761
+ }
2762
+ }
2763
+
2764
+ /**
2765
+ * Starts a key update from the server (RFC 9001 §6.1): the write keys move
2766
+ * to the next generation and the Key Phase bit of every packet after
2767
+ * toggles; the client follows when it reads one. Answers whether it did.
2768
+ * It does not while it may not: before the handshake is confirmed, while
2769
+ * an update is still in flight (the client has not answered the last one,
2770
+ * or answered it less than a probe timeout ago, so the next read keys are
2771
+ * not ready), and until the client has acknowledged a packet sent with the
2772
+ * current keys.
2773
+ */
2774
+ updateKeys(): boolean {
2775
+ if (
2776
+ this.state !== QUIC_STATE_CONNECTED ||
2777
+ this.writePhase !== this.readPhase ||
2778
+ !this.otherIsNext ||
2779
+ this.application.largestAcked < this.writePhaseFirst
2780
+ ) {
2781
+ return false
2782
+ }
2783
+ return this.updateWriteKeys()
2784
+ }
2785
+
2786
+ // ---- Streams ------------------------------------------------------------------
2787
+
2788
+ /**
2789
+ * The ID of the next stream with something new for the application — the
2790
+ * client opened it, it has bytes or its FIN to read, it was reset or
2791
+ * stopped, or it has room to write again after a short write — or -1
2792
+ * when there is none. Each stream comes up once however much happened;
2793
+ * read and write it until it has nothing more to say.
2794
+ */
2795
+ nextStreamEvent(): i64 {
2796
+ return this.streams.nextEvent()
2797
+ }
2798
+
2799
+ /**
2800
+ * Opens a stream of the server's (RFC 9000 §2.1): bidirectional when
2801
+ * `bidirectional`, else unidirectional. Answers its ID, or
2802
+ * `QUIC_STREAM_ERR_STATE` when not connected, or `QUIC_STREAM_ERR_LIMIT`
2803
+ * when the client's MAX_STREAMS is reached (STREAMS_BLOCKED then goes out)
2804
+ * or `localStreams` of the server's are open.
2805
+ */
2806
+ openStream(bidirectional: boolean): i64 {
2807
+ if (this.state !== QUIC_STATE_CONNECTED) {
2808
+ return toI64(QUIC_STREAM_ERR_STATE)
2809
+ }
2810
+ return this.streams.open(bidirectional)
2811
+ }
2812
+
2813
+ /**
2814
+ * Writes up to `length` bytes of `buf` from `from` to stream `id`, and
2815
+ * finishes its sending side when `fin` and every byte fit. Answers how many
2816
+ * bytes it took — fewer than `length` when the stream's buffer is full, in
2817
+ * which case `nextStreamEvent` names it again once acknowledgements free
2818
+ * room — or a `QUIC_STREAM_ERR_*`: not connected, an unknown stream, one
2819
+ * only the client sends on, one already finished or reset, or one the
2820
+ * client asked to stop (STOP_SENDING, which this side answered with
2821
+ * RESET_STREAM).
2822
+ */
2823
+ streamWrite(id: i64, buf: u8[], from: i32, length: i32, fin: boolean): i32 {
2824
+ if (this.state !== QUIC_STATE_CONNECTED) {
2825
+ return QUIC_STREAM_ERR_STATE
2826
+ }
2827
+ return this.streams.write(id, buf, from, length, fin)
2828
+ }
2829
+
2830
+ /**
2831
+ * Reads up to `length` bytes of stream `id` into `buf` from `at`. Answers
2832
+ * how many it read, 0 when nothing more has arrived yet, `QUIC_STREAM_END`
2833
+ * once every byte up to the FIN was read, `QUIC_STREAM_ERR_RESET` once the
2834
+ * client reset the stream (its code is the stream's `resetCode`), or
2835
+ * another `QUIC_STREAM_ERR_*`. Reading is what gives the client more credit.
2836
+ */
2837
+ streamRead(id: i64, buf: u8[], at: i32, length: i32): i32 {
2838
+ return this.streams.read(id, buf, at, length)
2839
+ }
2840
+
2841
+ /** Abandons stream `id`'s sending side with application error `code` (RESET_STREAM). Answers `QUIC_STREAM_OK` or a `QUIC_STREAM_ERR_*`. */
2842
+ streamReset(id: i64, code: i64): i32 {
2843
+ if (this.state !== QUIC_STATE_CONNECTED) {
2844
+ return QUIC_STREAM_ERR_STATE
2845
+ }
2846
+ return this.streams.resetStream(id, code)
2847
+ }
2848
+
2849
+ /** Asks the client to stop sending on stream `id`, with application error `code` (STOP_SENDING). Answers `QUIC_STREAM_OK` or a `QUIC_STREAM_ERR_*`. */
2850
+ streamStopSending(id: i64, code: i64): i32 {
2851
+ if (this.state !== QUIC_STATE_CONNECTED) {
2852
+ return QUIC_STREAM_ERR_STATE
2853
+ }
2854
+ return this.streams.stopSending(id, code)
2855
+ }
2856
+
2857
+ /**
2858
+ * The next run of stream data the client sent, or `null` when there is
2859
+ * none: the all-at-once form of `nextStreamEvent` and `streamRead`, which
2860
+ * allocates the run it answers. `fin` marks the last of its stream; a
2861
+ * stream the client reset is passed over.
2862
+ */
2863
+ readStream(): QuicStreamData | null {
2864
+ let id: i64 = this.streams.nextEvent()
2865
+ while (id >= 0) {
2866
+ const stream: QuicStream | null = this.streams.find(id)
2867
+ const available: i64 = stream !== null ? stream.readable() : 0
2868
+ const data: u8[] = new Array<u8>(toI32(available))
2869
+ const n: i32 = this.streams.read(id, data, QUIC_CONN_FROM, toI32(available))
2870
+ // Every byte up to the FIN read: one more read sees the end, which lets the side finish.
2871
+ const end: boolean =
2872
+ n === QUIC_STREAM_END ||
2873
+ (n > 0 && this.streams.read(id, data, QUIC_CONN_FROM, QUIC_CONN_FROM) === QUIC_STREAM_END)
2874
+ if (n > 0 || end) {
2875
+ return new QuicStreamData(id, data, end)
2876
+ }
2877
+ // A reset stream answered QUIC_STREAM_ERR_RESET, which saw it and let
2878
+ // it finish; there is nothing to hand over.
2879
+ id = this.streams.nextEvent()
2880
+ }
2881
+ return null
2882
+ }
2883
+
2884
+ /**
2885
+ * Queues all of `data` on stream `id`, finishing its sending side when
2886
+ * `fin`: the all-or-nothing form of `streamWrite`. Answers
2887
+ * `QUIC_STREAM_OK`, or `QUIC_STREAM_ERR_*`: the connection is not
2888
+ * connected, the stream is unknown, its side is already finished (or the
2889
+ * client stopped it), or the data would pass the credit the client gave
2890
+ * for the stream or the connection, or the room in the stream's buffer.
2891
+ */
2892
+ writeStream(id: i64, data: u8[], fin: boolean): i32 {
2893
+ if (this.state !== QUIC_STATE_CONNECTED) {
2894
+ return QUIC_STREAM_ERR_STATE
2895
+ }
2896
+ const stream: QuicStream | null = this.streams.find(id)
2897
+ if (stream === null) {
2898
+ return QUIC_STREAM_ERR_UNKNOWN
2899
+ }
2900
+ if (stream.finQueued || stream.stopCode >= 0 || stream.sendState >= QUIC_SEND_RESET_SENT) {
2901
+ return QUIC_STREAM_ERR_FINISHED
2902
+ }
2903
+ const length: i64 = toI64(toI32(data.length))
2904
+ if (
2905
+ stream.sendEnd + length > stream.sendLimit ||
2906
+ this.streams.writtenTotal + length > this.streams.sendMaxData ||
2907
+ length > stream.room()
2908
+ ) {
2909
+ return QUIC_STREAM_ERR_FLOW
2910
+ }
2911
+ const n: i32 = this.streams.write(id, data, QUIC_CONN_FROM, toI32(data.length), fin)
2912
+ return n < 0 ? n : QUIC_STREAM_OK
2913
+ }
2914
+
2915
+ // ---- Datagrams ----------------------------------------------------------------
2916
+
2917
+ /**
2918
+ * The largest DATAGRAM payload `sendDatagram` takes now: what fits the
2919
+ * client's `max_datagram_frame_size` with the frame's type and Length, and
2920
+ * a 1-RTT packet of `QUIC_CONN_DATAGRAM_SIZE` with the longest packet
2921
+ * number; 0 when the client takes none, or the server sends none.
2922
+ */
2923
+ maxDatagramPayload(): i32 {
2924
+ const entry: QuicCidEntry | null = this.cids.currentPeerEntry()
2925
+ const cid: i32 = entry !== null ? entry.length : QUIC_CONN_FROM
2926
+ let limit: i64 = toI64(QUIC_CONN_DATAGRAM_SIZE - 1 - cid - 4 - QUIC_AEAD_TAG_SIZE)
2927
+ if (this.peerMaxDatagramFrame < limit) {
2928
+ limit = this.peerMaxDatagramFrame
2929
+ }
2930
+ // The frame is its type, a Length of 1, 2 or 4 bytes, and the payload.
2931
+ let payload: i64 = limit - 2
2932
+ if (payload > 63) {
2933
+ payload = limit - 3
2934
+ }
2935
+ if (payload > 16383) {
2936
+ payload = limit - 5
2937
+ }
2938
+ const entrySize: i64 = toI64(this.datagramsOut.entrySize)
2939
+ if (payload > entrySize) {
2940
+ payload = entrySize
2941
+ }
2942
+ return payload > 0 && this.datagramsOut.capacity() > 0 ? toI32(payload) : QUIC_CONN_FROM
2943
+ }
2944
+
2945
+ /**
2946
+ * Queues `buf[from .. from + length)` as a DATAGRAM (RFC 9221) for the next
2947
+ * packet the congestion window allows; it is never sent again if lost.
2948
+ * Answers `QUIC_DATAGRAM_OK`, `QUIC_DATAGRAM_ERR_DISABLED` when not
2949
+ * connected, the client takes none, or the server takes none and so sends
2950
+ * none, `QUIC_DATAGRAM_ERR_TOO_BIG` for a frame past the client's limit or
2951
+ * a payload past `maxDatagramPayload()`, or `QUIC_DATAGRAM_ERR_FULL` while
2952
+ * the ring holds `QUIC_CONN_DATAGRAM_QUEUE` waiting.
2953
+ */
2954
+ sendDatagram(buf: u8[], from: i32, length: i32): i32 {
2955
+ if (
2956
+ this.state !== QUIC_STATE_CONNECTED ||
2957
+ this.peerMaxDatagramFrame === 0 ||
2958
+ this.datagramsOut.capacity() === 0
2959
+ ) {
2960
+ return QUIC_DATAGRAM_ERR_DISABLED
2961
+ }
2962
+ if (
2963
+ length < 0 ||
2964
+ toI64(quicDatagramSize(length)) > this.peerMaxDatagramFrame ||
2965
+ length > this.maxDatagramPayload()
2966
+ ) {
2967
+ return QUIC_DATAGRAM_ERR_TOO_BIG
2968
+ }
2969
+ if (this.datagramsOut.count >= this.datagramsOut.capacity()) {
2970
+ return QUIC_DATAGRAM_ERR_FULL
2971
+ }
2972
+ return this.datagramsOut.push(buf, from, length) ? QUIC_DATAGRAM_OK : QUIC_DATAGRAM_ERR_TOO_BIG
2973
+ }
2974
+
2975
+ /**
2976
+ * Copies the oldest DATAGRAM payload the client sent into `buf` at `at`,
2977
+ * at most `cap` bytes, and forgets it. Answers its length,
2978
+ * `QUIC_DATAGRAM_NONE` when none is waiting, or `QUIC_DATAGRAM_ERR_ROOM`,
2979
+ * keeping it, when it does not fit.
2980
+ */
2981
+ readDatagram(buf: u8[], at: i32, cap: i32): i32 {
2982
+ return this.datagramsIn.pop(buf, at, cap)
2983
+ }
2984
+
2985
+ // ---- Sending ------------------------------------------------------------------
2986
+
2987
+ /**
2988
+ * The next datagram to send, or `null` when nothing is due: the
2989
+ * allocating form of `takeDatagramInto`, which a test or a recording uses.
2990
+ */
2991
+ takeDatagram(now: i64): u8[] | null {
2992
+ const out: u8[] = new Array<u8>(QUIC_CONN_DATAGRAM_SIZE)
2993
+ const n: i32 = this.takeDatagramInto(out, QUIC_CONN_FROM, now)
2994
+ return n > 0 ? quicConnSlice(out, 0, n) : null
2995
+ }
2996
+
2997
+ /**
2998
+ * Writes the next datagram to send into `out` at `at`, which needs
2999
+ * `QUIC_CONN_DATAGRAM_SIZE` bytes of room, and answers its length, or 0
3000
+ * when nothing is due (or the room is not there). Call it until it answers
3001
+ * 0 after every `receive`, `sign`, write and `close`. It coalesces an
3002
+ * Initial, a Handshake and a 1-RTT packet as each has something to carry,
3003
+ * pads a datagram with an ack-eliciting Initial to 1200 bytes (RFC 9000
3004
+ * §14.1), and before the client's address is validated sends only while
3005
+ * three times what was received allows (§8.1). After a close it writes the
3006
+ * CONNECTION_CLOSE once, then nothing. While the handshake waits for a
3007
+ * signature it writes nothing, so the server's flight leaves whole. Lost
3008
+ * data goes before new data. While the congestion window is full (RFC
3009
+ * 9002 §7), or a space's record of packets in flight is, a space sends
3010
+ * only an ACK, unless a probe timeout asked it for a probe. `now` is the
3011
+ * caller's time in milliseconds: a datagram that elicits an
3012
+ * acknowledgement restarts the idle timer when it is the first since the
3013
+ * client's last packet (RFC 9000 §10.1), and once the idle timeout has
3014
+ * passed nothing goes out. The datagram is built and sealed in place,
3015
+ * inside an arena block: nothing it does allocates past the call. This is
3016
+ * not paced: a carrier that paces calls `nish/net/quic-listener`'s
3017
+ * `quicListenerTakeFlight` instead.
3018
+ */
3019
+ takeDatagramInto(out: u8[], at: i32, now: i64): i32 {
3020
+ this.handleTimer(now)
3021
+ if (
3022
+ this.state === QUIC_STATE_WAIT_INITIAL ||
3023
+ this.state === QUIC_STATE_DRAINING ||
3024
+ this.state === QUIC_STATE_TIMED_OUT ||
3025
+ at < 0 ||
3026
+ at > toI32(out.length) - QUIC_CONN_DATAGRAM_SIZE
3027
+ ) {
3028
+ return 0
3029
+ }
3030
+ let n: i32 = 0
3031
+ if (this.state === QUIC_STATE_CLOSING) {
3032
+ using _scope = arena()
3033
+ n = this.takeCloseInto(out, at)
3034
+ return n
3035
+ }
3036
+ const tls: TlsServer | null = this.tls
3037
+ if (tls !== null && tls.state === TLS_STATE_WAIT_SIGNATURE) {
3038
+ return 0
3039
+ }
3040
+ // Unvalidated, a datagram goes only when a whole padded one fits the budget.
3041
+ if (this.amplificationBlocked()) {
3042
+ return 0
3043
+ }
3044
+ {
3045
+ using _scope = arena()
3046
+ n = this.buildDatagram(out, at)
3047
+ }
3048
+ return n
3049
+ }
3050
+
3051
+ /**
3052
+ * `takeDatagramInto`'s datagram, built and sealed into `out` at `at`: one
3053
+ * packet per space with something to carry, each written in place, the
3054
+ * last padded when an Initial elicits, then each sealed where it lies.
3055
+ * Answers the datagram's length, or 0 when no space had anything.
3056
+ */
3057
+ buildDatagram(out: u8[], at: i32): i32 {
3058
+ let remaining: i32 = QUIC_CONN_DATAGRAM_SIZE
3059
+ let position: i32 = at
3060
+ let paddedInitial: boolean = false
3061
+ let eliciting: boolean = false
3062
+ const open: boolean = this.recovery.canSend()
3063
+ let last: i32 = -1
3064
+ for (let level: i32 = 0; level < 3; level += 1) {
3065
+ const space: QuicConnSpace = this.spaceAt(level)
3066
+ space.builtStart = -1
3067
+ const overhead: i32 = this.overhead(space)
3068
+ const sent: QuicSentPackets | null = this.recovery.space(level)
3069
+ if (sent === null || overhead === 0 || remaining <= overhead + 8) {
3070
+ continue
3071
+ }
3072
+ if (space.probes > 0 && sent.full()) {
3073
+ // A probe has to be recorded: the oldest packet gives up its slot,
3074
+ // and what it carried, already queued again by the probe, stays queued.
3075
+ const slot: i32 = this.recovery.evictOldest(level)
3076
+ if (slot >= 0) {
3077
+ this.requeue(space, slot)
3078
+ }
3079
+ }
3080
+ const elicit: boolean = space.probes > 0 || (open && !sent.full())
3081
+ const headerLength: i32 = overhead - QUIC_AEAD_TAG_SIZE
3082
+ const payloadStart: i32 = position + headerLength
3083
+ let payloadEnd: i32 = this.buildPayloadInto(
3084
+ space,
3085
+ out,
3086
+ payloadStart,
3087
+ payloadStart + remaining - overhead,
3088
+ elicit
3089
+ )
3090
+ if (payloadEnd <= payloadStart) {
3091
+ continue
3092
+ }
3093
+ // RFC 9001 §5.4.2: the packet number and payload give header
3094
+ // protection at least four bytes to sample from.
3095
+ const pnLength: i32 = quicPacketNumberLength(space.nextPn, space.largestAcked)
3096
+ if (pnLength + payloadEnd - payloadStart < 4) {
3097
+ payloadEnd = quicPutPadding(out, payloadEnd, 4 - pnLength - (payloadEnd - payloadStart))
3098
+ }
3099
+ eliciting = eliciting || space.stagedEliciting
3100
+ // RFC 9000 §14.1: a datagram with an ack-eliciting Initial is padded.
3101
+ if (level === TLS_LEVEL_INITIAL) {
3102
+ paddedInitial = space.stagedEliciting
3103
+ }
3104
+ space.builtStart = position
3105
+ space.builtEnd = payloadEnd
3106
+ last = level
3107
+ remaining = remaining - (payloadEnd + QUIC_AEAD_TAG_SIZE - position)
3108
+ position = payloadEnd + QUIC_AEAD_TAG_SIZE
3109
+ }
3110
+ if (last < 0) {
3111
+ return 0
3112
+ }
3113
+ if (paddedInitial && remaining > 0) {
3114
+ const padded: QuicConnSpace = this.spaceAt(last)
3115
+ padded.builtEnd = quicPutPadding(out, padded.builtEnd, remaining)
3116
+ }
3117
+ if (eliciting && !this.elicitingSent) {
3118
+ this.idleSince = this.now
3119
+ this.elicitingSent = true
3120
+ }
3121
+ // A packet that cannot be sealed, which only keys `quicKeys` did not
3122
+ // make cause, leaves the datagram unsendable: none of it goes.
3123
+ let total: i32 = 0
3124
+ let sealed: boolean = true
3125
+ for (let level: i32 = 0; level <= last; level += 1) {
3126
+ const space: QuicConnSpace = this.spaceAt(level)
3127
+ if (space.builtStart >= 0) {
3128
+ const n: i32 = this.sealAt(space, out, space.builtStart, space.builtEnd)
3129
+ sealed = sealed && n > 0
3130
+ total = total + n
3131
+ }
3132
+ }
3133
+ if (!sealed) {
3134
+ return 0
3135
+ }
3136
+ this.bytesSent = this.bytesSent + toI64(total)
3137
+ return total
3138
+ }
3139
+
3140
+ /**
3141
+ * The bytes a packet of `space` takes beyond its payload: the header with
3142
+ * its packet number, and the AEAD tag. 0 when the space cannot send: no
3143
+ * keys, discarded, or no packet number length for its next number.
3144
+ */
3145
+ overhead(space: QuicConnSpace): i32 {
3146
+ if (space.writeKeys === null || space.discarded) {
3147
+ return 0
3148
+ }
3149
+ const pnLength: i32 = quicPacketNumberLength(space.nextPn, space.largestAcked)
3150
+ if (pnLength === 0) {
3151
+ return 0
3152
+ }
3153
+ if (space.level === TLS_LEVEL_APPLICATION) {
3154
+ const entry: QuicCidEntry | null = this.cids.currentPeerEntry()
3155
+ const cid: i32 = entry !== null ? entry.length : QUIC_CONN_FROM
3156
+ return 1 + cid + pnLength + QUIC_AEAD_TAG_SIZE
3157
+ }
3158
+ // First byte, version, both IDs with their lengths, the Length (two
3159
+ // bytes, always) and, for an Initial, the empty token's length.
3160
+ const token: i32 = space.level === TLS_LEVEL_INITIAL ? 1 : 0
3161
+ return 7 + this.peerScidLength + QUIC_CONN_CID_LENGTH + token + 2 + pnLength + QUIC_AEAD_TAG_SIZE
3162
+ }
3163
+
3164
+ /**
3165
+ * The frames of one packet of `space`, written into `out[start .. end)`:
3166
+ * an ACK when one is due, and, when `elicit` allows frames that elicit an
3167
+ * acknowledgement, CRYPTO data (lost bytes first), at the application
3168
+ * level HANDSHAKE_DONE, RETIRE_CONNECTION_ID, NEW_CONNECTION_ID,
3169
+ * PATH_RESPONSE, the streams' control frames, DATAGRAMs and stream data,
3170
+ * and for a probe with nothing else to carry a PING. Answers the offset
3171
+ * past the last frame, `start` when nothing is due. What it carries is
3172
+ * written to the space's staging row, and `stagedEliciting` says whether
3173
+ * anything elicits an acknowledgement.
3174
+ */
3175
+ buildPayloadInto(space: QuicConnSpace, out: u8[], start: i32, end: i32, elicit: boolean): i32 {
3176
+ let at: i32 = start
3177
+ space.clearStaged()
3178
+ if (space.received.ackPending) {
3179
+ // One too large for the room is left out and stays due.
3180
+ const next: i32 = space.received.putAck(out, at, end, QUIC_CONN_NONE)
3181
+ if (next >= 0) {
3182
+ at = next
3183
+ }
3184
+ }
3185
+ if (!elicit) {
3186
+ return at
3187
+ }
3188
+ at = this.buildCrypto(space, out, at, end)
3189
+ if (space.level === TLS_LEVEL_APPLICATION) {
3190
+ at = this.buildApplication(space, out, at, end)
3191
+ }
3192
+ if (space.probes > 0 && !space.stagedEliciting && at < end) {
3193
+ at = quicPutTypeOnly(out, at, end, QUIC_FRAME_PING)
3194
+ space.stagedEliciting = true
3195
+ }
3196
+ return at
3197
+ }
3198
+
3199
+ /**
3200
+ * One CRYPTO frame into `out[at .. end)`: the bytes lost first, from the
3201
+ * level's kept stream, then those not sent yet. The level's CRYPTO stream
3202
+ * is kept whole, so a byte's index in `cryptoOut` is its offset.
3203
+ */
3204
+ buildCrypto(space: QuicConnSpace, out: u8[], at: i32, end: i32): i32 {
3205
+ if (space.cryptoResendLow >= 0) {
3206
+ const low: i64 = space.cryptoResendLow
3207
+ const want: i32 = toI32(space.cryptoResendHigh - low)
3208
+ const left: i32 = end - at - quicCryptoOverhead(low, want)
3209
+ const n: i32 = left < want ? left : want
3210
+ if (n <= 0) {
3211
+ return at
3212
+ }
3213
+ const next: i32 = quicPutCrypto(out, at, end, low, space.cryptoOut, toI32(low), n)
3214
+ if (next < 0) {
3215
+ return at
3216
+ }
3217
+ space.stageCrypto(low, n)
3218
+ space.cryptoResendLow = low + toI64(n)
3219
+ if (space.cryptoResendLow >= space.cryptoResendHigh) {
3220
+ space.cryptoResendLow = -1
3221
+ space.cryptoResendHigh = -1
3222
+ }
3223
+ return next
3224
+ }
3225
+ const unsent: i32 = space.cryptoUnsent()
3226
+ if (unsent <= 0) {
3227
+ return at
3228
+ }
3229
+ const left: i32 = end - at - quicCryptoOverhead(space.cryptoOutOffset, unsent)
3230
+ const n: i32 = left < unsent ? left : unsent
3231
+ if (n <= 0) {
3232
+ return at
3233
+ }
3234
+ const next: i32 = quicPutCrypto(
3235
+ out,
3236
+ at,
3237
+ end,
3238
+ space.cryptoOutOffset,
3239
+ space.cryptoOut,
3240
+ space.cryptoOutHead,
3241
+ n
3242
+ )
3243
+ if (next < 0) {
3244
+ return at
3245
+ }
3246
+ space.stageCrypto(space.cryptoOutOffset, n)
3247
+ space.cryptoOutHead = space.cryptoOutHead + n
3248
+ space.cryptoOutOffset = space.cryptoOutOffset + toI64(n)
3249
+ return next
3250
+ }
3251
+
3252
+ /**
3253
+ * The 1-RTT frames beyond ACK, as many as fit in `out[at .. end)` and in
3254
+ * the packet's record: `QUIC_CONN_PACKET_CONTROL` control frames and
3255
+ * `QUIC_CONN_PACKET_STREAMS` STREAM frames, and every DATAGRAM that fits.
3256
+ */
3257
+ buildApplication(space: QuicConnSpace, out: u8[], start: i32, end: i32): i32 {
3258
+ let at: i32 = start
3259
+ if (this.handshakeDonePending && at < end) {
3260
+ at = quicPutTypeOnly(out, at, end, QUIC_FRAME_HANDSHAKE_DONE)
3261
+ this.handshakeDonePending = false
3262
+ space.setFlags(space.staging, QUIC_CONN_SENT_HANDSHAKE_DONE)
3263
+ space.stagedEliciting = true
3264
+ }
3265
+ // RETIRE_CONNECTION_ID is at most 9 bytes, NEW_CONNECTION_ID with an
3266
+ // 8-byte ID at most 42, PATH_RESPONSE 9.
3267
+ while (
3268
+ this.cids.retireCount > 0 &&
3269
+ end - at >= 9 &&
3270
+ space.controlCount(space.staging) < QUIC_CONN_PACKET_CONTROL
3271
+ ) {
3272
+ const sequence: i64 = this.cids.takeRetire()
3273
+ at = quicPutValue(out, at, end, QUIC_FRAME_RETIRE_CONNECTION_ID, sequence)
3274
+ space.stageControl(QUIC_CONN_CONTROL_RETIRE, sequence)
3275
+ }
3276
+ let entry: QuicCidEntry | null = this.cids.nextUnannounced()
3277
+ while (entry !== null && end - at >= 42 && space.controlCount(space.staging) < QUIC_CONN_PACKET_CONTROL) {
3278
+ const next: i32 = quicPutNewConnectionId(
3279
+ out,
3280
+ at,
3281
+ end,
3282
+ entry.sequence,
3283
+ QUIC_CONN_NONE,
3284
+ entry.cid,
3285
+ entry.length,
3286
+ entry.resetToken
3287
+ )
3288
+ if (next < 0) {
3289
+ break
3290
+ }
3291
+ at = next
3292
+ entry.announced = true
3293
+ space.stageControl(QUIC_CONN_CONTROL_NEW_CID, entry.sequence)
3294
+ entry = this.cids.nextUnannounced()
3295
+ }
3296
+ while (this.pathCount > 0 && end - at >= 9) {
3297
+ const next: i32 = quicPutPathData(
3298
+ out,
3299
+ at,
3300
+ end,
3301
+ QUIC_FRAME_PATH_RESPONSE,
3302
+ this.pathData,
3303
+ (this.pathHead % 4) * QUIC_PATH_DATA_SIZE
3304
+ )
3305
+ if (next < 0) {
3306
+ break
3307
+ }
3308
+ at = next
3309
+ this.pathHead = (this.pathHead + 1) % 4
3310
+ this.pathCount = this.pathCount - 1
3311
+ // Ack-eliciting, but never sent again (RFC 9000 §13.3).
3312
+ space.stagedEliciting = true
3313
+ }
3314
+ const streams: QuicStreams = this.streams
3315
+ while (space.controlCount(space.staging) < QUIC_CONN_PACKET_CONTROL) {
3316
+ const next: i32 = streams.putNextControl(out, at, end)
3317
+ if (next <= at) {
3318
+ break
3319
+ }
3320
+ at = next
3321
+ space.stageControl(streams.lastKind, streams.lastValue)
3322
+ }
3323
+ // DATAGRAMs ride in any packet the window lets elicit, and are recorded
3324
+ // only by size: a lost one is not sent again (RFC 9221 §5.2).
3325
+ let datagram: i32 = this.datagramsOut.putFrame(out, at, end)
3326
+ while (datagram > at) {
3327
+ at = datagram
3328
+ space.stagedEliciting = true
3329
+ datagram = this.datagramsOut.putFrame(out, at, end)
3330
+ }
3331
+ streams.beginPacket()
3332
+ while (space.streamCount(space.staging) < QUIC_CONN_PACKET_STREAMS) {
3333
+ const next: i32 = streams.putNextChunk(out, at, end)
3334
+ if (next <= at) {
3335
+ break
3336
+ }
3337
+ at = next
3338
+ space.stageChunk(streams.lastId, streams.lastOffset, streams.lastLength, streams.lastFin)
3339
+ }
3340
+ return at
3341
+ }
3342
+
3343
+ /**
3344
+ * Seals the packet of `space` whose payload `buildDatagram` wrote from
3345
+ * `start` plus its header to `payloadEnd`: the header written in front of
3346
+ * it, the payload sealed where it lies, and the packet recorded. Answers
3347
+ * the packet's length, or 0 when it could not be sealed, which only keys
3348
+ * `quicKeys` did not make cause.
3349
+ */
3350
+ sealAt(space: QuicConnSpace, out: u8[], start: i32, payloadEnd: i32): i32 {
3351
+ const keys: QuicKeys | null = space.writeKeys
3352
+ const pn: i64 = space.nextPn
3353
+ const pnLength: i32 = quicPacketNumberLength(pn, space.largestAcked)
3354
+ const headerLength: i32 = this.overhead(space) - QUIC_AEAD_TAG_SIZE
3355
+ const payloadLength: i32 = payloadEnd - start - headerLength
3356
+ const entry: QuicCidEntry | null = this.cids.currentPeerEntry()
3357
+ const headerEnd: i32 =
3358
+ space.level === TLS_LEVEL_APPLICATION
3359
+ ? quicPutShortHeader(
3360
+ out,
3361
+ start,
3362
+ entry !== null ? entry.cid : this.none,
3363
+ entry !== null ? entry.length : 0,
3364
+ false,
3365
+ this.writePhase,
3366
+ pn,
3367
+ pnLength
3368
+ )
3369
+ : quicPutLongHeader(
3370
+ out,
3371
+ start,
3372
+ space.level === TLS_LEVEL_INITIAL ? QUIC_PACKET_INITIAL : QUIC_PACKET_HANDSHAKE,
3373
+ this.peerScid,
3374
+ this.peerScidLength,
3375
+ this.localScid,
3376
+ QUIC_CONN_CID_LENGTH,
3377
+ this.none,
3378
+ pn,
3379
+ pnLength,
3380
+ payloadLength
3381
+ )
3382
+ if (keys === null || headerEnd !== start + headerLength) {
3383
+ return 0
3384
+ }
3385
+ const end: i32 = quicSealInPlace(keys, out, start, headerLength, payloadLength, pn)
3386
+ if (end < 0) {
3387
+ return 0
3388
+ }
3389
+ space.nextPn = pn + 1
3390
+ this.recordSent(space, pn, end - start)
3391
+ return end - start
3392
+ }
3393
+
3394
+ /**
3395
+ * Hands an ack-eliciting packet just sealed to loss recovery (RFC 9002
3396
+ * §A.5), with what it carried copied from the staging row to its slot. A
3397
+ * packet that elicits nothing (an ACK, a CONNECTION_CLOSE) is not
3398
+ * recorded. `buildDatagram` builds nothing ack-eliciting into a space
3399
+ * whose record is full, so recovery does not refuse one; if it ever did,
3400
+ * what the packet carried is queued again at once, as though lost.
3401
+ */
3402
+ recordSent(space: QuicConnSpace, pn: i64, size: i32): void {
3403
+ if (!space.stagedEliciting) {
3404
+ return
3405
+ }
3406
+ const slot: i32 = this.recovery.onPacketSent(space.level, pn, size, this.now)
3407
+ if (slot >= 0) {
3408
+ space.commitStaged(slot)
3409
+ if (space.probes > 0) {
3410
+ space.probes -= 1
3411
+ // §6.2.4: a second probe carries unacknowledged data too. When the
3412
+ // first took everything queued, the oldest — what it just carried —
3413
+ // goes again.
3414
+ if (space.probes > 0 && space.cryptoResendLow < 0) {
3415
+ this.requeue(space, slot)
3416
+ }
3417
+ }
3418
+ } else {
3419
+ this.requeue(space, space.staging)
3420
+ }
3421
+ space.clearStaged()
3422
+ }
3423
+
3424
+ /** An acknowledged packet in row `row`: each stream chunk and stream control frame it carried is settled. */
3425
+ untrack(space: QuicConnSpace, row: i32): void {
3426
+ const streams: i32 = space.streamCount(row)
3427
+ for (let j: i32 = 0; j < streams; j += 1) {
3428
+ const at: i32 = row * QUIC_CONN_PACKET_STREAMS + j
3429
+ if (
3430
+ at >= 0 &&
3431
+ at < toI32(space.sentStreamId.length) &&
3432
+ at < toI32(space.sentStreamOffset.length) &&
3433
+ at < toI32(space.sentStreamLength.length) &&
3434
+ at < toI32(space.sentStreamFin.length)
3435
+ ) {
3436
+ this.streams.chunkAcked(
3437
+ space.sentStreamId[at],
3438
+ space.sentStreamOffset[at],
3439
+ space.sentStreamLength[at],
3440
+ toI32(space.sentStreamFin[at]) !== 0
3441
+ )
3442
+ }
3443
+ }
3444
+ const control: i32 = space.controlCount(row)
3445
+ for (let j: i32 = 0; j < control; j += 1) {
3446
+ const at: i32 = row * QUIC_CONN_PACKET_CONTROL + j
3447
+ if (at >= 0 && at < toI32(space.sentControlKind.length) && at < toI32(space.sentControlValue.length)) {
3448
+ this.streams.controlAcked(toI32(space.sentControlKind[at]), space.sentControlValue[at])
3449
+ }
3450
+ }
3451
+ }
3452
+
3453
+ /**
3454
+ * The one CONNECTION_CLOSE this side owes, written into `out` at `at`,
3455
+ * then nothing (0). It goes in every space the server still has keys for,
3456
+ * since the client may not yet have the newest (RFC 9000 §10.2.3); in an
3457
+ * Initial or Handshake packet an application's close is sent as the
3458
+ * transport close APPLICATION_ERROR, since those packets may not carry
3459
+ * 0x1d (§12.4). Before the handshake is complete no 1-RTT packet carries
3460
+ * it, because the client cannot yet read one.
3461
+ */
3462
+ takeCloseInto(out: u8[], at: i32): i32 {
3463
+ if (this.closeSent) {
3464
+ return 0
3465
+ }
3466
+ this.closeSent = true
3467
+ let position: i32 = at
3468
+ const limit: i32 = at + QUIC_CONN_DATAGRAM_SIZE
3469
+ for (let level: i32 = 0; level < 3; level += 1) {
3470
+ const space: QuicConnSpace = this.spaceAt(level)
3471
+ const overhead: i32 = this.overhead(space)
3472
+ if (overhead === 0 || (level === TLS_LEVEL_APPLICATION && !this.handshakeComplete)) {
3473
+ continue
3474
+ }
3475
+ space.clearStaged()
3476
+ const payloadStart: i32 = position + overhead - QUIC_AEAD_TAG_SIZE
3477
+ const room: i32 = limit - QUIC_AEAD_TAG_SIZE
3478
+ let payloadEnd: i32 =
3479
+ level === TLS_LEVEL_APPLICATION || !this.errorIsApplication
3480
+ ? quicPutConnectionClose(
3481
+ out,
3482
+ payloadStart,
3483
+ room,
3484
+ this.errorIsApplication,
3485
+ this.error,
3486
+ this.errorFrameType,
3487
+ this.none
3488
+ )
3489
+ : quicPutConnectionClose(
3490
+ out,
3491
+ payloadStart,
3492
+ room,
3493
+ false,
3494
+ QUIC_ERROR_APPLICATION,
3495
+ QUIC_CONN_NONE,
3496
+ this.none
3497
+ )
3498
+ if (payloadEnd < 0) {
3499
+ continue
3500
+ }
3501
+ const pnLength: i32 = quicPacketNumberLength(space.nextPn, space.largestAcked)
3502
+ if (pnLength + payloadEnd - payloadStart < 4) {
3503
+ payloadEnd = quicPutPadding(out, payloadEnd, 4 - pnLength - (payloadEnd - payloadStart))
3504
+ }
3505
+ position = position + this.sealAt(space, out, position, payloadEnd)
3506
+ }
3507
+ this.bytesSent = this.bytesSent + toI64(position - at)
3508
+ return position - at
3509
+ }
3510
+
3511
+ /**
3512
+ * Wipes everything secret the connection and its `TlsServer` still hold:
3513
+ * every level's packet keys, the next generation's read keys, the traffic
3514
+ * secrets and expected client Finished, the ephemeral key, the
3515
+ * connection-ID seed and the stateless reset tokens (QUIC-2). Call it when
3516
+ * the connection is done with (or `reset` the slot, which does the same
3517
+ * first); the connection is closed after it and sends nothing more.
3518
+ */
3519
+ release(): void {
3520
+ this.wipeAll()
3521
+ if (!this.closed()) {
3522
+ this.state = QUIC_STATE_CLOSING
3523
+ this.error = QUIC_ERROR_NO_ERROR
3524
+ }
3525
+ this.closeSent = true
3526
+ }
3527
+
3528
+ /**
3529
+ * Wipes every key and secret the connection holds and discards every
3530
+ * level: `release()`'s work, the idle timeout's and `reset`'s. That takes
3531
+ * in both sides' stateless reset tokens and `TlsServer`'s expected client
3532
+ * Finished.
3533
+ */
3534
+ wipeAll(): void {
3535
+ this.discard(this.initial)
3536
+ this.discard(this.handshake)
3537
+ this.discard(this.application)
3538
+ quicConnWipePacketKey(this.otherReadKeys)
3539
+ this.otherReadKeys = null
3540
+ this.otherIsNext = false
3541
+ this.keyRetainUntil = -1
3542
+ secureZero(this.appReadSecret)
3543
+ secureZero(this.appWriteSecret)
3544
+ secureZero(this.nextReadSecret)
3545
+ for (const secret of this.writeSecrets) {
3546
+ secureZero(secret)
3547
+ }
3548
+ for (const secret of this.readSecrets) {
3549
+ secureZero(secret)
3550
+ }
3551
+ secureZero(this.initialClient)
3552
+ secureZero(this.initialServer)
3553
+ secureZero(this.cidSeed)
3554
+ secureZero(this.ephemeralPrivate)
3555
+ // The stateless reset tokens of both sides' IDs: either one ends the
3556
+ // connection for whoever learns it (RFC 9000 §10.3).
3557
+ for (const entry of this.cids.local) {
3558
+ secureZero(entry.resetToken)
3559
+ }
3560
+ for (const entry of this.cids.peer) {
3561
+ secureZero(entry.resetToken)
3562
+ }
3563
+ secureZero(this.peerParameters.statelessResetToken)
3564
+ secureZero(this.localParameters.statelessResetToken)
3565
+ const tls: TlsServer | null = this.tls
3566
+ if (tls !== null) {
3567
+ // The server's encoded transport parameters carry its first ID's token.
3568
+ secureZero(tls.config.quicTransportParameters)
3569
+ secureZero(tls.expectedClientFinished)
3570
+ secureZero(tls.handshakeSecret)
3571
+ secureZero(tls.clientHandshakeSecret)
3572
+ secureZero(tls.serverHandshakeSecret)
3573
+ secureZero(tls.clientApplicationSecret)
3574
+ secureZero(tls.serverApplicationSecret)
3575
+ secureZero(tls.exporterSecret)
3576
+ }
3577
+ }
3578
+ }