quic/codec/Decode.cpp (1,002 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include <quic/codec/Decode.h> #include <folly/String.h> #include <quic/QuicException.h> #include <quic/codec/PacketNumber.h> #include <quic/codec/QuicInteger.h> namespace { quic::PacketNum nextAckedPacketGap(quic::PacketNum packetNum, uint64_t gap) { // Gap cannot overflow because of the definition of quic integer encoding, so // we can just add to gap. uint64_t adjustedGap = gap + 2; if (packetNum < adjustedGap) { throw quic::QuicTransportException( "Bad gap", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK); } return packetNum - adjustedGap; } quic::PacketNum nextAckedPacketLen( quic::PacketNum packetNum, uint64_t ackBlockLen) { // Going to allow 0 as a valid value. if (packetNum < ackBlockLen) { throw quic::QuicTransportException( "Bad block len", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK); } return packetNum - ackBlockLen; } } // namespace namespace quic { PaddingFrame decodePaddingFrame(folly::io::Cursor& cursor) { // we might have multiple padding frames in sequence in the common case. // Let's consume all the padding and return 1 padding frame for everything. static_assert( static_cast<int>(FrameType::PADDING) == 0, "Padding value is 0"); folly::ByteRange paddingBytes = cursor.peekBytes(); if (paddingBytes.size() == 0) { return PaddingFrame(); } uint8_t firstByte = paddingBytes.data()[0]; // While type can be variable length, since PADDING frame is always a 0 // byte frame, the length of the type should be 1 byte. if (static_cast<FrameType>(firstByte) != FrameType::PADDING) { return PaddingFrame(); } int ret = memcmp( paddingBytes.data(), paddingBytes.data() + 1, paddingBytes.size() - 1); if (ret == 0) { cursor.skip(paddingBytes.size()); } return PaddingFrame(); } PingFrame decodePingFrame(folly::io::Cursor&) { return PingFrame(); } KnobFrame decodeKnobFrame(folly::io::Cursor& cursor) { auto knobSpace = decodeQuicInteger(cursor); if (!knobSpace) { throw QuicTransportException( "Bad knob space", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::KNOB); } auto knobId = decodeQuicInteger(cursor); if (!knobId) { throw QuicTransportException( "Bad knob id", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::KNOB); } auto knobLen = decodeQuicInteger(cursor); if (!knobLen) { throw QuicTransportException( "Bad knob len", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::KNOB); } Buf knobBlob; cursor.cloneAtMost(knobBlob, knobLen->first); return KnobFrame(knobSpace->first, knobId->first, std::move(knobBlob)); } AckFrequencyFrame decodeAckFrequencyFrame(folly::io::Cursor& cursor) { auto sequenceNumber = decodeQuicInteger(cursor); if (!sequenceNumber) { throw QuicTransportException( "Bad sequence number", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK_FREQUENCY); } auto packetTolerance = decodeQuicInteger(cursor); if (!packetTolerance) { throw QuicTransportException( "Bad packet tolerance", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK_FREQUENCY); } auto updateMaxAckDelay = decodeQuicInteger(cursor); if (!updateMaxAckDelay) { throw QuicTransportException( "Bad update max ack delay", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK_FREQUENCY); } if (cursor.isAtEnd()) { throw QuicTransportException( "Bad ignore order", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK_FREQUENCY); } auto ignoreOrder = cursor.readBE<uint8_t>(); AckFrequencyFrame frame; frame.sequenceNumber = sequenceNumber->first; frame.packetTolerance = packetTolerance->first; frame.updateMaxAckDelay = updateMaxAckDelay->first; frame.ignoreOrder = ignoreOrder; return frame; } ReadAckFrame decodeAckFrame( folly::io::Cursor& cursor, const PacketHeader& header, const CodecParameters& params) { ReadAckFrame frame; auto largestAckedInt = decodeQuicInteger(cursor); if (!largestAckedInt) { throw QuicTransportException( "Bad largest acked", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK); } auto largestAcked = folly::to<PacketNum>(largestAckedInt->first); auto ackDelay = decodeQuicInteger(cursor); if (!ackDelay) { throw QuicTransportException( "Bad ack delay", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK); } auto additionalAckBlocks = decodeQuicInteger(cursor); if (!additionalAckBlocks) { throw QuicTransportException( "Bad ack block count", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK); } auto firstAckBlockLen = decodeQuicInteger(cursor); if (!firstAckBlockLen) { throw QuicTransportException( "Bad first block", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK); } // Using default ack delay for long header packets. Before negotiating // and ack delay, the sender has to use something, so they use the default // ack delay. To keep it consistent the protocol specifies using the same // ack delay for all the long header packets. uint8_t ackDelayExponentToUse = (header.getHeaderForm() == HeaderForm::Long) ? kDefaultAckDelayExponent : params.peerAckDelayExponent; DCHECK_LT(ackDelayExponentToUse, sizeof(ackDelay->first) * 8); // ackDelayExponentToUse is guaranteed to be less than the size of uint64_t uint64_t delayOverflowMask = 0xFFFFFFFFFFFFFFFF; uint8_t leftShift = (sizeof(ackDelay->first) * 8 - ackDelayExponentToUse); DCHECK_LT(leftShift, sizeof(delayOverflowMask) * 8); delayOverflowMask = delayOverflowMask << leftShift; if ((ackDelay->first & delayOverflowMask) != 0) { throw QuicTransportException( "Decoded ack delay overflows", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK); } uint64_t adjustedAckDelay = ackDelay->first << ackDelayExponentToUse; if (adjustedAckDelay > static_cast<uint64_t>( std::numeric_limits<std::chrono::microseconds::rep>::max())) { throw QuicTransportException( "Bad ack delay", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK); } else if (UNLIKELY(adjustedAckDelay > 1000 * 1000 * 1000 /* 1000s */)) { LOG(ERROR) << "Quic recvd long ack delay=" << adjustedAckDelay; adjustedAckDelay = 0; } PacketNum currentPacketNum = nextAckedPacketLen(largestAcked, firstAckBlockLen->first); frame.largestAcked = largestAcked; frame.ackDelay = std::chrono::microseconds(adjustedAckDelay); frame.ackBlocks.emplace_back(currentPacketNum, largestAcked); for (uint64_t numBlocks = 0; numBlocks < additionalAckBlocks->first; ++numBlocks) { auto currentGap = decodeQuicInteger(cursor); if (!currentGap) { throw QuicTransportException( "Bad gap", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK); } auto blockLen = decodeQuicInteger(cursor); if (!blockLen) { throw QuicTransportException( "Bad block len", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK); } PacketNum nextEndPacket = nextAckedPacketGap(currentPacketNum, currentGap->first); currentPacketNum = nextAckedPacketLen(nextEndPacket, blockLen->first); // We don't need to add the entry when the block length is zero since we // already would have processed it in the previous iteration. frame.ackBlocks.emplace_back(currentPacketNum, nextEndPacket); } return frame; } ReadAckFrame decodeAckFrameWithECN( folly::io::Cursor& cursor, const PacketHeader& header, const CodecParameters& params) { // TODO this is incomplete auto readAckFrame = decodeAckFrame(cursor, header, params); // TODO we simply ignore ECN blocks in ACK-ECN frames for now. auto ect_0 = decodeQuicInteger(cursor); if (!ect_0) { throw QuicTransportException( "Bad ECT(0) value", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK_ECN); } auto ect_1 = decodeQuicInteger(cursor); if (!ect_1) { throw QuicTransportException( "Bad ECT(1) value", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK_ECN); } auto ect_ce = decodeQuicInteger(cursor); if (!ect_ce) { throw QuicTransportException( "Bad ECT-CE value", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::ACK_ECN); } return readAckFrame; } RstStreamFrame decodeRstStreamFrame(folly::io::Cursor& cursor) { auto streamId = decodeQuicInteger(cursor); if (!streamId) { throw QuicTransportException( "Bad streamId", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::RST_STREAM); } ApplicationErrorCode errorCode; auto varCode = decodeQuicInteger(cursor); if (varCode) { errorCode = static_cast<ApplicationErrorCode>(varCode->first); } else { throw QuicTransportException( "Cannot decode error code", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::RST_STREAM); } auto offset = decodeQuicInteger(cursor); if (!offset) { throw QuicTransportException( "Bad offset", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::RST_STREAM); } return RstStreamFrame( folly::to<StreamId>(streamId->first), errorCode, offset->first); } StopSendingFrame decodeStopSendingFrame(folly::io::Cursor& cursor) { auto streamId = decodeQuicInteger(cursor); if (!streamId) { throw QuicTransportException( "Bad streamId", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::STOP_SENDING); } ApplicationErrorCode errorCode; auto varCode = decodeQuicInteger(cursor); if (varCode) { errorCode = static_cast<ApplicationErrorCode>(varCode->first); } else { throw QuicTransportException( "Cannot decode error code", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::STOP_SENDING); } return StopSendingFrame(folly::to<StreamId>(streamId->first), errorCode); } ReadCryptoFrame decodeCryptoFrame(folly::io::Cursor& cursor) { auto optionalOffset = decodeQuicInteger(cursor); if (!optionalOffset) { throw QuicTransportException( "Invalid offset", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::CRYPTO_FRAME); } uint64_t offset = optionalOffset->first; auto dataLength = decodeQuicInteger(cursor); if (!dataLength) { throw QuicTransportException( "Invalid length", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::CRYPTO_FRAME); } Buf data; if (cursor.totalLength() < dataLength->first) { throw QuicTransportException( "Length mismatch", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::CRYPTO_FRAME); } // If dataLength > data's actual length then the cursor will throw. cursor.clone(data, dataLength->first); return ReadCryptoFrame(offset, std::move(data)); } ReadNewTokenFrame decodeNewTokenFrame(folly::io::Cursor& cursor) { auto tokenLength = decodeQuicInteger(cursor); if (!tokenLength) { throw QuicTransportException( "Invalid length", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::NEW_TOKEN); } Buf token; if (cursor.totalLength() < tokenLength->first) { throw QuicTransportException( "Length mismatch", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::NEW_TOKEN); } // If tokenLength > token's actual length then the cursor will throw. cursor.clone(token, tokenLength->first); return ReadNewTokenFrame(std::move(token)); } ReadStreamFrame decodeStreamFrame( BufQueue& queue, StreamTypeField frameTypeField) { folly::io::Cursor cursor(queue.front()); auto streamId = decodeQuicInteger(cursor); if (!streamId) { throw QuicTransportException( "Invalid stream id", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::STREAM); } uint64_t offset = 0; if (frameTypeField.hasOffset()) { auto optionalOffset = decodeQuicInteger(cursor); if (!optionalOffset) { throw QuicTransportException( "Invalid offset", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::STREAM); } offset = optionalOffset->first; } auto fin = frameTypeField.hasFin(); folly::Optional<std::pair<uint64_t, size_t>> dataLength; if (frameTypeField.hasDataLength()) { dataLength = decodeQuicInteger(cursor); if (!dataLength) { throw QuicTransportException( "Invalid length", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::STREAM); } } Buf data; if (dataLength.has_value()) { if (cursor.totalLength() < dataLength->first) { throw QuicTransportException( "Length mismatch", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::STREAM); } // If dataLength > data's actual length then the cursor will throw. queue.trimStart(cursor - queue.front()); data = queue.splitAtMost(dataLength->first); } else { // Missing Data Length field doesn't mean no data. It means the rest of the // frame are all data. queue.trimStart(cursor - queue.front()); data = queue.move(); } return ReadStreamFrame( folly::to<StreamId>(streamId->first), offset, std::move(data), fin); } MaxDataFrame decodeMaxDataFrame(folly::io::Cursor& cursor) { auto maximumData = decodeQuicInteger(cursor); if (!maximumData) { throw QuicTransportException( "Bad Max Data", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::MAX_DATA); } return MaxDataFrame(maximumData->first); } MaxStreamDataFrame decodeMaxStreamDataFrame(folly::io::Cursor& cursor) { auto streamId = decodeQuicInteger(cursor); if (!streamId) { throw QuicTransportException( "Invalid streamId", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::MAX_STREAM_DATA); } auto offset = decodeQuicInteger(cursor); if (!offset) { throw QuicTransportException( "Invalid offset", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::MAX_STREAM_DATA); } return MaxStreamDataFrame( folly::to<StreamId>(streamId->first), offset->first); } MaxStreamsFrame decodeBiDiMaxStreamsFrame(folly::io::Cursor& cursor) { auto streamCount = decodeQuicInteger(cursor); if (!streamCount || streamCount->first > kMaxMaxStreams) { throw QuicTransportException( "Invalid Bi-directional streamId", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::MAX_STREAMS_BIDI); } return MaxStreamsFrame(streamCount->first, true /* isBidirectional*/); } MaxStreamsFrame decodeUniMaxStreamsFrame(folly::io::Cursor& cursor) { auto streamCount = decodeQuicInteger(cursor); if (!streamCount || streamCount->first > kMaxMaxStreams) { throw QuicTransportException( "Invalid Uni-directional streamId", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::MAX_STREAMS_UNI); } return MaxStreamsFrame(streamCount->first, false /* isUnidirectional */); } DataBlockedFrame decodeDataBlockedFrame(folly::io::Cursor& cursor) { auto dataLimit = decodeQuicInteger(cursor); if (!dataLimit) { throw QuicTransportException( "Bad offset", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::DATA_BLOCKED); } return DataBlockedFrame(dataLimit->first); } StreamDataBlockedFrame decodeStreamDataBlockedFrame(folly::io::Cursor& cursor) { auto streamId = decodeQuicInteger(cursor); if (!streamId) { throw QuicTransportException( "Bad streamId", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::STREAM_DATA_BLOCKED); } auto dataLimit = decodeQuicInteger(cursor); if (!dataLimit) { throw QuicTransportException( "Bad offset", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::STREAM_DATA_BLOCKED); } return StreamDataBlockedFrame( folly::to<StreamId>(streamId->first), dataLimit->first); } StreamsBlockedFrame decodeBiDiStreamsBlockedFrame(folly::io::Cursor& cursor) { auto streamId = decodeQuicInteger(cursor); if (!streamId) { throw QuicTransportException( "Bad Bi-Directional streamId", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::STREAMS_BLOCKED_BIDI); } return StreamsBlockedFrame( folly::to<StreamId>(streamId->first), true /* isBidirectional */); } StreamsBlockedFrame decodeUniStreamsBlockedFrame(folly::io::Cursor& cursor) { auto streamId = decodeQuicInteger(cursor); if (!streamId) { throw QuicTransportException( "Bad Uni-direcitonal streamId", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::STREAMS_BLOCKED_UNI); } return StreamsBlockedFrame( folly::to<StreamId>(streamId->first), false /* isBidirectional */); } NewConnectionIdFrame decodeNewConnectionIdFrame(folly::io::Cursor& cursor) { auto sequenceNumber = decodeQuicInteger(cursor); if (!sequenceNumber) { throw QuicTransportException( "Bad sequence", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::NEW_CONNECTION_ID); } auto retirePriorTo = decodeQuicInteger(cursor); if (!retirePriorTo) { throw QuicTransportException( "Bad retire prior to", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::NEW_CONNECTION_ID); } if (!cursor.canAdvance(sizeof(uint8_t))) { throw QuicTransportException( "Not enough input bytes to read Dest. ConnectionId", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::NEW_CONNECTION_ID); } auto connIdLen = cursor.readBE<uint8_t>(); if (cursor.totalLength() < connIdLen) { throw QuicTransportException( "Bad connid", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::NEW_CONNECTION_ID); } if (connIdLen > kMaxConnectionIdSize) { throw QuicTransportException( "ConnectionId invalid length", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::NEW_CONNECTION_ID); } ConnectionId connId(cursor, connIdLen); StatelessResetToken statelessResetToken; cursor.pull(statelessResetToken.data(), statelessResetToken.size()); return NewConnectionIdFrame( sequenceNumber->first, retirePriorTo->first, std::move(connId), std::move(statelessResetToken)); } RetireConnectionIdFrame decodeRetireConnectionIdFrame( folly::io::Cursor& cursor) { auto sequenceNum = decodeQuicInteger(cursor); if (!sequenceNum) { throw QuicTransportException( // TODO change the error code "Bad sequence num", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::RETIRE_CONNECTION_ID); } return RetireConnectionIdFrame(sequenceNum->first); } PathChallengeFrame decodePathChallengeFrame(folly::io::Cursor& cursor) { // just parse and ignore expected data // A PATH_CHALLENGE frame contains 8 bytes if (!cursor.canAdvance(sizeof(uint64_t))) { throw QuicTransportException( "Not enough input bytes to read path challenge frame.", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::PATH_CHALLENGE); } auto pathData = cursor.readBE<uint64_t>(); return PathChallengeFrame(pathData); } PathResponseFrame decodePathResponseFrame(folly::io::Cursor& cursor) { // just parse and ignore expected data // Its format is identical to the PATH_CHALLENGE frame if (!cursor.canAdvance(sizeof(uint64_t))) { throw QuicTransportException( "Not enough input bytes to read path response frame.", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::PATH_RESPONSE); } auto pathData = cursor.readBE<uint64_t>(); return PathResponseFrame(pathData); } ConnectionCloseFrame decodeConnectionCloseFrame(folly::io::Cursor& cursor) { TransportErrorCode errorCode{}; auto varCode = decodeQuicInteger(cursor); if (varCode) { errorCode = static_cast<TransportErrorCode>(varCode->first); } else { throw QuicTransportException( "Failed to parse error code.", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::CONNECTION_CLOSE); } auto frameTypeField = decodeQuicInteger(cursor); if (!frameTypeField || frameTypeField->second != sizeof(uint8_t)) { throw QuicTransportException( "Bad connection close triggering frame type value", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::CONNECTION_CLOSE); } FrameType triggeringFrameType = static_cast<FrameType>(frameTypeField->first); auto reasonPhraseLength = decodeQuicInteger(cursor); if (!reasonPhraseLength || reasonPhraseLength->first > kMaxReasonPhraseLength) { throw QuicTransportException( "Bad reason phrase length", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::CONNECTION_CLOSE); } auto reasonPhrase = cursor.readFixedString(folly::to<size_t>(reasonPhraseLength->first)); return ConnectionCloseFrame( QuicErrorCode(errorCode), std::move(reasonPhrase), triggeringFrameType); } ConnectionCloseFrame decodeApplicationClose(folly::io::Cursor& cursor) { ApplicationErrorCode errorCode{}; auto varCode = decodeQuicInteger(cursor); if (varCode) { errorCode = static_cast<ApplicationErrorCode>(varCode->first); } else { throw QuicTransportException( "Failed to parse error code.", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::CONNECTION_CLOSE_APP_ERR); } auto reasonPhraseLength = decodeQuicInteger(cursor); if (!reasonPhraseLength || reasonPhraseLength->first > kMaxReasonPhraseLength) { throw QuicTransportException( "Bad reason phrase length", quic::TransportErrorCode::FRAME_ENCODING_ERROR, quic::FrameType::CONNECTION_CLOSE_APP_ERR); } auto reasonPhrase = cursor.readFixedString(folly::to<size_t>(reasonPhraseLength->first)); return ConnectionCloseFrame( QuicErrorCode(errorCode), std::move(reasonPhrase)); } HandshakeDoneFrame decodeHandshakeDoneFrame(folly::io::Cursor& /*cursor*/) { return HandshakeDoneFrame(); } /** * Both retry and new tokens have the same plaintext encoding: timestamp. We * differentiate tokens based on the success of decrypting with differing aead * associated data. */ folly::Expected<uint64_t, TransportErrorCode> parsePlaintextRetryOrNewToken( folly::io::Cursor& cursor) { // Read in the timestamp if (!cursor.canAdvance(sizeof(uint64_t))) { return folly::makeUnexpected(TransportErrorCode::INVALID_TOKEN); } auto timestampInMs = cursor.readBE<uint64_t>(); return timestampInMs; } DatagramFrame decodeDatagramFrame(BufQueue& queue, bool hasLen) { folly::io::Cursor cursor(queue.front()); size_t length = cursor.length(); if (hasLen) { auto decodeLength = decodeQuicInteger(cursor); if (!decodeLength) { throw QuicTransportException( "Invalid datagram len", TransportErrorCode::FRAME_ENCODING_ERROR, FrameType::DATAGRAM_LEN); } length = decodeLength->first; if (cursor.length() < length) { throw QuicTransportException( "Invalid datagram frame", TransportErrorCode::FRAME_ENCODING_ERROR, FrameType::DATAGRAM_LEN); } queue.trimStart(decodeLength->second); } return DatagramFrame(length, queue.splitAtMost(length)); } QuicFrame parseFrame( BufQueue& queue, const PacketHeader& header, const CodecParameters& params) { folly::io::Cursor cursor(queue.front()); auto frameTypeInt = decodeQuicInteger(cursor); if (!frameTypeInt) { throw QuicTransportException( "Invalid frame-type field", TransportErrorCode::FRAME_ENCODING_ERROR); } queue.trimStart(cursor - queue.front()); bool consumedQueue = false; bool error = false; SCOPE_EXIT { if (consumedQueue || error) { return; } queue.trimStart(cursor - queue.front()); }; cursor.reset(queue.front()); FrameType frameType = static_cast<FrameType>(frameTypeInt->first); try { switch (frameType) { case FrameType::PADDING: return QuicFrame(decodePaddingFrame(cursor)); case FrameType::PING: return QuicFrame(decodePingFrame(cursor)); case FrameType::ACK: return QuicFrame(decodeAckFrame(cursor, header, params)); case FrameType::ACK_ECN: return QuicFrame(decodeAckFrameWithECN(cursor, header, params)); case FrameType::RST_STREAM: return QuicFrame(decodeRstStreamFrame(cursor)); case FrameType::STOP_SENDING: return QuicFrame(decodeStopSendingFrame(cursor)); case FrameType::CRYPTO_FRAME: return QuicFrame(decodeCryptoFrame(cursor)); case FrameType::NEW_TOKEN: return QuicFrame(decodeNewTokenFrame(cursor)); case FrameType::STREAM: case FrameType::STREAM_FIN: case FrameType::STREAM_LEN: case FrameType::STREAM_LEN_FIN: case FrameType::STREAM_OFF: case FrameType::STREAM_OFF_FIN: case FrameType::STREAM_OFF_LEN: case FrameType::STREAM_OFF_LEN_FIN: consumedQueue = true; return QuicFrame( decodeStreamFrame(queue, StreamTypeField(frameTypeInt->first))); case FrameType::MAX_DATA: return QuicFrame(decodeMaxDataFrame(cursor)); case FrameType::MAX_STREAM_DATA: return QuicFrame(decodeMaxStreamDataFrame(cursor)); case FrameType::MAX_STREAMS_BIDI: return QuicFrame(decodeBiDiMaxStreamsFrame(cursor)); case FrameType::MAX_STREAMS_UNI: return QuicFrame(decodeUniMaxStreamsFrame(cursor)); case FrameType::DATA_BLOCKED: return QuicFrame(decodeDataBlockedFrame(cursor)); case FrameType::STREAM_DATA_BLOCKED: return QuicFrame(decodeStreamDataBlockedFrame(cursor)); case FrameType::STREAMS_BLOCKED_BIDI: return QuicFrame(decodeBiDiStreamsBlockedFrame(cursor)); case FrameType::STREAMS_BLOCKED_UNI: return QuicFrame(decodeUniStreamsBlockedFrame(cursor)); case FrameType::NEW_CONNECTION_ID: return QuicFrame(decodeNewConnectionIdFrame(cursor)); case FrameType::RETIRE_CONNECTION_ID: return QuicFrame(decodeRetireConnectionIdFrame(cursor)); case FrameType::PATH_CHALLENGE: return QuicFrame(decodePathChallengeFrame(cursor)); case FrameType::PATH_RESPONSE: return QuicFrame(decodePathResponseFrame(cursor)); case FrameType::CONNECTION_CLOSE: return QuicFrame(decodeConnectionCloseFrame(cursor)); case FrameType::CONNECTION_CLOSE_APP_ERR: return QuicFrame(decodeApplicationClose(cursor)); case FrameType::HANDSHAKE_DONE: return QuicFrame(decodeHandshakeDoneFrame(cursor)); case FrameType::DATAGRAM: { consumedQueue = true; return QuicFrame(decodeDatagramFrame(queue, false /* hasLen */)); } case FrameType::DATAGRAM_LEN: { consumedQueue = true; return QuicFrame(decodeDatagramFrame(queue, true /* hasLen */)); } case FrameType::KNOB: return QuicFrame(decodeKnobFrame(cursor)); case FrameType::ACK_FREQUENCY: return QuicFrame(decodeAckFrequencyFrame(cursor)); } } catch (const std::exception&) { error = true; throw QuicTransportException( folly::to<std::string>( "Frame format invalid, type=", frameTypeInt->first), TransportErrorCode::FRAME_ENCODING_ERROR, frameType); } error = true; throw QuicTransportException( folly::to<std::string>("Unknown frame, type=", frameTypeInt->first), TransportErrorCode::FRAME_ENCODING_ERROR, frameType); } // Parse packet RegularQuicPacket decodeRegularPacket( PacketHeader&& header, const CodecParameters& params, std::unique_ptr<folly::IOBuf> packetData) { RegularQuicPacket packet(std::move(header)); BufQueue queue; queue.append(std::move(packetData)); while (queue.chainLength() > 0) { packet.frames.push_back(parseFrame(queue, packet.header, params)); } return packet; } folly::Optional<VersionNegotiationPacket> decodeVersionNegotiation( const ParsedLongHeaderInvariant& longHeaderInvariant, folly::io::Cursor& cursor) { auto cursorLength = cursor.totalLength(); if (cursorLength < sizeof(QuicVersionType) || cursorLength % sizeof(QuicVersionType)) { VLOG(4) << "Version negotiation packet invalid"; return folly::none; } VersionNegotiationPacket packet( longHeaderInvariant.initialByte, longHeaderInvariant.invariant.srcConnId, longHeaderInvariant.invariant.dstConnId); while (!cursor.isAtEnd()) { packet.versions.push_back( static_cast<QuicVersion>(cursor.readBE<QuicVersionType>())); } return packet; } ParsedLongHeaderResult::ParsedLongHeaderResult( bool isVersionNegotiationIn, folly::Optional<ParsedLongHeader> parsedLongHeaderIn) : isVersionNegotiation(isVersionNegotiationIn), parsedLongHeader(std::move(parsedLongHeaderIn)) { CHECK(isVersionNegotiation || parsedLongHeader); } ParsedLongHeaderInvariant::ParsedLongHeaderInvariant( uint8_t initialByteIn, LongHeaderInvariant headerInvariant, size_t length) : initialByte(initialByteIn), invariant(std::move(headerInvariant)), invariantLength(length) {} folly::Expected<ParsedLongHeaderInvariant, TransportErrorCode> parseLongHeaderInvariant(uint8_t initialByte, folly::io::Cursor& cursor) { size_t initialLength = cursor.totalLength(); if (!cursor.canAdvance(sizeof(QuicVersionType))) { VLOG(5) << "Not enough input bytes to read Version or connection-id"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } auto version = static_cast<QuicVersion>(cursor.readBE<QuicVersionType>()); if (!cursor.canAdvance(1)) { VLOG(5) << "Not enough input bytes to read Dest. ConnectionId length"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } uint8_t destConnIdLen = cursor.readBE<uint8_t>(); if (destConnIdLen > kMaxConnectionIdSize) { VLOG(5) << "destConnIdLen > kMaxConnectionIdSize: " << destConnIdLen; return folly::makeUnexpected(TransportErrorCode::PROTOCOL_VIOLATION); } if (!cursor.canAdvance(destConnIdLen)) { VLOG(5) << "Not enough input bytes to read Dest. ConnectionId"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } ConnectionId destConnId(cursor, destConnIdLen); if (!cursor.canAdvance(1)) { VLOG(5) << "Not enough input bytes to read Source ConnectionId length"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } uint8_t srcConnIdLen = cursor.readBE<uint8_t>(); if (srcConnIdLen > kMaxConnectionIdSize) { VLOG(5) << "srcConnIdLen > kMaxConnectionIdSize: " << srcConnIdLen; return folly::makeUnexpected(TransportErrorCode::PROTOCOL_VIOLATION); } if (!cursor.canAdvance(srcConnIdLen)) { VLOG(5) << "Not enough input bytes to read Source ConnectionId"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } ConnectionId srcConnId(cursor, srcConnIdLen); size_t currentLength = cursor.totalLength(); size_t bytesRead = initialLength - currentLength; return ParsedLongHeaderInvariant( initialByte, LongHeaderInvariant(version, std::move(srcConnId), std::move(destConnId)), bytesRead); } LongHeader::Types parseLongHeaderType(uint8_t initialByte) { return static_cast<LongHeader::Types>( (initialByte & LongHeader::kPacketTypeMask) >> LongHeader::kTypeShift); } size_t parsePacketNumberLength(uint8_t initialByte) { static_assert( LongHeader::kPacketNumLenMask == ShortHeader::kPacketNumLenMask, "Expected both pn masks are the same"); return (initialByte & LongHeader::kPacketNumLenMask) + 1; } /** * Returns the packet number and the length of the packet number */ std::pair<PacketNum, size_t> parsePacketNumber( uint8_t initialByte, folly::ByteRange packetNumberRange, PacketNum expectedNextPacketNum) { size_t packetNumLen = parsePacketNumberLength(initialByte); uint32_t encodedPacketNum = 0; memcpy( reinterpret_cast<char*>(&encodedPacketNum) + sizeof(uint32_t) - packetNumLen, packetNumberRange.data(), packetNumLen); uint32_t bigEncodedPacketNum = folly::Endian::big(encodedPacketNum); return std::make_pair( decodePacketNumber( bigEncodedPacketNum, packetNumLen, expectedNextPacketNum), packetNumLen); } folly::Expected<ParsedLongHeaderResult, TransportErrorCode> parseLongHeader( uint8_t initialByte, folly::io::Cursor& cursor) { if (getHeaderForm(initialByte) != HeaderForm::Long) { VLOG(5) << "Bad header form bit"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } LongHeader::Types type = parseLongHeaderType(initialByte); switch (type) { case LongHeader::Types::Initial: case LongHeader::Types::Retry: case LongHeader::Types::Handshake: case LongHeader::Types::ZeroRtt: break; default: return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } auto parsedLongHeaderInvariant = parseLongHeaderInvariant(initialByte, cursor); if (!parsedLongHeaderInvariant) { VLOG(5) << "Bad invariants fields in long header"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } auto version = parsedLongHeaderInvariant->invariant.version; if (version == QuicVersion::VERSION_NEGOTIATION) { return ParsedLongHeaderResult(true, folly::none); } auto parsedHeader = parseLongHeaderVariants( type, std::move(*parsedLongHeaderInvariant), cursor); if (!parsedHeader) { return folly::makeUnexpected(parsedHeader.error()); } return ParsedLongHeaderResult(false, std::move(*parsedHeader)); } folly::Expected<ParsedLongHeader, TransportErrorCode> parseLongHeaderVariants( LongHeader::Types type, ParsedLongHeaderInvariant parsedLongHeaderInvariant, folly::io::Cursor& cursor, QuicNodeType nodeType) { if (type == LongHeader::Types::Retry) { // The integrity tag is kRetryIntegrityTagLen bytes in length, and the // token must be at least one byte, so the remaining length must // be > kRetryIntegrityTagLen. if (cursor.totalLength() <= kRetryIntegrityTagLen) { VLOG(5) << "Not enough bytes for retry token"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } Buf token; cursor.clone(token, cursor.totalLength() - kRetryIntegrityTagLen); return ParsedLongHeader( LongHeader( type, std::move(parsedLongHeaderInvariant.invariant), token ? token->moveToFbString().toStdString() : std::string()), PacketLength(0, 0)); } // TODO Checking kMinInitialDestinationConnIdLength isn't necessary // if this packet is in response to a retry. if (type == LongHeader::Types::Initial && nodeType == QuicNodeType::Server && parsedLongHeaderInvariant.invariant.dstConnId.size() < kMinInitialDestinationConnIdLength) { VLOG(5) << "Dest Conn-Id length in client initial packet must be >= 8 bytes."; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } Buf token; if (type == LongHeader::Types::Initial) { auto tokenLen = decodeQuicInteger(cursor); if (!tokenLen) { VLOG(5) << "Token len not found in Long header"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } if (!cursor.canAdvance(tokenLen->first)) { VLOG(5) << "Not enough input bytes to read input token"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } if (tokenLen->first > 0) { Buf tokenBuf; // If tokenLen > token's actual length then the cursor will throw. cursor.clone(tokenBuf, tokenLen->first); token = std::move(tokenBuf); } } auto pktLen = decodeQuicInteger(cursor); if (!pktLen) { VLOG(5) << "Packet len not found in Long header"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } if (!cursor.canAdvance(pktLen->first)) { VLOG(5) << "Not enough input bytes to read packet number"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } size_t packetNumLen = parsePacketNumberLength(parsedLongHeaderInvariant.initialByte); if (!cursor.canAdvance(packetNumLen)) { return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } if (packetNumLen > kMaxPacketNumEncodingSize) { return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } return ParsedLongHeader( LongHeader( type, std::move(parsedLongHeaderInvariant.invariant), token ? token->moveToFbString().toStdString() : std::string()), PacketLength(pktLen->first, pktLen->second)); } folly::Expected<ShortHeaderInvariant, TransportErrorCode> parseShortHeaderInvariants( uint8_t initialByte, folly::io::Cursor& cursor, size_t dstConnIdSize) { if (getHeaderForm(initialByte) != HeaderForm::Short) { VLOG(5) << "Bad header form bit"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } // TODO(t39154014, yangchi): read the length from the connection state in // draft-17 if (dstConnIdSize > kMaxConnectionIdSize) { VLOG(5) << "dstConnIdSize > kMaxConnectionIdSize: " << dstConnIdSize; return folly::makeUnexpected(TransportErrorCode::PROTOCOL_VIOLATION); } if (!cursor.canAdvance(dstConnIdSize)) { VLOG(5) << "Not enough input bytes for ConnectionId"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } ConnectionId connId(cursor, dstConnIdSize); return ShortHeaderInvariant(std::move(connId)); } folly::Expected<ShortHeader, TransportErrorCode> parseShortHeader( uint8_t initialByte, folly::io::Cursor& cursor, size_t dstConnIdSize) { if (getHeaderForm(initialByte) != HeaderForm::Short) { VLOG(5) << "Bad header form bit"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } if (!(initialByte & ShortHeader::kFixedBitMask)) { VLOG(5) << "Fixed bit in ShortHeader is 0"; // Specs doesn't say which error code to use return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } if (initialByte & ShortHeader::kReservedBitsMask) { VLOG(5) << "Non-zero reserved bits in ShortHeader"; // Specs asks this to be PROTOCOL_VIOLATION return folly::makeUnexpected(TransportErrorCode::PROTOCOL_VIOLATION); } auto invariant = parseShortHeaderInvariants(initialByte, cursor, dstConnIdSize); if (!invariant) { VLOG(5) << "Error parsing short header invariant"; return folly::makeUnexpected(TransportErrorCode::FRAME_ENCODING_ERROR); } auto protectionType = initialByte & ShortHeader::kKeyPhaseMask ? ProtectionType::KeyPhaseOne : ProtectionType::KeyPhaseZero; return ShortHeader(protectionType, std::move(invariant->destinationConnId)); } } // namespace quic