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 <memory>
8 #include <string>
9 #include <type_traits>
10 
11 #include "base/bind.h"
12 #include "base/files/file.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_path_watcher.h"
15 #include "base/lazy_instance.h"
16 #include "base/location.h"
17 #include "base/logging.h"
18 #include "base/macros.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/threading/scoped_blocking_call.h"
22 #include "base/threading/sequenced_task_runner_handle.h"
23 #include "base/time/time.h"
24 #include "build/build_config.h"
25 #include "net/base/ip_address.h"
26 #include "net/base/ip_endpoint.h"
27 #include "net/dns/dns_config.h"
28 #include "net/dns/dns_hosts.h"
29 #include "net/dns/notify_watcher_mac.h"
30 #include "net/dns/public/dns_protocol.h"
31 #include "net/dns/serial_worker.h"
32 
33 #if defined(OS_MAC)
34 #include "net/dns/dns_config_watcher_mac.h"
35 #endif
36 
37 #if defined(OS_ANDROID)
38 #include <sys/system_properties.h>
39 #include "base/android/build_info.h"
40 #include "net/android/network_library.h"
41 #include "net/base/address_tracker_linux.h"
42 #include "net/base/network_change_notifier.h"
43 #include "net/base/network_interfaces.h"
44 #endif
45 
46 namespace net {
47 
48 namespace internal {
49 
50 namespace {
51 
52 #if defined(OS_ANDROID)
53 const base::FilePath::CharType kFilePathHosts[] =
54     FILE_PATH_LITERAL("/system/etc/hosts");
55 #else
56 const base::FilePath::CharType kFilePathHosts[] =
57     FILE_PATH_LITERAL("/etc/hosts");
58 #endif
59 
60 #if defined(OS_IOS)
61 // There is no public API to watch the DNS configuration on iOS.
62 class DnsConfigWatcher {
63  public:
64   using CallbackType = base::RepeatingCallback<void(bool succeeded)>;
65 
Watch(const CallbackType & callback)66   bool Watch(const CallbackType& callback) {
67     return false;
68   }
69 };
70 
71 #elif defined(OS_ANDROID)
72 
73 // On Android, assume DNS config may have changed on every network change.
74 class DnsConfigWatcher : public NetworkChangeNotifier::NetworkChangeObserver {
75  public:
DnsConfigWatcher()76   DnsConfigWatcher() { NetworkChangeNotifier::AddNetworkChangeObserver(this); }
77 
~DnsConfigWatcher()78   ~DnsConfigWatcher() override {
79     NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
80   }
81 
82   using CallbackType = base::RepeatingCallback<void(bool succeeded)>;
83 
Watch(const CallbackType & callback)84   bool Watch(const CallbackType& callback) {
85     callback_ = callback;
86     return true;
87   }
88 
89   // NetworkChangeNotifier::NetworkChangeObserver implementation:
OnNetworkChanged(NetworkChangeNotifier::ConnectionType type)90   void OnNetworkChanged(NetworkChangeNotifier::ConnectionType type) override {
91     if (!callback_.is_null() && type != NetworkChangeNotifier::CONNECTION_NONE)
92       callback_.Run(true);
93   }
94 
95  private:
96   CallbackType callback_;
97 };
98 
99 #elif defined(OS_MAC)
100 
101 // DnsConfigWatcher for OS_MAC is in dns_config_watcher_mac.{hh,cc}.
102 
103 #else  // !defined(OS_IOS) && !defined(OS_ANDROID) && !defined(OS_MAC)
104 
105 #ifndef _PATH_RESCONF  // Normally defined in <resolv.h>
106 #define _PATH_RESCONF "/etc/resolv.conf"
107 #endif
108 
109 const base::FilePath::CharType kFilePathConfig[] =
110     FILE_PATH_LITERAL(_PATH_RESCONF);
111 
112 class DnsConfigWatcher {
113  public:
114   using CallbackType = base::RepeatingCallback<void(bool succeeded)>;
115 
Watch(const CallbackType & callback)116   bool Watch(const CallbackType& callback) {
117     callback_ = callback;
118     return watcher_.Watch(base::FilePath(kFilePathConfig), false,
119                           base::BindRepeating(&DnsConfigWatcher::OnCallback,
120                                               base::Unretained(this)));
121   }
122 
123  private:
OnCallback(const base::FilePath & path,bool error)124   void OnCallback(const base::FilePath& path, bool error) {
125     callback_.Run(!error);
126   }
127 
128   base::FilePathWatcher watcher_;
129   CallbackType callback_;
130 };
131 #endif  // defined(OS_IOS)
132 
133 #if defined(OS_ANDROID)
IsVpnPresent()134 bool IsVpnPresent() {
135   NetworkInterfaceList networks;
136   if (!GetNetworkList(&networks, INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES))
137     return false;
138 
139   for (NetworkInterface network : networks) {
140     if (AddressTrackerLinux::IsTunnelInterfaceName(network.name.c_str()))
141       return true;
142   }
143   return false;
144 }
145 #endif  // defined(OS_ANDROID)
146 
ReadDnsConfig(DnsConfig * dns_config)147 ConfigParsePosixResult ReadDnsConfig(DnsConfig* dns_config) {
148   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
149                                                 base::BlockingType::MAY_BLOCK);
150   dns_config->unhandled_options = false;
151 #if !defined(OS_ANDROID)
152   ConfigParsePosixResult result;
153 // TODO(fuchsia): Use res_ninit() when it's implemented on Fuchsia.
154 #if defined(OS_OPENBSD) || defined(OS_FUCHSIA)
155   // Note: res_ninit in glibc always returns 0 and sets RES_INIT.
156   // res_init behaves the same way.
157   memset(&_res, 0, sizeof(_res));
158   if (res_init() == 0) {
159     result = ConvertResStateToDnsConfig(_res, dns_config);
160   } else {
161     result = CONFIG_PARSE_POSIX_RES_INIT_FAILED;
162   }
163 #else  // all other OS_POSIX
164   struct __res_state res;
165   memset(&res, 0, sizeof(res));
166   if (res_ninit(&res) == 0) {
167     result = ConvertResStateToDnsConfig(res, dns_config);
168   } else {
169     result = CONFIG_PARSE_POSIX_RES_INIT_FAILED;
170   }
171   // Prefer res_ndestroy where available.
172 #if defined(OS_APPLE) || defined(OS_FREEBSD) || defined(OS_DRAGONFLY)
173   res_ndestroy(&res);
174 #else
175   res_nclose(&res);
176 #endif  // defined(OS_APPLE) || defined(OS_FREEBSD)
177 #endif  // defined(OS_OPENBSD)
178 
179 #if defined(OS_MAC)
180   ConfigParsePosixResult error = DnsConfigWatcher::CheckDnsConfig();
181   switch (error) {
182     case CONFIG_PARSE_POSIX_OK:
183       break;
184     case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS:
185       LOG(WARNING) << "dns_config has unhandled options!";
186       dns_config->unhandled_options = true;
187       FALLTHROUGH;
188     default:
189       return error;
190   }
191 #endif  // defined(OS_MAC)
192   // Override |fallback_period| value to match default setting on Windows.
193   dns_config->fallback_period = kDnsDefaultFallbackPeriod;
194   return result;
195 #else  // defined(OS_ANDROID)
196   dns_config->nameservers.clear();
197 
198   if (base::android::BuildInfo::GetInstance()->sdk_int() >=
199       base::android::SDK_VERSION_MARSHMALLOW) {
200     return net::android::GetDnsServers(&dns_config->nameservers,
201                                        &dns_config->dns_over_tls_active,
202                                        &dns_config->dns_over_tls_hostname);
203   }
204 
205   if (IsVpnPresent()) {
206     dns_config->unhandled_options = true;
207     return CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS;
208   }
209 
210   // NOTE(pauljensen): __system_property_get and the net.dns1/2 properties are
211   // not supported APIs, but they're only read on pre-Marshmallow Android which
212   // was released years ago and isn't changing.
213   char property_value[PROP_VALUE_MAX];
214   __system_property_get("net.dns1", property_value);
215   std::string dns1_string = property_value;
216   __system_property_get("net.dns2", property_value);
217   std::string dns2_string = property_value;
218   if (dns1_string.empty() && dns2_string.empty())
219     return CONFIG_PARSE_POSIX_NO_NAMESERVERS;
220 
221   IPAddress dns1_address;
222   IPAddress dns2_address;
223   bool parsed1 = dns1_address.AssignFromIPLiteral(dns1_string);
224   bool parsed2 = dns2_address.AssignFromIPLiteral(dns2_string);
225   if (!parsed1 && !parsed2)
226     return CONFIG_PARSE_POSIX_BAD_ADDRESS;
227 
228   if (parsed1) {
229     IPEndPoint dns1(dns1_address, dns_protocol::kDefaultPort);
230     dns_config->nameservers.push_back(dns1);
231   }
232   if (parsed2) {
233     IPEndPoint dns2(dns2_address, dns_protocol::kDefaultPort);
234     dns_config->nameservers.push_back(dns2);
235   }
236 
237   return CONFIG_PARSE_POSIX_OK;
238 #endif  // !defined(OS_ANDROID)
239 }
240 
241 }  // namespace
242 
243 class DnsConfigServicePosix::Watcher {
244  public:
Watcher(DnsConfigServicePosix * service)245   explicit Watcher(DnsConfigServicePosix* service) : service_(service) {}
246   ~Watcher() = default;
247 
Watch()248   bool Watch() {
249     bool success = true;
250     if (!config_watcher_.Watch(base::BindRepeating(&Watcher::OnConfigChanged,
251                                                    base::Unretained(this)))) {
252       LOG(ERROR) << "DNS config watch failed to start.";
253       success = false;
254       UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
255                                 DNS_CONFIG_WATCH_FAILED_TO_START_CONFIG,
256                                 DNS_CONFIG_WATCH_MAX);
257     }
258 // Hosts file should never change on Android or iOS (and watching it on Android
259 // is problematic; see http://crbug.com/600442), so don't watch it there.
260 #if !defined(OS_ANDROID) && !defined(OS_IOS)
261     if (!hosts_watcher_.Watch(base::FilePath(service_->file_path_hosts_), false,
262                               base::BindRepeating(&Watcher::OnHostsChanged,
263                                                   base::Unretained(this)))) {
264       LOG(ERROR) << "DNS hosts watch failed to start.";
265       success = false;
266       UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
267                                 DNS_CONFIG_WATCH_FAILED_TO_START_HOSTS,
268                                 DNS_CONFIG_WATCH_MAX);
269     }
270 #endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
271     return success;
272   }
273 
274  private:
OnConfigChanged(bool succeeded)275   void OnConfigChanged(bool succeeded) {
276     // Ignore transient flutter of resolv.conf by delaying the signal a bit.
277     const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(50);
278     base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
279         FROM_HERE,
280         base::BindOnce(&Watcher::OnConfigChangedDelayed,
281                        weak_factory_.GetWeakPtr(), succeeded),
282         kDelay);
283   }
284 
OnConfigChangedDelayed(bool succeeded)285   void OnConfigChangedDelayed(bool succeeded) {
286     service_->OnConfigChanged(succeeded);
287   }
288 
OnHostsChanged(const base::FilePath & path,bool error)289   void OnHostsChanged(const base::FilePath& path, bool error) {
290     service_->OnHostsChanged(!error);
291   }
292 
293   DnsConfigServicePosix* const service_;
294   DnsConfigWatcher config_watcher_;
295 #if !defined(OS_ANDROID) && !defined(OS_IOS)
296   base::FilePathWatcher hosts_watcher_;
297 #endif  // !defined(OS_ANDROID) && !defined(OS_IOS)
298 
299   base::WeakPtrFactory<Watcher> weak_factory_{this};
300 
301   DISALLOW_COPY_AND_ASSIGN(Watcher);
302 };
303 
304 // A SerialWorker that uses libresolv to initialize res_state and converts
305 // it to DnsConfig (except on Android, where it reads system properties
306 // net.dns1 and net.dns2; see #if around ReadDnsConfig above.)
307 class DnsConfigServicePosix::ConfigReader : public SerialWorker {
308  public:
ConfigReader(DnsConfigServicePosix * service)309   explicit ConfigReader(DnsConfigServicePosix* service)
310       : service_(service), success_(false) {
311     // Allow execution on another thread; nothing thread-specific about
312     // constructor.
313     DETACH_FROM_SEQUENCE(sequence_checker_);
314   }
315 
DoWork()316   void DoWork() override {
317     base::TimeTicks start_time = base::TimeTicks::Now();
318     ConfigParsePosixResult result = ReadDnsConfig(&dns_config_);
319     switch (result) {
320       case CONFIG_PARSE_POSIX_MISSING_OPTIONS:
321       case CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS:
322         DCHECK(dns_config_.unhandled_options);
323         FALLTHROUGH;
324       case CONFIG_PARSE_POSIX_OK:
325         success_ = true;
326         break;
327       default:
328         success_ = false;
329         break;
330     }
331     UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ConfigParsePosix",
332                               result, CONFIG_PARSE_POSIX_MAX);
333     UMA_HISTOGRAM_TIMES("AsyncDNS.ConfigParseDuration",
334                         base::TimeTicks::Now() - start_time);
335   }
336 
OnWorkFinished()337   void OnWorkFinished() override {
338     DCHECK(!IsCancelled());
339     if (success_) {
340       service_->OnConfigRead(dns_config_);
341     } else {
342       LOG(WARNING) << "Failed to read DnsConfig.";
343     }
344   }
345 
346  private:
347   ~ConfigReader() override = default;
348 
349   // Raw pointer to owning DnsConfigService. This must never be accessed inside
350   // DoWork(), since service may be destroyed while SerialWorker is running
351   // on worker thread.
352   DnsConfigServicePosix* const service_;
353   // Written in DoWork, read in OnWorkFinished, no locking necessary.
354   DnsConfig dns_config_;
355   bool success_;
356 
357   DISALLOW_COPY_AND_ASSIGN(ConfigReader);
358 };
359 
360 // A SerialWorker that reads the HOSTS file and runs Callback.
361 class DnsConfigServicePosix::HostsReader : public SerialWorker {
362  public:
HostsReader(DnsConfigServicePosix * service)363   explicit HostsReader(DnsConfigServicePosix* service)
364       : service_(service),
365         file_path_hosts_(service->file_path_hosts_),
366         success_(false) {
367     // Allow execution on another thread; nothing thread-specific about
368     // constructor.
369     DETACH_FROM_SEQUENCE(sequence_checker_);
370   }
371 
372  private:
~HostsReader()373   ~HostsReader() override {}
374 
DoWork()375   void DoWork() override {
376     base::TimeTicks start_time = base::TimeTicks::Now();
377     base::ScopedBlockingCall scoped_blocking_call(
378         FROM_HERE, base::BlockingType::MAY_BLOCK);
379     success_ = ParseHostsFile(file_path_hosts_, &hosts_);
380     UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HostParseResult", success_);
381     UMA_HISTOGRAM_TIMES("AsyncDNS.HostsParseDuration",
382                         base::TimeTicks::Now() - start_time);
383   }
384 
OnWorkFinished()385   void OnWorkFinished() override {
386     if (success_) {
387       service_->OnHostsRead(hosts_);
388     } else {
389       LOG(WARNING) << "Failed to read DnsHosts.";
390     }
391   }
392 
393   // Raw pointer to owning DnsConfigService. This must never be accessed inside
394   // DoWork(), since service may be destroyed while SerialWorker is running
395   // on worker thread.
396   DnsConfigServicePosix* const service_;
397   // Hosts file path to parse.
398   const base::FilePath file_path_hosts_;
399   // Written in DoWork, read in OnWorkFinished, no locking necessary.
400   DnsHosts hosts_;
401   bool success_;
402 
403   DISALLOW_COPY_AND_ASSIGN(HostsReader);
404 };
405 
DnsConfigServicePosix()406 DnsConfigServicePosix::DnsConfigServicePosix()
407     : file_path_hosts_(kFilePathHosts) {
408   // Allow constructing on one thread and living on another.
409   DETACH_FROM_SEQUENCE(sequence_checker_);
410 }
411 
~DnsConfigServicePosix()412 DnsConfigServicePosix::~DnsConfigServicePosix() {
413   config_reader_->Cancel();
414   hosts_reader_->Cancel();
415 }
416 
RefreshConfig()417 void DnsConfigServicePosix::RefreshConfig() {
418   InvalidateConfig();
419   InvalidateHosts();
420   ReadNow();
421 }
422 
ReadNow()423 void DnsConfigServicePosix::ReadNow() {
424   config_reader_->WorkNow();
425   hosts_reader_->WorkNow();
426 }
427 
StartWatching()428 bool DnsConfigServicePosix::StartWatching() {
429   CreateReaders();
430   // TODO(szym): re-start watcher if that makes sense. http://crbug.com/116139
431   watcher_.reset(new Watcher(this));
432   UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus", DNS_CONFIG_WATCH_STARTED,
433                             DNS_CONFIG_WATCH_MAX);
434   return watcher_->Watch();
435 }
436 
OnConfigChanged(bool succeeded)437 void DnsConfigServicePosix::OnConfigChanged(bool succeeded) {
438   InvalidateConfig();
439   if (succeeded) {
440     config_reader_->WorkNow();
441   } else {
442     LOG(ERROR) << "DNS config watch failed.";
443     set_watch_failed(true);
444     UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
445                               DNS_CONFIG_WATCH_FAILED_CONFIG,
446                               DNS_CONFIG_WATCH_MAX);
447   }
448 }
449 
OnHostsChanged(bool succeeded)450 void DnsConfigServicePosix::OnHostsChanged(bool succeeded) {
451   InvalidateHosts();
452   if (succeeded) {
453     hosts_reader_->WorkNow();
454   } else {
455     LOG(ERROR) << "DNS hosts watch failed.";
456     set_watch_failed(true);
457     UMA_HISTOGRAM_ENUMERATION("AsyncDNS.WatchStatus",
458                               DNS_CONFIG_WATCH_FAILED_HOSTS,
459                               DNS_CONFIG_WATCH_MAX);
460   }
461 }
462 
CreateReaders()463 void DnsConfigServicePosix::CreateReaders() {
464   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
465   DCHECK(!config_reader_);
466   config_reader_ = base::MakeRefCounted<ConfigReader>(this);
467   DCHECK(!hosts_reader_);
468   hosts_reader_ = base::MakeRefCounted<HostsReader>(this);
469 }
470 
471 #if !defined(OS_ANDROID)
ConvertResStateToDnsConfig(const struct __res_state & res,DnsConfig * dns_config)472 ConfigParsePosixResult ConvertResStateToDnsConfig(const struct __res_state& res,
473                                                   DnsConfig* dns_config) {
474   DCHECK(dns_config);
475 
476   if (!(res.options & RES_INIT))
477     return CONFIG_PARSE_POSIX_RES_INIT_UNSET;
478 
479   dns_config->nameservers.clear();
480 
481 #if defined(OS_APPLE) || defined(OS_FREEBSD) || defined(OS_DRAGONFLY)
482   union res_sockaddr_union addresses[MAXNS];
483   int nscount = res_getservers(const_cast<res_state>(&res), addresses, MAXNS);
484   DCHECK_GE(nscount, 0);
485   DCHECK_LE(nscount, MAXNS);
486   for (int i = 0; i < nscount; ++i) {
487     IPEndPoint ipe;
488     if (!ipe.FromSockAddr(
489             reinterpret_cast<const struct sockaddr*>(&addresses[i]),
490             sizeof addresses[i])) {
491       return CONFIG_PARSE_POSIX_BAD_ADDRESS;
492     }
493     dns_config->nameservers.push_back(ipe);
494   }
495 #elif defined(OS_LINUX) || defined(OS_CHROMEOS)
496   static_assert(std::extent<decltype(res.nsaddr_list)>() >= MAXNS &&
497                     std::extent<decltype(res._u._ext.nsaddrs)>() >= MAXNS,
498                 "incompatible libresolv res_state");
499   DCHECK_LE(res.nscount, MAXNS);
500   // Initially, glibc stores IPv6 in |_ext.nsaddrs| and IPv4 in |nsaddr_list|.
501   // In res_send.c:res_nsend, it merges |nsaddr_list| into |nsaddrs|,
502   // but we have to combine the two arrays ourselves.
503   for (int i = 0; i < res.nscount; ++i) {
504     IPEndPoint ipe;
505     const struct sockaddr* addr = nullptr;
506     size_t addr_len = 0;
507     if (res.nsaddr_list[i].sin_family) {  // The indicator used by res_nsend.
508       addr = reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]);
509       addr_len = sizeof res.nsaddr_list[i];
510     } else if (res._u._ext.nsaddrs[i]) {
511       addr = reinterpret_cast<const struct sockaddr*>(res._u._ext.nsaddrs[i]);
512       addr_len = sizeof *res._u._ext.nsaddrs[i];
513     } else {
514       return CONFIG_PARSE_POSIX_BAD_EXT_STRUCT;
515     }
516     if (!ipe.FromSockAddr(addr, addr_len))
517       return CONFIG_PARSE_POSIX_BAD_ADDRESS;
518     dns_config->nameservers.push_back(ipe);
519   }
520 #else   // !(defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_APPLE) ||
521         // defined(OS_FREEBSD)) || defined(OS_DRAGONFLY)
522   DCHECK_LE(res.nscount, MAXNS);
523   for (int i = 0; i < res.nscount; ++i) {
524     IPEndPoint ipe;
525     if (!ipe.FromSockAddr(
526             reinterpret_cast<const struct sockaddr*>(&res.nsaddr_list[i]),
527             sizeof res.nsaddr_list[i])) {
528       return CONFIG_PARSE_POSIX_BAD_ADDRESS;
529     }
530     dns_config->nameservers.push_back(ipe);
531   }
532 #endif  // defined(OS_APPLE) || defined(OS_FREEBSD) || defined(OS_DRAGONFLY)
533 
534   dns_config->search.clear();
535   for (int i = 0; (i < MAXDNSRCH) && res.dnsrch[i]; ++i) {
536     dns_config->search.push_back(std::string(res.dnsrch[i]));
537   }
538 
539   dns_config->ndots = res.ndots;
540   dns_config->fallback_period = base::TimeDelta::FromSeconds(res.retrans);
541   dns_config->attempts = res.retry;
542 #if defined(RES_ROTATE)
543   dns_config->rotate = res.options & RES_ROTATE;
544 #endif
545 #if !defined(RES_USE_DNSSEC)
546   // Some versions of libresolv don't have support for the DO bit. In this
547   // case, we proceed without it.
548   static const int RES_USE_DNSSEC = 0;
549 #endif
550 
551   // The current implementation assumes these options are set. They normally
552   // cannot be overwritten by /etc/resolv.conf
553   const unsigned kRequiredOptions = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
554   if ((res.options & kRequiredOptions) != kRequiredOptions) {
555     dns_config->unhandled_options = true;
556     return CONFIG_PARSE_POSIX_MISSING_OPTIONS;
557   }
558 
559   const unsigned kUnhandledOptions = RES_USEVC | RES_IGNTC | RES_USE_DNSSEC;
560   if (res.options & kUnhandledOptions) {
561     dns_config->unhandled_options = true;
562     return CONFIG_PARSE_POSIX_UNHANDLED_OPTIONS;
563   }
564 
565   if (dns_config->nameservers.empty())
566     return CONFIG_PARSE_POSIX_NO_NAMESERVERS;
567 
568   // If any name server is 0.0.0.0, assume the configuration is invalid.
569   // TODO(szym): Measure how often this happens. http://crbug.com/125599
570   for (unsigned i = 0; i < dns_config->nameservers.size(); ++i) {
571     if (dns_config->nameservers[i].address().IsZero())
572       return CONFIG_PARSE_POSIX_NULL_ADDRESS;
573   }
574   return CONFIG_PARSE_POSIX_OK;
575 }
576 
577 #endif  // !defined(OS_ANDROID)
578 
579 }  // namespace internal
580 
581 // static
CreateSystemService()582 std::unique_ptr<DnsConfigService> DnsConfigService::CreateSystemService() {
583   // DnsConfigService on iOS doesn't watch the config so its result can become
584   // inaccurate at any time.  Disable it to prevent promulgation of inaccurate
585   // DnsConfigs.
586 #ifdef OS_IOS
587   return nullptr;
588 #else   // defined(OS_IOS)
589   return std::unique_ptr<DnsConfigService>(
590       new internal::DnsConfigServicePosix());
591 #endif  // defined(OS_IOS)
592 }
593 
594 }  // namespace net
595