quic: transport parameter encoding and decoding
Transport parameters are passed in the extension_data field of the
quic_transport_parameters TLS extension.
RFC 9000, Section 18.
RFC 9001, Section 8.2.
For golang/go#58547
Change-Id: I294ab6cdef19256f5db02dc269e8b417b1d5e54b
Reviewed-on: https://go-review.googlesource.com/c/net/+/510575
Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Damien Neil <dneil@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/internal/quic/transport_params.go b/internal/quic/transport_params.go
new file mode 100644
index 0000000..416bfb8
--- /dev/null
+++ b/internal/quic/transport_params.go
@@ -0,0 +1,277 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package quic
+
+import (
+ "encoding/binary"
+ "net/netip"
+ "time"
+)
+
+// transportParameters transferred in the quic_transport_parameters TLS extension.
+// https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2
+type transportParameters struct {
+ originalDstConnID []byte
+ maxIdleTimeout time.Duration
+ statelessResetToken []byte
+ maxUDPPayloadSize int64
+ initialMaxData int64
+ initialMaxStreamDataBidiLocal int64
+ initialMaxStreamDataBidiRemote int64
+ initialMaxStreamDataUni int64
+ initialMaxStreamsBidi int64
+ initialMaxStreamsUni int64
+ ackDelayExponent uint8
+ maxAckDelay time.Duration
+ disableActiveMigration bool
+ preferredAddrV4 netip.AddrPort
+ preferredAddrV6 netip.AddrPort
+ preferredAddrConnID []byte
+ preferredAddrResetToken []byte
+ activeConnIDLimit int64
+ initialSrcConnID []byte
+ retrySrcConnID []byte
+}
+
+const (
+ defaultParamMaxUDPPayloadSize = 65527
+ defaultParamAckDelayExponent = 3
+ defaultParamMaxAckDelayMilliseconds = 25
+ defaultParamActiveConnIDLimit = 2
+)
+
+// defaultTransportParameters is initialized to the RFC 9000 default values.
+func defaultTransportParameters() transportParameters {
+ return transportParameters{
+ maxUDPPayloadSize: defaultParamMaxUDPPayloadSize,
+ ackDelayExponent: defaultParamAckDelayExponent,
+ maxAckDelay: defaultParamMaxAckDelayMilliseconds * time.Millisecond,
+ activeConnIDLimit: defaultParamActiveConnIDLimit,
+ }
+}
+
+const (
+ paramOriginalDestinationConnectionID = 0x00
+ paramMaxIdleTimeout = 0x01
+ paramStatelessResetToken = 0x02
+ paramMaxUDPPayloadSize = 0x03
+ paramInitialMaxData = 0x04
+ paramInitialMaxStreamDataBidiLocal = 0x05
+ paramInitialMaxStreamDataBidiRemote = 0x06
+ paramInitialMaxStreamDataUni = 0x07
+ paramInitialMaxStreamsBidi = 0x08
+ paramInitialMaxStreamsUni = 0x09
+ paramAckDelayExponent = 0x0a
+ paramMaxAckDelay = 0x0b
+ paramDisableActiveMigration = 0x0c
+ paramPreferredAddress = 0x0d
+ paramActiveConnectionIDLimit = 0x0e
+ paramInitialSourceConnectionID = 0x0f
+ paramRetrySourceConnectionID = 0x10
+)
+
+func marshalTransportParameters(p transportParameters) []byte {
+ var b []byte
+ if v := p.originalDstConnID; v != nil {
+ b = appendVarint(b, paramOriginalDestinationConnectionID)
+ b = appendVarintBytes(b, v)
+ }
+ if v := uint64(p.maxIdleTimeout / time.Millisecond); v != 0 {
+ b = appendVarint(b, paramMaxIdleTimeout)
+ b = appendVarint(b, uint64(sizeVarint(v)))
+ b = appendVarint(b, uint64(v))
+ }
+ if v := p.statelessResetToken; v != nil {
+ b = appendVarint(b, paramStatelessResetToken)
+ b = appendVarintBytes(b, v)
+ }
+ if v := p.maxUDPPayloadSize; v != defaultParamMaxUDPPayloadSize {
+ b = appendVarint(b, paramMaxUDPPayloadSize)
+ b = appendVarint(b, uint64(sizeVarint(uint64(v))))
+ b = appendVarint(b, uint64(v))
+ }
+ if v := p.initialMaxData; v != 0 {
+ b = appendVarint(b, paramInitialMaxData)
+ b = appendVarint(b, uint64(sizeVarint(uint64(v))))
+ b = appendVarint(b, uint64(v))
+ }
+ if v := p.initialMaxStreamDataBidiLocal; v != 0 {
+ b = appendVarint(b, paramInitialMaxStreamDataBidiLocal)
+ b = appendVarint(b, uint64(sizeVarint(uint64(v))))
+ b = appendVarint(b, uint64(v))
+ }
+ if v := p.initialMaxStreamDataBidiRemote; v != 0 {
+ b = appendVarint(b, paramInitialMaxStreamDataBidiRemote)
+ b = appendVarint(b, uint64(sizeVarint(uint64(v))))
+ b = appendVarint(b, uint64(v))
+ }
+ if v := p.initialMaxStreamDataUni; v != 0 {
+ b = appendVarint(b, paramInitialMaxStreamDataUni)
+ b = appendVarint(b, uint64(sizeVarint(uint64(v))))
+ b = appendVarint(b, uint64(v))
+ }
+ if v := p.initialMaxStreamsBidi; v != 0 {
+ b = appendVarint(b, paramInitialMaxStreamsBidi)
+ b = appendVarint(b, uint64(sizeVarint(uint64(v))))
+ b = appendVarint(b, uint64(v))
+ }
+ if v := p.initialMaxStreamsUni; v != 0 {
+ b = appendVarint(b, paramInitialMaxStreamsUni)
+ b = appendVarint(b, uint64(sizeVarint(uint64(v))))
+ b = appendVarint(b, uint64(v))
+ }
+ if v := p.ackDelayExponent; v != defaultParamAckDelayExponent {
+ b = appendVarint(b, paramAckDelayExponent)
+ b = appendVarint(b, uint64(sizeVarint(uint64(v))))
+ b = appendVarint(b, uint64(v))
+ }
+ if v := uint64(p.maxAckDelay / time.Millisecond); v != defaultParamMaxAckDelayMilliseconds {
+ b = appendVarint(b, paramMaxAckDelay)
+ b = appendVarint(b, uint64(sizeVarint(v)))
+ b = appendVarint(b, v)
+ }
+ if p.disableActiveMigration {
+ b = appendVarint(b, paramDisableActiveMigration)
+ b = append(b, 0) // 0-length value
+ }
+ if p.preferredAddrConnID != nil {
+ b = append(b, paramPreferredAddress)
+ b = appendVarint(b, uint64(4+2+16+2+1+len(p.preferredAddrConnID)+16))
+ b = append(b, p.preferredAddrV4.Addr().AsSlice()...) // 4 bytes
+ b = binary.BigEndian.AppendUint16(b, p.preferredAddrV4.Port()) // 2 bytes
+ b = append(b, p.preferredAddrV6.Addr().AsSlice()...) // 16 bytes
+ b = binary.BigEndian.AppendUint16(b, p.preferredAddrV6.Port()) // 2 bytes
+ b = appendUint8Bytes(b, p.preferredAddrConnID) // 1 byte + len(conn_id)
+ b = append(b, p.preferredAddrResetToken...) // 16 bytes
+ }
+ if v := p.activeConnIDLimit; v != defaultParamActiveConnIDLimit {
+ b = appendVarint(b, paramActiveConnectionIDLimit)
+ b = appendVarint(b, uint64(sizeVarint(uint64(v))))
+ b = appendVarint(b, uint64(v))
+ }
+ if v := p.initialSrcConnID; v != nil {
+ b = appendVarint(b, paramInitialSourceConnectionID)
+ b = appendVarintBytes(b, v)
+ }
+ if v := p.retrySrcConnID; v != nil {
+ b = appendVarint(b, paramRetrySourceConnectionID)
+ b = appendVarintBytes(b, v)
+ }
+ return b
+}
+
+func unmarshalTransportParams(params []byte) (transportParameters, error) {
+ p := defaultTransportParameters()
+ for len(params) > 0 {
+ id, n := consumeVarint(params)
+ if n < 0 {
+ return p, localTransportError(errTransportParameter)
+ }
+ params = params[n:]
+ val, n := consumeVarintBytes(params)
+ if n < 0 {
+ return p, localTransportError(errTransportParameter)
+ }
+ params = params[n:]
+ n = 0
+ switch id {
+ case paramOriginalDestinationConnectionID:
+ p.originalDstConnID = val
+ n = len(val)
+ case paramMaxIdleTimeout:
+ var v uint64
+ v, n = consumeVarint(val)
+ // If this is unreasonably large, consider it as no timeout to avoid
+ // time.Duration overflows.
+ if v > 1<<32 {
+ v = 0
+ }
+ p.maxIdleTimeout = time.Duration(v) * time.Millisecond
+ case paramStatelessResetToken:
+ if len(val) != 16 {
+ return p, localTransportError(errTransportParameter)
+ }
+ p.statelessResetToken = val
+ n = 16
+ case paramMaxUDPPayloadSize:
+ p.maxUDPPayloadSize, n = consumeVarintInt64(val)
+ if p.maxUDPPayloadSize < 1200 {
+ return p, localTransportError(errTransportParameter)
+ }
+ case paramInitialMaxData:
+ p.initialMaxData, n = consumeVarintInt64(val)
+ case paramInitialMaxStreamDataBidiLocal:
+ p.initialMaxStreamDataBidiLocal, n = consumeVarintInt64(val)
+ case paramInitialMaxStreamDataBidiRemote:
+ p.initialMaxStreamDataBidiRemote, n = consumeVarintInt64(val)
+ case paramInitialMaxStreamDataUni:
+ p.initialMaxStreamDataUni, n = consumeVarintInt64(val)
+ case paramInitialMaxStreamsBidi:
+ p.initialMaxStreamsBidi, n = consumeVarintInt64(val)
+ case paramInitialMaxStreamsUni:
+ p.initialMaxStreamsUni, n = consumeVarintInt64(val)
+ case paramAckDelayExponent:
+ var v uint64
+ v, n = consumeVarint(val)
+ if v > 20 {
+ return p, localTransportError(errTransportParameter)
+ }
+ p.ackDelayExponent = uint8(v)
+ case paramMaxAckDelay:
+ var v uint64
+ v, n = consumeVarint(val)
+ if v >= 1<<14 {
+ return p, localTransportError(errTransportParameter)
+ }
+ p.maxAckDelay = time.Duration(v) * time.Millisecond
+ case paramDisableActiveMigration:
+ p.disableActiveMigration = true
+ case paramPreferredAddress:
+ if len(val) < 4+2+16+2+1 {
+ return p, localTransportError(errTransportParameter)
+ }
+ p.preferredAddrV4 = netip.AddrPortFrom(
+ netip.AddrFrom4(*(*[4]byte)(val[:4])),
+ binary.BigEndian.Uint16(val[4:][:2]),
+ )
+ val = val[4+2:]
+ p.preferredAddrV6 = netip.AddrPortFrom(
+ netip.AddrFrom16(*(*[16]byte)(val[:16])),
+ binary.BigEndian.Uint16(val[16:][:2]),
+ )
+ val = val[16+2:]
+ var nn int
+ p.preferredAddrConnID, nn = consumeUint8Bytes(val)
+ if nn < 0 {
+ return p, localTransportError(errTransportParameter)
+ }
+ val = val[nn:]
+ if len(val) != 16 {
+ return p, localTransportError(errTransportParameter)
+ }
+ p.preferredAddrResetToken = val
+ val = nil
+ case paramActiveConnectionIDLimit:
+ p.activeConnIDLimit, n = consumeVarintInt64(val)
+ if p.activeConnIDLimit < 2 {
+ return p, localTransportError(errTransportParameter)
+ }
+ case paramInitialSourceConnectionID:
+ p.initialSrcConnID = val
+ n = len(val)
+ case paramRetrySourceConnectionID:
+ p.retrySrcConnID = val
+ n = len(val)
+ default:
+ n = len(val)
+ }
+ if n != len(val) {
+ return p, localTransportError(errTransportParameter)
+ }
+ }
+ return p, nil
+}
diff --git a/internal/quic/transport_params_test.go b/internal/quic/transport_params_test.go
new file mode 100644
index 0000000..e1c45ca
--- /dev/null
+++ b/internal/quic/transport_params_test.go
@@ -0,0 +1,374 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.21
+
+package quic
+
+import (
+ "bytes"
+ "math"
+ "net/netip"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestTransportParametersMarshalUnmarshal(t *testing.T) {
+ for _, test := range []struct {
+ params func(p *transportParameters)
+ enc []byte
+ }{{
+ params: func(p *transportParameters) {
+ p.originalDstConnID = []byte("connid")
+ },
+ enc: []byte{
+ 0x00, // original_destination_connection_id
+ byte(len("connid")),
+ 'c', 'o', 'n', 'n', 'i', 'd',
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.maxIdleTimeout = 10 * time.Millisecond
+ },
+ enc: []byte{
+ 0x01, // max_idle_timeout
+ 1, // length
+ 10, // varint msecs
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.statelessResetToken = []byte("0123456789abcdef")
+ },
+ enc: []byte{
+ 0x02, // stateless_reset_token
+ 16, // length
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', // reset token
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.maxUDPPayloadSize = 1200
+ },
+ enc: []byte{
+ 0x03, // max_udp_payload_size
+ 2, // length
+ 0x44, 0xb0, // varint value
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.initialMaxData = 10
+ },
+ enc: []byte{
+ 0x04, // initial_max_data
+ 1, // length
+ 10, // varint value
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.initialMaxStreamDataBidiLocal = 10
+ },
+ enc: []byte{
+ 0x05, // initial_max_stream_data_bidi_local
+ 1, // length
+ 10, // varint value
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.initialMaxStreamDataBidiRemote = 10
+ },
+ enc: []byte{
+ 0x06, // initial_max_stream_data_bidi_remote
+ 1, // length
+ 10, // varint value
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.initialMaxStreamDataUni = 10
+ },
+ enc: []byte{
+ 0x07, // initial_max_stream_data_uni
+ 1, // length
+ 10, // varint value
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.initialMaxStreamsBidi = 10
+ },
+ enc: []byte{
+ 0x08, // initial_max_streams_bidi
+ 1, // length
+ 10, // varint value
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.initialMaxStreamsUni = 10
+ },
+ enc: []byte{
+ 0x09, // initial_max_streams_uni
+ 1, // length
+ 10, // varint value
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.ackDelayExponent = 4
+ },
+ enc: []byte{
+ 0x0a, // ack_delay_exponent
+ 1, // length
+ 4, // varint value
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.maxAckDelay = 10 * time.Millisecond
+ },
+ enc: []byte{
+ 0x0b, // max_ack_delay
+ 1, // length
+ 10, // varint value
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.disableActiveMigration = true
+ },
+ enc: []byte{
+ 0x0c, // disable_active_migration
+ 0, // length
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.preferredAddrV4 = netip.MustParseAddrPort("127.0.0.1:80")
+ p.preferredAddrV6 = netip.MustParseAddrPort("[fe80::1]:1024")
+ p.preferredAddrConnID = []byte("connid")
+ p.preferredAddrResetToken = []byte("0123456789abcdef")
+ },
+ enc: []byte{
+ 0x0d, // preferred_address
+ byte(4 + 2 + 16 + 2 + 1 + len("connid") + 16), // length
+ 127, 0, 0, 1, // v4 address
+ 0, 80, // v4 port
+ 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // v6 address
+ 0x04, 0x00, // v6 port,
+ 6, // connection id length
+ 'c', 'o', 'n', 'n', 'i', 'd', // connection id
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', // reset token
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.activeConnIDLimit = 10
+ },
+ enc: []byte{
+ 0x0e, // active_connection_id_limit
+ 1, // length
+ 10, // varint value
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.initialSrcConnID = []byte("connid")
+ },
+ enc: []byte{
+ 0x0f, // initial_source_connection_id
+ byte(len("connid")),
+ 'c', 'o', 'n', 'n', 'i', 'd',
+ },
+ }, {
+ params: func(p *transportParameters) {
+ p.retrySrcConnID = []byte("connid")
+ },
+ enc: []byte{
+ 0x10, // retry_source_connection_id
+ byte(len("connid")),
+ 'c', 'o', 'n', 'n', 'i', 'd',
+ },
+ }} {
+ wantParams := defaultTransportParameters()
+ test.params(&wantParams)
+ gotBytes := marshalTransportParameters(wantParams)
+ if !bytes.Equal(gotBytes, test.enc) {
+ t.Errorf("marshalTransportParameters(%#v):\n got: %x\nwant: %x", wantParams, gotBytes, test.enc)
+ }
+ gotParams, err := unmarshalTransportParams(test.enc)
+ if err != nil {
+ t.Errorf("unmarshalTransportParams(%x): unexpected error: %v", test.enc, err)
+ } else if !reflect.DeepEqual(gotParams, wantParams) {
+ t.Errorf("unmarshalTransportParams(%x):\n got: %#v\nwant: %#v", test.enc, gotParams, wantParams)
+ }
+ }
+}
+
+func TestTransportParametersErrors(t *testing.T) {
+ for _, test := range []struct {
+ desc string
+ enc []byte
+ }{{
+ desc: "invalid id",
+ enc: []byte{
+ 0x40, // too short
+ },
+ }, {
+ desc: "parameter too short",
+ enc: []byte{
+ 0x00, // original_destination_connection_id
+ 0x04, // length
+ 1, 2, 3, // not enough data
+ },
+ }, {
+ desc: "extra data in parameter",
+ enc: []byte{
+ 0x01, // max_idle_timeout
+ 2, // length
+ 10, // varint msecs
+ 0, // extra junk
+ },
+ }, {
+ desc: "invalid varint in parameter",
+ enc: []byte{
+ 0x01, // max_idle_timeout
+ 1, // length
+ 0x40, // incomplete varint
+ },
+ }, {
+ desc: "stateless_reset_token not 16 bytes",
+ enc: []byte{
+ 0x02, // stateless_reset_token,
+ 15, // length
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ },
+ }, {
+ desc: "preferred_address is too short",
+ enc: []byte{
+ 0x0d, // preferred_address
+ byte(3),
+ 127, 0, 0,
+ },
+ }, {
+ desc: "preferred_address reset token too short",
+ enc: []byte{
+ 0x0d, // preferred_address
+ byte(4 + 2 + 16 + 2 + 1 + len("connid") + 15), // length
+ 127, 0, 0, 1, // v4 address
+ 0, 80, // v4 port
+ 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // v6 address
+ 0x04, 0x00, // v6 port,
+ 6, // connection id length
+ 'c', 'o', 'n', 'n', 'i', 'd', // connection id
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', // reset token, one byte too short
+
+ },
+ }, {
+ desc: "preferred_address conn id too long",
+ enc: []byte{
+ 0x0d, // preferred_address
+ byte(4 + 2 + 16 + 2 + 1 + len("connid") + 16), // length
+ 127, 0, 0, 1, // v4 address
+ 0, 80, // v4 port
+ 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // v6 address
+ 0x04, 0x00, // v6 port,
+ byte(len("connid")) + 16 + 1, // connection id length, too long
+ 'c', 'o', 'n', 'n', 'i', 'd', // connection id
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', // reset token
+
+ },
+ }} {
+ _, err := unmarshalTransportParams(test.enc)
+ if err == nil {
+ t.Errorf("%v:\nunmarshalTransportParams(%x): unexpectedly succeeded", test.desc, test.enc)
+ }
+ }
+}
+
+func TestTransportParametersRangeErrors(t *testing.T) {
+ for _, test := range []struct {
+ desc string
+ params func(p *transportParameters)
+ }{{
+ desc: "max_udp_payload_size < 1200",
+ params: func(p *transportParameters) {
+ p.maxUDPPayloadSize = 1199
+ },
+ }, {
+ desc: "ack_delay_exponent > 20",
+ params: func(p *transportParameters) {
+ p.ackDelayExponent = 21
+ },
+ }, {
+ desc: "max_ack_delay > 1^14 ms",
+ params: func(p *transportParameters) {
+ p.maxAckDelay = (1 << 14) * time.Millisecond
+ },
+ }, {
+ desc: "active_connection_id_limit < 2",
+ params: func(p *transportParameters) {
+ p.activeConnIDLimit = 1
+ },
+ }} {
+ p := defaultTransportParameters()
+ test.params(&p)
+ enc := marshalTransportParameters(p)
+ _, err := unmarshalTransportParams(enc)
+ if err == nil {
+ t.Errorf("%v: unmarshalTransportParams unexpectedly succeeded", test.desc)
+ }
+ }
+}
+
+func TestTransportParameterMaxIdleTimeoutOverflowsDuration(t *testing.T) {
+ tooManyMS := 1 + (math.MaxInt64 / uint64(time.Millisecond))
+
+ var enc []byte
+ enc = appendVarint(enc, paramMaxIdleTimeout)
+ enc = appendVarint(enc, uint64(sizeVarint(tooManyMS)))
+ enc = appendVarint(enc, uint64(tooManyMS))
+
+ dec, err := unmarshalTransportParams(enc)
+ if err != nil {
+ t.Fatalf("unmarshalTransportParameters(enc) = %v", err)
+ }
+ if got, want := dec.maxIdleTimeout, time.Duration(0); got != want {
+ t.Errorf("max_idle_timeout=%v, got maxIdleTimeout=%v; want %v", tooManyMS, got, want)
+ }
+}
+
+func TestTransportParametersSkipUnknownParameters(t *testing.T) {
+ enc := []byte{
+ 0x20, // unknown transport parameter
+ 1, // length
+ 0, // varint value
+
+ 0x04, // initial_max_data
+ 1, // length
+ 10, // varint value
+
+ 0x21, // unknown transport parameter
+ 1, // length
+ 0, // varint value
+ }
+ dec, err := unmarshalTransportParams(enc)
+ if err != nil {
+ t.Fatalf("unmarshalTransportParameters(enc) = %v", err)
+ }
+ if got, want := dec.initialMaxData, int64(10); got != want {
+ t.Errorf("got initial_max_data=%v; want %v", got, want)
+ }
+}
+
+func FuzzTransportParametersMarshalUnmarshal(f *testing.F) {
+ f.Fuzz(func(t *testing.T, in []byte) {
+ p1, err := unmarshalTransportParams(in)
+ if err != nil {
+ return
+ }
+ out := marshalTransportParameters(p1)
+ p2, err := unmarshalTransportParams(out)
+ if err != nil {
+ t.Fatalf("round trip unmarshal/remarshal: unmarshal error: %v\n%x", err, in)
+ }
+ if !reflect.DeepEqual(p1, p2) {
+ t.Fatalf("round trip unmarshal/remarshal: parameters differ:\n%x\n%#v\n%#v", in, p1, p2)
+ }
+ })
+}