// 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 ( "bytes" "encoding/binary" "errors" "time" ) func (c *Conn) handleDatagram(now time.Time, dgram *datagram) { buf := dgram.b c.loss.datagramReceived(now, len(buf)) if c.isDraining() { return } for len(buf) > 0 { var n int ptype := getPacketType(buf) switch ptype { case packetTypeInitial: if c.side == serverSide && len(dgram.b) < paddedInitialDatagramSize { // Discard client-sent Initial packets in too-short datagrams. // https://www.rfc-editor.org/rfc/rfc9000#section-14.1-4 return } n = c.handleLongHeader(now, ptype, initialSpace, c.keysInitial.r, buf) case packetTypeHandshake: n = c.handleLongHeader(now, ptype, handshakeSpace, c.keysHandshake.r, buf) case packetType1RTT: n = c.handle1RTT(now, buf) case packetTypeRetry: c.handleRetry(now, buf) return case packetTypeVersionNegotiation: c.handleVersionNegotiation(now, buf) return default: n = -1 } if n <= 0 { // We don't expect to get a stateless reset with a valid // destination connection ID, since the sender of a stateless // reset doesn't know what the connection ID is. // // We're required to perform this check anyway. // // "[...] the comparison MUST be performed when the first packet // in an incoming datagram [...] cannot be decrypted." // https://www.rfc-editor.org/rfc/rfc9000#section-10.3.1-2 if len(buf) == len(dgram.b) && len(buf) > statelessResetTokenLen { var token statelessResetToken copy(token[:], buf[len(buf)-len(token):]) c.handleStatelessReset(now, token) } // Invalid data at the end of a datagram is ignored. break } c.idleHandlePacketReceived(now) buf = buf[n:] } } func (c *Conn) handleLongHeader(now time.Time, ptype packetType, space numberSpace, k fixedKeys, buf []byte) int { if !k.isSet() { return skipLongHeaderPacket(buf) } pnumMax := c.acks[space].largestSeen() p, n := parseLongHeaderPacket(buf, k, pnumMax) if n < 0 { return -1 } if buf[0]&reservedLongBits != 0 { // Reserved header bits must be 0. // https://www.rfc-editor.org/rfc/rfc9000#section-17.2-8.2.1 c.abort(now, localTransportError{ code: errProtocolViolation, reason: "reserved header bits are not zero", }) return -1 } if p.version != quicVersion1 { // The peer has changed versions on us mid-handshake? c.abort(now, localTransportError{ code: errProtocolViolation, reason: "protocol version changed during handshake", }) return -1 } if !c.acks[space].shouldProcess(p.num) { return n } if logPackets { logInboundLongPacket(c, p) } if c.logEnabled(QLogLevelPacket) { c.logLongPacketReceived(p, buf[:n]) } c.connIDState.handlePacket(c, p.ptype, p.srcConnID) ackEliciting := c.handleFrames(now, ptype, space, p.payload) c.acks[space].receive(now, space, p.num, ackEliciting) if p.ptype == packetTypeHandshake && c.side == serverSide { c.loss.validateClientAddress() // "[...] a server MUST discard Initial keys when it first successfully // processes a Handshake packet [...]" // https://www.rfc-editor.org/rfc/rfc9001#section-4.9.1-2 c.discardKeys(now, initialSpace) } return n } func (c *Conn) handle1RTT(now time.Time, buf []byte) int { if !c.keysAppData.canRead() { // 1-RTT packets extend to the end of the datagram, // so skip the remainder of the datagram if we can't parse this. return len(buf) } pnumMax := c.acks[appDataSpace].largestSeen() p, err := parse1RTTPacket(buf, &c.keysAppData, connIDLen, pnumMax) if err != nil { // A localTransportError terminates the connection. // Other errors indicate an unparseable packet, but otherwise may be ignored. if _, ok := err.(localTransportError); ok { c.abort(now, err) } return -1 } if buf[0]&reserved1RTTBits != 0 { // Reserved header bits must be 0. // https://www.rfc-editor.org/rfc/rfc9000#section-17.3.1-4.8.1 c.abort(now, localTransportError{ code: errProtocolViolation, reason: "reserved header bits are not zero", }) return -1 } if !c.acks[appDataSpace].shouldProcess(p.num) { return len(buf) } if logPackets { logInboundShortPacket(c, p) } if c.logEnabled(QLogLevelPacket) { c.log1RTTPacketReceived(p, buf) } ackEliciting := c.handleFrames(now, packetType1RTT, appDataSpace, p.payload) c.acks[appDataSpace].receive(now, appDataSpace, p.num, ackEliciting) return len(buf) } func (c *Conn) handleRetry(now time.Time, pkt []byte) { if c.side != clientSide { return // clients don't send Retry packets } // "After the client has received and processed an Initial or Retry packet // from the server, it MUST discard any subsequent Retry packets that it receives." // https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2-1 if !c.keysInitial.canRead() { return // discarded Initial keys, connection is already established } if c.acks[initialSpace].seen.numRanges() != 0 { return // processed at least one packet } if c.retryToken != nil { return // received a Retry already } // "Clients MUST discard Retry packets that have a Retry Integrity Tag // that cannot be validated." // https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2-2 p, ok := parseRetryPacket(pkt, c.connIDState.originalDstConnID) if !ok { return } // "A client MUST discard a Retry packet with a zero-length Retry Token field." // https://www.rfc-editor.org/rfc/rfc9000#section-17.2.5.2-2 if len(p.token) == 0 { return } c.retryToken = cloneBytes(p.token) c.connIDState.handleRetryPacket(p.srcConnID) // We need to resend any data we've already sent in Initial packets. // We must not reuse already sent packet numbers. c.loss.discardPackets(initialSpace, c.handleAckOrLoss) // TODO: Discard 0-RTT packets as well, once we support 0-RTT. } var errVersionNegotiation = errors.New("server does not support QUIC version 1") func (c *Conn) handleVersionNegotiation(now time.Time, pkt []byte) { if c.side != clientSide { return // servers don't handle Version Negotiation packets } // "A client MUST discard any Version Negotiation packet if it has // received and successfully processed any other packet [...]" // https://www.rfc-editor.org/rfc/rfc9000#section-6.2-2 if !c.keysInitial.canRead() { return // discarded Initial keys, connection is already established } if c.acks[initialSpace].seen.numRanges() != 0 { return // processed at least one packet } _, srcConnID, versions := parseVersionNegotiation(pkt) if len(c.connIDState.remote) < 1 || !bytes.Equal(c.connIDState.remote[0].cid, srcConnID) { return // Source Connection ID doesn't match what we sent } for len(versions) >= 4 { ver := binary.BigEndian.Uint32(versions) if ver == 1 { // "A client MUST discard a Version Negotiation packet that lists // the QUIC version selected by the client." // https://www.rfc-editor.org/rfc/rfc9000#section-6.2-2 return } versions = versions[4:] } // "A client that supports only this version of QUIC MUST // abandon the current connection attempt if it receives // a Version Negotiation packet, [with the two exceptions handled above]." // https://www.rfc-editor.org/rfc/rfc9000#section-6.2-2 c.abortImmediately(now, errVersionNegotiation) } func (c *Conn) handleFrames(now time.Time, ptype packetType, space numberSpace, payload []byte) (ackEliciting bool) { if len(payload) == 0 { // "An endpoint MUST treat receipt of a packet containing no frames // as a connection error of type PROTOCOL_VIOLATION." // https://www.rfc-editor.org/rfc/rfc9000#section-12.4-3 c.abort(now, localTransportError{ code: errProtocolViolation, reason: "packet contains no frames", }) return false } // frameOK verifies that ptype is one of the packets in mask. frameOK := func(c *Conn, ptype, mask packetType) (ok bool) { if ptype&mask == 0 { // "An endpoint MUST treat receipt of a frame in a packet type // that is not permitted as a connection error of type // PROTOCOL_VIOLATION." // https://www.rfc-editor.org/rfc/rfc9000#section-12.4-3 c.abort(now, localTransportError{ code: errProtocolViolation, reason: "frame not allowed in packet", }) return false } return true } // Packet masks from RFC 9000 Table 3. // https://www.rfc-editor.org/rfc/rfc9000#table-3 const ( IH_1 = packetTypeInitial | packetTypeHandshake | packetType1RTT __01 = packetType0RTT | packetType1RTT ___1 = packetType1RTT ) for len(payload) > 0 { switch payload[0] { case frameTypePadding, frameTypeAck, frameTypeAckECN, frameTypeConnectionCloseTransport, frameTypeConnectionCloseApplication: default: ackEliciting = true } n := -1 switch payload[0] { case frameTypePadding: // PADDING is OK in all spaces. n = 1 case frameTypePing: // PING is OK in all spaces. // // A PING frame causes us to respond with an ACK by virtue of being // an ack-eliciting frame, but requires no other action. n = 1 case frameTypeAck, frameTypeAckECN: if !frameOK(c, ptype, IH_1) { return } n = c.handleAckFrame(now, space, payload) case frameTypeResetStream: if !frameOK(c, ptype, __01) { return } n = c.handleResetStreamFrame(now, space, payload) case frameTypeStopSending: if !frameOK(c, ptype, __01) { return } n = c.handleStopSendingFrame(now, space, payload) case frameTypeCrypto: if !frameOK(c, ptype, IH_1) { return } n = c.handleCryptoFrame(now, space, payload) case frameTypeNewToken: if !frameOK(c, ptype, ___1) { return } _, n = consumeNewTokenFrame(payload) case 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f: // STREAM if !frameOK(c, ptype, __01) { return } n = c.handleStreamFrame(now, space, payload) case frameTypeMaxData: if !frameOK(c, ptype, __01) { return } n = c.handleMaxDataFrame(now, payload) case frameTypeMaxStreamData: if !frameOK(c, ptype, __01) { return } n = c.handleMaxStreamDataFrame(now, payload) case frameTypeMaxStreamsBidi, frameTypeMaxStreamsUni: if !frameOK(c, ptype, __01) { return } n = c.handleMaxStreamsFrame(now, payload) case frameTypeDataBlocked: if !frameOK(c, ptype, __01) { return } _, n = consumeDataBlockedFrame(payload) case frameTypeStreamsBlockedBidi, frameTypeStreamsBlockedUni: if !frameOK(c, ptype, __01) { return } _, _, n = consumeStreamsBlockedFrame(payload) case frameTypeStreamDataBlocked: if !frameOK(c, ptype, __01) { return } _, _, n = consumeStreamDataBlockedFrame(payload) case frameTypeNewConnectionID: if !frameOK(c, ptype, __01) { return } n = c.handleNewConnectionIDFrame(now, space, payload) case frameTypeRetireConnectionID: if !frameOK(c, ptype, __01) { return } n = c.handleRetireConnectionIDFrame(now, space, payload) case frameTypeConnectionCloseTransport: // Transport CONNECTION_CLOSE is OK in all spaces. n = c.handleConnectionCloseTransportFrame(now, payload) case frameTypeConnectionCloseApplication: if !frameOK(c, ptype, __01) { return } n = c.handleConnectionCloseApplicationFrame(now, payload) case frameTypeHandshakeDone: if !frameOK(c, ptype, ___1) { return } n = c.handleHandshakeDoneFrame(now, space, payload) } if n < 0 { c.abort(now, localTransportError{ code: errFrameEncoding, reason: "frame encoding error", }) return false } payload = payload[n:] } return ackEliciting } func (c *Conn) handleAckFrame(now time.Time, space numberSpace, payload []byte) int { c.loss.receiveAckStart() largest, ackDelay, n := consumeAckFrame(payload, func(rangeIndex int, start, end packetNumber) { if end > c.loss.nextNumber(space) { // Acknowledgement of a packet we never sent. c.abort(now, localTransportError{ code: errProtocolViolation, reason: "acknowledgement for unsent packet", }) return } c.loss.receiveAckRange(now, space, rangeIndex, start, end, c.handleAckOrLoss) }) // Prior to receiving the peer's transport parameters, we cannot // interpret the ACK Delay field because we don't know the ack_delay_exponent // to apply. // // For servers, we should always know the ack_delay_exponent because the // client's transport parameters are carried in its Initial packets and we // won't send an ack-eliciting Initial packet until after receiving the last // client Initial packet. // // For clients, we won't receive the server's transport parameters until handling // its Handshake flight, which will probably happen after reading its ACK for our // Initial packet(s). However, the peer's acknowledgement delay cannot reduce our // adjusted RTT sample below min_rtt, and min_rtt is generally going to be set // by the packet containing the ACK for our Initial flight. Therefore, the // ACK Delay for an ACK in the Initial space is likely to be ignored anyway. // // Long story short, setting the delay to 0 prior to reading transport parameters // is usually going to have no effect, will have only a minor effect in the rare // cases when it happens, and there aren't any good alternatives anyway since we // can't interpret the ACK Delay field without knowing the exponent. var delay time.Duration if c.peerAckDelayExponent >= 0 { delay = ackDelay.Duration(uint8(c.peerAckDelayExponent)) } c.loss.receiveAckEnd(now, space, delay, c.handleAckOrLoss) if space == appDataSpace { c.keysAppData.handleAckFor(largest) } return n } func (c *Conn) handleMaxDataFrame(now time.Time, payload []byte) int { maxData, n := consumeMaxDataFrame(payload) if n < 0 { return -1 } c.streams.outflow.setMaxData(maxData) return n } func (c *Conn) handleMaxStreamDataFrame(now time.Time, payload []byte) int { id, maxStreamData, n := consumeMaxStreamDataFrame(payload) if n < 0 { return -1 } if s := c.streamForFrame(now, id, sendStream); s != nil { if err := s.handleMaxStreamData(maxStreamData); err != nil { c.abort(now, err) return -1 } } return n } func (c *Conn) handleMaxStreamsFrame(now time.Time, payload []byte) int { styp, max, n := consumeMaxStreamsFrame(payload) if n < 0 { return -1 } c.streams.localLimit[styp].setMax(max) return n } func (c *Conn) handleResetStreamFrame(now time.Time, space numberSpace, payload []byte) int { id, code, finalSize, n := consumeResetStreamFrame(payload) if n < 0 { return -1 } if s := c.streamForFrame(now, id, recvStream); s != nil { if err := s.handleReset(code, finalSize); err != nil { c.abort(now, err) } } return n } func (c *Conn) handleStopSendingFrame(now time.Time, space numberSpace, payload []byte) int { id, code, n := consumeStopSendingFrame(payload) if n < 0 { return -1 } if s := c.streamForFrame(now, id, sendStream); s != nil { if err := s.handleStopSending(code); err != nil { c.abort(now, err) } } return n } func (c *Conn) handleCryptoFrame(now time.Time, space numberSpace, payload []byte) int { off, data, n := consumeCryptoFrame(payload) err := c.handleCrypto(now, space, off, data) if err != nil { c.abort(now, err) return -1 } return n } func (c *Conn) handleStreamFrame(now time.Time, space numberSpace, payload []byte) int { id, off, fin, b, n := consumeStreamFrame(payload) if n < 0 { return -1 } if s := c.streamForFrame(now, id, recvStream); s != nil { if err := s.handleData(off, b, fin); err != nil { c.abort(now, err) } } return n } func (c *Conn) handleNewConnectionIDFrame(now time.Time, space numberSpace, payload []byte) int { seq, retire, connID, resetToken, n := consumeNewConnectionIDFrame(payload) if n < 0 { return -1 } if err := c.connIDState.handleNewConnID(c, seq, retire, connID, resetToken); err != nil { c.abort(now, err) } return n } func (c *Conn) handleRetireConnectionIDFrame(now time.Time, space numberSpace, payload []byte) int { seq, n := consumeRetireConnectionIDFrame(payload) if n < 0 { return -1 } if err := c.connIDState.handleRetireConnID(c, seq); err != nil { c.abort(now, err) } return n } func (c *Conn) handleConnectionCloseTransportFrame(now time.Time, payload []byte) int { code, _, reason, n := consumeConnectionCloseTransportFrame(payload) if n < 0 { return -1 } c.handlePeerConnectionClose(now, peerTransportError{code: code, reason: reason}) return n } func (c *Conn) handleConnectionCloseApplicationFrame(now time.Time, payload []byte) int { code, reason, n := consumeConnectionCloseApplicationFrame(payload) if n < 0 { return -1 } c.handlePeerConnectionClose(now, &ApplicationError{Code: code, Reason: reason}) return n } func (c *Conn) handleHandshakeDoneFrame(now time.Time, space numberSpace, payload []byte) int { if c.side == serverSide { // Clients should never send HANDSHAKE_DONE. // https://www.rfc-editor.org/rfc/rfc9000#section-19.20-4 c.abort(now, localTransportError{ code: errProtocolViolation, reason: "client sent HANDSHAKE_DONE", }) return -1 } if c.isAlive() { c.confirmHandshake(now) } return 1 } var errStatelessReset = errors.New("received stateless reset") func (c *Conn) handleStatelessReset(now time.Time, resetToken statelessResetToken) { if !c.connIDState.isValidStatelessResetToken(resetToken) { return } c.setFinalError(errStatelessReset) c.enterDraining(now) }