@amritk/nish-x86_64-linux 0.16.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 (55) hide show
  1. package/INSTALL.md +43 -34
  2. package/bin/nish +0 -0
  3. package/package.json +1 -1
  4. package/runtime/nish.d.ts +117 -8
  5. package/runtime/nish.h +30 -7
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +43 -24
  8. package/runtime/runtime-net.c +57 -10
  9. package/runtime/runtime-os.c +62 -20
  10. package/runtime/runtime-wasm.c +1 -0
  11. package/runtime/runtime.c +46 -8
  12. package/runtime/shim.mjs +114 -5
  13. package/scripts/build.sh +1 -3
  14. package/std/README.md +166 -35
  15. package/std/crypto/aes.ts +51 -8
  16. package/std/crypto/base64url.ts +25 -12
  17. package/std/crypto/chacha20poly1305.ts +43 -29
  18. package/std/crypto/ct.ts +6 -1
  19. package/std/crypto/hkdf.ts +364 -3
  20. package/std/crypto/hmac.ts +307 -7
  21. package/std/crypto/p256.ts +148 -27
  22. package/std/crypto/sha1.ts +147 -0
  23. package/std/crypto/sha256.ts +3 -2
  24. package/std/crypto/x25519.ts +88 -36
  25. package/std/crypto/x509.ts +130 -47
  26. package/std/net/hpack.ts +1094 -0
  27. package/std/net/http-fields.ts +604 -0
  28. package/std/net/http1-server.ts +1389 -0
  29. package/std/net/http1.ts +1492 -0
  30. package/std/net/http2-frame.ts +745 -0
  31. package/std/net/http2-tls.ts +216 -0
  32. package/std/net/http2.ts +1433 -0
  33. package/std/net/http3-frame.ts +457 -0
  34. package/std/net/http3-server.ts +905 -0
  35. package/std/net/http3.ts +2145 -0
  36. package/std/net/qpack.ts +1163 -0
  37. package/std/net/quic-conn-ack.ts +206 -0
  38. package/std/net/quic-conn-cid.ts +425 -0
  39. package/std/net/quic-conn-params.ts +392 -0
  40. package/std/net/quic-datagram.ts +130 -0
  41. package/std/net/quic-frame.ts +1206 -0
  42. package/std/net/quic-listener.ts +653 -0
  43. package/std/net/quic-packet.ts +1474 -0
  44. package/std/net/quic-recovery.ts +849 -0
  45. package/std/net/quic-stream.ts +1603 -0
  46. package/std/net/quic.ts +3214 -0
  47. package/std/net/tls/codec.ts +1089 -0
  48. package/std/net/tls/record-server.ts +1173 -0
  49. package/std/net/tls/record.ts +621 -0
  50. package/std/net/tls/schedule.ts +429 -0
  51. package/std/net/tls-tcp.ts +410 -0
  52. package/std/net/tls.ts +1368 -0
  53. package/std/net/websocket.ts +823 -0
  54. package/std/net/webtransport.ts +1334 -0
  55. package/std/secret.ts +62 -0
