// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build go1.21 package quic import ( "encoding/binary" "fmt" ) // packetType is a QUIC packet type. // https://www.rfc-editor.org/rfc/rfc9000.html#section-17 type packetType byte const ( packetTypeInvalid = packetType(iota) packetTypeInitial packetType0RTT packetTypeHandshake packetTypeRetry packetType1RTT packetTypeVersionNegotiation ) func (p packetType) String() string { switch p { case packetTypeInitial: return "Initial" case packetType0RTT: return "0-RTT" case packetTypeHandshake: return "Handshake" case packetTypeRetry: return "Retry" case packetType1RTT: return "1-RTT" } return fmt.Sprintf("unknown packet type %v", byte(p)) } func (p packetType) qlogString() string { switch p { case packetTypeInitial: return "initial" case packetType0RTT: return "0RTT" case packetTypeHandshake: return "handshake" case packetTypeRetry: return "retry" case packetType1RTT: return "1RTT" } return "unknown" } // Bits set in the first byte of a packet. const ( headerFormLong = 0x80 // https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2-3.2.1 headerFormShort = 0x00 // https://www.rfc-editor.org/rfc/rfc9000.html#section-17.3.1-4.2.1 fixedBit = 0x40 // https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2-3.4.1 reservedLongBits = 0x0c // https://www.rfc-editor.org/rfc/rfc9000#section-17.2-8.2.1 reserved1RTTBits = 0x18 // https://www.rfc-editor.org/rfc/rfc9000#section-17.3.1-4.8.1 keyPhaseBit = 0x04 // https://www.rfc-editor.org/rfc/rfc9000#section-17.3.1-4.10.1 ) // Long Packet Type bits. // https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2-3.6.1 const ( longPacketTypeInitial = 0 << 4 longPacketType0RTT = 1 << 4 longPacketTypeHandshake = 2 << 4 longPacketTypeRetry = 3 << 4 ) // Frame types. // https://www.rfc-editor.org/rfc/rfc9000.html#section-19 const ( frameTypePadding = 0x00 frameTypePing = 0x01 frameTypeAck = 0x02 frameTypeAckECN = 0x03 frameTypeResetStream = 0x04 frameTypeStopSending = 0x05 frameTypeCrypto = 0x06 frameTypeNewToken = 0x07 frameTypeStreamBase = 0x08 // low three bits carry stream flags frameTypeMaxData = 0x10 frameTypeMaxStreamData = 0x11 frameTypeMaxStreamsBidi = 0x12 frameTypeMaxStreamsUni = 0x13 frameTypeDataBlocked = 0x14 frameTypeStreamDataBlocked = 0x15 frameTypeStreamsBlockedBidi = 0x16 frameTypeStreamsBlockedUni = 0x17 frameTypeNewConnectionID = 0x18 frameTypeRetireConnectionID = 0x19 frameTypePathChallenge = 0x1a frameTypePathResponse = 0x1b frameTypeConnectionCloseTransport = 0x1c frameTypeConnectionCloseApplication = 0x1d frameTypeHandshakeDone = 0x1e ) // The low three bits of STREAM frames. // https://www.rfc-editor.org/rfc/rfc9000.html#section-19.8 const ( streamOffBit = 0x04 streamLenBit = 0x02 streamFinBit = 0x01 ) // Maximum length of a connection ID. const maxConnIDLen = 20 // isLongHeader returns true if b is the first byte of a long header. func isLongHeader(b byte) bool { return b&headerFormLong == headerFormLong } // getPacketType returns the type of a packet. func getPacketType(b []byte) packetType { if len(b) == 0 { return packetTypeInvalid } if !isLongHeader(b[0]) { if b[0]&fixedBit != fixedBit { return packetTypeInvalid } return packetType1RTT } if len(b) < 5 { return packetTypeInvalid } if b[1] == 0 && b[2] == 0 && b[3] == 0 && b[4] == 0 { // Version Negotiation packets don't necessarily set the fixed bit. return packetTypeVersionNegotiation } if b[0]&fixedBit != fixedBit { return packetTypeInvalid } switch b[0] & 0x30 { case longPacketTypeInitial: return packetTypeInitial case longPacketType0RTT: return packetType0RTT case longPacketTypeHandshake: return packetTypeHandshake case longPacketTypeRetry: return packetTypeRetry } return packetTypeInvalid } // dstConnIDForDatagram returns the destination connection ID field of the // first QUIC packet in a datagram. func dstConnIDForDatagram(pkt []byte) (id []byte, ok bool) { if len(pkt) < 1 { return nil, false } var n int var b []byte if isLongHeader(pkt[0]) { if len(pkt) < 6 { return nil, false } n = int(pkt[5]) b = pkt[6:] } else { n = connIDLen b = pkt[1:] } if len(b) < n { return nil, false } return b[:n], true } // parseVersionNegotiation parses a Version Negotiation packet. // The returned versions is a slice of big-endian uint32s. // It returns (nil, nil, nil) for an invalid packet. func parseVersionNegotiation(pkt []byte) (dstConnID, srcConnID, versions []byte) { p, ok := parseGenericLongHeaderPacket(pkt) if !ok { return nil, nil, nil } if len(p.data)%4 != 0 { return nil, nil, nil } return p.dstConnID, p.srcConnID, p.data } // appendVersionNegotiation appends a Version Negotiation packet to pkt, // returning the result. func appendVersionNegotiation(pkt, dstConnID, srcConnID []byte, versions ...uint32) []byte { pkt = append(pkt, headerFormLong|fixedBit) // header byte pkt = append(pkt, 0, 0, 0, 0) // Version (0 for Version Negotiation) pkt = appendUint8Bytes(pkt, dstConnID) // Destination Connection ID pkt = appendUint8Bytes(pkt, srcConnID) // Source Connection ID for _, v := range versions { pkt = binary.BigEndian.AppendUint32(pkt, v) // Supported Version } return pkt } // A longPacket is a long header packet. type longPacket struct { ptype packetType version uint32 num packetNumber dstConnID []byte srcConnID []byte payload []byte // The extra data depends on the packet type: // Initial: Token. // Retry: Retry token and integrity tag. extra []byte } // A shortPacket is a short header (1-RTT) packet. type shortPacket struct { num packetNumber payload []byte } // A genericLongPacket is a long header packet of an arbitrary QUIC version. // https://www.rfc-editor.org/rfc/rfc8999#section-5.1 type genericLongPacket struct { version uint32 dstConnID []byte srcConnID []byte data []byte } func parseGenericLongHeaderPacket(b []byte) (p genericLongPacket, ok bool) { if len(b) < 5 || !isLongHeader(b[0]) { return genericLongPacket{}, false } b = b[1:] // Version (32), var n int p.version, n = consumeUint32(b) if n < 0 { return genericLongPacket{}, false } b = b[n:] // Destination Connection ID Length (8), // Destination Connection ID (0..2048), p.dstConnID, n = consumeUint8Bytes(b) if n < 0 || len(p.dstConnID) > 2048/8 { return genericLongPacket{}, false } b = b[n:] // Source Connection ID Length (8), // Source Connection ID (0..2048), p.srcConnID, n = consumeUint8Bytes(b) if n < 0 || len(p.dstConnID) > 2048/8 { return genericLongPacket{}, false } b = b[n:] p.data = b return p, true }