xref: /aosp_15_r20/external/cronet/net/http/http_proxy_client_socket.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 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/http/http_proxy_client_socket.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "base/functional/bind.h"
11 #include "base/functional/callback_helpers.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "net/base/auth.h"
15 #include "net/base/host_port_pair.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/proxy_chain.h"
18 #include "net/base/proxy_delegate.h"
19 #include "net/http/http_basic_stream.h"
20 #include "net/http/http_log_util.h"
21 #include "net/http/http_network_session.h"
22 #include "net/http/http_request_info.h"
23 #include "net/http/http_response_headers.h"
24 #include "net/http/http_stream_parser.h"
25 #include "net/log/net_log.h"
26 #include "net/log/net_log_event_type.h"
27 #include "net/socket/stream_socket.h"
28 #include "url/gurl.h"
29 
30 namespace net {
31 
32 const int HttpProxyClientSocket::kDrainBodyBufferSize;
33 
HttpProxyClientSocket(std::unique_ptr<StreamSocket> socket,const std::string & user_agent,const HostPortPair & endpoint,const ProxyChain & proxy_chain,size_t proxy_chain_index,scoped_refptr<HttpAuthController> http_auth_controller,ProxyDelegate * proxy_delegate,const NetworkTrafficAnnotationTag & traffic_annotation)34 HttpProxyClientSocket::HttpProxyClientSocket(
35     std::unique_ptr<StreamSocket> socket,
36     const std::string& user_agent,
37     const HostPortPair& endpoint,
38     const ProxyChain& proxy_chain,
39     size_t proxy_chain_index,
40     scoped_refptr<HttpAuthController> http_auth_controller,
41     ProxyDelegate* proxy_delegate,
42     const NetworkTrafficAnnotationTag& traffic_annotation)
43     : io_callback_(base::BindRepeating(&HttpProxyClientSocket::OnIOComplete,
44                                        base::Unretained(this))),
45       socket_(std::move(socket)),
46       endpoint_(endpoint),
47       auth_(std::move(http_auth_controller)),
48       proxy_chain_(proxy_chain),
49       proxy_chain_index_(proxy_chain_index),
50       proxy_delegate_(proxy_delegate),
51       traffic_annotation_(traffic_annotation),
52       net_log_(socket_->NetLog()) {
53   // Synthesize the bits of a request that are actually used.
54   request_.url = GURL("https://" + endpoint.ToString());
55   request_.method = "CONNECT";
56   if (!user_agent.empty())
57     request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
58                                      user_agent);
59 }
60 
~HttpProxyClientSocket()61 HttpProxyClientSocket::~HttpProxyClientSocket() {
62   Disconnect();
63 }
64 
RestartWithAuth(CompletionOnceCallback callback)65 int HttpProxyClientSocket::RestartWithAuth(CompletionOnceCallback callback) {
66   DCHECK_EQ(STATE_NONE, next_state_);
67   DCHECK(user_callback_.is_null());
68 
69   int rv = PrepareForAuthRestart();
70   if (rv != OK)
71     return rv;
72 
73   rv = DoLoop(OK);
74   if (rv == ERR_IO_PENDING) {
75     if (!callback.is_null())
76       user_callback_ = std::move(callback);
77   }
78 
79   return rv;
80 }
81 
82 const scoped_refptr<HttpAuthController>&
GetAuthController() const83 HttpProxyClientSocket::GetAuthController() const {
84   return auth_;
85 }
86 
GetConnectResponseInfo() const87 const HttpResponseInfo* HttpProxyClientSocket::GetConnectResponseInfo() const {
88   return response_.headers.get() ? &response_ : nullptr;
89 }
90 
Connect(CompletionOnceCallback callback)91 int HttpProxyClientSocket::Connect(CompletionOnceCallback callback) {
92   DCHECK(socket_);
93   DCHECK(user_callback_.is_null());
94 
95   if (next_state_ == STATE_DONE)
96     return OK;
97 
98   DCHECK_EQ(STATE_NONE, next_state_);
99   next_state_ = STATE_GENERATE_AUTH_TOKEN;
100 
101   int rv = DoLoop(OK);
102   if (rv == ERR_IO_PENDING)
103     user_callback_ = std::move(callback);
104   return rv;
105 }
106 
Disconnect()107 void HttpProxyClientSocket::Disconnect() {
108   if (socket_)
109     socket_->Disconnect();
110 
111   // Reset other states to make sure they aren't mistakenly used later.
112   // These are the states initialized by Connect().
113   next_state_ = STATE_NONE;
114   user_callback_.Reset();
115 }
116 
IsConnected() const117 bool HttpProxyClientSocket::IsConnected() const {
118   return next_state_ == STATE_DONE && socket_->IsConnected();
119 }
120 
IsConnectedAndIdle() const121 bool HttpProxyClientSocket::IsConnectedAndIdle() const {
122   return next_state_ == STATE_DONE && socket_->IsConnectedAndIdle();
123 }
124 
NetLog() const125 const NetLogWithSource& HttpProxyClientSocket::NetLog() const {
126   return net_log_;
127 }
128 
WasEverUsed() const129 bool HttpProxyClientSocket::WasEverUsed() const {
130   if (socket_)
131     return socket_->WasEverUsed();
132   NOTREACHED();
133   return false;
134 }
135 
GetNegotiatedProtocol() const136 NextProto HttpProxyClientSocket::GetNegotiatedProtocol() const {
137   // Do not delegate to `socket_`. While `socket_` may negotiate ALPN with the
138   // proxy, this object represents the tunneled TCP connection to the origin.
139   return kProtoUnknown;
140 }
141 
GetSSLInfo(SSLInfo * ssl_info)142 bool HttpProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
143   // Do not delegate to `socket_`. While `socket_` may connect to the proxy with
144   // TLS, this object represents the tunneled TCP connection to the origin.
145   return false;
146 }
147 
GetTotalReceivedBytes() const148 int64_t HttpProxyClientSocket::GetTotalReceivedBytes() const {
149   return socket_->GetTotalReceivedBytes();
150 }
151 
ApplySocketTag(const SocketTag & tag)152 void HttpProxyClientSocket::ApplySocketTag(const SocketTag& tag) {
153   return socket_->ApplySocketTag(tag);
154 }
155 
Read(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)156 int HttpProxyClientSocket::Read(IOBuffer* buf,
157                                 int buf_len,
158                                 CompletionOnceCallback callback) {
159   DCHECK(user_callback_.is_null());
160   if (!CheckDone())
161     return ERR_TUNNEL_CONNECTION_FAILED;
162 
163   return socket_->Read(buf, buf_len, std::move(callback));
164 }
165 
ReadIfReady(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)166 int HttpProxyClientSocket::ReadIfReady(IOBuffer* buf,
167                                        int buf_len,
168                                        CompletionOnceCallback callback) {
169   DCHECK(user_callback_.is_null());
170   if (!CheckDone())
171     return ERR_TUNNEL_CONNECTION_FAILED;
172 
173   return socket_->ReadIfReady(buf, buf_len, std::move(callback));
174 }
175 
CancelReadIfReady()176 int HttpProxyClientSocket::CancelReadIfReady() {
177   return socket_->CancelReadIfReady();
178 }
179 
Write(IOBuffer * buf,int buf_len,CompletionOnceCallback callback,const NetworkTrafficAnnotationTag & traffic_annotation)180 int HttpProxyClientSocket::Write(
181     IOBuffer* buf,
182     int buf_len,
183     CompletionOnceCallback callback,
184     const NetworkTrafficAnnotationTag& traffic_annotation) {
185   DCHECK_EQ(STATE_DONE, next_state_);
186   DCHECK(user_callback_.is_null());
187 
188   return socket_->Write(buf, buf_len, std::move(callback), traffic_annotation);
189 }
190 
SetReceiveBufferSize(int32_t size)191 int HttpProxyClientSocket::SetReceiveBufferSize(int32_t size) {
192   return socket_->SetReceiveBufferSize(size);
193 }
194 
SetSendBufferSize(int32_t size)195 int HttpProxyClientSocket::SetSendBufferSize(int32_t size) {
196   return socket_->SetSendBufferSize(size);
197 }
198 
GetPeerAddress(IPEndPoint * address) const199 int HttpProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
200   return socket_->GetPeerAddress(address);
201 }
202 
GetLocalAddress(IPEndPoint * address) const203 int HttpProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
204   return socket_->GetLocalAddress(address);
205 }
206 
PrepareForAuthRestart()207 int HttpProxyClientSocket::PrepareForAuthRestart() {
208   if (!response_.headers.get())
209     return ERR_CONNECTION_RESET;
210 
211   // If the connection can't be reused, return
212   // ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH.  The request will be retried
213   // at a higher layer.
214   if (!response_.headers->IsKeepAlive() ||
215       !http_stream_parser_->CanFindEndOfResponse() || !socket_->IsConnected()) {
216     socket_->Disconnect();
217     return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
218   }
219 
220   // If the auth request had a body, need to drain it before reusing the socket.
221   if (!http_stream_parser_->IsResponseBodyComplete()) {
222     next_state_ = STATE_DRAIN_BODY;
223     drain_buf_ = base::MakeRefCounted<IOBufferWithSize>(kDrainBodyBufferSize);
224     return OK;
225   }
226 
227   return DidDrainBodyForAuthRestart();
228 }
229 
DidDrainBodyForAuthRestart()230 int HttpProxyClientSocket::DidDrainBodyForAuthRestart() {
231   // Can't reuse the socket if there's still unread data on it.
232   if (!socket_->IsConnectedAndIdle())
233     return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
234 
235   next_state_ = STATE_GENERATE_AUTH_TOKEN;
236   is_reused_ = true;
237 
238   // Reset the other member variables.
239   drain_buf_ = nullptr;
240   parser_buf_ = nullptr;
241   http_stream_parser_.reset();
242   request_line_.clear();
243   request_headers_.Clear();
244   response_ = HttpResponseInfo();
245   return OK;
246 }
247 
DoCallback(int result)248 void HttpProxyClientSocket::DoCallback(int result) {
249   DCHECK_NE(ERR_IO_PENDING, result);
250   DCHECK(!user_callback_.is_null());
251 
252   // Since Run() may result in Read being called,
253   // clear user_callback_ up front.
254   std::move(user_callback_).Run(result);
255 }
256 
OnIOComplete(int result)257 void HttpProxyClientSocket::OnIOComplete(int result) {
258   DCHECK_NE(STATE_NONE, next_state_);
259   DCHECK_NE(STATE_DONE, next_state_);
260   int rv = DoLoop(result);
261   if (rv != ERR_IO_PENDING)
262     DoCallback(rv);
263 }
264 
DoLoop(int last_io_result)265 int HttpProxyClientSocket::DoLoop(int last_io_result) {
266   DCHECK_NE(next_state_, STATE_NONE);
267   DCHECK_NE(next_state_, STATE_DONE);
268   int rv = last_io_result;
269   do {
270     State state = next_state_;
271     next_state_ = STATE_NONE;
272     switch (state) {
273       case STATE_GENERATE_AUTH_TOKEN:
274         DCHECK_EQ(OK, rv);
275         rv = DoGenerateAuthToken();
276         break;
277       case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
278         rv = DoGenerateAuthTokenComplete(rv);
279         break;
280       case STATE_SEND_REQUEST:
281         DCHECK_EQ(OK, rv);
282         net_log_.BeginEvent(
283             NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
284         rv = DoSendRequest();
285         break;
286       case STATE_SEND_REQUEST_COMPLETE:
287         rv = DoSendRequestComplete(rv);
288         net_log_.EndEventWithNetErrorCode(
289             NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
290         break;
291       case STATE_READ_HEADERS:
292         DCHECK_EQ(OK, rv);
293         net_log_.BeginEvent(
294             NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
295         rv = DoReadHeaders();
296         break;
297       case STATE_READ_HEADERS_COMPLETE:
298         rv = DoReadHeadersComplete(rv);
299         net_log_.EndEventWithNetErrorCode(
300             NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
301         break;
302       case STATE_DRAIN_BODY:
303         DCHECK_EQ(OK, rv);
304         rv = DoDrainBody();
305         break;
306       case STATE_DRAIN_BODY_COMPLETE:
307         rv = DoDrainBodyComplete(rv);
308         break;
309       case STATE_DONE:
310         break;
311       default:
312         NOTREACHED() << "bad state";
313         rv = ERR_UNEXPECTED;
314         break;
315     }
316   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE &&
317            next_state_ != STATE_DONE);
318   return rv;
319 }
320 
DoGenerateAuthToken()321 int HttpProxyClientSocket::DoGenerateAuthToken() {
322   next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
323   return auth_->MaybeGenerateAuthToken(&request_, io_callback_, net_log_);
324 }
325 
DoGenerateAuthTokenComplete(int result)326 int HttpProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
327   DCHECK_NE(ERR_IO_PENDING, result);
328   if (result == OK)
329     next_state_ = STATE_SEND_REQUEST;
330   return result;
331 }
332 
DoSendRequest()333 int HttpProxyClientSocket::DoSendRequest() {
334   next_state_ = STATE_SEND_REQUEST_COMPLETE;
335 
336   // This is constructed lazily (instead of within our Start method), so that
337   // we have proxy info available.
338   if (request_line_.empty()) {
339     DCHECK(request_headers_.IsEmpty());
340 
341     HttpRequestHeaders extra_headers;
342     if (auth_->HaveAuth())
343       auth_->AddAuthorizationHeader(&extra_headers);
344     // AddAuthorizationHeader() might not have added the header even if
345     // HaveAuth().
346     response_.did_use_http_auth =
347         extra_headers.HasHeader(HttpRequestHeaders::kProxyAuthorization);
348 
349     if (proxy_delegate_) {
350       HttpRequestHeaders proxy_delegate_headers;
351       proxy_delegate_->OnBeforeTunnelRequest(proxy_chain_, proxy_chain_index_,
352                                              &proxy_delegate_headers);
353       extra_headers.MergeFrom(proxy_delegate_headers);
354     }
355 
356     std::string user_agent;
357     if (!request_.extra_headers.GetHeader(HttpRequestHeaders::kUserAgent,
358                                           &user_agent)) {
359       user_agent.clear();
360     }
361     BuildTunnelRequest(endpoint_, extra_headers, user_agent, &request_line_,
362                        &request_headers_);
363 
364     NetLogRequestHeaders(net_log_,
365                          NetLogEventType::HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
366                          request_line_, &request_headers_);
367   }
368 
369   parser_buf_ = base::MakeRefCounted<GrowableIOBuffer>();
370   http_stream_parser_ = std::make_unique<HttpStreamParser>(
371       socket_.get(), is_reused_, &request_, parser_buf_.get(), net_log_);
372   return http_stream_parser_->SendRequest(request_line_, request_headers_,
373                                           traffic_annotation_, &response_,
374                                           io_callback_);
375 }
376 
DoSendRequestComplete(int result)377 int HttpProxyClientSocket::DoSendRequestComplete(int result) {
378   if (result < 0)
379     return result;
380 
381   next_state_ = STATE_READ_HEADERS;
382   return OK;
383 }
384 
DoReadHeaders()385 int HttpProxyClientSocket::DoReadHeaders() {
386   next_state_ = STATE_READ_HEADERS_COMPLETE;
387   return http_stream_parser_->ReadResponseHeaders(io_callback_);
388 }
389 
DoReadHeadersComplete(int result)390 int HttpProxyClientSocket::DoReadHeadersComplete(int result) {
391   if (result < 0)
392     return result;
393 
394   // Require the "HTTP/1.x" status line for SSL CONNECT.
395   if (response_.headers->GetHttpVersion() < HttpVersion(1, 0))
396     return ERR_TUNNEL_CONNECTION_FAILED;
397 
398   NetLogResponseHeaders(
399       net_log_, NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
400       response_.headers.get());
401 
402   if (proxy_delegate_) {
403     int rv = proxy_delegate_->OnTunnelHeadersReceived(
404         proxy_chain_, proxy_chain_index_, *response_.headers);
405     if (rv != OK) {
406       DCHECK_NE(ERR_IO_PENDING, rv);
407       return rv;
408     }
409   }
410 
411   switch (response_.headers->response_code()) {
412     case 200:  // OK
413       if (http_stream_parser_->IsMoreDataBuffered())
414         // The proxy sent extraneous data after the headers.
415         return ERR_TUNNEL_CONNECTION_FAILED;
416 
417       next_state_ = STATE_DONE;
418       return OK;
419 
420       // We aren't able to CONNECT to the remote host through the proxy.  We
421       // need to be very suspicious about the response because an active network
422       // attacker can force us into this state by masquerading as the proxy.
423       // The only safe thing to do here is to fail the connection because our
424       // client is expecting an SSL protected response.
425       // See http://crbug.com/7338.
426 
427     case 407:  // Proxy Authentication Required
428       // We need this status code to allow proxy authentication.  Our
429       // authentication code is smart enough to avoid being tricked by an
430       // active network attacker.
431       // The next state is intentionally not set as it should be STATE_NONE;
432       SanitizeProxyAuth(response_);
433       return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
434 
435     default:
436       // Ignore response to avoid letting the proxy impersonate the target
437       // server.  (See http://crbug.com/137891.)
438       // We lose something by doing this.  We have seen proxy 403, 404, and
439       // 501 response bodies that contain a useful error message.  For
440       // example, Squid uses a 404 response to report the DNS error: "The
441       // domain name does not exist."
442       return ERR_TUNNEL_CONNECTION_FAILED;
443   }
444 }
445 
DoDrainBody()446 int HttpProxyClientSocket::DoDrainBody() {
447   DCHECK(drain_buf_.get());
448   next_state_ = STATE_DRAIN_BODY_COMPLETE;
449   return http_stream_parser_->ReadResponseBody(
450       drain_buf_.get(), kDrainBodyBufferSize, io_callback_);
451 }
452 
DoDrainBodyComplete(int result)453 int HttpProxyClientSocket::DoDrainBodyComplete(int result) {
454   if (result < 0)
455     return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
456 
457   if (!http_stream_parser_->IsResponseBodyComplete()) {
458     // Keep draining.
459     next_state_ = STATE_DRAIN_BODY;
460     return OK;
461   }
462 
463   return DidDrainBodyForAuthRestart();
464 }
465 
CheckDone()466 bool HttpProxyClientSocket::CheckDone() {
467   if (next_state_ != STATE_DONE) {
468     // We're trying to read the body of the response but we're still trying
469     // to establish an SSL tunnel through the proxy.  We can't read these
470     // bytes when establishing a tunnel because they might be controlled by
471     // an active network attacker.  We don't worry about this for HTTP
472     // because an active network attacker can already control HTTP sessions.
473     // We reach this case when the user cancels a 407 proxy auth prompt.
474     // See http://crbug.com/8473.
475     DCHECK_EQ(407, response_.headers->response_code());
476 
477     return false;
478   }
479   return true;
480 }
481 
482 //----------------------------------------------------------------
483 
484 }  // namespace net
485