1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/dns/dns_reloader.h"
#include "build/build_config.h"
// If we're not on a POSIX system, it's not even safe to try to include resolv.h
// - there's not guarantee it exists at all. :(
#if BUILDFLAG(IS_POSIX)
#include <resolv.h>
// This code only works on systems where the C library provides res_ninit(3) and
// res_nclose(3), which requires __RES >= 19991006 (most libcs at this point,
// but not all).
//
// This code is also not used on either macOS or iOS, even though both platforms
// have res_ninit(3). On iOS, /etc/hosts is immutable so there's no reason for
// us to watch it; on macOS, there is a system mechanism for listening to DNS
// changes which does not require use to do this kind of reloading. See
// //net/dns/dns_config_watcher_mac.cc.
//
// It *also* is not used on Android, because Android handles nameserver changes
// for us and has no /etc/resolv.conf. Despite that, Bionic does export these
// interfaces, so we need to not use them.
//
// It is also also not used on Fuchsia. Regrettably, Fuchsia's resolv.h has
// __RES set to 19991006, but does not actually provide res_ninit(3). This was
// an old musl bug that was fixed by musl c8fdcfe5, but Fuchsia's SDK doesn't
// have that change.
#if defined(__RES) && __RES >= 19991006 && !BUILDFLAG(IS_APPLE) && \
!BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
// We define this so we don't need to restate the complex condition here twice
// below - it would be easy for the copies below to get out of sync.
#define USE_RES_NINIT
#endif // defined(_RES) && ...
#endif // BUILDFLAG(IS_POSIX)
#if defined(USE_RES_NINIT)
#include "base/lazy_instance.h"
#include "base/notreached.h"
#include "base/synchronization/lock.h"
#include "base/task/current_thread.h"
#include "base/threading/thread_local.h"
#include "net/base/network_change_notifier.h"
namespace net {
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.
//
// Android does not have /etc/resolv.conf. The system takes care of nameserver
// changes, so none of this is needed.
//
// TODO(crbug.com/971411): Convert to SystemDnsConfigChangeNotifier because this
// really only cares about system DNS config changes, not Chrome effective
// config changes.
class DnsReloader : public NetworkChangeNotifier::DNSObserver {
public:
DnsReloader(const DnsReloader&) = delete;
DnsReloader& operator=(const DnsReloader&) = delete;
// NetworkChangeNotifier::DNSObserver:
void OnDNSChanged() override {
base::AutoLock lock(lock_);
resolver_generation_++;
}
void MaybeReload() {
ReloadState* reload_state = tls_reload_state_.Get();
base::AutoLock lock(lock_);
if (!reload_state) {
auto new_reload_state = std::make_unique<ReloadState>();
new_reload_state->resolver_generation = resolver_generation_;
res_ninit(&_res);
tls_reload_state_.Set(std::move(new_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);
}
}
private:
struct ReloadState {
~ReloadState() { res_nclose(&_res); }
int resolver_generation;
};
DnsReloader() { NetworkChangeNotifier::AddDNSObserver(this); }
~DnsReloader() override {
NOTREACHED(); // LeakyLazyInstance is not destructed.
}
base::Lock lock_; // Protects resolver_generation_.
int resolver_generation_ = 0;
friend struct base::LazyInstanceTraitsBase<DnsReloader>;
// We use thread local storage to identify which ReloadState to interact with.
base::ThreadLocalOwnedPointer<ReloadState> tls_reload_state_;
};
base::LazyInstance<DnsReloader>::Leaky
g_dns_reloader = LAZY_INSTANCE_INITIALIZER;
} // namespace
void EnsureDnsReloaderInit() {
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
#else // !USE_RES_NINIT
namespace net {
void EnsureDnsReloaderInit() {}
void DnsReloaderMaybeReload() {}
} // namespace net
#endif // defined(USE_RES_NINIT)
|