@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.
- package/INSTALL.md +58 -40
- package/bin/nish +0 -0
- package/package.json +1 -1
- package/runtime/nish.d.ts +117 -8
- package/runtime/nish.h +67 -10
- package/runtime/nish.mjs +71 -7
- package/runtime/runtime-host.c +72 -26
- package/runtime/runtime-net.c +72 -19
- package/runtime/runtime-os.c +127 -45
- package/runtime/runtime-parallel.c +5 -1
- package/runtime/runtime-wasm.c +26 -2
- package/runtime/runtime.c +86 -14
- package/runtime/shim.mjs +114 -5
- package/scripts/build.sh +1 -3
- package/std/README.md +192 -36
- package/std/crypto/aes.ts +104 -21
- package/std/crypto/base64url.ts +43 -17
- package/std/crypto/chacha20poly1305.ts +52 -31
- package/std/crypto/ct.ts +14 -3
- package/std/crypto/hkdf.ts +385 -8
- package/std/crypto/hmac.ts +325 -9
- package/std/crypto/p256.ts +153 -27
- package/std/crypto/sha1.ts +147 -0
- package/std/crypto/sha256.ts +15 -3
- package/std/crypto/sha512.ts +16 -2
- package/std/crypto/x25519.ts +90 -37
- package/std/crypto/x509.ts +265 -65
- package/std/net/hpack.ts +1094 -0
- package/std/net/http-fields.ts +604 -0
- package/std/net/http1-server.ts +1389 -0
- package/std/net/http1.ts +1492 -0
- package/std/net/http2-frame.ts +745 -0
- package/std/net/http2-tls.ts +216 -0
- package/std/net/http2.ts +1433 -0
- package/std/net/http3-frame.ts +457 -0
- package/std/net/http3-server.ts +905 -0
- package/std/net/http3.ts +2145 -0
- package/std/net/qpack.ts +1163 -0
- package/std/net/quic-conn-ack.ts +206 -0
- package/std/net/quic-conn-cid.ts +425 -0
- package/std/net/quic-conn-params.ts +392 -0
- package/std/net/quic-datagram.ts +130 -0
- package/std/net/quic-frame.ts +1206 -0
- package/std/net/quic-listener.ts +653 -0
- package/std/net/quic-packet.ts +1474 -0
- package/std/net/quic-recovery.ts +849 -0
- package/std/net/quic-stream.ts +1603 -0
- package/std/net/quic.ts +3214 -0
- package/std/net/tls/codec.ts +1089 -0
- package/std/net/tls/record-server.ts +1173 -0
- package/std/net/tls/record.ts +621 -0
- package/std/net/tls/schedule.ts +429 -0
- package/std/net/tls-tcp.ts +410 -0
- package/std/net/tls.ts +1368 -0
- package/std/net/websocket.ts +823 -0
- package/std/net/webtransport.ts +1334 -0
- package/std/secret.ts +62 -0
|
@@ -0,0 +1,1206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nish/net/quic-frame` — the frames of QUIC version 1 (RFC 9000 §19) and
|
|
3
|
+
* RFC 9221's DATAGRAM, read from a decrypted packet payload and written into
|
|
4
|
+
* one, and the transport error codes a connection closes with (§20.1).
|
|
5
|
+
*
|
|
6
|
+
* import { QuicFrame, quicParseFrame, quicPushAck, QUIC_FRAME_CRYPTO } from "nish/net/quic-frame";
|
|
7
|
+
*
|
|
8
|
+
* const frame = new QuicFrame(); // one per connection, reused
|
|
9
|
+
* let at: i32 = 0;
|
|
10
|
+
* while (at < toI32(payload.length)) {
|
|
11
|
+
* const error: i64 = quicParseFrame(frame, payload, at, toI32(payload.length));
|
|
12
|
+
* if (error !== 0) { … close the connection with `error` … }
|
|
13
|
+
* if (frame.type === QUIC_FRAME_CRYPTO) { … payload[frame.dataStart .. + frame.dataLength) … }
|
|
14
|
+
* at = frame.end;
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* **Every frame type of RFC 9000 is read**, 0x00 to 0x1e, and RFC 9221's
|
|
18
|
+
* DATAGRAM, 0x30 and 0x31, so a type outside those is the one parse error a
|
|
19
|
+
* well-formed packet cannot produce. What
|
|
20
|
+
* a frame *means* is `nish/net/quic`'s: this module checks only what §19 says
|
|
21
|
+
* of the encoding itself, and answers the transport error the RFC names for
|
|
22
|
+
* each break — `QUIC_ERROR_FRAME_ENCODING` for a truncated field, a length
|
|
23
|
+
* past the packet, an ACK range below packet number zero, an offset past
|
|
24
|
+
* 2^62 − 1, a stream count past 2^60, an empty NEW_TOKEN, a connection ID
|
|
25
|
+
* outside 1 to 20 bytes or a Retire Prior To above its own sequence number;
|
|
26
|
+
* `QUIC_ERROR_PROTOCOL_VIOLATION` for a frame type spelled in more bytes than
|
|
27
|
+
* it needs (§12.4). Every read is bounds-checked against the window the caller
|
|
28
|
+
* gives, so nothing a peer sends can make this module panic.
|
|
29
|
+
*
|
|
30
|
+
* **No copy of the bulk.** A CRYPTO, STREAM or DATAGRAM frame's data, a
|
|
31
|
+
* NEW_TOKEN's token, a PATH_CHALLENGE's eight bytes and a CONNECTION_CLOSE's
|
|
32
|
+
* reason are left in the payload and described by `dataStart` and
|
|
33
|
+
* `dataLength`, and a NEW_CONNECTION_ID's ID and token by windows of their
|
|
34
|
+
* own, so a connection reassembles straight from the packet. One `QuicFrame`
|
|
35
|
+
* is reused for every frame of every packet: `quicParseFrame` overwrites
|
|
36
|
+
* every field it reads and resets the rest, and stores nothing but numbers,
|
|
37
|
+
* so a payload made inside an arena block can be read inside it.
|
|
38
|
+
*
|
|
39
|
+
* **The writers** come in two forms. A `put` writer writes one frame into a
|
|
40
|
+
* caller's buffer at an offset, before an end, and answers the offset past
|
|
41
|
+
* it, or -1 with nothing written; a `push` writer appends one to an array
|
|
42
|
+
* and answers whether it could, by growing the array and calling the `put`
|
|
43
|
+
* form, so each frame has one encoder. Either refuses a value no varint
|
|
44
|
+
* holds or a window outside the array given. Their inputs are this
|
|
45
|
+
* endpoint's own, never a peer's. `quicFrameAllowed` is RFC 9000 §12.4's
|
|
46
|
+
* table of which frames each packet type may carry.
|
|
47
|
+
*
|
|
48
|
+
* Written from RFC 9000 §12.4, §19 and §20 and RFC 9221 §4, in this module's own structure;
|
|
49
|
+
* nothing here is ported from another implementation. Private names carry the
|
|
50
|
+
* `quicFrame` prefix because a `std/` module's private functions share the
|
|
51
|
+
* importing program's flat symbol namespace (`docs/wp26-stdlib.md` §3e).
|
|
52
|
+
*/
|
|
53
|
+
import {
|
|
54
|
+
QUIC_MAX_CID_LENGTH,
|
|
55
|
+
QUIC_MAX_VARINT,
|
|
56
|
+
QUIC_PACKET_HANDSHAKE,
|
|
57
|
+
QUIC_PACKET_INITIAL,
|
|
58
|
+
QUIC_PACKET_SHORT,
|
|
59
|
+
QUIC_PACKET_ZERO_RTT,
|
|
60
|
+
quicPacketCopy,
|
|
61
|
+
quicPacketGrow,
|
|
62
|
+
quicVarintLength,
|
|
63
|
+
quicVarintPut,
|
|
64
|
+
quicVarintRead,
|
|
65
|
+
quicVarintSize,
|
|
66
|
+
} from "nish/net/quic-packet"
|
|
67
|
+
|
|
68
|
+
// ---- Frame types (RFC 9000 §19, Table 3 of §12.4) ------------------------------
|
|
69
|
+
|
|
70
|
+
export const QUIC_FRAME_PADDING: i32 = 0x00
|
|
71
|
+
export const QUIC_FRAME_PING: i32 = 0x01
|
|
72
|
+
/** ACK without ECN counts. */
|
|
73
|
+
export const QUIC_FRAME_ACK: i32 = 0x02
|
|
74
|
+
/** ACK with the three ECN counts after the ranges. */
|
|
75
|
+
export const QUIC_FRAME_ACK_ECN: i32 = 0x03
|
|
76
|
+
export const QUIC_FRAME_RESET_STREAM: i32 = 0x04
|
|
77
|
+
export const QUIC_FRAME_STOP_SENDING: i32 = 0x05
|
|
78
|
+
export const QUIC_FRAME_CRYPTO: i32 = 0x06
|
|
79
|
+
export const QUIC_FRAME_NEW_TOKEN: i32 = 0x07
|
|
80
|
+
/**
|
|
81
|
+
* The first of the eight STREAM types, 0x08 to 0x0f. `quicParseFrame` answers
|
|
82
|
+
* every one of them as this type, with the three low bits read into
|
|
83
|
+
* `offset`, `dataLength` and `fin` (§19.8).
|
|
84
|
+
*/
|
|
85
|
+
export const QUIC_FRAME_STREAM: i32 = 0x08
|
|
86
|
+
export const QUIC_FRAME_MAX_DATA: i32 = 0x10
|
|
87
|
+
export const QUIC_FRAME_MAX_STREAM_DATA: i32 = 0x11
|
|
88
|
+
export const QUIC_FRAME_MAX_STREAMS_BIDI: i32 = 0x12
|
|
89
|
+
export const QUIC_FRAME_MAX_STREAMS_UNI: i32 = 0x13
|
|
90
|
+
export const QUIC_FRAME_DATA_BLOCKED: i32 = 0x14
|
|
91
|
+
export const QUIC_FRAME_STREAM_DATA_BLOCKED: i32 = 0x15
|
|
92
|
+
export const QUIC_FRAME_STREAMS_BLOCKED_BIDI: i32 = 0x16
|
|
93
|
+
export const QUIC_FRAME_STREAMS_BLOCKED_UNI: i32 = 0x17
|
|
94
|
+
export const QUIC_FRAME_NEW_CONNECTION_ID: i32 = 0x18
|
|
95
|
+
export const QUIC_FRAME_RETIRE_CONNECTION_ID: i32 = 0x19
|
|
96
|
+
export const QUIC_FRAME_PATH_CHALLENGE: i32 = 0x1a
|
|
97
|
+
export const QUIC_FRAME_PATH_RESPONSE: i32 = 0x1b
|
|
98
|
+
/** CONNECTION_CLOSE with a transport error code and the frame type that caused it. */
|
|
99
|
+
export const QUIC_FRAME_CONNECTION_CLOSE: i32 = 0x1c
|
|
100
|
+
/** CONNECTION_CLOSE with an application error code, allowed only in 1-RTT packets. */
|
|
101
|
+
export const QUIC_FRAME_CONNECTION_CLOSE_APP: i32 = 0x1d
|
|
102
|
+
export const QUIC_FRAME_HANDSHAKE_DONE: i32 = 0x1e
|
|
103
|
+
/**
|
|
104
|
+
* DATAGRAM (RFC 9221 §4): an unreliable application payload. Type 0x30 runs
|
|
105
|
+
* to the end of the packet and 0x31 carries a Length; `quicParseFrame`
|
|
106
|
+
* answers both as this type, with `fin` set for 0x31 (it had a Length).
|
|
107
|
+
*/
|
|
108
|
+
export const QUIC_FRAME_DATAGRAM: i32 = 0x30
|
|
109
|
+
/** The DATAGRAM type with a Length field (RFC 9221 §4), which `quicPutDatagram` always writes. */
|
|
110
|
+
export const QUIC_FRAME_DATAGRAM_LENGTH: i32 = 0x31
|
|
111
|
+
|
|
112
|
+
// ---- Transport error codes (RFC 9000 §20.1) ------------------------------------
|
|
113
|
+
|
|
114
|
+
export const QUIC_ERROR_NO_ERROR: i64 = 0x00
|
|
115
|
+
export const QUIC_ERROR_INTERNAL: i64 = 0x01
|
|
116
|
+
export const QUIC_ERROR_CONNECTION_REFUSED: i64 = 0x02
|
|
117
|
+
export const QUIC_ERROR_FLOW_CONTROL: i64 = 0x03
|
|
118
|
+
export const QUIC_ERROR_STREAM_LIMIT: i64 = 0x04
|
|
119
|
+
export const QUIC_ERROR_STREAM_STATE: i64 = 0x05
|
|
120
|
+
export const QUIC_ERROR_FINAL_SIZE: i64 = 0x06
|
|
121
|
+
export const QUIC_ERROR_FRAME_ENCODING: i64 = 0x07
|
|
122
|
+
export const QUIC_ERROR_TRANSPORT_PARAMETER: i64 = 0x08
|
|
123
|
+
export const QUIC_ERROR_CONNECTION_ID_LIMIT: i64 = 0x09
|
|
124
|
+
export const QUIC_ERROR_PROTOCOL_VIOLATION: i64 = 0x0a
|
|
125
|
+
export const QUIC_ERROR_INVALID_TOKEN: i64 = 0x0b
|
|
126
|
+
export const QUIC_ERROR_APPLICATION: i64 = 0x0c
|
|
127
|
+
export const QUIC_ERROR_CRYPTO_BUFFER_EXCEEDED: i64 = 0x0d
|
|
128
|
+
export const QUIC_ERROR_KEY_UPDATE: i64 = 0x0e
|
|
129
|
+
export const QUIC_ERROR_AEAD_LIMIT_REACHED: i64 = 0x0f
|
|
130
|
+
export const QUIC_ERROR_NO_VIABLE_PATH: i64 = 0x10
|
|
131
|
+
/** CRYPTO_ERROR is this plus the TLS alert description (RFC 9001 §4.8): 0x0100 to 0x01ff. */
|
|
132
|
+
export const QUIC_ERROR_CRYPTO: i64 = 0x0100
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The most ACK ranges a `QuicFrame` keeps. A frame may list more, and every
|
|
136
|
+
* one is still checked; the ones past this are counted in `ackRangeTotal`
|
|
137
|
+
* but not stored. Loss recovery, which would read them, is WP34 Q3.
|
|
138
|
+
*/
|
|
139
|
+
export const QUIC_FRAME_MAX_ACK_RANGES: i32 = 32
|
|
140
|
+
|
|
141
|
+
/** A stream count's ceiling, 2^60 (RFC 9000 §4.6, §19.11). Spelled as a product: an `i64` literal past 2^53 is refused. */
|
|
142
|
+
const QUIC_FRAME_MAX_STREAMS: i64 = 1073741824 * 1073741824
|
|
143
|
+
|
|
144
|
+
/** The length of a stateless reset token (RFC 9000 §10.3). */
|
|
145
|
+
export const QUIC_RESET_TOKEN_SIZE: i32 = 16
|
|
146
|
+
|
|
147
|
+
/** The length of a PATH_CHALLENGE's and a PATH_RESPONSE's data (RFC 9000 §19.17). */
|
|
148
|
+
export const QUIC_PATH_DATA_SIZE: i32 = 8
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* One frame as `quicParseFrame` read it. Which fields mean something depends
|
|
152
|
+
* on `type`; the rest hold zero, or empty arrays.
|
|
153
|
+
*
|
|
154
|
+
* - ACK and ACK_ECN: `largest`, `ackDelay` (still scaled by the peer's
|
|
155
|
+
* exponent), the ranges as `[smallest, largest]` pairs from the highest
|
|
156
|
+
* down in `ackRanges` (`ackRangeCount` pairs stored, `ackRangeTotal` read),
|
|
157
|
+
* and for ACK_ECN the counts `ect0`, `ect1` and `ce`.
|
|
158
|
+
* - CRYPTO: `offset` and the data `dataStart`, `dataLength`.
|
|
159
|
+
* - STREAM: `streamId`, `offset`, the data, and `fin`.
|
|
160
|
+
* - NEW_TOKEN and PATH_CHALLENGE / PATH_RESPONSE: the token or data.
|
|
161
|
+
* - RESET_STREAM: `streamId`, `errorCode`, the final size in `value`.
|
|
162
|
+
* - STOP_SENDING: `streamId`, `errorCode`.
|
|
163
|
+
* - MAX_DATA, MAX_STREAMS_*, DATA_BLOCKED, STREAMS_BLOCKED_*: `value`.
|
|
164
|
+
* - MAX_STREAM_DATA, STREAM_DATA_BLOCKED: `streamId` and `value`.
|
|
165
|
+
* - NEW_CONNECTION_ID: the sequence number in `value`, `retirePriorTo`,
|
|
166
|
+
* the ID's window `connectionIdStart` / `connectionIdLength` and the
|
|
167
|
+
* reset token's `resetTokenStart` (16 bytes) in the payload.
|
|
168
|
+
* RETIRE_CONNECTION_ID: `value`.
|
|
169
|
+
* - DATAGRAM: the payload as the data, and `fin` when the frame carried a
|
|
170
|
+
* Length (type 0x31).
|
|
171
|
+
* - CONNECTION_CLOSE: `errorCode`, `frameType` (0x1c only) and the reason
|
|
172
|
+
* phrase as the data.
|
|
173
|
+
*/
|
|
174
|
+
export class QuicFrame {
|
|
175
|
+
streamId: i64 = 0
|
|
176
|
+
offset: i64 = 0
|
|
177
|
+
value: i64 = 0
|
|
178
|
+
errorCode: i64 = 0
|
|
179
|
+
frameType: i64 = 0
|
|
180
|
+
retirePriorTo: i64 = 0
|
|
181
|
+
largest: i64 = 0
|
|
182
|
+
ackDelay: i64 = 0
|
|
183
|
+
ackRanges: i64[]
|
|
184
|
+
ackRangeTotal: i64 = 0
|
|
185
|
+
ect0: i64 = 0
|
|
186
|
+
ect1: i64 = 0
|
|
187
|
+
ce: i64 = 0
|
|
188
|
+
/** The frame type, with every STREAM type answered as `QUIC_FRAME_STREAM`. */
|
|
189
|
+
type: i32 = 0
|
|
190
|
+
/** One past the frame's last byte, where the next frame starts. */
|
|
191
|
+
end: i32 = 0
|
|
192
|
+
dataStart: i32 = 0
|
|
193
|
+
dataLength: i32 = 0
|
|
194
|
+
ackRangeCount: i32 = 0
|
|
195
|
+
connectionIdStart: i32 = 0
|
|
196
|
+
connectionIdLength: i32 = 0
|
|
197
|
+
resetTokenStart: i32 = 0
|
|
198
|
+
fin: boolean = false
|
|
199
|
+
|
|
200
|
+
constructor() {
|
|
201
|
+
// Sized once, so reading an ACK stores nothing new into the frame.
|
|
202
|
+
this.ackRanges = new Array<i64>(QUIC_FRAME_MAX_ACK_RANGES * 2)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The cursor `quicParseFrame` reads with: a window of the payload, and
|
|
208
|
+
* `failed` once a read ran past it, after which every read fails too. So a
|
|
209
|
+
* frame's fields are read in order and `failed` tested once. It holds only
|
|
210
|
+
* positions; the payload is passed beside it, so reading a frame stores no
|
|
211
|
+
* pointer to it anywhere and a payload made inside an arena block can be
|
|
212
|
+
* read inside it.
|
|
213
|
+
*/
|
|
214
|
+
class QuicFrameReader {
|
|
215
|
+
at: i32
|
|
216
|
+
end: i32
|
|
217
|
+
failed: boolean = false
|
|
218
|
+
|
|
219
|
+
constructor(at: i32, end: i32) {
|
|
220
|
+
this.at = at
|
|
221
|
+
this.end = end
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** The next varint, or 0 and a failed reader when it does not fit in the window. */
|
|
226
|
+
const quicFrameVarint = (r: QuicFrameReader, d: u8[]): i64 => {
|
|
227
|
+
if (r.failed) {
|
|
228
|
+
return 0
|
|
229
|
+
}
|
|
230
|
+
const value: i64 = quicVarintRead(d, r.at, r.end)
|
|
231
|
+
if (value < 0) {
|
|
232
|
+
r.failed = true
|
|
233
|
+
return 0
|
|
234
|
+
}
|
|
235
|
+
r.at = r.at + quicVarintLength(d, r.at)
|
|
236
|
+
return value
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Skips `length` bytes, answering where they start; a length past the window
|
|
241
|
+
* fails the reader and answers the current position.
|
|
242
|
+
*/
|
|
243
|
+
const quicFrameSkip = (r: QuicFrameReader, length: i64): i32 => {
|
|
244
|
+
const at: i32 = r.at
|
|
245
|
+
if (r.failed || length < 0 || length > toI64(r.end - at)) {
|
|
246
|
+
r.failed = true
|
|
247
|
+
return at
|
|
248
|
+
}
|
|
249
|
+
r.at = at + toI32(length)
|
|
250
|
+
return at
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Stores the `index`-th ACK range pair; the caller keeps `index` under `QUIC_FRAME_MAX_ACK_RANGES`. */
|
|
254
|
+
const quicFrameStoreRange = (frame: QuicFrame, index: i32, smallest: i64, largest: i64): void => {
|
|
255
|
+
const at: i32 = index * 2
|
|
256
|
+
const ranges: i64[] = frame.ackRanges
|
|
257
|
+
if (at >= 0 && at + 1 < toI32(ranges.length)) {
|
|
258
|
+
ranges[at] = smallest
|
|
259
|
+
ranges[at + 1] = largest
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Resets every field a previous frame may have set, so a reused `QuicFrame` holds only this one. */
|
|
264
|
+
const quicFrameReset = (frame: QuicFrame, at: i32): void => {
|
|
265
|
+
frame.type = 0
|
|
266
|
+
frame.end = at
|
|
267
|
+
frame.streamId = 0
|
|
268
|
+
frame.offset = 0
|
|
269
|
+
frame.value = 0
|
|
270
|
+
frame.errorCode = 0
|
|
271
|
+
frame.frameType = 0
|
|
272
|
+
frame.retirePriorTo = 0
|
|
273
|
+
frame.fin = false
|
|
274
|
+
frame.dataStart = at
|
|
275
|
+
frame.dataLength = 0
|
|
276
|
+
frame.largest = 0
|
|
277
|
+
frame.ackDelay = 0
|
|
278
|
+
frame.ackRangeCount = 0
|
|
279
|
+
frame.ackRangeTotal = 0
|
|
280
|
+
frame.ect0 = 0
|
|
281
|
+
frame.ect1 = 0
|
|
282
|
+
frame.ce = 0
|
|
283
|
+
frame.connectionIdStart = at
|
|
284
|
+
frame.connectionIdLength = 0
|
|
285
|
+
frame.resetTokenStart = at
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* The ranges of an ACK frame (§19.3.1): the first range down from `largest`,
|
|
290
|
+
* then each Gap and ACK Range Length pair below it. A range that reaches
|
|
291
|
+
* below packet number zero is `QUIC_ERROR_FRAME_ENCODING`. Answers 0 or that.
|
|
292
|
+
*/
|
|
293
|
+
const quicFrameReadAck = (r: QuicFrameReader, d: u8[], frame: QuicFrame, ecn: boolean): i64 => {
|
|
294
|
+
frame.largest = quicFrameVarint(r, d)
|
|
295
|
+
frame.ackDelay = quicFrameVarint(r, d)
|
|
296
|
+
const count: i64 = quicFrameVarint(r, d)
|
|
297
|
+
const first: i64 = quicFrameVarint(r, d)
|
|
298
|
+
if (r.failed) {
|
|
299
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
300
|
+
}
|
|
301
|
+
let smallest: i64 = frame.largest - first
|
|
302
|
+
if (smallest < 0) {
|
|
303
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
304
|
+
}
|
|
305
|
+
quicFrameStoreRange(frame, 0, smallest, frame.largest)
|
|
306
|
+
let stored: i32 = 1
|
|
307
|
+
// Each further range takes at least two bytes, so the loop is bounded by the
|
|
308
|
+
// payload whatever count the peer claims: a short read ends it.
|
|
309
|
+
for (let k: i64 = 0; k < count; k += 1) {
|
|
310
|
+
const gap: i64 = quicFrameVarint(r, d)
|
|
311
|
+
const length: i64 = quicFrameVarint(r, d)
|
|
312
|
+
if (r.failed) {
|
|
313
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
314
|
+
}
|
|
315
|
+
// The next range's largest is two below this one's smallest, less the gap.
|
|
316
|
+
const top: i64 = smallest - gap - 2
|
|
317
|
+
const bottom: i64 = top - length
|
|
318
|
+
if (top < 0 || bottom < 0) {
|
|
319
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
320
|
+
}
|
|
321
|
+
if (stored < QUIC_FRAME_MAX_ACK_RANGES) {
|
|
322
|
+
quicFrameStoreRange(frame, stored, bottom, top)
|
|
323
|
+
stored = stored + 1
|
|
324
|
+
}
|
|
325
|
+
smallest = bottom
|
|
326
|
+
}
|
|
327
|
+
frame.ackRangeCount = stored
|
|
328
|
+
frame.ackRangeTotal = count + 1
|
|
329
|
+
if (ecn) {
|
|
330
|
+
frame.ect0 = quicFrameVarint(r, d)
|
|
331
|
+
frame.ect1 = quicFrameVarint(r, d)
|
|
332
|
+
frame.ce = quicFrameVarint(r, d)
|
|
333
|
+
}
|
|
334
|
+
return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* The data of a CRYPTO or STREAM frame, `length` bytes from the reader's
|
|
339
|
+
* position; past the window, or reaching past offset 2^62 − 1 (§19.6,
|
|
340
|
+
* §19.8), it is `QUIC_ERROR_FRAME_ENCODING`.
|
|
341
|
+
*/
|
|
342
|
+
const quicFrameReadData = (r: QuicFrameReader, frame: QuicFrame, length: i64): i64 => {
|
|
343
|
+
frame.dataStart = quicFrameSkip(r, length)
|
|
344
|
+
if (r.failed) {
|
|
345
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
346
|
+
}
|
|
347
|
+
frame.dataLength = toI32(length)
|
|
348
|
+
return frame.offset > QUIC_MAX_VARINT - length ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** STREAM (§19.8): the type's three low bits say whether Offset and Length are present and whether FIN is set. */
|
|
352
|
+
const quicFrameReadStream = (r: QuicFrameReader, d: u8[], frame: QuicFrame, bits: i32): i64 => {
|
|
353
|
+
frame.streamId = quicFrameVarint(r, d)
|
|
354
|
+
if ((bits & 0x04) !== 0) {
|
|
355
|
+
frame.offset = quicFrameVarint(r, d)
|
|
356
|
+
}
|
|
357
|
+
// Without a Length the data runs to the end of the packet.
|
|
358
|
+
const length: i64 = (bits & 0x02) !== 0 ? quicFrameVarint(r, d) : toI64(r.end - r.at)
|
|
359
|
+
frame.fin = (bits & 0x01) !== 0
|
|
360
|
+
if (r.failed) {
|
|
361
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
362
|
+
}
|
|
363
|
+
return quicFrameReadData(r, frame, length)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** DATAGRAM (RFC 9221 §4): with a Length, that many bytes; without, the rest of the packet. */
|
|
367
|
+
const quicFrameReadDatagram = (r: QuicFrameReader, d: u8[], frame: QuicFrame, hasLength: boolean): i64 => {
|
|
368
|
+
const length: i64 = hasLength ? quicFrameVarint(r, d) : toI64(r.end - r.at)
|
|
369
|
+
frame.fin = hasLength
|
|
370
|
+
frame.dataStart = quicFrameSkip(r, length)
|
|
371
|
+
if (r.failed) {
|
|
372
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
373
|
+
}
|
|
374
|
+
frame.dataLength = toI32(length)
|
|
375
|
+
return QUIC_ERROR_NO_ERROR
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** NEW_CONNECTION_ID (§19.15): a sequence number, Retire Prior To, a 1 to 20 byte ID and a 16-byte reset token. */
|
|
379
|
+
const quicFrameReadNewConnectionId = (r: QuicFrameReader, d: u8[], frame: QuicFrame): i64 => {
|
|
380
|
+
frame.value = quicFrameVarint(r, d)
|
|
381
|
+
frame.retirePriorTo = quicFrameVarint(r, d)
|
|
382
|
+
const lengthAt: i32 = quicFrameSkip(r, 1)
|
|
383
|
+
if (r.failed || lengthAt < 0 || lengthAt >= toI32(d.length)) {
|
|
384
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
385
|
+
}
|
|
386
|
+
const length: i32 = toI32(d[lengthAt])
|
|
387
|
+
const cidAt: i32 = quicFrameSkip(r, toI64(length))
|
|
388
|
+
const tokenAt: i32 = quicFrameSkip(r, toI64(QUIC_RESET_TOKEN_SIZE))
|
|
389
|
+
if (r.failed || length < 1 || length > QUIC_MAX_CID_LENGTH || frame.retirePriorTo > frame.value) {
|
|
390
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
391
|
+
}
|
|
392
|
+
frame.connectionIdStart = cidAt
|
|
393
|
+
frame.connectionIdLength = length
|
|
394
|
+
frame.resetTokenStart = tokenAt
|
|
395
|
+
return QUIC_ERROR_NO_ERROR
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** A frame of one varint-length field followed by that many bytes, left in place: NEW_TOKEN's token, a close's reason. */
|
|
399
|
+
const quicFrameReadBytes = (r: QuicFrameReader, d: u8[], frame: QuicFrame): void => {
|
|
400
|
+
const length: i64 = quicFrameVarint(r, d)
|
|
401
|
+
frame.dataStart = quicFrameSkip(r, length)
|
|
402
|
+
frame.dataLength = r.failed ? 0 : toI32(length)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* The body of a frame of type `type` (already read and known), from the
|
|
407
|
+
* reader's position. Answers 0, or the transport error the RFC names.
|
|
408
|
+
*/
|
|
409
|
+
const quicFrameReadBody = (r: QuicFrameReader, d: u8[], frame: QuicFrame, type: i32): i64 => {
|
|
410
|
+
switch (type) {
|
|
411
|
+
case QUIC_FRAME_PADDING: {
|
|
412
|
+
// Padding is a run of zero bytes; read it as one frame, so a packet of
|
|
413
|
+
// a thousand padding bytes is one call rather than a thousand.
|
|
414
|
+
while (r.at < r.end && r.at >= 0 && r.at < toI32(d.length) && toI32(d[r.at]) === 0) {
|
|
415
|
+
r.at = r.at + 1
|
|
416
|
+
}
|
|
417
|
+
return QUIC_ERROR_NO_ERROR
|
|
418
|
+
}
|
|
419
|
+
case QUIC_FRAME_PING:
|
|
420
|
+
case QUIC_FRAME_HANDSHAKE_DONE:
|
|
421
|
+
return QUIC_ERROR_NO_ERROR
|
|
422
|
+
case QUIC_FRAME_ACK:
|
|
423
|
+
return quicFrameReadAck(r, d, frame, false)
|
|
424
|
+
case QUIC_FRAME_ACK_ECN:
|
|
425
|
+
return quicFrameReadAck(r, d, frame, true)
|
|
426
|
+
case QUIC_FRAME_RESET_STREAM: {
|
|
427
|
+
frame.streamId = quicFrameVarint(r, d)
|
|
428
|
+
frame.errorCode = quicFrameVarint(r, d)
|
|
429
|
+
frame.value = quicFrameVarint(r, d)
|
|
430
|
+
return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
|
|
431
|
+
}
|
|
432
|
+
case QUIC_FRAME_STOP_SENDING: {
|
|
433
|
+
frame.streamId = quicFrameVarint(r, d)
|
|
434
|
+
frame.errorCode = quicFrameVarint(r, d)
|
|
435
|
+
return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
|
|
436
|
+
}
|
|
437
|
+
case QUIC_FRAME_CRYPTO: {
|
|
438
|
+
frame.offset = quicFrameVarint(r, d)
|
|
439
|
+
const length: i64 = quicFrameVarint(r, d)
|
|
440
|
+
return r.failed ? QUIC_ERROR_FRAME_ENCODING : quicFrameReadData(r, frame, length)
|
|
441
|
+
}
|
|
442
|
+
case QUIC_FRAME_NEW_TOKEN: {
|
|
443
|
+
quicFrameReadBytes(r, d, frame)
|
|
444
|
+
// §19.7: an empty token is FRAME_ENCODING_ERROR.
|
|
445
|
+
return r.failed || frame.dataLength === 0 ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
|
|
446
|
+
}
|
|
447
|
+
case QUIC_FRAME_MAX_STREAM_DATA:
|
|
448
|
+
case QUIC_FRAME_STREAM_DATA_BLOCKED: {
|
|
449
|
+
frame.streamId = quicFrameVarint(r, d)
|
|
450
|
+
frame.value = quicFrameVarint(r, d)
|
|
451
|
+
return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
|
|
452
|
+
}
|
|
453
|
+
case QUIC_FRAME_MAX_DATA:
|
|
454
|
+
case QUIC_FRAME_DATA_BLOCKED:
|
|
455
|
+
case QUIC_FRAME_RETIRE_CONNECTION_ID: {
|
|
456
|
+
frame.value = quicFrameVarint(r, d)
|
|
457
|
+
return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
|
|
458
|
+
}
|
|
459
|
+
case QUIC_FRAME_NEW_CONNECTION_ID:
|
|
460
|
+
return quicFrameReadNewConnectionId(r, d, frame)
|
|
461
|
+
case QUIC_FRAME_PATH_CHALLENGE:
|
|
462
|
+
case QUIC_FRAME_PATH_RESPONSE: {
|
|
463
|
+
frame.dataStart = quicFrameSkip(r, toI64(QUIC_PATH_DATA_SIZE))
|
|
464
|
+
frame.dataLength = QUIC_PATH_DATA_SIZE
|
|
465
|
+
return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
|
|
466
|
+
}
|
|
467
|
+
case QUIC_FRAME_CONNECTION_CLOSE: {
|
|
468
|
+
frame.errorCode = quicFrameVarint(r, d)
|
|
469
|
+
frame.frameType = quicFrameVarint(r, d)
|
|
470
|
+
quicFrameReadBytes(r, d, frame)
|
|
471
|
+
return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
|
|
472
|
+
}
|
|
473
|
+
case QUIC_FRAME_CONNECTION_CLOSE_APP: {
|
|
474
|
+
frame.errorCode = quicFrameVarint(r, d)
|
|
475
|
+
quicFrameReadBytes(r, d, frame)
|
|
476
|
+
return r.failed ? QUIC_ERROR_FRAME_ENCODING : QUIC_ERROR_NO_ERROR
|
|
477
|
+
}
|
|
478
|
+
default: {
|
|
479
|
+
// The four stream-count frames share one rule: a count past 2^60 is a
|
|
480
|
+
// FRAME_ENCODING_ERROR (§19.11, §19.14). Every other type was handled
|
|
481
|
+
// above or is STREAM's, which `quicParseFrame` routes separately.
|
|
482
|
+
frame.value = quicFrameVarint(r, d)
|
|
483
|
+
if (r.failed || frame.value > QUIC_FRAME_MAX_STREAMS) {
|
|
484
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
485
|
+
}
|
|
486
|
+
return QUIC_ERROR_NO_ERROR
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Reads the frame starting at `payload[at]`, inside `payload[at .. end)`,
|
|
493
|
+
* into `frame`, which is reset first, and answers 0 or the transport error
|
|
494
|
+
* the RFC names for the break (see the module header). On 0, `frame.end` is
|
|
495
|
+
* where the next frame starts; after an error the packet is not to be read
|
|
496
|
+
* further, and `frame.type` still holds the type when it was read, which is
|
|
497
|
+
* what CONNECTION_CLOSE reports. A window outside `payload` is
|
|
498
|
+
* `QUIC_ERROR_INTERNAL`, the caller's fault rather than the peer's.
|
|
499
|
+
*/
|
|
500
|
+
export const quicParseFrame = (frame: QuicFrame, payload: u8[], at: i32, end: i32): i64 => {
|
|
501
|
+
quicFrameReset(frame, at)
|
|
502
|
+
if (at < 0 || end > toI32(payload.length) || at >= end) {
|
|
503
|
+
return QUIC_ERROR_INTERNAL
|
|
504
|
+
}
|
|
505
|
+
const r: QuicFrameReader = new QuicFrameReader(at, end)
|
|
506
|
+
const typeLength: i32 = quicVarintLength(payload, at)
|
|
507
|
+
const type: i64 = quicFrameVarint(r, payload)
|
|
508
|
+
if (r.failed) {
|
|
509
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
510
|
+
}
|
|
511
|
+
const datagram: boolean = type === toI64(QUIC_FRAME_DATAGRAM) || type === toI64(QUIC_FRAME_DATAGRAM_LENGTH)
|
|
512
|
+
if (type > toI64(QUIC_FRAME_HANDSHAKE_DONE) && !datagram) {
|
|
513
|
+
return QUIC_ERROR_FRAME_ENCODING
|
|
514
|
+
}
|
|
515
|
+
const small: i32 = toI32(type)
|
|
516
|
+
frame.type = small >= QUIC_FRAME_STREAM && small <= 0x0f ? QUIC_FRAME_STREAM : small
|
|
517
|
+
// §12.4: a frame type is written in the fewest bytes. Every type here fits
|
|
518
|
+
// in one, so a longer spelling is the peer's mistake.
|
|
519
|
+
if (typeLength !== 1) {
|
|
520
|
+
return QUIC_ERROR_PROTOCOL_VIOLATION
|
|
521
|
+
}
|
|
522
|
+
let error: i64 = QUIC_ERROR_NO_ERROR
|
|
523
|
+
if (datagram) {
|
|
524
|
+
frame.type = QUIC_FRAME_DATAGRAM
|
|
525
|
+
error = quicFrameReadDatagram(r, payload, frame, small === QUIC_FRAME_DATAGRAM_LENGTH)
|
|
526
|
+
} else if (frame.type === QUIC_FRAME_STREAM) {
|
|
527
|
+
error = quicFrameReadStream(r, payload, frame, small & 7)
|
|
528
|
+
} else {
|
|
529
|
+
error = quicFrameReadBody(r, payload, frame, small)
|
|
530
|
+
}
|
|
531
|
+
frame.end = r.at
|
|
532
|
+
return error
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Whether a frame of `type` may appear in a packet of `packetType` (RFC 9000
|
|
537
|
+
* §12.4, Table 3). Initial and Handshake packets carry only PADDING, PING,
|
|
538
|
+
* ACK, CRYPTO and the transport CONNECTION_CLOSE; a 0-RTT packet anything
|
|
539
|
+
* but ACK, CRYPTO, NEW_TOKEN, HANDSHAKE_DONE and the retire and response
|
|
540
|
+
* frames a client cannot have a reason to send yet; a 1-RTT packet anything.
|
|
541
|
+
*/
|
|
542
|
+
export const quicFrameAllowed = (type: i32, packetType: i32): boolean => {
|
|
543
|
+
if (packetType === QUIC_PACKET_SHORT) {
|
|
544
|
+
return true
|
|
545
|
+
}
|
|
546
|
+
if (packetType === QUIC_PACKET_INITIAL || packetType === QUIC_PACKET_HANDSHAKE) {
|
|
547
|
+
return (
|
|
548
|
+
type === QUIC_FRAME_PADDING ||
|
|
549
|
+
type === QUIC_FRAME_PING ||
|
|
550
|
+
type === QUIC_FRAME_ACK ||
|
|
551
|
+
type === QUIC_FRAME_ACK_ECN ||
|
|
552
|
+
type === QUIC_FRAME_CRYPTO ||
|
|
553
|
+
type === QUIC_FRAME_CONNECTION_CLOSE
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
if (packetType === QUIC_PACKET_ZERO_RTT) {
|
|
557
|
+
return !(
|
|
558
|
+
type === QUIC_FRAME_ACK ||
|
|
559
|
+
type === QUIC_FRAME_ACK_ECN ||
|
|
560
|
+
type === QUIC_FRAME_CRYPTO ||
|
|
561
|
+
type === QUIC_FRAME_NEW_TOKEN ||
|
|
562
|
+
type === QUIC_FRAME_HANDSHAKE_DONE ||
|
|
563
|
+
type === QUIC_FRAME_RETIRE_CONNECTION_ID ||
|
|
564
|
+
type === QUIC_FRAME_PATH_RESPONSE
|
|
565
|
+
)
|
|
566
|
+
}
|
|
567
|
+
return false
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Whether a frame of `type` asks for an acknowledgement (RFC 9000 §13.2):
|
|
572
|
+
* every frame but ACK, PADDING and CONNECTION_CLOSE.
|
|
573
|
+
*/
|
|
574
|
+
export const quicFrameAckEliciting = (type: i32): boolean =>
|
|
575
|
+
!(
|
|
576
|
+
type === QUIC_FRAME_ACK ||
|
|
577
|
+
type === QUIC_FRAME_ACK_ECN ||
|
|
578
|
+
type === QUIC_FRAME_PADDING ||
|
|
579
|
+
type === QUIC_FRAME_CONNECTION_CLOSE ||
|
|
580
|
+
type === QUIC_FRAME_CONNECTION_CLOSE_APP
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
// ---- Writers --------------------------------------------------------------------
|
|
584
|
+
//
|
|
585
|
+
// Each frame has a `put` writer, which writes it into a caller's buffer at an
|
|
586
|
+
// offset and answers the offset past it (-1, writing nothing, for an argument
|
|
587
|
+
// out of range or a frame that does not fit before `end`), and a `push`
|
|
588
|
+
// writer, which appends it to an array: the `push` form grows the array by
|
|
589
|
+
// the frame's size and calls the `put` form, so each frame has one encoder.
|
|
590
|
+
// A connection builds its packets with the `put` forms, in place, with no
|
|
591
|
+
// allocation.
|
|
592
|
+
|
|
593
|
+
/** Whether `from .. from + length` is a window of `bytes`. */
|
|
594
|
+
const quicFrameWindowFits = (bytes: u8[], from: i32, length: i32): boolean =>
|
|
595
|
+
from >= 0 && length >= 0 && from <= toI32(bytes.length) - length
|
|
596
|
+
|
|
597
|
+
/** Whether `size` bytes from `at` fit before `end` and inside `buf`. */
|
|
598
|
+
const quicFrameRoom = (buf: u8[], at: i32, end: i32, size: i32): boolean =>
|
|
599
|
+
size > 0 && at >= 0 && end <= toI32(buf.length) && at <= end - size
|
|
600
|
+
|
|
601
|
+
/** Writes one byte and answers the offset past it; the caller has checked the room. */
|
|
602
|
+
const quicFramePutByte = (buf: u8[], at: i32, value: i32): i32 => {
|
|
603
|
+
if (at >= 0 && at < toI32(buf.length)) {
|
|
604
|
+
buf[at] = toU8(value)
|
|
605
|
+
}
|
|
606
|
+
return at + 1
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/** Writes a varint in the fewest bytes; the caller has checked the room and the value. */
|
|
610
|
+
const quicFramePutVarint = (buf: u8[], at: i32, value: i64): i32 =>
|
|
611
|
+
quicVarintPut(buf, at, value, quicVarintSize(value))
|
|
612
|
+
|
|
613
|
+
/** Grows `out` by `size` bytes and answers where they start, for a `push` writer to `put` into. */
|
|
614
|
+
const quicFrameGrow = (out: u8[], size: i32): i32 => {
|
|
615
|
+
const at: i32 = toI32(out.length)
|
|
616
|
+
quicPacketGrow(out, size)
|
|
617
|
+
return at
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** Whether every value is a varint, which is what a frame of them needs. */
|
|
621
|
+
const quicFrameVarints3 = (a: i64, b: i64, c: i64): boolean =>
|
|
622
|
+
quicVarintSize(a) !== 0 && quicVarintSize(b) !== 0 && quicVarintSize(c) !== 0
|
|
623
|
+
|
|
624
|
+
/** Appends `count` PADDING frames, which are `count` zero bytes. A count below one appends nothing. */
|
|
625
|
+
export const quicPushPadding = (out: u8[], count: i32): void => {
|
|
626
|
+
quicPacketGrow(out, count)
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Writes `count` PADDING frames, zero bytes, into `buf[at .. at + count)`,
|
|
631
|
+
* clamped to `buf`, and answers the offset past them.
|
|
632
|
+
*/
|
|
633
|
+
export const quicPutPadding = (buf: u8[], at: i32, count: i32): i32 => {
|
|
634
|
+
let k: i32 = 0
|
|
635
|
+
while (k < count && at + k >= 0 && at + k < toI32(buf.length)) {
|
|
636
|
+
buf[at + k] = toU8(0)
|
|
637
|
+
k += 1
|
|
638
|
+
}
|
|
639
|
+
return at + k
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/** Writes a frame that is its type alone, PING or HANDSHAKE_DONE; -1 for another type or no room. */
|
|
643
|
+
export const quicPutTypeOnly = (buf: u8[], at: i32, end: i32, type: i32): i32 => {
|
|
644
|
+
if ((type !== QUIC_FRAME_PING && type !== QUIC_FRAME_HANDSHAKE_DONE) || !quicFrameRoom(buf, at, end, 1)) {
|
|
645
|
+
return -1
|
|
646
|
+
}
|
|
647
|
+
return quicFramePutByte(buf, at, type)
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/** Appends a frame that is its type alone: PING or HANDSHAKE_DONE. Answers `false` for another type. */
|
|
651
|
+
export const quicPushTypeOnly = (out: u8[], type: i32): boolean => {
|
|
652
|
+
if (type !== QUIC_FRAME_PING && type !== QUIC_FRAME_HANDSHAKE_DONE) {
|
|
653
|
+
return false
|
|
654
|
+
}
|
|
655
|
+
const at: i32 = quicFrameGrow(out, 1)
|
|
656
|
+
return quicPutTypeOnly(out, at, at + 1, type) >= 0
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* The size of the ACK frame `quicPutAck` writes for these ranges, or 0 when
|
|
661
|
+
* it would refuse them: no ranges, ranges out of order or touching, a pair
|
|
662
|
+
* the array does not hold, or a value no varint holds.
|
|
663
|
+
*/
|
|
664
|
+
export const quicAckSize = (ranges: i64[], rangeCount: i32, ackDelay: i64): i32 => {
|
|
665
|
+
if (rangeCount < 1 || rangeCount * 2 > toI32(ranges.length) || quicVarintSize(ackDelay) === 0) {
|
|
666
|
+
return 0
|
|
667
|
+
}
|
|
668
|
+
let size: i32 = 1
|
|
669
|
+
let previousSmallest: i64 = 0
|
|
670
|
+
for (let k: i32 = 0; k < rangeCount; k += 1) {
|
|
671
|
+
const at: i32 = k * 2
|
|
672
|
+
const next: i32 = at + 1
|
|
673
|
+
if (at < 0 || at >= toI32(ranges.length) || next < 0 || next >= toI32(ranges.length)) {
|
|
674
|
+
return 0
|
|
675
|
+
}
|
|
676
|
+
const smallest: i64 = ranges[at]
|
|
677
|
+
const largest: i64 = ranges[next]
|
|
678
|
+
if (smallest < 0 || largest < smallest || largest > QUIC_MAX_VARINT) {
|
|
679
|
+
return 0
|
|
680
|
+
}
|
|
681
|
+
if (k === 0) {
|
|
682
|
+
size = size + quicVarintSize(largest) + quicVarintSize(ackDelay) + quicVarintSize(toI64(rangeCount - 1))
|
|
683
|
+
} else {
|
|
684
|
+
// Gap: the packets missing between this range and the one above, less one (§19.3.1).
|
|
685
|
+
const gap: i64 = previousSmallest - largest - 2
|
|
686
|
+
if (gap < 0) {
|
|
687
|
+
return 0
|
|
688
|
+
}
|
|
689
|
+
size = size + quicVarintSize(gap)
|
|
690
|
+
}
|
|
691
|
+
size = size + quicVarintSize(largest - smallest)
|
|
692
|
+
previousSmallest = smallest
|
|
693
|
+
}
|
|
694
|
+
return size
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Writes an ACK frame (§19.3) for the `rangeCount` pairs of `ranges`, each
|
|
699
|
+
* `[smallest, largest]`, from the highest down and not touching (each pair's
|
|
700
|
+
* largest at least two below the smallest of the pair before it), with
|
|
701
|
+
* `ackDelay` already scaled by this endpoint's exponent. Answers -1, writing
|
|
702
|
+
* nothing, where `quicAckSize` answers 0 or the frame does not fit.
|
|
703
|
+
*/
|
|
704
|
+
export const quicPutAck = (
|
|
705
|
+
buf: u8[],
|
|
706
|
+
at: i32,
|
|
707
|
+
end: i32,
|
|
708
|
+
ranges: i64[],
|
|
709
|
+
rangeCount: i32,
|
|
710
|
+
ackDelay: i64
|
|
711
|
+
): i32 => {
|
|
712
|
+
const size: i32 = quicAckSize(ranges, rangeCount, ackDelay)
|
|
713
|
+
if (!quicFrameRoom(buf, at, end, size)) {
|
|
714
|
+
return -1
|
|
715
|
+
}
|
|
716
|
+
let cursor: i32 = quicFramePutByte(buf, at, QUIC_FRAME_ACK)
|
|
717
|
+
let previousSmallest: i64 = 0
|
|
718
|
+
for (let k: i32 = 0; k < rangeCount; k += 1) {
|
|
719
|
+
const pair: i32 = k * 2
|
|
720
|
+
if (pair >= 0 && pair < toI32(ranges.length) && pair + 1 < toI32(ranges.length)) {
|
|
721
|
+
const smallest: i64 = ranges[pair]
|
|
722
|
+
const largest: i64 = ranges[pair + 1]
|
|
723
|
+
if (k === 0) {
|
|
724
|
+
cursor = quicFramePutVarint(buf, cursor, largest)
|
|
725
|
+
cursor = quicFramePutVarint(buf, cursor, ackDelay)
|
|
726
|
+
cursor = quicFramePutVarint(buf, cursor, toI64(rangeCount - 1))
|
|
727
|
+
} else {
|
|
728
|
+
cursor = quicFramePutVarint(buf, cursor, previousSmallest - largest - 2)
|
|
729
|
+
}
|
|
730
|
+
cursor = quicFramePutVarint(buf, cursor, largest - smallest)
|
|
731
|
+
previousSmallest = smallest
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return cursor
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/** Appends the ACK frame `quicPutAck` writes. Answers `false`, appending nothing, where it refuses. */
|
|
738
|
+
export const quicPushAck = (out: u8[], ranges: i64[], rangeCount: i32, ackDelay: i64): boolean => {
|
|
739
|
+
const size: i32 = quicAckSize(ranges, rangeCount, ackDelay)
|
|
740
|
+
if (size === 0) {
|
|
741
|
+
return false
|
|
742
|
+
}
|
|
743
|
+
const at: i32 = quicFrameGrow(out, size)
|
|
744
|
+
return quicPutAck(out, at, at + size, ranges, rangeCount, ackDelay) >= 0
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* The bytes a CRYPTO frame's header takes for `length` bytes of data at
|
|
749
|
+
* `offset`: the type, the offset and the length.
|
|
750
|
+
*/
|
|
751
|
+
export const quicCryptoOverhead = (offset: i64, length: i32): i32 =>
|
|
752
|
+
1 + quicVarintSize(offset) + quicVarintSize(toI64(length))
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Writes a CRYPTO frame (§19.6) carrying `data[from .. from + length)` at
|
|
756
|
+
* stream offset `offset`. Answers -1, writing nothing, for a window outside
|
|
757
|
+
* `data`, an offset whose end passes 2^62 − 1, or no room.
|
|
758
|
+
*/
|
|
759
|
+
export const quicPutCrypto = (
|
|
760
|
+
buf: u8[],
|
|
761
|
+
at: i32,
|
|
762
|
+
end: i32,
|
|
763
|
+
offset: i64,
|
|
764
|
+
data: u8[],
|
|
765
|
+
from: i32,
|
|
766
|
+
length: i32
|
|
767
|
+
): i32 => {
|
|
768
|
+
if (
|
|
769
|
+
!quicFrameWindowFits(data, from, length) ||
|
|
770
|
+
offset < 0 ||
|
|
771
|
+
offset > QUIC_MAX_VARINT - toI64(length) ||
|
|
772
|
+
!quicFrameRoom(buf, at, end, quicCryptoOverhead(offset, length) + length)
|
|
773
|
+
) {
|
|
774
|
+
return -1
|
|
775
|
+
}
|
|
776
|
+
let cursor: i32 = quicFramePutByte(buf, at, QUIC_FRAME_CRYPTO)
|
|
777
|
+
cursor = quicFramePutVarint(buf, cursor, offset)
|
|
778
|
+
cursor = quicFramePutVarint(buf, cursor, toI64(length))
|
|
779
|
+
quicPacketCopy(buf, cursor, data, from, length)
|
|
780
|
+
return cursor + length
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/** Appends the CRYPTO frame `quicPutCrypto` writes. Answers `false`, appending nothing, where it refuses. */
|
|
784
|
+
export const quicPushCrypto = (out: u8[], offset: i64, data: u8[], from: i32, length: i32): boolean => {
|
|
785
|
+
if (!quicFrameWindowFits(data, from, length) || offset < 0 || offset > QUIC_MAX_VARINT - toI64(length)) {
|
|
786
|
+
return false
|
|
787
|
+
}
|
|
788
|
+
const size: i32 = quicCryptoOverhead(offset, length) + length
|
|
789
|
+
const at: i32 = quicFrameGrow(out, size)
|
|
790
|
+
return quicPutCrypto(out, at, at + size, offset, data, from, length) >= 0
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* The bytes a STREAM frame's header takes for `length` bytes of data on
|
|
795
|
+
* `streamId` at `offset`: the type, the stream ID, the offset when it is not
|
|
796
|
+
* zero, and the length, which `quicPutStream` always writes.
|
|
797
|
+
*/
|
|
798
|
+
export const quicStreamOverhead = (streamId: i64, offset: i64, length: i32): i32 =>
|
|
799
|
+
1 + quicVarintSize(streamId) + (offset === 0 ? 0 : quicVarintSize(offset)) + quicVarintSize(toI64(length))
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Writes a STREAM frame (§19.8) carrying `length` bytes of `data` on
|
|
803
|
+
* `streamId` at `offset`, with FIN when `fin`. The data is read from `data`
|
|
804
|
+
* as a ring: byte `k` of it is `data[(from + k) % data.length]`, so a
|
|
805
|
+
* stream's send buffer is framed where it wraps without a copy; a `from`
|
|
806
|
+
* plus `length` inside the array is the plain window. The Length field is
|
|
807
|
+
* always written, so another frame may follow; the Offset field only when it
|
|
808
|
+
* is not zero. Answers -1, writing nothing, for a `length` past the array, a
|
|
809
|
+
* stream ID that is not a varint, an end past 2^62 − 1, or no room.
|
|
810
|
+
*/
|
|
811
|
+
export const quicPutStream = (
|
|
812
|
+
buf: u8[],
|
|
813
|
+
at: i32,
|
|
814
|
+
end: i32,
|
|
815
|
+
streamId: i64,
|
|
816
|
+
offset: i64,
|
|
817
|
+
data: u8[],
|
|
818
|
+
from: i32,
|
|
819
|
+
length: i32,
|
|
820
|
+
fin: boolean
|
|
821
|
+
): i32 => {
|
|
822
|
+
const size: i32 = toI32(data.length)
|
|
823
|
+
if (
|
|
824
|
+
from < 0 ||
|
|
825
|
+
length < 0 ||
|
|
826
|
+
length > size ||
|
|
827
|
+
(length > 0 && from >= size) ||
|
|
828
|
+
quicVarintSize(streamId) === 0 ||
|
|
829
|
+
offset < 0 ||
|
|
830
|
+
offset > QUIC_MAX_VARINT - toI64(length) ||
|
|
831
|
+
!quicFrameRoom(buf, at, end, quicStreamOverhead(streamId, offset, length) + length)
|
|
832
|
+
) {
|
|
833
|
+
return -1
|
|
834
|
+
}
|
|
835
|
+
const offsetBit: i32 = offset === 0 ? 0 : 0x04
|
|
836
|
+
const finBit: i32 = fin ? 0x01 : 0
|
|
837
|
+
let cursor: i32 = quicFramePutByte(buf, at, QUIC_FRAME_STREAM | offsetBit | 0x02 | finBit)
|
|
838
|
+
cursor = quicFramePutVarint(buf, cursor, streamId)
|
|
839
|
+
if (offset !== 0) {
|
|
840
|
+
cursor = quicFramePutVarint(buf, cursor, offset)
|
|
841
|
+
}
|
|
842
|
+
cursor = quicFramePutVarint(buf, cursor, toI64(length))
|
|
843
|
+
// Two runs: up to the end of the array, then from its start.
|
|
844
|
+
const first: i32 = length < size - from ? length : size - from
|
|
845
|
+
quicPacketCopy(buf, cursor, data, from, first)
|
|
846
|
+
quicPacketCopy(buf, cursor + first, data, 0, length - first)
|
|
847
|
+
return cursor + length
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Appends a STREAM frame (§19.8) carrying `data[from .. from + length)` on
|
|
852
|
+
* `streamId` at `offset`, with FIN when `fin`, as `quicPutStream` writes it.
|
|
853
|
+
* Answers `false`, appending nothing, for a window outside `data`, a stream
|
|
854
|
+
* ID that is not a varint or an end past 2^62 − 1.
|
|
855
|
+
*/
|
|
856
|
+
export const quicPushStream = (
|
|
857
|
+
out: u8[],
|
|
858
|
+
streamId: i64,
|
|
859
|
+
offset: i64,
|
|
860
|
+
data: u8[],
|
|
861
|
+
from: i32,
|
|
862
|
+
length: i32,
|
|
863
|
+
fin: boolean
|
|
864
|
+
): boolean => {
|
|
865
|
+
if (
|
|
866
|
+
!quicFrameWindowFits(data, from, length) ||
|
|
867
|
+
quicVarintSize(streamId) === 0 ||
|
|
868
|
+
offset < 0 ||
|
|
869
|
+
offset > QUIC_MAX_VARINT - toI64(length)
|
|
870
|
+
) {
|
|
871
|
+
return false
|
|
872
|
+
}
|
|
873
|
+
const size: i32 = quicStreamOverhead(streamId, offset, length) + length
|
|
874
|
+
const at: i32 = quicFrameGrow(out, size)
|
|
875
|
+
// A window inside the array is a ring that does not wrap.
|
|
876
|
+
return quicPutStream(out, at, at + size, streamId, offset, data, from, length, fin) >= 0
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/** Whether `type` is one of the frames `quicPutValue` writes, and `value` one it may carry. */
|
|
880
|
+
const quicFrameValueFits = (type: i32, value: i64): boolean => {
|
|
881
|
+
const counts: boolean =
|
|
882
|
+
type === QUIC_FRAME_MAX_STREAMS_BIDI ||
|
|
883
|
+
type === QUIC_FRAME_MAX_STREAMS_UNI ||
|
|
884
|
+
type === QUIC_FRAME_STREAMS_BLOCKED_BIDI ||
|
|
885
|
+
type === QUIC_FRAME_STREAMS_BLOCKED_UNI
|
|
886
|
+
const known: boolean =
|
|
887
|
+
counts ||
|
|
888
|
+
type === QUIC_FRAME_MAX_DATA ||
|
|
889
|
+
type === QUIC_FRAME_DATA_BLOCKED ||
|
|
890
|
+
type === QUIC_FRAME_RETIRE_CONNECTION_ID
|
|
891
|
+
return known && quicVarintSize(value) !== 0 && !(counts && value > QUIC_FRAME_MAX_STREAMS)
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Writes a frame of one varint after its type: MAX_DATA, MAX_STREAMS_BIDI,
|
|
896
|
+
* MAX_STREAMS_UNI, DATA_BLOCKED, STREAMS_BLOCKED_BIDI, STREAMS_BLOCKED_UNI or
|
|
897
|
+
* RETIRE_CONNECTION_ID. Answers -1 for another type, a value no varint holds,
|
|
898
|
+
* a stream count past 2^60, or no room.
|
|
899
|
+
*/
|
|
900
|
+
export const quicPutValue = (buf: u8[], at: i32, end: i32, type: i32, value: i64): i32 => {
|
|
901
|
+
if (!quicFrameValueFits(type, value) || !quicFrameRoom(buf, at, end, 1 + quicVarintSize(value))) {
|
|
902
|
+
return -1
|
|
903
|
+
}
|
|
904
|
+
return quicFramePutVarint(buf, quicFramePutByte(buf, at, type), value)
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/** Appends the frame `quicPutValue` writes. Answers `false`, appending nothing, where it refuses. */
|
|
908
|
+
export const quicPushValue = (out: u8[], type: i32, value: i64): boolean => {
|
|
909
|
+
if (!quicFrameValueFits(type, value)) {
|
|
910
|
+
return false
|
|
911
|
+
}
|
|
912
|
+
const size: i32 = 1 + quicVarintSize(value)
|
|
913
|
+
const at: i32 = quicFrameGrow(out, size)
|
|
914
|
+
return quicPutValue(out, at, at + size, type, value) >= 0
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Writes a MAX_STREAM_DATA or STREAM_DATA_BLOCKED frame: the stream ID and
|
|
919
|
+
* the limit. Answers -1 for another type, a value no varint holds, or no room.
|
|
920
|
+
*/
|
|
921
|
+
export const quicPutStreamValue = (
|
|
922
|
+
buf: u8[],
|
|
923
|
+
at: i32,
|
|
924
|
+
end: i32,
|
|
925
|
+
type: i32,
|
|
926
|
+
streamId: i64,
|
|
927
|
+
value: i64
|
|
928
|
+
): i32 => {
|
|
929
|
+
if (
|
|
930
|
+
(type !== QUIC_FRAME_MAX_STREAM_DATA && type !== QUIC_FRAME_STREAM_DATA_BLOCKED) ||
|
|
931
|
+
quicVarintSize(streamId) === 0 ||
|
|
932
|
+
quicVarintSize(value) === 0 ||
|
|
933
|
+
!quicFrameRoom(buf, at, end, 1 + quicVarintSize(streamId) + quicVarintSize(value))
|
|
934
|
+
) {
|
|
935
|
+
return -1
|
|
936
|
+
}
|
|
937
|
+
const cursor: i32 = quicFramePutVarint(buf, quicFramePutByte(buf, at, type), streamId)
|
|
938
|
+
return quicFramePutVarint(buf, cursor, value)
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/** Appends the frame `quicPutStreamValue` writes. Answers `false`, appending nothing, where it refuses. */
|
|
942
|
+
export const quicPushStreamValue = (out: u8[], type: i32, streamId: i64, value: i64): boolean => {
|
|
943
|
+
if (
|
|
944
|
+
(type !== QUIC_FRAME_MAX_STREAM_DATA && type !== QUIC_FRAME_STREAM_DATA_BLOCKED) ||
|
|
945
|
+
quicVarintSize(streamId) === 0 ||
|
|
946
|
+
quicVarintSize(value) === 0
|
|
947
|
+
) {
|
|
948
|
+
return false
|
|
949
|
+
}
|
|
950
|
+
const size: i32 = 1 + quicVarintSize(streamId) + quicVarintSize(value)
|
|
951
|
+
const at: i32 = quicFrameGrow(out, size)
|
|
952
|
+
return quicPutStreamValue(out, at, at + size, type, streamId, value) >= 0
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/** The size of a NEW_CONNECTION_ID frame, or 0 for arguments `quicPutNewConnectionId` refuses. */
|
|
956
|
+
const quicFrameNewCidSize = (sequence: i64, retirePriorTo: i64, cidLength: i32, tokenLength: i32): i32 => {
|
|
957
|
+
if (
|
|
958
|
+
cidLength < 1 ||
|
|
959
|
+
cidLength > QUIC_MAX_CID_LENGTH ||
|
|
960
|
+
tokenLength !== QUIC_RESET_TOKEN_SIZE ||
|
|
961
|
+
retirePriorTo < 0 ||
|
|
962
|
+
retirePriorTo > sequence ||
|
|
963
|
+
quicVarintSize(sequence) === 0
|
|
964
|
+
) {
|
|
965
|
+
return 0
|
|
966
|
+
}
|
|
967
|
+
return 1 + quicVarintSize(sequence) + quicVarintSize(retirePriorTo) + 1 + cidLength + QUIC_RESET_TOKEN_SIZE
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* Writes a NEW_CONNECTION_ID frame (§19.15) for the first `cidLength` bytes
|
|
972
|
+
* of `connectionId`. Answers -1 for a connection ID outside 1 to 20 bytes or
|
|
973
|
+
* past the array, a reset token that is not 16 bytes, a Retire Prior To
|
|
974
|
+
* above the sequence number, a value no varint holds, or no room.
|
|
975
|
+
*/
|
|
976
|
+
export const quicPutNewConnectionId = (
|
|
977
|
+
buf: u8[],
|
|
978
|
+
at: i32,
|
|
979
|
+
end: i32,
|
|
980
|
+
sequence: i64,
|
|
981
|
+
retirePriorTo: i64,
|
|
982
|
+
connectionId: u8[],
|
|
983
|
+
cidLength: i32,
|
|
984
|
+
resetToken: u8[]
|
|
985
|
+
): i32 => {
|
|
986
|
+
const size: i32 = quicFrameNewCidSize(sequence, retirePriorTo, cidLength, toI32(resetToken.length))
|
|
987
|
+
if (size === 0 || cidLength > toI32(connectionId.length) || !quicFrameRoom(buf, at, end, size)) {
|
|
988
|
+
return -1
|
|
989
|
+
}
|
|
990
|
+
let cursor: i32 = quicFramePutByte(buf, at, QUIC_FRAME_NEW_CONNECTION_ID)
|
|
991
|
+
cursor = quicFramePutVarint(buf, cursor, sequence)
|
|
992
|
+
cursor = quicFramePutVarint(buf, cursor, retirePriorTo)
|
|
993
|
+
cursor = quicFramePutByte(buf, cursor, cidLength)
|
|
994
|
+
quicPacketCopy(buf, cursor, connectionId, 0, cidLength)
|
|
995
|
+
quicPacketCopy(buf, cursor + cidLength, resetToken, 0, QUIC_RESET_TOKEN_SIZE)
|
|
996
|
+
return cursor + cidLength + QUIC_RESET_TOKEN_SIZE
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Appends a NEW_CONNECTION_ID frame (§19.15). Answers `false` for a
|
|
1001
|
+
* connection ID outside 1 to 20 bytes, a reset token that is not 16 bytes, a
|
|
1002
|
+
* Retire Prior To above the sequence number, or a value no varint holds.
|
|
1003
|
+
*/
|
|
1004
|
+
export const quicPushNewConnectionId = (
|
|
1005
|
+
out: u8[],
|
|
1006
|
+
sequence: i64,
|
|
1007
|
+
retirePriorTo: i64,
|
|
1008
|
+
connectionId: u8[],
|
|
1009
|
+
resetToken: u8[]
|
|
1010
|
+
): boolean => {
|
|
1011
|
+
const cidLength: i32 = toI32(connectionId.length)
|
|
1012
|
+
const size: i32 = quicFrameNewCidSize(sequence, retirePriorTo, cidLength, toI32(resetToken.length))
|
|
1013
|
+
if (size === 0) {
|
|
1014
|
+
return false
|
|
1015
|
+
}
|
|
1016
|
+
const at: i32 = quicFrameGrow(out, size)
|
|
1017
|
+
return (
|
|
1018
|
+
quicPutNewConnectionId(
|
|
1019
|
+
out,
|
|
1020
|
+
at,
|
|
1021
|
+
at + size,
|
|
1022
|
+
sequence,
|
|
1023
|
+
retirePriorTo,
|
|
1024
|
+
connectionId,
|
|
1025
|
+
cidLength,
|
|
1026
|
+
resetToken
|
|
1027
|
+
) >= 0
|
|
1028
|
+
)
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Writes a PATH_CHALLENGE or PATH_RESPONSE frame carrying the eight bytes
|
|
1033
|
+
* `data[from .. from + 8)`. Answers -1 for another type, a window outside
|
|
1034
|
+
* `data`, or no room.
|
|
1035
|
+
*/
|
|
1036
|
+
export const quicPutPathData = (buf: u8[], at: i32, end: i32, type: i32, data: u8[], from: i32): i32 => {
|
|
1037
|
+
if (
|
|
1038
|
+
(type !== QUIC_FRAME_PATH_CHALLENGE && type !== QUIC_FRAME_PATH_RESPONSE) ||
|
|
1039
|
+
!quicFrameWindowFits(data, from, QUIC_PATH_DATA_SIZE) ||
|
|
1040
|
+
!quicFrameRoom(buf, at, end, 1 + QUIC_PATH_DATA_SIZE)
|
|
1041
|
+
) {
|
|
1042
|
+
return -1
|
|
1043
|
+
}
|
|
1044
|
+
const cursor: i32 = quicFramePutByte(buf, at, type)
|
|
1045
|
+
quicPacketCopy(buf, cursor, data, from, QUIC_PATH_DATA_SIZE)
|
|
1046
|
+
return cursor + QUIC_PATH_DATA_SIZE
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
/** Appends the frame `quicPutPathData` writes. Answers `false`, appending nothing, where it refuses. */
|
|
1050
|
+
export const quicPushPathData = (out: u8[], type: i32, data: u8[], from: i32): boolean => {
|
|
1051
|
+
if (
|
|
1052
|
+
(type !== QUIC_FRAME_PATH_CHALLENGE && type !== QUIC_FRAME_PATH_RESPONSE) ||
|
|
1053
|
+
!quicFrameWindowFits(data, from, QUIC_PATH_DATA_SIZE)
|
|
1054
|
+
) {
|
|
1055
|
+
return false
|
|
1056
|
+
}
|
|
1057
|
+
const at: i32 = quicFrameGrow(out, 1 + QUIC_PATH_DATA_SIZE)
|
|
1058
|
+
return quicPutPathData(out, at, at + 1 + QUIC_PATH_DATA_SIZE, type, data, from) >= 0
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* Writes a RESET_STREAM frame (§19.4) or, with `finalSize` at -1, a
|
|
1063
|
+
* STOP_SENDING frame (§19.5). Answers -1 for a value no varint holds, or no room.
|
|
1064
|
+
*/
|
|
1065
|
+
export const quicPutStreamError = (
|
|
1066
|
+
buf: u8[],
|
|
1067
|
+
at: i32,
|
|
1068
|
+
end: i32,
|
|
1069
|
+
streamId: i64,
|
|
1070
|
+
errorCode: i64,
|
|
1071
|
+
finalSize: i64
|
|
1072
|
+
): i32 => {
|
|
1073
|
+
const reset: boolean = finalSize !== -1
|
|
1074
|
+
if (!quicFrameVarints3(streamId, errorCode, reset ? finalSize : 0)) {
|
|
1075
|
+
return -1
|
|
1076
|
+
}
|
|
1077
|
+
const size: i32 =
|
|
1078
|
+
1 + quicVarintSize(streamId) + quicVarintSize(errorCode) + (reset ? quicVarintSize(finalSize) : 0)
|
|
1079
|
+
if (!quicFrameRoom(buf, at, end, size)) {
|
|
1080
|
+
return -1
|
|
1081
|
+
}
|
|
1082
|
+
let cursor: i32 = quicFramePutByte(buf, at, reset ? QUIC_FRAME_RESET_STREAM : QUIC_FRAME_STOP_SENDING)
|
|
1083
|
+
cursor = quicFramePutVarint(buf, cursor, streamId)
|
|
1084
|
+
cursor = quicFramePutVarint(buf, cursor, errorCode)
|
|
1085
|
+
if (reset) {
|
|
1086
|
+
cursor = quicFramePutVarint(buf, cursor, finalSize)
|
|
1087
|
+
}
|
|
1088
|
+
return cursor
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/** Appends the frame `quicPutStreamError` writes. Answers `false`, appending nothing, where it refuses. */
|
|
1092
|
+
export const quicPushStreamError = (out: u8[], streamId: i64, errorCode: i64, finalSize: i64): boolean => {
|
|
1093
|
+
const reset: boolean = finalSize !== -1
|
|
1094
|
+
if (!quicFrameVarints3(streamId, errorCode, reset ? finalSize : 0)) {
|
|
1095
|
+
return false
|
|
1096
|
+
}
|
|
1097
|
+
const size: i32 =
|
|
1098
|
+
1 + quicVarintSize(streamId) + quicVarintSize(errorCode) + (reset ? quicVarintSize(finalSize) : 0)
|
|
1099
|
+
const at: i32 = quicFrameGrow(out, size)
|
|
1100
|
+
return quicPutStreamError(out, at, at + size, streamId, errorCode, finalSize) >= 0
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/** The size of a CONNECTION_CLOSE frame, or 0 for a value no varint holds. */
|
|
1104
|
+
const quicFrameCloseSize = (application: boolean, errorCode: i64, frameType: i64, reasonLength: i32): i32 => {
|
|
1105
|
+
if (!quicFrameVarints3(errorCode, application ? 0 : frameType, toI64(reasonLength))) {
|
|
1106
|
+
return 0
|
|
1107
|
+
}
|
|
1108
|
+
return (
|
|
1109
|
+
1 +
|
|
1110
|
+
quicVarintSize(errorCode) +
|
|
1111
|
+
(application ? 0 : quicVarintSize(frameType)) +
|
|
1112
|
+
quicVarintSize(toI64(reasonLength)) +
|
|
1113
|
+
reasonLength
|
|
1114
|
+
)
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Writes a CONNECTION_CLOSE frame (§19.19): with `application` false the
|
|
1119
|
+
* transport form 0x1c, carrying `frameType`, the type of the frame that
|
|
1120
|
+
* caused the error (0 when none did); with `application` true the 0x1d form,
|
|
1121
|
+
* which has no frame type. `reason` is the phrase, UTF-8, possibly empty.
|
|
1122
|
+
* Answers -1 for a value no varint holds, or no room.
|
|
1123
|
+
*/
|
|
1124
|
+
export const quicPutConnectionClose = (
|
|
1125
|
+
buf: u8[],
|
|
1126
|
+
at: i32,
|
|
1127
|
+
end: i32,
|
|
1128
|
+
application: boolean,
|
|
1129
|
+
errorCode: i64,
|
|
1130
|
+
frameType: i64,
|
|
1131
|
+
reason: u8[]
|
|
1132
|
+
): i32 => {
|
|
1133
|
+
const reasonLength: i32 = toI32(reason.length)
|
|
1134
|
+
const size: i32 = quicFrameCloseSize(application, errorCode, frameType, reasonLength)
|
|
1135
|
+
if (!quicFrameRoom(buf, at, end, size)) {
|
|
1136
|
+
return -1
|
|
1137
|
+
}
|
|
1138
|
+
let cursor: i32 = quicFramePutByte(
|
|
1139
|
+
buf,
|
|
1140
|
+
at,
|
|
1141
|
+
application ? QUIC_FRAME_CONNECTION_CLOSE_APP : QUIC_FRAME_CONNECTION_CLOSE
|
|
1142
|
+
)
|
|
1143
|
+
cursor = quicFramePutVarint(buf, cursor, errorCode)
|
|
1144
|
+
if (!application) {
|
|
1145
|
+
cursor = quicFramePutVarint(buf, cursor, frameType)
|
|
1146
|
+
}
|
|
1147
|
+
cursor = quicFramePutVarint(buf, cursor, toI64(reasonLength))
|
|
1148
|
+
quicPacketCopy(buf, cursor, reason, 0, reasonLength)
|
|
1149
|
+
return cursor + reasonLength
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/** Appends the frame `quicPutConnectionClose` writes. Answers `false`, appending nothing, where it refuses. */
|
|
1153
|
+
export const quicPushConnectionClose = (
|
|
1154
|
+
out: u8[],
|
|
1155
|
+
application: boolean,
|
|
1156
|
+
errorCode: i64,
|
|
1157
|
+
frameType: i64,
|
|
1158
|
+
reason: u8[]
|
|
1159
|
+
): boolean => {
|
|
1160
|
+
const size: i32 = quicFrameCloseSize(application, errorCode, frameType, toI32(reason.length))
|
|
1161
|
+
if (size === 0) {
|
|
1162
|
+
return false
|
|
1163
|
+
}
|
|
1164
|
+
const at: i32 = quicFrameGrow(out, size)
|
|
1165
|
+
return quicPutConnectionClose(out, at, at + size, application, errorCode, frameType, reason) >= 0
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Appends a NEW_TOKEN frame (§19.7) carrying `token`. Answers `false` for an
|
|
1170
|
+
* empty token, which §19.7 forbids.
|
|
1171
|
+
*/
|
|
1172
|
+
export const quicPushNewToken = (out: u8[], token: u8[]): boolean => {
|
|
1173
|
+
const length: i32 = toI32(token.length)
|
|
1174
|
+
if (length === 0) {
|
|
1175
|
+
return false
|
|
1176
|
+
}
|
|
1177
|
+
const size: i32 = 1 + quicVarintSize(toI64(length)) + length
|
|
1178
|
+
let cursor: i32 = quicFramePutByte(out, quicFrameGrow(out, size), QUIC_FRAME_NEW_TOKEN)
|
|
1179
|
+
cursor = quicFramePutVarint(out, cursor, toI64(length))
|
|
1180
|
+
quicPacketCopy(out, cursor, token, 0, length)
|
|
1181
|
+
return true
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* The size of the DATAGRAM frame `quicPutDatagram` writes for a payload of
|
|
1186
|
+
* `length` bytes: the type, the Length and the payload. This is the size RFC
|
|
1187
|
+
* 9221 §3 holds to `max_datagram_frame_size`.
|
|
1188
|
+
*/
|
|
1189
|
+
export const quicDatagramSize = (length: i32): i32 => 1 + quicVarintSize(toI64(length)) + length
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* Writes a DATAGRAM frame with a Length (type 0x31, RFC 9221 §4) carrying
|
|
1193
|
+
* `data[from .. from + length)`. Answers -1 for a window outside `data` or no room.
|
|
1194
|
+
*/
|
|
1195
|
+
export const quicPutDatagram = (buf: u8[], at: i32, end: i32, data: u8[], from: i32, length: i32): i32 => {
|
|
1196
|
+
if (!quicFrameWindowFits(data, from, length) || !quicFrameRoom(buf, at, end, quicDatagramSize(length))) {
|
|
1197
|
+
return -1
|
|
1198
|
+
}
|
|
1199
|
+
const cursor: i32 = quicFramePutVarint(
|
|
1200
|
+
buf,
|
|
1201
|
+
quicFramePutByte(buf, at, QUIC_FRAME_DATAGRAM_LENGTH),
|
|
1202
|
+
toI64(length)
|
|
1203
|
+
)
|
|
1204
|
+
quicPacketCopy(buf, cursor, data, from, length)
|
|
1205
|
+
return cursor + length
|
|
1206
|
+
}
|