// 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 // parseLongHeaderPacket parses a QUIC long header packet. // // It does not parse Version Negotiation packets. // // On input, pkt contains a long header packet (possibly followed by more packets), // k the decryption keys for the packet, and pnumMax the largest packet number seen // in the number space of this packet. // // parseLongHeaderPacket returns the parsed packet with protection removed // and its length in bytes. // // It returns an empty packet and -1 if the packet could not be parsed. func parseLongHeaderPacket(pkt []byte, k fixedKeys, pnumMax packetNumber) (p longPacket, n int) { if len(pkt) < 5 || !isLongHeader(pkt[0]) { return longPacket{}, -1 } // Header Form (1) = 1, // Fixed Bit (1) = 1, // Long Packet Type (2), // Type-Specific Bits (4), b := pkt p.ptype = getPacketType(b) if p.ptype == packetTypeInvalid { return longPacket{}, -1 } b = b[1:] // Version (32), p.version, n = consumeUint32(b) if n < 0 { return longPacket{}, -1 } b = b[n:] if p.version == 0 { // Version Negotiation packet; not handled here. return longPacket{}, -1 } // Destination Connection ID Length (8), // Destination Connection ID (0..160), p.dstConnID, n = consumeUint8Bytes(b) if n < 0 || len(p.dstConnID) > maxConnIDLen { return longPacket{}, -1 } b = b[n:] // Source Connection ID Length (8), // Source Connection ID (0..160), p.srcConnID, n = consumeUint8Bytes(b) if n < 0 || len(p.dstConnID) > maxConnIDLen { return longPacket{}, -1 } b = b[n:] switch p.ptype { case packetTypeInitial: // Token Length (i), // Token (..), p.extra, n = consumeVarintBytes(b) if n < 0 { return longPacket{}, -1 } b = b[n:] case packetTypeRetry: // Retry Token (..), // Retry Integrity Tag (128), p.extra = b return p, len(pkt) } // Length (i), payLen, n := consumeVarint(b) if n < 0 { return longPacket{}, -1 } b = b[n:] if uint64(len(b)) < payLen { return longPacket{}, -1 } // Packet Number (8..32), // Packet Payload (..), pnumOff := len(pkt) - len(b) pkt = pkt[:pnumOff+int(payLen)] if k.isSet() { var err error p.payload, p.num, err = k.unprotect(pkt, pnumOff, pnumMax) if err != nil { return longPacket{}, -1 } } return p, len(pkt) } // skipLongHeaderPacket returns the length of the long header packet at the start of pkt, // or -1 if the buffer does not contain a valid packet. func skipLongHeaderPacket(pkt []byte) int { // Header byte, 4 bytes of version. n := 5 if len(pkt) <= n { return -1 } // Destination connection ID length, destination connection ID. n += 1 + int(pkt[n]) if len(pkt) <= n { return -1 } // Source connection ID length, source connection ID. n += 1 + int(pkt[n]) if len(pkt) <= n { return -1 } if getPacketType(pkt) == packetTypeInitial { // Token length, token. _, nn := consumeVarintBytes(pkt[n:]) if nn < 0 { return -1 } n += nn } // Length, packet number, payload. _, nn := consumeVarintBytes(pkt[n:]) if nn < 0 { return -1 } n += nn if len(pkt) < n { return -1 } return n } // parse1RTTPacket parses a QUIC 1-RTT (short header) packet. // // On input, pkt contains a short header packet, k the decryption keys for the packet, // and pnumMax the largest packet number seen in the number space of this packet. func parse1RTTPacket(pkt []byte, k *updatingKeyPair, dstConnIDLen int, pnumMax packetNumber) (p shortPacket, err error) { pay, pnum, err := k.unprotect(pkt, 1+dstConnIDLen, pnumMax) if err != nil { return shortPacket{}, err } p.num = pnum p.payload = pay return p, nil } // Consume functions return n=-1 on conditions which result in FRAME_ENCODING_ERROR, // which includes both general parse failures and specific violations of frame // constraints. func consumeAckFrame(frame []byte, f func(rangeIndex int, start, end packetNumber)) (largest packetNumber, ackDelay unscaledAckDelay, n int) { b := frame[1:] // type largestAck, n := consumeVarint(b) if n < 0 { return 0, 0, -1 } b = b[n:] v, n := consumeVarintInt64(b) if n < 0 { return 0, 0, -1 } b = b[n:] ackDelay = unscaledAckDelay(v) ackRangeCount, n := consumeVarint(b) if n < 0 { return 0, 0, -1 } b = b[n:] rangeMax := packetNumber(largestAck) for i := uint64(0); ; i++ { rangeLen, n := consumeVarint(b) if n < 0 { return 0, 0, -1 } b = b[n:] rangeMin := rangeMax - packetNumber(rangeLen) if rangeMin < 0 || rangeMin > rangeMax { return 0, 0, -1 } f(int(i), rangeMin, rangeMax+1) if i == ackRangeCount { break } gap, n := consumeVarint(b) if n < 0 { return 0, 0, -1 } b = b[n:] rangeMax = rangeMin - packetNumber(gap) - 2 } if frame[0] != frameTypeAckECN { return packetNumber(largestAck), ackDelay, len(frame) - len(b) } ect0Count, n := consumeVarint(b) if n < 0 { return 0, 0, -1 } b = b[n:] ect1Count, n := consumeVarint(b) if n < 0 { return 0, 0, -1 } b = b[n:] ecnCECount, n := consumeVarint(b) if n < 0 { return 0, 0, -1 } b = b[n:] // TODO: Make use of ECN feedback. // https://www.rfc-editor.org/rfc/rfc9000.html#section-19.3.2 _ = ect0Count _ = ect1Count _ = ecnCECount return packetNumber(largestAck), ackDelay, len(frame) - len(b) } func consumeResetStreamFrame(b []byte) (id streamID, code uint64, finalSize int64, n int) { n = 1 idInt, nn := consumeVarint(b[n:]) if nn < 0 { return 0, 0, 0, -1 } n += nn code, nn = consumeVarint(b[n:]) if nn < 0 { return 0, 0, 0, -1 } n += nn v, nn := consumeVarint(b[n:]) if nn < 0 { return 0, 0, 0, -1 } n += nn finalSize = int64(v) return streamID(idInt), code, finalSize, n } func consumeStopSendingFrame(b []byte) (id streamID, code uint64, n int) { n = 1 idInt, nn := consumeVarint(b[n:]) if nn < 0 { return 0, 0, -1 } n += nn code, nn = consumeVarint(b[n:]) if nn < 0 { return 0, 0, -1 } n += nn return streamID(idInt), code, n } func consumeCryptoFrame(b []byte) (off int64, data []byte, n int) { n = 1 v, nn := consumeVarint(b[n:]) if nn < 0 { return 0, nil, -1 } off = int64(v) n += nn data, nn = consumeVarintBytes(b[n:]) if nn < 0 { return 0, nil, -1 } n += nn return off, data, n } func consumeNewTokenFrame(b []byte) (token []byte, n int) { n = 1 data, nn := consumeVarintBytes(b[n:]) if nn < 0 { return nil, -1 } if len(data) == 0 { return nil, -1 } n += nn return data, n } func consumeStreamFrame(b []byte) (id streamID, off int64, fin bool, data []byte, n int) { fin = (b[0] & 0x01) != 0 n = 1 idInt, nn := consumeVarint(b[n:]) if nn < 0 { return 0, 0, false, nil, -1 } n += nn if b[0]&0x04 != 0 { v, nn := consumeVarint(b[n:]) if nn < 0 { return 0, 0, false, nil, -1 } n += nn off = int64(v) } if b[0]&0x02 != 0 { data, nn = consumeVarintBytes(b[n:]) if nn < 0 { return 0, 0, false, nil, -1 } n += nn } else { data = b[n:] n += len(data) } if off+int64(len(data)) >= 1<<62 { return 0, 0, false, nil, -1 } return streamID(idInt), off, fin, data, n } func consumeMaxDataFrame(b []byte) (max int64, n int) { n = 1 v, nn := consumeVarint(b[n:]) if nn < 0 { return 0, -1 } n += nn return int64(v), n } func consumeMaxStreamDataFrame(b []byte) (id streamID, max int64, n int) { n = 1 v, nn := consumeVarint(b[n:]) if nn < 0 { return 0, 0, -1 } n += nn id = streamID(v) v, nn = consumeVarint(b[n:]) if nn < 0 { return 0, 0, -1 } n += nn max = int64(v) return id, max, n } func consumeMaxStreamsFrame(b []byte) (typ streamType, max int64, n int) { switch b[0] { case frameTypeMaxStreamsBidi: typ = bidiStream case frameTypeMaxStreamsUni: typ = uniStream default: return 0, 0, -1 } n = 1 v, nn := consumeVarint(b[n:]) if nn < 0 { return 0, 0, -1 } n += nn if v > maxStreamsLimit { return 0, 0, -1 } return typ, int64(v), n } func consumeStreamDataBlockedFrame(b []byte) (id streamID, max int64, n int) { n = 1 v, nn := consumeVarint(b[n:]) if nn < 0 { return 0, 0, -1 } n += nn id = streamID(v) max, nn = consumeVarintInt64(b[n:]) if nn < 0 { return 0, 0, -1 } n += nn return id, max, n } func consumeDataBlockedFrame(b []byte) (max int64, n int) { n = 1 max, nn := consumeVarintInt64(b[n:]) if nn < 0 { return 0, -1 } n += nn return max, n } func consumeStreamsBlockedFrame(b []byte) (typ streamType, max int64, n int) { if b[0] == frameTypeStreamsBlockedBidi { typ = bidiStream } else { typ = uniStream } n = 1 max, nn := consumeVarintInt64(b[n:]) if nn < 0 { return 0, 0, -1 } n += nn return typ, max, n } func consumeNewConnectionIDFrame(b []byte) (seq, retire int64, connID []byte, resetToken statelessResetToken, n int) { n = 1 var nn int seq, nn = consumeVarintInt64(b[n:]) if nn < 0 { return 0, 0, nil, statelessResetToken{}, -1 } n += nn retire, nn = consumeVarintInt64(b[n:]) if nn < 0 { return 0, 0, nil, statelessResetToken{}, -1 } n += nn if seq < retire { return 0, 0, nil, statelessResetToken{}, -1 } connID, nn = consumeVarintBytes(b[n:]) if nn < 0 { return 0, 0, nil, statelessResetToken{}, -1 } if len(connID) < 1 || len(connID) > 20 { return 0, 0, nil, statelessResetToken{}, -1 } n += nn if len(b[n:]) < len(resetToken) { return 0, 0, nil, statelessResetToken{}, -1 } copy(resetToken[:], b[n:]) n += len(resetToken) return seq, retire, connID, resetToken, n } func consumeRetireConnectionIDFrame(b []byte) (seq int64, n int) { n = 1 var nn int seq, nn = consumeVarintInt64(b[n:]) if nn < 0 { return 0, -1 } n += nn return seq, n } func consumePathChallengeFrame(b []byte) (data uint64, n int) { n = 1 var nn int data, nn = consumeUint64(b[n:]) if nn < 0 { return 0, -1 } n += nn return data, n } func consumePathResponseFrame(b []byte) (data uint64, n int) { return consumePathChallengeFrame(b) // identical frame format } func consumeConnectionCloseTransportFrame(b []byte) (code transportError, frameType uint64, reason string, n int) { n = 1 var nn int var codeInt uint64 codeInt, nn = consumeVarint(b[n:]) if nn < 0 { return 0, 0, "", -1 } code = transportError(codeInt) n += nn frameType, nn = consumeVarint(b[n:]) if nn < 0 { return 0, 0, "", -1 } n += nn reasonb, nn := consumeVarintBytes(b[n:]) if nn < 0 { return 0, 0, "", -1 } n += nn reason = string(reasonb) return code, frameType, reason, n } func consumeConnectionCloseApplicationFrame(b []byte) (code uint64, reason string, n int) { n = 1 var nn int code, nn = consumeVarint(b[n:]) if nn < 0 { return 0, "", -1 } n += nn reasonb, nn := consumeVarintBytes(b[n:]) if nn < 0 { return 0, "", -1 } n += nn reason = string(reasonb) return code, reason, n }