github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/internal/wire/transport_parameter_test.go (about)

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