[tbb-commits] [Git][tpo/applications/tor-browser][tor-browser-115.8.0esr-13.5-1] 5 commits: fixup! Bug 40933: Add tor-launcher functionality
Pier Angelo Vendrame (@pierov)
git at gitlab.torproject.org
Mon Mar 11 15:41:03 UTC 2024
Pier Angelo Vendrame pushed to branch tor-browser-115.8.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
9cecc61f by Pier Angelo Vendrame at 2024-03-11T09:33:51+01:00
fixup! Bug 40933: Add tor-launcher functionality
Bug 42336: Rework the relationship between TorSettings and TorProvider.
- - - - -
138e8d6b by Pier Angelo Vendrame at 2024-03-11T16:19:47+01:00
fixup! Bug 40597: Implement TorSettings module
Bug 42336: Rework the relationship between TorSettings and TorProvider.
- - - - -
6265cc34 by Pier Angelo Vendrame at 2024-03-11T16:19:52+01:00
fixup! Bug 42247: Android helpers for the TorProvider
Bug 42336: Rework the relationship between TorSettings and TorProvider.
- - - - -
e409c32a by Pier Angelo Vendrame at 2024-03-11T16:19:52+01:00
fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
Bug 42336: Rework the relationship between TorSettings and TorProvider.
- - - - -
f8451ae0 by Pier Angelo Vendrame at 2024-03-11T16:19:53+01:00
fixup! Bug 40597: Implement TorSettings module
Use a logger instead of console in TorConnect.
- - - - -
10 changed files:
- browser/components/torpreferences/content/connectionPane.js
- browser/components/torpreferences/content/connectionSettingsDialog.js
- toolkit/components/tor-launcher/TorControlPort.sys.mjs
- toolkit/components/tor-launcher/TorProvider.sys.mjs
- toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
- toolkit/components/tor-launcher/TorStartupService.sys.mjs
- toolkit/modules/Moat.sys.mjs
- toolkit/modules/TorAndroidIntegration.sys.mjs
- toolkit/modules/TorConnect.sys.mjs
- toolkit/modules/TorSettings.sys.mjs
Changes:
=====================================
browser/components/torpreferences/content/connectionPane.js
=====================================
@@ -114,10 +114,9 @@ async function setTorSettings(changes) {
// This will trigger TorSettings.#cleanupSettings()
TorSettings.saveToPrefs();
try {
- // May throw.
await TorSettings.applySettings();
} catch (e) {
- console.error("Failed to save Tor settings", e);
+ console.error("Failed to apply Tor settings", e);
}
} finally {
TorSettings.thawNotifications();
=====================================
browser/components/torpreferences/content/connectionSettingsDialog.js
=====================================
@@ -362,6 +362,8 @@ const gConnectionSettingsDialog = {
}
TorSettings.saveToPrefs();
+ // FIXME: What if this fails? Should we prevent the dialog to close and show
+ // an error?
TorSettings.applySettings();
},
};
=====================================
toolkit/components/tor-launcher/TorControlPort.sys.mjs
=====================================
@@ -838,10 +838,12 @@ export class TorController {
/**
* Send multiple configuration values to tor.
*
- * @param {object} values The values to set
+ * @param {Array} values The values to set. It should be an array of
+ * [key, value] pairs to pass to SETCONF. Keys can be repeated, and array
+ * values will be automatically unrolled.
*/
async setConf(values) {
- const args = Object.entries(values)
+ const args = values
.flatMap(([key, value]) => {
if (value === undefined || value === null) {
return [key];
@@ -871,7 +873,7 @@ export class TorController {
* @param {boolean} enabled Tell whether the network should be enabled
*/
async setNetworkEnabled(enabled) {
- return this.setConf({ DisableNetwork: !enabled });
+ return this.setConf([["DisableNetwork", !enabled]]);
}
/**
=====================================
toolkit/components/tor-launcher/TorProvider.sys.mjs
=====================================
@@ -15,6 +15,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
TorController: "resource://gre/modules/TorControlPort.sys.mjs",
TorProcess: "resource://gre/modules/TorProcess.sys.mjs",
TorProcessAndroid: "resource://gre/modules/TorProcessAndroid.sys.mjs",
+ TorProxyType: "resource://gre/modules/TorSettings.sys.mjs",
+ TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
});
const logger = new ConsoleAPI({
@@ -73,6 +75,20 @@ const Preferences = Object.freeze({
PromptAtStartup: "extensions.torlauncher.prompt_at_startup",
});
+/* Config Keys used to configure tor daemon */
+const TorConfigKeys = Object.freeze({
+ useBridges: "UseBridges",
+ bridgeList: "Bridge",
+ socks4Proxy: "Socks4Proxy",
+ socks5Proxy: "Socks5Proxy",
+ socks5ProxyUsername: "Socks5ProxyUsername",
+ socks5ProxyPassword: "Socks5ProxyPassword",
+ httpsProxy: "HTTPSProxy",
+ httpsProxyAuthenticator: "HTTPSProxyAuthenticator",
+ reachableAddresses: "ReachableAddresses",
+ clientTransportPlugin: "ClientTransportPlugin",
+});
+
/**
* This is a Tor provider for the C Tor daemon.
*
@@ -166,15 +182,6 @@ export class TorProvider {
*/
#currentBridge = null;
- /**
- * Maintain a map of tor settings set by Tor Browser so that we don't
- * repeatedly set the same key/values over and over.
- * This map contains string keys to primitives or array values.
- *
- * @type {Map<string, any>}
- */
- #settingsCache = new Map();
-
/**
* Starts a new tor process and connect to its control port, or connect to the
* control port of an existing tor daemon.
@@ -219,13 +226,22 @@ export class TorProvider {
throw e;
}
+ try {
+ await lazy.TorSettings.initializedPromise;
+ await this.writeSettings(lazy.TorSettings.getSettings());
+ } catch (e) {
+ logger.warn(
+ "Failed to initialize TorSettings or to write our settings, so uninitializing.",
+ e
+ );
+ this.uninit();
+ throw e;
+ }
+
TorLauncherUtil.setProxyConfiguration(this.#socksSettings);
logger.info("The Tor provider is ready.");
- logger.debug(`Notifying ${TorProviderTopics.ProcessIsReady}`);
- Services.obs.notifyObservers(null, TorProviderTopics.ProcessIsReady);
-
// If we are using an external Tor daemon, we might need to fetch circuits
// already, in case streams use them. Do not await because we do not want to
// block the intialization on this (it should not fail anyway...).
@@ -252,42 +268,74 @@ export class TorProvider {
// Provider API
- async writeSettings(settingsObj) {
- // TODO: Move the translation from settings object to settings understood by
- // tor here.
- const entries =
- settingsObj instanceof Map
- ? Array.from(settingsObj.entries())
- : Object.entries(settingsObj);
- // only write settings that have changed
- const newSettings = entries.filter(([setting, value]) => {
- if (!this.#settingsCache.has(setting)) {
- // no cached setting, so write
- return true;
- }
+ /**
+ * Send settings to the tor daemon.
+ *
+ * @param {object} settings A settings object, as returned by
+ * TorSettings.getSettings(). This allow to try settings without passing
+ * through TorSettings.
+ */
+ async writeSettings(settings) {
+ logger.debug("TorProvider.writeSettings", settings);
+ const torSettings = new Map();
+
+ // Bridges
+ const haveBridges =
+ settings.bridges?.enabled && !!settings.bridges.bridge_strings.length;
+ torSettings.set(TorConfigKeys.useBridges, haveBridges);
+ if (haveBridges) {
+ torSettings.set(
+ TorConfigKeys.bridgeList,
+ settings.bridges.bridge_strings
+ );
+ } else {
+ torSettings.set(TorConfigKeys.bridgeList, null);
+ }
- const cachedValue = this.#settingsCache.get(setting);
- // Arrays are the only special case for which === could fail.
- // The other values we accept (strings, booleans, numbers, null and
- // undefined) work correctly with ===.
- if (Array.isArray(value) && Array.isArray(cachedValue)) {
- return (
- value.length !== cachedValue.length ||
- value.some((val, idx) => val !== cachedValue[idx])
+ // Proxy
+ torSettings.set(TorConfigKeys.socks4Proxy, null);
+ torSettings.set(TorConfigKeys.socks5Proxy, null);
+ torSettings.set(TorConfigKeys.socks5ProxyUsername, null);
+ torSettings.set(TorConfigKeys.socks5ProxyPassword, null);
+ torSettings.set(TorConfigKeys.httpsProxy, null);
+ torSettings.set(TorConfigKeys.httpsProxyAuthenticator, null);
+ if (settings.proxy && !settings.proxy.enabled) {
+ settings.proxy.type = null;
+ }
+ const address = settings.proxy?.address;
+ const port = settings.proxy?.port;
+ const username = settings.proxy?.username;
+ const password = settings.proxy?.password;
+ switch (settings.proxy?.type) {
+ case lazy.TorProxyType.Socks4:
+ torSettings.set(TorConfigKeys.socks4Proxy, `${address}:${port}`);
+ break;
+ case lazy.TorProxyType.Socks5:
+ torSettings.set(TorConfigKeys.socks5Proxy, `${address}:${port}`);
+ torSettings.set(TorConfigKeys.socks5ProxyUsername, username);
+ torSettings.set(TorConfigKeys.socks5ProxyPassword, password);
+ break;
+ case lazy.TorProxyType.HTTPS:
+ torSettings.set(TorConfigKeys.httpsProxy, `${address}:${port}`);
+ torSettings.set(
+ TorConfigKeys.httpsProxyAuthenticator,
+ `${username}:${password}`
);
- }
- return value !== cachedValue;
- });
-
- // only write if new setting to save
- if (newSettings.length) {
- await this.#controller.setConf(Object.fromEntries(newSettings));
+ break;
+ }
- // save settings to cache after successfully writing to Tor
- for (const [setting, value] of newSettings) {
- this.#settingsCache.set(setting, value);
- }
+ // Firewall
+ if (settings.firewall?.enabled) {
+ const reachableAddresses = settings.firewall.allowed_ports
+ .map(port => `*:${port}`)
+ .join(",");
+ torSettings.set(TorConfigKeys.reachableAddresses, reachableAddresses);
+ } else {
+ torSettings.set(TorConfigKeys.reachableAddresses, null);
}
+
+ logger.debug("Mapped settings object", settings, torSettings);
+ await this.#controller.setConf(Array.from(torSettings));
}
async flushSettings() {
=====================================
toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
=====================================
@@ -9,7 +9,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
});
export const TorProviderTopics = Object.freeze({
- ProcessIsReady: "TorProcessIsReady",
ProcessExited: "TorProcessExited",
BootstrapStatus: "TorBootstrapStatus",
BootstrapError: "TorBootstrapError",
=====================================
toolkit/components/tor-launcher/TorStartupService.sys.mjs
=====================================
@@ -31,15 +31,16 @@ export class TorStartupService {
}
}
- async #init() {
+ #init() {
Services.obs.addObserver(this, BrowserTopics.QuitApplicationGranted);
- // Do not await on this init. build() is expected to await the
- // initialization, so anything that should need the Tor Provider should
- // block there, instead.
+ lazy.TorSettings.init();
+
+ // Theoretically, build() is expected to await the initialization of the
+ // provider, and anything needing the Tor Provider should be able to just
+ // await on TorProviderBuilder.build().
lazy.TorProviderBuilder.init();
- await lazy.TorSettings.init();
lazy.TorConnect.init();
lazy.TorDomainIsolator.init();
=====================================
toolkit/modules/Moat.sys.mjs
=====================================
@@ -2,13 +2,13 @@
* 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/. */
-import { TorBridgeSource } from "resource://gre/modules/TorSettings.sys.mjs";
-
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
DomainFrontRequestBuilder:
"resource://gre/modules/DomainFrontedRequests.sys.mjs",
+ TorBridgeSource: "resource://gre/modules/TorSettings.sys.mjs",
+ TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
});
const TorLauncherPrefs = Object.freeze({
@@ -211,13 +211,23 @@ export class MoatRPC {
};
switch (settings.bridges.source) {
case "builtin":
- retval.bridges.source = TorBridgeSource.BuiltIn;
+ retval.bridges.source = lazy.TorBridgeSource.BuiltIn;
retval.bridges.builtin_type = settings.bridges.type;
// TorSettings will ignore strings for built-in bridges, and use the
- // ones it already knows, instead.
+ // ones it already knows, instead. However, when we try these settings
+ // in the connect assist, we skip TorSettings. Therefore, we set the
+ // lines also here (the ones we already known, not the ones we receive
+ // from Moat). This needs TorSettings to be initialized, which by now
+ // should have already happened (this method is used only by TorConnect,
+ // that needs TorSettings to be initialized).
+ // In any case, getBuiltinBridges will throw if the data is not ready,
+ // yet.
+ retval.bridges.bridge_strings = lazy.TorSettings.getBuiltinBridges(
+ settings.bridges.type
+ );
break;
case "bridgedb":
- retval.bridges.source = TorBridgeSource.BridgeDB;
+ retval.bridges.source = lazy.TorBridgeSource.BridgeDB;
if (settings.bridges.bridge_strings) {
retval.bridges.bridge_strings = settings.bridges.bridge_strings;
} else {
=====================================
toolkit/modules/TorAndroidIntegration.sys.mjs
=====================================
@@ -150,7 +150,7 @@ class TorAndroidIntegrationImpl {
lazy.TorSettings.saveToPrefs();
}
if (data.apply) {
- lazy.TorSettings.applySettings();
+ await lazy.TorSettings.applySettings();
}
break;
case ListenedEvents.settingsApply:
=====================================
toolkit/modules/TorConnect.sys.mjs
=====================================
@@ -2,14 +2,17 @@
* 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/. */
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
+ ConsoleAPI: "resource://gre/modules/Console.sys.mjs",
EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
MoatRPC: "resource://gre/modules/Moat.sys.mjs",
TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
});
// TODO: Should we move this to the about:torconnect actor?
@@ -20,10 +23,7 @@ ChromeUtils.defineModuleGetter(
);
import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
-import {
- TorSettings,
- TorSettingsTopics,
-} from "resource://gre/modules/TorSettings.sys.mjs";
+import { TorSettings } from "resource://gre/modules/TorSettings.sys.mjs";
import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs";
@@ -40,6 +40,7 @@ const TorLauncherPrefs = Object.freeze({
const TorConnectPrefs = Object.freeze({
censorship_level: "torbrowser.debug.censorship_level",
allow_internet_test: "torbrowser.bootstrap.allow_internet_test",
+ log_level: "torbrowser.bootstrap.log_level",
});
export const TorConnectState = Object.freeze({
@@ -59,6 +60,17 @@ export const TorConnectState = Object.freeze({
Disabled: "Disabled",
});
+XPCOMUtils.defineLazyGetter(
+ lazy,
+ "logger",
+ () =>
+ new lazy.ConsoleAPI({
+ maxLogLevel: "info",
+ maxLogLevelPref: TorConnectPrefs.log_level,
+ prefix: "TorConnect",
+ })
+);
+
/*
TorConnect State Transitions
@@ -194,12 +206,12 @@ class StateCallback {
}
async begin(...args) {
- console.log(`TorConnect: Entering ${this._state} state`);
+ lazy.logger.trace(`Entering ${this._state} state`);
this._init();
try {
// this Promise will block until this StateCallback has completed its work
await Promise.resolve(this._callback.call(this._context, ...args));
- console.log(`TorConnect: Exited ${this._state} state`);
+ lazy.logger.info(`Exited ${this._state} state`);
// handled state transition
Services.obs.notifyObservers(
@@ -267,16 +279,14 @@ class InternetTest {
this.cancel();
this._pending = true;
- console.log("TorConnect: starting the Internet test");
+ lazy.logger.info("Starting the Internet test");
this._testAsync()
.then(status => {
this._pending = false;
this._status = status.successful
? InternetStatus.Online
: InternetStatus.Offline;
- console.log(
- `TorConnect: performed Internet test, outcome ${this._status}`
- );
+ lazy.logger.info(`Performed Internet test, outcome ${this._status}`);
this.onResult(this.status, status.date);
})
.catch(error => {
@@ -305,7 +315,7 @@ class InternetTest {
await mrpc.init();
status = await mrpc.testInternetConnection();
} catch (err) {
- console.error("Error while checking the Internet connection", err);
+ lazy.logger.error("Error while checking the Internet connection", err);
error = err;
} finally {
mrpc.uninit();
@@ -523,8 +533,8 @@ export const TorConnect = (() => {
// get "Building circuits: Establishing a Tor circuit failed".
// TODO: Maybe move this logic deeper in the process to know
// when to filter out such errors triggered by cancelling.
- console.log(
- `TorConnect: Post-cancel error => ${message}; ${details}`
+ lazy.logger.warn(
+ `Post-cancel error => ${message}; ${details}`
);
return;
}
@@ -628,7 +638,7 @@ export const TorConnect = (() => {
"vanilla",
]);
} catch (err) {
- console.error(
+ lazy.logger.error(
"We did not get localized settings, and default settings failed as well",
err
);
@@ -651,10 +661,19 @@ export const TorConnect = (() => {
}
}
+ const restoreOriginalSettings = async () => {
+ try {
+ await TorSettings.applySettings();
+ } catch (e) {
+ // We cannot do much if the original settings were bad or
+ // if the connection closed, so just report it in the
+ // console.
+ lazy.logger.warn("Failed to restore original settings.", e);
+ }
+ };
+
// apply each of our settings and try to bootstrap with each
try {
- this.originalSettings = TorSettings.getSettings();
-
for (const [
index,
currentSetting,
@@ -664,14 +683,32 @@ export const TorConnect = (() => {
break;
}
- console.log(
- `TorConnect: Attempting Bootstrap with configuration ${
- index + 1
- }/${this.settings.length}`
+ lazy.logger.info(
+ `Attempting Bootstrap with configuration ${index + 1}/${
+ this.settings.length
+ }`
);
- TorSettings.setSettings(currentSetting);
- await TorSettings.applySettings();
+ // Send the new settings directly to the provider. We will
+ // save them only if the bootstrap succeeds.
+ // FIXME: We should somehow signal TorSettings users that we
+ // have set custom settings, and they should not apply
+ // theirs until we are done with trying ours.
+ // Otherwise, the new settings provided by the user while we
+ // were bootstrapping could be the ones that cause the
+ // bootstrap to succeed, but we overwrite them (unless we
+ // backup the original settings, and then save our new
+ // settings only if they have not changed).
+ // Another idea (maybe easier to implement) is to disable
+ // the settings UI while *any* bootstrap is going on.
+ // This is also documented in tor-browser#41921.
+ const provider = await lazy.TorProviderBuilder.build();
+ // We need to merge with old settings, in case the user is
+ // using a proxy or is behind a firewall.
+ await provider.writeSettings({
+ ...TorSettings.getSettings(),
+ ...currentSetting,
+ });
// build out our bootstrap request
const tbr = new lazy.TorBootstrapRequest();
@@ -679,8 +716,8 @@ export const TorConnect = (() => {
TorConnect._updateBootstrapStatus(progress, status);
};
tbr.onbootstraperror = (message, details) => {
- console.log(
- `TorConnect: Auto-Bootstrap error => ${message}; ${details}`
+ lazy.logger.error(
+ `Auto-Bootstrap error => ${message}; ${details}`
);
};
@@ -688,6 +725,7 @@ export const TorConnect = (() => {
this.on_transition = async nextState => {
if (nextState === TorConnectState.Configuring) {
await tbr.cancel();
+ await restoreOriginalSettings();
}
resolve();
};
@@ -695,23 +733,20 @@ export const TorConnect = (() => {
// begin bootstrap
if (await tbr.bootstrap()) {
// persist the current settings to preferences
+ TorSettings.setSettings(currentSetting);
TorSettings.saveToPrefs();
+ await TorSettings.applySettings();
TorConnect._changeState(TorConnectState.Bootstrapped);
return;
}
}
- // bootstrapped failed for all potential settings, so reset daemon to use original
- TorSettings.setSettings(this.originalSettings);
- // The original settings should be good, so we save them to
- // preferences before trying to apply them, as it might fail
- // if the actual problem is with the connection to the control
- // port.
- // FIXME: We should handle this case in a better way.
- TorSettings.saveToPrefs();
- await TorSettings.applySettings();
-
- // only explicitly change state here if something else has not transitioned us
+ // Bootstrap failed for all potential settings, so restore the
+ // original settings the provider.
+ await restoreOriginalSettings();
+
+ // Only explicitly change state here if something else has not
+ // transitioned us.
if (!this.transitioning) {
throw_error(
TorStrings.torConnect.autoBootstrappingFailed,
@@ -720,18 +755,8 @@ export const TorConnect = (() => {
}
return;
} catch (err) {
- // restore original settings in case of error
- try {
- TorSettings.setSettings(this.originalSettings);
- // As above
- TorSettings.saveToPrefs();
- await TorSettings.applySettings();
- } catch (errRestore) {
- console.log(
- `TorConnect: Failed to restore original settings => ${errRestore}`
- );
- }
- // throw to outer catch to transition us
+ await restoreOriginalSettings();
+ // throw to outer catch to transition us.
throw err;
}
} catch (err) {
@@ -748,8 +773,8 @@ export const TorConnect = (() => {
true
);
} else {
- console.error(
- "TorConnect: Received AutoBootstrapping error after transitioning",
+ lazy.logger.error(
+ "Received AutoBootstrapping error after transitioning",
err
);
}
@@ -793,8 +818,8 @@ export const TorConnect = (() => {
TorConnect._errorMessage = errorMessage;
TorConnect._errorDetails = errorDetails;
- console.error(
- `[TorConnect] Entering error state (${errorMessage}, ${errorDetails})`
+ lazy.logger.error(
+ `Entering error state (${errorMessage}, ${errorDetails})`
);
Services.obs.notifyObservers(
@@ -835,9 +860,7 @@ export const TorConnect = (() => {
);
}
- console.log(
- `TorConnect: Try transitioning from ${prevState} to ${newState}`
- );
+ lazy.logger.trace(`Try transitioning from ${prevState} to ${newState}`);
// set our new state first so that state transitions can themselves trigger
// a state transition
@@ -851,8 +874,8 @@ export const TorConnect = (() => {
this._bootstrapProgress = progress;
this._bootstrapStatus = status;
- console.log(
- `TorConnect: Bootstrapping ${this._bootstrapProgress}% complete (${this._bootstrapStatus})`
+ lazy.logger.info(
+ `Bootstrapping ${this._bootstrapProgress}% complete (${this._bootstrapStatus})`
);
Services.obs.notifyObservers(
{
@@ -866,7 +889,7 @@ export const TorConnect = (() => {
// init should be called by TorStartupService
init() {
- console.log("TorConnect: init()");
+ lazy.logger.debug("TorConnect.init()");
this._callback(TorConnectState.Initial).begin();
if (!this.enabled) {
@@ -875,9 +898,17 @@ export const TorConnect = (() => {
} else {
let observeTopic = addTopic => {
Services.obs.addObserver(this, addTopic);
- console.log(`TorConnect: Observing topic '${addTopic}'`);
+ lazy.logger.debug(`Observing topic '${addTopic}'`);
};
+ // Wait for TorSettings, as we will need it.
+ // We will wait for a TorProvider only after TorSettings is ready,
+ // because the TorProviderBuilder initialization might not have finished
+ // at this point, and TorSettings initialization is a prerequisite for
+ // having a provider.
+ // So, we prefer initializing TorConnect as soon as possible, so that
+ // the UI will be able to detect it is in the Initializing state and act
+ // consequently.
TorSettings.initializedPromise.then(() => this._settingsInitialized());
// register the Tor topics we always care about
@@ -887,7 +918,7 @@ export const TorConnect = (() => {
},
async observe(subject, topic, data) {
- console.log(`TorConnect: Observed ${topic}`);
+ lazy.logger.debug(`Observed ${topic}`);
switch (topic) {
case TorTopics.LogHasWarnOrErr: {
@@ -919,19 +950,25 @@ export const TorConnect = (() => {
}
},
- _settingsInitialized() {
+ async _settingsInitialized() {
+ // TODO: Handle failures here, instead of the prompt to restart the
+ // daemon when it exits (tor-browser#21053, tor-browser#41921).
+ await lazy.TorProviderBuilder.build();
+
// tor-browser#41907: This is only a workaround to avoid users being
// bounced back to the initial panel without any explanation.
// Longer term we should disable the clickable elements, or find a UX
// to prevent this from happening (e.g., allow buttons to be clicked,
// but show an intermediate starting state, or a message that tor is
// starting while the butons are disabled, etc...).
+ // See also tor-browser#41921.
if (this.state !== TorConnectState.Initial) {
- console.warn(
- "TorConnect: Seen the torsettings:ready after the state has already changed, ignoring the notification."
+ lazy.logger.warn(
+ "The TorProvider was built after the state had already changed."
);
return;
}
+ lazy.logger.debug("The TorProvider is ready, changing state.");
if (this.shouldQuickStart) {
// Quickstart
this._changeState(TorConnectState.Bootstrapping);
@@ -1074,17 +1111,17 @@ export const TorConnect = (() => {
*/
beginBootstrap() {
- console.log("TorConnect: beginBootstrap()");
+ lazy.logger.debug("TorConnect.beginBootstrap()");
this._changeState(TorConnectState.Bootstrapping);
},
cancelBootstrap() {
- console.log("TorConnect: cancelBootstrap()");
+ lazy.logger.debug("TorConnect.cancelBootstrap()");
this._changeState(TorConnectState.Configuring);
},
beginAutoBootstrap(countryCode) {
- console.log("TorConnect: beginAutoBootstrap()");
+ lazy.logger.debug("TorConnect.beginAutoBootstrap()");
this._changeState(TorConnectState.AutoBootstrapping, countryCode);
},
@@ -1154,7 +1191,10 @@ export const TorConnect = (() => {
await mrpc.init();
this._countryCodes = await mrpc.circumvention_countries();
} catch (err) {
- console.log("An error occurred while fetching country codes", err);
+ lazy.logger.error(
+ "An error occurred while fetching country codes",
+ err
+ );
} finally {
mrpc.uninit();
}
@@ -1187,8 +1227,8 @@ export const TorConnect = (() => {
uriArray = uriVariant;
} else {
// about:tor as safe fallback
- console.error(
- `TorConnect: received unknown variant '${JSON.stringify(uriVariant)}'`
+ lazy.logger.error(
+ `Received unknown variant '${JSON.stringify(uriVariant)}'`
);
uriArray = ["about:tor"];
}
@@ -1209,9 +1249,7 @@ export const TorConnect = (() => {
// which redirect after bootstrapping
getURIsToLoad(uriVariant) {
const uris = this.fixupURIs(uriVariant);
- console.log(
- `TorConnect: Will load after bootstrap => [${uris.join(", ")}]`
- );
+ lazy.logger.debug(`Will load after bootstrap => [${uris.join(", ")}]`);
return uris.map(uri => this.getRedirectURL(uri));
},
};
=====================================
toolkit/modules/TorSettings.sys.mjs
=====================================
@@ -6,10 +6,9 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
- TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
- TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
Lox: "resource://gre/modules/Lox.sys.mjs",
TorParsers: "resource://gre/modules/TorParsers.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "logger", () => {
@@ -71,20 +70,6 @@ const TorSettingsPrefs = Object.freeze({
},
});
-/* Config Keys used to configure tor daemon */
-const TorConfigKeys = Object.freeze({
- useBridges: "UseBridges",
- bridgeList: "Bridge",
- socks4Proxy: "Socks4Proxy",
- socks5Proxy: "Socks5Proxy",
- socks5ProxyUsername: "Socks5ProxyUsername",
- socks5ProxyPassword: "Socks5ProxyPassword",
- httpsProxy: "HTTPSProxy",
- httpsProxyAuthenticator: "HTTPSProxyAuthenticator",
- reachableAddresses: "ReachableAddresses",
- clientTransportPlugin: "ClientTransportPlugin",
-});
-
export const TorBridgeSource = Object.freeze({
Invalid: -1,
BuiltIn: 0,
@@ -322,7 +307,7 @@ class TorSettingsImpl {
if (!val) {
return;
}
- const bridgeStrings = this.#getBuiltinBridges(val);
+ const bridgeStrings = this.getBuiltinBridges(val);
if (bridgeStrings.length) {
this.bridges.bridge_strings = bridgeStrings;
return;
@@ -659,14 +644,17 @@ class TorSettingsImpl {
* @param {string} pt The pluggable transport to return the lines for
* @returns {string[]} The bridge lines in random order
*/
- #getBuiltinBridges(pt) {
+ getBuiltinBridges(pt) {
+ if (!this.#allowUninitialized) {
+ this.#checkIfInitialized();
+ }
// Shuffle so that Tor Browser users do not all try the built-in bridges in
// the same order.
return arrayShuffle(this.#builtinBridges[pt] ?? []);
}
/**
- * Load or init our settings, and register observers.
+ * Load or init our settings.
*/
async init() {
if (this.#initialized) {
@@ -677,6 +665,7 @@ class TorSettingsImpl {
await this.#initInternal();
this.#initialized = true;
this.#initComplete();
+ Services.obs.notifyObservers(null, TorSettingsTopics.Ready);
} catch (e) {
this.#initFailed(e);
throw e;
@@ -698,45 +687,35 @@ class TorSettingsImpl {
lazy.logger.error("Could not load the built-in PT config.", e);
}
- // Initialize this before loading from prefs because we need Lox initialized before
- // any calls to Lox.getBridges()
+ // Initialize this before loading from prefs because we need Lox initialized
+ // before any calls to Lox.getBridges().
try {
await lazy.Lox.init();
} catch (e) {
lazy.logger.error("Could not initialize Lox.", e.type);
}
- // TODO: We could use a shared promise, and wait for it to be fullfilled
- // instead of Service.obs.
- if (lazy.TorLauncherUtil.shouldStartAndOwnTor) {
- // if the settings branch exists, load settings from prefs
- if (Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)) {
- // Do not want notifications for initially loaded prefs.
- this.freezeNotifications();
- try {
- this.#allowUninitialized = true;
- this.#loadFromPrefs();
- } finally {
- this.#allowUninitialized = false;
- this.#notificationQueue.clear();
- this.thawNotifications();
- }
- }
+ if (
+ lazy.TorLauncherUtil.shouldStartAndOwnTor &&
+ Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)
+ ) {
+ // Do not want notifications for initially loaded prefs.
+ this.freezeNotifications();
try {
- const provider = await lazy.TorProviderBuilder.build();
- if (provider.isRunning) {
- this.#handleProcessReady();
- // No need to add an observer to call this again.
- return;
- }
- } catch {}
-
- Services.obs.addObserver(this, lazy.TorProviderTopics.ProcessIsReady);
+ this.#allowUninitialized = true;
+ this.#loadFromPrefs();
+ } finally {
+ this.#allowUninitialized = false;
+ this.#notificationQueue.clear();
+ this.thawNotifications();
+ }
}
+
+ lazy.logger.info("Ready");
}
/**
- * Unload or uninit our settings, and unregister observers.
+ * Unload or uninit our settings.
*/
async uninit() {
await lazy.Lox.uninit();
@@ -764,34 +743,6 @@ class TorSettingsImpl {
return this.#initialized;
}
- /**
- * Wait for relevant life-cycle events to apply saved settings.
- */
- async observe(subject, topic, data) {
- lazy.logger.debug(`Observed ${topic}`);
-
- switch (topic) {
- case lazy.TorProviderTopics.ProcessIsReady:
- Services.obs.removeObserver(
- this,
- lazy.TorProviderTopics.ProcessIsReady
- );
- await this.#handleProcessReady();
- break;
- }
- }
-
- /**
- * Apply the settings once the tor provider is ready and notify any observer
- * that the settings can be used.
- */
- async #handleProcessReady() {
- // push down settings to tor
- await this.#applySettings(true);
- lazy.logger.info("Ready");
- Services.obs.notifyObservers(null, TorSettingsTopics.Ready);
- }
-
/**
* Load our settings from prefs.
*/
@@ -972,85 +923,14 @@ class TorSettingsImpl {
/**
* Push our settings down to the tor provider.
+ *
+ * Even though this introduces a circular depdency, it makes the API nicer for
+ * frontend consumers.
*/
async applySettings() {
this.#checkIfInitialized();
- return this.#applySettings(false);
- }
-
- /**
- * Internal implementation of applySettings that does not check if we are
- * initialized.
- */
- async #applySettings(allowUninitialized) {
- lazy.logger.debug("#applySettings()");
-
- this.#cleanupSettings();
-
- const settingsMap = new Map();
-
- // #applySettings can be called only when #allowUninitialized is false
- this.#allowUninitialized = allowUninitialized;
-
- try {
- /* Bridges */
- const haveBridges =
- this.bridges.enabled && !!this.bridges.bridge_strings.length;
- settingsMap.set(TorConfigKeys.useBridges, haveBridges);
- if (haveBridges) {
- settingsMap.set(TorConfigKeys.bridgeList, this.bridges.bridge_strings);
- } else {
- settingsMap.set(TorConfigKeys.bridgeList, null);
- }
-
- /* Proxy */
- settingsMap.set(TorConfigKeys.socks4Proxy, null);
- settingsMap.set(TorConfigKeys.socks5Proxy, null);
- settingsMap.set(TorConfigKeys.socks5ProxyUsername, null);
- settingsMap.set(TorConfigKeys.socks5ProxyPassword, null);
- settingsMap.set(TorConfigKeys.httpsProxy, null);
- settingsMap.set(TorConfigKeys.httpsProxyAuthenticator, null);
- if (this.proxy.enabled) {
- const address = this.proxy.address;
- const port = this.proxy.port;
- const username = this.proxy.username;
- const password = this.proxy.password;
-
- switch (this.proxy.type) {
- case TorProxyType.Socks4:
- settingsMap.set(TorConfigKeys.socks4Proxy, `${address}:${port}`);
- break;
- case TorProxyType.Socks5:
- settingsMap.set(TorConfigKeys.socks5Proxy, `${address}:${port}`);
- settingsMap.set(TorConfigKeys.socks5ProxyUsername, username);
- settingsMap.set(TorConfigKeys.socks5ProxyPassword, password);
- break;
- case TorProxyType.HTTPS:
- settingsMap.set(TorConfigKeys.httpsProxy, `${address}:${port}`);
- settingsMap.set(
- TorConfigKeys.httpsProxyAuthenticator,
- `${username}:${password}`
- );
- break;
- }
- }
-
- /* Firewall */
- if (this.firewall.enabled) {
- const reachableAddresses = this.firewall.allowed_ports
- .map(port => `*:${port}`)
- .join(",");
- settingsMap.set(TorConfigKeys.reachableAddresses, reachableAddresses);
- } else {
- settingsMap.set(TorConfigKeys.reachableAddresses, null);
- }
- } finally {
- this.#allowUninitialized = false;
- }
-
- /* Push to Tor */
const provider = await lazy.TorProviderBuilder.build();
- await provider.writeSettings(settingsMap);
+ await provider.writeSettings(this.getSettings());
}
/**
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/f6c7b7f2877a96e92de2d2403aae09543ef96fef...f8451ae06babd6094cac779d9cc649e55e439292
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/f6c7b7f2877a96e92de2d2403aae09543ef96fef...f8451ae06babd6094cac779d9cc649e55e439292
You're receiving this email because of your account on gitlab.torproject.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.torproject.org/pipermail/tbb-commits/attachments/20240311/45aa3e88/attachment-0001.htm>
More information about the tbb-commits
mailing list