Home | History | Annotate | Download | only in base
      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/base/dns_reloader.h"
      6 
      7 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && \
      8     !defined(OS_ANDROID)
      9 
     10 #include <resolv.h>
     11 
     12 #include "base/basictypes.h"
     13 #include "base/lazy_instance.h"
     14 #include "base/logging.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/synchronization/lock.h"
     17 #include "base/threading/thread_local_storage.h"
     18 #include "net/base/network_change_notifier.h"
     19 
     20 namespace {
     21 
     22 // On Linux/BSD, changes to /etc/resolv.conf can go unnoticed thus resulting
     23 // in DNS queries failing either because nameservers are unknown on startup
     24 // or because nameserver info has changed as a result of e.g. connecting to
     25 // a new network. Some distributions patch glibc to stat /etc/resolv.conf
     26 // to try to automatically detect such changes but these patches are not
     27 // universal and even patched systems such as Jaunty appear to need calls
     28 // to res_ninit to reload the nameserver information in different threads.
     29 //
     30 // To fix this, on systems with FilePathWatcher support, we use
     31 // NetworkChangeNotifier::DNSObserver to monitor /etc/resolv.conf to
     32 // enable us to respond to DNS changes and reload the resolver state.
     33 //
     34 // OpenBSD does not have thread-safe res_ninit/res_nclose so we can't do
     35 // the same trick there and most *BSD's don't yet have support for
     36 // FilePathWatcher (but perhaps the new kqueue mac code just needs to be
     37 // ported to *BSD to support that).
     38 //
     39 // Android does not have /etc/resolv.conf. The system takes care of nameserver
     40 // changes, so none of this is needed.
     41 
     42 class DnsReloader : public net::NetworkChangeNotifier::DNSObserver {
     43  public:
     44   struct ReloadState {
     45     int resolver_generation;
     46   };
     47 
     48   // NetworkChangeNotifier::DNSObserver:
     49   virtual void OnDNSChanged() OVERRIDE {
     50     DCHECK(base::MessageLoopForIO::IsCurrent());
     51     base::AutoLock l(lock_);
     52     resolver_generation_++;
     53   }
     54 
     55   void MaybeReload() {
     56     ReloadState* reload_state = static_cast<ReloadState*>(tls_index_.Get());
     57     base::AutoLock l(lock_);
     58 
     59     if (!reload_state) {
     60       reload_state = new ReloadState();
     61       reload_state->resolver_generation = resolver_generation_;
     62       res_ninit(&_res);
     63       tls_index_.Set(reload_state);
     64     } else if (reload_state->resolver_generation != resolver_generation_) {
     65       reload_state->resolver_generation = resolver_generation_;
     66       // It is safe to call res_nclose here since we know res_ninit will have
     67       // been called above.
     68       res_nclose(&_res);
     69       res_ninit(&_res);
     70     }
     71   }
     72 
     73   // Free the allocated state.
     74   static void SlotReturnFunction(void* data) {
     75     ReloadState* reload_state = static_cast<ReloadState*>(data);
     76     if (reload_state)
     77       res_nclose(&_res);
     78     delete reload_state;
     79   }
     80 
     81  private:
     82   DnsReloader() : resolver_generation_(0) {
     83     tls_index_.Initialize(SlotReturnFunction);
     84     net::NetworkChangeNotifier::AddDNSObserver(this);
     85   }
     86 
     87   virtual ~DnsReloader() {
     88     NOTREACHED();  // LeakyLazyInstance is not destructed.
     89   }
     90 
     91   base::Lock lock_;  // Protects resolver_generation_.
     92   int resolver_generation_;
     93   friend struct base::DefaultLazyInstanceTraits<DnsReloader>;
     94 
     95   // We use thread local storage to identify which ReloadState to interact with.
     96   static base::ThreadLocalStorage::StaticSlot tls_index_;
     97 
     98   DISALLOW_COPY_AND_ASSIGN(DnsReloader);
     99 };
    100 
    101 // A TLS slot to the ReloadState for the current thread.
    102 // static
    103 base::ThreadLocalStorage::StaticSlot DnsReloader::tls_index_ = TLS_INITIALIZER;
    104 
    105 base::LazyInstance<DnsReloader>::Leaky
    106     g_dns_reloader = LAZY_INSTANCE_INITIALIZER;
    107 
    108 }  // namespace
    109 
    110 namespace net {
    111 
    112 void EnsureDnsReloaderInit() {
    113   DnsReloader* t ALLOW_UNUSED = g_dns_reloader.Pointer();
    114 }
    115 
    116 void DnsReloaderMaybeReload() {
    117   // This routine can be called by any of the DNS worker threads.
    118   DnsReloader* dns_reloader = g_dns_reloader.Pointer();
    119   dns_reloader->MaybeReload();
    120 }
    121 
    122 }  // namespace net
    123 
    124 #endif  // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) &&
    125         // !defined(OS_ANDROID)
    126