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