netwerk/dns/nsDNSService2.cpp

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim: set sw=4 ts=8 et tw=80 : */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsDNSService2.h"
     8 #include "nsIDNSRecord.h"
     9 #include "nsIDNSListener.h"
    10 #include "nsICancelable.h"
    11 #include "nsIPrefService.h"
    12 #include "nsIPrefBranch.h"
    13 #include "nsIServiceManager.h"
    14 #include "nsIXPConnect.h"
    15 #include "nsProxyRelease.h"
    16 #include "nsReadableUtils.h"
    17 #include "nsString.h"
    18 #include "nsAutoPtr.h"
    19 #include "nsNetCID.h"
    20 #include "nsError.h"
    21 #include "nsDNSPrefetch.h"
    22 #include "nsThreadUtils.h"
    23 #include "nsIProtocolProxyService.h"
    24 #include "prsystem.h"
    25 #include "prnetdb.h"
    26 #include "prmon.h"
    27 #include "prio.h"
    28 #include "plstr.h"
    29 #include "nsIOService.h"
    30 #include "nsCharSeparatedTokenizer.h"
    31 #include "nsNetAddr.h"
    32 #include "nsProxyRelease.h"
    34 #include "mozilla/Attributes.h"
    35 #include "mozilla/VisualEventTracer.h"
    36 #include "mozilla/net/NeckoCommon.h"
    37 #include "mozilla/net/ChildDNSService.h"
    38 #include "mozilla/net/DNSListenerProxy.h"
    40 using namespace mozilla;
    41 using namespace mozilla::net;
    43 static const char kPrefDnsCacheEntries[]     = "network.dnsCacheEntries";
    44 static const char kPrefDnsCacheExpiration[]  = "network.dnsCacheExpiration";
    45 static const char kPrefDnsCacheGrace[]       = "network.dnsCacheExpirationGracePeriod";
    46 static const char kPrefIPv4OnlyDomains[]     = "network.dns.ipv4OnlyDomains";
    47 static const char kPrefDisableIPv6[]         = "network.dns.disableIPv6";
    48 static const char kPrefDisablePrefetch[]     = "network.dns.disablePrefetch";
    49 static const char kPrefDnsLocalDomains[]     = "network.dns.localDomains";
    50 static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
    52 //-----------------------------------------------------------------------------
    54 class nsDNSRecord : public nsIDNSRecord
    55 {
    56 public:
    57     NS_DECL_THREADSAFE_ISUPPORTS
    58     NS_DECL_NSIDNSRECORD
    60     nsDNSRecord(nsHostRecord *hostRecord)
    61         : mHostRecord(hostRecord)
    62         , mIter(nullptr)
    63         , mIterGenCnt(-1)
    64         , mDone(false) {}
    66 private:
    67     virtual ~nsDNSRecord() {}
    69     nsRefPtr<nsHostRecord>  mHostRecord;
    70     NetAddrElement         *mIter;
    71     int                     mIterGenCnt; // the generation count of
    72                                          // mHostRecord->addr_info when we
    73                                          // start iterating
    74     bool                    mDone;
    75 };
    77 NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord)
    79 NS_IMETHODIMP
    80 nsDNSRecord::GetCanonicalName(nsACString &result)
    81 {
    82     // this method should only be called if we have a CNAME
    83     NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME,
    84                    NS_ERROR_NOT_AVAILABLE);
    86     // if the record is for an IP address literal, then the canonical
    87     // host name is the IP address literal.
    88     const char *cname;
    89     {
    90         MutexAutoLock lock(mHostRecord->addr_info_lock);
    91         if (mHostRecord->addr_info)
    92             cname = mHostRecord->addr_info->mCanonicalName ?
    93                 mHostRecord->addr_info->mCanonicalName :
    94                 mHostRecord->addr_info->mHostName;
    95         else
    96             cname = mHostRecord->host;
    97         result.Assign(cname);
    98     }
    99     return NS_OK;
   100 }
   102 NS_IMETHODIMP
   103 nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
   104 {
   105     if (mDone) {
   106         return NS_ERROR_NOT_AVAILABLE;
   107     }
   109     mHostRecord->addr_info_lock.Lock();
   110     if (mHostRecord->addr_info) {
   111         if (mIterGenCnt != mHostRecord->addr_info_gencnt) {
   112             // mHostRecord->addr_info has changed, restart the iteration.
   113             mIter = nullptr;
   114             mIterGenCnt = mHostRecord->addr_info_gencnt;
   115         }
   117         bool startedFresh = !mIter;
   119         do {
   120             if (!mIter) {
   121                 mIter = mHostRecord->addr_info->mAddresses.getFirst();
   122             } else {
   123                 mIter = mIter->getNext();
   124             }
   125         }
   126         while (mIter && mHostRecord->Blacklisted(&mIter->mAddress));
   128         if (!mIter && startedFresh) {
   129             // If everything was blacklisted we want to reset the blacklist (and
   130             // likely relearn it) and return the first address. That is better
   131             // than nothing.
   132             mHostRecord->ResetBlacklist();
   133             mIter = mHostRecord->addr_info->mAddresses.getFirst();
   134         }
   136         if (mIter) {
   137             memcpy(addr, &mIter->mAddress, sizeof(NetAddr));
   138         }
   140         mHostRecord->addr_info_lock.Unlock();
   142         if (!mIter) {
   143             mDone = true;
   144             return NS_ERROR_NOT_AVAILABLE;
   145         }
   146     }
   147     else {
   148         mHostRecord->addr_info_lock.Unlock();
   150         if (!mHostRecord->addr) {
   151             // Both mHostRecord->addr_info and mHostRecord->addr are null.
   152             // This can happen if mHostRecord->addr_info expired and the
   153             // attempt to reresolve it failed.
   154             return NS_ERROR_NOT_AVAILABLE;
   155         }
   156         memcpy(addr, mHostRecord->addr, sizeof(NetAddr));
   157         mDone = true;
   158     }
   160     // set given port
   161     port = htons(port);
   162     if (addr->raw.family == AF_INET) {
   163         addr->inet.port = port;
   164     }
   165     else if (addr->raw.family == AF_INET6) {
   166         addr->inet6.port = port;
   167     }
   169     return NS_OK;
   170 }
   172 NS_IMETHODIMP
   173 nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr * *result)
   174 {
   175     NetAddr addr;
   176     nsresult rv = GetNextAddr(port, &addr);
   177     if (NS_FAILED(rv)) return rv;
   179     NS_ADDREF(*result = new nsNetAddr(&addr));
   181     return NS_OK;
   182 }
   184 NS_IMETHODIMP
   185 nsDNSRecord::GetNextAddrAsString(nsACString &result)
   186 {
   187     NetAddr addr;
   188     nsresult rv = GetNextAddr(0, &addr);
   189     if (NS_FAILED(rv)) return rv;
   191     char buf[kIPv6CStrBufSize];
   192     if (NetAddrToString(&addr, buf, sizeof(buf))) {
   193         result.Assign(buf);
   194         return NS_OK;
   195     }
   196     NS_ERROR("NetAddrToString failed unexpectedly");
   197     return NS_ERROR_FAILURE; // conversion failed for some reason
   198 }
   200 NS_IMETHODIMP
   201 nsDNSRecord::HasMore(bool *result)
   202 {
   203     if (mDone) {
   204         *result = false;
   205         return NS_OK;
   206     }
   208     NetAddrElement *iterCopy = mIter;
   210     NetAddr addr;
   211     *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
   213     mIter = iterCopy;
   214     mDone = false;
   216     return NS_OK;
   217 }
   219 NS_IMETHODIMP
   220 nsDNSRecord::Rewind()
   221 {
   222     mIter = nullptr;
   223     mIterGenCnt = -1;
   224     mDone = false;
   225     return NS_OK;
   226 }
   228 NS_IMETHODIMP
   229 nsDNSRecord::ReportUnusable(uint16_t aPort)
   230 {
   231     // right now we don't use the port in the blacklist
   233     MutexAutoLock lock(mHostRecord->addr_info_lock);
   235     // Check that we are using a real addr_info (as opposed to a single
   236     // constant address), and that the generation count is valid. Otherwise,
   237     // ignore the report.
   239     if (mHostRecord->addr_info &&
   240         mIterGenCnt == mHostRecord->addr_info_gencnt &&
   241         mIter) {
   242         mHostRecord->ReportUnusable(&mIter->mAddress);
   243     }
   245     return NS_OK;
   246 }
   248 //-----------------------------------------------------------------------------
   250 class nsDNSAsyncRequest MOZ_FINAL : public nsResolveHostCallback
   251                                   , public nsICancelable
   252 {
   253 public:
   254     NS_DECL_THREADSAFE_ISUPPORTS
   255     NS_DECL_NSICANCELABLE
   257     nsDNSAsyncRequest(nsHostResolver   *res,
   258                       const nsACString &host,
   259                       nsIDNSListener   *listener,
   260                       uint16_t          flags,
   261                       uint16_t          af)
   262         : mResolver(res)
   263         , mHost(host)
   264         , mListener(listener)
   265         , mFlags(flags)
   266         , mAF(af) {}
   267     ~nsDNSAsyncRequest() {}
   269     void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
   270     // Returns TRUE if the DNS listener arg is the same as the member listener
   271     // Used in Cancellations to remove DNS requests associated with a
   272     // particular hostname and nsIDNSListener
   273     bool EqualsAsyncListener(nsIDNSListener *aListener);
   275     size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const;
   277     nsRefPtr<nsHostResolver> mResolver;
   278     nsCString                mHost; // hostname we're resolving
   279     nsCOMPtr<nsIDNSListener> mListener;
   280     uint16_t                 mFlags;
   281     uint16_t                 mAF;
   282 };
   284 void
   285 nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
   286                                     nsHostRecord   *hostRecord,
   287                                     nsresult        status)
   288 {
   289     // need to have an owning ref when we issue the callback to enable
   290     // the caller to be able to addref/release multiple times without
   291     // destroying the record prematurely.
   292     nsCOMPtr<nsIDNSRecord> rec;
   293     if (NS_SUCCEEDED(status)) {
   294         NS_ASSERTION(hostRecord, "no host record");
   295         rec = new nsDNSRecord(hostRecord);
   296         if (!rec)
   297             status = NS_ERROR_OUT_OF_MEMORY;
   298     }
   300     MOZ_EVENT_TRACER_DONE(this, "net::dns::lookup");
   302     mListener->OnLookupComplete(this, rec, status);
   303     mListener = nullptr;
   305     // release the reference to ourselves that was added before we were
   306     // handed off to the host resolver.
   307     NS_RELEASE_THIS();
   308 }
   310 bool
   311 nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
   312 {
   313     return (aListener == mListener);
   314 }
   316 size_t
   317 nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
   318 {
   319     size_t n = mallocSizeOf(this);
   321     // The following fields aren't measured.
   322     // - mHost, because it's a non-owning pointer
   323     // - mResolver, because it's a non-owning pointer
   324     // - mListener, because it's a non-owning pointer
   326     return n;
   327 }
   329 NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
   331 NS_IMETHODIMP
   332 nsDNSAsyncRequest::Cancel(nsresult reason)
   333 {
   334     NS_ENSURE_ARG(NS_FAILED(reason));
   335     mResolver->DetachCallback(mHost.get(), mFlags, mAF, this, reason);
   336     return NS_OK;
   337 }
   339 //-----------------------------------------------------------------------------
   341 class nsDNSSyncRequest : public nsResolveHostCallback
   342 {
   343 public:
   344     nsDNSSyncRequest(PRMonitor *mon)
   345         : mDone(false)
   346         , mStatus(NS_OK)
   347         , mMonitor(mon) {}
   348     virtual ~nsDNSSyncRequest() {}
   350     void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
   351     bool EqualsAsyncListener(nsIDNSListener *aListener);
   352     size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const;
   354     bool                   mDone;
   355     nsresult               mStatus;
   356     nsRefPtr<nsHostRecord> mHostRecord;
   358 private:
   359     PRMonitor             *mMonitor;
   360 };
   362 void
   363 nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver,
   364                                    nsHostRecord   *hostRecord,
   365                                    nsresult        status)
   366 {
   367     // store results, and wake up nsDNSService::Resolve to process results.
   368     PR_EnterMonitor(mMonitor);
   369     mDone = true;
   370     mStatus = status;
   371     mHostRecord = hostRecord;
   372     PR_Notify(mMonitor);
   373     PR_ExitMonitor(mMonitor);
   374 }
   376 bool
   377 nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
   378 {
   379     // Sync request: no listener to compare
   380     return false;
   381 }
   383 size_t
   384 nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
   385 {
   386     size_t n = mallocSizeOf(this);
   388     // The following fields aren't measured.
   389     // - mHostRecord, because it's a non-owning pointer
   391     // Measurement of the following members may be added later if DMD finds it
   392     // is worthwhile:
   393     // - mMonitor
   395     return n;
   396 }
   398 class NotifyDNSResolution: public nsRunnable
   399 {
   400 public:
   401     NotifyDNSResolution(nsMainThreadPtrHandle<nsIObserverService> &aObs,
   402                         const nsACString &aHostname)
   403         : mObs(aObs)
   404         , mHostname(aHostname)
   405     {
   406         MOZ_ASSERT(mObs);
   407     }
   409     NS_IMETHOD Run()
   410     {
   411         MOZ_ASSERT(NS_IsMainThread());
   412         mObs->NotifyObservers(nullptr,
   413                               "dns-resolution-request",
   414                               NS_ConvertUTF8toUTF16(mHostname).get());
   415         return NS_OK;
   416     }
   418 private:
   419     nsMainThreadPtrHandle<nsIObserverService> mObs;
   420     nsCString                                 mHostname;
   421 };
   423 //-----------------------------------------------------------------------------
   425 nsDNSService::nsDNSService()
   426     : mLock("nsDNSServer.mLock")
   427     , mFirstTime(true)
   428     , mOffline(false)
   429 {
   430 }
   432 nsDNSService::~nsDNSService()
   433 {
   434 }
   436 NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver,
   437                   nsIMemoryReporter)
   439 /******************************************************************************
   440  * nsDNSService impl:
   441  * singleton instance ctor/dtor methods
   442  ******************************************************************************/
   443 static nsDNSService *gDNSService;
   445 nsIDNSService*
   446 nsDNSService::GetXPCOMSingleton()
   447 {
   448     if (IsNeckoChild()) {
   449         return ChildDNSService::GetSingleton();
   450     }
   452     return GetSingleton();
   453 }
   455 nsDNSService*
   456 nsDNSService::GetSingleton()
   457 {
   458     NS_ASSERTION(!IsNeckoChild(), "not a parent process");
   460     if (gDNSService) {
   461         NS_ADDREF(gDNSService);
   462         return gDNSService;
   463     }
   465     gDNSService = new nsDNSService();
   466     if (gDNSService) {
   467         NS_ADDREF(gDNSService);
   468         if (NS_FAILED(gDNSService->Init())) {
   469               NS_RELEASE(gDNSService);
   470         }
   471     }
   473     return gDNSService;
   474 }
   476 NS_IMETHODIMP
   477 nsDNSService::Init()
   478 {
   479     if (mResolver)
   480         return NS_OK;
   481     NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
   483     // prefs
   484     uint32_t maxCacheEntries  = 400;
   485     uint32_t maxCacheLifetime = 120; // seconds
   486     uint32_t lifetimeGracePeriod = 60; // seconds
   487     bool     disableIPv6      = false;
   488     bool     disablePrefetch  = false;
   489     bool     disableDNS       = false;
   490     int      proxyType        = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
   491     bool     notifyResolution = false;
   493     nsAdoptingCString ipv4OnlyDomains;
   494     nsAdoptingCString localDomains;
   496     // read prefs
   497     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   498     if (prefs) {
   499         int32_t val;
   500         if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
   501             maxCacheEntries = (uint32_t) val;
   502         if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
   503             maxCacheLifetime = val;
   504         if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val)))
   505             lifetimeGracePeriod = val;
   507         // ASSUMPTION: pref branch does not modify out params on failure
   508         prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
   509         prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
   510         prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains));
   511         prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
   513         // If a manual proxy is in use, disable prefetch implicitly
   514         prefs->GetIntPref("network.proxy.type", &proxyType);
   516         // If the user wants remote DNS, we should fail any lookups that still
   517         // make it here.
   518         prefs->GetBoolPref("network.proxy.socks_remote_dns", &disableDNS);
   520         prefs->GetBoolPref(kPrefDnsNotifyResolution, &notifyResolution);
   521     }
   523     if (mFirstTime) {
   524         mFirstTime = false;
   526         // register as prefs observer
   527         if (prefs) {
   528             prefs->AddObserver(kPrefDnsCacheEntries, this, false);
   529             prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
   530             prefs->AddObserver(kPrefDnsCacheGrace, this, false);
   531             prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
   532             prefs->AddObserver(kPrefDnsLocalDomains, this, false);
   533             prefs->AddObserver(kPrefDisableIPv6, this, false);
   534             prefs->AddObserver(kPrefDisablePrefetch, this, false);
   535             prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
   537             // Monitor these to see if there is a change in proxy configuration
   538             // If a manual proxy is in use, disable prefetch implicitly
   539             prefs->AddObserver("network.proxy.", this, false);
   540         }
   542         nsresult rv;
   543         nsCOMPtr<nsIObserverService> observerService =
   544             do_GetService("@mozilla.org/observer-service;1", &rv);
   545         if (NS_SUCCEEDED(rv)) {
   546             observerService->AddObserver(this, "last-pb-context-exited", false);
   547         }
   548     }
   550     nsDNSPrefetch::Initialize(this);
   552     // Don't initialize the resolver if we're in offline mode.
   553     // Later on, the IO service will reinitialize us when going online.
   554     if (gIOService->IsOffline() && !gIOService->IsComingOnline())
   555         return NS_OK;
   557     nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
   559     nsCOMPtr<nsIObserverService> obs =
   560         do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
   562     nsRefPtr<nsHostResolver> res;
   563     nsresult rv = nsHostResolver::Create(maxCacheEntries,
   564                                          maxCacheLifetime,
   565                                          lifetimeGracePeriod,
   566                                          getter_AddRefs(res));
   567     if (NS_SUCCEEDED(rv)) {
   568         // now, set all of our member variables while holding the lock
   569         MutexAutoLock lock(mLock);
   570         mResolver = res;
   571         mIDN = idn;
   572         mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
   573         mDisableIPv6 = disableIPv6;
   574         mDisableDNS = disableDNS;
   576         // Disable prefetching either by explicit preference or if a manual proxy is configured 
   577         mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
   579         mLocalDomains.Clear();
   580         if (localDomains) {
   581             nsAdoptingString domains;
   582             domains.AssignASCII(nsDependentCString(localDomains).get());
   583             nsCharSeparatedTokenizer tokenizer(domains, ',',
   584                                                nsCharSeparatedTokenizerTemplate<>::SEPARATOR_OPTIONAL);
   586             while (tokenizer.hasMoreTokens()) {
   587                 const nsSubstring& domain = tokenizer.nextToken();
   588                 mLocalDomains.PutEntry(nsDependentCString(NS_ConvertUTF16toUTF8(domain).get()));
   589             }
   590         }
   591         mNotifyResolution = notifyResolution;
   592         if (mNotifyResolution) {
   593             mObserverService =
   594               new nsMainThreadPtrHolder<nsIObserverService>(obs);
   595         }
   596     }
   598     RegisterWeakMemoryReporter(this);
   600     return rv;
   601 }
   603 NS_IMETHODIMP
   604 nsDNSService::Shutdown()
   605 {
   606     UnregisterWeakMemoryReporter(this);
   608     nsRefPtr<nsHostResolver> res;
   609     {
   610         MutexAutoLock lock(mLock);
   611         res = mResolver;
   612         mResolver = nullptr;
   613     }
   614     if (res)
   615         res->Shutdown();
   616     return NS_OK;
   617 }
   619 NS_IMETHODIMP
   620 nsDNSService::GetOffline(bool *offline)
   621 {
   622     *offline = mOffline;
   623     return NS_OK;
   624 }
   626 NS_IMETHODIMP
   627 nsDNSService::SetOffline(bool offline)
   628 {
   629     mOffline = offline;
   630     return NS_OK;
   631 }
   633 NS_IMETHODIMP
   634 nsDNSService::GetPrefetchEnabled(bool *outVal)
   635 {
   636     *outVal = !mDisablePrefetch;
   637     return NS_OK;
   638 }
   640 NS_IMETHODIMP
   641 nsDNSService::SetPrefetchEnabled(bool inVal)
   642 {
   643     mDisablePrefetch = !inVal;
   644     return NS_OK;
   645 }
   648 NS_IMETHODIMP
   649 nsDNSService::AsyncResolve(const nsACString  &hostname,
   650                            uint32_t           flags,
   651                            nsIDNSListener    *listener,
   652                            nsIEventTarget    *target_,
   653                            nsICancelable    **result)
   654 {
   655     // grab reference to global host resolver and IDN service.  beware
   656     // simultaneous shutdown!!
   657     nsRefPtr<nsHostResolver> res;
   658     nsCOMPtr<nsIIDNService> idn;
   659     nsCOMPtr<nsIEventTarget> target = target_;
   660     bool localDomain = false;
   661     {
   662         MutexAutoLock lock(mLock);
   664         if (mDisablePrefetch && (flags & RESOLVE_SPECULATE))
   665             return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
   667         res = mResolver;
   668         idn = mIDN;
   669         localDomain = mLocalDomains.GetEntry(hostname);
   670     }
   672     if (mNotifyResolution) {
   673         NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService,
   674                                                         hostname));
   675     }
   677     PRNetAddr tempAddr;
   678     if (mDisableDNS) {
   679         // Allow IP lookups through, but nothing else.
   680         if (PR_StringToNetAddr(hostname.BeginReading(), &tempAddr) != PR_SUCCESS) {
   681             return NS_ERROR_UNKNOWN_PROXY_HOST; // XXX: NS_ERROR_NOT_IMPLEMENTED?
   682         }
   683     }
   685     if (!res)
   686         return NS_ERROR_OFFLINE;
   688     if (mOffline)
   689         flags |= RESOLVE_OFFLINE;
   691     const nsACString *hostPtr = &hostname;
   693     if (localDomain) {
   694         hostPtr = &(NS_LITERAL_CSTRING("localhost"));
   695     }
   697     nsresult rv;
   698     nsAutoCString hostACE;
   699     if (idn && !IsASCII(*hostPtr)) {
   700         if (IsUTF8(*hostPtr) &&
   701             NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE))) {
   702             hostPtr = &hostACE;
   703         } else {
   704             return NS_ERROR_FAILURE;
   705         }
   706     }
   708     // make sure JS callers get notification on the main thread
   709     nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
   710     if (wrappedListener && !target) {
   711         nsCOMPtr<nsIThread> mainThread;
   712         NS_GetMainThread(getter_AddRefs(mainThread));
   713         target = do_QueryInterface(mainThread);
   714     }
   716     if (target) {
   717       listener = new DNSListenerProxy(listener, target);
   718     }
   720     uint16_t af = GetAFForLookup(*hostPtr, flags);
   722     nsDNSAsyncRequest *req =
   723             new nsDNSAsyncRequest(res, *hostPtr, listener, flags, af);
   724     if (!req)
   725         return NS_ERROR_OUT_OF_MEMORY;
   726     NS_ADDREF(*result = req);
   728     MOZ_EVENT_TRACER_NAME_OBJECT(req, hostname.BeginReading());
   729     MOZ_EVENT_TRACER_WAIT(req, "net::dns::lookup");
   731     // addref for resolver; will be released when OnLookupComplete is called.
   732     NS_ADDREF(req);
   733     rv = res->ResolveHost(req->mHost.get(), flags, af, req);
   734     if (NS_FAILED(rv)) {
   735         NS_RELEASE(req);
   736         NS_RELEASE(*result);
   737     }
   738     return rv;
   739 }
   741 NS_IMETHODIMP
   742 nsDNSService::CancelAsyncResolve(const nsACString  &aHostname,
   743                                  uint32_t           aFlags,
   744                                  nsIDNSListener    *aListener,
   745                                  nsresult           aReason)
   746 {
   747     // grab reference to global host resolver and IDN service.  beware
   748     // simultaneous shutdown!!
   749     nsRefPtr<nsHostResolver> res;
   750     nsCOMPtr<nsIIDNService> idn;
   751     {
   752         MutexAutoLock lock(mLock);
   754         if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE))
   755             return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
   757         res = mResolver;
   758         idn = mIDN;
   759     }
   760     if (!res)
   761         return NS_ERROR_OFFLINE;
   763     nsCString hostname(aHostname);
   765     nsAutoCString hostACE;
   766     if (idn && !IsASCII(aHostname)) {
   767         if (IsUTF8(aHostname) &&
   768             NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE))) {
   769             hostname = hostACE;
   770         } else {
   771             return NS_ERROR_FAILURE;
   772         }
   773     }
   775     uint16_t af = GetAFForLookup(hostname, aFlags);
   777     res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason);
   778     return NS_OK;
   779 }
   781 NS_IMETHODIMP
   782 nsDNSService::Resolve(const nsACString &hostname,
   783                       uint32_t          flags,
   784                       nsIDNSRecord    **result)
   785 {
   786     // grab reference to global host resolver and IDN service.  beware
   787     // simultaneous shutdown!!
   788     nsRefPtr<nsHostResolver> res;
   789     nsCOMPtr<nsIIDNService> idn;
   790     bool localDomain = false;
   791     {
   792         MutexAutoLock lock(mLock);
   793         res = mResolver;
   794         idn = mIDN;
   795         localDomain = mLocalDomains.GetEntry(hostname);
   796     }
   798     if (mNotifyResolution) {
   799         NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService,
   800                                                         hostname));
   801     }
   803     NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
   805     if (mOffline)
   806         flags |= RESOLVE_OFFLINE;
   808     PRNetAddr tempAddr;
   809     if (mDisableDNS) {
   810         // Allow IP lookups through, but nothing else.
   811         if (PR_StringToNetAddr(hostname.BeginReading(), &tempAddr) != PR_SUCCESS) {
   812             return NS_ERROR_UNKNOWN_PROXY_HOST; // XXX: NS_ERROR_NOT_IMPLEMENTED?
   813         }
   814     }
   816     const nsACString *hostPtr = &hostname;
   818     if (localDomain) {
   819         hostPtr = &(NS_LITERAL_CSTRING("localhost"));
   820     }
   822     nsresult rv;
   823     nsAutoCString hostACE;
   824     if (idn && !IsASCII(*hostPtr)) {
   825         if (IsUTF8(*hostPtr) &&
   826             NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE))) {
   827             hostPtr = &hostACE;
   828         } else {
   829             return NS_ERROR_FAILURE;
   830         }
   831     }
   833     //
   834     // sync resolve: since the host resolver only works asynchronously, we need
   835     // to use a mutex and a condvar to wait for the result.  however, since the
   836     // result may be in the resolvers cache, we might get called back recursively
   837     // on the same thread.  so, our mutex needs to be re-entrant.  in other words,
   838     // we need to use a monitor! ;-)
   839     //
   841     PRMonitor *mon = PR_NewMonitor();
   842     if (!mon)
   843         return NS_ERROR_OUT_OF_MEMORY;
   845     PR_EnterMonitor(mon);
   846     nsDNSSyncRequest syncReq(mon);
   848     uint16_t af = GetAFForLookup(*hostPtr, flags);
   850     rv = res->ResolveHost(PromiseFlatCString(*hostPtr).get(), flags, af, &syncReq);
   851     if (NS_SUCCEEDED(rv)) {
   852         // wait for result
   853         while (!syncReq.mDone)
   854             PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
   856         if (NS_FAILED(syncReq.mStatus))
   857             rv = syncReq.mStatus;
   858         else {
   859             NS_ASSERTION(syncReq.mHostRecord, "no host record");
   860             nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord);
   861             if (!rec)
   862                 rv = NS_ERROR_OUT_OF_MEMORY;
   863             else
   864                 NS_ADDREF(*result = rec);
   865         }
   866     }
   868     PR_ExitMonitor(mon);
   869     PR_DestroyMonitor(mon);
   870     return rv;
   871 }
   873 NS_IMETHODIMP
   874 nsDNSService::GetMyHostName(nsACString &result)
   875 {
   876     char name[100];
   877     if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
   878         result = name;
   879         return NS_OK;
   880     }
   881     return NS_ERROR_FAILURE;
   882 }
   884 NS_IMETHODIMP
   885 nsDNSService::Observe(nsISupports *subject, const char *topic, const char16_t *data)
   886 {
   887     // we are only getting called if a preference has changed. 
   888     NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 ||
   889         strcmp(topic, "last-pb-context-exited") == 0,
   890         "unexpected observe call");
   892     //
   893     // Shutdown and this function are both only called on the UI thread, so we don't
   894     // have to worry about mResolver being cleared out from under us.
   895     //
   896     // NOTE Shutting down and reinitializing the service like this is obviously
   897     // suboptimal if Observe gets called several times in a row, but we don't
   898     // expect that to be the case.
   899     //
   901     if (mResolver) {
   902         Shutdown();
   903     }
   904     Init();
   905     return NS_OK;
   906 }
   908 uint16_t
   909 nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags)
   910 {
   911     if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6))
   912         return PR_AF_INET;
   914     MutexAutoLock lock(mLock);
   916     uint16_t af = PR_AF_UNSPEC;
   918     if (!mIPv4OnlyDomains.IsEmpty()) {
   919         const char *domain, *domainEnd, *end;
   920         uint32_t hostLen, domainLen;
   922         // see if host is in one of the IPv4-only domains
   923         domain = mIPv4OnlyDomains.BeginReading();
   924         domainEnd = mIPv4OnlyDomains.EndReading(); 
   926         nsACString::const_iterator hostStart;
   927         host.BeginReading(hostStart);
   928         hostLen = host.Length();
   930         do {
   931             // skip any whitespace
   932             while (*domain == ' ' || *domain == '\t')
   933                 ++domain;
   935             // find end of this domain in the string
   936             end = strchr(domain, ',');
   937             if (!end)
   938                 end = domainEnd;
   940             // to see if the hostname is in the domain, check if the domain
   941             // matches the end of the hostname.
   942             domainLen = end - domain;
   943             if (domainLen && hostLen >= domainLen) {
   944                 const char *hostTail = hostStart.get() + hostLen - domainLen;
   945                 if (PL_strncasecmp(domain, hostTail, domainLen) == 0) {
   946                     // now, make sure either that the hostname is a direct match or
   947                     // that the hostname begins with a dot.
   948                     if (hostLen == domainLen ||
   949                             *hostTail == '.' || *(hostTail - 1) == '.') {
   950                         af = PR_AF_INET;
   951                         break;
   952                     }
   953                 }
   954             }
   956             domain = end + 1;
   957         } while (*end);
   958     }
   960     if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4))
   961         af = PR_AF_INET6;
   963     return af;
   964 }
   966 NS_IMETHODIMP
   967 nsDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
   968 {
   969     NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED);
   970     mResolver->GetDNSCacheEntries(args);
   971     return NS_OK;
   972 }
   974 static size_t
   975 SizeOfLocalDomainsEntryExcludingThis(nsCStringHashKey* entry,
   976                                      MallocSizeOf mallocSizeOf, void*)
   977 {
   978     return entry->GetKey().SizeOfExcludingThisMustBeUnshared(mallocSizeOf);
   979 }
   981 size_t
   982 nsDNSService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   983 {
   984     // Measurement of the following members may be added later if DMD finds it
   985     // is worthwhile:
   986     // - mIDN
   987     // - mLock
   989     size_t n = mallocSizeOf(this);
   990     n += mResolver->SizeOfIncludingThis(mallocSizeOf);
   991     n += mIPv4OnlyDomains.SizeOfExcludingThisMustBeUnshared(mallocSizeOf);
   992     n += mLocalDomains.SizeOfExcludingThis(SizeOfLocalDomainsEntryExcludingThis,
   993                                            mallocSizeOf);
   994     return n;
   995 }
   997 MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf)
   999 NS_IMETHODIMP
  1000 nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport,
  1001                              nsISupports* aData)
  1003     return MOZ_COLLECT_REPORT(
  1004         "explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
  1005         SizeOfIncludingThis(DNSServiceMallocSizeOf),
  1006         "Memory used for the DNS service.");

mercurial