1 // Copyright 2022 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 "quiche/quic/tools/connect_udp_tunnel.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "absl/status/status.h"
11 #include "absl/status/statusor.h"
12 #include "absl/strings/str_cat.h"
13 #include "absl/strings/string_view.h"
14 #include "quiche/quic/core/connecting_client_socket.h"
15 #include "quiche/quic/core/http/quic_spdy_stream.h"
16 #include "quiche/quic/core/quic_connection_id.h"
17 #include "quiche/quic/core/quic_error_codes.h"
18 #include "quiche/quic/core/quic_types.h"
19 #include "quiche/quic/core/socket_factory.h"
20 #include "quiche/quic/platform/api/quic_socket_address.h"
21 #include "quiche/quic/platform/api/quic_test_loopback.h"
22 #include "quiche/quic/test_tools/quic_test_utils.h"
23 #include "quiche/quic/tools/quic_simple_server_backend.h"
24 #include "quiche/common/masque/connect_udp_datagram_payload.h"
25 #include "quiche/common/platform/api/quiche_googleurl.h"
26 #include "quiche/common/platform/api/quiche_mem_slice.h"
27 #include "quiche/common/platform/api/quiche_test.h"
28 #include "quiche/common/platform/api/quiche_url_utils.h"
29
30 namespace quic::test {
31 namespace {
32
33 using ::testing::_;
34 using ::testing::AnyOf;
35 using ::testing::Eq;
36 using ::testing::Ge;
37 using ::testing::Gt;
38 using ::testing::HasSubstr;
39 using ::testing::InvokeWithoutArgs;
40 using ::testing::IsEmpty;
41 using ::testing::Matcher;
42 using ::testing::NiceMock;
43 using ::testing::Pair;
44 using ::testing::Property;
45 using ::testing::Return;
46 using ::testing::StrictMock;
47 using ::testing::UnorderedElementsAre;
48
49 constexpr QuicStreamId kStreamId = 100;
50
51 class MockStream : public QuicSpdyStream {
52 public:
MockStream(QuicSpdySession * spdy_session)53 explicit MockStream(QuicSpdySession* spdy_session)
54 : QuicSpdyStream(kStreamId, spdy_session, BIDIRECTIONAL) {}
55
OnBodyAvailable()56 void OnBodyAvailable() override {}
57
58 MOCK_METHOD(MessageStatus, SendHttp3Datagram, (absl::string_view data),
59 (override));
60 };
61
62 class MockRequestHandler : public QuicSimpleServerBackend::RequestHandler {
63 public:
connection_id() const64 QuicConnectionId connection_id() const override {
65 return TestConnectionId(41212);
66 }
stream_id() const67 QuicStreamId stream_id() const override { return kStreamId; }
peer_host() const68 std::string peer_host() const override { return "127.0.0.1"; }
69
70 MOCK_METHOD(QuicSpdyStream*, GetStream, (), (override));
71 MOCK_METHOD(void, OnResponseBackendComplete,
72 (const QuicBackendResponse* response), (override));
73 MOCK_METHOD(void, SendStreamData, (absl::string_view data, bool close_stream),
74 (override));
75 MOCK_METHOD(void, TerminateStreamWithError, (QuicResetStreamError error),
76 (override));
77 };
78
79 class MockSocketFactory : public SocketFactory {
80 public:
81 MOCK_METHOD(std::unique_ptr<ConnectingClientSocket>, CreateTcpClientSocket,
82 (const QuicSocketAddress& peer_address,
83 QuicByteCount receive_buffer_size,
84 QuicByteCount send_buffer_size,
85 ConnectingClientSocket::AsyncVisitor* async_visitor),
86 (override));
87 MOCK_METHOD(std::unique_ptr<ConnectingClientSocket>,
88 CreateConnectingUdpClientSocket,
89 (const QuicSocketAddress& peer_address,
90 QuicByteCount receive_buffer_size,
91 QuicByteCount send_buffer_size,
92 ConnectingClientSocket::AsyncVisitor* async_visitor),
93 (override));
94 };
95
96 class MockSocket : public ConnectingClientSocket {
97 public:
98 MOCK_METHOD(absl::Status, ConnectBlocking, (), (override));
99 MOCK_METHOD(void, ConnectAsync, (), (override));
100 MOCK_METHOD(void, Disconnect, (), (override));
101 MOCK_METHOD(absl::StatusOr<QuicSocketAddress>, GetLocalAddress, (),
102 (override));
103 MOCK_METHOD(absl::StatusOr<quiche::QuicheMemSlice>, ReceiveBlocking,
104 (QuicByteCount max_size), (override));
105 MOCK_METHOD(void, ReceiveAsync, (QuicByteCount max_size), (override));
106 MOCK_METHOD(absl::Status, SendBlocking, (std::string data), (override));
107 MOCK_METHOD(absl::Status, SendBlocking, (quiche::QuicheMemSlice data),
108 (override));
109 MOCK_METHOD(void, SendAsync, (std::string data), (override));
110 MOCK_METHOD(void, SendAsync, (quiche::QuicheMemSlice data), (override));
111 };
112
113 class ConnectUdpTunnelTest : public quiche::test::QuicheTest {
114 public:
SetUp()115 void SetUp() override {
116 #if defined(_WIN32)
117 WSADATA wsa_data;
118 const WORD version_required = MAKEWORD(2, 2);
119 ASSERT_EQ(WSAStartup(version_required, &wsa_data), 0);
120 #endif
121 auto socket = std::make_unique<StrictMock<MockSocket>>();
122 socket_ = socket.get();
123 ON_CALL(socket_factory_,
124 CreateConnectingUdpClientSocket(
125 AnyOf(QuicSocketAddress(TestLoopback4(), kAcceptablePort),
126 QuicSocketAddress(TestLoopback6(), kAcceptablePort)),
127 _, _, &tunnel_))
128 .WillByDefault(Return(ByMove(std::move(socket))));
129
130 EXPECT_CALL(request_handler_, GetStream()).WillRepeatedly(Return(&stream_));
131 }
132
133 protected:
134 static constexpr absl::string_view kAcceptableTarget = "localhost";
135 static constexpr uint16_t kAcceptablePort = 977;
136
137 NiceMock<MockQuicConnectionHelper> connection_helper_;
138 NiceMock<MockAlarmFactory> alarm_factory_;
139 NiceMock<MockQuicSpdySession> session_{new NiceMock<MockQuicConnection>(
140 &connection_helper_, &alarm_factory_, Perspective::IS_SERVER)};
141 StrictMock<MockStream> stream_{&session_};
142
143 StrictMock<MockRequestHandler> request_handler_;
144 NiceMock<MockSocketFactory> socket_factory_;
145 StrictMock<MockSocket>* socket_;
146
147 ConnectUdpTunnel tunnel_{
148 &request_handler_,
149 &socket_factory_,
150 "server_label",
151 /*acceptable_targets=*/
152 {{std::string(kAcceptableTarget), kAcceptablePort},
153 {TestLoopback4().ToString(), kAcceptablePort},
154 {absl::StrCat("[", TestLoopback6().ToString(), "]"), kAcceptablePort}}};
155 };
156
TEST_F(ConnectUdpTunnelTest,OpenTunnel)157 TEST_F(ConnectUdpTunnelTest, OpenTunnel) {
158 EXPECT_CALL(*socket_, ConnectBlocking()).WillOnce(Return(absl::OkStatus()));
159 EXPECT_CALL(*socket_, ReceiveAsync(Gt(0)));
160 EXPECT_CALL(*socket_, Disconnect()).WillOnce(InvokeWithoutArgs([this]() {
161 tunnel_.ReceiveComplete(absl::CancelledError());
162 }));
163
164 EXPECT_CALL(
165 request_handler_,
166 OnResponseBackendComplete(
167 AllOf(Property(&QuicBackendResponse::response_type,
168 QuicBackendResponse::INCOMPLETE_RESPONSE),
169 Property(&QuicBackendResponse::headers,
170 UnorderedElementsAre(Pair(":status", "200"),
171 Pair("Capsule-Protocol", "?1"))),
172 Property(&QuicBackendResponse::trailers, IsEmpty()),
173 Property(&QuicBackendResponse::body, IsEmpty()))));
174
175 spdy::Http2HeaderBlock request_headers;
176 request_headers[":method"] = "CONNECT";
177 request_headers[":protocol"] = "connect-udp";
178 request_headers[":authority"] = "proxy.test";
179 request_headers[":scheme"] = "https";
180 request_headers[":path"] = absl::StrCat(
181 "/.well-known/masque/udp/", kAcceptableTarget, "/", kAcceptablePort, "/");
182
183 tunnel_.OpenTunnel(request_headers);
184 EXPECT_TRUE(tunnel_.IsTunnelOpenToTarget());
185 tunnel_.OnClientStreamClose();
186 EXPECT_FALSE(tunnel_.IsTunnelOpenToTarget());
187 }
188
TEST_F(ConnectUdpTunnelTest,OpenTunnelToIpv4LiteralTarget)189 TEST_F(ConnectUdpTunnelTest, OpenTunnelToIpv4LiteralTarget) {
190 EXPECT_CALL(*socket_, ConnectBlocking()).WillOnce(Return(absl::OkStatus()));
191 EXPECT_CALL(*socket_, ReceiveAsync(Gt(0)));
192 EXPECT_CALL(*socket_, Disconnect()).WillOnce(InvokeWithoutArgs([this]() {
193 tunnel_.ReceiveComplete(absl::CancelledError());
194 }));
195
196 EXPECT_CALL(
197 request_handler_,
198 OnResponseBackendComplete(
199 AllOf(Property(&QuicBackendResponse::response_type,
200 QuicBackendResponse::INCOMPLETE_RESPONSE),
201 Property(&QuicBackendResponse::headers,
202 UnorderedElementsAre(Pair(":status", "200"),
203 Pair("Capsule-Protocol", "?1"))),
204 Property(&QuicBackendResponse::trailers, IsEmpty()),
205 Property(&QuicBackendResponse::body, IsEmpty()))));
206
207 spdy::Http2HeaderBlock request_headers;
208 request_headers[":method"] = "CONNECT";
209 request_headers[":protocol"] = "connect-udp";
210 request_headers[":authority"] = "proxy.test";
211 request_headers[":scheme"] = "https";
212 request_headers[":path"] =
213 absl::StrCat("/.well-known/masque/udp/", TestLoopback4().ToString(), "/",
214 kAcceptablePort, "/");
215
216 tunnel_.OpenTunnel(request_headers);
217 EXPECT_TRUE(tunnel_.IsTunnelOpenToTarget());
218 tunnel_.OnClientStreamClose();
219 EXPECT_FALSE(tunnel_.IsTunnelOpenToTarget());
220 }
221
TEST_F(ConnectUdpTunnelTest,OpenTunnelToIpv6LiteralTarget)222 TEST_F(ConnectUdpTunnelTest, OpenTunnelToIpv6LiteralTarget) {
223 EXPECT_CALL(*socket_, ConnectBlocking()).WillOnce(Return(absl::OkStatus()));
224 EXPECT_CALL(*socket_, ReceiveAsync(Gt(0)));
225 EXPECT_CALL(*socket_, Disconnect()).WillOnce(InvokeWithoutArgs([this]() {
226 tunnel_.ReceiveComplete(absl::CancelledError());
227 }));
228
229 EXPECT_CALL(
230 request_handler_,
231 OnResponseBackendComplete(
232 AllOf(Property(&QuicBackendResponse::response_type,
233 QuicBackendResponse::INCOMPLETE_RESPONSE),
234 Property(&QuicBackendResponse::headers,
235 UnorderedElementsAre(Pair(":status", "200"),
236 Pair("Capsule-Protocol", "?1"))),
237 Property(&QuicBackendResponse::trailers, IsEmpty()),
238 Property(&QuicBackendResponse::body, IsEmpty()))));
239
240 std::string path;
241 ASSERT_TRUE(quiche::ExpandURITemplate(
242 "/.well-known/masque/udp/{target_host}/{target_port}/",
243 {{"target_host", absl::StrCat("[", TestLoopback6().ToString(), "]")},
244 {"target_port", absl::StrCat(kAcceptablePort)}},
245 &path));
246
247 spdy::Http2HeaderBlock request_headers;
248 request_headers[":method"] = "CONNECT";
249 request_headers[":protocol"] = "connect-udp";
250 request_headers[":authority"] = "proxy.test";
251 request_headers[":scheme"] = "https";
252 request_headers[":path"] = path;
253
254 tunnel_.OpenTunnel(request_headers);
255 EXPECT_TRUE(tunnel_.IsTunnelOpenToTarget());
256 tunnel_.OnClientStreamClose();
257 EXPECT_FALSE(tunnel_.IsTunnelOpenToTarget());
258 }
259
TEST_F(ConnectUdpTunnelTest,OpenTunnelWithMalformedRequest)260 TEST_F(ConnectUdpTunnelTest, OpenTunnelWithMalformedRequest) {
261 EXPECT_CALL(request_handler_,
262 TerminateStreamWithError(Property(
263 &QuicResetStreamError::ietf_application_code,
264 static_cast<uint64_t>(QuicHttp3ErrorCode::MESSAGE_ERROR))));
265
266 spdy::Http2HeaderBlock request_headers;
267 request_headers[":method"] = "CONNECT";
268 request_headers[":protocol"] = "connect-udp";
269 request_headers[":authority"] = "proxy.test";
270 request_headers[":scheme"] = "https";
271 // No ":path" header.
272
273 tunnel_.OpenTunnel(request_headers);
274 EXPECT_FALSE(tunnel_.IsTunnelOpenToTarget());
275 tunnel_.OnClientStreamClose();
276 }
277
TEST_F(ConnectUdpTunnelTest,OpenTunnelWithUnacceptableTarget)278 TEST_F(ConnectUdpTunnelTest, OpenTunnelWithUnacceptableTarget) {
279 EXPECT_CALL(request_handler_,
280 OnResponseBackendComplete(AllOf(
281 Property(&QuicBackendResponse::response_type,
282 QuicBackendResponse::REGULAR_RESPONSE),
283 Property(&QuicBackendResponse::headers,
284 UnorderedElementsAre(
285 Pair(":status", "403"),
286 Pair("Proxy-Status",
287 HasSubstr("destination_ip_prohibited")))),
288 Property(&QuicBackendResponse::trailers, IsEmpty()))));
289
290 spdy::Http2HeaderBlock request_headers;
291 request_headers[":method"] = "CONNECT";
292 request_headers[":protocol"] = "connect-udp";
293 request_headers[":authority"] = "proxy.test";
294 request_headers[":scheme"] = "https";
295 request_headers[":path"] = "/.well-known/masque/udp/unacceptable.test/100/";
296
297 tunnel_.OpenTunnel(request_headers);
298 EXPECT_FALSE(tunnel_.IsTunnelOpenToTarget());
299 tunnel_.OnClientStreamClose();
300 }
301
TEST_F(ConnectUdpTunnelTest,ReceiveFromTarget)302 TEST_F(ConnectUdpTunnelTest, ReceiveFromTarget) {
303 static constexpr absl::string_view kData = "\x11\x22\x33\x44\x55";
304
305 EXPECT_CALL(*socket_, ConnectBlocking()).WillOnce(Return(absl::OkStatus()));
306 EXPECT_CALL(*socket_, ReceiveAsync(Ge(kData.size()))).Times(2);
307 EXPECT_CALL(*socket_, Disconnect()).WillOnce(InvokeWithoutArgs([this]() {
308 tunnel_.ReceiveComplete(absl::CancelledError());
309 }));
310
311 EXPECT_CALL(request_handler_, OnResponseBackendComplete(_));
312
313 EXPECT_CALL(
314 stream_,
315 SendHttp3Datagram(
316 quiche::ConnectUdpDatagramUdpPacketPayload(kData).Serialize()))
317 .WillOnce(Return(MESSAGE_STATUS_SUCCESS));
318
319 spdy::Http2HeaderBlock request_headers;
320 request_headers[":method"] = "CONNECT";
321 request_headers[":protocol"] = "connect-udp";
322 request_headers[":authority"] = "proxy.test";
323 request_headers[":scheme"] = "https";
324 request_headers[":path"] = absl::StrCat(
325 "/.well-known/masque/udp/", kAcceptableTarget, "/", kAcceptablePort, "/");
326
327 tunnel_.OpenTunnel(request_headers);
328
329 // Simulate receiving `kData`.
330 tunnel_.ReceiveComplete(MemSliceFromString(kData));
331
332 tunnel_.OnClientStreamClose();
333 }
334
TEST_F(ConnectUdpTunnelTest,SendToTarget)335 TEST_F(ConnectUdpTunnelTest, SendToTarget) {
336 static constexpr absl::string_view kData = "\x11\x22\x33\x44\x55";
337
338 EXPECT_CALL(*socket_, ConnectBlocking()).WillOnce(Return(absl::OkStatus()));
339 EXPECT_CALL(*socket_, ReceiveAsync(Gt(0)));
340 EXPECT_CALL(*socket_, SendBlocking(Matcher<std::string>(Eq(kData))))
341 .WillOnce(Return(absl::OkStatus()));
342 EXPECT_CALL(*socket_, Disconnect()).WillOnce(InvokeWithoutArgs([this]() {
343 tunnel_.ReceiveComplete(absl::CancelledError());
344 }));
345
346 EXPECT_CALL(request_handler_, OnResponseBackendComplete(_));
347
348 spdy::Http2HeaderBlock request_headers;
349 request_headers[":method"] = "CONNECT";
350 request_headers[":protocol"] = "connect-udp";
351 request_headers[":authority"] = "proxy.test";
352 request_headers[":scheme"] = "https";
353 request_headers[":path"] = absl::StrCat(
354 "/.well-known/masque/udp/", kAcceptableTarget, "/", kAcceptablePort, "/");
355
356 tunnel_.OpenTunnel(request_headers);
357 tunnel_.OnHttp3Datagram(
358 kStreamId, quiche::ConnectUdpDatagramUdpPacketPayload(kData).Serialize());
359 tunnel_.OnClientStreamClose();
360 }
361
362 } // namespace
363 } // namespace quic::test
364