diff --git a/quic/QuicConstants.h b/quic/QuicConstants.h index d433e744b..221d7f193 100644 --- a/quic/QuicConstants.h +++ b/quic/QuicConstants.h @@ -128,6 +128,7 @@ enum class TransportErrorCode : uint16_t { INVALID_MIGRATION = 0x000C, CRYPTO_ERROR = 0x100, CRYPTO_ERROR_MAX = 0x1ff, + INVALID_TOKEN = 0xb, }; /** diff --git a/quic/QuicException.cpp b/quic/QuicException.cpp index fbc7dce64..1a489f64a 100644 --- a/quic/QuicException.cpp +++ b/quic/QuicException.cpp @@ -147,6 +147,8 @@ std::string toString(TransportErrorCode code) { return "Invalid migration"; case TransportErrorCode::SERVER_BUSY: return "Server busy"; + case TransportErrorCode::INVALID_TOKEN: + return "Invalid token"; case TransportErrorCode::CRYPTO_ERROR: return cryptoErrorToString(code); case TransportErrorCode::CRYPTO_ERROR_MAX: diff --git a/quic/codec/Decode.cpp b/quic/codec/Decode.cpp index c2226bbf1..d0689c99a 100644 --- a/quic/codec/Decode.cpp +++ b/quic/codec/Decode.cpp @@ -649,6 +649,46 @@ HandshakeDoneFrame decodeHandshakeDoneFrame(folly::io::Cursor& /*cursor*/) { return HandshakeDoneFrame(); } +folly::Expected parsePlaintextRetryToken( + folly::io::Cursor& cursor) { + // Read in the length of the odcid. + if (!cursor.canAdvance(sizeof(uint8_t))) { + return folly::makeUnexpected(TransportErrorCode::INVALID_TOKEN); + } + auto odcidLen = cursor.readBE(); + + // Read in the odcid. + if (!cursor.canAdvance(odcidLen)) { + return folly::makeUnexpected(TransportErrorCode::INVALID_TOKEN); + } + ConnectionId connId(cursor, odcidLen); + + // Read in the port. + if (!cursor.canAdvance(sizeof(uint16_t))) { + return folly::makeUnexpected(TransportErrorCode::INVALID_TOKEN); + } + auto clientPort = cursor.readBE(); + + // Read in the length of the client ip address. + if (!cursor.canAdvance(sizeof(uint8_t))) { + return folly::makeUnexpected(TransportErrorCode::INVALID_TOKEN); + } + uint16_t ipAddrLen = cursor.readBE(); + + if (!cursor.canAdvance(ipAddrLen)) { + return folly::makeUnexpected(TransportErrorCode::INVALID_TOKEN); + } + + // Read in the ip address. + std::string ipAddressStr = cursor.readFixedString(ipAddrLen); + auto ipAddress = folly::IPAddress::tryFromString(ipAddressStr); + if (!ipAddress.hasValue()) { + return folly::makeUnexpected(TransportErrorCode::INVALID_TOKEN); + } + + return RetryToken(connId, *ipAddress, clientPort); +} + QuicFrame parseFrame( BufQueue& queue, const PacketHeader& header, diff --git a/quic/codec/Decode.h b/quic/codec/Decode.h index 8139b3410..af34e5772 100644 --- a/quic/codec/Decode.h +++ b/quic/codec/Decode.h @@ -133,6 +133,9 @@ ReadNewTokenFrame decodeNewTokenFrame(folly::io::Cursor& cursor); HandshakeDoneFrame decodeHandshakeDoneFrame(folly::io::Cursor& cursor); +folly::Expected parsePlaintextRetryToken( + folly::io::Cursor& cursor); + /** * Parse the Invariant fields in Long Header. * diff --git a/quic/codec/test/DecodeTest.cpp b/quic/codec/test/DecodeTest.cpp index 89a371de8..0770925b4 100644 --- a/quic/codec/test/DecodeTest.cpp +++ b/quic/codec/test/DecodeTest.cpp @@ -737,5 +737,31 @@ TEST_F(DecodeTest, DecodeExpiredStreamDataFrame) { EXPECT_EQ(result.minimumStreamOffset, 100); } +TEST_F(DecodeTest, ParsePlaintextRetryToken) { + ConnectionId odcid = getTestConnectionId(); + folly::IPAddress clientIp("109.115.3.49"); + uint16_t clientPort = 42069; + RetryToken retryToken(odcid, clientIp, clientPort); + Buf plaintextRetryToken = retryToken.getPlaintextToken(); + + folly::io::Cursor cursor(plaintextRetryToken.get()); + auto parseResult = parsePlaintextRetryToken(cursor); + + EXPECT_TRUE(parseResult.hasValue()); + EXPECT_EQ(parseResult->originalDstConnId, odcid); + EXPECT_EQ(parseResult->clientIp, clientIp); + EXPECT_EQ(parseResult->clientPort, clientPort); +} + +TEST_F(DecodeTest, ParsePlaintextRetryTokenMalformed) { + Buf plaintextRetryToken = folly::IOBuf::copyBuffer("This is some garbage"); + + folly::io::Cursor cursor(plaintextRetryToken.get()); + auto parseResult = parsePlaintextRetryToken(cursor); + + EXPECT_TRUE(parseResult.hasError()); + EXPECT_EQ(parseResult.error(), TransportErrorCode::INVALID_TOKEN); +} + } // namespace test } // namespace quic