1 // Copyright 2024 The Chromium Authors
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 <memory>
6
7 #include "net/base/network_anonymization_key.h"
8 #include "net/base/proxy_chain.h"
9 #include "net/base/proxy_server.h"
10 #include "net/cert/x509_certificate.h"
11 #include "net/quic/crypto/proof_verifier_chromium.h"
12 #include "net/quic/mock_quic_data.h"
13 #include "net/quic/quic_context.h"
14 #include "net/quic/quic_http_stream.h"
15 #include "net/quic/quic_session_pool.h"
16 #include "net/quic/quic_session_pool_test_base.h"
17 #include "net/quic/quic_test_packet_maker.h"
18 #include "net/test/cert_test_util.h"
19 #include "net/test/test_data_directory.h"
20 #include "net/third_party/quiche/src/quiche/quic/core/quic_types.h"
21 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
22 #include "net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 namespace net::test {
26
27 class QuicSessionPoolProxyJobTest
28 : public QuicSessionPoolTestBase,
29 public ::testing::TestWithParam<quic::ParsedQuicVersion> {
30 protected:
QuicSessionPoolProxyJobTest()31 QuicSessionPoolProxyJobTest() : QuicSessionPoolTestBase(GetParam()) {}
32 };
33
34 INSTANTIATE_TEST_SUITE_P(All,
35 QuicSessionPoolProxyJobTest,
36 ::testing::ValuesIn(AllSupportedQuicVersions()));
37
TEST_P(QuicSessionPoolProxyJobTest,CreateProxiedQuicSession)38 TEST_P(QuicSessionPoolProxyJobTest, CreateProxiedQuicSession) {
39 Initialize();
40
41 GURL url("https://www.example.org/");
42 GURL proxy(kProxy1Url);
43 auto origin = url::SchemeHostPort(url);
44 auto proxy_origin = url::SchemeHostPort(proxy);
45
46 scoped_refptr<X509Certificate> cert(
47 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
48 ASSERT_TRUE(cert->VerifyNameMatch(origin.host()));
49 ASSERT_TRUE(cert->VerifyNameMatch(proxy_origin.host()));
50 ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname));
51
52 ProofVerifyDetailsChromium verify_details;
53 verify_details.cert_verify_result.verified_cert = cert;
54 verify_details.cert_verify_result.is_issued_by_known_root = true;
55 crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
56
57 // QUIC proxies do not use priority header.
58 client_maker_.set_use_priority_header(false);
59
60 // Use a separate packet maker for the connection to the endpoint.
61 QuicTestPacketMaker endpoint_maker(
62 version_,
63 quic::QuicUtils::CreateRandomConnectionId(context_.random_generator()),
64 context_.clock(), kDefaultServerHostName, quic::Perspective::IS_CLIENT,
65 /*client_priority_uses_incremental=*/true,
66 /*use_priority_header=*/true);
67
68 const uint64_t stream_id = GetNthClientInitiatedBidirectionalStreamId(0);
69 MockQuicData socket_data(version_);
70 socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(1));
71 socket_data.AddWrite(
72 SYNCHRONOUS, ConstructConnectUdpRequestPacket(
73 2, stream_id, proxy.host(),
74 "/.well-known/masque/udp/www.example.org/443/", false));
75 socket_data.AddRead(ASYNC, ConstructServerSettingsPacket(3));
76 socket_data.AddRead(ASYNC, ConstructOkResponsePacket(4, stream_id, true));
77 socket_data.AddReadPauseForever();
78 socket_data.AddWrite(ASYNC, client_maker_.MakeAckPacket(3, 3, 4, 3));
79 socket_data.AddWrite(ASYNC, ConstructClientH3DatagramPacket(
80 4, stream_id, kConnectUdpContextId,
81 endpoint_maker.MakeInitialSettingsPacket(1)));
82 socket_data.AddSocketDataToFactory(socket_factory_.get());
83
84 auto proxy_chain = ProxyChain::ForIpProtection({
85 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
86 proxy_origin.host(), 443),
87 });
88 EXPECT_TRUE(proxy_chain.IsValid());
89
90 RequestBuilder builder(this);
91 builder.destination = origin;
92 builder.proxy_chain = proxy_chain;
93 builder.http_user_agent_settings = &http_user_agent_settings_;
94 builder.url = url;
95 EXPECT_EQ(ERR_IO_PENDING, builder.CallRequest());
96 ASSERT_EQ(OK, callback_.WaitForResult());
97 std::unique_ptr<HttpStream> stream = CreateStream(&builder.request);
98 EXPECT_TRUE(stream.get());
99 QuicChromiumClientSession* session =
100 GetActiveSession(origin, NetworkAnonymizationKey(), proxy_chain);
101 ASSERT_TRUE(session);
102
103 // Max datagram size is limited by two layers of packet framing (38 bytes
104 // each), 1 byte for the quarter-stream-ID (which is always less than 64, thus
105 // one byte), and one byte for the CONNECT-UDP context.
106 quic::QuicByteCount largest_message_payload =
107 quic::kDefaultMaxPacketSize - 38 * 2 - 1 - 1;
108 EXPECT_EQ(session->GetGuaranteedLargestMessagePayload(),
109 largest_message_payload);
110
111 stream.reset();
112
113 // Ensure the session finishes creating before proceeding.
114 RunUntilIdle();
115
116 socket_data.ExpectAllReadDataConsumed();
117 socket_data.ExpectAllWriteDataConsumed();
118 }
119
TEST_P(QuicSessionPoolProxyJobTest,CreateProxySessionFails)120 TEST_P(QuicSessionPoolProxyJobTest, CreateProxySessionFails) {
121 Initialize();
122
123 GURL url("https://www.example.org/");
124 GURL proxy(kProxy1Url);
125 auto origin = url::SchemeHostPort(url);
126 auto proxy_origin = url::SchemeHostPort(proxy);
127
128 scoped_refptr<X509Certificate> cert(
129 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
130 ASSERT_TRUE(cert->VerifyNameMatch(origin.host()));
131 ASSERT_TRUE(cert->VerifyNameMatch(proxy_origin.host()));
132 ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname));
133
134 ProofVerifyDetailsChromium verify_details;
135 verify_details.cert_verify_result.verified_cert = cert;
136 verify_details.cert_verify_result.is_issued_by_known_root = true;
137 crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
138
139 MockQuicData socket_data(version_);
140 socket_data.AddReadPauseForever();
141 // Creation of underlying session fails immediately.
142 socket_data.AddWrite(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED);
143 socket_data.AddSocketDataToFactory(socket_factory_.get());
144
145 auto proxy_chain = ProxyChain::ForIpProtection({
146 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
147 proxy_origin.host(), 443),
148 });
149 EXPECT_TRUE(proxy_chain.IsValid());
150
151 RequestBuilder builder(this);
152 builder.destination = origin;
153 builder.proxy_chain = proxy_chain;
154 builder.http_user_agent_settings = &http_user_agent_settings_;
155 builder.url = url;
156 EXPECT_EQ(ERR_IO_PENDING, builder.CallRequest());
157 ASSERT_EQ(ERR_QUIC_HANDSHAKE_FAILED, callback_.WaitForResult());
158
159 socket_data.ExpectAllReadDataConsumed();
160 socket_data.ExpectAllWriteDataConsumed();
161 }
162
TEST_P(QuicSessionPoolProxyJobTest,CreateSessionFails)163 TEST_P(QuicSessionPoolProxyJobTest, CreateSessionFails) {
164 Initialize();
165
166 GURL url("https://www.example.org/");
167 GURL proxy(kProxy1Url);
168 auto origin = url::SchemeHostPort(url);
169 auto proxy_origin = url::SchemeHostPort(proxy);
170
171 scoped_refptr<X509Certificate> cert(
172 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
173 ASSERT_TRUE(cert->VerifyNameMatch(origin.host()));
174 ASSERT_TRUE(cert->VerifyNameMatch(proxy_origin.host()));
175 ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname));
176
177 ProofVerifyDetailsChromium verify_details;
178 verify_details.cert_verify_result.verified_cert = cert;
179 verify_details.cert_verify_result.is_issued_by_known_root = true;
180 crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
181
182 // QUIC proxies do not use priority header.
183 client_maker_.set_use_priority_header(false);
184
185 MockQuicData socket_data(version_);
186 socket_data.AddReadPauseForever(); // SYNC/ERR_IO_PENDING
187 socket_data.AddWritePause();
188 socket_data.AddWrite(ASYNC, ConstructInitialSettingsPacket(1));
189 socket_data.AddSocketDataToFactory(socket_factory_.get());
190
191 auto proxy_chain = ProxyChain::ForIpProtection({
192 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
193 proxy_origin.host(), 443),
194 });
195 EXPECT_TRUE(proxy_chain.IsValid());
196
197 RequestBuilder builder(this);
198 builder.destination = origin;
199 builder.proxy_chain = proxy_chain;
200 builder.http_user_agent_settings = &http_user_agent_settings_;
201 builder.url = url;
202 EXPECT_EQ(ERR_IO_PENDING, builder.CallRequest());
203
204 RunUntilIdle();
205
206 // Oops, the session went away. This generates an error
207 // from `QuicSessionPool::CreateSessionOnProxyStream`.
208 factory_->CloseAllSessions(ERR_QUIC_HANDSHAKE_FAILED,
209 quic::QuicErrorCode::QUIC_INTERNAL_ERROR);
210 socket_data.Resume();
211
212 ASSERT_EQ(ERR_QUIC_HANDSHAKE_FAILED, callback_.WaitForResult());
213 }
214
215 } // namespace net::test
216