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