Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(484)

Unified Diff: chrome/browser/chromeos/web_socket_proxy.cc

Issue 21115004: extensions: Remove chrome.webSocketProxyPrivate API (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/chromeos/web_socket_proxy.h ('k') | chrome/browser/chromeos/web_socket_proxy_controller.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/chromeos/web_socket_proxy.cc
diff --git a/chrome/browser/chromeos/web_socket_proxy.cc b/chrome/browser/chromeos/web_socket_proxy.cc
deleted file mode 100644
index b1465c22230e1f87f55607928320af4b9602ab08..0000000000000000000000000000000000000000
--- a/chrome/browser/chromeos/web_socket_proxy.cc
+++ /dev/null
@@ -1,1918 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/web_socket_proxy.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <algorithm>
-#include <limits>
-#include <list>
-#include <map>
-#include <vector>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <signal.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include "base/base64.h"
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/sequenced_task_runner_helpers.h"
-#include "base/sha1.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/sys_byteorder.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/chromeos/web_socket_proxy_helper.h"
-#include "chrome/browser/internal_auth.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/common/url_constants.h"
-#include "extensions/common/constants.h"
-#include "net/base/address_list.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/cert/cert_verifier.h"
-#include "net/http/transport_security_state.h"
-#include "net/socket/client_socket_factory.h"
-#include "net/socket/client_socket_handle.h"
-#include "net/socket/ssl_client_socket.h"
-#include "net/socket/stream_socket.h"
-#include "net/ssl/ssl_config_service.h"
-#include "third_party/libevent/evdns.h"
-#include "third_party/libevent/event.h"
-#include "url/gurl.h"
-#include "url/url_parse.h"
-
-using content::BrowserThread;
-
-namespace chromeos {
-
-namespace {
-
-const uint8 kCRLF[] = "\r\n";
-const uint8 kCRLFCRLF[] = "\r\n\r\n";
-
-// Not a constant but preprocessor definition for easy concatenation.
-#define kProxyPath "/tcpproxy"
-
-enum WebSocketStatusCode {
- WS_CLOSE_NORMAL = 1000,
- WS_CLOSE_GOING_AWAY = 1001,
- WS_CLOSE_PROTOCOL_ERROR = 1002,
- WS_CLOSE_UNACCEPTABLE_DATA = 1003,
-
- WS_CLOSE_DESTINATION_ERROR = 4000,
- WS_CLOSE_LIMIT_VIOLATION = 4001,
- WS_CLOSE_RESOLUTION_FAILED = 4002,
- WS_CLOSE_UNEXPECTED = 4003
-};
-
-enum WebSocketFrameOpcode {
- WS_OPCODE_TEXT = 1,
- WS_OPCODE_CLOSE = 8
-};
-
-// Fixed-size (one-byte) messages communicated via control pipe.
-const char kControlMessageShutdown[] = { '.' };
-const char kControlMessageNetworkChange[] = { ':' };
-
-// Returns true on success.
-bool SetNonBlock(int fd) {
- int flags = fcntl(fd, F_GETFL, 0);
- return flags >= 0 && fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0;
-}
-
-// Returns true on success.
-bool IgnoreSigPipe() {
- struct sigaction sa;
- sa.sa_handler = SIG_IGN;
- sa.sa_flags = 0;
- if (sigemptyset(&sa.sa_mask) || sigaction(SIGPIPE, &sa, 0)) {
- LOG(ERROR) << "WebSocketProxy: Failed to disable sigpipe";
- return false;
- }
- return true;
-}
-
-uint64 ReadNetworkInteger(uint8* buf, int num_bytes) {
- uint64 rv = 0;
- DCHECK_GE(num_bytes, 0);
- DCHECK_LE(num_bytes, 8);
- while (num_bytes--) {
- rv <<= 8;
- rv += *buf++;
- }
- return rv;
-}
-
-void WriteNetworkInteger(int64 n, uint8* buf, int num_bytes) {
- DCHECK_GE(num_bytes, 0);
- DCHECK_LE(num_bytes, 8);
- while (num_bytes--) {
- buf[num_bytes] = n % (1 << 8);
- n >>= 8;
- }
-}
-
-typedef uint8 (*AsciiFilter)(uint8);
-
-uint8 AsciiFilterVerbatim(uint8 c) {
- return c;
-}
-
-uint8 AsciiFilterLower(uint8 c) {
- return base::ToLowerASCII(c);
-}
-
-std::string FetchAsciiSnippet(uint8* begin, uint8* end, AsciiFilter filter) {
- std::string rv;
- for (; begin < end; ++begin) {
- if (!isascii(*begin))
- return rv;
- rv += filter(*begin);
- }
- return rv;
-}
-
-std::string FetchExtensionIdFromOrigin(const std::string &origin) {
- GURL url(origin);
- if (url.SchemeIs(extensions::kExtensionScheme))
- return url.host();
- else
- return std::string();
-}
-
-inline size_t strlen(const void* s) {
- return ::strlen(static_cast<const char*>(s));
-}
-
-void SendNotification(int port) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- content::NotificationService::current()->Notify(
- chrome::NOTIFICATION_WEB_SOCKET_PROXY_STARTED,
- content::NotificationService::AllSources(), content::Details<int>(&port));
-}
-
-class Conn;
-
-// Websocket to TCP proxy server.
-class Serv {
- public:
- Serv();
- ~Serv();
-
- // Do not call it twice.
- void Run();
-
- // Terminates running server (should be called on a different thread).
- void Shutdown();
- // Called on network change. Reinitializes DNS resolution service.
- void OnNetworkChange();
-
- void ZapConn(Conn*);
- void MarkConnImportance(Conn*, bool important);
- Conn* GetFreshConn();
- bool IsConnSane(Conn*);
- void CloseAll();
-
- static void OnConnect(int listening_sock, short event, void*);
- static void OnControlRequest(int fd, short event, void*);
-
- struct event_base* evbase() { return evbase_; }
-
- // Libevent base.
- struct event_base* evbase_;
-
- // Socket to listen incoming websocket connections.
- int listening_sock_;
-
- // TODO(dilmah): remove this extra socket as soon as obsolete
- // getPassportForTCP function is removed from webSocketProxyPrivate API.
- // Additional socket to listen incoming connections on fixed port 10101.
- int extra_listening_sock_;
-
- // Used to communicate control requests: either shutdown request or network
- // change notification.
- int control_descriptor_[2];
-
- // Flag whether shutdown has been requested.
- bool shutdown_requested_;
-
- // List of pending connections; We are trying to keep size of this list
- // below kConnPoolLimit in LRU fashion.
- typedef std::list<Conn*> ConnPool;
- ConnPool conn_pool_;
-
- // Reverse map to look up a connection in a conn_pool.
- typedef std::map<Conn*, ConnPool::iterator> RevMap;
- RevMap rev_map_;
-
- scoped_ptr<struct event> connection_event_;
- scoped_ptr<struct event> control_event_;
- // TODO(dilmah): remove this extra event as soon as obsolete
- // getPassportForTCP function is removed from webSocketProxyPrivate API.
- scoped_ptr<struct event> extra_connection_event_;
-
- DISALLOW_COPY_AND_ASSIGN(Serv);
-};
-
-// Connection (amalgamates both channels between proxy and javascript and
-// between proxy and destination).
-class Conn {
- public:
- enum Phase {
- // Initial stage of connection.
- PHASE_WAIT_HANDSHAKE,
- PHASE_WAIT_DESTFRAME,
- PHASE_WAIT_DESTCONNECT,
-
- // Operational stage of connection.
- PHASE_OUTSIDE_FRAME,
- PHASE_INSIDE_FRAME_BASE64,
- PHASE_INSIDE_FRAME_SKIP,
-
- // Terminal stage of connection.
- PHASE_SHUT, // Closing handshake was emitted, buffers may be pending.
- PHASE_DEFUNCT // Connection was nuked.
- };
-
- // Channel structure (either proxy<->browser or proxy<->destination).
- class Chan {
- public:
- explicit Chan(Conn* master)
- : master_(master),
- write_pending_(false),
- read_bev_(NULL),
- write_bev_(NULL),
- read_fd_(-1),
- write_fd_(-1) {
- }
-
- ~Chan() {
- Zap();
- }
-
- // Returns true on success.
- bool Write(const void* data, size_t size) {
- if (write_bev_ == NULL)
- return false;
- write_pending_ = true;
- return (0 == bufferevent_write(write_bev_, data, size));
- }
-
- void Zap() {
- if (read_bev_) {
- bufferevent_disable(read_bev_, EV_READ);
- bufferevent_free(read_bev_);
- }
- if (write_bev_ && write_bev_ != read_bev_) {
- bufferevent_disable(write_bev_, EV_READ);
- bufferevent_free(write_bev_);
- }
- read_bev_ = NULL;
- write_bev_ = NULL;
- if (write_fd_ && read_fd_ == write_fd_)
- shutdown(write_fd_, SHUT_RDWR);
- if (write_fd_ >= 0) {
- close(write_fd_);
- DCHECK_GE(read_fd_, 0);
- }
- if (read_fd_ && read_fd_ != write_fd_)
- close(read_fd_);
- read_fd_ = -1;
- write_fd_ = -1;
- write_pending_ = false;
- master_->ConsiderSuicide();
- }
-
- void Shut() {
- if (!write_pending_)
- Zap();
- }
-
- int read_fd() const { return read_fd_; }
- void set_read_fd(int fd) { read_fd_ = fd; }
- int write_fd() const { return write_fd_; }
- void set_write_fd(int fd) { write_fd_ = fd; }
- bool write_pending() const { return write_pending_; }
- void set_write_pending(bool pending) { write_pending_ = pending; }
- struct bufferevent* read_bev() const { return read_bev_; }
- void set_read_bev(struct bufferevent* bev) { read_bev_ = bev; }
- struct bufferevent* write_bev() const { return write_bev_; }
- void set_write_bev(struct bufferevent* bev) { write_bev_ = bev; }
-
- private:
- Conn* master_;
- bool write_pending_; // Whether write buffer is not flushed yet.
- struct bufferevent* read_bev_;
- struct bufferevent* write_bev_;
- // UNIX descriptors.
- int read_fd_;
- int write_fd_;
-
- DISALLOW_COPY_AND_ASSIGN(Chan);
- };
-
- // Status of processing incoming data.
- enum Status {
- STATUS_OK,
- STATUS_INCOMPLETE, // Not all required data is present in buffer yet.
- STATUS_SKIP,
- STATUS_ABORT // Data is invalid. We must shut connection.
- };
-
- // Unfortunately evdns callbacks are uncancellable,
- // so potentially we can receive callback for a deleted Conn.
- // Even worse, storage of deleted Conn may be reused
- // for a new connection and new connection can receive callback
- // destined for deleted Conn.
- // EventKey is introduced in order to prevent that.
- typedef void* EventKey;
- typedef std::map<EventKey, Conn*> EventKeyMap;
-
- explicit Conn(Serv* master);
- ~Conn();
-
- static Conn* Get(EventKey evkey);
-
- void Shut(int status, const void* reason);
-
- void ConsiderSuicide();
-
- Status ConsumeHeader(struct evbuffer*);
- Status ConsumeDestframe(struct evbuffer*);
- Status ConsumeFrameHeader(struct evbuffer*);
- Status ProcessFrameData(struct evbuffer*);
-
- // Return true on success.
- bool EmitHandshake(Chan*);
- bool EmitFrame(
- Chan*, WebSocketFrameOpcode opcode, const void* buf, size_t size);
-
- // Attempts to establish second connection (to remote TCP service).
- // Returns true on success.
- bool TryConnectDest(const struct sockaddr*, socklen_t);
-
- // Return security origin associated with this connection.
- const std::string& GetOrigin();
-
- // Used as libevent callbacks.
- static void OnDestConnectTimeout(int, short, EventKey);
- static void OnPrimchanRead(struct bufferevent*, EventKey);
- static void OnPrimchanWrite(struct bufferevent*, EventKey);
- static void OnPrimchanError(struct bufferevent*, short what, EventKey);
- static void OnDestResolutionIPv4(int result, char type, int count,
- int ttl, void* addr_list, EventKey);
- static void OnDestResolutionIPv6(int result, char type, int count,
- int ttl, void* addr_list, EventKey);
- static void OnDestchanRead(struct bufferevent*, EventKey);
- static void OnDestchanWrite(struct bufferevent*, EventKey);
- static void OnDestchanError(struct bufferevent*, short what, EventKey);
-
- Chan& primchan() { return primchan_; }
- EventKey evkey() const { return evkey_; }
-
- private:
- Serv* master_;
- Phase phase_;
- uint64 frame_bytes_remaining_;
- uint8 frame_mask_[4];
- int frame_mask_index_;
-
- // We maintain two channels per Conn:
- // primary channel is websocket connection.
- Chan primchan_;
- // Destination channel is a proxied connection.
- Chan destchan_;
-
- EventKey evkey_;
-
- // Header fields supplied by client at initial websocket handshake.
- std::map<std::string, std::string> header_fields_;
-
- // Parameters requested via query component of GET resource.
- std::map<std::string, std::string> requested_parameters_;
-
- // Hostname and port of destination socket.
- // Websocket client supplies them in first data frame (destframe).
- std::string destname_;
- int destport_;
-
- // Preresolved |destname_| (empty if not pre-resolved).
- std::string destaddr_;
-
- // Whether TLS over TCP requested.
- bool do_tls_;
-
- // We try to DNS resolve hostname in both IPv4 and IPv6 domains.
- // Track resolution failures here.
- bool destresolution_ipv4_failed_;
- bool destresolution_ipv6_failed_;
-
- // Used to schedule a timeout for initial phase of connection.
- scoped_ptr<struct event> destconnect_timeout_event_;
-
- static base::LazyInstance<EventKeyMap>::Leaky evkey_map_;
- static EventKey last_evkey_;
-
- DISALLOW_COPY_AND_ASSIGN(Conn);
-};
-
-class SSLChan : public base::MessageLoopForIO::Watcher {
- public:
- static void Start(const net::AddressList& address_list,
- const net::HostPortPair& host_port_pair,
- int read_pipe,
- int write_pipe) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- SSLChan* ALLOW_UNUSED chan = new SSLChan(
- address_list, host_port_pair, read_pipe, write_pipe);
- }
-
- private:
- enum Phase {
- PHASE_CONNECTING,
- PHASE_RUNNING,
- PHASE_CLOSING,
- PHASE_CLOSED
- };
-
- class DerivedIOBufferWithSize : public net::IOBufferWithSize {
- public:
- DerivedIOBufferWithSize(net::IOBuffer* host, int size)
- : IOBufferWithSize(host->data(), size), host_(host) {
- DCHECK(host_.get());
- DCHECK(host_->data());
- }
-
- protected:
- virtual ~DerivedIOBufferWithSize() {
- data_ = NULL; // We do not own memory, bypass base class destructor.
- }
-
- scoped_refptr<net::IOBuffer> host_;
- };
-
- // Provides queue of data represented as IOBuffers.
- class IOBufferQueue {
- public:
- // We do not allocate all capacity at once but lazily in |buf_size_| chunks.
- explicit IOBufferQueue(int capacity)
- : buf_size_(1 + capacity / kNumBuffersLimit) {
- }
-
- // Obtains IOBuffer to add new data to back.
- net::IOBufferWithSize* GetIOBufferToFill() {
- if (back_.get() == NULL) {
- if (storage_.size() >= kNumBuffersLimit)
- return NULL;
- storage_.push_back(new net::IOBufferWithSize(buf_size_));
- back_ = new net::DrainableIOBuffer(storage_.back().get(), buf_size_);
- }
- return new DerivedIOBufferWithSize(
- back_.get(), back_->BytesRemaining());
- }
-
- // Obtains IOBuffer with some data from front.
- net::IOBufferWithSize* GetIOBufferToProcess() {
- if (front_.get() == NULL) {
- if (storage_.empty())
- return NULL;
- front_ = new net::DrainableIOBuffer(storage_.front().get(), buf_size_);
- }
- int front_capacity =
- (storage_.size() == 1 && back_.get()) ? back_->BytesConsumed()
- : buf_size_;
- return new DerivedIOBufferWithSize(
- front_.get(), front_capacity - front_->BytesConsumed());
- }
-
- // Records number of bytes as added to back.
- void DidFill(int bytes) {
- DCHECK(back_.get());
- back_->DidConsume(bytes);
- if (back_->BytesRemaining() == 0)
- back_ = NULL;
- }
-
- // Pops number of bytes from front.
- void DidProcess(int bytes) {
- DCHECK(front_.get());
- front_->DidConsume(bytes);
- if (front_->BytesRemaining() == 0) {
- storage_.pop_front();
- front_ = NULL;
- }
- }
-
- void Clear() {
- front_ = NULL;
- back_ = NULL;
- storage_.clear();
- }
-
- private:
- static const unsigned kNumBuffersLimit = 12;
- const int buf_size_;
- std::list< scoped_refptr<net::IOBufferWithSize> > storage_;
- scoped_refptr<net::DrainableIOBuffer> front_;
- scoped_refptr<net::DrainableIOBuffer> back_;
-
- DISALLOW_COPY_AND_ASSIGN(IOBufferQueue);
- };
-
- SSLChan(const net::AddressList address_list,
- const net::HostPortPair host_port_pair,
- int read_pipe,
- int write_pipe)
- : phase_(PHASE_CONNECTING),
- host_port_pair_(host_port_pair),
- inbound_stream_(WebSocketProxy::kBufferLimit),
- outbound_stream_(WebSocketProxy::kBufferLimit),
- read_pipe_(read_pipe),
- write_pipe_(write_pipe),
- weak_factory_(this) {
- if (!SetNonBlock(read_pipe_) || !SetNonBlock(write_pipe_)) {
- Shut(net::ERR_UNEXPECTED);
- return;
- }
- net::ClientSocketFactory* factory =
- net::ClientSocketFactory::GetDefaultFactory();
- socket_.reset(factory->CreateTransportClientSocket(
- address_list, NULL, net::NetLog::Source()));
- if (socket_ == NULL) {
- Shut(net::ERR_FAILED);
- return;
- }
- int result = socket_->Connect(base::Bind(&SSLChan::OnSocketConnect,
- base::Unretained(this)));
- if (result != net::ERR_IO_PENDING)
- OnSocketConnect(result);
- }
-
- virtual ~SSLChan() {
- phase_ = PHASE_CLOSED;
- write_pipe_controller_.StopWatchingFileDescriptor();
- read_pipe_controller_.StopWatchingFileDescriptor();
- close(write_pipe_);
- close(read_pipe_);
- }
-
- void Shut(int ALLOW_UNUSED net_error_code) {
- if (phase_ != PHASE_CLOSED) {
- phase_ = PHASE_CLOSING;
- scoped_refptr<net::IOBufferWithSize> buf[] = {
- outbound_stream_.GetIOBufferToProcess(),
- inbound_stream_.GetIOBufferToProcess()
- };
- for (int i = arraysize(buf); i--;) {
- if (buf[i].get() && buf[i]->size() > 0) {
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&SSLChan::Proceed, weak_factory_.GetWeakPtr()));
- return;
- }
- }
- phase_ = PHASE_CLOSED;
- if (socket_ != NULL) {
- socket_->Disconnect();
- socket_.reset();
- }
- base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
- }
- }
-
- void OnSocketConnect(int result) {
- if (phase_ != PHASE_CONNECTING) {
- NOTREACHED();
- return;
- }
- if (result) {
- Shut(result);
- return;
- }
- net::ClientSocketHandle* handle = new net::ClientSocketHandle();
- handle->set_socket(socket_.release());
- net::ClientSocketFactory* factory =
- net::ClientSocketFactory::GetDefaultFactory();
- net::SSLClientSocketContext ssl_context;
- if (!cert_verifier_.get())
- cert_verifier_.reset(net::CertVerifier::CreateDefault());
- ssl_context.cert_verifier = cert_verifier_.get();
- if (!transport_security_state_.get())
- transport_security_state_.reset(new net::TransportSecurityState);
- ssl_context.transport_security_state = transport_security_state_.get();
- socket_.reset(factory->CreateSSLClientSocket(
- handle, host_port_pair_, ssl_config_, ssl_context));
- if (!socket_.get()) {
- LOG(WARNING) << "Failed to create an SSL client socket.";
- OnSSLHandshakeCompleted(net::ERR_UNEXPECTED);
- return;
- }
- result = socket_->Connect(base::Bind(&SSLChan::OnSSLHandshakeCompleted,
- base::Unretained(this)));
- if (result != net::ERR_IO_PENDING)
- OnSSLHandshakeCompleted(result);
- }
-
- void OnSSLHandshakeCompleted(int result) {
- if (result) {
- Shut(result);
- return;
- }
- is_socket_read_pending_ = false;
- is_socket_write_pending_ = false;
- is_read_pipe_blocked_ = false;
- is_write_pipe_blocked_ = false;
- base::MessageLoopForIO::current()->WatchFileDescriptor(
- read_pipe_, false, base::MessageLoopForIO::WATCH_READ,
- &read_pipe_controller_, this);
- base::MessageLoopForIO::current()->WatchFileDescriptor(
- write_pipe_, false, base::MessageLoopForIO::WATCH_WRITE,
- &write_pipe_controller_, this);
- phase_ = PHASE_RUNNING;
- Proceed();
- }
-
- void OnSocketRead(int result) {
- DCHECK(is_socket_read_pending_);
- is_socket_read_pending_ = false;
- if (result <= 0) {
- Shut(result);
- return;
- }
- inbound_stream_.DidFill(result);
- Proceed();
- }
-
- void OnSocketWrite(int result) {
- DCHECK(is_socket_write_pending_);
- is_socket_write_pending_ = false;
- if (result < 0) {
- outbound_stream_.Clear();
- Shut(result);
- return;
- }
- outbound_stream_.DidProcess(result);
- Proceed();
- }
-
- // MessageLoopForIO::Watcher overrides.
- virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
- if (fd != read_pipe_) {
- NOTREACHED();
- return;
- }
- is_read_pipe_blocked_ = false;
- Proceed();
- }
-
- virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
- if (fd != write_pipe_) {
- NOTREACHED();
- return;
- }
- is_write_pipe_blocked_ = false;
- Proceed();
- }
-
- private:
- void Proceed() {
- if (phase_ != PHASE_RUNNING && phase_ != PHASE_CLOSING)
- return;
- for (bool proceed = true; proceed;) {
- proceed = false;
- if (!is_read_pipe_blocked_ && phase_ == PHASE_RUNNING) {
- scoped_refptr<net::IOBufferWithSize> buf =
- outbound_stream_.GetIOBufferToFill();
- if (buf.get() && buf->size() > 0) {
- int rv = read(read_pipe_, buf->data(), buf->size());
- if (rv > 0) {
- outbound_stream_.DidFill(rv);
- proceed = true;
- } else if (rv == -1 && errno == EAGAIN) {
- is_read_pipe_blocked_ = true;
- base::MessageLoopForIO::current()->WatchFileDescriptor(
- read_pipe_, false, base::MessageLoopForIO::WATCH_READ,
- &read_pipe_controller_, this);
- } else if (rv == 0) {
- Shut(0);
- } else {
- DCHECK_LT(rv, 0);
- Shut(net::ERR_UNEXPECTED);
- return;
- }
- }
- }
- if (!is_socket_read_pending_ && phase_ == PHASE_RUNNING) {
- scoped_refptr<net::IOBufferWithSize> buf =
- inbound_stream_.GetIOBufferToFill();
- if (buf.get() && buf->size() > 0) {
- int rv = socket_->Read(
- buf.get(),
- buf->size(),
- base::Bind(&SSLChan::OnSocketRead, base::Unretained(this)));
- is_socket_read_pending_ = true;
- if (rv != net::ERR_IO_PENDING) {
- base::MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(&SSLChan::OnSocketRead,
- weak_factory_.GetWeakPtr(), rv));
- }
- }
- }
- if (!is_socket_write_pending_) {
- scoped_refptr<net::IOBufferWithSize> buf =
- outbound_stream_.GetIOBufferToProcess();
- if (buf.get() && buf->size() > 0) {
- int rv = socket_->Write(
- buf.get(),
- buf->size(),
- base::Bind(&SSLChan::OnSocketWrite, base::Unretained(this)));
- is_socket_write_pending_ = true;
- if (rv != net::ERR_IO_PENDING) {
- base::MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(&SSLChan::OnSocketWrite,
- weak_factory_.GetWeakPtr(), rv));
- }
- } else if (phase_ == PHASE_CLOSING) {
- Shut(0);
- }
- }
- if (!is_write_pipe_blocked_) {
- scoped_refptr<net::IOBufferWithSize> buf =
- inbound_stream_.GetIOBufferToProcess();
- if (buf.get() && buf->size() > 0) {
- int rv = write(write_pipe_, buf->data(), buf->size());
- if (rv > 0) {
- inbound_stream_.DidProcess(rv);
- proceed = true;
- } else if (rv == -1 && errno == EAGAIN) {
- is_write_pipe_blocked_ = true;
- base::MessageLoopForIO::current()->WatchFileDescriptor(
- write_pipe_, false, base::MessageLoopForIO::WATCH_WRITE,
- &write_pipe_controller_, this);
- } else {
- DCHECK_LE(rv, 0);
- inbound_stream_.Clear();
- Shut(net::ERR_UNEXPECTED);
- return;
- }
- } else if (phase_ == PHASE_CLOSING) {
- Shut(0);
- }
- }
- }
- }
-
- Phase phase_;
- scoped_ptr<net::StreamSocket> socket_;
- net::HostPortPair host_port_pair_;
- scoped_ptr<net::CertVerifier> cert_verifier_;
- scoped_ptr<net::TransportSecurityState> transport_security_state_;
- net::SSLConfig ssl_config_;
- IOBufferQueue inbound_stream_;
- IOBufferQueue outbound_stream_;
- int read_pipe_;
- int write_pipe_;
- bool is_socket_read_pending_;
- bool is_socket_write_pending_;
- bool is_read_pipe_blocked_;
- bool is_write_pipe_blocked_;
- base::WeakPtrFactory<SSLChan> weak_factory_;
- base::MessageLoopForIO::FileDescriptorWatcher read_pipe_controller_;
- base::MessageLoopForIO::FileDescriptorWatcher write_pipe_controller_;
-
- friend class base::DeleteHelper<SSLChan>;
- DISALLOW_COPY_AND_ASSIGN(SSLChan);
-};
-
-Serv::Serv()
- : evbase_(NULL),
- listening_sock_(-1),
- extra_listening_sock_(-1),
- shutdown_requested_(false) {
- control_descriptor_[0] = -1;
- control_descriptor_[1] = -1;
-}
-
-Serv::~Serv() {
- CloseAll();
-}
-
-void Serv::Run() {
- if (evbase_ || shutdown_requested_)
- return;
-
- evbase_ = event_init();
- if (!evbase_) {
- LOG(ERROR) << "WebSocketProxy: Couldn't create libevent base";
- return;
- }
-
- if (pipe(control_descriptor_) ||
- !SetNonBlock(control_descriptor_[0]) ||
- !SetNonBlock(control_descriptor_[1])) {
- LOG(ERROR) << "WebSocketProxy: Failed to create control pipe";
- return;
- }
-
- listening_sock_ = socket(AF_INET, SOCK_STREAM, 0);
- if (listening_sock_ < 0) {
- LOG(ERROR) << "WebSocketProxy: Failed to create socket";
- return;
- }
- {
- int on = 1;
- setsockopt(listening_sock_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- }
-
- struct sockaddr_in addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- // Let the OS allocate a port number.
- addr.sin_port = base::HostToNet16(0);
- addr.sin_addr.s_addr = base::HostToNet32(INADDR_LOOPBACK);
- if (bind(listening_sock_,
- reinterpret_cast<struct sockaddr*>(&addr),
- sizeof(addr))) {
- LOG(ERROR) << "WebSocketProxy: Failed to bind server socket";
- return;
- }
- if (listen(listening_sock_, 12)) {
- LOG(ERROR) << "WebSocketProxy: Failed to listen server socket";
- return;
- }
- if (!SetNonBlock(listening_sock_)) {
- LOG(ERROR) << "WebSocketProxy: Failed to go non block";
- return;
- }
-
- connection_event_.reset(new struct event);
- event_set(connection_event_.get(), listening_sock_, EV_READ | EV_PERSIST,
- &OnConnect, this);
- event_base_set(evbase_, connection_event_.get());
- if (event_add(connection_event_.get(), NULL)) {
- LOG(ERROR) << "WebSocketProxy: Failed to add listening event";
- return;
- }
-
- {
- // TODO(dilmah): remove this control block as soon as obsolete
- // getPassportForTCP function is removed from webSocketProxyPrivate API.
- // Following block adds extra listening socket on fixed port 10101.
- extra_listening_sock_ = socket(AF_INET, SOCK_STREAM, 0);
- if (extra_listening_sock_ < 0) {
- LOG(ERROR) << "WebSocketProxy: Failed to create socket";
- return;
- }
- {
- int on = 1;
- setsockopt(listening_sock_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- }
- const int kPort = 10101;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = base::HostToNet16(kPort);
- addr.sin_addr.s_addr = base::HostToNet32(INADDR_LOOPBACK);
- if (bind(extra_listening_sock_,
- reinterpret_cast<struct sockaddr*>(&addr),
- sizeof(addr))) {
- LOG(ERROR) << "WebSocketProxy: Failed to bind server socket";
- return;
- }
- if (listen(extra_listening_sock_, 12)) {
- LOG(ERROR) << "WebSocketProxy: Failed to listen server socket";
- return;
- }
- if (!SetNonBlock(extra_listening_sock_)) {
- LOG(ERROR) << "WebSocketProxy: Failed to go non block";
- return;
- }
- extra_connection_event_.reset(new struct event);
- event_set(extra_connection_event_.get(), extra_listening_sock_,
- EV_READ | EV_PERSIST, &OnConnect, this);
- event_base_set(evbase_, extra_connection_event_.get());
- if (event_add(extra_connection_event_.get(), NULL)) {
- LOG(ERROR) << "WebSocketProxy: Failed to add listening event";
- return;
- }
- }
-
- control_event_.reset(new struct event);
- event_set(control_event_.get(), control_descriptor_[0], EV_READ | EV_PERSIST,
- &OnControlRequest, this);
- event_base_set(evbase_, control_event_.get());
- if (event_add(control_event_.get(), NULL)) {
- LOG(ERROR) << "WebSocketProxy: Failed to add control event";
- return;
- }
-
- if (evdns_init())
- LOG(WARNING) << "WebSocketProxy: Failed to initialize evDNS";
- if (!IgnoreSigPipe()) {
- LOG(ERROR) << "WebSocketProxy: Failed to ignore SIGPIPE";
- return;
- }
-
- memset(&addr, 0, sizeof(addr));
- socklen_t addr_len = sizeof(addr);
- if (getsockname(
- listening_sock_, reinterpret_cast<struct sockaddr*>(&addr), &addr_len)) {
- LOG(ERROR) << "Failed to determine listening port";
- return;
- }
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&SendNotification, base::NetToHost16(addr.sin_port)));
-
- LOG(INFO) << "WebSocketProxy: Starting event dispatch loop.";
- event_base_dispatch(evbase_);
- if (shutdown_requested_)
- LOG(INFO) << "WebSocketProxy: Event dispatch loop terminated upon request";
- else
- LOG(ERROR) << "WebSocketProxy: Event dispatch loop terminated unexpectedly";
- CloseAll();
-}
-
-void Serv::Shutdown() {
- int r ALLOW_UNUSED =
- write(control_descriptor_[1], kControlMessageShutdown, 1);
-}
-
-void Serv::OnNetworkChange() {
- int r ALLOW_UNUSED =
- write(control_descriptor_[1], kControlMessageNetworkChange, 1);
-}
-
-void Serv::CloseAll() {
- while (!conn_pool_.empty())
- ZapConn(conn_pool_.back());
- if (listening_sock_ >= 0) {
- shutdown(listening_sock_, SHUT_RDWR);
- close(listening_sock_);
- }
- for (int i = 0; i < 2; ++i) {
- if (control_descriptor_[i] >= 0) {
- control_descriptor_[i] = -1;
- close(control_descriptor_[i]);
- }
- }
- if (control_event_.get()) {
- event_del(control_event_.get());
- control_event_.reset();
- }
- if (extra_connection_event_.get()) {
- event_del(extra_connection_event_.get());
- extra_connection_event_.reset();
- }
- if (connection_event_.get()) {
- event_del(connection_event_.get());
- connection_event_.reset();
- }
- if (evbase_) {
- event_base_free(evbase_);
- evbase_ = NULL;
- }
-}
-
-void Serv::ZapConn(Conn* cs) {
- RevMap::iterator rit = rev_map_.find(cs);
- if (rit != rev_map_.end()) {
- conn_pool_.erase(rit->second);
- rev_map_.erase(rit);
- delete cs;
- }
-}
-
-void Serv::MarkConnImportance(Conn* cs, bool important) {
- if (conn_pool_.size() < WebSocketProxy::kConnPoolLimit / 4) {
- // Fast common path.
- return;
- }
- RevMap::iterator rit = rev_map_.find(cs);
- if (rit != rev_map_.end()) {
- ConnPool::iterator it = rit->second;
- CHECK(*it == cs);
- if (important && it == conn_pool_.begin()) {
- // Already at the top. Shortcut.
- return;
- }
- conn_pool_.erase(it);
- }
- if (important) {
- conn_pool_.push_front(cs);
- rev_map_[cs] = conn_pool_.begin();
- } else {
- conn_pool_.push_back(cs);
- rev_map_[cs] = conn_pool_.end();
- --rev_map_[cs];
- }
-}
-
-Conn* Serv::GetFreshConn() {
- if (conn_pool_.size() > WebSocketProxy::kConnPoolLimit) {
- // Connections overflow. Shut those oldest not active.
- ConnPool::iterator it = conn_pool_.end();
- --it;
- for (int i = conn_pool_.size() - WebSocketProxy::kConnPoolLimit; i-- > 0;) {
- // Shut may invalidate an iterator; hence postdecrement.
- (*it--)->Shut(WS_CLOSE_GOING_AWAY,
- "Flood of new connections, getting rid of old ones");
- }
- if (conn_pool_.size() > WebSocketProxy::kConnPoolLimit + 12) {
- // Connections overflow. Zap the oldest not active.
- ZapConn(conn_pool_.back());
- }
- }
- Conn* cs = new Conn(this);
- conn_pool_.push_front(cs);
- rev_map_[cs] = conn_pool_.begin();
- return cs;
-}
-
-bool Serv::IsConnSane(Conn* cs) {
- return rev_map_.find(cs) != rev_map_.end();
-}
-
-// static
-void Serv::OnConnect(int listening_sock, short event, void* ctx) {
- Serv* self = static_cast<Serv*>(ctx);
- Conn* cs = self->GetFreshConn();
- int sock = accept(listening_sock, NULL, NULL);
- if (sock < 0 || !SetNonBlock(sock)) {
- // Read readiness was triggered on listening socket
- // yet we failed to accept a connection; definitely weird.
- NOTREACHED();
- self->ZapConn(cs);
- return;
- }
- cs->primchan().set_read_fd(sock);
- cs->primchan().set_write_fd(sock);
-
- struct bufferevent* bev = bufferevent_new(
- sock,
- &Conn::OnPrimchanRead, &Conn::OnPrimchanWrite, &Conn::OnPrimchanError,
- cs->evkey());
- if (bev == NULL) {
- self->ZapConn(cs);
- return;
- }
- cs->primchan().set_read_bev(bev);
- cs->primchan().set_write_bev(bev);
- bufferevent_base_set(self->evbase_, bev);
- bufferevent_setwatermark(bev, EV_READ, 0, WebSocketProxy::kBufferLimit);
- if (bufferevent_enable(bev, EV_READ | EV_WRITE)) {
- self->ZapConn(cs);
- return;
- }
-}
-
-// static
-void Serv::OnControlRequest(int fd, short event, void* ctx) {
- Serv* self = static_cast<Serv*>(ctx);
-
- char c;
- if (1 == read(fd, &c, 1) && c == *kControlMessageNetworkChange) {
- // OnNetworkChange request.
- evdns_clear_nameservers_and_suspend();
- evdns_init();
- evdns_resume();
- } else if (c == *kControlMessageShutdown) {
- self->shutdown_requested_ = true;
- event_base_loopbreak(self->evbase_);
- }
-}
-
-Conn::Conn(Serv* master)
- : master_(master),
- phase_(PHASE_WAIT_HANDSHAKE),
- frame_bytes_remaining_(0),
- frame_mask_index_(0),
- primchan_(this),
- destchan_(this),
- do_tls_(false),
- destresolution_ipv4_failed_(false),
- destresolution_ipv6_failed_(false) {
- while (evkey_map_.Get().find(last_evkey_) != evkey_map_.Get().end()) {
- last_evkey_ = reinterpret_cast<EventKey>(reinterpret_cast<size_t>(
- last_evkey_) + 1);
- }
- evkey_ = last_evkey_;
- evkey_map_.Get()[evkey_] = this;
- // Schedule timeout for initial phase of connection.
- destconnect_timeout_event_.reset(new struct event);
- evtimer_set(destconnect_timeout_event_.get(),
- &OnDestConnectTimeout, evkey_);
- event_base_set(master_->evbase(),
- destconnect_timeout_event_.get());
-
- struct timeval tv;
- tv.tv_sec = 20;
- tv.tv_usec = 0;
- evtimer_add(destconnect_timeout_event_.get(), &tv);
-}
-
-Conn::~Conn() {
- phase_ = PHASE_DEFUNCT;
- event_del(destconnect_timeout_event_.get());
- if (evkey_map_.Get()[evkey_] == this)
- evkey_map_.Get().erase(evkey_);
- else
- NOTREACHED();
-}
-
-Conn* Conn::Get(EventKey evkey) {
- EventKeyMap::iterator it = evkey_map_.Get().find(evkey);
- if (it == evkey_map_.Get().end())
- return NULL;
- Conn* cs = it->second;
- if (cs == NULL ||
- cs->evkey_ != evkey ||
- cs->master_ == NULL ||
- cs->phase_ < 0 ||
- cs->phase_ > PHASE_SHUT ||
- !cs->master_->IsConnSane(cs)) {
- return NULL;
- }
- return cs;
-}
-
-void Conn::Shut(int status, const void* reason) {
- if (phase_ >= PHASE_SHUT)
- return;
- master_->MarkConnImportance(this, false);
-
- std::vector<uint8> payload(2 + strlen(reason));
- WriteNetworkInteger(status, vector_as_array(&payload), 2);
- memcpy(vector_as_array(&payload) + 2, reason, strlen(reason));
-
- EmitFrame(
- &primchan_, WS_OPCODE_CLOSE, vector_as_array(&payload), payload.size());
- primchan_.Shut();
- destchan_.Shut();
- phase_ = PHASE_SHUT;
-}
-
-void Conn::ConsiderSuicide() {
- if (!primchan_.write_pending() && !destchan_.write_pending())
- master_->ZapConn(this);
-}
-
-Conn::Status Conn::ConsumeHeader(struct evbuffer* evb) {
- uint8* buf = EVBUFFER_DATA(evb);
- size_t buf_size = EVBUFFER_LENGTH(evb);
-
- static const uint8 kGetPrefix[] = "GET " kProxyPath;
- static const uint8 kKeyValueDelimiter[] = ": ";
-
- if (buf_size <= 0)
- return STATUS_INCOMPLETE;
- if (!buf)
- return STATUS_ABORT;
- if (!std::equal(buf, buf + std::min(buf_size, strlen(kGetPrefix)),
- kGetPrefix)) {
- // Data head does not match what is expected.
- return STATUS_ABORT;
- }
-
- if (buf_size >= WebSocketProxy::kHeaderLimit)
- return STATUS_ABORT;
- uint8* buf_end = buf + buf_size;
- // Handshake request must end with double CRLF.
- uint8* term_pos = std::search(buf, buf_end, kCRLFCRLF,
- kCRLFCRLF + strlen(kCRLFCRLF));
- if (term_pos == buf_end)
- return STATUS_INCOMPLETE;
- term_pos += strlen(kCRLFCRLF);
- // First line is "GET path?query protocol" line. If query is empty then we
- // fall back to (obsolete) way of obtaining parameters from first websocket
- // frame. Otherwise query contains all required parameters (host, port etc).
- uint8* get_request_end = std::search(
- buf, term_pos, kCRLF, kCRLF + strlen(kCRLF));
- DCHECK(get_request_end != term_pos);
- uint8* resource_end = std::find(
- buf + strlen(kGetPrefix), get_request_end, ' ');
- if (*resource_end != ' ')
- return STATUS_ABORT;
- if (resource_end != buf + strlen(kGetPrefix)) {
- char* piece = reinterpret_cast<char*>(buf) + strlen(kGetPrefix) + 1;
- url_parse::Component query(
- 0, resource_end - reinterpret_cast<uint8*>(piece));
- for (url_parse::Component key, value;
- url_parse::ExtractQueryKeyValue(piece, &query, &key, &value);) {
- if (key.len > 0) {
- requested_parameters_[std::string(piece + key.begin, key.len)] =
- net::UnescapeURLComponent(std::string(piece + value.begin,
- value.len), net::UnescapeRule::URL_SPECIAL_CHARS);
- }
- }
- }
- for (uint8* pos = get_request_end;;) {
- pos += strlen(kCRLF);
- if (term_pos - pos < static_cast<ptrdiff_t>(strlen(kCRLF)))
- return STATUS_ABORT;
- if (term_pos - pos == static_cast<ptrdiff_t>(strlen(kCRLF)))
- break;
- uint8* npos = std::search(pos, term_pos, kKeyValueDelimiter,
- kKeyValueDelimiter + strlen(kKeyValueDelimiter));
- if (npos == term_pos)
- return STATUS_ABORT;
- std::string key = FetchAsciiSnippet(pos, npos, AsciiFilterLower);
- pos = std::search(npos += strlen(kKeyValueDelimiter), term_pos,
- kCRLF, kCRLF + strlen(kCRLF));
- if (pos == term_pos)
- return STATUS_ABORT;
- if (!key.empty()) {
- header_fields_[key] = FetchAsciiSnippet(npos, pos,
- key == "sec-websocket-key" ? AsciiFilterVerbatim : AsciiFilterLower);
- }
- }
-
- // Values of Upgrade and Connection fields are hardcoded in the protocol.
- if (header_fields_["upgrade"] != "websocket" ||
- header_fields_["connection"] != "upgrade" ||
- header_fields_["sec-websocket-key"].size() != 24) {
- return STATUS_ABORT;
- }
- if (header_fields_["sec-websocket-version"] != "8" &&
- header_fields_["sec-websocket-version"] != "13") {
- return STATUS_ABORT;
- }
- // Normalize origin (e.g. leading slash).
- GURL origin = GURL(GetOrigin()).GetOrigin();
- if (!origin.is_valid())
- return STATUS_ABORT;
-
- if (!requested_parameters_.empty()) {
- destname_ = requested_parameters_["hostname"];
- int port;
- if (!base::StringToInt(requested_parameters_["port"], &port) ||
- port < 0 || port >= 1 << 16) {
- return STATUS_ABORT;
- }
- destport_ = port;
- destaddr_ = requested_parameters_["addr"];
- do_tls_ = (requested_parameters_["tls"] == "true");
-
- requested_parameters_["extension_id"] =
- FetchExtensionIdFromOrigin(GetOrigin());
- std::string passport(requested_parameters_["passport"]);
- requested_parameters_.erase("passport");
- if (!chrome::InternalAuthVerification::VerifyPassport(
- passport, "web_socket_proxy", requested_parameters_)) {
- return STATUS_ABORT;
- }
- }
-
- evbuffer_drain(evb, term_pos - buf);
- return STATUS_OK;
-}
-
-bool Conn::EmitHandshake(Chan* chan) {
- std::vector<std::string> boilerplate;
- boilerplate.push_back("HTTP/1.1 101 WebSocket Protocol Handshake");
- boilerplate.push_back("Upgrade: websocket");
- boilerplate.push_back("Connection: Upgrade");
-
- {
- // Take care of Accept field.
- std::string word = header_fields_["sec-websocket-key"];
- word += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- std::string accept_token;
- base::Base64Encode(base::SHA1HashString(word), &accept_token);
- boilerplate.push_back("Sec-WebSocket-Accept: " + accept_token);
- }
-
- boilerplate.push_back("");
- for (size_t i = 0; i < boilerplate.size(); ++i) {
- if (!chan->Write(boilerplate[i].c_str(), boilerplate[i].size()) ||
- !chan->Write(kCRLF, strlen(kCRLF))) {
- return false;
- }
- }
- return true;
-}
-
-bool Conn::EmitFrame(
- Chan* chan, WebSocketFrameOpcode opcode, const void* buf, size_t size) {
- uint8 header[10];
- int header_size = 2;
- DCHECK(chan);
- DCHECK(opcode >= 0 && static_cast<int>(opcode) < 16);
- header[0] = opcode | 0x80; // FIN bit set.
- if (size < 126) {
- header[1] = size;
- } else if (size < (1 << 16)) {
- header[1] = 126;
- WriteNetworkInteger(size, header + 2, 2);
- header_size += 2;
- } else {
- header[1] = 127;
- WriteNetworkInteger(size, header + 2, 8);
- header_size += 8;
- }
- return chan->Write(header, header_size) && chan->Write(buf, size);
-}
-
-Conn::Status Conn::ConsumeDestframe(struct evbuffer* evb) {
- if (!requested_parameters_.empty()) {
- // Parameters were already provided (and verified) in query component of
- // websocket URL.
- return STATUS_OK;
- }
- if (frame_bytes_remaining_ == 0) {
- Conn::Status rv = ConsumeFrameHeader(evb);
- if (rv != STATUS_OK)
- return rv;
- if (frame_bytes_remaining_ == 0 ||
- frame_bytes_remaining_ >= WebSocketProxy::kHeaderLimit) {
- return STATUS_ABORT;
- }
- }
-
- uint8* buf = EVBUFFER_DATA(evb);
- size_t buf_size = EVBUFFER_LENGTH(evb);
- if (buf_size < frame_bytes_remaining_)
- return STATUS_INCOMPLETE;
- for (size_t i = 0; i < buf_size; ++i) {
- buf[i] ^= frame_mask_[frame_mask_index_];
- frame_mask_index_ = (frame_mask_index_ + 1) % 4;
- }
- std::string passport;
- if (!WebSocketProxyHelper::FetchPassportAddrNamePort(
- buf, buf + frame_bytes_remaining_,
- &passport, &destaddr_, &destname_, &destport_)) {
- return STATUS_ABORT;
- }
- std::map<std::string, std::string> map;
- map["hostname"] = destname_;
- map["port"] = base::IntToString(destport_);
- map["extension_id"] = FetchExtensionIdFromOrigin(GetOrigin());
- if (!destaddr_.empty())
- map["addr"] = destaddr_;
- if (!chrome::InternalAuthVerification::VerifyPassport(
- passport, "web_socket_proxy", map)) {
- return STATUS_ABORT;
- }
-
- evbuffer_drain(evb, frame_bytes_remaining_);
- frame_bytes_remaining_ = 0;
- return STATUS_OK;
-}
-
-Conn::Status Conn::ConsumeFrameHeader(struct evbuffer* evb) {
- uint8* buf = EVBUFFER_DATA(evb);
- size_t buf_size = EVBUFFER_LENGTH(evb);
- size_t header_size = 2;
-
- if (buf_size < header_size)
- return STATUS_INCOMPLETE;
- if (buf[0] & 0x70) {
- // We are not able to handle non-zero reserved bits.
- NOTIMPLEMENTED();
- return STATUS_ABORT;
- }
- bool fin_flag = buf[0] & 0x80;
- if (!fin_flag) {
- NOTIMPLEMENTED();
- return STATUS_ABORT;
- }
- int opcode = buf[0] & 0x0f;
- switch (opcode) {
- case WS_OPCODE_TEXT:
- break;
- case WS_OPCODE_CLOSE:
- return STATUS_ABORT;
- default:
- NOTIMPLEMENTED();
- return STATUS_ABORT;
- }
-
- bool mask_flag = buf[1] & 0x80;
- if (!mask_flag) {
- // Client-to-server frames must be masked.
- return STATUS_ABORT;
- }
- frame_bytes_remaining_ = buf[1] & 0x7f;
- int extra_size = 0;
- if (frame_bytes_remaining_ == 126)
- extra_size = 2;
- else if (frame_bytes_remaining_ == 127)
- extra_size = 8;
- if (buf_size < header_size + extra_size + sizeof(frame_mask_))
- return STATUS_INCOMPLETE;
- if (extra_size)
- frame_bytes_remaining_ = ReadNetworkInteger(buf + header_size, extra_size);
- header_size += extra_size;
- memcpy(frame_mask_, buf + header_size, sizeof(frame_mask_));
- header_size += sizeof(frame_mask_);
- frame_mask_index_ = 0;
- evbuffer_drain(evb, header_size);
- return STATUS_OK;
-}
-
-Conn::Status Conn::ProcessFrameData(struct evbuffer* evb) {
- uint8* buf = EVBUFFER_DATA(evb);
- size_t buf_size = EVBUFFER_LENGTH(evb);
-
- DCHECK_GE(frame_bytes_remaining_, 1u);
- if (frame_bytes_remaining_ < buf_size)
- buf_size = frame_bytes_remaining_;
- // base64 is encoded in chunks of 4 bytes.
- buf_size = buf_size / 4 * 4;
- if (buf_size < 1)
- return STATUS_INCOMPLETE;
- switch (phase_) {
- case PHASE_INSIDE_FRAME_BASE64: {
- for (size_t i = 0; i < buf_size; ++i) {
- buf[i] ^= frame_mask_[frame_mask_index_];
- frame_mask_index_ = (frame_mask_index_ + 1) % 4;
- }
- std::string out_bytes;
- base::Base64Decode(std::string(buf, buf + buf_size), &out_bytes);
- evbuffer_drain(evb, buf_size);
- DCHECK(destchan_.write_bev());
- if (!destchan_.Write(out_bytes.c_str(), out_bytes.size()))
- return STATUS_ABORT;
- break;
- }
- case PHASE_INSIDE_FRAME_SKIP: {
- evbuffer_drain(evb, buf_size);
- break;
- }
- default: {
- return STATUS_ABORT;
- }
- }
- frame_bytes_remaining_ -= buf_size;
- return frame_bytes_remaining_ ? STATUS_INCOMPLETE : STATUS_OK;
-}
-
-bool Conn::TryConnectDest(const struct sockaddr* addr, socklen_t addrlen) {
- if (destchan_.read_fd() >= 0 || destchan_.read_bev() != NULL)
- return false;
- if (do_tls_) {
- int fd[4];
- if (pipe(fd) || pipe(fd + 2))
- return false;
- destchan_.set_read_fd(fd[0]);
- destchan_.set_write_fd(fd[3]);
- for (int i = arraysize(fd); i--;) {
- if (!SetNonBlock(fd[i]))
- return false;
- }
- destchan_.set_read_bev(bufferevent_new(
- destchan_.read_fd(),
- &OnDestchanRead, NULL, &OnDestchanError,
- evkey_));
- destchan_.set_write_bev(bufferevent_new(
- destchan_.write_fd(),
- NULL, &OnDestchanWrite, &OnDestchanError,
- evkey_));
- net::IPEndPoint endpoint;
- if (!endpoint.FromSockAddr(addr, addrlen))
- return false;
- net::AddressList addrlist(endpoint);
- net::HostPortPair host_port_pair(destname_, destport_);
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE, base::Bind(
- &SSLChan::Start, addrlist, host_port_pair, fd[2], fd[1]));
- } else {
- int sock = socket(addr->sa_family, SOCK_STREAM, 0);
- if (sock < 0)
- return false;
- destchan_.set_read_fd(sock);
- destchan_.set_write_fd(sock);
- if (!SetNonBlock(sock))
- return false;
- if (connect(sock, addr, addrlen)) {
- if (errno != EINPROGRESS)
- return false;
- }
- destchan_.set_read_bev(bufferevent_new(
- sock,
- &OnDestchanRead, &OnDestchanWrite, &OnDestchanError,
- evkey_));
- destchan_.set_write_bev(destchan_.read_bev());
- }
- if (destchan_.read_bev() == NULL || destchan_.write_bev() == NULL)
- return false;
- if (bufferevent_base_set(master_->evbase(), destchan_.read_bev()) ||
- bufferevent_base_set(master_->evbase(), destchan_.write_bev())) {
- return false;
- }
- bufferevent_setwatermark(
- destchan_.read_bev(), EV_READ, 0, WebSocketProxy::kBufferLimit);
- if (bufferevent_enable(destchan_.read_bev(), EV_READ) ||
- bufferevent_enable(destchan_.write_bev(), EV_WRITE)) {
- return false;
- }
- return true;
-}
-
-const std::string& Conn::GetOrigin() {
- return header_fields_[header_fields_["sec-websocket-version"] == "8" ?
- "sec-websocket-origin" : "origin"];
-}
-
-// static
-void Conn::OnPrimchanRead(struct bufferevent* bev, EventKey evkey) {
- Conn* cs = Conn::Get(evkey);
- if (bev == NULL ||
- cs == NULL ||
- bev != cs->primchan_.read_bev()) {
- NOTREACHED();
- return;
- }
- if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) <= 0)
- return;
- cs->master_->MarkConnImportance(cs, true);
- for (;;) {
- switch (cs->phase_) {
- case PHASE_WAIT_HANDSHAKE: {
- switch (cs->ConsumeHeader(EVBUFFER_INPUT(bev))) {
- case STATUS_OK: {
- break;
- }
- case STATUS_INCOMPLETE: {
- return;
- }
- case STATUS_ABORT:
- default: {
- cs->master_->ZapConn(cs);
- return;
- }
- }
- // Header consumed OK. Do respond.
- if (!cs->EmitHandshake(&cs->primchan_)) {
- cs->master_->ZapConn(cs);
- return;
- }
- cs->phase_ = PHASE_WAIT_DESTFRAME;
- }
- case PHASE_WAIT_DESTFRAME: {
- switch (cs->ConsumeDestframe(EVBUFFER_INPUT(bev))) {
- case STATUS_OK: {
- {
- // Unfortunately libevent as of 1.4 does not look into /etc/hosts.
- // There seems to be no easy API to perform only "local" part of
- // getaddrinfo resolution. Hence this hack for "localhost".
- if (cs->destname_ == "localhost")
- cs->destname_ = "127.0.0.1";
- }
- if (cs->destaddr_.empty())
- cs->destaddr_ = cs->destname_;
- {
- struct sockaddr_in sa;
- memset(&sa, 0, sizeof(sa));
- sa.sin_port = base::HostToNet16(cs->destport_);
- if (inet_pton(sa.sin_family = AF_INET,
- cs->destaddr_.c_str(),
- &sa.sin_addr) == 1) {
- // valid IPv4 address supplied.
- if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa))) {
- cs->phase_ = PHASE_WAIT_DESTCONNECT;
- return;
- }
- }
- }
- {
- if (cs->destaddr_.size() >= 2 &&
- cs->destaddr_[0] == '[' &&
- cs->destaddr_[cs->destaddr_.size() - 1] == ']') {
- // Literal IPv6 address in brackets.
- cs->destaddr_ =
- cs->destaddr_.substr(1, cs->destaddr_.size() - 2);
- }
- struct sockaddr_in6 sa;
- memset(&sa, 0, sizeof(sa));
- sa.sin6_port = base::HostToNet16(cs->destport_);
- if (inet_pton(sa.sin6_family = AF_INET6,
- cs->destaddr_.c_str(),
- &sa.sin6_addr) == 1) {
- // valid IPv6 address supplied.
- if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa))) {
- cs->phase_ = PHASE_WAIT_DESTCONNECT;
- return;
- }
- }
- }
- // Asynchronous DNS resolution.
- if (evdns_count_nameservers() < 1) {
- evdns_clear_nameservers_and_suspend();
- evdns_init();
- evdns_resume();
- }
- evdns_resolve_ipv4(cs->destname_.c_str(), 0,
- &OnDestResolutionIPv4, evkey);
- evdns_resolve_ipv6(cs->destname_.c_str(), 0,
- &OnDestResolutionIPv6, evkey);
- cs->phase_ = PHASE_WAIT_DESTCONNECT;
- return;
- }
- case STATUS_INCOMPLETE: {
- return;
- }
- case STATUS_ABORT:
- default: {
- cs->Shut(WS_CLOSE_DESTINATION_ERROR,
- "Incorrect destination specification in first frame");
- return;
- }
- }
- }
- case PHASE_WAIT_DESTCONNECT: {
- if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) >=
- WebSocketProxy::kBufferLimit) {
- cs->Shut(WS_CLOSE_LIMIT_VIOLATION, "Read buffer overflow");
- }
- return;
- }
- case PHASE_OUTSIDE_FRAME: {
- switch (cs->ConsumeFrameHeader(EVBUFFER_INPUT(bev))) {
- case STATUS_OK: {
- if (cs->frame_bytes_remaining_ % 4) {
- // We expect base64 encoded data (encoded in 4-bytes chunks).
- cs->Shut(WS_CLOSE_UNACCEPTABLE_DATA,
- "Frame payload size is not multiple of 4");
- return;
- }
- cs->phase_ = PHASE_INSIDE_FRAME_BASE64;
- // Process remaining data if any.
- break;
- }
- case STATUS_SKIP: {
- cs->phase_ = PHASE_INSIDE_FRAME_SKIP;
- // Process remaining data if any.
- break;
- }
- case STATUS_INCOMPLETE: {
- return;
- }
- case STATUS_ABORT:
- default: {
- cs->Shut(WS_CLOSE_PROTOCOL_ERROR, "Invalid frame header");
- return;
- }
- }
- break;
- }
- case PHASE_INSIDE_FRAME_BASE64:
- case PHASE_INSIDE_FRAME_SKIP: {
- switch (cs->ProcessFrameData(EVBUFFER_INPUT(bev))) {
- case STATUS_OK: {
- cs->phase_ = PHASE_OUTSIDE_FRAME;
- // Handle remaining data if any.
- break;
- }
- case STATUS_INCOMPLETE: {
- return;
- }
- case STATUS_ABORT:
- default: {
- cs->Shut(WS_CLOSE_UNACCEPTABLE_DATA, "Invalid frame data");
- return;
- }
- }
- break;
- }
- case PHASE_SHUT: {
- evbuffer_drain(EVBUFFER_INPUT(bev),
- EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)));
- return;
- }
- case PHASE_DEFUNCT:
- default: {
- NOTREACHED();
- cs->master_->ZapConn(cs);
- return;
- }
- }
- }
-}
-
-// static
-void Conn::OnPrimchanWrite(struct bufferevent* bev, EventKey evkey) {
- Conn* cs = Conn::Get(evkey);
- if (bev == NULL ||
- cs == NULL ||
- bev != cs->primchan_.write_bev()) {
- NOTREACHED();
- return;
- }
- // Write callback is called when low watermark is reached, 0 by default.
- cs->primchan_.set_write_pending(false);
- if (cs->phase_ >= PHASE_SHUT) {
- cs->master_->ZapConn(cs);
- return;
- }
- if (cs->phase_ > PHASE_WAIT_DESTCONNECT)
- OnDestchanRead(cs->destchan_.read_bev(), evkey);
- if (cs->phase_ >= PHASE_SHUT)
- cs->primchan_.Zap();
-}
-
-// static
-void Conn::OnPrimchanError(struct bufferevent* bev,
- short what, EventKey evkey) {
- Conn* cs = Conn::Get(evkey);
- if (bev == NULL ||
- cs == NULL ||
- (bev != cs->primchan_.read_bev() && bev != cs->primchan_.write_bev())) {
- return;
- }
- cs->primchan_.set_write_pending(false);
- if (cs->phase_ >= PHASE_SHUT)
- cs->master_->ZapConn(cs);
- else
- cs->Shut(WS_CLOSE_NORMAL, "Error reported on websocket channel");
-}
-
-// static
-void Conn::OnDestResolutionIPv4(int result, char type,
- int count, int ttl,
- void* addr_list, EventKey evkey) {
- Conn* cs = Conn::Get(evkey);
- if (cs == NULL)
- return;
- if (cs->phase_ != PHASE_WAIT_DESTCONNECT)
- return;
- if (result == DNS_ERR_NONE &&
- count >= 1 &&
- addr_list != NULL &&
- type == DNS_IPv4_A) {
- for (int i = 0; i < count; ++i) {
- struct sockaddr_in sa;
- memset(&sa, 0, sizeof(sa));
- sa.sin_family = AF_INET;
- sa.sin_port = base::HostToNet16(cs->destport_);
- DCHECK(sizeof(sa.sin_addr) == sizeof(struct in_addr));
- memcpy(&sa.sin_addr,
- static_cast<struct in_addr*>(addr_list) + i,
- sizeof(sa.sin_addr));
- if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa)))
- return;
- }
- }
- cs->destresolution_ipv4_failed_ = true;
- if (cs->destresolution_ipv4_failed_ && cs->destresolution_ipv6_failed_)
- cs->Shut(WS_CLOSE_RESOLUTION_FAILED, "DNS resolution failed");
-}
-
-// static
-void Conn::OnDestResolutionIPv6(int result, char type,
- int count, int ttl,
- void* addr_list, EventKey evkey) {
- Conn* cs = Conn::Get(evkey);
- if (cs == NULL)
- return;
- if (cs->phase_ != PHASE_WAIT_DESTCONNECT)
- return;
- if (result == DNS_ERR_NONE &&
- count >= 1 &&
- addr_list != NULL &&
- type == DNS_IPv6_AAAA) {
- for (int i = 0; i < count; ++i) {
- struct sockaddr_in6 sa;
- memset(&sa, 0, sizeof(sa));
- sa.sin6_family = AF_INET6;
- sa.sin6_port = base::HostToNet16(cs->destport_);
- DCHECK(sizeof(sa.sin6_addr) == sizeof(struct in6_addr));
- memcpy(&sa.sin6_addr,
- static_cast<struct in6_addr*>(addr_list) + i,
- sizeof(sa.sin6_addr));
- if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa)))
- return;
- }
- }
- cs->destresolution_ipv6_failed_ = true;
- if (cs->destresolution_ipv4_failed_ && cs->destresolution_ipv6_failed_)
- cs->Shut(WS_CLOSE_RESOLUTION_FAILED, "DNS resolution failed");
-}
-
-// static
-void Conn::OnDestConnectTimeout(int, short, EventKey evkey) {
- Conn* cs = Conn::Get(evkey);
- if (cs == NULL)
- return;
- if (cs->phase_ > PHASE_WAIT_DESTCONNECT)
- return;
- cs->Shut(WS_CLOSE_RESOLUTION_FAILED, "DNS resolution timeout");
-}
-
-// static
-void Conn::OnDestchanRead(struct bufferevent* bev, EventKey evkey) {
- Conn* cs = Conn::Get(evkey);
- if (bev == NULL ||
- cs == NULL ||
- bev != cs->destchan_.read_bev()) {
- NOTREACHED();
- return;
- }
- if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) <= 0)
- return;
- if (cs->primchan_.write_bev() == NULL) {
- cs->master_->ZapConn(cs);
- return;
- }
- cs->master_->MarkConnImportance(cs, true);
- std::string out_bytes;
- base::Base64Encode(
- std::string(
- static_cast<const char*>(static_cast<void*>(
- EVBUFFER_DATA(EVBUFFER_INPUT(bev)))),
- EVBUFFER_LENGTH(EVBUFFER_INPUT(bev))),
- &out_bytes);
- evbuffer_drain(EVBUFFER_INPUT(bev), EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)));
- if (!cs->EmitFrame(&cs->primchan_, WS_OPCODE_TEXT,
- out_bytes.c_str(), out_bytes.size())) {
- cs->Shut(WS_CLOSE_UNEXPECTED, "Failed to write websocket frame");
- }
-}
-
-// static
-void Conn::OnDestchanWrite(struct bufferevent* bev, EventKey evkey) {
- Conn* cs = Conn::Get(evkey);
- if (bev == NULL ||
- cs == NULL ||
- bev != cs->destchan_.write_bev()) {
- NOTREACHED();
- return;
- }
- // Write callback is called when low watermark is reached, 0 by default.
- cs->destchan_.set_write_pending(false);
- if (cs->phase_ == PHASE_WAIT_DESTCONNECT)
- cs->phase_ = PHASE_OUTSIDE_FRAME;
- if (cs->phase_ < PHASE_SHUT)
- OnPrimchanRead(cs->primchan_.read_bev(), evkey);
- else
- cs->destchan_.Zap();
-}
-
-// static
-void Conn::OnDestchanError(struct bufferevent* bev,
- short what, EventKey evkey) {
- Conn* cs = Conn::Get(evkey);
- if (bev == NULL ||
- cs == NULL ||
- (bev != cs->destchan_.read_bev() && bev != cs->destchan_.write_bev())) {
- return;
- }
- cs->destchan_.set_write_pending(false);
- if (cs->phase_ >= PHASE_SHUT)
- cs->master_->ZapConn(cs);
- else
- cs->Shut(WS_CLOSE_DESTINATION_ERROR,
- "Failure reported on destination channel");
-}
-
-// static
-Conn::EventKey Conn::last_evkey_ = 0;
-
-// static
-base::LazyInstance<Conn::EventKeyMap>::Leaky
- Conn::evkey_map_ = LAZY_INSTANCE_INITIALIZER;
-
-} // namespace
-
-WebSocketProxy::WebSocketProxy() : impl_(new Serv()) {
-}
-
-WebSocketProxy::~WebSocketProxy() {
- delete static_cast<Serv*>(impl_);
- impl_ = NULL;
-}
-
-void WebSocketProxy::Run() {
- static_cast<Serv*>(impl_)->Run();
-}
-
-void WebSocketProxy::Shutdown() {
- static_cast<Serv*>(impl_)->Shutdown();
-}
-
-void WebSocketProxy::OnNetworkChange() {
- static_cast<Serv*>(impl_)->OnNetworkChange();
-}
-
-} // namespace chromeos
« no previous file with comments | « chrome/browser/chromeos/web_socket_proxy.h ('k') | chrome/browser/chromeos/web_socket_proxy_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698