[tbb-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 tbb-commits
mailing list