Bumping manifests a=b2g-bump
[gecko.git] / netwerk / dns / nsDNSService2.cpp
blob23366ea1f2c4870795d31331b1303c3331132812
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
56 public:
57 NS_DECL_THREADSAFE_ISUPPORTS
58 NS_DECL_NSIDNSRECORD
60 explicit 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;
77 NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord)
79 NS_IMETHODIMP
80 nsDNSRecord::GetCanonicalName(nsACString &result)
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;
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);
99 return NS_OK;
102 NS_IMETHODIMP
103 nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
105 if (mDone) {
106 return NS_ERROR_NOT_AVAILABLE;
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;
117 bool startedFresh = !mIter;
119 do {
120 if (!mIter) {
121 mIter = mHostRecord->addr_info->mAddresses.getFirst();
122 } else {
123 mIter = mIter->getNext();
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();
136 if (mIter) {
137 memcpy(addr, &mIter->mAddress, sizeof(NetAddr));
140 mHostRecord->addr_info_lock.Unlock();
142 if (!mIter) {
143 mDone = true;
144 return NS_ERROR_NOT_AVAILABLE;
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;
156 memcpy(addr, mHostRecord->addr, sizeof(NetAddr));
157 mDone = true;
160 // set given port
161 port = htons(port);
162 if (addr->raw.family == AF_INET) {
163 addr->inet.port = port;
165 else if (addr->raw.family == AF_INET6) {
166 addr->inet6.port = port;
169 return NS_OK;
172 NS_IMETHODIMP
173 nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr * *result)
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;
184 NS_IMETHODIMP
185 nsDNSRecord::GetNextAddrAsString(nsACString &result)
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;
196 NS_ERROR("NetAddrToString failed unexpectedly");
197 return NS_ERROR_FAILURE; // conversion failed for some reason
200 NS_IMETHODIMP
201 nsDNSRecord::HasMore(bool *result)
203 if (mDone) {
204 *result = false;
205 return NS_OK;
208 NetAddrElement *iterCopy = mIter;
210 NetAddr addr;
211 *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
213 mIter = iterCopy;
214 mDone = false;
216 return NS_OK;
219 NS_IMETHODIMP
220 nsDNSRecord::Rewind()
222 mIter = nullptr;
223 mIterGenCnt = -1;
224 mDone = false;
225 return NS_OK;
228 NS_IMETHODIMP
229 nsDNSRecord::ReportUnusable(uint16_t aPort)
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);
245 return NS_OK;
248 //-----------------------------------------------------------------------------
250 class nsDNSAsyncRequest MOZ_FINAL : public nsResolveHostCallback
251 , public nsICancelable
253 ~nsDNSAsyncRequest() {}
255 public:
256 NS_DECL_THREADSAFE_ISUPPORTS
257 NS_DECL_NSICANCELABLE
259 nsDNSAsyncRequest(nsHostResolver *res,
260 const nsACString &host,
261 nsIDNSListener *listener,
262 uint16_t flags,
263 uint16_t af)
264 : mResolver(res)
265 , mHost(host)
266 , mListener(listener)
267 , mFlags(flags)
268 , mAF(af) {}
270 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
271 // Returns TRUE if the DNS listener arg is the same as the member listener
272 // Used in Cancellations to remove DNS requests associated with a
273 // particular hostname and nsIDNSListener
274 bool EqualsAsyncListener(nsIDNSListener *aListener);
276 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const;
278 nsRefPtr<nsHostResolver> mResolver;
279 nsCString mHost; // hostname we're resolving
280 nsCOMPtr<nsIDNSListener> mListener;
281 uint16_t mFlags;
282 uint16_t mAF;
285 void
286 nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
287 nsHostRecord *hostRecord,
288 nsresult status)
290 // need to have an owning ref when we issue the callback to enable
291 // the caller to be able to addref/release multiple times without
292 // destroying the record prematurely.
293 nsCOMPtr<nsIDNSRecord> rec;
294 if (NS_SUCCEEDED(status)) {
295 NS_ASSERTION(hostRecord, "no host record");
296 rec = new nsDNSRecord(hostRecord);
297 if (!rec)
298 status = NS_ERROR_OUT_OF_MEMORY;
301 MOZ_EVENT_TRACER_DONE(this, "net::dns::lookup");
303 mListener->OnLookupComplete(this, rec, status);
304 mListener = nullptr;
306 // release the reference to ourselves that was added before we were
307 // handed off to the host resolver.
308 NS_RELEASE_THIS();
311 bool
312 nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
314 nsCOMPtr<nsIDNSListenerProxy> wrapper = do_QueryInterface(mListener);
315 if (wrapper) {
316 nsCOMPtr<nsIDNSListener> originalListener;
317 wrapper->GetOriginalListener(getter_AddRefs(originalListener));
318 return aListener == originalListener;
320 return (aListener == mListener);
323 size_t
324 nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
326 size_t n = mallocSizeOf(this);
328 // The following fields aren't measured.
329 // - mHost, because it's a non-owning pointer
330 // - mResolver, because it's a non-owning pointer
331 // - mListener, because it's a non-owning pointer
333 return n;
336 NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
338 NS_IMETHODIMP
339 nsDNSAsyncRequest::Cancel(nsresult reason)
341 NS_ENSURE_ARG(NS_FAILED(reason));
342 mResolver->DetachCallback(mHost.get(), mFlags, mAF, this, reason);
343 return NS_OK;
346 //-----------------------------------------------------------------------------
348 class nsDNSSyncRequest : public nsResolveHostCallback
350 public:
351 explicit nsDNSSyncRequest(PRMonitor *mon)
352 : mDone(false)
353 , mStatus(NS_OK)
354 , mMonitor(mon) {}
355 virtual ~nsDNSSyncRequest() {}
357 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
358 bool EqualsAsyncListener(nsIDNSListener *aListener);
359 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const;
361 bool mDone;
362 nsresult mStatus;
363 nsRefPtr<nsHostRecord> mHostRecord;
365 private:
366 PRMonitor *mMonitor;
369 void
370 nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver,
371 nsHostRecord *hostRecord,
372 nsresult status)
374 // store results, and wake up nsDNSService::Resolve to process results.
375 PR_EnterMonitor(mMonitor);
376 mDone = true;
377 mStatus = status;
378 mHostRecord = hostRecord;
379 PR_Notify(mMonitor);
380 PR_ExitMonitor(mMonitor);
383 bool
384 nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
386 // Sync request: no listener to compare
387 return false;
390 size_t
391 nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
393 size_t n = mallocSizeOf(this);
395 // The following fields aren't measured.
396 // - mHostRecord, because it's a non-owning pointer
398 // Measurement of the following members may be added later if DMD finds it
399 // is worthwhile:
400 // - mMonitor
402 return n;
405 class NotifyDNSResolution: public nsRunnable
407 public:
408 NotifyDNSResolution(nsMainThreadPtrHandle<nsIObserverService> &aObs,
409 const nsACString &aHostname)
410 : mObs(aObs)
411 , mHostname(aHostname)
413 MOZ_ASSERT(mObs);
416 NS_IMETHOD Run()
418 MOZ_ASSERT(NS_IsMainThread());
419 mObs->NotifyObservers(nullptr,
420 "dns-resolution-request",
421 NS_ConvertUTF8toUTF16(mHostname).get());
422 return NS_OK;
425 private:
426 nsMainThreadPtrHandle<nsIObserverService> mObs;
427 nsCString mHostname;
430 //-----------------------------------------------------------------------------
432 nsDNSService::nsDNSService()
433 : mLock("nsDNSServer.mLock")
434 , mFirstTime(true)
435 , mOffline(false)
439 nsDNSService::~nsDNSService()
443 NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver,
444 nsIMemoryReporter)
446 /******************************************************************************
447 * nsDNSService impl:
448 * singleton instance ctor/dtor methods
449 ******************************************************************************/
450 static nsDNSService *gDNSService;
452 nsIDNSService*
453 nsDNSService::GetXPCOMSingleton()
455 if (IsNeckoChild()) {
456 return ChildDNSService::GetSingleton();
459 return GetSingleton();
462 nsDNSService*
463 nsDNSService::GetSingleton()
465 NS_ASSERTION(!IsNeckoChild(), "not a parent process");
467 if (gDNSService) {
468 NS_ADDREF(gDNSService);
469 return gDNSService;
472 gDNSService = new nsDNSService();
473 if (gDNSService) {
474 NS_ADDREF(gDNSService);
475 if (NS_FAILED(gDNSService->Init())) {
476 NS_RELEASE(gDNSService);
480 return gDNSService;
483 NS_IMETHODIMP
484 nsDNSService::Init()
486 if (mResolver)
487 return NS_OK;
488 NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
490 // prefs
491 uint32_t maxCacheEntries = 400;
492 uint32_t maxCacheLifetime = 120; // seconds
493 uint32_t lifetimeGracePeriod = 60; // seconds
494 bool disableIPv6 = false;
495 bool disablePrefetch = false;
496 int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
497 bool notifyResolution = false;
499 nsAdoptingCString ipv4OnlyDomains;
500 nsAdoptingCString localDomains;
502 // read prefs
503 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
504 if (prefs) {
505 int32_t val;
506 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
507 maxCacheEntries = (uint32_t) val;
508 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
509 maxCacheLifetime = val;
510 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val)))
511 lifetimeGracePeriod = val;
513 // ASSUMPTION: pref branch does not modify out params on failure
514 prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
515 prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
516 prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains));
517 prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
519 // If a manual proxy is in use, disable prefetch implicitly
520 prefs->GetIntPref("network.proxy.type", &proxyType);
521 prefs->GetBoolPref(kPrefDnsNotifyResolution, &notifyResolution);
524 if (mFirstTime) {
525 mFirstTime = false;
527 // register as prefs observer
528 if (prefs) {
529 prefs->AddObserver(kPrefDnsCacheEntries, this, false);
530 prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
531 prefs->AddObserver(kPrefDnsCacheGrace, this, false);
532 prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
533 prefs->AddObserver(kPrefDnsLocalDomains, this, false);
534 prefs->AddObserver(kPrefDisableIPv6, this, false);
535 prefs->AddObserver(kPrefDisablePrefetch, this, false);
536 prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
538 // Monitor these to see if there is a change in proxy configuration
539 // If a manual proxy is in use, disable prefetch implicitly
540 prefs->AddObserver("network.proxy.type", this, false);
543 nsresult rv;
544 nsCOMPtr<nsIObserverService> observerService =
545 do_GetService("@mozilla.org/observer-service;1", &rv);
546 if (NS_SUCCEEDED(rv)) {
547 observerService->AddObserver(this, "last-pb-context-exited", false);
551 nsDNSPrefetch::Initialize(this);
553 // Don't initialize the resolver if we're in offline mode.
554 // Later on, the IO service will reinitialize us when going online.
555 if (gIOService->IsOffline() && !gIOService->IsComingOnline())
556 return NS_OK;
558 nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
560 nsCOMPtr<nsIObserverService> obs =
561 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
563 nsRefPtr<nsHostResolver> res;
564 nsresult rv = nsHostResolver::Create(maxCacheEntries,
565 maxCacheLifetime,
566 lifetimeGracePeriod,
567 getter_AddRefs(res));
568 if (NS_SUCCEEDED(rv)) {
569 // now, set all of our member variables while holding the lock
570 MutexAutoLock lock(mLock);
571 mResolver = res;
572 mIDN = idn;
573 mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
574 mDisableIPv6 = disableIPv6;
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()));
591 mNotifyResolution = notifyResolution;
592 if (mNotifyResolution) {
593 mObserverService =
594 new nsMainThreadPtrHolder<nsIObserverService>(obs);
598 RegisterWeakMemoryReporter(this);
600 return rv;
603 NS_IMETHODIMP
604 nsDNSService::Shutdown()
606 UnregisterWeakMemoryReporter(this);
608 nsRefPtr<nsHostResolver> res;
610 MutexAutoLock lock(mLock);
611 res = mResolver;
612 mResolver = nullptr;
614 if (res)
615 res->Shutdown();
616 return NS_OK;
619 NS_IMETHODIMP
620 nsDNSService::GetOffline(bool *offline)
622 *offline = mOffline;
623 return NS_OK;
626 NS_IMETHODIMP
627 nsDNSService::SetOffline(bool offline)
629 mOffline = offline;
630 return NS_OK;
633 NS_IMETHODIMP
634 nsDNSService::GetPrefetchEnabled(bool *outVal)
636 *outVal = !mDisablePrefetch;
637 return NS_OK;
640 NS_IMETHODIMP
641 nsDNSService::SetPrefetchEnabled(bool inVal)
643 mDisablePrefetch = !inVal;
644 return NS_OK;
648 NS_IMETHODIMP
649 nsDNSService::AsyncResolve(const nsACString &hostname,
650 uint32_t flags,
651 nsIDNSListener *listener,
652 nsIEventTarget *target_,
653 nsICancelable **result)
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;
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);
672 if (mNotifyResolution) {
673 NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService,
674 hostname));
677 if (!res)
678 return NS_ERROR_OFFLINE;
680 if (mOffline)
681 flags |= RESOLVE_OFFLINE;
683 const nsACString *hostPtr = &hostname;
685 nsAutoCString strLocalhost(NS_LITERAL_CSTRING("localhost"));
686 if (localDomain) {
687 hostPtr = &strLocalhost;
690 nsresult rv;
691 nsAutoCString hostACE;
692 if (idn && !IsASCII(*hostPtr)) {
693 if (IsUTF8(*hostPtr) &&
694 NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE))) {
695 hostPtr = &hostACE;
696 } else {
697 return NS_ERROR_FAILURE;
701 // make sure JS callers get notification on the main thread
702 nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
703 if (wrappedListener && !target) {
704 nsCOMPtr<nsIThread> mainThread;
705 NS_GetMainThread(getter_AddRefs(mainThread));
706 target = do_QueryInterface(mainThread);
709 if (target) {
710 listener = new DNSListenerProxy(listener, target);
713 uint16_t af = GetAFForLookup(*hostPtr, flags);
715 nsDNSAsyncRequest *req =
716 new nsDNSAsyncRequest(res, *hostPtr, listener, flags, af);
717 if (!req)
718 return NS_ERROR_OUT_OF_MEMORY;
719 NS_ADDREF(*result = req);
721 MOZ_EVENT_TRACER_NAME_OBJECT(req, hostname.BeginReading());
722 MOZ_EVENT_TRACER_WAIT(req, "net::dns::lookup");
724 // addref for resolver; will be released when OnLookupComplete is called.
725 NS_ADDREF(req);
726 rv = res->ResolveHost(req->mHost.get(), flags, af, req);
727 if (NS_FAILED(rv)) {
728 NS_RELEASE(req);
729 NS_RELEASE(*result);
731 return rv;
734 NS_IMETHODIMP
735 nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
736 uint32_t aFlags,
737 nsIDNSListener *aListener,
738 nsresult aReason)
740 // grab reference to global host resolver and IDN service. beware
741 // simultaneous shutdown!!
742 nsRefPtr<nsHostResolver> res;
743 nsCOMPtr<nsIIDNService> idn;
745 MutexAutoLock lock(mLock);
747 if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE))
748 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
750 res = mResolver;
751 idn = mIDN;
753 if (!res)
754 return NS_ERROR_OFFLINE;
756 nsCString hostname(aHostname);
758 nsAutoCString hostACE;
759 if (idn && !IsASCII(aHostname)) {
760 if (IsUTF8(aHostname) &&
761 NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE))) {
762 hostname = hostACE;
763 } else {
764 return NS_ERROR_FAILURE;
768 uint16_t af = GetAFForLookup(hostname, aFlags);
770 res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason);
771 return NS_OK;
774 NS_IMETHODIMP
775 nsDNSService::Resolve(const nsACString &hostname,
776 uint32_t flags,
777 nsIDNSRecord **result)
779 // grab reference to global host resolver and IDN service. beware
780 // simultaneous shutdown!!
781 nsRefPtr<nsHostResolver> res;
782 nsCOMPtr<nsIIDNService> idn;
783 bool localDomain = false;
785 MutexAutoLock lock(mLock);
786 res = mResolver;
787 idn = mIDN;
788 localDomain = mLocalDomains.GetEntry(hostname);
791 if (mNotifyResolution) {
792 NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService,
793 hostname));
796 NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
798 if (mOffline)
799 flags |= RESOLVE_OFFLINE;
801 const nsACString *hostPtr = &hostname;
803 nsAutoCString strLocalhost(NS_LITERAL_CSTRING("localhost"));
804 if (localDomain) {
805 hostPtr = &strLocalhost;
808 nsresult rv;
809 nsAutoCString hostACE;
810 if (idn && !IsASCII(*hostPtr)) {
811 if (IsUTF8(*hostPtr) &&
812 NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE))) {
813 hostPtr = &hostACE;
814 } else {
815 return NS_ERROR_FAILURE;
820 // sync resolve: since the host resolver only works asynchronously, we need
821 // to use a mutex and a condvar to wait for the result. however, since the
822 // result may be in the resolvers cache, we might get called back recursively
823 // on the same thread. so, our mutex needs to be re-entrant. in other words,
824 // we need to use a monitor! ;-)
827 PRMonitor *mon = PR_NewMonitor();
828 if (!mon)
829 return NS_ERROR_OUT_OF_MEMORY;
831 PR_EnterMonitor(mon);
832 nsDNSSyncRequest syncReq(mon);
834 uint16_t af = GetAFForLookup(*hostPtr, flags);
836 rv = res->ResolveHost(PromiseFlatCString(*hostPtr).get(), flags, af, &syncReq);
837 if (NS_SUCCEEDED(rv)) {
838 // wait for result
839 while (!syncReq.mDone)
840 PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
842 if (NS_FAILED(syncReq.mStatus))
843 rv = syncReq.mStatus;
844 else {
845 NS_ASSERTION(syncReq.mHostRecord, "no host record");
846 nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord);
847 if (!rec)
848 rv = NS_ERROR_OUT_OF_MEMORY;
849 else
850 NS_ADDREF(*result = rec);
854 PR_ExitMonitor(mon);
855 PR_DestroyMonitor(mon);
856 return rv;
859 NS_IMETHODIMP
860 nsDNSService::GetMyHostName(nsACString &result)
862 char name[100];
863 if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
864 result = name;
865 return NS_OK;
867 return NS_ERROR_FAILURE;
870 NS_IMETHODIMP
871 nsDNSService::Observe(nsISupports *subject, const char *topic, const char16_t *data)
873 // we are only getting called if a preference has changed.
874 NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 ||
875 strcmp(topic, "last-pb-context-exited") == 0,
876 "unexpected observe call");
879 // Shutdown and this function are both only called on the UI thread, so we don't
880 // have to worry about mResolver being cleared out from under us.
882 // NOTE Shutting down and reinitializing the service like this is obviously
883 // suboptimal if Observe gets called several times in a row, but we don't
884 // expect that to be the case.
887 if (mResolver) {
888 Shutdown();
890 Init();
891 return NS_OK;
894 uint16_t
895 nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags)
897 if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6))
898 return PR_AF_INET;
900 MutexAutoLock lock(mLock);
902 uint16_t af = PR_AF_UNSPEC;
904 if (!mIPv4OnlyDomains.IsEmpty()) {
905 const char *domain, *domainEnd, *end;
906 uint32_t hostLen, domainLen;
908 // see if host is in one of the IPv4-only domains
909 domain = mIPv4OnlyDomains.BeginReading();
910 domainEnd = mIPv4OnlyDomains.EndReading();
912 nsACString::const_iterator hostStart;
913 host.BeginReading(hostStart);
914 hostLen = host.Length();
916 do {
917 // skip any whitespace
918 while (*domain == ' ' || *domain == '\t')
919 ++domain;
921 // find end of this domain in the string
922 end = strchr(domain, ',');
923 if (!end)
924 end = domainEnd;
926 // to see if the hostname is in the domain, check if the domain
927 // matches the end of the hostname.
928 domainLen = end - domain;
929 if (domainLen && hostLen >= domainLen) {
930 const char *hostTail = hostStart.get() + hostLen - domainLen;
931 if (PL_strncasecmp(domain, hostTail, domainLen) == 0) {
932 // now, make sure either that the hostname is a direct match or
933 // that the hostname begins with a dot.
934 if (hostLen == domainLen ||
935 *hostTail == '.' || *(hostTail - 1) == '.') {
936 af = PR_AF_INET;
937 break;
942 domain = end + 1;
943 } while (*end);
946 if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4))
947 af = PR_AF_INET6;
949 return af;
952 NS_IMETHODIMP
953 nsDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
955 NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED);
956 mResolver->GetDNSCacheEntries(args);
957 return NS_OK;
960 size_t
961 nsDNSService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
963 // Measurement of the following members may be added later if DMD finds it
964 // is worthwhile:
965 // - mIDN
966 // - mLock
968 size_t n = mallocSizeOf(this);
969 n += mResolver->SizeOfIncludingThis(mallocSizeOf);
970 n += mIPv4OnlyDomains.SizeOfExcludingThisMustBeUnshared(mallocSizeOf);
971 n += mLocalDomains.SizeOfExcludingThis(mallocSizeOf);
972 return n;
975 MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf)
977 NS_IMETHODIMP
978 nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport,
979 nsISupports* aData, bool aAnonymize)
981 return MOZ_COLLECT_REPORT(
982 "explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
983 SizeOfIncludingThis(DNSServiceMallocSizeOf),
984 "Memory used for the DNS service.");