Bug 788829 - Call SetSizeConstraints even if a popup is not open. r=enndeakin
[gecko.git] / netwerk / dns / nsDNSService2.cpp
blobe87094db13d69c52c577aac346e6f0160b45cc93
1 /* vim:set ts=4 sw=4 sts=4 et cin: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsDNSService2.h"
7 #include "nsIDNSRecord.h"
8 #include "nsIDNSListener.h"
9 #include "nsICancelable.h"
10 #include "nsIPrefService.h"
11 #include "nsIPrefBranch.h"
12 #include "nsIServiceManager.h"
13 #include "nsProxyRelease.h"
14 #include "nsReadableUtils.h"
15 #include "nsString.h"
16 #include "nsAutoPtr.h"
17 #include "nsNetCID.h"
18 #include "nsError.h"
19 #include "nsDNSPrefetch.h"
20 #include "nsThreadUtils.h"
21 #include "nsIProtocolProxyService.h"
22 #include "prsystem.h"
23 #include "prnetdb.h"
24 #include "prmon.h"
25 #include "prio.h"
26 #include "plstr.h"
27 #include "nsIOService.h"
28 #include "nsCharSeparatedTokenizer.h"
30 #include "mozilla/Attributes.h"
32 using namespace mozilla;
34 static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries";
35 static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
36 static const char kPrefDnsCacheGrace[] = "network.dnsCacheExpirationGracePeriod";
37 static const char kPrefEnableIDN[] = "network.enableIDN";
38 static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains";
39 static const char kPrefDisableIPv6[] = "network.dns.disableIPv6";
40 static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch";
41 static const char kPrefDnsLocalDomains[] = "network.dns.localDomains";
43 //-----------------------------------------------------------------------------
45 class nsDNSRecord : public nsIDNSRecord
47 public:
48 NS_DECL_ISUPPORTS
49 NS_DECL_NSIDNSRECORD
51 nsDNSRecord(nsHostRecord *hostRecord)
52 : mHostRecord(hostRecord)
53 , mIter(nullptr)
54 , mLastIter(nullptr)
55 , mIterGenCnt(-1)
56 , mDone(false) {}
58 private:
59 virtual ~nsDNSRecord() {}
61 nsRefPtr<nsHostRecord> mHostRecord;
62 void *mIter; // enum ptr for PR_EnumerateAddrInfo
63 void *mLastIter; // previous enum ptr, for use in
64 // getting addrinfo in ReportUnusable
65 int mIterGenCnt; // the generation count of
66 // mHostRecord->addr_info when we
67 // start iterating
68 bool mDone;
71 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSRecord, nsIDNSRecord)
73 NS_IMETHODIMP
74 nsDNSRecord::GetCanonicalName(nsACString &result)
76 // this method should only be called if we have a CNAME
77 NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME,
78 NS_ERROR_NOT_AVAILABLE);
80 // if the record is for an IP address literal, then the canonical
81 // host name is the IP address literal.
82 const char *cname;
84 MutexAutoLock lock(mHostRecord->addr_info_lock);
85 if (mHostRecord->addr_info)
86 cname = PR_GetCanonNameFromAddrInfo(mHostRecord->addr_info);
87 else
88 cname = mHostRecord->host;
89 result.Assign(cname);
91 return NS_OK;
94 NS_IMETHODIMP
95 nsDNSRecord::GetNextAddr(uint16_t port, PRNetAddr *addr)
97 // not a programming error to poke the DNS record when it has no more
98 // entries. just fail without any debug warnings. this enables consumers
99 // to enumerate the DNS record without calling HasMore.
100 if (mDone)
101 return NS_ERROR_NOT_AVAILABLE;
103 mHostRecord->addr_info_lock.Lock();
104 bool startedFresh = !mIter;
106 if (mHostRecord->addr_info) {
107 if (!mIter)
108 mIterGenCnt = mHostRecord->addr_info_gencnt;
109 else if (mIterGenCnt != mHostRecord->addr_info_gencnt) {
110 // mHostRecord->addr_info has changed, so mIter is invalid.
111 // Restart the iteration. Alternatively, we could just fail.
112 mIter = nullptr;
113 mIterGenCnt = mHostRecord->addr_info_gencnt;
114 startedFresh = true;
117 do {
118 mLastIter = mIter;
119 mIter = PR_EnumerateAddrInfo(mIter, mHostRecord->addr_info,
120 port, addr);
122 while (mIter && mHostRecord->Blacklisted(addr));
124 if (startedFresh && !mIter) {
125 // if everything was blacklisted we want to reset the blacklist (and
126 // likely relearn it) and return the first address. That is better
127 // than nothing
128 mHostRecord->ResetBlacklist();
129 mLastIter = nullptr;
130 mIter = PR_EnumerateAddrInfo(nullptr, mHostRecord->addr_info,
131 port, addr);
134 mHostRecord->addr_info_lock.Unlock();
135 if (!mIter) {
136 mDone = true;
137 return NS_ERROR_NOT_AVAILABLE;
140 else {
141 mHostRecord->addr_info_lock.Unlock();
142 if (!mHostRecord->addr) {
143 // Both mHostRecord->addr_info and mHostRecord->addr are null.
144 // This can happen if mHostRecord->addr_info expired and the
145 // attempt to reresolve it failed.
146 return NS_ERROR_NOT_AVAILABLE;
148 memcpy(addr, mHostRecord->addr, sizeof(PRNetAddr));
149 // set given port
150 port = PR_htons(port);
151 if (addr->raw.family == PR_AF_INET)
152 addr->inet.port = port;
153 else
154 addr->ipv6.port = port;
155 mDone = true; // no iterations
158 return NS_OK;
161 NS_IMETHODIMP
162 nsDNSRecord::GetNextAddrAsString(nsACString &result)
164 PRNetAddr addr;
165 nsresult rv = GetNextAddr(0, &addr);
166 if (NS_FAILED(rv)) return rv;
168 char buf[64];
169 if (PR_NetAddrToString(&addr, buf, sizeof(buf)) == PR_SUCCESS) {
170 result.Assign(buf);
171 return NS_OK;
173 NS_ERROR("PR_NetAddrToString failed unexpectedly");
174 return NS_ERROR_FAILURE; // conversion failed for some reason
177 NS_IMETHODIMP
178 nsDNSRecord::HasMore(bool *result)
180 if (mDone)
181 *result = false;
182 else {
183 // unfortunately, NSPR does not provide a way for us to determine if
184 // there is another address other than to simply get the next address.
185 void *iterCopy = mIter;
186 void *iterLastCopy = mLastIter;
187 PRNetAddr addr;
188 *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
189 mIter = iterCopy; // backup iterator
190 mLastIter = iterLastCopy; // backup iterator
191 mDone = false;
193 return NS_OK;
196 NS_IMETHODIMP
197 nsDNSRecord::Rewind()
199 mIter = nullptr;
200 mLastIter = nullptr;
201 mIterGenCnt = -1;
202 mDone = false;
203 return NS_OK;
206 NS_IMETHODIMP
207 nsDNSRecord::ReportUnusable(uint16_t aPort)
209 // right now we don't use the port in the blacklist
211 mHostRecord->addr_info_lock.Lock();
213 // Check that we are using a real addr_info (as opposed to a single
214 // constant address), and that the generation count is valid. Otherwise,
215 // ignore the report.
217 if (mHostRecord->addr_info &&
218 mIterGenCnt == mHostRecord->addr_info_gencnt) {
219 PRNetAddr addr;
220 void *id = PR_EnumerateAddrInfo(mLastIter, mHostRecord->addr_info,
221 aPort, &addr);
222 if (id)
223 mHostRecord->ReportUnusable(&addr);
226 mHostRecord->addr_info_lock.Unlock();
227 return NS_OK;
230 //-----------------------------------------------------------------------------
232 class nsDNSAsyncRequest MOZ_FINAL : public nsResolveHostCallback
233 , public nsICancelable
235 public:
236 NS_DECL_ISUPPORTS
237 NS_DECL_NSICANCELABLE
239 nsDNSAsyncRequest(nsHostResolver *res,
240 const nsACString &host,
241 nsIDNSListener *listener,
242 uint16_t flags,
243 uint16_t af)
244 : mResolver(res)
245 , mHost(host)
246 , mListener(listener)
247 , mFlags(flags)
248 , mAF(af) {}
249 ~nsDNSAsyncRequest() {}
251 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
252 // Returns TRUE if the DNS listener arg is the same as the member listener
253 // Used in Cancellations to remove DNS requests associated with a
254 // particular hostname and nsIDNSListener
255 bool EqualsAsyncListener(nsIDNSListener *aListener);
257 nsRefPtr<nsHostResolver> mResolver;
258 nsCString mHost; // hostname we're resolving
259 nsCOMPtr<nsIDNSListener> mListener;
260 uint16_t mFlags;
261 uint16_t mAF;
264 void
265 nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
266 nsHostRecord *hostRecord,
267 nsresult status)
269 // need to have an owning ref when we issue the callback to enable
270 // the caller to be able to addref/release multiple times without
271 // destroying the record prematurely.
272 nsCOMPtr<nsIDNSRecord> rec;
273 if (NS_SUCCEEDED(status)) {
274 NS_ASSERTION(hostRecord, "no host record");
275 rec = new nsDNSRecord(hostRecord);
276 if (!rec)
277 status = NS_ERROR_OUT_OF_MEMORY;
280 mListener->OnLookupComplete(this, rec, status);
281 mListener = nullptr;
283 // release the reference to ourselves that was added before we were
284 // handed off to the host resolver.
285 NS_RELEASE_THIS();
288 bool
289 nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
291 return (aListener == mListener);
294 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSAsyncRequest, nsICancelable)
296 NS_IMETHODIMP
297 nsDNSAsyncRequest::Cancel(nsresult reason)
299 NS_ENSURE_ARG(NS_FAILED(reason));
300 mResolver->DetachCallback(mHost.get(), mFlags, mAF, this, reason);
301 return NS_OK;
304 //-----------------------------------------------------------------------------
306 class nsDNSSyncRequest : public nsResolveHostCallback
308 public:
309 nsDNSSyncRequest(PRMonitor *mon)
310 : mDone(false)
311 , mStatus(NS_OK)
312 , mMonitor(mon) {}
313 virtual ~nsDNSSyncRequest() {}
315 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
316 bool EqualsAsyncListener(nsIDNSListener *aListener);
318 bool mDone;
319 nsresult mStatus;
320 nsRefPtr<nsHostRecord> mHostRecord;
322 private:
323 PRMonitor *mMonitor;
326 void
327 nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver,
328 nsHostRecord *hostRecord,
329 nsresult status)
331 // store results, and wake up nsDNSService::Resolve to process results.
332 PR_EnterMonitor(mMonitor);
333 mDone = true;
334 mStatus = status;
335 mHostRecord = hostRecord;
336 PR_Notify(mMonitor);
337 PR_ExitMonitor(mMonitor);
340 bool
341 nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
343 // Sync request: no listener to compare
344 return false;
347 //-----------------------------------------------------------------------------
349 nsDNSService::nsDNSService()
350 : mLock("nsDNSServer.mLock")
351 , mFirstTime(true)
352 , mOffline(false)
356 nsDNSService::~nsDNSService()
360 NS_IMPL_THREADSAFE_ISUPPORTS3(nsDNSService, nsIDNSService, nsPIDNSService,
361 nsIObserver)
363 NS_IMETHODIMP
364 nsDNSService::Init()
366 if (mResolver)
367 return NS_OK;
368 NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
370 // prefs
371 uint32_t maxCacheEntries = 400;
372 uint32_t maxCacheLifetime = 2; // minutes
373 uint32_t lifetimeGracePeriod = 1;
374 bool enableIDN = true;
375 bool disableIPv6 = false;
376 bool disablePrefetch = false;
377 int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
379 nsAdoptingCString ipv4OnlyDomains;
380 nsAdoptingCString localDomains;
382 // read prefs
383 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
384 if (prefs) {
385 int32_t val;
386 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
387 maxCacheEntries = (uint32_t) val;
388 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
389 maxCacheLifetime = val / 60; // convert from seconds to minutes
390 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val)))
391 lifetimeGracePeriod = val / 60; // convert from seconds to minutes
393 // ASSUMPTION: pref branch does not modify out params on failure
394 prefs->GetBoolPref(kPrefEnableIDN, &enableIDN);
395 prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
396 prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
397 prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains));
398 prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
400 // If a manual proxy is in use, disable prefetch implicitly
401 prefs->GetIntPref("network.proxy.type", &proxyType);
404 if (mFirstTime) {
405 mFirstTime = false;
407 mLocalDomains.Init();
409 // register as prefs observer
410 if (prefs) {
411 prefs->AddObserver(kPrefDnsCacheEntries, this, false);
412 prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
413 prefs->AddObserver(kPrefDnsCacheGrace, this, false);
414 prefs->AddObserver(kPrefEnableIDN, this, false);
415 prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
416 prefs->AddObserver(kPrefDnsLocalDomains, this, false);
417 prefs->AddObserver(kPrefDisableIPv6, this, false);
418 prefs->AddObserver(kPrefDisablePrefetch, this, false);
420 // Monitor these to see if there is a change in proxy configuration
421 // If a manual proxy is in use, disable prefetch implicitly
422 prefs->AddObserver("network.proxy.type", this, false);
426 // we have to null out mIDN since we might be getting re-initialized
427 // as a result of a pref change.
428 nsCOMPtr<nsIIDNService> idn;
429 if (enableIDN)
430 idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
432 nsDNSPrefetch::Initialize(this);
434 // Don't initialize the resolver if we're in offline mode.
435 // Later on, the IO service will reinitialize us when going online.
436 if (gIOService->IsOffline() && !gIOService->IsComingOnline())
437 return NS_OK;
439 nsRefPtr<nsHostResolver> res;
440 nsresult rv = nsHostResolver::Create(maxCacheEntries,
441 maxCacheLifetime,
442 lifetimeGracePeriod,
443 getter_AddRefs(res));
444 if (NS_SUCCEEDED(rv)) {
445 // now, set all of our member variables while holding the lock
446 MutexAutoLock lock(mLock);
447 mResolver = res;
448 mIDN = idn;
449 mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
450 mDisableIPv6 = disableIPv6;
452 // Disable prefetching either by explicit preference or if a manual proxy is configured
453 mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
455 mLocalDomains.Clear();
456 if (localDomains) {
457 nsAdoptingString domains;
458 domains.AssignASCII(nsDependentCString(localDomains).get());
459 nsCharSeparatedTokenizer tokenizer(domains, ',',
460 nsCharSeparatedTokenizerTemplate<>::SEPARATOR_OPTIONAL);
462 while (tokenizer.hasMoreTokens()) {
463 const nsSubstring& domain = tokenizer.nextToken();
464 mLocalDomains.PutEntry(nsDependentCString(NS_ConvertUTF16toUTF8(domain).get()));
468 return rv;
471 NS_IMETHODIMP
472 nsDNSService::Shutdown()
474 nsRefPtr<nsHostResolver> res;
476 MutexAutoLock lock(mLock);
477 res = mResolver;
478 mResolver = nullptr;
480 if (res)
481 res->Shutdown();
482 return NS_OK;
485 NS_IMETHODIMP
486 nsDNSService::GetOffline(bool *offline)
488 *offline = mOffline;
489 return NS_OK;
492 NS_IMETHODIMP
493 nsDNSService::SetOffline(bool offline)
495 mOffline = offline;
496 return NS_OK;
499 NS_IMETHODIMP
500 nsDNSService::GetPrefetchEnabled(bool *outVal)
502 *outVal = !mDisablePrefetch;
503 return NS_OK;
506 NS_IMETHODIMP
507 nsDNSService::SetPrefetchEnabled(bool inVal)
509 mDisablePrefetch = !inVal;
510 return NS_OK;
513 namespace {
515 class DNSListenerProxy MOZ_FINAL : public nsIDNSListener
517 public:
518 DNSListenerProxy(nsIDNSListener* aListener, nsIEventTarget* aTargetThread)
519 : mListener(aListener)
520 , mTargetThread(aTargetThread)
523 ~DNSListenerProxy()
525 nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
526 NS_ProxyRelease(mainThread, mListener);
529 NS_DECL_ISUPPORTS
530 NS_DECL_NSIDNSLISTENER
532 class OnLookupCompleteRunnable : public nsRunnable
534 public:
535 OnLookupCompleteRunnable(nsIDNSListener* aListener,
536 nsICancelable* aRequest,
537 nsIDNSRecord* aRecord,
538 nsresult aStatus)
539 : mListener(aListener)
540 , mRequest(aRequest)
541 , mRecord(aRecord)
542 , mStatus(aStatus)
545 ~OnLookupCompleteRunnable()
547 nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
548 NS_ProxyRelease(mainThread, mListener);
551 NS_DECL_NSIRUNNABLE
553 private:
554 nsCOMPtr<nsIDNSListener> mListener;
555 nsCOMPtr<nsICancelable> mRequest;
556 nsCOMPtr<nsIDNSRecord> mRecord;
557 nsresult mStatus;
560 private:
561 nsCOMPtr<nsIDNSListener> mListener;
562 nsCOMPtr<nsIEventTarget> mTargetThread;
565 NS_IMPL_THREADSAFE_ISUPPORTS1(DNSListenerProxy, nsIDNSListener)
567 NS_IMETHODIMP
568 DNSListenerProxy::OnLookupComplete(nsICancelable* aRequest,
569 nsIDNSRecord* aRecord,
570 nsresult aStatus)
572 nsRefPtr<OnLookupCompleteRunnable> r =
573 new OnLookupCompleteRunnable(mListener, aRequest, aRecord, aStatus);
574 return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
577 NS_IMETHODIMP
578 DNSListenerProxy::OnLookupCompleteRunnable::Run()
580 mListener->OnLookupComplete(mRequest, mRecord, mStatus);
581 return NS_OK;
584 } // anonymous namespace
586 NS_IMETHODIMP
587 nsDNSService::AsyncResolve(const nsACString &hostname,
588 uint32_t flags,
589 nsIDNSListener *listener,
590 nsIEventTarget *target,
591 nsICancelable **result)
593 // grab reference to global host resolver and IDN service. beware
594 // simultaneous shutdown!!
595 nsRefPtr<nsHostResolver> res;
596 nsCOMPtr<nsIIDNService> idn;
597 bool localDomain = false;
599 MutexAutoLock lock(mLock);
601 if (mDisablePrefetch && (flags & RESOLVE_SPECULATE))
602 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
604 res = mResolver;
605 idn = mIDN;
606 localDomain = mLocalDomains.GetEntry(hostname);
608 if (!res)
609 return NS_ERROR_OFFLINE;
611 if (mOffline)
612 flags |= RESOLVE_OFFLINE;
614 const nsACString *hostPtr = &hostname;
616 if (localDomain) {
617 hostPtr = &(NS_LITERAL_CSTRING("localhost"));
620 nsresult rv;
621 nsAutoCString hostACE;
622 if (idn && !IsASCII(*hostPtr)) {
623 if (NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE)))
624 hostPtr = &hostACE;
627 if (target) {
628 listener = new DNSListenerProxy(listener, target);
631 uint16_t af = GetAFForLookup(*hostPtr, flags);
633 nsDNSAsyncRequest *req =
634 new nsDNSAsyncRequest(res, *hostPtr, listener, flags, af);
635 if (!req)
636 return NS_ERROR_OUT_OF_MEMORY;
637 NS_ADDREF(*result = req);
639 // addref for resolver; will be released when OnLookupComplete is called.
640 NS_ADDREF(req);
641 rv = res->ResolveHost(req->mHost.get(), flags, af, req);
642 if (NS_FAILED(rv)) {
643 NS_RELEASE(req);
644 NS_RELEASE(*result);
646 return rv;
649 NS_IMETHODIMP
650 nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
651 uint32_t aFlags,
652 nsIDNSListener *aListener,
653 nsresult aReason)
655 // grab reference to global host resolver and IDN service. beware
656 // simultaneous shutdown!!
657 nsRefPtr<nsHostResolver> res;
658 nsCOMPtr<nsIIDNService> idn;
660 MutexAutoLock lock(mLock);
662 if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE))
663 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
665 res = mResolver;
666 idn = mIDN;
668 if (!res)
669 return NS_ERROR_OFFLINE;
671 nsCString hostname(aHostname);
673 nsAutoCString hostACE;
674 if (idn && !IsASCII(aHostname)) {
675 if (NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE)))
676 hostname = hostACE;
679 uint16_t af = GetAFForLookup(hostname, aFlags);
681 res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason);
682 return NS_OK;
685 NS_IMETHODIMP
686 nsDNSService::Resolve(const nsACString &hostname,
687 uint32_t flags,
688 nsIDNSRecord **result)
690 NS_WARNING("Do not use synchronous DNS resolution! This API may be removed soon.");
692 // We will not allow this to be called on the main thread. This is transitional
693 // and a bit of a test for removing the synchronous API entirely.
694 if (NS_IsMainThread()) {
695 NS_ERROR("Synchronous DNS resolve failing - not allowed on the main thread!");
696 return NS_ERROR_FAILURE;
699 // grab reference to global host resolver and IDN service. beware
700 // simultaneous shutdown!!
701 nsRefPtr<nsHostResolver> res;
702 nsCOMPtr<nsIIDNService> idn;
703 bool localDomain = false;
705 MutexAutoLock lock(mLock);
706 res = mResolver;
707 idn = mIDN;
708 localDomain = mLocalDomains.GetEntry(hostname);
710 NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
712 if (mOffline)
713 flags |= RESOLVE_OFFLINE;
715 const nsACString *hostPtr = &hostname;
717 if (localDomain) {
718 hostPtr = &(NS_LITERAL_CSTRING("localhost"));
721 nsresult rv;
722 nsAutoCString hostACE;
723 if (idn && !IsASCII(*hostPtr)) {
724 if (NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE)))
725 hostPtr = &hostACE;
729 // sync resolve: since the host resolver only works asynchronously, we need
730 // to use a mutex and a condvar to wait for the result. however, since the
731 // result may be in the resolvers cache, we might get called back recursively
732 // on the same thread. so, our mutex needs to be re-entrant. in other words,
733 // we need to use a monitor! ;-)
736 PRMonitor *mon = PR_NewMonitor();
737 if (!mon)
738 return NS_ERROR_OUT_OF_MEMORY;
740 PR_EnterMonitor(mon);
741 nsDNSSyncRequest syncReq(mon);
743 uint16_t af = GetAFForLookup(*hostPtr, flags);
745 rv = res->ResolveHost(PromiseFlatCString(*hostPtr).get(), flags, af, &syncReq);
746 if (NS_SUCCEEDED(rv)) {
747 // wait for result
748 while (!syncReq.mDone)
749 PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
751 if (NS_FAILED(syncReq.mStatus))
752 rv = syncReq.mStatus;
753 else {
754 NS_ASSERTION(syncReq.mHostRecord, "no host record");
755 nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord);
756 if (!rec)
757 rv = NS_ERROR_OUT_OF_MEMORY;
758 else
759 NS_ADDREF(*result = rec);
763 PR_ExitMonitor(mon);
764 PR_DestroyMonitor(mon);
765 return rv;
768 NS_IMETHODIMP
769 nsDNSService::GetMyHostName(nsACString &result)
771 char name[100];
772 if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
773 result = name;
774 return NS_OK;
776 return NS_ERROR_FAILURE;
779 NS_IMETHODIMP
780 nsDNSService::Observe(nsISupports *subject, const char *topic, const PRUnichar *data)
782 // we are only getting called if a preference has changed.
783 NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
784 "unexpected observe call");
787 // Shutdown and this function are both only called on the UI thread, so we don't
788 // have to worry about mResolver being cleared out from under us.
790 // NOTE Shutting down and reinitializing the service like this is obviously
791 // suboptimal if Observe gets called several times in a row, but we don't
792 // expect that to be the case.
795 if (mResolver) {
796 Shutdown();
798 Init();
799 return NS_OK;
802 uint16_t
803 nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags)
805 if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6))
806 return PR_AF_INET;
808 MutexAutoLock lock(mLock);
810 uint16_t af = PR_AF_UNSPEC;
812 if (!mIPv4OnlyDomains.IsEmpty()) {
813 const char *domain, *domainEnd, *end;
814 uint32_t hostLen, domainLen;
816 // see if host is in one of the IPv4-only domains
817 domain = mIPv4OnlyDomains.BeginReading();
818 domainEnd = mIPv4OnlyDomains.EndReading();
820 nsACString::const_iterator hostStart;
821 host.BeginReading(hostStart);
822 hostLen = host.Length();
824 do {
825 // skip any whitespace
826 while (*domain == ' ' || *domain == '\t')
827 ++domain;
829 // find end of this domain in the string
830 end = strchr(domain, ',');
831 if (!end)
832 end = domainEnd;
834 // to see if the hostname is in the domain, check if the domain
835 // matches the end of the hostname.
836 domainLen = end - domain;
837 if (domainLen && hostLen >= domainLen) {
838 const char *hostTail = hostStart.get() + hostLen - domainLen;
839 if (PL_strncasecmp(domain, hostTail, domainLen) == 0) {
840 // now, make sure either that the hostname is a direct match or
841 // that the hostname begins with a dot.
842 if (hostLen == domainLen ||
843 *hostTail == '.' || *(hostTail - 1) == '.') {
844 af = PR_AF_INET;
845 break;
850 domain = end + 1;
851 } while (*end);
854 return af;