move the transport parameters from the handshake to the wire package

This commit is contained in:
Marten Seemann 2020-03-13 16:20:11 +07:00
parent 699c988917
commit 0ef4f06189
17 changed files with 82 additions and 86 deletions

View file

@ -0,0 +1,405 @@
package wire
import (
"bytes"
"fmt"
"math"
"math/rand"
"net"
"time"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Transport Parameters", func() {
getRandomValue := func() uint64 {
maxVals := []int64{math.MaxUint8 / 4, math.MaxUint16 / 4, math.MaxUint32 / 4, math.MaxUint64 / 4}
return uint64(rand.Int63n(maxVals[int(rand.Int31n(4))]))
}
BeforeEach(func() {
rand.Seed(GinkgoRandomSeed())
})
It("has a string representation", func() {
p := &TransportParameters{
InitialMaxStreamDataBidiLocal: 0x1234,
InitialMaxStreamDataBidiRemote: 0x2345,
InitialMaxStreamDataUni: 0x3456,
InitialMaxData: 0x4567,
MaxBidiStreamNum: 1337,
MaxUniStreamNum: 7331,
MaxIdleTimeout: 42 * time.Second,
OriginalConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef},
AckDelayExponent: 14,
MaxAckDelay: 37 * time.Millisecond,
StatelessResetToken: &[16]byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00},
ActiveConnectionIDLimit: 123,
}
Expect(p.String()).To(Equal("&wire.TransportParameters{OriginalConnectionID: 0xdeadbeef, InitialMaxStreamDataBidiLocal: 0x1234, InitialMaxStreamDataBidiRemote: 0x2345, InitialMaxStreamDataUni: 0x3456, InitialMaxData: 0x4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42s, AckDelayExponent: 14, MaxAckDelay: 37ms, ActiveConnectionIDLimit: 123, StatelessResetToken: 0x112233445566778899aabbccddeeff00}"))
})
It("has a string representation, if there's no stateless reset token", func() {
p := &TransportParameters{
InitialMaxStreamDataBidiLocal: 0x1234,
InitialMaxStreamDataBidiRemote: 0x2345,
InitialMaxStreamDataUni: 0x3456,
InitialMaxData: 0x4567,
MaxBidiStreamNum: 1337,
MaxUniStreamNum: 7331,
MaxIdleTimeout: 42 * time.Second,
OriginalConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef},
AckDelayExponent: 14,
MaxAckDelay: 37 * time.Second,
ActiveConnectionIDLimit: 89,
}
Expect(p.String()).To(Equal("&wire.TransportParameters{OriginalConnectionID: 0xdeadbeef, InitialMaxStreamDataBidiLocal: 0x1234, InitialMaxStreamDataBidiRemote: 0x2345, InitialMaxStreamDataUni: 0x3456, InitialMaxData: 0x4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42s, AckDelayExponent: 14, MaxAckDelay: 37s, ActiveConnectionIDLimit: 89}"))
})
It("marshals and unmarshals", func() {
var token [16]byte
rand.Read(token[:])
params := &TransportParameters{
InitialMaxStreamDataBidiLocal: protocol.ByteCount(getRandomValue()),
InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()),
InitialMaxStreamDataUni: protocol.ByteCount(getRandomValue()),
InitialMaxData: protocol.ByteCount(getRandomValue()),
MaxIdleTimeout: 0xcafe * time.Second,
MaxBidiStreamNum: protocol.StreamNum(getRandomValue()),
MaxUniStreamNum: protocol.StreamNum(getRandomValue()),
DisableActiveMigration: true,
StatelessResetToken: &token,
OriginalConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef},
AckDelayExponent: 13,
MaxAckDelay: 42 * time.Millisecond,
ActiveConnectionIDLimit: getRandomValue(),
}
data := params.Marshal()
p := &TransportParameters{}
Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed())
Expect(p.InitialMaxStreamDataBidiLocal).To(Equal(params.InitialMaxStreamDataBidiLocal))
Expect(p.InitialMaxStreamDataBidiRemote).To(Equal(params.InitialMaxStreamDataBidiRemote))
Expect(p.InitialMaxStreamDataUni).To(Equal(params.InitialMaxStreamDataUni))
Expect(p.InitialMaxData).To(Equal(params.InitialMaxData))
Expect(p.MaxUniStreamNum).To(Equal(params.MaxUniStreamNum))
Expect(p.MaxBidiStreamNum).To(Equal(params.MaxBidiStreamNum))
Expect(p.MaxIdleTimeout).To(Equal(params.MaxIdleTimeout))
Expect(p.DisableActiveMigration).To(Equal(params.DisableActiveMigration))
Expect(p.StatelessResetToken).To(Equal(params.StatelessResetToken))
Expect(p.OriginalConnectionID).To(Equal(protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}))
Expect(p.AckDelayExponent).To(Equal(uint8(13)))
Expect(p.MaxAckDelay).To(Equal(42 * time.Millisecond))
Expect(p.ActiveConnectionIDLimit).To(Equal(params.ActiveConnectionIDLimit))
})
It("errors when the stateless_reset_token has the wrong length", func() {
b := &bytes.Buffer{}
utils.WriteVarInt(b, uint64(statelessResetTokenParameterID))
utils.WriteVarInt(b, 15)
b.Write(make([]byte, 15))
p := &TransportParameters{}
Expect(p.Unmarshal(b.Bytes(), protocol.PerspectiveServer)).To(MatchError("TRANSPORT_PARAMETER_ERROR: wrong length for stateless_reset_token: 15 (expected 16)"))
})
It("errors when the max_packet_size is too small", func() {
b := &bytes.Buffer{}
utils.WriteVarInt(b, uint64(maxPacketSizeParameterID))
utils.WriteVarInt(b, uint64(utils.VarIntLen(1199)))
utils.WriteVarInt(b, 1199)
p := &TransportParameters{}
Expect(p.Unmarshal(b.Bytes(), protocol.PerspectiveServer)).To(MatchError("TRANSPORT_PARAMETER_ERROR: invalid value for max_packet_size: 1199 (minimum 1200)"))
})
It("errors when disable_active_migration has content", func() {
b := &bytes.Buffer{}
utils.WriteVarInt(b, uint64(disableActiveMigrationParameterID))
utils.WriteVarInt(b, 6)
b.Write([]byte("foobar"))
p := &TransportParameters{}
Expect(p.Unmarshal(b.Bytes(), protocol.PerspectiveServer)).To(MatchError("TRANSPORT_PARAMETER_ERROR: wrong length for disable_active_migration: 6 (expected empty)"))
})
It("errors when the max_ack_delay is too large", func() {
data := (&TransportParameters{MaxAckDelay: 1 << 14 * time.Millisecond}).Marshal()
p := &TransportParameters{}
Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError("TRANSPORT_PARAMETER_ERROR: invalid value for max_ack_delay: 16384ms (maximum 16383ms)"))
})
It("doesn't send the max_ack_delay, if it has the default value", func() {
const num = 1000
var defaultLen, dataLen int
// marshal 1000 times to average out the greasing transport parameter
maxAckDelay := protocol.DefaultMaxAckDelay + time.Millisecond
for i := 0; i < num; i++ {
dataDefault := (&TransportParameters{MaxAckDelay: protocol.DefaultMaxAckDelay}).Marshal()
defaultLen += len(dataDefault)
data := (&TransportParameters{MaxAckDelay: maxAckDelay}).Marshal()
dataLen += len(data)
}
entryLen := utils.VarIntLen(uint64(ackDelayExponentParameterID)) /* parameter id */ + utils.VarIntLen(uint64(utils.VarIntLen(uint64(maxAckDelay.Milliseconds())))) /*length */ + utils.VarIntLen(uint64(maxAckDelay.Milliseconds())) /* value */
Expect(float32(dataLen) / num).To(BeNumerically("~", float32(defaultLen)/num+float32(entryLen), 1))
})
It("errors when the ack_delay_exponenent is too large", func() {
data := (&TransportParameters{AckDelayExponent: 21}).Marshal()
p := &TransportParameters{}
Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError("TRANSPORT_PARAMETER_ERROR: invalid value for ack_delay_exponent: 21 (maximum 20)"))
})
It("doesn't send the ack_delay_exponent, if it has the default value", func() {
const num = 1000
var defaultLen, dataLen int
// marshal 1000 times to average out the greasing transport parameter
for i := 0; i < num; i++ {
dataDefault := (&TransportParameters{AckDelayExponent: protocol.DefaultAckDelayExponent}).Marshal()
defaultLen += len(dataDefault)
data := (&TransportParameters{AckDelayExponent: protocol.DefaultAckDelayExponent + 1}).Marshal()
dataLen += len(data)
}
entryLen := utils.VarIntLen(uint64(ackDelayExponentParameterID)) /* parameter id */ + utils.VarIntLen(uint64(utils.VarIntLen(protocol.DefaultAckDelayExponent+1))) /* length */ + utils.VarIntLen(protocol.DefaultAckDelayExponent+1) /* value */
Expect(float32(dataLen) / num).To(BeNumerically("~", float32(defaultLen)/num+float32(entryLen), 1))
})
It("sets the default value for the ack_delay_exponent, when no value was sent", func() {
data := (&TransportParameters{AckDelayExponent: protocol.DefaultAckDelayExponent}).Marshal()
p := &TransportParameters{}
Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed())
Expect(p.AckDelayExponent).To(BeEquivalentTo(protocol.DefaultAckDelayExponent))
})
It("errors when the varint value has the wrong length", func() {
b := &bytes.Buffer{}
utils.WriteVarInt(b, uint64(initialMaxStreamDataBidiLocalParameterID))
utils.WriteVarInt(b, 2)
val := uint64(0xdeadbeef)
Expect(utils.VarIntLen(val)).ToNot(BeEquivalentTo(2))
utils.WriteVarInt(b, val)
p := &TransportParameters{}
err := p.Unmarshal(b.Bytes(), protocol.PerspectiveServer)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("TRANSPORT_PARAMETER_ERROR: inconsistent transport parameter length"))
})
It("handles max_ack_delays that decode to a negative duration", func() {
b := &bytes.Buffer{}
val := uint64(math.MaxUint64) / 5
utils.WriteVarInt(b, uint64(maxAckDelayParameterID))
utils.WriteVarInt(b, uint64(utils.VarIntLen(val)))
utils.WriteVarInt(b, val)
p := &TransportParameters{}
Expect(p.Unmarshal(b.Bytes(), protocol.PerspectiveServer)).To(Succeed())
Expect(p.MaxAckDelay).To(BeNumerically(">", 290*365*24*time.Hour))
})
It("skips unknown parameters", func() {
b := &bytes.Buffer{}
// write a known parameter
utils.WriteVarInt(b, uint64(initialMaxStreamDataBidiLocalParameterID))
utils.WriteVarInt(b, uint64(utils.VarIntLen(0x1337)))
utils.WriteVarInt(b, 0x1337)
// write an unknown parameter
utils.WriteVarInt(b, 0x42)
utils.WriteVarInt(b, 6)
b.Write([]byte("foobar"))
// write a known parameter
utils.WriteVarInt(b, uint64(initialMaxStreamDataBidiRemoteParameterID))
utils.WriteVarInt(b, uint64(utils.VarIntLen(0x42)))
utils.WriteVarInt(b, 0x42)
p := &TransportParameters{}
Expect(p.Unmarshal(b.Bytes(), protocol.PerspectiveServer)).To(Succeed())
Expect(p.InitialMaxStreamDataBidiLocal).To(Equal(protocol.ByteCount(0x1337)))
Expect(p.InitialMaxStreamDataBidiRemote).To(Equal(protocol.ByteCount(0x42)))
})
It("rejects duplicate parameters", func() {
b := &bytes.Buffer{}
// write first parameter
utils.WriteVarInt(b, uint64(initialMaxStreamDataBidiLocalParameterID))
utils.WriteVarInt(b, uint64(utils.VarIntLen(0x1337)))
utils.WriteVarInt(b, 0x1337)
// write a second parameter
utils.WriteVarInt(b, uint64(initialMaxStreamDataBidiRemoteParameterID))
utils.WriteVarInt(b, uint64(utils.VarIntLen(0x42)))
utils.WriteVarInt(b, 0x42)
// write first parameter again
utils.WriteVarInt(b, uint64(initialMaxStreamDataBidiLocalParameterID))
utils.WriteVarInt(b, uint64(utils.VarIntLen(0x1337)))
utils.WriteVarInt(b, 0x1337)
p := &TransportParameters{}
err := p.Unmarshal(b.Bytes(), protocol.PerspectiveServer)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("received duplicate transport parameter"))
})
It("errors if there's not enough data to read", func() {
b := &bytes.Buffer{}
utils.WriteVarInt(b, 0x42)
utils.WriteVarInt(b, 7)
b.Write([]byte("foobar"))
p := &TransportParameters{}
Expect(p.Unmarshal(b.Bytes(), protocol.PerspectiveServer)).To(MatchError("TRANSPORT_PARAMETER_ERROR: remaining length (6) smaller than parameter length (7)"))
})
It("errors if the client sent a stateless_reset_token", func() {
var token [16]byte
params := &TransportParameters{StatelessResetToken: &token}
data := params.Marshal()
Expect((&TransportParameters{}).Unmarshal(data, protocol.PerspectiveClient)).To(MatchError("TRANSPORT_PARAMETER_ERROR: client sent a stateless_reset_token"))
})
It("errors if the client sent a stateless_reset_token", func() {
params := &TransportParameters{
OriginalConnectionID: protocol.ConnectionID{0xca, 0xfe},
}
data := params.Marshal()
Expect((&TransportParameters{}).Unmarshal(data, protocol.PerspectiveClient)).To(MatchError("TRANSPORT_PARAMETER_ERROR: client sent an original_connection_id"))
})
Context("preferred address", func() {
pa := &PreferredAddress{
IPv4: net.IPv4(127, 0, 0, 1),
IPv4Port: 42,
IPv6: net.IP{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
IPv6Port: 13,
ConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef},
StatelessResetToken: [16]byte{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
}
It("marshals and unmarshals", func() {
data := (&TransportParameters{PreferredAddress: pa}).Marshal()
p := &TransportParameters{}
Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed())
Expect(p.PreferredAddress.IPv4.String()).To(Equal(pa.IPv4.String()))
Expect(p.PreferredAddress.IPv4Port).To(Equal(pa.IPv4Port))
Expect(p.PreferredAddress.IPv6.String()).To(Equal(pa.IPv6.String()))
Expect(p.PreferredAddress.IPv6Port).To(Equal(pa.IPv6Port))
Expect(p.PreferredAddress.ConnectionID).To(Equal(pa.ConnectionID))
Expect(p.PreferredAddress.StatelessResetToken).To(Equal(pa.StatelessResetToken))
})
It("errors if the client sent a preferred_address", func() {
data := (&TransportParameters{PreferredAddress: pa}).Marshal()
p := &TransportParameters{}
Expect(p.Unmarshal(data, protocol.PerspectiveClient)).To(MatchError("TRANSPORT_PARAMETER_ERROR: client sent a preferred_address"))
})
It("errors on EOF", func() {
raw := []byte{
127, 0, 0, 1, // IPv4
0, 42, // IPv4 Port
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // IPv6
13, 37, // IPv6 Port,
4, // conn ID len
0xde, 0xad, 0xbe, 0xef,
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, // stateless reset token
}
for i := 1; i < len(raw); i++ {
buf := &bytes.Buffer{}
utils.WriteVarInt(buf, uint64(preferredAddressParameterID))
buf.Write(raw[:i])
p := &TransportParameters{}
Expect(p.Unmarshal(buf.Bytes(), protocol.PerspectiveServer)).ToNot(Succeed())
}
})
})
Context("saving and retrieving from a session ticket", func() {
It("saves and retrieves the parameters", func() {
params := &TransportParameters{
InitialMaxStreamDataBidiLocal: protocol.ByteCount(getRandomValue()),
InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()),
InitialMaxStreamDataUni: protocol.ByteCount(getRandomValue()),
InitialMaxData: protocol.ByteCount(getRandomValue()),
MaxBidiStreamNum: protocol.StreamNum(getRandomValue()),
MaxUniStreamNum: protocol.StreamNum(getRandomValue()),
ActiveConnectionIDLimit: getRandomValue(),
}
Expect(params.ValidFor0RTT(params)).To(BeTrue())
b := &bytes.Buffer{}
params.MarshalForSessionTicket(b)
var tp TransportParameters
Expect(tp.UnmarshalFromSessionTicket(b.Bytes())).To(Succeed())
Expect(tp.InitialMaxStreamDataBidiLocal).To(Equal(params.InitialMaxStreamDataBidiLocal))
Expect(tp.InitialMaxStreamDataBidiRemote).To(Equal(params.InitialMaxStreamDataBidiRemote))
Expect(tp.InitialMaxStreamDataUni).To(Equal(params.InitialMaxStreamDataUni))
Expect(tp.InitialMaxData).To(Equal(params.InitialMaxData))
Expect(tp.MaxBidiStreamNum).To(Equal(params.MaxBidiStreamNum))
Expect(tp.MaxUniStreamNum).To(Equal(params.MaxUniStreamNum))
Expect(tp.ActiveConnectionIDLimit).To(Equal(params.ActiveConnectionIDLimit))
})
It("rejects the parameters if it can't parse them", func() {
var p TransportParameters
Expect(p.UnmarshalFromSessionTicket([]byte("foobar"))).ToNot(Succeed())
})
It("rejects the parameters if the version changed", func() {
var p TransportParameters
buf := &bytes.Buffer{}
p.MarshalForSessionTicket(buf)
data := buf.Bytes()
b := &bytes.Buffer{}
utils.WriteVarInt(b, transportParameterMarshalingVersion+1)
b.Write(data[utils.VarIntLen(transportParameterMarshalingVersion):])
Expect(p.UnmarshalFromSessionTicket(b.Bytes())).To(MatchError(fmt.Sprintf("unknown transport parameter marshaling version: %d", transportParameterMarshalingVersion+1)))
})
Context("rejects the parameters if they changed", func() {
var p *TransportParameters
params := &TransportParameters{
InitialMaxStreamDataBidiLocal: 1,
InitialMaxStreamDataBidiRemote: 2,
InitialMaxStreamDataUni: 3,
InitialMaxData: 4,
MaxBidiStreamNum: 5,
MaxUniStreamNum: 6,
}
BeforeEach(func() {
p = &TransportParameters{
InitialMaxStreamDataBidiLocal: 1,
InitialMaxStreamDataBidiRemote: 2,
InitialMaxStreamDataUni: 3,
InitialMaxData: 4,
MaxBidiStreamNum: 5,
MaxUniStreamNum: 6,
}
Expect(params.ValidFor0RTT(p)).To(BeTrue())
})
It("rejects the parameters if the InitialMaxStreamDataBidiLocal changed", func() {
p.InitialMaxStreamDataBidiLocal = 0
Expect(params.ValidFor0RTT(p)).To(BeFalse())
})
It("rejects the parameters if the InitialMaxStreamDataBidiRemote changed", func() {
p.InitialMaxStreamDataBidiRemote = 0
Expect(params.ValidFor0RTT(p)).To(BeFalse())
})
It("rejects the parameters if the InitialMaxStreamDataUni changed", func() {
p.InitialMaxStreamDataUni = 0
Expect(params.ValidFor0RTT(p)).To(BeFalse())
})
It("rejects the parameters if the InitialMaxData changed", func() {
p.InitialMaxData = 0
Expect(params.ValidFor0RTT(p)).To(BeFalse())
})
It("rejects the parameters if the MaxBidiStreamNum changed", func() {
p.MaxBidiStreamNum = 0
Expect(params.ValidFor0RTT(p)).To(BeFalse())
})
It("rejects the parameters if the MaxUniStreamNum changed", func() {
p.MaxUniStreamNum = 0
Expect(params.ValidFor0RTT(p)).To(BeFalse())
})
})
})
})