[tor-commits] [tor-browser] 02/05: fixup! Bug 40597: Implement TorSettings module

gitolite role git at cupani.torproject.org
Tue Apr 5 20:42:32 UTC 2022


This is an automated email from the git hooks/post-receive script.

richard pushed a commit to branch tor-browser-91.8.0esr-11.5-1
in repository tor-browser.

commit 3c85de34837cc10c2d182465e40ef6c02cc5d18f
Author: Pier Angelo Vendrame <pierov at torproject.org>
AuthorDate: Thu Feb 10 10:03:39 2022 +0100

    fixup! Bug 40597: Implement TorSettings module
---
 browser/modules/Moat.jsm               | 122 ++++++++++-----
 browser/modules/TorConnect.jsm         | 278 +++++++++++++++++++++++----------
 browser/modules/TorProtocolService.jsm | 100 +++++++-----
 browser/modules/TorSettings.jsm        | 138 +++++++---------
 4 files changed, 399 insertions(+), 239 deletions(-)

diff --git a/browser/modules/Moat.jsm b/browser/modules/Moat.jsm
index d02075e4412f2..2995a9148f0a3 100644
--- a/browser/modules/Moat.jsm
+++ b/browser/modules/Moat.jsm
@@ -16,7 +16,7 @@ const { TorProtocolService } = ChromeUtils.import(
   "resource:///modules/TorProtocolService.jsm"
 );
 
-const { TorSettings, TorBridgeSource, TorProxyType } = ChromeUtils.import(
+const { TorSettings, TorBridgeSource } = ChromeUtils.import(
   "resource:///modules/TorSettings.jsm"
 );
 
@@ -384,6 +384,42 @@ class MoatResponseListener {
   }
 }
 
