1 // Copyright 2017 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 "net/quic/quic_proxy_client_socket.h"
6
7 #include <cstdio>
8 #include <string_view>
9 #include <utility>
10
11 #include "base/functional/bind.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/values.h"
14 #include "net/base/proxy_chain.h"
15 #include "net/base/proxy_delegate.h"
16 #include "net/http/http_auth_controller.h"
17 #include "net/http/http_log_util.h"
18 #include "net/http/http_response_headers.h"
19 #include "net/log/net_log_source.h"
20 #include "net/log/net_log_source_type.h"
21 #include "net/quic/quic_http_utils.h"
22 #include "net/spdy/spdy_http_utils.h"
23 #include "net/traffic_annotation/network_traffic_annotation.h"
24
25 namespace net {
26
QuicProxyClientSocket(std::unique_ptr<QuicChromiumClientStream::Handle> stream,std::unique_ptr<QuicChromiumClientSession::Handle> session,const ProxyChain & proxy_chain,size_t proxy_chain_index,const std::string & user_agent,const HostPortPair & endpoint,const NetLogWithSource & net_log,scoped_refptr<HttpAuthController> auth_controller,ProxyDelegate * proxy_delegate)27 QuicProxyClientSocket::QuicProxyClientSocket(
28 std::unique_ptr<QuicChromiumClientStream::Handle> stream,
29 std::unique_ptr<QuicChromiumClientSession::Handle> session,
30 const ProxyChain& proxy_chain,
31 size_t proxy_chain_index,
32 const std::string& user_agent,
33 const HostPortPair& endpoint,
34 const NetLogWithSource& net_log,
35 scoped_refptr<HttpAuthController> auth_controller,
36 ProxyDelegate* proxy_delegate)
37 : stream_(std::move(stream)),
38 session_(std::move(session)),
39 endpoint_(endpoint),
40 auth_(std::move(auth_controller)),
41 proxy_chain_(proxy_chain),
42 proxy_chain_index_(proxy_chain_index),
43 proxy_delegate_(proxy_delegate),
44 user_agent_(user_agent),
45 net_log_(net_log) {
46 DCHECK(stream_->IsOpen());
47
48 request_.method = "CONNECT";
49 request_.url = GURL("https://" + endpoint.ToString());
50
51 net_log_.BeginEventReferencingSource(NetLogEventType::SOCKET_ALIVE,
52 net_log_.source());
53 net_log_.AddEventReferencingSource(
54 NetLogEventType::HTTP2_PROXY_CLIENT_SESSION, stream_->net_log().source());
55 }
56
~QuicProxyClientSocket()57 QuicProxyClientSocket::~QuicProxyClientSocket() {
58 Disconnect();
59 net_log_.EndEvent(NetLogEventType::SOCKET_ALIVE);
60 }
61
GetConnectResponseInfo() const62 const HttpResponseInfo* QuicProxyClientSocket::GetConnectResponseInfo() const {
63 return response_.headers.get() ? &response_ : nullptr;
64 }
65
66 const scoped_refptr<HttpAuthController>&
GetAuthController() const67 QuicProxyClientSocket::GetAuthController() const {
68 return auth_;
69 }
70
RestartWithAuth(CompletionOnceCallback callback)71 int QuicProxyClientSocket::RestartWithAuth(CompletionOnceCallback callback) {
72 // A QUIC Stream can only handle a single request, so the underlying
73 // stream may not be reused and a new QuicProxyClientSocket must be
74 // created (possibly on top of the same QUIC Session).
75 next_state_ = STATE_DISCONNECTED;
76 return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
77 }
78
79 // Ignore priority changes, just use priority of initial request. Since multiple
80 // requests are pooled on the QuicProxyClientSocket, reprioritization doesn't
81 // really work.
82 //
83 // TODO(mmenke): Use a single priority value for all QuicProxyClientSockets,
84 // regardless of what priority they're created with.
SetStreamPriority(RequestPriority priority)85 void QuicProxyClientSocket::SetStreamPriority(RequestPriority priority) {}
86
87 // Sends a HEADERS frame to the proxy with a CONNECT request
88 // for the specified endpoint. Waits for the server to send back
89 // a HEADERS frame. OK will be returned if the status is 200.
90 // ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
91 // In any of these cases, Read() may be called to retrieve the HTTP
92 // response body. Any other return values should be considered fatal.
Connect(CompletionOnceCallback callback)93 int QuicProxyClientSocket::Connect(CompletionOnceCallback callback) {
94 DCHECK(connect_callback_.is_null());
95 if (!stream_->IsOpen())
96 return ERR_CONNECTION_CLOSED;
97
98 DCHECK_EQ(STATE_DISCONNECTED, next_state_);
99 next_state_ = STATE_GENERATE_AUTH_TOKEN;
100
101 int rv = DoLoop(OK);
102 if (rv == ERR_IO_PENDING)
103 connect_callback_ = std::move(callback);
104 return rv;
105 }
106
Disconnect()107 void QuicProxyClientSocket::Disconnect() {
108 connect_callback_.Reset();
109 read_callback_.Reset();
110 read_buf_ = nullptr;
111 write_callback_.Reset();
112 write_buf_len_ = 0;
113
114 next_state_ = STATE_DISCONNECTED;
115
116 stream_->Reset(quic::QUIC_STREAM_CANCELLED);
117 }
118
IsConnected() const119 bool QuicProxyClientSocket::IsConnected() const {
120 return next_state_ == STATE_CONNECT_COMPLETE && stream_->IsOpen();
121 }
122
IsConnectedAndIdle() const123 bool QuicProxyClientSocket::IsConnectedAndIdle() const {
124 return IsConnected() && !stream_->HasBytesToRead();
125 }
126
NetLog() const127 const NetLogWithSource& QuicProxyClientSocket::NetLog() const {
128 return net_log_;
129 }
130
WasEverUsed() const131 bool QuicProxyClientSocket::WasEverUsed() const {
132 return session_->WasEverUsed();
133 }
134
GetNegotiatedProtocol() const135 NextProto QuicProxyClientSocket::GetNegotiatedProtocol() const {
136 // Do not delegate to `session_`. While `session_` negotiates ALPN with the
137 // proxy, this object represents the tunneled TCP connection to the origin.
138 return kProtoUnknown;
139 }
140
GetSSLInfo(SSLInfo * ssl_info)141 bool QuicProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
142 // Do not delegate to `session_`. While `session_` has a secure channel to the
143 // proxy, this object represents the tunneled TCP connection to the origin.
144 return false;
145 }
146
GetTotalReceivedBytes() const147 int64_t QuicProxyClientSocket::GetTotalReceivedBytes() const {
148 return stream_->NumBytesConsumed();
149 }
150
ApplySocketTag(const SocketTag & tag)151 void QuicProxyClientSocket::ApplySocketTag(const SocketTag& tag) {
152 // In the case of a connection to the proxy using HTTP/2 or HTTP/3 where the
153 // underlying socket may multiplex multiple streams, applying this request's
154 // socket tag to the multiplexed session would incorrectly apply the socket
155 // tag to all mutliplexed streams. Fortunately socket tagging is only
156 // supported on Android without the data reduction proxy, so only simple HTTP
157 // proxies are supported, so proxies won't be using HTTP/2 or HTTP/3. Enforce
158 // that a specific (non-default) tag isn't being applied.
159 CHECK(tag == SocketTag());
160 }
161
Read(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)162 int QuicProxyClientSocket::Read(IOBuffer* buf,
163 int buf_len,
164 CompletionOnceCallback callback) {
165 DCHECK(connect_callback_.is_null());
166 DCHECK(read_callback_.is_null());
167 DCHECK(!read_buf_);
168
169 if (next_state_ == STATE_DISCONNECTED)
170 return ERR_SOCKET_NOT_CONNECTED;
171
172 if (!stream_->IsOpen()) {
173 return 0;
174 }
175
176 int rv =
177 stream_->ReadBody(buf, buf_len,
178 base::BindOnce(&QuicProxyClientSocket::OnReadComplete,
179 weak_factory_.GetWeakPtr()));
180
181 if (rv == ERR_IO_PENDING) {
182 read_callback_ = std::move(callback);
183 read_buf_ = buf;
184 } else if (rv == 0) {
185 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, 0,
186 nullptr);
187 } else if (rv > 0) {
188 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, rv,
189 buf->data());
190 }
191 return rv;
192 }
193
OnReadComplete(int rv)194 void QuicProxyClientSocket::OnReadComplete(int rv) {
195 if (!stream_->IsOpen())
196 rv = 0;
197
198 if (!read_callback_.is_null()) {
199 DCHECK(read_buf_);
200 if (rv >= 0) {
201 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, rv,
202 read_buf_->data());
203 }
204 read_buf_ = nullptr;
205 std::move(read_callback_).Run(rv);
206 }
207 }
208
Write(IOBuffer * buf,int buf_len,CompletionOnceCallback callback,const NetworkTrafficAnnotationTag & traffic_annotation)209 int QuicProxyClientSocket::Write(
210 IOBuffer* buf,
211 int buf_len,
212 CompletionOnceCallback callback,
213 const NetworkTrafficAnnotationTag& traffic_annotation) {
214 DCHECK(connect_callback_.is_null());
215 DCHECK(write_callback_.is_null());
216
217 if (next_state_ != STATE_CONNECT_COMPLETE)
218 return ERR_SOCKET_NOT_CONNECTED;
219
220 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_SENT, buf_len,
221 buf->data());
222
223 int rv = stream_->WriteStreamData(
224 std::string_view(buf->data(), buf_len), false,
225 base::BindOnce(&QuicProxyClientSocket::OnWriteComplete,
226 weak_factory_.GetWeakPtr()));
227 if (rv == OK)
228 return buf_len;
229
230 if (rv == ERR_IO_PENDING) {
231 write_callback_ = std::move(callback);
232 write_buf_len_ = buf_len;
233 }
234
235 return rv;
236 }
237
OnWriteComplete(int rv)238 void QuicProxyClientSocket::OnWriteComplete(int rv) {
239 if (!write_callback_.is_null()) {
240 if (rv == OK)
241 rv = write_buf_len_;
242 write_buf_len_ = 0;
243 std::move(write_callback_).Run(rv);
244 }
245 }
246
SetReceiveBufferSize(int32_t size)247 int QuicProxyClientSocket::SetReceiveBufferSize(int32_t size) {
248 return ERR_NOT_IMPLEMENTED;
249 }
250
SetSendBufferSize(int32_t size)251 int QuicProxyClientSocket::SetSendBufferSize(int32_t size) {
252 return ERR_NOT_IMPLEMENTED;
253 }
254
GetPeerAddress(IPEndPoint * address) const255 int QuicProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
256 return IsConnected() ? session_->GetPeerAddress(address)
257 : ERR_SOCKET_NOT_CONNECTED;
258 }
259
GetLocalAddress(IPEndPoint * address) const260 int QuicProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
261 return IsConnected() ? session_->GetSelfAddress(address)
262 : ERR_SOCKET_NOT_CONNECTED;
263 }
264
OnIOComplete(int result)265 void QuicProxyClientSocket::OnIOComplete(int result) {
266 DCHECK_NE(STATE_DISCONNECTED, next_state_);
267 int rv = DoLoop(result);
268 if (rv != ERR_IO_PENDING) {
269 // Connect() finished (successfully or unsuccessfully).
270 DCHECK(!connect_callback_.is_null());
271 std::move(connect_callback_).Run(rv);
272 }
273 }
274
DoLoop(int last_io_result)275 int QuicProxyClientSocket::DoLoop(int last_io_result) {
276 DCHECK_NE(next_state_, STATE_DISCONNECTED);
277 int rv = last_io_result;
278 do {
279 State state = next_state_;
280 next_state_ = STATE_DISCONNECTED;
281 switch (state) {
282 case STATE_GENERATE_AUTH_TOKEN:
283 DCHECK_EQ(OK, rv);
284 rv = DoGenerateAuthToken();
285 break;
286 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
287 rv = DoGenerateAuthTokenComplete(rv);
288 break;
289 case STATE_SEND_REQUEST:
290 DCHECK_EQ(OK, rv);
291 net_log_.BeginEvent(
292 NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
293 rv = DoSendRequest();
294 break;
295 case STATE_SEND_REQUEST_COMPLETE:
296 net_log_.EndEventWithNetErrorCode(
297 NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
298 rv = DoSendRequestComplete(rv);
299 break;
300 case STATE_READ_REPLY:
301 rv = DoReadReply();
302 break;
303 case STATE_READ_REPLY_COMPLETE:
304 rv = DoReadReplyComplete(rv);
305 net_log_.EndEventWithNetErrorCode(
306 NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
307 break;
308 default:
309 NOTREACHED() << "bad state";
310 rv = ERR_UNEXPECTED;
311 break;
312 }
313 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
314 next_state_ != STATE_CONNECT_COMPLETE);
315 return rv;
316 }
317
DoGenerateAuthToken()318 int QuicProxyClientSocket::DoGenerateAuthToken() {
319 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
320 return auth_->MaybeGenerateAuthToken(
321 &request_,
322 base::BindOnce(&QuicProxyClientSocket::OnIOComplete,
323 weak_factory_.GetWeakPtr()),
324 net_log_);
325 }
326
DoGenerateAuthTokenComplete(int result)327 int QuicProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
328 DCHECK_NE(ERR_IO_PENDING, result);
329 if (result == OK)
330 next_state_ = STATE_SEND_REQUEST;
331 return result;
332 }
333
DoSendRequest()334 int QuicProxyClientSocket::DoSendRequest() {
335 next_state_ = STATE_SEND_REQUEST_COMPLETE;
336
337 // Add Proxy-Authentication header if necessary.
338 HttpRequestHeaders authorization_headers;
339 if (auth_->HaveAuth()) {
340 auth_->AddAuthorizationHeader(&authorization_headers);
341 }
342
343 if (proxy_delegate_) {
344 HttpRequestHeaders proxy_delegate_headers;
345 proxy_delegate_->OnBeforeTunnelRequest(proxy_chain_, proxy_chain_index_,
346 &proxy_delegate_headers);
347 request_.extra_headers.MergeFrom(proxy_delegate_headers);
348 }
349
350 std::string request_line;
351 BuildTunnelRequest(endpoint_, authorization_headers, user_agent_,
352 &request_line, &request_.extra_headers);
353
354 NetLogRequestHeaders(net_log_,
355 NetLogEventType::HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
356 request_line, &request_.extra_headers);
357
358 spdy::Http2HeaderBlock headers;
359 CreateSpdyHeadersFromHttpRequest(request_, std::nullopt,
360 request_.extra_headers, &headers);
361
362 return stream_->WriteHeaders(std::move(headers), false, nullptr);
363 }
364
DoSendRequestComplete(int result)365 int QuicProxyClientSocket::DoSendRequestComplete(int result) {
366 if (result >= 0) {
367 // Wait for HEADERS frame from the server
368 next_state_ = STATE_READ_REPLY; // STATE_READ_REPLY_COMPLETE;
369 result = OK;
370 }
371
372 if (result >= 0 || result == ERR_IO_PENDING) {
373 // Emit extra event so can use the same events as HttpProxyClientSocket.
374 net_log_.BeginEvent(NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
375 }
376
377 return result;
378 }
379
DoReadReply()380 int QuicProxyClientSocket::DoReadReply() {
381 next_state_ = STATE_READ_REPLY_COMPLETE;
382
383 int rv = stream_->ReadInitialHeaders(
384 &response_header_block_,
385 base::BindOnce(&QuicProxyClientSocket::OnReadResponseHeadersComplete,
386 weak_factory_.GetWeakPtr()));
387 if (rv == ERR_IO_PENDING)
388 return ERR_IO_PENDING;
389 if (rv < 0)
390 return rv;
391
392 return ProcessResponseHeaders(response_header_block_);
393 }
394
DoReadReplyComplete(int result)395 int QuicProxyClientSocket::DoReadReplyComplete(int result) {
396 if (result < 0)
397 return result;
398
399 // Require the "HTTP/1.x" status line for SSL CONNECT.
400 if (response_.headers->GetHttpVersion() < HttpVersion(1, 0))
401 return ERR_TUNNEL_CONNECTION_FAILED;
402
403 NetLogResponseHeaders(
404 net_log_, NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
405 response_.headers.get());
406
407 if (proxy_delegate_) {
408 int rv = proxy_delegate_->OnTunnelHeadersReceived(
409 proxy_chain_, proxy_chain_index_, *response_.headers);
410 if (rv != OK) {
411 DCHECK_NE(ERR_IO_PENDING, rv);
412 return rv;
413 }
414 }
415
416 switch (response_.headers->response_code()) {
417 case 200: // OK
418 next_state_ = STATE_CONNECT_COMPLETE;
419 return OK;
420
421 case 407: // Proxy Authentication Required
422 next_state_ = STATE_CONNECT_COMPLETE;
423 SanitizeProxyAuth(response_);
424 return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
425
426 default:
427 // Ignore response to avoid letting the proxy impersonate the target
428 // server. (See http://crbug.com/137891.)
429 return ERR_TUNNEL_CONNECTION_FAILED;
430 }
431 }
432
OnReadResponseHeadersComplete(int result)433 void QuicProxyClientSocket::OnReadResponseHeadersComplete(int result) {
434 // Convert the now-populated spdy::Http2HeaderBlock to HttpResponseInfo
435 if (result > 0)
436 result = ProcessResponseHeaders(response_header_block_);
437
438 if (result != ERR_IO_PENDING)
439 OnIOComplete(result);
440 }
441
ProcessResponseHeaders(const spdy::Http2HeaderBlock & headers)442 int QuicProxyClientSocket::ProcessResponseHeaders(
443 const spdy::Http2HeaderBlock& headers) {
444 if (SpdyHeadersToHttpResponse(headers, &response_) != OK) {
445 DLOG(WARNING) << "Invalid headers";
446 return ERR_QUIC_PROTOCOL_ERROR;
447 }
448 return OK;
449 }
450
451 } // namespace net
452