1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/dns/dns_config_watcher_mac.h"
6
7#include <dlfcn.h>
8
9#include "base/lazy_instance.h"
10#include "third_party/apple_apsl/dnsinfo.h"
11
12namespace {
13
14// dnsinfo symbols are available via libSystem.dylib, but can also be present in
15// SystemConfiguration.framework. To avoid confusion, load them explicitly from
16// libSystem.dylib.
17class DnsInfoApi {
18 public:
19  typedef const char* (*dns_configuration_notify_key_t)();
20  typedef dns_config_t* (*dns_configuration_copy_t)();
21  typedef void (*dns_configuration_free_t)(dns_config_t*);
22
23  DnsInfoApi()
24      : dns_configuration_notify_key(NULL),
25        dns_configuration_copy(NULL),
26        dns_configuration_free(NULL) {
27    handle_ = dlopen("/usr/lib/libSystem.dylib",
28                     RTLD_LAZY | RTLD_NOLOAD);
29    if (!handle_)
30      return;
31    dns_configuration_notify_key =
32        reinterpret_cast<dns_configuration_notify_key_t>(
33            dlsym(handle_, "dns_configuration_notify_key"));
34    dns_configuration_copy =
35        reinterpret_cast<dns_configuration_copy_t>(
36            dlsym(handle_, "dns_configuration_copy"));
37    dns_configuration_free =
38        reinterpret_cast<dns_configuration_free_t>(
39            dlsym(handle_, "dns_configuration_free"));
40  }
41
42  ~DnsInfoApi() {
43    if (handle_)
44      dlclose(handle_);
45  }
46
47  dns_configuration_notify_key_t dns_configuration_notify_key;
48  dns_configuration_copy_t dns_configuration_copy;
49  dns_configuration_free_t dns_configuration_free;
50
51 private:
52  void* handle_;
53};
54
55const DnsInfoApi& GetDnsInfoApi() {
56  static base::LazyInstance<DnsInfoApi>::Leaky api = LAZY_INSTANCE_INITIALIZER;
57  return api.Get();
58}
59
60struct DnsConfigTDeleter {
61  inline void operator()(dns_config_t* ptr) const {
62    if (GetDnsInfoApi().dns_configuration_free)
63      GetDnsInfoApi().dns_configuration_free(ptr);
64  }
65};
66
67}  // namespace
68
69namespace net {
70namespace internal {
71
72bool DnsConfigWatcher::Watch(
73    const base::Callback<void(bool succeeded)>& callback) {
74  if (!GetDnsInfoApi().dns_configuration_notify_key)
75    return false;
76  return watcher_.Watch(GetDnsInfoApi().dns_configuration_notify_key(),
77                        callback);
78}
79
80// static
81ConfigParsePosixResult DnsConfigWatcher::CheckDnsConfig() {
82  if (!GetDnsInfoApi().dns_configuration_copy)
83    return CONFIG_PARSE_POSIX_NO_DNSINFO;
84  scoped_ptr<dns_config_t, DnsConfigTDeleter> dns_config(
85      GetDnsInfoApi().dns_configuration_copy());
86  if (!dns_config)
87    return CONFIG_PARSE_POSIX_NO_DNSINFO;
88
89  // TODO(szym): Parse dns_config_t for resolvers rather than res_state.
90  // DnsClient can't handle domain-specific unscoped resolvers.
91  unsigned num_resolvers = 0;
92  for (int i = 0; i < dns_config->n_resolver; ++i) {
93    dns_resolver_t* resolver = dns_config->resolver[i];
94    if (!resolver->n_nameserver)
95      continue;
96    if (resolver->options && !strcmp(resolver->options, "mdns"))
97      continue;
98    ++num_resolvers;
99  }
100  if (num_resolvers > 1)
101    return CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS;
102  return CONFIG_PARSE_POSIX_OK;
103}
104
105}  // namespace internal
106}  // namespace net
107