lib/quic/src/frame/codec.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const quic = @import("../root.zig");
3 const model = @import("model.zig");
4
5 const cursor = quic.cursor;
6 const varint = quic.varint;
7
8 const DecodeSpecific = error{
9 ConnectionIdTooLong,
10 EmptyToken,
11 InvalidAckRange,
12 InvalidFrameType,
13 InvalidLength,
14 InvalidNewConnectionId,
15 NonMinimalFrameType,
16 RangeLimitExceeded,
17 StreamLimitExceeded,
18 };
19
20 const EncodeSpecific = error{
21 EmptyToken,
22 InvalidAckRange,
23 InvalidLength,
24 InvalidNewConnectionId,
25 RangeLimitExceeded,
26 StreamLimitExceeded,
27 };
28
29 pub const DecodeError = cursor.ReadError || varint.DecodeError || DecodeSpecific;
30 pub const EncodeError = cursor.WriteError || varint.EncodeError || EncodeSpecific;
31
32 fn valueLength(value: u62) DecodeError!usize {
33 if (comptime @bitSizeOf(usize) < 62) {
34 if (value > std.math.maxInt(usize)) return error.InvalidLength;
35 }
36 return @intCast(value);
37 }
38
39 fn sliceLength(length: usize) EncodeError!u62 {
40 if (@as(u64, length) > std.math.maxInt(u62)) return error.InvalidLength;
41 return @intCast(length);
42 }
43
44 fn takePayload(input: *cursor.Read, value: u62) DecodeError![]const u8 {
45 return input.take(try valueLength(value));
46 }
47
48 fn rangeFits(offset: u62, length: usize) bool {
49 if (@as(u64, length) > std.math.maxInt(u62)) return false;
50 const bounded_length: u62 = @intCast(length);
51 return offset <= std.math.maxInt(u62) - bounded_length;
52 }
53
54 fn readType(input: *cursor.Read) DecodeError!u62 {
55 const decoded = try varint.read(input);
56 if (decoded.length != varint.encodedLength(decoded.value)) {
57 return error.NonMinimalFrameType;
58 }
59 return decoded.value;
60 }
61
62 fn readAckRange(
63 input: *cursor.Read,
64 previous_smallest: *u62,
65 ) DecodeError!void {
66 const gap = (try varint.read(input)).value;
67 const length = (try varint.read(input)).value;
68 const distance = @as(u64, gap) + 2;
69 if (distance > previous_smallest.*) return error.InvalidAckRange;
70 const largest = previous_smallest.* - @as(u62, @intCast(distance));
71 if (length > largest) return error.InvalidAckRange;
72 previous_smallest.* = largest - length;
73 }
74
75 fn decodeAck(input: *cursor.Read, has_ecn: bool) DecodeError!model.Frame {
76 const largest = (try varint.read(input)).value;
77 const delay = (try varint.read(input)).value;
78 const count = (try varint.read(input)).value;
79 const first_range = (try varint.read(input)).value;
80 if (count > model.ack_ranges_max) return error.RangeLimitExceeded;
81 if (first_range > largest) return error.InvalidAckRange;
82 const range_count: u8 = @intCast(count);
83 const range_start = input.index;
84 var previous_smallest = largest - first_range;
85 for (0..model.ack_ranges_max) |index| {
86 if (index >= range_count) break;
87 try readAckRange(input, &previous_smallest);
88 }
89 const ranges = input.bytes[range_start..input.index];
90 const ecn: ?model.Ecn = if (has_ecn) .{
91 .ect0 = (try varint.read(input)).value,
92 .ect1 = (try varint.read(input)).value,
93 .ce = (try varint.read(input)).value,
94 } else null;
95 return .{ .ack = .{
96 .largest = largest,
97 .delay = delay,
98 .first_range = first_range,
99 .range_count = range_count,
100 .ranges = ranges,
101 .ecn = ecn,
102 } };
103 }
104
105 fn decodeCrypto(input: *cursor.Read) DecodeError!model.Frame {
106 const offset = (try varint.read(input)).value;
107 const length = (try varint.read(input)).value;
108 const data = try takePayload(input, length);
109 if (!rangeFits(offset, data.len)) return error.InvalidLength;
110 return .{ .crypto = .{ .offset = offset, .data = data } };
111 }
112
113 fn decodeToken(input: *cursor.Read) DecodeError!model.Frame {
114 const length = (try varint.read(input)).value;
115 if (length == 0) return error.EmptyToken;
116 return .{ .new_token = try takePayload(input, length) };
117 }
118
119 fn decodeStream(input: *cursor.Read, frame_type: u62) DecodeError!model.Frame {
120 const offset_present = frame_type & 0x04 != 0;
121 const length_present = frame_type & 0x02 != 0;
122 const stream_id = (try varint.read(input)).value;
123 const offset = if (offset_present) (try varint.read(input)).value else 0;
124 const data = if (length_present) blk: {
125 const length = (try varint.read(input)).value;
126 break :blk try takePayload(input, length);
127 } else input.rest();
128 if (!rangeFits(offset, data.len)) return error.InvalidLength;
129 return .{ .stream = .{
130 .stream_id = stream_id,
131 .offset = offset,
132 .offset_present = offset_present,
133 .length_present = length_present,
134 .fin = frame_type & 0x01 != 0,
135 .data = data,
136 } };
137 }
138
139 fn decodeStreamLimit(input: *cursor.Read, frame_type: u62) DecodeError!model.StreamLimit {
140 const maximum = (try varint.read(input)).value;
141 if (maximum > @as(u62, 1) << 60) return error.StreamLimitExceeded;
142 return .{ .unidirectional = frame_type & 1 != 0, .maximum = maximum };
143 }
144
145 fn decodeNewConnectionId(input: *cursor.Read) DecodeError!model.Frame {
146 const sequence = (try varint.read(input)).value;
147 const retire_prior_to = (try varint.read(input)).value;
148 if (retire_prior_to > sequence) return error.InvalidNewConnectionId;
149 const length = try input.byte();
150 if (length == 0) return error.InvalidNewConnectionId;
151 if (length > quic.packet.connection_id_bytes_max) return error.ConnectionIdTooLong;
152 const connection_id = quic.packet.ConnectionId.init(try input.take(length)) catch
153 return error.ConnectionIdTooLong;
154 var reset_token: [16]u8 = undefined;
155 @memcpy(&reset_token, try input.take(16));
156 return .{ .new_connection_id = .{
157 .sequence = sequence,
158 .retire_prior_to = retire_prior_to,
159 .connection_id = connection_id,
160 .reset_token = reset_token,
161 } };
162 }
163
164 fn decodeConnectionClose(
165 input: *cursor.Read,
166 application: bool,
167 ) DecodeError!model.Frame {
168 const error_code = (try varint.read(input)).value;
169 const frame_type: ?u62 = if (application) null else (try varint.read(input)).value;
170 const reason_length = (try varint.read(input)).value;
171 return .{ .connection_close = .{
172 .application = application,
173 .error_code = error_code,
174 .frame_type = frame_type,
175 .reason = try takePayload(input, reason_length),
176 } };
177 }
178
179 fn decodeDatagram(input: *cursor.Read, length_present: bool) DecodeError!model.Frame {
180 const data = if (length_present) blk: {
181 const length = (try varint.read(input)).value;
182 break :blk try takePayload(input, length);
183 } else input.rest();
184 return .{ .datagram = .{ .length_present = length_present, .data = data } };
185 }
186
187 pub fn decode(input: *cursor.Read) DecodeError!model.Frame {
188 const frame_type = try readType(input);
189 if (frame_type >= 0x08 and frame_type <= 0x0f) {
190 return decodeStream(input, frame_type);
191 }
192 return switch (frame_type) {
193 0x00 => .{ .padding = {} },
194 0x01 => .{ .ping = {} },
195 0x02 => decodeAck(input, false),
196 0x03 => decodeAck(input, true),
197 0x04 => .{ .reset_stream = .{
198 .stream_id = (try varint.read(input)).value,
199 .error_code = (try varint.read(input)).value,
200 .final_size = (try varint.read(input)).value,
201 } },
202 0x05 => .{ .stop_sending = .{
203 .stream_id = (try varint.read(input)).value,
204 .error_code = (try varint.read(input)).value,
205 } },
206 0x06 => decodeCrypto(input),
207 0x07 => decodeToken(input),
208 0x10 => .{ .max_data = (try varint.read(input)).value },
209 0x11 => .{ .max_stream_data = try decodeStreamData(input) },
210 0x12, 0x13 => .{ .max_streams = try decodeStreamLimit(input, frame_type) },
211 0x14 => .{ .data_blocked = (try varint.read(input)).value },
212 0x15 => .{ .stream_data_blocked = try decodeStreamData(input) },
213 0x16, 0x17 => .{ .streams_blocked = try decodeStreamLimit(input, frame_type) },
214 0x18 => decodeNewConnectionId(input),
215 0x19 => .{ .retire_connection_id = (try varint.read(input)).value },
216 0x1a => .{ .path_challenge = try readEight(input) },
217 0x1b => .{ .path_response = try readEight(input) },
218 0x1c => decodeConnectionClose(input, false),
219 0x1d => decodeConnectionClose(input, true),
220 0x1e => .{ .handshake_done = {} },
221 0x30 => decodeDatagram(input, false),
222 0x31 => decodeDatagram(input, true),
223 else => error.InvalidFrameType,
224 };
225 }
226
227 fn decodeStreamData(input: *cursor.Read) DecodeError!model.StreamData {
228 return .{
229 .stream_id = (try varint.read(input)).value,
230 .maximum = (try varint.read(input)).value,
231 };
232 }
233
234 fn readEight(input: *cursor.Read) DecodeError![8]u8 {
235 var value: [8]u8 = undefined;
236 @memcpy(&value, try input.take(8));
237 return value;
238 }
239
240 fn writeAckRange(
241 output: *cursor.Write,
242 range: model.AckRange,
243 previous_smallest: *u62,
244 ) EncodeError!void {
245 const distance = @as(u64, range.gap) + 2;
246 if (distance > previous_smallest.*) return error.InvalidAckRange;
247 const largest = previous_smallest.* - @as(u62, @intCast(distance));
248 if (range.length > largest) return error.InvalidAckRange;
249 previous_smallest.* = largest - range.length;
250 _ = try varint.write(range.gap, output);
251 _ = try varint.write(range.length, output);
252 }
253
254 fn encodeAck(value: model.Ack, output: *cursor.Write) EncodeError!void {
255 if (value.range_count > model.ack_ranges_max) return error.RangeLimitExceeded;
256 if (value.first_range > value.largest) return error.InvalidAckRange;
257 try output.byte(if (value.ecn == null) 0x02 else 0x03);
258 _ = try varint.write(value.largest, output);
259 _ = try varint.write(value.delay, output);
260 _ = try varint.write(value.range_count, output);
261 _ = try varint.write(value.first_range, output);
262 var iterator = value.iterator();
263 var previous_smallest = value.largest - value.first_range;
264 for (0..model.ack_ranges_max) |index| {
265 if (index >= value.range_count) break;
266 const range = (iterator.next() catch return error.InvalidAckRange) orelse
267 return error.InvalidAckRange;
268 try writeAckRange(output, range, &previous_smallest);
269 }
270 if (!iterator.exhausted()) return error.InvalidAckRange;
271 if (value.ecn) |ecn| {
272 _ = try varint.write(ecn.ect0, output);
273 _ = try varint.write(ecn.ect1, output);
274 _ = try varint.write(ecn.ce, output);
275 }
276 }
277
278 fn encodeStream(value: model.Stream, output: *cursor.Write) EncodeError!void {
279 if (!value.offset_present and value.offset != 0) return error.InvalidLength;
280 if (!rangeFits(value.offset, value.data.len)) return error.InvalidLength;
281 const frame_type: u8 = 0x08 |
282 (@as(u8, @intFromBool(value.offset_present)) << 2) |
283 (@as(u8, @intFromBool(value.length_present)) << 1) |
284 @as(u8, @intFromBool(value.fin));
285 try output.byte(frame_type);
286 _ = try varint.write(value.stream_id, output);
287 if (value.offset_present) _ = try varint.write(value.offset, output);
288 if (value.length_present) _ = try varint.write(try sliceLength(value.data.len), output);
289 try output.put(value.data);
290 }
291
292 fn encodeStreamLimit(
293 base_type: u8,
294 value: model.StreamLimit,
295 output: *cursor.Write,
296 ) EncodeError!void {
297 if (value.maximum > @as(u62, 1) << 60) return error.StreamLimitExceeded;
298 try output.byte(base_type | @as(u8, @intFromBool(value.unidirectional)));
299 _ = try varint.write(value.maximum, output);
300 }
301
302 fn encodeNewConnectionId(
303 value: model.NewConnectionId,
304 output: *cursor.Write,
305 ) EncodeError!void {
306 if (value.retire_prior_to > value.sequence) return error.InvalidNewConnectionId;
307 if (value.connection_id.length == 0) return error.InvalidNewConnectionId;
308 try output.byte(0x18);
309 _ = try varint.write(value.sequence, output);
310 _ = try varint.write(value.retire_prior_to, output);
311 try output.byte(value.connection_id.length);
312 try output.put(value.connection_id.slice());
313 try output.put(&value.reset_token);
314 }
315
316 fn encodeConnectionClose(
317 value: model.ConnectionClose,
318 output: *cursor.Write,
319 ) EncodeError!void {
320 try output.byte(if (value.application) 0x1d else 0x1c);
321 _ = try varint.write(value.error_code, output);
322 if (value.application) {
323 if (value.frame_type != null) return error.InvalidLength;
324 } else {
325 _ = try varint.write(value.frame_type orelse return error.InvalidLength, output);
326 }
327 _ = try varint.write(try sliceLength(value.reason.len), output);
328 try output.put(value.reason);
329 }
330
331 pub fn encode(value: model.Frame, output: *cursor.Write) EncodeError!void {
332 switch (value) {
333 .padding => try output.byte(0x00),
334 .ping => try output.byte(0x01),
335 .ack => |frame| try encodeAck(frame, output),
336 .reset_stream => |frame| {
337 try output.byte(0x04);
338 _ = try varint.write(frame.stream_id, output);
339 _ = try varint.write(frame.error_code, output);
340 _ = try varint.write(frame.final_size, output);
341 },
342 .stop_sending => |frame| {
343 try output.byte(0x05);
344 _ = try varint.write(frame.stream_id, output);
345 _ = try varint.write(frame.error_code, output);
346 },
347 .crypto => |frame| {
348 if (!rangeFits(frame.offset, frame.data.len)) return error.InvalidLength;
349 try output.byte(0x06);
350 _ = try varint.write(frame.offset, output);
351 _ = try varint.write(try sliceLength(frame.data.len), output);
352 try output.put(frame.data);
353 },
354 .new_token => |token| {
355 if (token.len == 0) return error.EmptyToken;
356 try output.byte(0x07);
357 _ = try varint.write(try sliceLength(token.len), output);
358 try output.put(token);
359 },
360 .stream => |frame| try encodeStream(frame, output),
361 .max_data => |maximum| try encodeOne(0x10, maximum, output),
362 .max_stream_data => |frame| try encodeStreamData(0x11, frame, output),
363 .max_streams => |frame| try encodeStreamLimit(0x12, frame, output),
364 .data_blocked => |maximum| try encodeOne(0x14, maximum, output),
365 .stream_data_blocked => |frame| try encodeStreamData(0x15, frame, output),
366 .streams_blocked => |frame| try encodeStreamLimit(0x16, frame, output),
367 .new_connection_id => |frame| try encodeNewConnectionId(frame, output),
368 .retire_connection_id => |sequence| try encodeOne(0x19, sequence, output),
369 .path_challenge => |data| try encodeEight(0x1a, data, output),
370 .path_response => |data| try encodeEight(0x1b, data, output),
371 .connection_close => |frame| try encodeConnectionClose(frame, output),
372 .handshake_done => try output.byte(0x1e),
373 .datagram => |frame| {
374 try output.byte(if (frame.length_present) 0x31 else 0x30);
375 if (frame.length_present) {
376 _ = try varint.write(try sliceLength(frame.data.len), output);
377 }
378 try output.put(frame.data);
379 },
380 }
381 }
382
383 fn encodeOne(frame_type: u8, value: u62, output: *cursor.Write) EncodeError!void {
384 try output.byte(frame_type);
385 _ = try varint.write(value, output);
386 }
387
388 fn encodeStreamData(
389 frame_type: u8,
390 value: model.StreamData,
391 output: *cursor.Write,
392 ) EncodeError!void {
393 try output.byte(frame_type);
394 _ = try varint.write(value.stream_id, output);
395 _ = try varint.write(value.maximum, output);
396 }
397
398 fn encodeEight(frame_type: u8, data: [8]u8, output: *cursor.Write) EncodeError!void {
399 try output.byte(frame_type);
400 try output.put(&data);
401 }