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 }