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/. */
6
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"
35 #include "TRRService.h"
36
37 #include "mozilla/Attributes.h"
38 #include "mozilla/ClearOnShutdown.h"
39 #include "mozilla/net/NeckoCommon.h"
40 #include "mozilla/net/ChildDNSService.h"
41 #include "mozilla/net/DNSListenerProxy.h"
42 #include "mozilla/Services.h"
43 #include "mozilla/StaticPtr.h"
44
45 using namespace mozilla;
46 using namespace mozilla::net;
47
48 static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries";
49 static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
50 static const char kPrefDnsCacheGrace[] =
51 "network.dnsCacheExpirationGracePeriod";
52 static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains";
53 static const char kPrefDisableIPv6[] = "network.dns.disableIPv6";
54 static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch";
55 static const char kPrefBlockDotOnion[] = "network.dns.blockDotOnion";
56 static const char kPrefDnsLocalDomains[] = "network.dns.localDomains";
57 static const char kPrefDnsForceResolve[] = "network.dns.forceResolve";
58 static const char kPrefDnsOfflineLocalhost[] = "network.dns.offline-localhost";
59 static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
60
61 //-----------------------------------------------------------------------------
62
63 class nsDNSRecord : public nsIDNSRecord {
64 public:
65 NS_DECL_THREADSAFE_ISUPPORTS
66 NS_DECL_NSIDNSRECORD
67
nsDNSRecord(nsHostRecord * hostRecord)68 explicit nsDNSRecord(nsHostRecord *hostRecord)
69 : mHostRecord(hostRecord),
70 mIter(nullptr),
71 mIterGenCnt(-1),
72 mDone(false) {}
73
74 private:
75 virtual ~nsDNSRecord() = default;
76
77 RefPtr<nsHostRecord> mHostRecord;
78 NetAddrElement *mIter;
79 int mIterGenCnt; // the generation count of
80 // mHostRecord->addr_info when we
81 // start iterating
82 bool mDone;
83 };
84
NS_IMPL_ISUPPORTS(nsDNSRecord,nsIDNSRecord)85 NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord)
86
87 NS_IMETHODIMP
88 nsDNSRecord::GetCanonicalName(nsACString &result) {
89 // this method should only be called if we have a CNAME
90 NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME,
91 NS_ERROR_NOT_AVAILABLE);
92
93 MutexAutoLock lock(mHostRecord->addr_info_lock);
94 if (mHostRecord->addr_info) {
95 const char *cname = mHostRecord->addr_info->mCanonicalName
96 ? mHostRecord->addr_info->mCanonicalName
97 : mHostRecord->addr_info->mHostName;
98 result.Assign(cname);
99 } else {
100 // if the record is for an IP address literal, then the canonical
101 // host name is the IP address literal.
102 result = mHostRecord->host;
103 }
104 return NS_OK;
105 }
106
107 NS_IMETHODIMP
IsTRR(bool * retval)108 nsDNSRecord::IsTRR(bool *retval) {
109 MutexAutoLock lock(mHostRecord->addr_info_lock);
110 if (mHostRecord->addr_info) {
111 *retval = mHostRecord->addr_info->IsTRR();
112 } else {
113 *retval = false;
114 }
115 return NS_OK;
116 }
117 NS_IMETHODIMP
GetNextAddr(uint16_t port,NetAddr * addr)118 nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr) {
119 if (mDone) {
120 return NS_ERROR_NOT_AVAILABLE;
121 }
122
123 mHostRecord->addr_info_lock.Lock();
124 if (mHostRecord->addr_info) {
125 if (mIterGenCnt != mHostRecord->addr_info_gencnt) {
126 // mHostRecord->addr_info has changed, restart the iteration.
127 mIter = nullptr;
128 mIterGenCnt = mHostRecord->addr_info_gencnt;
129 }
130
131 bool startedFresh = !mIter;
132
133 do {
134 if (!mIter) {
135 mIter = mHostRecord->addr_info->mAddresses.getFirst();
136 } else {
137 mIter = mIter->getNext();
138 }
139 } while (mIter && mHostRecord->Blacklisted(&mIter->mAddress));
140
141 if (!mIter && startedFresh) {
142 // If everything was blacklisted we want to reset the blacklist (and
143 // likely relearn it) and return the first address. That is better
144 // than nothing.
145 mHostRecord->ResetBlacklist();
146 mIter = mHostRecord->addr_info->mAddresses.getFirst();
147 }
148
149 if (mIter) {
150 memcpy(addr, &mIter->mAddress, sizeof(NetAddr));
151 }
152
153 mHostRecord->addr_info_lock.Unlock();
154
155 if (!mIter) {
156 mDone = true;
157 return NS_ERROR_NOT_AVAILABLE;
158 }
159 } else {
160 mHostRecord->addr_info_lock.Unlock();
161
162 if (!mHostRecord->addr) {
163 // Both mHostRecord->addr_info and mHostRecord->addr are null.
164 // This can happen if mHostRecord->addr_info expired and the
165 // attempt to reresolve it failed.
166 return NS_ERROR_NOT_AVAILABLE;
167 }
168 memcpy(addr, mHostRecord->addr.get(), sizeof(NetAddr));
169 mDone = true;
170 }
171
172 // set given port
173 port = htons(port);
174 if (addr->raw.family == AF_INET) {
175 addr->inet.port = port;
176 } else if (addr->raw.family == AF_INET6) {
177 addr->inet6.port = port;
178 }
179
180 return NS_OK;
181 }
182
183 NS_IMETHODIMP
GetAddresses(nsTArray<NetAddr> & aAddressArray)184 nsDNSRecord::GetAddresses(nsTArray<NetAddr> &aAddressArray) {
185 if (mDone) {
186 return NS_ERROR_NOT_AVAILABLE;
187 }
188
189 mHostRecord->addr_info_lock.Lock();
190 if (mHostRecord->addr_info) {
191 for (NetAddrElement *iter = mHostRecord->addr_info->mAddresses.getFirst();
192 iter; iter = iter->getNext()) {
193 if (mHostRecord->Blacklisted(&iter->mAddress)) {
194 continue;
195 }
196 NetAddr *addr = aAddressArray.AppendElement(NetAddr());
197 memcpy(addr, &iter->mAddress, sizeof(NetAddr));
198 if (addr->raw.family == AF_INET) {
199 addr->inet.port = 0;
200 } else if (addr->raw.family == AF_INET6) {
201 addr->inet6.port = 0;
202 }
203 }
204 mHostRecord->addr_info_lock.Unlock();
205 } else {
206 mHostRecord->addr_info_lock.Unlock();
207
208 if (!mHostRecord->addr) {
209 return NS_ERROR_NOT_AVAILABLE;
210 }
211 NetAddr *addr = aAddressArray.AppendElement(NetAddr());
212 memcpy(addr, mHostRecord->addr.get(), sizeof(NetAddr));
213 if (addr->raw.family == AF_INET) {
214 addr->inet.port = 0;
215 } else if (addr->raw.family == AF_INET6) {
216 addr->inet6.port = 0;
217 }
218 }
219 return NS_OK;
220 }
221
222 NS_IMETHODIMP
GetScriptableNextAddr(uint16_t port,nsINetAddr ** result)223 nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr **result) {
224 NetAddr addr;
225 nsresult rv = GetNextAddr(port, &addr);
226 if (NS_FAILED(rv)) return rv;
227
228 NS_ADDREF(*result = new nsNetAddr(&addr));
229
230 return NS_OK;
231 }
232
233 NS_IMETHODIMP
GetNextAddrAsString(nsACString & result)234 nsDNSRecord::GetNextAddrAsString(nsACString &result) {
235 NetAddr addr;
236 nsresult rv = GetNextAddr(0, &addr);
237 if (NS_FAILED(rv)) return rv;
238
239 char buf[kIPv6CStrBufSize];
240 if (NetAddrToString(&addr, buf, sizeof(buf))) {
241 result.Assign(buf);
242 return NS_OK;
243 }
244 NS_ERROR("NetAddrToString failed unexpectedly");
245 return NS_ERROR_FAILURE; // conversion failed for some reason
246 }
247
248 NS_IMETHODIMP
HasMore(bool * result)249 nsDNSRecord::HasMore(bool *result) {
250 if (mDone) {
251 *result = false;
252 return NS_OK;
253 }
254
255 NetAddrElement *iterCopy = mIter;
256 int iterGenCntCopy = mIterGenCnt;
257
258 NetAddr addr;
259 *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
260
261 mIter = iterCopy;
262 mIterGenCnt = iterGenCntCopy;
263 mDone = false;
264
265 return NS_OK;
266 }
267
268 NS_IMETHODIMP
Rewind()269 nsDNSRecord::Rewind() {
270 mIter = nullptr;
271 mIterGenCnt = -1;
272 mDone = false;
273 return NS_OK;
274 }
275
276 NS_IMETHODIMP
ReportUnusable(uint16_t aPort)277 nsDNSRecord::ReportUnusable(uint16_t aPort) {
278 // right now we don't use the port in the blacklist
279
280 MutexAutoLock lock(mHostRecord->addr_info_lock);
281
282 // Check that we are using a real addr_info (as opposed to a single
283 // constant address), and that the generation count is valid. Otherwise,
284 // ignore the report.
285
286 if (mHostRecord->addr_info && mIterGenCnt == mHostRecord->addr_info_gencnt &&
287 mIter) {
288 mHostRecord->ReportUnusable(&mIter->mAddress);
289 }
290
291 return NS_OK;
292 }
293
294 //-----------------------------------------------------------------------------
295
296 class nsDNSAsyncRequest final : public nsResolveHostCallback,
297 public nsICancelable {
298 public:
299 NS_DECL_THREADSAFE_ISUPPORTS
300 NS_DECL_NSICANCELABLE
301
nsDNSAsyncRequest(nsHostResolver * res,const nsACString & host,const OriginAttributes & attrs,nsIDNSListener * listener,uint16_t flags,uint16_t af,const nsACString & netInterface)302 nsDNSAsyncRequest(nsHostResolver *res, const nsACString &host,
303 const OriginAttributes &attrs, nsIDNSListener *listener,
304 uint16_t flags, uint16_t af, const nsACString &netInterface)
305 : mResolver(res),
306 mHost(host),
307 mOriginAttributes(attrs),
308 mListener(listener),
309 mFlags(flags),
310 mAF(af),
311 mNetworkInterface(netInterface) {}
312
313 void OnResolveHostComplete(nsHostResolver *, nsHostRecord *,
314 nsresult) override;
315 // Returns TRUE if the DNS listener arg is the same as the member listener
316 // Used in Cancellations to remove DNS requests associated with a
317 // particular hostname and nsIDNSListener
318 bool EqualsAsyncListener(nsIDNSListener *aListener) override;
319
320 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
321
322 RefPtr<nsHostResolver> mResolver;
323 nsCString mHost; // hostname we're resolving
324 const OriginAttributes
325 mOriginAttributes; // The originAttributes for this resolving
326 nsCOMPtr<nsIDNSListener> mListener;
327 uint16_t mFlags;
328 uint16_t mAF;
329 nsCString mNetworkInterface;
330
331 private:
332 virtual ~nsDNSAsyncRequest() = default;
333 };
334
NS_IMPL_ISUPPORTS(nsDNSAsyncRequest,nsICancelable)335 NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
336
337 void nsDNSAsyncRequest::OnResolveHostComplete(nsHostResolver *resolver,
338 nsHostRecord *hostRecord,
339 nsresult status) {
340 // need to have an owning ref when we issue the callback to enable
341 // the caller to be able to addref/release multiple times without
342 // destroying the record prematurely.
343 nsCOMPtr<nsIDNSRecord> rec;
344 if (NS_SUCCEEDED(status)) {
345 NS_ASSERTION(hostRecord, "no host record");
346 rec = new nsDNSRecord(hostRecord);
347 }
348
349 mListener->OnLookupComplete(this, rec, status);
350 mListener = nullptr;
351 }
352
EqualsAsyncListener(nsIDNSListener * aListener)353 bool nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) {
354 nsCOMPtr<nsIDNSListenerProxy> wrapper = do_QueryInterface(mListener);
355 if (wrapper) {
356 nsCOMPtr<nsIDNSListener> originalListener;
357 wrapper->GetOriginalListener(getter_AddRefs(originalListener));
358 return aListener == originalListener;
359 }
360 return (aListener == mListener);
361 }
362
SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const363 size_t nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
364 size_t n = mallocSizeOf(this);
365
366 // The following fields aren't measured.
367 // - mHost, because it's a non-owning pointer
368 // - mResolver, because it's a non-owning pointer
369 // - mListener, because it's a non-owning pointer
370
371 return n;
372 }
373
374 NS_IMETHODIMP
Cancel(nsresult reason)375 nsDNSAsyncRequest::Cancel(nsresult reason) {
376 NS_ENSURE_ARG(NS_FAILED(reason));
377 mResolver->DetachCallback(mHost.get(), mOriginAttributes, mFlags, mAF,
378 mNetworkInterface.get(), this, reason);
379 return NS_OK;
380 }
381
382 //-----------------------------------------------------------------------------
383
384 class nsDNSSyncRequest : public nsResolveHostCallback {
385 NS_DECL_THREADSAFE_ISUPPORTS
386 public:
nsDNSSyncRequest(PRMonitor * mon)387 explicit nsDNSSyncRequest(PRMonitor *mon)
388 : mDone(false), mStatus(NS_OK), mMonitor(mon) {}
389
390 void OnResolveHostComplete(nsHostResolver *, nsHostRecord *,
391 nsresult) override;
392 bool EqualsAsyncListener(nsIDNSListener *aListener) override;
393 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
394
395 bool mDone;
396 nsresult mStatus;
397 RefPtr<nsHostRecord> mHostRecord;
398
399 private:
400 virtual ~nsDNSSyncRequest() = default;
401
402 PRMonitor *mMonitor;
403 };
404
NS_IMPL_ISUPPORTS0(nsDNSSyncRequest)405 NS_IMPL_ISUPPORTS0(nsDNSSyncRequest)
406
407 void nsDNSSyncRequest::OnResolveHostComplete(nsHostResolver *resolver,
408 nsHostRecord *hostRecord,
409 nsresult status) {
410 // store results, and wake up nsDNSService::Resolve to process results.
411 PR_EnterMonitor(mMonitor);
412 mDone = true;
413 mStatus = status;
414 mHostRecord = hostRecord;
415 PR_Notify(mMonitor);
416 PR_ExitMonitor(mMonitor);
417 }
418
EqualsAsyncListener(nsIDNSListener * aListener)419 bool nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) {
420 // Sync request: no listener to compare
421 return false;
422 }
423
SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const424 size_t nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
425 size_t n = mallocSizeOf(this);
426
427 // The following fields aren't measured.
428 // - mHostRecord, because it's a non-owning pointer
429
430 // Measurement of the following members may be added later if DMD finds it
431 // is worthwhile:
432 // - mMonitor
433
434 return n;
435 }
436
437 class NotifyDNSResolution : public Runnable {
438 public:
NotifyDNSResolution(const nsACString & aHostname)439 explicit NotifyDNSResolution(const nsACString &aHostname)
440 : mozilla::Runnable("NotifyDNSResolution"), mHostname(aHostname) {}
441
Run()442 NS_IMETHOD Run() override {
443 MOZ_ASSERT(NS_IsMainThread());
444 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
445 if (obs) {
446 obs->NotifyObservers(nullptr, "dns-resolution-request",
447 NS_ConvertUTF8toUTF16(mHostname).get());
448 }
449 return NS_OK;
450 }
451
452 private:
453 nsCString mHostname;
454 };
455
456 //-----------------------------------------------------------------------------
457
nsDNSService()458 nsDNSService::nsDNSService()
459 : mLock("nsDNSServer.mLock"),
460 mDisableIPv6(false),
461 mDisablePrefetch(false),
462 mFirstTime(true),
463 mNotifyResolution(false),
464 mOfflineLocalhost(false),
465 mForceResolveOn(false),
466 mTrrService(nullptr) {}
467
468 nsDNSService::~nsDNSService() = default;
469
470 NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver,
471 nsIMemoryReporter)
472
473 /******************************************************************************
474 * nsDNSService impl:
475 * singleton instance ctor/dtor methods
476 ******************************************************************************/
477 static StaticRefPtr<nsDNSService> gDNSService;
478
GetXPCOMSingleton()479 already_AddRefed<nsIDNSService> nsDNSService::GetXPCOMSingleton() {
480 if (IsNeckoChild()) {
481 return ChildDNSService::GetSingleton();
482 }
483
484 return GetSingleton();
485 }
486
GetSingleton()487 already_AddRefed<nsDNSService> nsDNSService::GetSingleton() {
488 NS_ASSERTION(!IsNeckoChild(), "not a parent process");
489
490 if (!gDNSService) {
491 gDNSService = new nsDNSService();
492 if (NS_SUCCEEDED(gDNSService->Init())) {
493 ClearOnShutdown(&gDNSService);
494 } else {
495 gDNSService = nullptr;
496 }
497 }
498
499 return do_AddRef(gDNSService);
500 }
501
502 NS_IMETHODIMP
Init()503 nsDNSService::Init() {
504 if (mResolver) return NS_OK;
505 NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
506 // prefs
507 uint32_t maxCacheEntries = 400;
508 uint32_t defaultCacheLifetime = 120; // seconds
509 uint32_t defaultGracePeriod = 60; // seconds
510 bool disableIPv6 = false;
511 bool offlineLocalhost = true;
512 bool disablePrefetch = false;
513 bool blockDotOnion = true;
514 int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
515 bool notifyResolution = false;
516
517 nsAutoCString ipv4OnlyDomains;
518 nsAutoCString localDomains;
519 nsAutoCString forceResolve;
520
521 // read prefs
522 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
523 if (prefs) {
524 int32_t val;
525 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
526 maxCacheEntries = (uint32_t)val;
527 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
528 defaultCacheLifetime = val;
529 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val)))
530 defaultGracePeriod = val;
531
532 // ASSUMPTION: pref branch does not modify out params on failure
533 prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
534 prefs->GetCharPref(kPrefIPv4OnlyDomains, ipv4OnlyDomains);
535 prefs->GetCharPref(kPrefDnsLocalDomains, localDomains);
536 prefs->GetCharPref(kPrefDnsForceResolve, forceResolve);
537 prefs->GetBoolPref(kPrefDnsOfflineLocalhost, &offlineLocalhost);
538 prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
539 prefs->GetBoolPref(kPrefBlockDotOnion, &blockDotOnion);
540
541 // If a manual proxy is in use, disable prefetch implicitly
542 prefs->GetIntPref("network.proxy.type", &proxyType);
543 prefs->GetBoolPref(kPrefDnsNotifyResolution, ¬ifyResolution);
544
545 if (mFirstTime) {
546 mFirstTime = false;
547
548 // register as prefs observer
549 prefs->AddObserver(kPrefDnsCacheEntries, this, false);
550 prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
551 prefs->AddObserver(kPrefDnsCacheGrace, this, false);
552 prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
553 prefs->AddObserver(kPrefDnsLocalDomains, this, false);
554 prefs->AddObserver(kPrefDnsForceResolve, this, false);
555 prefs->AddObserver(kPrefDisableIPv6, this, false);
556 prefs->AddObserver(kPrefDnsOfflineLocalhost, this, false);
557 prefs->AddObserver(kPrefDisablePrefetch, this, false);
558 prefs->AddObserver(kPrefBlockDotOnion, this, false);
559 prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
560
561 // Monitor these to see if there is a change in proxy configuration
562 // If a manual proxy is in use, disable prefetch implicitly
563 prefs->AddObserver("network.proxy.type", this, false);
564 }
565 }
566
567 nsCOMPtr<nsIObserverService> observerService =
568 mozilla::services::GetObserverService();
569 if (observerService) {
570 observerService->AddObserver(this, "last-pb-context-exited", false);
571 observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
572 }
573
574 nsDNSPrefetch::Initialize(this);
575
576 nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
577
578 RefPtr<nsHostResolver> res;
579 nsresult rv = nsHostResolver::Create(maxCacheEntries, defaultCacheLifetime,
580 defaultGracePeriod, getter_AddRefs(res));
581 if (NS_SUCCEEDED(rv)) {
582 // now, set all of our member variables while holding the lock
583 MutexAutoLock lock(mLock);
584 mResolver = res;
585 mIDN = idn;
586 mIPv4OnlyDomains = ipv4OnlyDomains;
587 mOfflineLocalhost = offlineLocalhost;
588 mDisableIPv6 = disableIPv6;
589 mBlockDotOnion = blockDotOnion;
590 mForceResolve = forceResolve;
591 mForceResolveOn = !mForceResolve.IsEmpty();
592
593 // Disable prefetching either by explicit preference or if a manual proxy is
594 // configured
595 mDisablePrefetch =
596 disablePrefetch ||
597 (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
598
599 mLocalDomains.Clear();
600 if (!localDomains.IsVoid()) {
601 nsCCharSeparatedTokenizer tokenizer(
602 localDomains, ',', nsCCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
603
604 while (tokenizer.hasMoreTokens()) {
605 mLocalDomains.PutEntry(tokenizer.nextToken());
606 }
607 }
608 mNotifyResolution = notifyResolution;
609 }
610
611 RegisterWeakMemoryReporter(this);
612
613 mTrrService = new TRRService();
614 if (NS_FAILED(mTrrService->Init())) {
615 mTrrService = nullptr;
616 }
617
618 return rv;
619 }
620
621 NS_IMETHODIMP
Shutdown()622 nsDNSService::Shutdown() {
623 UnregisterWeakMemoryReporter(this);
624
625 RefPtr<nsHostResolver> res;
626 {
627 MutexAutoLock lock(mLock);
628 res = mResolver;
629 mResolver = nullptr;
630 }
631 if (res) {
632 res->Shutdown();
633 }
634
635 nsCOMPtr<nsIObserverService> observerService =
636 mozilla::services::GetObserverService();
637 if (observerService) {
638 observerService->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
639 observerService->RemoveObserver(this, "last-pb-context-exited");
640 }
641
642 return NS_OK;
643 }
644
GetOffline() const645 bool nsDNSService::GetOffline() const {
646 bool offline = false;
647 nsCOMPtr<nsIIOService> io = do_GetService(NS_IOSERVICE_CONTRACTID);
648 if (io) {
649 io->GetOffline(&offline);
650 }
651 return offline;
652 }
653
654 NS_IMETHODIMP
GetPrefetchEnabled(bool * outVal)655 nsDNSService::GetPrefetchEnabled(bool *outVal) {
656 MutexAutoLock lock(mLock);
657 *outVal = !mDisablePrefetch;
658 return NS_OK;
659 }
660
661 NS_IMETHODIMP
SetPrefetchEnabled(bool inVal)662 nsDNSService::SetPrefetchEnabled(bool inVal) {
663 MutexAutoLock lock(mLock);
664 mDisablePrefetch = !inVal;
665 return NS_OK;
666 }
667
PreprocessHostname(bool aLocalDomain,const nsACString & aInput,nsIIDNService * aIDN,nsACString & aACE)668 nsresult nsDNSService::PreprocessHostname(bool aLocalDomain,
669 const nsACString &aInput,
670 nsIIDNService *aIDN,
671 nsACString &aACE) {
672 // Enforce RFC 7686
673 if (mBlockDotOnion && StringEndsWith(aInput, NS_LITERAL_CSTRING(".onion"))) {
674 return NS_ERROR_UNKNOWN_HOST;
675 }
676
677 if (aLocalDomain) {
678 aACE.AssignLiteral("localhost");
679 return NS_OK;
680 }
681
682 if (mTrrService && mTrrService->MaybeBootstrap(aInput, aACE)) {
683 return NS_OK;
684 }
685
686 if (mForceResolveOn) {
687 MutexAutoLock lock(mLock);
688 if (!aInput.LowerCaseEqualsASCII("localhost") &&
689 !aInput.LowerCaseEqualsASCII("127.0.0.1")) {
690 aACE.Assign(mForceResolve);
691 return NS_OK;
692 }
693 }
694
695 if (!aIDN || IsASCII(aInput)) {
696 aACE = aInput;
697 return NS_OK;
698 }
699
700 if (!(IsUTF8(aInput) && NS_SUCCEEDED(aIDN->ConvertUTF8toACE(aInput, aACE)))) {
701 return NS_ERROR_FAILURE;
702 }
703 return NS_OK;
704 }
705
706 NS_IMETHODIMP
AsyncResolve(const nsACString & aHostname,uint32_t flags,nsIDNSListener * listener,nsIEventTarget * target_,JS::HandleValue aOriginAttributes,JSContext * aCx,uint8_t aArgc,nsICancelable ** result)707 nsDNSService::AsyncResolve(const nsACString &aHostname, uint32_t flags,
708 nsIDNSListener *listener, nsIEventTarget *target_,
709 JS::HandleValue aOriginAttributes, JSContext *aCx,
710 uint8_t aArgc, nsICancelable **result) {
711 OriginAttributes attrs;
712
713 if (aArgc == 1) {
714 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
715 return NS_ERROR_INVALID_ARG;
716 }
717 }
718
719 return AsyncResolveExtendedNative(aHostname, flags, EmptyCString(), listener,
720 target_, attrs, result);
721 }
722
723 NS_IMETHODIMP
AsyncResolveNative(const nsACString & aHostname,uint32_t flags,nsIDNSListener * listener,nsIEventTarget * target_,const OriginAttributes & aOriginAttributes,nsICancelable ** result)724 nsDNSService::AsyncResolveNative(const nsACString &aHostname, uint32_t flags,
725 nsIDNSListener *listener,
726 nsIEventTarget *target_,
727 const OriginAttributes &aOriginAttributes,
728 nsICancelable **result) {
729 return AsyncResolveExtendedNative(aHostname, flags, EmptyCString(), listener,
730 target_, aOriginAttributes, result);
731 }
732
733 NS_IMETHODIMP
AsyncResolveExtended(const nsACString & aHostname,uint32_t flags,const nsACString & aNetworkInterface,nsIDNSListener * listener,nsIEventTarget * target_,JS::HandleValue aOriginAttributes,JSContext * aCx,uint8_t aArgc,nsICancelable ** result)734 nsDNSService::AsyncResolveExtended(const nsACString &aHostname, uint32_t flags,
735 const nsACString &aNetworkInterface,
736 nsIDNSListener *listener,
737 nsIEventTarget *target_,
738 JS::HandleValue aOriginAttributes,
739 JSContext *aCx, uint8_t aArgc,
740 nsICancelable **result) {
741 OriginAttributes attrs;
742
743 if (aArgc == 1) {
744 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
745 return NS_ERROR_INVALID_ARG;
746 }
747 }
748
749 return AsyncResolveExtendedNative(aHostname, flags, aNetworkInterface,
750 listener, target_, attrs, result);
751 }
752
753 NS_IMETHODIMP
AsyncResolveExtendedNative(const nsACString & aHostname,uint32_t flags,const nsACString & aNetworkInterface,nsIDNSListener * aListener,nsIEventTarget * target_,const OriginAttributes & aOriginAttributes,nsICancelable ** result)754 nsDNSService::AsyncResolveExtendedNative(
755 const nsACString &aHostname, uint32_t flags,
756 const nsACString &aNetworkInterface, nsIDNSListener *aListener,
757 nsIEventTarget *target_, const OriginAttributes &aOriginAttributes,
758 nsICancelable **result) {
759 // grab reference to global host resolver and IDN service. beware
760 // simultaneous shutdown!!
761 RefPtr<nsHostResolver> res;
762 nsCOMPtr<nsIIDNService> idn;
763 nsCOMPtr<nsIEventTarget> target = target_;
764 nsCOMPtr<nsIDNSListener> listener = aListener;
765 bool localDomain = false;
766 {
767 MutexAutoLock lock(mLock);
768
769 if (mDisablePrefetch && (flags & RESOLVE_SPECULATE))
770 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
771
772 res = mResolver;
773 idn = mIDN;
774 localDomain = mLocalDomains.GetEntry(aHostname);
775 }
776
777 if (mNotifyResolution) {
778 NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
779 }
780
781 if (!res) return NS_ERROR_OFFLINE;
782
783 nsCString hostname;
784 nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
785 if (NS_FAILED(rv)) {
786 return rv;
787 }
788
789 if (GetOffline() &&
790 (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) {
791 flags |= RESOLVE_OFFLINE;
792 }
793
794 // make sure JS callers get notification on the main thread
795 nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
796 if (wrappedListener && !target) {
797 target = GetMainThreadEventTarget();
798 }
799
800 if (target) {
801 listener = new DNSListenerProxy(listener, target);
802 }
803
804 uint16_t af = GetAFForLookup(hostname, flags);
805
806 MOZ_ASSERT(listener);
807 RefPtr<nsDNSAsyncRequest> req = new nsDNSAsyncRequest(
808 res, hostname, aOriginAttributes, listener, flags, af, aNetworkInterface);
809 if (!req) return NS_ERROR_OUT_OF_MEMORY;
810
811 rv = res->ResolveHost(req->mHost.get(), req->mOriginAttributes, flags, af,
812 req->mNetworkInterface.get(), req);
813 req.forget(result);
814 return rv;
815 }
816
817 NS_IMETHODIMP
CancelAsyncResolve(const nsACString & aHostname,uint32_t aFlags,nsIDNSListener * aListener,nsresult aReason,JS::HandleValue aOriginAttributes,JSContext * aCx,uint8_t aArgc)818 nsDNSService::CancelAsyncResolve(const nsACString &aHostname, uint32_t aFlags,
819 nsIDNSListener *aListener, nsresult aReason,
820 JS::HandleValue aOriginAttributes,
821 JSContext *aCx, uint8_t aArgc) {
822 OriginAttributes attrs;
823
824 if (aArgc == 1) {
825 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
826 return NS_ERROR_INVALID_ARG;
827 }
828 }
829
830 return CancelAsyncResolveExtendedNative(aHostname, aFlags, EmptyCString(),
831 aListener, aReason, attrs);
832 }
833
834 NS_IMETHODIMP
CancelAsyncResolveNative(const nsACString & aHostname,uint32_t aFlags,nsIDNSListener * aListener,nsresult aReason,const OriginAttributes & aOriginAttributes)835 nsDNSService::CancelAsyncResolveNative(
836 const nsACString &aHostname, uint32_t aFlags, nsIDNSListener *aListener,
837 nsresult aReason, const OriginAttributes &aOriginAttributes) {
838 return CancelAsyncResolveExtendedNative(
839 aHostname, aFlags, EmptyCString(), aListener, aReason, aOriginAttributes);
840 }
841
842 NS_IMETHODIMP
CancelAsyncResolveExtended(const nsACString & aHostname,uint32_t aFlags,const nsACString & aNetworkInterface,nsIDNSListener * aListener,nsresult aReason,JS::HandleValue aOriginAttributes,JSContext * aCx,uint8_t aArgc)843 nsDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
844 uint32_t aFlags,
845 const nsACString &aNetworkInterface,
846 nsIDNSListener *aListener,
847 nsresult aReason,
848 JS::HandleValue aOriginAttributes,
849 JSContext *aCx, uint8_t aArgc) {
850 OriginAttributes attrs;
851
852 if (aArgc == 1) {
853 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
854 return NS_ERROR_INVALID_ARG;
855 }
856 }
857
858 return CancelAsyncResolveExtendedNative(aHostname, aFlags, aNetworkInterface,
859 aListener, aReason, attrs);
860 }
861
862 NS_IMETHODIMP
CancelAsyncResolveExtendedNative(const nsACString & aHostname,uint32_t aFlags,const nsACString & aNetworkInterface,nsIDNSListener * aListener,nsresult aReason,const OriginAttributes & aOriginAttributes)863 nsDNSService::CancelAsyncResolveExtendedNative(
864 const nsACString &aHostname, uint32_t aFlags,
865 const nsACString &aNetworkInterface, nsIDNSListener *aListener,
866 nsresult aReason, const OriginAttributes &aOriginAttributes) {
867 // grab reference to global host resolver and IDN service. beware
868 // simultaneous shutdown!!
869 RefPtr<nsHostResolver> res;
870 nsCOMPtr<nsIIDNService> idn;
871 bool localDomain = false;
872 {
873 MutexAutoLock lock(mLock);
874
875 if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE))
876 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
877
878 res = mResolver;
879 idn = mIDN;
880 localDomain = mLocalDomains.GetEntry(aHostname);
881 }
882 if (!res) return NS_ERROR_OFFLINE;
883
884 nsCString hostname;
885 nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
886 if (NS_FAILED(rv)) {
887 return rv;
888 }
889
890 uint16_t af = GetAFForLookup(hostname, aFlags);
891
892 res->CancelAsyncRequest(hostname.get(), aOriginAttributes, aFlags, af,
893 nsPromiseFlatCString(aNetworkInterface).get(),
894 aListener, aReason);
895 return NS_OK;
896 }
897
898 NS_IMETHODIMP
Resolve(const nsACString & aHostname,uint32_t flags,JS::HandleValue aOriginAttributes,JSContext * aCx,uint8_t aArgc,nsIDNSRecord ** result)899 nsDNSService::Resolve(const nsACString &aHostname, uint32_t flags,
900 JS::HandleValue aOriginAttributes, JSContext *aCx,
901 uint8_t aArgc, nsIDNSRecord **result) {
902 OriginAttributes attrs;
903
904 if (aArgc == 1) {
905 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
906 return NS_ERROR_INVALID_ARG;
907 }
908 }
909
910 return ResolveNative(aHostname, flags, attrs, result);
911 }
912
913 NS_IMETHODIMP
ResolveNative(const nsACString & aHostname,uint32_t flags,const OriginAttributes & aOriginAttributes,nsIDNSRecord ** result)914 nsDNSService::ResolveNative(const nsACString &aHostname, uint32_t flags,
915 const OriginAttributes &aOriginAttributes,
916 nsIDNSRecord **result) {
917 // Synchronous resolution is not available on the main thread.
918 if (NS_IsMainThread()) {
919 return NS_ERROR_NOT_AVAILABLE;
920 }
921
922 return ResolveInternal(aHostname, flags, aOriginAttributes, result);
923 }
924
DeprecatedSyncResolve(const nsACString & aHostname,uint32_t flags,const OriginAttributes & aOriginAttributes,nsIDNSRecord ** result)925 nsresult nsDNSService::DeprecatedSyncResolve(
926 const nsACString &aHostname, uint32_t flags,
927 const OriginAttributes &aOriginAttributes, nsIDNSRecord **result) {
928 return ResolveInternal(aHostname, flags, aOriginAttributes, result);
929 }
930
ResolveInternal(const nsACString & aHostname,uint32_t flags,const OriginAttributes & aOriginAttributes,nsIDNSRecord ** result)931 nsresult nsDNSService::ResolveInternal(
932 const nsACString &aHostname, uint32_t flags,
933 const OriginAttributes &aOriginAttributes, nsIDNSRecord **result) {
934 // grab reference to global host resolver and IDN service. beware
935 // simultaneous shutdown!!
936 RefPtr<nsHostResolver> res;
937 nsCOMPtr<nsIIDNService> idn;
938 bool localDomain = false;
939 {
940 MutexAutoLock lock(mLock);
941 res = mResolver;
942 idn = mIDN;
943 localDomain = mLocalDomains.GetEntry(aHostname);
944 }
945
946 if (mNotifyResolution) {
947 NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
948 }
949
950 NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
951
952 nsCString hostname;
953 nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
954 if (NS_FAILED(rv)) {
955 return rv;
956 }
957
958 if (GetOffline() &&
959 (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) {
960 flags |= RESOLVE_OFFLINE;
961 }
962
963 //
964 // sync resolve: since the host resolver only works asynchronously, we need
965 // to use a mutex and a condvar to wait for the result. however, since the
966 // result may be in the resolvers cache, we might get called back recursively
967 // on the same thread. so, our mutex needs to be re-entrant. in other words,
968 // we need to use a monitor! ;-)
969 //
970
971 PRMonitor *mon = PR_NewMonitor();
972 if (!mon) return NS_ERROR_OUT_OF_MEMORY;
973
974 PR_EnterMonitor(mon);
975 RefPtr<nsDNSSyncRequest> syncReq = new nsDNSSyncRequest(mon);
976
977 uint16_t af = GetAFForLookup(hostname, flags);
978
979 rv = res->ResolveHost(hostname.get(), aOriginAttributes, flags, af, "",
980 syncReq);
981 if (NS_SUCCEEDED(rv)) {
982 // wait for result
983 while (!syncReq->mDone) {
984 PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
985 }
986
987 if (NS_FAILED(syncReq->mStatus)) {
988 rv = syncReq->mStatus;
989 } else {
990 NS_ASSERTION(syncReq->mHostRecord, "no host record");
991 RefPtr<nsDNSRecord> rec = new nsDNSRecord(syncReq->mHostRecord);
992 rec.forget(result);
993 }
994 }
995
996 PR_ExitMonitor(mon);
997 PR_DestroyMonitor(mon);
998 return rv;
999 }
1000
1001 NS_IMETHODIMP
GetMyHostName(nsACString & result)1002 nsDNSService::GetMyHostName(nsACString &result) {
1003 char name[100];
1004 if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
1005 result = name;
1006 return NS_OK;
1007 }
1008 return NS_ERROR_FAILURE;
1009 }
1010
1011 NS_IMETHODIMP
Observe(nsISupports * subject,const char * topic,const char16_t * data)1012 nsDNSService::Observe(nsISupports *subject, const char *topic,
1013 const char16_t *data) {
1014 // We are only getting called if a preference has changed or there's a
1015 // network link event.
1016 NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 ||
1017 strcmp(topic, "last-pb-context-exited") == 0 ||
1018 strcmp(topic, NS_NETWORK_LINK_TOPIC) == 0,
1019 "unexpected observe call");
1020
1021 bool flushCache = false;
1022 if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
1023 nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
1024 if (mResolver && !strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) {
1025 flushCache = true;
1026 }
1027 } else if (!strcmp(topic, "last-pb-context-exited")) {
1028 flushCache = true;
1029 }
1030 if (flushCache) {
1031 mResolver->FlushCache();
1032 return NS_OK;
1033 }
1034
1035 //
1036 // Shutdown and this function are both only called on the UI thread, so we
1037 // don't have to worry about mResolver being cleared out from under us.
1038 //
1039 // NOTE Shutting down and reinitializing the service like this is obviously
1040 // suboptimal if Observe gets called several times in a row, but we don't
1041 // expect that to be the case.
1042 //
1043
1044 if (mResolver) {
1045 Shutdown();
1046 }
1047 Init();
1048 return NS_OK;
1049 }
1050
GetAFForLookup(const nsACString & host,uint32_t flags)1051 uint16_t nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags) {
1052 if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6)) return PR_AF_INET;
1053
1054 MutexAutoLock lock(mLock);
1055
1056 uint16_t af = PR_AF_UNSPEC;
1057
1058 if (!mIPv4OnlyDomains.IsEmpty()) {
1059 const char *domain, *domainEnd, *end;
1060 uint32_t hostLen, domainLen;
1061
1062 // see if host is in one of the IPv4-only domains
1063 domain = mIPv4OnlyDomains.BeginReading();
1064 domainEnd = mIPv4OnlyDomains.EndReading();
1065
1066 nsACString::const_iterator hostStart;
1067 host.BeginReading(hostStart);
1068 hostLen = host.Length();
1069
1070 do {
1071 // skip any whitespace
1072 while (*domain == ' ' || *domain == '\t') ++domain;
1073
1074 // find end of this domain in the string
1075 end = strchr(domain, ',');
1076 if (!end) end = domainEnd;
1077
1078 // to see if the hostname is in the domain, check if the domain
1079 // matches the end of the hostname.
1080 domainLen = end - domain;
1081 if (domainLen && hostLen >= domainLen) {
1082 const char *hostTail = hostStart.get() + hostLen - domainLen;
1083 if (PL_strncasecmp(domain, hostTail, domainLen) == 0) {
1084 // now, make sure either that the hostname is a direct match or
1085 // that the hostname begins with a dot.
1086 if (hostLen == domainLen || *hostTail == '.' ||
1087 *(hostTail - 1) == '.') {
1088 af = PR_AF_INET;
1089 break;
1090 }
1091 }
1092 }
1093
1094 domain = end + 1;
1095 } while (*end);
1096 }
1097
1098 if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4)) af = PR_AF_INET6;
1099
1100 return af;
1101 }
1102
1103 NS_IMETHODIMP
GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> * args)1104 nsDNSService::GetDNSCacheEntries(
1105 nsTArray<mozilla::net::DNSCacheEntries> *args) {
1106 NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED);
1107 mResolver->GetDNSCacheEntries(args);
1108 return NS_OK;
1109 }
1110
SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const1111 size_t nsDNSService::SizeOfIncludingThis(
1112 mozilla::MallocSizeOf mallocSizeOf) const {
1113 // Measurement of the following members may be added later if DMD finds it
1114 // is worthwhile:
1115 // - mIDN
1116 // - mLock
1117
1118 size_t n = mallocSizeOf(this);
1119 n += mResolver ? mResolver->SizeOfIncludingThis(mallocSizeOf) : 0;
1120 n += mIPv4OnlyDomains.SizeOfExcludingThisIfUnshared(mallocSizeOf);
1121 n += mLocalDomains.SizeOfExcludingThis(mallocSizeOf);
1122 return n;
1123 }
1124
MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf)1125 MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf)
1126
1127 NS_IMETHODIMP
1128 nsDNSService::CollectReports(nsIHandleReportCallback *aHandleReport,
1129 nsISupports *aData, bool aAnonymize) {
1130 MOZ_COLLECT_REPORT("explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
1131 SizeOfIncludingThis(DNSServiceMallocSizeOf),
1132 "Memory used for the DNS service.");
1133
1134 return NS_OK;
1135 }
1136