[tbb-commits] [Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] 8 commits: fixup! Bug 40933: Add tor-launcher functionality
richard (@richard)
git at gitlab.torproject.org
Mon Aug 7 19:56:04 UTC 2023
richard pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
d339153b by Pier Angelo Vendrame at 2023-08-07T18:36:05+02:00
fixup! Bug 40933: Add tor-launcher functionality
Use the new functions whenever possible, adjust some property names and
other minor fixes.
- - - - -
e8d3c0b8 by Pier Angelo Vendrame at 2023-08-07T18:36:14+02:00
fixup! Bug 40597: Implement TorSettings module
Changes needed for the new control port implementation.
Also, moved to ES modules and done some refactors on Moat.
- - - - -
c1b05a65 by Pier Angelo Vendrame at 2023-08-07T18:36:15+02:00
fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
Changes for the new control port implementation and following
ESMification.
- - - - -
d23cb300 by Pier Angelo Vendrame at 2023-08-07T18:36:15+02:00
fixup! Bug 30237: Add v3 onion services client authentication prompt
Changes for the new control port implementation.
- - - - -
c801d620 by Pier Angelo Vendrame at 2023-08-07T18:36:16+02:00
fixup! Bug 7494: Create local home page for TBB.
Use the TorProvider in TorCheckService
- - - - -
fa105e4f by Pier Angelo Vendrame at 2023-08-07T18:36:16+02:00
fixup! Bug 3455: Add DomainIsolator, for isolating circuit by domain.
Remove TorMonitorService and TorProtocolService references.
- - - - -
cd92f30e by Pier Angelo Vendrame at 2023-08-07T18:36:17+02:00
fixup! Bug 41668: Tweaks to the Base Browser updater for Tor Browser
Removed TorMonitorService reference
- - - - -
384dff97 by Pier Angelo Vendrame at 2023-08-07T18:36:17+02:00
fixup! Bug 40933: Add tor-launcher functionality
Remove the final references to TorMonitorService and TorProtocolService.
- - - - -
24 changed files:
- browser/components/abouttor/TorCheckService.sys.mjs
- browser/components/onionservices/content/authPrompt.js
- browser/components/onionservices/content/savedKeysDialog.js
- browser/components/torpreferences/content/builtinBridgeDialog.jsm
- browser/components/torpreferences/content/connectionPane.js
- browser/components/torpreferences/content/connectionSettingsDialog.jsm
- browser/components/torpreferences/content/provideBridgeDialog.jsm
- browser/components/torpreferences/content/requestBridgeDialog.jsm
- browser/components/torpreferences/content/torLogDialog.jsm
- browser/modules/BridgeDB.jsm → browser/modules/BridgeDB.sys.mjs
- browser/modules/Moat.jsm → browser/modules/Moat.sys.mjs
- browser/modules/TorConnect.jsm → browser/modules/TorConnect.sys.mjs
- browser/modules/TorSettings.jsm → browser/modules/TorSettings.sys.mjs
- browser/modules/moz.build
- toolkit/components/tor-launcher/TorBootstrapRequest.sys.mjs
- toolkit/components/tor-launcher/TorControlPort.sys.mjs
- toolkit/components/tor-launcher/TorDomainIsolator.sys.mjs
- − toolkit/components/tor-launcher/TorMonitorService.sys.mjs
- toolkit/components/tor-launcher/TorParsers.sys.mjs
- toolkit/components/tor-launcher/TorProtocolService.sys.mjs → toolkit/components/tor-launcher/TorProvider.sys.mjs
- toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
- toolkit/components/tor-launcher/TorStartupService.sys.mjs
- toolkit/components/tor-launcher/moz.build
- toolkit/mozapps/update/UpdateService.sys.mjs
Changes:
=====================================
browser/components/abouttor/TorCheckService.sys.mjs
=====================================
@@ -11,14 +11,9 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ConsoleAPI: "resource://gre/modules/Console.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
});
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorProtocolService",
- "resource://gre/modules/TorProtocolService.jsm"
-);
-
export const TorCheckService = {
kCheckNotInitiated: 0, // Possible values for status.
kCheckSuccessful: 1,
@@ -109,7 +104,7 @@ export const TorCheckService = {
let listeners;
try {
- listeners = await lazy.TorProtocolService.getSocksListeners();
+ listeners = await lazy.TorProviderBuilder.build().getSocksListeners();
} catch (e) {
this._logger.error("Failed to get the SOCKS listerner addresses.", e);
return false;
=====================================
browser/components/onionservices/content/authPrompt.js
=====================================
@@ -4,10 +4,13 @@
/* globals gBrowser, PopupNotifications, Services, XPCOMUtils */
+ChromeUtils.defineESModuleGetters(this, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
+
XPCOMUtils.defineLazyModuleGetters(this, {
OnionAuthUtil: "chrome://browser/content/onionservices/authUtil.jsm",
CommonUtils: "resource://services-common/utils.js",
- TorProtocolService: "resource://gre/modules/TorProtocolService.jsm",
TorStrings: "resource:///modules/TorStrings.jsm",
});
@@ -203,7 +206,8 @@ const OnionAuthPrompt = (function () {
let checkboxElem = this._getCheckboxElement();
let isPermanent = checkboxElem && checkboxElem.checked;
- TorProtocolService.onionAuthAdd(onionServiceId, base64key, isPermanent)
+ TorProviderBuilder.build()
+ .onionAuthAdd(onionServiceId, base64key, isPermanent)
.then(aResponse => {
// Success! Reload the page.
this._browser.sendMessageToActor(
=====================================
browser/components/onionservices/content/savedKeysDialog.js
=====================================
@@ -8,11 +8,9 @@ ChromeUtils.defineModuleGetter(
"resource:///modules/TorStrings.jsm"
);
-ChromeUtils.defineModuleGetter(
- this,
- "TorProtocolService",
- "resource://gre/modules/TorProtocolService.jsm"
-);
+ChromeUtils.defineESModuleGetters(this, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
var gOnionServicesSavedKeysDialog = {
selector: {
@@ -54,6 +52,7 @@ var gOnionServicesSavedKeysDialog = {
await this._deleteOneKey(indexesToDelete[i]);
}
} catch (e) {
+ console.error("Removing a saved key failed", e);
if (e.torMessage) {
this._showError(e.torMessage);
} else {
@@ -125,22 +124,16 @@ var gOnionServicesSavedKeysDialog = {
try {
this._tree.view = this;
- const keyInfoList = await TorProtocolService.onionAuthViewKeys();
+ const keyInfoList = await TorProviderBuilder.build().onionAuthViewKeys();
if (keyInfoList) {
// Filter out temporary keys.
- this._keyInfoList = keyInfoList.filter(aKeyInfo => {
- if (!aKeyInfo.Flags) {
- return false;
- }
-
- const flags = aKeyInfo.Flags.split(",");
- return flags.includes("Permanent");
- });
-
+ this._keyInfoList = keyInfoList.filter(aKeyInfo =>
+ aKeyInfo.flags?.includes("Permanent")
+ );
// Sort by the .onion address.
this._keyInfoList.sort((aObj1, aObj2) => {
- const hsAddr1 = aObj1.hsAddress.toLowerCase();
- const hsAddr2 = aObj2.hsAddress.toLowerCase();
+ const hsAddr1 = aObj1.address.toLowerCase();
+ const hsAddr2 = aObj2.address.toLowerCase();
if (hsAddr1 < hsAddr2) {
return -1;
}
@@ -164,7 +157,7 @@ var gOnionServicesSavedKeysDialog = {
// This method may throw; callers should catch errors.
async _deleteOneKey(aIndex) {
const keyInfoObj = this._keyInfoList[aIndex];
- await TorProtocolService.onionAuthRemove(keyInfoObj.hsAddress);
+ await TorProviderBuilder.build().onionAuthRemove(keyInfoObj.address);
this._tree.view.selection.clearRange(aIndex, aIndex);
this._keyInfoList.splice(aIndex, 1);
this._tree.rowCountChanged(aIndex + 1, -1);
@@ -193,26 +186,20 @@ var gOnionServicesSavedKeysDialog = {
// XUL tree widget view implementation.
get rowCount() {
- return this._keyInfoList ? this._keyInfoList.length : 0;
+ return this._keyInfoList?.length ?? 0;
},
getCellText(aRow, aCol) {
- let val = "";
if (this._keyInfoList && aRow < this._keyInfoList.length) {
const keyInfo = this._keyInfoList[aRow];
if (aCol.id.endsWith("-siteCol")) {
- val = keyInfo.hsAddress;
+ return keyInfo.address;
} else if (aCol.id.endsWith("-keyCol")) {
- val = keyInfo.typeAndKey;
- // Omit keyType because it is always "x25519".
- const idx = val.indexOf(":");
- if (idx > 0) {
- val = val.substring(idx + 1);
- }
+ // keyType is always "x25519", so do not show it.
+ return keyInfo.keyBlob;
}
}
-
- return val;
+ return "";
},
isSeparator(index) {
=====================================
browser/components/torpreferences/content/builtinBridgeDialog.jsm
=====================================
@@ -7,10 +7,10 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
const { TorSettings, TorBridgeSource, TorBuiltinBridgeTypes } =
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
+ ChromeUtils.importESModule("resource:///modules/TorSettings.sys.mjs");
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
- "resource:///modules/TorConnect.jsm"
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
+ "resource:///modules/TorConnect.sys.mjs"
);
class BuiltinBridgeDialog {
=====================================
browser/components/torpreferences/content/connectionPane.js
=====================================
@@ -12,20 +12,17 @@ const { setTimeout, clearTimeout } = ChromeUtils.import(
);
const { TorSettings, TorSettingsTopics, TorSettingsData, TorBridgeSource } =
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
+ ChromeUtils.importESModule("resource:///modules/TorSettings.sys.mjs");
const { TorParsers } = ChromeUtils.importESModule(
"resource://gre/modules/TorParsers.sys.mjs"
);
-const { TorProtocolService } = ChromeUtils.importESModule(
- "resource://gre/modules/TorProtocolService.sys.mjs"
-);
-const { TorMonitorService, TorMonitorTopics } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
+const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule(
+ "resource://gre/modules/TorProviderBuilder.sys.mjs"
);
const { TorConnect, TorConnectTopics, TorConnectState, TorCensorshipLevel } =
- ChromeUtils.import("resource:///modules/TorConnect.jsm");
+ ChromeUtils.importESModule("resource:///modules/TorConnect.sys.mjs");
const { TorLogDialog } = ChromeUtils.import(
"chrome://browser/content/torpreferences/torLogDialog.jsm"
@@ -51,7 +48,9 @@ const { ProvideBridgeDialog } = ChromeUtils.import(
"chrome://browser/content/torpreferences/provideBridgeDialog.jsm"
);
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
+const { MoatRPC } = ChromeUtils.importESModule(
+ "resource:///modules/Moat.sys.mjs"
+);
const { QRCode } = ChromeUtils.import("resource://gre/modules/QRCode.jsm");
@@ -156,7 +155,7 @@ const gConnectionPane = (function () {
_populateXUL() {
// saves tor settings to disk when navigate away from about:preferences
window.addEventListener("blur", val => {
- TorProtocolService.flushSettings();
+ TorProviderBuilder.build().flushSettings();
});
document
@@ -751,7 +750,7 @@ const gConnectionPane = (function () {
// TODO: We could make sure TorSettings is in sync by monitoring also
// changes of settings. At that point, we could query it, instead of
// doing a query over the control port.
- const bridge = TorMonitorService.currentBridge;
+ const bridge = TorProviderBuilder.build().currentBridge;
if (bridge?.fingerprint !== this._currentBridgeId) {
this._currentBridgeId = bridge?.fingerprint ?? null;
this._updateConnectedBridges();
@@ -850,7 +849,7 @@ const gConnectionPane = (function () {
});
Services.obs.addObserver(this, TorConnectTopics.StateChange);
- Services.obs.addObserver(this, TorMonitorTopics.BridgeChanged);
+ Services.obs.addObserver(this, TorProviderTopics.BridgeChanged);
Services.obs.addObserver(this, "intl:app-locales-changed");
},
@@ -875,7 +874,7 @@ const gConnectionPane = (function () {
// unregister our observer topics
Services.obs.removeObserver(this, TorSettingsTopics.SettingChanged);
Services.obs.removeObserver(this, TorConnectTopics.StateChange);
- Services.obs.removeObserver(this, TorMonitorTopics.BridgeChanged);
+ Services.obs.removeObserver(this, TorProviderTopics.BridgeChanged);
Services.obs.removeObserver(this, "intl:app-locales-changed");
},
@@ -907,7 +906,7 @@ const gConnectionPane = (function () {
this.onStateChange();
break;
}
- case TorMonitorTopics.BridgeChanged: {
+ case TorProviderTopics.BridgeChanged: {
if (data?.fingerprint !== this._currentBridgeId) {
this._checkConnectedBridge();
}
=====================================
browser/components/torpreferences/content/connectionSettingsDialog.jsm
=====================================
@@ -2,8 +2,8 @@
var EXPORTED_SYMBOLS = ["ConnectionSettingsDialog"];
-const { TorSettings, TorProxyType } = ChromeUtils.import(
- "resource:///modules/TorSettings.jsm"
+const { TorSettings, TorProxyType } = ChromeUtils.importESModule(
+ "resource:///modules/TorSettings.sys.mjs"
);
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
=====================================
browser/components/torpreferences/content/provideBridgeDialog.jsm
=====================================
@@ -6,12 +6,12 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
-const { TorSettings, TorBridgeSource } = ChromeUtils.import(
- "resource:///modules/TorSettings.jsm"
+const { TorSettings, TorBridgeSource } = ChromeUtils.importESModule(
+ "resource:///modules/TorSettings.sys.mjs"
);
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
- "resource:///modules/TorConnect.jsm"
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
+ "resource:///modules/TorConnect.sys.mjs"
);
class ProvideBridgeDialog {
=====================================
browser/components/torpreferences/content/requestBridgeDialog.jsm
=====================================
@@ -4,11 +4,13 @@ var EXPORTED_SYMBOLS = ["RequestBridgeDialog"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const { BridgeDB } = ChromeUtils.import("resource:///modules/BridgeDB.jsm");
+const { BridgeDB } = ChromeUtils.importESModule(
+ "resource:///modules/BridgeDB.sys.mjs"
+);
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
- "resource:///modules/TorConnect.jsm"
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
+ "resource:///modules/TorConnect.sys.mjs"
);
class RequestBridgeDialog {
=====================================
browser/components/torpreferences/content/torLogDialog.jsm
=====================================
@@ -2,12 +2,12 @@
var EXPORTED_SYMBOLS = ["TorLogDialog"];
-const { setTimeout, clearTimeout } = ChromeUtils.import(
- "resource://gre/modules/Timer.jsm"
+const { setTimeout, clearTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
);
-const { TorMonitorService } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
+const { TorProviderBuilder } = ChromeUtils.importESModule(
+ "resource://gre/modules/TorProviderBuilder.sys.mjs"
);
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
@@ -56,7 +56,7 @@ class TorLogDialog {
}, RESTORE_TIME);
});
- this._logTextarea.value = TorMonitorService.getLog();
+ this._logTextarea.value = TorProviderBuilder.build().getLog();
}
init(window, aDialog) {
=====================================
browser/modules/BridgeDB.jsm → browser/modules/BridgeDB.sys.mjs
=====================================
@@ -1,10 +1,14 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = ["BridgeDB"];
+const lazy = {};
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
+ChromeUtils.defineESModuleGetters(lazy, {
+ MoatRPC: "resource:///modules/Moat.sys.mjs",
+});
-var BridgeDB = {
+export var BridgeDB = {
_moatRPC: null,
_challenge: null,
_image: null,
@@ -20,7 +24,7 @@ var BridgeDB = {
async submitCaptchaGuess(solution) {
if (!this._moatRPC) {
- this._moatRPC = new MoatRPC();
+ this._moatRPC = new lazy.MoatRPC();
await this._moatRPC.init();
}
@@ -37,7 +41,7 @@ var BridgeDB = {
async requestNewCaptchaImage() {
try {
if (!this._moatRPC) {
- this._moatRPC = new MoatRPC();
+ this._moatRPC = new lazy.MoatRPC();
await this._moatRPC.init();
}
=====================================
browser/modules/Moat.jsm → browser/modules/Moat.sys.mjs
=====================================
@@ -1,24 +1,19 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = ["MoatRPC"];
+import {
+ TorSettings,
+ TorBridgeSource,
+} from "resource:///modules/TorSettings.sys.mjs";
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const lazy = {};
-const { Subprocess } = ChromeUtils.import(
- "resource://gre/modules/Subprocess.jsm"
-);
-
-const { TorLauncherUtil } = ChromeUtils.import(
- "resource://gre/modules/TorLauncherUtil.jsm"
-);
-
-const { TorProtocolService } = ChromeUtils.import(
- "resource://gre/modules/TorProtocolService.jsm"
-);
-
-const { TorSettings, TorBridgeSource } = ChromeUtils.import(
- "resource:///modules/TorSettings.jsm"
-);
+ChromeUtils.defineESModuleGetters(lazy, {
+ Subprocess: "resource://gre/modules/Subprocess.sys.mjs",
+ TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
const TorLauncherPrefs = Object.freeze({
bridgedb_front: "extensions.torlauncher.bridgedb_front",
@@ -26,73 +21,54 @@ const TorLauncherPrefs = Object.freeze({
moat_service: "extensions.torlauncher.moat_service",
});
-// Config keys used to query tor daemon properties
-const TorConfigKeys = Object.freeze({
- clientTransportPlugin: "ClientTransportPlugin",
-});
-
//
// Launches and controls the PT process lifetime
//
class MeekTransport {
- constructor() {
- this._inited = false;
- this._meekClientProcess = null;
- this._meekProxyType = null;
- this._meekProxyAddress = null;
- this._meekProxyPort = 0;
- this._meekProxyUsername = null;
- this._meekProxyPassword = null;
- }
+ // These members are used by consumers to setup the proxy to do requests over
+ // meek. They are passed to newProxyInfoWithAuth.
+ proxyType = null;
+ proxyAddress = null;
+ proxyPort = 0;
+ proxyUsername = null;
+ proxyPassword = null;
+
+ #inited = false;
+ #meekClientProcess = null;
// launches the meekprocess
async init() {
// ensure we haven't already init'd
- if (this._inited) {
+ if (this.#inited) {
throw new Error("MeekTransport: Already initialized");
}
- // cleanup function for killing orphaned pt process
- let onException = () => {};
try {
// figure out which pluggable transport to use
const supportedTransports = ["meek", "meek_lite"];
- let transportPlugins = await TorProtocolService.readStringArraySetting(
- TorConfigKeys.clientTransportPlugin
+ const proxy = (
+ await lazy.TorProviderBuilder.build().getPluggableTransports()
+ ).find(
+ pt =>
+ pt.type === "exec" &&
+ supportedTransports.some(t => pt.transports.includes(t))
);
+ if (!proxy) {
+ throw new Error("No supported transport found.");
+ }
- let { meekTransport, meekClientPath, meekClientArgs } = (() => {
- for (const line of transportPlugins) {
- let tokens = line.split(" ");
- if (tokens.length > 2 && tokens[1] == "exec") {
- let transportArray = tokens[0].split(",").map(aStr => aStr.trim());
- let transport = transportArray.find(aTransport =>
- supportedTransports.includes(aTransport)
- );
-
- if (transport != undefined) {
- return {
- meekTransport: transport,
- meekClientPath: tokens[2],
- meekClientArgs: tokens.slice(3),
- };
- }
- }
- }
-
- return {
- meekTransport: null,
- meekClientPath: null,
- meekClientArgs: null,
- };
- })();
-
+ const meekTransport = proxy.transports.find(t =>
+ supportedTransports.includes(t)
+ );
// Convert meek client path to absolute path if necessary
- let meekWorkDir = TorLauncherUtil.getTorFile("pt-startup-dir", false);
- if (TorLauncherUtil.isPathRelative(meekClientPath)) {
- let meekPath = meekWorkDir.clone();
- meekPath.appendRelativePath(meekClientPath);
- meekClientPath = meekPath.path;
+ const meekWorkDir = lazy.TorLauncherUtil.getTorFile(
+ "pt-startup-dir",
+ false
+ );
+ if (lazy.TorLauncherUtil.isPathRelative(proxy.pathToBinary)) {
+ const meekPath = meekWorkDir.clone();
+ meekPath.appendRelativePath(proxy.pathToBinary);
+ proxy.pathToBinary = meekPath.path;
}
// Construct the per-connection arguments.
@@ -105,16 +81,13 @@ class MeekTransport {
// First the "<Key>=<Value>" formatted arguments MUST be escaped,
// such that all backslash, equal sign, and semicolon characters
// are escaped with a backslash.
- let escapeArgValue = aValue => {
- if (!aValue) {
- return "";
- }
-
- let rv = aValue.replace(/\\/g, "\\\\");
- rv = rv.replace(/=/g, "\\=");
- rv = rv.replace(/;/g, "\\;");
- return rv;
- };
+ const escapeArgValue = aValue =>
+ aValue
+ ? aValue
+ .replaceAll("\\", "\\\\")
+ .replaceAll("=", "\\=")
+ .replaceAll(";", "\\;")
+ : "";
if (meekReflector) {
meekClientEscapedArgs += "url=";
@@ -132,10 +105,10 @@ class MeekTransport {
}
// Setup env and start meek process
- let ptStateDir = TorLauncherUtil.getTorFile("tordatadir", false);
+ const ptStateDir = lazy.TorLauncherUtil.getTorFile("tordatadir", false);
ptStateDir.append("pt_state"); // Match what tor uses.
- let envAdditions = {
+ const envAdditions = {
TOR_PT_MANAGED_TRANSPORT_VER: "1",
TOR_PT_STATE_LOCATION: ptStateDir.path,
TOR_PT_EXIT_ON_STDIN_CLOSE: "1",
@@ -145,9 +118,9 @@ class MeekTransport {
envAdditions.TOR_PT_PROXY = TorSettings.proxy.uri;
}
- let opts = {
- command: meekClientPath,
- arguments: meekClientArgs,
+ const opts = {
+ command: proxy.pathToBinary,
+ arguments: proxy.options.split(/s+/),
workdir: meekWorkDir.path,
environmentAppend: true,
environment: envAdditions,
@@ -155,27 +128,23 @@ class MeekTransport {
};
// Launch meek client
- let meekClientProcess = await Subprocess.call(opts);
- // kill our process if exception is thrown
- onException = () => {
- meekClientProcess.kill();
- };
+ this.#meekClientProcess = await lazy.Subprocess.call(opts);
// Callback chain for reading stderr
- let stderrLogger = async () => {
- if (this._meekClientProcess) {
- let errString = await this._meekClientProcess.stderr.readString();
- console.log(`MeekTransport: stderr => ${errString}`);
- await stderrLogger();
+ const stderrLogger = async () => {
+ while (this.#meekClientProcess) {
+ const errString = await this.#meekClientProcess.stderr.readString();
+ if (errString) {
+ console.log(`MeekTransport: stderr => ${errString}`);
+ }
}
};
stderrLogger();
// Read pt's stdout until terminal (CMETHODS DONE) is reached
// returns array of lines for parsing
- let getInitLines = async (stdout = "") => {
- let string = await meekClientProcess.stdout.readString();
- stdout += string;
+ const getInitLines = async (stdout = "") => {
+ stdout += await this.#meekClientProcess.stdout.readString();
// look for the final message
const CMETHODS_DONE = "CMETHODS DONE";
@@ -188,20 +157,16 @@ class MeekTransport {
};
// read our lines from pt's stdout
- let meekInitLines = await getInitLines();
+ const meekInitLines = await getInitLines();
// tokenize our pt lines
- let meekInitTokens = meekInitLines.map(line => {
- let tokens = line.split(" ");
+ const meekInitTokens = meekInitLines.map(line => {
+ const tokens = line.split(" ");
return {
keyword: tokens[0],
args: tokens.slice(1),
};
});
- let meekProxyType = null;
- let meekProxyAddr = null;
- let meekProxyPort = 0;
-
// parse our pt tokens
for (const { keyword, args } of meekInitTokens) {
const argsJoined = args.join(" ");
@@ -251,9 +216,9 @@ class MeekTransport {
}
// convert proxy type to strings used by protocol-proxy-servce
- meekProxyType = proxyType === "socks5" ? "socks" : "socks4";
- meekProxyAddr = addr;
- meekProxyPort = port;
+ this.proxyType = proxyType === "socks5" ? "socks" : "socks4";
+ this.proxyAddress = addr;
+ this.proxyPort = port;
break;
}
@@ -278,49 +243,47 @@ class MeekTransport {
}
}
- this._meekClientProcess = meekClientProcess;
// register callback to cleanup on process exit
- this._meekClientProcess.wait().then(exitObj => {
- this._meekClientProcess = null;
+ this.#meekClientProcess.wait().then(exitObj => {
+ this.#meekClientProcess = null;
this.uninit();
});
- this._meekProxyType = meekProxyType;
- this._meekProxyAddress = meekProxyAddr;
- this._meekProxyPort = meekProxyPort;
-
// socks5
- if (meekProxyType === "socks") {
+ if (this.proxyType === "socks") {
if (meekClientEscapedArgs.length <= 255) {
- this._meekProxyUsername = meekClientEscapedArgs;
- this._meekProxyPassword = "\x00";
+ this.proxyUsername = meekClientEscapedArgs;
+ this.proxyPassword = "\x00";
} else {
- this._meekProxyUsername = meekClientEscapedArgs.substring(0, 255);
- this._meekProxyPassword = meekClientEscapedArgs.substring(255);
+ this.proxyUsername = meekClientEscapedArgs.substring(0, 255);
+ this.proxyPassword = meekClientEscapedArgs.substring(255);
}
// socks4
} else {
- this._meekProxyUsername = meekClientEscapedArgs;
- this._meekProxyPassword = undefined;
+ this.proxyUsername = meekClientEscapedArgs;
+ this.proxyPassword = undefined;
}
- this._inited = true;
+ this.#inited = true;
} catch (ex) {
- onException();
+ if (this.#meekClientProcess) {
+ this.#meekClientProcess.kill();
+ this.#meekClientProcess = null;
+ }
throw ex;
}
}
async uninit() {
- this._inited = false;
-
- await this._meekClientProcess?.kill();
- this._meekClientProcess = null;
- this._meekProxyType = null;
- this._meekProxyAddress = null;
- this._meekProxyPort = 0;
- this._meekProxyUsername = null;
- this._meekProxyPassword = null;
+ this.#inited = false;
+
+ await this.#meekClientProcess?.kill();
+ this.#meekClientProcess = null;
+ this.proxyType = null;
+ this.proxyAddress = null;
+ this.proxyPort = 0;
+ this.proxyUsername = null;
+ this.proxyPassword = null;
}
}
@@ -328,21 +291,25 @@ class MeekTransport {
// Callback object with a cached promise for the returned Moat data
//
class MoatResponseListener {
+ #response = "";
+ #responsePromise;
+ #resolve;
+ #reject;
constructor() {
- this._response = "";
+ this.#response = "";
// we need this promise here because await nsIHttpChannel::asyncOpen does
// not return only once the request is complete, it seems to return
// after it begins, so we have to get the result from this listener object.
// This promise is only resolved once onStopRequest is called
- this._responsePromise = new Promise((resolve, reject) => {
- this._resolve = resolve;
- this._reject = reject;
+ this.#responsePromise = new Promise((resolve, reject) => {
+ this.#resolve = resolve;
+ this.#reject = reject;
});
}
// callers wait on this for final response
response() {
- return this._responsePromise;
+ return this.#responsePromise;
}
// noop
@@ -352,16 +319,17 @@ class MoatResponseListener {
onStopRequest(request, status) {
try {
if (!Components.isSuccessCode(status)) {
- const errorMessage = TorLauncherUtil.getLocalizedStringForError(status);
- this._reject(new Error(errorMessage));
+ const errorMessage =
+ lazy.TorLauncherUtil.getLocalizedStringForError(status);
+ this.#reject(new Error(errorMessage));
}
if (request.responseStatus != 200) {
- this._reject(new Error(request.responseStatusText));
+ this.#reject(new Error(request.responseStatusText));
}
} catch (err) {
- this._reject(err);
+ this.#reject(err);
}
- this._resolve(this._response);
+ this.#resolve(this.#response);
}
// read response data
@@ -370,30 +338,32 @@ class MoatResponseListener {
"@mozilla.org/scriptableinputstream;1"
].createInstance(Ci.nsIScriptableInputStream);
scriptableStream.init(stream);
- this._response += scriptableStream.read(length);
+ this.#response += scriptableStream.read(length);
}
}
class InternetTestResponseListener {
+ #promise;
+ #resolve;
+ #reject;
constructor() {
- this._promise = new Promise((resolve, reject) => {
- this._resolve = resolve;
- this._reject = reject;
+ this.#promise = new Promise((resolve, reject) => {
+ this.#resolve = resolve;
+ this.#reject = reject;
});
}
// callers wait on this for final response
get status() {
- return this._promise;
+ return this.#promise;
}
onStartRequest(request) {}
// resolve or reject our Promise
onStopRequest(request, status) {
- let statuses = {};
try {
- statuses = {
+ const statuses = {
components: status,
successful: Components.isSuccessCode(status),
};
@@ -408,56 +378,51 @@ class InternetTestResponseListener {
err
);
}
+ this.#resolve(statuses);
} catch (err) {
- this._reject(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
+ // 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() {
- this._meekTransport = null;
- this._inited = false;
- }
+export class MoatRPC {
+ #inited = false;
+ #meekTransport = null;
get inited() {
- return this._inited;
+ return this.#inited;
}
async init() {
- if (this._inited) {
+ if (this.#inited) {
throw new Error("MoatRPC: Already initialized");
}
let meekTransport = new MeekTransport();
await meekTransport.init();
- this._meekTransport = meekTransport;
- this._inited = true;
+ this.#meekTransport = meekTransport;
+ this.#inited = true;
}
async uninit() {
- await this._meekTransport?.uninit();
- this._meekTransport = null;
- this._inited = false;
+ await this.#meekTransport?.uninit();
+ this.#meekTransport = null;
+ this.#inited = false;
}
- _makeHttpHandler(uriString) {
- if (!this._inited) {
+ #makeHttpHandler(uriString) {
+ if (!this.#inited) {
throw new Error("MoatRPC: Not initialized");
}
- const proxyType = this._meekTransport._meekProxyType;
- const proxyAddress = this._meekTransport._meekProxyAddress;
- const proxyPort = this._meekTransport._meekProxyPort;
- const proxyUsername = this._meekTransport._meekProxyUsername;
- const proxyPassword = this._meekTransport._meekProxyPassword;
+ const { proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword } =
+ this.#meekTransport;
const proxyPS = Cc[
"@mozilla.org/network/protocol-proxy-service;1"
@@ -511,11 +476,11 @@ class MoatRPC {
return ch;
}
- async _makeRequest(procedure, args) {
+ async #makeRequest(procedure, args) {
const procedureURIString = `${Services.prefs.getStringPref(
TorLauncherPrefs.moat_service
)}/${procedure}`;
- const ch = this._makeHttpHandler(procedureURIString);
+ const ch = this.#makeHttpHandler(procedureURIString);
// Arrange for the POST data to be sent.
const argsJson = JSON.stringify(args);
@@ -544,7 +509,7 @@ class MoatRPC {
const uri = `${Services.prefs.getStringPref(
TorLauncherPrefs.moat_service
)}/circumvention/countries`;
- const ch = this._makeHttpHandler(uri);
+ const ch = this.#makeHttpHandler(uri);
ch.requestMethod = "HEAD";
const listener = new InternetTestResponseListener();
@@ -582,7 +547,7 @@ class MoatRPC {
},
],
};
- const response = await this._makeRequest("fetch", args);
+ const response = await this.#makeRequest("fetch", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
@@ -623,7 +588,7 @@ class MoatRPC {
},
],
};
- const response = await this._makeRequest("check", args);
+ const response = await this.#makeRequest("check", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
@@ -642,7 +607,7 @@ class MoatRPC {
// Convert received settings object to format used by TorSettings module
// In the event of error, just return null
- _fixupSettings(settings) {
+ #fixupSettings(settings) {
try {
let retval = TorSettings.defaultSettings();
if ("bridges" in settings) {
@@ -691,11 +656,11 @@ class MoatRPC {
// Converts a list of settings objects received from BridgeDB to a list of settings objects
// understood by the TorSettings module
// In the event of error, returns and empty list
- _fixupSettingsList(settingsList) {
+ #fixupSettingsList(settingsList) {
try {
let retval = [];
for (let settings of settingsList) {
- settings = this._fixupSettings(settings);
+ settings = this.#fixupSettings(settings);
if (settings != null) {
retval.push(settings);
}
@@ -724,7 +689,7 @@ class MoatRPC {
transports: transports ? transports : [],
country,
};
- const response = await this._makeRequest("circumvention/settings", args);
+ const response = await this.#makeRequest("circumvention/settings", args);
let settings = {};
if ("errors" in response) {
const code = response.errors[0].code;
@@ -739,7 +704,7 @@ class MoatRPC {
throw new Error(`MoatRPC: ${detail} (${code})`);
} else if ("settings" in response) {
- settings.settings = this._fixupSettingsList(response.settings);
+ settings.settings = this.#fixupSettingsList(response.settings);
}
if ("country" in response) {
settings.country = response.country;
@@ -753,7 +718,7 @@ class MoatRPC {
// for
async circumvention_countries() {
const args = {};
- return this._makeRequest("circumvention/countries", args);
+ return this.#makeRequest("circumvention/countries", args);
}
// Request a copy of the builtin bridges, takes the following parameters:
@@ -766,7 +731,7 @@ class MoatRPC {
const args = {
transports: transports ? transports : [],
};
- const response = await this._makeRequest("circumvention/builtin", args);
+ const response = await this.#makeRequest("circumvention/builtin", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
@@ -791,13 +756,13 @@ class MoatRPC {
const args = {
transports: transports ? transports : [],
};
- const response = await this._makeRequest("circumvention/defaults", args);
+ const response = await this.#makeRequest("circumvention/defaults", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
throw new Error(`MoatRPC: ${detail} (${code})`);
} else if ("settings" in response) {
- return this._fixupSettingsList(response.settings);
+ return this.#fixupSettingsList(response.settings);
}
return [];
}
=====================================
browser/modules/TorConnect.jsm → browser/modules/TorConnect.sys.mjs
=====================================
@@ -1,36 +1,32 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = [
- "InternetStatus",
- "TorConnect",
- "TorConnectTopics",
- "TorConnectState",
-];
+import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const lazy = {};
-const { setTimeout, clearTimeout } = ChromeUtils.import(
- "resource://gre/modules/Timer.jsm"
-);
+ChromeUtils.defineESModuleGetters(lazy, {
+ MoatRPC: "resource:///modules/Moat.sys.mjs",
+ TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
-const { BrowserWindowTracker } = ChromeUtils.import(
+// TODO: Should we move this to the about:torconnect actor?
+ChromeUtils.defineModuleGetter(
+ lazy,
+ "BrowserWindowTracker",
"resource:///modules/BrowserWindowTracker.jsm"
);
-const { TorMonitorService } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
-);
-const { TorBootstrapRequest } = ChromeUtils.import(
- "resource://gre/modules/TorBootstrapRequest.jsm"
-);
-
-const { TorSettings, TorSettingsTopics, TorBuiltinBridgeTypes } =
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
+import {
+ TorSettings,
+ TorSettingsTopics,
+ TorBuiltinBridgeTypes,
+} from "resource:///modules/TorSettings.sys.mjs";
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
-
const TorTopics = Object.freeze({
LogHasWarnOrErr: "TorLogHasWarnOrErr",
ProcessExited: "TorProcessExited",
@@ -46,7 +42,7 @@ const TorConnectPrefs = Object.freeze({
allow_internet_test: "torbrowser.bootstrap.allow_internet_test",
});
-const TorConnectState = Object.freeze({
+export const TorConnectState = Object.freeze({
/* Our initial state */
Initial: "Initial",
/* In-between initial boot and bootstrapping, users can change tor network settings during this state */
@@ -156,7 +152,7 @@ const TorConnectStateTransitions = Object.freeze(
);
/* Topics Notified by the TorConnect module */
-const TorConnectTopics = Object.freeze({
+export const TorConnectTopics = Object.freeze({
StateChange: "torconnect:state-change",
BootstrapProgress: "torconnect:bootstrap-progress",
BootstrapComplete: "torconnect:bootstrap-complete",
@@ -238,7 +234,7 @@ const debug_sleep = async ms => {
});
};
-const InternetStatus = Object.freeze({
+export const InternetStatus = Object.freeze({
Unknown: -1,
Offline: 0,
Online: 1,
@@ -302,7 +298,7 @@ class InternetTest {
// waiting both for the bootstrap, and for the Internet test.
// However, managing Moat with async/await is much easier as it avoids a
// callback hell, and it makes extra explicit that we are uniniting it.
- const mrpc = new MoatRPC();
+ const mrpc = new lazy.MoatRPC();
let status = null;
let error = null;
try {
@@ -340,7 +336,7 @@ class InternetTest {
}
}
-const TorConnect = (() => {
+export const TorConnect = (() => {
let retval = {
_state: TorConnectState.Initial,
_bootstrapProgress: 0,
@@ -459,7 +455,7 @@ const TorConnect = (() => {
return;
}
- const tbr = new TorBootstrapRequest();
+ const tbr = new lazy.TorBootstrapRequest();
const internetTest = new InternetTest();
let cancelled = false;
@@ -604,7 +600,7 @@ const TorConnect = (() => {
// lookup user's potential censorship circumvention settings from Moat service
try {
- this.mrpc = new MoatRPC();
+ this.mrpc = new lazy.MoatRPC();
await this.mrpc.init();
if (this.transitioning) {
@@ -678,7 +674,7 @@ const TorConnect = (() => {
await TorSettings.applySettings();
// build out our bootstrap request
- const tbr = new TorBootstrapRequest();
+ const tbr = new lazy.TorBootstrapRequest();
tbr.onbootstrapstatus = (progress, status) => {
TorConnect._updateBootstrapStatus(progress, status);
};
@@ -915,7 +911,7 @@ const TorConnect = (() => {
* @type {boolean}
*/
get enabled() {
- return TorMonitorService.ownsTorDaemon;
+ return lazy.TorProviderBuilder.build().ownsTorDaemon;
},
get shouldShowTorConnect() {
@@ -1053,7 +1049,7 @@ const TorConnect = (() => {
Further external commands and helper methods
*/
openTorPreferences() {
- const win = BrowserWindowTracker.getTopWindow();
+ const win = lazy.BrowserWindowTracker.getTopWindow();
win.switchToTabHavingURI("about:preferences#connection", true);
},
@@ -1073,7 +1069,7 @@ const TorConnect = (() => {
* begin AutoBootstrapping, if possible.
*/
openTorConnect(options) {
- const win = BrowserWindowTracker.getTopWindow();
+ const win = lazy.BrowserWindowTracker.getTopWindow();
win.switchToTabHavingURI("about:torconnect", true, {
ignoreQueryString: true,
});
@@ -1094,7 +1090,7 @@ const TorConnect = (() => {
},
viewTorLogs() {
- const win = BrowserWindowTracker.getTopWindow();
+ const win = lazy.BrowserWindowTracker.getTopWindow();
win.switchToTabHavingURI("about:preferences#connection-viewlogs", true);
},
@@ -1104,7 +1100,7 @@ const TorConnect = (() => {
if (this._countryCodes.length) {
return this._countryCodes;
}
- const mrpc = new MoatRPC();
+ const mrpc = new lazy.MoatRPC();
try {
await mrpc.init();
this._countryCodes = await mrpc.circumvention_countries();
=====================================
browser/modules/TorSettings.jsm → browser/modules/TorSettings.sys.mjs
=====================================
@@ -1,36 +1,22 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = [
- "TorSettings",
- "TorSettingsTopics",
- "TorSettingsData",
- "TorBridgeSource",
- "TorBuiltinBridgeTypes",
- "TorProxyType",
-];
+const lazy = {};
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-const { TorMonitorService } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
-);
-const { TorProtocolService } = ChromeUtils.import(
- "resource://gre/modules/TorProtocolService.jsm"
-);
-
-/* tor-launcher observer topics */
-const TorTopics = Object.freeze({
- ProcessIsReady: "TorProcessIsReady",
+ChromeUtils.defineESModuleGetters(lazy, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+ TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
});
/* TorSettings observer topics */
-const TorSettingsTopics = Object.freeze({
+export const TorSettingsTopics = Object.freeze({
Ready: "torsettings:ready",
SettingChanged: "torsettings:setting-changed",
});
/* TorSettings observer data (for SettingChanged topic) */
-const TorSettingsData = Object.freeze({
+export const TorSettingsData = Object.freeze({
QuickStartEnabled: "torsettings:quickstart_enabled",
});
@@ -98,21 +84,21 @@ const TorConfigKeys = Object.freeze({
clientTransportPlugin: "ClientTransportPlugin",
});
-const TorBridgeSource = Object.freeze({
+export const TorBridgeSource = Object.freeze({
Invalid: -1,
BuiltIn: 0,
BridgeDB: 1,
UserProvided: 2,
});
-const TorProxyType = Object.freeze({
+export const TorProxyType = Object.freeze({
Invalid: -1,
Socks4: 0,
Socks5: 1,
HTTPS: 2,
});
-const TorBuiltinBridgeTypes = Object.freeze(
+export const TorBuiltinBridgeTypes = Object.freeze(
(() => {
const bridgeListBranch = Services.prefs.getBranch(
TorLauncherPrefs.default_bridge
@@ -254,7 +240,7 @@ const arrayCopy = function (array) {
/* TorSettings module */
-const TorSettings = (() => {
+export const TorSettings = (() => {
const self = {
_settings: null,
@@ -288,7 +274,8 @@ const TorSettings = (() => {
/* load or init our settings, and register observers */
init() {
- if (TorMonitorService.ownsTorDaemon) {
+ const provider = lazy.TorProviderBuilder.build();
+ if (provider.ownsTorDaemon) {
// if the settings branch exists, load settings from prefs
if (Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)) {
this.loadFromPrefs();
@@ -296,9 +283,9 @@ const TorSettings = (() => {
// otherwise load defaults
this._settings = this.defaultSettings();
}
- Services.obs.addObserver(this, TorTopics.ProcessIsReady);
+ Services.obs.addObserver(this, lazy.TorProviderTopics.ProcessIsReady);
- if (TorMonitorService.isRunning) {
+ if (provider.isRunning) {
this.handleProcessReady();
}
}
@@ -309,8 +296,11 @@ const TorSettings = (() => {
console.log(`TorSettings: Observed ${topic}`);
switch (topic) {
- case TorTopics.ProcessIsReady:
- Services.obs.removeObserver(this, TorTopics.ProcessIsReady);
+ case lazy.TorProviderTopics.ProcessIsReady:
+ Services.obs.removeObserver(
+ this,
+ lazy.TorProviderTopics.ProcessIsReady
+ );
await this.handleProcessReady();
break;
}
@@ -569,7 +559,7 @@ const TorSettings = (() => {
}
/* Push to Tor */
- await TorProtocolService.writeSettings(settingsMap);
+ await lazy.TorProviderBuilder.build().writeSettings(settingsMap);
return this;
},
=====================================
browser/modules/moz.build
=====================================
@@ -123,7 +123,7 @@ XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"]
EXTRA_JS_MODULES += [
"AboutNewTab.jsm",
"AsyncTabSwitcher.jsm",
- "BridgeDB.jsm",
+ "BridgeDB.sys.mjs",
"BrowserUIUtils.jsm",
"BrowserUsageTelemetry.jsm",
"BrowserWindowTracker.jsm",
@@ -135,7 +135,7 @@ EXTRA_JS_MODULES += [
"FeatureCallout.sys.mjs",
"HomePage.jsm",
"LaterRun.jsm",
- 'Moat.jsm',
+ "Moat.sys.mjs",
"NewTabPagePreloading.jsm",
"OpenInTabsUtils.jsm",
"PageActions.jsm",
@@ -149,8 +149,8 @@ EXTRA_JS_MODULES += [
"SitePermissions.sys.mjs",
"TabsList.jsm",
"TabUnloader.jsm",
- "TorConnect.jsm",
- "TorSettings.jsm",
+ "TorConnect.sys.mjs",
+ "TorSettings.sys.mjs",
"TorStrings.jsm",
"TransientPrefs.jsm",
"URILoadingHelper.sys.mjs",
=====================================
toolkit/components/tor-launcher/TorBootstrapRequest.sys.mjs
=====================================
@@ -1,6 +1,6 @@
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
-import { TorProtocolService } from "resource://gre/modules/TorProtocolService.sys.mjs";
+import { TorProviderBuilder } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
/* tor-launcher observer topics */
@@ -13,19 +13,23 @@ export const TorTopics = Object.freeze({
// modeled after XMLHttpRequest
// nicely encapsulates the observer register/unregister logic
export class TorBootstrapRequest {
+ // number of ms to wait before we abandon the bootstrap attempt
+ // a value of 0 implies we never wait
+ timeout = 0;
+
+ // callbacks for bootstrap process status updates
+ onbootstrapstatus = (progress, status) => {};
+ onbootstrapcomplete = () => {};
+ onbootstraperror = (message, details) => {};
+
+ // internal resolve() method for bootstrap
+ #bootstrapPromiseResolve = null;
+ #bootstrapPromise = null;
+ #timeoutID = null;
+ #provider = null;
+
constructor() {
- // number of ms to wait before we abandon the bootstrap attempt
- // a value of 0 implies we never wait
- this.timeout = 0;
- // callbacks for bootstrap process status updates
- this.onbootstrapstatus = (progress, status) => {};
- this.onbootstrapcomplete = () => {};
- this.onbootstraperror = (message, details) => {};
-
- // internal resolve() method for bootstrap
- this._bootstrapPromiseResolve = null;
- this._bootstrapPromise = null;
- this._timeoutID = null;
+ this.#provider = TorProviderBuilder.build();
}
observe(subject, topic, data) {
@@ -41,15 +45,16 @@ export class TorBootstrapRequest {
if (this.onbootstrapcomplete) {
this.onbootstrapcomplete();
}
- this._bootstrapPromiseResolve(true);
- clearTimeout(this._timeoutID);
+ this.#bootstrapPromiseResolve(true);
+ clearTimeout(this.#timeoutID);
+ this.#timeoutID = null;
}
break;
}
case TorTopics.BootstrapError: {
console.info("TorBootstrapRequest: observerd TorBootstrapError", obj);
- this._stop(obj?.message, obj?.details);
+ this.#stop(obj?.message, obj?.details);
break;
}
}
@@ -57,12 +62,12 @@ export class TorBootstrapRequest {
// resolves 'true' if bootstrap succeeds, false otherwise
bootstrap() {
- if (this._bootstrapPromise) {
- return this._bootstrapPromise;
+ if (this.#bootstrapPromise) {
+ return this.#bootstrapPromise;
}
- this._bootstrapPromise = new Promise((resolve, reject) => {
- this._bootstrapPromiseResolve = resolve;
+ this.#bootstrapPromise = new Promise((resolve, reject) => {
+ this.#bootstrapPromiseResolve = resolve;
// register ourselves to listen for bootstrap events
Services.obs.addObserver(this, TorTopics.BootstrapStatus);
@@ -70,10 +75,10 @@ export class TorBootstrapRequest {
// optionally cancel bootstrap after a given timeout
if (this.timeout > 0) {
- this._timeoutID = setTimeout(async () => {
- this._timeoutID = null;
+ this.#timeoutID = setTimeout(async () => {
+ this.#timeoutID = null;
// TODO: Translate, if really used
- await this._stop(
+ await this.#stop(
"Tor Bootstrap process timed out",
`Bootstrap attempt abandoned after waiting ${this.timeout} ms`
);
@@ -81,38 +86,45 @@ export class TorBootstrapRequest {
}
// wait for bootstrapping to begin and maybe handle error
- TorProtocolService.connect().catch(err => {
- this._stop(err.message, "");
+ this.#provider.connect().catch(err => {
+ this.#stop(err.message, "");
});
}).finally(() => {
// and remove ourselves once bootstrap is resolved
Services.obs.removeObserver(this, TorTopics.BootstrapStatus);
Services.obs.removeObserver(this, TorTopics.BootstrapError);
- this._bootstrapPromise = null;
+ this.#bootstrapPromise = null;
});
- return this._bootstrapPromise;
+ return this.#bootstrapPromise;
}
async cancel() {
- await this._stop();
+ await this.#stop();
}
// Internal implementation. Do not use directly, but call cancel, instead.
- async _stop(message, details) {
+ async #stop(message, details) {
// first stop our bootstrap timeout before handling the error
- if (this._timeoutID !== null) {
- clearTimeout(this._timeoutID);
- this._timeoutID = null;
+ if (this.#timeoutID !== null) {
+ clearTimeout(this.#timeoutID);
+ this.#timeoutID = null;
}
- // stopBootstrap never throws
- await TorProtocolService.stopBootstrap();
+ try {
+ await this.#provider.stopBootstrap();
+ } catch (e) {
+ console.error("Failed to stop the bootstrap.", e);
+ if (!message) {
+ message = e.message;
+ details = "";
+ }
+ }
if (this.onbootstraperror && message) {
this.onbootstraperror(message, details);
}
- this._bootstrapPromiseResolve(false);
+ this.#bootstrapPromiseResolve(false);
}
}
=====================================
toolkit/components/tor-launcher/TorControlPort.sys.mjs
=====================================
@@ -274,6 +274,44 @@ class AsyncSocket {
* the command
*/
+/**
+ * @typedef {object} Bridge
+ * @property {string} transport The transport of the bridge, or vanilla if not
+ * specified.
+ * @property {string} addr The IP address and port of the bridge
+ * @property {string} id The fingerprint of the bridge
+ * @property {string} args Optional arguments passed to the bridge
+ */
+/**
+ * @typedef {object} PTInfo The information about a pluggable transport
+ * @property {string[]} transports An array with all the transports supported by
+ * this configuration.
+ * @property {string} type Either socks4, socks5 or exec
+ * @property {string} [ip] The IP address of the proxy (only for socks4 and
+ * socks5)
+ * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
+ * @property {string} [pathToBinary] Path to the binary that is run (only for
+ * exec)
+ * @property {string} [options] Optional options passed to the binary (only for
+ * exec)
+ */
+/**
+ * @typedef {object} OnionAuthKeyInfo
+ * @property {string} address The address of the onion service
+ * @property {string} typeAndKey Onion service key and type of key, as
+ * `type:base64-private-key`
+ * @property {string} Flags Additional flags, such as Permanent
+ */
+/**
+ * @callback EventFilterCallback
+ * @param {any} data Either a raw string, or already parsed data
+ * @returns {boolean}
+ */
+/**
+ * @callback EventCallback
+ * @param {any} data Either a raw string, or already parsed data
+ */
+
class TorError extends Error {
constructor(command, reply) {
super(`${command} -> ${reply}`);
@@ -584,319 +622,6 @@ class ControlSocket {
}
}
-// ## utils
-// A namespace for utility functions
-let utils = {};
-
-// __utils.identity(x)__.
-// Returns its argument unchanged.
-utils.identity = function (x) {
- return x;
-};
-
-// __utils.capture(string, regex)__.
-// Takes a string and returns an array of capture items, where regex must have a single
-// capturing group and use the suffix /.../g to specify a global search.
-utils.capture = function (string, regex) {
- let matches = [];
- // Special trick to use string.replace for capturing multiple matches.
- string.replace(regex, function (a, captured) {
- matches.push(captured);
- });
- return matches;
-};
-
-// __utils.extractor(regex)__.
-// Returns a function that takes a string and returns an array of regex matches. The
-// regex must use the suffix /.../g to specify a global search.
-utils.extractor = function (regex) {
- return function (text) {
- return utils.capture(text, regex);
- };
-};
-
-// __utils.splitLines(string)__.
-// Splits a string into an array of strings, each corresponding to a line.
-utils.splitLines = function (string) {
- return string.split(/\r?\n/);
-};
-
-// __utils.splitAtSpaces(string)__.
-// Splits a string into chunks between spaces. Does not split at spaces
-// inside pairs of quotation marks.
-utils.splitAtSpaces = utils.extractor(/((\S*?"(.*?)")+\S*|\S+)/g);
-
-// __utils.splitAtFirst(string, regex)__.
-// Splits a string at the first instance of regex match. If no match is
-// found, returns the whole string.
-utils.splitAtFirst = function (string, regex) {
- let match = string.match(regex);
- return match
- ? [
- string.substring(0, match.index),
- string.substring(match.index + match[0].length),
- ]
- : string;
-};
-
-// __utils.splitAtEquals(string)__.
-// Splits a string into chunks between equals. Does not split at equals
-// inside pairs of quotation marks.
-utils.splitAtEquals = utils.extractor(/(([^=]*?"(.*?)")+[^=]*|[^=]+)/g);
-
-// __utils.mergeObjects(arrayOfObjects)__.
-// Takes an array of objects like [{"a":"b"},{"c":"d"}] and merges to a single object.
-// Pure function.
-utils.mergeObjects = function (arrayOfObjects) {
- let result = {};
- for (let obj of arrayOfObjects) {
- for (let key in obj) {
- result[key] = obj[key];
- }
- }
- return result;
-};
-
-// __utils.listMapData(parameterString, listNames)__.
-// Takes a list of parameters separated by spaces, of which the first several are
-// unnamed, and the remainder are named, in the form `NAME=VALUE`. Apply listNames
-// to the unnamed parameters, and combine them in a map with the named parameters.
-// Example: `40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH`
-//
-// utils.listMapData("40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH",
-// ["streamID", "event", "circuitID", "IP"])
-// // --> {"streamID" : "40", "event" : "FAILED", "circuitID" : "0",
-// // "address" : "95.78.59.36:80", "REASON" : "CANT_ATTACH"}"
-utils.listMapData = function (parameterString, listNames) {
- // Split out the space-delimited parameters.
- let parameters = utils.splitAtSpaces(parameterString),
- dataMap = {};
- // Assign listNames to the first n = listNames.length parameters.
- for (let i = 0; i < listNames.length; ++i) {
- dataMap[listNames[i]] = parameters[i];
- }
- // Read key-value pairs and copy these to the dataMap.
- for (let i = listNames.length; i < parameters.length; ++i) {
- let [key, value] = utils.splitAtEquals(parameters[i]);
- if (key && value) {
- dataMap[key] = value;
- }
- }
- return dataMap;
-};
-
-// ## info
-// A namespace for functions related to tor's GETINFO and GETCONF command.
-let info = {};
-
-// __info.keyValueStringsFromMessage(messageText)__.
-// Takes a message (text) response to GETINFO or GETCONF and provides
-// a series of key-value strings, which are either multiline (with a `250+` prefix):
-//
-// 250+config/defaults=
-// AccountingMax "0 bytes"
-// AllowDotExit "0"
-// .
-//
-// or single-line (with a `250-` or `250 ` prefix):
-//
-// 250-version=0.2.6.0-alpha-dev (git-b408125288ad6943)
-info.keyValueStringsFromMessage = utils.extractor(
- /^(250\+[\s\S]+?^\.|250[- ].+?)$/gim
-);
-
-// __info.applyPerLine(transformFunction)__.
-// Returns a function that splits text into lines,
-// and applies transformFunction to each line.
-info.applyPerLine = function (transformFunction) {
- return function (text) {
- return utils.splitLines(text.trim()).map(transformFunction);
- };
-};
-
-// __info.routerStatusParser(valueString)__.
-// Parses a router status entry as, described in
-// https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt
-// (search for "router status entry")
-info.routerStatusParser = function (valueString) {
- let lines = utils.splitLines(valueString),
- objects = [];
- for (let line of lines) {
- // Drop first character and grab data following it.
- let myData = line.substring(2),
- // Accumulate more maps with data, depending on the first character in the line.
- dataFun = {
- r: data =>
- utils.listMapData(data, [
- "nickname",
- "identity",
- "digest",
- "publicationDate",
- "publicationTime",
- "IP",
- "ORPort",
- "DirPort",
- ]),
- a: data => ({ IPv6: data }),
- s: data => ({ statusFlags: utils.splitAtSpaces(data) }),
- v: data => ({ version: data }),
- w: data => utils.listMapData(data, []),
- p: data => ({ portList: data.split(",") }),
- }[line.charAt(0)];
- if (dataFun !== undefined) {
- objects.push(dataFun(myData));
- }
- }
- return utils.mergeObjects(objects);
-};
-
-// __info.circuitStatusParser(line)__.
-// Parse the output of a circuit status line.
-info.circuitStatusParser = function (line) {
- let data = utils.listMapData(line, ["id", "status", "circuit"]),
- circuit = data.circuit;
- // Parse out the individual circuit IDs and names.
- if (circuit) {
- data.circuit = circuit.split(",").map(function (x) {
- return x.split(/~|=/);
- });
- }
- return data;
-};
-
-// __info.streamStatusParser(line)__.
-// Parse the output of a stream status line.
-info.streamStatusParser = function (text) {
- return utils.listMapData(text, [
- "StreamID",
- "StreamStatus",
- "CircuitID",
- "Target",
- ]);
-};
-
-// TODO: fix this parsing logic to handle bridgeLine correctly
-// fingerprint/id is an optional parameter
-// __info.bridgeParser(bridgeLine)__.
-// Takes a single line from a `getconf bridge` result and returns
-// a map containing the bridge's type, address, and ID.
-info.bridgeParser = function (bridgeLine) {
- let result = {},
- tokens = bridgeLine.split(/\s+/);
- // First check if we have a "vanilla" bridge:
- if (tokens[0].match(/^\d+\.\d+\.\d+\.\d+/)) {
- result.type = "vanilla";
- [result.address, result.ID] = tokens;
- // Several bridge types have a similar format:
- } else {
- result.type = tokens[0];
- if (
- [
- "flashproxy",
- "fte",
- "meek",
- "meek_lite",
- "obfs3",
- "obfs4",
- "scramblesuit",
- "snowflake",
- ].includes(result.type)
- ) {
- [result.address, result.ID] = tokens.slice(1);
- }
- }
- return result.type ? result : null;
-};
-
-// __info.parsers__.
-// A map of GETINFO and GETCONF keys to parsing function, which convert
-// result strings to JavaScript data.
-info.parsers = {
- "ns/id/": info.routerStatusParser,
- "ip-to-country/": utils.identity,
- "circuit-status": info.applyPerLine(info.circuitStatusParser),
- bridge: info.bridgeParser,
- // Currently unused parsers:
- // "ns/name/" : info.routerStatusParser,
- // "stream-status" : info.applyPerLine(info.streamStatusParser),
- // "version" : utils.identity,
- // "config-file" : utils.identity,
-};
-
-// __info.getParser(key)__.
-// Takes a key and determines the parser function that should be used to
-// convert its corresponding valueString to JavaScript data.
-info.getParser = function (key) {
- return (
- info.parsers[key] ||
- info.parsers[key.substring(0, key.lastIndexOf("/") + 1)]
- );
-};
-
-// __info.stringToValue(string)__.
-// Converts a key-value string as from GETINFO or GETCONF to a value.
-info.stringToValue = function (string) {
- // key should look something like `250+circuit-status=` or `250-circuit-status=...`
- // or `250 circuit-status=...`
- let matchForKey = string.match(/^250[ +-](.+?)=/),
- key = matchForKey ? matchForKey[1] : null;
- if (key === null) {
- return null;
- }
- // matchResult finds a single-line result for `250-` or `250 `,
- // or a multi-line one for `250+`.
- let matchResult =
- string.match(/^250[ -].+?=(.*)$/) ||
- string.match(/^250\+.+?=([\s\S]*?)^\.$/m),
- // Retrieve the captured group (the text of the value in the key-value pair)
- valueString = matchResult ? matchResult[1] : null,
- // Get the parser function for the key found.
- parse = info.getParser(key.toLowerCase());
- if (parse === undefined) {
- throw new Error("No parser found for '" + key + "'");
- }
- // Return value produced by the parser.
- return parse(valueString);
-};
-
-/**
- * @typedef {object} Bridge
- * @property {string} transport The transport of the bridge, or vanilla if not
- * specified.
- * @property {string} addr The IP address and port of the bridge
- * @property {string} id The fingerprint of the bridge
- * @property {string} args Optional arguments passed to the bridge
- */
-/**
- * @typedef {object} PTInfo The information about a pluggable transport
- * @property {string[]} transports An array with all the transports supported by
- * this configuration.
- * @property {string} type Either socks4, socks5 or exec
- * @property {string} [ip] The IP address of the proxy (only for socks4 and
- * socks5)
- * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
- * @property {string} [pathToBinary] Path to the binary that is run (only for
- * exec)
- * @property {string} [options] Optional options passed to the binary (only for
- * exec)
- */
-/**
- * @typedef {object} OnionAuthKeyInfo
- * @property {string} address The address of the onion service
- * @property {string} typeAndKey Onion service key and type of key, as
- * `type:base64-private-key`
- * @property {string} Flags Additional flags, such as Permanent
- */
-/**
- * @callback EventFilterCallback
- * @param {any} data Either a raw string, or already parsed data
- * @returns {boolean}
- */
-/**
- * @callback EventCallback
- * @param {any} data Either a raw string, or already parsed data
- */
-
class TorController {
/**
* The control socket
@@ -905,16 +630,6 @@ class TorController {
*/
#socket;
- /**
- * A map of EVENT keys to parsing functions, which convert result strings to
- * JavaScript data.
- */
- #eventParsers = {
- stream: info.streamStatusParser,
- // Currently unused:
- // "circ" : info.circuitStatusParser,
- };
-
/**
* Builds a new TorController.
*
@@ -981,18 +696,6 @@ class TorController {
await this.#sendCommandSimple(`authenticate ${password || ""}`);
}
- /**
- * Sends a GETINFO for a single key.
- *
- * @param {string} key The key to get value for
- * @returns {any} The return value depends on the requested key
- */
- async getInfo(key) {
- this.#expectString(key, "key");
- const response = await this.sendCommand(`getinfo ${key}`);
- return this.#getMultipleResponseValues(response)[0];
- }
-
/**
* Sends a GETINFO for a single key.
* control-spec.txt says "one ReplyLine is sent for each requested value", so,
@@ -1054,9 +757,7 @@ class TorController {
const addresses = [v4[5]];
// a address:port
// dir-spec.txt also states only the first one should be taken
- // TODO: The consumers do not care about the port or the square brackets
- // either. Remove them when integrating this function with the rest
- const v6 = reply.match(/^a\s+(\[[0-9a-fA-F:]+\]:[0-9]{1,5})$/m);
+ const v6 = reply.match(/^a\s+\[([0-9a-fA-F:]+)\]:\d{1,5}$/m);
if (v6) {
addresses.push(v6[1]);
}
@@ -1091,23 +792,6 @@ class TorController {
// Configuration
- /**
- * Sends a GETCONF for a single key.
- * GETCONF with a single argument returns results with one or more lines that
- * look like `250[- ]key=value`.
- * Any GETCONF lines that contain a single keyword only are currently dropped.
- * So we can use similar parsing to that for getInfo.
- *
- * @param {string} key The key to get value for
- * @returns {any} A parsed config value (it depends if a parser is known)
- */
- async getConf(key) {
- this.#expectString(key, "key");
- return this.#getMultipleResponseValues(
- await this.sendCommand(`getconf ${key}`)
- );
- }
-
/**
* Sends a GETCONF for a single key.
* The function could be easily generalized to get multiple keys at once, but
@@ -1264,12 +948,14 @@ class TorController {
// TODO: Change the consumer and make the fields more consistent with what
// we get (e.g., separate key and type, and use a boolen for permanent).
const info = {
- hsAddress: match.groups.HSAddress,
- typeAndKey: `${match.groups.KeyType}:${match.groups.PrivateKeyBlob}`,
+ address: match.groups.HSAddress,
+ keyType: match.groups.KeyType,
+ keyBlob: match.groups.PrivateKeyBlob,
+ flags: [],
};
const maybeFlags = match.groups.other?.match(/Flags=(\S+)/);
if (maybeFlags) {
- info.Flags = maybeFlags[1];
+ info.flags = maybeFlags[1].split(",");
}
return info;
});
@@ -1369,28 +1055,12 @@ class TorController {
* first.
*
* @param {string} type The event type to catch
- * @param {EventFilterCallback?} filter An optional callback to filter
- * events for which the callback will be called. If null, all events will be
- * passed.
* @param {EventCallback} callback The callback that will handle the event
- * @param {boolean} raw Tell whether to ignore the data parser, even if
- * supported
*/
- watchEvent(type, filter, callback, raw = false) {
+ watchEvent(type, callback) {
this.#expectString(type, "type");
const start = `650 ${type}`;
- this.#socket.addNotificationCallback(new RegExp(`^${start}`), message => {
- // Remove also the initial text
- const dataText = message.substring(start.length + 1);
- const parser = this.#eventParsers[type.toLowerCase()];
- const data = dataText && parser ? parser(dataText) : null;
- // FIXME: This is the original code, but we risk of not filtering on the
- // data, if we ask for raw data (which we always do at the moment, but we
- // do not use a filter either...)
- if (filter === null || filter(data)) {
- callback(data && !raw ? data : message);
- }
- });
+ this.#socket.addNotificationCallback(new RegExp(`^${start}`), callback);
}
// Other helpers
@@ -1453,19 +1123,6 @@ class TorController {
)
);
}
-
- /**
- * Process multiple responses to a GETINFO or GETCONF request.
- *
- * @param {string} message The message to process
- * @returns {object[]} The keys depend on the message
- */
- #getMultipleResponseValues(message) {
- return info
- .keyValueStringsFromMessage(message)
- .map(info.stringToValue)
- .filter(x => x);
- }
}
const controlPortInfo = {};
=====================================
toolkit/components/tor-launcher/TorDomainIsolator.sys.mjs
=====================================
@@ -12,6 +12,11 @@ import {
const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+ TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
+
XPCOMUtils.defineLazyServiceGetters(lazy, {
ProtocolProxyService: [
"@mozilla.org/network/protocol-proxy-service;1",
@@ -19,11 +24,6 @@ XPCOMUtils.defineLazyServiceGetters(lazy, {
],
});
-ChromeUtils.defineESModuleGetters(lazy, {
- TorMonitorTopics: "resource://gre/modules/TorMonitorService.sys.mjs",
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
-});
-
const logger = new ConsoleAPI({
prefix: "TorDomainIsolator",
maxLogLevel: "warn",
@@ -143,7 +143,7 @@ class TorDomainIsolatorImpl {
Services.prefs.addObserver(NON_TOR_PROXY_PREF, this);
Services.obs.addObserver(this, NEW_IDENTITY_TOPIC);
- Services.obs.addObserver(this, lazy.TorMonitorTopics.StreamSucceeded);
+ Services.obs.addObserver(this, lazy.TorProviderTopics.StreamSucceeded);
this.#cleanupIntervalId = setInterval(
this.#clearKnownCircuits.bind(this),
@@ -158,7 +158,7 @@ class TorDomainIsolatorImpl {
uninit() {
Services.prefs.removeObserver(NON_TOR_PROXY_PREF, this);
Services.obs.removeObserver(this, NEW_IDENTITY_TOPIC);
- Services.obs.removeObserver(this, lazy.TorMonitorTopics.StreamSucceeded);
+ Services.obs.removeObserver(this, lazy.TorProviderTopics.StreamSucceeded);
clearInterval(this.#cleanupIntervalId);
this.#cleanupIntervalId = null;
this.clearIsolation();
@@ -257,12 +257,12 @@ class TorDomainIsolatorImpl {
);
this.clearIsolation();
try {
- await lazy.TorProtocolService.newnym();
+ await lazy.TorProviderBuilder.build().newnym();
} catch (e) {
logger.error("Could not send the newnym command", e);
// TODO: What UX to use here? See tor-browser#41708
}
- } else if (topic === lazy.TorMonitorTopics.StreamSucceeded) {
+ } else if (topic === lazy.TorProviderTopics.StreamSucceeded) {
const { username, password, circuit } = subject.wrappedJSObject;
this.#updateCircuit(username, password, circuit);
}
@@ -553,7 +553,7 @@ class TorDomainIsolatorImpl {
data = await Promise.all(
circuit.map(fingerprint =>
- lazy.TorProtocolService.getNodeInfo(fingerprint)
+ lazy.TorProviderBuilder.build().getNodeInfo(fingerprint)
)
);
this.#knownCircuits.set(id, data);
=====================================
toolkit/components/tor-launcher/TorMonitorService.sys.mjs deleted
=====================================
@@ -1,42 +0,0 @@
-// Copyright (c) 2022, The Tor Project, Inc.
-
-import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
-
-const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
-});
-
-export const TorMonitorTopics = Object.freeze({
- BridgeChanged: TorProviderTopics.BridgeChanged,
- StreamSucceeded: TorProviderTopics.StreamSucceeded,
-});
-
-/**
- * This service monitors an existing Tor instance, or starts one, if needed, and
- * then starts monitoring it.
- *
- * This is the service which should be queried to know information about the
- * status of the bootstrap, the logs, etc...
- */
-export const TorMonitorService = {
- get currentBridge() {
- return lazy.TorProtocolService.currentBridge;
- },
-
- get ownsTorDaemon() {
- return lazy.TorProtocolService.ownsTorDaemon;
- },
-
- get isRunning() {
- return lazy.TorProtocolService.isRunning;
- },
-
- get isBootstrapDone() {
- return lazy.TorProtocolService.isBootstrapDone;
- },
-
- getLog() {
- return lazy.TorProtocolService.getLog();
- },
-};
=====================================
toolkit/components/tor-launcher/TorParsers.sys.mjs
=====================================
@@ -269,11 +269,14 @@ export const TorParsers = Object.freeze({
},
parseBridgeLine(line) {
+ if (!line) {
+ return null;
+ }
const re =
/\s*(?:(?<transport>\S+)\s+)?(?<addr>[0-9a-fA-F\.\[\]\:]+:\d{1,5})(?:\s+(?<id>[0-9a-fA-F]{40}))?(?:\s+(?<args>.+))?/;
const match = re.exec(line);
if (!match) {
- throw new Error("Invalid bridge line.");
+ throw new Error(`Invalid bridge line: ${line}.`);
}
const bridge = match.groups;
if (!bridge.transport) {
=====================================
toolkit/components/tor-launcher/TorProtocolService.sys.mjs → toolkit/components/tor-launcher/TorProvider.sys.mjs
=====================================
@@ -1,4 +1,6 @@
-// Copyright (c) 2021, The Tor Project, Inc.
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
@@ -11,7 +13,6 @@ import {
import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
const lazy = {};
-
ChromeUtils.defineESModuleGetters(lazy, {
controller: "resource://gre/modules/TorControlPort.sys.mjs",
configureControlPortModule: "resource://gre/modules/TorControlPort.sys.mjs",
@@ -21,7 +22,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
const logger = new ConsoleAPI({
maxLogLevel: "warn",
- prefix: "TorProtocolService",
+ maxLogLevelPref: "browser.tor_provider.log_level",
+ prefix: "TorProvider",
});
/**
@@ -70,7 +72,7 @@ const ControlConnTimings = Object.freeze({
* It can start a new tor instance, or connect to an existing one.
* In the former case, it also takes its ownership by default.
*/
-class TorProvider {
+export class TorProvider {
#inited = false;
// Maintain a map of tor settings set by Tor Browser so that we don't
@@ -85,7 +87,6 @@ class TorProvider {
#SOCKSPortInfo = null; // An object that contains ipcFile, host, port.
#controlConnection = null; // This is cached and reused.
- #connectionQueue = [];
// Public methods
@@ -123,39 +124,34 @@ class TorProvider {
// takes a Map containing tor settings
// throws on error
async writeSettings(aSettingsObj) {
+ const entries =
+ aSettingsObj instanceof Map
+ ? Array.from(aSettingsObj.entries())
+ : Object.entries(aSettingsObj);
// only write settings that have changed
- const newSettings = Array.from(aSettingsObj).filter(([setting, value]) => {
- // make sure we have valid data here
- this.#assertValidSetting(setting, value);
-
+ const newSettings = entries.filter(([setting, value]) => {
if (!this.#settingsCache.has(setting)) {
// no cached setting, so write
return true;
}
const cachedValue = this.#settingsCache.get(setting);
- if (value === cachedValue) {
- return false;
- } else if (Array.isArray(value) && Array.isArray(cachedValue)) {
- // compare arrays member-wise
- if (value.length !== cachedValue.length) {
- return true;
- }
- for (let i = 0; i < value.length; i++) {
- if (value[i] !== cachedValue[i]) {
- return true;
- }
- }
- return false;
+ // 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])
+ );
}
- // some other different values
- return true;
+ return value !== cachedValue;
});
// only write if new setting to save
if (newSettings.length) {
- const settingsObject = Object.fromEntries(newSettings);
- await this.setConfWithReply(settingsObject);
+ const conn = await this.#getConnection();
+ await conn.setConf(Object.fromEntries(newSettings));
// save settings to cache after successfully writing to Tor
for (const [setting, value] of newSettings) {
@@ -164,23 +160,15 @@ class TorProvider {
}
}
- async readStringArraySetting(aSetting) {
- const value = await this.#readSetting(aSetting);
- this.#settingsCache.set(aSetting, value);
- return value;
- }
-
// writes current tor settings to disk
async flushSettings() {
- await this.sendCommand("SAVECONF");
+ const conn = await this.#getConnection();
+ await conn.flushSettings();
}
async connect() {
- const kTorConfKeyDisableNetwork = "DisableNetwork";
- const settings = {};
- settings[kTorConfKeyDisableNetwork] = false;
- await this.setConfWithReply(settings);
- await this.sendCommand("SAVECONF");
+ const conn = await this.#getConnection();
+ await conn.setNetworkEnabled(true);
this.clearBootstrapError();
this.retrieveBootstrapStatus();
}
@@ -188,12 +176,8 @@ class TorProvider {
async stopBootstrap() {
// Tell tor to disable use of the network; this should stop the bootstrap
// process.
- try {
- const settings = { DisableNetwork: true };
- await this.setConfWithReply(settings);
- } catch (e) {
- logger.error("Error stopping bootstrap", e);
- }
+ const conn = await this.#getConnection();
+ await conn.setNetworkEnabled(false);
// We are not interested in waiting for this, nor in **catching its error**,
// so we do not await this. We just want to be notified when the bootstrap
// status is actually updated through observers.
@@ -201,28 +185,31 @@ class TorProvider {
}
async newnym() {
- return this.sendCommand("SIGNAL NEWNYM");
+ const conn = await this.#getConnection();
+ await conn.newnym();
}
// Ask tor which ports it is listening to for SOCKS connections.
// At the moment this is used only in TorCheckService.
async getSocksListeners() {
- const cmd = "GETINFO";
- const keyword = "net/listeners/socks";
- const response = await this.sendCommand(cmd, keyword);
- return TorParsers.parseReply(cmd, keyword, response);
+ const conn = await this.#getConnection();
+ return conn.getSocksListeners();
}
async getBridges() {
+ const conn = await this.#getConnection();
// Ideally, we would not need this function, because we should be the one
// setting them with TorSettings. However, TorSettings is not notified of
// change of settings. So, asking tor directly with the control connection
// is the most reliable way of getting the configured bridges, at the
// moment. Also, we are using this for the circuit display, which should
// work also when we are not configuring the tor daemon, but just using it.
- return this.#withConnection(conn => {
- return conn.getConf("bridge");
- });
+ return conn.getBridges();
+ }
+
+ async getPluggableTransports() {
+ const conn = await this.#getConnection();
+ return conn.getPluggableTransports();
}
/**
@@ -232,68 +219,55 @@ class TorProvider {
* @returns {Promise<NodeData>}
*/
async getNodeInfo(id) {
- return this.#withConnection(async conn => {
- const node = {
- fingerprint: id,
- ipAddrs: [],
- bridgeType: null,
- regionCode: null,
- };
- const bridge = (await conn.getConf("bridge"))?.find(
- foundBridge => foundBridge.ID?.toUpperCase() === id.toUpperCase()
- );
- const addrRe = /^\[?([^\]]+)\]?:\d+$/;
- if (bridge) {
- node.bridgeType = bridge.type ?? "";
- // Attempt to get an IP address from bridge address string.
- const ip = bridge.address.match(addrRe)?.[1];
- if (ip && !ip.startsWith("0.")) {
- node.ipAddrs.push(ip);
- }
- } else {
- // Either dealing with a relay, or a bridge whose fingerprint is not
- // saved in torrc.
- const info = await conn.getInfo(`ns/id/${id}`);
- if (info.IP && !info.IP.startsWith("0.")) {
- node.ipAddrs.push(info.IP);
- }
- const ip6 = info.IPv6?.match(addrRe)?.[1];
- if (ip6) {
- node.ipAddrs.push(ip6);
- }
+ const conn = await this.#getConnection();
+ const node = {
+ fingerprint: id,
+ ipAddrs: [],
+ bridgeType: null,
+ regionCode: null,
+ };
+ const bridge = (await conn.getBridges())?.find(
+ foundBridge => foundBridge.id?.toUpperCase() === id.toUpperCase()
+ );
+ if (bridge) {
+ node.bridgeType = bridge.transport ?? "";
+ // Attempt to get an IP address from bridge address string.
+ const ip = bridge.addr.match(/^\[?([^\]]+)\]?:\d+$/)?.[1];
+ if (ip && !ip.startsWith("0.")) {
+ node.ipAddrs.push(ip);
}
- if (node.ipAddrs.length) {
- // Get the country code for the node's IP address.
- let regionCode;
- try {
- // Expect a 2-letter ISO3166-1 code, which should also be a valid
- // BCP47 Region subtag.
- regionCode = await conn.getInfo("ip-to-country/" + node.ipAddrs[0]);
- } catch {}
+ } else {
+ node.ipAddrs = await conn.getNodeAddresses(id);
+ }
+ if (node.ipAddrs.length) {
+ // Get the country code for the node's IP address.
+ try {
+ // Expect a 2-letter ISO3166-1 code, which should also be a valid
+ // BCP47 Region subtag.
+ const regionCode = await conn.getIPCountry(node.ipAddrs[0]);
if (regionCode && regionCode !== "??") {
node.regionCode = regionCode.toUpperCase();
}
+ } catch (e) {
+ logger.warn(`Cannot get a country for IP ${node.ipAddrs[0]}`, e);
}
- return node;
- });
+ }
+ return node;
}
- async onionAuthAdd(hsAddress, b64PrivateKey, isPermanent) {
- return this.#withConnection(conn => {
- return conn.onionAuthAdd(hsAddress, b64PrivateKey, isPermanent);
- });
+ async onionAuthAdd(address, b64PrivateKey, isPermanent) {
+ const conn = await this.#getConnection();
+ return conn.onionAuthAdd(address, b64PrivateKey, isPermanent);
}
- async onionAuthRemove(hsAddress) {
- return this.#withConnection(conn => {
- return conn.onionAuthRemove(hsAddress);
- });
+ async onionAuthRemove(address) {
+ const conn = await this.#getConnection();
+ return conn.onionAuthRemove(address);
}
async onionAuthViewKeys() {
- return this.#withConnection(conn => {
- return conn.onionAuthViewKeys();
- });
+ const conn = await this.#getConnection();
+ return conn.onionAuthViewKeys();
}
// TODO: transform the following 4 functions in getters.
@@ -333,106 +307,6 @@ class TorProvider {
return this.#SOCKSPortInfo;
}
- // Public, but called only internally
-
- // Executes a command on the control port.
- // Return a reply object or null if a fatal error occurs.
- async sendCommand(cmd, args) {
- const maxTimeout = 1000;
- let leftConnAttempts = 5;
- let timeout = 250;
- let reply;
- while (leftConnAttempts-- > 0) {
- const response = await this.#trySend(cmd, args, leftConnAttempts === 0);
- if (response.connected) {
- reply = response.reply;
- break;
- }
- // We failed to acquire the controller after multiple attempts.
- // Try again after some time.
- logger.warn(
- "sendCommand: Acquiring control connection failed, trying again later.",
- cmd,
- args
- );
- await new Promise(resolve => setTimeout(() => resolve(), timeout));
- timeout = Math.min(2 * timeout, maxTimeout);
- }
-
- // We sent the command, but we still got an empty response.
- // Something must be busted elsewhere.
- if (!reply) {
- throw new Error(`${cmd} sent an empty response`);
- }
-
- // TODO: Move the parsing of the reply to the controller, because anyone
- // calling sendCommand on it actually wants a parsed reply.
-
- reply = TorParsers.parseCommandResponse(reply);
- if (!TorParsers.commandSucceeded(reply)) {
- if (reply?.lineArray) {
- throw new Error(reply.lineArray.join("\n"));
- }
- throw new Error(`${cmd} failed with code ${reply.statusCode}`);
- }
-
- return reply;
- }
-
- // Perform a SETCONF command.
- // aSettingsObj should be a JavaScript object with keys (property values)
- // that correspond to tor config. keys. The value associated with each
- // key should be a simple string, a string array, or a Boolean value.
- // If an associated value is undefined or null, a key with no value is
- // passed in the SETCONF command.
- // Throws in case of error, or returns a reply object.
- async setConfWithReply(settings) {
- if (!settings) {
- throw new Error("Empty settings object");
- }
- const args = Object.entries(settings)
- .map(([key, val]) => {
- if (val === undefined || val === null) {
- return key;
- }
- const valType = typeof val;
- let rv = `${key}=`;
- if (valType === "boolean") {
- rv += val ? "1" : "0";
- } else if (Array.isArray(val)) {
- rv += val.map(TorParsers.escapeString).join(` ${key}=`);
- } else if (valType === "string") {
- rv += TorParsers.escapeString(val);
- } else {
- logger.error(`Got unsupported type for ${key}`, val);
- throw new Error(`Unsupported type ${valType} (key ${key})`);
- }
- return rv;
- })
- .filter(arg => arg);
- if (!args.length) {
- throw new Error("No settings to set");
- }
-
- await this.sendCommand("SETCONF", args.join(" "));
- }
-
- // Public, never called?
-
- async readBoolSetting(aSetting) {
- let value = await this.#readBoolSetting(aSetting);
- this.#settingsCache.set(aSetting, value);
- return value;
- }
-
- async readStringSetting(aSetting) {
- let value = await this.#readStringSetting(aSetting);
- this.#settingsCache.set(aSetting, value);
- return value;
- }
-
- // Private
-
async #setSockets() {
try {
const isWindows = TorLauncherUtil.isWindows;
@@ -511,167 +385,24 @@ class TorProvider {
}
}
- #assertValidSettingKey(aSetting) {
- // ensure the 'key' is a string
- if (typeof aSetting !== "string") {
- throw new Error(
- `Expected setting of type string but received ${typeof aSetting}`
- );
- }
- }
-
- #assertValidSetting(aSetting, aValue) {
- this.#assertValidSettingKey(aSetting);
- switch (typeof aValue) {
- case "boolean":
- case "string":
- return;
- case "object":
- if (aValue === null) {
- return;
- } else if (Array.isArray(aValue)) {
- for (const element of aValue) {
- if (typeof element !== "string") {
- throw new Error(
- `Setting '${aSetting}' array contains value of invalid type '${typeof element}'`
- );
- }
- }
- return;
- }
- // fall through
- default:
- throw new Error(
- `Invalid object type received for setting '${aSetting}'`
- );
- }
- }
-
- // Perform a GETCONF command.
- async #readSetting(aSetting) {
- this.#assertValidSettingKey(aSetting);
-
- const cmd = "GETCONF";
- let reply = await this.sendCommand(cmd, aSetting);
- return TorParsers.parseReply(cmd, aSetting, reply);
- }
-
- async #readStringSetting(aSetting) {
- 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}`
- );
- }
- return lineArray[0];
- }
-
- async #readBoolSetting(aSetting) {
- const value = this.#readStringSetting(aSetting);
- switch (value) {
- case "0":
- return false;
- case "1":
- return true;
- default:
- throw new Error(`Expected boolean (1 or 0) but received '${value}'`);
- }
- }
-
- async #trySend(cmd, args, rethrow) {
- let connected = false;
- let reply;
- let leftAttempts = 2;
- while (leftAttempts-- > 0) {
- let conn;
- try {
- conn = await this.#getConnection();
- } catch (e) {
- logger.error("Cannot get a connection to the control port", e);
- if (leftAttempts == 0 && rethrow) {
- throw e;
- }
- }
- if (!conn) {
- continue;
- }
- // If we _ever_ got a connection, the caller should not try again
- connected = true;
- try {
- reply = await conn.sendCommand(cmd + (args ? " " + args : ""));
- if (reply) {
- // Return for reuse.
- this.#returnConnection();
- } else {
- // Connection is bad.
- logger.warn(
- "sendCommand returned an empty response, taking the connection as broken and closing it."
- );
- this.#closeConnection();
- }
- } catch (e) {
- logger.error(`Cannot send the command ${cmd}`, e);
- this.#closeConnection();
- if (leftAttempts == 0 && rethrow) {
- throw e;
- }
- }
- }
- return { connected, reply };
- }
-
- // Opens an authenticated connection, sets it to this.#controlConnection, and
- // return it.
async #getConnection() {
- if (!this.#controlConnection) {
+ if (!this.#controlConnection?.isOpen) {
this.#controlConnection = await lazy.controller();
}
- if (this.#controlConnection.inUse) {
- await new Promise((resolve, reject) =>
- this.#connectionQueue.push({ resolve, reject })
- );
- } else {
- this.#controlConnection.inUse = true;
- }
return this.#controlConnection;
}
- #returnConnection() {
- if (this.#connectionQueue.length) {
- this.#connectionQueue.shift().resolve();
- } else {
- this.#controlConnection.inUse = false;
- }
- }
-
- async #withConnection(func) {
- // TODO: Make more robust?
- const conn = await this.#getConnection();
- try {
- return await func(conn);
- } finally {
- this.#returnConnection();
- }
- }
-
- // If aConn is omitted, the cached connection is closed.
#closeConnection() {
if (this.#controlConnection) {
logger.info("Closing the control connection");
this.#controlConnection.close();
this.#controlConnection = null;
}
- for (const promise of this.#connectionQueue) {
- promise.reject("Connection closed");
- }
- this.#connectionQueue = [];
}
async #reconnect() {
this.#closeConnection();
- const conn = await this.#getConnection();
- logger.debug("Reconnected to the control port.");
- this.#returnConnection(conn);
+ await this.#getConnection();
}
async #readAuthenticationCookie(aPath) {
@@ -777,8 +508,9 @@ class TorProvider {
if (this.ownsTorDaemon) {
// When we own the tor daemon, we listen to more events, that are used
// for about:torconnect or for showing the logs in the settings page.
- this._eventHandlers.set("STATUS_CLIENT", (_eventType, lines) =>
- this._processBootstrapStatus(lines[0], false)
+ this._eventHandlers.set(
+ "STATUS_CLIENT",
+ this._processStatusClient.bind(this)
);
this._eventHandlers.set("NOTICE", this._processLog.bind(this));
this._eventHandlers.set("WARN", this._processLog.bind(this));
@@ -809,23 +541,10 @@ class TorProvider {
throw new Error("Event monitor connection not available");
}
- // TODO: Unify with TorProtocolService.sendCommand and put everything in the
- // reviewed torbutton replacement.
- const cmd = "GETINFO";
- const key = "status/bootstrap-phase";
- let reply = await this._connection.sendCommand(`${cmd} ${key}`);
-
- // A typical reply looks like:
- // 250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"
- // 250 OK
- reply = TorParsers.parseCommandResponse(reply);
- if (!TorParsers.commandSucceeded(reply)) {
- throw new Error(`${cmd} failed`);
- }
- reply = TorParsers.parseReply(cmd, key, reply);
- if (reply.length) {
- this._processBootstrapStatus(reply[0], true);
- }
+ this._processBootstrapStatus(
+ await this._connection.getBootstrapPhase(),
+ true
+ );
}
// Returns captured log message as a text string (one message per line).
@@ -1058,37 +777,32 @@ class TorProvider {
_monitorEvent(type, callback) {
logger.info(`Watching events of type ${type}.`);
let replyObj = {};
- this._connection.watchEvent(
- type,
- null,
- line => {
- if (!line) {
- return;
- }
- logger.debug("Event response: ", line);
- const isComplete = TorParsers.parseReplyLine(line, replyObj);
- if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
- return;
- }
- const reply = replyObj;
- replyObj = {};
- if (reply.statusCode !== TorStatuses.EventNotification) {
- logger.error("Unexpected event status code:", reply.statusCode);
- return;
- }
- if (!reply.lineArray[0].startsWith(`${type} `)) {
- logger.error("Wrong format for the first line:", reply.lineArray[0]);
- return;
- }
- reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
- try {
- callback(type, reply.lineArray);
- } catch (e) {
- logger.error("Exception while handling an event", reply, e);
- }
- },
- true
- );
+ this._connection.watchEvent(type, line => {
+ if (!line) {
+ return;
+ }
+ logger.debug("Event response: ", line);
+ const isComplete = TorParsers.parseReplyLine(line, replyObj);
+ if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
+ return;
+ }
+ const reply = replyObj;
+ replyObj = {};
+ if (reply.statusCode !== TorStatuses.EventNotification) {
+ logger.error("Unexpected event status code:", reply.statusCode);
+ return;
+ }
+ if (!reply.lineArray[0].startsWith(`${type} `)) {
+ logger.error("Wrong format for the first line:", reply.lineArray[0]);
+ return;
+ }
+ reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
+ try {
+ callback(type, reply.lineArray);
+ } catch (e) {
+ logger.error("Exception while handling an event", reply, e);
+ }
+ });
}
_processLog(type, lines) {
@@ -1116,15 +830,12 @@ class TorProvider {
// to TorBootstrapStatus observers.
// If aSuppressErrors is true, errors are ignored. This is used when we
// are handling the response to a "GETINFO status/bootstrap-phase" command.
- _processBootstrapStatus(aStatusMsg, aSuppressErrors) {
- const statusObj = TorParsers.parseBootstrapStatus(aStatusMsg);
- if (!statusObj) {
- return;
- }
-
+ _processBootstrapStatus(statusObj, suppressErrors) {
// Notify observers
- statusObj.wrappedJSObject = statusObj;
- Services.obs.notifyObservers(statusObj, "TorBootstrapStatus");
+ Services.obs.notifyObservers(
+ { wrappedJSObject: statusObj },
+ "TorBootstrapStatus"
+ );
if (statusObj.PROGRESS === 100) {
this._isBootstrapDone = true;
@@ -1141,7 +852,7 @@ class TorProvider {
if (
statusObj.TYPE === "WARN" &&
statusObj.RECOMMENDATION !== "ignore" &&
- !aSuppressErrors
+ !suppressErrors
) {
this._notifyBootstrapError(statusObj);
}
@@ -1184,6 +895,15 @@ class TorProvider {
}
}
+ _processStatusClient(_type, lines) {
+ const statusObj = TorParsers.parseBootstrapStatus(lines[0]);
+ if (!statusObj) {
+ // No `BOOTSTRAP` in the line
+ return;
+ }
+ this._processBootstrapStatus(statusObj, false);
+ }
+
async _processCircEvent(_type, lines) {
const builtEvent =
/^(?<CircuitID>[a-zA-Z0-9]{1,16})\sBUILT\s(?<Path>(?:,?\$[0-9a-fA-F]{40}(?:~[a-zA-Z0-9]{1,19})?)+)/.exec(
@@ -1295,7 +1015,3 @@ class TorProvider {
this.clearBootstrapError();
}
}
-
-// TODO: Stop defining TorProtocolService, make the builder instance the
-// TorProvider.
-export const TorProtocolService = new TorProvider();
=====================================
toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
=====================================
@@ -4,7 +4,7 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
+ TorProvider: "resource://gre/modules/TorProvider.sys.mjs",
});
export const TorProviderTopics = Object.freeze({
@@ -19,16 +19,25 @@ export const TorProviderTopics = Object.freeze({
});
export class TorProviderBuilder {
+ static #provider = null;
+
static async init() {
- await lazy.TorProtocolService.init();
+ const provider = new lazy.TorProvider();
+ await provider.init();
+ // Assign it only when initialization succeeds.
+ TorProviderBuilder.#provider = provider;
}
static uninit() {
- lazy.TorProtocolService.uninit();
+ TorProviderBuilder.#provider.uninit();
+ TorProviderBuilder.#provider = null;
}
// TODO: Switch to an async build?
static build() {
- return lazy.TorProtocolService;
+ if (!TorProviderBuilder.#provider) {
+ throw new Error("TorProviderBuilder has not been initialized yet.");
+ }
+ return TorProviderBuilder.#provider;
}
}
=====================================
toolkit/components/tor-launcher/TorStartupService.sys.mjs
=====================================
@@ -3,22 +3,13 @@ const lazy = {};
// We will use the modules only when the profile is loaded, so prefer lazy
// loading
ChromeUtils.defineESModuleGetters(lazy, {
+ TorConnect: "resource:///modules/TorConnect.sys.mjs",
TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+ TorSettings: "resource:///modules/TorSettings.sys.mjs",
});
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorConnect",
- "resource:///modules/TorConnect.jsm"
-);
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorSettings",
- "resource:///modules/TorSettings.jsm"
-);
-
/* Browser observer topis */
const BrowserTopics = Object.freeze({
ProfileAfterChange: "profile-after-change",
=====================================
toolkit/components/tor-launcher/moz.build
=====================================
@@ -3,10 +3,9 @@ EXTRA_JS_MODULES += [
"TorControlPort.sys.mjs",
"TorDomainIsolator.sys.mjs",
"TorLauncherUtil.sys.mjs",
- "TorMonitorService.sys.mjs",
"TorParsers.sys.mjs",
"TorProcess.sys.mjs",
- "TorProtocolService.sys.mjs",
+ "TorProvider.sys.mjs",
"TorProviderBuilder.sys.mjs",
"TorStartupService.sys.mjs",
]
=====================================
toolkit/mozapps/update/UpdateService.sys.mjs
=====================================
@@ -23,18 +23,13 @@ ChromeUtils.defineESModuleGetters(lazy, {
AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
CertUtils: "resource://gre/modules/CertUtils.sys.mjs",
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
ctypes: "resource://gre/modules/ctypes.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
});
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorMonitorService",
- "resource://gre/modules/TorMonitorService.jsm"
-);
-
XPCOMUtils.defineLazyServiceGetter(
lazy,
"AUS",
@@ -394,10 +389,11 @@ XPCOMUtils.defineLazyGetter(
);
function _shouldRegisterBootstrapObserver(errorCode) {
+ const provider = lazy.TorProviderBuilder.build();
return (
errorCode == PROXY_SERVER_CONNECTION_REFUSED &&
- !lazy.TorMonitorService.isBootstrapDone &&
- lazy.TorMonitorService.ownsTorDaemon
+ !provider.isBootstrapDone &&
+ provider.ownsTorDaemon
);
}
@@ -5833,10 +5829,7 @@ Downloader.prototype = {
// we choose to compute these hashes.
hash = hash.finish(false);
digest = Array.from(hash, (c, i) =>
- hash
- .charCodeAt(i)
- .toString(16)
- .padStart(2, "0")
+ hash.charCodeAt(i).toString(16).padStart(2, "0")
).join("");
} catch (e) {
LOG(
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/ab8a15b721bc218d4b1588f06337c96054cac91b...384dff974998a27132ad4669f2ff2a0a07a4c274
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/ab8a15b721bc218d4b1588f06337c96054cac91b...384dff974998a27132ad4669f2ff2a0a07a4c274
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/20230807/c5826d54/attachment-0001.htm>
More information about the tbb-commits
mailing list