diff options
author | reillyg <reillyg@chromium.org> | 2015-03-13 12:41:44 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-03-13 19:43:02 +0000 |
commit | bed8fdc7a0995427e0af24e63693ad790920b612 (patch) | |
tree | d5371fd6a4aa2f7cf1a33bc18a6e1eaea34bda41 /extensions | |
parent | fee295ea8f4d8bac532eae149029362dccfb4800 (diff) | |
download | chromium_src-bed8fdc7a0995427e0af24e63693ad790920b612.zip chromium_src-bed8fdc7a0995427e0af24e63693ad790920b612.tar.gz chromium_src-bed8fdc7a0995427e0af24e63693ad790920b612.tar.bz2 |
Open a firewall hole when a TCP server or UDP socket is bound.
This patch implements firewall hole punching when a Chrome App uses
the chrome.socket, chrome.sockets.tcpServer or chrome.sockets.udp APIs
to open a socket to receive packets from the network. Sockets that only
listen on the local loopback device do not open a port.
A primitive FirewallHole class is added that could be reused by the
Pepper sockets API as well.
BUG=435404
Review URL: https://codereview.chromium.org/965613002
Cr-Commit-Position: refs/heads/master@{#320552}
Diffstat (limited to 'extensions')
7 files changed, 184 insertions, 49 deletions
diff --git a/extensions/browser/api/socket/socket.h b/extensions/browser/api/socket/socket.h index e2287fe..5987915 100644 --- a/extensions/browser/api/socket/socket.h +++ b/extensions/browser/api/socket/socket.h @@ -18,6 +18,10 @@ #include "net/base/ip_endpoint.h" #include "net/socket/tcp_client_socket.h" +#if defined(OS_CHROMEOS) +#include "chromeos/network/firewall_hole.h" +#endif // OS_CHROMEOS + namespace net { class AddressList; class IPEndPoint; @@ -55,6 +59,12 @@ class Socket : public ApiResource { // unbracketed. void set_hostname(const std::string& hostname) { hostname_ = hostname; } +#if defined(OS_CHROMEOS) + void set_firewall_hole(scoped_ptr<chromeos::FirewallHole> firewall_hole) { + firewall_hole_.reset(firewall_hole.release()); + } +#endif // OS_CHROMEOS + // Note: |address| contains the resolved IP address, not the hostname of // the remote endpoint. In order to upgrade this socket to TLS, callers // must also supply the hostname of the endpoint via set_hostname(). @@ -135,6 +145,12 @@ class Socket : public ApiResource { }; std::queue<WriteRequest> write_queue_; scoped_refptr<net::IOBuffer> io_buffer_write_; + +#if defined(OS_CHROMEOS) + // Represents a hole punched in the system firewall for this socket. + scoped_ptr<chromeos::FirewallHole, content::BrowserThread::DeleteOnUIThread> + firewall_hole_; +#endif // OS_CHROMEOS }; } // namespace extensions diff --git a/extensions/browser/api/socket/socket_api.cc b/extensions/browser/api/socket/socket_api.cc index dca379a..dae0c52 100644 --- a/extensions/browser/api/socket/socket_api.cc +++ b/extensions/browser/api/socket/socket_api.cc @@ -29,6 +29,13 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" +#if defined(OS_CHROMEOS) +#include "base/command_line.h" +#include "chromeos/chromeos_switches.h" +#include "chromeos/network/firewall_hole.h" +#include "content/public/browser/browser_thread.h" +#endif // OS_CHROMEOS + namespace extensions { using content::SocketPermissionRequest; @@ -52,6 +59,10 @@ const char kSocketNotConnectedError[] = "Socket not connected"; const char kWildcardAddress[] = "*"; const uint16 kWildcardPort = 0; +#if defined(OS_CHROMEOS) +const char kFirewallFailure[] = "Failed to open firewall port"; +#endif // OS_CHROMEOS + SocketAsyncApiFunction::SocketAsyncApiFunction() {} SocketAsyncApiFunction::~SocketAsyncApiFunction() {} @@ -90,6 +101,77 @@ void SocketAsyncApiFunction::RemoveSocket(int api_resource_id) { manager_->Remove(extension_->id(), api_resource_id); } +void SocketAsyncApiFunction::OpenFirewallHole(const std::string& address, + int socket_id, + Socket* socket) { +#if defined(OS_CHROMEOS) + if (!net::IsLocalhost(address) && + base::CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kEnableFirewallHolePunching)) { + net::IPEndPoint local_address; + if (!socket->GetLocalAddress(&local_address)) { + NOTREACHED() << "Cannot get address of recently bound socket."; + error_ = kFirewallFailure; + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); + return; + } + + chromeos::FirewallHole::PortType port_type; + if (socket->GetSocketType() == Socket::TYPE_TCP) { + port_type = chromeos::FirewallHole::PortType::TCP; + } else { + port_type = chromeos::FirewallHole::PortType::UDP; + } + + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind( + &chromeos::FirewallHole::Open, port_type, local_address.port(), + "" /* all interfaces */, + base::Bind(&SocketAsyncApiFunction::OnFirewallHoleOpenedOnUIThread, + this, socket_id))); + return; + } +#endif + AsyncWorkCompleted(); +} + +#if defined(OS_CHROMEOS) + +void SocketAsyncApiFunction::OnFirewallHoleOpenedOnUIThread( + int socket_id, + scoped_ptr<chromeos::FirewallHole> hole) { + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&SocketAsyncApiFunction::OnFirewallHoleOpened, this, socket_id, + base::Passed(&hole))); +} + +void SocketAsyncApiFunction::OnFirewallHoleOpened( + int socket_id, + scoped_ptr<chromeos::FirewallHole> hole) { + if (!hole) { + error_ = kFirewallFailure; + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); + return; + } + + Socket* socket = GetSocket(socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); + return; + } + + socket->set_firewall_hole(hole.Pass()); + AsyncWorkCompleted(); +} + +#endif // OS_CHROMEOS + SocketExtensionWithDnsLookupFunction::SocketExtensionWithDnsLookupFunction() : resource_context_(NULL), request_handle_(new net::HostResolver::RequestHandle), @@ -294,33 +376,41 @@ bool SocketBindFunction::Prepare() { return true; } -void SocketBindFunction::Work() { - int result = -1; +void SocketBindFunction::AsyncWorkStart() { Socket* socket = GetSocket(socket_id_); - if (!socket) { error_ = kSocketNotFoundError; - SetResult(new base::FundamentalValue(result)); + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); return; } - if (socket->GetSocketType() == Socket::TYPE_UDP) { - SocketPermission::CheckParam param( - SocketPermissionRequest::UDP_BIND, address_, port_); - if (!extension()->permissions_data()->CheckAPIPermissionWithParam( - APIPermission::kSocket, ¶m)) { - error_ = kPermissionError; - SetResult(new base::FundamentalValue(result)); - return; - } - } else if (socket->GetSocketType() == Socket::TYPE_TCP) { + if (socket->GetSocketType() == Socket::TYPE_TCP) { error_ = kTCPSocketBindError; - SetResult(new base::FundamentalValue(result)); + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); + return; + } + + CHECK(socket->GetSocketType() == Socket::TYPE_UDP); + SocketPermission::CheckParam param(SocketPermissionRequest::UDP_BIND, + address_, port_); + if (!extension()->permissions_data()->CheckAPIPermissionWithParam( + APIPermission::kSocket, ¶m)) { + error_ = kPermissionError; + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); return; } - result = socket->Bind(address_, port_); + int result = socket->Bind(address_, port_); SetResult(new base::FundamentalValue(result)); + if (result != net::OK) { + AsyncWorkCompleted(); + return; + } + + OpenFirewallHole(address_, socket_id_, socket); } SocketListenFunction::SocketListenFunction() {} @@ -333,30 +423,35 @@ bool SocketListenFunction::Prepare() { return true; } -void SocketListenFunction::Work() { - int result = -1; - +void SocketListenFunction::AsyncWorkStart() { Socket* socket = GetSocket(params_->socket_id); - if (socket) { - SocketPermission::CheckParam param( - SocketPermissionRequest::TCP_LISTEN, params_->address, params_->port); - if (!extension()->permissions_data()->CheckAPIPermissionWithParam( - APIPermission::kSocket, ¶m)) { - error_ = kPermissionError; - SetResult(new base::FundamentalValue(result)); - return; - } - - result = - socket->Listen(params_->address, - params_->port, - params_->backlog.get() ? *params_->backlog.get() : 5, - &error_); - } else { + if (!socket) { error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); + return; + } + + SocketPermission::CheckParam param(SocketPermissionRequest::TCP_LISTEN, + params_->address, params_->port); + if (!extension()->permissions_data()->CheckAPIPermissionWithParam( + APIPermission::kSocket, ¶m)) { + error_ = kPermissionError; + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); + return; } + int result = socket->Listen( + params_->address, params_->port, + params_->backlog.get() ? *params_->backlog.get() : 5, &error_); SetResult(new base::FundamentalValue(result)); + if (result != net::OK) { + AsyncWorkCompleted(); + return; + } + + OpenFirewallHole(params_->address, params_->socket_id, socket); } SocketAcceptFunction::SocketAcceptFunction() {} diff --git a/extensions/browser/api/socket/socket_api.h b/extensions/browser/api/socket/socket_api.h index 12f4557..2500dd0 100644 --- a/extensions/browser/api/socket/socket_api.h +++ b/extensions/browser/api/socket/socket_api.h @@ -17,6 +17,10 @@ #include "net/dns/host_resolver.h" #include "net/socket/tcp_client_socket.h" +namespace chromeos { +class FirewallHole; +} + namespace content { class BrowserContext; class ResourceContext; @@ -118,7 +122,19 @@ class SocketAsyncApiFunction : public AsyncApiFunction { void RemoveSocket(int api_resource_id); base::hash_set<int>* GetSocketIds(); + // Only implemented on Chrome OS. + void OpenFirewallHole(const std::string& address, + int socket_id, + Socket* socket); + private: +#if defined(OS_CHROMEOS) + void OnFirewallHoleOpenedOnUIThread(int socket_id, + scoped_ptr<chromeos::FirewallHole> hole); + void OnFirewallHoleOpened(int socket_id, + scoped_ptr<chromeos::FirewallHole> hole); +#endif // OS_CHROMEOS + scoped_ptr<SocketResourceManagerInterface> manager_; }; @@ -230,7 +246,7 @@ class SocketBindFunction : public SocketAsyncApiFunction { // AsyncApiFunction: bool Prepare() override; - void Work() override; + void AsyncWorkStart() override; private: int socket_id_; @@ -249,7 +265,7 @@ class SocketListenFunction : public SocketAsyncApiFunction { // AsyncApiFunction: bool Prepare() override; - void Work() override; + void AsyncWorkStart() override; private: scoped_ptr<core_api::socket::Listen::Params> params_; diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc index a55d0b5..a4a1dca 100644 --- a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc +++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc @@ -176,10 +176,11 @@ bool SocketsTcpServerListenFunction::Prepare() { return socket_event_dispatcher_ != NULL; } -void SocketsTcpServerListenFunction::Work() { +void SocketsTcpServerListenFunction::AsyncWorkStart() { ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); if (!socket) { error_ = kSocketNotFoundError; + AsyncWorkCompleted(); return; } @@ -187,6 +188,7 @@ void SocketsTcpServerListenFunction::Work() { SocketPermissionRequest::TCP_LISTEN, params_->address, params_->port); if (!SocketsManifestData::CheckRequest(extension(), param)) { error_ = kPermissionError; + AsyncWorkCompleted(); return; } @@ -195,16 +197,17 @@ void SocketsTcpServerListenFunction::Work() { params_->port, params_->backlog.get() ? *params_->backlog.get() : kDefaultListenBacklog, &error_); - - if (net_result != net::OK) - error_ = net::ErrorToString(net_result); - + results_ = sockets_tcp_server::Listen::Results::Create(net_result); if (net_result == net::OK) { socket_event_dispatcher_->OnServerSocketListen(extension_->id(), params_->socket_id); + } else { + error_ = net::ErrorToString(net_result); + AsyncWorkCompleted(); + return; } - results_ = sockets_tcp_server::Listen::Results::Create(net_result); + OpenFirewallHole(params_->address, params_->socket_id, socket); } SocketsTcpServerDisconnectFunction::SocketsTcpServerDisconnectFunction() {} diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h index f13f083..531fd2c 100644 --- a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h +++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h @@ -94,7 +94,7 @@ class SocketsTcpServerListenFunction : public TCPServerSocketAsyncApiFunction { // AsyncApiFunction: bool Prepare() override; - void Work() override; + void AsyncWorkStart() override; private: scoped_ptr<sockets_tcp_server::Listen::Params> params_; diff --git a/extensions/browser/api/sockets_udp/sockets_udp_api.cc b/extensions/browser/api/sockets_udp/sockets_udp_api.cc index d213ffd..b269aa8 100644 --- a/extensions/browser/api/sockets_udp/sockets_udp_api.cc +++ b/extensions/browser/api/sockets_udp/sockets_udp_api.cc @@ -185,10 +185,11 @@ bool SocketsUdpBindFunction::Prepare() { return socket_event_dispatcher_ != NULL; } -void SocketsUdpBindFunction::Work() { +void SocketsUdpBindFunction::AsyncWorkStart() { ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); if (!socket) { error_ = kSocketNotFoundError; + AsyncWorkCompleted(); return; } @@ -196,18 +197,22 @@ void SocketsUdpBindFunction::Work() { SocketPermissionRequest::UDP_BIND, params_->address, params_->port); if (!SocketsManifestData::CheckRequest(extension(), param)) { error_ = kPermissionError; + AsyncWorkCompleted(); return; } int net_result = socket->Bind(params_->address, params_->port); + results_ = sockets_udp::Bind::Results::Create(net_result); if (net_result == net::OK) { socket_event_dispatcher_->OnSocketBind(extension_->id(), params_->socket_id); + } else { + error_ = net::ErrorToString(net_result); + AsyncWorkCompleted(); + return; } - if (net_result != net::OK) - error_ = net::ErrorToString(net_result); - results_ = sockets_udp::Bind::Results::Create(net_result); + OpenFirewallHole(params_->address, params_->socket_id, socket); } SocketsUdpSendFunction::SocketsUdpSendFunction() : io_buffer_size_(0) {} diff --git a/extensions/browser/api/sockets_udp/sockets_udp_api.h b/extensions/browser/api/sockets_udp/sockets_udp_api.h index 32e0c4c..20fd7e6 100644 --- a/extensions/browser/api/sockets_udp/sockets_udp_api.h +++ b/extensions/browser/api/sockets_udp/sockets_udp_api.h @@ -102,7 +102,7 @@ class SocketsUdpBindFunction : public UDPSocketAsyncApiFunction { // AsyncApiFunction: bool Prepare() override; - void Work() override; + void AsyncWorkStart() override; private: scoped_ptr<sockets_udp::Bind::Params> params_; |