github.com/tumi8/quic-go@v0.37.4-tum/noninternal/wire/version_negotiation.go (about)

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