Skip to content

Commit

Permalink
Bind open firewall ports to visible application windows.
Browse files Browse the repository at this point in the history
To prevent an application from continuing to listen for TCP connections
or incoming UDP packets when the user is not aware that it is still
running this patch observes the state of its associated AppWindows and
opens and closes ports as necessary. The socket itself is left open so
that state is not lost.

BUG=435404

Review URL: https://codereview.chromium.org/1022663003

Cr-Commit-Position: refs/heads/master@{#321911}
  • Loading branch information
reillyeon authored and Commit bot committed Mar 24, 2015
1 parent b785963 commit 8d0ba1d
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 51 deletions.
3 changes: 0 additions & 3 deletions extensions/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,6 @@ test("extensions_unittests") {
# TODO(rockot): DisplayInfoProvider::Create() is only implemented in Chrome
# and app_shell. This is wrong.
"shell/browser/shell_display_info_provider.cc",

# TODO(rockot): See above, but the header is in //components.
"shell/browser/shell_web_contents_modal_dialog_manager.cc",
]

deps = [
Expand Down
166 changes: 166 additions & 0 deletions extensions/browser/api/socket/app_firewall_hole_manager.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright 2015 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 "extensions/browser/api/socket/app_firewall_hole_manager.h"

#include <utility>

#include "base/bind.h"
#include "base/stl_util.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/app_window/app_window.h"

using chromeos::FirewallHole;
using content::BrowserContext;

namespace extensions {

namespace {

class AppFirewallHoleManagerFactory : public BrowserContextKeyedServiceFactory {
public:
static AppFirewallHoleManager* GetForBrowserContext(BrowserContext* context,
bool create) {
return static_cast<AppFirewallHoleManager*>(
GetInstance()->GetServiceForBrowserContext(context, create));
}

static AppFirewallHoleManagerFactory* GetInstance() {
return Singleton<AppFirewallHoleManagerFactory>::get();
}

AppFirewallHoleManagerFactory()
: BrowserContextKeyedServiceFactory(
"AppFirewallHoleManager",
BrowserContextDependencyManager::GetInstance()) {
DependsOn(AppWindowRegistry::Factory::GetInstance());
}

~AppFirewallHoleManagerFactory() override {}

private:
// BrowserContextKeyedServiceFactory
KeyedService* BuildServiceInstanceFor(
BrowserContext* context) const override {
return new AppFirewallHoleManager(context);
}

BrowserContext* GetBrowserContextToUse(
BrowserContext* context) const override {
return context;
}
};

bool HasVisibleAppWindows(BrowserContext* context,
const std::string& extension_id) {
AppWindowRegistry* registry = AppWindowRegistry::Get(context);

for (const AppWindow* window : registry->GetAppWindowsForApp(extension_id)) {
if (!window->is_hidden()) {
return true;
}
}

return false;
}

} // namespace

AppFirewallHole::~AppFirewallHole() {
manager_->Close(this);
}

AppFirewallHole::AppFirewallHole(AppFirewallHoleManager* manager,
PortType type,
uint16_t port,
const std::string& extension_id)
: type_(type),
port_(port),
extension_id_(extension_id),
manager_(manager),
weak_factory_(this) {
}

void AppFirewallHole::SetVisible(bool app_visible) {
app_visible_ = app_visible;
if (app_visible_) {
if (!firewall_hole_) {
FirewallHole::Open(type_, port_, "" /* all interfaces */,
base::Bind(&AppFirewallHole::OnFirewallHoleOpened,
weak_factory_.GetWeakPtr()));
}
} else {
firewall_hole_.reset(nullptr);
}
}

void AppFirewallHole::OnFirewallHoleOpened(
scoped_ptr<FirewallHole> firewall_hole) {
if (app_visible_) {
DCHECK(!firewall_hole_);
firewall_hole_ = firewall_hole.Pass();
}
}

AppFirewallHoleManager::AppFirewallHoleManager(BrowserContext* context)
: context_(context), observer_(this) {
observer_.Add(AppWindowRegistry::Get(context));
}

AppFirewallHoleManager::~AppFirewallHoleManager() {
STLDeleteValues(&tracked_holes_);
}

AppFirewallHoleManager* AppFirewallHoleManager::Get(BrowserContext* context) {
return AppFirewallHoleManagerFactory::GetForBrowserContext(context, true);
}

scoped_ptr<AppFirewallHole> AppFirewallHoleManager::Open(
AppFirewallHole::PortType type,
uint16_t port,
const std::string& extension_id) {
scoped_ptr<AppFirewallHole> hole(
new AppFirewallHole(this, type, port, extension_id));
tracked_holes_.insert(std::make_pair(extension_id, hole.get()));
if (HasVisibleAppWindows(context_, extension_id)) {
hole->SetVisible(true);
}
return hole.Pass();
}

void AppFirewallHoleManager::Close(AppFirewallHole* hole) {
auto range = tracked_holes_.equal_range(hole->extension_id());
for (auto iter = range.first; iter != range.second; ++iter) {
if (iter->second == hole) {
tracked_holes_.erase(iter);
return;
}
}
NOTREACHED();
}

void AppFirewallHoleManager::OnAppWindowRemoved(AppWindow* app_window) {
OnAppWindowHidden(app_window);
}

void AppFirewallHoleManager::OnAppWindowHidden(AppWindow* app_window) {
DCHECK(context_ == app_window->browser_context());
if (!HasVisibleAppWindows(context_, app_window->extension_id())) {
const auto& range = tracked_holes_.equal_range(app_window->extension_id());
for (auto iter = range.first; iter != range.second; ++iter) {
iter->second->SetVisible(false);
}
}
}

void AppFirewallHoleManager::OnAppWindowShown(AppWindow* app_window,
bool was_hidden) {
const auto& range = tracked_holes_.equal_range(app_window->extension_id());
for (auto iter = range.first; iter != range.second; ++iter) {
iter->second->SetVisible(true);
}
}

} // namespace extensions
96 changes: 96 additions & 0 deletions extensions/browser/api/socket/app_firewall_hole_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2015 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.

#ifndef EXTENSIONS_BROWSER_API_SOCKET_APP_FIREWALL_HOLE_MANAGER_H_
#define EXTENSIONS_BROWSER_API_SOCKET_APP_FIREWALL_HOLE_MANAGER_H_

#include <map>

#include "base/scoped_observer.h"
#include "chromeos/network/firewall_hole.h"
#include "extensions/browser/app_window/app_window_registry.h"

namespace content {
class BrowserContext;
}

namespace extensions {

class AppFirewallHoleManager;

// Represents an open port in the system firewall that will be opened and closed
// automatically when the application has a visible window or not. The hole is
// closed on destruction.
class AppFirewallHole {
public:
typedef chromeos::FirewallHole::PortType PortType;

~AppFirewallHole();

PortType type() const { return type_; }
uint16_t port() const { return port_; }
const std::string& extension_id() const { return extension_id_; }

private:
friend class AppFirewallHoleManager;

AppFirewallHole(AppFirewallHoleManager* manager,
PortType type,
uint16_t port,
const std::string& extension_id);

void SetVisible(bool app_visible);
void OnFirewallHoleOpened(scoped_ptr<chromeos::FirewallHole> firewall_hole);

PortType type_;
uint16_t port_;
std::string extension_id_;
bool app_visible_ = false;

// This object is destroyed when the AppFirewallHoleManager that owns it is
// destroyed and so a raw pointer is okay here.
AppFirewallHoleManager* manager_;

// This will hold the FirewallHole object if one is opened.
scoped_ptr<chromeos::FirewallHole> firewall_hole_;

base::WeakPtrFactory<AppFirewallHole> weak_factory_;
};

// Tracks ports in the system firewall opened by an application so that they
// may be automatically opened and closed only when the application has a
// visible window.
class AppFirewallHoleManager : public KeyedService,
public AppWindowRegistry::Observer {
public:
explicit AppFirewallHoleManager(content::BrowserContext* context);
~AppFirewallHoleManager() override;

// Returns the instance for a given browser context, or NULL if none.
static AppFirewallHoleManager* Get(content::BrowserContext* context);

// Takes ownership of the AppFirewallHole and will open a port on the system
// firewall if the associated application is currently visible.
scoped_ptr<AppFirewallHole> Open(AppFirewallHole::PortType type,
uint16_t port,
const std::string& extension_id);

private:
friend class AppFirewallHole;

void Close(AppFirewallHole* hole);

// AppWindowRegistry::Observer
void OnAppWindowRemoved(AppWindow* app_window) override;
void OnAppWindowHidden(AppWindow* app_window) override;
void OnAppWindowShown(AppWindow* app_window, bool was_hidden) override;

content::BrowserContext* context_;
ScopedObserver<AppWindowRegistry, AppWindowRegistry::Observer> observer_;
std::multimap<std::string, AppFirewallHole*> tracked_holes_;
};

} // namespace extensions

#endif // EXTENSIONS_BROWSER_API_SOCKET_APP_FIREWALL_HOLE_MANAGER_H_
10 changes: 6 additions & 4 deletions extensions/browser/api/socket/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#include "net/socket/tcp_client_socket.h"

#if defined(OS_CHROMEOS)
#include "chromeos/network/firewall_hole.h"
#include "extensions/browser/api/socket/app_firewall_hole_manager.h"
#endif // OS_CHROMEOS

namespace net {
Expand Down Expand Up @@ -60,8 +60,10 @@ class Socket : public ApiResource {
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());
void set_firewall_hole(
scoped_ptr<AppFirewallHole, content::BrowserThread::DeleteOnUIThread>
firewall_hole) {
firewall_hole_ = firewall_hole.Pass();
}
#endif // OS_CHROMEOS

Expand Down Expand Up @@ -148,7 +150,7 @@ class Socket : public ApiResource {

#if defined(OS_CHROMEOS)
// Represents a hole punched in the system firewall for this socket.
scoped_ptr<chromeos::FirewallHole, content::BrowserThread::DeleteOnUIThread>
scoped_ptr<AppFirewallHole, content::BrowserThread::DeleteOnUIThread>
firewall_hole_;
#endif // OS_CHROMEOS
};
Expand Down
Loading

0 comments on commit 8d0ba1d

Please sign in to comment.