Skip to documentation
SLOP

tiny.quic.transport

Reference tiny.quic transport

Defined in tiny.quic.

API (7)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callstiny.quictransport
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallstest sourcelib.quic.src.transport.test_RFC_9000_section_181 skips reserved and unknown paramete...test sourcelib.quic.src.transport.test_RFC_9000_section_182 and RFC 9221 section 3 parameters r...test sourcelib.quic.src.transport.test_RFC_9000_section_182 max UDP payload size upper boundtest sourcelib.quic.src.transport.test_RFC_9000_section_182 parameter count maximum and maximum...test sourcelib.quic.src.transport.test_RFC_9000_section_182 rejects invalid parameter valuestest sourcelib.quic.src.transport.test_RFC_9000_sections_74 and 18 reject duplicate parameterscursor.Readinitcursor.Readremainingcursor.Readtakeprivate sourcelib.quic.src.transportassignParameterprivate sourcelib.quic.src.transportduplicate+2 moretransportdecode
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.quic.src.transport.test_RFC_9000_section_182 and RFC 9221 section 3 parameters r...test sourcelib.quic.src.transport.test_RFC_9000_section_182 max UDP payload size upper boundprivate sourcelib.quic.src.transportvalidateprivate sourcelib.quic.src.transportwriteFlowControlprivate sourcelib.quic.src.transportwriteIntegerprivate sourcelib.quic.src.transportwritePreferredAddressprivate sourcelib.quic.src.transportwriteTupletransportencode
Static calls · unresolved targets: 0 · external targets: 1.

Source: lib/quic/src/root.zig:51

zig
pub const transport = @import("transport.zig");

Source: lib/quic/src/transport.zig

