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 // Sets up a dispatcher and sends requests via the QboneClient.
6 
7 #include "net/third_party/quiche/src/quic/qbone/qbone_client.h"
8 
9 #include "absl/strings/string_view.h"
10 #include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h"
11 #include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h"
12 #include "net/third_party/quiche/src/quic/core/quic_dispatcher.h"
13 #include "net/third_party/quiche/src/quic/core/quic_epoll_alarm_factory.h"
14 #include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h"
15 #include "net/third_party/quiche/src/quic/core/quic_packet_reader.h"
16 #include "net/third_party/quiche/src/quic/platform/api/quic_mutex.h"
17 #include "net/third_party/quiche/src/quic/platform/api/quic_port_utils.h"
18 #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
19 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
20 #include "net/third_party/quiche/src/quic/platform/api/quic_test_loopback.h"
21 #include "net/third_party/quiche/src/quic/qbone/qbone_constants.h"
22 #include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor_test_tools.h"
23 #include "net/third_party/quiche/src/quic/qbone/qbone_server_session.h"
24 #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
25 #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
26 #include "net/third_party/quiche/src/quic/test_tools/quic_server_peer.h"
27 #include "net/third_party/quiche/src/quic/test_tools/server_thread.h"
28 #include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
29 #include "net/third_party/quiche/src/quic/tools/quic_server.h"
30 
31 namespace quic {
32 namespace test {
33 namespace {
34 
GetTestParams()35 ParsedQuicVersionVector GetTestParams() {
36   ParsedQuicVersionVector test_versions;
37 
38   // TODO(b/113130636): Make QBONE work with TLS.
39   for (const auto& version : CurrentSupportedVersionsWithQuicCrypto()) {
40     // QBONE requires MESSAGE frames
41     if (!version.SupportsMessageFrames()) {
42       continue;
43     }
44     test_versions.push_back(version);
45   }
46 
47   return test_versions;
48 }
49 
TestPacketIn(const std::string & body)50 std::string TestPacketIn(const std::string& body) {
51   return PrependIPv6HeaderForTest(body, 5);
52 }
53 
TestPacketOut(const std::string & body)54 std::string TestPacketOut(const std::string& body) {
55   return PrependIPv6HeaderForTest(body, 4);
56 }
57 
58 class DataSavingQbonePacketWriter : public QbonePacketWriter {
59  public:
WritePacketToNetwork(const char * packet,size_t size)60   void WritePacketToNetwork(const char* packet, size_t size) override {
61     QuicWriterMutexLock lock(&mu_);
62     data_.push_back(std::string(packet, size));
63   }
64 
data()65   std::vector<std::string> data() {
66     QuicWriterMutexLock lock(&mu_);
67     return data_;
68   }
69 
70  private:
71   QuicMutex mu_;
72   std::vector<std::string> data_;
73 };
74 
75 // A subclass of a QBONE session that will own the connection passed in.
76 class ConnectionOwningQboneServerSession : public QboneServerSession {
77  public:
ConnectionOwningQboneServerSession(const ParsedQuicVersionVector & supported_versions,QuicConnection * connection,Visitor * owner,const QuicConfig & config,const QuicCryptoServerConfig * quic_crypto_server_config,QuicCompressedCertsCache * compressed_certs_cache,QbonePacketWriter * writer)78   ConnectionOwningQboneServerSession(
79       const ParsedQuicVersionVector& supported_versions,
80       QuicConnection* connection,
81       Visitor* owner,
82       const QuicConfig& config,
83       const QuicCryptoServerConfig* quic_crypto_server_config,
84       QuicCompressedCertsCache* compressed_certs_cache,
85       QbonePacketWriter* writer)
86       : QboneServerSession(supported_versions,
87                            connection,
88                            owner,
89                            config,
90                            quic_crypto_server_config,
91                            compressed_certs_cache,
92                            writer,
93                            TestLoopback6(),
94                            TestLoopback6(),
95                            64,
96                            nullptr),
97         connection_(connection) {}
98 
99  private:
100   // Note that we don't expect the QboneServerSession or any of its parent
101   // classes to do anything with the connection_ in their destructors.
102   std::unique_ptr<QuicConnection> connection_;
103 };
104 
105 class QuicQboneDispatcher : public QuicDispatcher {
106  public:
QuicQboneDispatcher(const QuicConfig * config,const QuicCryptoServerConfig * crypto_config,QuicVersionManager * version_manager,std::unique_ptr<QuicConnectionHelperInterface> helper,std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper,std::unique_ptr<QuicAlarmFactory> alarm_factory,QbonePacketWriter * writer)107   QuicQboneDispatcher(
108       const QuicConfig* config,
109       const QuicCryptoServerConfig* crypto_config,
110       QuicVersionManager* version_manager,
111       std::unique_ptr<QuicConnectionHelperInterface> helper,
112       std::unique_ptr<QuicCryptoServerStreamBase::Helper> session_helper,
113       std::unique_ptr<QuicAlarmFactory> alarm_factory,
114       QbonePacketWriter* writer)
115       : QuicDispatcher(config,
116                        crypto_config,
117                        version_manager,
118                        std::move(helper),
119                        std::move(session_helper),
120                        std::move(alarm_factory),
121                        kQuicDefaultConnectionIdLength),
122         writer_(writer) {}
123 
CreateQuicSession(QuicConnectionId id,const QuicSocketAddress & self_address,const QuicSocketAddress & peer_address,absl::string_view alpn,const quic::ParsedQuicVersion & version)124   std::unique_ptr<QuicSession> CreateQuicSession(
125       QuicConnectionId id,
126       const QuicSocketAddress& self_address,
127       const QuicSocketAddress& peer_address,
128       absl::string_view alpn,
129       const quic::ParsedQuicVersion& version) override {
130     CHECK_EQ(alpn, "qbone");
131     QuicConnection* connection = new QuicConnection(
132         id, self_address, peer_address, helper(), alarm_factory(), writer(),
133         /* owns_writer= */ false, Perspective::IS_SERVER,
134         ParsedQuicVersionVector{version});
135     // The connection owning wrapper owns the connection created.
136     auto session = std::make_unique<ConnectionOwningQboneServerSession>(
137         GetSupportedVersions(), connection, this, config(), crypto_config(),
138         compressed_certs_cache(), writer_);
139     session->Initialize();
140     return session;
141   }
142 
143  private:
144   QbonePacketWriter* writer_;
145 };
146 
147 class QboneTestServer : public QuicServer {
148  public:
QboneTestServer(std::unique_ptr<ProofSource> proof_source)149   explicit QboneTestServer(std::unique_ptr<ProofSource> proof_source)
150       : QuicServer(std::move(proof_source), &response_cache_) {}
CreateQuicDispatcher()151   QuicDispatcher* CreateQuicDispatcher() override {
152     QuicEpollAlarmFactory alarm_factory(epoll_server());
153     return new QuicQboneDispatcher(
154         &config(), &crypto_config(), version_manager(),
155         std::unique_ptr<QuicEpollConnectionHelper>(
156             new QuicEpollConnectionHelper(epoll_server(),
157                                           QuicAllocator::BUFFER_POOL)),
158         std::unique_ptr<QuicCryptoServerStreamBase::Helper>(
159             new QboneCryptoServerStreamHelper()),
160         std::unique_ptr<QuicEpollAlarmFactory>(
161             new QuicEpollAlarmFactory(epoll_server())),
162         &writer_);
163   }
164 
data()165   std::vector<std::string> data() { return writer_.data(); }
166 
167  private:
168   quic::QuicMemoryCacheBackend response_cache_;
169   DataSavingQbonePacketWriter writer_;
170 };
171 
172 class QboneTestClient : public QboneClient {
173  public:
QboneTestClient(QuicSocketAddress server_address,const QuicServerId & server_id,const ParsedQuicVersionVector & supported_versions,QuicEpollServer * epoll_server,std::unique_ptr<ProofVerifier> proof_verifier)174   QboneTestClient(QuicSocketAddress server_address,
175                   const QuicServerId& server_id,
176                   const ParsedQuicVersionVector& supported_versions,
177                   QuicEpollServer* epoll_server,
178                   std::unique_ptr<ProofVerifier> proof_verifier)
179       : QboneClient(server_address,
180                     server_id,
181                     supported_versions,
182                     /*session_owner=*/nullptr,
183                     QuicConfig(),
184                     epoll_server,
185                     std::move(proof_verifier),
186                     &qbone_writer_,
187                     nullptr) {}
188 
~QboneTestClient()189   ~QboneTestClient() override {}
190 
SendData(const std::string & data)191   void SendData(const std::string& data) {
192     qbone_session()->ProcessPacketFromNetwork(data);
193   }
194 
WaitForWriteToFlush()195   void WaitForWriteToFlush() {
196     while (connected() && session()->HasDataToWrite()) {
197       WaitForEvents();
198     }
199   }
200 
201   // Returns true when the data size is reached or false on timeouts.
WaitForDataSize(int n,QuicTime::Delta timeout)202   bool WaitForDataSize(int n, QuicTime::Delta timeout) {
203     const QuicClock* clock =
204         quic::test::QuicConnectionPeer::GetHelper(session()->connection())
205             ->GetClock();
206     const QuicTime deadline = clock->Now() + timeout;
207     while (data().size() < n) {
208       if (clock->Now() > deadline) {
209         return false;
210       }
211       WaitForEvents();
212     }
213     return true;
214   }
215 
data()216   std::vector<std::string> data() { return qbone_writer_.data(); }
217 
218  private:
219   DataSavingQbonePacketWriter qbone_writer_;
220 };
221 
222 class QboneClientTest : public QuicTestWithParam<ParsedQuicVersion> {};
223 
224 INSTANTIATE_TEST_SUITE_P(Tests,
225                          QboneClientTest,
226                          ::testing::ValuesIn(GetTestParams()),
227                          ::testing::PrintToStringParamName());
228 
TEST_P(QboneClientTest,SendDataFromClient)229 TEST_P(QboneClientTest, SendDataFromClient) {
230   auto server = new QboneTestServer(crypto_test_utils::ProofSourceForTesting());
231   QuicSocketAddress server_address(TestLoopback(),
232                                    QuicPickServerPortForTestsOrDie());
233   ServerThread server_thread(server, server_address);
234   server_thread.Initialize();
235   server_address =
236       QuicSocketAddress(server_address.host(), server_thread.GetPort());
237   server_thread.Start();
238 
239   QuicEpollServer epoll_server;
240   QboneTestClient client(
241       server_address,
242       QuicServerId("test.example.com", server_address.port(), false),
243       ParsedQuicVersionVector{GetParam()}, &epoll_server,
244       crypto_test_utils::ProofVerifierForTesting());
245   ASSERT_TRUE(client.Initialize());
246   ASSERT_TRUE(client.Connect());
247   ASSERT_TRUE(client.WaitForOneRttKeysAvailable());
248   client.SendData(TestPacketIn("hello"));
249   client.SendData(TestPacketIn("world"));
250   client.WaitForWriteToFlush();
251 
252   // Wait until the server has received at least two packets, timeout after 5s.
253   ASSERT_TRUE(
254       server_thread.WaitUntil([&] { return server->data().size() >= 2; },
255                               QuicTime::Delta::FromSeconds(5)));
256 
257   std::string long_data(
258       QboneConstants::kMaxQbonePacketBytes - sizeof(ip6_hdr) - 1, 'A');
259 
260   // Pretend the server gets data.
261   server_thread.Schedule([&server, &long_data]() {
262     EXPECT_THAT(server->data()[0], testing::Eq(TestPacketOut("hello")));
263     EXPECT_THAT(server->data()[1], testing::Eq(TestPacketOut("world")));
264     auto server_session =
265         static_cast<QboneServerSession*>(QuicServerPeer::GetDispatcher(server)
266                                              ->session_map()
267                                              .begin()
268                                              ->second.get());
269     server_session->ProcessPacketFromNetwork(
270         TestPacketIn("Somethingsomething"));
271     server_session->ProcessPacketFromNetwork(TestPacketIn(long_data));
272     server_session->ProcessPacketFromNetwork(TestPacketIn(long_data));
273   });
274   ASSERT_TRUE(client.WaitForDataSize(3, QuicTime::Delta::FromSeconds(5)));
275   EXPECT_THAT(client.data()[0],
276               testing::Eq(TestPacketOut("Somethingsomething")));
277   EXPECT_THAT(client.data()[1], testing::Eq(TestPacketOut(long_data)));
278   EXPECT_THAT(client.data()[2], testing::Eq(TestPacketOut(long_data)));
279 
280   client.Disconnect();
281   server_thread.Quit();
282   server_thread.Join();
283 }
284 
285 }  // namespace
286 }  // namespace test
287 }  // namespace quic
288