// 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 "content/browser/devtools/tethering_handler.h" #include "base/bind.h" #include "base/callback.h" #include "base/stl_util.h" #include "base/values.h" #include "content/browser/devtools/devtools_http_handler_impl.h" #include "content/public/browser/devtools_client_host.h" #include "content/public/browser/devtools_http_handler_delegate.h" #include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/net_log.h" #include "net/socket/stream_listen_socket.h" #include "net/socket/stream_socket.h" #include "net/socket/tcp_server_socket.h" namespace content { namespace { const char kTetheringBind[] = "Tethering.bind"; const char kTetheringUnbind[] = "Tethering.unbind"; const char kTetheringAccepted[] = "Tethering.accepted"; const char kPortParam[] = "port"; const char kConnectionIdParam[] = "connectionId"; const char kLocalhost[] = "127.0.0.1"; const int kListenBacklog = 5; const int kBufferSize = 16 * 1024; const int kMinTetheringPort = 5000; const int kMaxTetheringPort = 10000; class SocketPump : public net::StreamListenSocket::Delegate { public: SocketPump(DevToolsHttpHandlerDelegate* delegate, net::StreamSocket* client_socket) : client_socket_(client_socket), delegate_(delegate), wire_buffer_size_(0), pending_destruction_(false) { } std::string Init() { std::string channel_name; server_socket_ = delegate_->CreateSocketForTethering(this, &channel_name); if (!server_socket_.get() || channel_name.empty()) SelfDestruct(); return channel_name; } virtual ~SocketPump() { } private: virtual void DidAccept(net::StreamListenSocket* server, scoped_ptr<net::StreamListenSocket> socket) OVERRIDE { if (accepted_socket_.get()) return; buffer_ = new net::IOBuffer(kBufferSize); wire_buffer_ = new net::GrowableIOBuffer(); wire_buffer_->SetCapacity(kBufferSize); accepted_socket_ = socket.Pass(); int result = client_socket_->Read( buffer_.get(), kBufferSize, base::Bind(&SocketPump::OnClientRead, base::Unretained(this))); if (result != net::ERR_IO_PENDING) OnClientRead(result); } virtual void DidRead(net::StreamListenSocket* socket, const char* data, int len) OVERRIDE { int old_size = wire_buffer_size_; wire_buffer_size_ += len; while (wire_buffer_->capacity() < wire_buffer_size_) wire_buffer_->SetCapacity(wire_buffer_->capacity() * 2); memcpy(wire_buffer_->StartOfBuffer() + old_size, data, len); if (old_size != wire_buffer_->offset()) return; OnClientWrite(0); } virtual void DidClose(net::StreamListenSocket* socket) OVERRIDE { SelfDestruct(); } void OnClientRead(int result) { if (result <= 0) { SelfDestruct(); return; } accepted_socket_->Send(buffer_->data(), result); result = client_socket_->Read( buffer_.get(), kBufferSize, base::Bind(&SocketPump::OnClientRead, base::Unretained(this))); if (result != net::ERR_IO_PENDING) OnClientRead(result); } void OnClientWrite(int result) { if (result < 0) SelfDestruct(); wire_buffer_->set_offset(wire_buffer_->offset() + result); int remaining = wire_buffer_size_ - wire_buffer_->offset(); if (remaining == 0) { if (pending_destruction_) SelfDestruct(); return; } if (remaining > kBufferSize) remaining = kBufferSize; scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(remaining); memcpy(buffer->data(), wire_buffer_->data(), remaining); result = client_socket_->Write( buffer.get(), remaining, base::Bind(&SocketPump::OnClientWrite, base::Unretained(this))); // Shrink buffer int offset = wire_buffer_->offset(); if (offset > kBufferSize) { memcpy(wire_buffer_->StartOfBuffer(), wire_buffer_->data(), wire_buffer_size_ - offset); wire_buffer_size_ -= offset; wire_buffer_->set_offset(0); } if (result != net::ERR_IO_PENDING) OnClientWrite(result); return; } void SelfDestruct() { if (wire_buffer_->offset() != wire_buffer_size_) { pending_destruction_ = true; return; } delete this; } private: scoped_ptr<net::StreamSocket> client_socket_; scoped_ptr<net::StreamListenSocket> server_socket_; scoped_ptr<net::StreamListenSocket> accepted_socket_; scoped_refptr<net::IOBuffer> buffer_; scoped_refptr<net::GrowableIOBuffer> wire_buffer_; DevToolsHttpHandlerDelegate* delegate_; int wire_buffer_size_; bool pending_destruction_; }; } // namespace const char TetheringHandler::kDomain[] = "Tethering"; class TetheringHandler::BoundSocket { public: BoundSocket(TetheringHandler* handler, DevToolsHttpHandlerDelegate* delegate) : handler_(handler), delegate_(delegate), socket_(new net::TCPServerSocket(NULL, net::NetLog::Source())), port_(0) { } virtual ~BoundSocket() { } bool Listen(int port) { port_ = port; net::IPAddressNumber ip_number; if (!net::ParseIPLiteralToNumber(kLocalhost, &ip_number)) return false; net::IPEndPoint end_point(ip_number, port); int result = socket_->Listen(end_point, kListenBacklog); if (result < 0) return false; net::IPEndPoint local_address; result = socket_->GetLocalAddress(&local_address); if (result < 0) return false; DoAccept(); return true; } private: typedef std::map<net::IPEndPoint, net::StreamSocket*> AcceptedSocketsMap; void DoAccept() { while (true) { int result = socket_->Accept( &accept_socket_, base::Bind(&BoundSocket::OnAccepted, base::Unretained(this))); if (result == net::ERR_IO_PENDING) break; else HandleAcceptResult(result); } } void OnAccepted(int result) { HandleAcceptResult(result); if (result == net::OK) DoAccept(); } void HandleAcceptResult(int result) { if (result != net::OK) return; SocketPump* pump = new SocketPump(delegate_, accept_socket_.release()); std::string name = pump->Init(); if (!name.empty()) handler_->Accepted(port_, name); } TetheringHandler* handler_; DevToolsHttpHandlerDelegate* delegate_; scoped_ptr<net::ServerSocket> socket_; scoped_ptr<net::StreamSocket> accept_socket_; int port_; }; TetheringHandler::TetheringHandler(DevToolsHttpHandlerDelegate* delegate) : delegate_(delegate) { RegisterCommandHandler(kTetheringBind, base::Bind(&TetheringHandler::OnBind, base::Unretained(this))); RegisterCommandHandler(kTetheringUnbind, base::Bind(&TetheringHandler::OnUnbind, base::Unretained(this))); } TetheringHandler::~TetheringHandler() { STLDeleteContainerPairSecondPointers(bound_sockets_.begin(), bound_sockets_.end()); } void TetheringHandler::Accepted(int port, const std::string& name) { base::DictionaryValue* params = new base::DictionaryValue(); params->SetInteger(kPortParam, port); params->SetString(kConnectionIdParam, name); SendNotification(kTetheringAccepted, params); } static int GetPort(scoped_refptr<DevToolsProtocol::Command> command) { base::DictionaryValue* params = command->params(); int port = 0; if (!params || !params->GetInteger(kPortParam, &port) || port < kMinTetheringPort || port > kMaxTetheringPort) return 0; return port; } scoped_refptr<DevToolsProtocol::Response> TetheringHandler::OnBind(scoped_refptr<DevToolsProtocol::Command> command) { int port = GetPort(command); if (port == 0) return command->InvalidParamResponse(kPortParam); if (bound_sockets_.find(port) != bound_sockets_.end()) return command->InternalErrorResponse("Port already bound"); scoped_ptr<BoundSocket> bound_socket(new BoundSocket(this, delegate_)); if (!bound_socket->Listen(port)) return command->InternalErrorResponse("Could not bind port"); bound_sockets_[port] = bound_socket.release(); return command->SuccessResponse(NULL); } scoped_refptr<DevToolsProtocol::Response> TetheringHandler::OnUnbind(scoped_refptr<DevToolsProtocol::Command> command) { int port = GetPort(command); if (port == 0) return command->InvalidParamResponse(kPortParam); BoundSockets::iterator it = bound_sockets_.find(port); if (it == bound_sockets_.end()) return command->InternalErrorResponse("Port is not bound"); delete it->second; bound_sockets_.erase(it); return command->SuccessResponse(NULL); } } // namespace content