Home | History | Annotate | Download | only in dns
      1 // Copyright (c) 2012 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_service_posix.h"
      6 
      7 #include <string>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/bind.h"
     11 #include "base/files/file_path.h"
     12 #include "base/files/file_path_watcher.h"
     13 #include "base/lazy_instance.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/metrics/histogram.h"
     16 #include "base/time/time.h"
     17 #include "net/base/ip_endpoint.h"
     18 #include "net/base/net_util.h"
     19 #include "net/dns/dns_hosts.h"
     20 #include "net/dns/dns_protocol.h"
     21 #include "net/dns/notify_watcher_mac.h"
     22 #include "net/dns/serial_worker.h"
     23 
     24 #if defined(OS_MACOSX) && !defined(OS_IOS)
     25 #include "net/dns/dns_config_watcher_mac.h"
     26 #endif
     27 
     28 #if defined(OS_ANDROID)
     29 #include <sys/system_properties.h>
     30 #include "net/base/network_change_notifier.h"
     31 #endif
     32 
     33 namespace net {
     34 
     35 namespace internal {
     36 
     37 namespace {
     38 
     39 #if !defined(OS_ANDROID)
     40 const base::FilePath::CharType* kFilePathHosts =
     41     FILE_PATH_LITERAL("/etc/hosts");
     42 #else
     43 const base::FilePath::CharType* kFilePathHosts =
     44     FILE_PATH_LITERAL("/system/etc/hosts");
     45 #endif
     46 
     47 #if defined(OS_IOS)
     48 
     49 // There is no public API to watch the DNS configuration on iOS.
     50 class DnsConfigWatcher {
     51  public:
     52   typedef base::Callback<void(bool succeeded)> CallbackType;
     53 
     54   bool Watch(const CallbackType& callback) {
     55     return false;
     56   }
     57 };
     58 
     59 #elif defined(OS_ANDROID)
     60 // On Android, assume DNS config may have changed on every network change.
     61 class DnsConfigWatcher : public NetworkChangeNotifier::NetworkChangeObserver {
     62  public:
     63   DnsConfigWatcher() {
     64     NetworkChangeNotifier::AddNetworkChangeObserver(this);
     65   }
     66 
     67   virtual ~DnsConfigWatcher() {
     68     NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
     69   }
     70 
     71   bool Watch(const base::Callback<void(bool succeeded)>& callback) {
     72     callback_ = callback;
     73     return true;
     74   }
     75 
     76   virtual void OnNetworkChanged(NetworkChangeNotifier::ConnectionType type)
     77       OVERRIDE {
     78     if (!callback_.is_null() && type != NetworkChangeNotifier::CONNECTION_NONE)
     79       callback_.Run(true);
     80   }
     81 
     82  private:
     83   base::Callback<void(bool succeeded)> callback_;
     84 };
     85 #elif !defined(OS_MACOSX)
     86 // DnsConfigWatcher for OS_MACOSX is in dns_config_watcher_mac.{hh,cc}.
     87 
     88 #ifndef _PATH_RESCONF  // Normally defined in <resolv.h>
     89 #define _PATH_RESCONF "/etc/resolv.conf"
     90 #endif
     91 
     92 static const base::FilePath::CharType* kFilePathConfig =
     93     FILE_PATH_LITERAL(_PATH_RESCONF);
     94 
     95 class DnsConfigWatcher {
     96  public:
     97   typedef base::Callback<void(bool succeeded)> CallbackType;
     98 
     99   bool Watch(const CallbackType& callback) {
    100     callback_ = callback;
    101     return watcher_.Watch(base::FilePath(kFilePathConfig), false,
    102                           base::Bind(&DnsConfigWatcher::OnCallback,
    103                                      base::Unretained(this)));
    104   }
    105 
    106  private:
    107   void OnCallback(const base::FilePath& path, bool error) {
    108     callback_.Run(!error);
    109   }
    110 
    111   base::FilePathWatcher watcher_;
    112   CallbackType callback_;
    113 };
    114 #endif
    115 
    116 #if !defined(OS_ANDROID)
    117 ConfigParsePosixResult ReadDnsConfig(DnsConfig* config) {
    118   ConfigParsePosixResult result;
    119   config->unhandled_options = false;
    120 #if defined(OS_OPENBSD)
    121   // Note: res_ninit in glibc always returns 0 and sets RES_INIT.
    122   // res_init behaves the same way.
    123   memset(&_res, 0, sizeof(_res));
    124   if (res_init() == 0) {
    125     result = ConvertResStateToDnsConfig(_res, config);
    126   } else {
    127     result = CONFIG_PARSE_POSIX_RES_INIT_FAILED;
    128   }
    129 #else  // all other OS_POSIX
    130   struct __res_state res;
    131   memset(&res, 0, sizeof(res));
    132   if (res_ninit(&res) == 0) {
    133     result = ConvertResStateToDnsConfig(res, config);
    134   } else {
    135     result = CONFIG_PARSE_POSIX_RES_INIT_FAILED;
    136   }
    137   // Prefer res_ndestroy where available.
    138 #if defined(OS_MACOSX) || defined(OS_FREEBSD)
    139   res_ndestroy(&res);
    140 #else
    141   res_nclose(&res);
    142 #endif
    143 #endif
    144 
    145 #if defined(OS_MACOSX) && !defined(OS_IOS)
    146   ConfigParsePosixResult error = DnsConfigWatcher::CheckDnsConfig();
    147   switch (error) {
    148     case CONFIG_PARSE_POSIX_OK:
    149       break;
    150     case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS:
    151       LOG(WARNING) << "dns_config has unhandled options!";
    152       config->unhandled_options = true;
    153     default:
    154       return error;
    155   }
    156 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
    157   // Override timeout value to match default setting on Windows.
    158   config->timeout = base::TimeDelta::FromSeconds(kDnsTimeoutSeconds);
    159   return result;
    160 }
    161 #else  // defined(OS_ANDROID)
    162 // Theoretically, this is bad. __system_property_get is not a supported API
    163 // (but it's currently visible to anyone using Bionic), and the properties
    164 // are implementation details that may disappear in future Android releases.
    165 // Practically, libcutils provides property_get, which is a public API, and the
    166 // DNS code (and its clients) are already robust against failing to get the DNS
    167 // config for whatever reason, so the properties can disappear and the world
    168 // won't end.
    169 // TODO(ttuttle): Depend on libcutils, then switch this (and other uses of
    170 //                __system_property_get) to property_get.
    171 ConfigParsePosixResult ReadDnsConfig(DnsConfig* dns_config) {
    172    std::string dns1_string, dns2_string;
    173   char property_value[PROP_VALUE_MAX];
    174   __system_property_get("net.dns1", property_value);
    175   dns1_string = property_value;
    176   __system_property_get("net.dns2", property_value);
    177   dns2_string = property_value;
    178   if (dns1_string.length() == 0 && dns2_string.length() == 0)
    179     return CONFIG_PARSE_POSIX_NO_NAMESERVERS;
    180 
    181   IPAddressNumber dns1_number, dns2_number;
    182   bool parsed1 = ParseIPLiteralToNumber(dns1_string, &dns1_number);
    183   bool parsed2 = ParseIPLiteralToNumber(dns2_string, &dns2_number);
    184   if (!parsed1 && !parsed2)
    185     return CONFIG_PARSE_POSIX_BAD_ADDRESS;
    186 
    187   if (parsed1) {
    188     IPEndPoint dns1(dns1_number, dns_protocol::kDefaultPort);
    189     dns_config->nameservers.push_back(dns1);
    190   }
    191   if (parsed2) {
    192     IPEndPoint dns2(dns2_number, dns_protocol::kDefaultPort);
    193     dns_config->nameservers.push_back(dns2);
    194   }
    195 
    196   return CONFIG_PARSE_POSIX_OK;
    197 }
    198 #endif
    199 
    200 }  // namespace
    201 
    202 class DnsConfigServicePosix::Watcher {
    203  public:
    204   explicit Watcher(DnsConfigServicePosix* service)
    205       : service_(service),
    206         weak_factory_(this) {}
    207   ~Watcher() {}
    208 
    209   bool Watch() {
    210     bool success = true;
    211     if (!config_watcher_.Watch(base::Bind(&Watcher::OnConfigChanged,
    212                                           base::Unretained(this)))) {
    213       LOG(ERROR) << "DNS config watch failed to start.";
    214       success = false;
    215       UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
    216                                 DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG,
    217                                 DNS_CONFIG_WATCH_MAX);
    218     }
    219     if (!hosts_watcher_.Watch(base::FilePath(kFilePathHosts), false,
    220                               base::Bind(&Watcher::OnHostsChanged,
    221                                          base::Unretained(this)))) {
    222       LOG(ERROR) << "DNS hosts watch failed to start.";
    223       success = false;
    224       UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
    225                                 DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS,
    226                                 DNS_CONFIG_WATCH_MAX);
    227     }
    228     return success;
    229   }
    230 
    231  private:
    232   void OnConfigChanged(bool succeeded) {
    233     // Ignore transient flutter of resolv.conf by delaying the signal a bit.
    234     const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(50);
    235     base::MessageLoop::current()->PostDelayedTask(
    236         FROM_HERE,
    237         base::Bind(&Watcher::OnConfigChangedDelayed,
    238                    weak_factory_.GetWeakPtr(),
    239                    succeeded),
    240         kDelay);
    241   }
    242   void OnConfigChangedDelayed(bool succeeded) {
    243     service_->OnConfigChanged(succeeded);
    244   }
    245   void OnHostsChanged(const base::FilePath& path, bool error) {
    246     service_->OnHostsChanged(!error);
    247   }
    248 
    249   DnsConfigServicePosix* service_;
    250   DnsConfigWatcher config_watcher_;
    251   base::FilePathWatcher hosts_watcher_;
    252 
    253   base::WeakPtrFactory<Watcher> weak_factory_;
    254 
    255   DISALLOW_COPY_AND_ASSIGN(Watcher);
    256 };
    257 
    258 // A SerialWorker that uses libresolv to initialize res_state and converts
    259 // it to DnsConfig (except on Android, where it reads system properties
    260 // net.dns1 and net.dns2; see #if around ReadDnsConfig above.)
    261 class DnsConfigServicePosix::ConfigReader : public SerialWorker {
    262  public:
    263   explicit ConfigReader(DnsConfigServicePosix* service)
    264       : service_(service), success_(false) {}
    265 
    266   virtual void DoWork() OVERRIDE {
    267     base::TimeTicks start_time = base::TimeTicks::Now();
    268     ConfigParsePosixResult result = ReadDnsConfig(&dns_config_);
    269     switch (result) {
    270       case CONFIG_PARSE_POSIX_MISSING_OPTIONS:
    271       case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS:
    272         DCHECK(dns_config_.unhandled_options);
    273         // Fall through.
    274       case CONFIG_PARSE_POSIX_OK:
    275         success_ = true;
    276         break;
    277       default:
    278         success_ = false;
    279         break;
    280     }
    281     UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParsePosix",
    282                               result, CONFIG_PARSE_POSIX_MAX);
    283     UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ConfigParseResult", success_);
    284     UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration",
    285                         base::TimeTicks::Now() - start_time);
    286   }
    287 
    288   virtual void OnWorkFinished() OVERRIDE {
    289     DCHECK(!IsCancelled());
    290     if (success_) {
    291       service_->OnConfigRead(dns_config_);
    292     } else {
    293       LOG(WARNING) << "Failed to read DnsConfig.";
    294     }
    295   }
    296 
    297  private:
    298   virtual ~ConfigReader() {}
    299 
    300   DnsConfigServicePosix* service_;
    301   // Written in DoWork, read in OnWorkFinished, no locking necessary.
    302   DnsConfig dns_config_;
    303   bool success_;
    304 
    305   DISALLOW_COPY_AND_ASSIGN(ConfigReader);
    306 };
    307 
    308 // A SerialWorker that reads the HOSTS file and runs Callback.
    309 class DnsConfigServicePosix::HostsReader : public SerialWorker {
    310  public:
    311   explicit HostsReader(DnsConfigServicePosix* service)
    312       :  service_(service), path_(kFilePathHosts), success_(false) {}
    313 
    314  private:
    315   virtual ~HostsReader() {}
    316 
    317   virtual void DoWork() OVERRIDE {
    318     base::TimeTicks start_time = base::TimeTicks::Now();
    319     success_ = ParseHostsFile(path_, &hosts_);
    320     UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_);
    321     UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration",
    322                         base::TimeTicks::Now() - start_time);
    323   }
    324 
    325   virtual void OnWorkFinished() OVERRIDE {
    326     if (success_) {
    327       service_->OnHostsRead(hosts_);
    328     } else {
    329       LOG(WARNING) << "Failed to read DnsHosts.";
    330     }
    331   }
    332 
    333   DnsConfigServicePosix* service_;
    334   const base::FilePath path_;
    335   // Written in DoWork, read in OnWorkFinished, no locking necessary.
    336   DnsHosts hosts_;
    337   bool success_;
    338 
    339   DISALLOW_COPY_AND_ASSIGN(HostsReader);
    340 };
    341 
    342 DnsConfigServicePosix::DnsConfigServicePosix()
    343     : config_reader_(new ConfigReader(this)),
    344       hosts_reader_(new HostsReader(this)) {}
    345 
    346 DnsConfigServicePosix::~DnsConfigServicePosix() {
    347   config_reader_->Cancel();
    348   hosts_reader_->Cancel();
    349 }
    350 
    351 void DnsConfigServicePosix::ReadNow() {
    352   config_reader_->WorkNow();
    353   hosts_reader_->WorkNow();
    354 }
    355 
    356 bool DnsConfigServicePosix::StartWatching() {
    357   // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
    358   watcher_.reset(new Watcher(this));
    359   UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED,
    360                             DNS_CONFIG_WATCH_MAX);
    361   return watcher_->Watch();
    362 }
    363 
    364 void DnsConfigServicePosix::OnConfigChanged(bool succeeded) {
    365   InvalidateConfig();
    366   if (succeeded) {
    367     config_reader_->WorkNow();
    368   } else {
    369     LOG(ERROR) << "DNS config watch failed.";
    370     set_watch_failed(true);
    371     UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
    372                               DNS_CONFIG_WATCH_FAILED_CONFIG,
    373                               DNS_CONFIG_WATCH_MAX);
    374   }
    375 }
    376 
    377 void DnsConfigServicePosix::OnHostsChanged(bool succeeded) {
    378   InvalidateHosts();
    379   if (succeeded) {
    380     hosts_reader_->WorkNow();
    381   } else {
    382     LOG(ERROR) << "DNS hosts watch failed.";
    383     set_watch_failed(true);
    384     UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
    385                               DNS_CONFIG_WATCH_FAILED_HOSTS,
    386                               DNS_CONFIG_WATCH_MAX);
    387   }
    388 }
    389 
    390 #if !defined(OS_ANDROID)
    391 ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res,
    392                                                   DnsConfig* dns_config) {
    393   CHECK(dns_config != NULL);
    394   if (!(res.options & RES_INIT))
    395     return CONFIG_PARSE_POSIX_RES_INIT_UNSET;
    396 
    397   dns_config->nameservers.clear();
    398 
    399 #if defined(OS_MACOSX) || defined(OS_FREEBSD)
    400   union res_sockaddr_union addresses[MAXNS];
    401   int nscount = res_getservers(const_cast<res_state>(&res), addresses, MAXNS);
    402   DCHECK_GE(nscount, 0);
    403   DCHECK_LE(nscount, MAXNS);
    404   for (int i = 0; i < nscount; ++i) {
    405     IPEndPoint ipe;
    406     if (!ipe.FromSockAddr(
    407             reinterpret_cast<const struct sockaddr*>(&addresses[i]),
    408             sizeof addresses[i])) {
    409       return CONFIG_PARSE_POSIX_BAD_ADDRESS;
    410     }
    411     dns_config->nameservers.push_back(ipe);
    412   }
    413 #elif defined(OS_LINUX)
    414   COMPILE_ASSERT(arraysize(res.nsaddr_list) >= MAXNS &&
    415                  arraysize(res._u._ext.nsaddrs) >= MAXNS,
    416                  incompatible_libresolv_res_state);
    417   DCHECK_LE(res.nscount, MAXNS);
    418   // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|.
    419   // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|,
    420   // but we have to combine the two arrays ourselves.
    421   for (int i = 0; i < res.nscount; ++i) {
    422     IPEndPoint ipe;
    423     const struct sockaddr* addr = NULL;
    424     size_t addr_len = 0;
    425     if (res.nsaddr_list[i].sin_family) {  // The indicator used by res_nsend.
    426       addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]);
    427       addr_len = sizeof res.nsaddr_list[i];
    428     } else if (res._u._ext.nsaddrs[i] != NULL) {
    429       addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]);
    430       addr_len = sizeof *res._u._ext.nsaddrs[i];
    431     } else {
    432       return CONFIG_PARSE_POSIX_BAD_EXT_STRUCT;
    433     }
    434     if (!ipe.FromSockAddr(addr, addr_len))
    435       return CONFIG_PARSE_POSIX_BAD_ADDRESS;
    436     dns_config->nameservers.push_back(ipe);
    437   }
    438 #else  // !(defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FREEBSD))
    439   DCHECK_LE(res.nscount, MAXNS);
    440   for (int i = 0; i < res.nscount; ++i) {
    441     IPEndPoint ipe;
    442     if (!ipe.FromSockAddr(
    443             reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
    444             sizeof res.nsaddr_list[i])) {
    445       return CONFIG_PARSE_POSIX_BAD_ADDRESS;
    446     }
    447     dns_config->nameservers.push_back(ipe);
    448   }
    449 #endif
    450 
    451   dns_config->search.clear();
    452   for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) {
    453     dns_config->search.push_back(std::string(res.dnsrch[i]));
    454   }
    455 
    456   dns_config->ndots = res.ndots;
    457   dns_config->timeout = base::TimeDelta::FromSeconds(res.retrans);
    458   dns_config->attempts = res.retry;
    459 #if defined(RES_ROTATE)
    460   dns_config->rotate = res.options & RES_ROTATE;
    461 #endif
    462   dns_config->edns0 = res.options & RES_USE_EDNS0;
    463 
    464   // The current implementation assumes these options are set. They normally
    465   // cannot be overwritten by /etc/resolv.conf
    466   unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
    467   if ((res.options & kRequiredOptions) != kRequiredOptions) {
    468     dns_config->unhandled_options = true;
    469     return CONFIG_PARSE_POSIX_MISSING_OPTIONS;
    470   }
    471 
    472   unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC;
    473   if (res.options & kUnhandledOptions) {
    474     dns_config->unhandled_options = true;
    475     return CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS;
    476   }
    477 
    478   if (dns_config->nameservers.empty())
    479     return CONFIG_PARSE_POSIX_NO_NAMESERVERS;
    480 
    481   // If any name server is 0.0.0.0, assume the configuration is invalid.
    482   // TODO(szym): Measure how often this happens. http://crbug.com/125599
    483   const IPAddressNumber kEmptyAddress(kIPv4AddressSize);
    484   for (unsigned i = 0; i < dns_config->nameservers.size(); ++i) {
    485     if (dns_config->nameservers[i].address() == kEmptyAddress)
    486       return CONFIG_PARSE_POSIX_NULL_ADDRESS;
    487   }
    488   return CONFIG_PARSE_POSIX_OK;
    489 }
    490 #endif  // !defined(OS_ANDROID)
    491 
    492 }  // namespace internal
    493 
    494 // static
    495 scoped_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
    496   return scoped_ptr<DnsConfigService>(new internal::DnsConfigServicePosix());
    497 }
    498 
    499 }  // namespace net
    500