1 // Copyright (c) 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/third_party/quiche/src/quic/core/tls_chlo_extractor.h"
6 #include <memory>
7 
8 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session.h"
9 #include "net/third_party/quiche/src/quic/core/quic_connection.h"
10 #include "net/third_party/quiche/src/quic/core/quic_packet_writer_wrapper.h"
11 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
12 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
13 #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
14 #include "net/third_party/quiche/src/quic/test_tools/first_flight.h"
15 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
16 
17 namespace quic {
18 namespace test {
19 namespace {
20 
21 class TlsChloExtractorTest : public QuicTestWithParam<ParsedQuicVersion> {
22  protected:
TlsChloExtractorTest()23   TlsChloExtractorTest() : version_(GetParam()) {}
24 
Initialize()25   void Initialize() { packets_ = GetFirstFlightOfPackets(version_, config_); }
26 
IngestPackets()27   void IngestPackets() {
28     for (const std::unique_ptr<QuicReceivedPacket>& packet : packets_) {
29       ReceivedPacketInfo packet_info(
30           QuicSocketAddress(TestPeerIPAddress(), kTestPort),
31           QuicSocketAddress(TestPeerIPAddress(), kTestPort), *packet);
32       std::string detailed_error;
33       bool retry_token_present;
34       absl::string_view retry_token;
35       const QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher(
36           *packet, /*expected_destination_connection_id_length=*/0,
37           &packet_info.form, &packet_info.long_packet_type,
38           &packet_info.version_flag, &packet_info.use_length_prefix,
39           &packet_info.version_label, &packet_info.version,
40           &packet_info.destination_connection_id,
41           &packet_info.source_connection_id, &retry_token_present, &retry_token,
42           &detailed_error);
43       ASSERT_THAT(error, IsQuicNoError()) << detailed_error;
44       tls_chlo_extractor_.IngestPacket(packet_info.version, packet_info.packet);
45     }
46     packets_.clear();
47   }
48 
ValidateChloDetails()49   void ValidateChloDetails() {
50     EXPECT_TRUE(tls_chlo_extractor_.HasParsedFullChlo());
51     std::vector<std::string> alpns = tls_chlo_extractor_.alpns();
52     ASSERT_EQ(alpns.size(), 1u);
53     EXPECT_EQ(alpns[0], AlpnForVersion(version_));
54     EXPECT_EQ(tls_chlo_extractor_.server_name(), TestHostname());
55   }
56 
IncreaseSizeOfChlo()57   void IncreaseSizeOfChlo() {
58     // Add a 2000-byte custom parameter to increase the length of the CHLO.
59     constexpr auto kCustomParameterId =
60         static_cast<TransportParameters::TransportParameterId>(0xff33);
61     std::string kCustomParameterValue(2000, '-');
62     config_.custom_transport_parameters_to_send()[kCustomParameterId] =
63         kCustomParameterValue;
64   }
65 
66   ParsedQuicVersion version_;
67   TlsChloExtractor tls_chlo_extractor_;
68   QuicConfig config_;
69   std::vector<std::unique_ptr<QuicReceivedPacket>> packets_;
70 };
71 
72 INSTANTIATE_TEST_SUITE_P(TlsChloExtractorTests,
73                          TlsChloExtractorTest,
74                          ::testing::ValuesIn(AllSupportedVersionsWithTls()),
75                          ::testing::PrintToStringParamName());
76 
TEST_P(TlsChloExtractorTest,Simple)77 TEST_P(TlsChloExtractorTest, Simple) {
78   Initialize();
79   EXPECT_EQ(packets_.size(), 1u);
80   IngestPackets();
81   ValidateChloDetails();
82   EXPECT_EQ(tls_chlo_extractor_.state(),
83             TlsChloExtractor::State::kParsedFullSinglePacketChlo);
84 }
85 
TEST_P(TlsChloExtractorTest,MultiPacket)86 TEST_P(TlsChloExtractorTest, MultiPacket) {
87   IncreaseSizeOfChlo();
88   Initialize();
89   EXPECT_EQ(packets_.size(), 2u);
90   IngestPackets();
91   ValidateChloDetails();
92   EXPECT_EQ(tls_chlo_extractor_.state(),
93             TlsChloExtractor::State::kParsedFullMultiPacketChlo);
94 }
95 
TEST_P(TlsChloExtractorTest,MultiPacketReordered)96 TEST_P(TlsChloExtractorTest, MultiPacketReordered) {
97   IncreaseSizeOfChlo();
98   Initialize();
99   ASSERT_EQ(packets_.size(), 2u);
100   // Artifically reorder both packets.
101   std::swap(packets_[0], packets_[1]);
102   IngestPackets();
103   ValidateChloDetails();
104   EXPECT_EQ(tls_chlo_extractor_.state(),
105             TlsChloExtractor::State::kParsedFullMultiPacketChlo);
106 }
107 
TEST_P(TlsChloExtractorTest,MoveAssignment)108 TEST_P(TlsChloExtractorTest, MoveAssignment) {
109   Initialize();
110   EXPECT_EQ(packets_.size(), 1u);
111   TlsChloExtractor other_extractor;
112   tls_chlo_extractor_ = std::move(other_extractor);
113   IngestPackets();
114   ValidateChloDetails();
115   EXPECT_EQ(tls_chlo_extractor_.state(),
116             TlsChloExtractor::State::kParsedFullSinglePacketChlo);
117 }
118 
TEST_P(TlsChloExtractorTest,MoveAssignmentBetweenPackets)119 TEST_P(TlsChloExtractorTest, MoveAssignmentBetweenPackets) {
120   IncreaseSizeOfChlo();
121   Initialize();
122   ASSERT_EQ(packets_.size(), 2u);
123   TlsChloExtractor other_extractor;
124 
125   // Have |other_extractor| parse the first packet.
126   ReceivedPacketInfo packet_info(
127       QuicSocketAddress(TestPeerIPAddress(), kTestPort),
128       QuicSocketAddress(TestPeerIPAddress(), kTestPort), *packets_[0]);
129   std::string detailed_error;
130   bool retry_token_present;
131   absl::string_view retry_token;
132   const QuicErrorCode error = QuicFramer::ParsePublicHeaderDispatcher(
133       *packets_[0], /*expected_destination_connection_id_length=*/0,
134       &packet_info.form, &packet_info.long_packet_type,
135       &packet_info.version_flag, &packet_info.use_length_prefix,
136       &packet_info.version_label, &packet_info.version,
137       &packet_info.destination_connection_id, &packet_info.source_connection_id,
138       &retry_token_present, &retry_token, &detailed_error);
139   ASSERT_THAT(error, IsQuicNoError()) << detailed_error;
140   other_extractor.IngestPacket(packet_info.version, packet_info.packet);
141   // Remove the first packet from the list.
142   packets_.erase(packets_.begin());
143   EXPECT_EQ(packets_.size(), 1u);
144 
145   // Move the extractor.
146   tls_chlo_extractor_ = std::move(other_extractor);
147 
148   // Have |tls_chlo_extractor_| parse the second packet.
149   IngestPackets();
150 
151   ValidateChloDetails();
152   EXPECT_EQ(tls_chlo_extractor_.state(),
153             TlsChloExtractor::State::kParsedFullMultiPacketChlo);
154 }
155 
156 }  // namespace
157 }  // namespace test
158 }  // namespace quic
159