Skip to content

Commit

Permalink
Linux: Monitor resolv.conf for changes and use that to reload resolver.
Browse files Browse the repository at this point in the history
BUG=67734
TEST=manual testing by poking at resolv.conf

Review URL: http://codereview.chromium.org/6903061

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99666 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
craig.schlenter@chromium.org committed Sep 5, 2011
1 parent 37dd9ae commit 74482ef
Show file tree
Hide file tree
Showing 13 changed files with 332 additions and 199 deletions.
100 changes: 0 additions & 100 deletions net/base/dns_reload_timer.cc

This file was deleted.

22 changes: 0 additions & 22 deletions net/base/dns_reload_timer.h

This file was deleted.

122 changes: 122 additions & 0 deletions net/base/dns_reloader.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (c) 2011 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/base/dns_reloader.h"

#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)

#include <resolv.h>

#include "base/basictypes.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_local_storage.h"
#include "net/base/network_change_notifier.h"

namespace {

// On Linux/BSD, changes to /etc/resolv.conf can go unnoticed thus resulting
// in DNS queries failing either because nameservers are unknown on startup
// or because nameserver info has changed as a result of e.g. connecting to
// a new network. Some distributions patch glibc to stat /etc/resolv.conf
// to try to automatically detect such changes but these patches are not
// universal and even patched systems such as Jaunty appear to need calls
// to res_ninit to reload the nameserver information in different threads.
//
// To fix this, on systems with FilePathWatcher support, we use
// NetworkChangeNotifier::DNSObserver to monitor /etc/resolv.conf to
// enable us to respond to DNS changes and reload the resolver state.
//
// OpenBSD does not have thread-safe res_ninit/res_nclose so we can't do
// the same trick there and most *BSD's don't yet have support for
// FilePathWatcher (but perhaps the new kqueue mac code just needs to be
// ported to *BSD to support that).

class DnsReloader : public net::NetworkChangeNotifier::DNSObserver {
public:
struct ReloadState {
int resolver_generation;
};

// NetworkChangeNotifier::OnDNSChanged methods:
virtual void OnDNSChanged() OVERRIDE {
DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_IO);
base::AutoLock l(lock_);
resolver_generation_++;
}

void MaybeReload() {
ReloadState* reload_state = static_cast<ReloadState*>(tls_index_.Get());
base::AutoLock l(lock_);

if (!reload_state) {
reload_state = new ReloadState();
reload_state->resolver_generation = resolver_generation_;
res_ninit(&_res);
tls_index_.Set(reload_state);
} else if (reload_state->resolver_generation != resolver_generation_) {
reload_state->resolver_generation = resolver_generation_;
// It is safe to call res_nclose here since we know res_ninit will have
// been called above.
res_nclose(&_res);
res_ninit(&_res);
}
}

// Free the allocated state.
static void SlotReturnFunction(void* data) {
ReloadState* reload_state = static_cast<ReloadState*>(data);
if (reload_state)
res_nclose(&_res);
delete reload_state;
}

private:
DnsReloader() : resolver_generation_(0) {
tls_index_.Initialize(SlotReturnFunction);
net::NetworkChangeNotifier::AddDNSObserver(this);
}

~DnsReloader() {
NOTREACHED(); // LeakyLazyInstance is not destructed.
}

base::Lock lock_; // Protects resolver_generation_.
int resolver_generation_;
friend struct base::DefaultLazyInstanceTraits<DnsReloader>;

// We use thread local storage to identify which ReloadState to interact with.
static base::ThreadLocalStorage::Slot tls_index_ ;

DISALLOW_COPY_AND_ASSIGN(DnsReloader);
};

// A TLS slot to the ReloadState for the current thread.
// static
base::ThreadLocalStorage::Slot DnsReloader::tls_index_(
base::LINKER_INITIALIZED);

base::LazyInstance<DnsReloader,
base::LeakyLazyInstanceTraits<DnsReloader> >
g_dns_reloader(base::LINKER_INITIALIZED);

} // namespace

namespace net {

void EnsureDnsReloaderInit() {
DnsReloader* t ALLOW_UNUSED = g_dns_reloader.Pointer();
}

void DnsReloaderMaybeReload() {
// This routine can be called by any of the DNS worker threads.
DnsReloader* dns_reloader = g_dns_reloader.Pointer();
dns_reloader->MaybeReload();
}

} // namespace net

#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
24 changes: 24 additions & 0 deletions net/base/dns_reloader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2011 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_BASE_DNS_RELOADER_H_
#define NET_BASE_DNS_RELOADER_H_
#pragma once

