netwerk/dns/nsDNSService2.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim: set sw=4 ts=8 et tw=80 : */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsDNSService2.h"
michael@0 8 #include "nsIDNSRecord.h"
michael@0 9 #include "nsIDNSListener.h"
michael@0 10 #include "nsICancelable.h"
michael@0 11 #include "nsIPrefService.h"
michael@0 12 #include "nsIPrefBranch.h"
michael@0 13 #include "nsIServiceManager.h"
michael@0 14 #include "nsIXPConnect.h"
michael@0 15 #include "nsProxyRelease.h"
michael@0 16 #include "nsReadableUtils.h"
michael@0 17 #include "nsString.h"
michael@0 18 #include "nsAutoPtr.h"
michael@0 19 #include "nsNetCID.h"
michael@0 20 #include "nsError.h"
michael@0 21 #include "nsDNSPrefetch.h"
michael@0 22 #include "nsThreadUtils.h"
michael@0 23 #include "nsIProtocolProxyService.h"
michael@0 24 #include "prsystem.h"
michael@0 25 #include "prnetdb.h"
michael@0 26 #include "prmon.h"
michael@0 27 #include "prio.h"
michael@0 28 #include "plstr.h"
michael@0 29 #include "nsIOService.h"
michael@0 30 #include "nsCharSeparatedTokenizer.h"
michael@0 31 #include "nsNetAddr.h"
michael@0 32 #include "nsProxyRelease.h"
michael@0 33
michael@0 34 #include "mozilla/Attributes.h"
michael@0 35 #include "mozilla/VisualEventTracer.h"
michael@0 36 #include "mozilla/net/NeckoCommon.h"
michael@0 37 #include "mozilla/net/ChildDNSService.h"
michael@0 38 #include "mozilla/net/DNSListenerProxy.h"
michael@0 39
michael@0 40 using namespace mozilla;
michael@0 41 using namespace mozilla::net;
michael@0 42
michael@0 43 static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries";
michael@0 44 static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
michael@0 45 static const char kPrefDnsCacheGrace[] = "network.dnsCacheExpirationGracePeriod";
michael@0 46 static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains";
michael@0 47 static const char kPrefDisableIPv6[] = "network.dns.disableIPv6";
michael@0 48 static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch";
michael@0 49 static const char kPrefDnsLocalDomains[] = "network.dns.localDomains";
michael@0 50 static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
michael@0 51
michael@0 52 //-----------------------------------------------------------------------------
michael@0 53
michael@0 54 class nsDNSRecord : public nsIDNSRecord
michael@0 55 {
michael@0 56 public:
michael@0 57 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 58 NS_DECL_NSIDNSRECORD
michael@0 59
michael@0 60 nsDNSRecord(nsHostRecord *hostRecord)
michael@0 61 : mHostRecord(hostRecord)
michael@0 62 , mIter(nullptr)
michael@0 63 , mIterGenCnt(-1)
michael@0 64 , mDone(false) {}
michael@0 65
michael@0 66 private:
michael@0 67 virtual ~nsDNSRecord() {}
michael@0 68
michael@0 69 nsRefPtr<nsHostRecord> mHostRecord;
michael@0 70 NetAddrElement *mIter;
michael@0 71 int mIterGenCnt; // the generation count of
michael@0 72 // mHostRecord->addr_info when we
michael@0 73 // start iterating
michael@0 74 bool mDone;
michael@0 75 };
michael@0 76
michael@0 77 NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord)
michael@0 78
michael@0 79 NS_IMETHODIMP
michael@0 80 nsDNSRecord::GetCanonicalName(nsACString &result)
michael@0 81 {
michael@0 82 // this method should only be called if we have a CNAME
michael@0 83 NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME,
michael@0 84 NS_ERROR_NOT_AVAILABLE);
michael@0 85
michael@0 86 // if the record is for an IP address literal, then the canonical
michael@0 87 // host name is the IP address literal.
michael@0 88 const char *cname;
michael@0 89 {
michael@0 90 MutexAutoLock lock(mHostRecord->addr_info_lock);
michael@0 91 if (mHostRecord->addr_info)
michael@0 92 cname = mHostRecord->addr_info->mCanonicalName ?
michael@0 93 mHostRecord->addr_info->mCanonicalName :
michael@0 94 mHostRecord->addr_info->mHostName;
michael@0 95 else
michael@0 96 cname = mHostRecord->host;
michael@0 97 result.Assign(cname);
michael@0 98 }
michael@0 99 return NS_OK;
michael@0 100 }
michael@0 101
michael@0 102 NS_IMETHODIMP
michael@0 103 nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
michael@0 104 {
michael@0 105 if (mDone) {
michael@0 106 return NS_ERROR_NOT_AVAILABLE;
michael@0 107 }
michael@0 108
michael@0 109 mHostRecord->addr_info_lock.Lock();
michael@0 110 if (mHostRecord->addr_info) {
michael@0 111 if (mIterGenCnt != mHostRecord->addr_info_gencnt) {
michael@0 112 // mHostRecord->addr_info has changed, restart the iteration.
michael@0 113 mIter = nullptr;
michael@0 114 mIterGenCnt = mHostRecord->addr_info_gencnt;
michael@0 115 }
michael@0 116
michael@0 117 bool startedFresh = !mIter;
michael@0 118
michael@0 119 do {
michael@0 120 if (!mIter) {
michael@0 121 mIter = mHostRecord->addr_info->mAddresses.getFirst();
michael@0 122 } else {
michael@0 123 mIter = mIter->getNext();
michael@0 124 }
michael@0 125 }
michael@0 126 while (mIter && mHostRecord->Blacklisted(&mIter->mAddress));
michael@0 127
michael@0 128 if (!mIter && startedFresh) {
michael@0 129 // If everything was blacklisted we want to reset the blacklist (and
michael@0 130 // likely relearn it) and return the first address. That is better
michael@0 131 // than nothing.
michael@0 132 mHostRecord->ResetBlacklist();
michael@0 133 mIter = mHostRecord->addr_info->mAddresses.getFirst();
michael@0 134 }
michael@0 135
michael@0 136 if (mIter) {
michael@0 137 memcpy(addr, &mIter->mAddress, sizeof(NetAddr));
michael@0 138 }
michael@0 139
michael@0 140 mHostRecord->addr_info_lock.Unlock();
michael@0 141
michael@0 142 if (!mIter) {
michael@0 143 mDone = true;
michael@0 144 return NS_ERROR_NOT_AVAILABLE;
michael@0 145 }
michael@0 146 }
michael@0 147 else {
michael@0 148 mHostRecord->addr_info_lock.Unlock();
michael@0 149
michael@0 150 if (!mHostRecord->addr) {
michael@0 151 // Both mHostRecord->addr_info and mHostRecord->addr are null.
michael@0 152 // This can happen if mHostRecord->addr_info expired and the
michael@0 153 // attempt to reresolve it failed.
michael@0 154 return NS_ERROR_NOT_AVAILABLE;
michael@0 155 }
michael@0 156 memcpy(addr, mHostRecord->addr, sizeof(NetAddr));
michael@0 157 mDone = true;
michael@0 158 }
michael@0 159
michael@0 160 // set given port
michael@0 161 port = htons(port);
michael@0 162 if (addr->raw.family == AF_INET) {
michael@0 163 addr->inet.port = port;
michael@0 164 }
michael@0 165 else if (addr->raw.family == AF_INET6) {
michael@0 166 addr->inet6.port = port;
michael@0 167 }
michael@0 168
michael@0 169 return NS_OK;
michael@0 170 }
michael@0 171
michael@0 172 NS_IMETHODIMP
michael@0 173 nsDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr * *result)
michael@0 174 {
michael@0 175 NetAddr addr;
michael@0 176 nsresult rv = GetNextAddr(port, &addr);
michael@0 177 if (NS_FAILED(rv)) return rv;
michael@0 178
michael@0 179 NS_ADDREF(*result = new nsNetAddr(&addr));
michael@0 180
michael@0 181 return NS_OK;
michael@0 182 }
michael@0 183
michael@0 184 NS_IMETHODIMP
michael@0 185 nsDNSRecord::GetNextAddrAsString(nsACString &result)
michael@0 186 {
michael@0 187 NetAddr addr;
michael@0 188 nsresult rv = GetNextAddr(0, &addr);
michael@0 189 if (NS_FAILED(rv)) return rv;
michael@0 190
michael@0 191 char buf[kIPv6CStrBufSize];
michael@0 192 if (NetAddrToString(&addr, buf, sizeof(buf))) {
michael@0 193 result.Assign(buf);
michael@0 194 return NS_OK;
michael@0 195 }
michael@0 196 NS_ERROR("NetAddrToString failed unexpectedly");
michael@0 197 return NS_ERROR_FAILURE; // conversion failed for some reason
michael@0 198 }
michael@0 199
michael@0 200 NS_IMETHODIMP
michael@0 201 nsDNSRecord::HasMore(bool *result)
michael@0 202 {
michael@0 203 if (mDone) {
michael@0 204 *result = false;
michael@0 205 return NS_OK;
michael@0 206 }
michael@0 207
michael@0 208 NetAddrElement *iterCopy = mIter;
michael@0 209
michael@0 210 NetAddr addr;
michael@0 211 *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
michael@0 212
michael@0 213 mIter = iterCopy;
michael@0 214 mDone = false;
michael@0 215
michael@0 216 return NS_OK;
michael@0 217 }
michael@0 218
michael@0 219 NS_IMETHODIMP
michael@0 220 nsDNSRecord::Rewind()
michael@0 221 {
michael@0 222 mIter = nullptr;
michael@0 223 mIterGenCnt = -1;
michael@0 224 mDone = false;
michael@0 225 return NS_OK;
michael@0 226 }
michael@0 227
michael@0 228 NS_IMETHODIMP
michael@0 229 nsDNSRecord::ReportUnusable(uint16_t aPort)
michael@0 230 {
michael@0 231 // right now we don't use the port in the blacklist
michael@0 232
michael@0 233 MutexAutoLock lock(mHostRecord->addr_info_lock);
michael@0 234
michael@0 235 // Check that we are using a real addr_info (as opposed to a single
michael@0 236 // constant address), and that the generation count is valid. Otherwise,
michael@0 237 // ignore the report.
michael@0 238
michael@0 239 if (mHostRecord->addr_info &&
michael@0 240 mIterGenCnt == mHostRecord->addr_info_gencnt &&
michael@0 241 mIter) {
michael@0 242 mHostRecord->ReportUnusable(&mIter->mAddress);
michael@0 243 }
michael@0 244
michael@0 245 return NS_OK;
michael@0 246 }
michael@0 247
michael@0 248 //-----------------------------------------------------------------------------
michael@0 249
michael@0 250 class nsDNSAsyncRequest MOZ_FINAL : public nsResolveHostCallback
michael@0 251 , public nsICancelable
michael@0 252 {
michael@0 253 public:
michael@0 254 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 255 NS_DECL_NSICANCELABLE
michael@0 256
michael@0 257 nsDNSAsyncRequest(nsHostResolver *res,
michael@0 258 const nsACString &host,
michael@0 259 nsIDNSListener *listener,
michael@0 260 uint16_t flags,
michael@0 261 uint16_t af)
michael@0 262 : mResolver(res)
michael@0 263 , mHost(host)
michael@0 264 , mListener(listener)
michael@0 265 , mFlags(flags)
michael@0 266 , mAF(af) {}
michael@0 267 ~nsDNSAsyncRequest() {}
michael@0 268
michael@0 269 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
michael@0 270 // Returns TRUE if the DNS listener arg is the same as the member listener
michael@0 271 // Used in Cancellations to remove DNS requests associated with a
michael@0 272 // particular hostname and nsIDNSListener
michael@0 273 bool EqualsAsyncListener(nsIDNSListener *aListener);
michael@0 274
michael@0 275 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const;
michael@0 276
michael@0 277 nsRefPtr<nsHostResolver> mResolver;
michael@0 278 nsCString mHost; // hostname we're resolving
michael@0 279 nsCOMPtr<nsIDNSListener> mListener;
michael@0 280 uint16_t mFlags;
michael@0 281 uint16_t mAF;
michael@0 282 };
michael@0 283
michael@0 284 void
michael@0 285 nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
michael@0 286 nsHostRecord *hostRecord,
michael@0 287 nsresult status)
michael@0 288 {
michael@0 289 // need to have an owning ref when we issue the callback to enable
michael@0 290 // the caller to be able to addref/release multiple times without
michael@0 291 // destroying the record prematurely.
michael@0 292 nsCOMPtr<nsIDNSRecord> rec;
michael@0 293 if (NS_SUCCEEDED(status)) {
michael@0 294 NS_ASSERTION(hostRecord, "no host record");
michael@0 295 rec = new nsDNSRecord(hostRecord);
michael@0 296 if (!rec)
michael@0 297 status = NS_ERROR_OUT_OF_MEMORY;
michael@0 298 }
michael@0 299
michael@0 300 MOZ_EVENT_TRACER_DONE(this, "net::dns::lookup");
michael@0 301
michael@0 302 mListener->OnLookupComplete(this, rec, status);
michael@0 303 mListener = nullptr;
michael@0 304
michael@0 305 // release the reference to ourselves that was added before we were
michael@0 306 // handed off to the host resolver.
michael@0 307 NS_RELEASE_THIS();
michael@0 308 }
michael@0 309
michael@0 310 bool
michael@0 311 nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
michael@0 312 {
michael@0 313 return (aListener == mListener);
michael@0 314 }
michael@0 315
michael@0 316 size_t
michael@0 317 nsDNSAsyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
michael@0 318 {
michael@0 319 size_t n = mallocSizeOf(this);
michael@0 320
michael@0 321 // The following fields aren't measured.
michael@0 322 // - mHost, because it's a non-owning pointer
michael@0 323 // - mResolver, because it's a non-owning pointer
michael@0 324 // - mListener, because it's a non-owning pointer
michael@0 325
michael@0 326 return n;
michael@0 327 }
michael@0 328
michael@0 329 NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
michael@0 330
michael@0 331 NS_IMETHODIMP
michael@0 332 nsDNSAsyncRequest::Cancel(nsresult reason)
michael@0 333 {
michael@0 334 NS_ENSURE_ARG(NS_FAILED(reason));
michael@0 335 mResolver->DetachCallback(mHost.get(), mFlags, mAF, this, reason);
michael@0 336 return NS_OK;
michael@0 337 }
michael@0 338
michael@0 339 //-----------------------------------------------------------------------------
michael@0 340
michael@0 341 class nsDNSSyncRequest : public nsResolveHostCallback
michael@0 342 {
michael@0 343 public:
michael@0 344 nsDNSSyncRequest(PRMonitor *mon)
michael@0 345 : mDone(false)
michael@0 346 , mStatus(NS_OK)
michael@0 347 , mMonitor(mon) {}
michael@0 348 virtual ~nsDNSSyncRequest() {}
michael@0 349
michael@0 350 void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
michael@0 351 bool EqualsAsyncListener(nsIDNSListener *aListener);
michael@0 352 size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const;
michael@0 353
michael@0 354 bool mDone;
michael@0 355 nsresult mStatus;
michael@0 356 nsRefPtr<nsHostRecord> mHostRecord;
michael@0 357
michael@0 358 private:
michael@0 359 PRMonitor *mMonitor;
michael@0 360 };
michael@0 361
michael@0 362 void
michael@0 363 nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver,
michael@0 364 nsHostRecord *hostRecord,
michael@0 365 nsresult status)
michael@0 366 {
michael@0 367 // store results, and wake up nsDNSService::Resolve to process results.
michael@0 368 PR_EnterMonitor(mMonitor);
michael@0 369 mDone = true;
michael@0 370 mStatus = status;
michael@0 371 mHostRecord = hostRecord;
michael@0 372 PR_Notify(mMonitor);
michael@0 373 PR_ExitMonitor(mMonitor);
michael@0 374 }
michael@0 375
michael@0 376 bool
michael@0 377 nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener)
michael@0 378 {
michael@0 379 // Sync request: no listener to compare
michael@0 380 return false;
michael@0 381 }
michael@0 382
michael@0 383 size_t
michael@0 384 nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
michael@0 385 {
michael@0 386 size_t n = mallocSizeOf(this);
michael@0 387
michael@0 388 // The following fields aren't measured.
michael@0 389 // - mHostRecord, because it's a non-owning pointer
michael@0 390
michael@0 391 // Measurement of the following members may be added later if DMD finds it
michael@0 392 // is worthwhile:
michael@0 393 // - mMonitor
michael@0 394
michael@0 395 return n;
michael@0 396 }
michael@0 397
michael@0 398 class NotifyDNSResolution: public nsRunnable
michael@0 399 {
michael@0 400 public:
michael@0 401 NotifyDNSResolution(nsMainThreadPtrHandle<nsIObserverService> &aObs,
michael@0 402 const nsACString &aHostname)
michael@0 403 : mObs(aObs)
michael@0 404 , mHostname(aHostname)
michael@0 405 {
michael@0 406 MOZ_ASSERT(mObs);
michael@0 407 }
michael@0 408
michael@0 409 NS_IMETHOD Run()
michael@0 410 {
michael@0 411 MOZ_ASSERT(NS_IsMainThread());
michael@0 412 mObs->NotifyObservers(nullptr,
michael@0 413 "dns-resolution-request",
michael@0 414 NS_ConvertUTF8toUTF16(mHostname).get());
michael@0 415 return NS_OK;
michael@0 416 }
michael@0 417
michael@0 418 private:
michael@0 419 nsMainThreadPtrHandle<nsIObserverService> mObs;
michael@0 420 nsCString mHostname;
michael@0 421 };
michael@0 422
michael@0 423 //-----------------------------------------------------------------------------
michael@0 424
michael@0 425 nsDNSService::nsDNSService()
michael@0 426 : mLock("nsDNSServer.mLock")
michael@0 427 , mFirstTime(true)
michael@0 428 , mOffline(false)
michael@0 429 {
michael@0 430 }
michael@0 431
michael@0 432 nsDNSService::~nsDNSService()
michael@0 433 {
michael@0 434 }
michael@0 435
michael@0 436 NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver,
michael@0 437 nsIMemoryReporter)
michael@0 438
michael@0 439 /******************************************************************************
michael@0 440 * nsDNSService impl:
michael@0 441 * singleton instance ctor/dtor methods
michael@0 442 ******************************************************************************/
michael@0 443 static nsDNSService *gDNSService;
michael@0 444
michael@0 445 nsIDNSService*
michael@0 446 nsDNSService::GetXPCOMSingleton()
michael@0 447 {
michael@0 448 if (IsNeckoChild()) {
michael@0 449 return ChildDNSService::GetSingleton();
michael@0 450 }
michael@0 451
michael@0 452 return GetSingleton();
michael@0 453 }
michael@0 454
michael@0 455 nsDNSService*
michael@0 456 nsDNSService::GetSingleton()
michael@0 457 {
michael@0 458 NS_ASSERTION(!IsNeckoChild(), "not a parent process");
michael@0 459
michael@0 460 if (gDNSService) {
michael@0 461 NS_ADDREF(gDNSService);
michael@0 462 return gDNSService;
michael@0 463 }
michael@0 464
michael@0 465 gDNSService = new nsDNSService();
michael@0 466 if (gDNSService) {
michael@0 467 NS_ADDREF(gDNSService);
michael@0 468 if (NS_FAILED(gDNSService->Init())) {
michael@0 469 NS_RELEASE(gDNSService);
michael@0 470 }
michael@0 471 }
michael@0 472
michael@0 473 return gDNSService;
michael@0 474 }
michael@0 475
michael@0 476 NS_IMETHODIMP
michael@0 477 nsDNSService::Init()
michael@0 478 {
michael@0 479 if (mResolver)
michael@0 480 return NS_OK;
michael@0 481 NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
michael@0 482
michael@0 483 // prefs
michael@0 484 uint32_t maxCacheEntries = 400;
michael@0 485 uint32_t maxCacheLifetime = 120; // seconds
michael@0 486 uint32_t lifetimeGracePeriod = 60; // seconds
michael@0 487 bool disableIPv6 = false;
michael@0 488 bool disablePrefetch = false;
michael@0 489 bool disableDNS = false;
michael@0 490 int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
michael@0 491 bool notifyResolution = false;
michael@0 492
michael@0 493 nsAdoptingCString ipv4OnlyDomains;
michael@0 494 nsAdoptingCString localDomains;
michael@0 495
michael@0 496 // read prefs
michael@0 497 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 498 if (prefs) {
michael@0 499 int32_t val;
michael@0 500 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheEntries, &val)))
michael@0 501 maxCacheEntries = (uint32_t) val;
michael@0 502 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheExpiration, &val)))
michael@0 503 maxCacheLifetime = val;
michael@0 504 if (NS_SUCCEEDED(prefs->GetIntPref(kPrefDnsCacheGrace, &val)))
michael@0 505 lifetimeGracePeriod = val;
michael@0 506
michael@0 507 // ASSUMPTION: pref branch does not modify out params on failure
michael@0 508 prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
michael@0 509 prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
michael@0 510 prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains));
michael@0 511 prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
michael@0 512
michael@0 513 // If a manual proxy is in use, disable prefetch implicitly
michael@0 514 prefs->GetIntPref("network.proxy.type", &proxyType);
michael@0 515
michael@0 516 // If the user wants remote DNS, we should fail any lookups that still
michael@0 517 // make it here.
michael@0 518 prefs->GetBoolPref("network.proxy.socks_remote_dns", &disableDNS);
michael@0 519
michael@0 520 prefs->GetBoolPref(kPrefDnsNotifyResolution, &notifyResolution);
michael@0 521 }
michael@0 522
michael@0 523 if (mFirstTime) {
michael@0 524 mFirstTime = false;
michael@0 525
michael@0 526 // register as prefs observer
michael@0 527 if (prefs) {
michael@0 528 prefs->AddObserver(kPrefDnsCacheEntries, this, false);
michael@0 529 prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
michael@0 530 prefs->AddObserver(kPrefDnsCacheGrace, this, false);
michael@0 531 prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
michael@0 532 prefs->AddObserver(kPrefDnsLocalDomains, this, false);
michael@0 533 prefs->AddObserver(kPrefDisableIPv6, this, false);
michael@0 534 prefs->AddObserver(kPrefDisablePrefetch, this, false);
michael@0 535 prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
michael@0 536
michael@0 537 // Monitor these to see if there is a change in proxy configuration
michael@0 538 // If a manual proxy is in use, disable prefetch implicitly
michael@0 539 prefs->AddObserver("network.proxy.", this, false);
michael@0 540 }
michael@0 541
michael@0 542 nsresult rv;
michael@0 543 nsCOMPtr<nsIObserverService> observerService =
michael@0 544 do_GetService("@mozilla.org/observer-service;1", &rv);
michael@0 545 if (NS_SUCCEEDED(rv)) {
michael@0 546 observerService->AddObserver(this, "last-pb-context-exited", false);
michael@0 547 }
michael@0 548 }
michael@0 549
michael@0 550 nsDNSPrefetch::Initialize(this);
michael@0 551
michael@0 552 // Don't initialize the resolver if we're in offline mode.
michael@0 553 // Later on, the IO service will reinitialize us when going online.
michael@0 554 if (gIOService->IsOffline() && !gIOService->IsComingOnline())
michael@0 555 return NS_OK;
michael@0 556
michael@0 557 nsCOMPtr<nsIIDNService> idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
michael@0 558
michael@0 559 nsCOMPtr<nsIObserverService> obs =
michael@0 560 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
michael@0 561
michael@0 562 nsRefPtr<nsHostResolver> res;
michael@0 563 nsresult rv = nsHostResolver::Create(maxCacheEntries,
michael@0 564 maxCacheLifetime,
michael@0 565 lifetimeGracePeriod,
michael@0 566 getter_AddRefs(res));
michael@0 567 if (NS_SUCCEEDED(rv)) {
michael@0 568 // now, set all of our member variables while holding the lock
michael@0 569 MutexAutoLock lock(mLock);
michael@0 570 mResolver = res;
michael@0 571 mIDN = idn;
michael@0 572 mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
michael@0 573 mDisableIPv6 = disableIPv6;
michael@0 574 mDisableDNS = disableDNS;
michael@0 575
michael@0 576 // Disable prefetching either by explicit preference or if a manual proxy is configured
michael@0 577 mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
michael@0 578
michael@0 579 mLocalDomains.Clear();
michael@0 580 if (localDomains) {
michael@0 581 nsAdoptingString domains;
michael@0 582 domains.AssignASCII(nsDependentCString(localDomains).get());
michael@0 583 nsCharSeparatedTokenizer tokenizer(domains, ',',
michael@0 584 nsCharSeparatedTokenizerTemplate<>::SEPARATOR_OPTIONAL);
michael@0 585
michael@0 586 while (tokenizer.hasMoreTokens()) {
michael@0 587 const nsSubstring& domain = tokenizer.nextToken();
michael@0 588 mLocalDomains.PutEntry(nsDependentCString(NS_ConvertUTF16toUTF8(domain).get()));
michael@0 589 }
michael@0 590 }
michael@0 591 mNotifyResolution = notifyResolution;
michael@0 592 if (mNotifyResolution) {
michael@0 593 mObserverService =
michael@0 594 new nsMainThreadPtrHolder<nsIObserverService>(obs);
michael@0 595 }
michael@0 596 }
michael@0 597
michael@0 598 RegisterWeakMemoryReporter(this);
michael@0 599
michael@0 600 return rv;
michael@0 601 }
michael@0 602
michael@0 603 NS_IMETHODIMP
michael@0 604 nsDNSService::Shutdown()
michael@0 605 {
michael@0 606 UnregisterWeakMemoryReporter(this);
michael@0 607
michael@0 608 nsRefPtr<nsHostResolver> res;
michael@0 609 {
michael@0 610 MutexAutoLock lock(mLock);
michael@0 611 res = mResolver;
michael@0 612 mResolver = nullptr;
michael@0 613 }
michael@0 614 if (res)
michael@0 615 res->Shutdown();
michael@0 616 return NS_OK;
michael@0 617 }
michael@0 618
michael@0 619 NS_IMETHODIMP
michael@0 620 nsDNSService::GetOffline(bool *offline)
michael@0 621 {
michael@0 622 *offline = mOffline;
michael@0 623 return NS_OK;
michael@0 624 }
michael@0 625
michael@0 626 NS_IMETHODIMP
michael@0 627 nsDNSService::SetOffline(bool offline)
michael@0 628 {
michael@0 629 mOffline = offline;
michael@0 630 return NS_OK;
michael@0 631 }
michael@0 632
michael@0 633 NS_IMETHODIMP
michael@0 634 nsDNSService::GetPrefetchEnabled(bool *outVal)
michael@0 635 {
michael@0 636 *outVal = !mDisablePrefetch;
michael@0 637 return NS_OK;
michael@0 638 }
michael@0 639
michael@0 640 NS_IMETHODIMP
michael@0 641 nsDNSService::SetPrefetchEnabled(bool inVal)
michael@0 642 {
michael@0 643 mDisablePrefetch = !inVal;
michael@0 644 return NS_OK;
michael@0 645 }
michael@0 646
michael@0 647
michael@0 648 NS_IMETHODIMP
michael@0 649 nsDNSService::AsyncResolve(const nsACString &hostname,
michael@0 650 uint32_t flags,
michael@0 651 nsIDNSListener *listener,
michael@0 652 nsIEventTarget *target_,
michael@0 653 nsICancelable **result)
michael@0 654 {
michael@0 655 // grab reference to global host resolver and IDN service. beware
michael@0 656 // simultaneous shutdown!!
michael@0 657 nsRefPtr<nsHostResolver> res;
michael@0 658 nsCOMPtr<nsIIDNService> idn;
michael@0 659 nsCOMPtr<nsIEventTarget> target = target_;
michael@0 660 bool localDomain = false;
michael@0 661 {
michael@0 662 MutexAutoLock lock(mLock);
michael@0 663
michael@0 664 if (mDisablePrefetch && (flags & RESOLVE_SPECULATE))
michael@0 665 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
michael@0 666
michael@0 667 res = mResolver;
michael@0 668 idn = mIDN;
michael@0 669 localDomain = mLocalDomains.GetEntry(hostname);
michael@0 670 }
michael@0 671
michael@0 672 if (mNotifyResolution) {
michael@0 673 NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService,
michael@0 674 hostname));
michael@0 675 }
michael@0 676
michael@0 677 PRNetAddr tempAddr;
michael@0 678 if (mDisableDNS) {
michael@0 679 // Allow IP lookups through, but nothing else.
michael@0 680 if (PR_StringToNetAddr(hostname.BeginReading(), &tempAddr) != PR_SUCCESS) {
michael@0 681 return NS_ERROR_UNKNOWN_PROXY_HOST; // XXX: NS_ERROR_NOT_IMPLEMENTED?
michael@0 682 }
michael@0 683 }
michael@0 684
michael@0 685 if (!res)
michael@0 686 return NS_ERROR_OFFLINE;
michael@0 687
michael@0 688 if (mOffline)
michael@0 689 flags |= RESOLVE_OFFLINE;
michael@0 690
michael@0 691 const nsACString *hostPtr = &hostname;
michael@0 692
michael@0 693 if (localDomain) {
michael@0 694 hostPtr = &(NS_LITERAL_CSTRING("localhost"));
michael@0 695 }
michael@0 696
michael@0 697 nsresult rv;
michael@0 698 nsAutoCString hostACE;
michael@0 699 if (idn && !IsASCII(*hostPtr)) {
michael@0 700 if (IsUTF8(*hostPtr) &&
michael@0 701 NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE))) {
michael@0 702 hostPtr = &hostACE;
michael@0 703 } else {
michael@0 704 return NS_ERROR_FAILURE;
michael@0 705 }
michael@0 706 }
michael@0 707
michael@0 708 // make sure JS callers get notification on the main thread
michael@0 709 nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
michael@0 710 if (wrappedListener && !target) {
michael@0 711 nsCOMPtr<nsIThread> mainThread;
michael@0 712 NS_GetMainThread(getter_AddRefs(mainThread));
michael@0 713 target = do_QueryInterface(mainThread);
michael@0 714 }
michael@0 715
michael@0 716 if (target) {
michael@0 717 listener = new DNSListenerProxy(listener, target);
michael@0 718 }
michael@0 719
michael@0 720 uint16_t af = GetAFForLookup(*hostPtr, flags);
michael@0 721
michael@0 722 nsDNSAsyncRequest *req =
michael@0 723 new nsDNSAsyncRequest(res, *hostPtr, listener, flags, af);
michael@0 724 if (!req)
michael@0 725 return NS_ERROR_OUT_OF_MEMORY;
michael@0 726 NS_ADDREF(*result = req);
michael@0 727
michael@0 728 MOZ_EVENT_TRACER_NAME_OBJECT(req, hostname.BeginReading());
michael@0 729 MOZ_EVENT_TRACER_WAIT(req, "net::dns::lookup");
michael@0 730
michael@0 731 // addref for resolver; will be released when OnLookupComplete is called.
michael@0 732 NS_ADDREF(req);
michael@0 733 rv = res->ResolveHost(req->mHost.get(), flags, af, req);
michael@0 734 if (NS_FAILED(rv)) {
michael@0 735 NS_RELEASE(req);
michael@0 736 NS_RELEASE(*result);
michael@0 737 }
michael@0 738 return rv;
michael@0 739 }
michael@0 740
michael@0 741 NS_IMETHODIMP
michael@0 742 nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
michael@0 743 uint32_t aFlags,
michael@0 744 nsIDNSListener *aListener,
michael@0 745 nsresult aReason)
michael@0 746 {
michael@0 747 // grab reference to global host resolver and IDN service. beware
michael@0 748 // simultaneous shutdown!!
michael@0 749 nsRefPtr<nsHostResolver> res;
michael@0 750 nsCOMPtr<nsIIDNService> idn;
michael@0 751 {
michael@0 752 MutexAutoLock lock(mLock);
michael@0 753
michael@0 754 if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE))
michael@0 755 return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
michael@0 756
michael@0 757 res = mResolver;
michael@0 758 idn = mIDN;
michael@0 759 }
michael@0 760 if (!res)
michael@0 761 return NS_ERROR_OFFLINE;
michael@0 762
michael@0 763 nsCString hostname(aHostname);
michael@0 764
michael@0 765 nsAutoCString hostACE;
michael@0 766 if (idn && !IsASCII(aHostname)) {
michael@0 767 if (IsUTF8(aHostname) &&
michael@0 768 NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE))) {
michael@0 769 hostname = hostACE;
michael@0 770 } else {
michael@0 771 return NS_ERROR_FAILURE;
michael@0 772 }
michael@0 773 }
michael@0 774
michael@0 775 uint16_t af = GetAFForLookup(hostname, aFlags);
michael@0 776
michael@0 777 res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason);
michael@0 778 return NS_OK;
michael@0 779 }
michael@0 780
michael@0 781 NS_IMETHODIMP
michael@0 782 nsDNSService::Resolve(const nsACString &hostname,
michael@0 783 uint32_t flags,
michael@0 784 nsIDNSRecord **result)
michael@0 785 {
michael@0 786 // grab reference to global host resolver and IDN service. beware
michael@0 787 // simultaneous shutdown!!
michael@0 788 nsRefPtr<nsHostResolver> res;
michael@0 789 nsCOMPtr<nsIIDNService> idn;
michael@0 790 bool localDomain = false;
michael@0 791 {
michael@0 792 MutexAutoLock lock(mLock);
michael@0 793 res = mResolver;
michael@0 794 idn = mIDN;
michael@0 795 localDomain = mLocalDomains.GetEntry(hostname);
michael@0 796 }
michael@0 797
michael@0 798 if (mNotifyResolution) {
michael@0 799 NS_DispatchToMainThread(new NotifyDNSResolution(mObserverService,
michael@0 800 hostname));
michael@0 801 }
michael@0 802
michael@0 803 NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
michael@0 804
michael@0 805 if (mOffline)
michael@0 806 flags |= RESOLVE_OFFLINE;
michael@0 807
michael@0 808 PRNetAddr tempAddr;
michael@0 809 if (mDisableDNS) {
michael@0 810 // Allow IP lookups through, but nothing else.
michael@0 811 if (PR_StringToNetAddr(hostname.BeginReading(), &tempAddr) != PR_SUCCESS) {
michael@0 812 return NS_ERROR_UNKNOWN_PROXY_HOST; // XXX: NS_ERROR_NOT_IMPLEMENTED?
michael@0 813 }
michael@0 814 }
michael@0 815
michael@0 816 const nsACString *hostPtr = &hostname;
michael@0 817
michael@0 818 if (localDomain) {
michael@0 819 hostPtr = &(NS_LITERAL_CSTRING("localhost"));
michael@0 820 }
michael@0 821
michael@0 822 nsresult rv;
michael@0 823 nsAutoCString hostACE;
michael@0 824 if (idn && !IsASCII(*hostPtr)) {
michael@0 825 if (IsUTF8(*hostPtr) &&
michael@0 826 NS_SUCCEEDED(idn->ConvertUTF8toACE(*hostPtr, hostACE))) {
michael@0 827 hostPtr = &hostACE;
michael@0 828 } else {
michael@0 829 return NS_ERROR_FAILURE;
michael@0 830 }
michael@0 831 }
michael@0 832
michael@0 833 //
michael@0 834 // sync resolve: since the host resolver only works asynchronously, we need
michael@0 835 // to use a mutex and a condvar to wait for the result. however, since the
michael@0 836 // result may be in the resolvers cache, we might get called back recursively
michael@0 837 // on the same thread. so, our mutex needs to be re-entrant. in other words,
michael@0 838 // we need to use a monitor! ;-)
michael@0 839 //
michael@0 840
michael@0 841 PRMonitor *mon = PR_NewMonitor();
michael@0 842 if (!mon)
michael@0 843 return NS_ERROR_OUT_OF_MEMORY;
michael@0 844
michael@0 845 PR_EnterMonitor(mon);
michael@0 846 nsDNSSyncRequest syncReq(mon);
michael@0 847
michael@0 848 uint16_t af = GetAFForLookup(*hostPtr, flags);
michael@0 849
michael@0 850 rv = res->ResolveHost(PromiseFlatCString(*hostPtr).get(), flags, af, &syncReq);
michael@0 851 if (NS_SUCCEEDED(rv)) {
michael@0 852 // wait for result
michael@0 853 while (!syncReq.mDone)
michael@0 854 PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
michael@0 855
michael@0 856 if (NS_FAILED(syncReq.mStatus))
michael@0 857 rv = syncReq.mStatus;
michael@0 858 else {
michael@0 859 NS_ASSERTION(syncReq.mHostRecord, "no host record");
michael@0 860 nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord);
michael@0 861 if (!rec)
michael@0 862 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 863 else
michael@0 864 NS_ADDREF(*result = rec);
michael@0 865 }
michael@0 866 }
michael@0 867
michael@0 868 PR_ExitMonitor(mon);
michael@0 869 PR_DestroyMonitor(mon);
michael@0 870 return rv;
michael@0 871 }
michael@0 872
michael@0 873 NS_IMETHODIMP
michael@0 874 nsDNSService::GetMyHostName(nsACString &result)
michael@0 875 {
michael@0 876 char name[100];
michael@0 877 if (PR_GetSystemInfo(PR_SI_HOSTNAME, name, sizeof(name)) == PR_SUCCESS) {
michael@0 878 result = name;
michael@0 879 return NS_OK;
michael@0 880 }
michael@0 881 return NS_ERROR_FAILURE;
michael@0 882 }
michael@0 883
michael@0 884 NS_IMETHODIMP
michael@0 885 nsDNSService::Observe(nsISupports *subject, const char *topic, const char16_t *data)
michael@0 886 {
michael@0 887 // we are only getting called if a preference has changed.
michael@0 888 NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0 ||
michael@0 889 strcmp(topic, "last-pb-context-exited") == 0,
michael@0 890 "unexpected observe call");
michael@0 891
michael@0 892 //
michael@0 893 // Shutdown and this function are both only called on the UI thread, so we don't
michael@0 894 // have to worry about mResolver being cleared out from under us.
michael@0 895 //
michael@0 896 // NOTE Shutting down and reinitializing the service like this is obviously
michael@0 897 // suboptimal if Observe gets called several times in a row, but we don't
michael@0 898 // expect that to be the case.
michael@0 899 //
michael@0 900
michael@0 901 if (mResolver) {
michael@0 902 Shutdown();
michael@0 903 }
michael@0 904 Init();
michael@0 905 return NS_OK;
michael@0 906 }
michael@0 907
michael@0 908 uint16_t
michael@0 909 nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags)
michael@0 910 {
michael@0 911 if (mDisableIPv6 || (flags & RESOLVE_DISABLE_IPV6))
michael@0 912 return PR_AF_INET;
michael@0 913
michael@0 914 MutexAutoLock lock(mLock);
michael@0 915
michael@0 916 uint16_t af = PR_AF_UNSPEC;
michael@0 917
michael@0 918 if (!mIPv4OnlyDomains.IsEmpty()) {
michael@0 919 const char *domain, *domainEnd, *end;
michael@0 920 uint32_t hostLen, domainLen;
michael@0 921
michael@0 922 // see if host is in one of the IPv4-only domains
michael@0 923 domain = mIPv4OnlyDomains.BeginReading();
michael@0 924 domainEnd = mIPv4OnlyDomains.EndReading();
michael@0 925
michael@0 926 nsACString::const_iterator hostStart;
michael@0 927 host.BeginReading(hostStart);
michael@0 928 hostLen = host.Length();
michael@0 929
michael@0 930 do {
michael@0 931 // skip any whitespace
michael@0 932 while (*domain == ' ' || *domain == '\t')
michael@0 933 ++domain;
michael@0 934
michael@0 935 // find end of this domain in the string
michael@0 936 end = strchr(domain, ',');
michael@0 937 if (!end)
michael@0 938 end = domainEnd;
michael@0 939
michael@0 940 // to see if the hostname is in the domain, check if the domain
michael@0 941 // matches the end of the hostname.
michael@0 942 domainLen = end - domain;
michael@0 943 if (domainLen && hostLen >= domainLen) {
michael@0 944 const char *hostTail = hostStart.get() + hostLen - domainLen;
michael@0 945 if (PL_strncasecmp(domain, hostTail, domainLen) == 0) {
michael@0 946 // now, make sure either that the hostname is a direct match or
michael@0 947 // that the hostname begins with a dot.
michael@0 948 if (hostLen == domainLen ||
michael@0 949 *hostTail == '.' || *(hostTail - 1) == '.') {
michael@0 950 af = PR_AF_INET;
michael@0 951 break;
michael@0 952 }
michael@0 953 }
michael@0 954 }
michael@0 955
michael@0 956 domain = end + 1;
michael@0 957 } while (*end);
michael@0 958 }
michael@0 959
michael@0 960 if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4))
michael@0 961 af = PR_AF_INET6;
michael@0 962
michael@0 963 return af;
michael@0 964 }
michael@0 965
michael@0 966 NS_IMETHODIMP
michael@0 967 nsDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
michael@0 968 {
michael@0 969 NS_ENSURE_TRUE(mResolver, NS_ERROR_NOT_INITIALIZED);
michael@0 970 mResolver->GetDNSCacheEntries(args);
michael@0 971 return NS_OK;
michael@0 972 }
michael@0 973
michael@0 974 static size_t
michael@0 975 SizeOfLocalDomainsEntryExcludingThis(nsCStringHashKey* entry,
michael@0 976 MallocSizeOf mallocSizeOf, void*)
michael@0 977 {
michael@0 978 return entry->GetKey().SizeOfExcludingThisMustBeUnshared(mallocSizeOf);
michael@0 979 }
michael@0 980
michael@0 981 size_t
michael@0 982 nsDNSService::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 983 {
michael@0 984 // Measurement of the following members may be added later if DMD finds it
michael@0 985 // is worthwhile:
michael@0 986 // - mIDN
michael@0 987 // - mLock
michael@0 988
michael@0 989 size_t n = mallocSizeOf(this);
michael@0 990 n += mResolver->SizeOfIncludingThis(mallocSizeOf);
michael@0 991 n += mIPv4OnlyDomains.SizeOfExcludingThisMustBeUnshared(mallocSizeOf);
michael@0 992 n += mLocalDomains.SizeOfExcludingThis(SizeOfLocalDomainsEntryExcludingThis,
michael@0 993 mallocSizeOf);
michael@0 994 return n;
michael@0 995 }
michael@0 996
michael@0 997 MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf)
michael@0 998
michael@0 999 NS_IMETHODIMP
michael@0 1000 nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 1001 nsISupports* aData)
michael@0 1002 {
michael@0 1003 return MOZ_COLLECT_REPORT(
michael@0 1004 "explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
michael@0 1005 SizeOfIncludingThis(DNSServiceMallocSizeOf),
michael@0 1006 "Memory used for the DNS service.");
michael@0 1007 }
michael@0 1008

mercurial