File: dns_reloader.cc

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (150 lines) | stat: -rw-r--r-- 5,123 bytes parent folder | download | duplicates (2)
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)