Bug 1570732 - Skip trr when parental control is enabled r=dragana
authorKershaw Chang <kershaw@mozilla.com>
Fri, 23 Aug 2019 21:17:15 +0000
changeset 489809 f421b779735281007fd34681fdb4f9a3358e234b
parent 489808 045217bb047625f7ba59febd30280d5adb2a02e0
child 489810 2e351321e147ae5e5bc8e9d94ac78bab0b0d1530
push id93578
push userkjang@mozilla.com
push dateSun, 25 Aug 2019 18:32:53 +0000
treeherderautoland@f421b7797352 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1570732
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1570732 - Skip trr when parental control is enabled r=dragana Differential Revision: https://phabricator.services.mozilla.com/D42489
modules/libpref/init/all.js
netwerk/dns/ChildDNSService.cpp
netwerk/dns/TRRService.cpp
netwerk/dns/TRRService.h
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsHostResolver.cpp
netwerk/dns/nsHostResolver.h
netwerk/dns/nsIDNSService.idl
netwerk/test/unit/test_trr.js
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2081,16 +2081,19 @@ pref("network.dns.forceResolve", "");
 
 // Contols whether or not "localhost" should resolve when offline
 pref("network.dns.offline-localhost", true);
 
 // Defines how much longer resolver threads should stay idle before are shut down.
 // A negative value will keep the thread alive forever.
 pref("network.dns.resolver-thread-extra-idle-time-seconds", 60);
 
+// Whether to disable TRR when parental control is enabled.
+pref("network.dns.skipTRR-when-parental-control-enabled", true);
+
 // The maximum allowed length for a URL - 1MB default
 pref("network.standard-url.max-length", 1048576);
 
 // Whether nsIURI.host/.hostname/.spec should return a punycode string
 // If set to false we will revert to previous behaviour and return a unicode string.
 pref("network.standard-url.punycode-host", true);
 
 // Idle timeout for ftp control connections - 5 minute default
