Skip to content
Snippets Groups Projects
Verified Commit 9cecc61f authored by Pier Angelo Vendrame's avatar Pier Angelo Vendrame :jack_o_lantern:
Browse files

fixup! Bug 40933: Add tor-launcher functionality

Bug 42336: Rework the relationship between TorSettings and TorProvider.
parent f6c7b7f2
Branches
No related tags found
1 merge request!927Bug 42336: Rework the relationship between TorSettings and TorProvider.
......@@ -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]]);
}
/**
......
......@@ -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;
}
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])
/**
* 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);
}
// 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}`
);
break;
}
return value !== cachedValue;
});
// only write if new setting to save
if (newSettings.length) {
await this.#controller.setConf(Object.fromEntries(newSettings));
// 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() {
......
......@@ -9,7 +9,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
});
export const TorProviderTopics = Object.freeze({
ProcessIsReady: "TorProcessIsReady",
ProcessExited: "TorProcessExited",
BootstrapStatus: "TorBootstrapStatus",
BootstrapError: "TorBootstrapError",
......
......@@ -34,9 +34,9 @@ export class TorStartupService {
async #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.
// 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();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment