Bumping manifests a=b2g-bump
[gecko.git] / netwerk / dns / nsDNSService2.cpp
blobf2462634a12256377496a04eb811c587e8b33d2f
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"
33 #include "nsIObserverService.h"
34 #include "nsINetworkLinkService.h"
36 #include "mozilla/Attributes.h"
37 #include "mozilla/VisualEventTracer.h"
38 #include "mozilla/net/NeckoCommon.h"
39 #include "mozilla/net/ChildDNSService.h"
40 #include "mozilla/net/DNSListenerProxy.h"
41 #include "mozilla/Services.h"
43 using namespace mozilla;
44 using namespace mozilla::net;
46 static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries";
47 static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
48 static const char kPrefDnsCacheGrace[] = "network.dnsCacheExpirationGracePeriod";
49 static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains";
50 static const char kPrefDisableIPv6[] = "network.dns.disableIPv6";
51 static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch";
52 static const char kPrefDnsLocalDomains[] = "network.dns.localDomains";
53 static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
55 //-----------------------------------------------------------------------------
57 class nsDNSRecord : public nsIDNSRecord
59 public:
60 NS_DECL_THREADSAFE_ISUPPORTS
61 NS_DECL_NSIDNSRECORD
63 explicit nsDNSRecord(nsHostRecord *hostRecord)
64 : mHostRecord(hostRecord)
65 , mIter(nullptr)
66 , mIterGenCnt(-1)
67 , mDone(false) {}
69 private:
70 virtual ~nsDNSRecord() {}
72 nsRefPtr<nsHostRecord> mHostRecord;
73 NetAddrElement *mIter;
74 int mIterGenCnt; // the generation count of
75 // mHostRecord->addr_info when we
76 // start iterating
77 bool mDone;
80 NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord)
82 NS_IMETHODIMP
83 nsDNSRecord::GetCanonicalName(nsACString &result)
85 // this method should only be called if we have a CNAME
86 NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME,
87 NS_ERROR_NOT_AVAILABLE);
89 // if the record is for an IP address literal, then the canonical
90 // host name is the IP address literal.
91 const char *cname;
93 MutexAutoLock lock(mHostRecord->addr_info_lock);
94 if (mHostRecord->addr_info)
95 cname = mHostRecord->addr_info->mCanonicalName ?
96 mHostRecord->addr_info->mCanonicalName :
97 mHostRecord->addr_info->mHostName;
98 else
99 cname = mHostRecord->host;
100 result.Assign(cname);
102 return NS_OK;
105 NS_IMETHODIMP
106 nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
108 if (mDone) {
109 return NS_ERROR_NOT_AVAILABLE;
112 mHostRecord->addr_info_lock.Lock();
113 if (mHostRecord->addr_info) {
114 if (mIterGenCnt != mHostRecord->addr_info_gencnt) {
115 // mHostRecord->addr_info has changed, restart the iteration.
116 mIter = nullptr;
117 mIterGenCnt = mHostRecord->addr_info_gencnt;
120 bool startedFresh = !mIter;
122 do {
123 if (!mIter) {
124 mIter = mHostRecord->addr_info->mAddresses.getFirst();
125 } else {
126 mIter = mIter->getNext();
129 while (mIter && mHostRecord->Blacklisted(&mIter->mAddress));
131 if (!mIter && startedFresh) {
132 // If everything was blacklisted we want to reset the blacklist (and
133 // likely relearn it) and return the first address. That is better
134 // than nothing.
135 mHostRecord->ResetBlacklist();
136 mIter = mHostRecord->addr_info->mAddresses.getFirst();
139 if (mIter) {
140 memcpy(addr, &mIter->mAddress, sizeof(NetAddr));
143 mHostRecord->addr_info_lock.Unlock();
145 if (!mIter) {
146 mDone = true;
147 return NS_ERROR_NOT_AVAILABLE;
150 else {
151 mHostRecord->addr_info_lock.Unlock();
153 if (!mHostRecord->addr) {
154 // Both mHostRecord->addr_info and mHostRecord->addr are null.
155 // This can happen if mHostRecord->addr_info expired and the
156 // attempt to reresolve it failed.
157 return NS_ERROR_NOT_AVAILABLE;
159 memcpy(addr, mHostRecord->addr, sizeof(NetAddr));
160 mDone = true;
163 // set given port
164 port = htons(port);
165 if (addr->raw.family == AF_INET) {
166 addr->inet.port = port;
168 else if (addr->raw.family == AF_INET6) {
169 addr->inet6.port = port;
172 return NS_OK;
175 NS_IMETHODIMP
176 nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr * *result)
178 NetAddr addr;
179 nsresult rv = GetNextAddr(port, &addr);
180 if (NS_FAILED(rv)) return rv;
182 NS_ADDREF(*result = new nsNetAddr(&addr));
184 return NS_OK;
187 NS_IMETHODIMP
188 nsDNSRecord::GetNextAddrAsString(nsACString &result)
190 NetAddr addr;
191 nsresult rv = GetNextAddr(0, &addr);
192 if (NS_FAILED(rv)) return rv;
194 char buf[kIPv6CStrBufSize];
195 if (NetAddrToString(&addr, buf, sizeof(buf))) {
196 result.Assign(buf);
197 return NS_OK;
199 NS_ERROR("NetAddrToString failed unexpectedly");
200 return NS_ERROR_FAILURE; // conversion failed for some reason
203 NS_IMETHODIMP
204 nsDNSRecord::HasMore(bool *result)
206 if (mDone) {
207 *result = false;
208 return NS_OK;
211 NetAddrElement *iterCopy = mIter;
212 int iterGenCntCopy = mIterGenCnt;
214 NetAddr addr;
215 *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
217 mIter = iterCopy;
218 mIterGenCnt = iterGenCntCopy;
219 mDone = false;
221 return NS_OK;
224 NS_IMETHODIMP
225 nsDNSRecord::Rewind()
227 mIter = nullptr;
228 mIterGenCnt = -1;
229 mDone = false;
230 return NS_OK;
233 NS_IMETHODIMP
234 nsDNSRecord::ReportUnusable(uint16_t aPort)
236 // right now we don't use the port in the blacklist
238 MutexAutoLock lock(mHostRecord->addr_info_lock);
240 // Check that we are using a real addr_info (as opposed to a single
241 // constant address), and that the generation count is valid. Otherwise,
242 // ignore the report.
244 if (mHostRecord->addr_info &&
245 mIterGenCnt == mHostRecord->addr_info_gencnt &&
246 mIter) {
247 mHostRecord->ReportUnusable(&mIter->mAddress);
250 return NS_OK;
253 //-----------------------------------------------------------------------------
255 class nsDNSAsyncRequest MOZ_FINAL : public nsResolveHostCallback
256 , public nsICancelable
258 ~nsDNSAsyncRequest() {}
260 public:
261 NS_DECL_THREADSAFE_ISUPPORTS
262 NS_DECL_NSICANCELABLE
264 nsDNSAsyncRequest(nsHostResolver *res,
265 const nsACString &host,
266 nsIDNSListener *listener,
267 uint16_t flags,
268 uint16_t af,
269 const nsACString &netInterface)
270 : mResolver(res)
271 , mHost(host)
272 , mListener(listener)
273 , mFlags(flags)
274 , mAF(af)
275 , mNetworkInterface(netInterface) {}
277 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult) MOZ_OVERRIDE;
278 // Returns TRUE if the DNS listener arg is the same as the member listener
279 // Used in Cancellations to remove DNS requests associated with a
280 // particular hostname and nsIDNSListener
281 bool EqualsAsyncListener(nsIDNSListener *aListener) MOZ_OVERRIDE;
283 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const MOZ_OVERRIDE;
285 nsRefPtr<nsHostResolver> mResolver;
286 nsCString mHost; // hostname we're resolving
287 nsCOMPtr<nsIDNSListener> mListener;
288 uint16_t mFlags;
289 uint16_t mAF;
290 nsCString mNetworkInterface;
293 void
294 nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
295 nsHostRecord *hostRecord,
296 nsresult status)
298 // need to have an owning ref when we issue the callback to enable
299 // the caller to be able to addref/release multiple times without
300 // destroying the record prematurely.
301 nsCOMPtr<nsIDNSRecord> rec;
302 if (NS_SUCCEEDED(status)) {
303 NS_ASSERTION(hostRecord, "no host record");
304 rec = new nsDNSRecord(hostRecord);
305 if (!rec)
306 status = NS_ERROR_OUT_OF_MEMORY;
309 MOZ_EVENT_TRACER_DONE(this, "net::dns::lookup");
311 mListener->OnLookupComplete(this, rec, status);
312 mListener = nullptr;
314 // release the reference to ourselves that was added before we were
315 // handed off to the host resolver.
316 NS_RELEASE_THIS();
319 bool
320 nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
322 nsCOMPtr<nsIDNSListenerProxy> wrapper = do_QueryInterface(mListener);
323 if (wrapper) {
324 nsCOMPtr<nsIDNSListener> originalListener;
325 wrapper->GetOriginalListener(getter_AddRefs(originalListener));
326 return aListener == originalListener;
328 return (aListener == mListener);
331 size_t
332 nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
334 size_t n = mallocSizeOf(this);
336 // The following fields aren't measured.
337 // - mHost, because it's a non-owning pointer
338 // - mResolver, because it's a non-owning pointer
339 // - mListener, because it's a non-owning pointer
341 return n;
344 NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
346 NS_IMETHODIMP
347 nsDNSAsyncRequest::Cancel(nsresult reason)
349 NS_ENSURE_ARG(NS_FAILED(reason));
350 mResolver->DetachCallback(mHost.get(), mFlags, mAF, mNetworkInterface.get(),
351 this, reason);
352 return NS_OK;
355 //-----------------------------------------------------------------------------
357 class nsDNSSyncRequest : public nsResolveHostCallback
359 public:
360 explicit nsDNSSyncRequest(PRMonitor *mon)
361 : mDone(false)
362 , mStatus(NS_OK)
363 , mMonitor(mon) {}
364 virtual ~nsDNSSyncRequest() {}
366 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
367 bool EqualsAsyncListener(nsIDNSListener *aListener);
368 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const;
370 bool mDone;
371 nsresult mStatus;
372 nsRefPtr<nsHostRecord> mHostRecord;
374 private:
375 PRMonitor *mMonitor;
378 void
379 nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver,
380 nsHostRecord *hostRecord,
381 nsresult status)
383 // store results, and wake up nsDNSService::Resolve to process results.
384 PR_EnterMonitor(mMonitor);
385 mDone = true;
386 mStatus = status;
387 mHostRecord = hostRecord;
388 PR_Notify(mMonitor);
389 PR_ExitMonitor(mMonitor);
392 bool
393 nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
395 // Sync request: no listener to compare
396 return false;
399 size_t
400 nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
402 size_t n = mallocSizeOf(this);
404 // The following fields aren't measured.
405 // - mHostRecord, because it's a non-owning pointer
407 // Measurement of the following members may be added later if DMD finds it
408 // is worthwhile:
409 // - mMonitor
411 return n;
414 class NotifyDNSResolution: public nsRunnable
416 public:
417 NotifyDNSResolution(nsMainThreadPtrHandle<nsIObserverService> &aObs,
418 const nsACString &aHostname)
419 : mObs(aObs)
420 , mHostname(aHostname)
422 MOZ_ASSERT(mObs);
425 NS_IMETHOD Run()
427 MOZ_ASSERT(NS_IsMainThread());
428 mObs->NotifyObservers(nullptr,
429 "dns-resolution-request",
430 NS_ConvertUTF8toUTF16(mHostname).get());
431 return NS_OK;
434 private:
435 nsMainThreadPtrHandle<nsIObserverService> mObs;
436 nsCString mHostname;
439 //-----------------------------------------------------------------------------
441 nsDNSService::nsDNSService()
442 : mLock("nsDNSServer.mLock")
443 , mFirstTime(true)
444 , mOffline(false)
448 nsDNSService::~nsDNSService()
452 NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver,
453 nsIMemoryReporter)
455 /******************************************************************************
456 * nsDNSService impl:
457 * singleton instance ctor/dtor methods
458 ******************************************************************************/
459 static nsDNSService *gDNSService;
461 nsIDNSService*
462 nsDNSService::GetXPCOMSingleton()
464 if (IsNeckoChild()) {
465 return ChildDNSService::GetSingleton();
468 return GetSingleton();
471 nsDNSService*
472 nsDNSService::GetSingleton()
474 NS_ASSERTION(!IsNeckoChild(), "not a parent process");
476 if (gDNSService) {
477 NS_ADDREF(gDNSService);
478 return gDNSService;
481 gDNSService = new nsDNSService();
482 if (gDNSService) {
483 NS_ADDREF(gDNSService);
484 if (NS_FAILED(gDNSService->Init())) {
485 NS_RELEASE(gDNSService);
489 return gDNSService;
492 NS_IMETHODIMP
493 nsDNSService::Init()
495 if (mResolver)
496 return NS_OK;
497 NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
499 // prefs
500 uint32_t maxCacheEntries = 400;
501 uint32_t defaultCacheLifetime = 120; // seconds
502 uint32_t defaultGracePeriod = 60; // seconds
503 bool disableIPv6 = false;
504 bool disablePrefetch = false;
505 int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
506 bool notifyResolution = false;
508 nsAdoptingCString ipv4OnlyDomains;
509 nsAdoptingCString localDomains;
511 // read prefs
512 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
513 if (prefs) {
514 int32_t val;
515 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
516 maxCacheEntries = (uint32_t) val;
517 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
518 defaultCacheLifetime = val;
519 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val)))
520 defaultGracePeriod = val;
522 // ASSUMPTION: pref branch does not modify out params on failure
523 prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
524 prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
525 prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains));
526 prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
528 // If a manual proxy is in use, disable prefetch implicitly
529 prefs->GetIntPref("network.proxy.type", &proxyType);
530 prefs->GetBoolPref(kPrefDnsNotifyResolution, &notifyResolution);
533 if (mFirstTime) {
534 mFirstTime = false;
536 // register as prefs observer
537 if (prefs) {
538 prefs->AddObserver(kPrefDnsCacheEntries, this, false);
539 prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
540 prefs->AddObserver(kPrefDnsCacheGrace, this, false);
541 prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
542 prefs->AddObserver(kPrefDnsLocalDomains, this, false);
543 prefs->AddObserver(kPrefDisableIPv6, this, false);
544 prefs->AddObserver(kPrefDisablePrefetch, this, false);
545 prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
547 // Monitor these to see if there is a change in proxy configuration
548 // If a manual proxy is in use, disable prefetch implicitly
549 prefs->AddObserver("network.proxy.type", this, false);
552 nsCOMPtr<nsIObserverService> observerService =
553 mozilla::services::GetObserverService();
554 if (observerService) {
555 observerService->AddObserver(this, "last-pb-context-exited", false);
556 observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
561 nsDNSPrefetch::Initialize(this);
563 // Don't initialize the resolver if we're in offline mode.
564 // Later on, the IO service will reinitialize us when going online.
565 if (gIOService->IsOffline() && !gIOService->IsComingOnline())
566 return NS_OK;
568 nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
570 nsCOMPtr<nsIObserverService> obs =
571 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
573 nsRefPtr<nsHostResolver> res;
574 nsresult rv = nsHostResolver::Create(maxCacheEntries,
575 defaultCacheLifetime,
576 defaultGracePeriod,
577 getter_AddRefs(res));
578 if (NS_SUCCEEDED(rv)) {
579 // now, set all of our member variables while holding the lock
580 MutexAutoLock lock(mLock);
581 mResolver = res;
582 mIDN = idn;
583 mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
584 mDisableIPv6 = disableIPv6;
586 // Disable prefetching either by explicit preference or if a manual proxy is configured
587 mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
589 mLocalDomains.Clear();
590 if (localDomains) {
591 nsCCharSeparatedTokenizer tokenizer(localDomains, ',',
592 nsCCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
594 while (tokenizer.hasMoreTokens()) {
595 mLocalDomains.PutEntry(tokenizer.nextToken());
598 mNotifyResolution = notifyResolution;
599 if (mNotifyResolution) {
600 mObserverService =
601 new nsMainThreadPtrHolder<nsIObserverService>(obs);
605 RegisterWeakMemoryReporter(this);
607 return rv;
610 NS_IMETHODIMP
611 nsDNSService::Shutdown()
613 UnregisterWeakMemoryReporter(this);
615 nsRefPtr<nsHostResolver> res;
617 MutexAutoLock lock(mLock);
618 res = mResolver;
619 mResolver = nullptr;
621 if (res)
622 res->Shutdown();
623 return NS_OK;
626 NS_IMETHODIMP
627 nsDNSService::GetOffline(bool *offline)
629 *offline = mOffline;
630 return NS_OK;
633 NS_IMETHODIMP
634 nsDNSService::SetOffline(bool offline)
636 mOffline = offline;
637 return NS_OK;
640 NS_IMETHODIMP
641 nsDNSService::GetPrefetchEnabled(bool *outVal)
643 *outVal = !mDisablePrefetch;
644 return NS_OK;
647 NS_IMETHODIMP
648 nsDNSService::SetPrefetchEnabled(bool inVal)
650 mDisablePrefetch = !inVal;
651 return NS_OK;
654 static inline bool PreprocessHostname(bool aLocalDomain,
655 const nsACString &aInput,
656 nsIIDNService *aIDN,
657 nsACString &aACE)
659 if (aLocalDomain) {
660 aACE.AssignLiteral("localhost");
661 return true;
664 if (!aIDN || IsASCII(aInput)) {
665 aACE = aInput;
666 return true;
669 return IsUTF8(aInput) && NS_SUCCEEDED(aIDN->ConvertUTF8toACE(aInput, aACE));
672 NS_IMETHODIMP
673 nsDNSService::AsyncResolve(const nsACString &aHostname,
674 uint32_t flags,
675 nsIDNSListener *listener,
676 nsIEventTarget *target_,
677 nsICancelable **result)
679 return AsyncResolveExtended(aHostname, flags, EmptyCString(), listener, target_,
680 result);
683 NS_IMETHODIMP
684 nsDNSService::AsyncResolveExtended(const nsACString &aHostname,
685 uint32_t flags,
686 const nsACString &aNetworkInterface,
687 nsIDNSListener *listener,
688 nsIEventTarget *target_,
689 nsICancelable **result)
691 // grab reference to global host resolver and IDN service. beware
692 // simultaneous shutdown!!
693 nsRefPtr<nsHostResolver> res;
694 nsCOMPtr<nsIIDNService> idn;
695 nsCOMPtr<nsIEventTarget> target = target_;
696 bool localDomain = false;
698 MutexAutoLock lock(mLock);
700 if (mDisablePrefetch && (flags & RESOLVE_SPECULATE))
701 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
703 res = mResolver;
704 idn = mIDN;
705 localDomain = mLocalDomains.GetEntry(aHostname);
708 if (mNotifyResolution) {
709 NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService,
710 aHostname));
713 if (!res)
714 return NS_ERROR_OFFLINE;
716 if (mOffline)
717 flags |= RESOLVE_OFFLINE;
719 nsCString hostname;
720 if (!PreprocessHostname(localDomain, aHostname, idn, hostname))
721 return NS_ERROR_FAILURE;
723 // make sure JS callers get notification on the main thread
724 nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
725 if (wrappedListener && !target) {
726 nsCOMPtr<nsIThread> mainThread;
727 NS_GetMainThread(getter_AddRefs(mainThread));
728 target = do_QueryInterface(mainThread);
731 if (target) {
732 listener = new DNSListenerProxy(listener, target);
735 uint16_t af = GetAFForLookup(hostname, flags);
737 nsDNSAsyncRequest *req =
738 new nsDNSAsyncRequest(res, hostname, listener, flags, af,
739 aNetworkInterface);
740 if (!req)
741 return NS_ERROR_OUT_OF_MEMORY;
742 NS_ADDREF(*result = req);
744 MOZ_EVENT_TRACER_NAME_OBJECT(req, aHostname.BeginReading());
745 MOZ_EVENT_TRACER_WAIT(req, "net::dns::lookup");
747 // addref for resolver; will be released when OnLookupComplete is called.
748 NS_ADDREF(req);
749 nsresult rv = res->ResolveHost(req->mHost.get(), flags, af,
750 req->mNetworkInterface.get(),
751 req);
752 if (NS_FAILED(rv)) {
753 NS_RELEASE(req);
754 NS_RELEASE(*result);
756 return rv;
759 NS_IMETHODIMP
760 nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
761 uint32_t aFlags,
762 nsIDNSListener *aListener,
763 nsresult aReason)
765 return CancelAsyncResolveExtended(aHostname, aFlags, EmptyCString(), aListener,
766 aReason);
769 NS_IMETHODIMP
770 nsDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
771 uint32_t aFlags,
772 const nsACString &aNetworkInterface,
773 nsIDNSListener *aListener,
774 nsresult aReason)
776 // grab reference to global host resolver and IDN service. beware
777 // simultaneous shutdown!!
778 nsRefPtr<nsHostResolver> res;
779 nsCOMPtr<nsIIDNService> idn;
780 bool localDomain = false;
782 MutexAutoLock lock(mLock);
784 if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE))
785 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
787 res = mResolver;
788 idn = mIDN;
789 localDomain = mLocalDomains.GetEntry(aHostname);
791 if (!res)
792 return NS_ERROR_OFFLINE;
794 nsCString hostname;
795 if (!PreprocessHostname(localDomain, aHostname, idn, hostname))
796 return NS_ERROR_FAILURE;
798 uint16_t af = GetAFForLookup(hostname, aFlags);
800 res->CancelAsyncRequest(hostname.get(), aFlags, af,
801 nsPromiseFlatCString(aNetworkInterface).get(), aListener,
802 aReason);
803 return NS_OK;
806 NS_IMETHODIMP
807 nsDNSService::Resolve(const nsACString &aHostname,
808 uint32_t flags,
809 nsIDNSRecord **result)
811 // grab reference to global host resolver and IDN service. beware
812 // simultaneous shutdown!!
813 nsRefPtr<nsHostResolver> res;
814 nsCOMPtr<nsIIDNService> idn;
815 bool localDomain = false;
817 MutexAutoLock lock(mLock);
818 res = mResolver;
819 idn = mIDN;
820 localDomain = mLocalDomains.GetEntry(aHostname);
823 if (mNotifyResolution) {
824 NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService,
825 aHostname));
828 NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
830 if (mOffline)
831 flags |= RESOLVE_OFFLINE;
833 nsCString hostname;
834 if (!PreprocessHostname(localDomain, aHostname, idn, hostname))
835 return NS_ERROR_FAILURE;
838 // sync resolve: since the host resolver only works asynchronously, we need
839 // to use a mutex and a condvar to wait for the result. however, since the
840 // result may be in the resolvers cache, we might get called back recursively
841 // on the same thread. so, our mutex needs to be re-entrant. in other words,
842 // we need to use a monitor! ;-)
845 PRMonitor *mon = PR_NewMonitor();
846 if (!mon)
847 return NS_ERROR_OUT_OF_MEMORY;
849 PR_EnterMonitor(mon);
850 nsDNSSyncRequest syncReq(mon);
852 uint16_t af = GetAFForLookup(hostname, flags);
854 nsresult rv = res->ResolveHost(hostname.get(), flags, af, "", &syncReq);
855 if (NS_SUCCEEDED(rv)) {
856 // wait for result
857 while (!syncReq.mDone)
858 PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
860 if (NS_FAILED(syncReq.mStatus))
861 rv = syncReq.mStatus;
862 else {
863 NS_ASSERTION(syncReq.mHostRecord, "no host record");
864 nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord);
865 if (!rec)
866 rv = NS_ERROR_OUT_OF_MEMORY;
867 else
868 NS_ADDREF(*result = rec);
872 PR_ExitMonitor(mon);
873 PR_DestroyMonitor(mon);
874 return rv;
877 NS_IMETHODIMP
878 nsDNSService::GetMyHostName(nsACString &result)
880 char name[100];
881 if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
882 result = name;
883 return NS_OK;
885 return NS_ERROR_FAILURE;
888 NS_IMETHODIMP
889 nsDNSService::Observe(nsISupports *subject, const char *topic, const char16_t *data)
891 // We are only getting called if a preference has changed or there's a
892 // network link event.
893 NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 ||
894 strcmp(topic, "last-pb-context-exited") == 0 ||
895 strcmp(topic, NS_NETWORK_LINK_TOPIC) == 0,
896 "unexpected observe call");
898 if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
899 nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
900 if (mResolver && !strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) {
901 mResolver->FlushCache();
903 return NS_OK;
907 // Shutdown and this function are both only called on the UI thread, so we don't
908 // have to worry about mResolver being cleared out from under us.
910 // NOTE Shutting down and reinitializing the service like this is obviously
911 // suboptimal if Observe gets called several times in a row, but we don't
912 // expect that to be the case.
915 if (mResolver) {
916 Shutdown();
918 Init();
919 return NS_OK;
922 uint16_t
923 nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags)
925 if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6))
926 return PR_AF_INET;
928 MutexAutoLock lock(mLock);
930 uint16_t af = PR_AF_UNSPEC;
932 if (!mIPv4OnlyDomains.IsEmpty()) {
933 const char *domain, *domainEnd, *end;
934 uint32_t hostLen, domainLen;
936 // see if host is in one of the IPv4-only domains
937 domain = mIPv4OnlyDomains.BeginReading();
938 domainEnd = mIPv4OnlyDomains.EndReading();
940 nsACString::const_iterator hostStart;
941 host.BeginReading(hostStart);
942 hostLen = host.Length();
944 do {
945 // skip any whitespace
946 while (*domain == ' ' || *domain == '\t')
947 ++domain;
949 // find end of this domain in the string
950 end = strchr(domain, ',');
951 if (!end)
952 end = domainEnd;
954 // to see if the hostname is in the domain, check if the domain
955 // matches the end of the hostname.
956 domainLen = end - domain;
957 if (domainLen && hostLen >= domainLen) {
958 const char *hostTail = hostStart.get() + hostLen - domainLen;
959 if (PL_strncasecmp(domain, hostTail, domainLen) == 0) {
960 // now, make sure either that the hostname is a direct match or
961 // that the hostname begins with a dot.
962 if (hostLen == domainLen ||
963 *hostTail == '.' || *(hostTail - 1) == '.') {
964 af = PR_AF_INET;
965 break;
970 domain = end + 1;
971 } while (*end);
974 if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4))
975 af = PR_AF_INET6;
977 return af;
980 NS_IMETHODIMP
981 nsDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
983 NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED);
984 mResolver->GetDNSCacheEntries(args);
985 return NS_OK;
988 size_t
989 nsDNSService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
991 // Measurement of the following members may be added later if DMD finds it
992 // is worthwhile:
993 // - mIDN
994 // - mLock
996 size_t n = mallocSizeOf(this);
997 n += mResolver->SizeOfIncludingThis(mallocSizeOf);
998 n += mIPv4OnlyDomains.SizeOfExcludingThisMustBeUnshared(mallocSizeOf);
999 n += mLocalDomains.SizeOfExcludingThis(mallocSizeOf);
1000 return n;
1003 MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf)
1005 NS_IMETHODIMP
1006 nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport,
1007 nsISupports* aData, bool aAnonymize)
1009 return MOZ_COLLECT_REPORT(
1010 "explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
1011 SizeOfIncludingThis(DNSServiceMallocSizeOf),
1012 "Memory used for the DNS service.");