summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorreillyg <reillyg@chromium.org>2015-03-13 12:41:44 -0700
committerCommit bot <commit-bot@chromium.org>2015-03-13 19:43:02 +0000
commitbed8fdc7a0995427e0af24e63693ad790920b612 (patch)
treed5371fd6a4aa2f7cf1a33bc18a6e1eaea34bda41 /extensions
parentfee295ea8f4d8bac532eae149029362dccfb4800 (diff)
downloadchromium_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')
-rw-r--r--extensions/browser/api/socket/socket.h16
-rw-r--r--extensions/browser/api/socket/socket_api.cc165
-rw-r--r--extensions/browser/api/socket/socket_api.h20
-rw-r--r--extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc15
-rw-r--r--extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h2
-rw-r--r--extensions/browser/api/sockets_udp/sockets_udp_api.cc13
-rw-r--r--extensions/browser/api/sockets_udp/sockets_udp_api.h2
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, &param)) {
- 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, &param)) {
+ 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, &param)) {
- 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, &param)) {
+ 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_;