#include "build/build_config.h"

#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
namespace net {

// Call on the network thread before calling DnsReloaderMaybeReload() anywhere.
void EnsureDnsReloaderInit();

// Call on the worker thread before doing a DNS lookup to reload the
// resolver for that thread by doing res_ninit() if required.
void DnsReloaderMaybeReload();

} // namespace net
#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)

#endif // NET_BASE_DNS_RELOADER_H_
18 changes: 5 additions & 13 deletions net/base/dnsrr_resolver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include "base/synchronization/lock.h"
#include "base/task.h"
#include "base/threading/worker_pool.h"
#include "net/base/dns_reload_timer.h"
#include "net/base/dns_reloader.h"
#include "net/base/dns_util.h"
#include "net/base/net_errors.h"

Expand Down Expand Up @@ -184,26 +184,18 @@ class RRResolverWorker {
}

bool r = true;
#if defined(OS_MACOSX) || defined(OS_OPENBSD)
if ((_res.options & RES_INIT) == 0) {
if (res_ninit(&_res) != 0)
r = false;
}
#else
DnsReloaderMaybeReload();
#endif

if (r) {
unsigned long saved_options = _res.options;
r = Do();

#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
if (!r && DnsReloadTimerHasExpired()) {
// When there's no network connection, _res may not be initialized by
// getaddrinfo. Therefore, we call res_nclose only when there are ns
// entries.
if (_res.nscount > 0)
res_nclose(&_res);
if (res_ninit(&_res) == 0)
r = Do();
}
#endif
_res.options = saved_options;
}

Expand Down
21 changes: 21 additions & 0 deletions net/base/host_resolver_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "base/values.h"
#include "net/base/address_list.h"
#include "net/base/address_list_net_log_param.h"
#include "net/base/dns_reloader.h"
#include "net/base/host_port_pair.h"
#include "net/base/host_resolver_proc.h"
#include "net/base/net_errors.h"
Expand Down Expand Up @@ -1090,6 +1091,10 @@ HostResolverImpl::HostResolverImpl(
additional_resolver_flags_ |= HOST_RESOLVER_LOOPBACK_ONLY;
#endif
NetworkChangeNotifier::AddIPAddressObserver(this);
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
EnsureDnsReloaderInit();
NetworkChangeNotifier::AddDNSObserver(this);
#endif
}

HostResolverImpl::~HostResolverImpl() {
Expand All @@ -1104,6 +1109,9 @@ HostResolverImpl::~HostResolverImpl() {
cur_completing_job_->Cancel();

NetworkChangeNotifier::RemoveIPAddressObserver(this);
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
NetworkChangeNotifier::RemoveDNSObserver(this);
#endif

// Delete the job pools.
for (size_t i = 0u; i < arraysize(job_pools_); ++i)
Expand Down Expand Up @@ -1644,4 +1652,17 @@ void HostResolverImpl::OnIPAddressChanged() {
// |this| may be deleted inside AbortAllInProgressJobs().
}

void HostResolverImpl::OnDNSChanged() {
// If the DNS server has changed, existing cached info could be wrong so we
// have to drop our internal cache :( Note that OS level DNS caches, such
// as NSCD's cache should be dropped automatically by the OS when
// resolv.conf changes so we don't need to do anything to clear that cache.
if (cache_.get())
cache_->clear();
// Existing jobs will have been sent to the original server so they need to
// be aborted. TODO(Craig): Should these jobs be restarted?
AbortAllInProgressJobs();
// |this| may be deleted inside AbortAllInProgressJobs().
}

} // namespace net
6 changes: 5 additions & 1 deletion net/base/host_resolver_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ namespace net {
class NET_EXPORT HostResolverImpl
: public HostResolver,
NON_EXPORTED_BASE(public base::NonThreadSafe),
public NetworkChangeNotifier::IPAddressObserver {
public NetworkChangeNotifier::IPAddressObserver,
public NetworkChangeNotifier::DNSObserver {
public:
// The index into |job_pools_| for the various job pools. Pools with a higher
// index have lower priority.
Expand Down Expand Up @@ -304,6 +305,9 @@ class NET_EXPORT HostResolverImpl
retry_factor_ = retry_factor;
}

// NetworkChangeNotifier::OnDNSChanged methods:
virtual void OnDNSChanged();

// Cache of host resolution results.
scoped_ptr<HostCache> cache_;

Expand Down
Loading

0 comments on commit 74482ef

Please sign in to comment.