@@ -0,0 +1,392 @@
1
+ /**
2
+ * `nish/net/quic-conn-params` — QUIC's transport parameters (RFC 9000 §18),
3
+ * the bytes each endpoint carries in TLS's `quic_transport_parameters`
4
+ * extension (RFC 9001 §8.2), encoded and parsed.
5
+ *
6
+ * import { QuicTransportParameters, quicEncodeTransportParameters,
7
+ * quicParseTransportParameters } from "nish/net/quic-conn-params";
8
+ *
9
+ * const mine = new QuicTransportParameters();
10
+ * mine.initialMaxData = 65536;
11
+ * const bytes: u8[] = quicEncodeTransportParameters(mine); // for TlsServerConfig
12
+ * const theirs = quicParseTransportParameters(clientBytes, false);
13
+ * if (theirs.error !== 0) { … close with theirs.error … }
14
+ *
15
+ * A `QuicTransportParameters` starts at every parameter's default (§18.2),
16
+ * so a parse leaves a parameter the peer did not send at the value the RFC
17
+ * says it then has. The encoder writes a parameter only when it differs from
18
+ * that default, plus the connection IDs, which have none.
19
+ *
20
+ * **What a parse refuses**, each a TRANSPORT_PARAMETER_ERROR (§7.4, §18.2):
21
+ * an ID or a length that does not fit, a parameter running past the bytes, a
22
+ * known parameter sent twice, a varint-valued parameter whose value is not
23
+ * exactly one varint, `disable_active_migration` with a value, a server-only
24
+ * parameter from a client (`original_destination_connection_id`,
25
+ * `preferred_address`, `retry_source_connection_id`,
26
+ * `stateless_reset_token`), a stateless reset token that is not 16 bytes, a
27
+ * connection ID over 20 bytes, `max_udp_payload_size` below 1200,
28
+ * `ack_delay_exponent` above 20, `max_ack_delay` at 2^14 or above,
29
+ * `active_connection_id_limit` below 2, and a stream count above 2^60.
30
+ * Unknown parameters, the reserved ones (31 × N + 27) among them, are
31
+ * skipped, as §18.1 asks; a second copy of an unknown one is not noticed.
32
+ * Whether `initial_source_connection_id` is present and right is the
33
+ * connection's check, because it needs the packet the ID came in.
34
+ *
35
+ * Written from RFC 9000 §18, in this module's own structure; nothing here is
36
+ * ported from another implementation. Private names carry the
37
+ * `quicParams` prefix (`docs/wp26-stdlib.md` §3e).
38
+ */
39
+ import {
40
+ QUIC_MAX_CID_LENGTH,
41
+ quicVarintLength,
42
+ quicVarintPush,
43
+ quicVarintRead,
44
+ quicVarintSize,
45
+ } from "nish/net/quic-packet"
46
+ import {
47
+ QUIC_ERROR_NO_ERROR,
48
+ QUIC_ERROR_TRANSPORT_PARAMETER,
49
+ QUIC_RESET_TOKEN_SIZE,
50
+ } from "nish/net/quic-frame"
51
+
52
+ // ---- Parameter IDs (RFC 9000 §18.2) ---------------------------------------------
53
+
54
+ export const QUIC_TP_ORIGINAL_DCID: i32 = 0x00
55
+ export const QUIC_TP_MAX_IDLE_TIMEOUT: i32 = 0x01
56
+ export const QUIC_TP_STATELESS_RESET_TOKEN: i32 = 0x02
57
+ export const QUIC_TP_MAX_UDP_PAYLOAD_SIZE: i32 = 0x03
58
+ export const QUIC_TP_INITIAL_MAX_DATA: i32 = 0x04
59
+ export const QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL: i32 = 0x05
60
+ export const QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE: i32 = 0x06
61
+ export const QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI: i32 = 0x07
62
+ export const QUIC_TP_INITIAL_MAX_STREAMS_BIDI: i32 = 0x08
63
+ export const QUIC_TP_INITIAL_MAX_STREAMS_UNI: i32 = 0x09
64
+ export const QUIC_TP_ACK_DELAY_EXPONENT: i32 = 0x0a
65
+ export const QUIC_TP_MAX_ACK_DELAY: i32 = 0x0b
66
+ export const QUIC_TP_DISABLE_ACTIVE_MIGRATION: i32 = 0x0c
67
+ export const QUIC_TP_PREFERRED_ADDRESS: i32 = 0x0d
68
+ export const QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT: i32 = 0x0e
69
+ export const QUIC_TP_INITIAL_SCID: i32 = 0x0f
70
+ export const QUIC_TP_RETRY_SCID: i32 = 0x10
71
+ /**
72
+ * `max_datagram_frame_size` (RFC 9221 §3): the largest DATAGRAM frame, type
73
+ * and Length included, the sender takes; absent or 0, it takes none.
74
+ */
75
+ export const QUIC_TP_MAX_DATAGRAM_FRAME_SIZE: i32 = 0x20
76
+
77
+ /** The smallest `max_udp_payload_size` allowed, and every QUIC path's minimum datagram (RFC 9000 §14). */
78
+ export const QUIC_MIN_UDP_PAYLOAD: i64 = 1200
79
+
80
+ /** A stream count's ceiling, 2^60 (§18.2). A product, because an `i64` literal past 2^53 is refused. */
81
+ const QUIC_PARAMS_MAX_STREAMS: i64 = 1073741824 * 1073741824
82
+
83
+ /**
84
+ * One endpoint's transport parameters. Every field starts at the default
85
+ * §18.2 gives it, and the connection IDs and token start empty with their
86
+ * `has*` flag false. `preferredAddress` is kept as the bytes the server sent,
87
+ * since this side never acts on it. `error` is what a parse answers:
88
+ * `QUIC_ERROR_NO_ERROR`, or `QUIC_ERROR_TRANSPORT_PARAMETER` with
89
+ * `errorParameter` the ID of the parameter at fault (-1 when the bytes did not
90
+ * frame).
91
+ */
92
+ export class QuicTransportParameters {
93
+ error: i64 = 0
94
+ maxIdleTimeout: i64 = 0
95
+ maxUdpPayloadSize: i64 = 65527
96
+ initialMaxData: i64 = 0
97
+ initialMaxStreamDataBidiLocal: i64 = 0
98
+ initialMaxStreamDataBidiRemote: i64 = 0
99
+ initialMaxStreamDataUni: i64 = 0
100
+ initialMaxStreamsBidi: i64 = 0
101
+ initialMaxStreamsUni: i64 = 0
102
+ ackDelayExponent: i64 = 3
103
+ maxAckDelay: i64 = 25
104
+ activeConnectionIdLimit: i64 = 2
105
+ /** RFC 9221's `max_datagram_frame_size`: 0, the default, for no DATAGRAM frames. */
106
+ maxDatagramFrameSize: i64 = 0
107
+ errorParameter: i64 = 0
108
+ originalDcid: u8[]
109
+ statelessResetToken: u8[]
110
+ preferredAddress: u8[]
111
+ initialScid: u8[]
112
+ retryScid: u8[]
113
+ hasOriginalDcid: boolean = false
114
+ hasStatelessResetToken: boolean = false
115
+ hasPreferredAddress: boolean = false
116
+ hasInitialScid: boolean = false
117
+ hasRetryScid: boolean = false
118
+ disableActiveMigration: boolean = false
119
+
120
+ constructor() {
121
+ this.originalDcid = []
122
+ this.statelessResetToken = []
123
+ this.preferredAddress = []
124
+ this.initialScid = []
125
+ this.retryScid = []
126
+ }
127
+ }
128
+
129
+ /** Appends one parameter whose value is a varint, when it differs from `fallback`, its default. */
130
+ const quicParamsPushVarint = (out: u8[], id: i32, value: i64, fallback: i64): void => {
131
+ if (value === fallback || quicVarintSize(value) === 0) {
132
+ return
133
+ }
134
+ quicVarintPush(out, toI64(id))
135
+ quicVarintPush(out, toI64(quicVarintSize(value)))
136
+ quicVarintPush(out, value)
137
+ }
138
+
139
+ /** Appends one parameter whose value is `bytes`, when `present`. */
140
+ const quicParamsPushBytes = (out: u8[], id: i32, bytes: u8[], present: boolean): void => {
141
+ if (!present) {
142
+ return
143
+ }
144
+ quicVarintPush(out, toI64(id))
145
+ quicVarintPush(out, toI64(toI32(bytes.length)))
146
+ for (const b of bytes) {
147
+ out.push(b)
148
+ }
149
+ }
150
+
151
+ /**
152
+ * The transport parameters `p` as the `quic_transport_parameters`
153
+ * extension's body (§18): each parameter as its ID, its length and its
154
+ * value, in ID order, every varint-valued one only when it is not its
155
+ * default, every byte-valued one only when its `has*` flag is set, and
156
+ * `disable_active_migration` only when true. Nothing is checked: these are
157
+ * this endpoint's own values, and a value no varint holds is left out.
158
+ */
159
+ export const quicEncodeTransportParameters = (p: QuicTransportParameters): u8[] => {
160
+ const out: u8[] = []
161
+ quicParamsPushBytes(out, QUIC_TP_ORIGINAL_DCID, p.originalDcid, p.hasOriginalDcid)
162
+ quicParamsPushVarint(out, QUIC_TP_MAX_IDLE_TIMEOUT, p.maxIdleTimeout, 0)
163
+ quicParamsPushBytes(out, QUIC_TP_STATELESS_RESET_TOKEN, p.statelessResetToken, p.hasStatelessResetToken)
164
+ quicParamsPushVarint(out, QUIC_TP_MAX_UDP_PAYLOAD_SIZE, p.maxUdpPayloadSize, 65527)
165
+ quicParamsPushVarint(out, QUIC_TP_INITIAL_MAX_DATA, p.initialMaxData, 0)
166
+ quicParamsPushVarint(out, QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, p.initialMaxStreamDataBidiLocal, 0)
167
+ quicParamsPushVarint(out, QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, p.initialMaxStreamDataBidiRemote, 0)
168
+ quicParamsPushVarint(out, QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI, p.initialMaxStreamDataUni, 0)
169
+ quicParamsPushVarint(out, QUIC_TP_INITIAL_MAX_STREAMS_BIDI, p.initialMaxStreamsBidi, 0)
170
+ quicParamsPushVarint(out, QUIC_TP_INITIAL_MAX_STREAMS_UNI, p.initialMaxStreamsUni, 0)
171
+ quicParamsPushVarint(out, QUIC_TP_ACK_DELAY_EXPONENT, p.ackDelayExponent, 3)
172
+ quicParamsPushVarint(out, QUIC_TP_MAX_ACK_DELAY, p.maxAckDelay, 25)
173
+ const none: u8[] = []
174
+ quicParamsPushBytes(out, QUIC_TP_DISABLE_ACTIVE_MIGRATION, none, p.disableActiveMigration)
175
+ quicParamsPushBytes(out, QUIC_TP_PREFERRED_ADDRESS, p.preferredAddress, p.hasPreferredAddress)
176
+ quicParamsPushVarint(out, QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT, p.activeConnectionIdLimit, 2)
177
+ quicParamsPushBytes(out, QUIC_TP_INITIAL_SCID, p.initialScid, p.hasInitialScid)
178
+ quicParamsPushBytes(out, QUIC_TP_RETRY_SCID, p.retryScid, p.hasRetryScid)
179
+ quicParamsPushVarint(out, QUIC_TP_MAX_DATAGRAM_FRAME_SIZE, p.maxDatagramFrameSize, 0)
180
+ return out
181
+ }
182
+
183
+ /** The bytes `data[from .. from + length)`, copied; the caller has checked the window. */
184
+ const quicParamsCopy = (data: u8[], from: i32, length: i32): u8[] => {
185
+ const out: u8[] = new Array<u8>(length)
186
+ const n: i32 = toI32(out.length)
187
+ for (let k: i32 = 0; k < n; k += 1) {
188
+ if (from + k >= 0 && from + k < toI32(data.length)) {
189
+ out[k] = data[from + k]
190
+ }
191
+ }
192
+ return out
193
+ }
194
+
195
+ /** Fails `p` over parameter `id` and answers it. */
196
+ const quicParamsRefuse = (p: QuicTransportParameters, id: i64): QuicTransportParameters => {
197
+ p.error = QUIC_ERROR_TRANSPORT_PARAMETER
198
+ p.errorParameter = id
199
+ return p
200
+ }
201
+
202
+ /**
203
+ * The value of a varint-valued parameter in `bytes[at .. at + length)`: it
204
+ * must be exactly one varint filling the value. Answers -1, which no varint
205
+ * holds, otherwise.
206
+ */
207
+ const quicParamsVarintValue = (bytes: u8[], at: i32, length: i32): i64 => {
208
+ const value: i64 = quicVarintRead(bytes, at, at + length)
209
+ if (value < 0 || quicVarintLength(bytes, at) !== length) {
210
+ return -1
211
+ }
212
+ return value
213
+ }
214
+
215
+ /**
216
+ * Whether the varint-valued parameter `id` may hold `value` (§18.2): the
217
+ * ranges the RFC sets, and any varint for the rest.
218
+ */
219
+ const quicParamsValueFits = (id: i32, value: i64): boolean => {
220
+ switch (id) {
221
+ case QUIC_TP_MAX_UDP_PAYLOAD_SIZE:
222
+ return value >= QUIC_MIN_UDP_PAYLOAD
223
+ case QUIC_TP_ACK_DELAY_EXPONENT:
224
+ return value <= 20
225
+ case QUIC_TP_MAX_ACK_DELAY:
226
+ return value < 16384
227
+ case QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT:
228
+ return value >= 2
229
+ case QUIC_TP_INITIAL_MAX_STREAMS_BIDI:
230
+ return value <= QUIC_PARAMS_MAX_STREAMS
231
+ case QUIC_TP_INITIAL_MAX_STREAMS_UNI:
232
+ return value <= QUIC_PARAMS_MAX_STREAMS
233
+ default:
234
+ return true
235
+ }
236
+ }
237
+
238
+ /** Stores the varint-valued parameter `id`; the caller has checked that it is one. */
239
+ const quicParamsStoreVarint = (p: QuicTransportParameters, id: i32, value: i64): void => {
240
+ switch (id) {
241
+ case QUIC_TP_MAX_IDLE_TIMEOUT:
242
+ p.maxIdleTimeout = value
243
+ break
244
+ case QUIC_TP_MAX_UDP_PAYLOAD_SIZE:
245
+ p.maxUdpPayloadSize = value
246
+ break
247
+ case QUIC_TP_INITIAL_MAX_DATA:
248
+ p.initialMaxData = value
249
+ break
250
+ case QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL:
251
+ p.initialMaxStreamDataBidiLocal = value
252
+ break
253
+ case QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE:
254
+ p.initialMaxStreamDataBidiRemote = value
255
+ break
256
+ case QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI:
257
+ p.initialMaxStreamDataUni = value
258
+ break
259
+ case QUIC_TP_INITIAL_MAX_STREAMS_BIDI:
260
+ p.initialMaxStreamsBidi = value
261
+ break
262
+ case QUIC_TP_INITIAL_MAX_STREAMS_UNI:
263
+ p.initialMaxStreamsUni = value
264
+ break
265
+ case QUIC_TP_ACK_DELAY_EXPONENT:
266
+ p.ackDelayExponent = value
267
+ break
268
+ case QUIC_TP_MAX_ACK_DELAY:
269
+ p.maxAckDelay = value
270
+ break
271
+ default:
272
+ p.activeConnectionIdLimit = value
273
+ break
274
+ }
275
+ }
276
+
277
+ /** Whether `id` is a parameter only a server may send (§18.2). */
278
+ const quicParamsServerOnly = (id: i32): boolean =>
279
+ id === QUIC_TP_ORIGINAL_DCID ||
280
+ id === QUIC_TP_STATELESS_RESET_TOKEN ||
281
+ id === QUIC_TP_PREFERRED_ADDRESS ||
282
+ id === QUIC_TP_RETRY_SCID
283
+
284
+ /**
285
+ * Stores the byte-valued parameter `id` from `bytes[at .. at + length)`.
286
+ * Answers whether its length is one the parameter allows: 16 for the reset
287
+ * token, at most 20 for a connection ID, 0 for `disable_active_migration`.
288
+ */
289
+ const quicParamsStoreBytes = (
290
+ p: QuicTransportParameters,
291
+ id: i32,
292
+ bytes: u8[],
293
+ at: i32,
294
+ length: i32
295
+ ): boolean => {
296
+ switch (id) {
297
+ case QUIC_TP_ORIGINAL_DCID:
298
+ p.originalDcid = quicParamsCopy(bytes, at, length)
299
+ p.hasOriginalDcid = true
300
+ return length <= QUIC_MAX_CID_LENGTH
301
+ case QUIC_TP_STATELESS_RESET_TOKEN:
302
+ p.statelessResetToken = quicParamsCopy(bytes, at, length)
303
+ p.hasStatelessResetToken = true
304
+ return length === QUIC_RESET_TOKEN_SIZE
305
+ case QUIC_TP_DISABLE_ACTIVE_MIGRATION:
306
+ p.disableActiveMigration = true
307
+ return length === 0
308
+ case QUIC_TP_PREFERRED_ADDRESS:
309
+ p.preferredAddress = quicParamsCopy(bytes, at, length)
310
+ p.hasPreferredAddress = true
311
+ return true
312
+ case QUIC_TP_INITIAL_SCID:
313
+ p.initialScid = quicParamsCopy(bytes, at, length)
314
+ p.hasInitialScid = true
315
+ return length <= QUIC_MAX_CID_LENGTH
316
+ default:
317
+ p.retryScid = quicParamsCopy(bytes, at, length)
318
+ p.hasRetryScid = true
319
+ return length <= QUIC_MAX_CID_LENGTH
320
+ }
321
+ }
322
+
323
+ /** Whether `id` is one of the parameters whose value is a varint. */
324
+ const quicParamsIsVarint = (id: i32): boolean =>
325
+ id === QUIC_TP_MAX_IDLE_TIMEOUT ||
326
+ (id >= QUIC_TP_MAX_UDP_PAYLOAD_SIZE && id <= QUIC_TP_MAX_ACK_DELAY) ||
327
+ id === QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT
328
+
329
+ /**
330
+ * Parses the body of a `quic_transport_parameters` extension. `fromServer`
331
+ * says who sent it, which decides whether the server-only parameters are
332
+ * allowed. The answer's `error` is `QUIC_ERROR_NO_ERROR`, or
333
+ * `QUIC_ERROR_TRANSPORT_PARAMETER` for any of the breaks the module header
334
+ * lists, with `errorParameter` naming the parameter; on an error the other
335
+ * fields hold what was read so far and are not to be used.
336
+ */
337
+ export const quicParseTransportParameters = (bytes: u8[], fromServer: boolean): QuicTransportParameters => {
338
+ const p: QuicTransportParameters = new QuicTransportParameters()
339
+ const end: i32 = toI32(bytes.length)
340
+ // The known IDs are 0 to 16, so one bit each of an i32 records which were
341
+ // seen; RFC 9221's 0x20 has a flag of its own.
342
+ let seen: i32 = 0
343
+ let seenDatagram: boolean = false
344
+ let at: i32 = 0
345
+ while (at < end) {
346
+ const id: i64 = quicVarintRead(bytes, at, end)
347
+ if (id < 0) {
348
+ return quicParamsRefuse(p, -1)
349
+ }
350
+ at = at + quicVarintLength(bytes, at)
351
+ const length: i64 = quicVarintRead(bytes, at, end)
352
+ if (length < 0) {
353
+ return quicParamsRefuse(p, id)
354
+ }
355
+ at = at + quicVarintLength(bytes, at)
356
+ if (length > toI64(end - at)) {
357
+ return quicParamsRefuse(p, id)
358
+ }
359
+ const valueAt: i32 = at
360
+ const valueLength: i32 = toI32(length)
361
+ at = at + valueLength
362
+ if (id === toI64(QUIC_TP_MAX_DATAGRAM_FRAME_SIZE)) {
363
+ const value: i64 = quicParamsVarintValue(bytes, valueAt, valueLength)
364
+ if (seenDatagram || value < 0) {
365
+ return quicParamsRefuse(p, id)
366
+ }
367
+ seenDatagram = true
368
+ p.maxDatagramFrameSize = value
369
+ continue
370
+ }
371
+ if (id > toI64(QUIC_TP_RETRY_SCID)) {
372
+ continue
373
+ }
374
+ const known: i32 = toI32(id)
375
+ const bit: i32 = 1 << known
376
+ if ((seen & bit) !== 0 || (!fromServer && quicParamsServerOnly(known))) {
377
+ return quicParamsRefuse(p, id)
378
+ }
379
+ seen = seen | bit
380
+ if (quicParamsIsVarint(known)) {
381
+ const value: i64 = quicParamsVarintValue(bytes, valueAt, valueLength)
382
+ if (value < 0 || !quicParamsValueFits(known, value)) {
383
+ return quicParamsRefuse(p, id)
384
+ }
385
+ quicParamsStoreVarint(p, known, value)
386
+ } else if (!quicParamsStoreBytes(p, known, bytes, valueAt, valueLength)) {
387
+ return quicParamsRefuse(p, id)
388
+ }
389
+ }
390
+ p.error = QUIC_ERROR_NO_ERROR
391
+ return p
392
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * `nish/net/quic-datagram` — a fixed ring of QUIC DATAGRAM payloads (RFC
3
+ * 9221), one each way per connection: what the application queued to send,
4
+ * waiting for room in a packet and in the congestion window, and what the
5
+ * peer sent, waiting for the application to read it. Sans-IO, like the rest
6
+ * of `nish/net`; `nish/net/quic`'s `QuicConnection` owns both rings and the
7
+ * application reaches them through its `sendDatagram` and `readDatagram`.
8
+ *
9
+ * **Fixed.** A ring is `entries` payloads of at most `entrySize` bytes,
10
+ * allocated once; a payload is copied in and out, and nothing allocates
11
+ * after the ring is made. A datagram is unreliable by definition (RFC 9221
12
+ * §5), so a full ring refuses the next rather than growing, and what the
13
+ * peer sends past a full receive ring, or past `entrySize`, is dropped and
14
+ * counted in `dropped`.
15
+ *
16
+ * Written from RFC 9221, in this module's own structure; nothing here is
17
+ * ported from another implementation. Private names carry the
18
+ * `quicDatagram` prefix (`docs/wp26-stdlib.md` §3e).
19
+ */
20
+ import { quicPutDatagram } from "nish/net/quic-frame"
21
+ import { quicPacketCopy } from "nish/net/quic-packet"
22
+
23
+ /** A ring of `entries` datagram payloads of at most `entrySize` bytes each, oldest first. */
24
+ export class QuicDatagramQueue {
25
+ /** Entry `k`'s bytes are `data[k * entrySize ..]`, `lengths[k]` of them. */
26
+ data: u8[]
27
+ lengths: i32[]
28
+ entrySize: i32 = 0
29
+ head: i32 = 0
30
+ count: i32 = 0
31
+ /** Datagrams refused for want of room or for their size, which the peer's side counts as dropped. */
32
+ dropped: i32 = 0
33
+
34
+ constructor(entries: i32, entrySize: i32) {
35
+ this.data = new Array<u8>(entries * entrySize)
36
+ this.lengths = new Array<i32>(entries)
37
+ this.entrySize = entrySize
38
+ }
39
+
40
+ /** Empties the ring, for a connection slot reused for another peer. */
41
+ reset(): void {
42
+ this.head = 0
43
+ this.count = 0
44
+ this.dropped = 0
45
+ }
46
+
47
+ /** How many datagrams the ring holds at most. */
48
+ capacity(): i32 {
49
+ return toI32(this.lengths.length)
50
+ }
51
+
52
+ /**
53
+ * Copies `buf[from .. from + length)` in as the newest datagram. Answers
54
+ * whether it could: not when the ring is full, nor for a payload past
55
+ * `entrySize` or a window outside `buf`, each of which counts in `dropped`.
56
+ */
57
+ push(buf: u8[], from: i32, length: i32): boolean {
58
+ const entries: i32 = this.capacity()
59
+ if (
60
+ this.count >= entries ||
61
+ length < 0 ||
62
+ length > this.entrySize ||
63
+ from < 0 ||
64
+ from > toI32(buf.length) - length
65
+ ) {
66
+ if (this.dropped < 2147483647) {
67
+ this.dropped = this.dropped + 1
68
+ }
69
+ return false
70
+ }
71
+ const k: i32 = (this.head + this.count) % entries
72
+ const base: i32 = k * this.entrySize
73
+ quicPacketCopy(this.data, base, buf, from, length)
74
+ if (k >= 0 && k < entries) {
75
+ this.lengths[k] = length
76
+ }
77
+ this.count = this.count + 1
78
+ return true
79
+ }
80
+
81
+ /** The oldest datagram's length, or -1 when the ring is empty. */
82
+ peekLength(): i32 {
83
+ const k: i32 = this.head
84
+ return this.count > 0 && k >= 0 && k < toI32(this.lengths.length) ? this.lengths[k] : -1
85
+ }
86
+
87
+ /** Forgets the oldest datagram. */
88
+ drop(): void {
89
+ if (this.count > 0) {
90
+ this.head = (this.head + 1) % this.capacity()
91
+ this.count = this.count - 1
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Copies the oldest datagram into `buf` at `at` and forgets it. Answers
97
+ * its length, -1 when the ring is empty, or -2, keeping it, when it does
98
+ * not fit in `cap` bytes from `at` inside `buf`.
99
+ */
100
+ pop(buf: u8[], at: i32, cap: i32): i32 {
101
+ const length: i32 = this.peekLength()
102
+ if (length < 0) {
103
+ return -1
104
+ }
105
+ if (at < 0 || cap < length || at > toI32(buf.length) - length) {
106
+ return -2
107
+ }
108
+ quicPacketCopy(buf, at, this.data, this.head * this.entrySize, length)
109
+ this.drop()
110
+ return length
111
+ }
112
+
113
+ /**
114
+ * Writes the oldest datagram as a DATAGRAM frame into `buf[at .. end)` and
115
+ * forgets it. Answers the offset past the frame, or `at` when the ring is
116
+ * empty or the frame does not fit, keeping it for the next packet.
117
+ */
118
+ putFrame(buf: u8[], at: i32, end: i32): i32 {
119
+ const length: i32 = this.peekLength()
120
+ if (length < 0) {
121
+ return at
122
+ }
123
+ const next: i32 = quicPutDatagram(buf, at, end, this.data, this.head * this.entrySize, length)
124
+ if (next < 0) {
125
+ return at
126
+ }
127
+ this.drop()
128
+ return next
129
+ }
130
+ }