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