lib/quic/src/connection/machine.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const quic = @import("../root.zig");
3
4 /// Three-second span that a closing or draining connection stands before it reports itself closed,
5 /// so a caller waiting on `nextTimeout` knows how long the connection stays before reporting
6 /// closed. Both `close` and the path that fails a connection from inside set that deadline three
7 /// seconds past the time the caller gave. The span is a constant, so it rests on no measurement of
8 /// the path.
9 pub const closing_period_ns: u64 = 3 * std.time.ns_per_s;
10
11 /// Maximum byte length of a close reason, published so a caller can size its reason string against
12 /// this bound because `close` answers `ReasonTooLong` for anything longer. The figure defines how
13 /// far a close reason may run while its packets still fit a 1200-byte datagram with 20-byte
14 /// connection IDs at both ends. The figure is the lesser of what one Initial close packet leaves
15 /// free and what a Handshake and a 1-RTT close packet leave free between them, because a 1-RTT
16 /// close raised before the handshake is confirmed travels behind a Handshake copy.
17 pub const close_reason_bytes_max: usize = closeReasonCapacity();
18
19 const PacketView = struct {
20 kind: quic.connection.SpaceKind,
21 length: usize,
22 packet_number_offset: usize,
23 destination: quic.packet.ConnectionId,
24 source: ?quic.packet.ConnectionId,
25 };
26
27 const LongMetadata = struct {
28 kind: quic.connection.SpaceKind,
29 length: u62,
30 offset: usize,
31 destination: quic.packet.ConnectionId,
32 source: quic.packet.ConnectionId,
33 };
34
35 const Payload = struct {
36 bytes: []const u8,
37 summary: quic.connection.FrameSummary,
38 ack_eliciting: bool,
39 sent_ack: bool,
40 sent_ping: bool,
41 sent_handshake_done: bool,
42 sent_close: bool,
43 };
44
45 const PacketPlan = struct {
46 number: quic.connection.NumberEncoding,
47 payload_capacity: usize,
48 };
49
50 /// Armed probe timeout holding when it fires and which packet number space it is meant for, built
51 /// by the timer path to store on the connection. The `armedProbe` function picks the soonest probe
52 /// among the spaces holding packets the peer has yet to settle.
53 const Probe = struct {
54 deadline_ns: u64,
55 kind: quic.connection.SpaceKind,
56 };
57
58 /// Pair of results from opening one packet, holding the packet itself and whether it was protected
59 /// under a phase other than the one being read now. The receive path needs both results before it
60 /// can record anything in the key state. A long header carries no phase bit, so a packet with one
61 /// reports no difference.
62 const OpenedPacket = struct {
63 opened: quic.crypto.packet.Opened,
64 phase_changed: bool,
65 };
66
67 const ViewError = error{ Malformed, Unsupported };
68 const PayloadError = quic.frame.EncodeError || error{EmptyRanges};
69
70 const StreamId = quic.connection.stream.StreamId;
71 const SendState = quic.connection.stream.SendState;
72 const ReceiveState = quic.connection.stream.ReceiveState;
73 const ReadResult = quic.connection.stream.ReadResult;
74 const frameDataCapacity = quic.connection.stream.frameDataCapacity;
75 const frameHeaderBytes = quic.connection.stream.frameHeaderBytes;
76
77 pub const OpenError = error{ NotClient, NotEstablished, StreamExists, StreamLimit };
78
79 /// Acknowledgment sink taking each sent record that one ACK frame settled for the first time, so
80 /// the connection releases what the packet carried. A settled CRYPTO frame takes its bytes out of
81 /// the handshake stretch that space had waiting. In the application space, a settled STREAM frame
82 /// and a settled RESET_STREAM go on to the sending half of stream 0. A record from either other
83 /// space stops once the CRYPTO step is done.
84 const AckSink = struct {
85 connection: *Connection,
86 kind: quic.connection.SpaceKind,
87
88 pub fn onAcknowledged(self: AckSink, record: quic.connection.SentRecord) void {
89 const frames = record.frames;
90 if (frames.crypto) |range| {
91 const lost = &self.connection.crypto_lost[spaceIndex(self.kind)];
92 lost.acknowledge(range.offset, range.length);
93 }
94 if (self.kind != .application) return;
95 const send_part = &self.connection.stream_send;
96 if (frames.stream) |range| send_part.acknowledge(range.offset, range.length, range.fin);
97 if (frames.reset_stream != null) send_part.acknowledgeReset();
98 }
99 };
100
101 /// Loss sink taking and counting each record one detection pass gave up on, so the connection
102 /// queues those frames again. A lost CRYPTO frame puts its bytes into the handshake stretch that
103 /// space has waiting. In the application space, the remainder of the record goes to
104 /// `loseApplicationFrames`. ACK, PING, and CONNECTION_CLOSE frames find no route back into a
105 /// packet, since `loseApplicationFrames` holds no branch for them.
106 const LossSink = struct {
107 connection: *Connection,
108 kind: quic.connection.SpaceKind,
109
110 pub fn onLost(self: LossSink, record: quic.connection.SentRecord) void {
111 std.debug.assert(record.lost);
112 std.debug.assert(self.kind == self.connection.space(self.kind).kind);
113 std.debug.assert(record.frames.max_streams == null);
114 increment(&self.connection.stats_value.packets_lost);
115 const frames = record.frames;
116 if (frames.crypto) |range| {
117 const lost = &self.connection.crypto_lost[spaceIndex(self.kind)];
118 lost.add(range.offset, range.length);
119 }
120 if (self.kind == .application) self.connection.loseApplicationFrames(frames);
121 }
122 };
123
124 pub const InitError = quic.connection.Storage.Capacity.DeriveError ||
125 quic.connection.KeyState.InstallError || quic.tls.InitError ||
126 quic.connection.parameters.Error ||
127 error{ InvalidConfig, RandomFailed, StorageMismatch };
128
129 /// Primary type for one QUIC endpoint, held by a caller to drive with `receive`, `send`, and
130 /// `nextTimeout`, reading and writing byte slices its caller owns, and opening no socket of its
131 /// own. The connection holds a pointer into its storage, so it and that storage both stay at fixed
132 /// addresses for as long as it lives. The `init` constructor wants a configuration, the limits, and
133 /// storage already set up for those same limits, and answers `StorageMismatch` for storage set up
134 /// for others. Inside the connection are one record per packet number space, the key state, the TLS
135 /// engine, both halves of stream 0, and the send credit and receive window that cover the whole
136 /// connection. The `deinit` method gives back the TLS storage and the keys, and marks the storage
137 /// ready for the caller to take back.
138 pub const Connection = struct {
139 config: quic.connection.Config,
140 limits: quic.connection.Limits,
141 storage: *quic.connection.Storage,
142 local_cid: quic.packet.ConnectionId,
143 peer_cid: ?quic.packet.ConnectionId,
144 original_dcid: ?quic.packet.ConnectionId,
145 peer_source_cid: ?quic.packet.ConnectionId,
146 engine: ?quic.tls.Engine,
147 keys: quic.connection.KeyState,
148 spaces: [3]quic.connection.Space,
149 crypto_streams: [3]quic.connection.Reassembler,
150 crypto_send_offsets: [3]u62 = .{ 0, 0, 0 },
151 /// Storage where each packet number space's lost handshake bytes wait for a packet, holding
152 /// them until they go out again ahead of anything TLS has newly produced. The `emitCrypto`
153 /// method clears them before it asks TLS for anything new. Dropping a space's keys empties its
154 /// entry.
155 crypto_lost: [3]quic.connection.recovery.LostRange = @splat(.{}),
156 status_value: quic.connection.Status = .handshaking,
157 close_reason_value: ?quic.connection.CloseReason = null,
158 close_pending: bool = false,
159 close_deadline_ns: ?u64 = null,
160 close_packets_since_response: u16 = 0,
161 close_response_packet_limit: u16 = 1,
162 pending_ping: bool = false,
163 pending_handshake_done: bool = false,
164 handshake_done_received: bool = false,
165 handshake_confirmed: bool = false,
166 handshake_acknowledged: bool = false,
167 rtt: quic.connection.recovery.Estimator = .{},
168 pto_count: u32 = 0,
169 pto_deadline_ns: ?u64 = null,
170 pto_space: quic.connection.SpaceKind = .initial,
171 /// Timestamp recording when a client first armed its anti-deadlock probe with nothing
172 /// outstanding, holding that deadline still across the arming that `send` and `receive` do on
173 /// every call. Both `send` and `receive` arm the timer afresh on every call, and this anchor
174 /// keeps those calls from pushing the probe further off. The anchor empties once packets go
175 /// out, once the peer has proof of this endpoint's address, and once a probe deadline passes.
176 pto_anchor_ns: ?u64 = null,
177 probe_packets: [3]u8 = @splat(0),
178 peer_parameters_authenticated: bool = false,
179 peer_parameters: ?quic.transport.Parameters = null,
180 stream_send: quic.connection.stream.Send,
181 stream_receive: quic.connection.stream.Receive,
182 connection_credit: quic.connection.stream.Credit = .{ .limit = 0 },
183 connection_window: quic.connection.stream.Window,
184 stream_open: bool = false,
185 stream_accepted: bool = false,
186 peer_streams_bidi: u62 = 0,
187 streams_blocked_pending: bool = false,
188 streams_blocked_sent: bool = false,
189 effective_idle_timeout_ms: u62,
190 timer_started: bool = false,
191 last_activity_ns: u64 = 0,
192 client_address_validated: bool,
193 received_before_validation: u64 = 0,
194 sent_before_validation: u64 = 0,
195 stats_value: quic.connection.Stats = .{},
196 deinitialized: bool = false,
197
198 pub fn init(
199 config: quic.connection.Config,
200 limits: quic.connection.Limits,
201 storage: *quic.connection.Storage,
202 ) InitError!Connection {
203 const expected = try quic.connection.Storage.Capacity.derive(limits);
204 if (storage.phase != .initialization) return error.StorageMismatch;
205 if (storage.capacity.storage_bytes != expected.storage_bytes) {
206 return error.StorageMismatch;
207 }
208 if (storage.capacity.stream_receive_offset != expected.stream_receive_offset) {
209 return error.StorageMismatch;
210 }
211 if (config.local_cid.len == 0) return error.InvalidConfig;
212 if (config.local_cid.len > quic.packet.connection_id_bytes_max) {
213 return error.InvalidConfig;
214 }
215 if (config.ack_delay_exponent > 20) return error.InvalidConfig;
216 const local_cid = quic.packet.ConnectionId.init(config.local_cid) catch
217 return error.InvalidConfig;
218 var self = Connection{
219 .config = config,
220 .limits = limits,
221 .storage = storage,
222 .local_cid = local_cid,
223 .peer_cid = null,
224 .original_dcid = null,
225 .peer_source_cid = null,
226 .engine = null,
227 .keys = quic.connection.KeyState.init(storage),
228 .spaces = initSpaces(storage),
229 .crypto_streams = initCryptoStreams(storage),
230 .stream_send = quic.connection.stream.Send.init(
231 storage.stream_send_bytes,
232 storage.stream_sent_ranges,
233 0,
234 ),
235 .stream_receive = quic.connection.stream.Receive.init(
236 storage.stream_receive_bytes,
237 storage.stream_receive_ranges,
238 quic.connection.parameters.streamReceiveWindow(config, limits),
239 ),
240 .connection_window = quic.connection.stream.Window.init(config.initial_max_data),
241 .effective_idle_timeout_ms = config.max_idle_timeout,
242 .client_address_validated = config.role == .client,
243 };
244 errdefer self.keys.deinit();
245 if (config.role == .client) try self.initializeClient();
246 storage.activate();
247 std.debug.assert(self.storage == storage);
248 std.debug.assert(self.local_cid.length >= 1);
249 return self;
250 }
251
252 pub fn deinit(self: *Connection) void {
253 std.debug.assert(!self.deinitialized);
254 if (self.engine) |*engine| {
255 engine.deinit();
256 } else {
257 self.storage.tls_storage.activate();
258 }
259 _ = self.storage.tls_storage.deinit();
260 self.keys.deinit();
261 self.storage.nested_released = true;
262 self.deinitialized = true;
263 }
264
265 pub fn state(self: *const Connection) quic.connection.Status {
266 std.debug.assert(!self.deinitialized);
267 return self.status_value;
268 }
269
270 pub fn closeReason(self: *const Connection) ?quic.connection.CloseReason {
271 std.debug.assert(!self.deinitialized);
272 return self.close_reason_value;
273 }
274
275 pub fn stats(self: *const Connection) quic.connection.Stats {
276 std.debug.assert(!self.deinitialized);
277 return self.stats_value;
278 }
279
280 /// Hands back what the connection has measured of the path, so a caller sees what the
281 /// connection's timers rest on.
282 pub fn roundTrip(self: *const Connection) quic.connection.recovery.Estimator {
283 std.debug.assert(!self.deinitialized);
284 return self.rtt;
285 }
286
287 /// Adds up what all three packet number spaces have outstanding, so a caller watching
288 /// congestion reads the total bytes in flight.
289 pub fn bytesInFlight(self: *const Connection) u64 {
290 std.debug.assert(!self.deinitialized);
291 var total: u64 = 0;
292 for (&self.spaces) |*space_value| total += space_value.bytesInFlight();
293 return total;
294 }
295
296 pub fn peer(self: *const Connection) ?quic.tls.Peer {
297 std.debug.assert(!self.deinitialized);
298 const engine = if (self.engine) |*value| value else return null;
299 return engine.peer();
300 }
301
302 pub fn initialKeysDiscarded(self: *const Connection) bool {
303 return self.keys.initial_write == null and self.keys.initial_read == null;
304 }
305
306 pub fn handshakeKeysDiscarded(self: *const Connection) bool {
307 return self.keys.handshake_write == null and self.keys.handshake_read == null;
308 }
309
310 pub fn requestKeyUpdate(self: *Connection) error{NotEstablished}!void {
311 if (self.status_value != .established) return error.NotEstablished;
312 self.keys.requestUpdate();
313 self.pending_ping = true;
314 }
315
316 pub fn ping(self: *Connection) error{NotEstablished}!void {
317 if (self.status_value != .established) return error.NotEstablished;
318 self.pending_ping = true;
319 }
320
321 /// Opens stream 0 for a client once the connection is established, and gives back the
322 /// identifier 0, so the client obtains stream 0 before it writes. A server gets `NotClient`, a
323 /// call made ahead of that point gets `NotEstablished`, and a repeat gets `StreamExists`. A
324 /// peer that allows no streams gets `StreamLimit`, and one STREAMS_BLOCKED frame goes in the
325 /// queue.
326 pub fn openStream(self: *Connection) OpenError!StreamId {
327 std.debug.assert(!self.deinitialized);
328 if (self.config.role != .client) return error.NotClient;
329 if (self.status_value != .established) return error.NotEstablished;
330 if (self.stream_open) return error.StreamExists;
331 if (self.peer_streams_bidi == 0) {
332 if (!self.streams_blocked_sent) self.streams_blocked_pending = true;
333 return error.StreamLimit;
334 }
335 self.stream_open = true;
336 return 0;
337 }
338
339 /// Gives a server the identifier 0 once, after the client's first frame for that stream has
340 /// arrived, so the server learns that the client has opened stream 0. A client receives null,
341 /// and a server asking ahead of that frame or asking a second time also receives null.
342 pub fn acceptStream(self: *Connection) ?StreamId {
343 std.debug.assert(!self.deinitialized);
344 if (self.config.role != .server) return null;
345 if (!self.stream_open) return null;
346 if (self.stream_accepted) return null;
347 self.stream_accepted = true;
348 return 0;
349 }
350
351 /// Takes application bytes into the send buffer, as many as the free room allows, and reports
352 /// how many it took, providing the entry point for an application to hand stream bytes to the
353 /// connection. Bytes go in for identifier 0 on an open stream, while the connection is
354 /// handshaking or established.
355 pub fn write(self: *Connection, id: StreamId, bytes: []const u8) usize {
356 if (!self.streamOpenForApplication(id)) return 0;
357 return self.stream_send.write(bytes);
358 }
359
360 /// Settles the final size at whatever has been written so far, so an application ends its side
361 /// of the stream. The call acts for identifier 0 on an open stream, while the connection is
362 /// handshaking or established.
363 pub fn finish(self: *Connection, id: StreamId) void {
364 if (!self.streamOpenForApplication(id)) return;
365 self.stream_send.finish();
366 }
367
368 /// Copies waiting bytes in offset order and says whether they carry the reader to the FIN,
369 /// enabling an application to take received stream bytes and release receive window room back
370 /// to the peer. The method copies for identifier 0 on an open stream.
371 pub fn read(self: *Connection, id: StreamId, out: []u8) ReadResult {
372 if (!self.streamKnown(id)) return .{ .bytes = 0, .fin = false };
373 return self.stream_receive.read(&self.connection_window, out);
374 }
375
376 /// Gives up on sending and puts RESET_STREAM in the queue under the caller's code, so an
377 /// application abandons its side of the stream. The call acts for identifier 0 on an open
378 /// stream, while the connection is handshaking or established.
379 pub fn resetStream(self: *Connection, id: StreamId, app_error_code: u62) void {
380 if (!self.streamOpenForApplication(id)) return;
381 _ = self.stream_send.reset(app_error_code);
382 }
383
384 /// Puts STOP_SENDING in the queue under the caller's code while data may still arrive, so an
385 /// application tells the peer to stop sending. The call acts for identifier 0 on an open
386 /// stream, while the connection is handshaking or established.
387 pub fn stopSending(self: *Connection, id: StreamId, app_error_code: u62) void {
388 if (!self.streamOpenForApplication(id)) return;
389 _ = self.stream_receive.stop(app_error_code);
390 }
391
392 /// Hands back how far the sending half of an open stream has traveled, so an application sees
393 /// whether its data was acknowledged or its reset took effect. The method answers for
394 /// identifier 0 on an open stream, and offers null elsewhere.
395 pub fn sendState(self: *const Connection, id: StreamId) ?SendState {
396 if (!self.streamKnown(id)) return null;
397 return self.stream_send.state;
398 }
399
400 /// Hands back how far the receiving half of an open stream has traveled, so an application
401 /// sees whether the peer has finished or reset the stream. The method answers for identifier 0
402 /// on an open stream, and offers null elsewhere.
403 pub fn receiveState(self: *const Connection, id: StreamId) ?ReceiveState {
404 if (!self.streamKnown(id)) return null;
405 return self.stream_receive.state;
406 }
407
408 /// Hands back how far an open stream's data has traveled and which codes each side sent, so an
409 /// application inspects stream progress and exchanged error codes. The method answers for
410 /// identifier 0 on an open stream, and offers null elsewhere.
411 pub fn streamStats(self: *const Connection, id: StreamId) ?quic.connection.StreamStats {
412 if (!self.streamKnown(id)) return null;
413 const send_part = &self.stream_send;
414 const receive_part = &self.stream_receive;
415 return .{
416 .bytes_written = send_part.written,
417 .bytes_sent = send_part.sent,
418 .bytes_released = send_part.acknowledged,
419 .bytes_received = receive_part.window.received,
420 .bytes_read = receive_part.read_offset,
421 .send_final_size = send_part.final_size,
422 .receive_final_size = receive_part.final_size,
423 .reset_sent_code = send_part.reset_code,
424 .reset_received_code = receive_part.reset_code,
425 .stop_sending_sent_code = receive_part.stop_code,
426 };
427 }
428
429 /// Moves the connection into closing and starts the closing period from the time the caller
430 /// gave, so a close unable to reach the wire still ends and the caller receives the deadline to
431 /// stop driving the connection. A reason past `close_reason_bytes_max` answers `ReasonTooLong`
432 /// and changes nothing. The reason text is copied into the connection's own storage. When
433 /// called on a connection that has already left handshaking or established, the call returns
434 /// with the status untouched.
435 pub fn close(
436 self: *Connection,
437 error_code: u62,
438 application: bool,
439 reason: []const u8,
440 now_ns: u64,
441 ) error{ReasonTooLong}!void {
442 std.debug.assert(!self.deinitialized);
443 std.debug.assert(close_reason_bytes_max <= self.storage.reason.len);
444 if (reason.len > close_reason_bytes_max) return error.ReasonTooLong;
445 self.advanceTime(now_ns);
446 if (!isOpen(self.status_value)) return;
447 std.debug.assert(self.close_deadline_ns == null);
448 self.storeReason(error_code, application, null, reason, false);
449 self.status_value = .closing;
450 self.close_pending = true;
451 self.close_deadline_ns = closeDeadline(now_ns);
452 }
453
454 pub fn receive(self: *Connection, datagram: []const u8, now_ns: u64) void {
455 std.debug.assert(self.storage.phase == .steady);
456 std.debug.assert(!self.deinitialized);
457 self.advanceTime(now_ns);
458 if (self.status_value == .closed or self.status_value == .draining) return;
459 increment(&self.stats_value.datagrams_received);
460 if (self.config.role == .server and !self.client_address_validated) {
461 self.received_before_validation +|= datagram.len;
462 }
463 defer self.armProbeTimeout(now_ns);
464 if (datagram.len > self.limits.datagram_bytes) {
465 increment(&self.stats_value.drops.oversized);
466 return;
467 }
468 if (self.status_value == .closing) self.noteClosingDatagram();
469 self.receivePackets(datagram, now_ns);
470 }
471
472 pub fn send(self: *Connection, out: []u8, now_ns: u64) usize {
473 std.debug.assert(self.storage.phase == .steady);
474 std.debug.assert(!self.deinitialized);
475 self.advanceTime(now_ns);
476 if (self.status_value == .closed or self.status_value == .draining) return 0;
477 if (out.len == 0) return 0;
478 if (self.status_value == .closing) return self.sendClosing(out, now_ns);
479 if (self.status_value == .established) {
480 self.keys.prepareSend(true, self.space(.application).next_packet_number);
481 }
482 var datagram_length: usize = 0;
483 var carries_initial = false;
484 const can_coalesce_handshake = self.keys.write(.handshake) != null;
485 for ([_]quic.connection.SpaceKind{ .initial, .handshake, .application }) |kind| {
486 const limit = self.sendLimit(out.len);
487 if (datagram_length >= limit) break;
488 const available = limit - datagram_length;
489 const minimum = packetMinimum(kind, available, carries_initial, can_coalesce_handshake);
490 const force_ping = (kind == .handshake and carries_initial) or
491 self.probe_packets[spaceIndex(kind)] != 0;
492 const count = self.sendSpace(
493 kind,
494 out[datagram_length..limit],
495 available,
496 minimum,
497 force_ping,
498 now_ns,
499 );
500 if (kind == .initial and count != 0) carries_initial = true;
501 datagram_length += count;
502 }
503 if (carries_initial) std.debug.assert(datagram_length >= 1200);
504 if (datagram_length != 0) self.recordDatagramSent(datagram_length);
505 self.armProbeTimeout(now_ns);
506 return datagram_length;
507 }
508
509 pub fn nextTimeout(self: *const Connection, now_ns: u64) ?u64 {
510 std.debug.assert(self.storage.phase == .steady);
511 std.debug.assert(!self.deinitialized);
512 const deadline = switch (self.status_value) {
513 .handshaking, .established => self.activeDeadline() orelse return null,
514 .closing, .draining => self.close_deadline_ns orelse return null,
515 .closed => return null,
516 };
517 return @max(deadline, now_ns);
518 }
519
520 fn activeDeadline(self: *const Connection) ?u64 {
521 std.debug.assert(isOpen(self.status_value));
522 std.debug.assert(self.close_deadline_ns == null);
523 var deadline: ?u64 = null;
524 for (&self.spaces) |*space_value| {
525 if (space_value.ack_due_at_ns) |due| deadline = earlier(deadline, due);
526 if (space_value.loss_deadline_ns) |due| deadline = earlier(deadline, due);
527 }
528 if (self.pto_deadline_ns) |due| deadline = earlier(deadline, due);
529 if (self.idleDeadline()) |idle| deadline = earlier(deadline, idle);
530 return deadline;
531 }
532
533 fn initializeClient(self: *Connection) InitError!void {
534 std.debug.assert(self.config.role == .client);
535 std.debug.assert(self.engine == null);
536 var random = self.config.random.values() catch return error.RandomFailed;
537 defer std.crypto.secureZero(u8, std.mem.asBytes(&random));
538 const destination = quic.packet.ConnectionId.init(random.hello[0..8]) catch
539 return error.InvalidConfig;
540 self.peer_cid = destination;
541 self.original_dcid = destination;
542 try self.keys.installInitial(.client, destination.slice());
543 const encoded = try quic.connection.parameters.encode(
544 self.config,
545 self.limits,
546 self.local_cid,
547 null,
548 self.storage.transport,
549 );
550 self.engine = try quic.tls.Engine.init(.{
551 .role = .client,
552 .identity = self.config.identity,
553 .expected_peer = self.config.expected_peer,
554 .alpn = self.config.alpn,
555 .transport_parameters = encoded,
556 .server_name = self.config.server_name,
557 .random = self.config.random,
558 .cipher_suite = preferredCipherSuite(self.config.preferred_suite),
559 }, .{ .max_message = self.limits.tls_message_max }, &self.storage.tls_storage);
560 }
561
562 fn initializeServer(self: *Connection, view: PacketView, now_ns: u64) bool {
563 std.debug.assert(self.config.role == .server);
564 std.debug.assert(self.engine == null);
565 std.debug.assert(view.kind == .initial);
566 std.debug.assert(view.destination.length >= 8);
567 const source = view.source orelse return false;
568 self.original_dcid = view.destination;
569 self.peer_source_cid = source;
570 self.peer_cid = source;
571 self.keys.installInitial(.server, view.destination.slice()) catch {
572 self.fail(quic.connection.TransportError.internal_error, null, "initial keys", now_ns);
573 return false;
574 };
575 const encoded = quic.connection.parameters.encode(
576 self.config,
577 self.limits,
578 self.local_cid,
579 view.destination,
580 self.storage.transport,
581 ) catch {
582 self.fail(quic.connection.TransportError.internal_error, null, "parameters", now_ns);
583 return false;
584 };
585 self.engine = quic.tls.Engine.init(.{
586 .role = .server,
587 .identity = self.config.identity,
588 .expected_peer = self.config.expected_peer,
589 .alpn = self.config.alpn,
590 .transport_parameters = encoded,
591 .server_name = self.config.server_name,
592 .random = self.config.random,
593 .cipher_suite = preferredCipherSuite(self.config.preferred_suite),
594 }, .{ .max_message = self.limits.tls_message_max }, &self.storage.tls_storage) catch {
595 self.fail(quic.connection.TransportError.internal_error, null, "TLS init", now_ns);
596 return false;
597 };
598 return true;
599 }
600
601 fn receivePackets(self: *Connection, datagram: []const u8, now_ns: u64) void {
602 std.debug.assert(datagram.len <= self.limits.datagram_bytes);
603 std.debug.assert(self.status_value != .closed);
604 var offset: usize = 0;
605 var first_destination: ?quic.packet.ConnectionId = null;
606 for (0..self.limits.datagram_bytes) |_| {
607 if (offset >= datagram.len) break;
608 const view = inspectPacket(datagram[offset..], self.local_cid.length) catch |failure| {
609 if (failure == error.Malformed) increment(&self.stats_value.drops.malformed);
610 if (failure == error.Unsupported) increment(&self.stats_value.drops.unsupported);
611 return;
612 };
613 if (first_destination == null) first_destination = view.destination;
614 if (!sameCid(first_destination.?, view.destination)) {
615 increment(&self.stats_value.drops.wrong_connection);
616 offset += view.length;
617 continue;
618 }
619 self.receivePacket(datagram[offset..][0..view.length], datagram.len, view, now_ns);
620 if (self.status_value == .draining or self.status_value == .closed) return;
621 offset += view.length;
622 if (view.kind == .application) break;
623 }
624 }
625
626 /// Admits one packet by opening the packet, working through the payload, and writing the packet
627 /// number into the space, serving as the single admission path that keeps the key state honest
628 /// through the order of its steps. The key state is written last, after the payload has been
629 /// worked through and the number written. That order holds because a packet can decrypt and
630 /// still be dropped without ending the connection, and the key state is meant to describe the
631 /// packets this endpoint took. A number the space holds already counts as a repeat, and the
632 /// packet goes no further. A server taking in a Handshake packet has its proof of the client's
633 /// address, and drops its Initial keys.
634 fn receivePacket(
635 self: *Connection,
636 packet_bytes: []const u8,
637 datagram_length: usize,
638 view: PacketView,
639 now_ns: u64,
640 ) void {
641 std.debug.assert(packet_bytes.len <= datagram_length);
642 std.debug.assert(datagram_length <= self.limits.datagram_bytes);
643 if (self.config.role == .server and self.engine == null and
644 view.kind == .initial and view.destination.length < 8)
645 {
646 increment(&self.stats_value.drops.malformed);
647 return;
648 }
649 if (self.config.role == .server and view.kind == .initial and datagram_length < 1200) {
650 increment(&self.stats_value.drops.undersized_initial);
651 return;
652 }
653 if (self.config.role == .server and self.engine == null) {
654 if (view.kind != .initial) {
655 increment(&self.stats_value.drops.unavailable_keys);
656 return;
657 }
658 if (!self.initializeServer(view, now_ns)) return;
659 }
660 if (!self.validateConnectionIds(view)) {
661 increment(&self.stats_value.drops.wrong_connection);
662 return;
663 }
664 @memcpy(self.storage.packet[0..packet_bytes.len], packet_bytes);
665 const mutable = self.storage.packet[0..packet_bytes.len];
666 const result = self.openPacket(view, mutable, now_ns) orelse return;
667 const opened = result.opened;
668 const space_value = self.space(view.kind);
669 if (space_value.received.contains(opened.packet_number)) {
670 increment(&self.stats_value.drops.duplicate);
671 return;
672 }
673 const ack_eliciting = self.processPayload(view.kind, opened.payload, now_ns) orelse return;
674 const accepted = space_value.recordReceived(
675 opened.packet_number,
676 ack_eliciting,
677 now_ns,
678 maxAckDelayNs(self.config.max_ack_delay),
679 );
680 std.debug.assert(accepted);
681 if (view.kind == .application and isOpen(self.status_value)) {
682 const observed = self.observeApplicationRead(
683 opened.packet_number,
684 result.phase_changed,
685 now_ns,
686 );
687 if (!observed) return;
688 }
689 if (self.keys.write(view.kind) == null) space_value.clearAck();
690 increment(&self.stats_value.packets_received);
691 self.touchActivity(now_ns);
692 if (self.config.role == .server and view.kind == .handshake) {
693 self.client_address_validated = true;
694 self.discardInitialKeys();
695 }
696 }
697
698 fn openPacket(
699 self: *Connection,
700 view: PacketView,
701 bytes: []u8,
702 now_ns: u64,
703 ) ?OpenedPacket {
704 std.debug.assert(bytes.len <= self.limits.datagram_bytes);
705 const space_value = self.space(view.kind);
706 if (view.kind != .application) {
707 const read_keys = self.keys.read(view.kind) orelse {
708 increment(&self.stats_value.drops.unavailable_keys);
709 return null;
710 };
711 const long = quic.crypto.packet.open(
712 read_keys,
713 bytes,
714 self.storage.scratch,
715 view.packet_number_offset,
716 space_value.largest_received,
717 ) catch |failure| return self.openFailure(read_keys, failure, now_ns);
718 return .{ .opened = long, .phase_changed = false };
719 }
720 const header_keys = self.keys.read(.application) orelse {
721 increment(&self.stats_value.drops.unavailable_keys);
722 return null;
723 };
724 const header = quic.crypto.packet.unprotect(
725 header_keys,
726 bytes,
727 view.packet_number_offset,
728 space_value.largest_received,
729 ) catch {
730 increment(&self.stats_value.drops.malformed);
731 return null;
732 };
733 const phase_changed = header.key_phase.? != self.keys.receive_phase;
734 const read_keys = self.keys.selectApplicationRead(header) orelse {
735 increment(&self.stats_value.drops.unavailable_keys);
736 return null;
737 };
738 const opened = quic.crypto.packet.openPayload(
739 read_keys,
740 bytes,
741 header,
742 self.storage.scratch,
743 ) catch |failure| return self.openFailure(read_keys, failure, now_ns);
744 return .{ .opened = opened, .phase_changed = phase_changed };
745 }
746
747 /// Writes one accepted 1-RTT packet into the key state, recognizing a key update and closing
748 /// the connection on an illegal update. A packet of the phase now being read, arriving behind
749 /// its neighbors, pulls the boundary down. A packet of the other phase sitting under that
750 /// boundary is written against the older keys. A packet of the other phase in any other
751 /// position moves the read keys on a generation. Either check failing ends the connection with
752 /// KEY_UPDATE_ERROR and answers false. The caller has written the number down already, which
753 /// holds the first packet of the phase now being read at or under the space's largest received
754 /// number.
755 fn observeApplicationRead(
756 self: *Connection,
757 packet_number: u62,
758 phase_changed: bool,
759 now_ns: u64,
760 ) bool {
761 std.debug.assert(self.keys.application_read_current != null);
762 std.debug.assert(isOpen(self.status_value));
763 std.debug.assert(self.space(.application).largest_received != null);
764 std.debug.assert(self.space(.application).largest_received.? >= packet_number);
765 const key_update_error = quic.connection.TransportError.key_update_error;
766 if (!phase_changed) {
767 self.keys.observeCurrentRead(packet_number) catch {
768 self.fail(key_update_error, null, "previous keys above current packet", now_ns);
769 return false;
770 };
771 return true;
772 }
773 const uses_previous = self.keys.previous_read_valid and
774 packet_number < self.keys.first_current_read_packet;
775 if (uses_previous) {
776 self.keys.observePreviousRead(packet_number);
777 return true;
778 }
779 const next_send = self.space(.application).next_packet_number;
780 self.keys.promoteApplicationRead(packet_number, next_send) catch {
781 self.fail(key_update_error, null, "consecutive key update", now_ns);
782 return false;
783 };
784 return true;
785 }
786
787 fn openFailure(
788 self: *Connection,
789 read_keys: *quic.crypto.Keys,
790 failure: quic.crypto.packet.OpenError,
791 now_ns: u64,
792 ) ?OpenedPacket {
793 if (failure == error.AuthenticationFailed) {
794 increment(&self.stats_value.drops.unauthenticated);
795 increment(&self.stats_value.failed_authentications);
796 if (self.stats_value.failed_authentications >= read_keys.integrityLimit()) {
797 self.fail(
798 quic.connection.TransportError.aead_limit_reached,
799 null,
800 "AEAD integrity limit",
801 now_ns,
802 );
803 }
804 return null;
805 }
806 if (failure == error.IntegrityLimitReached) {
807 self.fail(
808 quic.connection.TransportError.aead_limit_reached,
809 null,
810 "AEAD integrity limit",
811 now_ns,
812 );
813 return null;
814 }
815 if (failure == error.ReservedBits) {
816 self.fail(
817 quic.connection.TransportError.protocol_violation,
818 null,
819 "reserved packet bits",
820 now_ns,
821 );
822 return null;
823 }
824 increment(&self.stats_value.drops.malformed);
825 return null;
826 }
827
828 fn processPayload(
829 self: *Connection,
830 kind: quic.connection.SpaceKind,
831 payload: []u8,
832 now_ns: u64,
833 ) ?bool {
834 std.debug.assert(payload.len <= self.limits.datagram_bytes);
835 if (payload.len == 0) {
836 self.fail(
837 quic.connection.TransportError.protocol_violation,
838 null,
839 "empty packet",
840 now_ns,
841 );
842 return null;
843 }
844 var input = quic.cursor.Read.init(payload);
845 var ack_eliciting = false;
846 for (0..self.limits.datagram_bytes) |_| {
847 if (input.remaining() == 0) return ack_eliciting;
848 const value = quic.frame.decode(&input) catch {
849 self.fail(
850 quic.connection.TransportError.frame_encoding_error,
851 null,
852 "frame encoding",
853 now_ns,
854 );
855 return null;
856 };
857 const eliciting = self.processFrame(kind, value, now_ns) orelse return null;
858 if (eliciting) ack_eliciting = true;
859 if (self.status_value == .draining) return ack_eliciting;
860 }
861 unreachable;
862 }
863
864 fn processFrame(
865 self: *Connection,
866 kind: quic.connection.SpaceKind,
867 value: quic.frame.Frame,
868 now_ns: u64,
869 ) ?bool {
870 std.debug.assert(self.status_value != .draining);
871 std.debug.assert(self.status_value != .closed);
872 if (self.status_value == .closing and value != .connection_close) return false;
873 return switch (value) {
874 .padding => false,
875 .ping => true,
876 .ack => |ack| self.processAck(kind, ack, now_ns),
877 .crypto => |crypto_frame| self.processCrypto(kind, crypto_frame, now_ns),
878 .handshake_done => self.processHandshakeDone(kind, now_ns),
879 .connection_close => |close_frame| self.processClose(kind, close_frame, now_ns),
880 .stream,
881 .reset_stream,
882 .stop_sending,
883 .max_data,
884 .max_stream_data,
885 .max_streams,
886 .data_blocked,
887 .stream_data_blocked,
888 .streams_blocked,
889 => self.processStreamFrame(kind, value, now_ns),
890 else => {
891 self.fail(
892 quic.connection.TransportError.protocol_violation,
893 frameType(value),
894 "frame not implemented",
895 now_ns,
896 );
897 return null;
898 },
899 };
900 }
901
902 fn processAck(
903 self: *Connection,
904 kind: quic.connection.SpaceKind,
905 value: quic.frame.Ack,
906 now_ns: u64,
907 ) ?bool {
908 const sink = AckSink{ .connection = self, .kind = kind };
909 const acknowledged = self.space(kind).processAck(value, sink) catch {
910 self.fail(
911 quic.connection.TransportError.protocol_violation,
912 0x02,
913 "ACK for unsent packet",
914 now_ns,
915 );
916 return null;
917 };
918 self.sampleRoundTrip(kind, value, acknowledged, now_ns);
919 if (kind == .handshake) self.handshake_acknowledged = true;
920 if (acknowledged.records != 0 and self.peerCompletedAddressValidation()) self.pto_count = 0;
921 self.detectLoss(kind, now_ns);
922 if (kind == .application) self.keys.observeAck(value);
923 return false;
924 }
925
926 /// Runs one detection pass over a space at the delay in force, serving as the single function
927 /// starting loss detection so every pass uses the same delay. That delay is worked out from the
928 /// latest and the smoothed measurement of the path. Whatever the pass removed is added to the
929 /// connection's counters.
930 fn detectLoss(self: *Connection, kind: quic.connection.SpaceKind, now_ns: u64) void {
931 const delay_ns = quic.connection.recovery.lossDelayNs(
932 self.rtt.latest_ns,
933 self.rtt.smoothed_ns,
934 );
935 const sink = LossSink{ .connection = self, .kind = kind };
936 const lost = self.space(kind).detectLost(now_ns, delay_ns, sink);
937 self.stats_value.bytes_lost +|= lost.bytes;
938 }
939
940 /// Arms the one recovery deadline the connection reports, deciding which single deadline
941 /// `nextTimeout` will report for recovery. While any space is waiting on the time rule, that
942 /// wait governs and no probe is armed at all. With packets outstanding the function arms the
943 /// soonest probe, unless an unvalidated server could not afford to send it. With nothing
944 /// outstanding the function arms a client's anti-deadlock probe. A connection that has left
945 /// handshaking or established arms nothing.
946 fn armProbeTimeout(self: *Connection, now_ns: u64) void {
947 self.pto_deadline_ns = null;
948 if (!isOpen(self.status_value)) {
949 self.pto_anchor_ns = null;
950 return;
951 }
952 for (&self.spaces) |*space_value| {
953 if (space_value.loss_deadline_ns == null) continue;
954 self.pto_anchor_ns = null;
955 return;
956 }
957 if (self.bytesInFlight() != 0) {
958 self.pto_anchor_ns = null;
959 const probe = self.armedProbe() orelse return;
960 if (self.amplificationBlocks(probe.kind)) return;
961 self.pto_deadline_ns = probe.deadline_ns;
962 self.pto_space = probe.kind;
963 return;
964 }
965 self.armDeadlockProbe(now_ns);
966 }
967
968 /// Arms the anti-deadlock probe from the anchor, keeping a client sending while it waits for a
969 /// server flight that never came. A client keeps it armed with nothing outstanding until a
970 /// Handshake acknowledgment, or a confirmed handshake, gives the peer its proof of the address.
971 /// It points at the Handshake space if Handshake keys exist, and at the Initial space if
972 /// Handshake keys are absent. With no write keys in the space it picked, the function leaves
973 /// the timer disarmed and empties the anchor.
974 fn armDeadlockProbe(self: *Connection, now_ns: u64) void {
975 std.debug.assert(self.bytesInFlight() == 0);
976 if (self.peerCompletedAddressValidation()) {
977 self.pto_anchor_ns = null;
978 return;
979 }
980 const kind: quic.connection.SpaceKind =
981 if (self.keys.write(.handshake) != null) .handshake else .initial;
982 if (self.keys.write(kind) == null) {
983 self.pto_anchor_ns = null;
984 return;
985 }
986 const anchor_ns = self.pto_anchor_ns orelse now_ns;
987 self.pto_anchor_ns = anchor_ns;
988 self.pto_space = kind;
989 self.pto_deadline_ns = quic.connection.recovery.probeDeadlineNs(
990 anchor_ns,
991 self.probePeriodNs(kind),
992 );
993 }
994
995 /// Picks the soonest probe deadline among the spaces holding packets the peer has yet to
996 /// settle, selecting the single winning probe timer among the three spaces. Each space measures
997 /// its deadline from the last packet it sent that the peer owes an answer to. The application
998 /// space is left out until the handshake is confirmed. With all spaces settled, the function
999 /// returns null.
1000 fn armedProbe(self: *const Connection) ?Probe {
1001 var earliest: ?Probe = null;
1002 for ([_]quic.connection.SpaceKind{ .initial, .handshake, .application }) |kind| {
1003 const space_value = &self.spaces[spaceIndex(kind)];
1004 if (space_value.bytesInFlight() == 0) continue;
1005 if (kind == .application and !self.handshake_confirmed) break;
1006 const sent_ns = space_value.last_ack_eliciting_sent_ns orelse continue;
1007 const deadline_ns = quic.connection.recovery.probeDeadlineNs(
1008 sent_ns,
1009 self.probePeriodNs(kind),
1010 );
1011 if (earliest) |current| {
1012 if (deadline_ns >= current.deadline_ns) continue;
1013 }
1014 earliest = .{ .deadline_ns = deadline_ns, .kind = kind };
1015 }
1016 return earliest;
1017 }
1018
1019 /// Works out how long one space waits for an answer, at the doubling now in force, defining the
1020 /// period added to a send time for every probe deadline. The wait rests on the connection's
1021 /// smoothed measurement and its spread.
1022 fn probePeriodNs(self: *const Connection, kind: quic.connection.SpaceKind) u64 {
1023 const delay_ns = self.probeAckDelayNs(kind);
1024 return quic.connection.recovery.probePeriodNs(
1025 self.rtt.smoothed_ns,
1026 self.rtt.variation_ns,
1027 delay_ns,
1028 self.pto_count,
1029 );
1030 }
1031
1032 /// Reads the peer's `max_ack_delay` parameter into nanoseconds, for the application space and
1033 /// only past handshake confirmation, isolating the rule that adds the peer's delay only in one
1034 /// space and only after one point. It yields zero for the other two spaces, ahead of that
1035 /// confirmation, and before the peer's parameters have arrived.
1036 fn probeAckDelayNs(self: *const Connection, kind: quic.connection.SpaceKind) u64 {
1037 if (kind != .application) return 0;
1038 if (!self.handshake_confirmed) return 0;
1039 const parameters = self.peer_parameters orelse return 0;
1040 return maxAckDelayNs(parameters.max_ack_delay);
1041 }
1042
1043 /// Says whether the peer has proof of this endpoint's address, deciding both whether the
1044 /// anti-deadlock probe stays armed and whether an acknowledgment resets the backoff. A server
1045 /// always has it, because a client proves the server's address by finishing the handshake with
1046 /// it. A client has it once a Handshake acknowledgment arrives, or once the handshake is
1047 /// confirmed.
1048 fn peerCompletedAddressValidation(self: *const Connection) bool {
1049 if (self.config.role == .server) return true;
1050 return self.handshake_acknowledged or self.handshake_confirmed;
1051 }
1052
1053 /// Says whether an unvalidated server is short of the budget one probe in the space would cost,
1054 /// so the timer asks before arming a probe that would spin. That budget is three times what the
1055 /// client has sent this server, less what the server has sent back. The timer then waits on the
1056 /// client's next datagram to lift the budget. A client, and a server whose peer is validated,
1057 /// are never short.
1058 fn amplificationBlocks(self: *const Connection, kind: quic.connection.SpaceKind) bool {
1059 if (self.config.role != .server) return false;
1060 if (self.client_address_validated) return false;
1061 return self.remainingAmplificationBudget() < sendRoomMin(kind);
1062 }
1063
1064 /// Owes the armed space its probe packets once the deadline has passed, turning an expired
1065 /// deadline into packets the next `send` call emits. One packet is owed when nothing is
1066 /// outstanding, and two otherwise. It then lifts the doubling count, empties the anchor the
1067 /// anti-deadlock probe measured from, adds one to the timeout counter, and arms the timer
1068 /// afresh. `send` puts the owed packets out, forcing a PING into each.
1069 fn expireProbeTimeout(self: *Connection, now_ns: u64) void {
1070 std.debug.assert(isOpen(self.status_value));
1071 const deadline = self.pto_deadline_ns orelse return;
1072 if (now_ns < deadline) return;
1073 const kind = self.pto_space;
1074 const owed: u8 = if (self.bytesInFlight() == 0)
1075 1
1076 else
1077 quic.connection.recovery.probe_packets;
1078 self.probe_packets[spaceIndex(kind)] = owed;
1079 self.pto_count +|= 1;
1080 self.pto_anchor_ns = null;
1081 increment(&self.stats_value.probe_timeouts);
1082 self.armProbeTimeout(now_ns);
1083 }
1084
1085 /// Runs a detection pass over every space whose wait has run out, providing the path through
1086 /// which a caller calling back at the reported deadline reaches the loss pass. `receive`,
1087 /// `send`, and `close` all arrive here by way of `advanceTime`.
1088 fn detectExpiredLoss(self: *Connection, now_ns: u64) void {
1089 std.debug.assert(isOpen(self.status_value));
1090 for ([_]quic.connection.SpaceKind{ .initial, .handshake, .application }) |kind| {
1091 const deadline = self.space(kind).loss_deadline_ns orelse continue;
1092 if (now_ns < deadline) continue;
1093 self.detectLoss(kind, now_ns);
1094 }
1095 }
1096
1097 /// Measures the path from one ACK frame as the single source feeding the round-trip estimate,
1098 /// enforcing two conditions that determine whether an acknowledgment contributes a sample. A
1099 /// measurement wants the ACK frame's largest number to be one this ACK settled first. It also
1100 /// wants at least one packet the peer owed an answer to among what this ACK settled. What it
1101 /// measures is the time since that packet went out, less the delay the peer reported.
1102 fn sampleRoundTrip(
1103 self: *Connection,
1104 kind: quic.connection.SpaceKind,
1105 value: quic.frame.Ack,
1106 acknowledged: quic.connection.Acknowledged,
1107 now_ns: u64,
1108 ) void {
1109 if (!acknowledged.ack_eliciting) return;
1110 const sent_ns = acknowledged.largest_sent_ns orelse return;
1111 const latest_ns = quic.connection.recovery.sample(sent_ns, now_ns) orelse return;
1112 self.rtt.update(latest_ns, self.peerAckDelayNs(kind, value));
1113 }
1114
1115 /// Works out how much of the gap one ACK's sender says it spent before answering, centralizing
1116 /// the rule that counts delay only in one space and caps it only after one point. Initial and
1117 /// Handshake acknowledgments omit this delay figure. The peer's `max_ack_delay` holds that
1118 /// figure down once the handshake is confirmed. The figure is zero until the peer's parameters
1119 /// have arrived.
1120 fn peerAckDelayNs(
1121 self: *const Connection,
1122 kind: quic.connection.SpaceKind,
1123 value: quic.frame.Ack,
1124 ) u64 {
1125 const parameters = self.peer_parameters orelse return 0;
1126 const maximum_ns: ?u64 = if (self.handshake_confirmed)
1127 maxAckDelayNs(parameters.max_ack_delay)
1128 else
1129 null;
1130 return quic.connection.recovery.reportedDelayNs(
1131 kind == .application,
1132 value.delay,
1133 parameters.ack_delay_exponent,
1134 maximum_ns,
1135 );
1136 }
1137
1138 /// Applies one stream or flow control frame as the entry point where these frames enter the
1139 /// connection, which only a 1-RTT packet may carry. The same frame inside an Initial or
1140 /// Handshake packet ends the connection with PROTOCOL_VIOLATION. Answering with null leaves the
1141 /// packet unacknowledged. An arriving STOP_SENDING resets the sending half, a MAX_STREAM_DATA
1142 /// lifts the stream's allowance, and a MAX_DATA lifts the connection's. An arriving MAX_STREAMS
1143 /// lifts how many bidirectional streams the peer allows.
1144 fn processStreamFrame(
1145 self: *Connection,
1146 kind: quic.connection.SpaceKind,
1147 value: quic.frame.Frame,
1148 now_ns: u64,
1149 ) ?bool {
1150 std.debug.assert(isOpen(self.status_value));
1151 if (kind != .application) {
1152 self.fail(
1153 quic.connection.TransportError.protocol_violation,
1154 frameType(value),
1155 "stream frame outside 1-RTT",
1156 now_ns,
1157 );
1158 return null;
1159 }
1160 const received = &self.stats_value.stream_received;
1161 switch (value) {
1162 .stream => |data| return self.processStreamData(data, now_ns),
1163 .reset_stream => |reset| return self.processResetStream(reset, now_ns),
1164 .stop_sending => |stop| {
1165 if (!self.admitStream(stop.stream_id, 0x05, now_ns)) return null;
1166 increment(&received.stop_sending);
1167 _ = self.stream_send.reset(stop.error_code);
1168 },
1169 .max_stream_data => |limit| {
1170 if (!self.admitStream(limit.stream_id, 0x11, now_ns)) return null;
1171 increment(&received.max_stream_data);
1172 self.stream_send.credit.raise(limit.maximum);
1173 },
1174 .stream_data_blocked => |limit| {
1175 if (!self.admitStream(limit.stream_id, 0x15, now_ns)) return null;
1176 increment(&received.stream_data_blocked);
1177 },
1178 .max_data => |maximum| {
1179 increment(&received.max_data);
1180 self.connection_credit.raise(maximum);
1181 },
1182 .data_blocked => increment(&received.data_blocked),
1183 .max_streams => |limit| {
1184 increment(&received.max_streams);
1185 if (!limit.unidirectional) {
1186 self.peer_streams_bidi = @max(self.peer_streams_bidi, limit.maximum);
1187 }
1188 },
1189 .streams_blocked => increment(&received.streams_blocked),
1190 else => unreachable,
1191 }
1192 return true;
1193 }
1194
1195 /// Puts one STREAM frame's data in order on the receiving half, delivering arriving STREAM data
1196 /// to the receiving part of stream 0. With the reassembly table full, the packet goes
1197 /// unacknowledged and the `stream_ranges` counter climbs. Data past a window ends the
1198 /// connection with FLOW_CONTROL_ERROR, and a final size at odds with one already settled ends
1199 /// it with FINAL_SIZE_ERROR.
1200 fn processStreamData(self: *Connection, data: quic.frame.Stream, now_ns: u64) ?bool {
1201 const frame_type = frameType(.{ .stream = data });
1202 if (!self.admitStream(data.stream_id, frame_type, now_ns)) return null;
1203 const window = &self.connection_window;
1204 self.stream_receive.receive(window, data.offset, data.data, data.fin) catch |failure| {
1205 if (failure == error.RangesFull) {
1206 increment(&self.stats_value.drops.stream_ranges);
1207 return null;
1208 }
1209 const code = if (failure == error.FlowControl)
1210 quic.connection.TransportError.flow_control_error
1211 else
1212 quic.connection.TransportError.final_size_error;
1213 self.fail(code, frame_type, "STREAM data", now_ns);
1214 return null;
1215 };
1216 const received = &self.stats_value.stream_received;
1217 increment(&received.stream_frames);
1218 received.stream_bytes +|= data.data.len;
1219 return true;
1220 }
1221
1222 fn processResetStream(self: *Connection, reset: quic.frame.ResetStream, now_ns: u64) ?bool {
1223 if (!self.admitStream(reset.stream_id, 0x04, now_ns)) return null;
1224 const window = &self.connection_window;
1225 const code = reset.error_code;
1226 const final = reset.final_size;
1227 self.stream_receive.resetReceived(window, code, final) catch |failure| {
1228 const transport_code = if (failure == error.FlowControl)
1229 quic.connection.TransportError.flow_control_error
1230 else
1231 quic.connection.TransportError.final_size_error;
1232 self.fail(transport_code, 0x04, "RESET_STREAM final size", now_ns);
1233 return null;
1234 };
1235 increment(&self.stats_value.stream_received.reset_stream);
1236 return true;
1237 }
1238
1239 /// Runs the identifier checks as every frame naming a stream passes through it, and opens
1240 /// stream 0 on a server so the server first learns stream 0 exists. A frame naming a stream
1241 /// this endpoint should have opened itself ends the connection with STREAM_STATE_ERROR, unless
1242 /// it names stream 0 and stream 0 is open. A unidirectional identifier, or one whose sequence
1243 /// reaches the advertised limit, ends the connection with STREAM_LIMIT_ERROR. The function
1244 /// answers false after ending the connection.
1245 fn admitStream(self: *Connection, stream_id: StreamId, frame_type: u62, now_ns: u64) bool {
1246 const local: quic.connection.stream.Initiator = switch (self.config.role) {
1247 .client => .client,
1248 .server => .server,
1249 };
1250 if (quic.connection.stream.initiator(stream_id) == local) {
1251 if (stream_id == 0 and self.stream_open) return true;
1252 self.fail(
1253 quic.connection.TransportError.stream_state_error,
1254 frame_type,
1255 "stream not opened",
1256 now_ns,
1257 );
1258 return false;
1259 }
1260 const limit = quic.connection.parameters.localStreamsBidi(self.config.role);
1261 const unidirectional = quic.connection.stream.direction(stream_id) == .unidirectional;
1262 if (unidirectional or quic.connection.stream.sequence(stream_id) >= limit) {
1263 self.fail(
1264 quic.connection.TransportError.stream_limit_error,
1265 frame_type,
1266 "stream limit",
1267 now_ns,
1268 );
1269 return false;
1270 }
1271 std.debug.assert(stream_id == 0);
1272 self.stream_open = true;
1273 return true;
1274 }
1275
1276 fn processCrypto(
1277 self: *Connection,
1278 kind: quic.connection.SpaceKind,
1279 value: quic.frame.Crypto,
1280 now_ns: u64,
1281 ) ?bool {
1282 const stream = &self.crypto_streams[spaceIndex(kind)];
1283 stream.receive(value.offset, value.data) catch |failure| {
1284 const code = if (failure == error.BufferExceeded)
1285 quic.connection.TransportError.crypto_buffer_exceeded
1286 else
1287 quic.connection.TransportError.protocol_violation;
1288 self.fail(code, 0x06, "CRYPTO reassembly", now_ns);
1289 return null;
1290 };
1291 if (!self.driveTls(kind, now_ns)) return null;
1292 return true;
1293 }
1294
1295 fn driveTls(self: *Connection, kind: quic.connection.SpaceKind, now_ns: u64) bool {
1296 const engine = if (self.engine) |*value| value else return false;
1297 const stream = &self.crypto_streams[spaceIndex(kind)];
1298 for (0..self.limits.crypto_buffer_bytes) |_| {
1299 const bytes = stream.contiguous();
1300 if (bytes.len == 0) break;
1301 engine.receive(kind.level(), bytes) catch {
1302 const alert = engine.alert() orelse quic.tls.Alert.internal_error;
1303 self.fail(@intCast(alert.quicError()), 0x06, "TLS alert", now_ns);
1304 return false;
1305 };
1306 stream.consume(bytes.len);
1307 }
1308 self.keys.installTls(engine) catch {
1309 self.fail(quic.connection.TransportError.internal_error, 0x06, "TLS keys", now_ns);
1310 return false;
1311 };
1312 if (!self.authenticatePeer(now_ns)) return false;
1313 self.observeTlsState();
1314 return self.status_value != .closing;
1315 }
1316
1317 fn authenticatePeer(self: *Connection, now_ns: u64) bool {
1318 if (self.peer_parameters_authenticated) return true;
1319 const engine = if (self.engine) |*value| value else return true;
1320 const peer_info = engine.peer() orelse return true;
1321 const source = self.peer_source_cid orelse return true;
1322 const parameters = quic.connection.parameters.validatePeer(
1323 self.config.role,
1324 peer_info.transport_parameters,
1325 source,
1326 self.original_dcid,
1327 ) catch {
1328 self.fail(
1329 quic.connection.TransportError.transport_parameter_error,
1330 null,
1331 "transport parameters",
1332 now_ns,
1333 );
1334 return false;
1335 };
1336 self.peer_parameters = parameters;
1337 self.connection_credit.raise(parameters.initial_max_data);
1338 const send_limit = quic.connection.parameters.streamSendLimit(
1339 self.config.role,
1340 parameters,
1341 );
1342 self.stream_send.credit.raise(send_limit);
1343 self.peer_streams_bidi = parameters.initial_max_streams_bidi;
1344 self.peer_parameters_authenticated = true;
1345 self.effective_idle_timeout_ms = quic.connection.parameters.effectiveIdle(
1346 self.config.max_idle_timeout,
1347 parameters.max_idle_timeout,
1348 );
1349 return true;
1350 }
1351
1352 fn observeTlsState(self: *Connection) void {
1353 std.debug.assert(isOpen(self.status_value));
1354 const engine = if (self.engine) |*value| value else return;
1355 if (self.config.role != .server) return;
1356 if (engine.state() != .handshake_confirmed) return;
1357 if (self.status_value == .handshaking) {
1358 self.pending_handshake_done = true;
1359 self.status_value = .established;
1360 }
1361 self.handshake_confirmed = true;
1362 self.discardHandshakeKeys();
1363 }
1364
1365 fn processHandshakeDone(
1366 self: *Connection,
1367 kind: quic.connection.SpaceKind,
1368 now_ns: u64,
1369 ) ?bool {
1370 std.debug.assert(isOpen(self.status_value));
1371 if (kind != .application or self.config.role != .client) {
1372 self.fail(
1373 quic.connection.TransportError.protocol_violation,
1374 0x1e,
1375 "invalid HANDSHAKE_DONE",
1376 now_ns,
1377 );
1378 return null;
1379 }
1380 const engine = if (self.engine) |*value| value else return null;
1381 engine.confirm();
1382 self.handshake_confirmed = true;
1383 self.discardHandshakeKeys();
1384 if (self.status_value == .handshaking) self.status_value = .established;
1385 self.handshake_done_received = true;
1386 increment(&self.stats_value.handshake_done_received);
1387 return true;
1388 }
1389
1390 fn processClose(
1391 self: *Connection,
1392 kind: quic.connection.SpaceKind,
1393 value: quic.frame.ConnectionClose,
1394 now_ns: u64,
1395 ) ?bool {
1396 if (value.application and kind != .application) {
1397 self.fail(
1398 quic.connection.TransportError.protocol_violation,
1399 0x1d,
1400 "application close level",
1401 now_ns,
1402 );
1403 return null;
1404 }
1405 if (self.status_value != .closing) {
1406 self.storeReason(
1407 value.error_code,
1408 value.application,
1409 value.frame_type,
1410 value.reason,
1411 true,
1412 );
1413 }
1414 self.close_deadline_ns = self.close_deadline_ns orelse closeDeadline(now_ns);
1415 self.status_value = .draining;
1416 self.close_pending = false;
1417 return false;
1418 }
1419
1420 fn sendClosing(self: *Connection, out: []u8, now_ns: u64) usize {
1421 std.debug.assert(self.status_value == .closing);
1422 std.debug.assert(self.close_reason_value != null);
1423 if (!self.close_pending) return 0;
1424 const kind = self.closeSpace() orelse {
1425 self.status_value = .closed;
1426 return 0;
1427 };
1428 const limit = self.sendLimit(out.len);
1429 var length: usize = 0;
1430 if (kind == .application and self.keys.write(.handshake) != null) {
1431 length = self.sendClosePacket(.handshake, out[0..limit], now_ns);
1432 }
1433 length += self.sendClosePacket(kind, out[length..limit], now_ns);
1434 if (length == 0) return 0;
1435 std.debug.assert(length <= limit);
1436 std.debug.assert(self.close_deadline_ns != null);
1437 self.close_pending = false;
1438 self.advanceCloseResponseLimit();
1439 self.recordDatagramSent(length);
1440 return length;
1441 }
1442
1443 fn sendClosePacket(
1444 self: *Connection,
1445 kind: quic.connection.SpaceKind,
1446 out: []u8,
1447 now_ns: u64,
1448 ) usize {
1449 std.debug.assert(out.len <= self.limits.datagram_bytes);
1450 std.debug.assert(self.keys.write(kind) != null);
1451 const packet_limit: usize = if (kind == .initial) 1200 else out.len;
1452 if (out.len < packet_limit) return 0;
1453 const plan = self.planPacket(kind, packet_limit, now_ns) orelse return 0;
1454 var output = quic.cursor.Write.init(self.storage.packet[0..plan.payload_capacity]);
1455 const reason = self.close_reason_value.?;
1456 const application = reason.application and kind == .application;
1457 const converted = reason.application and !application;
1458 quic.frame.encode(.{ .connection_close = .{
1459 .application = application,
1460 .error_code = if (converted)
1461 quic.connection.TransportError.application_error
1462 else
1463 reason.error_code,
1464 .frame_type = if (application) null else reason.frame_type orelse 0,
1465 .reason = if (converted) &.{} else reason.reason,
1466 } }, &output) catch return 0;
1467 const payload = Payload{
1468 .bytes = output.written(),
1469 .summary = .{ .connection_close = true },
1470 .ack_eliciting = false,
1471 .sent_ack = false,
1472 .sent_ping = false,
1473 .sent_handshake_done = false,
1474 .sent_close = true,
1475 };
1476 const minimum: u16 = if (kind == .initial) 1200 else 0;
1477 const count = self.assemblePayload(kind, plan.number, payload, minimum, out, now_ns);
1478 std.debug.assert(count <= out.len);
1479 return count;
1480 }
1481
1482 fn sendSpace(
1483 self: *Connection,
1484 kind: quic.connection.SpaceKind,
1485 out: []u8,
1486 available: usize,
1487 minimum_bytes: u16,
1488 force_ping: bool,
1489 now_ns: u64,
1490 ) usize {
1491 std.debug.assert(available <= out.len);
1492 std.debug.assert(available <= self.limits.datagram_bytes);
1493 if (available < sendRoomMin(kind)) return 0;
1494 if (self.keys.write(kind) == null) return 0;
1495 const payload_limit = if (kind == .initial and minimum_bytes != 0)
1496 @min(available, @as(usize, minimum_bytes))
1497 else
1498 available;
1499 const plan = self.planPacket(kind, payload_limit, now_ns) orelse return 0;
1500 const payload = self.buildPayload(
1501 kind,
1502 plan.payload_capacity,
1503 force_ping,
1504 now_ns,
1505 ) catch {
1506 self.fail(
1507 quic.connection.TransportError.internal_error,
1508 null,
1509 "packet assembly",
1510 now_ns,
1511 );
1512 return 0;
1513 } orelse return 0;
1514 return self.assemblePayload(kind, plan.number, payload, minimum_bytes, out, now_ns);
1515 }
1516
1517 fn buildPayload(
1518 self: *Connection,
1519 kind: quic.connection.SpaceKind,
1520 available: usize,
1521 force_ping: bool,
1522 now_ns: u64,
1523 ) PayloadError!?Payload {
1524 std.debug.assert(available <= self.storage.packet.len);
1525 var output = quic.cursor.Write.init(self.storage.packet[0..available]);
1526 var payload = Payload{
1527 .bytes = &.{},
1528 .summary = .{},
1529 .ack_eliciting = false,
1530 .sent_ack = false,
1531 .sent_ping = false,
1532 .sent_handshake_done = false,
1533 .sent_close = false,
1534 };
1535 const space_value = self.space(kind);
1536 const record_room = space_value.sent.hasCapacity();
1537 const application = kind == .application;
1538 const eliciting = force_ping or (application and self.applicationFramesPending());
1539 const send_ack = space_value.ack_pending and
1540 ((record_room and eliciting) or space_value.ackDue(now_ns));
1541 if (send_ack) payload.sent_ack = try self.emitAck(space_value, &output, now_ns);
1542 payload.summary.ack = payload.sent_ack;
1543 if (record_room) {
1544 if (application) try self.emitApplicationControl(&output, &payload);
1545 if (force_ping and !payload.ack_eliciting) try emitPing(&output, &payload);
1546 if (try self.emitCrypto(kind, &output)) |crypto_summary| {
1547 payload.summary.crypto = crypto_summary;
1548 payload.ack_eliciting = true;
1549 }
1550 if (application) try self.emitStreamFrames(&output, &payload);
1551 }
1552 if (output.index == 0) return null;
1553 payload.bytes = output.written();
1554 return payload;
1555 }
1556
1557 fn emitAck(
1558 self: *Connection,
1559 space_value: *quic.connection.Space,
1560 output: *quic.cursor.Write,
1561 now_ns: u64,
1562 ) PayloadError!bool {
1563 const start = output.index;
1564 const delay = space_value.ackDelay(now_ns, self.config.ack_delay_exponent);
1565 space_value.received.encode(delay, output) catch |failure| switch (failure) {
1566 error.NoSpace => {
1567 output.index = start;
1568 return false;
1569 },
1570 else => |other| return other,
1571 };
1572 return true;
1573 }
1574
1575 fn emitApplicationControl(
1576 self: *Connection,
1577 output: *quic.cursor.Write,
1578 payload: *Payload,
1579 ) PayloadError!void {
1580 if (self.pending_handshake_done) {
1581 if (try encodeDeferred(output, .{ .handshake_done = {} })) {
1582 payload.summary.handshake_done = true;
1583 payload.sent_handshake_done = true;
1584 payload.ack_eliciting = true;
1585 }
1586 }
1587 if (self.pending_ping) try emitPing(output, payload);
1588 }
1589
1590 /// Packs the flow control frames first, and a single STREAM frame for stream 0 after them, into
1591 /// whatever room the packet has left, constructing the stream half of one 1-RTT payload. The
1592 /// function packs the stream frames once stream 0 is open. A packet that took any of them
1593 /// obliges the peer to answer.
1594 fn emitStreamFrames(
1595 self: *Connection,
1596 output: *quic.cursor.Write,
1597 payload: *Payload,
1598 ) PayloadError!void {
1599 const start = output.index;
1600 try self.emitConnectionFlow(output, &payload.summary);
1601 if (self.stream_open) {
1602 try self.emitStreamControl(output, &payload.summary);
1603 try self.emitStream(output, &payload.summary);
1604 }
1605 if (output.index > start) payload.ack_eliciting = true;
1606 }
1607
1608 fn emitConnectionFlow(
1609 self: *Connection,
1610 output: *quic.cursor.Write,
1611 summary: *quic.connection.FrameSummary,
1612 ) PayloadError!void {
1613 if (self.connection_window.update()) |limit| {
1614 if (try encodeDeferred(output, .{ .max_data = limit })) {
1615 summary.max_data = limit;
1616 }
1617 }
1618 if (self.connectionBlocked()) |limit| {
1619 if (try encodeDeferred(output, .{ .data_blocked = limit })) {
1620 summary.data_blocked = limit;
1621 }
1622 }
1623 if (self.streams_blocked_pending) {
1624 const limit = quic.frame.StreamLimit{
1625 .unidirectional = false,
1626 .maximum = self.peer_streams_bidi,
1627 };
1628 if (try encodeDeferred(output, .{ .streams_blocked = limit })) {
1629 summary.streams_blocked = limit;
1630 }
1631 }
1632 }
1633
1634 fn emitStreamControl(
1635 self: *Connection,
1636 output: *quic.cursor.Write,
1637 summary: *quic.connection.FrameSummary,
1638 ) PayloadError!void {
1639 std.debug.assert(self.stream_open);
1640 if (self.stream_send.resetFrame(0)) |reset| {
1641 if (try encodeDeferred(output, .{ .reset_stream = reset })) {
1642 summary.reset_stream = reset;
1643 }
1644 }
1645 if (self.stream_receive.stop_pending) {
1646 const code = self.stream_receive.stop_code.?;
1647 const stop = quic.frame.StopSending{ .stream_id = 0, .error_code = code };
1648 if (try encodeDeferred(output, .{ .stop_sending = stop })) {
1649 summary.stop_sending = stop;
1650 }
1651 }
1652 if (self.stream_receive.windowUpdate()) |maximum| {
1653 const limit = quic.frame.StreamData{ .stream_id = 0, .maximum = maximum };
1654 if (try encodeDeferred(output, .{ .max_stream_data = limit })) {
1655 summary.max_stream_data = limit;
1656 }
1657 }
1658 if (self.stream_send.blocked()) |maximum| {
1659 const limit = quic.frame.StreamData{ .stream_id = 0, .maximum = maximum };
1660 if (try encodeDeferred(output, .{ .stream_data_blocked = limit })) {
1661 summary.stream_data_blocked = limit;
1662 }
1663 }
1664 }
1665
1666 /// Packs a single STREAM frame for stream 0, last of all, sizing the frame to whatever room is
1667 /// left. Bytes going a second time travel ahead of fresh ones, since the pick starts at the
1668 /// sending half's next offset. The length field goes in when padding follows, which occurs when
1669 /// the frame would otherwise end short of a full header protection sample. The FIN travels on
1670 /// the last byte. A frame too large for the room left keeps its bytes back, for some later
1671 /// packet to carry.
1672 fn emitStream(
1673 self: *Connection,
1674 output: *quic.cursor.Write,
1675 summary: *quic.connection.FrameSummary,
1676 ) PayloadError!void {
1677 std.debug.assert(self.stream_open);
1678 const send_part = &self.stream_send;
1679 const credit = self.connection_credit.available();
1680 const offset = send_part.nextOffset();
1681 const remaining = output.remaining();
1682 const plain_max = frameDataCapacity(0, offset, remaining, false) orelse return;
1683 const plain = send_part.nextChunk(plain_max, credit) orelse return;
1684 std.debug.assert(plain.offset == offset);
1685 const plain_header = frameHeaderBytes(0, offset, plain.data.len, false);
1686 const plain_end = output.index + plain_header + plain.data.len;
1687 const length_present = plain_end < quic.connection.assemble.sampled_bytes_min;
1688 var chunk = plain;
1689 if (length_present) {
1690 const framed_max = frameDataCapacity(0, offset, remaining, true) orelse return;
1691 chunk = send_part.nextChunk(framed_max, credit) orelse return;
1692 }
1693 const value = quic.connection.stream.frame(0, chunk, length_present);
1694 if (!try encodeDeferred(output, value)) return;
1695 summary.stream = .{
1696 .stream_id = 0,
1697 .offset = chunk.offset,
1698 .length = @intCast(chunk.data.len),
1699 .fin = chunk.fin,
1700 };
1701 }
1702
1703 /// Packs one CRYPTO frame for the space so handshake bytes reach a packet. Handshake bytes
1704 /// waiting in that space go first, and TLS is asked for nothing new. Otherwise fresh TLS bytes
1705 /// move the send offset on. TLS has no way to take bytes back, so fresh bytes that will not fit
1706 /// the packet join the ones waiting.
1707 fn emitCrypto(
1708 self: *Connection,
1709 kind: quic.connection.SpaceKind,
1710 output: *quic.cursor.Write,
1711 ) PayloadError!?quic.connection.CryptoSummary {
1712 std.debug.assert(output.remaining() <= self.limits.datagram_bytes);
1713 const engine = if (self.engine) |*value| value else return null;
1714 const index = spaceIndex(kind);
1715 std.debug.assert(engine.emitted(kind.level()).len == self.crypto_send_offsets[index]);
1716 if (!self.crypto_lost[index].isEmpty()) return self.emitLostCrypto(kind, output);
1717 const offset = self.crypto_send_offsets[index];
1718 const maximum = cryptoDataCapacity(offset, output.remaining(), self.storage.scratch.len);
1719 if (maximum == 0) return null;
1720 const count = engine.emit(kind.level(), self.storage.scratch[0..maximum]);
1721 std.debug.assert(count <= maximum);
1722 if (count == 0) return null;
1723 self.crypto_send_offsets[index] += @intCast(count);
1724 const data = self.storage.scratch[0..count];
1725 if (!try encodeDeferred(output, .{ .crypto = .{ .offset = offset, .data = data } })) {
1726 self.crypto_lost[index].add(offset, @intCast(count));
1727 return null;
1728 }
1729 return .{ .offset = offset, .length = @intCast(count) };
1730 }
1731
1732 /// Packs one CRYPTO frame from the first waiting offset, carrying as many waiting bytes as fit,
1733 /// to rebuild a lost handshake frame from bytes TLS already emitted. The bytes come back out of
1734 /// the TLS engine's own record of what it produced. The function takes the packed bytes out of
1735 /// the waiting stretch there and then. Doing so there and then is safe, because the packet is
1736 /// written down as sent immediately after packing, or the connection fails.
1737 fn emitLostCrypto(
1738 self: *Connection,
1739 kind: quic.connection.SpaceKind,
1740 output: *quic.cursor.Write,
1741 ) PayloadError!?quic.connection.CryptoSummary {
1742 const engine = if (self.engine) |*value| value else return null;
1743 const lost = &self.crypto_lost[spaceIndex(kind)];
1744 std.debug.assert(!lost.isEmpty());
1745 const flight = engine.emitted(kind.level());
1746 std.debug.assert(lost.end <= flight.len);
1747 const offset = lost.start;
1748 const room = output.remaining();
1749 const waiting: usize = @intCast(lost.length());
1750 const count = cryptoDataCapacity(offset, room, @min(waiting, room));
1751 if (count == 0) return null;
1752 const data = flight[@intCast(offset)..][0..count];
1753 const value = quic.frame.Frame{ .crypto = .{ .offset = offset, .data = data } };
1754 if (!try encodeDeferred(output, value)) return null;
1755 lost.advance(@intCast(count));
1756 return .{ .offset = offset, .length = @intCast(count) };
1757 }
1758
1759 fn assemblePayload(
1760 self: *Connection,
1761 kind: quic.connection.SpaceKind,
1762 number: quic.connection.NumberEncoding,
1763 payload: Payload,
1764 minimum_bytes: u16,
1765 out: []u8,
1766 now_ns: u64,
1767 ) usize {
1768 std.debug.assert(out.len <= self.limits.datagram_bytes);
1769 std.debug.assert(payload.bytes.len <= self.limits.datagram_bytes);
1770 const space_value = self.space(kind);
1771 if (payload.ack_eliciting) std.debug.assert(space_value.sent.hasCapacity());
1772 const write_keys = self.keys.write(kind) orelse {
1773 self.fail(quic.connection.TransportError.internal_error, null, "write keys", now_ns);
1774 return 0;
1775 };
1776 const destination_cid = self.peer_cid orelse {
1777 self.fail(quic.connection.TransportError.internal_error, null, "peer CID", now_ns);
1778 return 0;
1779 };
1780 const packet_type: quic.connection.assemble.PacketType = switch (kind) {
1781 .initial => .initial,
1782 .handshake => .handshake,
1783 .application => .one_rtt,
1784 };
1785 const built = quic.connection.assemble.packet(write_keys, .{
1786 .packet_type = packet_type,
1787 .destination = destination_cid,
1788 .source = self.local_cid,
1789 .number = number,
1790 .key_phase = self.keys.send_phase,
1791 .minimum_bytes = minimum_bytes,
1792 }, payload.bytes, out) catch |failure| {
1793 if (failure == error.ConfidentialityLimitReached) {
1794 self.fail(
1795 quic.connection.TransportError.aead_limit_reached,
1796 null,
1797 "AEAD confidentiality limit",
1798 now_ns,
1799 );
1800 } else {
1801 self.fail(
1802 quic.connection.TransportError.internal_error,
1803 null,
1804 "packet protection",
1805 now_ns,
1806 );
1807 }
1808 return 0;
1809 };
1810 if (!self.recordPacketSent(space_value, kind, number, payload, built, now_ns)) return 0;
1811 self.commitPayload(kind, payload, now_ns);
1812 increment(&self.stats_value.packets_sent);
1813 self.touchActivityOnSend(now_ns, payload.ack_eliciting);
1814 return built.length;
1815 }
1816
1817 fn recordPacketSent(
1818 self: *Connection,
1819 space_value: *quic.connection.Space,
1820 kind: quic.connection.SpaceKind,
1821 number: quic.connection.NumberEncoding,
1822 payload: Payload,
1823 built: quic.connection.assemble.Built,
1824 now_ns: u64,
1825 ) bool {
1826 std.debug.assert(built.length <= self.limits.datagram_bytes);
1827 std.debug.assert(payload.bytes.len <= built.payload_length);
1828 std.debug.assert(number.packet_number == space_value.next_packet_number);
1829 if (!payload.ack_eliciting) {
1830 space_value.next_packet_number += 1;
1831 return true;
1832 }
1833 space_value.recordSent(.{
1834 .packet_number = number.packet_number,
1835 .time_sent_ns = now_ns,
1836 .ack_eliciting = true,
1837 .acknowledged = false,
1838 .lost = false,
1839 .in_flight_bytes = built.length,
1840 .key_phase = self.keys.send_phase,
1841 .key_phase_present = kind == .application,
1842 .frames = payload.summary,
1843 }) catch {
1844 self.fail(
1845 quic.connection.TransportError.internal_error,
1846 null,
1847 "sent records",
1848 now_ns,
1849 );
1850 return false;
1851 };
1852 return true;
1853 }
1854
1855 fn planPacket(
1856 self: *Connection,
1857 kind: quic.connection.SpaceKind,
1858 packet_bytes: usize,
1859 now_ns: u64,
1860 ) ?PacketPlan {
1861 std.debug.assert(packet_bytes <= self.limits.datagram_bytes);
1862 const destination = self.peer_cid orelse {
1863 self.fail(quic.connection.TransportError.internal_error, null, "peer CID", now_ns);
1864 return null;
1865 };
1866 const number = self.space(kind).numberEncoding() catch {
1867 self.fail(quic.connection.TransportError.internal_error, null, "packet number", now_ns);
1868 return null;
1869 };
1870 const number_bytes: usize = number.bits / 8;
1871 const reserve = packetReserve(
1872 kind,
1873 packet_bytes,
1874 @intCast(number_bytes),
1875 destination.length,
1876 self.local_cid.length,
1877 );
1878 const sampled = quic.connection.assemble.sampled_bytes_min - number_bytes;
1879 if (reserve + @max(sampled, 1) > packet_bytes) return null;
1880 std.debug.assert(reserve < packet_bytes);
1881 return .{ .number = number, .payload_capacity = packet_bytes - reserve };
1882 }
1883
1884 fn commitPayload(
1885 self: *Connection,
1886 kind: quic.connection.SpaceKind,
1887 payload: Payload,
1888 now_ns: u64,
1889 ) void {
1890 if (payload.sent_ack) {
1891 const space_value = self.space(kind);
1892 space_value.markAckSent();
1893 increment(&self.stats_value.ack_frames_sent);
1894 if (kind == .application) self.keys.observeAckSent(space_value.largest_received.?);
1895 }
1896 if (payload.ack_eliciting) {
1897 const owed = &self.probe_packets[spaceIndex(kind)];
1898 if (owed.* != 0) owed.* -= 1;
1899 }
1900 if (payload.sent_ping) self.pending_ping = false;
1901 if (payload.sent_handshake_done) {
1902 self.pending_handshake_done = false;
1903 increment(&self.stats_value.handshake_done_sent);
1904 }
1905 if (payload.sent_close) self.close_pending = false;
1906 if (kind == .application) self.commitStreamFrames(payload.summary);
1907 if (kind == .handshake and self.config.role == .client) self.discardInitialKeys();
1908 if (kind == .application and self.keys.application_write.?.confidentialityExhausted()) {
1909 self.fail(
1910 quic.connection.TransportError.aead_limit_reached,
1911 null,
1912 "AEAD confidentiality limit",
1913 now_ns,
1914 );
1915 }
1916 }
1917
1918 fn commitStreamFrames(self: *Connection, summary: quic.connection.FrameSummary) void {
1919 const sent_stats = &self.stats_value.stream_sent;
1920 if (summary.max_data) |limit| {
1921 self.connection_window.advertise(limit);
1922 increment(&sent_stats.max_data);
1923 }
1924 if (summary.data_blocked) |limit| {
1925 self.connection_credit.markBlocked(limit);
1926 increment(&sent_stats.data_blocked);
1927 }
1928 if (summary.streams_blocked != null) {
1929 self.streams_blocked_pending = false;
1930 self.streams_blocked_sent = true;
1931 increment(&sent_stats.streams_blocked);
1932 }
1933 if (summary.reset_stream != null) {
1934 self.stream_send.commitReset();
1935 increment(&sent_stats.reset_stream);
1936 }
1937 if (summary.stop_sending != null) {
1938 self.stream_receive.stop_pending = false;
1939 increment(&sent_stats.stop_sending);
1940 }
1941 if (summary.max_stream_data) |limit| {
1942 self.stream_receive.window.advertise(limit.maximum);
1943 increment(&sent_stats.max_stream_data);
1944 }
1945 if (summary.stream_data_blocked) |limit| {
1946 self.stream_send.commitBlocked(limit.maximum);
1947 increment(&sent_stats.stream_data_blocked);
1948 }
1949 const range = summary.stream orelse return;
1950 const new_bytes = self.stream_send.commitChunk(range.offset, range.length, range.fin);
1951 self.connection_credit.consume(new_bytes);
1952 increment(&sent_stats.stream_frames);
1953 sent_stats.stream_bytes +|= range.length;
1954 }
1955
1956 /// Decides what one lost 1-RTT packet puts back in the queue, centralizing the whole
1957 /// retransmission policy for that packet in one place. Stream 0 decides for the STREAM bytes,
1958 /// the FIN, the RESET_STREAM, and the STOP_SENDING. The two MAX frames go again, each carrying
1959 /// the figure now in force. The three BLOCKED frames go again while the sender still stands at
1960 /// the figure the lost one reported. HANDSHAKE_DONE goes again.
1961 fn loseApplicationFrames(self: *Connection, frames: quic.connection.FrameSummary) void {
1962 std.debug.assert(isOpen(self.status_value));
1963 if (frames.handshake_done) self.pending_handshake_done = true;
1964 if (frames.max_data) |limit| self.connection_window.loseLimit(limit);
1965 if (frames.data_blocked) |limit| self.connection_credit.loseBlocked(limit);
1966 if (frames.streams_blocked) |limit| self.loseStreamsBlocked(limit);
1967 const send_part = &self.stream_send;
1968 const receive_part = &self.stream_receive;
1969 if (frames.stream) |range| send_part.lose(range.offset, range.length, range.fin);
1970 if (frames.reset_stream != null) send_part.loseReset();
1971 if (frames.stop_sending != null) receive_part.loseStop();
1972 if (frames.max_stream_data) |limit| receive_part.window.loseLimit(limit.maximum);
1973 if (frames.stream_data_blocked) |limit| send_part.credit.loseBlocked(limit.maximum);
1974 }
1975
1976 /// Puts STREAMS_BLOCKED back in the queue while stream 0 is still unopened and the peer still
1977 /// allows the same number of streams the lost frame reported, resending the blocked report
1978 /// while that condition holds.
1979 fn loseStreamsBlocked(self: *Connection, limit: quic.frame.StreamLimit) void {
1980 std.debug.assert(!limit.unidirectional);
1981 if (self.stream_open) return;
1982 if (self.peer_streams_bidi != limit.maximum) return;
1983 self.streams_blocked_pending = true;
1984 }
1985
1986 fn validateConnectionIds(self: *Connection, view: PacketView) bool {
1987 if (self.peer_source_cid == null) {
1988 const source = view.source orelse return false;
1989 if (view.kind != .initial) return false;
1990 self.peer_source_cid = source;
1991 self.peer_cid = source;
1992 } else if (view.source) |source| {
1993 if (!sameCid(self.peer_source_cid.?, source)) return false;
1994 }
1995 if (self.config.role == .server and view.kind == .initial) {
1996 if (sameCid(view.destination, self.original_dcid.?)) return true;
1997 }
1998 return sameCid(view.destination, self.local_cid);
1999 }
2000
2001 /// Drops the Initial keys, everything the Initial space held for recovery, and its waiting
2002 /// handshake bytes, so an abandoned space leaves no timer or record behind. A client arrives
2003 /// here as it sends each Handshake packet, and a server as it takes one in. Only the first call
2004 /// does anything, which keeps a repeat from putting the doubling count back to the start.
2005 fn discardInitialKeys(self: *Connection) void {
2006 if (self.initialKeysDiscarded()) return;
2007 self.keys.discardInitial();
2008 self.space(.initial).discard();
2009 self.crypto_lost[spaceIndex(.initial)] = .{};
2010 self.resetProbeTimeout(.initial);
2011 std.debug.assert(self.keys.write(.initial) == null);
2012 std.debug.assert(self.keys.read(.initial) == null);
2013 }
2014
2015 /// Drops the Handshake keys, everything the Handshake space held for recovery, and its waiting
2016 /// handshake bytes, so an abandoned space leaves no timer or record behind. A client arrives
2017 /// here on each HANDSHAKE_DONE frame it takes in, and a server wherever TLS says the handshake
2018 /// stands confirmed. Only the first call does anything, which keeps a repeat from putting the
2019 /// doubling count back to the start.
2020 fn discardHandshakeKeys(self: *Connection) void {
2021 if (self.handshakeKeysDiscarded()) return;
2022 self.keys.discardHandshake();
2023 self.space(.handshake).discard();
2024 self.crypto_lost[spaceIndex(.handshake)] = .{};
2025 self.resetProbeTimeout(.handshake);
2026 std.debug.assert(self.keys.write(.handshake) == null);
2027 std.debug.assert(self.keys.read(.handshake) == null);
2028 }
2029
2030 /// Puts the probe timeout of a space whose keys are gone back to the start, so a space that no
2031 /// longer exists stops contributing to consecutive backoff timeouts. The doubling count starts
2032 /// over, and no probe is owed to that space. The function empties the deadline and the
2033 /// anti-deadlock anchor, and every caller leaves through `send` or `receive`, which arm the
2034 /// timer afresh.
2035 fn resetProbeTimeout(self: *Connection, kind: quic.connection.SpaceKind) void {
2036 std.debug.assert(kind != .application);
2037 self.pto_count = 0;
2038 self.probe_packets[spaceIndex(kind)] = 0;
2039 self.pto_deadline_ns = null;
2040 self.pto_anchor_ns = null;
2041 }
2042
2043 fn noteClosingDatagram(self: *Connection) void {
2044 std.debug.assert(self.status_value == .closing);
2045 std.debug.assert(self.close_response_packet_limit >= 1);
2046 self.close_packets_since_response +|= 1;
2047 if (self.close_packets_since_response >= self.close_response_packet_limit) {
2048 self.close_pending = true;
2049 }
2050 }
2051
2052 fn advanceCloseResponseLimit(self: *Connection) void {
2053 std.debug.assert(self.status_value == .closing);
2054 std.debug.assert(self.close_response_packet_limit >= 1);
2055 self.close_packets_since_response = 0;
2056 self.close_response_packet_limit *|= 2;
2057 }
2058
2059 fn fail(
2060 self: *Connection,
2061 error_code: u62,
2062 frame_type: ?u62,
2063 reason: []const u8,
2064 now_ns: u64,
2065 ) void {
2066 switch (self.status_value) {
2067 .handshaking, .established => {},
2068 .closing => {
2069 std.debug.assert(self.close_deadline_ns != null);
2070 return;
2071 },
2072 .draining, .closed => return,
2073 }
2074 std.debug.assert(self.close_deadline_ns == null);
2075 self.storeReason(error_code, false, frame_type, reason, false);
2076 self.status_value = .closing;
2077 self.close_pending = true;
2078 self.close_deadline_ns = closeDeadline(now_ns);
2079 }
2080
2081 fn storeReason(
2082 self: *Connection,
2083 error_code: u62,
2084 application: bool,
2085 frame_type: ?u62,
2086 reason: []const u8,
2087 remote: bool,
2088 ) void {
2089 std.debug.assert(reason.len <= self.storage.reason.len);
2090 @memcpy(self.storage.reason[0..reason.len], reason);
2091 self.close_reason_value = .{
2092 .error_code = error_code,
2093 .application = application,
2094 .frame_type = frame_type,
2095 .reason = self.storage.reason[0..reason.len],
2096 .remote = remote,
2097 };
2098 }
2099
2100 fn closeSpace(self: *Connection) ?quic.connection.SpaceKind {
2101 if (self.keys.write(.application) != null) return .application;
2102 if (self.keys.write(.handshake) != null) return .handshake;
2103 if (self.keys.write(.initial) != null) return .initial;
2104 return null;
2105 }
2106
2107 /// Says whether an identifier names the one stream this connection has open, providing the
2108 /// single initial check for every public method that names an already-open stream. `write`,
2109 /// `finish`, `read`, `resetStream`, `stopSending`, `sendState`, `receiveState`, and
2110 /// `streamStats` all pass through it. The check asserts that the connection still holds its
2111 /// storage, so a call made after the caller took that storage back trips in a debug build.
2112 /// Identifier 0 is the one it answers true for.
2113 fn streamKnown(self: *const Connection, id: StreamId) bool {
2114 std.debug.assert(!self.deinitialized);
2115 if (id != 0) return false;
2116 return self.stream_open;
2117 }
2118
2119 fn streamOpenForApplication(self: *const Connection, id: StreamId) bool {
2120 if (!self.streamKnown(id)) return false;
2121 return isOpen(self.status_value);
2122 }
2123
2124 fn connectionBlocked(self: *const Connection) ?u62 {
2125 if (!self.stream_open) return null;
2126 const send_part = &self.stream_send;
2127 const waiting = send_part.reset_code == null and send_part.sent < send_part.written;
2128 return self.connection_credit.blocked(waiting);
2129 }
2130
2131 /// Says whether any 1-RTT control, flow control, or stream frame is waiting, which lets an ACK
2132 /// travel alongside one because an ACK alone does not oblige the peer to answer. The function
2133 /// covers a waiting HANDSHAKE_DONE, PING, and STREAMS_BLOCKED, a connection window figure, and
2134 /// a connection blocked report. Once stream 0 is open, the function covers a waiting reset, a
2135 /// waiting stop, a stream window figure, a stream blocked report, and a pick ready to go.
2136 fn applicationFramesPending(self: *const Connection) bool {
2137 if (self.pending_handshake_done) return true;
2138 if (self.pending_ping) return true;
2139 if (self.streams_blocked_pending) return true;
2140 if (self.connection_window.update() != null) return true;
2141 if (!self.stream_open) return false;
2142 if (self.stream_send.reset_pending) return true;
2143 if (self.stream_receive.stop_pending) return true;
2144 if (self.stream_receive.windowUpdate() != null) return true;
2145 if (self.stream_send.blocked() != null) return true;
2146 if (self.connectionBlocked() != null) return true;
2147 const credit = self.connection_credit.available();
2148 return self.stream_send.nextChunk(std.math.maxInt(u16), credit) != null;
2149 }
2150
2151 fn space(self: *Connection, kind: quic.connection.SpaceKind) *quic.connection.Space {
2152 std.debug.assert(spaceIndex(kind) < self.spaces.len);
2153 return &self.spaces[spaceIndex(kind)];
2154 }
2155
2156 fn sendLimit(self: *const Connection, out_length: usize) usize {
2157 var limit = @min(out_length, self.limits.datagram_bytes);
2158 if (self.config.role == .server and !self.client_address_validated) {
2159 limit = @min(limit, self.remainingAmplificationBudget());
2160 }
2161 return limit;
2162 }
2163
2164 fn remainingAmplificationBudget(self: *const Connection) usize {
2165 const received = std.math.mul(u64, self.received_before_validation, 3) catch
2166 std.math.maxInt(u64);
2167 if (received <= self.sent_before_validation) return 0;
2168 return std.math.cast(usize, received - self.sent_before_validation) orelse
2169 std.math.maxInt(usize);
2170 }
2171
2172 fn recordDatagramSent(self: *Connection, length: usize) void {
2173 increment(&self.stats_value.datagrams_sent);
2174 if (self.config.role == .server and !self.client_address_validated) {
2175 self.sent_before_validation +|= length;
2176 }
2177 }
2178
2179 fn touchActivity(self: *Connection, now_ns: u64) void {
2180 self.timer_started = true;
2181 self.last_activity_ns = now_ns;
2182 }
2183
2184 fn touchActivityOnSend(self: *Connection, now_ns: u64, ack_eliciting: bool) void {
2185 if (!ack_eliciting) return;
2186 self.touchActivity(now_ns);
2187 }
2188
2189 fn idleDeadline(self: *const Connection) ?u64 {
2190 if (!self.timer_started) return null;
2191 if (self.effective_idle_timeout_ms == 0) return null;
2192 const duration = std.math.mul(
2193 u64,
2194 self.effective_idle_timeout_ms,
2195 std.time.ns_per_ms,
2196 ) catch std.math.maxInt(u64);
2197 return std.math.add(u64, self.last_activity_ns, duration) catch std.math.maxInt(u64);
2198 }
2199
2200 fn advanceTime(self: *Connection, now_ns: u64) void {
2201 if (self.close_deadline_ns) |deadline| {
2202 if (now_ns >= deadline) {
2203 self.status_value = .closed;
2204 self.close_pending = false;
2205 return;
2206 }
2207 }
2208 if (!isOpen(self.status_value)) return;
2209 self.detectExpiredLoss(now_ns);
2210 if (!isOpen(self.status_value)) return;
2211 self.expireProbeTimeout(now_ns);
2212 if (!isOpen(self.status_value)) return;
2213 const idle = self.idleDeadline() orelse return;
2214 if (now_ns < idle) return;
2215 self.storeReason(0, false, null, "idle timeout", false);
2216 self.status_value = .closed;
2217 }
2218 };
2219
2220 fn initSpaces(storage: *quic.connection.Storage) [3]quic.connection.Space {
2221 return .{
2222 quic.connection.Space.init(.initial, storage.ranges(0), storage.records(0)),
2223 quic.connection.Space.init(.handshake, storage.ranges(1), storage.records(1)),
2224 quic.connection.Space.init(.application, storage.ranges(2), storage.records(2)),
2225 };
2226 }
2227
2228 fn initCryptoStreams(storage: *quic.connection.Storage) [3]quic.connection.Reassembler {
2229 return .{
2230 quic.connection.Reassembler.init(storage.crypto_bytes[0], storage.crypto_present[0]),
2231 quic.connection.Reassembler.init(storage.crypto_bytes[1], storage.crypto_present[1]),
2232 quic.connection.Reassembler.init(storage.crypto_bytes[2], storage.crypto_present[2]),
2233 };
2234 }
2235
2236 fn inspectPacket(bytes: []const u8, destination_length: u5) ViewError!PacketView {
2237 if (bytes.len == 0) return error.Malformed;
2238 if (bytes[0] & 0x80 == 0) {
2239 const short = quic.packet.decodeShort(bytes, destination_length) catch
2240 return error.Malformed;
2241 return .{
2242 .kind = .application,
2243 .length = bytes.len,
2244 .packet_number_offset = short.packet_number_offset,
2245 .destination = short.destination,
2246 .source = null,
2247 };
2248 }
2249 const decoded = quic.packet.decodeLong(bytes) catch |failure| {
2250 if (failure == error.UnsupportedVersion) return error.Unsupported;
2251 return error.Malformed;
2252 };
2253 const metadata: LongMetadata = switch (decoded) {
2254 .initial => |value| .{
2255 .kind = .initial,
2256 .length = value.length,
2257 .offset = value.packet_number_offset,
2258 .destination = value.common.destination,
2259 .source = value.common.source,
2260 },
2261 .handshake => |value| .{
2262 .kind = .handshake,
2263 .length = value.length,
2264 .offset = value.packet_number_offset,
2265 .destination = value.common.destination,
2266 .source = value.common.source,
2267 },
2268 else => return error.Unsupported,
2269 };
2270 const length = std.math.cast(usize, metadata.length) orelse return error.Malformed;
2271 if (metadata.offset > bytes.len) return error.Malformed;
2272 if (length > bytes.len - metadata.offset) return error.Malformed;
2273 return .{
2274 .kind = metadata.kind,
2275 .length = metadata.offset + length,
2276 .packet_number_offset = metadata.offset,
2277 .destination = metadata.destination,
2278 .source = metadata.source,
2279 };
2280 }
2281
2282 /// Writes one frame, or puts the cursor back to the position where it stood and answers false when
2283 /// the frame will not fit, leaving the packet exactly as it was during frame-by-frame packing. The
2284 /// frame then travels in a later packet, since the state that produced it is left alone.
2285 fn encodeDeferred(output: *quic.cursor.Write, value: quic.frame.Frame) PayloadError!bool {
2286 const start = output.index;
2287 quic.frame.encode(value, output) catch |failure| switch (failure) {
2288 error.NoSpace => {
2289 output.index = start;
2290 return false;
2291 },
2292 else => |other| return other,
2293 };
2294 return true;
2295 }
2296
2297 fn emitPing(output: *quic.cursor.Write, payload: *Payload) PayloadError!void {
2298 const written = try encodeDeferred(output, .{ .ping = {} });
2299 if (!written) return;
2300 payload.summary.ping = true;
2301 payload.sent_ping = true;
2302 payload.ack_eliciting = true;
2303 }
2304
2305 /// Gives the frame type number that a close reason reports, providing the exact code for a
2306 /// CONNECTION_CLOSE frame. A STREAM frame's number carries its OFF, LEN, and FIN bits, and the
2307 /// numbers for ACK, MAX_STREAMS, and STREAMS_BLOCKED each turn on which variant it is.
2308 fn frameType(value: quic.frame.Frame) u62 {
2309 return switch (value) {
2310 .padding => 0x00,
2311 .ping => 0x01,
2312 .ack => |ack| if (ack.ecn == null) 0x02 else 0x03,
2313 .reset_stream => 0x04,
2314 .stop_sending => 0x05,
2315 .crypto => 0x06,
2316 .new_token => 0x07,
2317 .stream => |data| streamFrameType(data),
2318 .max_data => 0x10,
2319 .max_stream_data => 0x11,
2320 .max_streams => |limit| if (limit.unidirectional) 0x13 else 0x12,
2321 .data_blocked => 0x14,
2322 .stream_data_blocked => 0x15,
2323 .streams_blocked => |limit| if (limit.unidirectional) 0x17 else 0x16,
2324 .new_connection_id => 0x18,
2325 .retire_connection_id => 0x19,
2326 .path_challenge => 0x1a,
2327 .path_response => 0x1b,
2328 .connection_close => |close_frame| if (close_frame.application) 0x1d else 0x1c,
2329 .handshake_done => 0x1e,
2330 .datagram => |datagram| if (datagram.length_present) 0x31 else 0x30,
2331 };
2332 }
2333
2334 fn streamFrameType(data: quic.frame.Stream) u62 {
2335 var value: u62 = 0x08;
2336 if (data.offset_present) value |= 0x04;
2337 if (data.length_present) value |= 0x02;
2338 if (data.fin) value |= 0x01;
2339 return value;
2340 }
2341
2342 fn preferredCipherSuite(suite: quic.crypto.Suite) std.crypto.tls.CipherSuite {
2343 return switch (suite) {
2344 .aes_128_gcm_sha256 => .AES_128_GCM_SHA256,
2345 .chacha20_poly1305_sha256 => .CHACHA20_POLY1305_SHA256,
2346 };
2347 }
2348
2349 /// Gives the least room the send path wants before it will plan a packet in the space, so a
2350 /// datagram too small to carry one is left alone. A datagram carrying an Initial packet wants 1200
2351 /// bytes, and anything else wants 64.
2352 fn sendRoomMin(kind: quic.connection.SpaceKind) usize {
2353 return if (kind == .initial) 1200 else 64;
2354 }
2355
2356 fn packetMinimum(
2357 kind: quic.connection.SpaceKind,
2358 available: usize,
2359 carries_initial: bool,
2360 can_coalesce_handshake: bool,
2361 ) u16 {
2362 std.debug.assert(available <= std.math.maxInt(u16));
2363 if (kind == .initial) {
2364 if (can_coalesce_handshake and available >= 1200) return 256;
2365 return 1200;
2366 }
2367 if (kind == .handshake and carries_initial) return @intCast(available);
2368 return 0;
2369 }
2370
2371 fn packetReserve(
2372 kind: quic.connection.SpaceKind,
2373 packet_bytes: usize,
2374 packet_number_bytes: u3,
2375 destination_bytes: u5,
2376 source_bytes: u5,
2377 ) usize {
2378 std.debug.assert(packet_bytes <= quic.crypto.packet.packet_bytes_max);
2379 std.debug.assert(packet_number_bytes >= 1);
2380 std.debug.assert(packet_number_bytes <= 4);
2381 const length_bytes: usize = quic.varint.encodedLength(@intCast(packet_bytes));
2382 const destination: usize = destination_bytes;
2383 const source: usize = source_bytes;
2384 const header_bytes: usize = switch (kind) {
2385 .initial => 1 + 4 + 1 + destination + 1 + source + 1 + length_bytes,
2386 .handshake => 1 + 4 + 1 + destination + 1 + source + length_bytes,
2387 .application => 1 + destination,
2388 };
2389 return header_bytes + packet_number_bytes + quic.crypto.packet.tag_bytes;
2390 }
2391
2392 fn closeReasonCapacity() usize {
2393 const cid_bytes = quic.packet.connection_id_bytes_max;
2394 const frame_bytes: usize = 1 + 8 + 8 + @as(usize, quic.varint.encodedLength(1200));
2395 const initial = packetReserve(.initial, 1200, 4, cid_bytes, cid_bytes) + frame_bytes;
2396 const handshake = packetReserve(.handshake, 1200, 4, cid_bytes, cid_bytes) + frame_bytes;
2397 const application = packetReserve(.application, 1200, 4, cid_bytes, cid_bytes) + frame_bytes;
2398 return @min(1200 - initial, (1200 - handshake - application) / 2);
2399 }
2400
2401 fn closeDeadline(now_ns: u64) u64 {
2402 return std.math.add(u64, now_ns, closing_period_ns) catch std.math.maxInt(u64);
2403 }
2404
2405 fn isOpen(status: quic.connection.Status) bool {
2406 return status == .handshaking or status == .established;
2407 }
2408
2409 fn cryptoDataCapacity(offset: u62, available: usize, storage_bytes: usize) usize {
2410 std.debug.assert(available <= quic.crypto.packet.packet_bytes_max);
2411 std.debug.assert(storage_bytes <= std.math.maxInt(u20));
2412 const fixed = 1 + @as(usize, quic.varint.encodedLength(offset));
2413 if (available <= fixed) return 0;
2414 var data_bytes = @min(storage_bytes, available - fixed);
2415 for (0..4) |_| {
2416 const length_bytes: usize = quic.varint.encodedLength(@intCast(data_bytes));
2417 const header_bytes = fixed + length_bytes;
2418 if (header_bytes > available) return 0;
2419 if (data_bytes <= available - header_bytes) return data_bytes;
2420 data_bytes = @min(storage_bytes, available - header_bytes);
2421 }
2422 return 0;
2423 }
2424
2425 fn sameCid(left: quic.packet.ConnectionId, right: quic.packet.ConnectionId) bool {
2426 return std.mem.eql(u8, left.slice(), right.slice());
2427 }
2428
2429 fn spaceIndex(kind: quic.connection.SpaceKind) usize {
2430 return @backingInt(kind);
2431 }
2432
2433 fn maxAckDelayNs(delay_ms: u14) u64 {
2434 return @as(u64, delay_ms) * std.time.ns_per_ms;
2435 }
2436
2437 fn earlier(current: ?u64, candidate: u64) u64 {
2438 return if (current) |value| @min(value, candidate) else candidate;
2439 }
2440
2441 fn increment(value: *u64) void {
2442 value.* +|= 1;
2443 }