github.com/ooni/psiphon/tunnel-core@v0.0.0-20230105123940-fe12a24c96ee/oovendor/quic-go/internal/wire/version_negotiation.go (about) 1 package wire 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "errors" 7 8 "github.com/ooni/psiphon/tunnel-core/oovendor/quic-go/internal/protocol" 9 "github.com/ooni/psiphon/tunnel-core/oovendor/quic-go/internal/utils" 10 ) 11 12 // ParseVersionNegotiationPacket parses a Version Negotiation packet. 13 func ParseVersionNegotiationPacket(b *bytes.Reader) (*Header, []protocol.VersionNumber, error) { 14 hdr, err := parseHeader(b, 0) 15 if err != nil { 16 return nil, nil, err 17 } 18 if b.Len() == 0 { 19 //nolint:stylecheck 20 return nil, nil, errors.New("Version Negotiation packet has empty version list") 21 } 22 if b.Len()%4 != 0 { 23 //nolint:stylecheck 24 return nil, nil, errors.New("Version Negotiation packet has a version list with an invalid length") 25 } 26 versions := make([]protocol.VersionNumber, b.Len()/4) 27 for i := 0; b.Len() > 0; i++ { 28 v, err := utils.BigEndian.ReadUint32(b) 29 if err != nil { 30 return nil, nil, err 31 } 32 versions[i] = protocol.VersionNumber(v) 33 } 34 return hdr, versions, nil 35 } 36 37 // ComposeVersionNegotiation composes a Version Negotiation 38 func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, versions []protocol.VersionNumber) ([]byte, error) { 39 greasedVersions := protocol.GetGreasedVersions(versions) 40 expectedLen := 1 /* type byte */ + 4 /* version field */ + 1 /* dest connection ID length field */ + destConnID.Len() + 1 /* src connection ID length field */ + srcConnID.Len() + len(greasedVersions)*4 41 buf := bytes.NewBuffer(make([]byte, 0, expectedLen)) 42 r := make([]byte, 1) 43 _, _ = rand.Read(r) // ignore the error here. It is not critical to have perfect random here. 44 buf.WriteByte(r[0] | 0x80) 45 utils.BigEndian.WriteUint32(buf, 0) // version 0 46 buf.WriteByte(uint8(destConnID.Len())) 47 buf.Write(destConnID) 48 buf.WriteByte(uint8(srcConnID.Len())) 49 buf.Write(srcConnID) 50 for _, v := range greasedVersions { 51 utils.BigEndian.WriteUint32(buf, uint32(v)) 52 } 53 return buf.Bytes(), nil 54 }