Wed, 31 Dec 2014 06:55:46 +0100
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, ¬ifyResolution);
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)
1002 {
1003 return MOZ_COLLECT_REPORT(
1004 "explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
1005 SizeOfIncludingThis(DNSServiceMallocSizeOf),
1006 "Memory used for the DNS service.");
1007 }