1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "net/dns/dns_config_watcher_mac.h"
11
12 #include <dlfcn.h>
13
14 #include "base/compiler_specific.h"
15 #include "base/lazy_instance.h"
16 #include "base/memory/raw_ptr.h"
17 #include "third_party/apple_apsl/dnsinfo.h"
18
19 namespace {
20
21 // dnsinfo symbols are available via libSystem.dylib, but can also be present in
22 // SystemConfiguration.framework. To avoid confusion, load them explicitly from
23 // libSystem.dylib.
24 class DnsInfoApi {
25 public:
26 typedef const char* (*dns_configuration_notify_key_t)();
27 typedef dns_config_t* (*dns_configuration_copy_t)();
28 typedef void (*dns_configuration_free_t)(dns_config_t*);
29
DnsInfoApi()30 DnsInfoApi() {
31 handle_ = dlopen("/usr/lib/libSystem.dylib",
32 RTLD_LAZY | RTLD_NOLOAD);
33 if (!handle_)
34 return;
35 dns_configuration_notify_key =
36 reinterpret_cast<dns_configuration_notify_key_t>(
37 dlsym(handle_, "dns_configuration_notify_key"));
38 dns_configuration_copy =
39 reinterpret_cast<dns_configuration_copy_t>(
40 dlsym(handle_, "dns_configuration_copy"));
41 dns_configuration_free =
42 reinterpret_cast<dns_configuration_free_t>(
43 dlsym(handle_, "dns_configuration_free"));
44 }
45
~DnsInfoApi()46 ~DnsInfoApi() {
47 if (handle_)
48 dlclose(handle_);
49 }
50
51 dns_configuration_notify_key_t dns_configuration_notify_key = nullptr;
52 dns_configuration_copy_t dns_configuration_copy = nullptr;
53 dns_configuration_free_t dns_configuration_free = nullptr;
54
55 private:
56 raw_ptr<void> handle_;
57 };
58
GetDnsInfoApi()59 const DnsInfoApi& GetDnsInfoApi() {
60 static base::LazyInstance<DnsInfoApi>::Leaky api = LAZY_INSTANCE_INITIALIZER;
61 return api.Get();
62 }
63
64 struct DnsConfigTDeleter {
operator ()__anona84d699c0111::DnsConfigTDeleter65 inline void operator()(dns_config_t* ptr) const {
66 if (GetDnsInfoApi().dns_configuration_free)
67 GetDnsInfoApi().dns_configuration_free(ptr);
68 }
69 };
70
71 } // namespace
72
73 namespace net {
74 namespace internal {
75
Watch(const base::RepeatingCallback<void (bool succeeded)> & callback)76 bool DnsConfigWatcher::Watch(
77 const base::RepeatingCallback<void(bool succeeded)>& callback) {
78 if (!GetDnsInfoApi().dns_configuration_notify_key)
79 return false;
80 return watcher_.Watch(GetDnsInfoApi().dns_configuration_notify_key(),
81 callback);
82 }
83
84 // `dns_config->resolver` contains an array of pointers but is not correctly
85 // aligned. Pointers, on 64-bit, have 8-byte alignment but everything in
86 // dnsinfo.h is modified to have 4-byte alignment with pragma pack. Those
87 // pragmas are not sufficient to realign the `dns_resolver_t*` elements of
88 // `dns_config->resolver`. The header would need to be patched to replace
89 // `dns_resolver_t**` with, say, a `dns_resolver_ptr*` where `dns_resolver_ptr`
90 // is a less aligned `dns_resolver_t*` type.
91 NO_SANITIZE("alignment")
CheckDnsConfig(bool & out_unhandled_options)92 bool DnsConfigWatcher::CheckDnsConfig(bool& out_unhandled_options) {
93 if (!GetDnsInfoApi().dns_configuration_copy)
94 return false;
95 std::unique_ptr<dns_config_t, DnsConfigTDeleter> dns_config(
96 GetDnsInfoApi().dns_configuration_copy());
97 if (!dns_config)
98 return false;
99
100 // TODO(szym): Parse dns_config_t for resolvers rather than res_state.
101 // DnsClient can't handle domain-specific unscoped resolvers.
102 unsigned num_resolvers = 0;
103 for (int i = 0; i < dns_config->n_resolver; ++i) {
104 dns_resolver_t* resolver = dns_config->resolver[i];
105 if (!resolver->n_nameserver)
106 continue;
107 if (resolver->options && !strcmp(resolver->options, "mdns"))
108 continue;
109 ++num_resolvers;
110 }
111
112 out_unhandled_options = num_resolvers > 1;
113 return true;
114 }
115
116 } // namespace internal
117 } // namespace net
118