1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/dns/nsDNSService2.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1008 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set sw=4 ts=8 et tw=80 : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsDNSService2.h" 1.11 +#include "nsIDNSRecord.h" 1.12 +#include "nsIDNSListener.h" 1.13 +#include "nsICancelable.h" 1.14 +#include "nsIPrefService.h" 1.15 +#include "nsIPrefBranch.h" 1.16 +#include "nsIServiceManager.h" 1.17 +#include "nsIXPConnect.h" 1.18 +#include "nsProxyRelease.h" 1.19 +#include "nsReadableUtils.h" 1.20 +#include "nsString.h" 1.21 +#include "nsAutoPtr.h" 1.22 +#include "nsNetCID.h" 1.23 +#include "nsError.h" 1.24 +#include "nsDNSPrefetch.h" 1.25 +#include "nsThreadUtils.h" 1.26 +#include "nsIProtocolProxyService.h" 1.27 +#include "prsystem.h" 1.28 +#include "prnetdb.h" 1.29 +#include "prmon.h" 1.30 +#include "prio.h" 1.31 +#include "plstr.h" 1.32 +#include "nsIOService.h" 1.33 +#include "nsCharSeparatedTokenizer.h" 1.34 +#include "nsNetAddr.h" 1.35 +#include "nsProxyRelease.h" 1.36 + 1.37 +#include "mozilla/Attributes.h" 1.38 +#include "mozilla/VisualEventTracer.h" 1.39 +#include "mozilla/net/NeckoCommon.h" 1.40 +#include "mozilla/net/ChildDNSService.h" 1.41 +#include "mozilla/net/DNSListenerProxy.h" 1.42 + 1.43 +using namespace mozilla; 1.44 +using namespace mozilla::net; 1.45 + 1.46 +static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries"; 1.47 +static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration"; 1.48 +static const char kPrefDnsCacheGrace[] = "network.dnsCacheExpirationGracePeriod"; 1.49 +static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains"; 1.50 +static const char kPrefDisableIPv6[] = "network.dns.disableIPv6"; 1.51 +static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch"; 1.52 +static const char kPrefDnsLocalDomains[] = "network.dns.localDomains"; 1.53 +static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution"; 1.54 + 1.55 +//----------------------------------------------------------------------------- 1.56 + 1.57 +class nsDNSRecord : public nsIDNSRecord 1.58 +{ 1.59 +public: 1.60 + NS_DECL_THREADSAFE_ISUPPORTS 1.61 + NS_DECL_NSIDNSRECORD 1.62 + 1.63 + nsDNSRecord(nsHostRecord *hostRecord) 1.64 + : mHostRecord(hostRecord) 1.65 + , mIter(nullptr) 1.66 + , mIterGenCnt(-1) 1.67 + , mDone(false) {} 1.68 + 1.69 +private: 1.70 + virtual ~nsDNSRecord() {} 1.71 + 1.72 + nsRefPtr<nsHostRecord> mHostRecord; 1.73 + NetAddrElement *mIter; 1.74 + int mIterGenCnt; // the generation count of 1.75 + // mHostRecord->addr_info when we 1.76 + // start iterating 1.77 + bool mDone; 1.78 +}; 1.79 + 1.80 +NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord) 1.81 + 1.82 +NS_IMETHODIMP 1.83 +nsDNSRecord::GetCanonicalName(nsACString &result) 1.84 +{ 1.85 + // this method should only be called if we have a CNAME 1.86 + NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME, 1.87 + NS_ERROR_NOT_AVAILABLE); 1.88 + 1.89 + // if the record is for an IP address literal, then the canonical 1.90 + // host name is the IP address literal. 1.91 + const char *cname; 1.92 + { 1.93 + MutexAutoLock lock(mHostRecord->addr_info_lock); 1.94 + if (mHostRecord->addr_info) 1.95 + cname = mHostRecord->addr_info->mCanonicalName ? 1.96 + mHostRecord->addr_info->mCanonicalName : 1.97 + mHostRecord->addr_info->mHostName; 1.98 + else 1.99 + cname = mHostRecord->host; 1.100 + result.Assign(cname); 1.101 + } 1.102 + return NS_OK; 1.103 +} 1.104 + 1.105 +NS_IMETHODIMP 1.106 +nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr) 1.107 +{ 1.108 + if (mDone) { 1.109 + return NS_ERROR_NOT_AVAILABLE; 1.110 + } 1.111 + 1.112 + mHostRecord->addr_info_lock.Lock(); 1.113 + if (mHostRecord->addr_info) { 1.114 + if (mIterGenCnt != mHostRecord->addr_info_gencnt) { 1.115 + // mHostRecord->addr_info has changed, restart the iteration. 1.116 + mIter = nullptr; 1.117 + mIterGenCnt = mHostRecord->addr_info_gencnt; 1.118 + } 1.119 + 1.120 + bool startedFresh = !mIter; 1.121 + 1.122 + do { 1.123 + if (!mIter) { 1.124 + mIter = mHostRecord->addr_info->mAddresses.getFirst(); 1.125 + } else { 1.126 + mIter = mIter->getNext(); 1.127 + } 1.128 + } 1.129 + while (mIter && mHostRecord->Blacklisted(&mIter->mAddress)); 1.130 + 1.131 + if (!mIter && startedFresh) { 1.132 + // If everything was blacklisted we want to reset the blacklist (and 1.133 + // likely relearn it) and return the first address. That is better 1.134 + // than nothing. 1.135 + mHostRecord->ResetBlacklist(); 1.136 + mIter = mHostRecord->addr_info->mAddresses.getFirst(); 1.137 + } 1.138 + 1.139 + if (mIter) { 1.140 + memcpy(addr, &mIter->mAddress, sizeof(NetAddr)); 1.141 + } 1.142 + 1.143 + mHostRecord->addr_info_lock.Unlock(); 1.144 + 1.145 + if (!mIter) { 1.146 + mDone = true; 1.147 + return NS_ERROR_NOT_AVAILABLE; 1.148 + } 1.149 + } 1.150 + else { 1.151 + mHostRecord->addr_info_lock.Unlock(); 1.152 + 1.153 + if (!mHostRecord->addr) { 1.154 + // Both mHostRecord->addr_info and mHostRecord->addr are null. 1.155 + // This can happen if mHostRecord->addr_info expired and the 1.156 + // attempt to reresolve it failed. 1.157 + return NS_ERROR_NOT_AVAILABLE; 1.158 + } 1.159 + memcpy(addr, mHostRecord->addr, sizeof(NetAddr)); 1.160 + mDone = true; 1.161 + } 1.162 + 1.163 + // set given port 1.164 + port = htons(port); 1.165 + if (addr->raw.family == AF_INET) { 1.166 + addr->inet.port = port; 1.167 + } 1.168 + else if (addr->raw.family == AF_INET6) { 1.169 + addr->inet6.port = port; 1.170 + } 1.171 + 1.172 + return NS_OK; 1.173 +} 1.174 + 1.175 +NS_IMETHODIMP 1.176 +nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr * *result) 1.177 +{ 1.178 + NetAddr addr; 1.179 + nsresult rv = GetNextAddr(port, &addr); 1.180 + if (NS_FAILED(rv)) return rv; 1.181 + 1.182 + NS_ADDREF(*result = new nsNetAddr(&addr)); 1.183 + 1.184 + return NS_OK; 1.185 +} 1.186 + 1.187 +NS_IMETHODIMP 1.188 +nsDNSRecord::GetNextAddrAsString(nsACString &result) 1.189 +{ 1.190 + NetAddr addr; 1.191 + nsresult rv = GetNextAddr(0, &addr); 1.192 + if (NS_FAILED(rv)) return rv; 1.193 + 1.194 + char buf[kIPv6CStrBufSize]; 1.195 + if (NetAddrToString(&addr, buf, sizeof(buf))) { 1.196 + result.Assign(buf); 1.197 + return NS_OK; 1.198 + } 1.199 + NS_ERROR("NetAddrToString failed unexpectedly"); 1.200 + return NS_ERROR_FAILURE; // conversion failed for some reason 1.201 +} 1.202 + 1.203 +NS_IMETHODIMP 1.204 +nsDNSRecord::HasMore(bool *result) 1.205 +{ 1.206 + if (mDone) { 1.207 + *result = false; 1.208 + return NS_OK; 1.209 + } 1.210 + 1.211 + NetAddrElement *iterCopy = mIter; 1.212 + 1.213 + NetAddr addr; 1.214 + *result = NS_SUCCEEDED(GetNextAddr(0, &addr)); 1.215 + 1.216 + mIter = iterCopy; 1.217 + mDone = false; 1.218 + 1.219 + return NS_OK; 1.220 +} 1.221 + 1.222 +NS_IMETHODIMP 1.223 +nsDNSRecord::Rewind() 1.224 +{ 1.225 + mIter = nullptr; 1.226 + mIterGenCnt = -1; 1.227 + mDone = false; 1.228 + return NS_OK; 1.229 +} 1.230 + 1.231 +NS_IMETHODIMP 1.232 +nsDNSRecord::ReportUnusable(uint16_t aPort) 1.233 +{ 1.234 + // right now we don't use the port in the blacklist 1.235 + 1.236 + MutexAutoLock lock(mHostRecord->addr_info_lock); 1.237 + 1.238 + // Check that we are using a real addr_info (as opposed to a single 1.239 + // constant address), and that the generation count is valid. Otherwise, 1.240 + // ignore the report. 1.241 + 1.242 + if (mHostRecord->addr_info && 1.243 + mIterGenCnt == mHostRecord->addr_info_gencnt && 1.244 + mIter) { 1.245 + mHostRecord->ReportUnusable(&mIter->mAddress); 1.246 + } 1.247 + 1.248 + return NS_OK; 1.249 +} 1.250 + 1.251 +//----------------------------------------------------------------------------- 1.252 + 1.253 +class nsDNSAsyncRequest MOZ_FINAL : public nsResolveHostCallback 1.254 + , public nsICancelable 1.255 +{ 1.256 +public: 1.257 + NS_DECL_THREADSAFE_ISUPPORTS 1.258 + NS_DECL_NSICANCELABLE 1.259 + 1.260 + nsDNSAsyncRequest(nsHostResolver *res, 1.261 + const nsACString &host, 1.262 + nsIDNSListener *listener, 1.263 + uint16_t flags, 1.264 + uint16_t af) 1.265 + : mResolver(res) 1.266 + , mHost(host) 1.267 + , mListener(listener) 1.268 + , mFlags(flags) 1.269 + , mAF(af) {} 1.270 + ~nsDNSAsyncRequest() {} 1.271 + 1.272 + void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); 1.273 + // Returns TRUE if the DNS listener arg is the same as the member listener 1.274 + // Used in Cancellations to remove DNS requests associated with a 1.275 + // particular hostname and nsIDNSListener 1.276 + bool EqualsAsyncListener(nsIDNSListener *aListener); 1.277 + 1.278 + size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const; 1.279 + 1.280 + nsRefPtr<nsHostResolver> mResolver; 1.281 + nsCString mHost; // hostname we're resolving 1.282 + nsCOMPtr<nsIDNSListener> mListener; 1.283 + uint16_t mFlags; 1.284 + uint16_t mAF; 1.285 +}; 1.286 + 1.287 +void 1.288 +nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver, 1.289 + nsHostRecord *hostRecord, 1.290 + nsresult status) 1.291 +{ 1.292 + // need to have an owning ref when we issue the callback to enable 1.293 + // the caller to be able to addref/release multiple times without 1.294 + // destroying the record prematurely. 1.295 + nsCOMPtr<nsIDNSRecord> rec; 1.296 + if (NS_SUCCEEDED(status)) { 1.297 + NS_ASSERTION(hostRecord, "no host record"); 1.298 + rec = new nsDNSRecord(hostRecord); 1.299 + if (!rec) 1.300 + status = NS_ERROR_OUT_OF_MEMORY; 1.301 + } 1.302 + 1.303 + MOZ_EVENT_TRACER_DONE(this, "net::dns::lookup"); 1.304 + 1.305 + mListener->OnLookupComplete(this, rec, status); 1.306 + mListener = nullptr; 1.307 + 1.308 + // release the reference to ourselves that was added before we were 1.309 + // handed off to the host resolver. 1.310 + NS_RELEASE_THIS(); 1.311 +} 1.312 + 1.313 +bool 1.314 +nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) 1.315 +{ 1.316 + return (aListener == mListener); 1.317 +} 1.318 + 1.319 +size_t 1.320 +nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const 1.321 +{ 1.322 + size_t n = mallocSizeOf(this); 1.323 + 1.324 + // The following fields aren't measured. 1.325 + // - mHost, because it's a non-owning pointer 1.326 + // - mResolver, because it's a non-owning pointer 1.327 + // - mListener, because it's a non-owning pointer 1.328 + 1.329 + return n; 1.330 +} 1.331 + 1.332 +NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable) 1.333 + 1.334 +NS_IMETHODIMP 1.335 +nsDNSAsyncRequest::Cancel(nsresult reason) 1.336 +{ 1.337 + NS_ENSURE_ARG(NS_FAILED(reason)); 1.338 + mResolver->DetachCallback(mHost.get(), mFlags, mAF, this, reason); 1.339 + return NS_OK; 1.340 +} 1.341 + 1.342 +//----------------------------------------------------------------------------- 1.343 + 1.344 +class nsDNSSyncRequest : public nsResolveHostCallback 1.345 +{ 1.346 +public: 1.347 + nsDNSSyncRequest(PRMonitor *mon) 1.348 + : mDone(false) 1.349 + , mStatus(NS_OK) 1.350 + , mMonitor(mon) {} 1.351 + virtual ~nsDNSSyncRequest() {} 1.352 + 1.353 + void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); 1.354 + bool EqualsAsyncListener(nsIDNSListener *aListener); 1.355 + size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const; 1.356 + 1.357 + bool mDone; 1.358 + nsresult mStatus; 1.359 + nsRefPtr<nsHostRecord> mHostRecord; 1.360 + 1.361 +private: 1.362 + PRMonitor *mMonitor; 1.363 +}; 1.364 + 1.365 +void 1.366 +nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver, 1.367 + nsHostRecord *hostRecord, 1.368 + nsresult status) 1.369 +{ 1.370 + // store results, and wake up nsDNSService::Resolve to process results. 1.371 + PR_EnterMonitor(mMonitor); 1.372 + mDone = true; 1.373 + mStatus = status; 1.374 + mHostRecord = hostRecord; 1.375 + PR_Notify(mMonitor); 1.376 + PR_ExitMonitor(mMonitor); 1.377 +} 1.378 + 1.379 +bool 1.380 +nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) 1.381 +{ 1.382 + // Sync request: no listener to compare 1.383 + return false; 1.384 +} 1.385 + 1.386 +size_t 1.387 +nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const 1.388 +{ 1.389 + size_t n = mallocSizeOf(this); 1.390 + 1.391 + // The following fields aren't measured. 1.392 + // - mHostRecord, because it's a non-owning pointer 1.393 + 1.394 + // Measurement of the following members may be added later if DMD finds it 1.395 + // is worthwhile: 1.396 + // - mMonitor 1.397 + 1.398 + return n; 1.399 +} 1.400 + 1.401 +class NotifyDNSResolution: public nsRunnable 1.402 +{ 1.403 +public: 1.404 + NotifyDNSResolution(nsMainThreadPtrHandle<nsIObserverService> &aObs, 1.405 + const nsACString &aHostname) 1.406 + : mObs(aObs) 1.407 + , mHostname(aHostname) 1.408 + { 1.409 + MOZ_ASSERT(mObs); 1.410 + } 1.411 + 1.412 + NS_IMETHOD Run() 1.413 + { 1.414 + MOZ_ASSERT(NS_IsMainThread()); 1.415 + mObs->NotifyObservers(nullptr, 1.416 + "dns-resolution-request", 1.417 + NS_ConvertUTF8toUTF16(mHostname).get()); 1.418 + return NS_OK; 1.419 + } 1.420 + 1.421 +private: 1.422 + nsMainThreadPtrHandle<nsIObserverService> mObs; 1.423 + nsCString mHostname; 1.424 +}; 1.425 + 1.426 +//----------------------------------------------------------------------------- 1.427 + 1.428 +nsDNSService::nsDNSService() 1.429 + : mLock("nsDNSServer.mLock") 1.430 + , mFirstTime(true) 1.431 + , mOffline(false) 1.432 +{ 1.433 +} 1.434 + 1.435 +nsDNSService::~nsDNSService() 1.436 +{ 1.437 +} 1.438 + 1.439 +NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver, 1.440 + nsIMemoryReporter) 1.441 + 1.442 +/****************************************************************************** 1.443 + * nsDNSService impl: 1.444 + * singleton instance ctor/dtor methods 1.445 + ******************************************************************************/ 1.446 +static nsDNSService *gDNSService; 1.447 + 1.448 +nsIDNSService* 1.449 +nsDNSService::GetXPCOMSingleton() 1.450 +{ 1.451 + if (IsNeckoChild()) { 1.452 + return ChildDNSService::GetSingleton(); 1.453 + } 1.454 + 1.455 + return GetSingleton(); 1.456 +} 1.457 + 1.458 +nsDNSService* 1.459 +nsDNSService::GetSingleton() 1.460 +{ 1.461 + NS_ASSERTION(!IsNeckoChild(), "not a parent process"); 1.462 + 1.463 + if (gDNSService) { 1.464 + NS_ADDREF(gDNSService); 1.465 + return gDNSService; 1.466 + } 1.467 + 1.468 + gDNSService = new nsDNSService(); 1.469 + if (gDNSService) { 1.470 + NS_ADDREF(gDNSService); 1.471 + if (NS_FAILED(gDNSService->Init())) { 1.472 + NS_RELEASE(gDNSService); 1.473 + } 1.474 + } 1.475 + 1.476 + return gDNSService; 1.477 +} 1.478 + 1.479 +NS_IMETHODIMP 1.480 +nsDNSService::Init() 1.481 +{ 1.482 + if (mResolver) 1.483 + return NS_OK; 1.484 + NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED); 1.485 + 1.486 + // prefs 1.487 + uint32_t maxCacheEntries = 400; 1.488 + uint32_t maxCacheLifetime = 120; // seconds 1.489 + uint32_t lifetimeGracePeriod = 60; // seconds 1.490 + bool disableIPv6 = false; 1.491 + bool disablePrefetch = false; 1.492 + bool disableDNS = false; 1.493 + int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT; 1.494 + bool notifyResolution = false; 1.495 + 1.496 + nsAdoptingCString ipv4OnlyDomains; 1.497 + nsAdoptingCString localDomains; 1.498 + 1.499 + // read prefs 1.500 + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); 1.501 + if (prefs) { 1.502 + int32_t val; 1.503 + if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val))) 1.504 + maxCacheEntries = (uint32_t) val; 1.505 + if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val))) 1.506 + maxCacheLifetime = val; 1.507 + if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val))) 1.508 + lifetimeGracePeriod = val; 1.509 + 1.510 + // ASSUMPTION: pref branch does not modify out params on failure 1.511 + prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6); 1.512 + prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains)); 1.513 + prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains)); 1.514 + prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch); 1.515 + 1.516 + // If a manual proxy is in use, disable prefetch implicitly 1.517 + prefs->GetIntPref("network.proxy.type", &proxyType); 1.518 + 1.519 + // If the user wants remote DNS, we should fail any lookups that still 1.520 + // make it here. 1.521 + prefs->GetBoolPref("network.proxy.socks_remote_dns", &disableDNS); 1.522 + 1.523 + prefs->GetBoolPref(kPrefDnsNotifyResolution, ¬ifyResolution); 1.524 + } 1.525 + 1.526 + if (mFirstTime) { 1.527 + mFirstTime = false; 1.528 + 1.529 + // register as prefs observer 1.530 + if (prefs) { 1.531 + prefs->AddObserver(kPrefDnsCacheEntries, this, false); 1.532 + prefs->AddObserver(kPrefDnsCacheExpiration, this, false); 1.533 + prefs->AddObserver(kPrefDnsCacheGrace, this, false); 1.534 + prefs->AddObserver(kPrefIPv4OnlyDomains, this, false); 1.535 + prefs->AddObserver(kPrefDnsLocalDomains, this, false); 1.536 + prefs->AddObserver(kPrefDisableIPv6, this, false); 1.537 + prefs->AddObserver(kPrefDisablePrefetch, this, false); 1.538 + prefs->AddObserver(kPrefDnsNotifyResolution, this, false); 1.539 + 1.540 + // Monitor these to see if there is a change in proxy configuration 1.541 + // If a manual proxy is in use, disable prefetch implicitly 1.542 + prefs->AddObserver("network.proxy.", this, false); 1.543 + } 1.544 + 1.545 + nsresult rv; 1.546 + nsCOMPtr<nsIObserverService> observerService = 1.547 + do_GetService("@mozilla.org/observer-service;1", &rv); 1.548 + if (NS_SUCCEEDED(rv)) { 1.549 + observerService->AddObserver(this, "last-pb-context-exited", false); 1.550 + } 1.551 + } 1.552 + 1.553 + nsDNSPrefetch::Initialize(this); 1.554 + 1.555 + // Don't initialize the resolver if we're in offline mode. 1.556 + // Later on, the IO service will reinitialize us when going online. 1.557 + if (gIOService->IsOffline() && !gIOService->IsComingOnline()) 1.558 + return NS_OK; 1.559 + 1.560 + nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID); 1.561 + 1.562 + nsCOMPtr<nsIObserverService> obs = 1.563 + do_GetService(NS_OBSERVERSERVICE_CONTRACTID); 1.564 + 1.565 + nsRefPtr<nsHostResolver> res; 1.566 + nsresult rv = nsHostResolver::Create(maxCacheEntries, 1.567 + maxCacheLifetime, 1.568 + lifetimeGracePeriod, 1.569 + getter_AddRefs(res)); 1.570 + if (NS_SUCCEEDED(rv)) { 1.571 + // now, set all of our member variables while holding the lock 1.572 + MutexAutoLock lock(mLock); 1.573 + mResolver = res; 1.574 + mIDN = idn; 1.575 + mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership 1.576 + mDisableIPv6 = disableIPv6; 1.577 + mDisableDNS = disableDNS; 1.578 + 1.579 + // Disable prefetching either by explicit preference or if a manual proxy is configured 1.580 + mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL); 1.581 + 1.582 + mLocalDomains.Clear(); 1.583 + if (localDomains) { 1.584 + nsAdoptingString domains; 1.585 + domains.AssignASCII(nsDependentCString(localDomains).get()); 1.586 + nsCharSeparatedTokenizer tokenizer(domains, ',', 1.587 + nsCharSeparatedTokenizerTemplate<>::SEPARATOR_OPTIONAL); 1.588 + 1.589 + while (tokenizer.hasMoreTokens()) { 1.590 + const nsSubstring& domain = tokenizer.nextToken(); 1.591 + mLocalDomains.PutEntry(nsDependentCString(NS_ConvertUTF16toUTF8(domain).get())); 1.592 + } 1.593 + } 1.594 + mNotifyResolution = notifyResolution; 1.595 + if (mNotifyResolution) { 1.596 + mObserverService = 1.597 + new nsMainThreadPtrHolder<nsIObserverService>(obs); 1.598 + } 1.599 + } 1.600 + 1.601 + RegisterWeakMemoryReporter(this); 1.602 + 1.603 + return rv; 1.604 +} 1.605 + 1.606 +NS_IMETHODIMP 1.607 +nsDNSService::Shutdown() 1.608 +{ 1.609 + UnregisterWeakMemoryReporter(this); 1.610 + 1.611 + nsRefPtr<nsHostResolver> res; 1.612 + { 1.613 + MutexAutoLock lock(mLock); 1.614 + res = mResolver; 1.615 + mResolver = nullptr; 1.616 + } 1.617 + if (res) 1.618 + res->Shutdown(); 1.619 + return NS_OK; 1.620 +} 1.621 + 1.622 +NS_IMETHODIMP 1.623 +nsDNSService::GetOffline(bool *offline) 1.624 +{ 1.625 + *offline = mOffline; 1.626 + return NS_OK; 1.627 +} 1.628 + 1.629 +NS_IMETHODIMP 1.630 +nsDNSService::SetOffline(bool offline) 1.631 +{ 1.632 + mOffline = offline; 1.633 + return NS_OK; 1.634 +} 1.635 + 1.636 +NS_IMETHODIMP 1.637 +nsDNSService::GetPrefetchEnabled(bool *outVal) 1.638 +{ 1.639 + *outVal = !mDisablePrefetch; 1.640 + return NS_OK; 1.641 +} 1.642 + 1.643 +NS_IMETHODIMP 1.644 +nsDNSService::SetPrefetchEnabled(bool inVal) 1.645 +{ 1.646 + mDisablePrefetch = !inVal; 1.647 + return NS_OK; 1.648 +} 1.649 + 1.650 + 1.651 +NS_IMETHODIMP 1.652 +nsDNSService::AsyncResolve(const nsACString &hostname, 1.653 + uint32_t flags, 1.654 + nsIDNSListener *listener, 1.655 + nsIEventTarget *target_, 1.656 + nsICancelable **result) 1.657 +{ 1.658 + // grab reference to global host resolver and IDN service. beware 1.659 + // simultaneous shutdown!! 1.660 + nsRefPtr<nsHostResolver> res; 1.661 + nsCOMPtr<nsIIDNService> idn; 1.662 + nsCOMPtr<nsIEventTarget> target = target_; 1.663 + bool localDomain = false; 1.664 + { 1.665 + MutexAutoLock lock(mLock); 1.666 + 1.667 + if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) 1.668 + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; 1.669 + 1.670 + res = mResolver; 1.671 + idn = mIDN; 1.672 + localDomain = mLocalDomains.GetEntry(hostname); 1.673 + } 1.674 + 1.675 + if (mNotifyResolution) { 1.676 + NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService, 1.677 + hostname)); 1.678 + } 1.679 + 1.680 + PRNetAddr tempAddr; 1.681 + if (mDisableDNS) { 1.682 + // Allow IP lookups through, but nothing else. 1.683 + if (PR_StringToNetAddr(hostname.BeginReading(), &tempAddr) != PR_SUCCESS) { 1.684 + return NS_ERROR_UNKNOWN_PROXY_HOST; // XXX: NS_ERROR_NOT_IMPLEMENTED? 1.685 + } 1.686 + } 1.687 + 1.688 + if (!res) 1.689 + return NS_ERROR_OFFLINE; 1.690 + 1.691 + if (mOffline) 1.692 + flags |= RESOLVE_OFFLINE; 1.693 + 1.694 + const nsACString *hostPtr = &hostname; 1.695 + 1.696 + if (localDomain) { 1.697 + hostPtr = &(NS_LITERAL_CSTRING("localhost")); 1.698 + } 1.699 + 1.700 + nsresult rv; 1.701 + nsAutoCString hostACE; 1.702 + if (idn && !IsASCII(*hostPtr)) { 1.703 + if (IsUTF8(*hostPtr) && 1.704 + NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE))) { 1.705 + hostPtr = &hostACE; 1.706 + } else { 1.707 + return NS_ERROR_FAILURE; 1.708 + } 1.709 + } 1.710 + 1.711 + // make sure JS callers get notification on the main thread 1.712 + nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener); 1.713 + if (wrappedListener && !target) { 1.714 + nsCOMPtr<nsIThread> mainThread; 1.715 + NS_GetMainThread(getter_AddRefs(mainThread)); 1.716 + target = do_QueryInterface(mainThread); 1.717 + } 1.718 + 1.719 + if (target) { 1.720 + listener = new DNSListenerProxy(listener, target); 1.721 + } 1.722 + 1.723 + uint16_t af = GetAFForLookup(*hostPtr, flags); 1.724 + 1.725 + nsDNSAsyncRequest *req = 1.726 + new nsDNSAsyncRequest(res, *hostPtr, listener, flags, af); 1.727 + if (!req) 1.728 + return NS_ERROR_OUT_OF_MEMORY; 1.729 + NS_ADDREF(*result = req); 1.730 + 1.731 + MOZ_EVENT_TRACER_NAME_OBJECT(req, hostname.BeginReading()); 1.732 + MOZ_EVENT_TRACER_WAIT(req, "net::dns::lookup"); 1.733 + 1.734 + // addref for resolver; will be released when OnLookupComplete is called. 1.735 + NS_ADDREF(req); 1.736 + rv = res->ResolveHost(req->mHost.get(), flags, af, req); 1.737 + if (NS_FAILED(rv)) { 1.738 + NS_RELEASE(req); 1.739 + NS_RELEASE(*result); 1.740 + } 1.741 + return rv; 1.742 +} 1.743 + 1.744 +NS_IMETHODIMP 1.745 +nsDNSService::CancelAsyncResolve(const nsACString &aHostname, 1.746 + uint32_t aFlags, 1.747 + nsIDNSListener *aListener, 1.748 + nsresult aReason) 1.749 +{ 1.750 + // grab reference to global host resolver and IDN service. beware 1.751 + // simultaneous shutdown!! 1.752 + nsRefPtr<nsHostResolver> res; 1.753 + nsCOMPtr<nsIIDNService> idn; 1.754 + { 1.755 + MutexAutoLock lock(mLock); 1.756 + 1.757 + if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) 1.758 + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; 1.759 + 1.760 + res = mResolver; 1.761 + idn = mIDN; 1.762 + } 1.763 + if (!res) 1.764 + return NS_ERROR_OFFLINE; 1.765 + 1.766 + nsCString hostname(aHostname); 1.767 + 1.768 + nsAutoCString hostACE; 1.769 + if (idn && !IsASCII(aHostname)) { 1.770 + if (IsUTF8(aHostname) && 1.771 + NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE))) { 1.772 + hostname = hostACE; 1.773 + } else { 1.774 + return NS_ERROR_FAILURE; 1.775 + } 1.776 + } 1.777 + 1.778 + uint16_t af = GetAFForLookup(hostname, aFlags); 1.779 + 1.780 + res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason); 1.781 + return NS_OK; 1.782 +} 1.783 + 1.784 +NS_IMETHODIMP 1.785 +nsDNSService::Resolve(const nsACString &hostname, 1.786 + uint32_t flags, 1.787 + nsIDNSRecord **result) 1.788 +{ 1.789 + // grab reference to global host resolver and IDN service. beware 1.790 + // simultaneous shutdown!! 1.791 + nsRefPtr<nsHostResolver> res; 1.792 + nsCOMPtr<nsIIDNService> idn; 1.793 + bool localDomain = false; 1.794 + { 1.795 + MutexAutoLock lock(mLock); 1.796 + res = mResolver; 1.797 + idn = mIDN; 1.798 + localDomain = mLocalDomains.GetEntry(hostname); 1.799 + } 1.800 + 1.801 + if (mNotifyResolution) { 1.802 + NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService, 1.803 + hostname)); 1.804 + } 1.805 + 1.806 + NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE); 1.807 + 1.808 + if (mOffline) 1.809 + flags |= RESOLVE_OFFLINE; 1.810 + 1.811 + PRNetAddr tempAddr; 1.812 + if (mDisableDNS) { 1.813 + // Allow IP lookups through, but nothing else. 1.814 + if (PR_StringToNetAddr(hostname.BeginReading(), &tempAddr) != PR_SUCCESS) { 1.815 + return NS_ERROR_UNKNOWN_PROXY_HOST; // XXX: NS_ERROR_NOT_IMPLEMENTED? 1.816 + } 1.817 + } 1.818 + 1.819 + const nsACString *hostPtr = &hostname; 1.820 + 1.821 + if (localDomain) { 1.822 + hostPtr = &(NS_LITERAL_CSTRING("localhost")); 1.823 + } 1.824 + 1.825 + nsresult rv; 1.826 + nsAutoCString hostACE; 1.827 + if (idn && !IsASCII(*hostPtr)) { 1.828 + if (IsUTF8(*hostPtr) && 1.829 + NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE))) { 1.830 + hostPtr = &hostACE; 1.831 + } else { 1.832 + return NS_ERROR_FAILURE; 1.833 + } 1.834 + } 1.835 + 1.836 + // 1.837 + // sync resolve: since the host resolver only works asynchronously, we need 1.838 + // to use a mutex and a condvar to wait for the result. however, since the 1.839 + // result may be in the resolvers cache, we might get called back recursively 1.840 + // on the same thread. so, our mutex needs to be re-entrant. in other words, 1.841 + // we need to use a monitor! ;-) 1.842 + // 1.843 + 1.844 + PRMonitor *mon = PR_NewMonitor(); 1.845 + if (!mon) 1.846 + return NS_ERROR_OUT_OF_MEMORY; 1.847 + 1.848 + PR_EnterMonitor(mon); 1.849 + nsDNSSyncRequest syncReq(mon); 1.850 + 1.851 + uint16_t af = GetAFForLookup(*hostPtr, flags); 1.852 + 1.853 + rv = res->ResolveHost(PromiseFlatCString(*hostPtr).get(), flags, af, &syncReq); 1.854 + if (NS_SUCCEEDED(rv)) { 1.855 + // wait for result 1.856 + while (!syncReq.mDone) 1.857 + PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); 1.858 + 1.859 + if (NS_FAILED(syncReq.mStatus)) 1.860 + rv = syncReq.mStatus; 1.861 + else { 1.862 + NS_ASSERTION(syncReq.mHostRecord, "no host record"); 1.863 + nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord); 1.864 + if (!rec) 1.865 + rv = NS_ERROR_OUT_OF_MEMORY; 1.866 + else 1.867 + NS_ADDREF(*result = rec); 1.868 + } 1.869 + } 1.870 + 1.871 + PR_ExitMonitor(mon); 1.872 + PR_DestroyMonitor(mon); 1.873 + return rv; 1.874 +} 1.875 + 1.876 +NS_IMETHODIMP 1.877 +nsDNSService::GetMyHostName(nsACString &result) 1.878 +{ 1.879 + char name[100]; 1.880 + if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) { 1.881 + result = name; 1.882 + return NS_OK; 1.883 + } 1.884 + return NS_ERROR_FAILURE; 1.885 +} 1.886 + 1.887 +NS_IMETHODIMP 1.888 +nsDNSService::Observe(nsISupports *subject, const char *topic, const char16_t *data) 1.889 +{ 1.890 + // we are only getting called if a preference has changed. 1.891 + NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 || 1.892 + strcmp(topic, "last-pb-context-exited") == 0, 1.893 + "unexpected observe call"); 1.894 + 1.895 + // 1.896 + // Shutdown and this function are both only called on the UI thread, so we don't 1.897 + // have to worry about mResolver being cleared out from under us. 1.898 + // 1.899 + // NOTE Shutting down and reinitializing the service like this is obviously 1.900 + // suboptimal if Observe gets called several times in a row, but we don't 1.901 + // expect that to be the case. 1.902 + // 1.903 + 1.904 + if (mResolver) { 1.905 + Shutdown(); 1.906 + } 1.907 + Init(); 1.908 + return NS_OK; 1.909 +} 1.910 + 1.911 +uint16_t 1.912 +nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags) 1.913 +{ 1.914 + if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6)) 1.915 + return PR_AF_INET; 1.916 + 1.917 + MutexAutoLock lock(mLock); 1.918 + 1.919 + uint16_t af = PR_AF_UNSPEC; 1.920 + 1.921 + if (!mIPv4OnlyDomains.IsEmpty()) { 1.922 + const char *domain, *domainEnd, *end; 1.923 + uint32_t hostLen, domainLen; 1.924 + 1.925 + // see if host is in one of the IPv4-only domains 1.926 + domain = mIPv4OnlyDomains.BeginReading(); 1.927 + domainEnd = mIPv4OnlyDomains.EndReading(); 1.928 + 1.929 + nsACString::const_iterator hostStart; 1.930 + host.BeginReading(hostStart); 1.931 + hostLen = host.Length(); 1.932 + 1.933 + do { 1.934 + // skip any whitespace 1.935 + while (*domain == ' ' || *domain == '\t') 1.936 + ++domain; 1.937 + 1.938 + // find end of this domain in the string 1.939 + end = strchr(domain, ','); 1.940 + if (!end) 1.941 + end = domainEnd; 1.942 + 1.943 + // to see if the hostname is in the domain, check if the domain 1.944 + // matches the end of the hostname. 1.945 + domainLen = end - domain; 1.946 + if (domainLen && hostLen >= domainLen) { 1.947 + const char *hostTail = hostStart.get() + hostLen - domainLen; 1.948 + if (PL_strncasecmp(domain, hostTail, domainLen) == 0) { 1.949 + // now, make sure either that the hostname is a direct match or 1.950 + // that the hostname begins with a dot. 1.951 + if (hostLen == domainLen || 1.952 + *hostTail == '.' || *(hostTail - 1) == '.') { 1.953 + af = PR_AF_INET; 1.954 + break; 1.955 + } 1.956 + } 1.957 + } 1.958 + 1.959 + domain = end + 1; 1.960 + } while (*end); 1.961 + } 1.962 + 1.963 + if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4)) 1.964 + af = PR_AF_INET6; 1.965 + 1.966 + return af; 1.967 +} 1.968 + 1.969 +NS_IMETHODIMP 1.970 +nsDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args) 1.971 +{ 1.972 + NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED); 1.973 + mResolver->GetDNSCacheEntries(args); 1.974 + return NS_OK; 1.975 +} 1.976 + 1.977 +static size_t 1.978 +SizeOfLocalDomainsEntryExcludingThis(nsCStringHashKey* entry, 1.979 + MallocSizeOf mallocSizeOf, void*) 1.980 +{ 1.981 + return entry->GetKey().SizeOfExcludingThisMustBeUnshared(mallocSizeOf); 1.982 +} 1.983 + 1.984 +size_t 1.985 +nsDNSService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const 1.986 +{ 1.987 + // Measurement of the following members may be added later if DMD finds it 1.988 + // is worthwhile: 1.989 + // - mIDN 1.990 + // - mLock 1.991 + 1.992 + size_t n = mallocSizeOf(this); 1.993 + n += mResolver->SizeOfIncludingThis(mallocSizeOf); 1.994 + n += mIPv4OnlyDomains.SizeOfExcludingThisMustBeUnshared(mallocSizeOf); 1.995 + n += mLocalDomains.SizeOfExcludingThis(SizeOfLocalDomainsEntryExcludingThis, 1.996 + mallocSizeOf); 1.997 + return n; 1.998 +} 1.999 + 1.1000 +MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf) 1.1001 + 1.1002 +NS_IMETHODIMP 1.1003 +nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport, 1.1004 + nsISupports* aData) 1.1005 +{ 1.1006 + return MOZ_COLLECT_REPORT( 1.1007 + "explicit/network/dns-service", KIND_HEAP, UNITS_BYTES, 1.1008 + SizeOfIncludingThis(DNSServiceMallocSizeOf), 1.1009 + "Memory used for the DNS service."); 1.1010 +} 1.1011 +