blob: 1ba1d21fb70f4f9232069212a7f1deb4bb3d7818 [file] [log] [blame]
Paul Stewartf65320c2011-10-13 14:34:52 -07001// Copyright (c) 2011 The Chromium OS 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 "shill/http_proxy.h"
6
7#include <errno.h>
8#include <netinet/in.h>
9#include <linux/if.h> // Needs definitions from netinet/in.h
10#include <stdio.h>
11#include <time.h>
12
13#include <string>
14#include <vector>
15
16#include <base/logging.h>
17#include <base/string_number_conversions.h>
18#include <base/string_split.h>
19#include <base/string_util.h>
20#include <base/stringprintf.h>
21
22#include "shill/async_connection.h"
Paul Stewartc8f4bef2011-12-13 09:45:51 -080023#include "shill/connection.h"
Paul Stewartf65320c2011-10-13 14:34:52 -070024#include "shill/dns_client.h"
25#include "shill/event_dispatcher.h"
26#include "shill/ip_address.h"
27#include "shill/sockets.h"
28
29using base::StringPrintf;
30using std::string;
31using std::vector;
32
33namespace shill {
34
35const int HTTPProxy::kClientHeaderTimeoutSeconds = 1;
36const int HTTPProxy::kConnectTimeoutSeconds = 10;
37const int HTTPProxy::kDNSTimeoutSeconds = 5;
38const int HTTPProxy::kDefaultServerPort = 80;
39const int HTTPProxy::kInputTimeoutSeconds = 30;
40const size_t HTTPProxy::kMaxClientQueue = 10;
41const size_t HTTPProxy::kMaxHeaderCount = 128;
42const size_t HTTPProxy::kMaxHeaderSize = 2048;
43const int HTTPProxy::kTransactionTimeoutSeconds = 600;
44
45const char HTTPProxy::kHTTPURLDelimiters[] = " /#?";
46const char HTTPProxy::kHTTPURLPrefix[] = "http://";
47const char HTTPProxy::kHTTPVersionPrefix[] = " HTTP/1";
48const char HTTPProxy::kInternalErrorMsg[] = "Proxy Failed: Internal Error";
49
Paul Stewartc8f4bef2011-12-13 09:45:51 -080050HTTPProxy::HTTPProxy(ConnectionRefPtr connection)
Paul Stewartf65320c2011-10-13 14:34:52 -070051 : state_(kStateIdle),
Paul Stewartc8f4bef2011-12-13 09:45:51 -080052 connection_(connection),
Paul Stewartf65320c2011-10-13 14:34:52 -070053 accept_callback_(NewCallback(this, &HTTPProxy::AcceptClient)),
54 connect_completion_callback_(
55 NewCallback(this, &HTTPProxy::OnConnectCompletion)),
56 dns_client_callback_(NewCallback(this, &HTTPProxy::GetDNSResult)),
57 read_client_callback_(NewCallback(this, &HTTPProxy::ReadFromClient)),
58 read_server_callback_(NewCallback(this, &HTTPProxy::ReadFromServer)),
59 write_client_callback_(NewCallback(this, &HTTPProxy::WriteToClient)),
60 write_server_callback_(NewCallback(this, &HTTPProxy::WriteToServer)),
61 task_factory_(this),
62 dispatcher_(NULL),
63 dns_client_(NULL),
64 proxy_port_(-1),
65 proxy_socket_(-1),
66 server_async_connection_(NULL),
67 sockets_(NULL),
68 client_socket_(-1),
69 server_port_(kDefaultServerPort),
70 server_socket_(-1),
Paul Stewartc8f4bef2011-12-13 09:45:51 -080071 is_route_requested_(false),
Paul Stewartf65320c2011-10-13 14:34:52 -070072 idle_timeout_(NULL) { }
73
74HTTPProxy::~HTTPProxy() {
75 Stop();
76}
77
78bool HTTPProxy::Start(EventDispatcher *dispatcher,
79 Sockets *sockets) {
80 VLOG(3) << "In " << __func__;
81
82 if (sockets_) {
83 // We are already running.
84 return true;
85 }
86
87 proxy_socket_ = sockets->Socket(PF_INET, SOCK_STREAM, 0);
88 if (proxy_socket_ < 0) {
89 PLOG(ERROR) << "Failed to open proxy socket";
90 return false;
91 }
92
93 struct sockaddr_in addr;
94 socklen_t addrlen = sizeof(addr);
95 memset(&addr, 0, sizeof(addr));
96 addr.sin_family = AF_INET;
97 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
98 if (sockets->Bind(proxy_socket_,
99 reinterpret_cast<struct sockaddr *>(&addr),
100 sizeof(addr)) < 0 ||
101 sockets->GetSockName(proxy_socket_,
102 reinterpret_cast<struct sockaddr *>(&addr),
103 &addrlen) < 0 ||
104 sockets->SetNonBlocking(proxy_socket_) < 0 ||
105 sockets->Listen(proxy_socket_, kMaxClientQueue) < 0) {
106 sockets->Close(proxy_socket_);
107 proxy_socket_ = -1;
108 PLOG(ERROR) << "HTTPProxy socket setup failed";
109 return false;
110 }
111
112 accept_handler_.reset(
113 dispatcher->CreateReadyHandler(proxy_socket_, IOHandler::kModeInput,
114 accept_callback_.get()));
115 dispatcher_ = dispatcher;
116 dns_client_.reset(new DNSClient(IPAddress::kFamilyIPv4,
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800117 connection_->interface_name(),
118 connection_->dns_servers(),
Paul Stewartf65320c2011-10-13 14:34:52 -0700119 kDNSTimeoutSeconds * 1000,
120 dispatcher,
121 dns_client_callback_.get()));
122 proxy_port_ = ntohs(addr.sin_port);
123 server_async_connection_.reset(
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800124 new AsyncConnection(connection_->interface_name(), dispatcher, sockets,
Paul Stewartf65320c2011-10-13 14:34:52 -0700125 connect_completion_callback_.get()));
126 sockets_ = sockets;
127 state_ = kStateWaitConnection;
128 return true;
129}
130
131void HTTPProxy::Stop() {
132 VLOG(3) << "In " << __func__;
133
134 if (!sockets_ ) {
135 return;
136 }
137
138 StopClient();
139
140 accept_handler_.reset();
141 dispatcher_ = NULL;
142 dns_client_.reset();
143 proxy_port_ = -1;
144 server_async_connection_.reset();
145 sockets_->Close(proxy_socket_);
146 proxy_socket_ = -1;
147 sockets_ = NULL;
148 state_ = kStateIdle;
149}
150
151// IOReadyHandler callback routine fired when a client connects to the
152// proxy's socket. We Accept() the client and start reading a request
153// from it.
154void HTTPProxy::AcceptClient(int fd) {
155 VLOG(3) << "In " << __func__;
156
157 int client_fd = sockets_->Accept(fd, NULL, NULL);
158 if (client_fd < 0) {
159 PLOG(ERROR) << "Client accept failed";
160 return;
161 }
162
163 accept_handler_->Stop();
164
165 client_socket_ = client_fd;
166
167 sockets_->SetNonBlocking(client_socket_);
168 read_client_handler_.reset(
169 dispatcher_->CreateInputHandler(client_socket_,
170 read_client_callback_.get()));
171 // Overall transaction timeout.
172 dispatcher_->PostDelayedTask(
173 task_factory_.NewRunnableMethod(&HTTPProxy::StopClient),
174 kTransactionTimeoutSeconds * 1000);
175
176 state_ = kStateReadClientHeader;
177 StartIdleTimeout();
178}
179
180bool HTTPProxy::ConnectServer(const IPAddress &address, int port) {
181 state_ = kStateConnectServer;
182 if (!server_async_connection_->Start(address, port)) {
183 SendClientError(500, "Could not create socket to connect to server");
184 return false;
185 }
186 StartIdleTimeout();
187 return true;
188}
189
190// DNSClient callback that fires when the DNS request completes.
191void HTTPProxy::GetDNSResult(bool result) {
192 if (!result) {
193 SendClientError(502, string("Could not resolve hostname: ") +
194 dns_client_->error());
195 return;
196 }
197 ConnectServer(dns_client_->address(), server_port_);
198}
199
200// IOReadyHandler callback routine which fires when the asynchronous Connect()
201// to the remote server completes (or fails).
202void HTTPProxy::OnConnectCompletion(bool success, int fd) {
203 if (!success) {
204 SendClientError(500, string("Socket connection delayed failure: ") +
205 server_async_connection_->error());
206 return;
207 }
208 server_socket_ = fd;
209 state_ = kStateTunnelData;
210 StartTransmit();
211}
212
213// Read through the header lines from the client, modifying or adding
214// lines as necessary. Perform final determination of the hostname/port
215// we should connect to and either start a DNS request or connect to a
216// numeric address.
217bool HTTPProxy::ParseClientRequest() {
218 VLOG(3) << "In " << __func__;
219
220 string host;
221 bool found_via = false;
222 bool found_connection = false;
223 for (vector<string>::iterator it = client_headers_.begin();
224 it != client_headers_.end(); ++it) {
225 if (StartsWithASCII(*it, "Host:", false)) {
226 host = it->substr(5);
227 } else if (StartsWithASCII(*it, "Via:", false)) {
228 found_via = true;
229 (*it).append(StringPrintf(", %s shill-proxy", client_version_.c_str()));
230 } else if (StartsWithASCII(*it, "Connection:", false)) {
231 found_connection = true;
232 (*it).assign("Connection: close");
233 } else if (StartsWithASCII(*it, "Proxy-Connection:", false)) {
234 (*it).assign("Proxy-Connection: close");
235 }
236 }
237
238 if (!found_connection) {
239 client_headers_.push_back("Connection: close");
240 }
241 if (!found_via) {
242 client_headers_.push_back(
243 StringPrintf("Via: %s shill-proxy", client_version_.c_str()));
244 }
245
246 // Assemble the request as it will be sent to the server.
247 client_data_.Clear();
248 for (vector<string>::iterator it = client_headers_.begin();
249 it != client_headers_.end(); ++it) {
250 client_data_.Append(ByteString(*it + "\r\n", false));
251 }
252 client_data_.Append(ByteString(string("\r\n"), false));
253
254 TrimWhitespaceASCII(host, TRIM_ALL, &host);
255 if (host.empty()) {
256 // Revert to using the hostname in the URL if no "Host:" header exists.
257 host = server_hostname_;
258 }
259
260 if (host.empty()) {
261 SendClientError(400, "I don't know what host you want me to connect to");
262 return false;
263 }
264
265 server_port_ = 80;
266 vector<string> host_parts;
267 base::SplitString(host, ':', &host_parts);
268
269 if (host_parts.size() > 2) {
270 SendClientError(400, "Too many colons in hostname");
271 return false;
272 } else if (host_parts.size() == 2) {
273 server_hostname_ = host_parts[0];
274 if (!base::StringToInt(host_parts[1], &server_port_)) {
275 SendClientError(400, "Could not parse port number");
276 return false;
277 }
278 } else {
279 server_hostname_ = host;
280 }
281
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800282 connection_->RequestRouting();
283 is_route_requested_ = true;
284
Paul Stewartf65320c2011-10-13 14:34:52 -0700285 IPAddress addr(IPAddress::kFamilyIPv4);
286 if (addr.SetAddressFromString(server_hostname_)) {
287 if (!ConnectServer(addr, server_port_)) {
288 return false;
289 }
290 } else {
291 VLOG(3) << "Looking up host: " << server_hostname_;
292 if (!dns_client_->Start(server_hostname_)) {
293 SendClientError(502, "Could not resolve hostname");
294 return false;
295 }
296 state_ = kStateLookupServer;
297 }
298 return true;
299}
300
301// Accept a new line into the client headers. Returns false if a parse
302// error occurs.
303bool HTTPProxy::ProcessLastHeaderLine() {
304 string *header = &client_headers_.back();
305 TrimString(*header, "\r", header);
306
307 if (header->empty()) {
308 // Empty line terminates client headers.
309 client_headers_.pop_back();
310 if (!ParseClientRequest()) {
311 return false;
312 }
313 }
314
315 // Is this is the first header line?
316 if (client_headers_.size() == 1) {
317 if (!ReadClientHTTPVersion(header) || !ReadClientHostname(header)) {
318 return false;
319 }
320 }
321
322 if (client_headers_.size() >= kMaxHeaderCount) {
323 SendClientError(500, kInternalErrorMsg);
324 return false;
325 }
326
327 return true;
328}
329
330// Split input from client into header lines, and consume parsed lines
331// from InputData. The passed in |data| is modified to indicate the
332// characters consumed.
333bool HTTPProxy::ReadClientHeaders(InputData *data) {
334 unsigned char *ptr = data->buf;
335 unsigned char *end = ptr + data->len;
336
337 if (client_headers_.empty()) {
338 client_headers_.push_back(string());
339 }
340
341 for (; ptr < end && state_ == kStateReadClientHeader; ++ptr) {
342 if (*ptr == '\n') {
343 if (!ProcessLastHeaderLine()) {
344 return false;
345 }
346
347 // Start a new line. New chararacters we receive will be appended there.
348 client_headers_.push_back(string());
349 continue;
350 }
351
352 string *header = &client_headers_.back();
353 // Is the first character of the header line a space or tab character?
354 if (header->empty() && (*ptr == ' ' || *ptr == '\t') &&
355 client_headers_.size() > 1) {
356 // Line Continuation: Add this character to the previous header line.
357 // This way, all of the data (including newlines and line continuation
358 // characters) related to a specific header will be contained within
359 // a single element of |client_headers_|, and manipulation of headers
360 // such as appending will be simpler. This is accomplished by removing
361 // the empty line we started, and instead appending the whitespace
362 // and following characters to the previous line.
363 client_headers_.pop_back();
364 header = &client_headers_.back();
365 header->append("\r\n");
366 }
367
368 if (header->length() >= kMaxHeaderSize) {
369 SendClientError(500, kInternalErrorMsg);
370 return false;
371 }
372 header->push_back(*ptr);
373 }
374
375 // Return the remaining data to the caller -- this could be POST data
376 // or other non-header data sent with the client request.
377 data->buf = ptr;
378 data->len = end - ptr;
379
380 return true;
381}
382
383// Finds the URL in the first line of an HTTP client header, and extracts
384// and removes the hostname (and port) from the URL. Returns false if a
385// parse error occurs, and true otherwise (whether or not the hostname was
386// found).
387bool HTTPProxy::ReadClientHostname(string *header) {
388 const string http_url_prefix(kHTTPURLPrefix);
389 size_t url_idx = header->find(http_url_prefix);
390 if (url_idx != string::npos) {
391 size_t host_start = url_idx + http_url_prefix.length();
392 size_t host_end =
393 header->find_first_of(kHTTPURLDelimiters, host_start);
394 if (host_end != string::npos) {
395 server_hostname_ = header->substr(host_start,
396 host_end - host_start);
397 // Modify the URL passed upstream to remove "http://<hostname>".
398 header->erase(url_idx, host_end - url_idx);
399 if ((*header)[url_idx] != '/') {
400 header->insert(url_idx, "/");
401 }
402 } else {
403 LOG(ERROR) << "Could not find end of hostname in request. Line was: "
404 << *header;
405 SendClientError(500, kInternalErrorMsg);
406 return false;
407 }
408 }
409 return true;
410}
411
412// Extract the HTTP version number from the first line of the client headers.
413// Returns true if found.
414bool HTTPProxy::ReadClientHTTPVersion(string *header) {
415 const string http_version_prefix(kHTTPVersionPrefix);
416 size_t http_ver_pos = header->find(http_version_prefix);
417 if (http_ver_pos != string::npos) {
418 client_version_ =
419 header->substr(http_ver_pos + http_version_prefix.length() - 1);
420 } else {
421 SendClientError(501, "Server only accepts HTTP/1.x requests");
422 return false;
423 }
424 return true;
425}
426
427// IOInputHandler callback that fires when data is read from the client.
428// This could be header data, or perhaps POST data that follows the headers.
429void HTTPProxy::ReadFromClient(InputData *data) {
430 VLOG(3) << "In " << __func__ << " length " << data->len;
431
432 if (data->len == 0) {
433 // EOF from client.
434 StopClient();
435 return;
436 }
437
438 if (state_ == kStateReadClientHeader) {
439 if (!ReadClientHeaders(data)) {
440 return;
441 }
442 if (state_ == kStateReadClientHeader) {
443 // Still consuming client headers; restart the input timer.
444 StartIdleTimeout();
445 return;
446 }
447 }
448
449 // Check data->len again since ReadClientHeaders() may have consumed some
450 // part of it.
451 if (data->len != 0) {
452 // The client sent some information after its headers. Buffer the client
453 // input and temporarily disable input events from the client.
454 client_data_.Append(ByteString(data->buf, data->len));
455 read_client_handler_->Stop();
456 StartTransmit();
457 }
458}
459
460// IOInputHandler callback which fires when data has been read from the
461// server.
462void HTTPProxy::ReadFromServer(InputData *data) {
463 VLOG(3) << "In " << __func__ << " length " << data->len;
464 if (data->len == 0) {
465 // Server closed connection.
466 if (server_data_.IsEmpty()) {
467 StopClient();
468 return;
469 }
470 state_ = kStateFlushResponse;
471 } else {
472 read_server_handler_->Stop();
473 }
474
475 server_data_.Append(ByteString(data->buf, data->len));
476
477 StartTransmit();
478}
479
480// Return an HTTP error message back to the client.
481void HTTPProxy::SendClientError(int code, const string &error) {
482 VLOG(3) << "In " << __func__;
483 LOG(ERROR) << "Sending error " << error;
484
485 string error_msg = StringPrintf("HTTP/1.1 %d ERROR\r\n"
486 "Content-Type: text/plain\r\n\r\n"
487 "%s", code, error.c_str());
488 server_data_ = ByteString(error_msg, false);
489 state_ = kStateFlushResponse;
490 StartTransmit();
491}
492
493// Start a timeout for "the next event". This timeout augments the overall
494// transaction timeout to make sure there is some activity occurring at
495// reasonable intervals.
496void HTTPProxy::StartIdleTimeout() {
497 int timeout_seconds = 0;
498 switch (state_) {
499 case kStateReadClientHeader:
500 timeout_seconds = kClientHeaderTimeoutSeconds;
501 break;
502 case kStateConnectServer:
503 timeout_seconds = kConnectTimeoutSeconds;
504 break;
505 case kStateLookupServer:
506 // DNSClient has its own internal timeout, so we need not set one here.
507 timeout_seconds = 0;
508 break;
509 default:
510 timeout_seconds = kInputTimeoutSeconds;
511 break;
512 }
513 if (idle_timeout_) {
514 idle_timeout_->Cancel();
515 idle_timeout_ = NULL;
516 }
517 if (timeout_seconds != 0) {
518 idle_timeout_ = task_factory_.NewRunnableMethod(&HTTPProxy::StopClient);
519 dispatcher_->PostDelayedTask(idle_timeout_, timeout_seconds * 1000);
520 }
521}
522
523// Start the various input handlers. Listen for new data only if we have
524// completely written the last data we've received to the other end.
525void HTTPProxy::StartReceive() {
526 if (state_ == kStateTunnelData && client_data_.IsEmpty()) {
527 read_client_handler_->Start();
528 }
529 if (server_data_.IsEmpty()) {
530 if (state_ == kStateTunnelData) {
531 if (read_server_handler_.get()) {
532 read_server_handler_->Start();
533 } else {
534 read_server_handler_.reset(
535 dispatcher_->CreateInputHandler(server_socket_,
536 read_server_callback_.get()));
537 }
538 } else if (state_ == kStateFlushResponse) {
539 StopClient();
540 return;
541 }
542 }
543 StartIdleTimeout();
544}
545
546// Start the various output-ready handlers for the endpoints we have
547// data waiting for.
548void HTTPProxy::StartTransmit() {
549 if (state_ == kStateTunnelData && !client_data_.IsEmpty()) {
550 if (write_server_handler_.get()) {
551 write_server_handler_->Start();
552 } else {
553 write_server_handler_.reset(
554 dispatcher_->CreateReadyHandler(server_socket_,
555 IOHandler::kModeOutput,
556 write_server_callback_.get()));
557 }
558 }
559 if ((state_ == kStateFlushResponse || state_ == kStateTunnelData) &&
560 !server_data_.IsEmpty()) {
561 if (write_client_handler_.get()) {
562 write_client_handler_->Start();
563 } else {
564 write_client_handler_.reset(
565 dispatcher_->CreateReadyHandler(client_socket_,
566 IOHandler::kModeOutput,
567 write_client_callback_.get()));
568 }
569 }
570 StartIdleTimeout();
571}
572
573// End the transaction with the current client, restart the IOHandler
574// which alerts us to new clients connecting. This function is called
575// during various error conditions and is a callback for all timeouts.
576void HTTPProxy::StopClient() {
577 VLOG(3) << "In " << __func__;
578
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800579 if (is_route_requested_) {
580 connection_->ReleaseRouting();
581 is_route_requested_ = false;
582 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700583 write_client_handler_.reset();
584 read_client_handler_.reset();
585 if (client_socket_ != -1) {
586 sockets_->Close(client_socket_);
587 client_socket_ = -1;
588 }
589 client_headers_.clear();
590 client_version_.clear();
591 server_port_ = kDefaultServerPort;
592 write_server_handler_.reset();
593 read_server_handler_.reset();
594 if (server_socket_ != -1) {
595 sockets_->Close(server_socket_);
596 server_socket_ = -1;
597 }
598 server_hostname_.clear();
599 client_data_.Clear();
600 server_data_.Clear();
601 dns_client_->Stop();
602 server_async_connection_->Stop();
603 task_factory_.RevokeAll();
604 idle_timeout_ = NULL;
605 accept_handler_->Start();
606 state_ = kStateWaitConnection;
607}
608
609// Output ReadyHandler callback which fires when the client socket is
610// ready for data to be sent to it.
611void HTTPProxy::WriteToClient(int fd) {
612 CHECK_EQ(client_socket_, fd);
613 int ret = sockets_->Send(fd, server_data_.GetConstData(),
614 server_data_.GetLength(), 0);
615 VLOG(3) << "In " << __func__ << " wrote " << ret << " of " <<
616 server_data_.GetLength();
617 if (ret < 0) {
618 LOG(ERROR) << "Server write failed";
619 StopClient();
620 return;
621 }
622
623 server_data_ = ByteString(server_data_.GetConstData() + ret,
624 server_data_.GetLength() - ret);
625
626 if (server_data_.IsEmpty()) {
627 write_client_handler_->Stop();
628 }
629
630 StartReceive();
631}
632
633// Output ReadyHandler callback which fires when the server socket is
634// ready for data to be sent to it.
635void HTTPProxy::WriteToServer(int fd) {
636 CHECK_EQ(server_socket_, fd);
637 int ret = sockets_->Send(fd, client_data_.GetConstData(),
638 client_data_.GetLength(), 0);
639 VLOG(3) << "In " << __func__ << " wrote " << ret << " of " <<
640 client_data_.GetLength();
641
642 if (ret < 0) {
643 LOG(ERROR) << "Client write failed";
644 StopClient();
645 return;
646 }
647
648 client_data_ = ByteString(client_data_.GetConstData() + ret,
649 client_data_.GetLength() - ret);
650
651 if (client_data_.IsEmpty()) {
652 write_server_handler_->Stop();
653 }
654
655 StartReceive();
656}
657
658} // namespace shill