zig
const std = @import("std");const quic = @import("root.zig");const cursor = quic.cursor;const varint = quic.varint;pub const parameter_count_max: u8 = 64;pub const PreferredAddress = struct {    ipv4: [4]u8,    ipv4_port: u16,    ipv6: [16]u8,    ipv6_port: u16,    connection_id: quic.packet.ConnectionId,    reset_token: [16]u8,};pub const Parameters = struct {    original_destination_connection_id: ?quic.packet.ConnectionId = null,    max_idle_timeout: u62 = 0,    stateless_reset_token: ?[16]u8 = null,    max_udp_payload_size: u62 = 65_527,    initial_max_data: u62 = 0,    initial_max_stream_data_bidi_local: u62 = 0,    initial_max_stream_data_bidi_remote: u62 = 0,    initial_max_stream_data_uni: u62 = 0,    initial_max_streams_bidi: u62 = 0,    initial_max_streams_uni: u62 = 0,    ack_delay_exponent: u5 = 3,    max_ack_delay: u14 = 25,    disable_active_migration: bool = false,    preferred_address: ?PreferredAddress = null,    active_connection_id_limit: u62 = 2,    initial_source_connection_id: ?quic.packet.ConnectionId = null,    retry_source_connection_id: ?quic.packet.ConnectionId = null,    max_datagram_frame_size: u62 = 0,};const DecodeSpecific = error{    DuplicateParameter,    InvalidAckDelayExponent,    InvalidActiveConnectionIdLimit,    InvalidConnectionId,    InvalidDisableActiveMigration,    InvalidInteger,    InvalidLength,    InvalidMaxAckDelay,    InvalidMaxUdpPayloadSize,    InvalidPreferredAddress,    InvalidStatelessResetToken,    InvalidStreamLimit,    TooManyParameters,};const EncodeSpecific = error{    InvalidAckDelayExponent,    InvalidActiveConnectionIdLimit,    InvalidLength,    InvalidMaxAckDelay,    InvalidMaxUdpPayloadSize,    InvalidPreferredAddress,    InvalidStreamLimit,};pub const DecodeError = cursor.ReadError || varint.DecodeError || DecodeSpecific;pub const EncodeError = cursor.WriteError || varint.EncodeError || EncodeSpecific;fn valueLength(value: u62) DecodeError!usize {    if (comptime @bitSizeOf(usize) < 62) {        if (value > std.math.maxInt(usize)) return error.InvalidLength;    }    return @intCast(value);}fn sliceLength(length: usize) EncodeError!u62 {    if (@as(u64, length) > std.math.maxInt(u62)) return error.InvalidLength;    return @intCast(length);}fn readInteger(bytes: []const u8) DecodeError!u62 {    var input = cursor.Read.init(bytes);    const decoded = varint.read(&input) catch return error.InvalidInteger;    if (input.remaining() != 0) return error.InvalidInteger;    return decoded.value;}fn readConnectionId(bytes: []const u8) DecodeError!quic.packet.ConnectionId {    return quic.packet.ConnectionId.init(bytes) catch error.InvalidConnectionId;}fn readPreferredAddress(bytes: []const u8) DecodeError!PreferredAddress {    return parsePreferredAddress(bytes) catch error.InvalidPreferredAddress;}fn parsePreferredAddress(bytes: []const u8) DecodeError!PreferredAddress {    var input = cursor.Read.init(bytes);    var ipv4: [4]u8 = undefined;    @memcpy(&ipv4, try input.take(4));    const ipv4_port = try input.int(u16);    var ipv6: [16]u8 = undefined;    @memcpy(&ipv6, try input.take(16));    const ipv6_port = try input.int(u16);    const connection_id_length = try input.byte();    if (connection_id_length == 0) return error.InvalidPreferredAddress;    if (connection_id_length > quic.packet.connection_id_bytes_max) {        return error.InvalidPreferredAddress;    }    const connection_id = quic.packet.ConnectionId.init(        try input.take(connection_id_length),    ) catch return error.InvalidPreferredAddress;    var reset_token: [16]u8 = undefined;    @memcpy(&reset_token, try input.take(16));    if (input.remaining() != 0) return error.InvalidPreferredAddress;    return .{        .ipv4 = ipv4,        .ipv4_port = ipv4_port,        .ipv6 = ipv6,        .ipv6_port = ipv6_port,        .connection_id = connection_id,        .reset_token = reset_token,    };}fn assignInteger(parameters: *Parameters, id: u62, value: u62) DecodeError!void {    switch (id) {        0x01 => parameters.max_idle_timeout = value,        0x03 => {            if (value < 1200 or value > 65_527) return error.InvalidMaxUdpPayloadSize;            parameters.max_udp_payload_size = value;        },        0x04 => parameters.initial_max_data = value,        0x05 => parameters.initial_max_stream_data_bidi_local = value,        0x06 => parameters.initial_max_stream_data_bidi_remote = value,        0x07 => parameters.initial_max_stream_data_uni = value,        0x08 => {            if (value > @as(u62, 1) << 60) return error.InvalidStreamLimit;            parameters.initial_max_streams_bidi = value;        },        0x09 => {            if (value > @as(u62, 1) << 60) return error.InvalidStreamLimit;            parameters.initial_max_streams_uni = value;        },        0x0a => {            if (value > 20) return error.InvalidAckDelayExponent;            parameters.ack_delay_exponent = @intCast(value);        },        0x0b => {            if (value >= @as(u62, 1) << 14) return error.InvalidMaxAckDelay;            parameters.max_ack_delay = @intCast(value);        },        0x0e => {            if (value < 2) return error.InvalidActiveConnectionIdLimit;            parameters.active_connection_id_limit = value;        },        0x20 => parameters.max_datagram_frame_size = value,        else => unreachable,    }}fn assignParameter(    parameters: *Parameters,    id: u62,    value: []const u8,) DecodeError!void {    switch (id) {        0x00 => parameters.original_destination_connection_id = try readConnectionId(value),        0x01, 0x03...0x0b, 0x0e, 0x20 => {            try assignInteger(parameters, id, try readInteger(value));        },        0x02 => {            if (value.len != 16) return error.InvalidStatelessResetToken;            var token: [16]u8 = undefined;            @memcpy(&token, value);            parameters.stateless_reset_token = token;        },        0x0c => {            if (value.len != 0) return error.InvalidDisableActiveMigration;            parameters.disable_active_migration = true;        },        0x0d => parameters.preferred_address = try readPreferredAddress(value),        0x0f => parameters.initial_source_connection_id = try readConnectionId(value),        0x10 => parameters.retry_source_connection_id = try readConnectionId(value),        else => {},    }}fn duplicate(seen: *const [parameter_count_max]u62, count: u8, id: u62) bool {    for (0..parameter_count_max) |index| {        if (index >= count) break;        if (seen[index] == id) return true;    }    return false;}pub fn decode(bytes: []const u8) DecodeError!Parameters {    var input = cursor.Read.init(bytes);    var seen: [parameter_count_max]u62 = undefined;    var count: u8 = 0;    var parameters = Parameters{};    for (0..parameter_count_max + 1) |_| {        if (input.remaining() == 0) return parameters;        if (count == parameter_count_max) return error.TooManyParameters;        const id = (try varint.read(&input)).value;        const length = (try varint.read(&input)).value;        const value = try input.take(try valueLength(length));        if (duplicate(&seen, count, id)) return error.DuplicateParameter;        seen[count] = id;        count += 1;        try assignParameter(&parameters, id, value);    }    unreachable;}fn writeTuple(id: u62, value: []const u8, output: *cursor.Write) EncodeError!void {    _ = try varint.write(id, output);    _ = try varint.write(try sliceLength(value.len), output);    try output.put(value);}fn writeInteger(id: u62, value: u62, output: *cursor.Write) EncodeError!void {    var bytes: [8]u8 = undefined;    const length = try varint.encode(value, &bytes);    try writeTuple(id, bytes[0..length], output);}fn writePreferredAddress(    value: PreferredAddress,    output: *cursor.Write,) EncodeError!void {    if (value.connection_id.length == 0) return error.InvalidPreferredAddress;    var bytes: [61]u8 = undefined;    var payload = cursor.Write.init(&bytes);    try payload.put(&value.ipv4);    try payload.int(u16, value.ipv4_port);    try payload.put(&value.ipv6);    try payload.int(u16, value.ipv6_port);    try payload.byte(value.connection_id.length);    try payload.put(value.connection_id.slice());    try payload.put(&value.reset_token);    try writeTuple(0x0d, payload.written(), output);}fn validate(parameters: Parameters) EncodeError!void {    if (parameters.max_udp_payload_size < 1200 or parameters.max_udp_payload_size > 65_527) {        return error.InvalidMaxUdpPayloadSize;    }    if (parameters.ack_delay_exponent > 20) return error.InvalidAckDelayExponent;    if (parameters.active_connection_id_limit < 2) {        return error.InvalidActiveConnectionIdLimit;    }    if (parameters.initial_max_streams_bidi > @as(u62, 1) << 60) {        return error.InvalidStreamLimit;    }    if (parameters.initial_max_streams_uni > @as(u62, 1) << 60) {        return error.InvalidStreamLimit;    }}pub fn encode(parameters: Parameters, output: *cursor.Write) EncodeError!void {    try validate(parameters);    if (parameters.original_destination_connection_id) |id| {        try writeTuple(0x00, id.slice(), output);    }    if (parameters.max_idle_timeout != 0) {        try writeInteger(0x01, parameters.max_idle_timeout, output);    }    if (parameters.stateless_reset_token) |token| try writeTuple(0x02, &token, output);    if (parameters.max_udp_payload_size != 65_527) {        try writeInteger(0x03, parameters.max_udp_payload_size, output);    }    try writeFlowControl(parameters, output);    if (parameters.ack_delay_exponent != 3) {        try writeInteger(0x0a, parameters.ack_delay_exponent, output);    }    if (parameters.max_ack_delay != 25) {        try writeInteger(0x0b, parameters.max_ack_delay, output);    }    if (parameters.disable_active_migration) try writeTuple(0x0c, &.{}, output);    if (parameters.preferred_address) |address| try writePreferredAddress(address, output);    if (parameters.active_connection_id_limit != 2) {        try writeInteger(0x0e, parameters.active_connection_id_limit, output);    }    if (parameters.initial_source_connection_id) |id| {        try writeTuple(0x0f, id.slice(), output);    }    if (parameters.retry_source_connection_id) |id| {        try writeTuple(0x10, id.slice(), output);    }    if (parameters.max_datagram_frame_size != 0) {        try writeInteger(0x20, parameters.max_datagram_frame_size, output);    }}fn writeFlowControl(parameters: Parameters, output: *cursor.Write) EncodeError!void {    const values = [_]u62{        parameters.initial_max_data,        parameters.initial_max_stream_data_bidi_local,        parameters.initial_max_stream_data_bidi_remote,        parameters.initial_max_stream_data_uni,        parameters.initial_max_streams_bidi,        parameters.initial_max_streams_uni,    };    for (values, 0x04..) |value, id| {        if (value != 0) try writeInteger(@intCast(id), value, output);    }}test "RFC 9000 section 18.2 and RFC 9221 section 3 parameters round trip" {    const source = try quic.packet.ConnectionId.init(&.{ 1, 2, 3, 4 });    const original = try quic.packet.ConnectionId.init(&.{ 5, 6, 7 });    const preferred_id = try quic.packet.ConnectionId.init(&.{ 8, 9 });    const parameters = Parameters{        .original_destination_connection_id = original,        .max_idle_timeout = 30_000,        .stateless_reset_token = @splat(0x11),        .max_udp_payload_size = 1400,        .initial_max_data = 1000,        .initial_max_stream_data_bidi_local = 2000,        .initial_max_stream_data_bidi_remote = 3000,        .initial_max_stream_data_uni = 4000,        .initial_max_streams_bidi = 10,        .initial_max_streams_uni = 11,        .ack_delay_exponent = 4,        .max_ack_delay = 26,        .disable_active_migration = true,        .preferred_address = .{            .ipv4 = .{ 192, 0, 2, 1 },            .ipv4_port = 443,            .ipv6 = .{ 0x20, 1, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },            .ipv6_port = 8443,            .connection_id = preferred_id,            .reset_token = @splat(0x22),        },        .active_connection_id_limit = 4,        .initial_source_connection_id = source,        .retry_source_connection_id = original,        .max_datagram_frame_size = 1200,    };    var bytes: [512]u8 = undefined;    var output = cursor.Write.init(&bytes);    try encode(parameters, &output);    const decoded = try decode(output.written());    try std.testing.expectEqualDeep(parameters, decoded);}test "RFC 9000 section 18.2 rejects invalid parameter values" {    var long_connection_id: [23]u8 = @splat(0xaa);    long_connection_id[0] = 0x0f;    long_connection_id[1] = 21;    const Case = struct { bytes: []const u8, expected: DecodeError };    const cases = [_]Case{        .{            .bytes = &.{ 0x03, 0x01, 0x25 },            .expected = error.InvalidMaxUdpPayloadSize,        },        .{            .bytes = &.{ 0x03, 0x04, 0x80, 0x00, 0xff, 0xf8 },            .expected = error.InvalidMaxUdpPayloadSize,        },        .{            .bytes = &.{ 0x0a, 0x01, 21 },            .expected = error.InvalidAckDelayExponent,        },        .{            .bytes = &.{ 0x0b, 0x04, 0x80, 0x00, 0x40, 0x00 },            .expected = error.InvalidMaxAckDelay,        },        .{            .bytes = &.{ 0x0e, 0x01, 1 },            .expected = error.InvalidActiveConnectionIdLimit,        },        .{            .bytes = &.{ 0x01, 0x02, 0x01, 0x00 },            .expected = error.InvalidInteger,        },        .{            .bytes = &.{ 0x01, 0x01, 0x40 },            .expected = error.InvalidInteger,        },        .{            .bytes = &long_connection_id,            .expected = error.InvalidConnectionId,        },        .{            .bytes = &.{ 0x0d, 0x01, 0x00 },            .expected = error.InvalidPreferredAddress,        },    };    for (cases) |case| try std.testing.expectError(case.expected, decode(case.bytes));}test "RFC 9000 section 18.2 max UDP payload size upper bound" {    const decoded = try decode(&.{ 0x03, 0x04, 0x80, 0x00, 0xff, 0xf7 });    try std.testing.expectEqual(@as(u62, 65_527), decoded.max_udp_payload_size);    var bytes: [16]u8 = undefined;    var output = cursor.Write.init(&bytes);    try encode(.{ .max_udp_payload_size = 65_527 }, &output);    try std.testing.expectEqualDeep(Parameters{}, try decode(output.written()));    output = cursor.Write.init(&bytes);    try std.testing.expectError(        error.InvalidMaxUdpPayloadSize,        encode(.{ .max_udp_payload_size = 65_528 }, &output),    );}test "RFC 9000 sections 7.4 and 18 reject duplicate parameters" {    try std.testing.expectError(        error.DuplicateParameter,        decode(&.{ 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 }),    );}test "RFC 9000 section 18.1 skips reserved and unknown parameters" {    const decoded = try decode(&.{ 27, 1, 0xaa, 0x21, 2, 0xbb, 0xcc });    try std.testing.expectEqualDeep(Parameters{}, decoded);}test "RFC 9000 section 18.2 parameter count maximum and maximum plus one" {    var bytes: [parameter_count_max * 3]u8 = undefined;    for (0..parameter_count_max) |index| {        bytes[index * 3] = 0x40;        bytes[index * 3 + 1] = @intCast(128 + index);        bytes[index * 3 + 2] = 0;    }    _ = try decode(&bytes);    var plus_one: [bytes.len + 3]u8 = undefined;    @memcpy(plus_one[0..bytes.len], &bytes);    plus_one[bytes.len] = 0x40;    plus_one[bytes.len + 1] = 0xc0;    plus_one[bytes.len + 2] = 0;    try std.testing.expectError(error.TooManyParameters, decode(&plus_one));}

Complete call list for transport.decode

7 direct calls.

Audit

Definitions8
Public names8
Members24
Version26.7.0
Revisiondaab053ee433