--- a/netwerk/dns/ChildDNSService.cpp
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -277,16 +277,21 @@ ChildDNSService::GetDNSCacheEntries(
   // unless we start keeping per-app DNS caches).
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
 ChildDNSService::ClearCache(bool aTrrToo) { return NS_ERROR_NOT_AVAILABLE; }
 
 NS_IMETHODIMP
+ChildDNSService::ReloadParentalControlEnabled() {
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
 ChildDNSService::GetMyHostName(nsACString& result) {
   // TODO: get value from parent during PNecko construction?
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 void ChildDNSService::NotifyRequestDone(DNSRequestChild* aDnsRequest) {
   // We need the original flags and listener for the pending requests hash.
   uint32_t originalFlags = aDnsRequest->mFlags & ~RESOLVE_OFFLINE;
--- a/netwerk/dns/TRRService.cpp
+++ b/netwerk/dns/TRRService.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsICaptivePortalService.h"
+#include "nsIParentalControlsService.h"
 #include "nsIObserverService.h"
 #include "nsIURIMutator.h"
 #include "nsNetUtil.h"
 #include "nsStandardURL.h"
 #include "TRR.h"
 #include "TRRService.h"
 
 #include "mozilla/Preferences.h"
@@ -45,17 +46,18 @@ TRRService::TRRService()
       mRfc1918(false),
       mCaptiveIsPassed(false),
       mUseGET(false),
       mDisableECS(true),
       mDisableAfterFails(5),
       mClearTRRBLStorage(false),
       mConfirmationState(CONFIRM_INIT),
       mRetryConfirmInterval(1000),
-      mTRRFailures(0) {
+      mTRRFailures(0),
+      mParentalControlEnabled(false) {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
 }
 
 nsresult TRRService::Init() {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   if (mInitialized) {
     return NS_OK;
   }
@@ -85,24 +87,36 @@ nsresult TRRService::Init() {
     if ((captiveState == nsICaptivePortalService::UNLOCKED_PORTAL) ||
         (captiveState == nsICaptivePortalService::NOT_CAPTIVE)) {
       mCaptiveIsPassed = true;
     }
     LOG(("TRRService::Init mCaptiveState=%d mCaptiveIsPassed=%d\n",
          captiveState, (int)mCaptiveIsPassed));
   }
 
+  GetParentalControlEnabledInternal();
+
   ReadPrefs(nullptr);
 
   gTRRService = this;
 
   LOG(("Initialized TRRService\n"));
   return NS_OK;
 }
 
+void TRRService::GetParentalControlEnabledInternal() {
+  nsCOMPtr<nsIParentalControlsService> pc =
+      do_CreateInstance("@mozilla.org/parental-controls-service;1");
+  if (pc) {
+    pc->GetParentalControlsEnabled(&mParentalControlEnabled);
+    LOG(("TRRService::GetParentalControlEnabledInternal=%d\n",
+         mParentalControlEnabled));
+  }
+}
+
 bool TRRService::Enabled() {
   if (mConfirmationState == CONFIRM_INIT &&
       (!mWaitForCaptive || mCaptiveIsPassed || (mMode == MODE_TRRONLY))) {
     LOG(("TRRService::Enabled => CONFIRM_TRYING\n"));
     mConfirmationState = CONFIRM_TRYING;
   }
 
   if (mConfirmationState == CONFIRM_TRYING) {
--- a/netwerk/dns/TRRService.h
+++ b/netwerk/dns/TRRService.h
@@ -7,16 +7,17 @@
 #define TRRService_h_
 
 #include "mozilla/Atomics.h"
 #include "mozilla/DataStorage.h"
 #include "nsHostResolver.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 
+class nsDNSService;
 class nsIPrefBranch;
 
 namespace mozilla {
 namespace net {
 
 class TRRService : public nsIObserver,
                    public nsITimerCallback,
                    public nsSupportsWeakReference,
@@ -54,23 +55,26 @@ class TRRService : public nsIObserver,
   bool IsTRRBlacklisted(const nsACString& aHost,
                         const nsACString& aOriginSuffix, bool aPrivateBrowsing,
                         bool aParentsToo);
   bool IsExcludedFromTRR(const nsACString& aHost);
 
   bool MaybeBootstrap(const nsACString& possible, nsACString& result);
   enum TrrOkay { OKAY_NORMAL = 0, OKAY_TIMEOUT = 1, OKAY_BAD = 2 };
   void TRRIsOkay(enum TrrOkay aReason);
+  bool ParentalControlEnabled() const { return mParentalControlEnabled; }
 
  private:
   virtual ~TRRService();
   nsresult ReadPrefs(const char* name);
   void GetPrefBranch(nsIPrefBranch** result);
   void MaybeConfirm();
   void MaybeConfirm_locked();
+  friend class ::nsDNSService;
+  void GetParentalControlEnabledInternal();
 
   bool mInitialized;
   Atomic<uint32_t, Relaxed> mMode;
   Atomic<uint32_t, Relaxed> mTRRBlacklistExpireTime;
   Atomic<uint32_t, Relaxed> mTRRTimeout;
 
   Mutex mLock;
 
@@ -110,16 +114,17 @@ class TRRService : public nsIObserver,
     CONFIRM_OK = 2,
     CONFIRM_FAILED = 3
   };
   Atomic<ConfirmationState, Relaxed> mConfirmationState;
   RefPtr<TRR> mConfirmer;
   nsCOMPtr<nsITimer> mRetryConfirmTimer;
   uint32_t mRetryConfirmInterval;  // milliseconds until retry
   Atomic<uint32_t, Relaxed> mTRRFailures;
+  bool mParentalControlEnabled;
 };
 
 extern TRRService* gTRRService;
 
 }  // namespace net
 }  // namespace mozilla
 
 #endif  // TRRService_h_
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -1201,16 +1201,24 @@ nsDNSService::GetDNSCacheEntries(
 }
 
 NS_IMETHODIMP
 nsDNSService::ClearCache(bool aTrrToo) {
   mResolver->FlushCache(aTrrToo);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDNSService::ReloadParentalControlEnabled() {
+  if (mTrrService) {
+    mTrrService->GetParentalControlEnabledInternal();
+  }
+  return NS_OK;
+}
+
 size_t nsDNSService::SizeOfIncludingThis(
     mozilla::MallocSizeOf mallocSizeOf) const {
   // Measurement of the following members may be added later if DMD finds it
   // is worthwhile:
   // - mIDN
   // - mLock
 
   size_t n = mallocSizeOf(this);
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -542,16 +542,18 @@ void TypeHostRecord::Cancel() {
 }
 
 //----------------------------------------------------------------------------
 
 static const char kPrefGetTtl[] = "network.dns.get-ttl";
 static const char kPrefNativeIsLocalhost[] = "network.dns.native-is-localhost";
 static const char kPrefThreadIdleTime[] =
     "network.dns.resolver-thread-extra-idle-time-seconds";
+static const char kPrefSkipTRRParentalControl[] =
+    "network.dns.skipTRR-when-parental-control-enabled";
 static bool sGetTtlEnabled = false;
 mozilla::Atomic<bool, mozilla::Relaxed> gNativeIsLocalhost;
 
 static void DnsPrefChanged(const char* aPref, nsHostResolver* aSelf) {
   MOZ_ASSERT(NS_IsMainThread(),
              "Should be getting pref changed notification on main thread!");
 
   MOZ_ASSERT(aSelf);
@@ -571,16 +573,17 @@ nsHostResolver::nsHostResolver(uint32_t 
                                uint32_t defaultCacheEntryLifetime,
                                uint32_t defaultGracePeriod)
     : mMaxCacheEntries(maxCacheEntries),
       mDefaultCacheLifetime(defaultCacheEntryLifetime),
       mDefaultGracePeriod(defaultGracePeriod),
       mLock("nsHostResolver.mLock"),
       mIdleTaskCV(mLock, "nsHostResolver.mIdleTaskCV"),
       mEvictionQSize(0),
+      mSkipTRRWhenParentalControlEnabled(true),
       mShutdown(true),
       mNumIdleTasks(0),
       mActiveTaskCount(0),
       mActiveAnyThreadCount(0),
       mPendingCount(0) {
   mCreationTime = PR_Now();
 
   mLongIdleTimeout = TimeDuration::FromSeconds(LongIdleTimeoutSeconds);
@@ -594,16 +597,18 @@ nsresult nsHostResolver::Init() {
   if (NS_FAILED(GetAddrInfoInit())) {
     return NS_ERROR_FAILURE;
   }
 
   LOG(("nsHostResolver::Init this=%p", this));
 
   mShutdown = false;
   mNCS = NetworkConnectivityService::GetSingleton();
+  mSkipTRRWhenParentalControlEnabled =
+      Preferences::GetBool(kPrefSkipTRRParentalControl, true);
 
   // The preferences probably haven't been loaded from the disk yet, so we
   // need to register a callback that will set up the experiment once they
   // are. We also need to explicitly set a value for the props otherwise the
   // callback won't be called.
   {
     DebugOnly<nsresult> rv = Preferences::RegisterCallbackAndCall(
         &DnsPrefChanged, kPrefGetTtl, this);
@@ -1391,23 +1396,25 @@ nsresult nsHostResolver::NameLookup(nsHo
     addrRec->mTRRUsed = false;
     addrRec->mNativeSuccess = false;
     addrRec->mTRRSuccess = 0;
     addrRec->mDidCallbacks = false;
     addrRec->mTrrAUsed = AddrHostRecord::INIT;
     addrRec->mTrrAAAAUsed = AddrHostRecord::INIT;
   }
 
-  // For domains that are excluded from TRR we fallback to NativeLookup.
-  // This happens even in MODE_TRRONLY.
-  // By default localhost and local are excluded (so we cover *.local hosts)
-  // See the network.trr.excluded-domains pref.
+  // For domains that are excluded from TRR or when parental control is enabled,
+  // we fallback to NativeLookup. This happens even in MODE_TRRONLY. By default
+  // localhost and local are excluded (so we cover *.local hosts) See the
+  // network.trr.excluded-domains pref.
   bool skipTRR = true;
   if (gTRRService) {
-    skipTRR = gTRRService->IsExcludedFromTRR(rec->host);
+    skipTRR = gTRRService->IsExcludedFromTRR(rec->host) ||
+              (mSkipTRRWhenParentalControlEnabled &&
+               gTRRService->ParentalControlEnabled());
   }
 
   if (rec->flags & RES_DISABLE_TRR) {
     if (mode == MODE_TRRONLY && !skipTRR) {
       return rv;
     }
     mode = MODE_NATIVEONLY;
   }
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -546,16 +546,17 @@ class nsHostResolver : public nsISupport
   mozilla::LinkedList<RefPtr<nsHostRecord>> mHighQ;
   mozilla::LinkedList<RefPtr<nsHostRecord>> mMediumQ;
   mozilla::LinkedList<RefPtr<nsHostRecord>> mLowQ;
   mozilla::LinkedList<RefPtr<nsHostRecord>> mEvictionQ;
   uint32_t mEvictionQSize;
   PRTime mCreationTime;
   mozilla::TimeDuration mLongIdleTimeout;
   mozilla::TimeDuration mShortIdleTimeout;
+  bool mSkipTRRWhenParentalControlEnabled;
 
   RefPtr<nsIThreadPool> mResolverThreads;
   RefPtr<mozilla::net::NetworkConnectivityService> mNCS;
 
   mozilla::Atomic<bool> mShutdown;
   mozilla::Atomic<uint32_t> mNumIdleTasks;
   mozilla::Atomic<uint32_t> mActiveTaskCount;
   mozilla::Atomic<uint32_t> mActiveAnyThreadCount;
--- a/netwerk/dns/nsIDNSService.idl
+++ b/netwerk/dns/nsIDNSService.idl
@@ -219,16 +219,22 @@ interface nsIDNSService : nsISupports
      *        If true we will clear TRR cached entries too. Since these
      *        are resolved remotely it's not necessary to clear them when
      *        the network status changes, but it's sometimes useful to do so
      *        for tests or other situations.
      */
     void clearCache(in boolean aTrrToo);
 
     /**
+     * The method is used only for test purpose. We use this to recheck if
+     * parental control is enabled or not.
+     */
+    void reloadParentalControlEnabled();
+
+    /**
      * @return the hostname of the operating system.
      */
     readonly attribute AUTF8String myHostName;
 
     /*************************************************************************
      * Listed below are the various flags that may be OR'd together to form
      * the aFlags parameter passed to asyncResolve() and resolve().
      */
--- a/netwerk/test/unit/test_trr.js
+++ b/netwerk/test/unit/test_trr.js
@@ -1,18 +1,34 @@
 "use strict";
 
 const dns = Cc["@mozilla.org/network/dns-service;1"].getService(
   Ci.nsIDNSService
 );
+const { MockRegistrar } = ChromeUtils.import(
+  "resource://testing-common/MockRegistrar.jsm"
+);
 const mainThread = Services.tm.currentThread;
 
 const defaultOriginAttributes = {};
 let h2Port = null;
 
+async function SetParentalControlEnabled(aEnabled) {
+  let parentalControlsService = {
+    parentalControlsEnabled: aEnabled,
+    QueryInterface: ChromeUtils.generateQI([Ci.nsIParentalControlsService]),
+  };
+  let cid = MockRegistrar.register(
+    "@mozilla.org/parental-controls-service;1",
+    parentalControlsService
+  );
+  dns.reloadParentalControlEnabled();
+  MockRegistrar.unregister(cid);
+}
+
 add_task(function setup() {
   dump("start!\n");
 
   let env = Cc["@mozilla.org/process/environment;1"].getService(
     Ci.nsIEnvironment
   );
   h2Port = env.get("MOZHTTP2_PORT");
   Assert.notEqual(h2Port, null);
@@ -43,16 +59,18 @@ add_task(function setup() {
   Services.prefs.setCharPref("network.trr.confirmationNS", "skip");
 
   // The moz-http2 cert is for foo.example.com and is signed by http2-ca.pem
   // so add that cert to the trust list as a signing cert.  // the foo.example.com domain name.
   let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
     Ci.nsIX509CertDB
   );
   addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
+
+  SetParentalControlEnabled(false);
 });
 
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("network.trr.mode");
   Services.prefs.clearUserPref("network.trr.uri");
   Services.prefs.clearUserPref("network.trr.credentials");
   Services.prefs.clearUserPref("network.trr.wait-for-portal");
   Services.prefs.clearUserPref("network.trr.allow-rfc1918");
@@ -705,16 +723,24 @@ add_task(async function test24f() {
   Services.prefs.setCharPref(
     "captivedetect.canonicalURL",
     "http://test.detectportal.com/success.txt"
   );
 
   await new DNSListener("test.detectportal.com", "127.0.0.1");
 });
 
+// TRR-first check that a domain is resolved via native DNS when parental control is enabled.
+add_task(async function test24g() {
+  dns.clearCache(true);
+  await SetParentalControlEnabled(true);
+  await new DNSListener("www.example.com", "127.0.0.1");
+  await SetParentalControlEnabled(false);
+});
+
 // TRR-only that resolving localhost with TRR-only mode will use the remote
 // resolver if it's not in the excluded domains
 add_task(async function test25() {
   dns.clearCache(true);
   Services.prefs.setIntPref("network.trr.mode", 3); // TRR-only
   Services.prefs.setCharPref("network.trr.excluded-domains", "");
   Services.prefs.setCharPref(
     "network.trr.uri",
@@ -777,16 +803,25 @@ add_task(async function test25e() {
   Services.prefs.setCharPref(
     "network.trr.uri",
     `https://foo.example.com:${h2Port}/doh?responseIP=192.192.192.192`
   );
 
   await new DNSListener("test.detectportal.com", "127.0.0.1");
 });
 
+// TRR-only check that a domain is resolved via native DNS when parental control is enabled.
+add_task(async function test25f() {
+  dns.clearCache(true);
+  Services.prefs.setIntPref("network.trr.mode", 3); // TRR-only
+  await SetParentalControlEnabled(true);
+  await new DNSListener("www.example.com", "127.0.0.1");
+  await SetParentalControlEnabled(false);
+});
+
 // Check that none of the requests have set any cookies.
 add_task(async function count_cookies() {
   let cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
   Assert.equal(cm.countCookiesFromHost("example.com"), 0);
   Assert.equal(cm.countCookiesFromHost("foo.example.com."), 0);
 });
 
 add_task(async function test_connection_closed() {