in quic/codec/QuicReadCodec.cpp [86:249]
CodecResult QuicReadCodec::parseLongHeaderPacket(
BufQueue& queue,
const AckStates& ackStates) {
folly::io::Cursor cursor(queue.front());
const uint8_t initialByte = *cursor.peekBytes().data();
auto res = tryParseLongHeader(cursor, nodeType_);
if (res.hasError()) {
VLOG(4) << "Failed to parse long header " << connIdToHex();
queue.move();
return CodecResult(Nothing());
}
auto parsedLongHeader = std::move(res.value());
auto type = parsedLongHeader.header.getHeaderType();
// As soon as we have parsed out the long header we can split off any
// coalesced packets. We do this early since the spec mandates that decryption
// failure must not stop the processing of subsequent coalesced packets.
auto longHeader = std::move(parsedLongHeader.header);
if (type == LongHeader::Types::Retry) {
Buf integrityTag;
cursor.clone(integrityTag, kRetryIntegrityTagLen);
queue.move();
return RetryPacket(
std::move(longHeader), std::move(integrityTag), initialByte);
}
uint64_t packetNumberOffset = cursor.getCurrentPosition();
size_t currentPacketLen =
packetNumberOffset + parsedLongHeader.packetLength.packetLength;
if (queue.chainLength() < currentPacketLen) {
// Packet appears truncated, there's no parse-able data left.
queue.move();
return CodecResult(Nothing());
}
auto currentPacketData = queue.splitAtMost(currentPacketLen);
cursor.reset(currentPacketData.get());
cursor.skip(packetNumberOffset);
// Sample starts after the max packet number size. This ensures that we
// have enough bytes to skip before we can start reading the sample.
if (!cursor.canAdvance(kMaxPacketNumEncodingSize)) {
VLOG(4) << "Dropping packet, not enough for packet number "
<< connIdToHex();
// Packet appears truncated, there's no parse-able data left.
queue.move();
return CodecResult(Nothing());
}
cursor.skip(kMaxPacketNumEncodingSize);
Sample sample;
if (!cursor.canAdvance(sample.size())) {
VLOG(4) << "Dropping packet, sample too small " << connIdToHex();
// Packet appears truncated, there's no parse-able data left.
queue.move();
return CodecResult(Nothing());
}
cursor.pull(sample.data(), sample.size());
const PacketNumberCipher* headerCipher{nullptr};
const Aead* cipher{nullptr};
auto protectionType = longHeader.getProtectionType();
switch (protectionType) {
case ProtectionType::Initial:
if (!initialHeaderCipher_) {
VLOG(4) << nodeToString(nodeType_)
<< " dropping initial packet after initial keys dropped"
<< connIdToHex();
return CodecResult(Nothing());
}
headerCipher = initialHeaderCipher_.get();
cipher = initialReadCipher_.get();
break;
case ProtectionType::Handshake:
headerCipher = handshakeHeaderCipher_.get();
cipher = handshakeReadCipher_.get();
break;
case ProtectionType::ZeroRtt:
if (handshakeDoneTime_) {
// TODO actually drop the 0-rtt keys in addition to dropping packets.
auto timeBetween = Clock::now() - *handshakeDoneTime_;
if (timeBetween > kTimeToRetainZeroRttKeys) {
VLOG(4) << nodeToString(nodeType_)
<< " dropping zero rtt packet for exceeding key timeout"
<< connIdToHex();
return CodecResult(Nothing());
}
}
headerCipher = zeroRttHeaderCipher_.get();
cipher = zeroRttReadCipher_.get();
break;
case ProtectionType::KeyPhaseZero:
case ProtectionType::KeyPhaseOne:
CHECK(false) << "one rtt protection type in long header";
}
if (!headerCipher || !cipher) {
return CodecResult(
CipherUnavailable(std::move(currentPacketData), protectionType));
}
PacketNum expectedNextPacketNum = 0;
folly::Optional<PacketNum> largestReceivedPacketNum;
switch (longHeaderTypeToProtectionType(type)) {
case ProtectionType::Initial:
largestReceivedPacketNum =
ackStates.initialAckState.largestReceivedPacketNum;
break;
case ProtectionType::Handshake:
largestReceivedPacketNum =
ackStates.handshakeAckState.largestReceivedPacketNum;
break;
case ProtectionType::ZeroRtt:
largestReceivedPacketNum =
ackStates.appDataAckState.largestReceivedPacketNum;
break;
default:
folly::assume_unreachable();
}
if (largestReceivedPacketNum) {
expectedNextPacketNum = 1 + *largestReceivedPacketNum;
}
folly::MutableByteRange initialByteRange(
currentPacketData->writableData(), 1);
folly::MutableByteRange packetNumberByteRange(
currentPacketData->writableData() + packetNumberOffset,
kMaxPacketNumEncodingSize);
headerCipher->decryptLongHeader(
folly::range(sample), initialByteRange, packetNumberByteRange);
std::pair<PacketNum, size_t> packetNum = parsePacketNumber(
initialByteRange.data()[0], packetNumberByteRange, expectedNextPacketNum);
longHeader.setPacketNumber(packetNum.first);
BufQueue decryptQueue;
decryptQueue.append(std::move(currentPacketData));
size_t aadLen = packetNumberOffset + packetNum.second;
auto headerData = decryptQueue.splitAtMost(aadLen);
// parsing verifies that packetLength >= packet number length.
auto encryptedData = decryptQueue.splitAtMost(
parsedLongHeader.packetLength.packetLength - packetNum.second);
if (!encryptedData) {
// There should normally be some integrity tag at least in the data,
// however allowing the aead to process the data even if the tag is not
// present helps with writing tests.
encryptedData = folly::IOBuf::create(0);
}
Buf decrypted;
auto decryptAttempt = cipher->tryDecrypt(
std::move(encryptedData), headerData.get(), packetNum.first);
if (!decryptAttempt) {
VLOG(4) << "Unable to decrypt packet=" << packetNum.first
<< " packetNumLen=" << parsePacketNumberLength(initialByte)
<< " protectionType=" << toString(protectionType) << " "
<< connIdToHex();
return CodecResult(Nothing());
}
decrypted = std::move(*decryptAttempt);
if (!decrypted) {
// TODO better way of handling this (tests break without this)
decrypted = folly::IOBuf::create(0);
}
return decodeRegularPacket(
std::move(longHeader), params_, std::move(decrypted));
}