| // 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 |
| } |