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