1 // Copyright (c) 2019 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/quic_transport/quic_transport_server_session.h"
6
7 #include <cstddef>
8 #include <memory>
9 #include <string>
10
11 #include "url/gurl.h"
12 #include "url/origin.h"
13 #include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h"
14 #include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
15 #include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
16 #include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
17 #include "net/third_party/quiche/src/quic/core/quic_versions.h"
18 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
19 #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
20 #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
21 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
22 #include "net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h"
23 #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h"
24 #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
25
26 namespace quic {
27 namespace test {
28 namespace {
29
30 using testing::_;
31 using testing::AnyNumber;
32 using testing::DoAll;
33 using testing::HasSubstr;
34 using testing::Return;
35 using testing::SaveArg;
36
37 constexpr char kTestOrigin[] = "https://test-origin.test";
38 constexpr char kTestOriginClientIndication[] =
39 "\0\0" // key (0x0000, origin)
40 "\0\x18" // length
41 "https://test-origin.test" // value
42 "\0\x01" // key (0x0001, path)
43 "\0\x05" // length
44 "/test"; // value
GetTestOrigin()45 const url::Origin GetTestOrigin() {
46 return url::Origin::Create(GURL(kTestOrigin));
47 }
GetTestOriginClientIndication()48 const std::string GetTestOriginClientIndication() {
49 return std::string(kTestOriginClientIndication,
50 sizeof(kTestOriginClientIndication) - 1);
51 }
52
GetVersions()53 ParsedQuicVersionVector GetVersions() {
54 return {DefaultVersionForQuicTransport()};
55 }
56
57 class QuicTransportServerSessionTest : public QuicTest {
58 public:
QuicTransportServerSessionTest()59 QuicTransportServerSessionTest()
60 : connection_(&helper_,
61 &alarm_factory_,
62 Perspective::IS_SERVER,
63 GetVersions()),
64 crypto_config_(QuicCryptoServerConfig::TESTING,
65 QuicRandom::GetInstance(),
66 crypto_test_utils::ProofSourceForTesting(),
67 KeyExchangeSource::Default()),
68 compressed_certs_cache_(
69 QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {
70 QuicEnableVersion(DefaultVersionForQuicTransport());
71 connection_.AdvanceTime(QuicTime::Delta::FromSeconds(100000));
72 crypto_test_utils::SetupCryptoServerConfigForTest(
73 helper_.GetClock(), helper_.GetRandomGenerator(), &crypto_config_);
74 session_ = std::make_unique<QuicTransportServerSession>(
75 &connection_, nullptr, DefaultQuicConfig(), GetVersions(),
76 &crypto_config_, &compressed_certs_cache_, &visitor_);
77 session_->Initialize();
78 crypto_stream_ = session_->GetMutableCryptoStream();
79 ON_CALL(visitor_, ProcessPath(_))
80 .WillByDefault(DoAll(SaveArg<0>(&path_), Return(true)));
81 }
82
Connect()83 void Connect() {
84 crypto_test_utils::FakeClientOptions options;
85 options.only_tls_versions = true;
86 crypto_test_utils::HandshakeWithFakeClient(
87 &helper_, &alarm_factory_, &connection_, crypto_stream_,
88 QuicServerId("test.example.com", 443), options, QuicTransportAlpn());
89 }
90
ReceiveIndication(quiche::QuicheStringPiece indication)91 void ReceiveIndication(quiche::QuicheStringPiece indication) {
92 QUIC_LOG(INFO) << "Receiving indication: "
93 << quiche::QuicheTextUtils::HexDump(indication);
94 constexpr size_t kChunkSize = 1024;
95 // Shard the indication, since some of the tests cause it to not fit into a
96 // single frame.
97 for (size_t i = 0; i < indication.size(); i += kChunkSize) {
98 QuicStreamFrame frame(ClientIndicationStream(), /*fin=*/false, i,
99 indication.substr(i, i + kChunkSize));
100 session_->OnStreamFrame(frame);
101 }
102 session_->OnStreamFrame(QuicStreamFrame(ClientIndicationStream(),
103 /*fin=*/true, indication.size(),
104 quiche::QuicheStringPiece()));
105 }
106
ReceiveIndicationWithPath(quiche::QuicheStringPiece path)107 void ReceiveIndicationWithPath(quiche::QuicheStringPiece path) {
108 constexpr char kTestOriginClientIndicationPrefix[] =
109 "\0\0" // key (0x0000, origin)
110 "\0\x18" // length
111 "https://test-origin.test" // value
112 "\0\x01"; // key (0x0001, path)
113 std::string indication{kTestOriginClientIndicationPrefix,
114 sizeof(kTestOriginClientIndicationPrefix) - 1};
115 indication.push_back(static_cast<char>(path.size() >> 8));
116 indication.push_back(static_cast<char>(path.size() & 0xff));
117 indication += std::string{path};
118 ReceiveIndication(indication);
119 }
120
121 protected:
122 MockAlarmFactory alarm_factory_;
123 MockQuicConnectionHelper helper_;
124
125 PacketSavingConnection connection_;
126 QuicCryptoServerConfig crypto_config_;
127 std::unique_ptr<QuicTransportServerSession> session_;
128 QuicCompressedCertsCache compressed_certs_cache_;
129 testing::NiceMock<MockServerVisitor> visitor_;
130 QuicCryptoServerStreamBase* crypto_stream_;
131 GURL path_;
132 };
133
TEST_F(QuicTransportServerSessionTest,SuccessfulHandshake)134 TEST_F(QuicTransportServerSessionTest, SuccessfulHandshake) {
135 Connect();
136
137 url::Origin origin;
138 EXPECT_CALL(visitor_, CheckOrigin(_))
139 .WillOnce(DoAll(SaveArg<0>(&origin), Return(true)));
140 ReceiveIndication(GetTestOriginClientIndication());
141 EXPECT_TRUE(session_->IsSessionReady());
142 EXPECT_EQ(origin, GetTestOrigin());
143 EXPECT_EQ(path_.path(), "/test");
144 }
145
TEST_F(QuicTransportServerSessionTest,PiecewiseClientIndication)146 TEST_F(QuicTransportServerSessionTest, PiecewiseClientIndication) {
147 Connect();
148 size_t i = 0;
149 for (; i < sizeof(kTestOriginClientIndication) - 2; i++) {
150 QuicStreamFrame frame(
151 ClientIndicationStream(), false, i,
152 quiche::QuicheStringPiece(&kTestOriginClientIndication[i], 1));
153 session_->OnStreamFrame(frame);
154 }
155
156 EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
157 QuicStreamFrame last_frame(
158 ClientIndicationStream(), true, i,
159 quiche::QuicheStringPiece(&kTestOriginClientIndication[i], 1));
160 session_->OnStreamFrame(last_frame);
161 EXPECT_TRUE(session_->IsSessionReady());
162 }
163
TEST_F(QuicTransportServerSessionTest,OriginRejected)164 TEST_F(QuicTransportServerSessionTest, OriginRejected) {
165 Connect();
166 EXPECT_CALL(connection_,
167 CloseConnection(_, HasSubstr("Origin check failed"), _));
168 EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(false));
169 ReceiveIndication(GetTestOriginClientIndication());
170 EXPECT_FALSE(session_->IsSessionReady());
171 }
172
MakeUnknownField(quiche::QuicheStringPiece payload)173 std::string MakeUnknownField(quiche::QuicheStringPiece payload) {
174 std::string buffer;
175 buffer.resize(payload.size() + 4);
176 QuicDataWriter writer(buffer.size(), &buffer[0]);
177 EXPECT_TRUE(writer.WriteUInt16(0xffff));
178 EXPECT_TRUE(writer.WriteUInt16(payload.size()));
179 EXPECT_TRUE(writer.WriteStringPiece(payload));
180 EXPECT_EQ(writer.remaining(), 0u);
181 return buffer;
182 }
183
TEST_F(QuicTransportServerSessionTest,SkipUnusedFields)184 TEST_F(QuicTransportServerSessionTest, SkipUnusedFields) {
185 Connect();
186 EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
187 ReceiveIndication(GetTestOriginClientIndication() +
188 MakeUnknownField("foobar"));
189 EXPECT_TRUE(session_->IsSessionReady());
190 }
191
TEST_F(QuicTransportServerSessionTest,SkipLongUnusedFields)192 TEST_F(QuicTransportServerSessionTest, SkipLongUnusedFields) {
193 const size_t bytes =
194 ClientIndicationMaxSize() - GetTestOriginClientIndication().size() - 4;
195 Connect();
196 EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
197 ReceiveIndication(GetTestOriginClientIndication() +
198 MakeUnknownField(std::string(bytes, 'a')));
199 EXPECT_TRUE(session_->IsSessionReady());
200 }
201
TEST_F(QuicTransportServerSessionTest,ClientIndicationTooLong)202 TEST_F(QuicTransportServerSessionTest, ClientIndicationTooLong) {
203 Connect();
204 EXPECT_CALL(
205 connection_,
206 CloseConnection(_, HasSubstr("Client indication size exceeds"), _))
207 .Times(AnyNumber());
208 ReceiveIndication(GetTestOriginClientIndication() +
209 MakeUnknownField(std::string(65534, 'a')));
210 EXPECT_FALSE(session_->IsSessionReady());
211 }
212
TEST_F(QuicTransportServerSessionTest,NoOrigin)213 TEST_F(QuicTransportServerSessionTest, NoOrigin) {
214 Connect();
215 EXPECT_CALL(connection_, CloseConnection(_, HasSubstr("No origin"), _));
216 ReceiveIndication(MakeUnknownField("foobar"));
217 EXPECT_FALSE(session_->IsSessionReady());
218 }
219
TEST_F(QuicTransportServerSessionTest,EmptyClientIndication)220 TEST_F(QuicTransportServerSessionTest, EmptyClientIndication) {
221 Connect();
222 EXPECT_CALL(connection_, CloseConnection(_, HasSubstr("No origin"), _));
223 ReceiveIndication("");
224 EXPECT_FALSE(session_->IsSessionReady());
225 }
226
TEST_F(QuicTransportServerSessionTest,MalformedIndicationHeader)227 TEST_F(QuicTransportServerSessionTest, MalformedIndicationHeader) {
228 Connect();
229 EXPECT_CALL(connection_,
230 CloseConnection(_, HasSubstr("Expected 16-bit key"), _));
231 ReceiveIndication("\xff");
232 EXPECT_FALSE(session_->IsSessionReady());
233 }
234
TEST_F(QuicTransportServerSessionTest,FieldTooShort)235 TEST_F(QuicTransportServerSessionTest, FieldTooShort) {
236 Connect();
237 EXPECT_CALL(
238 connection_,
239 CloseConnection(_, HasSubstr("Failed to read value for key 257"), _));
240 ReceiveIndication("\x01\x01\x01\x01");
241 EXPECT_FALSE(session_->IsSessionReady());
242 }
243
TEST_F(QuicTransportServerSessionTest,InvalidOrigin)244 TEST_F(QuicTransportServerSessionTest, InvalidOrigin) {
245 const std::string kEmptyOriginIndication(4, '\0');
246 Connect();
247 EXPECT_CALL(
248 connection_,
249 CloseConnection(_, HasSubstr("Unable to parse the specified origin"), _));
250 ReceiveIndication(kEmptyOriginIndication);
251 EXPECT_FALSE(session_->IsSessionReady());
252 }
253
TEST_F(QuicTransportServerSessionTest,PathWithQuery)254 TEST_F(QuicTransportServerSessionTest, PathWithQuery) {
255 Connect();
256 EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
257 ReceiveIndicationWithPath("/test?foo=bar");
258 EXPECT_TRUE(session_->IsSessionReady());
259 EXPECT_EQ(path_.path(), "/test");
260 EXPECT_EQ(path_.query(), "foo=bar");
261 }
262
TEST_F(QuicTransportServerSessionTest,PathNormalization)263 TEST_F(QuicTransportServerSessionTest, PathNormalization) {
264 Connect();
265 EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
266 ReceiveIndicationWithPath("/foo/../bar");
267 EXPECT_TRUE(session_->IsSessionReady());
268 EXPECT_EQ(path_.path(), "/bar");
269 }
270
TEST_F(QuicTransportServerSessionTest,EmptyPath)271 TEST_F(QuicTransportServerSessionTest, EmptyPath) {
272 Connect();
273 EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
274 EXPECT_CALL(connection_,
275 CloseConnection(_, HasSubstr("Path must begin with a '/'"), _));
276 ReceiveIndicationWithPath("");
277 EXPECT_FALSE(session_->IsSessionReady());
278 }
279
TEST_F(QuicTransportServerSessionTest,UnprefixedPath)280 TEST_F(QuicTransportServerSessionTest, UnprefixedPath) {
281 Connect();
282 EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
283 EXPECT_CALL(connection_,
284 CloseConnection(_, HasSubstr("Path must begin with a '/'"), _));
285 ReceiveIndicationWithPath("test");
286 EXPECT_FALSE(session_->IsSessionReady());
287 }
288
289 } // namespace
290 } // namespace test
291 } // namespace quic
292