version.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package protocol
  2. import (
  3. "crypto/rand"
  4. "encoding/binary"
  5. "fmt"
  6. "math"
  7. )
  8. // VersionNumber is a version number as int
  9. type VersionNumber uint32
  10. // gQUIC version range as defined in the wiki: https://github.com/quicwg/base-drafts/wiki/QUIC-Versions
  11. const (
  12. gquicVersion0 = 0x51303030
  13. maxGquicVersion = 0x51303439
  14. )
  15. // The version numbers, making grepping easier
  16. const (
  17. Version39 VersionNumber = gquicVersion0 + 3*0x100 + 0x9
  18. Version43 VersionNumber = gquicVersion0 + 4*0x100 + 0x3
  19. Version44 VersionNumber = gquicVersion0 + 4*0x100 + 0x4
  20. VersionTLS VersionNumber = 101
  21. VersionWhatever VersionNumber = 0 // for when the version doesn't matter
  22. VersionUnknown VersionNumber = math.MaxUint32
  23. VersionMilestone0_10_0 VersionNumber = 0x51474f02
  24. )
  25. // SupportedVersions lists the versions that the server supports
  26. // must be in sorted descending order
  27. var SupportedVersions = []VersionNumber{
  28. Version44,
  29. Version43,
  30. Version39,
  31. }
  32. // IsValidVersion says if the version is known to quic-go
  33. func IsValidVersion(v VersionNumber) bool {
  34. return v == VersionTLS || v == VersionMilestone0_10_0 || IsSupportedVersion(SupportedVersions, v)
  35. }
  36. // UsesTLS says if this QUIC version uses TLS 1.3 for the handshake
  37. func (vn VersionNumber) UsesTLS() bool {
  38. return !vn.isGQUIC()
  39. }
  40. func (vn VersionNumber) String() string {
  41. switch vn {
  42. case VersionWhatever:
  43. return "whatever"
  44. case VersionUnknown:
  45. return "unknown"
  46. case VersionMilestone0_10_0:
  47. return "quic-go Milestone 0.10.0"
  48. case VersionTLS:
  49. return "TLS dev version (WIP)"
  50. default:
  51. if vn.isGQUIC() {
  52. return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion())
  53. }
  54. return fmt.Sprintf("%#x", uint32(vn))
  55. }
  56. }
  57. // ToAltSvc returns the representation of the version for the H2 Alt-Svc parameters
  58. func (vn VersionNumber) ToAltSvc() string {
  59. if vn.isGQUIC() {
  60. return fmt.Sprintf("%d", vn.toGQUICVersion())
  61. }
  62. return fmt.Sprintf("%d", vn)
  63. }
  64. // CryptoStreamID gets the Stream ID of the crypto stream
  65. func (vn VersionNumber) CryptoStreamID() StreamID {
  66. if vn.isGQUIC() {
  67. return 1
  68. }
  69. return 0
  70. }
  71. // UsesIETFFrameFormat tells if this version uses the IETF frame format
  72. func (vn VersionNumber) UsesIETFFrameFormat() bool {
  73. return !vn.isGQUIC()
  74. }
  75. // UsesIETFHeaderFormat tells if this version uses the IETF header format
  76. func (vn VersionNumber) UsesIETFHeaderFormat() bool {
  77. return !vn.isGQUIC() || vn >= Version44
  78. }
  79. // UsesLengthInHeader tells if this version uses the Length field in the IETF header
  80. func (vn VersionNumber) UsesLengthInHeader() bool {
  81. return !vn.isGQUIC()
  82. }
  83. // UsesTokenInHeader tells if this version uses the Token field in the IETF header
  84. func (vn VersionNumber) UsesTokenInHeader() bool {
  85. return !vn.isGQUIC()
  86. }
  87. // UsesStopWaitingFrames tells if this version uses STOP_WAITING frames
  88. func (vn VersionNumber) UsesStopWaitingFrames() bool {
  89. return vn.isGQUIC() && vn <= Version43
  90. }
  91. // UsesVarintPacketNumbers tells if this version uses 7/14/30 bit packet numbers
  92. func (vn VersionNumber) UsesVarintPacketNumbers() bool {
  93. return !vn.isGQUIC()
  94. }
  95. // StreamContributesToConnectionFlowControl says if a stream contributes to connection-level flow control
  96. func (vn VersionNumber) StreamContributesToConnectionFlowControl(id StreamID) bool {
  97. if id == vn.CryptoStreamID() {
  98. return false
  99. }
  100. if vn.isGQUIC() && id == 3 {
  101. return false
  102. }
  103. return true
  104. }
  105. func (vn VersionNumber) isGQUIC() bool {
  106. return vn > gquicVersion0 && vn <= maxGquicVersion
  107. }
  108. func (vn VersionNumber) toGQUICVersion() int {
  109. return int(10*(vn-gquicVersion0)/0x100) + int(vn%0x10)
  110. }
  111. // IsSupportedVersion returns true if the server supports this version
  112. func IsSupportedVersion(supported []VersionNumber, v VersionNumber) bool {
  113. for _, t := range supported {
  114. if t == v {
  115. return true
  116. }
  117. }
  118. return false
  119. }
  120. // ChooseSupportedVersion finds the best version in the overlap of ours and theirs
  121. // ours is a slice of versions that we support, sorted by our preference (descending)
  122. // theirs is a slice of versions offered by the peer. The order does not matter.
  123. // The bool returned indicates if a matching version was found.
  124. func ChooseSupportedVersion(ours, theirs []VersionNumber) (VersionNumber, bool) {
  125. for _, ourVer := range ours {
  126. for _, theirVer := range theirs {
  127. if ourVer == theirVer {
  128. return ourVer, true
  129. }
  130. }
  131. }
  132. return 0, false
  133. }
  134. // generateReservedVersion generates a reserved version number (v & 0x0f0f0f0f == 0x0a0a0a0a)
  135. func generateReservedVersion() VersionNumber {
  136. b := make([]byte, 4)
  137. _, _ = rand.Read(b) // ignore the error here. Failure to read random data doesn't break anything
  138. return VersionNumber((binary.BigEndian.Uint32(b) | 0x0a0a0a0a) & 0xfafafafa)
  139. }
  140. // GetGreasedVersions adds one reserved version number to a slice of version numbers, at a random position
  141. func GetGreasedVersions(supported []VersionNumber) []VersionNumber {
  142. b := make([]byte, 1)
  143. _, _ = rand.Read(b) // ignore the error here. Failure to read random data doesn't break anything
  144. randPos := int(b[0]) % (len(supported) + 1)
  145. greased := make([]VersionNumber, len(supported)+1)
  146. copy(greased, supported[:randPos])
  147. greased[randPos] = generateReservedVersion()
  148. copy(greased[randPos+1:], supported[randPos:])
  149. return greased
  150. }
  151. // StripGreasedVersions strips all greased versions from a slice of versions
  152. func StripGreasedVersions(versions []VersionNumber) []VersionNumber {
  153. realVersions := make([]VersionNumber, 0, len(versions))
  154. for _, v := range versions {
  155. if v&0x0f0f0f0f != 0x0a0a0a0a {
  156. realVersions = append(realVersions, v)
  157. }
  158. }
  159. return realVersions
  160. }