github.com/quic-go/quic-go@v0.44.0/internal/wire/transport_parameter_test.go (about)

     1  package wire
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math"
     7  	"net/netip"
     8  	"testing"
     9  	"time"
    10  
    11  	"golang.org/x/exp/rand"
    12  
    13  	"github.com/quic-go/quic-go/internal/protocol"
    14  	"github.com/quic-go/quic-go/internal/qerr"
    15  	"github.com/quic-go/quic-go/quicvarint"
    16  
    17  	. "github.com/onsi/ginkgo/v2"
    18  	. "github.com/onsi/gomega"
    19  )
    20  
    21  func getRandomValueUpTo(max int64) uint64 {
    22  	maxVals := []int64{math.MaxUint8 / 4, math.MaxUint16 / 4, math.MaxUint32 / 4, math.MaxUint64 / 4}
    23  	m := maxVals[int(rand.Int31n(4))]
    24  	if m > max {
    25  		m = max
    26  	}
    27  	return uint64(rand.Int63n(m))
    28  }
    29  
    30  func getRandomValue() uint64 {
    31  	return getRandomValueUpTo(quicvarint.Max)
    32  }
    33  
    34  var _ = Describe("Transport Parameters", func() {
    35  	BeforeEach(func() {
    36  		rand.Seed(uint64(GinkgoRandomSeed()))
    37  	})
    38  
    39  	appendInitialSourceConnectionID := func(b []byte) []byte {
    40  		b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID))
    41  		b = quicvarint.Append(b, 6)
    42  		return append(b, []byte("foobar")...)
    43  	}
    44  
    45  	It("has a string representation", func() {
    46  		rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde})
    47  		p := &TransportParameters{
    48  			InitialMaxStreamDataBidiLocal:   1234,
    49  			InitialMaxStreamDataBidiRemote:  2345,
    50  			InitialMaxStreamDataUni:         3456,
    51  			InitialMaxData:                  4567,
    52  			MaxBidiStreamNum:                1337,
    53  			MaxUniStreamNum:                 7331,
    54  			MaxIdleTimeout:                  42 * time.Second,
    55  			OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
    56  			InitialSourceConnectionID:       protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}),
    57  			RetrySourceConnectionID:         &rcid,
    58  			AckDelayExponent:                14,
    59  			MaxAckDelay:                     37 * time.Millisecond,
    60  			StatelessResetToken:             &protocol.StatelessResetToken{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00},
    61  			ActiveConnectionIDLimit:         123,
    62  			MaxDatagramFrameSize:            876,
    63  		}
    64  		Expect(p.String()).To(Equal("&wire.TransportParameters{OriginalDestinationConnectionID: deadbeef, InitialSourceConnectionID: decafbad, RetrySourceConnectionID: deadc0de, InitialMaxStreamDataBidiLocal: 1234, InitialMaxStreamDataBidiRemote: 2345, InitialMaxStreamDataUni: 3456, InitialMaxData: 4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42s, AckDelayExponent: 14, MaxAckDelay: 37ms, ActiveConnectionIDLimit: 123, StatelessResetToken: 0x112233445566778899aabbccddeeff00, MaxDatagramFrameSize: 876}"))
    65  	})
    66  
    67  	It("has a string representation, if there's no stateless reset token, no Retry source connection id and no datagram support", func() {
    68  		p := &TransportParameters{
    69  			InitialMaxStreamDataBidiLocal:   1234,
    70  			InitialMaxStreamDataBidiRemote:  2345,
    71  			InitialMaxStreamDataUni:         3456,
    72  			InitialMaxData:                  4567,
    73  			MaxBidiStreamNum:                1337,
    74  			MaxUniStreamNum:                 7331,
    75  			MaxIdleTimeout:                  42 * time.Second,
    76  			OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
    77  			InitialSourceConnectionID:       protocol.ParseConnectionID([]byte{}),
    78  			AckDelayExponent:                14,
    79  			MaxAckDelay:                     37 * time.Second,
    80  			ActiveConnectionIDLimit:         89,
    81  			MaxDatagramFrameSize:            protocol.InvalidByteCount,
    82  		}
    83  		Expect(p.String()).To(Equal("&wire.TransportParameters{OriginalDestinationConnectionID: deadbeef, InitialSourceConnectionID: (empty), InitialMaxStreamDataBidiLocal: 1234, InitialMaxStreamDataBidiRemote: 2345, InitialMaxStreamDataUni: 3456, InitialMaxData: 4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42s, AckDelayExponent: 14, MaxAckDelay: 37s, ActiveConnectionIDLimit: 89}"))
    84  	})
    85  
    86  	It("marshals and unmarshals", func() {
    87  		var token protocol.StatelessResetToken
    88  		rand.Read(token[:])
    89  		rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde})
    90  		params := &TransportParameters{
    91  			InitialMaxStreamDataBidiLocal:   protocol.ByteCount(getRandomValue()),
    92  			InitialMaxStreamDataBidiRemote:  protocol.ByteCount(getRandomValue()),
    93  			InitialMaxStreamDataUni:         protocol.ByteCount(getRandomValue()),
    94  			InitialMaxData:                  protocol.ByteCount(getRandomValue()),
    95  			MaxIdleTimeout:                  0xcafe * time.Second,
    96  			MaxBidiStreamNum:                protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))),
    97  			MaxUniStreamNum:                 protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))),
    98  			DisableActiveMigration:          true,
    99  			StatelessResetToken:             &token,
   100  			OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
   101  			InitialSourceConnectionID:       protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}),
   102  			RetrySourceConnectionID:         &rcid,
   103  			AckDelayExponent:                13,
   104  			MaxAckDelay:                     42 * time.Millisecond,
   105  			ActiveConnectionIDLimit:         2 + getRandomValueUpTo(quicvarint.Max-2),
   106  			MaxUDPPayloadSize:               1200 + protocol.ByteCount(getRandomValueUpTo(quicvarint.Max-1200)),
   107  			MaxDatagramFrameSize:            protocol.ByteCount(getRandomValue()),
   108  		}
   109  		data := params.Marshal(protocol.PerspectiveServer)
   110  
   111  		p := &TransportParameters{}
   112  		Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed())
   113  		Expect(p.InitialMaxStreamDataBidiLocal).To(Equal(params.InitialMaxStreamDataBidiLocal))
   114  		Expect(p.InitialMaxStreamDataBidiRemote).To(Equal(params.InitialMaxStreamDataBidiRemote))
   115  		Expect(p.InitialMaxStreamDataUni).To(Equal(params.InitialMaxStreamDataUni))
   116  		Expect(p.InitialMaxData).To(Equal(params.InitialMaxData))
   117  		Expect(p.MaxUniStreamNum).To(Equal(params.MaxUniStreamNum))
   118  		Expect(p.MaxBidiStreamNum).To(Equal(params.MaxBidiStreamNum))
   119  		Expect(p.MaxIdleTimeout).To(Equal(params.MaxIdleTimeout))
   120  		Expect(p.DisableActiveMigration).To(Equal(params.DisableActiveMigration))
   121  		Expect(p.StatelessResetToken).To(Equal(params.StatelessResetToken))
   122  		Expect(p.OriginalDestinationConnectionID).To(Equal(protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef})))
   123  		Expect(p.InitialSourceConnectionID).To(Equal(protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad})))
   124  		Expect(p.RetrySourceConnectionID).To(Equal(&rcid))
   125  		Expect(p.AckDelayExponent).To(Equal(uint8(13)))
   126  		Expect(p.MaxAckDelay).To(Equal(42 * time.Millisecond))
   127  		Expect(p.ActiveConnectionIDLimit).To(Equal(params.ActiveConnectionIDLimit))
   128  		Expect(p.MaxUDPPayloadSize).To(Equal(params.MaxUDPPayloadSize))
   129  		Expect(p.MaxDatagramFrameSize).To(Equal(params.MaxDatagramFrameSize))
   130  	})
   131  
   132  	It("marshals additional transport parameters (used for testing large ClientHellos)", func() {
   133  		origAdditionalTransportParametersClient := AdditionalTransportParametersClient
   134  		defer func() {
   135  			AdditionalTransportParametersClient = origAdditionalTransportParametersClient
   136  		}()
   137  		AdditionalTransportParametersClient = map[uint64][]byte{1337: []byte("foobar")}
   138  
   139  		result := quicvarint.Append([]byte{}, 1337)
   140  		result = quicvarint.Append(result, 6)
   141  		result = append(result, []byte("foobar")...)
   142  
   143  		params := &TransportParameters{}
   144  		Expect(bytes.Contains(params.Marshal(protocol.PerspectiveClient), result)).To(BeTrue())
   145  		Expect(bytes.Contains(params.Marshal(protocol.PerspectiveServer), result)).To(BeFalse())
   146  	})
   147  
   148  	It("doesn't marshal a retry_source_connection_id, if no Retry was performed", func() {
   149  		data := (&TransportParameters{
   150  			StatelessResetToken:     &protocol.StatelessResetToken{},
   151  			ActiveConnectionIDLimit: 2,
   152  		}).Marshal(protocol.PerspectiveServer)
   153  		p := &TransportParameters{}
   154  		Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed())
   155  		Expect(p.RetrySourceConnectionID).To(BeNil())
   156  	})
   157  
   158  	It("marshals a zero-length retry_source_connection_id", func() {
   159  		rcid := protocol.ParseConnectionID([]byte{})
   160  		data := (&TransportParameters{
   161  			RetrySourceConnectionID: &rcid,
   162  			StatelessResetToken:     &protocol.StatelessResetToken{},
   163  			ActiveConnectionIDLimit: 2,
   164  		}).Marshal(protocol.PerspectiveServer)
   165  		p := &TransportParameters{}
   166  		Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed())
   167  		Expect(p.RetrySourceConnectionID).ToNot(BeNil())
   168  		Expect(p.RetrySourceConnectionID.Len()).To(BeZero())
   169  	})
   170  
   171  	It("errors when the stateless_reset_token has the wrong length", func() {
   172  		b := quicvarint.Append(nil, uint64(statelessResetTokenParameterID))
   173  		b = quicvarint.Append(b, 15)
   174  		b = append(b, make([]byte, 15)...)
   175  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   176  			ErrorCode:    qerr.TransportParameterError,
   177  			ErrorMessage: "wrong length for stateless_reset_token: 15 (expected 16)",
   178  		}))
   179  	})
   180  
   181  	It("errors when the max_udp_payload_size is too small", func() {
   182  		b := quicvarint.Append(nil, uint64(maxUDPPayloadSizeParameterID))
   183  		b = quicvarint.Append(b, uint64(quicvarint.Len(1199)))
   184  		b = quicvarint.Append(b, 1199)
   185  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   186  			ErrorCode:    qerr.TransportParameterError,
   187  			ErrorMessage: "invalid value for max_udp_payload_size: 1199 (minimum 1200)",
   188  		}))
   189  	})
   190  
   191  	It("errors when disable_active_migration has content", func() {
   192  		b := quicvarint.Append(nil, uint64(disableActiveMigrationParameterID))
   193  		b = quicvarint.Append(b, 6)
   194  		b = append(b, []byte("foobar")...)
   195  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   196  			ErrorCode:    qerr.TransportParameterError,
   197  			ErrorMessage: "wrong length for disable_active_migration: 6 (expected empty)",
   198  		}))
   199  	})
   200  
   201  	It("errors when the server doesn't set the original_destination_connection_id", func() {
   202  		b := quicvarint.Append(nil, uint64(statelessResetTokenParameterID))
   203  		b = quicvarint.Append(b, 16)
   204  		b = append(b, make([]byte, 16)...)
   205  		b = appendInitialSourceConnectionID(b)
   206  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   207  			ErrorCode:    qerr.TransportParameterError,
   208  			ErrorMessage: "missing original_destination_connection_id",
   209  		}))
   210  	})
   211  
   212  	It("errors when the initial_source_connection_id is missing", func() {
   213  		Expect((&TransportParameters{}).Unmarshal([]byte{}, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{
   214  			ErrorCode:    qerr.TransportParameterError,
   215  			ErrorMessage: "missing initial_source_connection_id",
   216  		}))
   217  	})
   218  
   219  	It("errors when the max_ack_delay is too large", func() {
   220  		data := (&TransportParameters{
   221  			MaxAckDelay:         1 << 14 * time.Millisecond,
   222  			StatelessResetToken: &protocol.StatelessResetToken{},
   223  		}).Marshal(protocol.PerspectiveServer)
   224  		p := &TransportParameters{}
   225  		Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   226  			ErrorCode:    qerr.TransportParameterError,
   227  			ErrorMessage: "invalid value for max_ack_delay: 16384ms (maximum 16383ms)",
   228  		}))
   229  	})
   230  
   231  	It("doesn't send the max_ack_delay, if it has the default value", func() {
   232  		const num = 1000
   233  		var defaultLen, dataLen int
   234  		// marshal 1000 times to average out the greasing transport parameter
   235  		maxAckDelay := protocol.DefaultMaxAckDelay + time.Millisecond
   236  		for i := 0; i < num; i++ {
   237  			dataDefault := (&TransportParameters{
   238  				MaxAckDelay:         protocol.DefaultMaxAckDelay,
   239  				StatelessResetToken: &protocol.StatelessResetToken{},
   240  			}).Marshal(protocol.PerspectiveServer)
   241  			defaultLen += len(dataDefault)
   242  			data := (&TransportParameters{
   243  				MaxAckDelay:         maxAckDelay,
   244  				StatelessResetToken: &protocol.StatelessResetToken{},
   245  			}).Marshal(protocol.PerspectiveServer)
   246  			dataLen += len(data)
   247  		}
   248  		entryLen := quicvarint.Len(uint64(ackDelayExponentParameterID)) /* parameter id */ + quicvarint.Len(uint64(quicvarint.Len(uint64(maxAckDelay.Milliseconds())))) /*length */ + quicvarint.Len(uint64(maxAckDelay.Milliseconds())) /* value */
   249  		Expect(float32(dataLen) / num).To(BeNumerically("~", float32(defaultLen)/num+float32(entryLen), 1))
   250  	})
   251  
   252  	It("errors when the active_connection_id_limit is too small", func() {
   253  		data := (&TransportParameters{
   254  			ActiveConnectionIDLimit: 1,
   255  			StatelessResetToken:     &protocol.StatelessResetToken{},
   256  		}).Marshal(protocol.PerspectiveServer)
   257  		p := &TransportParameters{}
   258  		Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   259  			ErrorCode:    qerr.TransportParameterError,
   260  			ErrorMessage: "invalid value for active_connection_id_limit: 1 (minimum 2)",
   261  		}))
   262  	})
   263  
   264  	It("errors when the ack_delay_exponenent is too large", func() {
   265  		data := (&TransportParameters{
   266  			AckDelayExponent:    21,
   267  			StatelessResetToken: &protocol.StatelessResetToken{},
   268  		}).Marshal(protocol.PerspectiveServer)
   269  		p := &TransportParameters{}
   270  		Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   271  			ErrorCode:    qerr.TransportParameterError,
   272  			ErrorMessage: "invalid value for ack_delay_exponent: 21 (maximum 20)",
   273  		}))
   274  	})
   275  
   276  	It("doesn't send the ack_delay_exponent, if it has the default value", func() {
   277  		const num = 1000
   278  		var defaultLen, dataLen int
   279  		// marshal 1000 times to average out the greasing transport parameter
   280  		for i := 0; i < num; i++ {
   281  			dataDefault := (&TransportParameters{
   282  				AckDelayExponent:    protocol.DefaultAckDelayExponent,
   283  				StatelessResetToken: &protocol.StatelessResetToken{},
   284  			}).Marshal(protocol.PerspectiveServer)
   285  			defaultLen += len(dataDefault)
   286  			data := (&TransportParameters{
   287  				AckDelayExponent:    protocol.DefaultAckDelayExponent + 1,
   288  				StatelessResetToken: &protocol.StatelessResetToken{},
   289  			}).Marshal(protocol.PerspectiveServer)
   290  			dataLen += len(data)
   291  		}
   292  		entryLen := quicvarint.Len(uint64(ackDelayExponentParameterID)) /* parameter id */ + quicvarint.Len(uint64(quicvarint.Len(protocol.DefaultAckDelayExponent+1))) /* length */ + quicvarint.Len(protocol.DefaultAckDelayExponent+1) /* value */
   293  		Expect(float32(dataLen) / num).To(BeNumerically("~", float32(defaultLen)/num+float32(entryLen), 1))
   294  	})
   295  
   296  	It("sets the default value for the ack_delay_exponent and max_active_connection_id_limit, when no values were sent", func() {
   297  		data := (&TransportParameters{
   298  			AckDelayExponent:        protocol.DefaultAckDelayExponent,
   299  			StatelessResetToken:     &protocol.StatelessResetToken{},
   300  			ActiveConnectionIDLimit: protocol.DefaultActiveConnectionIDLimit,
   301  		}).Marshal(protocol.PerspectiveServer)
   302  		p := &TransportParameters{}
   303  		Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed())
   304  		Expect(p.AckDelayExponent).To(BeEquivalentTo(protocol.DefaultAckDelayExponent))
   305  		Expect(p.ActiveConnectionIDLimit).To(BeEquivalentTo(protocol.DefaultActiveConnectionIDLimit))
   306  	})
   307  
   308  	It("errors when the varint value has the wrong length", func() {
   309  		b := quicvarint.Append(nil, uint64(initialMaxStreamDataBidiLocalParameterID))
   310  		b = quicvarint.Append(b, 2)
   311  		val := uint64(0xdeadbeef)
   312  		Expect(quicvarint.Len(val)).ToNot(BeEquivalentTo(2))
   313  		b = quicvarint.Append(b, val)
   314  		b = appendInitialSourceConnectionID(b)
   315  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   316  			ErrorCode:    qerr.TransportParameterError,
   317  			ErrorMessage: fmt.Sprintf("inconsistent transport parameter length for transport parameter %#x", initialMaxStreamDataBidiLocalParameterID),
   318  		}))
   319  	})
   320  
   321  	It("errors if initial_max_streams_bidi is too large", func() {
   322  		b := quicvarint.Append(nil, uint64(initialMaxStreamsBidiParameterID))
   323  		b = quicvarint.Append(b, uint64(quicvarint.Len(uint64(protocol.MaxStreamCount+1))))
   324  		b = quicvarint.Append(b, uint64(protocol.MaxStreamCount+1))
   325  		b = appendInitialSourceConnectionID(b)
   326  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   327  			ErrorCode:    qerr.TransportParameterError,
   328  			ErrorMessage: "initial_max_streams_bidi too large: 1152921504606846977 (maximum 1152921504606846976)",
   329  		}))
   330  	})
   331  
   332  	It("errors if initial_max_streams_uni is too large", func() {
   333  		b := quicvarint.Append(nil, uint64(initialMaxStreamsUniParameterID))
   334  		b = quicvarint.Append(b, uint64(quicvarint.Len(uint64(protocol.MaxStreamCount+1))))
   335  		b = quicvarint.Append(b, uint64(protocol.MaxStreamCount+1))
   336  		b = appendInitialSourceConnectionID(b)
   337  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   338  			ErrorCode:    qerr.TransportParameterError,
   339  			ErrorMessage: "initial_max_streams_uni too large: 1152921504606846977 (maximum 1152921504606846976)",
   340  		}))
   341  	})
   342  
   343  	It("handles huge max_ack_delay values", func() {
   344  		val := uint64(math.MaxUint64) / 5
   345  		b := quicvarint.Append(nil, uint64(maxAckDelayParameterID))
   346  		b = quicvarint.Append(b, uint64(quicvarint.Len(val)))
   347  		b = quicvarint.Append(b, val)
   348  		b = appendInitialSourceConnectionID(b)
   349  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{
   350  			ErrorCode:    qerr.TransportParameterError,
   351  			ErrorMessage: "invalid value for max_ack_delay: 3689348814741910323ms (maximum 16383ms)",
   352  		}))
   353  	})
   354  
   355  	It("skips unknown parameters", func() {
   356  		// write a known parameter
   357  		b := quicvarint.Append(nil, uint64(initialMaxStreamDataBidiLocalParameterID))
   358  		b = quicvarint.Append(b, uint64(quicvarint.Len(0x1337)))
   359  		b = quicvarint.Append(b, 0x1337)
   360  		// write an unknown parameter
   361  		b = quicvarint.Append(b, 0x42)
   362  		b = quicvarint.Append(b, 6)
   363  		b = append(b, []byte("foobar")...)
   364  		// write a known parameter
   365  		b = quicvarint.Append(b, uint64(initialMaxStreamDataBidiRemoteParameterID))
   366  		b = quicvarint.Append(b, uint64(quicvarint.Len(0x42)))
   367  		b = quicvarint.Append(b, 0x42)
   368  		b = appendInitialSourceConnectionID(b)
   369  		p := &TransportParameters{}
   370  		Expect(p.Unmarshal(b, protocol.PerspectiveClient)).To(Succeed())
   371  		Expect(p.InitialMaxStreamDataBidiLocal).To(Equal(protocol.ByteCount(0x1337)))
   372  		Expect(p.InitialMaxStreamDataBidiRemote).To(Equal(protocol.ByteCount(0x42)))
   373  	})
   374  
   375  	It("rejects duplicate parameters", func() {
   376  		// write first parameter
   377  		b := quicvarint.Append(nil, uint64(initialMaxStreamDataBidiLocalParameterID))
   378  		b = quicvarint.Append(b, uint64(quicvarint.Len(0x1337)))
   379  		b = quicvarint.Append(b, 0x1337)
   380  		// write a second parameter
   381  		b = quicvarint.Append(b, uint64(initialMaxStreamDataBidiRemoteParameterID))
   382  		b = quicvarint.Append(b, uint64(quicvarint.Len(0x42)))
   383  		b = quicvarint.Append(b, 0x42)
   384  		// write first parameter again
   385  		b = quicvarint.Append(b, uint64(initialMaxStreamDataBidiLocalParameterID))
   386  		b = quicvarint.Append(b, uint64(quicvarint.Len(0x1337)))
   387  		b = quicvarint.Append(b, 0x1337)
   388  		b = appendInitialSourceConnectionID(b)
   389  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{
   390  			ErrorCode:    qerr.TransportParameterError,
   391  			ErrorMessage: fmt.Sprintf("received duplicate transport parameter %#x", initialMaxStreamDataBidiLocalParameterID),
   392  		}))
   393  	})
   394  
   395  	It("errors if there's not enough data to read", func() {
   396  		b := quicvarint.Append(nil, 0x42)
   397  		b = quicvarint.Append(b, 7)
   398  		b = append(b, []byte("foobar")...)
   399  		p := &TransportParameters{}
   400  		Expect(p.Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   401  			ErrorCode:    qerr.TransportParameterError,
   402  			ErrorMessage: "remaining length (6) smaller than parameter length (7)",
   403  		}))
   404  	})
   405  
   406  	It("errors if the client sent a stateless_reset_token", func() {
   407  		b := quicvarint.Append(nil, uint64(statelessResetTokenParameterID))
   408  		b = quicvarint.Append(b, uint64(quicvarint.Len(16)))
   409  		b = append(b, make([]byte, 16)...)
   410  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{
   411  			ErrorCode:    qerr.TransportParameterError,
   412  			ErrorMessage: "client sent a stateless_reset_token",
   413  		}))
   414  	})
   415  
   416  	It("errors if the client sent the original_destination_connection_id", func() {
   417  		b := quicvarint.Append(nil, uint64(originalDestinationConnectionIDParameterID))
   418  		b = quicvarint.Append(b, 6)
   419  		b = append(b, []byte("foobar")...)
   420  		Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{
   421  			ErrorCode:    qerr.TransportParameterError,
   422  			ErrorMessage: "client sent an original_destination_connection_id",
   423  		}))
   424  	})
   425  
   426  	Context("preferred address", func() {
   427  		var pa *PreferredAddress
   428  
   429  		BeforeEach(func() {
   430  			pa = &PreferredAddress{
   431  				IPv4:                netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), 42),
   432  				IPv6:                netip.AddrPortFrom(netip.AddrFrom16([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}), 13),
   433  				ConnectionID:        protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
   434  				StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
   435  			}
   436  		})
   437  
   438  		It("marshals and unmarshals", func() {
   439  			data := (&TransportParameters{
   440  				PreferredAddress:        pa,
   441  				StatelessResetToken:     &protocol.StatelessResetToken{},
   442  				ActiveConnectionIDLimit: 2,
   443  			}).Marshal(protocol.PerspectiveServer)
   444  			p := &TransportParameters{}
   445  			Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed())
   446  			Expect(p.PreferredAddress.IPv4).To(Equal(pa.IPv4))
   447  			Expect(p.PreferredAddress.IPv6).To(Equal(pa.IPv6))
   448  			Expect(p.PreferredAddress.ConnectionID).To(Equal(pa.ConnectionID))
   449  			Expect(p.PreferredAddress.StatelessResetToken).To(Equal(pa.StatelessResetToken))
   450  		})
   451  
   452  		It("errors if the client sent a preferred_address", func() {
   453  			b := quicvarint.Append(nil, uint64(preferredAddressParameterID))
   454  			b = quicvarint.Append(b, 6)
   455  			b = append(b, []byte("foobar")...)
   456  			p := &TransportParameters{}
   457  			Expect(p.Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{
   458  				ErrorCode:    qerr.TransportParameterError,
   459  				ErrorMessage: "client sent a preferred_address",
   460  			}))
   461  		})
   462  
   463  		It("errors on zero-length connection IDs", func() {
   464  			pa.ConnectionID = protocol.ParseConnectionID([]byte{})
   465  			data := (&TransportParameters{
   466  				PreferredAddress:    pa,
   467  				StatelessResetToken: &protocol.StatelessResetToken{},
   468  			}).Marshal(protocol.PerspectiveServer)
   469  			p := &TransportParameters{}
   470  			Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   471  				ErrorCode:    qerr.TransportParameterError,
   472  				ErrorMessage: "invalid connection ID length: 0",
   473  			}))
   474  		})
   475  
   476  		It("errors on EOF", func() {
   477  			raw := []byte{
   478  				127, 0, 0, 1, // IPv4
   479  				0, 42, // IPv4 Port
   480  				1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // IPv6
   481  				13, 37, // IPv6 Port,
   482  				4, // conn ID len
   483  				0xde, 0xad, 0xbe, 0xef,
   484  				16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, // stateless reset token
   485  			}
   486  			for i := 1; i < len(raw); i++ {
   487  				b := quicvarint.Append(nil, uint64(preferredAddressParameterID))
   488  				b = append(b, raw[:i]...)
   489  				p := &TransportParameters{}
   490  				Expect(p.Unmarshal(b, protocol.PerspectiveServer)).ToNot(Succeed())
   491  			}
   492  		})
   493  	})
   494  
   495  	Context("saving and retrieving from a session ticket", func() {
   496  		It("saves and retrieves the parameters", func() {
   497  			params := &TransportParameters{
   498  				InitialMaxStreamDataBidiLocal:  protocol.ByteCount(getRandomValue()),
   499  				InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()),
   500  				InitialMaxStreamDataUni:        protocol.ByteCount(getRandomValue()),
   501  				InitialMaxData:                 protocol.ByteCount(getRandomValue()),
   502  				MaxBidiStreamNum:               protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))),
   503  				MaxUniStreamNum:                protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))),
   504  				ActiveConnectionIDLimit:        2 + getRandomValueUpTo(quicvarint.Max-2),
   505  				MaxDatagramFrameSize:           protocol.ByteCount(getRandomValueUpTo(int64(MaxDatagramSize))),
   506  			}
   507  			Expect(params.ValidFor0RTT(params)).To(BeTrue())
   508  			b := params.MarshalForSessionTicket(nil)
   509  			var tp TransportParameters
   510  			Expect(tp.UnmarshalFromSessionTicket(b)).To(Succeed())
   511  			Expect(tp.InitialMaxStreamDataBidiLocal).To(Equal(params.InitialMaxStreamDataBidiLocal))
   512  			Expect(tp.InitialMaxStreamDataBidiRemote).To(Equal(params.InitialMaxStreamDataBidiRemote))
   513  			Expect(tp.InitialMaxStreamDataUni).To(Equal(params.InitialMaxStreamDataUni))
   514  			Expect(tp.InitialMaxData).To(Equal(params.InitialMaxData))
   515  			Expect(tp.MaxBidiStreamNum).To(Equal(params.MaxBidiStreamNum))
   516  			Expect(tp.MaxUniStreamNum).To(Equal(params.MaxUniStreamNum))
   517  			Expect(tp.ActiveConnectionIDLimit).To(Equal(params.ActiveConnectionIDLimit))
   518  			Expect(tp.MaxDatagramFrameSize).To(Equal(params.MaxDatagramFrameSize))
   519  		})
   520  
   521  		It("rejects the parameters if it can't parse them", func() {
   522  			var p TransportParameters
   523  			Expect(p.UnmarshalFromSessionTicket([]byte("foobar"))).ToNot(Succeed())
   524  		})
   525  
   526  		It("rejects the parameters if the version changed", func() {
   527  			var p TransportParameters
   528  			data := p.MarshalForSessionTicket(nil)
   529  			b := quicvarint.Append(nil, transportParameterMarshalingVersion+1)
   530  			b = append(b, data[quicvarint.Len(transportParameterMarshalingVersion):]...)
   531  			Expect(p.UnmarshalFromSessionTicket(b)).To(MatchError(fmt.Sprintf("unknown transport parameter marshaling version: %d", transportParameterMarshalingVersion+1)))
   532  		})
   533  
   534  		Context("rejects the parameters if they changed", func() {
   535  			var p TransportParameters
   536  			saved := &TransportParameters{
   537  				InitialMaxStreamDataBidiLocal:  1,
   538  				InitialMaxStreamDataBidiRemote: 2,
   539  				InitialMaxStreamDataUni:        3,
   540  				InitialMaxData:                 4,
   541  				MaxBidiStreamNum:               5,
   542  				MaxUniStreamNum:                6,
   543  				ActiveConnectionIDLimit:        7,
   544  				MaxDatagramFrameSize:           1000,
   545  			}
   546  
   547  			BeforeEach(func() {
   548  				p = *saved
   549  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   550  			})
   551  
   552  			It("rejects the parameters if the InitialMaxStreamDataBidiLocal was reduced", func() {
   553  				p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal - 1
   554  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   555  			})
   556  
   557  			It("doesn't reject the parameters if the InitialMaxStreamDataBidiLocal was increased", func() {
   558  				p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal + 1
   559  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   560  			})
   561  
   562  			It("rejects the parameters if the InitialMaxStreamDataBidiRemote was reduced", func() {
   563  				p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote - 1
   564  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   565  			})
   566  
   567  			It("doesn't reject the parameters if the InitialMaxStreamDataBidiRemote was increased", func() {
   568  				p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote + 1
   569  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   570  			})
   571  
   572  			It("rejects the parameters if the InitialMaxStreamDataUni was reduced", func() {
   573  				p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni - 1
   574  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   575  			})
   576  
   577  			It("doesn't reject the parameters if the InitialMaxStreamDataUni was increased", func() {
   578  				p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni + 1
   579  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   580  			})
   581  
   582  			It("rejects the parameters if the InitialMaxData was reduced", func() {
   583  				p.InitialMaxData = saved.InitialMaxData - 1
   584  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   585  			})
   586  
   587  			It("doesn't reject the parameters if the InitialMaxData was increased", func() {
   588  				p.InitialMaxData = saved.InitialMaxData + 1
   589  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   590  			})
   591  
   592  			It("rejects the parameters if the MaxBidiStreamNum was reduced", func() {
   593  				p.MaxBidiStreamNum = saved.MaxBidiStreamNum - 1
   594  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   595  			})
   596  
   597  			It("accepts the parameters if the MaxBidiStreamNum was increased", func() {
   598  				p.MaxBidiStreamNum = saved.MaxBidiStreamNum + 1
   599  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   600  			})
   601  
   602  			It("rejects the parameters if the MaxUniStreamNum changed", func() {
   603  				p.MaxUniStreamNum = saved.MaxUniStreamNum - 1
   604  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   605  			})
   606  
   607  			It("accepts the parameters if the MaxUniStreamNum was increased", func() {
   608  				p.MaxUniStreamNum = saved.MaxUniStreamNum + 1
   609  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   610  			})
   611  
   612  			It("rejects the parameters if the ActiveConnectionIDLimit changed", func() {
   613  				p.ActiveConnectionIDLimit = 0
   614  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   615  			})
   616  
   617  			It("accepts the parameters if the MaxDatagramFrameSize was increased", func() {
   618  				p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize + 1
   619  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   620  			})
   621  
   622  			It("rejects the parameters if the MaxDatagramFrameSize reduced", func() {
   623  				p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize - 1
   624  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   625  			})
   626  		})
   627  
   628  		Context("client checks the parameters after successfully sending 0-RTT data", func() {
   629  			var p TransportParameters
   630  			saved := &TransportParameters{
   631  				InitialMaxStreamDataBidiLocal:  1,
   632  				InitialMaxStreamDataBidiRemote: 2,
   633  				InitialMaxStreamDataUni:        3,
   634  				InitialMaxData:                 4,
   635  				MaxBidiStreamNum:               5,
   636  				MaxUniStreamNum:                6,
   637  				ActiveConnectionIDLimit:        7,
   638  				MaxDatagramFrameSize:           1000,
   639  			}
   640  
   641  			BeforeEach(func() {
   642  				p = *saved
   643  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   644  			})
   645  
   646  			It("rejects the parameters if the InitialMaxStreamDataBidiLocal was reduced", func() {
   647  				p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal - 1
   648  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   649  			})
   650  
   651  			It("doesn't reject the parameters if the InitialMaxStreamDataBidiLocal was increased", func() {
   652  				p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal + 1
   653  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   654  			})
   655  
   656  			It("rejects the parameters if the InitialMaxStreamDataBidiRemote was reduced", func() {
   657  				p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote - 1
   658  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   659  			})
   660  
   661  			It("doesn't reject the parameters if the InitialMaxStreamDataBidiRemote was increased", func() {
   662  				p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote + 1
   663  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   664  			})
   665  
   666  			It("rejects the parameters if the InitialMaxStreamDataUni was reduced", func() {
   667  				p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni - 1
   668  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   669  			})
   670  
   671  			It("doesn't reject the parameters if the InitialMaxStreamDataUni was increased", func() {
   672  				p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni + 1
   673  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   674  			})
   675  
   676  			It("rejects the parameters if the InitialMaxData was reduced", func() {
   677  				p.InitialMaxData = saved.InitialMaxData - 1
   678  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   679  			})
   680  
   681  			It("doesn't reject the parameters if the InitialMaxData was increased", func() {
   682  				p.InitialMaxData = saved.InitialMaxData + 1
   683  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   684  			})
   685  
   686  			It("rejects the parameters if the MaxBidiStreamNum was reduced", func() {
   687  				p.MaxBidiStreamNum = saved.MaxBidiStreamNum - 1
   688  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   689  			})
   690  
   691  			It("doesn't reject the parameters if the MaxBidiStreamNum was increased", func() {
   692  				p.MaxBidiStreamNum = saved.MaxBidiStreamNum + 1
   693  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   694  			})
   695  
   696  			It("rejects the parameters if the MaxUniStreamNum reduced", func() {
   697  				p.MaxUniStreamNum = saved.MaxUniStreamNum - 1
   698  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   699  			})
   700  
   701  			It("doesn't reject the parameters if the MaxUniStreamNum was increased", func() {
   702  				p.MaxUniStreamNum = saved.MaxUniStreamNum + 1
   703  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   704  			})
   705  
   706  			It("rejects the parameters if the ActiveConnectionIDLimit reduced", func() {
   707  				p.ActiveConnectionIDLimit = saved.ActiveConnectionIDLimit - 1
   708  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   709  			})
   710  
   711  			It("doesn't reject the parameters if the ActiveConnectionIDLimit increased", func() {
   712  				p.ActiveConnectionIDLimit = saved.ActiveConnectionIDLimit + 1
   713  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   714  			})
   715  
   716  			It("rejects the parameters if the MaxDatagramFrameSize reduced", func() {
   717  				p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize - 1
   718  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   719  			})
   720  
   721  			It("doesn't reject the parameters if the MaxDatagramFrameSize increased", func() {
   722  				p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize + 1
   723  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   724  			})
   725  		})
   726  	})
   727  })
   728  
   729  func BenchmarkTransportParameters(b *testing.B) {
   730  	b.Run("without preferred address", func(b *testing.B) { benchmarkTransportParameters(b, false) })
   731  	b.Run("with preferred address", func(b *testing.B) { benchmarkTransportParameters(b, true) })
   732  }
   733  
   734  func benchmarkTransportParameters(b *testing.B, withPreferredAddress bool) {
   735  	var token protocol.StatelessResetToken
   736  	rand.Read(token[:])
   737  	rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde})
   738  	params := &TransportParameters{
   739  		InitialMaxStreamDataBidiLocal:   protocol.ByteCount(getRandomValue()),
   740  		InitialMaxStreamDataBidiRemote:  protocol.ByteCount(getRandomValue()),
   741  		InitialMaxStreamDataUni:         protocol.ByteCount(getRandomValue()),
   742  		InitialMaxData:                  protocol.ByteCount(getRandomValue()),
   743  		MaxIdleTimeout:                  0xcafe * time.Second,
   744  		MaxBidiStreamNum:                protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))),
   745  		MaxUniStreamNum:                 protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))),
   746  		DisableActiveMigration:          true,
   747  		StatelessResetToken:             &token,
   748  		OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
   749  		InitialSourceConnectionID:       protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}),
   750  		RetrySourceConnectionID:         &rcid,
   751  		AckDelayExponent:                13,
   752  		MaxAckDelay:                     42 * time.Millisecond,
   753  		ActiveConnectionIDLimit:         2 + getRandomValueUpTo(quicvarint.Max-2),
   754  		MaxDatagramFrameSize:            protocol.ByteCount(getRandomValue()),
   755  	}
   756  	var token2 protocol.StatelessResetToken
   757  	rand.Read(token2[:])
   758  	if withPreferredAddress {
   759  		var ip4 [4]byte
   760  		var ip6 [16]byte
   761  		rand.Read(ip4[:])
   762  		rand.Read(ip6[:])
   763  		params.PreferredAddress = &PreferredAddress{
   764  			IPv4:                netip.AddrPortFrom(netip.AddrFrom4(ip4), 1234),
   765  			IPv6:                netip.AddrPortFrom(netip.AddrFrom16(ip6), 4321),
   766  			ConnectionID:        protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
   767  			StatelessResetToken: token2,
   768  		}
   769  	}
   770  	data := params.Marshal(protocol.PerspectiveServer)
   771  
   772  	b.ResetTimer()
   773  	b.ReportAllocs()
   774  	var p TransportParameters
   775  	for i := 0; i < b.N; i++ {
   776  		if err := p.Unmarshal(data, protocol.PerspectiveServer); err != nil {
   777  			b.Fatal(err)
   778  		}
   779  		// check a few fields
   780  		if p.DisableActiveMigration != params.DisableActiveMigration ||
   781  			p.InitialMaxStreamDataBidiLocal != params.InitialMaxStreamDataBidiLocal ||
   782  			*p.StatelessResetToken != *params.StatelessResetToken ||
   783  			p.AckDelayExponent != params.AckDelayExponent {
   784  			b.Fatalf("params mismatch: %v vs %v", p, params)
   785  		}
   786  		if withPreferredAddress && *p.PreferredAddress != *params.PreferredAddress {
   787  			b.Fatalf("preferred address mismatch: %v vs %v", p.PreferredAddress, params.PreferredAddress)
   788  		}
   789  	}
   790  }