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

     1  package wire
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math"
     7  	"net"
     8  	"time"
     9  
    10  	"golang.org/x/exp/rand"
    11  
    12  	"github.com/MerlinKodo/quic-go/internal/protocol"
    13  	"github.com/MerlinKodo/quic-go/internal/qerr"
    14  	"github.com/MerlinKodo/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:                net.IPv4(127, 0, 0, 1),
   429  				IPv4Port:            42,
   430  				IPv6:                net.IP{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
   431  				IPv6Port:            13,
   432  				ConnectionID:        protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
   433  				StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
   434  			}
   435  		})
   436  
   437  		It("marshals and unmarshals", func() {
   438  			data := (&TransportParameters{
   439  				PreferredAddress:        pa,
   440  				StatelessResetToken:     &protocol.StatelessResetToken{},
   441  				ActiveConnectionIDLimit: 2,
   442  			}).Marshal(protocol.PerspectiveServer)
   443  			p := &TransportParameters{}
   444  			Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed())
   445  			Expect(p.PreferredAddress.IPv4.String()).To(Equal(pa.IPv4.String()))
   446  			Expect(p.PreferredAddress.IPv4Port).To(Equal(pa.IPv4Port))
   447  			Expect(p.PreferredAddress.IPv6.String()).To(Equal(pa.IPv6.String()))
   448  			Expect(p.PreferredAddress.IPv6Port).To(Equal(pa.IPv6Port))
   449  			Expect(p.PreferredAddress.ConnectionID).To(Equal(pa.ConnectionID))
   450  			Expect(p.PreferredAddress.StatelessResetToken).To(Equal(pa.StatelessResetToken))
   451  		})
   452  
   453  		It("errors if the client sent a preferred_address", func() {
   454  			b := quicvarint.Append(nil, uint64(preferredAddressParameterID))
   455  			b = quicvarint.Append(b, 6)
   456  			b = append(b, []byte("foobar")...)
   457  			p := &TransportParameters{}
   458  			Expect(p.Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{
   459  				ErrorCode:    qerr.TransportParameterError,
   460  				ErrorMessage: "client sent a preferred_address",
   461  			}))
   462  		})
   463  
   464  		It("errors on zero-length connection IDs", func() {
   465  			pa.ConnectionID = protocol.ParseConnectionID([]byte{})
   466  			data := (&TransportParameters{
   467  				PreferredAddress:    pa,
   468  				StatelessResetToken: &protocol.StatelessResetToken{},
   469  			}).Marshal(protocol.PerspectiveServer)
   470  			p := &TransportParameters{}
   471  			Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{
   472  				ErrorCode:    qerr.TransportParameterError,
   473  				ErrorMessage: "invalid connection ID length: 0",
   474  			}))
   475  		})
   476  
   477  		It("errors on EOF", func() {
   478  			raw := []byte{
   479  				127, 0, 0, 1, // IPv4
   480  				0, 42, // IPv4 Port
   481  				1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // IPv6
   482  				13, 37, // IPv6 Port,
   483  				4, // conn ID len
   484  				0xde, 0xad, 0xbe, 0xef,
   485  				16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, // stateless reset token
   486  			}
   487  			for i := 1; i < len(raw); i++ {
   488  				b := quicvarint.Append(nil, uint64(preferredAddressParameterID))
   489  				b = append(b, raw[:i]...)
   490  				p := &TransportParameters{}
   491  				Expect(p.Unmarshal(b, protocol.PerspectiveServer)).ToNot(Succeed())
   492  			}
   493  		})
   494  	})
   495  
   496  	Context("saving and retrieving from a session ticket", func() {
   497  		It("saves and retrieves the parameters", func() {
   498  			params := &TransportParameters{
   499  				InitialMaxStreamDataBidiLocal:  protocol.ByteCount(getRandomValue()),
   500  				InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()),
   501  				InitialMaxStreamDataUni:        protocol.ByteCount(getRandomValue()),
   502  				InitialMaxData:                 protocol.ByteCount(getRandomValue()),
   503  				MaxBidiStreamNum:               protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))),
   504  				MaxUniStreamNum:                protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))),
   505  				ActiveConnectionIDLimit:        2 + getRandomValueUpTo(math.MaxInt64-2),
   506  				MaxDatagramFrameSize:           protocol.ByteCount(getRandomValueUpTo(int64(protocol.MaxDatagramFrameSize))),
   507  			}
   508  			Expect(params.ValidFor0RTT(params)).To(BeTrue())
   509  			b := params.MarshalForSessionTicket(nil)
   510  			var tp TransportParameters
   511  			Expect(tp.UnmarshalFromSessionTicket(bytes.NewReader(b))).To(Succeed())
   512  			Expect(tp.InitialMaxStreamDataBidiLocal).To(Equal(params.InitialMaxStreamDataBidiLocal))
   513  			Expect(tp.InitialMaxStreamDataBidiRemote).To(Equal(params.InitialMaxStreamDataBidiRemote))
   514  			Expect(tp.InitialMaxStreamDataUni).To(Equal(params.InitialMaxStreamDataUni))
   515  			Expect(tp.InitialMaxData).To(Equal(params.InitialMaxData))
   516  			Expect(tp.MaxBidiStreamNum).To(Equal(params.MaxBidiStreamNum))
   517  			Expect(tp.MaxUniStreamNum).To(Equal(params.MaxUniStreamNum))
   518  			Expect(tp.ActiveConnectionIDLimit).To(Equal(params.ActiveConnectionIDLimit))
   519  			Expect(tp.MaxDatagramFrameSize).To(Equal(params.MaxDatagramFrameSize))
   520  		})
   521  
   522  		It("rejects the parameters if it can't parse them", func() {
   523  			var p TransportParameters
   524  			Expect(p.UnmarshalFromSessionTicket(bytes.NewReader([]byte("foobar")))).ToNot(Succeed())
   525  		})
   526  
   527  		It("rejects the parameters if the version changed", func() {
   528  			var p TransportParameters
   529  			data := p.MarshalForSessionTicket(nil)
   530  			b := quicvarint.Append(nil, transportParameterMarshalingVersion+1)
   531  			b = append(b, data[quicvarint.Len(transportParameterMarshalingVersion):]...)
   532  			Expect(p.UnmarshalFromSessionTicket(bytes.NewReader(b))).To(MatchError(fmt.Sprintf("unknown transport parameter marshaling version: %d", transportParameterMarshalingVersion+1)))
   533  		})
   534  
   535  		Context("rejects the parameters if they changed", func() {
   536  			var p TransportParameters
   537  			saved := &TransportParameters{
   538  				InitialMaxStreamDataBidiLocal:  1,
   539  				InitialMaxStreamDataBidiRemote: 2,
   540  				InitialMaxStreamDataUni:        3,
   541  				InitialMaxData:                 4,
   542  				MaxBidiStreamNum:               5,
   543  				MaxUniStreamNum:                6,
   544  				ActiveConnectionIDLimit:        7,
   545  				MaxDatagramFrameSize:           1000,
   546  			}
   547  
   548  			BeforeEach(func() {
   549  				p = *saved
   550  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   551  			})
   552  
   553  			It("rejects the parameters if the InitialMaxStreamDataBidiLocal was reduced", func() {
   554  				p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal - 1
   555  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   556  			})
   557  
   558  			It("doesn't reject the parameters if the InitialMaxStreamDataBidiLocal was increased", func() {
   559  				p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal + 1
   560  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   561  			})
   562  
   563  			It("rejects the parameters if the InitialMaxStreamDataBidiRemote was reduced", func() {
   564  				p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote - 1
   565  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   566  			})
   567  
   568  			It("doesn't reject the parameters if the InitialMaxStreamDataBidiRemote was increased", func() {
   569  				p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote + 1
   570  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   571  			})
   572  
   573  			It("rejects the parameters if the InitialMaxStreamDataUni was reduced", func() {
   574  				p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni - 1
   575  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   576  			})
   577  
   578  			It("doesn't reject the parameters if the InitialMaxStreamDataUni was increased", func() {
   579  				p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni + 1
   580  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   581  			})
   582  
   583  			It("rejects the parameters if the InitialMaxData was reduced", func() {
   584  				p.InitialMaxData = saved.InitialMaxData - 1
   585  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   586  			})
   587  
   588  			It("doesn't reject the parameters if the InitialMaxData was increased", func() {
   589  				p.InitialMaxData = saved.InitialMaxData + 1
   590  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   591  			})
   592  
   593  			It("rejects the parameters if the MaxBidiStreamNum was reduced", func() {
   594  				p.MaxBidiStreamNum = saved.MaxBidiStreamNum - 1
   595  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   596  			})
   597  
   598  			It("accepts the parameters if the MaxBidiStreamNum was increased", func() {
   599  				p.MaxBidiStreamNum = saved.MaxBidiStreamNum + 1
   600  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   601  			})
   602  
   603  			It("rejects the parameters if the MaxUniStreamNum changed", func() {
   604  				p.MaxUniStreamNum = saved.MaxUniStreamNum - 1
   605  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   606  			})
   607  
   608  			It("accepts the parameters if the MaxUniStreamNum was increased", func() {
   609  				p.MaxUniStreamNum = saved.MaxUniStreamNum + 1
   610  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   611  			})
   612  
   613  			It("rejects the parameters if the ActiveConnectionIDLimit changed", func() {
   614  				p.ActiveConnectionIDLimit = 0
   615  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   616  			})
   617  
   618  			It("accepts the parameters if the MaxDatagramFrameSize was increased", func() {
   619  				p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize + 1
   620  				Expect(p.ValidFor0RTT(saved)).To(BeTrue())
   621  			})
   622  
   623  			It("rejects the parameters if the MaxDatagramFrameSize reduced", func() {
   624  				p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize - 1
   625  				Expect(p.ValidFor0RTT(saved)).To(BeFalse())
   626  			})
   627  		})
   628  
   629  		Context("client checks the parameters after successfully sending 0-RTT data", func() {
   630  			var p TransportParameters
   631  			saved := &TransportParameters{
   632  				InitialMaxStreamDataBidiLocal:  1,
   633  				InitialMaxStreamDataBidiRemote: 2,
   634  				InitialMaxStreamDataUni:        3,
   635  				InitialMaxData:                 4,
   636  				MaxBidiStreamNum:               5,
   637  				MaxUniStreamNum:                6,
   638  				ActiveConnectionIDLimit:        7,
   639  				MaxDatagramFrameSize:           1000,
   640  			}
   641  
   642  			BeforeEach(func() {
   643  				p = *saved
   644  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   645  			})
   646  
   647  			It("rejects the parameters if the InitialMaxStreamDataBidiLocal was reduced", func() {
   648  				p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal - 1
   649  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   650  			})
   651  
   652  			It("doesn't reject the parameters if the InitialMaxStreamDataBidiLocal was increased", func() {
   653  				p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal + 1
   654  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   655  			})
   656  
   657  			It("rejects the parameters if the InitialMaxStreamDataBidiRemote was reduced", func() {
   658  				p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote - 1
   659  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   660  			})
   661  
   662  			It("doesn't reject the parameters if the InitialMaxStreamDataBidiRemote was increased", func() {
   663  				p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote + 1
   664  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   665  			})
   666  
   667  			It("rejects the parameters if the InitialMaxStreamDataUni was reduced", func() {
   668  				p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni - 1
   669  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   670  			})
   671  
   672  			It("doesn't reject the parameters if the InitialMaxStreamDataUni was increased", func() {
   673  				p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni + 1
   674  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   675  			})
   676  
   677  			It("rejects the parameters if the InitialMaxData was reduced", func() {
   678  				p.InitialMaxData = saved.InitialMaxData - 1
   679  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   680  			})
   681  
   682  			It("doesn't reject the parameters if the InitialMaxData was increased", func() {
   683  				p.InitialMaxData = saved.InitialMaxData + 1
   684  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   685  			})
   686  
   687  			It("rejects the parameters if the MaxBidiStreamNum was reduced", func() {
   688  				p.MaxBidiStreamNum = saved.MaxBidiStreamNum - 1
   689  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   690  			})
   691  
   692  			It("doesn't reject the parameters if the MaxBidiStreamNum was increased", func() {
   693  				p.MaxBidiStreamNum = saved.MaxBidiStreamNum + 1
   694  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   695  			})
   696  
   697  			It("rejects the parameters if the MaxUniStreamNum reduced", func() {
   698  				p.MaxUniStreamNum = saved.MaxUniStreamNum - 1
   699  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   700  			})
   701  
   702  			It("doesn't reject the parameters if the MaxUniStreamNum was increased", func() {
   703  				p.MaxUniStreamNum = saved.MaxUniStreamNum + 1
   704  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   705  			})
   706  
   707  			It("rejects the parameters if the ActiveConnectionIDLimit reduced", func() {
   708  				p.ActiveConnectionIDLimit = saved.ActiveConnectionIDLimit - 1
   709  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   710  			})
   711  
   712  			It("doesn't reject the parameters if the ActiveConnectionIDLimit increased", func() {
   713  				p.ActiveConnectionIDLimit = saved.ActiveConnectionIDLimit + 1
   714  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   715  			})
   716  
   717  			It("rejects the parameters if the MaxDatagramFrameSize reduced", func() {
   718  				p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize - 1
   719  				Expect(p.ValidForUpdate(saved)).To(BeFalse())
   720  			})
   721  
   722  			It("doesn't reject the parameters if the MaxDatagramFrameSize increased", func() {
   723  				p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize + 1
   724  				Expect(p.ValidForUpdate(saved)).To(BeTrue())
   725  			})
   726  		})
   727  	})
   728  })