[email protected] | 9fc4416 | 2012-01-23 22:56:41 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. | ||||
4 | |||||
tfarina | 47598f04 | 2015-10-07 23:08:35 | [diff] [blame] | 5 | #include "net/dns/dns_reloader.h" |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 6 | |
[email protected] | ae7c9f4 | 2011-11-21 11:41:16 | [diff] [blame] | 7 | #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && \ |
Sergey Ulanov | 4908557 | 2017-07-10 23:25:46 | [diff] [blame] | 8 | !defined(OS_ANDROID) && !defined(OS_FUCHSIA) |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 9 | |
10 | #include <resolv.h> | ||||
11 | |||||
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 12 | #include "base/lazy_instance.h" |
13 | #include "base/logging.h" | ||||
Avi Drissman | 13fc893 | 2015-12-20 04:40:46 | [diff] [blame] | 14 | #include "base/macros.h" |
Carlos Caballero | feae87148 | 2019-08-12 14:14:43 | [diff] [blame] | 15 | #include "base/message_loop/message_loop_current.h" |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 16 | #include "base/synchronization/lock.h" |
Gabriel Charette | f9d72da | 2019-01-24 13:34:32 | [diff] [blame] | 17 | #include "base/threading/thread_local.h" |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 18 | #include "net/base/network_change_notifier.h" |
19 | |||||
ttuttle | 859dc7a | 2015-04-23 19:42:29 | [diff] [blame] | 20 | namespace net { |
21 | |||||
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 22 | namespace { |
23 | |||||
24 | // On Linux/BSD, changes to /etc/resolv.conf can go unnoticed thus resulting | ||||
25 | // in DNS queries failing either because nameservers are unknown on startup | ||||
26 | // or because nameserver info has changed as a result of e.g. connecting to | ||||
27 | // a new network. Some distributions patch glibc to stat /etc/resolv.conf | ||||
28 | // to try to automatically detect such changes but these patches are not | ||||
29 | // universal and even patched systems such as Jaunty appear to need calls | ||||
30 | // to res_ninit to reload the nameserver information in different threads. | ||||
31 | // | ||||
32 | // To fix this, on systems with FilePathWatcher support, we use | ||||
33 | // NetworkChangeNotifier::DNSObserver to monitor /etc/resolv.conf to | ||||
34 | // enable us to respond to DNS changes and reload the resolver state. | ||||
35 | // | ||||
36 | // OpenBSD does not have thread-safe res_ninit/res_nclose so we can't do | ||||
37 | // the same trick there and most *BSD's don't yet have support for | ||||
38 | // FilePathWatcher (but perhaps the new kqueue mac code just needs to be | ||||
39 | // ported to *BSD to support that). | ||||
[email protected] | ae7c9f4 | 2011-11-21 11:41:16 | [diff] [blame] | 40 | // |
41 | // Android does not have /etc/resolv.conf. The system takes care of nameserver | ||||
42 | // changes, so none of this is needed. | ||||
Eric Orth | a3559ca | 2019-09-05 23:40:54 | [diff] [blame] | 43 | // |
44 | // TODO(crbug.com/971411): Convert to SystemDnsConfigChangeNotifier because this | ||||
45 | // really only cares about system DNS config changes, not Chrome effective | ||||
46 | // config changes. | ||||
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 47 | |
ttuttle | 859dc7a | 2015-04-23 19:42:29 | [diff] [blame] | 48 | class DnsReloader : public NetworkChangeNotifier::DNSObserver { |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 49 | public: |
[email protected] | 446df295 | 2012-02-28 07:22:51 | [diff] [blame] | 50 | // NetworkChangeNotifier::DNSObserver: |
dcheng | 67be2b1f | 2014-10-27 21:47:29 | [diff] [blame] | 51 | void OnDNSChanged() override { |
eroman | 6f506dcc | 2017-06-21 23:40:59 | [diff] [blame] | 52 | base::AutoLock lock(lock_); |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 53 | resolver_generation_++; |
54 | } | ||||
55 | |||||
56 | void MaybeReload() { | ||||
Gabriel Charette | f9d72da | 2019-01-24 13:34:32 | [diff] [blame] | 57 | ReloadState* reload_state = tls_reload_state_.Get(); |
eroman | 6f506dcc | 2017-06-21 23:40:59 | [diff] [blame] | 58 | base::AutoLock lock(lock_); |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 59 | |
60 | if (!reload_state) { | ||||
Gabriel Charette | f9d72da | 2019-01-24 13:34:32 | [diff] [blame] | 61 | auto new_reload_state = std::make_unique<ReloadState>(); |
62 | new_reload_state->resolver_generation = resolver_generation_; | ||||
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 63 | res_ninit(&_res); |
Gabriel Charette | f9d72da | 2019-01-24 13:34:32 | [diff] [blame] | 64 | tls_reload_state_.Set(std::move(new_reload_state)); |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 65 | } else if (reload_state->resolver_generation != resolver_generation_) { |
66 | reload_state->resolver_generation = resolver_generation_; | ||||
67 | // It is safe to call res_nclose here since we know res_ninit will have | ||||
68 | // been called above. | ||||
69 | res_nclose(&_res); | ||||
70 | res_ninit(&_res); | ||||
71 | } | ||||
72 | } | ||||
73 | |||||
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 74 | private: |
Gabriel Charette | f9d72da | 2019-01-24 13:34:32 | [diff] [blame] | 75 | struct ReloadState { |
76 | ~ReloadState() { res_nclose(&_res); } | ||||
77 | |||||
78 | int resolver_generation; | ||||
79 | }; | ||||
80 | |||||
Alexander Khaustov | fd6d802 | 2018-02-19 19:18:49 | [diff] [blame] | 81 | DnsReloader() { NetworkChangeNotifier::AddDNSObserver(this); } |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 82 | |
dcheng | 67be2b1f | 2014-10-27 21:47:29 | [diff] [blame] | 83 | ~DnsReloader() override { |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 84 | NOTREACHED(); // LeakyLazyInstance is not destructed. |
85 | } | ||||
86 | |||||
87 | base::Lock lock_; // Protects resolver_generation_. | ||||
Alexander Khaustov | fd6d802 | 2018-02-19 19:18:49 | [diff] [blame] | 88 | int resolver_generation_ = 0; |
scottmg | 5e65e3a | 2017-03-08 08:48:46 | [diff] [blame] | 89 | friend struct base::LazyInstanceTraitsBase<DnsReloader>; |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 90 | |
91 | // We use thread local storage to identify which ReloadState to interact with. | ||||
Gabriel Charette | f9d72da | 2019-01-24 13:34:32 | [diff] [blame] | 92 | base::ThreadLocalOwnedPointer<ReloadState> tls_reload_state_; |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 93 | |
94 | DISALLOW_COPY_AND_ASSIGN(DnsReloader); | ||||
95 | }; | ||||
96 | |||||
[email protected] | 9fc4416 | 2012-01-23 22:56:41 | [diff] [blame] | 97 | base::LazyInstance<DnsReloader>::Leaky |
[email protected] | 6de0fd1d | 2011-11-15 13:31:49 | [diff] [blame] | 98 | g_dns_reloader = LAZY_INSTANCE_INITIALIZER; |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 99 | |
100 | } // namespace | ||||
101 | |||||
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 102 | void EnsureDnsReloaderInit() { |
pkasting | 47ceb87 | 2014-10-09 18:53:22 | [diff] [blame] | 103 | g_dns_reloader.Pointer(); |
[email protected] | 46018c9d | 2011-09-06 03:42:34 | [diff] [blame] | 104 | } |
105 | |||||
106 | void DnsReloaderMaybeReload() { | ||||
107 | // This routine can be called by any of the DNS worker threads. | ||||
108 | DnsReloader* dns_reloader = g_dns_reloader.Pointer(); | ||||
109 | dns_reloader->MaybeReload(); | ||||
110 | } | ||||
111 | |||||
112 | } // namespace net | ||||
113 | |||||
[email protected] | ae7c9f4 | 2011-11-21 11:41:16 | [diff] [blame] | 114 | #endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && |
115 | // !defined(OS_ANDROID) |