github.com/MerlinKodo/quic-go@v0.39.2/internal/protocol/version.go (about) 1 package protocol 2 3 import ( 4 "crypto/rand" 5 "encoding/binary" 6 "fmt" 7 "math" 8 ) 9 10 // VersionNumber is a version number as int 11 type VersionNumber uint32 12 13 // gQUIC version range as defined in the wiki: https://github.com/quicwg/base-drafts/wiki/QUIC-Versions 14 const ( 15 gquicVersion0 = 0x51303030 16 maxGquicVersion = 0x51303439 17 ) 18 19 // The version numbers, making grepping easier 20 const ( 21 VersionUnknown VersionNumber = math.MaxUint32 22 versionDraft29 VersionNumber = 0xff00001d // draft-29 used to be a widely deployed version 23 Version1 VersionNumber = 0x1 24 Version2 VersionNumber = 0x6b3343cf 25 ) 26 27 // SupportedVersions lists the versions that the server supports 28 // must be in sorted descending order 29 var SupportedVersions = []VersionNumber{Version1, Version2} 30 31 // IsValidVersion says if the version is known to quic-go 32 func IsValidVersion(v VersionNumber) bool { 33 return v == Version1 || IsSupportedVersion(SupportedVersions, v) 34 } 35 36 func (vn VersionNumber) String() string { 37 //nolint:exhaustive 38 switch vn { 39 case VersionUnknown: 40 return "unknown" 41 case versionDraft29: 42 return "draft-29" 43 case Version1: 44 return "v1" 45 case Version2: 46 return "v2" 47 default: 48 if vn.isGQUIC() { 49 return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion()) 50 } 51 return fmt.Sprintf("%#x", uint32(vn)) 52 } 53 } 54 55 func (vn VersionNumber) isGQUIC() bool { 56 return vn > gquicVersion0 && vn <= maxGquicVersion 57 } 58 59 func (vn VersionNumber) toGQUICVersion() int { 60 return int(10*(vn-gquicVersion0)/0x100) + int(vn%0x10) 61 } 62 63 // IsSupportedVersion returns true if the server supports this version 64 func IsSupportedVersion(supported []VersionNumber, v VersionNumber) bool { 65 for _, t := range supported { 66 if t == v { 67 return true 68 } 69 } 70 return false 71 } 72 73 // ChooseSupportedVersion finds the best version in the overlap of ours and theirs 74 // ours is a slice of versions that we support, sorted by our preference (descending) 75 // theirs is a slice of versions offered by the peer. The order does not matter. 76 // The bool returned indicates if a matching version was found. 77 func ChooseSupportedVersion(ours, theirs []VersionNumber) (VersionNumber, bool) { 78 for _, ourVer := range ours { 79 for _, theirVer := range theirs { 80 if ourVer == theirVer { 81 return ourVer, true 82 } 83 } 84 } 85 return 0, false 86 } 87 88 // generateReservedVersion generates a reserved version number (v & 0x0f0f0f0f == 0x0a0a0a0a) 89 func generateReservedVersion() VersionNumber { 90 b := make([]byte, 4) 91 _, _ = rand.Read(b) // ignore the error here. Failure to read random data doesn't break anything 92 return VersionNumber((binary.BigEndian.Uint32(b) | 0x0a0a0a0a) & 0xfafafafa) 93 } 94 95 // GetGreasedVersions adds one reserved version number to a slice of version numbers, at a random position 96 func GetGreasedVersions(supported []VersionNumber) []VersionNumber { 97 b := make([]byte, 1) 98 _, _ = rand.Read(b) // ignore the error here. Failure to read random data doesn't break anything 99 randPos := int(b[0]) % (len(supported) + 1) 100 greased := make([]VersionNumber, len(supported)+1) 101 copy(greased, supported[:randPos]) 102 greased[randPos] = generateReservedVersion() 103 copy(greased[randPos+1:], supported[randPos:]) 104 return greased 105 }