+class InternetTestResponseListener {
+  constructor() {
+    this._promise = new Promise((resolve, reject) => {
+      this._resolve = resolve;
+      this._reject = reject;
+    });
+  }
+
+  // callers wait on this for final response
+  status() {
+    return this._promise;
+  }
+
+  onStartRequest(request) {}
+
+  // resolve or reject our Promise
+  onStopRequest(request, status) {
+    let statuses = {};
+    try {
+      statuses = {
+        components: status,
+        successful: Components.isSuccessCode(status),
+        http: request.responseStatus,
+      };
+    } catch (err) {
+      this._reject(err);
+    }
+    this._resolve(statuses);
+  }
+
+  onDataAvailable(request, stream, offset, length) {
+    //  We do not care of the actual data, as long as we have a successful
+    // connection
+  }
+}
+
 // constructs the json objects and sends the request over moat
 class MoatRPC {
   constructor() {
@@ -391,6 +427,10 @@ class MoatRPC {
     this._inited = false;
   }
 
+  get inited() {
+    return this._inited;
+  }
+
   async init() {
     if (this._inited) {
       throw new Error("MoatRPC: Already initialized");
@@ -408,7 +448,7 @@ class MoatRPC {
     this._inited = false;
   }
 
-  async _makeRequest(procedure, args) {
+  _makeHttpHandler(uriString) {
     if (!this._inited) {
       throw new Error("MoatRPC: Not initialized");
     }
@@ -437,16 +477,12 @@ class MoatRPC {
       undefined
     );
 
-    const procedureURIString = `${Services.prefs.getStringPref(
-      TorLauncherPrefs.moat_service
-    )}/${procedure}`;
-
-    const procedureURI = Services.io.newURI(procedureURIString);
+    const uri = Services.io.newURI(uriString);
     // There does not seem to be a way to directly create an nsILoadInfo from
     // JavaScript, so we create a throw away non-proxied channel to get one.
     const secFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
     const loadInfo = Services.io.newChannelFromURI(
-      procedureURI,
+      uri,
       undefined,
       Services.scriptSecurityManager.getSystemPrincipal(),
       undefined,
@@ -458,7 +494,7 @@ class MoatRPC {
       .getProtocolHandler("http")
       .QueryInterface(Ci.nsIHttpProtocolHandler);
     const ch = httpHandler
-      .newProxiedChannel(procedureURI, proxyInfo, 0, undefined, loadInfo)
+      .newProxiedChannel(uri, proxyInfo, 0, undefined, loadInfo)
       .QueryInterface(Ci.nsIHttpChannel);
 
     // remove all headers except for 'Host"
@@ -472,6 +508,15 @@ class MoatRPC {
     });
     headers.forEach(key => ch.setRequestHeader(key, "", false));
 
+    return ch;
+  }
+
+  async _makeRequest(procedure, args) {
+    const procedureURIString = `${Services.prefs.getStringPref(
+      TorLauncherPrefs.moat_service
+    )}/${procedure}`;
+    const ch = this._makeHttpHandler(procedureURIString);
+
     // Arrange for the POST data to be sent.
     const argsJson = JSON.stringify(args);
 
@@ -495,6 +540,18 @@ class MoatRPC {
     return JSON.parse(responseJSON);
   }
 
+  async testInternetConnection() {
+    const uri = `${Services.prefs.getStringPref(
+      TorLauncherPrefs.moat_service
+    )}/circumvention/countries`;
+    const ch = this._makeHttpHandler(uri);
+    ch.requestMethod = "HEAD";
+
+    const listener = new InternetTestResponseListener();
+    await ch.asyncOpen(listener, ch);
+    return listener.status();
+  }
+
   //
   // Moat APIs
   //
@@ -508,7 +565,6 @@ class MoatRPC {
   // - image: a base64 encoded jpeg with the captcha to complete
   // - challenge: a nonce/cookie string associated with this request
   async fetch(transports) {
-
     if (
       // ensure this is an array
       Array.isArray(transports) &&
@@ -588,10 +644,10 @@ class MoatRPC {
   // In the event of error, just return null
   _fixupSettings(settings) {
     try {
-      let retval = TorSettings.defaultSettings()
+      let retval = TorSettings.defaultSettings();
       if ("bridges" in settings) {
         retval.bridges.enabled = true;
-        switch(settings.bridges.source) {
+        switch (settings.bridges.source) {
           case "builtin":
             retval.bridges.source = TorBridgeSource.BuiltIn;
             retval.bridges.builtin_type = settings.bridges.type;
@@ -606,12 +662,17 @@ class MoatRPC {
             retval.bridges.source = TorBridgeSource.BridgeDB;
             if (settings.bridges.bridge_strings) {
               retval.bridges.bridge_strings = settings.bridges.bridge_strings;
+              retval.bridges.disabled_strings = [];
             } else {
-              throw new Error("MoatRPC::_fixupSettings(): Received no bridge-strings for BridgeDB bridge source");
+              throw new Error(
+                "MoatRPC::_fixupSettings(): Received no bridge-strings for BridgeDB bridge source"
+              );
             }
             break;
           default:
-            throw new Error(`MoatRPC::_fixupSettings(): Unexpected bridge source '${settings.bridges.source}'`);
+            throw new Error(
+              `MoatRPC::_fixupSettings(): Unexpected bridge source '${settings.bridges.source}'`
+            );
         }
       }
       if ("proxy" in settings) {
@@ -621,7 +682,7 @@ class MoatRPC {
         // TODO: populate firewall settings
       }
       return retval;
-    } catch(ex) {
+    } catch (ex) {
       console.log(ex.message);
       return null;
     }
@@ -661,14 +722,16 @@ class MoatRPC {
   async circumvention_settings(transports, country) {
     const args = {
       transports: transports ? transports : [],
-      country: country,
+      country,
     };
     const response = await this._makeRequest("circumvention/settings", args);
     if ("errors" in response) {
       const code = response.errors[0].code;
       const detail = response.errors[0].detail;
       if (code == 406) {
-        console.log("MoatRPC::circumvention_settings(): Cannot automatically determine user's country-code");
+        console.log(
+          "MoatRPC::circumvention_settings(): Cannot automatically determine user's country-code"
+        );
         // cannot determine user's country
         return null;
       }
@@ -681,26 +744,13 @@ class MoatRPC {
     return [];
   }
 
-  // Request a copy of the censorship circumvention map (as if cirumvention_settings were
-  // queried for all country codes)
+  // Request a list of country codes with available censorship circumvention settings
   //
-  // returns a map whose key is an ISO 3166-1 alpha-2 country code and whose
-  // values are arrays of settings objects
-  async circumvention_map() {
-    const args = { };
-    const response = await this._makeRequest("circumvention/map", args);
-    if ("errors" in response) {
-      const code = response.errors[0].code;
-      const detail = response.errors[0].detail;
-      throw new Error(`MoatRPC: ${detail} (${code})`);
-    }
-
-    let map = new Map();
-    for (const [country, config] of Object.entries(response)) {
-      map.set(country, this._fixupSettingsList(config.settings));
-    }
-
-    return map;
+  // returns an array of ISO 3166-1 alpha-2 country codes which we can query settings
+  // for
+  async circumvention_countries() {
+    const args = {};
+    return this._makeRequest("circumvention/countries", args);
   }
 
   // Request a copy of the builtin bridges, takes the following parameters:
diff --git a/browser/modules/TorConnect.jsm b/browser/modules/TorConnect.jsm
index c7ab480e2be06..13c1f54d2ee97 100644
--- a/browser/modules/TorConnect.jsm
+++ b/browser/modules/TorConnect.jsm
@@ -1,11 +1,15 @@
 "use strict";
 
-var EXPORTED_SYMBOLS = ["TorConnect", "TorConnectTopics", "TorConnectState"];
+var EXPORTED_SYMBOLS = ["TorConnect", "TorConnectTopics", "TorConnectState", "TorCensorshipLevel"];
 
 const { Services } = ChromeUtils.import(
     "resource://gre/modules/Services.jsm"
 );
 
+const { setTimeout } = ChromeUtils.import(
+    "resource://gre/modules/Timer.jsm"
+);
+
 const { BrowserWindowTracker } = ChromeUtils.import(
     "resource:///modules/BrowserWindowTracker.jsm"
 );
@@ -22,6 +26,8 @@ const { TorSettings, TorSettingsTopics, TorBridgeSource, TorBuiltinBridgeTypes,
     "resource:///modules/TorSettings.jsm"
 );
 
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+
 const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
 
 /* Browser observer topis */
@@ -31,7 +37,11 @@ const BrowserTopics = Object.freeze({
 
 /* Relevant prefs used by tor-launcher */
 const TorLauncherPrefs = Object.freeze({
-  prompt_at_startup: "extensions.torlauncher.prompt_at_startup",
+    prompt_at_startup: "extensions.torlauncher.prompt_at_startup",
+});
+
+const TorConnectPrefs = Object.freeze({
+    censorship_level: "torbrowser.debug.censorship_level",
 });
 
 const TorConnectState = Object.freeze({
@@ -51,6 +61,17 @@ const TorConnectState = Object.freeze({
     Disabled: "Disabled",
 });
 
+const TorCensorshipLevel = Object.freeze({
+    /* No censorship detected */
+    None: 0,
+    /* Moderate censorship detected, autobootstrap may evade it */
+    Moderate: 1,
+    /* Severe censorship detected, but connection may still succeed */
+    Severe: 2,
+    /* Extreme censorship detected, connection will always end in an error */
+    Extreme: 3,
+});
+
 /*
                              TorConnect State Transitions
 
@@ -197,12 +218,31 @@ class StateCallback {
     }
 }
 
+// async method to sleep for a given amount of time
+const debug_sleep = async (ms) => {
+    return new Promise((resolve, reject) => {
+        setTimeout(resolve, ms);
+    });
+}
+
 const TorConnect = (() => {
     let retval = {
 
         _state: TorConnectState.Initial,
         _bootstrapProgress: 0,
         _bootstrapStatus: null,
+        _detectedCensorshiplevel: TorCensorshipLevel.None,
+        // list of country codes Moat has settings for
+        _countryCodes: [],
+        _countryNames: Object.freeze((() => {
+            const codes = Services.intl.getAvailableLocaleDisplayNames("region");
+            const names = Services.intl.getRegionDisplayNames(undefined, codes);
+            let codesNames = {};
+            for (let i = 0; i < codes.length; i++) {
+                codesNames[codes[i]] = names[i];
+            }
+            return codesNames;
+        })()),
         _errorMessage: null,
         _errorDetails: null,
         _logHasWarningOrError: false,
@@ -254,6 +294,22 @@ const TorConnect = (() => {
             [TorConnectState.Bootstrapping, new StateCallback(TorConnectState.Bootstrapping, async function() {
                 // wait until bootstrap completes or we get an error
                 await new Promise(async (resolve, reject) => {
+
+                    // we reset the bootstrap failure count so users can manually try settings without the
+                    // censorship circumvention state machine getting in the way
+                    TorConnect._detectedCensorshipLevel = TorCensorshipLevel.None;
+
+                    // debug hook to simulate censorship preventing bootstrapping
+                    if (Services.prefs.getIntPref(TorConnectPrefs.censorship_level, 0) > 0) {
+                        this.on_transition = (nextState) => {
+                            resolve();
+                        };
+                        await debug_sleep(1500);
+                        TorConnect._changeState(TorConnectState.Error, "Bootstrap failed (for debugging purposes)", "Error: Censorship simulation", true);
+                        TorProtocolService._torBootstrapDebugSetError();
+                        return;
+                    }
+
                     const tbr = new TorBootstrapRequest();
                     this.on_transition = async (nextState) => {
                         if (nextState === TorConnectState.Configuring) {
@@ -270,7 +326,7 @@ const TorConnect = (() => {
                         TorConnect._changeState(TorConnectState.Bootstrapped);
                     };
                     tbr.onbootstraperror = (message, details) => {
-                        TorConnect._changeState(TorConnectState.Error, message, details);
+                        TorConnect._changeState(TorConnectState.Error, message, details, true);
                     };
 
                     tbr.bootstrap();
@@ -283,91 +339,123 @@ const TorConnect = (() => {
                         resolve();
                     };
 
+                    // debug hook to simulate censorship preventing bootstrapping
+                    {
+                        const censorshipLevel = Services.prefs.getIntPref(TorConnectPrefs.censorship_level, 0);
+                        if (censorshipLevel > 1) {
+                            this.on_transition = (nextState) => {
+                                resolve();
+                            };
+                            // always fail even after manually selecting location specific settings
+                            if (censorshipLevel == 3) {
+                                await debug_sleep(2500);
+                                TorConnect._changeState(TorConnectState.Error, "Error: Extreme Censorship simulation", "", true);
+                                return;
+                            // only fail after auto selecting, manually selecting succeeds
+                            } else if (censorshipLevel == 2 && !countryCode) {
+                                await debug_sleep(2500);
+                                TorConnect._changeState(TorConnectState.Error, "Error: Severe Censorship simulation", "", true);
+                                return;
+                            }
+                            TorProtocolService._torBootstrapDebugSetError();
+                        }
+                    }
+
+                    const throw_error = (message, details) => {
+                        let err = new Error(message);
+                        err.details = details;
+                        throw err;
+                    };
+
                     // lookup user's potential censorship circumvention settings from Moat service
                     try {
                         this.mrpc = new MoatRPC();
                         await this.mrpc.init();
 
+                        if (this.transitioning) return;
+
                         this.settings = await this.mrpc.circumvention_settings([...TorBuiltinBridgeTypes, "vanilla"], countryCode);
 
                         if (this.transitioning) return;
 
                         if (this.settings === null) {
                             // unable to determine country
-                            TorConnect._changeState(TorConnectState.Error, "Unable to determine user country", "DETAILS_STRING");
-                            return;
+                            throw_error(TorStrings.torConnect.autoBootstrappingFailed, TorStrings.torConnect.cannotDetermineCountry);
                         } else if (this.settings.length === 0) {
                             // no settings available for country
-                            TorConnect._changeState(TorConnectState.Error, "No settings available for your location", "DETAILS_STRING");
-                            return;
+                            throw_error(TorStrings.torConnect.autoBootstrappingFailed, TorStrings.torConnect.noSettingsForCountry);
                         }
-                    } catch (err) {
-                        TorConnect._changeState(TorConnectState.Error, err?.message, err?.details);
-                        return;
-                    } finally {
-                        // important to uninit MoatRPC object or else the pt process will live as long as tor-browser
-                        this.mrpc?.uninit();
-                    }
 
-                    // apply each of our settings and try to bootstrap with each
-                    try {
-                        this.originalSettings = TorSettings.getSettings();
-
-                        let index = 0;
-                        for (let currentSetting of this.settings) {
-                            // let us early out if user cancels
-                            if (this.transitioning) return;
-
-                            console.log(`TorConnect: Attempting Bootstrap with configuration ${++index}/${this.settings.length}`);
+                        // apply each of our settings and try to bootstrap with each
+                        try {
+                            this.originalSettings = TorSettings.getSettings();
+
+                            for (const [index, currentSetting] of this.settings.entries()) {
+
+                                // we want to break here so we can fall through and restore original settings
+                                if (this.transitioning) break;
+
+                                console.log(`TorConnect: Attempting Bootstrap with configuration ${index+1}/${this.settings.length}`);
+
+                                TorSettings.setSettings(currentSetting);
+                                await TorSettings.applySettings();
+
+                                // build out our bootstrap request
+                                const tbr = new TorBootstrapRequest();
+                                tbr.onbootstrapstatus = (progress, status) => {
+                                    TorConnect._updateBootstrapStatus(progress, status);
+                                };
+                                tbr.onbootstraperror = (message, details) => {
+                                    console.log(`TorConnect: Auto-Bootstrap error => ${message}; ${details}`);
+                                };
+
+                                // update transition callback for user cancel
+                                this.on_transition = async (nextState) => {
+                                    if (nextState === TorConnectState.Configuring) {
+                                        await tbr.cancel();
+                                    }
+                                    resolve();
+                                };
+
+                                // begin bootstrap
+                                if (await tbr.bootstrap()) {
+                                    // persist the current settings to preferences
+                                    TorSettings.saveToPrefs();
+                                    TorConnect._changeState(TorConnectState.Bootstrapped);
+                                    return;
+                                }
+                            }
 
-                            TorSettings.setSettings(currentSetting);
+                            // bootstrapped failed for all potential settings, so reset daemon to use original
+                            TorSettings.setSettings(this.originalSettings);
                             await TorSettings.applySettings();
+                            TorSettings.saveToPrefs();
 
-                            // build out our bootstrap request
-                            const tbr = new TorBootstrapRequest();
-                            tbr.onbootstrapstatus = (progress, status) => {
-                                TorConnect._updateBootstrapStatus(progress, status);
-                            };
-                            tbr.onbootstraperror = (message, details) => {
-                                console.log(`TorConnect: Auto-Bootstrap error => ${message}; ${details}`);
-                            };
-
-                            // update transition callback for user cancel
-                            this.on_transition = async (nextState) => {
-                                if (nextState === TorConnectState.Configuring) {
-                                    await tbr.cancel();
-                                }
-                                resolve();
-                            };
-
-                            // begin bootstrap
-                            if (await tbr.bootstrap()) {
-                                // persist the current settings to preferences
-                                TorSettings.saveToPrefs();
-                                TorConnect._changeState(TorConnectState.Bootstrapped);
-                                return;
+                            // only explicitly change state here if something else has not transitioned us
+                            if (!this.transitioning) {
+                                throw_error(TorStrings.torConnect.autoBootstrappingFailed, TorStrings.torConnect.autoBootstrappingAllFailed);
                             }
+                            return;
+                        } catch (err) {
+                            // restore original settings in case of error
+                            try {
+                                TorSettings.setSettings(this.originalSettings);
+                                await TorSettings.applySettings();
+                            } catch(err) {
+                                console.log(`TorConnect: Failed to restore original settings => ${err}`);
+                            }
+                            // throw to outer catch to transition us
+                            throw err;
                         }
-                        // bootstrapped failed for all potential settings, so reset daemon to use original
-                        TorSettings.setSettings(this.originalSettings);
-                        await TorSettings.applySettings();
-                        TorSettings.saveToPrefs();
-
-                        // only explicitly change state here if something else has not transitioned us
-                        if (!this.transitioning) {
-                            TorConnect._changeState(TorConnectState.Error, "AutoBootstrapping failed", "DETAILS_STRING");
-                        }
-                        return;
-                    } catch (err) {
-                        // restore original settings in case of error
-                        try {
-                            TorSettings.setSettings(this.originalSettings);
-                            await TorSettings.applySettings();
-                        } catch(err) {
-                            console.log(`TorConnect: Failed to restore original settings => ${err}`);
+                    } catch(err) {
+                        if (this.mrpc?.inited) {
+                            // lookup countries which have settings available
+                            TorConnect._countryCodes = await this.mrpc.circumvention_countries();
                         }
-                        TorConnect._changeState(TorConnectState.Error, err?.message, err?.details);
-                        return;
+                        TorConnect._changeState(TorConnectState.Error, err?.message, err?.details, true);
+                    } finally {
+                        // important to uninit MoatRPC object or else the pt process will live as long as tor-browser
+                        this.mrpc?.uninit();
                     }
                 });
             })],
@@ -380,7 +468,7 @@ const TorConnect = (() => {
                 });
             })],
             /* Error */
-            [TorConnectState.Error, new StateCallback(TorConnectState.Error, async function(errorMessage, errorDetails) {
+            [TorConnectState.Error, new StateCallback(TorConnectState.Error, async function(errorMessage, errorDetails, bootstrappingFailure) {
                 await new Promise((resolve, reject) => {
                     this.on_transition = async(nextState) => {
                         resolve();
@@ -389,7 +477,11 @@ const TorConnect = (() => {
                     TorConnect._errorMessage = errorMessage;
                     TorConnect._errorDetails = errorDetails;
 
-                    Services.obs.notifyObservers({message: errorMessage, details: errorDetails}, TorConnectTopics.BootstrapError);
+                    if (bootstrappingFailure && TorConnect._detectedCensorshipLevel < TorCensorshipLevel.Extreme) {
+                        TorConnect._detectedCensorshipLevel += 1;
+                    }
+
+                    Services.obs.notifyObservers({message: errorMessage, details: errorDetails, censorshipLevel: TorConnect.detectedCensorshipLevel}, TorConnectTopics.BootstrapError);
 
                     TorConnect._changeState(TorConnectState.Configuring);
                 });
@@ -523,6 +615,10 @@ const TorConnect = (() => {
             return this._bootstrapStatus;
         },
 
+        get detectedCensorshipLevel() {
+            return this._detectedCensorshipLevel;
+        },
+
         get errorMessage() {
             return this._errorMessage;
         },
@@ -535,6 +631,14 @@ const TorConnect = (() => {
             return this._logHasWarningOrError;
         },
 
+        get countryCodes() {
+            return this._countryCodes;
+        },
+
+        get countryNames() {
+            return this._countryNames;
+        },
+
         /*
         These functions allow external consumers to tell TorConnect to transition states
         */
@@ -564,7 +668,7 @@ const TorConnect = (() => {
         */
         openTorPreferences: function() {
             const win = BrowserWindowTracker.getTopWindow();
-            win.switchToTabHavingURI("about:preferences#tor", true);
+            win.switchToTabHavingURI("about:preferences#connection", true);
         },
 
         openTorConnect: function() {
@@ -572,19 +676,27 @@ const TorConnect = (() => {
             win.switchToTabHavingURI("about:torconnect", true, {ignoreQueryString: true});
         },
 
-        copyTorLogs: function() {
-            // Copy tor log messages to the system clipboard.
-            const chSvc = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
-              Ci.nsIClipboardHelper
-            );
-            const countObj = { value: 0 };
-            chSvc.copyString(TorProtocolService.getLog(countObj));
-            const count = countObj.value;
-            return TorLauncherUtil.getFormattedLocalizedString(
-              "copiedNLogMessagesShort",
-              [count],
-              1
-            );
+        viewTorLogs: function() {
+            const win = BrowserWindowTracker.getTopWindow();
+            win.switchToTabHavingURI("about:preferences#connection-viewlogs", true);
+        },
+
+        getCountryCodes: async function() {
+            // Difference with the getter: this is to be called by TorConnectParent, and downloads
+            // the country codes if they are not already in cache.
+            if (this._countryCodes.length) {
+                return this._countryCodes;
+            }
+            const mrpc = new MoatRPC();
+            try {
+              await mrpc.init();
+              this._countryCodes = await mrpc.circumvention_countries();
+            } catch(err) {
+              console.log("An error occurred while fetching country codes", err);
+            } finally {
+              mrpc.uninit();
+            }
+            return this._countryCodes;
         },
 
         getRedirectURL: function(url) {
diff --git a/browser/modules/TorProtocolService.jsm b/browser/modules/TorProtocolService.jsm
index ac6d643691f6b..aa27e13e11711 100644
--- a/browser/modules/TorProtocolService.jsm
+++ b/browser/modules/TorProtocolService.jsm
@@ -2,16 +2,21 @@
 
 "use strict";
 
-var EXPORTED_SYMBOLS = ["TorProtocolService", "TorProcessStatus", "TorTopics", "TorBootstrapRequest"];
+var EXPORTED_SYMBOLS = [
+  "TorProtocolService",
+  "TorProcessStatus",
+  "TorTopics",
+  "TorBootstrapRequest",
+];
 
-const { Services } = ChromeUtils.import(
-    "resource://gre/modules/Services.jsm"
-);
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-const { setTimeout, clearTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+const { setTimeout, clearTimeout } = ChromeUtils.import(
+  "resource://gre/modules/Timer.jsm"
+);
 
 const { TorLauncherUtil } = ChromeUtils.import(
-    "resource://torlauncher/modules/tl-util.jsm"
+  "resource://torlauncher/modules/tl-util.jsm"
 );
 
 // see tl-process.js
@@ -24,24 +29,18 @@ const TorProcessStatus = Object.freeze({
 
 /* tor-launcher observer topics */
 const TorTopics = Object.freeze({
-    BootstrapStatus: "TorBootstrapStatus",
-    BootstrapError: "TorBootstrapError",
-    ProcessExited: "TorProcessExited",
-    LogHasWarnOrErr: "TorLogHasWarnOrErr",
+  BootstrapStatus: "TorBootstrapStatus",
+  BootstrapError: "TorBootstrapError",
+  ProcessExited: "TorProcessExited",
+  LogHasWarnOrErr: "TorLogHasWarnOrErr",
 });
 
 /* Browser observer topis */
 const BrowserTopics = Object.freeze({
-    ProfileAfterChange: "profile-after-change",
+  ProfileAfterChange: "profile-after-change",
 });
 
 var TorProtocolService = {
-  _TorLauncherUtil: function() {
-      let { TorLauncherUtil } = ChromeUtils.import(
-        "resource://torlauncher/modules/tl-util.jsm"
-      );
-      return TorLauncherUtil;
-    }(),
   _TorLauncherProtocolService: null,
   _TorProcessService: null,
 
@@ -58,13 +57,12 @@ var TorProtocolService = {
     if (topic === BrowserTopics.ProfileAfterChange) {
       // we have to delay init'ing this or else the crypto service inits too early without a profile
       // which breaks the password manager
-      this._TorLauncherProtocolService = Cc["@torproject.org/torlauncher-protocol-service;1"].getService(
-        Ci.nsISupports
-      ).wrappedJSObject;
-      this._TorProcessService = Cc["@torproject.org/torlauncher-process-service;1"].getService(
-        Ci.nsISupports
-      ).wrappedJSObject,
-
+      this._TorLauncherProtocolService = Cc[
+        "@torproject.org/torlauncher-protocol-service;1"
+      ].getService(Ci.nsISupports).wrappedJSObject;
+      this._TorProcessService = Cc[
+        "@torproject.org/torlauncher-process-service;1"
+      ].getService(Ci.nsISupports).wrappedJSObject;
       Services.obs.removeObserver(this, topic);
     }
   },
@@ -171,7 +169,12 @@ var TorProtocolService = {
       }
 
       let errorObject = {};
-      if (! await this._TorLauncherProtocolService.TorSetConfWithReply(settingsObject, errorObject)) {
+      if (
+        !(await this._TorLauncherProtocolService.TorSetConfWithReply(
+          settingsObject,
+          errorObject
+        ))
+      ) {
         throw new Error(errorObject.details);
       }
 
@@ -195,9 +198,7 @@ var TorProtocolService = {
     let lineArray = await this._readSetting(aSetting);
     if (lineArray.length != 1) {
       throw new Error(
-        `Expected an array with length 1 but received array of length ${
-          lineArray.length
-        }`
+        `Expected an array with length 1 but received array of length ${lineArray.length}`
       );
     }
 
@@ -216,9 +217,7 @@ var TorProtocolService = {
     let lineArray = await this._readSetting(aSetting);
     if (lineArray.length != 1) {
       throw new Error(
-        `Expected an array with length 1 but received array of length ${
-          lineArray.length
-        }`
+        `Expected an array with length 1 but received array of length ${lineArray.length}`
       );
     }
     return lineArray[0];
@@ -260,7 +259,7 @@ var TorProtocolService = {
 
   // true if we launched and control tor, false if using system tor
   get ownsTorDaemon() {
-    return this._TorLauncherUtil.shouldStartAndOwnTor;
+    return TorLauncherUtil.shouldStartAndOwnTor;
   },
 
   // Assumes `ownsTorDaemon` is true
@@ -269,7 +268,9 @@ var TorProtocolService = {
       "DisableNetwork",
       true
     );
-    if (TorProtocolService._TorLauncherProtocolService.TorCommandSucceeded(reply)) {
+    if (
+      TorProtocolService._TorLauncherProtocolService.TorCommandSucceeded(reply)
+    ) {
       return reply.retVal;
     }
     return true;
@@ -279,13 +280,18 @@ var TorProtocolService = {
     let settings = {};
     settings.DisableNetwork = false;
     let errorObject = {};
-    if (! await this._TorLauncherProtocolService.TorSetConfWithReply(settings, errorObject)) {
+    if (
+      !(await this._TorLauncherProtocolService.TorSetConfWithReply(
+        settings,
+        errorObject
+      ))
+    ) {
       throw new Error(errorObject.details);
     }
   },
 
   async sendCommand(cmd) {
-    return await this._TorLauncherProtocolService.TorSendCommand(cmd);
+    return this._TorLauncherProtocolService.TorSendCommand(cmd);
   },
 
   retrieveBootstrapStatus() {
@@ -294,7 +300,7 @@ var TorProtocolService = {
 
   _GetSaveSettingsErrorMessage(aDetails) {
     try {
-      return this._TorLauncherUtil.getSaveSettingsErrorMessage(aDetails);
+      return TorLauncherUtil.getSaveSettingsErrorMessage(aDetails);
     } catch (e) {
       console.log("GetSaveSettingsErrorMessage error", e);
       return "Unexpected Error";
@@ -305,7 +311,10 @@ var TorProtocolService = {
     let result = false;
     const error = {};
     try {
-      result = await this._TorLauncherProtocolService.TorSetConfWithReply(settings, error);
+      result = await this._TorLauncherProtocolService.TorSetConfWithReply(
+        settings,
+        error
+      );
     } catch (e) {
       console.log("TorSetConfWithReply error", e);
       error.details = this._GetSaveSettingsErrorMessage(e.message);
@@ -325,6 +334,10 @@ var TorProtocolService = {
     return this._TorProcessService.TorBootstrapErrorOccurred;
   },
 
+  _torBootstrapDebugSetError() {
+    this._TorProcessService._TorSetBootstrapErrorForDebug();
+  },
+
   // Resolves to null if ok, or an error otherwise
   async connect() {
     const kTorConfKeyDisableNetwork = "DisableNetwork";
@@ -396,7 +409,7 @@ class TorBootstrapRequest {
 
   async observe(subject, topic, data) {
     const obj = subject?.wrappedJSObject;
-    switch(topic) {
+    switch (topic) {
       case TorTopics.BootstrapStatus: {
         const progress = obj.PROGRESS;
         const status = TorLauncherUtil.getLocalizedBootstrapStatus(obj, "TAG");
@@ -432,7 +445,9 @@ class TorBootstrapRequest {
 
   // resolves 'true' if bootstrap succeeds, false otherwise
   async bootstrap() {
-    if (this._bootstrapPromise) return this._bootstrapPromise;
+    if (this._bootstrapPromise) {
+      return this._bootstrapPromise;
+    }
 
     this._bootstrapPromise = new Promise(async (resolve, reject) => {
       this._bootstrapPromiseResolve = resolve;
@@ -446,7 +461,10 @@ class TorBootstrapRequest {
         this._timeoutID = setTimeout(async () => {
           await TorProtocolService.torStopBootstrap();
           if (this.onbootstraperror) {
-            this.onbootstraperror("Tor Bootstrap process timed out", `Bootstrap attempt abandoned after waiting ${this.timeout} ms`);
+            this.onbootstraperror(
+              "Tor Bootstrap process timed out",
+              `Bootstrap attempt abandoned after waiting ${this.timeout} ms`
+            );
           }
           this._bootstrapPromiseResolve(false);
         }, this.timeout);
@@ -481,4 +499,4 @@ class TorBootstrapRequest {
 
     this._bootstrapPromiseResolve(false);
   }
-};
+}
diff --git a/browser/modules/TorSettings.jsm b/browser/modules/TorSettings.jsm
index 1b5b564e1e62c..87bc129602930 100644
--- a/browser/modules/TorSettings.jsm
+++ b/browser/modules/TorSettings.jsm
@@ -195,7 +195,7 @@ let parseAddrPortList = function(aAddrPortList) {
   return retval;
 };
 
-// expects a '/n' or '/r/n' delimited bridge string, which we split and trim
+// expects a '\n' or '\r\n' delimited bridge string, which we split and trim
 // each bridge string can also optionally have 'bridge' at the beginning ie:
 // bridge $(type) $(address):$(port) $(certificate)
 // we strip out the 'bridge' prefix here
@@ -226,6 +226,10 @@ let parsePortList = function(aPortListString) {
 };
 
 let getBuiltinBridgeStrings = function(builtinType) {
+    if (!builtinType) {
+        return [];
+    }
+
     let bridgeBranch = Services.prefs.getBranch(TorLauncherPrefs.default_bridge);
     let bridgeBranchPrefs = bridgeBranch.getChildList("");
     let retval = [];
@@ -248,7 +252,7 @@ let getBuiltinBridgeStrings = function(builtinType) {
     return retval;
 };
 
-/* Array methods */
+/* Helper methods */
 
 let arrayShuffle = function(array) {
     // fisher-yates shuffle
@@ -356,37 +360,24 @@ const TorSettings = (() => {
             settings.quickstart.enabled = Services.prefs.getBoolPref(TorSettingsPrefs.quickstart.enabled);
             /* Bridges */
             settings.bridges.enabled = Services.prefs.getBoolPref(TorSettingsPrefs.bridges.enabled);
-            if (settings.bridges.enabled) {
-                settings.bridges.source = Services.prefs.getIntPref(TorSettingsPrefs.bridges.source);
-                // builtin bridge (obfs4, meek, snowlfake, etc)
-                if (settings.bridges.source == TorBridgeSource.BuiltIn) {
-                    let builtinType = Services.prefs.getStringPref(TorSettingsPrefs.bridges.builtin_type);
-                    settings.bridges.builtin_type = builtinType;
-                    // always dynamically load builtin bridges rather than loading the cached versions
-                    // if the user upgrades and the builtin bridges have changed, we want to ensure the user
-                    // can still bootstrap using the provided bridges
-                    let bridgeStrings = getBuiltinBridgeStrings(builtinType);
-                    if (bridgeStrings.length > 0) {
-                        settings.bridges.bridge_strings = bridgeStrings;
-                    } else {
-                        // in this case the user is using a builtin bridge that is no longer supported,
-                        // reset to settings to default values
-                        settings.bridges.enabled = false;
-                        settings.bridges.source = TorBridgeSource.Invalid;
-                        settings.bridges.builtin_type = null;
-                    }
-                } else {
-                    settings.bridges.bridge_strings = [];
-                    let bridgeBranchPrefs = Services.prefs.getBranch(TorSettingsPrefs.bridges.bridge_strings).getChildList("");
-                    bridgeBranchPrefs.forEach(pref => {
-                        let bridgeString = Services.prefs.getStringPref(`${TorSettingsPrefs.bridges.bridge_strings}${pref}`);
-                        settings.bridges.bridge_strings.push(bridgeString);
-                    });
+            settings.bridges.source = Services.prefs.getIntPref(TorSettingsPrefs.bridges.source, TorBridgeSource.Invalid);
+            if (settings.bridges.source == TorBridgeSource.BuiltIn) {
+                let builtinType = Services.prefs.getStringPref(TorSettingsPrefs.bridges.builtin_type);
+                settings.bridges.builtin_type = builtinType;
+                settings.bridges.bridge_strings = getBuiltinBridgeStrings(builtinType);
+                if (settings.bridges.bridge_strings.length == 0) {
+                    // in this case the user is using a builtin bridge that is no longer supported,
+                    // reset to settings to default values
+                    settings.bridges.source = TorBridgeSource.Invalid;
+                    settings.bridges.builtin_type = null;
                 }
             } else {
-                settings.bridges.source = TorBridgeSource.Invalid;
-                settings.bridges.builtin_type = null;
                 settings.bridges.bridge_strings = [];
+                let bridgeBranchPrefs = Services.prefs.getBranch(TorSettingsPrefs.bridges.bridge_strings).getChildList("");
+                bridgeBranchPrefs.forEach(pref => {
+                    const bridgeString = Services.prefs.getStringPref(`${TorSettingsPrefs.bridges.bridge_strings}${pref}`);
+                    settings.bridges.bridge_strings.push(bridgeString);
+                });
             }
             /* Proxy */
             settings.proxy.enabled = Services.prefs.getBoolPref(TorSettingsPrefs.proxy.enabled);
@@ -428,29 +419,18 @@ const TorSettings = (() => {
             Services.prefs.setBoolPref(TorSettingsPrefs.quickstart.enabled, settings.quickstart.enabled);
             /* Bridges */
             Services.prefs.setBoolPref(TorSettingsPrefs.bridges.enabled, settings.bridges.enabled);
-            if (settings.bridges.enabled) {
-                Services.prefs.setIntPref(TorSettingsPrefs.bridges.source, settings.bridges.source);
-                if (settings.bridges.source === TorBridgeSource.BuiltIn) {
-                    Services.prefs.setStringPref(TorSettingsPrefs.bridges.builtin_type, settings.bridges.builtin_type);
-                } else {
-                    Services.prefs.clearUserPref(TorSettingsPrefs.bridges.builtin_type);
-                }
-                // erase existing bridge strings
-                let bridgeBranchPrefs = Services.prefs.getBranch(TorSettingsPrefs.bridges.bridge_strings).getChildList("");
-                bridgeBranchPrefs.forEach(pref => {
-                    Services.prefs.clearUserPref(`${TorSettingsPrefs.bridges.bridge_strings}${pref}`);
-                });
-                // write new ones
+            Services.prefs.setIntPref(TorSettingsPrefs.bridges.source, settings.bridges.source);
+            Services.prefs.setStringPref(TorSettingsPrefs.bridges.builtin_type, settings.bridges.builtin_type);
+            // erase existing bridge strings
+            let bridgeBranchPrefs = Services.prefs.getBranch(TorSettingsPrefs.bridges.bridge_strings).getChildList("");
+            bridgeBranchPrefs.forEach(pref => {
+                Services.prefs.clearUserPref(`${TorSettingsPrefs.bridges.bridge_strings}${pref}`);
+            });
+            // write new ones
+            if (settings.bridges.source !== TorBridgeSource.BuiltIn) {
                 settings.bridges.bridge_strings.forEach((string, index) => {
                     Services.prefs.setStringPref(`${TorSettingsPrefs.bridges.bridge_strings}.${index}`, string);
                 });
-            } else {
-                Services.prefs.clearUserPref(TorSettingsPrefs.bridges.source);
-                Services.prefs.clearUserPref(TorSettingsPrefs.bridges.builtin_type);
-                let bridgeBranchPrefs = Services.prefs.getBranch(TorSettingsPrefs.bridges.bridge_strings).getChildList("");
-                bridgeBranchPrefs.forEach(pref => {
-                    Services.prefs.clearUserPref(`${TorSettingsPrefs.bridges.bridge_strings}${pref}`);
-                });
             }
             /* Proxy */
             Services.prefs.setBoolPref(TorSettingsPrefs.proxy.enabled, settings.proxy.enabled);
@@ -488,11 +468,11 @@ const TorSettings = (() => {
             let settingsMap = new Map();
 
             /* Bridges */
-            settingsMap.set(TorConfigKeys.useBridges, settings.bridges.enabled);
-            if (settings.bridges.enabled) {
+            const haveBridges = settings.bridges.enabled && settings.bridges.bridge_strings.length  > 0;
+            settingsMap.set(TorConfigKeys.useBridges, haveBridges);
+            if (haveBridges) {
                 settingsMap.set(TorConfigKeys.bridgeList, settings.bridges.bridge_strings);
             } else {
-                // shuffle bridge list
                 settingsMap.set(TorConfigKeys.bridgeList, null);
             }
 
@@ -545,29 +525,28 @@ const TorSettings = (() => {
             let backup = this.getSettings();
 
             try {
-                if (settings.bridges.enabled) {
-                    this._settings.bridges.enabled = true;
-                    this._settings.bridges.source = settings.bridges.source;
-                    switch(settings.bridges.source) {
-                        case TorBridgeSource.BridgeDB:
-                        case TorBridgeSource.UserProvided:
-                            this._settings.bridges.bridge_strings = settings.bridges.bridge_strings
-                            break;
-                        case TorBridgeSource.BuiltIn: {
-                            this._settings.bridges.builtin_type = settings.bridges.builtin_type;
-                            let bridgeStrings =  getBuiltinBridgeStrings(settings.bridges.builtin_type);
-                            if (bridgeStrings.length > 0) {
-                                this._settings.bridges.bridge_strings = bridgeStrings;
-                            } else {
-                                throw new Error(`No available builtin bridges of type ${settings.bridges.builtin_type}`);
-                            }
-                            break;
+                this._settings.bridges.enabled = !!settings.bridges.enabled;
+                this._settings.bridges.source = settings.bridges.source;
+                switch(settings.bridges.source) {
+                    case TorBridgeSource.BridgeDB:
+                    case TorBridgeSource.UserProvided:
+                        this._settings.bridges.bridge_strings = settings.bridges.bridge_strings;
+                        break;
+                    case TorBridgeSource.BuiltIn: {
+                        this._settings.bridges.builtin_type = settings.bridges.builtin_type;
+                        settings.bridges.bridge_strings = getBuiltinBridgeStrings(settings.bridges.builtin_type);
+                        if (settings.bridges.bridge_strings.length == 0 && settings.bridges.enabled) {
+                            throw new Error(`No available builtin bridges of type ${settings.bridges.builtin_type}`);
                         }
-                        default:
-                            throw new Error(`Bridge source '${settings.source}' is not a valid source`);
+                        break;
                     }
-                } else {
-                    this.bridges.enabled = false;
+                    case TorBridgeSource.Invalid:
+                        break;
+                    default:
+                        if (settings.bridges.enabled) {
+                            throw new Error(`Bridge source '${settings.source}' is not a valid source`);
+                        }
+                        break;
                 }
 
                 // TODO: proxy and firewall
@@ -609,19 +588,20 @@ const TorSettings = (() => {
                 get enabled() { return self._settings.bridges.enabled; },
                 set enabled(val) {
                     self._settings.bridges.enabled = val;
-                    // reset bridge settings
-                    self._settings.bridges.source = TorBridgeSource.Invalid;
-                    self._settings.bridges.builtin_type = null;
-                    self._settings.bridges.bridge_strings = [];
                 },
                 get source() { return self._settings.bridges.source; },
                 set source(val) { self._settings.bridges.source = val; },
                 get builtin_type() { return self._settings.bridges.builtin_type; },
                 set builtin_type(val) {
-                    let bridgeStrings = getBuiltinBridgeStrings(val);
+                    const bridgeStrings = getBuiltinBridgeStrings(val);
                     if (bridgeStrings.length > 0) {
                         self._settings.bridges.builtin_type = val;
                         self._settings.bridges.bridge_strings = bridgeStrings;
+                    } else {
+                        self._settings.bridges.builtin_type = "";
+                        if (self._settings.bridges.source === TorBridgeSource.BuiltIn) {
+                            self._settings.bridges.source = TorBridgeSource.Invalid;
+                        }
                     }
                 },
                 get bridge_strings() { return arrayCopy(self._settings.bridges.bridge_strings); },

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the tor-commits mailing list