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, &notifyResolution);
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