forked from quic-go/quic-go
Version Negotiation Packets and Public Resets are written separately, so we don't need to have code for that in the Public Header.
224 lines
7.1 KiB
Go
224 lines
7.1 KiB
Go
package wire
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
|
"github.com/lucas-clemente/quic-go/qerr"
|
|
)
|
|
|
|
var (
|
|
errInvalidConnectionID = qerr.Error(qerr.InvalidPacketHeader, "connection ID cannot be 0")
|
|
errInvalidPacketNumberLen6 = errors.New("invalid packet number length: 6 bytes")
|
|
)
|
|
|
|
// writePublicHeader writes a Public Header.
|
|
func (h *Header) writePublicHeader(b *bytes.Buffer, pers protocol.Perspective, _ protocol.VersionNumber) error {
|
|
if h.ResetFlag || (h.VersionFlag && pers == protocol.PerspectiveServer) {
|
|
return errors.New("PublicHeader: Can only write regular packets")
|
|
}
|
|
if h.SrcConnectionID.Len() != 0 {
|
|
return errors.New("PublicHeader: SrcConnectionID must not be set")
|
|
}
|
|
if len(h.DestConnectionID) != 0 && len(h.DestConnectionID) != 8 {
|
|
return fmt.Errorf("PublicHeader: wrong length for Connection ID: %d (expected 8)", len(h.DestConnectionID))
|
|
}
|
|
|
|
publicFlagByte := uint8(0x00)
|
|
if h.VersionFlag {
|
|
publicFlagByte |= 0x01
|
|
}
|
|
if h.ResetFlag {
|
|
publicFlagByte |= 0x02
|
|
}
|
|
if h.DestConnectionID.Len() > 0 {
|
|
publicFlagByte |= 0x08
|
|
}
|
|
if len(h.DiversificationNonce) > 0 {
|
|
if len(h.DiversificationNonce) != 32 {
|
|
return errors.New("invalid diversification nonce length")
|
|
}
|
|
publicFlagByte |= 0x04
|
|
}
|
|
switch h.PacketNumberLen {
|
|
case protocol.PacketNumberLen1:
|
|
publicFlagByte |= 0x00
|
|
case protocol.PacketNumberLen2:
|
|
publicFlagByte |= 0x10
|
|
case protocol.PacketNumberLen4:
|
|
publicFlagByte |= 0x20
|
|
}
|
|
b.WriteByte(publicFlagByte)
|
|
|
|
if h.DestConnectionID.Len() > 0 {
|
|
b.Write(h.DestConnectionID)
|
|
}
|
|
if h.VersionFlag && pers == protocol.PerspectiveClient {
|
|
utils.BigEndian.WriteUint32(b, uint32(h.Version))
|
|
}
|
|
if len(h.DiversificationNonce) > 0 {
|
|
b.Write(h.DiversificationNonce)
|
|
}
|
|
|
|
switch h.PacketNumberLen {
|
|
case protocol.PacketNumberLen1:
|
|
b.WriteByte(uint8(h.PacketNumber))
|
|
case protocol.PacketNumberLen2:
|
|
utils.BigEndian.WriteUint16(b, uint16(h.PacketNumber))
|
|
case protocol.PacketNumberLen4:
|
|
utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber))
|
|
case protocol.PacketNumberLen6:
|
|
return errInvalidPacketNumberLen6
|
|
default:
|
|
return errors.New("PublicHeader: PacketNumberLen not set")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// parsePublicHeader parses a QUIC packet's Public Header.
|
|
// The packetSentBy is the perspective of the peer that sent this PublicHeader, i.e. if we're the server, packetSentBy should be PerspectiveClient.
|
|
func parsePublicHeader(b *bytes.Reader, packetSentBy protocol.Perspective) (*Header, error) {
|
|
header := &Header{}
|
|
|
|
// First byte
|
|
publicFlagByte, err := b.ReadByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
header.ResetFlag = publicFlagByte&0x02 > 0
|
|
header.VersionFlag = publicFlagByte&0x01 > 0
|
|
|
|
// TODO: activate this check once Chrome sends the correct value
|
|
// see https://github.com/lucas-clemente/quic-go/issues/232
|
|
// if publicFlagByte&0x04 > 0 {
|
|
// return nil, errors.New("diversification nonces should only be sent by servers")
|
|
// }
|
|
|
|
hasConnectionID := publicFlagByte&0x08 > 0
|
|
if !hasConnectionID && packetSentBy == protocol.PerspectiveClient {
|
|
return nil, qerr.Error(qerr.InvalidPacketHeader, "receiving packets with omitted ConnectionID is not supported")
|
|
}
|
|
if header.hasPacketNumber(packetSentBy) {
|
|
switch publicFlagByte & 0x30 {
|
|
case 0x30:
|
|
return nil, errInvalidPacketNumberLen6
|
|
case 0x20:
|
|
header.PacketNumberLen = protocol.PacketNumberLen4
|
|
case 0x10:
|
|
header.PacketNumberLen = protocol.PacketNumberLen2
|
|
case 0x00:
|
|
header.PacketNumberLen = protocol.PacketNumberLen1
|
|
}
|
|
}
|
|
|
|
// Connection ID
|
|
if hasConnectionID {
|
|
connID, err := protocol.ReadConnectionID(b, 8)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if connID[0] == 0 && connID[1] == 0 && connID[2] == 0 && connID[3] == 0 && connID[4] == 0 && connID[5] == 0 && connID[6] == 0 && connID[7] == 0 {
|
|
return nil, errInvalidConnectionID
|
|
}
|
|
header.DestConnectionID = connID
|
|
}
|
|
|
|
// Contrary to what the gQUIC wire spec says, the 0x4 bit only indicates the presence of the diversification nonce for packets sent by the server.
|
|
// It doesn't have any meaning when sent by the client.
|
|
if packetSentBy == protocol.PerspectiveServer && publicFlagByte&0x04 > 0 {
|
|
if !header.VersionFlag && !header.ResetFlag {
|
|
header.DiversificationNonce = make([]byte, 32)
|
|
if _, err := io.ReadFull(b, header.DiversificationNonce); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
// Version (optional)
|
|
if !header.ResetFlag && header.VersionFlag {
|
|
if packetSentBy == protocol.PerspectiveServer { // parse the version negotiation packet
|
|
if b.Len() == 0 {
|
|
return nil, qerr.Error(qerr.InvalidVersionNegotiationPacket, "empty version list")
|
|
}
|
|
if b.Len()%4 != 0 {
|
|
return nil, qerr.InvalidVersionNegotiationPacket
|
|
}
|
|
header.IsVersionNegotiation = true
|
|
header.SupportedVersions = make([]protocol.VersionNumber, 0)
|
|
for {
|
|
var versionTag uint32
|
|
versionTag, err = utils.BigEndian.ReadUint32(b)
|
|
if err != nil {
|
|
break
|
|
}
|
|
v := protocol.VersionNumber(versionTag)
|
|
header.SupportedVersions = append(header.SupportedVersions, v)
|
|
}
|
|
// a version negotiation packet doesn't have a packet number
|
|
return header, nil
|
|
}
|
|
// packet was sent by the client. Read the version number
|
|
var versionTag uint32
|
|
versionTag, err = utils.BigEndian.ReadUint32(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
header.Version = protocol.VersionNumber(versionTag)
|
|
}
|
|
|
|
// Packet number
|
|
if header.hasPacketNumber(packetSentBy) {
|
|
packetNumber, err := utils.BigEndian.ReadUintN(b, uint8(header.PacketNumberLen))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
header.PacketNumber = protocol.PacketNumber(packetNumber)
|
|
}
|
|
return header, nil
|
|
}
|
|
|
|
// getPublicHeaderLength gets the length of the publicHeader in bytes.
|
|
// It can only be called for regular packets.
|
|
func (h *Header) getPublicHeaderLength(pers protocol.Perspective) (protocol.ByteCount, error) {
|
|
length := protocol.ByteCount(1) // 1 byte for public flags
|
|
if h.PacketNumberLen == protocol.PacketNumberLen6 {
|
|
return 0, errInvalidPacketNumberLen6
|
|
}
|
|
if h.PacketNumberLen != protocol.PacketNumberLen1 && h.PacketNumberLen != protocol.PacketNumberLen2 && h.PacketNumberLen != protocol.PacketNumberLen4 {
|
|
return 0, errPacketNumberLenNotSet
|
|
}
|
|
length += protocol.ByteCount(h.PacketNumberLen)
|
|
length += protocol.ByteCount(h.DestConnectionID.Len()) // if set, always 8 bytes
|
|
// Version Number in packets sent by the client
|
|
if h.VersionFlag {
|
|
length += 4
|
|
}
|
|
length += protocol.ByteCount(len(h.DiversificationNonce))
|
|
return length, nil
|
|
}
|
|
|
|
// hasPacketNumber determines if this Public Header will contain a packet number
|
|
// this depends on the ResetFlag, the VersionFlag and who sent the packet
|
|
func (h *Header) hasPacketNumber(packetSentBy protocol.Perspective) bool {
|
|
if h.ResetFlag {
|
|
return false
|
|
}
|
|
if h.VersionFlag && packetSentBy == protocol.PerspectiveServer {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (h *Header) logPublicHeader(logger utils.Logger) {
|
|
ver := "(unset)"
|
|
if h.Version != 0 {
|
|
ver = h.Version.String()
|
|
}
|
|
logger.Debugf("\tPublic Header{ConnectionID: %s, PacketNumber: %#x, PacketNumberLen: %d, Version: %s, DiversificationNonce: %#v}", h.DestConnectionID, h.PacketNumber, h.PacketNumberLen, ver, h.DiversificationNonce)
|
|
}
|