github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/internal/wire/version_negotiation_test.go (about) 1 package wire 2 3 import ( 4 "encoding/binary" 5 "testing" 6 7 "golang.org/x/exp/rand" 8 9 "github.com/apernet/quic-go/internal/protocol" 10 11 . "github.com/onsi/ginkgo/v2" 12 . "github.com/onsi/gomega" 13 ) 14 15 var _ = Describe("Version Negotiation Packets", func() { 16 randConnID := func(l int) protocol.ArbitraryLenConnectionID { 17 b := make(protocol.ArbitraryLenConnectionID, l) 18 _, err := rand.Read(b) 19 Expect(err).ToNot(HaveOccurred()) 20 return b 21 } 22 23 It("parses a Version Negotiation packet", func() { 24 srcConnID := randConnID(rand.Intn(255) + 1) 25 destConnID := randConnID(rand.Intn(255) + 1) 26 versions := []protocol.Version{0x22334455, 0x33445566} 27 data := []byte{0x80, 0, 0, 0, 0} 28 data = append(data, uint8(len(destConnID))) 29 data = append(data, destConnID...) 30 data = append(data, uint8(len(srcConnID))) 31 data = append(data, srcConnID...) 32 for _, v := range versions { 33 data = append(data, []byte{0, 0, 0, 0}...) 34 binary.BigEndian.PutUint32(data[len(data)-4:], uint32(v)) 35 } 36 Expect(IsVersionNegotiationPacket(data)).To(BeTrue()) 37 dest, src, supportedVersions, err := ParseVersionNegotiationPacket(data) 38 Expect(err).ToNot(HaveOccurred()) 39 Expect(dest).To(Equal(destConnID)) 40 Expect(src).To(Equal(srcConnID)) 41 Expect(supportedVersions).To(Equal(versions)) 42 }) 43 44 It("errors if it contains versions of the wrong length", func() { 45 connID := protocol.ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8} 46 versions := []protocol.Version{0x22334455, 0x33445566} 47 data := ComposeVersionNegotiation(connID, connID, versions) 48 _, _, _, err := ParseVersionNegotiationPacket(data[:len(data)-2]) 49 Expect(err).To(MatchError("Version Negotiation packet has a version list with an invalid length")) 50 }) 51 52 It("errors if the version list is empty", func() { 53 connID := protocol.ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8} 54 versions := []protocol.Version{0x22334455} 55 data := ComposeVersionNegotiation(connID, connID, versions) 56 // remove 8 bytes (two versions), since ComposeVersionNegotiation also added a reserved version number 57 data = data[:len(data)-8] 58 _, _, _, err := ParseVersionNegotiationPacket(data) 59 Expect(err).To(MatchError("Version Negotiation packet has empty version list")) 60 }) 61 62 It("adds a reserved version", func() { 63 srcConnID := protocol.ArbitraryLenConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37} 64 destConnID := protocol.ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8} 65 versions := []protocol.Version{1001, 1003} 66 data := ComposeVersionNegotiation(destConnID, srcConnID, versions) 67 Expect(IsLongHeaderPacket(data[0])).To(BeTrue()) 68 Expect(data[0] & 0x40).ToNot(BeZero()) 69 v, err := ParseVersion(data) 70 Expect(err).ToNot(HaveOccurred()) 71 Expect(v).To(BeZero()) 72 dest, src, supportedVersions, err := ParseVersionNegotiationPacket(data) 73 Expect(err).ToNot(HaveOccurred()) 74 Expect(dest).To(Equal(destConnID)) 75 Expect(src).To(Equal(srcConnID)) 76 // the supported versions should include one reserved version number 77 Expect(supportedVersions).To(HaveLen(len(versions) + 1)) 78 for _, v := range versions { 79 Expect(supportedVersions).To(ContainElement(v)) 80 } 81 var reservedVersion protocol.Version 82 versionLoop: 83 for _, ver := range supportedVersions { 84 for _, v := range versions { 85 if v == ver { 86 continue versionLoop 87 } 88 } 89 reservedVersion = ver 90 } 91 Expect(reservedVersion).ToNot(BeZero()) 92 Expect(reservedVersion&0x0f0f0f0f == 0x0a0a0a0a).To(BeTrue()) // check that it's a greased version number 93 }) 94 }) 95 96 func BenchmarkComposeVersionNegotiationPacket(b *testing.B) { 97 b.ReportAllocs() 98 supportedVersions := []protocol.Version{protocol.Version2, protocol.Version1, 0x1337} 99 destConnID := protocol.ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0xa, 0xb, 0xc, 0xd} 100 srcConnID := protocol.ArbitraryLenConnectionID{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} 101 for i := 0; i < b.N; i++ { 102 ComposeVersionNegotiation(destConnID, srcConnID, supportedVersions) 103 } 104 }