summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorszym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-16 18:10:53 +0000
committerszym@chromium.org <szym@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-16 18:10:53 +0000
commit05aad32d741012a173ea8f1a4b5072dabe7c0188 (patch)
treef9b8535284f0503ef96bac3a451f995b5fcd508f
parent1a92b28469774982203a8c88be0bb49d8275cf68 (diff)
downloadchromium_src-05aad32d741012a173ea8f1a4b5072dabe7c0188.zip
chromium_src-05aad32d741012a173ea8f1a4b5072dabe7c0188.tar.gz
chromium_src-05aad32d741012a173ea8f1a4b5072dabe7c0188.tar.bz2
[net/dns] Isolate DnsConfigWatcher from DnsConfigService.
DnsConfigWatcher is installed at NetworkChangeNotifier and provides signals to DNSObservers. DnsConfigService becomes a DNSObserver. BUG=114827,114223,128166 TEST=./net_unittests --gtest_filter=DnsConfigService* Review URL: https://chromiumcodereview.appspot.com/10377092 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@137457 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/base/network_change_notifier.cc27
-rw-r--r--net/base/network_change_notifier.h23
-rw-r--r--net/base/network_change_notifier_linux.cc74
-rw-r--r--net/base/network_change_notifier_linux.h3
-rw-r--r--net/base/network_change_notifier_mac.cc36
-rw-r--r--net/base/network_change_notifier_mac.h7
-rw-r--r--net/base/network_change_notifier_win.cc33
-rw-r--r--net/base/network_change_notifier_win.h7
-rw-r--r--net/dns/dns_config_service.cc24
-rw-r--r--net/dns/dns_config_service.h33
-rw-r--r--net/dns/dns_config_service_posix.cc90
-rw-r--r--net/dns/dns_config_service_posix.h16
-rw-r--r--net/dns/dns_config_service_unittest.cc12
-rw-r--r--net/dns/dns_config_service_win.cc231
-rw-r--r--net/dns/dns_config_service_win.h71
-rw-r--r--net/dns/dns_config_watcher.h45
-rw-r--r--net/dns/dns_config_watcher_posix.cc131
-rw-r--r--net/dns/dns_config_watcher_win.cc167
-rw-r--r--net/dns/dns_test_util.cc14
-rw-r--r--net/dns/dns_test_util.h14
-rw-r--r--net/net.gyp3
21 files changed, 655 insertions, 406 deletions
diff --git a/net/base/network_change_notifier.cc b/net/base/network_change_notifier.cc
index ec9cae0..7a14d0e 100644
--- a/net/base/network_change_notifier.cc
+++ b/net/base/network_change_notifier.cc
@@ -81,6 +81,14 @@ bool NetworkChangeNotifier::IsOffline() {
}
// static
+bool NetworkChangeNotifier::IsWatchingDNS() {
+ if (!g_network_change_notifier)
+ return false;
+ base::AutoLock(g_network_change_notifier->watching_dns_lock_);
+ return g_network_change_notifier->watching_dns_;
+}
+
+// static
NetworkChangeNotifier* NetworkChangeNotifier::CreateMock() {
return new MockNetworkChangeNotifier();
}
@@ -137,11 +145,13 @@ NetworkChangeNotifier::NetworkChangeNotifier()
ObserverListBase<OnlineStateObserver>::NOTIFY_EXISTING_ONLY)),
resolver_state_observer_list_(
new ObserverListThreadSafe<DNSObserver>(
- ObserverListBase<DNSObserver>::NOTIFY_EXISTING_ONLY)) {
+ ObserverListBase<DNSObserver>::NOTIFY_EXISTING_ONLY)),
+ watching_dns_(false) {
DCHECK(!g_network_change_notifier);
g_network_change_notifier = this;
}
+// static
void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() {
if (g_network_change_notifier) {
g_network_change_notifier->ip_address_observer_list_->Notify(
@@ -149,13 +159,28 @@ void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() {
}
}
+// static
void NetworkChangeNotifier::NotifyObserversOfDNSChange(unsigned detail) {
if (g_network_change_notifier) {
+ {
+ base::AutoLock(g_network_change_notifier->watching_dns_lock_);
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED) {
+ g_network_change_notifier->watching_dns_ = true;
+ } else if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED) {
+ g_network_change_notifier->watching_dns_ = false;
+ }
+ // Include detail that watch is off to spare the call to IsWatchingDNS.
+ if (!g_network_change_notifier->watching_dns_)
+ detail |= NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED;
+ }
+ DCHECK(!(detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED) ||
+ !(detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED));
g_network_change_notifier->resolver_state_observer_list_->Notify(
&DNSObserver::OnDNSChanged, detail);
}
}
+// static
void NetworkChangeNotifier::NotifyObserversOfOnlineStateChange() {
if (g_network_change_notifier) {
g_network_change_notifier->online_state_observer_list_->Notify(
diff --git a/net/base/network_change_notifier.h b/net/base/network_change_notifier.h
index f487501..477d1f7 100644
--- a/net/base/network_change_notifier.h
+++ b/net/base/network_change_notifier.h
@@ -8,12 +8,17 @@
#include "base/basictypes.h"
#include "base/observer_list_threadsafe.h"
+#include "base/synchronization/lock.h"
#include "net/base/net_export.h"
namespace net {
class NetworkChangeNotifierFactory;
+namespace internal {
+class DnsConfigWatcher;
+}
+
// NetworkChangeNotifier monitors the system for network changes, and notifies
// registered observers of those events. Observers may register on any thread,
// and will be called back on the thread from which they registered.
@@ -27,8 +32,10 @@ class NET_EXPORT NetworkChangeNotifier {
CHANGE_DNS_SETTINGS = 1 << 0,
// The HOSTS file has changed.
CHANGE_DNS_HOSTS = 1 << 1,
- // Computer name has changed.
- CHANGE_DNS_LOCALHOST = 1 << 2,
+ // The watcher has started.
+ CHANGE_DNS_WATCH_STARTED = 1 << 2,
+ // The watcher has failed and will not be available until further notice.
+ CHANGE_DNS_WATCH_FAILED = 1 << 3,
};
class NET_EXPORT IPAddressObserver {
@@ -105,6 +112,9 @@ class NET_EXPORT NetworkChangeNotifier {
// will be successfully.
static bool IsOffline();
+ // Returns true if DNS watcher is operational.
+ static bool IsWatchingDNS();
+
// Like Create(), but for use in tests. The mock object doesn't monitor any
// events, it merely rebroadcasts notifications when requested.
static NetworkChangeNotifier* CreateMock();
@@ -135,6 +145,8 @@ class NET_EXPORT NetworkChangeNotifier {
}
protected:
+ friend class internal::DnsConfigWatcher;
+
NetworkChangeNotifier();
// Broadcasts a notification to all registered observers. Note that this
@@ -172,6 +184,13 @@ class NET_EXPORT NetworkChangeNotifier {
const scoped_refptr<ObserverListThreadSafe<DNSObserver> >
resolver_state_observer_list_;
+ // True iff DNS watchers are operational.
+ // Otherwise, OnDNSChanged might not be issued for future changes.
+ // TODO(szym): This is a temporary interface, consider restarting them.
+ // http://crbug.com/116139
+ base::Lock watching_dns_lock_;
+ bool watching_dns_;
+
DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifier);
};
diff --git a/net/base/network_change_notifier_linux.cc b/net/base/network_change_notifier_linux.cc
index a4bc595..679593e 100644
--- a/net/base/network_change_notifier_linux.cc
+++ b/net/base/network_change_notifier_linux.cc
@@ -10,6 +10,7 @@
#include "net/base/network_change_notifier_linux.h"
#include <errno.h>
+#include <resolv.h>
#include <sys/socket.h>
#include "base/bind.h"
@@ -17,8 +18,6 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/eintr_wrapper.h"
-#include "base/file_util.h"
-#include "base/files/file_path_watcher.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
@@ -30,8 +29,7 @@
#include "dbus/object_proxy.h"
#include "net/base/net_errors.h"
#include "net/base/network_change_notifier_netlink_linux.h"
-
-using ::base::files::FilePathWatcher;
+#include "net/dns/dns_config_watcher.h"
namespace net {
@@ -64,28 +62,6 @@ enum {
NM_STATE_CONNECTED_GLOBAL = 70
};
-class DNSWatchDelegate : public FilePathWatcher::Delegate {
- public:
- explicit DNSWatchDelegate(const base::Closure& callback)
- : callback_(callback) {}
- virtual ~DNSWatchDelegate() {}
- // FilePathWatcher::Delegate interface
- virtual void OnFilePathChanged(const FilePath& path) OVERRIDE;
- virtual void OnFilePathError(const FilePath& path) OVERRIDE;
- private:
- base::Closure callback_;
- DISALLOW_COPY_AND_ASSIGN(DNSWatchDelegate);
-};
-
-void DNSWatchDelegate::OnFilePathChanged(const FilePath& path) {
- // Calls NetworkChangeNotifier::NotifyObserversOfDNSChange().
- callback_.Run();
-}
-
-void DNSWatchDelegate::OnFilePathError(const FilePath& path) {
- LOG(ERROR) << "DNSWatchDelegate::OnFilePathError for " << path.value();
-}
-
} // namespace
// A wrapper around NetworkManager's D-Bus API.
@@ -271,8 +247,8 @@ class NetworkChangeNotifierLinux::Thread
virtual ~Thread();
// MessageLoopForIO::Watcher:
- virtual void OnFileCanReadWithoutBlocking(int fd);
- virtual void OnFileCanWriteWithoutBlocking(int /* fd */);
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE;
// Plumbing for NetworkChangeNotifier::IsCurrentlyOffline.
// Safe to call from any thread.
@@ -282,8 +258,8 @@ class NetworkChangeNotifierLinux::Thread
protected:
// base::Thread
- virtual void Init();
- virtual void CleanUp();
+ virtual void Init() OVERRIDE;
+ virtual void CleanUp() OVERRIDE;
private:
// Starts listening for netlink messages. Also handles the messages if there
@@ -299,25 +275,17 @@ class NetworkChangeNotifierLinux::Thread
int netlink_fd_;
MessageLoopForIO::FileDescriptorWatcher netlink_watcher_;
- // Technically only needed for ChromeOS, but it's ugly to #ifdef out.
- base::WeakPtrFactory<Thread> ptr_factory_;
-
- // Used to watch for changes to /etc/resolv.conf and /etc/hosts.
- scoped_ptr<base::files::FilePathWatcher> resolv_file_watcher_;
- scoped_ptr<base::files::FilePathWatcher> hosts_file_watcher_;
- scoped_refptr<DNSWatchDelegate> resolv_watcher_delegate_;
- scoped_refptr<DNSWatchDelegate> hosts_watcher_delegate_;
-
// Used to detect online/offline state changes.
NetworkManagerApi network_manager_api_;
+ internal::DnsConfigWatcher dns_watcher_;
+
DISALLOW_COPY_AND_ASSIGN(Thread);
};
NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus)
: base::Thread("NetworkChangeNotifier"),
netlink_fd_(kInvalidSocket),
- ALLOW_THIS_IN_INITIALIZER_LIST(ptr_factory_(this)),
network_manager_api_(
base::Bind(&NetworkChangeNotifier
::NotifyObserversOfOnlineStateChange),
@@ -329,23 +297,6 @@ NetworkChangeNotifierLinux::Thread::~Thread() {
}
void NetworkChangeNotifierLinux::Thread::Init() {
- resolv_file_watcher_.reset(new FilePathWatcher);
- hosts_file_watcher_.reset(new FilePathWatcher);
- resolv_watcher_delegate_ = new DNSWatchDelegate(base::Bind(
- &NetworkChangeNotifier::NotifyObserversOfDNSChange,
- static_cast<unsigned>(CHANGE_DNS_SETTINGS)));
- hosts_watcher_delegate_ = new DNSWatchDelegate(base::Bind(
- &NetworkChangeNotifier::NotifyObserversOfDNSChange,
- static_cast<unsigned>(CHANGE_DNS_HOSTS)));
- if (!resolv_file_watcher_->Watch(
- FilePath(FILE_PATH_LITERAL("/etc/resolv.conf")),
- resolv_watcher_delegate_.get())) {
- LOG(ERROR) << "Failed to setup watch for /etc/resolv.conf";
- }
- if (!hosts_file_watcher_->Watch(FilePath(FILE_PATH_LITERAL("/etc/hosts")),
- hosts_watcher_delegate_.get())) {
- LOG(ERROR) << "Failed to setup watch for /etc/hosts";
- }
netlink_fd_ = InitializeNetlinkSocket();
if (netlink_fd_ < 0) {
netlink_fd_ = kInvalidSocket;
@@ -354,6 +305,8 @@ void NetworkChangeNotifierLinux::Thread::Init() {
ListenForNotifications();
network_manager_api_.Init();
+
+ dns_watcher_.Init();
}
void NetworkChangeNotifierLinux::Thread::CleanUp() {
@@ -363,12 +316,9 @@ void NetworkChangeNotifierLinux::Thread::CleanUp() {
netlink_fd_ = kInvalidSocket;
netlink_watcher_.StopWatchingFileDescriptor();
}
- // Kill watchers early to make sure they won't try to call
- // into us via the delegate during destruction.
- resolv_file_watcher_.reset();
- hosts_file_watcher_.reset();
-
network_manager_api_.CleanUp();
+
+ dns_watcher_.CleanUp();
}
void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) {
diff --git a/net/base/network_change_notifier_linux.h b/net/base/network_change_notifier_linux.h
index 3dbb814..e9426d0 100644
--- a/net/base/network_change_notifier_linux.h
+++ b/net/base/network_change_notifier_linux.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -38,6 +38,7 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierLinux
// The thread used to listen for notifications. This relays the notification
// to the registered observers without posting back to the thread the object
// was created on.
+ // Also used for DnsConfigWatcher which requires TYPE_IO message loop.
scoped_ptr<Thread> notifier_thread_;
DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierLinux);
diff --git a/net/base/network_change_notifier_mac.cc b/net/base/network_change_notifier_mac.cc
index ca314a1..0265ef1 100644
--- a/net/base/network_change_notifier_mac.cc
+++ b/net/base/network_change_notifier_mac.cc
@@ -5,6 +5,15 @@
#include "net/base/network_change_notifier_mac.h"
#include <netinet/in.h>
+#include <resolv.h>
+
+#include "base/basictypes.h"
+#include "base/threading/thread.h"
+#include "net/dns/dns_config_watcher.h"
+
+#ifndef _PATH_RESCONF // Normally defined in <resolv.h>
+#define _PATH_RESCONF "/etc/resolv.conf"
+#endif
namespace net {
@@ -14,13 +23,38 @@ static bool CalculateReachability(SCNetworkConnectionFlags flags) {
return reachable && !connection_required;
}
+class NetworkChangeNotifierMac::DnsWatcherThread : public base::Thread {
+ public:
+ DnsWatcherThread() : base::Thread("NetworkChangeNotifier") {}
+
+ virtual ~DnsWatcherThread() {
+ Stop();
+ }
+
+ virtual void Init() OVERRIDE {
+ watcher_.Init();
+ }
+
+ virtual void CleanUp() OVERRIDE {
+ watcher_.CleanUp();
+ }
+
+ private:
+ internal::DnsConfigWatcher watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsWatcherThread);
+};
+
NetworkChangeNotifierMac::NetworkChangeNotifierMac()
: online_state_(UNINITIALIZED),
initial_state_cv_(&online_state_lock_),
- forwarder_(this) {
+ forwarder_(this),
+ dns_watcher_thread_(new DnsWatcherThread()) {
// Must be initialized after the rest of this object, as it may call back into
// SetInitialState().
config_watcher_.reset(new NetworkConfigWatcherMac(&forwarder_));
+ dns_watcher_thread_->StartWithOptions(
+ base::Thread::Options(MessageLoop::TYPE_IO, 0));
}
NetworkChangeNotifierMac::~NetworkChangeNotifierMac() {
diff --git a/net/base/network_change_notifier_mac.h b/net/base/network_change_notifier_mac.h
index ec2bb1f..c95e952 100644
--- a/net/base/network_change_notifier_mac.h
+++ b/net/base/network_change_notifier_mac.h
@@ -24,7 +24,7 @@ class NetworkChangeNotifierMac: public NetworkChangeNotifier {
NetworkChangeNotifierMac();
virtual ~NetworkChangeNotifierMac();
- // NetworkChangeNotifier implementation:
+ // NetworkChangeNotifier:
virtual bool IsCurrentlyOffline() const OVERRIDE;
private:
@@ -34,6 +34,8 @@ class NetworkChangeNotifierMac: public NetworkChangeNotifier {
ONLINE = 1
};
+ class DnsWatcherThread;
+
// Forwarder just exists to keep the NetworkConfigWatcherMac API out of
// NetworkChangeNotifierMac's public API.
class Forwarder : public NetworkConfigWatcherMac::Delegate {
@@ -83,6 +85,9 @@ class NetworkChangeNotifierMac: public NetworkChangeNotifier {
Forwarder forwarder_;
scoped_ptr<const NetworkConfigWatcherMac> config_watcher_;
+ // Thread on which we can run DnsConfigWatcher, which requires TYPE_IO.
+ scoped_ptr<DnsWatcherThread> dns_watcher_thread_;
+
DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierMac);
};
diff --git a/net/base/network_change_notifier_win.cc b/net/base/network_change_notifier_win.cc
index eccacaa..928a688 100644
--- a/net/base/network_change_notifier_win.cc
+++ b/net/base/network_change_notifier_win.cc
@@ -10,11 +10,15 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
+#include "base/threading/thread.h"
#include "base/time.h"
#include "net/base/winsock_init.h"
+#include "net/dns/dns_config_watcher.h"
#pragma comment(lib, "iphlpapi.lib")
+namespace net {
+
namespace {
// Time between NotifyAddrChange retries, on failure.
@@ -22,14 +26,39 @@ const int kWatchForAddressChangeRetryIntervalMs = 500;
} // namespace
-namespace net {
+// Thread on which we can run DnsConfigWatcher, which requires AssertIOAllowed
+// to open registry keys and to handle FilePathWatcher updates.
+class NetworkChangeNotifierWin::DnsWatcherThread : public base::Thread {
+ public:
+ DnsWatcherThread() : base::Thread("NetworkChangeNotifier") {}
+
+ virtual ~DnsWatcherThread() {
+ Stop();
+ }
+
+ virtual void Init() OVERRIDE {
+ watcher_.Init();
+ }
+
+ virtual void CleanUp() OVERRIDE {
+ watcher_.CleanUp();
+ }
+
+ private:
+ internal::DnsConfigWatcher watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsWatcherThread);
+};
NetworkChangeNotifierWin::NetworkChangeNotifierWin()
: is_watching_(false),
sequential_failures_(0),
- ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
+ dns_watcher_thread_(new DnsWatcherThread()) {
memset(&addr_overlapped_, 0, sizeof addr_overlapped_);
addr_overlapped_.hEvent = WSACreateEvent();
+ dns_watcher_thread_->StartWithOptions(
+ base::Thread::Options(MessageLoop::TYPE_IO, 0));
}
NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {
diff --git a/net/base/network_change_notifier_win.h b/net/base/network_change_notifier_win.h
index aee5304..d8c9a45 100644
--- a/net/base/network_change_notifier_win.h
+++ b/net/base/network_change_notifier_win.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -10,6 +10,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/non_thread_safe.h"
#include "base/timer.h"
@@ -47,6 +48,7 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin
int sequential_failures() { return sequential_failures_; }
private:
+ class DnsWatcherThread;
friend class NetworkChangeNotifierWinTest;
// NetworkChangeNotifier methods:
@@ -89,6 +91,9 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin
// Used for calling WatchForAddressChange again on failure.
base::WeakPtrFactory<NetworkChangeNotifierWin> weak_factory_;
+ // Thread on which we can run DnsConfigWatcher.
+ scoped_ptr<DnsWatcherThread> dns_watcher_thread_;
+
DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierWin);
};
diff --git a/net/dns/dns_config_service.cc b/net/dns/dns_config_service.cc
index ffd5fe9..d189def 100644
--- a/net/dns/dns_config_service.cc
+++ b/net/dns/dns_config_service.cc
@@ -77,7 +77,29 @@ DnsConfigService::DnsConfigService()
have_hosts_(false),
need_update_(false) {}
-DnsConfigService::~DnsConfigService() {}
+DnsConfigService::~DnsConfigService() {
+ // Must always clean up.
+ NetworkChangeNotifier::RemoveDNSObserver(this);
+}
+
+void DnsConfigService::Read(const CallbackType& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!callback.is_null());
+ DCHECK(callback_.is_null());
+ callback_ = callback;
+ OnDNSChanged(NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED);
+}
+
+void DnsConfigService::Watch(const CallbackType& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!callback.is_null());
+ DCHECK(callback_.is_null());
+ NetworkChangeNotifier::AddDNSObserver(this);
+ callback_ = callback;
+ if (NetworkChangeNotifier::IsWatchingDNS())
+ OnDNSChanged(NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED);
+ // else: Wait until signal before reading.
+}
void DnsConfigService::InvalidateConfig() {
DCHECK(CalledOnValidThread());
diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h
index 6b3e9f3..bc3542d 100644
--- a/net/dns/dns_config_service.h
+++ b/net/dns/dns_config_service.h
@@ -19,6 +19,7 @@
// std::vector<IPEndPoint>.
#include "net/base/address_list.h"
#include "net/base/ip_endpoint.h" // win requires size of IPEndPoint
+#include "net/base/network_change_notifier.h"
#include "net/base/net_export.h"
#include "net/dns/dns_hosts.h"
@@ -75,12 +76,14 @@ struct NET_EXPORT_PRIVATE DnsConfig {
};
-// Service for watching when the system DNS settings have changed.
-// Depending on the platform, watches files in /etc/ or Windows registry.
+// Service for reading system DNS settings, on demand or when signalled by
+// NetworkChangeNotifier.
class NET_EXPORT_PRIVATE DnsConfigService
- : NON_EXPORTED_BASE(public base::NonThreadSafe) {
+ : NON_EXPORTED_BASE(public base::NonThreadSafe),
+ public NetworkChangeNotifier::DNSObserver {
public:
- // Callback interface for the client, called on the same thread as Watch().
+ // Callback interface for the client, called on the same thread as Read() and
+ // Watch().
typedef base::Callback<void(const DnsConfig& config)> CallbackType;
// Creates the platform-specific DnsConfigService.
@@ -89,10 +92,15 @@ class NET_EXPORT_PRIVATE DnsConfigService
DnsConfigService();
virtual ~DnsConfigService();
- // Immediately starts watching system configuration for changes and attempts
- // to read the configuration. For some platform implementations, the current
- // thread must have an IO loop (for base::files::FilePathWatcher).
- virtual void Watch(const CallbackType& callback) = 0;
+ // Attempts to read the configuration. Will run |callback| when succeeded.
+ // Can be called at most once.
+ void Read(const CallbackType& callback);
+
+ // Registers for notifications at NetworkChangeNotifier. Will attempt to read
+ // config after watch is started by NetworkChangeNotifier. Will run |callback|
+ // iff config changes from last call or should be withdrawn.
+ // Can be called at most once.
+ virtual void Watch(const CallbackType& callback);
protected:
friend class DnsHostsReader;
@@ -107,9 +115,9 @@ class NET_EXPORT_PRIVATE DnsConfigService
// Called with new hosts. Rest of the config is assumed unchanged.
void OnHostsRead(const DnsHosts& hosts);
- void set_callback(const CallbackType& callback) {
- callback_ = callback;
- }
+ // NetworkChangeNotifier::DNSObserver:
+ // Must be defined by implementations.
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE = 0;
private:
void StartTimer();
@@ -122,7 +130,7 @@ class NET_EXPORT_PRIVATE DnsConfigService
DnsConfig dns_config_;
- // True after On*Read, before Invalidate*. Tell if the config is complete.
+ // True after On*Read, before Invalidate*. Tells if the config is complete.
bool have_config_;
bool have_hosts_;
// True if receiver needs to be updated when the config becomes complete.
@@ -131,7 +139,6 @@ class NET_EXPORT_PRIVATE DnsConfigService
// Started in Invalidate*, cleared in On*Read.
base::OneShotTimer<DnsConfigService> timer_;
- private:
DISALLOW_COPY_AND_ASSIGN(DnsConfigService);
};
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
index a53739e..11a48a5 100644
--- a/net/dns/dns_config_service_posix.cc
+++ b/net/dns/dns_config_service_posix.cc
@@ -16,18 +16,14 @@
#include "net/dns/file_path_watcher_wrapper.h"
#include "net/dns/serial_worker.h"
-#if defined(OS_MACOSX)
-#include "net/dns/notify_watcher_mac.h"
-#endif
+namespace net {
+
+namespace {
#ifndef _PATH_RESCONF // Normally defined in <resolv.h>
#define _PATH_RESCONF "/etc/resolv.conf"
#endif
-namespace net {
-
-namespace {
-
const FilePath::CharType* kFilePathHosts = FILE_PATH_LITERAL("/etc/hosts");
// A SerialWorker that uses libresolv to initialize res_state and converts
@@ -85,32 +81,7 @@ class ConfigReader : public SerialWorker {
namespace internal {
-#if defined(OS_MACOSX)
-// From 10.7.3 configd-395.10/dnsinfo/dnsinfo.h
-static const char* kDnsNotifyKey =
- "com.apple.system.SystemConfiguration.dns_configuration";
-
-class DnsConfigServicePosix::ConfigWatcher : public NotifyWatcherMac {
- public:
- bool Watch(const base::Callback<void(bool succeeded)>& callback) {
- return NotifyWatcherMac::Watch(kDnsNotifyKey, callback);
- }
-};
-#else
-static const FilePath::CharType* kFilePathConfig =
- FILE_PATH_LITERAL(_PATH_RESCONF);
-
-class DnsConfigServicePosix::ConfigWatcher : public FilePathWatcherWrapper {
- public:
- bool Watch(const base::Callback<void(bool succeeded)>& callback) {
- return FilePathWatcherWrapper::Watch(FilePath(kFilePathConfig), callback);
- }
-};
-#endif
-
-DnsConfigServicePosix::DnsConfigServicePosix()
- : config_watcher_(new ConfigWatcher()),
- hosts_watcher_(new FilePathWatcherWrapper()) {
+DnsConfigServicePosix::DnsConfigServicePosix() {
config_reader_ = new ConfigReader(
base::Bind(&DnsConfigServicePosix::OnConfigRead,
base::Unretained(this)));
@@ -125,46 +96,25 @@ DnsConfigServicePosix::~DnsConfigServicePosix() {
hosts_reader_->Cancel();
}
-void DnsConfigServicePosix::Watch(const CallbackType& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(!callback.is_null());
- set_callback(callback);
-
- // Even if watchers fail, we keep the other one as it provides useful signals.
- if (config_watcher_->Watch(
- base::Bind(&DnsConfigServicePosix::OnConfigChanged,
- base::Unretained(this)))) {
- OnConfigChanged(true);
- } else {
- OnConfigChanged(false);
- }
-
- if (hosts_watcher_->Watch(
- FilePath(kFilePathHosts),
- base::Bind(&DnsConfigServicePosix::OnHostsChanged,
- base::Unretained(this)))) {
- OnHostsChanged(true);
- } else {
- OnHostsChanged(false);
+void DnsConfigServicePosix::OnDNSChanged(unsigned detail) {
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED) {
+ InvalidateConfig();
+ InvalidateHosts();
+ // We don't trust a config that we cannot watch in the future.
+ config_reader_->Cancel();
+ hosts_reader_->Cancel();
+ return;
}
-}
-
-void DnsConfigServicePosix::OnConfigChanged(bool watch_succeeded) {
- InvalidateConfig();
- // We don't trust a config that we cannot watch in the future.
- // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
- if (watch_succeeded)
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED)
+ detail = ~0; // Assume everything changed.
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_SETTINGS) {
+ InvalidateConfig();
config_reader_->WorkNow();
- else
- LOG(ERROR) << "Failed to watch DNS config";
-}
-
-void DnsConfigServicePosix::OnHostsChanged(bool watch_succeeded) {
- InvalidateHosts();
- if (watch_succeeded)
+ }
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_HOSTS) {
+ InvalidateHosts();
hosts_reader_->WorkNow();
- else
- LOG(ERROR) << "Failed to watch DNS hosts";
+ }
}
#if !defined(OS_ANDROID)
diff --git a/net/dns/dns_config_service_posix.h b/net/dns/dns_config_service_posix.h
index f28609b..30588efc 100644
--- a/net/dns/dns_config_service_posix.h
+++ b/net/dns/dns_config_service_posix.h
@@ -17,27 +17,17 @@
namespace net {
-class FilePathWatcherWrapper;
-
// Use DnsConfigService::CreateSystemService to use it outside of tests.
namespace internal {
-class NET_EXPORT_PRIVATE DnsConfigServicePosix
- : NON_EXPORTED_BASE(public DnsConfigService) {
+class NET_EXPORT_PRIVATE DnsConfigServicePosix : public DnsConfigService {
public:
DnsConfigServicePosix();
virtual ~DnsConfigServicePosix();
- virtual void Watch(const CallbackType& callback) OVERRIDE;
-
private:
- class ConfigWatcher;
-
- void OnConfigChanged(bool watch_succeeded);
- void OnHostsChanged(bool watch_succeeded);
-
- scoped_ptr<ConfigWatcher> config_watcher_;
- scoped_ptr<FilePathWatcherWrapper> hosts_watcher_;
+ // NetworkChangeNotifier::DNSObserver:
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE;
scoped_refptr<SerialWorker> config_reader_;
scoped_refptr<SerialWorker> hosts_reader_;
diff --git a/net/dns/dns_config_service_unittest.cc b/net/dns/dns_config_service_unittest.cc
index 9d27cb7..e68db27 100644
--- a/net/dns/dns_config_service_unittest.cc
+++ b/net/dns/dns_config_service_unittest.cc
@@ -29,9 +29,7 @@ class DnsConfigServiceTest : public testing::Test {
protected:
class TestDnsConfigService : public DnsConfigService {
public:
- virtual void Watch(const CallbackType& callback) OVERRIDE {
- set_callback(callback);
- }
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE {}
// Expose the protected methods to this test suite.
void InvalidateConfig() {
@@ -189,16 +187,12 @@ TEST_F(DnsConfigServiceTest, FLAKY_GetSystemConfig) {
service_.reset();
scoped_ptr<DnsConfigService> service(DnsConfigService::CreateSystemService());
- service->Watch(base::Bind(&DnsConfigServiceTest::OnConfigChanged,
- base::Unretained(this)));
+ service->Read(base::Bind(&DnsConfigServiceTest::OnConfigChanged,
+ base::Unretained(this)));
base::TimeDelta kTimeout = TestTimeouts::action_max_timeout();
WaitForConfig(kTimeout);
ASSERT_TRUE(last_config_.IsValid()) << "Did not receive DnsConfig in " <<
kTimeout.InSecondsF() << "s";
-
- // Restart watch to confirm it's allowed.
- service->Watch(base::Bind(&DnsConfigServiceTest::OnConfigChanged,
- base::Unretained(this)));
}
#endif // OS_POSIX || OS_WIN
diff --git a/net/dns/dns_config_service_win.cc b/net/dns/dns_config_service_win.cc
index 327ea2a..c64f651 100644
--- a/net/dns/dns_config_service_win.cc
+++ b/net/dns/dns_config_service_win.cc
@@ -19,14 +19,12 @@
#include "base/threading/non_thread_safe.h"
#include "base/threading/thread_restrictions.h"
#include "base/utf_string_conversions.h"
-#include "base/win/object_watcher.h"
#include "base/win/registry.h"
#include "base/win/windows_version.h"
#include "googleurl/src/url_canon.h"
#include "net/base/net_util.h"
#include "net/base/network_change_notifier.h"
#include "net/dns/dns_protocol.h"
-#include "net/dns/file_path_watcher_wrapper.h"
#include "net/dns/serial_worker.h"
#pragma comment(lib, "iphlpapi.lib")
@@ -37,15 +35,6 @@ namespace internal {
namespace {
-// Registry key paths.
-const wchar_t* const kTcpipPath =
- L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
-const wchar_t* const kTcpip6Path =
- L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters";
-const wchar_t* const kDnscachePath =
- L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters";
-const wchar_t* const kPolicyPath =
- L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient";
const wchar_t* const kPrimaryDnsSuffixPath =
L"SOFTWARE\\Policies\\Microsoft\\System\\DNSClient";
@@ -95,62 +84,6 @@ class RegistryReader : public base::NonThreadSafe {
DISALLOW_COPY_AND_ASSIGN(RegistryReader);
};
-
-// Watches a single registry key for changes.
-class RegistryWatcher : public base::win::ObjectWatcher::Delegate,
- public base::NonThreadSafe {
- public:
- typedef base::Callback<void(bool succeeded)> CallbackType;
- RegistryWatcher() {}
-
- bool Watch(const wchar_t* key, const CallbackType& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(!callback.is_null());
- Cancel();
- if (key_.Open(HKEY_LOCAL_MACHINE, key, KEY_NOTIFY) != ERROR_SUCCESS)
- return false;
- if (key_.StartWatching() != ERROR_SUCCESS)
- return false;
- if (!watcher_.StartWatching(key_.watch_event(), this))
- return false;
- callback_ = callback;
- return true;
- }
-
- bool IsWatching() const {
- DCHECK(CalledOnValidThread());
- return !callback_.is_null();
- }
-
- void Cancel() {
- DCHECK(CalledOnValidThread());
- callback_.Reset();
- if (key_.Valid()) {
- watcher_.StopWatching();
- key_.StopWatching();
- key_.Close();
- }
- }
-
- virtual void OnObjectSignaled(HANDLE object) OVERRIDE {
- DCHECK(CalledOnValidThread());
- bool succeeded = (key_.StartWatching() == ERROR_SUCCESS) &&
- watcher_.StartWatching(key_.watch_event(), this);
- CallbackType callback = callback_;
- if (!succeeded)
- Cancel();
- if (!callback.is_null())
- callback.Run(succeeded);
- }
-
- private:
- CallbackType callback_;
- base::win::RegKey key_;
- base::win::ObjectWatcher watcher_;
-
- DISALLOW_COPY_AND_ASSIGN(RegistryWatcher);
-};
-
// Returns NULL if failed.
scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> ReadIpHelper(ULONG flags) {
base::ThreadRestrictions::AssertIOAllowed();
@@ -200,6 +133,13 @@ bool ParseDomainASCII(const string16& widestr, std::string* domain) {
} // namespace
+FilePath GetHostsPath() {
+ TCHAR buffer[MAX_PATH];
+ UINT rc = GetSystemDirectory(buffer, MAX_PATH);
+ DCHECK(0 < rc && rc < MAX_PATH);
+ return FilePath(buffer).Append(FILE_PATH_LITERAL("drivers\\etc\\hosts"));
+}
+
bool ParseSearchList(const string16& value, std::vector<std::string>* output) {
DCHECK(output);
if (value.empty())
@@ -378,58 +318,7 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
: service_(service),
success_(false) {}
- bool Watch() {
- DCHECK(loop()->BelongsToCurrentThread());
-
- RegistryWatcher::CallbackType callback =
- base::Bind(&ConfigReader::OnChange, base::Unretained(this));
-
- // The Tcpip key must be present.
- if (!tcpip_watcher_.Watch(kTcpipPath, callback))
- return false;
-
- // Watch for IPv6 nameservers.
- tcpip6_watcher_.Watch(kTcpip6Path, callback);
-
- // DNS suffix search list and devolution can be configured via group
- // policy which sets this registry key. If the key is missing, the policy
- // does not apply, and the DNS client uses Tcpip and Dnscache settings.
- // If a policy is installed, DnsConfigService will need to be restarted.
- // BUG=99509
-
- dnscache_watcher_.Watch(kDnscachePath, callback);
- policy_watcher_.Watch(kPolicyPath, callback);
-
- WorkNow();
- return true;
- }
-
- void Cancel() {
- DCHECK(loop()->BelongsToCurrentThread());
- SerialWorker::Cancel();
- policy_watcher_.Cancel();
- dnscache_watcher_.Cancel();
- tcpip6_watcher_.Cancel();
- tcpip_watcher_.Cancel();
- }
-
private:
- virtual ~ConfigReader() {
- DCHECK(IsCancelled());
- }
-
- void OnChange(bool succeeded) {
- DCHECK(loop()->BelongsToCurrentThread());
- if (!IsCancelled())
- service_->InvalidateConfig();
- // We don't trust a config that we cannot watch in the future.
- // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
- if (succeeded)
- WorkNow();
- else
- LOG(ERROR) << "Failed to watch DNS config";
- }
-
bool ReadDevolutionSetting(const RegistryReader& reader,
DnsSystemSettings::DevolutionSetting& setting) {
return reader.ReadDword(L"UseDomainNameDevolution", &setting.enabled) &&
@@ -500,73 +389,18 @@ class DnsConfigServiceWin::ConfigReader : public SerialWorker {
// Written in DoRead(), read in OnReadFinished(). No locking required.
DnsConfig dns_config_;
bool success_;
-
- RegistryWatcher tcpip_watcher_;
- RegistryWatcher tcpip6_watcher_;
- RegistryWatcher dnscache_watcher_;
- RegistryWatcher policy_watcher_;
};
-FilePath GetHostsPath() {
- TCHAR buffer[MAX_PATH];
- UINT rc = GetSystemDirectory(buffer, MAX_PATH);
- DCHECK(0 < rc && rc < MAX_PATH);
- return FilePath(buffer).Append(FILE_PATH_LITERAL("drivers\\etc\\hosts"));
-}
-
// An extension for DnsHostsReader which also watches the HOSTS file,
// reads local name from GetComputerNameEx, local IP from GetAdaptersAddresses,
// and observes changes to local IP address.
-class DnsConfigServiceWin::HostsReader
- : public DnsHostsReader,
- public NetworkChangeNotifier::IPAddressObserver {
+class DnsConfigServiceWin::HostsReader : public DnsHostsReader {
public:
explicit HostsReader(DnsConfigServiceWin* service)
: DnsHostsReader(GetHostsPath()), service_(service) {
}
- bool Watch() {
- DCHECK(loop()->BelongsToCurrentThread());
- DCHECK(!IsCancelled());
-
- // In case the reader is restarted, remove it from the observer list.
- NetworkChangeNotifier::RemoveIPAddressObserver(this);
-
- if (!hosts_watcher_.Watch(path(),
- base::Bind(&HostsReader::OnHostsChanged,
- base::Unretained(this)))) {
- return false;
- }
- NetworkChangeNotifier::AddIPAddressObserver(this);
- WorkNow();
- return true;
- }
-
- // Cancels the underlying SerialWorker. Cannot be undone.
- void Cancel() {
- DnsHostsReader::Cancel();
- hosts_watcher_.Cancel();
- NetworkChangeNotifier::RemoveIPAddressObserver(this);
- }
-
private:
- virtual void OnIPAddressChanged() OVERRIDE {
- DCHECK(loop()->BelongsToCurrentThread());
- service_->InvalidateHosts();
- if (!hosts_watcher_.IsWatching())
- return;
- WorkNow();
- }
-
- void OnHostsChanged(bool succeeded) {
- DCHECK(loop()->BelongsToCurrentThread());
- service_->InvalidateHosts();
- if (succeeded)
- WorkNow();
- else
- LOG(ERROR) << "Failed to watch DNS hosts";
- }
-
virtual void DoWork() OVERRIDE {
DnsHostsReader::DoWork();
@@ -658,18 +492,18 @@ class DnsConfigServiceWin::HostsReader
virtual void OnWorkFinished() OVERRIDE {
DCHECK(loop()->BelongsToCurrentThread());
- if (!success_ || !hosts_watcher_.IsWatching())
- return;
- service_->OnHostsRead(dns_hosts_);
+ if (success_) {
+ service_->OnHostsRead(dns_hosts_);
+ } else {
+ LOG(WARNING) << "Failed to read hosts.";
+ }
}
DnsConfigServiceWin* service_;
- FilePathWatcherWrapper hosts_watcher_;
DISALLOW_COPY_AND_ASSIGN(HostsReader);
};
-
DnsConfigServiceWin::DnsConfigServiceWin()
: config_reader_(new ConfigReader(this)),
hosts_reader_(new HostsReader(this)) {}
@@ -678,29 +512,42 @@ DnsConfigServiceWin::~DnsConfigServiceWin() {
DCHECK(CalledOnValidThread());
config_reader_->Cancel();
hosts_reader_->Cancel();
+ NetworkChangeNotifier::RemoveIPAddressObserver(this);
}
void DnsConfigServiceWin::Watch(const CallbackType& callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(!callback.is_null());
- set_callback(callback);
-
- // This is done only once per lifetime so open the keys and file watcher
- // handles on this thread.
- // TODO(szym): Should/can this be avoided? http://crbug.com/114223
- base::ThreadRestrictions::ScopedAllowIO allow_io;
+ DnsConfigService::Watch(callback);
+ // Also need to observe changes to local non-loopback IP for DnsHosts.
+ NetworkChangeNotifier::AddIPAddressObserver(this);
+}
- if (!config_reader_->Watch()) {
- LOG(ERROR) << "Failed to start watching DNS config";
+void DnsConfigServiceWin::OnDNSChanged(unsigned detail) {
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED) {
InvalidateConfig();
+ InvalidateHosts();
+ // We don't trust a config that we cannot watch in the future.
+ config_reader_->Cancel();
+ hosts_reader_->Cancel();
+ return;
}
-
- if (!hosts_reader_->Watch()) {
- LOG(ERROR) << "Failed to start watching HOSTS";
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED)
+ detail = ~0; // Assume everything changed.
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_SETTINGS) {
+ InvalidateConfig();
+ config_reader_->WorkNow();
+ }
+ if (detail & NetworkChangeNotifier::CHANGE_DNS_HOSTS) {
InvalidateHosts();
+ hosts_reader_->WorkNow();
}
}
+void DnsConfigServiceWin::OnIPAddressChanged() {
+ // Need to update non-loopback IP of local host.
+ if (NetworkChangeNotifier::IsWatchingDNS())
+ OnDNSChanged(NetworkChangeNotifier::CHANGE_DNS_HOSTS);
+}
+
} // namespace internal
// static
diff --git a/net/dns/dns_config_service_win.h b/net/dns/dns_config_service_win.h
index 703eb7a..a502e8b 100644
--- a/net/dns/dns_config_service_win.h
+++ b/net/dns/dns_config_service_win.h
@@ -33,28 +33,27 @@
namespace net {
-class FilePathWatcherWrapper;
-
-// Use DnsConfigService::CreateSystemService to use it outside of tests.
namespace internal {
-class NET_EXPORT_PRIVATE DnsConfigServiceWin
- : NON_EXPORTED_BASE(public DnsConfigService) {
- public:
- DnsConfigServiceWin();
- virtual ~DnsConfigServiceWin();
+// Registry key paths.
+const wchar_t* const kTcpipPath =
+ L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
+const wchar_t* const kTcpip6Path =
+ L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters";
+const wchar_t* const kDnscachePath =
+ L"SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters";
+const wchar_t* const kPolicyPath =
+ L"SOFTWARE\\Policies\\Microsoft\\Windows NT\\DNSClient";
- virtual void Watch(const CallbackType& callback) OVERRIDE;
+// Returns the path to the HOSTS file.
+FilePath GetHostsPath();
- private:
- class ConfigReader;
- class HostsReader;
-
- scoped_refptr<ConfigReader> config_reader_;
- scoped_refptr<HostsReader> hosts_reader_;
-
- DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceWin);
-};
+// Parses |value| as search list (comma-delimited list of domain names) from
+// a registry key and stores it in |out|. Returns true on success. Empty
+// entries (e.g., "chromium.org,,org") terminate the list. Non-ascii hostnames
+// are converted to punycode.
+bool NET_EXPORT_PRIVATE ParseSearchList(const string16& value,
+ std::vector<std::string>* out);
// All relevant settings read from registry and IP Helper. This isolates our
// logic from system calls and is exposed for unit tests. Keep it an aggregate
@@ -102,16 +101,36 @@ struct NET_EXPORT_PRIVATE DnsSystemSettings {
RegDword append_to_multi_label_name;
};
-// Parses |value| as search list (comma-delimited list of domain names) from
-// a registry key and stores it in |out|. Returns true on success. Empty
-// entries (e.g., "chromium.org,,org") terminate the list. Non-ascii hostnames
-// are converted to punycode.
-bool NET_EXPORT_PRIVATE ParseSearchList(const string16& value,
- std::vector<std::string>* out);
-
// Fills in |dns_config| from |settings|. Exposed for tests.
bool NET_EXPORT_PRIVATE ConvertSettingsToDnsConfig(
- const DnsSystemSettings& settings, DnsConfig* dns_config);
+ const DnsSystemSettings& settings,
+ DnsConfig* dns_config);
+
+// Use DnsConfigService::CreateSystemService to use it outside of tests.
+class NET_EXPORT_PRIVATE DnsConfigServiceWin
+ : public DnsConfigService,
+ public NetworkChangeNotifier::IPAddressObserver {
+ public:
+ DnsConfigServiceWin();
+ virtual ~DnsConfigServiceWin();
+
+ virtual void Watch(const CallbackType& callback) OVERRIDE;
+
+ private:
+ class ConfigReader;
+ class HostsReader;
+
+ // NetworkChangeNotifier::DNSObserver:
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE;
+
+ // NetworkChangeNotifier::IPAddressObserver:
+ virtual void OnIPAddressChanged() OVERRIDE;
+
+ scoped_refptr<ConfigReader> config_reader_;
+ scoped_refptr<HostsReader> hosts_reader_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceWin);
+};
} // namespace internal
diff --git a/net/dns/dns_config_watcher.h b/net/dns/dns_config_watcher.h
new file mode 100644
index 0000000..04d7056
--- /dev/null
+++ b/net/dns/dns_config_watcher.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef NET_DNS_DNS_CONFIG_WATCHER_H_
+#define NET_DNS_DNS_CONFIG_WATCHER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace net {
+namespace internal {
+
+// Watches when the system DNS settings have changed. It is used by
+// NetworkChangeNotifier to provide signals to registered DNSObservers.
+// Depending on the platform, watches files, Windows registry, or libnotify key.
+// If some watches fail, we keep the working parts, but NetworkChangeNotifier
+// will mark notifications to DNSObserver with the CHANGE_DNS_WATCH_FAILED bit.
+class DnsConfigWatcher {
+ public:
+ DnsConfigWatcher();
+ ~DnsConfigWatcher();
+
+ // Starts watching system configuration for changes. The current thread must
+ // have a MessageLoopForIO. The signals will be delivered directly to
+ // the global NetworkChangeNotifier.
+ void Init();
+
+ // Must be called on the same thread as Init. Required if dtor will be called
+ // on a different thread.
+ void CleanUp();
+
+ private:
+ // Platform-specific implementation.
+ class Core;
+ scoped_ptr<Core> core_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsConfigWatcher);
+};
+
+} // namespace internal
+} // namespace net
+
+#endif // NET_DNS_DNS_CONFIG_WATCHER_H_
diff --git a/net/dns/dns_config_watcher_posix.cc b/net/dns/dns_config_watcher_posix.cc
new file mode 100644
index 0000000..ebebf51
--- /dev/null
+++ b/net/dns/dns_config_watcher_posix.cc
@@ -0,0 +1,131 @@
+// 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 "net/dns/dns_config_watcher.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/network_change_notifier.h"
+#include "net/dns/file_path_watcher_wrapper.h"
+
+#if defined(OS_MACOSX)
+#include "net/dns/notify_watcher_mac.h"
+#endif
+
+namespace net {
+namespace internal {
+
+namespace {
+
+const FilePath::CharType* kFilePathHosts = FILE_PATH_LITERAL("/etc/hosts");
+
+#if defined(OS_MACOSX)
+// From 10.7.3 configd-395.10/dnsinfo/dnsinfo.h
+static const char* kDnsNotifyKey =
+ "com.apple.system.SystemConfiguration.dns_configuration";
+
+class ConfigWatcher {
+ public:
+ bool Watch(const base::Callback<void(bool succeeded)>& callback) {
+ return watcher_.Watch(kDnsNotifyKey, callback);
+ }
+ private:
+ NotifyWatcherMac watcher_;
+};
+#else
+
+#ifndef _PATH_RESCONF // Normally defined in <resolv.h>
+#define _PATH_RESCONF "/etc/resolv.conf"
+#endif
+
+static const FilePath::CharType* kFilePathConfig =
+ FILE_PATH_LITERAL(_PATH_RESCONF);
+
+class ConfigWatcher {
+ public:
+ bool Watch(const base::Callback<void(bool succeeded)>& callback) {
+ return watcher_.Watch(FilePath(kFilePathConfig), callback);
+ }
+ private:
+ FilePathWatcherWrapper watcher_;
+};
+#endif
+
+} // namespace
+
+class DnsConfigWatcher::Core {
+ public:
+ Core() {}
+ ~Core() {}
+
+ bool Watch() {
+ bool success = true;
+ if (!config_watcher_.Watch(base::Bind(&Core::OnConfigChanged,
+ base::Unretained(this)))) {
+ LOG(ERROR) << "DNS config watch failed to start.";
+ success = false;
+ }
+ if (!hosts_watcher_.Watch(FilePath(kFilePathHosts),
+ base::Bind(&Core::OnHostsChanged,
+ base::Unretained(this)))) {
+ LOG(ERROR) << "DNS hosts watch failed to start.";
+ success = false;
+ }
+ return success;
+ }
+
+ private:
+ void OnConfigChanged(bool succeeded) {
+ if (succeeded) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_SETTINGS);
+ } else {
+ LOG(ERROR) << "DNS config watch failed.";
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED);
+ }
+ }
+
+ void OnHostsChanged(bool succeeded) {
+ if (succeeded) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_HOSTS);
+ } else {
+ LOG(ERROR) << "DNS hosts watch failed.";
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED);
+ }
+ }
+
+ ConfigWatcher config_watcher_;
+ FilePathWatcherWrapper hosts_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+DnsConfigWatcher::DnsConfigWatcher() {}
+
+DnsConfigWatcher::~DnsConfigWatcher() {}
+
+void DnsConfigWatcher::Init() {
+ core_.reset(new Core());
+ if (core_->Watch()) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED);
+ }
+ // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
+}
+
+void DnsConfigWatcher::CleanUp() {
+ core_.reset();
+}
+
+} // namespace internal
+} // namespace net
diff --git a/net/dns/dns_config_watcher_win.cc b/net/dns/dns_config_watcher_win.cc
new file mode 100644
index 0000000..47a12d0
--- /dev/null
+++ b/net/dns/dns_config_watcher_win.cc
@@ -0,0 +1,167 @@
+// 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 "net/dns/dns_config_watcher.h"
+
+#include <winsock2.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/win/object_watcher.h"
+#include "base/win/registry.h"
+#include "net/base/net_util.h"
+#include "net/base/network_change_notifier.h"
+#include "net/dns/dns_config_service_win.h"
+#include "net/dns/file_path_watcher_wrapper.h"
+
+namespace net {
+namespace internal {
+
+namespace {
+
+// Watches a single registry key for changes.
+class RegistryWatcher : public base::win::ObjectWatcher::Delegate,
+ public base::NonThreadSafe {
+ public:
+ typedef base::Callback<void(bool succeeded)> CallbackType;
+ RegistryWatcher() {}
+
+ bool Watch(const wchar_t* key, const CallbackType& callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!callback.is_null());
+ DCHECK(callback_.is_null());
+ callback_ = callback;
+ if (key_.Open(HKEY_LOCAL_MACHINE, key, KEY_NOTIFY) != ERROR_SUCCESS)
+ return false;
+ if (key_.StartWatching() != ERROR_SUCCESS)
+ return false;
+ if (!watcher_.StartWatching(key_.watch_event(), this))
+ return false;
+ return true;
+ }
+
+ virtual void OnObjectSignaled(HANDLE object) OVERRIDE {
+ DCHECK(CalledOnValidThread());
+ bool succeeded = (key_.StartWatching() == ERROR_SUCCESS) &&
+ watcher_.StartWatching(key_.watch_event(), this);
+ if (!succeeded) {
+ if (key_.Valid()) {
+ watcher_.StopWatching();
+ key_.StopWatching();
+ key_.Close();
+ }
+ }
+ if (!callback_.is_null())
+ callback_.Run(succeeded);
+ }
+
+ private:
+ CallbackType callback_;
+ base::win::RegKey key_;
+ base::win::ObjectWatcher watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryWatcher);
+};
+
+} // namespace
+
+// Watches registry for changes. Setting up watches requires IO loop.
+class DnsConfigWatcher::Core {
+ public:
+ Core() {}
+ ~Core() {}
+
+ bool Watch() {
+ RegistryWatcher::CallbackType callback =
+ base::Bind(&Core::OnRegistryChanged, base::Unretained(this));
+
+ bool success = true;
+
+ // The Tcpip key must be present.
+ if (!tcpip_watcher_.Watch(kTcpipPath, callback)) {
+ LOG(ERROR) << "DNS registry watch failed to start.";
+ success = false;
+ }
+
+ // Watch for IPv6 nameservers.
+ tcpip6_watcher_.Watch(kTcpip6Path, callback);
+
+ // DNS suffix search list and devolution can be configured via group
+ // policy which sets this registry key. If the key is missing, the policy
+ // does not apply, and the DNS client uses Tcpip and Dnscache settings.
+ // If a policy is installed, DnsConfigService will need to be restarted.
+ // BUG=99509
+
+ dnscache_watcher_.Watch(kDnscachePath, callback);
+ policy_watcher_.Watch(kPolicyPath, callback);
+
+ if (!hosts_watcher_.Watch(GetHostsPath(),
+ base::Bind(&Core::OnHostsChanged,
+ base::Unretained(this)))) {
+ LOG(ERROR) << "DNS hosts watch failed to start.";
+ success = false;
+ }
+ return success;
+ }
+
+ private:
+ void OnRegistryChanged(bool succeeded) {
+ if (succeeded) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_SETTINGS);
+ } else {
+ LOG(ERROR) << "DNS config watch failed.";
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED);
+ }
+ }
+
+ void OnHostsChanged(bool succeeded) {
+ if (succeeded) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_HOSTS);
+ } else {
+ LOG(ERROR) << "DNS hosts watch failed.";
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_FAILED);
+ }
+ }
+
+ RegistryWatcher tcpip_watcher_;
+ RegistryWatcher tcpip6_watcher_;
+ RegistryWatcher dnscache_watcher_;
+ RegistryWatcher policy_watcher_;
+ FilePathWatcherWrapper hosts_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+DnsConfigWatcher::DnsConfigWatcher() {}
+
+DnsConfigWatcher::~DnsConfigWatcher() {}
+
+void DnsConfigWatcher::Init() {
+ core_.reset(new Core());
+ if (core_->Watch()) {
+ NetworkChangeNotifier::NotifyObserversOfDNSChange(
+ NetworkChangeNotifier::CHANGE_DNS_WATCH_STARTED);
+ }
+ // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
+}
+
+void DnsConfigWatcher::CleanUp() {
+ core_.reset();
+}
+
+} // namespace internal
+} // namespace net
+
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 0cbdf94..051f595 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -163,8 +163,18 @@ scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config) {
return scoped_ptr<DnsClient>(new MockDnsClient(config));
}
-void MockDnsConfigService::Watch(const CallbackType& callback) {
- set_callback(callback);
+MockDnsConfigService::~MockDnsConfigService() {
+}
+
+void MockDnsConfigService::OnDNSChanged(unsigned detail) {
+}
+
+void MockDnsConfigService::ChangeConfig(const DnsConfig& config) {
+ DnsConfigService::OnConfigRead(config);
+}
+
+void MockDnsConfigService::ChangeHosts(const DnsHosts& hosts) {
+ DnsConfigService::OnHostsRead(hosts);
}
} // namespace net
diff --git a/net/dns/dns_test_util.h b/net/dns/dns_test_util.h
index e7bd457..3ca6e11 100644
--- a/net/dns/dns_test_util.h
+++ b/net/dns/dns_test_util.h
@@ -172,18 +172,14 @@ scoped_ptr<DnsClient> CreateMockDnsClient(const DnsConfig& config);
class MockDnsConfigService : public DnsConfigService {
public:
- virtual ~MockDnsConfigService() {}
+ virtual ~MockDnsConfigService();
- virtual void Watch(const CallbackType& callback) OVERRIDE;
+ // NetworkChangeNotifier::DNSObserver:
+ virtual void OnDNSChanged(unsigned detail) OVERRIDE;
// Expose the protected methods for tests.
- void ChangeConfig(const DnsConfig& config) {
- DnsConfigService::OnConfigRead(config);
- }
-
- void ChangeHosts(const DnsHosts& hosts) {
- DnsConfigService::OnHostsRead(hosts);
- }
+ void ChangeConfig(const DnsConfig& config);
+ void ChangeHosts(const DnsHosts& hosts);
};
diff --git a/net/net.gyp b/net/net.gyp
index 8cd4ac6..7bf527f 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -364,6 +364,9 @@
'dns/dns_config_service_posix.h',
'dns/dns_config_service_win.cc',
'dns/dns_config_service_win.h',
+ 'dns/dns_config_watcher.h',
+ 'dns/dns_config_watcher_posix.cc',
+ 'dns/dns_config_watcher_win.cc',
'dns/dns_hosts.cc',
'dns/dns_hosts.h',
'dns/dns_protocol.h',