• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2010 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/http/http_proxy_client_socket.h"
6 
7 #include "base/string_util.h"
8 #include "base/stringprintf.h"
9 #include "googleurl/src/gurl.h"
10 #include "net/base/auth.h"
11 #include "net/base/host_port_pair.h"
12 #include "net/base/io_buffer.h"
13 #include "net/base/net_log.h"
14 #include "net/base/net_util.h"
15 #include "net/http/http_basic_stream.h"
16 #include "net/http/http_net_log_params.h"
17 #include "net/http/http_network_session.h"
18 #include "net/http/http_proxy_utils.h"
19 #include "net/http/http_request_info.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/http/http_stream_parser.h"
22 #include "net/socket/client_socket_handle.h"
23 
24 namespace net {
25 
HttpProxyClientSocket(ClientSocketHandle * transport_socket,const GURL & request_url,const std::string & user_agent,const HostPortPair & endpoint,const HostPortPair & proxy_server,HttpAuthCache * http_auth_cache,HttpAuthHandlerFactory * http_auth_handler_factory,bool tunnel,bool using_spdy,bool is_https_proxy)26 HttpProxyClientSocket::HttpProxyClientSocket(
27     ClientSocketHandle* transport_socket,
28     const GURL& request_url,
29     const std::string& user_agent,
30     const HostPortPair& endpoint,
31     const HostPortPair& proxy_server,
32     HttpAuthCache* http_auth_cache,
33     HttpAuthHandlerFactory* http_auth_handler_factory,
34     bool tunnel,
35     bool using_spdy,
36     bool is_https_proxy)
37     : ALLOW_THIS_IN_INITIALIZER_LIST(
38           io_callback_(this, &HttpProxyClientSocket::OnIOComplete)),
39       next_state_(STATE_NONE),
40       user_callback_(NULL),
41       transport_(transport_socket),
42       endpoint_(endpoint),
43       auth_(tunnel ?
44           new HttpAuthController(HttpAuth::AUTH_PROXY,
45                                  GURL("http://" + proxy_server.ToString()),
46                                  http_auth_cache,
47                                  http_auth_handler_factory)
48           : NULL),
49       tunnel_(tunnel),
50       using_spdy_(using_spdy),
51       is_https_proxy_(is_https_proxy),
52       net_log_(transport_socket->socket()->NetLog()) {
53   // Synthesize the bits of a request that we actually use.
54   request_.url = request_url;
55   request_.method = "GET";
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(CompletionCallback * callback)65 int HttpProxyClientSocket::RestartWithAuth(CompletionCallback* callback) {
66   DCHECK_EQ(STATE_NONE, next_state_);
67   DCHECK(!user_callback_);
68 
69   int rv = PrepareForAuthRestart();
70   if (rv != OK)
71     return rv;
72 
73   rv = DoLoop(OK);
74   if (rv == ERR_IO_PENDING)
75     user_callback_ = callback;
76   return rv;
77 }
78 
GetConnectResponseInfo() const79 const HttpResponseInfo* HttpProxyClientSocket::GetConnectResponseInfo() const {
80   return response_.headers ? &response_ : NULL;
81 }
82 
CreateConnectResponseStream()83 HttpStream* HttpProxyClientSocket::CreateConnectResponseStream() {
84   return new HttpBasicStream(transport_.release(),
85                              http_stream_parser_.release(), false);
86 }
87 
88 #ifdef ANDROID
89 // TODO(kristianm): handle the case when wait_for_connect is true
90 // (sync requests)
91 #endif
Connect(CompletionCallback * callback,bool wait_for_connect,bool valid_uid,uid_t calling_uid)92 int HttpProxyClientSocket::Connect(CompletionCallback* callback
93 #ifdef ANDROID
94                                    , bool wait_for_connect
95                                    , bool valid_uid
96                                    , uid_t calling_uid
97 #endif
98                                   ) {
99   DCHECK(transport_.get());
100   DCHECK(transport_->socket());
101   DCHECK(!user_callback_);
102 
103   // TODO(rch): figure out the right way to set up a tunnel with SPDY.
104   // This approach sends the complete HTTPS request to the proxy
105   // which allows the proxy to see "private" data.  Instead, we should
106   // create an SSL tunnel to the origin server using the CONNECT method
107   // inside a single SPDY stream.
108   if (using_spdy_ || !tunnel_)
109     next_state_ = STATE_DONE;
110   if (next_state_ == STATE_DONE)
111     return OK;
112 
113   DCHECK_EQ(STATE_NONE, next_state_);
114   next_state_ = STATE_GENERATE_AUTH_TOKEN;
115 
116   int rv = DoLoop(OK);
117   if (rv == ERR_IO_PENDING)
118     user_callback_ = callback;
119   return rv;
120 }
121 
Disconnect()122 void HttpProxyClientSocket::Disconnect() {
123   if (transport_.get())
124     transport_->socket()->Disconnect();
125 
126   // Reset other states to make sure they aren't mistakenly used later.
127   // These are the states initialized by Connect().
128   next_state_ = STATE_NONE;
129   user_callback_ = NULL;
130 }
131 
IsConnected() const132 bool HttpProxyClientSocket::IsConnected() const {
133   return next_state_ == STATE_DONE && transport_->socket()->IsConnected();
134 }
135 
IsConnectedAndIdle() const136 bool HttpProxyClientSocket::IsConnectedAndIdle() const {
137   return next_state_ == STATE_DONE &&
138     transport_->socket()->IsConnectedAndIdle();
139 }
140 
NetLog() const141 const BoundNetLog& HttpProxyClientSocket::NetLog() const {
142   return net_log_;
143 }
144 
SetSubresourceSpeculation()145 void HttpProxyClientSocket::SetSubresourceSpeculation() {
146   if (transport_.get() && transport_->socket()) {
147     transport_->socket()->SetSubresourceSpeculation();
148   } else {
149     NOTREACHED();
150   }
151 }
152 
SetOmniboxSpeculation()153 void HttpProxyClientSocket::SetOmniboxSpeculation() {
154   if (transport_.get() && transport_->socket()) {
155     transport_->socket()->SetOmniboxSpeculation();
156   } else {
157     NOTREACHED();
158   }
159 }
160 
WasEverUsed() const161 bool HttpProxyClientSocket::WasEverUsed() const {
162   if (transport_.get() && transport_->socket()) {
163     return transport_->socket()->WasEverUsed();
164   }
165   NOTREACHED();
166   return false;
167 }
168 
UsingTCPFastOpen() const169 bool HttpProxyClientSocket::UsingTCPFastOpen() const {
170   if (transport_.get() && transport_->socket()) {
171     return transport_->socket()->UsingTCPFastOpen();
172   }
173   NOTREACHED();
174   return false;
175 }
176 
Read(IOBuffer * buf,int buf_len,CompletionCallback * callback)177 int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len,
178                                 CompletionCallback* callback) {
179   DCHECK(!user_callback_);
180   if (next_state_ != STATE_DONE) {
181     // We're trying to read the body of the response but we're still trying
182     // to establish an SSL tunnel through the proxy.  We can't read these
183     // bytes when establishing a tunnel because they might be controlled by
184     // an active network attacker.  We don't worry about this for HTTP
185     // because an active network attacker can already control HTTP sessions.
186     // We reach this case when the user cancels a 407 proxy auth prompt.
187     // See http://crbug.com/8473.
188     DCHECK_EQ(407, response_.headers->response_code());
189     LogBlockedTunnelResponse(response_.headers->response_code());
190 
191     return ERR_TUNNEL_CONNECTION_FAILED;
192   }
193 
194   return transport_->socket()->Read(buf, buf_len, callback);
195 }
196 
Write(IOBuffer * buf,int buf_len,CompletionCallback * callback)197 int HttpProxyClientSocket::Write(IOBuffer* buf, int buf_len,
198                                  CompletionCallback* callback) {
199   DCHECK_EQ(STATE_DONE, next_state_);
200   DCHECK(!user_callback_);
201 
202   return transport_->socket()->Write(buf, buf_len, callback);
203 }
204 
SetReceiveBufferSize(int32 size)205 bool HttpProxyClientSocket::SetReceiveBufferSize(int32 size) {
206   return transport_->socket()->SetReceiveBufferSize(size);
207 }
208 
SetSendBufferSize(int32 size)209 bool HttpProxyClientSocket::SetSendBufferSize(int32 size) {
210   return transport_->socket()->SetSendBufferSize(size);
211 }
212 
GetPeerAddress(AddressList * address) const213 int HttpProxyClientSocket::GetPeerAddress(AddressList* address) const {
214   return transport_->socket()->GetPeerAddress(address);
215 }
216 
GetLocalAddress(IPEndPoint * address) const217 int HttpProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
218   return transport_->socket()->GetLocalAddress(address);
219 }
220 
PrepareForAuthRestart()221 int HttpProxyClientSocket::PrepareForAuthRestart() {
222   if (!response_.headers.get())
223     return ERR_CONNECTION_RESET;
224 
225   bool keep_alive = false;
226   if (response_.headers->IsKeepAlive() &&
227       http_stream_parser_->CanFindEndOfResponse()) {
228     if (!http_stream_parser_->IsResponseBodyComplete()) {
229       next_state_ = STATE_DRAIN_BODY;
230       drain_buf_ = new IOBuffer(kDrainBodyBufferSize);
231       return OK;
232     }
233     keep_alive = true;
234   }
235 
236   // We don't need to drain the response body, so we act as if we had drained
237   // the response body.
238   return DidDrainBodyForAuthRestart(keep_alive);
239 }
240 
DidDrainBodyForAuthRestart(bool keep_alive)241 int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) {
242   if (keep_alive && transport_->socket()->IsConnectedAndIdle()) {
243     next_state_ = STATE_GENERATE_AUTH_TOKEN;
244     transport_->set_is_reused(true);
245   } else {
246     // This assumes that the underlying transport socket is a TCP socket,
247     // since only TCP sockets are restartable.
248     next_state_ = STATE_TCP_RESTART;
249     transport_->socket()->Disconnect();
250   }
251 
252   // Reset the other member variables.
253   drain_buf_ = NULL;
254   parser_buf_ = NULL;
255   http_stream_parser_.reset();
256   request_line_.clear();
257   request_headers_.Clear();
258   response_ = HttpResponseInfo();
259   return OK;
260 }
261 
HandleAuthChallenge()262 int HttpProxyClientSocket::HandleAuthChallenge() {
263   DCHECK(response_.headers);
264 
265   int rv = auth_->HandleAuthChallenge(response_.headers, false, true, net_log_);
266   response_.auth_challenge = auth_->auth_info();
267   if (rv == OK)
268     return ERR_PROXY_AUTH_REQUESTED;
269 
270   return rv;
271 }
272 
LogBlockedTunnelResponse(int response_code) const273 void HttpProxyClientSocket::LogBlockedTunnelResponse(int response_code) const {
274   LOG(WARNING) << "Blocked proxy response with status " << response_code
275                << " to CONNECT request for "
276                << GetHostAndPort(request_.url) << ".";
277 }
278 
DoCallback(int result)279 void HttpProxyClientSocket::DoCallback(int result) {
280   DCHECK_NE(ERR_IO_PENDING, result);
281   DCHECK(user_callback_);
282 
283   // Since Run() may result in Read being called,
284   // clear user_callback_ up front.
285   CompletionCallback* c = user_callback_;
286   user_callback_ = NULL;
287   c->Run(result);
288 }
289 
OnIOComplete(int result)290 void HttpProxyClientSocket::OnIOComplete(int result) {
291   DCHECK_NE(STATE_NONE, next_state_);
292   DCHECK_NE(STATE_DONE, next_state_);
293   int rv = DoLoop(result);
294   if (rv != ERR_IO_PENDING)
295     DoCallback(rv);
296 }
297 
DoLoop(int last_io_result)298 int HttpProxyClientSocket::DoLoop(int last_io_result) {
299   DCHECK_NE(next_state_, STATE_NONE);
300   DCHECK_NE(next_state_, STATE_DONE);
301   int rv = last_io_result;
302   do {
303     State state = next_state_;
304     next_state_ = STATE_NONE;
305     switch (state) {
306       case STATE_GENERATE_AUTH_TOKEN:
307         DCHECK_EQ(OK, rv);
308         rv = DoGenerateAuthToken();
309         break;
310       case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
311         rv = DoGenerateAuthTokenComplete(rv);
312         break;
313       case STATE_SEND_REQUEST:
314         DCHECK_EQ(OK, rv);
315         net_log_.BeginEvent(
316             NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, NULL);
317         rv = DoSendRequest();
318         break;
319       case STATE_SEND_REQUEST_COMPLETE:
320         rv = DoSendRequestComplete(rv);
321         net_log_.EndEventWithNetErrorCode(
322             NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
323         break;
324       case STATE_READ_HEADERS:
325         DCHECK_EQ(OK, rv);
326         net_log_.BeginEvent(
327             NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, NULL);
328         rv = DoReadHeaders();
329         break;
330       case STATE_READ_HEADERS_COMPLETE:
331         rv = DoReadHeadersComplete(rv);
332         net_log_.EndEventWithNetErrorCode(
333             NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
334         break;
335       case STATE_DRAIN_BODY:
336         DCHECK_EQ(OK, rv);
337         rv = DoDrainBody();
338         break;
339       case STATE_DRAIN_BODY_COMPLETE:
340         rv = DoDrainBodyComplete(rv);
341         break;
342       case STATE_TCP_RESTART:
343         DCHECK_EQ(OK, rv);
344         rv = DoTCPRestart();
345         break;
346       case STATE_TCP_RESTART_COMPLETE:
347         rv = DoTCPRestartComplete(rv);
348         break;
349       case STATE_DONE:
350         break;
351       default:
352         NOTREACHED() << "bad state";
353         rv = ERR_UNEXPECTED;
354         break;
355     }
356   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE &&
357            next_state_ != STATE_DONE);
358   return rv;
359 }
360 
DoGenerateAuthToken()361 int HttpProxyClientSocket::DoGenerateAuthToken() {
362   next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
363   return auth_->MaybeGenerateAuthToken(&request_, &io_callback_, net_log_);
364 }
365 
DoGenerateAuthTokenComplete(int result)366 int HttpProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
367   DCHECK_NE(ERR_IO_PENDING, result);
368   if (result == OK)
369     next_state_ = STATE_SEND_REQUEST;
370   return result;
371 }
372 
DoSendRequest()373 int HttpProxyClientSocket::DoSendRequest() {
374   next_state_ = STATE_SEND_REQUEST_COMPLETE;
375 
376   // This is constructed lazily (instead of within our Start method), so that
377   // we have proxy info available.
378   if (request_line_.empty()) {
379     DCHECK(request_headers_.IsEmpty());
380     HttpRequestHeaders authorization_headers;
381     if (auth_->HaveAuth())
382       auth_->AddAuthorizationHeader(&authorization_headers);
383     BuildTunnelRequest(request_, authorization_headers, endpoint_,
384                        &request_line_, &request_headers_);
385     if (net_log_.IsLoggingAllEvents()) {
386       net_log_.AddEvent(
387           NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
388           make_scoped_refptr(new NetLogHttpRequestParameter(
389               request_line_, request_headers_)));
390     }
391   }
392 
393   parser_buf_ = new GrowableIOBuffer();
394   http_stream_parser_.reset(
395       new HttpStreamParser(transport_.get(), &request_, parser_buf_, net_log_));
396   return http_stream_parser_->SendRequest(request_line_, request_headers_, NULL,
397                                           &response_, &io_callback_);
398 }
399 
DoSendRequestComplete(int result)400 int HttpProxyClientSocket::DoSendRequestComplete(int result) {
401   if (result < 0)
402     return result;
403 
404   next_state_ = STATE_READ_HEADERS;
405   return OK;
406 }
407 
DoReadHeaders()408 int HttpProxyClientSocket::DoReadHeaders() {
409   next_state_ = STATE_READ_HEADERS_COMPLETE;
410   return http_stream_parser_->ReadResponseHeaders(&io_callback_);
411 }
412 
DoReadHeadersComplete(int result)413 int HttpProxyClientSocket::DoReadHeadersComplete(int result) {
414   if (result < 0)
415     return result;
416 
417   // Require the "HTTP/1.x" status line for SSL CONNECT.
418   if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
419     return ERR_TUNNEL_CONNECTION_FAILED;
420 
421   if (net_log_.IsLoggingAllEvents()) {
422     net_log_.AddEvent(
423         NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
424         make_scoped_refptr(new NetLogHttpResponseParameter(response_.headers)));
425   }
426 
427   switch (response_.headers->response_code()) {
428     case 200:  // OK
429       if (http_stream_parser_->IsMoreDataBuffered())
430         // The proxy sent extraneous data after the headers.
431         return ERR_TUNNEL_CONNECTION_FAILED;
432 
433       next_state_ = STATE_DONE;
434       return OK;
435 
436       // We aren't able to CONNECT to the remote host through the proxy.  We
437       // need to be very suspicious about the response because an active network
438       // attacker can force us into this state by masquerading as the proxy.
439       // The only safe thing to do here is to fail the connection because our
440       // client is expecting an SSL protected response.
441       // See http://crbug.com/7338.
442     case 407:  // Proxy Authentication Required
443       // We need this status code to allow proxy authentication.  Our
444       // authentication code is smart enough to avoid being tricked by an
445       // active network attacker.
446       // The next state is intentionally not set as it should be STATE_NONE;
447       return HandleAuthChallenge();
448 
449     default:
450       if (is_https_proxy_)
451         return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
452       // For all other status codes, we conservatively fail the CONNECT
453       // request.
454       // We lose something by doing this.  We have seen proxy 403, 404, and
455       // 501 response bodies that contain a useful error message.  For
456       // example, Squid uses a 404 response to report the DNS error: "The
457       // domain name does not exist."
458       LogBlockedTunnelResponse(response_.headers->response_code());
459       return ERR_TUNNEL_CONNECTION_FAILED;
460   }
461 }
462 
DoDrainBody()463 int HttpProxyClientSocket::DoDrainBody() {
464   DCHECK(drain_buf_);
465   DCHECK(transport_->is_initialized());
466   next_state_ = STATE_DRAIN_BODY_COMPLETE;
467   return http_stream_parser_->ReadResponseBody(drain_buf_, kDrainBodyBufferSize,
468                                                &io_callback_);
469 }
470 
DoDrainBodyComplete(int result)471 int HttpProxyClientSocket::DoDrainBodyComplete(int result) {
472   if (result < 0)
473     return result;
474 
475   if (http_stream_parser_->IsResponseBodyComplete())
476     return DidDrainBodyForAuthRestart(true);
477 
478   // Keep draining.
479   next_state_ = STATE_DRAIN_BODY;
480   return OK;
481 }
482 
483 #ifdef ANDROID
484 // TODO(kristianm): Check if we can find out if Connect should block
485 // TODO(ashishsharma): Perhaps make ignore_limits, calling_uid, valid_uid part of ClientSocket
486 #endif
DoTCPRestart()487 int HttpProxyClientSocket::DoTCPRestart() {
488   next_state_ = STATE_TCP_RESTART_COMPLETE;
489   return transport_->socket()->Connect(&io_callback_
490 #ifdef ANDROID
491                                        , false
492                                        , false
493                                        , 0
494 #endif
495                                       );
496 }
497 
DoTCPRestartComplete(int result)498 int HttpProxyClientSocket::DoTCPRestartComplete(int result) {
499   if (result != OK)
500     return result;
501 
502   next_state_ = STATE_GENERATE_AUTH_TOKEN;
503   return result;
504 }
505 
506 }  // namespace net
507